diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 4ab6dc4f7..a85ab5bed 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -41,44 +41,92 @@ DFHACK_PLUGIN_IS_ENABLED(enabled); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(ui); -const char *tagline = "Allow the bookkeeper to queue jobs to keep dwarfs in adequate clothing."; -const char *usage = ( +const char* tagline = "Allow the bookkeeper to queue jobs to keep dwarfs in adequate clothing."; +const char* usage = ( " tailor enable\n" " Enable the plugin.\n" " tailor disable\n" " Disable the plugin.\n" " tailor status\n" " Display plugin status\n" + " tailor materials ...\n" + " for example: tailor materials silk cloth yarn leather\n" + " Set allowed material list to the specified list.\n" + " The example sets the list to silk, cloth, yarn, leather, in that order, which is the default.\n" "\n" "Whenever the bookkeeper updates stockpile records, this plugin will scan every unit in the fort,\n" "count up the number that are worn, and then order enough more made to replace all worn items.\n" "If there are enough replacement items in inventory to replace all worn items, the units wearing them\n" "will have the worn items confiscated (in the same manner as the _cleanowned_ plugin) so that they'll\n" "reeequip with replacement items.\n" -); + ); + +class Tailor { + // ARMOR, SHOES, HELM, GLOVES, PANTS + + // ah, if only STL had a bimap + +private: + + const map jobTypeMap = { + { df::job_type::MakeArmor, df::item_type::ARMOR }, + { df::job_type::MakePants, df::item_type::PANTS }, + { df::job_type::MakeHelm, df::item_type::HELM }, + { df::job_type::MakeGloves, df::item_type::GLOVES }, + { df::job_type::MakeShoes, df::item_type::SHOES } + }; + + const map itemTypeMap = { + { df::item_type::ARMOR, df::job_type::MakeArmor }, + { df::item_type::PANTS, df::job_type::MakePants }, + { df::item_type::HELM, df::job_type::MakeHelm }, + { df::item_type::GLOVES, df::job_type::MakeGloves }, + { df::item_type::SHOES, df::job_type::MakeShoes } + }; + +#define F(x) df::item_flags::mask_##x + const df::item_flags bad_flags = { + ( + F(dump) | F(forbid) | F(garbage_collect) | + F(hostile) | F(on_fire) | F(rotten) | F(trader) | + F(in_building) | F(construction) | F(owned) + ) + #undef F + }; + + class MatType { + + public: + std::string name; + df::job_material_category job_material; + df::armor_general_flags armor_flag; + + bool operator==(const MatType& m) const + { + return name == m.name; + } -// ARMOR, SHOES, HELM, GLOVES, PANTS + // operator< is required to use this as a std::map key + bool operator<(const MatType& m) const + { + return name < m.name; + } -// ah, if only STL had a bimap + MatType(std::string& n, df::job_material_category jm, df::armor_general_flags af) + : name(n), job_material(jm), armor_flag(af) {}; + MatType(const char* n, df::job_material_category jm, df::armor_general_flags af) + : name(std::string(n)), job_material(jm), armor_flag(af) {}; -static map jobTypeMap = { - { df::job_type::MakeArmor, df::item_type::ARMOR }, - { df::job_type::MakePants, df::item_type::PANTS }, - { df::job_type::MakeHelm, df::item_type::HELM }, - { df::job_type::MakeGloves, df::item_type::GLOVES }, - { df::job_type::MakeShoes, df::item_type::SHOES } -}; + }; -static map itemTypeMap = { - { df::item_type::ARMOR, df::job_type::MakeArmor }, - { df::item_type::PANTS, df::job_type::MakePants }, - { df::item_type::HELM, df::job_type::MakeHelm}, - { df::item_type::GLOVES, df::job_type::MakeGloves}, - { df::item_type::SHOES, df::job_type::MakeShoes} -}; + const MatType + M_SILK = MatType("silk", df::job_material_category::mask_silk, df::armor_general_flags::SOFT), + M_CLOTH = MatType("cloth", df::job_material_category::mask_cloth, df::armor_general_flags::SOFT), + M_YARN = MatType("yarn", df::job_material_category::mask_yarn, df::armor_general_flags::SOFT), + M_LEATHER = MatType("leather", df::job_material_category::mask_leather, df::armor_general_flags::LEATHER); + + std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; -void do_scan(color_ostream& out) -{ map, int> available; // key is item type & size map, int> needed; // same map, int> queued; // same @@ -87,335 +135,421 @@ void do_scan(color_ostream& out) map, int> orders; // key is item type, item subtype, size - df::item_flags bad_flags; - bad_flags.whole = 0; - -#define F(x) bad_flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(owned); -#undef F + std::map supply; - available.empty(); - needed.empty(); - queued.empty(); - orders.empty(); + color_ostream* out; - int silk = 0, yarn = 0, cloth = 0, leather = 0; + std::list material_order = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; + std::map reserves; - // scan for useable clothing + int default_reserve = 10; - for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" + void reset() { - if (i->flags.whole & bad_flags.whole) - continue; - if (i->flags.bits.owned) - continue; - if (i->getWear() >= 1) - continue; - df::item_type t = i->getType(); - int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; - - available[make_pair(t, size)] += 1; + available.clear(); + needed.clear(); + queued.clear(); + sizes.clear(); + orders.clear(); + supply.clear(); } - // scan for clothing raw materials - - for (auto i : world->items.other[df::items_other_id::CLOTH]) + void scan_clothing() { - if (i->flags.whole & bad_flags.whole) - continue; - if (!i->hasImprovements()) // only count dyed - continue; - MaterialInfo mat(i); - int ss = i->getStackSize(); - - if (mat.material) + for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" { - if (mat.material->flags.is_set(df::material_flags::SILK)) - silk += ss; - else if (mat.material->flags.is_set(df::material_flags::THREAD_PLANT)) - cloth += ss; - else if (mat.material->flags.is_set(df::material_flags::YARN)) - yarn += ss; - } - } + if (i->flags.whole & bad_flags.whole) + continue; + if (i->flags.bits.owned) + continue; + if (i->getWear() >= 1) + continue; + df::item_type t = i->getType(); + int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; - for (auto i : world->items.other[df::items_other_id::SKIN_TANNED]) - { - if (i->flags.whole & bad_flags.whole) - continue; - leather += i->getStackSize(); + available[make_pair(t, size)] += 1; + } } - out.print("available: silk %d yarn %d cloth %d leather %d\n", silk, yarn, cloth, leather); - - // scan for units who need replacement clothing - - for (auto u : world->units.active) + void scan_materials() { - if (!Units::isOwnCiv(u) || - !Units::isOwnGroup(u) || - !Units::isActive(u) || - Units::isBaby(u)) - continue; // skip units we don't control - - set wearing; - wearing.empty(); + for (auto i : world->items.other[df::items_other_id::CLOTH]) + { + if (i->flags.whole & bad_flags.whole) + continue; + if (!i->hasImprovements()) // only count dyed + continue; + MaterialInfo mat(i); + int ss = i->getStackSize(); - deque worn; - worn.empty(); + if (mat.material) + { + if (mat.material->flags.is_set(df::material_flags::SILK)) + supply[M_SILK] += ss; + else if (mat.material->flags.is_set(df::material_flags::THREAD_PLANT)) + supply[M_CLOTH] += ss; + else if (mat.material->flags.is_set(df::material_flags::YARN)) + supply[M_YARN] += ss; + } + } - for (auto inv : u->inventory) + for (auto i : world->items.other[df::items_other_id::SKIN_TANNED]) { - if (inv->mode != df::unit_inventory_item::Worn) + if (i->flags.whole & bad_flags.whole) continue; - if (inv->item->getWear() > 0) - worn.push_back(inv->item); - else - wearing.insert(inv->item->getType()); + supply[M_LEATHER] += i->getStackSize(); } - int size = world->raws.creatures.all[u->race]->adultsize; - sizes[size] = u->race; + out->print("tailor: available silk %d yarn %d cloth %d leather %d\n", supply[M_SILK], supply[M_YARN], supply[M_CLOTH], supply[M_LEATHER]); + } - for (auto ty : set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) + void scan_replacements() + { + for (auto u : world->units.active) { - if (wearing.count(ty) == 0) - needed[make_pair(ty, size)] += 1; - } + if (!Units::isOwnCiv(u) || + !Units::isOwnGroup(u) || + !Units::isActive(u) || + Units::isBaby(u)) + continue; // skip units we don't control - for (auto w : worn) - { - auto ty = w->getType(); - auto oo = itemTypeMap.find(ty); - if (oo == itemTypeMap.end()) - continue; - df::job_type o = oo->second; + set wearing; + wearing.clear(); - int size = world->raws.creatures.all[w->getMakerRace()]->adultsize; - std::string description; - w->getItemDescription(&description, 0); + deque worn; + worn.clear(); - if (available[make_pair(ty, size)] > 0) + for (auto inv : u->inventory) { - if (w->flags.bits.owned) - { - bool confiscated = Items::setOwner(w, NULL); - - out.print( - "%s %s from %s.\n", - (confiscated ? "Confiscated" : "Could not confiscate"), - description.c_str(), - Translation::TranslateName(&u->name, false).c_str() - ); - } + if (inv->mode != df::unit_inventory_item::Worn) + continue; + if (inv->item->getWear() > 0) + worn.push_back(inv->item); + else + wearing.insert(inv->item->getType()); + } - if (wearing.count(ty) == 0) - available[make_pair(ty, size)] -= 1; + int size = world->raws.creatures.all[u->race]->adultsize; + sizes[size] = u->race; - if (w->getWear() > 1) - w->flags.bits.dump = true; + for (auto ty : set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) + { + if (wearing.count(ty) == 0) + needed[make_pair(ty, size)] += 1; } - else + + for (auto w : worn) { -// out.print("%s worn by %s needs replacement\n", -// description.c_str(), -// Translation::TranslateName(&u->name, false).c_str() -// ); - orders[make_tuple(o, w->getSubtype(), size)] += 1; + auto ty = w->getType(); + auto o = itemTypeMap.at(ty); + + int size = world->raws.creatures.all[w->getMakerRace()]->adultsize; + std::string description; + w->getItemDescription(&description, 0); + + if (available[make_pair(ty, size)] > 0) + { + if (w->flags.bits.owned) + { + bool confiscated = Items::setOwner(w, NULL); + + out->print( + "tailor: %s %s from %s.\n", + (confiscated ? "confiscated" : "could not confiscate"), + description.c_str(), + Translation::TranslateName(&u->name, false).c_str() + ); + } + + if (wearing.count(ty) == 0) + available[make_pair(ty, size)] -= 1; + + if (w->getWear() > 1) + w->flags.bits.dump = true; + } + else + { + // out->print("%s worn by %s needs replacement\n", + // description.c_str(), + // Translation::TranslateName(&u->name, false).c_str() + // ); + orders[make_tuple(o, w->getSubtype(), size)] += 1; + } } } } - auto entity = world->entities.all[ui->civ_id]; - - for (auto a : needed) + void create_orders() { - df::item_type ty = a.first.first; - int size = a.first.second; - int count = a.second; - - int sub = 0; - vector v; - - switch (ty) { - case df::item_type::ARMOR: v = entity->resources.armor_type; break; - case df::item_type::GLOVES: v = entity->resources.gloves_type; break; - case df::item_type::HELM: v = entity->resources.helm_type; break; - case df::item_type::PANTS: v = entity->resources.pants_type; break; - case df::item_type::SHOES: v = entity->resources.shoes_type; break; - default: break; - } + auto entity = world->entities.all[ui->civ_id]; + + for (auto& a : needed) + { + df::item_type ty = a.first.first; + int size = a.first.second; + int count = a.second; + + int sub = 0; + vector v; - for (auto vv : v) { - bool isClothing = false; switch (ty) { - case df::item_type::ARMOR: isClothing = world->raws.itemdefs.armor[vv] ->armorlevel == 0; break; - case df::item_type::GLOVES: isClothing = world->raws.itemdefs.gloves[vv]->armorlevel == 0; break; - case df::item_type::HELM: isClothing = world->raws.itemdefs.helms[vv] ->armorlevel == 0; break; - case df::item_type::PANTS: isClothing = world->raws.itemdefs.pants[vv] ->armorlevel == 0; break; - case df::item_type::SHOES: isClothing = world->raws.itemdefs.shoes[vv] ->armorlevel == 0; break; + case df::item_type::ARMOR: v = entity->resources.armor_type; break; + case df::item_type::GLOVES: v = entity->resources.gloves_type; break; + case df::item_type::HELM: v = entity->resources.helm_type; break; + case df::item_type::PANTS: v = entity->resources.pants_type; break; + case df::item_type::SHOES: v = entity->resources.shoes_type; break; default: break; } - if (isClothing) - { - sub = vv; - break; + + for (auto vv : v) { + bool isClothing = false; + switch (ty) { + case df::item_type::ARMOR: isClothing = world->raws.itemdefs.armor[vv]->armorlevel == 0; break; + case df::item_type::GLOVES: isClothing = world->raws.itemdefs.gloves[vv]->armorlevel == 0; break; + case df::item_type::HELM: isClothing = world->raws.itemdefs.helms[vv]->armorlevel == 0; break; + case df::item_type::PANTS: isClothing = world->raws.itemdefs.pants[vv]->armorlevel == 0; break; + case df::item_type::SHOES: isClothing = world->raws.itemdefs.shoes[vv]->armorlevel == 0; break; + default: break; + } + if (isClothing) + { + sub = vv; + break; + } } - } - orders[make_tuple(itemTypeMap[ty], sub, size)] += count; + const df::job_type j = itemTypeMap.at(ty); + orders[make_tuple(j, sub, size)] += count; + } } - // scan orders - - for (auto o : world->manager_orders) + void scan_existing_orders() { - auto f = jobTypeMap.find(o->job_type); - if (f == jobTypeMap.end()) - continue; + for (auto o : world->manager_orders) + { + auto f = jobTypeMap.find(o->job_type); + if (f == jobTypeMap.end()) + continue; - auto sub = o->item_subtype; - int race = o->hist_figure_id; - if (race == -1) - continue; // -1 means that the race of the worker will determine the size made; we must ignore these jobs + auto sub = o->item_subtype; + int race = o->hist_figure_id; + if (race == -1) + continue; // -1 means that the race of the worker will determine the size made; we must ignore these jobs - int size = world->raws.creatures.all[race]->adultsize; + int size = world->raws.creatures.all[race]->adultsize; - orders[make_tuple(o->job_type, sub, size)] -= o->amount_left; - } + orders[make_tuple(o->job_type, sub, size)] -= o->amount_left; + } - // place orders + } - for (auto o : orders) + void place_orders() { - df::job_type ty; - int sub; - int size; + auto entity = world->entities.all[ui->civ_id]; - tie(ty, sub, size) = o.first; - int count = o.second; - - if (count > 0) + for (auto& o : orders) { - vector v; - BitArray* fl; - string name_s, name_p; + df::job_type ty; + int sub; + int size; - switch (ty) { - case df::job_type::MakeArmor: - name_s = world->raws.itemdefs.armor[sub]->name; - name_p = world->raws.itemdefs.armor[sub]->name_plural; - v = entity->resources.armor_type; - fl = &world->raws.itemdefs.armor[sub]->props.flags; - break; - case df::job_type::MakeGloves: - name_s = world->raws.itemdefs.gloves[sub]->name; - name_p = world->raws.itemdefs.gloves[sub]->name_plural; - v = entity->resources.gloves_type; - fl = &world->raws.itemdefs.gloves[sub]->props.flags; - break; - case df::job_type::MakeHelm: - name_s = world->raws.itemdefs.helms[sub]->name; - name_p = world->raws.itemdefs.helms[sub]->name_plural; - v = entity->resources.helm_type; - fl = &world->raws.itemdefs.helms[sub]->props.flags; - break; - case df::job_type::MakePants: - name_s = world->raws.itemdefs.pants[sub]->name; - name_p = world->raws.itemdefs.pants[sub]->name_plural; - v = entity->resources.pants_type; - fl = &world->raws.itemdefs.pants[sub]->props.flags; - break; - case df::job_type::MakeShoes: - name_s = world->raws.itemdefs.shoes[sub]->name; - name_p = world->raws.itemdefs.shoes[sub]->name_plural; - v = entity->resources.shoes_type; - fl = &world->raws.itemdefs.shoes[sub]->props.flags; - break; - default: - break; - } + tie(ty, sub, size) = o.first; + int count = o.second; - bool can_make = false; - for (auto vv : v) + if (count > 0) { - if (vv == sub) - { - can_make = true; + vector v; + BitArray* fl; + string name_s, name_p; + + switch (ty) { + + case df::job_type::MakeArmor: + v = entity->resources.armor_type; + name_s = world->raws.itemdefs.armor[sub]->name; + name_p = world->raws.itemdefs.armor[sub]->name_plural; + fl = &world->raws.itemdefs.armor[sub]->props.flags; + break; + case df::job_type::MakeGloves: + name_s = world->raws.itemdefs.gloves[sub]->name; + name_p = world->raws.itemdefs.gloves[sub]->name_plural; + v = entity->resources.gloves_type; + fl = &world->raws.itemdefs.gloves[sub]->props.flags; + break; + case df::job_type::MakeHelm: + name_s = world->raws.itemdefs.helms[sub]->name; + name_p = world->raws.itemdefs.helms[sub]->name_plural; + v = entity->resources.helm_type; + fl = &world->raws.itemdefs.helms[sub]->props.flags; + break; + case df::job_type::MakePants: + name_s = world->raws.itemdefs.pants[sub]->name; + name_p = world->raws.itemdefs.pants[sub]->name_plural; + v = entity->resources.pants_type; + fl = &world->raws.itemdefs.pants[sub]->props.flags; + break; + case df::job_type::MakeShoes: + name_s = world->raws.itemdefs.shoes[sub]->name; + name_p = world->raws.itemdefs.shoes[sub]->name_plural; + v = entity->resources.shoes_type; + fl = &world->raws.itemdefs.shoes[sub]->props.flags; + break; + default: break; } - } - if (!can_make) - { - out.print("Cannot make %s, skipped\n", name_p.c_str()); - continue; // this civilization does not know how to make this item, so sorry - } + bool can_make = std::find(v.begin(), v.end(), sub) != v.end(); - switch (ty) { - case df::item_type::ARMOR: break; - case df::item_type::GLOVES: break; - case df::item_type::HELM: break; - case df::item_type::PANTS: break; - case df::item_type::SHOES: break; - default: break; + if (!can_make) + { + out->print("tailor: civilization cannot make %s, skipped\n", name_p.c_str()); + continue; + } + + for (auto& m : material_order) + { + if (count <= 0) + break; + + auto r = reserves.find(m); + int res = (r == reserves.end()) ? default_reserve : r->second; + + if (supply[m] > res && fl->is_set(m.armor_flag)) { + int c = count; + if (supply[m] < count + res) + c = supply[m] - res; + supply[m] -= c; + + auto order = new df::manager_order; + order->job_type = ty; + order->item_type = df::item_type::NONE; + order->item_subtype = sub; + order->mat_type = -1; + order->mat_index = -1; + order->amount_left = c; + order->amount_total = c; + order->status.bits.validated = false; + order->status.bits.active = false; + order->id = world->manager_order_next_id++; + order->hist_figure_id = sizes[size]; + order->material_category = m.job_material; + + world->manager_orders.push_back(order); + + out->print("tailor: added order #%d for %d %s %s, sized for %s\n", + order->id, + c, + bitfield_to_string(order->material_category).c_str(), + (c > 1) ? name_p.c_str() : name_s.c_str(), + world->raws.creatures.all[order->hist_figure_id]->name[1].c_str() + ); + + count -= c; + } + } } + } + } - df::job_material_category mat; +public: + void do_scan(color_ostream& o) + { + out = &o; - if (silk > count + 10 && fl->is_set(df::armor_general_flags::SOFT)) { - mat.whole = df::job_material_category::mask_silk; - silk -= count; - } - else if (cloth > count + 10 && fl->is_set(df::armor_general_flags::SOFT)) { - mat.whole = df::job_material_category::mask_cloth; - cloth -= count; + reset(); + + // scan for useable clothing + + scan_clothing(); + + // scan for clothing raw materials + + scan_materials(); + + // scan for units who need replacement clothing + + scan_replacements(); + + // create new orders + + create_orders(); + + // scan existing orders and subtract + + scan_existing_orders(); + + // place orders + + place_orders(); + } + +public: + command_result set_materials(color_ostream& out, vector& parameters) + { + list newmat; + newmat.clear(); + + for (auto m = parameters.begin() + 1; m != parameters.end(); m++) + { + auto nameMatch = [m](MatType& m1) { return *m == m1.name; }; + auto mm = std::find_if(all_materials.begin(), all_materials.end(), nameMatch); + if (mm == all_materials.end()) + { + out.print("tailor: material %s not recognized\n", m->c_str()); + return CR_WRONG_USAGE; } - else if (yarn > count + 10 && fl->is_set(df::armor_general_flags::SOFT)) { - mat.whole = df::job_material_category::mask_yarn; - yarn -= count; + else { + newmat.push_back(*mm); } - else if (leather > count + 10 && fl->is_set(df::armor_general_flags::LEATHER)) { - mat.whole = df::job_material_category::mask_leather; - leather -= count; + } + + material_order = newmat; + out.print("tailor: material list set to %s\n", get_material_list().c_str()); + + return CR_OK; + } + +public: + std::string get_material_list() + { + std::string s; + for (const auto& m : material_order) + { + if (!s.empty()) s += ", "; + s += m.name; + } + return s; + } + +public: + void process(color_ostream& out) + { + bool found = false; + + for (df::job_list_link* link = &world->jobs.list; link != NULL; link = link->next) + { + if (link->item == NULL) continue; + if (link->item->job_type == df::enums::job_type::UpdateStockpileRecords) + { + found = true; + break; } - else // not enough appropriate material available - continue; + } - auto order = new df::manager_order(); - order->job_type = ty; - order->item_type = df::item_type::NONE; - order->item_subtype = sub; - order->mat_type = -1; - order->mat_index = -1; - order->amount_left = count; - order->amount_total = count; - order->status.bits.validated = false; - order->status.bits.active = false; - order->id = world->manager_order_next_id++; - order->hist_figure_id = sizes[size]; - order->material_category = mat; - - world->manager_orders.push_back(order); - - out.print("Added order #%d for %d %s %s (sized for %s)\n", - order->id, - count, - bitfield_to_string(order->material_category).c_str(), - (count > 1) ? name_p.c_str() : name_s.c_str(), - world->raws.creatures.all[order->hist_figure_id]->name[1].c_str() - ); + if (found) + { + do_scan(out); } } -} +}; + +static std::unique_ptr tailor_instance; #define DELTA_TICKS 600 -DFhackCExport command_result plugin_onupdate(color_ostream &out) +DFhackCExport command_result plugin_onupdate(color_ostream& out) { - if (!enabled) + if (!enabled || !tailor_instance) return CR_OK; if (!Maps::IsValid()) @@ -427,64 +561,76 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) if (world->frame_counter % DELTA_TICKS != 0) return CR_OK; - bool found = false; - - for (df::job_list_link* link = &world->jobs.list; link != NULL; link = link->next) { - if (link->item == NULL) continue; - if (link->item->job_type == df::enums::job_type::UpdateStockpileRecords) - { - found = true; - break; - } - } - - if (found) - { - do_scan(out); + CoreSuspender suspend; + tailor_instance->process(out); } return CR_OK; } -static command_result tailor_cmd(color_ostream &out, vector & parameters) { +static command_result tailor_cmd(color_ostream& out, vector & parameters) { bool desired = enabled; - if (parameters.size() == 1) + if (parameters.size() == 1 && parameters[0] == "enable" || parameters[0] == "on" || parameters[0] == "1") { - if (parameters[0] == "enable" || parameters[0] == "on" || parameters[0] == "1") - { - desired = true; - } - else if (parameters[0] == "disable" || parameters[0] == "off" || parameters[0] == "0") + desired = true; + } + else if (parameters.size() == 1 && parameters[0] == "disable" || parameters[0] == "off" || parameters[0] == "0") + { + desired = false; + } + else if (parameters.size() == 1 && parameters[0] == "usage" || parameters[0] == "help" || parameters[0] == "?") + { + out.print("%s: %s\nUsage:\n%s", plugin_name, tagline, usage); + return CR_OK; + } + else if (parameters.size() == 1 && parameters[0] == "test") + { + if (tailor_instance) { - desired = false; + tailor_instance->do_scan(out); + return CR_OK; } - else if (parameters[0] == "usage" || parameters[0] == "help" || parameters[0] == "?") + else { - out.print("%s: %s\nUsage:\n%s", plugin_name, tagline, usage); - return CR_OK; + out.print("%s: not instantiated\n", plugin_name); + return CR_FAILURE; } - else if (parameters[0] == "test") + } + else if (parameters.size() > 1 && parameters[0] == "materials") + { + if (tailor_instance) { - do_scan(out); - return CR_OK; + return tailor_instance->set_materials(out, parameters); } - else if (parameters[0] != "status") + else { - return CR_WRONG_USAGE; + out.print("%s: not instantiated\n", plugin_name); + return CR_FAILURE; } } - else + else if (parameters.size() == 1 && parameters[0] != "status") + { return CR_WRONG_USAGE; + } + + out.print("Tailor is %s %s.\n", (desired == enabled) ? "currently" : "now", desired ? "enabled" : "disabled"); + if (tailor_instance) + { + out.print("Material list is: %s\n", tailor_instance->get_material_list().c_str()); + } + else + { + out.print("%s: not instantiated\n", plugin_name); + } - out.print("Tailor is %s %s.\n", (desired == enabled)? "currently": "now", desired? "enabled": "disabled"); enabled = desired; return CR_OK; } -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event event) { return CR_OK; } @@ -495,8 +641,10 @@ DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) return CR_OK; } -DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) +DFhackCExport command_result plugin_init(color_ostream& out, std::vector & commands) { + tailor_instance = std::move(dts::make_unique()); + if (AUTOENABLE) { enabled = true; } @@ -505,6 +653,9 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector