Merge pull request #2843 from myk002/myk_tailor

[tailor] persist state, use best practices
develop
Myk 2023-02-08 14:22:23 -08:00 committed by GitHub
commit 8c046b070d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 350 additions and 264 deletions

@ -5,16 +5,15 @@ tailor
:summary: Automatically keep your dwarves in fresh clothing. :summary: Automatically keep your dwarves in fresh clothing.
:tags: fort auto workorders :tags: fort auto workorders
Whenever the bookkeeper updates stockpile records, this plugin will scan the Once a day, this plugin will scan the clothing situation in the fort. If there
fort. If there are fresh cloths available, dwarves who are wearing tattered are fresh cloths available, dwarves who are wearing tattered clothing will have
clothing will have their rags confiscated (in the same manner as the their rags confiscated (in the same manner as the `cleanowned` tool) so that
`cleanowned` tool) so that they'll reequip with replacement clothes. they'll reequip with replacement clothes.
If there are not enough clothes available, manager orders will be generated If there are not enough clothes available, manager orders will be generated to
to manufacture some more. ``tailor`` will intelligently create orders using manufacture some more. ``tailor`` will intelligently create orders using raw
raw materials that you have on hand in the fort. For example, if you have materials that you have on hand in the fort. For example, if you have lots of
lots of silk, but no cloth, then ``tailor`` will order only silk clothing to silk, but no cloth, then ``tailor`` will order only silk clothing to be made.
be made.
Usage Usage
----- -----
@ -22,7 +21,8 @@ Usage
:: ::
enable tailor enable tailor
tailor status tailor [status]
tailor now
tailor materials <material> [<material> ...] tailor materials <material> [<material> ...]
By default, ``tailor`` will prefer using materials in this order:: By default, ``tailor`` will prefer using materials in this order::
@ -32,12 +32,16 @@ By default, ``tailor`` will prefer using materials in this order::
but you can use the ``tailor materials`` command to restrict which materials but you can use the ``tailor materials`` command to restrict which materials
are used, and in what order. are used, and in what order.
Example Examples
------- --------
``enable tailor`` ``enable tailor``
Start replacing tattered clothes with default settings. Start replacing tattered clothes with default settings.
``tailor now``
Run a scan and order cycle right now, regardless of whether the plugin is
enabled.
``tailor materials silk cloth yarn`` ``tailor materials silk cloth yarn``
Restrict the materials used for automatically manufacturing clothing to Restrict the materials used for automatically manufacturing clothing to
silk, cloth, and yarn, preferred in that order. This saves leather for silk, cloth, and yarn, preferred in that order. This saves leather for

@ -1 +1 @@
Subproject commit cd5baf4ea82fead3883368da526ac2b606a6209b Subproject commit eb58cda0f106c20776c273516f010ce21c45f89d

@ -159,7 +159,7 @@ dfhack_plugin(showmood showmood.cpp)
#add_subdirectory(stockpiles) #add_subdirectory(stockpiles)
#dfhack_plugin(stocks stocks.cpp) #dfhack_plugin(stocks stocks.cpp)
#dfhack_plugin(strangemood strangemood.cpp) #dfhack_plugin(strangemood strangemood.cpp)
dfhack_plugin(tailor tailor.cpp) dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua)
dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua)
#dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(title-folder title-folder.cpp)
#dfhack_plugin(title-version title-version.cpp) #dfhack_plugin(title-version title-version.cpp)

@ -0,0 +1,56 @@
local _ENV = mkmodule('plugins.tailor')
local argparse = require('argparse')
local utils = require('utils')
local function process_args(opts, args)
if args[1] == 'help' then
opts.help = true
return
end
return argparse.processArgsGetopt(args, {
{'h', 'help', handler=function() opts.help = true end},
})
end
function status()
print(('tailor is %s'):format(isEnabled() and "enabled" or "disabled"))
print('materials preference order:')
for _,name in ipairs(tailor_getMaterialPreferences()) do
print((' %s'):format(name))
end
end
function setMaterials(names)
local idxs = utils.invert(names)
tailor_setMaterialPreferences(
idxs.silk or -1,
idxs.cloth or -1,
idxs.yarn or -1,
idxs.leather or -1)
end
function parse_commandline(...)
local args, opts = {...}, {}
local positionals = process_args(opts, args)
if opts.help then
return false
end
local command = table.remove(positionals, 1)
if not command or command == 'status' then
status()
elseif command == 'now' then
tailor_doCycle()
elseif command == 'materials' then
setMaterials(positionals)
else
return false
end
return true
end
return _ENV

