2023-02-01 05:56:25 -07:00
|
|
|
#include "Debug.h"
|
2012-05-01 09:58:12 -06:00
|
|
|
#include "PluginManager.h"
|
|
|
|
|
2023-02-08 17:37:46 -07:00
|
|
|
#include "modules/Items.h"
|
|
|
|
#include "modules/Job.h"
|
2023-02-01 05:56:25 -07:00
|
|
|
#include "modules/Persistence.h"
|
|
|
|
#include "modules/World.h"
|
|
|
|
|
2012-05-01 09:58:12 -06:00
|
|
|
#include "df/world.h"
|
|
|
|
#include "df/building_nest_boxst.h"
|
|
|
|
#include "df/item.h"
|
2023-02-10 20:52:01 -07:00
|
|
|
#include "df/item_eggst.h"
|
2012-05-01 09:58:12 -06:00
|
|
|
#include "df/unit.h"
|
|
|
|
|
|
|
|
using std::string;
|
|
|
|
using namespace DFHack;
|
|
|
|
using namespace df::enums;
|
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
DFHACK_PLUGIN("nestboxes");
|
|
|
|
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
2012-05-01 09:58:12 -06:00
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
REQUIRE_GLOBAL(world);
|
2012-05-01 09:58:12 -06:00
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
namespace DFHack {
|
|
|
|
// for configuration-related logging
|
|
|
|
DBG_DECLARE(nestboxes, config, DebugCategory::LINFO);
|
|
|
|
// for logging during the periodic scan
|
|
|
|
DBG_DECLARE(nestboxes, cycle, DebugCategory::LINFO);
|
|
|
|
}
|
2012-05-01 09:58:12 -06:00
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
static const string CONFIG_KEY = string(plugin_name) + "/config";
|
|
|
|
static PersistentDataItem config;
|
|
|
|
|
|
|
|
enum ConfigValues {
|
|
|
|
CONFIG_IS_ENABLED = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-02-10 09:20:11 -07:00
|
|
|
static const int32_t CYCLE_TICKS = 50; // need to react quickly when eggs are laid/unforbidden
|
2023-02-01 05:56:25 -07:00
|
|
|
static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle
|
|
|
|
|
2023-02-01 23:30:56 -07:00
|
|
|
static void do_cycle(color_ostream &out);
|
2023-02-01 05:56:25 -07:00
|
|
|
|
|
|
|
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) {
|
|
|
|
DEBUG(config,out).print("initializing %s\n", plugin_name);
|
|
|
|
|
|
|
|
return CR_OK;
|
2012-08-30 08:23:11 -06:00
|
|
|
}
|
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
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;
|
|
|
|
}
|
2012-08-30 08:23:11 -06:00
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
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);
|
2023-02-06 05:02:18 -07:00
|
|
|
if (enable)
|
|
|
|
do_cycle(out);
|
2023-02-01 05:56:25 -07:00
|
|
|
} else {
|
|
|
|
DEBUG(config,out).print("%s from the API, but already %s; no action\n",
|
|
|
|
is_enabled ? "enabled" : "disabled",
|
|
|
|
is_enabled ? "enabled" : "disabled");
|
2012-05-01 09:58:12 -06:00
|
|
|
}
|
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
DFhackCExport command_result plugin_shutdown (color_ostream &out) {
|
|
|
|
DEBUG(config,out).print("shutting down %s\n", plugin_name);
|
|
|
|
|
2012-05-01 09:58:12 -06:00
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
DFhackCExport command_result plugin_load_data (color_ostream &out) {
|
2023-02-03 00:44:33 -07:00
|
|
|
cycle_timestamp = 0;
|
2023-02-01 05:56:25 -07:00
|
|
|
config = World::GetPersistentData(CONFIG_KEY);
|
2012-08-30 08:23:11 -06:00
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
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);
|
|
|
|
}
|
2012-08-30 08:23:11 -06:00
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
is_enabled = get_config_bool(config, CONFIG_IS_ENABLED);
|
|
|
|
DEBUG(config,out).print("loading persisted enabled state: %s\n",
|
|
|
|
is_enabled ? "true" : "false");
|
2012-08-30 08:23:11 -06:00
|
|
|
|
|
|
|
return CR_OK;
|
|
|
|
}
|
2012-05-01 09:58:12 -06:00
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2013-09-30 03:19:51 -06:00
|
|
|
return CR_OK;
|
|
|
|
}
|
|
|
|
|
2023-02-01 05:56:25 -07:00
|
|
|
DFhackCExport command_result plugin_onupdate(color_ostream &out) {
|
2023-02-01 23:30:56 -07:00
|
|
|
if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS)
|
|
|
|
do_cycle(out);
|
2012-11-16 14:33:36 -07:00
|
|
|
return CR_OK;
|
2012-05-01 09:58:12 -06:00
|
|
|
}
|
2023-02-01 05:56:25 -07:00
|
|
|
|
|
|
|
/////////////////////////////////////////////////////
|
|
|
|
// cycle logic
|
|
|
|
//
|
|
|
|
|
2023-02-01 23:30:56 -07:00
|
|
|
static void do_cycle(color_ostream &out) {
|
2023-02-01 05:56:25 -07:00
|
|
|
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) {
|
2023-02-10 20:52:01 -07:00
|
|
|
auto *item = virtual_cast<df::item_eggst>(contained_item->item);
|
|
|
|
if (item && item->flags.bits.forbid != fertile) {
|
2023-02-01 05:56:25 -07:00
|
|
|
item->flags.bits.forbid = fertile;
|
2023-02-08 17:37:46 -07:00
|
|
|
if (fertile && item->flags.bits.in_job) {
|
|
|
|
// cancel any job involving the egg
|
|
|
|
df::specific_ref *sref = Items::getSpecificRef(
|
|
|
|
item, df::specific_ref_type::JOB);
|
|
|
|
if (sref && sref->data.job)
|
|
|
|
Job::removeJob(sref->data.job);
|
|
|
|
}
|
2023-02-01 05:56:25 -07:00
|
|
|
out.print("%d eggs %s.\n", item->getStackSize(), fertile ? "forbidden" : "unforbidden");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|