diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index aa23ddd0b..de133e668 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -144,12 +144,12 @@ static const vector & get_job_items(color_ostream &out, Bu return jitems; } -static void cache_matched(int16_t type, int32_t index) { - static const df::dfhack_material_category stone_cat(df::dfhack_material_category::mask_stone); - static const df::dfhack_material_category wood_cat(df::dfhack_material_category::mask_wood); - static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal); - static const df::dfhack_material_category other_cat(df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap); +static const df::dfhack_material_category stone_cat(df::dfhack_material_category::mask_stone); +static const df::dfhack_material_category wood_cat(df::dfhack_material_category::mask_wood); +static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal); +static const df::dfhack_material_category glass_cat(df::dfhack_material_category::mask_glass); +static void cache_matched(int16_t type, int32_t index) { MaterialInfo mi; mi.decode(type, index); if (mi.matches(stone_cat)) { @@ -161,9 +161,9 @@ static void cache_matched(int16_t type, int32_t index) { } else if (mi.matches(metal_cat)) { DEBUG(status).print("cached metal material: %s\n", mi.toString().c_str()); mat_cache.emplace(mi.toString(), std::make_pair(mi, "metal")); - } else if (mi.matches(other_cat)) { - DEBUG(status).print("cached other material: %s\n", mi.toString().c_str()); - mat_cache.emplace(mi.toString(), std::make_pair(mi, "other")); + } else if (mi.matches(glass_cat)) { + DEBUG(status).print("cached glass material: %s\n", mi.toString().c_str()); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "glass")); } else TRACE(status).print("not matched: %s\n", mi.toString().c_str()); @@ -729,19 +729,30 @@ static int setMaterialMaskFilter(lua_State *L) { Lua::GetVector(L, cats, 5); for (auto &cat : cats) { if (cat == "stone") - mask |= df::dfhack_material_category::mask_stone; + mask |= stone_cat.whole; else if (cat == "wood") - mask |= df::dfhack_material_category::mask_wood; + mask |= wood_cat.whole; else if (cat == "metal") - mask |= df::dfhack_material_category::mask_metal; - else if (cat == "other") - mask |= df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap; + mask |= metal_cat.whole; + else if (cat == "glass") + mask |= glass_cat.whole; } DEBUG(status,*out).print( "setting material mask filter for building_type=%d subtype=%d custom=%d index=%d to %x\n", type, subtype, custom, index, mask); ItemFilter filter = filters[index]; filter.setMaterialMask(mask); + if (mask) { + // remove materials from the list that don't match the mask + const auto &mats = filter.getMaterials(); + set new_mats; + const df::dfhack_material_category mat_mask(mask); + for (auto & mat : mats) { + if (mat.matches(mat_mask)) + new_mats.emplace(mat); + } + filter.setMaterials(new_mats); + } get_item_filters(*out, key).setItemFilter(*out, filter, index); call_buildingplan_lua(out, "signal_reset"); return 0; @@ -762,17 +773,14 @@ static int getMaterialMaskFilter(lua_State *L) { auto &filters = get_item_filters(*out, key); if (index < 0 || filters.getItemFilters().size() <= (size_t)index) return 0; - vector cat_names; + map ret; uint32_t bits = filters.getItemFilters()[index].getMaterialMask().whole; - if (!bits || bits & df::dfhack_material_category::mask_stone) - cat_names.emplace_back("stone"); - if (!bits || bits & df::dfhack_material_category::mask_wood) - cat_names.emplace_back("wood"); - if (!bits || bits & df::dfhack_material_category::mask_metal) - cat_names.emplace_back("metal"); - if (!bits || bits & (df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap)) - cat_names.emplace_back("other"); - Lua::PushVector(L, cat_names); + ret.emplace("unset", !bits); + ret.emplace("stone", !bits || bits & stone_cat.whole); + ret.emplace("wood", !bits || bits & wood_cat.whole); + ret.emplace("metal", !bits || bits & metal_cat.whole); + ret.emplace("glass", !bits || bits & glass_cat.whole); + Lua::Push(L, ret); return 1; } @@ -793,7 +801,7 @@ static int setMaterialFilter(lua_State *L) { return 0; set mats; vector matstrs; - Lua::GetVector(L, matstrs); + Lua::GetVector(L, matstrs, 5); for (auto &mat : matstrs) { if (mat_cache.count(mat)) mats.emplace(mat_cache.at(mat).first); @@ -803,6 +811,19 @@ static int setMaterialFilter(lua_State *L) { type, subtype, custom, index, mats.size()); ItemFilter filter = filters[index]; filter.setMaterials(mats); + // ensure relevant masks are explicitly enabled + df::dfhack_material_category mask = filter.getMaterialMask(); + for (auto & mat : mats) { + if (mat.matches(stone_cat)) + mask.whole |= stone_cat.whole; + else if (mat.matches(wood_cat)) + mask.whole |= wood_cat.whole; + else if (mat.matches(metal_cat)) + mask.whole |= metal_cat.whole; + else if (mat.matches(glass_cat)) + mask.whole |= glass_cat.whole; + } + filter.setMaterialMask(mask.whole); get_item_filters(*out, key).setItemFilter(*out, filter, index); call_buildingplan_lua(out, "signal_reset"); return 0; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 5137ee700..fae27c3e5 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -622,7 +622,7 @@ QualityAndMaterialsPage.ATTRS{ } local TYPE_COL_WIDTH = 20 -local HEADER_HEIGHT = 8 +local HEADER_HEIGHT = 6 local QUALITY_HEIGHT = 9 local FOOTER_HEIGHT = 4 @@ -649,10 +649,9 @@ local function mat_sort_by_quantity(a, b) end function QualityAndMaterialsPage:init() - self.lowest_other_item_heat_safety = 2 self.dirty = true - local enable_item_quality = can_be_improved(self.index) + local enable_item_quality = can_be_improved(self.index) self:addviews{ widgets.Panel{ @@ -667,38 +666,9 @@ function QualityAndMaterialsPage:init() {gap=1, pen=COLOR_LIGHTCYAN, text=self:callback('get_summary')} }, }, - widgets.CycleHotkeyLabel{ - view_id='safety', - frame={t=2, l=0, w=35}, - key='CUSTOM_SHIFT_G', - label='Building heat safety:', - options={ - {label='Fire Magma', value=0, pen=COLOR_GREY}, - {label='Fire Magma', value=2, pen=COLOR_RED}, - {label='Fire', value=1, pen=COLOR_LIGHTRED}, - }, - on_change=self:callback('set_heat_safety'), - }, - widgets.Label{ - frame={t=2, l=30}, - text='Magma', - auto_width=true, - text_pen=COLOR_GREY, - visible=function() return self.subviews.safety:getOptionValue() == 1 end, - }, - widgets.Label{ - frame={t=3, l=3}, - text='Other items for this building may not be able to use all of their selected materials.', - visible=function() return self.subviews.safety:getOptionValue() > self.lowest_other_item_heat_safety end, - }, - widgets.EditField{ - frame={l=0, t=4, w=23}, - label_text='Search: ', - on_char=function(ch) return ch:match('%l') end, - }, widgets.CycleHotkeyLabel{ view_id='mat_sort', - frame={l=24, t=4, w=21}, + frame={l=0, t=2, w=21}, label='Sort by:', key='CUSTOM_SHIFT_R', options={ @@ -709,12 +679,17 @@ function QualityAndMaterialsPage:init() }, widgets.ToggleHotkeyLabel{ view_id='hide_zero', - frame={l=24, t=5, w=24}, + frame={l=0, t=3, w=24}, label='Hide unavailable:', key='CUSTOM_SHIFT_H', initial_option=false, on_change=function() self.dirty = true end, }, + widgets.EditField{ + frame={l=26, t=2}, + label_text='Search: ', + on_char=function(ch) return ch:match('[%l -]') end, + }, widgets.Label{ frame={l=1, b=0}, text='Type', @@ -727,8 +702,7 @@ function QualityAndMaterialsPage:init() }, }, }, - widgets.Panel{ - view_id='materials_lists', + widgets.Panel{view_id='materials_lists', frame={l=0, t=HEADER_HEIGHT, r=0, b=FOOTER_HEIGHT+QUALITY_HEIGHT}, frame_style=gui.INTERIOR_FRAME, subviews={ @@ -739,11 +713,13 @@ function QualityAndMaterialsPage:init() icon_width=2, cursor_pen=COLOR_CYAN, on_double_click=self:callback('toggle_category'), + on_submit=self:callback('toggle_category'), }, widgets.List{ view_id='materials_mats', frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, icon_width=2, + on_submit=self:callback('toggle_material'), }, }, }, @@ -866,25 +842,35 @@ end local MAT_ENABLED_PEN = to_pen{ch=string.char(251), fg=COLOR_LIGHTGREEN} local MAT_DISABLED_PEN = to_pen{ch='x', fg=COLOR_RED} -local function make_cat_choice(label, cat, key, enabled_cats) - local enabled = enabled_cats[cat] +local function make_cat_choice(label, cat, key, cats) + local enabled = cats[cat] + local icon = nil + if not cats.unset then + icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + end return { text=label, key=key, enabled=enabled, cat=cat, - icon=enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + icon=icon, } end -local function make_mat_choice(name, props) +local function make_mat_choice(name, props, cats) local quantity = tonumber(props.count) local text = ('%5d - %s'):format(quantity, name) - local enabled = props.enabled == 'true' + local enabled = props.enabled == 'true' and cats[props.category] + local icon = nil + if not cats.unset then + icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + end return { text=text, - icon=enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN, + enabled=enabled, + icon=icon, name=name, + cat=props.category, quantity=quantity, } end @@ -894,7 +880,6 @@ function QualityAndMaterialsPage:refresh() local subviews = self.subviews local heat = getHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type) - subviews.safety:setOption(heat) if heat >= 2 then summary = summary .. 'Magma safe ' elseif heat == 1 then summary = summary .. 'Fire safe ' end @@ -904,12 +889,12 @@ function QualityAndMaterialsPage:refresh() subviews.min_quality:setOption(quality.min_quality) subviews.max_quality:setOption(quality.max_quality) - local categories = utils.invert(getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1)) + local cats = getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) local category_choices={ - make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', categories), - make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', categories), - make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', categories), - make_cat_choice('Other', 'other', 'CUSTOM_SHIFT_T', categories), + make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', cats), + make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats), + make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), + make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), } self.subviews.materials_categories:setChoices(category_choices) @@ -918,7 +903,7 @@ function QualityAndMaterialsPage:refresh() local hide_zero = self.subviews.hide_zero:getOptionValue() for name,props in pairs(mat_filter) do if not hide_zero or tonumber(props.count) > 0 then - table.insert(mat_choices, make_mat_choice(name, props)) + table.insert(mat_choices, make_mat_choice(name, props, cats)) end end table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) @@ -933,20 +918,37 @@ function QualityAndMaterialsPage:get_summary() return self.summary end -function QualityAndMaterialsPage:toggle_category(idx, choice) - choice.enabled = not choice.enabled +function QualityAndMaterialsPage:toggle_category(_, choice) local cats = {} - for _,c in ipairs(self.subviews.materials_categories:getChoices()) do - if c.enabled then - table.insert(cats, c.cat) + if not choice.icon then + -- toggling from unset to something is set + table.insert(cats, choice.cat) + else + choice.enabled = not choice.enabled + for _,c in ipairs(self.subviews.materials_categories:getChoices()) do + if c.enabled then + table.insert(cats, c.cat) + end end end setMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, cats) self.dirty = true end -function QualityAndMaterialsPage:set_heat_safety(heat) - setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) +function QualityAndMaterialsPage:toggle_material(_, choice) + local mats = {} + if not choice.icon then + -- toggling from unset to something is set + table.insert(mats, choice.name) + else + choice.enabled = not choice.enabled + for _,c in ipairs(self.subviews.materials_mats:getChoices()) do + if c.enabled then + table.insert(mats, c.name) + end + end + end + setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) self.dirty = true end