From 4a173aac997c05b7921ccd96ee0446250c82b318 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Mar 2023 00:07:40 -0500 Subject: [PATCH 1/3] tailor: fix #3093, #3103 I significantly restructured the assignment algorithm to eliminate the overproduction issues in #3093 #3103 is addressed by excluding units that don't have the caste-level EQUIPS flag closes #3093 closes #3103 --- docs/changelog.txt | 2 +- plugins/tailor.cpp | 151 ++++++++++++++++++++++----------------------- 2 files changed, 75 insertions(+), 78 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ced0f961e..619a538e3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,7 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: you can no longer designate constructions on tiles with magma or deep water - `buildingplan`: fixed material filter getting lost for planning buildings on save/reload - `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) -- `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, and properly tracks material requirements for already queued orders +- `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, properly tracks material requirements for already queued orders, skips units who can't wear clothes, and hopefully won't over-order items anymore ## Misc Improvements - `buildingplan`: filters and global settings are now ignored when manually choosing items for a building diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 3ba6744bb..bb84c438c 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -144,13 +144,15 @@ static struct BadFlags { } badFlags; class Tailor { -private: - std::map, int> available; // key is item type & size - std::map, int> needed; // same - std::map, int> queued; // same +private: std::map sizes; // this maps body size to races - std::map, int> orders; // key is item type, item subtype, size + + std::map, int> available; + + std::map, int> needed; + + std::map, int> orders; std::map supply; std::map reserves; @@ -169,50 +171,34 @@ public: { available.clear(); needed.clear(); - queued.clear(); sizes.clear(); - orders.clear(); supply.clear(); + orders.clear(); } void scan_clothing() { - if (!inventory_sanity_checking) + for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "nontattered clothing" { - for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" + if (i->flags.whole & badFlags.whole) { - if (i->flags.whole & badFlags.whole) - continue; - if (i->getWear() >= 1) - continue; - df::item_type t = i->getType(); - int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; - - available[std::make_pair(t, size)] += 1; + continue; } - } - else - { - auto& l = world->items.other[df::items_other_id::ANY_GENERIC37]; - - for (auto i : world->items.other[df::items_other_id::IN_PLAY]) - { - if (i->flags.whole & badFlags.whole) - continue; - if (!i->isClothing()) - continue; - if (std::find(std::begin(l), std::end(l), i) == std::end(l)) - { - DEBUG(cycle).print("tailor: clothing item %d missing from GENERIC37 list\n", i->id); - } - if (i->getWear() >= 1) - continue; - df::item_type t = i->getType(); - int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; + if (i->getWear() >= 1) + continue; + df::item_type t = i->getType(); + int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; - available[std::make_pair(t, size)] += 1; - } + available[std::make_pair(t, size)] += 1; + } + for (auto& i : available) + { + df::item_type t; + int size; + std::tie(t, size) = i.first; + DEBUG(cycle).print("tailor: %d %s of size %d found\n", + i.second, ENUM_KEY_STR(item_type, t).c_str(), size); } } @@ -273,14 +259,13 @@ public: if (!Units::isOwnCiv(u) || !Units::isOwnGroup(u) || !Units::isActive(u) || - Units::isBaby(u)) - continue; // skip units we don't control + Units::isBaby(u) || + !Units::casteFlagSet(u->race, u->caste, df::enums::caste_raw_flags::EQUIPS)) + continue; // skip units we don't control or that can't wear clothes std::set wearing; - wearing.clear(); - + std::set ordered; std::deque worn; - worn.clear(); for (auto inv : u->inventory) { @@ -295,33 +280,37 @@ public: int usize = world->raws.creatures.all[u->race]->adultsize; sizes[usize] = u->race; - for (auto ty : std::set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) - { - if (wearing.count(ty) == 0) - { - TRACE(cycle).print("tailor: one %s of size %d needed to cover %s\n", - ENUM_KEY_STR(item_type, ty).c_str(), - usize, - Translation::TranslateName(&u->name, false).c_str()); - needed[std::make_pair(ty, usize)] += 1; - } - } - for (auto w : worn) { auto ty = w->getType(); - auto oo = itemTypeMap.find(ty); - if (oo == itemTypeMap.end()) - { - continue; - } - const df::job_type o = oo->second; int isize = world->raws.creatures.all[w->getMakerRace()]->adultsize; std::string description; w->getItemDescription(&description, 0); - if (available[std::make_pair(ty, usize)] > 0) + bool allocated = false; + + if (wearing.count(ty) == 0) + { + if (available[std::make_pair(ty, usize)] > 0) + { + available[std::make_pair(ty, usize)] -= 1; + DEBUG(cycle).print("tailor: allocating a %s (size %d) to %s\n", + ENUM_KEY_STR(item_type, ty).c_str(), usize, + Translation::TranslateName(&u->name, false).c_str()); + wearing.insert(ty); + } + else if (ordered.count(ty) == 0) + { + DEBUG(cycle).print ("tailor: %s (size %d) worn by %s (size %d) needs replacement, but none available\n", + description.c_str(), isize, + Translation::TranslateName(&u->name, false).c_str(), usize); + needed[std::make_pair(ty, usize)] += 1; + ordered.insert(ty); + } + } + + if (wearing.count(ty) > 0) { if (w->flags.bits.owned) { @@ -335,23 +324,21 @@ public: ); } - if (wearing.count(ty) == 0) - { - DEBUG(cycle).print("tailor: allocating a %s (size %d) to %s\n", - ENUM_KEY_STR(item_type, ty).c_str(), usize, - Translation::TranslateName(&u->name, false).c_str()); - available[std::make_pair(ty, usize)] -= 1; - } - if (w->getWear() > 1) w->flags.bits.dump = true; } - else + + } + + for (auto ty : std::set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) + { + if (wearing.count(ty) == 0 && ordered.count(ty) == 0) { - DEBUG(cycle).print ("tailor: %s (size %d) worn by %s (size %d) needs replacement, but none available\n", - description.c_str(), isize, - Translation::TranslateName(&u->name, false).c_str(), usize); - orders[std::make_tuple(o, w->getSubtype(), usize)] += 1; + TRACE(cycle).print("tailor: one %s of size %d needed to cover %s\n", + ENUM_KEY_STR(item_type, ty).c_str(), + usize, + Translation::TranslateName(&u->name, false).c_str()); + needed[std::make_pair(ty, usize)] += 1; } } } @@ -367,6 +354,9 @@ public: int size = a.first.second; int count = a.second; + if (count <= 0) + continue; + int sub = 0; std::vector v; @@ -432,7 +422,14 @@ public: int size = world->raws.creatures.all[race]->adultsize; - orders[std::make_tuple(o->job_type, sub, size)] -= o->amount_left; + + auto tt = jobTypeMap.find(o->job_type); + if (tt == jobTypeMap.end()) + { + continue; + } + + needed[std::make_pair(tt->second, size)] -= o->amount_left; TRACE(cycle).print("tailor: existing order for %d %s of size %d detected\n", o->amount_left, ENUM_KEY_STR(job_type, o->job_type).c_str(), @@ -576,8 +573,8 @@ public: scan_clothing(); scan_materials(); scan_replacements(); - create_orders(); scan_existing_orders(); + create_orders(); return place_orders(); } From 0f5456c45b3324e9eb8135908cf425e83fe5481e Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Mar 2023 00:31:47 -0500 Subject: [PATCH 2/3] tidy up after self remove a couple of no-longer-used variables that i missed last time --- plugins/tailor.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index bb84c438c..286bd9c0d 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -288,8 +288,6 @@ public: std::string description; w->getItemDescription(&description, 0); - bool allocated = false; - if (wearing.count(ty) == 0) { if (available[std::make_pair(ty, usize)] > 0) @@ -404,7 +402,6 @@ public: if (f == jobTypeMap.end()) continue; - auto sub = o->item_subtype; int race = o->hist_figure_id; for (auto& m : all_materials) From f3862e3eecacd5af65d0799c49dcee6c3a964980 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Mar 2023 00:44:03 -0500 Subject: [PATCH 3/3] tailor: gatekeep debugging code --- docs/changelog.txt | 6 +++++- plugins/tailor.cpp | 15 +++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 619a538e3..65194dcfa 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,7 +44,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: you can no longer designate constructions on tiles with magma or deep water - `buildingplan`: fixed material filter getting lost for planning buildings on save/reload - `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) -- `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, properly tracks material requirements for already queued orders, skips units who can't wear clothes, and hopefully won't over-order items anymore +- `tailor`: properly discriminates between dyed and undyed cloth +- `tailor`: no longer defaults to using adamantine +- `tailor`: properly tracks material requirements for already queued orders +- `tailor`: skips units who can't wear clothes +- `tailor`: hopefully won't over-order items any more ## Misc Improvements - `buildingplan`: filters and global settings are now ignored when manually choosing items for a building diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 286bd9c0d..2b44d11ae 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -192,13 +192,16 @@ public: available[std::make_pair(t, size)] += 1; } - for (auto& i : available) + if (DBG_NAME(cycle).isEnabled(DebugCategory::LDEBUG)) { - df::item_type t; - int size; - std::tie(t, size) = i.first; - DEBUG(cycle).print("tailor: %d %s of size %d found\n", - i.second, ENUM_KEY_STR(item_type, t).c_str(), size); + for (auto& i : available) + { + df::item_type t; + int size; + std::tie(t, size) = i.first; + DEBUG(cycle).print("tailor: %d %s of size %d found\n", + i.second, ENUM_KEY_STR(item_type, t).c_str(), size); + } } }