print out more status info for buildingplan

develop
Myk Taylor 2023-02-08 18:47:10 -08:00
parent 4cc01f98e2
commit b443f81ecd
No known key found for this signature in database
3 changed files with 65 additions and 49 deletions

@ -11,11 +11,14 @@ available, and they will be created in a suspended state. Buildingplan will
periodically scan for appropriate items, and the jobs will be unsuspended when
the items are available.
This is very useful when combined with manager work orders or `workflow` -- you
can set a constraint to always have one or two doors/beds/tables/chairs/etc.
available, and place as many as you like. Materials are used to build the
planned buildings as they are produced, with minimal space dedicated to
stockpiles.
This is very powerful when used with tools like `quickfort`, which allow you to
set a building plan according to a blueprint, and the buildings will simply be
built when you can build them.
You can use manager work orders or `workflow` to ensure you always have one or
two doors/beds/tables/chairs/etc. available, and place as many as you like.
Materials are used to build the planned buildings as they are produced, with
minimal space dedicated to stockpiles.
Usage
-----
@ -23,37 +26,27 @@ Usage
::
enable buildingplan
buildingplan set
buildingplan [status]
buildingplan set <setting> true|false
Running ``buildingplan set`` without parameters displays the current settings.
.. _buildingplan-settings:
Global settings
---------------
The buildingplan plugin has global settings that can be set from the UI
(:kbd:`G` from any building placement screen, for example:
:kbd:`b`:kbd:`a`:kbd:`G`). These settings can also be set via the
``buildingplan set`` command. The available settings are:
The buildingplan plugin has several global settings that affect what materials
can be chosen when attaching items to planned buildings:
``all_enabled`` (default: false)
Enable planning mode for all building types.
``blocks``, ``boulders``, ``logs``, ``bars`` (defaults: true, true, true, false)
Allow blocks, boulders, logs, or bars to be matched for generic "building
material" items.
``quickfort_mode`` (default: false)
Enable compatibility mode for the legacy Python Quickfort (this setting is
not required for DFHack `quickfort`)
The settings for ``blocks``, ``boulders``, ``logs``, and ``bars`` are saved with
your fort, so you only have to set them once and they will be persisted in your
save.
These settings are saved with your fort, so you only have to set them once and
they will be persisted in your save.
If you normally embark with some blocks on hand for early workshops, you might
want to add this line to your ``dfhack-config/init/onMapLoad.init`` file to
always configure buildingplan to just use blocks for buildings and
always configure `buildingplan` to just use blocks for buildings and
constructions::
on-new-fortress buildingplan set boulders false; buildingplan set logs false
@ -76,17 +69,3 @@ keep the filter values that were set when the building was placed.
For example, you can be sure that all your constructed walls are the same color
by setting a filter to accept only certain types of stone.
Quickfort mode
--------------
If you use the external Python Quickfort to apply building blueprints instead of
the native DFHack `quickfort` script, you must enable Quickfort mode. This
temporarily enables buildingplan for all building types and adds an extra blank
screen after every building placement. This "dummy" screen is needed for Python
Quickfort to interact successfully with Dwarf Fortress.
Note that Quickfort mode is only for compatibility with the legacy Python
Quickfort. The DFHack `quickfort` script does not need this Quickfort mode to be
enabled. The `quickfort` script will successfully integrate with buildingplan as
long as the buildingplan plugin itself is enabled.