@ -1,101 +1,111 @@
/* /*
* Tailor plugin. Automatically manages keeping your dorfs clothed. * 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 <string>
#include "DataDefs.h" #include <vector>
#include "Debug.h"
#include "PluginManager.h"
#include "df/creature_raw.h" #include "df/creature_raw.h"
#include "df/global_objects.h"
#include "df/historical_entity.h" #include "df/historical_entity.h"
#include "df/item.h"
#include "df/item_flags.h"
#include "df/itemdef_armorst.h" #include "df/itemdef_armorst.h"
#include "df/itemdef_glovesst.h" #include "df/itemdef_glovesst.h"
#include "df/itemdef_helmst.h" #include "df/itemdef_helmst.h"
#include "df/itemdef_pantsst.h" #include "df/itemdef_pantsst.h"
#include "df/itemdef_shoesst.h" #include "df/itemdef_shoesst.h"
#include "df/items_other_id.h" #include "df/items_other_id.h"
#include "df/job.h"
#include "df/job_type.h"
#include "df/manager_order.h" #include "df/manager_order.h"
#include "df/plotinfost.h" #include "df/plotinfost.h"
#include "df/world.h" #include "df/world.h"
#include "modules/Maps.h" #include "Core.h"
#include "modules/Units.h" #include "Debug.h"
#include "LuaTools.h"
#include "PluginManager.h"
#include "modules/Materials.h"
#include "modules/Persistence.h"
#include "modules/Translation.h" #include "modules/Translation.h"
#include "modules/Units.h"
#include "modules/World.h" #include "modules/World.h"
using namespace DFHack; using std::string;
using std::vector;
using df::global::world; using namespace DFHack;
using df::global::plotinfo;
DFHACK_PLUGIN("tailor"); 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(plotinfo);
REQUIRE_GLOBAL(standing_orders_use_dyed_cloth); REQUIRE_GLOBAL(standing_orders_use_dyed_cloth);
REQUIRE_GLOBAL(world);
namespace DFHack { namespace DFHack {
DBG_DECLARE(tailor, cycle, DebugCategory::LINFO); DBG_DECLARE(tailor, cycle, DebugCategory::LINFO);
DBG_DECLARE(tailor, config, DebugCategory::LINFO); DBG_DECLARE(tailor, config, DebugCategory::LINFO);
} }
class Tailor { static const string CONFIG_KEY = string(plugin_name) + "/config";
// ARMOR, SHOES, HELM, GLOVES, PANTS 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);
}
static const int32_t CYCLE_TICKS = 1200; // one day
static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle
const std::map<df::job_type, df::item_type> jobTypeMap = { // 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::MakeArmor, df::item_type::ARMOR },
{ df::job_type::MakePants, df::item_type::PANTS }, { df::job_type::MakePants, df::item_type::PANTS },
{ df::job_type::MakeHelm, df::item_type::HELM }, { df::job_type::MakeHelm, df::item_type::HELM },
{ df::job_type::MakeGloves, df::item_type::GLOVES }, { df::job_type::MakeGloves, df::item_type::GLOVES },
{ df::job_type::MakeShoes, df::item_type::SHOES } { df::job_type::MakeShoes, df::item_type::SHOES }
}; };
const std::map<df::item_type, df::job_type> itemTypeMap = { static const std::map<df::item_type, df::job_type> itemTypeMap = {
{ df::item_type::ARMOR, df::job_type::MakeArmor }, { df::item_type::ARMOR, df::job_type::MakeArmor },
{ df::item_type::PANTS, df::job_type::MakePants }, { df::item_type::PANTS, df::job_type::MakePants },
{ df::item_type::HELM, df::job_type::MakeHelm }, { df::item_type::HELM, df::job_type::MakeHelm },
{ df::item_type::GLOVES, df::job_type::MakeGloves }, { df::item_type::GLOVES, df::job_type::MakeGloves },
{ df::item_type::SHOES, df::job_type::MakeShoes } { 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: class MatType {
std::string name; public:
df::job_material_category job_material; const std::string name;
df::armor_general_flags armor_flag; const df::job_material_category job_material;
const df::armor_general_flags armor_flag;
bool operator==(const MatType& m) const bool operator==(const MatType& m) const {
{
return name == m.name; return name == m.name;
} }
// operator< is required to use this as a std::map key // operator< is required to use this as a std::map key
bool operator<(const MatType& m) const bool operator<(const MatType& m) const {
{
return name < m.name; return name < m.name;
} }
@ -103,34 +113,48 @@ private:
: name(n), job_material(jm), armor_flag(af) {}; : name(n), job_material(jm), armor_flag(af) {};
MatType(const char* n, df::job_material_category jm, df::armor_general_flags 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) {}; : name(std::string(n)), job_material(jm), armor_flag(af) {};
};
}; static const MatType
const MatType
M_SILK = MatType("silk", df::job_material_category::mask_silk, df::armor_general_flags::SOFT), 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_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_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); M_LEATHER = MatType("leather", df::job_material_category::mask_leather, df::armor_general_flags::LEATHER);
std::list<MatType> all_materials = { M_SILK, M_CLOTH, M_YARN, M_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> 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> needed; // same
std::map<std::pair<df::item_type, int>, int> queued; // 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<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::tuple<df::job_type, int, int>, int> orders; // key is item type, item subtype, size
std::map<MatType, int> supply; 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; std::map<MatType, int> reserves;
int default_reserve = 10; int default_reserve = 10;
public:
void reset() void reset()
{ {
available.clear(); available.clear();
@ -145,9 +169,7 @@ private:
{ {
for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing"
{ {
if (i->flags.whole & bad_flags.whole) if (i->flags.whole & badFlags.whole)
continue;
if (i->flags.bits.owned)
continue; continue;
if (i->getWear() >= 1) if (i->getWear() >= 1)
continue; continue;
@ -164,7 +186,7 @@ private:
for (auto i : world->items.other[df::items_other_id::CLOTH]) 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; continue;
if (require_dyed && !i->hasImprovements()) if (require_dyed && !i->hasImprovements())
@ -197,7 +219,7 @@ private:
for (auto i : world->items.other[df::items_other_id::SKIN_TANNED]) 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; continue;
supply[M_LEATHER] += i->getStackSize(); 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]; auto entity = world->entities.all[plotinfo->civ_id];
for (auto& o : orders) for (auto& o : orders)
@ -477,6 +500,7 @@ private:
); );
count -= c; count -= c;
ordered += c;
} }
else else
{ {
@ -486,215 +510,217 @@ private:
} }
} }
} }
return ordered;
} }
};
public: static std::unique_ptr<Tailor> tailor_instance;
void do_scan(color_ostream& o)
{
out = &o;
reset();
// scan for useable clothing
scan_clothing(); static command_result do_command(color_ostream &out, vector<string> &parameters);
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: DFhackCExport command_result plugin_load_data (color_ostream &out) {
command_result set_materials(color_ostream& out, std::vector<std::string>& parameters) cycle_timestamp = 0;
{ config = World::GetPersistentData(CONFIG_KEY);
std::list<MatType> newmat;
newmat.clear();
for (auto m = parameters.begin() + 1; m != parameters.end(); m++) if (!config.isValid()) {
{ DEBUG(config,out).print("no config found in this save; initializing\n");
auto nameMatch = [m](MatType& m1) { return *m == m1.name; }; config = World::AddPersistentData(CONFIG_KEY);
auto mm = std::find_if(all_materials.begin(), all_materials.end(), nameMatch); set_config_bool(config, CONFIG_IS_ENABLED, is_enabled);
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; is_enabled = get_config_bool(config, CONFIG_IS_ENABLED);
INFO(config,out).print("tailor: material list set to %s\n", get_material_list().c_str()); DEBUG(config,out).print("loading persisted enabled state: %s\n",
is_enabled ? "true" : "false");
set_material_order();
return CR_OK; 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) DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) {
{ if (event == DFHack::SC_WORLD_UNLOADED) {
if (link->item == NULL) continue; if (is_enabled) {
if (link->item->job_type == df::enums::job_type::UpdateStockpileRecords) DEBUG(config,out).print("world unloaded; disabling %s\n",
{ plugin_name);
found = true; is_enabled = false;
break;
} }
} }
return CR_OK;
}
if (found) DFhackCExport command_result plugin_onupdate(color_ostream &out) {
{ if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) {
do_scan(out); int ordered = do_cycle(out);
if (0 < ordered)
out.print("tailor: ordered %d items of clothing\n", ordered);
} }
}
};
static std::unique_ptr<Tailor> tailor_instance;
#define DELTA_TICKS 50
DFhackCExport command_result plugin_onupdate(color_ostream& out)
{
if (!enabled || !tailor_instance)
return CR_OK; return CR_OK;
}
if (!Maps::IsValid()) static bool call_tailor_lua(color_ostream *out, const char *fn_name,
return CR_OK; 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);
if (DFHack::World::ReadPauseState()) CoreSuspender guard;
return CR_OK;
if (world->frame_counter % DELTA_TICKS != 0) auto L = Lua::Core::State;
return CR_OK; Lua::StackUnwinder top(L);
{ if (!out)
CoreSuspender suspend; out = &Core::getInstance().getConsole();
tailor_instance->process(out);
}
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));
} }
static command_result tailor_cmd(color_ostream& out, std::vector <std::string>& parameters) { static command_result do_command(color_ostream &out, vector<string> &parameters) {
bool desired = enabled; CoreSuspender suspend;
if (parameters.size() == 1 && (parameters[0] == "enable" || parameters[0] == "on" || parameters[0] == "1"))
{ if (!Core::getInstance().isWorldLoaded()) {
desired = true; out.printerr("Cannot run %s without a loaded world.\n", plugin_name);
}
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; return CR_FAILURE;
} }
}
else if (parameters.size() > 1 && parameters[0] == "materials") bool show_help = false;
{ if (!call_tailor_lua(&out, "parse_commandline", parameters.size(), 1,
if (tailor_instance) [&](lua_State *L) {
{ for (const string &param : parameters)
return tailor_instance->set_materials(out, parameters); Lua::Push(L, param);
} },
else [&](lua_State *L) {
{ show_help = !lua_toboolean(L, -1);
out.print("%s: not instantiated\n", plugin_name); })) {
return CR_FAILURE; return CR_FAILURE;
} }
}
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"); return show_help ? CR_WRONG_USAGE : CR_OK;
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);
}
enabled = desired; /////////////////////////////////////////////////////
// cycle logic
//
return CR_OK; static int do_cycle(color_ostream &out) {
} // mark that we have recently run
cycle_timestamp = world->frame_counter;
DEBUG(cycle,out).print("running %s cycle\n", plugin_name);
DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event event) tailor_instance->reset();
{ tailor_instance->scan_clothing();
return CR_OK; tailor_instance->scan_materials();
tailor_instance->scan_replacements();
tailor_instance->create_orders();
tailor_instance->scan_existing_orders();
return tailor_instance->place_orders();
} }
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) /////////////////////////////////////////////////////
{ // Lua API
enabled = enable; //
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_init(color_ostream& out, std::vector <PluginCommand>& commands) // remember, these are ONE-based indices from Lua
{ static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx,
tailor_instance = std::move(dts::make_unique<Tailor>()); int32_t clothIdx, int32_t yarnIdx, int32_t leatherIdx) {
DEBUG(config,out).print("entering tailor_setMaterialPreferences\n");
if (AUTOENABLE) { // it doesn't really matter if these are invalid. set_material_order will do
enabled = true; // 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);
commands.push_back(PluginCommand( set_material_order();
plugin_name,
"Automatically keep your dwarves in fresh clothing.",
tailor_cmd));
return CR_OK;
} }
DFhackCExport command_result plugin_shutdown(color_ostream& out) static int tailor_getMaterialPreferences(lua_State *L) {
{ color_ostream *out = Lua::GetOutput(L);
tailor_instance.release(); if (!out)
out = &Core::getInstance().getConsole();
return plugin_enable(out, false); 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;
} }
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(tailor_doCycle),
DFHACK_LUA_FUNCTION(tailor_setMaterialPreferences),
DFHACK_LUA_END
};
DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(tailor_getMaterialPreferences),
DFHACK_LUA_END
};