show queue position

develop
Myk Taylor 2023-02-15 19:10:42 -08:00
parent e5c3a2b519
commit 18ad29dde4
No known key found for this signature in database
5 changed files with 189 additions and 47 deletions

@ -175,7 +175,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) {
World::GetPersistentData(&building_configs, BLD_CONFIG_KEY);
const size_t num_building_configs = building_configs.size();
for (size_t idx = 0; idx < num_building_configs; ++idx) {
PlannedBuilding pb(building_configs[idx]);
PlannedBuilding pb(out, building_configs[idx]);
registerPlannedBuilding(out, pb);
}
@ -279,7 +279,7 @@ static string getBucket(const df::job_item & ji) {
}
// get a list of item vectors that we should search for matches
static vector<df::job_item_vector_id> getVectorIds(color_ostream &out, df::job_item *job_item) {
vector<df::job_item_vector_id> getVectorIds(color_ostream &out, df::job_item *job_item) {
std::vector<df::job_item_vector_id> ret;
// if the filter already has the vector_id set to something specific, use it
@ -310,6 +310,7 @@ static vector<df::job_item_vector_id> getVectorIds(color_ostream &out, df::job_i
ret.push_back(df::job_item_vector_id::IN_PLAY);
return ret;
}
static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) {
df::building * bld = pb.getBuildingIfValidOrRemoveIfNot(out);
if (!bld)
@ -385,7 +386,10 @@ static void printStatus(color_ostream &out) {
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));
string desc = "none";
call_buildingplan_lua(&out, "get_desc", 1, 1,
[&](lua_State *L) { Lua::Push(L, jitem); },
[&](lua_State *L) { desc = lua_tostring(L, -1); });
counts[desc] += quantity;
total += quantity;
}
@ -510,6 +514,42 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16
return count;
}
static int getQueuePosition(color_ostream &out, df::building *bld, int index) {
DEBUG(status,out).print("entering getQueuePosition\n");
if (!isPlannedBuilding(out, bld) || bld->jobs.size() != 1)
return 0;
auto &job_items = bld->jobs[0]->job_items;
if (job_items.size() <= index)
return 0;
PlannedBuilding &pb = planned_buildings.at(bld->id);
if (pb.vector_ids.size() <= index)
return 0;
auto &job_item = job_items[index];
int min_pos = -1;
for (auto &vec_id : pb.vector_ids[index]) {
if (!tasks.count(vec_id))
continue;
auto &buckets = tasks.at(vec_id);
string bucket_id = getBucket(*job_item);
if (!buckets.count(bucket_id))
continue;
int bucket_pos = -1;
for (auto &task : buckets.at(bucket_id)) {
++bucket_pos;
if (bld->id == task.first && index == task.second)
break;
}
if (bucket_pos++ >= 0)
min_pos = min_pos < 0 ? bucket_pos : std::min(min_pos, bucket_pos);
}
return min_pos < 0 ? 0 : min_pos;
}
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(printStatus),
DFHACK_LUA_FUNCTION(setSetting),
@ -519,5 +559,6 @@ DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(doCycle),
DFHACK_LUA_FUNCTION(scheduleCycle),
DFHACK_LUA_FUNCTION(countAvailableItems),
DFHACK_LUA_FUNCTION(getQueuePosition),
DFHACK_LUA_END
};

@ -28,5 +28,6 @@ bool get_config_bool(DFHack::PersistentDataItem &c, int index);
void set_config_val(DFHack::PersistentDataItem &c, int index, int value);
void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value);
std::vector<df::job_item_vector_id> getVectorIds(DFHack::color_ostream &out, df::job_item *job_item);
bool itemPassesScreen(df::item * item);
bool matchesFilters(df::item * item, df::job_item * job_item);

