diff --git a/docs/changelog.txt b/docs/changelog.txt index ced0f961e..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, and properly tracks material requirements for already queued orders +- `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 3ba6744bb..2b44d11ae 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,37 @@ 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; } + 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; } - 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 (DBG_NAME(cycle).isEnabled(DebugCategory::LDEBUG)) + { + for (auto& i : available) { - 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; - - available[std::make_pair(t, size)] += 1; + 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 +262,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 +283,35 @@ 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) + 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 +325,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 +355,9 @@ public: int size = a.first.second; int count = a.second; + if (count <= 0) + continue; + int sub = 0; std::vector v; @@ -414,7 +405,6 @@ public: if (f == jobTypeMap.end()) continue; - auto sub = o->item_subtype; int race = o->hist_figure_id; for (auto& m : all_materials) @@ -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(); }