Merge pull request #2773 from myk002/myk_nestboxes

[nestboxes] update and simplify; persist state
develop
Myk 2023-02-01 17:50:07 -08:00 committed by GitHub
commit c312a88bea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 118 additions and 84 deletions

@ -47,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- A new cross-compile build script was added for building DFHack for Windows from a Linux Docker builder (see the `compile` instructions in the docs) - A new cross-compile build script was added for building DFHack for Windows from a Linux Docker builder (see the `compile` instructions in the docs)
- You can now configure whether DFHack tool windows should pause the game by default - You can now configure whether DFHack tool windows should pause the game by default
- `hotkeys`: clicking on the DFHack logo no longer closes the popup menu - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu
- `nestboxes`: now saves enabled state in your savegame
- `gui/launcher`: sped up initialization time for faster load of the UI - `gui/launcher`: sped up initialization time for faster load of the UI
- `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open - `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open
- `gui/quickcmd`: now has its own global keybinding for your convenience: Ctrl-Shift-A - `gui/quickcmd`: now has its own global keybinding for your convenience: Ctrl-Shift-A

@ -1,120 +1,153 @@
#include "Core.h" #include "Debug.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "DataDefs.h" #include "modules/Persistence.h"
#include "modules/World.h"
#include "df/world.h" #include "df/world.h"
#include "df/plotinfost.h"
#include "df/building_nest_boxst.h" #include "df/building_nest_boxst.h"
#include "df/building_type.h"
#include "df/buildings_other_id.h"
#include "df/global_objects.h"
#include "df/item.h" #include "df/item.h"
#include "df/unit.h" #include "df/unit.h"
#include "df/building.h"
#include "df/items_other_id.h"
#include "df/creature_raw.h"
#include "modules/MapCache.h"
#include "modules/Items.h"
using std::vector;
using std::string; using std::string;
using std::endl;
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
using df::global::world; DFHACK_PLUGIN("nestboxes");
using df::global::plotinfo; DFHACK_PLUGIN_IS_ENABLED(is_enabled);
static command_result nestboxes(color_ostream &out, vector <string> & parameters); REQUIRE_GLOBAL(world);
DFHACK_PLUGIN("nestboxes"); namespace DFHack {
// for configuration-related logging
DBG_DECLARE(nestboxes, config, DebugCategory::LINFO);
// for logging during the periodic scan
DBG_DECLARE(nestboxes, cycle, DebugCategory::LINFO);
}
DFHACK_PLUGIN_IS_ENABLED(enabled); static const string CONFIG_KEY = string(plugin_name) + "/config";
static PersistentDataItem config;
static void eggscan(color_ostream &out)
{ enum ConfigValues {
CoreSuspender suspend; CONFIG_IS_ENABLED = 0,
};
for (df::building *build : world->buildings.other[df::buildings_other_id::NEST_BOX])
{ static int get_config_val(PersistentDataItem &c, int index) {
auto type = build->getType(); if (!c.isValid())
if (df::enums::building_type::NestBox == type) return -1;
{ return c.ival(index);
bool fertile = false; }
df::building_nest_boxst *nb = virtual_cast<df::building_nest_boxst>(build); static bool get_config_bool(PersistentDataItem &c, int index) {
if (nb->claimed_by != -1) return get_config_val(c, index) == 1;
{ }
df::unit* u = df::unit::find(nb->claimed_by); static void set_config_val(PersistentDataItem &c, int index, int value) {
if (u && u->pregnancy_timer > 0) if (c.isValid())
fertile = true; c.ival(index) = value;
} }
for (size_t j = 1; j < nb->contained_items.size(); j++) static void set_config_bool(PersistentDataItem &c, int index, bool value) {
{ set_config_val(c, index, value ? 1 : 0);
df::item* item = nb->contained_items[j]->item; }
if (item->flags.bits.forbid != fertile)
{ static const int32_t CYCLE_TICKS = 100; // need to react quickly if eggs are unforbidden
item->flags.bits.forbid = fertile; static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle
out << item->getStackSize() << " eggs " << (fertile ? "forbidden" : "unforbidden.") << endl;
} static void do_cycle(color_ostream &out, int32_t *num_enabled_seeds, int32_t *num_disabled_seeds);
}
} DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) {
} DEBUG(config,out).print("initializing %s\n", plugin_name);
return CR_OK;
} }
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;
}
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands) if (enable != is_enabled) {
{ is_enabled = enable;
if (world && plotinfo) { DEBUG(config,out).print("%s from the API; persisting\n",
commands.push_back( is_enabled ? "enabled" : "disabled");
PluginCommand( set_config_bool(config, CONFIG_IS_ENABLED, is_enabled);
"nestboxes", } else {
"Protect fertile eggs incubating in a nestbox.", DEBUG(config,out).print("%s from the API, but already %s; no action\n",
nestboxes)); is_enabled ? "enabled" : "disabled",
is_enabled ? "enabled" : "disabled");
} }
return CR_OK; return CR_OK;
} }
DFhackCExport command_result plugin_shutdown ( color_ostream &out ) DFhackCExport command_result plugin_shutdown (color_ostream &out) {
{ DEBUG(config,out).print("shutting down %s\n", plugin_name);
return CR_OK; return CR_OK;
} }
DFhackCExport command_result plugin_onupdate(color_ostream &out) DFhackCExport command_result plugin_load_data (color_ostream &out) {
{ config = World::GetPersistentData(CONFIG_KEY);
if (!enabled)
return CR_OK;
static unsigned cnt = 0; if (!config.isValid()) {
if ((++cnt % 5) != 0) DEBUG(config,out).print("no config found in this save; initializing\n");
return CR_OK; config = World::AddPersistentData(CONFIG_KEY);
set_config_bool(config, CONFIG_IS_ENABLED, is_enabled);
}
eggscan(out); is_enabled = get_config_bool(config, CONFIG_IS_ENABLED);
DEBUG(config,out).print("loading persisted enabled state: %s\n",
is_enabled ? "true" : "false");
return CR_OK; return CR_OK;
} }
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) {
{ if (event == DFHack::SC_WORLD_UNLOADED) {
enabled = enable; if (is_enabled) {
DEBUG(config,out).print("world unloaded; disabling %s\n",
plugin_name);
is_enabled = false;
}
}
return CR_OK; return CR_OK;
} }
static command_result nestboxes(color_ostream &out, vector <string> & parameters) DFhackCExport command_result plugin_onupdate(color_ostream &out) {
{ if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) {
CoreSuspender suspend; int32_t num_enabled_seeds, num_disabled_seeds;
do_cycle(out, &num_enabled_seeds, &num_disabled_seeds);
if (parameters.size() == 1) { if (0 < num_enabled_seeds)
if (parameters[0] == "enable") out.print("%s: enabled %d seed types for cooking\n",
enabled = true; plugin_name, num_enabled_seeds);
else if (parameters[0] == "disable") if (0 < num_disabled_seeds)
enabled = false; out.print("%s: protected %d seed types from cooking\n",
else plugin_name, num_disabled_seeds);
return CR_WRONG_USAGE;
} else {
out << "Plugin " << (enabled ? "enabled" : "disabled") << "." << endl;
} }
return CR_OK; return CR_OK;
} }
/////////////////////////////////////////////////////
// cycle logic
//
static void do_cycle(color_ostream &out, int32_t *num_enabled_seed_types, int32_t *num_disabled_seed_types) {
DEBUG(cycle,out).print("running %s cycle\n", plugin_name);
// mark that we have recently run
cycle_timestamp = world->frame_counter;
for (df::building_nest_boxst *nb : world->buildings.other.NEST_BOX) {
bool fertile = false;
if (nb->claimed_by != -1) {
df::unit *u = df::unit::find(nb->claimed_by);
if (u && u->pregnancy_timer > 0)
fertile = true;
}
for (auto &contained_item : nb->contained_items) {
df::item *item = contained_item->item;
if (item->flags.bits.forbid != fertile) {
item->flags.bits.forbid = fertile;
out.print("%d eggs %s.\n", item->getStackSize(), fertile ? "forbidden" : "unforbidden");
}
}
}
}