Add in a strictness parameter to Autoclothing. It's not used yet.

develop
Rose 2023-01-29 17:19:21 -08:00
parent f4c4d4dcdf
commit 75c560d9e1
1 changed files with 73 additions and 44 deletions

@ -1,7 +1,8 @@
#include "Core.h" #include "Core.h"
#include <Console.h> #include "Console.h"
#include <Export.h> #include "Debug.h"
#include <PluginManager.h> #include "Export.h"
#include "PluginManager.h"
#include <map> #include <map>
@ -11,6 +12,7 @@
#include "modules/Items.h" #include "modules/Items.h"
#include "modules/Maps.h" #include "modules/Maps.h"
#include "modules/Materials.h" #include "modules/Materials.h"
#include "modules/Persistence.h"
#include "modules/Units.h" #include "modules/Units.h"
#include "modules/World.h" #include "modules/World.h"
#include "modules/Translation.h" #include "modules/Translation.h"
@ -34,6 +36,9 @@
#include "df/world.h" #include "df/world.h"
using std::endl; using std::endl;
using std::string;
using std::vector;
using std::map;
using namespace DFHack; using namespace DFHack;
using namespace DFHack::Items; using namespace DFHack::Items;
@ -41,35 +46,46 @@ using namespace DFHack::Units;
using namespace df::enums; using namespace df::enums;
// A plugin must be able to return its name and version.
// The name string provided must correspond to the filename -
// skeleton.plug.so, skeleton.plug.dylib, or skeleton.plug.dll in this case
DFHACK_PLUGIN("autoclothing"); DFHACK_PLUGIN("autoclothing");
// Any globals a plugin requires (e.g. world) should be listed here.
// For example, this line expands to "using df::global::world" and prevents the
// plugin from being loaded if df::global::world is null (i.e. missing from symbols.xml):
//
REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(world);
// Only run if this is enabled // Only run if this is enabled
DFHACK_PLUGIN_IS_ENABLED(autoclothing_enabled); DFHACK_PLUGIN_IS_ENABLED(autoclothing_enabled);
// logging levels can be dynamically controlled with the `debugfilter` command.
namespace DFHack {
// for configuration-related logging
DBG_DECLARE(autoclothing, status, DebugCategory::LINFO);
// for logging during the periodic scan
DBG_DECLARE(autoclothing, cycle, DebugCategory::LINFO);
}
static const string CONFIG_KEY = string(plugin_name) + "/config";
// Here go all the command declarations... // Here go all the command declarations...
// mostly to allow having the mandatory stuff on top of the file and commands on the bottom // mostly to allow having the mandatory stuff on top of the file and commands on the bottom
struct ClothingRequirement; struct ClothingRequirement;
command_result autoclothing(color_ostream &out, std::vector <std::string> & parameters); command_result autoclothing(color_ostream &out, vector <string> & parameters);
static void init_state(color_ostream &out); static void init_state(color_ostream &out);
static void save_state(color_ostream &out); static void save_state(color_ostream &out);
static void cleanup_state(color_ostream &out); static void cleanup_state(color_ostream &out);
static void do_autoclothing(); static void do_autoclothing();
static bool validateMaterialCategory(ClothingRequirement * requirement); static bool validateMaterialCategory(ClothingRequirement * requirement);
static bool setItem(std::string name, ClothingRequirement* requirement); static bool setItem(string name, ClothingRequirement* requirement);
static void generate_report(color_ostream& out); static void generate_report(color_ostream& out);
static bool isAvailableItem(df::item* item); static bool isAvailableItem(df::item* item);
enum match_strictness
{
STRICT_PERMISSIVE,
STRICT_TYPE,
STRICT_MATERIAL
};
match_strictness strictnessSetting = STRICT_PERMISSIVE;
std::vector<ClothingRequirement>clothingOrders; vector<ClothingRequirement>clothingOrders;
struct ClothingRequirement struct ClothingRequirement
{ {
@ -78,7 +94,7 @@ struct ClothingRequirement
int16_t item_subtype; int16_t item_subtype;
df::job_material_category material_category; df::job_material_category material_category;
int16_t needed_per_citizen; int16_t needed_per_citizen;
std::map<int16_t, int32_t> total_needed_per_race; map<int16_t, int32_t> total_needed_per_race;
bool matches(ClothingRequirement * b) bool matches(ClothingRequirement * b)
{ {
@ -93,7 +109,7 @@ struct ClothingRequirement
return true; return true;
} }
std::string Serialize() string Serialize()
{ {
std::stringstream stream; std::stringstream stream;
stream << ENUM_KEY_STR(job_type, jobType) << " "; stream << ENUM_KEY_STR(job_type, jobType) << " ";
@ -104,10 +120,10 @@ struct ClothingRequirement
return stream.str(); return stream.str();
} }
void Deserialize(std::string s) void Deserialize(string s)
{ {
std::stringstream stream(s); std::stringstream stream(s);
std::string loadedJob; string loadedJob;
stream >> loadedJob; stream >> loadedJob;
FOR_ENUM_ITEMS(job_type, job) FOR_ENUM_ITEMS(job_type, job)
{ {
@ -117,7 +133,7 @@ struct ClothingRequirement
break; break;
} }
} }
std::string loadedItem; string loadedItem;
stream >> loadedItem; stream >> loadedItem;
FOR_ENUM_ITEMS(item_type, item) FOR_ENUM_ITEMS(item_type, item)
{ {
@ -132,7 +148,7 @@ struct ClothingRequirement
stream >> needed_per_citizen; stream >> needed_per_citizen;
} }
bool SetFromParameters(color_ostream &out, std::vector <std::string> & parameters) bool SetFromParameters(color_ostream &out, vector <string> & parameters)
{ {
if (!set_bitfield_field(&material_category, parameters[0], 1)) if (!set_bitfield_field(&material_category, parameters[0], 1))
{ {
@ -151,12 +167,12 @@ struct ClothingRequirement
return true; return true;
} }
std::string ToReadableLabel() string ToReadableLabel()
{ {
std::stringstream stream; std::stringstream stream;
stream << bitfield_to_string(material_category) << " "; stream << bitfield_to_string(material_category) << " ";
std::string adjective = ""; string adjective = "";
std::string name = ""; string name = "";
switch (itemType) switch (itemType)
{ {
case df::enums::item_type::ARMOR: case df::enums::item_type::ARMOR:
@ -193,7 +209,7 @@ struct ClothingRequirement
// Mandatory init function. If you have some global state, create it here. // Mandatory init function. If you have some global state, create it here.
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) DFhackCExport command_result plugin_init(color_ostream &out, vector <PluginCommand> &commands)
{ {
// Fill the command list with your commands. // Fill the command list with your commands.
commands.push_back(PluginCommand( commands.push_back(PluginCommand(
@ -256,12 +272,12 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
return CR_OK; return CR_OK;
} }
static bool setItemFromName(std::string name, ClothingRequirement* requirement) static bool setItemFromName(string name, ClothingRequirement* requirement)
{ {
#define SEARCH_ITEM_RAWS(rawType, job, item) \ #define SEARCH_ITEM_RAWS(rawType, job, item) \
for (auto& itemdef : world->raws.itemdefs.rawType) \ for (auto& itemdef : world->raws.itemdefs.rawType) \
{ \ { \
std::string fullName = itemdef->adjective.empty() ? itemdef->name : itemdef->adjective + " " + itemdef->name; \ string fullName = itemdef->adjective.empty() ? itemdef->name : itemdef->adjective + " " + itemdef->name; \
if (fullName == name) \ if (fullName == name) \
{ \ { \
requirement->jobType = job_type::job; \ requirement->jobType = job_type::job; \
@ -278,7 +294,7 @@ for (auto& itemdef : world->raws.itemdefs.rawType) \
return false; return false;
} }
static bool setItemFromToken(std::string token, ClothingRequirement* requirement) static bool setItemFromToken(string token, ClothingRequirement* requirement)
{ {
ItemTypeInfo itemInfo; ItemTypeInfo itemInfo;
if (!itemInfo.find(token)) if (!itemInfo.find(token))
@ -308,7 +324,7 @@ static bool setItemFromToken(std::string token, ClothingRequirement* requirement
return true; return true;
} }
static bool setItem(std::string name, ClothingRequirement* requirement) static bool setItem(string name, ClothingRequirement* requirement)
{ {
if (setItemFromName(name, requirement)) if (setItemFromName(name, requirement))
return true; return true;
@ -362,7 +378,7 @@ static bool validateMaterialCategory(ClothingRequirement * requirement)
// A command! It sits around and looks pretty. And it's nice and friendly. // A command! It sits around and looks pretty. And it's nice and friendly.
command_result autoclothing(color_ostream &out, std::vector <std::string> & parameters) command_result autoclothing(color_ostream &out, vector <string> & parameters)
{ {
// It's nice to print a help message you get invalid options // It's nice to print a help message you get invalid options
// from the user instead of just acting strange. // from the user instead of just acting strange.
@ -379,6 +395,20 @@ command_result autoclothing(color_ostream &out, std::vector <std::string> & para
} }
return CR_OK; return CR_OK;
} }
else if (parameters[0] == "strictness")
{
if (parameters.size() != 2)
{
out << "Wrong number of arguments." << endl;
return CR_WRONG_USAGE;
}
if (parameters[1] == "permissive")
strictnessSetting = STRICT_PERMISSIVE;
else if (parameters[1] == "type")
strictnessSetting = STRICT_TYPE;
else if (parameters[1] == "material")
strictnessSetting = STRICT_MATERIAL;
}
else if (parameters.size() == 1 && parameters[0] == "report") else if (parameters.size() == 1 && parameters[0] == "report")
{ {
CoreSuspender suspend; CoreSuspender suspend;
@ -631,9 +661,8 @@ static void init_state(color_ostream &out)
autoclothing_enabled = false; autoclothing_enabled = false;
} }
// Parse constraints // Parse constraints
std::vector<PersistentDataItem> items; vector<PersistentDataItem> items;
World::GetPersistentData(&items, "autoclothing/clothingItems"); World::GetPersistentData(&items, "autoclothing/clothingItems");
for (auto& item : items) for (auto& item : items)
@ -662,7 +691,7 @@ static void save_state(color_ostream &out)
// Parse constraints // Parse constraints
std::vector<PersistentDataItem> items; vector<PersistentDataItem> items;
World::GetPersistentData(&items, "autoclothing/clothingItems"); World::GetPersistentData(&items, "autoclothing/clothingItems");
for (size_t i = 0; i < items.size(); i++) for (size_t i = 0; i < items.size(); i++)
@ -683,7 +712,7 @@ static void save_state(color_ostream &out)
} }
} }
static void list_unit_counts(color_ostream& out, std::map<int, int>& unitList) static void list_unit_counts(color_ostream& out, map<int, int>& unitList)
{ {
for (const auto& race : unitList) for (const auto& race : unitList)
{ {
@ -733,12 +762,12 @@ static bool isAvailableItem(df::item* item)
static void generate_report(color_ostream& out) static void generate_report(color_ostream& out)
{ {
std::map<int, int> fullUnitList; map<int, int> fullUnitList;
std::map<int, int> missingArmor; map<int, int> missingArmor;
std::map<int, int> missingShoes; map<int, int> missingShoes;
std::map<int, int> missingHelms; map<int, int> missingHelms;
std::map<int, int> missingGloves; map<int, int> missingGloves;
std::map<int, int> missingPants; map<int, int> missingPants;
for (df::unit* unit : world->units.active) for (df::unit* unit : world->units.active)
{ {
if (!Units::isCitizen(unit)) if (!Units::isCitizen(unit))
@ -817,7 +846,7 @@ static void generate_report(color_ostream& out)
list_unit_counts(out, missingPants); list_unit_counts(out, missingPants);
} }
} }
std::map<int, int> availableArmor; map<int, int> availableArmor;
for (auto armor : world->items.other.ARMOR) for (auto armor : world->items.other.ARMOR)
{ {
if (!isAvailableItem(armor)) if (!isAvailableItem(armor))
@ -829,7 +858,7 @@ static void generate_report(color_ostream& out)
out << "We have available bodywear for:" << endl; out << "We have available bodywear for:" << endl;
list_unit_counts(out, availableArmor); list_unit_counts(out, availableArmor);
} }
std::map<int, int> availableShoes; map<int, int> availableShoes;
for (auto shoe : world->items.other.SHOES) for (auto shoe : world->items.other.SHOES)
{ {
if (!isAvailableItem(shoe)) if (!isAvailableItem(shoe))
@ -841,7 +870,7 @@ static void generate_report(color_ostream& out)
out << "We have available footwear for:" << endl; out << "We have available footwear for:" << endl;
list_unit_counts(out, availableShoes); list_unit_counts(out, availableShoes);
} }
std::map<int, int> availableHelms; map<int, int> availableHelms;
for (auto helm : world->items.other.HELM) for (auto helm : world->items.other.HELM)
{ {
if (!isAvailableItem(helm)) if (!isAvailableItem(helm))
@ -853,7 +882,7 @@ static void generate_report(color_ostream& out)
out << "We have available headwear for:" << endl; out << "We have available headwear for:" << endl;
list_unit_counts(out, availableHelms); list_unit_counts(out, availableHelms);
} }
std::map<int, int> availableGloves; map<int, int> availableGloves;
for (auto glove : world->items.other.HELM) for (auto glove : world->items.other.HELM)
{ {
if (!isAvailableItem(glove)) if (!isAvailableItem(glove))
@ -865,7 +894,7 @@ static void generate_report(color_ostream& out)
out << "We have available handwear for:" << endl; out << "We have available handwear for:" << endl;
list_unit_counts(out, availableGloves); list_unit_counts(out, availableGloves);
} }
std::map<int, int> availablePants; map<int, int> availablePants;
for (auto pants : world->items.other.HELM) for (auto pants : world->items.other.HELM)
{ {
if (!isAvailableItem(pants)) if (!isAvailableItem(pants))