Save state in persistent data.

develop
JapaMala 2019-04-27 21:01:12 -05:00
parent 6bed392331
commit 42226342dc
1 changed files with 184 additions and 32 deletions

@ -30,6 +30,34 @@ using namespace DFHack::Items;
using namespace DFHack::Units; 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");
// 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);
// Only run if this is enabled
DFHACK_PLUGIN_IS_ENABLED(autoclothing_enabled);
// Here go all the command declarations...
// mostly to allow having the mandatory stuff on top of the file and commands on the bottom
struct ClothingRequirement;
command_result autoclothing(color_ostream &out, std::vector <std::string> & parameters);
static void init_state(color_ostream &out);
static void save_state(color_ostream &out);
static void cleanup_state(color_ostream &out);
static void do_autoclothing();
static bool validateMaterialCategory(ClothingRequirement * requirement);
static bool setItem(std::string name, ClothingRequirement* requirement);
std::vector<ClothingRequirement>clothingOrders;
struct ClothingRequirement struct ClothingRequirement
{ {
df::job_type job_type; df::job_type job_type;
@ -51,29 +79,87 @@ struct ClothingRequirement
return false; return false;
return true; return true;
} }
};
std::vector<ClothingRequirement>clothingOrders; std::string Serialize()
{
stringstream stream;
stream << job_type << " ";
stream << item_type << " ";
stream << item_subtype << " ";
stream << material_category.whole << " ";
stream << needed_per_citizen;
return stream.str();
}
// A plugin must be able to return its name and version. void Deserialize(std::string s)
// The name string provided must correspond to the filename - {
// skeleton.plug.so, skeleton.plug.dylib, or skeleton.plug.dll in this case stringstream stream(s);
DFHACK_PLUGIN("autoclothing"); stream >> (int16_t&)job_type;
stream >> (int16_t&)item_type;
stream >> item_subtype;
stream >> material_category.whole;
stream >> needed_per_citizen;
}
// Any globals a plugin requires (e.g. world) should be listed here. bool SetFromParameters(color_ostream &out, std::vector <std::string> & parameters)
// 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): if (!set_bitfield_field(&material_category, parameters[0], 1))
// {
REQUIRE_GLOBAL(world); out << "Unrecognized material type: " << parameters[0] << endl;
}
if (!setItem(parameters[1], this))
{
out << "Unrecognized item name or token: " << parameters[1] << endl;
return false;
}
if (!validateMaterialCategory(this))
{
out << parameters[0] << " is not a valid material category for " << parameters[1] << endl;
return false;
}
return true;
}
// Only run if this is enabled std::string ToReadableLabel()
DFHACK_PLUGIN_IS_ENABLED(autoclothing_enabled); {
stringstream stream;
stream << bitfield_to_string(material_category) << " ";
std::string adjective = "";
std::string name = "";
switch (item_type)
{
case df::enums::item_type::ARMOR:
adjective = world->raws.itemdefs.armor[item_subtype]->adjective;
name = world->raws.itemdefs.armor[item_subtype]->name;
break;
case df::enums::item_type::SHOES:
adjective = world->raws.itemdefs.shoes[item_subtype]->adjective;
name = world->raws.itemdefs.shoes[item_subtype]->name;
break;
case df::enums::item_type::HELM:
adjective = world->raws.itemdefs.helms[item_subtype]->adjective;
name = world->raws.itemdefs.helms[item_subtype]->name;
break;
case df::enums::item_type::GLOVES:
adjective = world->raws.itemdefs.gloves[item_subtype]->adjective;
name = world->raws.itemdefs.gloves[item_subtype]->name;
break;
case df::enums::item_type::PANTS:
adjective = world->raws.itemdefs.pants[item_subtype]->adjective;
name = world->raws.itemdefs.pants[item_subtype]->name;
break;
default:
break;
}
if (!adjective.empty())
stream << adjective << " ";
stream << name << " ";
stream << needed_per_citizen;
// Here go all the command declarations... return stream.str();
// mostly to allow having the mandatory stuff on top of the file and commands on the bottom }
command_result autoclothing(color_ostream &out, std::vector <std::string> & parameters); };
static void do_autoclothing();
// 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, std::vector <PluginCommand> &commands)
@ -99,6 +185,8 @@ DFhackCExport command_result plugin_shutdown(color_ostream &out)
// You *MUST* kill all threads you created before this returns. // You *MUST* kill all threads you created before this returns.
// If everything fails, just return CR_FAILURE. Your plugin will be // If everything fails, just return CR_FAILURE. Your plugin will be
// in a zombie state, but things won't crash. // in a zombie state, but things won't crash.
cleanup_state(out);
return CR_OK; return CR_OK;
} }
@ -110,10 +198,10 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
{ {
switch (event) { switch (event) {
case SC_WORLD_LOADED: case SC_WORLD_LOADED:
// initialize from the world just loaded init_state(out);
break; break;
case SC_WORLD_UNLOADED: case SC_WORLD_UNLOADED:
// cleanup cleanup_state(out);
break; break;
default: default:
break; break;
@ -247,6 +335,8 @@ 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, std::vector <std::string> & parameters)
{ {
@ -268,20 +358,8 @@ command_result autoclothing(color_ostream &out, std::vector <std::string> & para
CoreSuspender suspend; CoreSuspender suspend;
// Actually do something here. Yay. // Actually do something here. Yay.
ClothingRequirement newRequirement; ClothingRequirement newRequirement;
if (!set_bitfield_field(&newRequirement.material_category, parameters[0], 1)) if (!newRequirement.SetFromParameters(out, parameters))
{
out << "Unrecognized material type: " << parameters[0] << endl;
}
if (!setItem(parameters[1], &newRequirement))
{
out << "Unrecognized item name or token: " << parameters[1] << endl;
return CR_WRONG_USAGE;
}
if (!validateMaterialCategory(&newRequirement))
{
out << parameters[0] << " is not a valid material category for " << parameters[1] << endl;
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
}
//all checks are passed. Now we either show or set the amount. //all checks are passed. Now we either show or set the amount.
bool settingSize = false; bool settingSize = false;
bool matchedExisting = false; bool matchedExisting = false;
@ -298,6 +376,7 @@ command_result autoclothing(color_ostream &out, std::vector <std::string> & para
} }
settingSize = true; settingSize = true;
} }
for (size_t i = 0; i < clothingOrders.size(); i++) for (size_t i = 0; i < clothingOrders.size(); i++)
{ {
if (!clothingOrders[i].matches(&newRequirement)) if (!clothingOrders[i].matches(&newRequirement))
@ -350,6 +429,8 @@ command_result autoclothing(color_ostream &out, std::vector <std::string> & para
} }
do_autoclothing(); do_autoclothing();
} }
save_state(out);
// Give control back to DF. // Give control back to DF.
return CR_OK; return CR_OK;
} }
@ -489,3 +570,74 @@ static void do_autoclothing()
//Finally loop through the clothing orders to find ones that need more made. //Finally loop through the clothing orders to find ones that need more made.
add_clothing_orders(); add_clothing_orders();
} }
static void cleanup_state(color_ostream &out)
{
clothingOrders.clear();
autoclothing_enabled = false;
}
static void init_state(color_ostream &out)
{
auto enabled = World::GetPersistentData("autoclothing/enabled");
if (enabled.isValid() && enabled.ival(0) == 1)
{
out << "autoclothing enabled" << endl;
autoclothing_enabled = true;
}
else
{
autoclothing_enabled = false;
}
// Parse constraints
std::vector<PersistentDataItem> items;
World::GetPersistentData(&items, "autoclothing/clothingItems");
for (auto& item : items)
{
if (!item.isValid())
continue;
ClothingRequirement req;
req.Deserialize(item.val());
clothingOrders.push_back(req);
out << "autoclothing added " << req.ToReadableLabel() << endl;
}
}
static void save_state(color_ostream &out)
{
auto enabled = World::GetPersistentData("autoclothing/enabled");
if (!enabled.isValid())
enabled = World::AddPersistentData("autoclothing/enabled");
enabled.ival(0) = autoclothing_enabled;
for (auto& order : clothingOrders)
{
auto orderSave = World::AddPersistentData("autoclothing/clothingItems");
orderSave.val() = order.Serialize();
}
// Parse constraints
std::vector<PersistentDataItem> items;
World::GetPersistentData(&items, "autoclothing/clothingItems");
for (int i = 0; i < items.size(); i++)
{
if (i < clothingOrders.size())
{
items[i].val() = clothingOrders[i].Serialize();
}
else
{
World::DeletePersistentData(items[i]);
}
}
for (int i = items.size(); i < clothingOrders.size(); i++)
{
auto item = World::AddPersistentData("autoclothing/clothingItems");
item.val() = clothingOrders[i].Serialize();
}
}