diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index ad21ada94..0a05c4ed5 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -74,6 +74,7 @@ struct BuildingTypeKeyHash { static PersistentDataItem config; // for use in counting available materials for the UI static unordered_map, BuildingTypeKeyHash> job_item_repo; +static unordered_map cur_heat_safety; // building id -> PlannedBuilding static unordered_map planned_buildings; // vector id -> filter bucket -> queue of (building id, job_item index) @@ -456,13 +457,20 @@ static bool isPlannedBuilding(color_ostream &out, df::building *bld) { return bld && planned_buildings.count(bld->id) > 0; } +static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { + if (cur_heat_safety.count(key)) + return cur_heat_safety.at(key); + return HEAT_SAFETY_ANY; +} + static bool addPlannedBuilding(color_ostream &out, df::building *bld) { DEBUG(status,out).print("entering addPlannedBuilding\n"); if (!bld || planned_buildings.count(bld->id) || !isPlannableBuilding(out, bld->getType(), bld->getSubtype(), bld->getCustomType())) return false; - PlannedBuilding pb(out, bld); + BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); + PlannedBuilding pb(out, bld, get_heat_safety_filter(key)); return registerPlannedBuilding(out, pb); } @@ -482,6 +490,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", type, subtype, custom, index); BuildingTypeKey key(type, subtype, custom); + HeatSafety heat = get_heat_safety_filter(key); auto &job_items = job_item_repo[key]; if (index >= (int)job_items.size()) { for (int i = job_items.size(); i <= index; ++i) { @@ -514,7 +523,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ for (auto vector_id : vector_ids) { auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); for (auto &item : df::global::world->items.other[other_id]) { - if (itemPassesScreen(item) && matchesFilters(item, jitem)) { + if (itemPassesScreen(item) && matchesFilters(item, jitem, heat)) { if (item_ids) item_ids->emplace_back(item->id); ++count; @@ -550,17 +559,56 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 return scanAvailableItems(out, type, subtype, custom, index); } -static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering hasFilter\n"); +static bool hasMaterialFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + DEBUG(status,out).print("entering hasMaterialFilter\n"); return false; } -static void setFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering setFilter\n"); +static void setMaterialFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index, string filter) { + DEBUG(status,out).print("entering setMaterialFilter\n"); + call_buildingplan_lua(&out, "signal_reset"); +} + +static int getMaterialFilter(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + int index = luaL_checkint(L, 4); + DEBUG(status,*out).print( + "entering getMaterialFilter building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + vector filter; + Lua::PushVector(L, filter); + return 1; +} + +static void setHeatSafetyFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int heat) { + DEBUG(status,out).print("entering setHeatSafetyFilter\n"); + BuildingTypeKey key(type, subtype, custom); + if (heat == HEAT_SAFETY_FIRE || heat == HEAT_SAFETY_MAGMA) + cur_heat_safety[key] = (HeatSafety)heat; + else + cur_heat_safety.erase(key); + call_buildingplan_lua(&out, "signal_reset"); } -static void clearFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering clearFilter\n"); +static int getHeatSafetyFilter(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + DEBUG(status,*out).print( + "entering getHeatSafetyFilter building_type=%d subtype=%d custom=%d\n", + type, subtype, custom); + BuildingTypeKey key(type, subtype, custom); + HeatSafety heat = get_heat_safety_filter(key); + Lua::Push(L, heat); + return 1; } static bool validate_pb(color_ostream &out, df::building *bld, int index) { @@ -659,9 +707,9 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), - DFHACK_LUA_FUNCTION(hasFilter), - DFHACK_LUA_FUNCTION(setFilter), - DFHACK_LUA_FUNCTION(clearFilter), + DFHACK_LUA_FUNCTION(hasMaterialFilter), + DFHACK_LUA_FUNCTION(setMaterialFilter), + DFHACK_LUA_FUNCTION(setHeatSafetyFilter), DFHACK_LUA_FUNCTION(getDescString), DFHACK_LUA_FUNCTION(getQueuePosition), DFHACK_LUA_FUNCTION(makeTopPriority), @@ -670,5 +718,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(getAvailableItems), + DFHACK_LUA_COMMAND(getMaterialFilter), + DFHACK_LUA_COMMAND(getHeatSafetyFilter), DFHACK_LUA_END }; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 01c72e370..787987586 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -2,6 +2,7 @@ #include "modules/Persistence.h" +#include "df/building.h" #include "df/job_item.h" #include "df/job_item_vector_id.h" @@ -21,6 +22,13 @@ enum ConfigValues { enum BuildingConfigValues { BLD_CONFIG_ID = 0, + BLD_CONFIG_HEAT = 1, +}; + +enum HeatSafety { + HEAT_SAFETY_ANY = 0, + HEAT_SAFETY_FIRE = 1, + HEAT_SAFETY_MAGMA = 2, }; int get_config_val(DFHack::PersistentDataItem &c, int index); @@ -30,6 +38,6 @@ void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); std::vector 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); +bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 069787f39..703bab9b0 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -46,7 +46,7 @@ bool itemPassesScreen(df::item * item) { && !item->isAssignedToStockpile(); } -bool matchesFilters(df::item * item, df::job_item * job_item) { +bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat) { // check the properties that are not checked by Job::isSuitableItem() if (job_item->item_type > -1 && job_item->item_type != item->getType()) return false; @@ -65,10 +65,17 @@ bool matchesFilters(df::item * item, df::job_item * job_item) { && !item->hasToolUse(job_item->has_tool_use)) return false; + df::job_item jitem = *job_item; + if (heat == HEAT_SAFETY_MAGMA) { + jitem.flags2.bits.magma_safe = true; + jitem.flags2.bits.fire_safe = false; + } else if (heat == HEAT_SAFETY_FIRE && !jitem.flags2.bits.magma_safe) + jitem.flags2.bits.fire_safe = true; + return Job::isSuitableItem( - job_item, item->getType(), item->getSubtype()) + &jitem, item->getType(), item->getSubtype()) && Job::isSuitableMaterial( - job_item, item->getMaterial(), item->getMaterialIndex(), + &jitem, item->getMaterial(), item->getMaterialIndex(), item->getType()); } @@ -173,7 +180,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, auto id = task.first; auto job = bld->jobs[0]; auto filter_idx = task.second; - if (matchesFilters(item, job->job_items[filter_idx]) + if (matchesFilters(item, job->job_items[filter_idx], planned_buildings.at(id).heat_safety) && Job::attachJobItem(job, item, df::job_item_ref::Hauled, filter_idx)) { diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index f4f3564b7..eb55a95b4 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -62,11 +62,12 @@ static string serialize(const vector> &vector_ids return join_strings("|", joined); } -PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *building) - : id(building->id), vector_ids(get_vector_ids(out, id)) { +PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *bld, HeatSafety heat) + : id(bld->id), vector_ids(get_vector_ids(out, id)), heat_safety(heat) { 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); + set_config_val(bld_config, BLD_CONFIG_HEAT, heat_safety); bld_config.val() = serialize(vector_ids); DEBUG(status,out).print("serialized state for building %d: %s\n", id, bld_config.val().c_str()); } @@ -74,6 +75,7 @@ PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *building) PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_config) : id(get_config_val(bld_config, BLD_CONFIG_ID)), vector_ids(deserialize(out, bld_config)), + heat_safety((HeatSafety)get_config_val(bld_config, BLD_CONFIG_HEAT)), bld_config(bld_config) { } // Ensure the building still exists and is in a valid state. It can disappear diff --git a/plugins/buildingplan/plannedbuilding.h b/plugins/buildingplan/plannedbuilding.h index 592f0e4b3..0a67e0edc 100644 --- a/plugins/buildingplan/plannedbuilding.h +++ b/plugins/buildingplan/plannedbuilding.h @@ -1,5 +1,7 @@ #pragma once +#include "buildingplan.h" + #include "Core.h" #include "modules/Persistence.h" @@ -14,7 +16,9 @@ public: // job_item idx -> list of vectors the task is linked to const std::vector> vector_ids; - PlannedBuilding(DFHack::color_ostream &out, df::building *building); + const HeatSafety heat_safety; + + PlannedBuilding(DFHack::color_ostream &out, df::building *bld, HeatSafety heat); PlannedBuilding(DFHack::color_ostream &out, DFHack::PersistentDataItem &bld_config); void remove(DFHack::color_ostream &out); diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 327f9f685..e4aee907b 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -251,14 +251,18 @@ function ItemSelection:get_entry_icon(item_id) return self.selected_set[item_id] and get_selected_item_pen() or nil end -ItemSelectionScreen = defclass(ItemSelectionScreen, gui.ZScreen) -ItemSelectionScreen.ATTRS { - focus_path='buildingplan/itemselection', +BuildingplanScreen = defclass(BuildingplanScreen, gui.ZScreen) +BuildingplanScreen.ATTRS { force_pause=true, pass_pause=false, pass_movement_keys=true, pass_mouse_clicks=false, defocusable=false, +} + +ItemSelectionScreen = defclass(ItemSelectionScreen, BuildingplanScreen) +ItemSelectionScreen.ATTRS { + focus_path='buildingplan/itemselection', index=DEFAULT_NIL, on_submit=DEFAULT_NIL, } @@ -414,7 +418,8 @@ function ItemLine:onInput(keys) end function ItemLine:get_x_pen() - return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx) and COLOR_GREEN or COLOR_GREY + return hasMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx) and + COLOR_GREEN or COLOR_GREY end function get_desc(filter) @@ -599,7 +604,7 @@ function PlannerOverlay:init() view_id='choose', frame={b=0, l=0}, key='CUSTOM_I', - label='Choose exact items:', + label='Choose from items:', options={{label='Yes', value=true}, {label='No', value=false}}, initial_option=false, @@ -617,10 +622,13 @@ function PlannerOverlay:init() key='CUSTOM_G', label='Building safety:', options={ - {label='Any', value='none'}, - {label='Magma', value='magma'}, - {label='Fire', value='fire'}, + {label='Any', value=0}, + {label='Magma', value=2, pen=COLOR_RED}, + {label='Fire', value=1, pen=COLOR_LIGHTRED}, }, + on_change=function(heat) + setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) + end, }, }, }, @@ -663,11 +671,11 @@ function PlannerOverlay:reset() end function PlannerOverlay:set_filter(idx) - print('set_filter', idx) + print('TODO: set_filter', idx) end function PlannerOverlay:clear_filter(idx) - print('clear_filter', idx) + setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx, "") end function PlannerOverlay:onInput(keys) @@ -679,8 +687,8 @@ function PlannerOverlay:onInput(keys) end self.selected = 1 self.subviews.choose:setOption(false) - self.subviews.safety:setOption('none') self:reset() + reset_counts_flag = true return false end if PlannerOverlay.super.onInput(self, keys) then @@ -753,6 +761,8 @@ function PlannerOverlay:onRenderFrame(dc, rect) if reset_counts_flag then self:reset() + self.subviews.safety:setOption(getHeatSafetyFilter( + uibs.building_type, uibs.building_subtype, uibs.custom_type)) end if not is_choosing_area() then return end