Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

insta-build plugin #1857

Closed
myk002 opened this issue May 24, 2021 · 3 comments · Fixed by DFHack/scripts#310
Closed

insta-build plugin #1857

myk002 opened this issue May 24, 2021 · 3 comments · Fixed by DFHack/scripts#310
Assignees
Labels
idea Suggestions, etc.
Projects

Comments

@myk002
Copy link
Member

myk002 commented May 24, 2021

Similar to the insta-dig plugin proposed in #1843, it would be nice to have an insta-build script/plugin ("build-now"?) that would instantly complete building construction tasks.

With this, we could test quickfort #query blueprints (which require built furniture from which to define rooms) in the functional test harness that's part of Milestone 1 of #1842

The key tasks here are:

  • complete "architecture" jobs (e.g. for bridges and furnaces)
  • move attached items to buildings (i.e. complete the construction job)
  • change buildings to "built" state
  • remove construction jobs and clean up references (Jobs::removeJob())

Only buildings that have all items attached should be completed. If the building has any non-0-quantity filters (i.e. job_items), or is suspended then this plugin should skip it.

It occurs to me that I'll also need insta-deconstruct functionality as well so I can clean up after a test. Not sure if that should be part of this plugin/script or part of a new "deconstruct-now" plugin/script.

I originally also thought that this plugin/script should create required items for buildingplanned buildings and call buildingplan.doCycle() to generate construction jobs, but I think now that item creation should be delegated to a separate plugin/script, perhaps one that completes manager orders instantly ("orders-now"?).

@myk002 myk002 added idea Suggestions, etc. question labels May 24, 2021
@myk002
Copy link
Member Author

myk002 commented Jun 13, 2021

@wolfboyft are you interested in taking this?

@Tachytaenius
Copy link
Contributor

Well... seems I already did something like this for my stepladder script, tho it's bugged somewhere. Should be ok to do. Unless i'm too late hahoops

This could sensibly be part of a tiled --> dwarf fort pipeline, right? what, tiled-->quickfort bp-->quickfort-->this? is that (minus the prepention) how this particular ecosystem of scripts works atm?

@myk002
Copy link
Member Author

myk002 commented Jun 27, 2021

Awesome : ) I'll start on an orders-now script that creates items to fulfill manager orders so build-now will have buildings to build.

This could sensibly be part of a tiled --> dwarf fort pipeline, right? what, tiled-->quickfort bp-->quickfort-->this?

That's pretty much the idea. You can see how they're all intended to work together in #1859

I had put together a skeleton for the build-now script that you can use as a starting point if you like:

local utils = require('utils')
local guidm = require('gui.dwarfmode')

local help_text = [====[
build-now
=========

Instantly completes unsuspended building construction jobs. By default, all
buildings on the map are completed, but the area of effect is configurable.

Note that no units will get architecture experience for any buildings that
require that skill to construct.

Usage::

    build-now [<pos> [<pos>]] [<options>]

Where the optional ``<pos>`` pair can be used to specify the coordinate bounds
within which ``build-now`` will operate. If they are not specified,
``build-now`` will scan the entire map. If only one ``<pos>`` is specified, only the
building at that coordinate is built.

The ``<pos>`` parameters can either be an ``<x>,<y>,<z>`` triple (e.g.
``35,12,150``) or the string ``here``, which means the position of the active
game cursor should be used.

Examples:

``build-now``
    Completes all unsuspended construction jobs on the map.

``build-now here``
    Builds the unsuspended, unconstructed building under the cursor.

Options:

:``-h``, ``--help``:
    Show quick usage help text.
]====]

function print_help() print(help_text) end

local function parse_coords(opts, configname, arg)
    local x, y, z
    if arg == 'here' then
        local cursor = guidm.getCursorPos()
        if not cursor then
            qerror('"here" specified for position, but no game cursor found!')
        end
        x, y, z = cursor.x, cursor.y, cursor.z
    else
        _, _, x, y, z = arg:find('^%s*(%d+)%s*,%s*(%d+)%s*,%s*(%d+)%s*$')
        if not x then
            qerror(('invalid position: "%s"; expected "here" or' ..
                    ' "<x>,<y>,<z>" triple (e.g. "30,60,150")'):format(arg))
        end
    end
    opts[configname].x = tonumber(x)
    opts[configname].y = tonumber(y)
    opts[configname].z = tonumber(z)
end

local function min_to_max(...)
    local args = {...}
    table.sort(args, function(a, b) return a < b end)
    return table.unpack(args)
end

function parse_commandline(opts, ...)
    local positionals = utils.processArgsGetopt({...}, {
            {'h', 'help', handler=function() opts.help = true end},
        })

    if positionals[1] == 'help' then opts.help = true end
    if opts.help then return end

    if #positionals >= 1 then
        parse_coords(opts, 'start', positionals[1])
        if #positionals >= 2 then
            parse_coords(opts, 'end', positionals[2])
            opts.start.x, opts['end'].x = min_to_max(opts.start.x, opts['end'].x)
            opts.start.y, opts['end'].y = min_to_max(opts.start.y, opts['end'].y)
            opts.start.z, opts['end'].z = min_to_max(opts.start.z, opts['end'].z)
        else
            opts['end'] = opts.start
        end
    end
end

And here's some code that I wrote in C++ as a proof of concept, but I do think this script should be done entirely in lua:

// get list of building construction jobs that have all items attached and are
// not suspended
static void get_jobs(std::vector<df::job*> &jobs) {
    df::job_list_link *link = world->jobs.list.next;
    for (; link; link = link->next) {
        df::job *job = link->item;

        if (job->job_type != df::job_type::ConstructBuilding)
            continue;

        if (job->flags.bits.suspend)
            continue;

        bool has_unattached_items = false;
        for (auto job_item : job->job_items) {
            // we have to check for quantity != 0 instead of just the existence of
            // the job_item since buildingplan leaves 0-quantity job_items in place
            // to protect against persistence errors.
            if (job_item->quantity) {
                has_unattached_items = true;
                break;
            }
        }
        if (has_unattached_items)
            continue;

        jobs.push_back(job);
    }
}

static void do_build(const build_now_options &options) {
    std::vector<df::job*> jobs;
    get_jobs(jobs);

    // complete architecture steps, if required
    // teleport remaining items to building center and make part of building
    // set building to maximum build stage
    // remove job and clean up links
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
idea Suggestions, etc.
Projects
No open projects
0.47.05-r3
  
Done
Development

Successfully merging a pull request may close this issue.

2 participants