|
|
|
@ -1,136 +1,160 @@
|
|
|
|
|
/*
|
|
|
|
|
* Tailor plugin. Automatically manages keeping your dorfs clothed.
|
|
|
|
|
* For best effect, place "tailor enable" in your dfhack.init configuration,
|
|
|
|
|
* or set AUTOENABLE to true.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "Core.h"
|
|
|
|
|
#include "DataDefs.h"
|
|
|
|
|
#include "Debug.h"
|
|
|
|
|
#include "PluginManager.h"
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "df/creature_raw.h"
|
|
|
|
|
#include "df/global_objects.h"
|
|
|
|
|
#include "df/historical_entity.h"
|
|
|
|
|
#include "df/item.h"
|
|
|
|
|
#include "df/item_flags.h"
|
|
|
|
|
#include "df/itemdef_armorst.h"
|
|
|
|
|
#include "df/itemdef_glovesst.h"
|
|
|
|
|
#include "df/itemdef_helmst.h"
|
|
|
|
|
#include "df/itemdef_pantsst.h"
|
|
|
|
|
#include "df/itemdef_shoesst.h"
|
|
|
|
|
#include "df/items_other_id.h"
|
|
|
|
|
#include "df/job.h"
|
|
|
|
|
#include "df/job_type.h"
|
|
|
|
|
#include "df/manager_order.h"
|
|
|
|
|
#include "df/plotinfost.h"
|
|
|
|
|
#include "df/world.h"
|
|
|
|
|
|
|
|
|
|
#include "modules/Maps.h"
|
|
|
|
|
#include "modules/Units.h"
|
|
|
|
|
#include "Core.h"
|
|
|
|
|
#include "Debug.h"
|
|
|
|
|
#include "LuaTools.h"
|
|
|
|
|
#include "PluginManager.h"
|
|
|
|
|
|
|
|
|
|
#include "modules/Materials.h"
|
|
|
|
|
#include "modules/Persistence.h"
|
|
|
|
|
#include "modules/Translation.h"
|
|
|
|
|
#include "modules/Units.h"
|
|
|
|
|
#include "modules/World.h"
|
|
|
|
|
|
|
|
|
|
using namespace DFHack;
|
|
|
|
|
using std::string;
|
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
|
|
using df::global::world;
|
|
|
|
|
using df::global::plotinfo;
|
|
|
|
|
using namespace DFHack;
|
|
|
|
|
|
|
|
|
|
DFHACK_PLUGIN("tailor");
|
|
|
|
|
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
|
|
|
|
|
|
|
|
|
#define AUTOENABLE false
|
|
|
|
|
DFHACK_PLUGIN_IS_ENABLED(enabled);
|
|
|
|
|
|
|
|
|
|
REQUIRE_GLOBAL(world);
|
|
|
|
|
REQUIRE_GLOBAL(plotinfo);
|
|
|
|
|
REQUIRE_GLOBAL(standing_orders_use_dyed_cloth);
|
|
|
|
|
REQUIRE_GLOBAL(world);
|
|
|
|
|
|
|
|
|
|
namespace DFHack {
|
|
|
|
|
DBG_DECLARE(tailor, cycle, DebugCategory::LINFO);
|
|
|
|
|
DBG_DECLARE(tailor, config, DebugCategory::LINFO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Tailor {
|
|
|
|
|
// ARMOR, SHOES, HELM, GLOVES, PANTS
|
|
|
|
|
static const string CONFIG_KEY = string(plugin_name) + "/config";
|
|
|
|
|
static PersistentDataItem config;
|
|
|
|
|
|
|
|
|
|
// ah, if only STL had a bimap
|
|
|
|
|
enum ConfigValues {
|
|
|
|
|
CONFIG_IS_ENABLED = 0,
|
|
|
|
|
CONFIG_SILK_IDX = 1,
|
|
|
|
|
CONFIG_CLOTH_IDX = 2,
|
|
|
|
|
CONFIG_YARN_IDX = 3,
|
|
|
|
|
CONFIG_LEATHER_IDX = 4,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
static int get_config_val(PersistentDataItem &c, int index) {
|
|
|
|
|
if (!c.isValid())
|
|
|
|
|
return -1;
|
|
|
|
|
return c.ival(index);
|
|
|
|
|
}
|
|
|
|
|
static bool get_config_bool(PersistentDataItem &c, int index) {
|
|
|
|
|
return get_config_val(c, index) == 1;
|
|
|
|
|
}
|
|
|
|
|
static void set_config_val(PersistentDataItem &c, int index, int value) {
|
|
|
|
|
if (c.isValid())
|
|
|
|
|
c.ival(index) = value;
|
|
|
|
|
}
|
|
|
|
|
static void set_config_bool(PersistentDataItem &c, int index, bool value) {
|
|
|
|
|
set_config_val(c, index, value ? 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::map<df::job_type, df::item_type> 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 std::map<df::item_type, df::job_type> 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;
|
|
|
|
|
}
|
|
|
|
|
static const int32_t CYCLE_TICKS = 1200; // one day
|
|
|
|
|
static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
static const std::map<df::job_type, df::item_type> 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 }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 const std::map<df::item_type, df::job_type> 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 }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class MatType {
|
|
|
|
|
public:
|
|
|
|
|
const std::string name;
|
|
|
|
|
const df::job_material_category job_material;
|
|
|
|
|
const df::armor_general_flags armor_flag;
|
|
|
|
|
|
|
|
|
|
bool operator==(const MatType& m) const {
|
|
|
|
|
return name == m.name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
// operator< is required to use this as a std::map key
|
|
|
|
|
bool operator<(const MatType& m) const {
|
|
|
|
|
return name < m.name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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) {};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::list<MatType> all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER };
|
|
|
|
|
static 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);
|
|
|
|
|
|
|
|
|
|
static const std::list<MatType> all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER };
|
|
|
|
|
static std::list<MatType> material_order = all_materials;
|
|
|
|
|
|
|
|
|
|
static struct BadFlags {
|
|
|
|
|
uint32_t whole;
|
|
|
|
|
|
|
|
|
|
BadFlags() {
|
|
|
|
|
df::item_flags flags;
|
|
|
|
|
#define F(x) 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);
|
|
|
|
|
F(in_chest); F(removed); F(encased);
|
|
|
|
|
F(spider_web);
|
|
|
|
|
#undef F
|
|
|
|
|
whole = flags.whole;
|
|
|
|
|
}
|
|
|
|
|
} badFlags;
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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<MatType, int> supply;
|
|
|
|
|
|
|
|
|
|
color_ostream* out;
|
|
|
|
|
|
|
|
|
|
std::list<MatType> material_order = { M_SILK, M_CLOTH, M_YARN, M_LEATHER };
|
|
|
|
|
std::map<MatType, int> reserves;
|
|
|
|
|
|
|
|
|
|
int default_reserve = 10;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
void reset()
|
|
|
|
|
{
|
|
|
|
|
available.clear();
|
|
|
|
@ -145,9 +169,7 @@ private:
|
|
|
|
|
{
|
|
|
|
|
for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing"
|
|
|
|
|
{
|
|
|
|
|
if (i->flags.whole & bad_flags.whole)
|
|
|
|
|
continue;
|
|
|
|
|
if (i->flags.bits.owned)
|
|
|
|
|
if (i->flags.whole & badFlags.whole)
|
|
|
|
|
continue;
|
|
|
|
|
if (i->getWear() >= 1)
|
|
|
|
|
continue;
|
|
|
|
@ -164,7 +186,7 @@ private:
|
|
|
|
|
|
|
|
|
|
for (auto i : world->items.other[df::items_other_id::CLOTH])
|
|
|
|
|
{
|
|
|
|
|
if (i->flags.whole & bad_flags.whole)
|
|
|
|
|
if (i->flags.whole & badFlags.whole)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (require_dyed && !i->hasImprovements())
|
|
|
|
@ -197,7 +219,7 @@ private:
|
|
|
|
|
|
|
|
|
|
for (auto i : world->items.other[df::items_other_id::SKIN_TANNED])
|
|
|
|
|
{
|
|
|
|
|
if (i->flags.whole & bad_flags.whole)
|
|
|
|
|
if (i->flags.whole & badFlags.whole)
|
|
|
|
|
continue;
|
|
|
|
|
supply[M_LEATHER] += i->getStackSize();
|
|
|
|
|
}
|
|
|
|
@ -369,8 +391,9 @@ private:
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void place_orders()
|
|
|
|
|
int place_orders()
|
|
|
|
|
{
|
|
|
|
|
int ordered = 0;
|
|
|
|
|
auto entity = world->entities.all[plotinfo->civ_id];
|
|
|
|
|
|
|
|
|
|
for (auto& o : orders)
|
|
|
|
@ -477,6 +500,7 @@ private:
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
count -= c;
|
|
|
|
|
ordered += c;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -486,215 +510,217 @@ private:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ordered;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
void do_scan(color_ostream& o)
|
|
|
|
|
{
|
|
|
|
|
out = &o;
|
|
|
|
|
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
|
|
// scan for useable clothing
|
|
|
|
|
static std::unique_ptr<Tailor> tailor_instance;
|
|
|
|
|
|
|
|
|
|
scan_clothing();
|
|
|
|
|
static command_result do_command(color_ostream &out, vector<string> ¶meters);
|
|
|
|
|
static int do_cycle(color_ostream &out);
|
|
|
|
|
|
|
|
|
|
// scan for clothing raw materials
|
|
|
|
|
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) {
|
|
|
|
|
DEBUG(config,out).print("initializing %s\n", plugin_name);
|
|
|
|
|
|
|
|
|
|
scan_materials();
|
|
|
|
|
tailor_instance = dts::make_unique<Tailor>();
|
|
|
|
|
|
|
|
|
|
// scan for units who need replacement clothing
|
|
|
|
|
// provide a configuration interface for the plugin
|
|
|
|
|
commands.push_back(PluginCommand(
|
|
|
|
|
plugin_name,
|
|
|
|
|
"Automatically keep your dwarves in fresh clothing.",
|
|
|
|
|
do_command));
|
|
|
|
|
|
|
|
|
|
scan_replacements();
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create new orders
|
|
|
|
|
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) {
|
|
|
|
|
if (!Core::getInstance().isWorldLoaded()) {
|
|
|
|
|
out.printerr("Cannot enable %s without a loaded world.\n", plugin_name);
|
|
|
|
|
return CR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
create_orders();
|
|
|
|
|
if (enable != is_enabled) {
|
|
|
|
|
is_enabled = enable;
|
|
|
|
|
DEBUG(config,out).print("%s from the API; persisting\n",
|
|
|
|
|
is_enabled ? "enabled" : "disabled");
|
|
|
|
|
set_config_bool(config, CONFIG_IS_ENABLED, is_enabled);
|
|
|
|
|
if (enable)
|
|
|
|
|
do_cycle(out);
|
|
|
|
|
} else {
|
|
|
|
|
DEBUG(config,out).print("%s from the API, but already %s; no action\n",
|
|
|
|
|
is_enabled ? "enabled" : "disabled",
|
|
|
|
|
is_enabled ? "enabled" : "disabled");
|
|
|
|
|
}
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scan existing orders and subtract
|
|
|
|
|
DFhackCExport command_result plugin_shutdown (color_ostream &out) {
|
|
|
|
|
DEBUG(config,out).print("shutting down %s\n", plugin_name);
|
|
|
|
|
|
|
|
|
|
scan_existing_orders();
|
|
|
|
|
tailor_instance.release();
|
|
|
|
|
|
|
|
|
|
// place orders
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
place_orders();
|
|
|
|
|
static void set_material_order() {
|
|
|
|
|
material_order.clear();
|
|
|
|
|
for (size_t i = 0; i < all_materials.size(); ++i) {
|
|
|
|
|
if (i == (size_t)get_config_val(config, CONFIG_SILK_IDX))
|
|
|
|
|
material_order.push_back(M_SILK);
|
|
|
|
|
else if (i == (size_t)get_config_val(config, CONFIG_CLOTH_IDX))
|
|
|
|
|
material_order.push_back(M_CLOTH);
|
|
|
|
|
else if (i == (size_t)get_config_val(config, CONFIG_YARN_IDX))
|
|
|
|
|
material_order.push_back(M_YARN);
|
|
|
|
|
else if (i == (size_t)get_config_val(config, CONFIG_LEATHER_IDX))
|
|
|
|
|
material_order.push_back(M_LEATHER);
|
|
|
|
|
}
|
|
|
|
|
if (!material_order.size())
|
|
|
|
|
std::copy(all_materials.begin(), all_materials.end(), std::back_inserter(material_order));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
command_result set_materials(color_ostream& out, std::vector<std::string>& parameters)
|
|
|
|
|
{
|
|
|
|
|
std::list<MatType> 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())
|
|
|
|
|
{
|
|
|
|
|
WARN(config,out).print("tailor: material %s not recognized\n", m->c_str());
|
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
newmat.push_back(*mm);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
material_order = newmat;
|
|
|
|
|
INFO(config,out).print("tailor: material list set to %s\n", get_material_list().c_str());
|
|
|
|
|
DFhackCExport command_result plugin_load_data (color_ostream &out) {
|
|
|
|
|
cycle_timestamp = 0;
|
|
|
|
|
config = World::GetPersistentData(CONFIG_KEY);
|
|
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
|
if (!config.isValid()) {
|
|
|
|
|
DEBUG(config,out).print("no config found in this save; initializing\n");
|
|
|
|
|
config = World::AddPersistentData(CONFIG_KEY);
|
|
|
|
|
set_config_bool(config, CONFIG_IS_ENABLED, is_enabled);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
std::string get_material_list()
|
|
|
|
|
{
|
|
|
|
|
std::string s;
|
|
|
|
|
for (const auto& m : material_order)
|
|
|
|
|
{
|
|
|
|
|
if (!s.empty()) s += ", ";
|
|
|
|
|
s += m.name;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
is_enabled = get_config_bool(config, CONFIG_IS_ENABLED);
|
|
|
|
|
DEBUG(config,out).print("loading persisted enabled state: %s\n",
|
|
|
|
|
is_enabled ? "true" : "false");
|
|
|
|
|
set_material_order();
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
void process(color_ostream& out)
|
|
|
|
|
{
|
|
|
|
|
bool found = false;
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) {
|
|
|
|
|
if (event == DFHack::SC_WORLD_UNLOADED) {
|
|
|
|
|
if (is_enabled) {
|
|
|
|
|
DEBUG(config,out).print("world unloaded; disabling %s\n",
|
|
|
|
|
plugin_name);
|
|
|
|
|
is_enabled = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
|
{
|
|
|
|
|
do_scan(out);
|
|
|
|
|
}
|
|
|
|
|
DFhackCExport command_result plugin_onupdate(color_ostream &out) {
|
|
|
|
|
if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) {
|
|
|
|
|
int ordered = do_cycle(out);
|
|
|
|
|
if (0 < ordered)
|
|
|
|
|
out.print("tailor: ordered %d items of clothing\n", ordered);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::unique_ptr<Tailor> tailor_instance;
|
|
|
|
|
static bool call_tailor_lua(color_ostream *out, const char *fn_name,
|
|
|
|
|
int nargs = 0, int nres = 0,
|
|
|
|
|
Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA,
|
|
|
|
|
Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) {
|
|
|
|
|
DEBUG(config).print("calling tailor lua function: '%s'\n", fn_name);
|
|
|
|
|
|
|
|
|
|
#define DELTA_TICKS 50
|
|
|
|
|
CoreSuspender guard;
|
|
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_onupdate(color_ostream& out)
|
|
|
|
|
{
|
|
|
|
|
if (!enabled || !tailor_instance)
|
|
|
|
|
return CR_OK;
|
|
|
|
|
auto L = Lua::Core::State;
|
|
|
|
|
Lua::StackUnwinder top(L);
|
|
|
|
|
|
|
|
|
|
if (!Maps::IsValid())
|
|
|
|
|
return CR_OK;
|
|
|
|
|
if (!out)
|
|
|
|
|
out = &Core::getInstance().getConsole();
|
|
|
|
|
|
|
|
|
|
if (DFHack::World::ReadPauseState())
|
|
|
|
|
return CR_OK;
|
|
|
|
|
return Lua::CallLuaModuleFunction(*out, L, "plugins.tailor", fn_name,
|
|
|
|
|
nargs, nres,
|
|
|
|
|
std::forward<Lua::LuaLambda&&>(args_lambda),
|
|
|
|
|
std::forward<Lua::LuaLambda&&>(res_lambda));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (world->frame_counter % DELTA_TICKS != 0)
|
|
|
|
|
return CR_OK;
|
|
|
|
|
static command_result do_command(color_ostream &out, vector<string> ¶meters) {
|
|
|
|
|
CoreSuspender suspend;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
CoreSuspender suspend;
|
|
|
|
|
tailor_instance->process(out);
|
|
|
|
|
if (!Core::getInstance().isWorldLoaded()) {
|
|
|
|
|
out.printerr("Cannot run %s without a loaded world.\n", plugin_name);
|
|
|
|
|
return CR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
|
bool show_help = false;
|
|
|
|
|
if (!call_tailor_lua(&out, "parse_commandline", parameters.size(), 1,
|
|
|
|
|
[&](lua_State *L) {
|
|
|
|
|
for (const string ¶m : parameters)
|
|
|
|
|
Lua::Push(L, param);
|
|
|
|
|
},
|
|
|
|
|
[&](lua_State *L) {
|
|
|
|
|
show_help = !lua_toboolean(L, -1);
|
|
|
|
|
})) {
|
|
|
|
|
return CR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return show_help ? CR_WRONG_USAGE : CR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static command_result tailor_cmd(color_ostream& out, std::vector <std::string>& parameters) {
|
|
|
|
|
bool desired = enabled;
|
|
|
|
|
if (parameters.size() == 1 && (parameters[0] == "enable" || parameters[0] == "on" || parameters[0] == "1"))
|
|
|
|
|
{
|
|
|
|
|
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] == "?"))
|
|
|
|
|
{
|
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
|
}
|
|
|
|
|
else if (parameters.size() == 1 && parameters[0] == "test")
|
|
|
|
|
{
|
|
|
|
|
if (tailor_instance)
|
|
|
|
|
{
|
|
|
|
|
tailor_instance->do_scan(out);
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
out.print("%s: not instantiated\n", plugin_name);
|
|
|
|
|
return CR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (parameters.size() > 1 && parameters[0] == "materials")
|
|
|
|
|
{
|
|
|
|
|
if (tailor_instance)
|
|
|
|
|
{
|
|
|
|
|
return tailor_instance->set_materials(out, parameters);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
out.print("%s: not instantiated\n", plugin_name);
|
|
|
|
|
return CR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (parameters.size() == 1 && parameters[0] != "status")
|
|
|
|
|
{
|
|
|
|
|
return CR_WRONG_USAGE;
|
|
|
|
|
}
|
|
|
|
|
/////////////////////////////////////////////////////
|
|
|
|
|
// cycle logic
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
static int do_cycle(color_ostream &out) {
|
|
|
|
|
// mark that we have recently run
|
|
|
|
|
cycle_timestamp = world->frame_counter;
|
|
|
|
|
|
|
|
|
|
enabled = desired;
|
|
|
|
|
DEBUG(cycle,out).print("running %s cycle\n", plugin_name);
|
|
|
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
|
tailor_instance->reset();
|
|
|
|
|
tailor_instance->scan_clothing();
|
|
|
|
|
tailor_instance->scan_materials();
|
|
|
|
|
tailor_instance->scan_replacements();
|
|
|
|
|
tailor_instance->create_orders();
|
|
|
|
|
tailor_instance->scan_existing_orders();
|
|
|
|
|
return tailor_instance->place_orders();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////
|
|
|
|
|
// Lua API
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event event)
|
|
|
|
|
{
|
|
|
|
|
return CR_OK;
|
|
|
|
|
static void tailor_doCycle(color_ostream &out) {
|
|
|
|
|
DEBUG(config,out).print("entering tailor_doCycle\n");
|
|
|
|
|
out.print("ordered %d items of clothing\n", do_cycle(out));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable)
|
|
|
|
|
{
|
|
|
|
|
enabled = enable;
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|
// remember, these are ONE-based indices from Lua
|
|
|
|
|
static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx,
|
|
|
|
|
int32_t clothIdx, int32_t yarnIdx, int32_t leatherIdx) {
|
|
|
|
|
DEBUG(config,out).print("entering tailor_setMaterialPreferences\n");
|
|
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_init(color_ostream& out, std::vector <PluginCommand>& commands)
|
|
|
|
|
{
|
|
|
|
|
tailor_instance = std::move(dts::make_unique<Tailor>());
|
|
|
|
|
// it doesn't really matter if these are invalid. set_material_order will do
|
|
|
|
|
// the right thing.
|
|
|
|
|
set_config_val(config, CONFIG_SILK_IDX, silkIdx - 1);
|
|
|
|
|
set_config_val(config, CONFIG_CLOTH_IDX, clothIdx - 1);
|
|
|
|
|
set_config_val(config, CONFIG_YARN_IDX, yarnIdx - 1);
|
|
|
|
|
set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx - 1);
|
|
|
|
|
|
|
|
|
|
if (AUTOENABLE) {
|
|
|
|
|
enabled = true;
|
|
|
|
|
}
|
|
|
|
|
set_material_order();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commands.push_back(PluginCommand(
|
|
|
|
|
plugin_name,
|
|
|
|
|
"Automatically keep your dwarves in fresh clothing.",
|
|
|
|
|
tailor_cmd));
|
|
|
|
|
return CR_OK;
|
|
|
|
|
static int tailor_getMaterialPreferences(lua_State *L) {
|
|
|
|
|
color_ostream *out = Lua::GetOutput(L);
|
|
|
|
|
if (!out)
|
|
|
|
|
out = &Core::getInstance().getConsole();
|
|
|
|
|
DEBUG(config,*out).print("entering tailor_getMaterialPreferences\n");
|
|
|
|
|
vector<string> names;
|
|
|
|
|
for (const auto& m : material_order)
|
|
|
|
|
names.emplace_back(m.name);
|
|
|
|
|
Lua::PushVector(L, names);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DFhackCExport command_result plugin_shutdown(color_ostream& out)
|
|
|
|
|
{
|
|
|
|
|
tailor_instance.release();
|
|
|
|
|
DFHACK_PLUGIN_LUA_FUNCTIONS {
|
|
|
|
|
DFHACK_LUA_FUNCTION(tailor_doCycle),
|
|
|
|
|
DFHACK_LUA_FUNCTION(tailor_setMaterialPreferences),
|
|
|
|
|
DFHACK_LUA_END
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return plugin_enable(out, false);
|
|
|
|
|
}
|
|
|
|
|
DFHACK_PLUGIN_LUA_COMMANDS {
|
|
|
|
|
DFHACK_LUA_COMMAND(tailor_getMaterialPreferences),
|
|
|
|
|
DFHACK_LUA_END
|
|
|
|
|
};
|
|
|
|
|