diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2c54aebad..670c9ce70 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -115,6 +115,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) + DFHACK_PLUGIN(misery misery.cpp) endif() diff --git a/plugins/misery.cpp b/plugins/misery.cpp new file mode 100644 index 000000000..f17a45599 --- /dev/null +++ b/plugins/misery.cpp @@ -0,0 +1,174 @@ +#include "PluginManager.h" +#include "Export.h" + +#include "DataDefs.h" +#include "df/world.h" +#include "df/ui.h" +#include "df/unit.h" +#include "df/unit_thought.h" +#include "df/unit_thought_type.h" + +#include +#include +#include +using namespace std; + +using namespace DFHack; + +static int factor = 1; +static map processedThoughtCountTable; + +//keep track of fake thoughts so you can remove them if requested +static vector > fakeThoughts; + +DFHACK_PLUGIN("misery"); + +command_result misery(color_ostream& out, vector& parameters); + +DFhackCExport command_result plugin_shutdown(color_ostream& out) { + factor = 1; + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream& out) { + static bool wasLoaded = false; + if ( factor == 1 || !df::global::world || !df::global::world->map.block_index ) { + if ( wasLoaded ) { + //we just unloaded the game: clear all data + factor = 1; + processedThoughtCountTable.clear(); + fakeThoughts.clear(); + wasLoaded = false; + } + return CR_OK; + } + + if ( !wasLoaded ) { + wasLoaded = true; + } + + int32_t race_id = df::global::ui->race_id; + int32_t civ_id = df::global::ui->civ_id; + for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { + df::unit* unit = df::global::world->units.all[a]; //TODO: consider units.active + //living, native units only + if ( unit->race != race_id || unit->civ_id != civ_id ) + continue; + if ( unit->flags1.bits.dead ) + continue; + + int processedThoughtCount; + map::iterator i = processedThoughtCountTable.find(unit->id); + if ( i == processedThoughtCountTable.end() ) { + processedThoughtCount = unit->status.recent_events.size(); + processedThoughtCountTable[unit->id] = processedThoughtCount; + } else { + processedThoughtCount = (*i).second; + } + + if ( processedThoughtCount == unit->status.recent_events.size() ) { + continue; + } else if ( processedThoughtCount > unit->status.recent_events.size() ) { + processedThoughtCount = unit->status.recent_events.size(); + } + + //don't reprocess any old thoughts + vector newThoughts; + for ( size_t b = processedThoughtCount; b < unit->status.recent_events.size(); b++ ) { + df::unit_thought* oldThought = unit->status.recent_events[b]; + const char* bob = ENUM_ATTR(unit_thought_type, value, oldThought->type); + if ( bob[0] != '-' ) { + //out.print("unit %4d: old thought value = %s\n", unit->id, bob); + continue; + } + /*out.print("unit %4d: Duplicating thought type %d (%s), value %s, age %d, subtype %d, severity %d\n", + unit->id, + oldThought->type.value, + ENUM_ATTR(unit_thought_type, caption, (oldThought->type)), + //df::enum_traits::attr_table[oldThought->type].caption + bob, + oldThought->age, + oldThought->subtype, + oldThought->severity + );*/ + //add duplicate thoughts to the temp list + for ( size_t c = 0; c < factor; c++ ) { + df::unit_thought* thought = new df::unit_thought; + thought->type = unit->status.recent_events[b]->type; + thought->age = unit->status.recent_events[b]->age; + thought->subtype = unit->status.recent_events[b]->subtype; + thought->severity = unit->status.recent_events[b]->severity; + newThoughts.push_back(thought); + } + } + for ( size_t b = 0; b < newThoughts.size(); b++ ) { + fakeThoughts.push_back(std::pair(a, unit->status.recent_events.size())); + unit->status.recent_events.push_back(newThoughts[b]); + } + processedThoughtCountTable[unit->id] = unit->status.recent_events.size(); + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { + commands.push_back(PluginCommand("misery", "increase the intensity of negative dwarven thoughts", + &misery, false, + "misery: When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).\n" + "Usage:\n" + " misery enable n\n" + " enable misery with optional magnitude n. If specified, n must be positive.\n" + " misery n\n" + " same as \"misery enable n\"\n" + " misery enable\n" + " same as \"misery enable 2\"\n" + " misery disable\n" + " stop adding new negative thoughts. This will not remove existing duplicated thoughts. Equivalent to \"misery 1\"\n" + " misery clear\n" + " remove fake thoughts added in this session of DF. Saving makes them permanent! Does not change factor.\n\n" + )); + return CR_OK; +} + +command_result misery(color_ostream &out, vector& parameters) { + if ( !df::global::world || !df::global::world->map.block_index ) { + out.printerr("misery can only be enabled in fortress mode with a fully-loaded game.\n"); + return CR_FAILURE; + } + + if ( parameters.size() < 1 || parameters.size() > 2 ) { + return CR_WRONG_USAGE; + } + + if ( parameters[0] == "disable" ) { + if ( parameters.size() > 1 ) { + return CR_WRONG_USAGE; + } + factor = 1; + return CR_OK; + } else if ( parameters[0] == "enable" ) { + factor = 2; + if ( parameters.size() == 2 ) { + factor = atoi(parameters[1].c_str()); + if ( factor < 1 ) { + out.printerr("Second argument must be a positive integer.\n"); + return CR_WRONG_USAGE; + } + } + } else if ( parameters[0] == "clear" ) { + for ( size_t a = 0; a < fakeThoughts.size(); a++ ) { + int dorfIndex = fakeThoughts[a].first; + int thoughtIndex = fakeThoughts[a].second; + df::global::world->units.all[dorfIndex]->status.recent_events[thoughtIndex]->age = 1000000; + } + fakeThoughts.clear(); + } else { + int a = atoi(parameters[0].c_str()); + if ( a < 1 ) { + return CR_WRONG_USAGE; + } + factor = a; + } + + return CR_OK; +}