From 4a173aac997c05b7921ccd96ee0446250c82b318 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Mar 2023 00:07:40 -0500 Subject: [PATCH] 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(); }