@ -2,25 +2,79 @@
#include "buildingplan.h"
#include "Debug.h"
#include "MiscUtils.h"
#include "modules/World.h"
#include "df/job.h"
namespace DFHack {
DBG_EXTERN(buildingplan, status);
DBG_EXTERN(buildingplan, cycle);
}
using std::string;
using std::vector;
using namespace DFHack;
static vector<vector<df::job_item_vector_id>> get_vector_ids(color_ostream &out, int bld_id) {
vector<vector<df::job_item_vector_id>> ret;
df::building *bld = df::building::find(bld_id);
if (!bld || bld->jobs.size() != 1)
return ret;
auto &job = bld->jobs[0];
for (auto &jitem : job->job_items) {
ret.emplace_back(getVectorIds(out, jitem));
}
return ret;
}
static vector<vector<df::job_item_vector_id>> deserialize(color_ostream &out, PersistentDataItem &bld_config) {
vector<vector<df::job_item_vector_id>> ret;
DEBUG(status,out).print("deserializing state for building %d: %s\n",
get_config_val(bld_config, BLD_CONFIG_ID), bld_config.val().c_str());
vector<string> joined;
split_string(&joined, bld_config.val(), "|");
for (auto &str : joined) {
vector<string> lst;
split_string(&lst, str, ",");
vector<df::job_item_vector_id> ids;
for (auto &s : lst)
ids.emplace_back(df::job_item_vector_id(string_to_int(s)));
ret.emplace_back(ids);
}
if (!ret.size())
ret = get_vector_ids(out, get_config_val(bld_config, BLD_CONFIG_ID));
return ret;
}
static string serialize(const vector<vector<df::job_item_vector_id>> &vector_ids) {
vector<string> joined;
for (auto &vec_list : vector_ids) {
joined.emplace_back(join_strings(",", vec_list));
}
return join_strings("|", joined);
}
PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *building)
: id(building->id) {
: id(building->id), vector_ids(get_vector_ids(out, id)) {
DEBUG(status,out).print("creating persistent data for building %d\n", id);
bld_config = World::AddPersistentData(BLD_CONFIG_KEY);
set_config_val(bld_config, BLD_CONFIG_ID, id);
bld_config.val() = serialize(vector_ids);
DEBUG(status,out).print("serialized state for building %d: %s\n", id, bld_config.val().c_str());
}
PlannedBuilding::PlannedBuilding(PersistentDataItem &bld_config)
: id(get_config_val(bld_config, BLD_CONFIG_ID)), bld_config(bld_config) { }
PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_config)
: id(get_config_val(bld_config, BLD_CONFIG_ID)),
vector_ids(deserialize(out, bld_config)),
bld_config(bld_config) { }
// Ensure the building still exists and is in a valid state. It can disappear
// for lots of reasons, such as running the game with the buildingplan plugin

