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
develop
Kelly Kinkade 2023-03-27 00:07:40 -05:00
parent 428f7572b1
commit 4a173aac99
2 changed files with 75 additions and 78 deletions

@ -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`: 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`: 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) - `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 ## Misc Improvements
- `buildingplan`: filters and global settings are now ignored when manually choosing items for a building - `buildingplan`: filters and global settings are now ignored when manually choosing items for a building

@ -144,13 +144,15 @@ static struct BadFlags {
} badFlags; } badFlags;
class Tailor { class Tailor {
private:
std::map<std::pair<df::item_type, int>, int> available; // key is item type & size
std::map<std::pair<df::item_type, int>, int> needed; // same
std::map<std::pair<df::item_type, int>, int> queued; // same
private:
std::map<int, int> sizes; // this maps body size to races std::map<int, int> sizes; // this maps body size to races
std::map<std::tuple<df::job_type, int, int>, int> orders; // key is item type, item subtype, size
std::map<std::pair<df::item_type, int>, int> available;
std::map<std::pair<df::item_type, int>, int> needed;
std::map<std::tuple<df::job_type, int, int>, int> orders;
std::map<MatType, int> supply; std::map<MatType, int> supply;
std::map<MatType, int> reserves; std::map<MatType, int> reserves;
@ -169,50 +171,34 @@ public:
{ {
available.clear(); available.clear();
needed.clear(); needed.clear();
queued.clear();
sizes.clear(); sizes.clear();
orders.clear();
supply.clear(); supply.clear();
orders.clear();
} }
void scan_clothing() 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;
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;
} }
} if (i->getWear() >= 1)
else continue;
{ df::item_type t = i->getType();
auto& l = world->items.other[df::items_other_id::ANY_GENERIC37]; int size = world->raws.creatures.all[i->getMakerRace()]->adultsize;
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;
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) || if (!Units::isOwnCiv(u) ||
!Units::isOwnGroup(u) || !Units::isOwnGroup(u) ||
!Units::isActive(u) || !Units::isActive(u) ||
Units::isBaby(u)) Units::isBaby(u) ||
continue; // skip units we don't control !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 <df::item_type> wearing; std::set <df::item_type> wearing;
wearing.clear(); std::set <df::item_type> ordered;
std::deque<df::item*> worn; std::deque<df::item*> worn;
worn.clear();
for (auto inv : u->inventory) for (auto inv : u->inventory)
{ {
@ -295,33 +280,37 @@ public:
int usize = world->raws.creatures.all[u->race]->adultsize; int usize = world->raws.creatures.all[u->race]->adultsize;
sizes[usize] = u->race; sizes[usize] = u->race;
for (auto ty : std::set<df::item_type>{ 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) for (auto w : worn)
{ {
auto ty = w->getType(); 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; int isize = world->raws.creatures.all[w->getMakerRace()]->adultsize;
std::string description; std::string description;
w->getItemDescription(&description, 0); 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) 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) if (w->getWear() > 1)
w->flags.bits.dump = true; w->flags.bits.dump = true;
} }
else
}
for (auto ty : std::set<df::item_type>{ 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", TRACE(cycle).print("tailor: one %s of size %d needed to cover %s\n",
description.c_str(), isize, ENUM_KEY_STR(item_type, ty).c_str(),
Translation::TranslateName(&u->name, false).c_str(), usize); usize,
orders[std::make_tuple(o, w->getSubtype(), usize)] += 1; 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 size = a.first.second;
int count = a.second; int count = a.second;
if (count <= 0)
continue;
int sub = 0; int sub = 0;
std::vector<int16_t> v; std::vector<int16_t> v;
@ -432,7 +422,14 @@ public:
int size = world->raws.creatures.all[race]->adultsize; 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", TRACE(cycle).print("tailor: existing order for %d %s of size %d detected\n",
o->amount_left, o->amount_left,
ENUM_KEY_STR(job_type, o->job_type).c_str(), ENUM_KEY_STR(job_type, o->job_type).c_str(),
@ -576,8 +573,8 @@ public:
scan_clothing(); scan_clothing();
scan_materials(); scan_materials();
scan_replacements(); scan_replacements();
create_orders();
scan_existing_orders(); scan_existing_orders();
create_orders();
return place_orders(); return place_orders();
} }