@ -15,14 +15,14 @@
#include "df/job_item.h"
#include "df/world.h"
#include <queue>
#include <deque>
#include <string>
#include <vector>
#include <unordered_map>
using std::map;
using std::pair;
using std::queue;
using std::deque;
using std::string;
using std::unordered_map;
using std::vector;
@ -34,11 +34,8 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled);
REQUIRE_GLOBAL(world);
// logging levels can be dynamically controlled with the `debugfilter` command.
namespace DFHack {
// for configuration-related logging
DBG_DECLARE(buildingplan, status, DebugCategory::LINFO);
// for logging during the periodic scan
DBG_DECLARE(buildingplan, cycle, DebugCategory::LINFO);
}
@ -108,7 +105,7 @@ static PersistentDataItem config;
// building id -> PlannedBuilding
unordered_map<int32_t, PlannedBuilding> planned_buildings;
// vector id -> filter bucket -> queue of (building id, job_item index)
map<df::job_item_vector_id, map<string, queue<pair<int32_t, int>>>> tasks;
map<df::job_item_vector_id, map<string, deque<pair<int32_t, int>>>> tasks;
// note that this just removes the PlannedBuilding. the tasks will get dropped
// as we discover them in the tasks queues and they fail to be found in planned_buildings.
@ -359,7 +356,7 @@ static void finalizeBuilding(color_ostream &out, df::building * bld) {
Job::checkBuildingsNow();
}
static df::building * popInvalidTasks(color_ostream &out, queue<pair<int32_t, int>> & task_queue) {
static df::building * popInvalidTasks(color_ostream &out, deque<pair<int32_t, int>> & task_queue) {
while (!task_queue.empty()) {
auto & task = task_queue.front();
auto id = task.first;
@ -369,13 +366,13 @@ static df::building * popInvalidTasks(color_ostream &out, queue<pair<int32_t, in
return bld;
}
DEBUG(cycle,out).print("discarding invalid task: bld=%d, job_item_idx=%d\n", id, task.second);
task_queue.pop();
task_queue.pop_front();
}
return NULL;
}
static void doVector(color_ostream &out, df::job_item_vector_id vector_id,
map<string, queue<pair<int32_t, int>>> & buckets) {
map<string, deque<pair<int32_t, int>>> & buckets) {
auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id);
auto item_vector = df::global::world->items.other[other_id];
DEBUG(cycle,out).print("matching %zu item(s) in vector %s against %zu filter bucket(s)\n",
@ -423,7 +420,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id,
// items so if buildingplan is turned off, the building will
// be completed with the correct number of items.
--job->job_items[filter_idx]->quantity;
task_queue.pop();
task_queue.pop_front();
if (isJobReady(out, job)) {
finalizeBuilding(out, bld);
planned_buildings.at(id).remove(out);
@ -586,7 +583,7 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) {
// as invalid
for (auto vector_id : vector_ids) {
for (int item_num = 0; item_num < job_item->quantity; ++item_num) {
tasks[vector_id][bucket].push(std::make_pair(id, job_item_idx));
tasks[vector_id][bucket].push_back(std::make_pair(id, job_item_idx));
DEBUG(status,out).print("added task: %s/%s/%d,%d; "
"%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket",
ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(),
@ -609,13 +606,46 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) {
static void printStatus(color_ostream &out) {
DEBUG(status,out).print("entering buildingplan_printStatus\n");
out.print("buildingplan is %s\n\n", is_enabled ? "enabled" : "disabled");
out.print(" finding materials for %zd buildings\n", planned_buildings.size());
out.print("Current settings:\n");
out.print(" use blocks: %s\n", get_config_bool(config, CONFIG_BLOCKS) ? "yes" : "no");
out.print(" use boulders: %s\n", get_config_bool(config, CONFIG_BOULDERS) ? "yes" : "no");
out.print(" use logs: %s\n", get_config_bool(config, CONFIG_LOGS) ? "yes" : "no");
out.print(" use bars: %s\n", get_config_bool(config, CONFIG_BARS) ? "yes" : "no");
out.print("\n");
map<string, int32_t> counts;
int32_t total = 0;
for (auto &buckets : tasks) {
for (auto &bucket_queue : buckets.second) {
deque<pair<int32_t, int>> &tqueue = bucket_queue.second;
for (auto it = tqueue.begin(); it != tqueue.end();) {
auto & task = *it;
auto id = task.first;
df::building *bld = NULL;
if (!planned_buildings.count(id) ||
!(bld = planned_buildings.at(id).getBuildingIfValidOrRemoveIfNot(out))) {
DEBUG(status,out).print("discarding invalid task: bld=%d, job_item_idx=%d\n",
id, task.second);
it = tqueue.erase(it);
continue;
}
auto *jitem = bld->jobs[0]->job_items[task.second];
int32_t quantity = jitem->quantity;
if (quantity) {
string desc = toLower(ENUM_KEY_STR(item_type, jitem->item_type));
counts[desc] += quantity;
total += quantity;
}
++it;
}
}
}
out.print("Waiting for %d item(s) to be produced or %zd building(s):\n",
total, planned_buildings.size());
for (auto &count : counts)
out.print(" %3d %s\n", count.second, count.first.c_str());
out.print("\n");
}
static bool setSetting(color_ostream &out, string name, bool value) {

@ -4,8 +4,6 @@ local _ENV = mkmodule('plugins.buildingplan')
Native functions:
* void setSetting(string name, boolean value)
* bool isPlanModeEnabled(df::building_type type, int16_t subtype, int32_t custom)
* bool isPlannableBuilding(df::building_type type, int16_t subtype, int32_t custom)
* bool isPlannedBuilding(df::building *bld)
* void addPlannedBuilding(df::building *bld)
@ -36,6 +34,15 @@ function parse_commandline(...)
return false
end
local command = table.remove(positionals, 1)
if not command or command == 'status' then
printStatus()
elseif command == 'set' then
setSetting(positionals[1], positionals[2] == 'true')
else
return false
end
return true
end