@ -5,13 +5,17 @@
#include "modules/Persistence.h"
#include "df/building.h"
#include "df/job_item_vector_id.h"
class PlannedBuilding {
public:
const df::building::key_field_type id;
// job_item idx -> list of vectors the task is linked to
const std::vector<std::vector<df::job_item_vector_id>> vector_ids;
PlannedBuilding(DFHack::color_ostream &out, df::building *building);
PlannedBuilding(DFHack::PersistentDataItem &bld_config);
PlannedBuilding(DFHack::color_ostream &out, DFHack::PersistentDataItem &bld_config);
void remove(DFHack::color_ostream &out);

@ -181,7 +181,7 @@ function ItemLine:reset()
self.available = nil
end
local function get_desc(filter)
function get_desc(filter)
local desc = 'Unknown'
if filter.has_tool_use then
desc = to_title_case(df.tool_uses[filter.has_tool_use])
@ -190,13 +190,15 @@ local function get_desc(filter)
desc = to_title_case(df.item_type[filter.item_type])
end
if filter.flags2 and filter.flags2.building_material then
desc = "Generic material";
desc = 'Generic material';
if filter.flags2.fire_safe then
desc = "Fire-safe material";
desc = 'Fire-safe material';
end
if filter.flags2.magma_safe then
desc = "Magma-safe material";
desc = 'Magma-safe material';
end
elseif filter.flags2 and filter.flags2.screw then
desc = 'Screw'
elseif filter.vector_id then
desc = to_title_case(df.job_item_vector_id[filter.vector_id])
end
@ -249,27 +251,30 @@ PlannerOverlay.ATTRS{
default_enabled=true,
viewscreens='dwarfmode/Building/Placement',
frame={w=54, h=9},
frame_style=gui.MEDIUM_FRAME,
frame_background=gui.CLEAR_PEN,
}
function PlannerOverlay:init()
self:addviews{
widgets.Panel{
frame={},
frame_style=gui.MEDIUM_FRAME,
},
widgets.Label{
frame={},
auto_width=true,
text='No items required.',
visible=function() return #get_cur_filters() == 0 end,
},
ItemLine{view_id='item1', frame={t=0, l=0}, idx=1},
ItemLine{view_id='item2', frame={t=2, l=0}, idx=2},
ItemLine{view_id='item3', frame={t=4, l=0}, idx=3},
ItemLine{view_id='item4', frame={t=6, l=0}, idx=4},
ItemLine{view_id='item1', frame={t=1, l=1, r=1}, idx=1},
ItemLine{view_id='item2', frame={t=3, l=1, r=1}, idx=2},
ItemLine{view_id='item3', frame={t=5, l=1, r=1}, idx=3},
ItemLine{view_id='item4', frame={t=7, l=1, r=1}, idx=4},
widgets.CycleHotkeyLabel{
view_id="stairs_top_subtype",
frame={t=3, l=0},
key="CUSTOM_R",
label="Top Stair Type: ",
view_id='stairs_top_subtype',
frame={t=4, l=1},
key='CUSTOM_R',
label='Top Stair Type: ',
visible=is_stairs,
options={
{label='Auto', value='auto'},
@ -278,10 +283,10 @@ function PlannerOverlay:init()
},
},
widgets.CycleHotkeyLabel {
view_id="stairs_bottom_subtype",
frame={t=4, l=0},
key="CUSTOM_B",
label="Bottom Stair Type: ",
view_id='stairs_bottom_subtype',
frame={t=5, l=1},
key='CUSTOM_B',
label='Bottom Stair Type: ',
visible=is_stairs,
options={
{label='Auto', value='auto'},
@ -290,7 +295,7 @@ function PlannerOverlay:init()
},
},
widgets.Label{
frame={b=0, l=17},
frame={b=1, l=17},
text={
'Selected area: ',
{text=function()
@ -300,6 +305,17 @@ function PlannerOverlay:init()
},
visible=is_choosing_area,
},
widgets.CycleHotkeyLabel{
view_id='safety',
frame={b=0, l=1},
key='CUSTOM_F',
label='Extra safety: ',
options={
{label='None', value='none'},
{label='Magma', value='magma'},
{label='Fire', value='fire'},
},
},
}
end
@ -473,12 +489,50 @@ function PlannerOverlay:place_building()
scheduleCycle()
end
--------------------------------
-- InspectorOverlay
--
local function get_building_filters()
local bld = dfhack.gui.getSelectedBuilding()
return dfhack.buildings.getFiltersByType({},
bld:getType(), bld:getSubtype(), bld:getCustomType())
end
InspectorLine = defclass(InspectorLine, widgets.Panel)
InspectorLine.ATTRS{
idx=DEFAULT_NIL,
}
function InspectorLine:init()
self.frame.h = 2
self.visible = function() return #get_building_filters() >= self.idx end
self:addviews{
widgets.Label{
frame={t=0, l=0},
text={{text=function() return get_desc(get_building_filters()[self.idx]) end}},
},
widgets.Label{
frame={t=1, l=2},
text={{text=self:callback('get_status_line')}},
},
}
end
function InspectorLine:get_status_line()
local queue_pos = getQueuePosition(dfhack.gui.getSelectedBuilding(), self.idx-1)
if queue_pos <= 0 then
return 'Item attached'
end
return ('Position in line: %d'):format(queue_pos)
end
InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget)
InspectorOverlay.ATTRS{
default_pos={x=-41,y=14},
default_enabled=true,
viewscreens='dwarfmode/ViewSheets/BUILDING',
frame={w=30, h=9},
frame={w=30, h=14},
frame_style=gui.MEDIUM_FRAME,
frame_background=gui.CLEAR_PEN,
}
@ -489,29 +543,17 @@ function InspectorOverlay:init()
frame={t=0, l=0},
text='Waiting for items:',
},
widgets.Label{
frame={t=1, l=0},
text='item1',
},
widgets.Label{
frame={t=2, l=0},
text='item2',
},
widgets.Label{
frame={t=3, l=0},
text='item3',
},
widgets.Label{
frame={t=4, l=0},
text='item4',
},
InspectorLine{view_id='item1', frame={t=2, l=0}, idx=1},
InspectorLine{view_id='item2', frame={t=4, l=0}, idx=2},
InspectorLine{view_id='item3', frame={t=6, l=0}, idx=3},
InspectorLine{view_id='item4', frame={t=8, l=0}, idx=4},
widgets.HotkeyLabel{
frame={t=5, l=0},
frame={t=10, l=0},
label='adjust filters',
key='CUSTOM_CTRL_F',
},
widgets.HotkeyLabel{
frame={t=6, l=0},
frame={t=11, l=0},
label='make top priority',
key='CUSTOM_CTRL_T',
},
@ -578,7 +620,7 @@ end
-- does not need the core suspended.
function show_global_settings_dialog(settings)
GlobalSettings{
frame_title="Buildingplan Global Settings",
frame_title='Buildingplan Global Settings',
settings=settings,
}:show()
end