Merge pull request #3122 from myk002/myk_automaterial

[buildingplan] implement automaterial selection
develop
Myk 2023-03-29 00:01:47 -07:00 committed by GitHub
commit 8b9a2321ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 100 additions and 33 deletions

@ -53,6 +53,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Misc Improvements
- `buildingplan`: filters and global settings are now ignored when manually choosing items for a building
- `buildingplan`: if `suspendmanager` is running, then planned buildings will be left suspended when their items are all attached. `suspendmanager` will unsuspsend them for construction when it is safe to do so.
- `buildingplan`: add option for autoselecting the last manually chosen item (like `automaterial` used to do)
- `confirm`: adds confirmation for removing burrows via the repaint menu
- `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from
- `stockpiles`: support partial application of a saved config based on dynamic filtering

@ -938,8 +938,10 @@ static int getMaterialFilter(lua_State *L) {
return 1;
}
static void setChooseItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, bool choose) {
DEBUG(status,out).print("entering setChooseItems\n");
static void setChooseItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int choose) {
DEBUG(status,out).print(
"entering setChooseItems building_type=%d subtype=%d custom=%d choose=%d\n",
type, subtype, custom, choose);
BuildingTypeKey key(type, subtype, custom);
auto &filters = get_item_filters(out, key);
filters.setChooseItems(choose);

@ -42,6 +42,12 @@ enum HeatSafety {
HEAT_SAFETY_MAGMA = 2,
};
enum ItemSelectionChoice {
ITEM_SELECTION_CHOICE_FILTER = 0,
ITEM_SELECTION_CHOICE_MANUAL = 1,
ITEM_SELECTION_CHOICE_AUTOMATERIAL = 2,
};
int get_config_val(DFHack::PersistentDataItem &c, int index);
bool get_config_bool(DFHack::PersistentDataItem &c, int index);
void set_config_val(DFHack::PersistentDataItem &c, int index, int value);

@ -39,14 +39,14 @@ static string serialize(const std::vector<ItemFilter> &item_filters, const std::
}
DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, const std::vector<const df::job_item *> &jitems)
: key(key), choose_items(false) {
: key(key), choose_items(ItemSelectionChoice::ITEM_SELECTION_CHOICE_FILTER) {
DEBUG(status,out).print("creating persistent data for filter key %d,%d,%d\n",
std::get<0>(key), std::get<1>(key), std::get<2>(key));
filter_config = World::AddPersistentData(FILTER_CONFIG_KEY);
set_config_val(filter_config, FILTER_CONFIG_TYPE, std::get<0>(key));
set_config_val(filter_config, FILTER_CONFIG_SUBTYPE, std::get<1>(key));
set_config_val(filter_config, FILTER_CONFIG_CUSTOM, std::get<2>(key));
set_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose_items);
set_config_val(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose_items);
item_filters.resize(jitems.size());
for (size_t idx = 0; idx < jitems.size(); ++idx) {
item_filters[idx].setMaxQuality(get_max_quality(jitems[idx]), true);
@ -56,7 +56,7 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key,
DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &filter_config, const std::vector<const df::job_item *> &jitems)
: key(getKey(filter_config)), filter_config(filter_config) {
choose_items = get_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS);
choose_items = get_config_val(filter_config, FILTER_CONFIG_CHOOSE_ITEMS);
auto &serialized = filter_config.val();
DEBUG(status,out).print("deserializing default item filters for key %d,%d,%d: %s\n",
std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str());
@ -81,9 +81,9 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &f
}
}
void DefaultItemFilters::setChooseItems(bool choose) {
void DefaultItemFilters::setChooseItems(int choose) {
choose_items = choose;
set_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose);
set_config_val(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose);
}
void DefaultItemFilters::setSpecial(const std::string &special, bool val) {

@ -14,17 +14,17 @@ public:
DefaultItemFilters(DFHack::color_ostream &out, BuildingTypeKey key, const std::vector<const df::job_item *> &jitems);
DefaultItemFilters(DFHack::color_ostream &out, DFHack::PersistentDataItem &filter_config, const std::vector<const df::job_item *> &jitems);
void setChooseItems(bool choose);
void setChooseItems(int choose);
void setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index);
void setSpecial(const std::string &special, bool val);
bool getChooseItems() const { return choose_items; }
int getChooseItems() const { return choose_items; }
const std::vector<ItemFilter> & getItemFilters() const { return item_filters; }
const std::set<std::string> & getSpecials() const { return specials; }
private:
DFHack::PersistentDataItem filter_config;
bool choose_items;
int choose_items;
std::vector<ItemFilter> item_filters;
std::set<std::string> specials;
};

@ -15,6 +15,12 @@ local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true}
-- most recent entries are at the *end* of the list
local recently_used = {}
function get_automaterial_selection(building_type)
local tracker = recently_used[building_type]
if not tracker or not tracker.list then return end
return tracker.list[#tracker.list]
end
local function sort_by_type(a, b)
local ad, bd = a.data, b.data
return ad.item_type < bd.item_type or
@ -54,6 +60,7 @@ ItemSelection.ATTRS{
index=DEFAULT_NIL,
desc=DEFAULT_NIL,
quantity=DEFAULT_NIL,
autoselect=DEFAULT_NIL,
on_submit=DEFAULT_NIL,
on_cancel=DEFAULT_NIL,
}
@ -63,34 +70,55 @@ function ItemSelection:init()
self.selected_set = {}
local plural = self.quantity == 1 and '' or 's'
local choices = self:get_choices(sort_by_recency)
if self.autoselect then
self:do_autoselect(choices)
if self.num_selected >= self.quantity then
self:submit(choices)
return
end
end
self:addviews{
widgets.Label{
frame={t=0, l=0, r=10},
frame={t=0, l=0, r=16},
text={
self.desc,
plural,
NEWLINE,
self.desc, plural, NEWLINE,
('Select up to %d item%s ('):format(self.quantity, plural),
{text=function() return self.num_selected end},
' selected)',
},
},
widgets.Label{
frame={r=0, w=11, t=0, h=3},
frame={r=0, w=15, t=0, h=3},
text_pen=BUILD_TEXT_PEN,
text_hpen=BUILD_TEXT_HPEN,
text={
' ', NEWLINE,
' Confirm ', NEWLINE,
' ',
' Use filter ', NEWLINE,
' for remaining ', NEWLINE,
' items ',
},
on_click=self:callback('submit'),
visible=function() return self.num_selected < self.quantity end,
},
widgets.Label{
frame={r=0, w=15, t=0, h=3},
text_pen=BUILD_TEXT_PEN,
text_hpen=BUILD_TEXT_HPEN,
text={
' ', NEWLINE,
' Continue ', NEWLINE,
' ',
},
on_click=self:callback('submit'),
visible=function() return self.num_selected >= self.quantity end,
},
widgets.FilteredList{
view_id='flist',
frame={t=3, l=0, r=0, b=4},
case_sensitive=false,
choices=self:get_choices(sort_by_recency),
choices=choices,
icon_width=2,
on_submit=self:callback('toggle_group'),
edit_on_char=function(ch) return ch:match('[%l -]') end,
@ -116,7 +144,7 @@ function ItemSelection:init()
widgets.HotkeyLabel{
frame={l=22, b=1},
key='CUSTOM_SHIFT_C',
label='Confirm',
label='Continue',
auto_width=true,
on_activate=self:callback('submit'),
},
@ -215,6 +243,13 @@ function ItemSelection:get_choices(sort_fn)
return choices
end
function ItemSelection:do_autoselect(choices)
if #choices == 0 then return end
local desired = get_automaterial_selection(uibs.building_type)
if choices[1].search_key ~= desired then return end
self:toggle_group(1, choices[1])
end
function ItemSelection:increment_group(idx, choice)
local data = choice.data
if self.quantity <= self.num_selected then return false end
@ -282,13 +317,13 @@ local function track_recently_used(choices)
end
end
function ItemSelection:submit()
function ItemSelection:submit(choices)
local selected_items = {}
for item_id in pairs(self.selected_set) do
table.insert(selected_items, item_id)
end
if #selected_items > 0 then
track_recently_used(self.subviews.flist:getChoices())
track_recently_used(choices or self.subviews.flist:getChoices())
end
self.on_submit(selected_items)
end
@ -328,6 +363,7 @@ ItemSelectionScreen.ATTRS {
index=DEFAULT_NIL,
desc=DEFAULT_NIL,
quantity=DEFAULT_NIL,
autoselect=DEFAULT_NIL,
on_submit=DEFAULT_NIL,
on_cancel=DEFAULT_NIL,
}
@ -338,6 +374,7 @@ function ItemSelectionScreen:init()
index=self.index,
desc=self.desc,
quantity=self.quantity,
autoselect=self.autoselect,
on_submit=self.on_submit,
on_cancel=self.on_cancel,
}

@ -490,12 +490,21 @@ function PlannerOverlay:init()
},
widgets.CycleHotkeyLabel{
view_id='choose',
frame={b=0, l=0, w=25},
frame={b=0, l=0},
key='CUSTOM_I',
label='Choose from items:',
options={{label='Yes', value=true},
{label='No', value=false}},
initial_option=false,
label='Item selection:',
options={
{label='Use filters', value=0},
{
label=function()
local automaterial = itemselection.get_automaterial_selection(uibs.building_type)
return ('Last choice (%s)'):format(automaterial or 'Will ask')
end,
value=2,
},
{label='Manual choice', value=1},
},
initial_option=0,
on_change=function(choose)
buildingplan.setChooseItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, choose)
end,
@ -670,10 +679,11 @@ function PlannerOverlay:onInput(keys)
if is_choosing_area() or cur_building_has_no_area() then
local filters = get_cur_filters()
local num_filters = #filters
local choose = self.subviews.choose
if choose:getOptionValue() then
local choose = self.subviews.choose:getOptionValue()
if choose > 0 then
local bounds = get_selected_bounds()
self:save_placement()
local autoselect = choose == 2
local is_hollow = self.subviews.hollow:getOptionValue()
local chosen_items, active_screens = {}, {}
local pending = num_filters
@ -681,14 +691,19 @@ function PlannerOverlay:onInput(keys)
for idx = num_filters,1,-1 do
chosen_items[idx] = {}
local filter = filters[idx]
active_screens[idx] = itemselection.ItemSelectionScreen{
local selection_screen = itemselection.ItemSelectionScreen{
index=idx,
desc=require('plugins.buildingplan').get_desc(filter),
quantity=get_quantity(filter, is_hollow, bounds),
autoselect=autoselect,
on_submit=function(items)
chosen_items[idx] = items
active_screens[idx]:dismiss()
active_screens[idx] = nil
if active_screens[idx] then
active_screens[idx]:dismiss()
active_screens[idx] = nil
else
active_screens[idx] = true
end
pending = pending - 1
if pending == 0 then
df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT
@ -702,7 +717,13 @@ function PlannerOverlay:onInput(keys)
df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT
self:restore_placement()
end,
}:show()
}
if active_screens[idx] then
-- we've already returned via autoselect
active_screens[idx] = nil
else
active_screens[idx] = selection_screen:show()
end
end
else
self:place_building(get_placement_data())