From fe2257427f6495cd536513fdfb114b00ffc94810 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 19 Nov 2012 20:17:05 -0500 Subject: [PATCH 01/56] workNow: plugin to make DF process jobs every time you pause. --- plugins/workNow.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 plugins/workNow.cpp diff --git a/plugins/workNow.cpp b/plugins/workNow.cpp new file mode 100644 index 000000000..acbb5bc30 --- /dev/null +++ b/plugins/workNow.cpp @@ -0,0 +1,43 @@ +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" +#include "DataDefs.h" +#include "modules/World.h" +#include "df/global_objects.h" + +#include +using namespace std; + +using namespace DFHack; + +DFHACK_PLUGIN("workNow"); + +static bool active = false; + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { + if ( !DFHack::Core::getInstance().getWorld()->ReadPauseState() ) + return CR_OK; + *df::global::process_jobs = true; + return CR_OK; +} + +DFhackCExport command_result workNow(color_ostream& out, vector& parameters); + +DFhackCExport command_result plugin_init(color_ostream& out, std::vector &commands) { + commands.push_back(PluginCommand("workNow", "makes dwarves look for jobs every time you pause", workNow, false, "Full help.")); + + return CR_OK; +} + +DFhackCExport command_result workNow(color_ostream& out, vector& parameters) { + + + return CR_OK; +} + + From 72921fbfd513723f3a52fb06615083cbdc9f127c Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 8 Dec 2012 12:50:33 -0500 Subject: [PATCH 02/56] Made workNow only check jobs when the game becomes paused instead of constantly when paused. Also made it enable/disable on command. --- plugins/CMakeLists.txt | 1 + plugins/workNow.cpp | 44 +++++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 91d578215..75da6f478 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -127,6 +127,7 @@ if (BUILD_SUPPORTED) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) DFHACK_PLUGIN(misery misery.cpp) + DFHACK_PLUGIN(workNow workNow.cpp) endif() diff --git a/plugins/workNow.cpp b/plugins/workNow.cpp index acbb5bc30..906df33dc 100644 --- a/plugins/workNow.cpp +++ b/plugins/workNow.cpp @@ -8,34 +8,60 @@ #include using namespace std; - using namespace DFHack; DFHACK_PLUGIN("workNow"); static bool active = false; +DFhackCExport command_result workNow(color_ostream& out, vector& parameters); + +DFhackCExport command_result plugin_init(color_ostream& out, std::vector &commands) { + commands.push_back(PluginCommand("workNow", "makes dwarves look for jobs every time you pause", workNow, false, "When workNow is active, every time the game pauses, DF will make dwarves perform any appropriate available jobs. This includes when you one step through the game using the pause menu.\n" + "workNow 1\n" + " activate workNow\n" + "workNow 0\n" + " deactivate workNow\n")); + + return CR_OK; +} + DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { + active = false; return CR_OK; } -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { - if ( !DFHack::Core::getInstance().getWorld()->ReadPauseState() ) +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event e) { + if ( !active ) + return CR_OK; + if ( e == DFHack::SC_WORLD_UNLOADED ) { + active = false; + return CR_OK; + } + if ( e != DFHack::SC_PAUSED ) return CR_OK; + *df::global::process_jobs = true; + return CR_OK; } -DFhackCExport command_result workNow(color_ostream& out, vector& parameters); -DFhackCExport command_result plugin_init(color_ostream& out, std::vector &commands) { - commands.push_back(PluginCommand("workNow", "makes dwarves look for jobs every time you pause", workNow, false, "Full help.")); - - return CR_OK; -} DFhackCExport command_result workNow(color_ostream& out, vector& parameters) { + if ( parameters.size() == 0 ) { + out.print("workNow status = %s\n", active ? "active" : "inactive"); + return CR_OK; + } + if ( parameters.size() > 1 ) { + return CR_WRONG_USAGE; + } + int32_t a = atoi(parameters[0].c_str()); + if (a < 0 || a > 1) + return CR_WRONG_USAGE; + + active = (bool)a; return CR_OK; } From e85f4eb880674a51fdf04ce7e5784a5ef70dfbe0 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 9 Dec 2012 21:18:29 -0500 Subject: [PATCH 03/56] First draft of autoSyndrome: a tool for replacing boiling rock syndromes with something more reliable. Uses non-df-recognized tags in material definition raws. --- plugins/CMakeLists.txt | 1 + plugins/autoSyndrome.cpp | 360 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 361 insertions(+) create mode 100644 plugins/autoSyndrome.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 8aeeee8c3..ed4898112 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -130,6 +130,7 @@ if (BUILD_SUPPORTED) #DFHACK_PLUGIN(versionosd versionosd.cpp) DFHACK_PLUGIN(misery misery.cpp) #DFHACK_PLUGIN(dfstream dfstream.cpp LINK_LIBRARIES clsocket dfhack-tinythread) + DFHACK_PLUGIN(autoSyndrome autoSyndrome.cpp) endif() diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp new file mode 100644 index 000000000..28a785adc --- /dev/null +++ b/plugins/autoSyndrome.cpp @@ -0,0 +1,360 @@ +#include "PluginManager.h" +#include "Export.h" +#include "DataDefs.h" +#include "Core.h" +#include "df/job.h" +#include "df/global_objects.h" +#include "df/ui.h" +#include "df/job_type.h" +#include "df/reaction.h" +#include "df/reaction_product.h" +#include "df/reaction_product_type.h" +#include "df/reaction_product_itemst.h" +#include "df/syndrome.h" +#include "df/unit_syndrome.h" +#include "df/unit.h" +#include "df/general_ref.h" +#include "df/general_ref_type.h" +#include "df/general_ref_unit_workerst.h" +#include "modules/Maps.h" +#include "modules/Job.h" + +#include +#include +#include +#include + +using namespace std; +using namespace DFHack; + +/* +Example usage: + +////////////////////////////////////////////// +//in file interaction_duck.txt +interaction_duck + +[OBJECT:INTERACTION] + +[INTERACTION:DUMMY_DUCK_INTERACTION] + [I_SOURCE:CREATURE_ACTION] + [I_TARGET:A:CREATURE] + [IT_LOCATION:CONTEXT_CREATURE] + [IT_MANUAL_INPUT:target] + [IT_IMMUNE_CREATURE:BIRD_DUCK:MALE] + [I_EFFECT:ADD_SYNDROME] + [IE_TARGET:A] + [IE_IMMEDIATE] + [SYNDROME] + [SYN_NAME:chronicDuckSyndrome] + [CE_BODY_TRANSFORMATION:PROB:100:START:0] + [CE:CREATURE:BIRD_DUCK:MALE] +////////////////////////////////////////////// +//In file inorganic_duck.txt +inorganic_stone_duck + +[OBJECT:INORGANIC] + +[INORGANIC:DUCK_ROCK] +[USE_MATERIAL_TEMPLATE:STONE_TEMPLATE] +[STATE_NAME_ADJ:ALL_SOLID:drakium][DISPLAY_COLOR:0:7:0][TILE:'.'] +[IS_STONE] +[SOLID_DENSITY:1][MELTING_POINT:25000] +[CAUSE_SYNDROME:chronicDuckSyndrome] +[BOILING_POINT:50000] +/////////////////////////////////////////////// +//In file building_duck.txt +building_duck + +[OBJECT:BUILDING] + +[BUILDING_WORKSHOP:DUCK_WORKSHOP] + [NAME:Duck Workshop] + [NAME_COLOR:7:0:1] + [DIM:1:1] + [WORK_LOCATION:1:1] + [BLOCK:1:0:0:0] + [TILE:0:1:236] + [COLOR:0:1:0:0:1] + [TILE:1:1:' '] + [COLOR:1:1:0:0:0] + [TILE:2:1:8] + [COLOR:2:1:0:0:1] + [TILE:3:1:8] + [COLOR:3:2:0:4:1] + [BUILD_ITEM:1:NONE:NONE:NONE:NONE] + [BUILDMAT] + [WORTHLESS_STONE_ONLY] + [CAN_USE_ARTIFACT] +/////////////////////////////////////////////// +//In file reaction_duck.txt +reaction_duck + +[OBJECT:REACTION] + +[REACTION:DUCKIFICATION] +[NAME:become a duck] +[BUILDING:DUCK_WORKSHOP:NONE] +[PRODUCT:100:100:STONE:NO_SUBTYPE:STONE:DUCK_ROCK] +////////////////////////////////////////////// +//Add the following lines to your entity in entity_default.txt (or wherever it is) + [PERMITTED_BUILDING:DUCK_WORKSHOP] + [PERMITTED_REACTION:DUCKIFICATION] +////////////////////////////////////////////// + +Next, start a new fort in a new world, build a duck workshop, then have someone become a duck. +*/ + +const int32_t ticksPerYear = 403200; +int32_t lastRun = 0; +unordered_map prevJobs; +unordered_map jobWorkers; +bool enabled = true; + +DFHACK_PLUGIN("autoSyndrome"); + +command_result autoSyndrome(color_ostream& out, vector& parameters); +int32_t processJob(color_ostream& out, int32_t id); +int32_t giveSyndrome(color_ostream& out, int32_t workerId, df::syndrome* syndrome); + +DFhackCExport command_result plugin_shutdown(color_ostream& out) { + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream& out) { + if ( !enabled ) + return CR_OK; + if(DFHack::Maps::IsValid() == false) { + return CR_OK; + } + + //don't run more than once per tick + int32_t time = (*df::global::cur_year)*ticksPerYear + (*df::global::cur_year_tick); + if ( time <= lastRun ) + return CR_OK; + lastRun = time; + + //keep track of all queued jobs. When one completes (and is not cancelled), check if it's a boiling rock job, and if so, give the worker the appropriate syndrome + unordered_map jobs; + df::job_list_link* link = &df::global::world->job_list; + for( ; link != NULL; link = link->next ) { + df::job* item = link->item; + if ( item == NULL ) + continue; + //-1 usually means it hasn't been assigned yet. + if ( item->completion_timer < 0 ) + continue; + //if the completion timer is more than one, then the job will never disappear next tick unless it's cancelled + if ( item->completion_timer > 1 ) + continue; + + //only consider jobs that have been started + int32_t workerId = -1; + for ( size_t a = 0; a < item->references.size(); a++ ) { + if ( item->references[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) + continue; + if ( workerId != -1 ) { + out.print("%s, line %d: Found two workers on the same job.\n", __FILE__, __LINE__); + } + workerId = ((df::general_ref_unit_workerst*)item->references[a])->unit_id; + } + if ( workerId == -1 ) + continue; + + jobs[item->id] = item; + jobWorkers[item->id] = workerId; + } + + //if it's not on the job list anymore, and its completion timer was 0, then it probably finished and was not cancelled, so process the job. + for ( unordered_map::iterator i = prevJobs.begin(); i != prevJobs.end(); i++ ) { + int32_t id = (*i).first; + df::job* completion = (*i).second; + if ( jobs.find(id) != jobs.end() ) + continue; + if ( completion->completion_timer > 0 ) + continue; + if ( processJob(out, id) < 0 ) { + //enabled = false; + return CR_FAILURE; + } + } + + //delete obselete job copies + for ( unordered_map::iterator i = prevJobs.begin(); i != prevJobs.end(); i++ ) { + int32_t id = (*i).first; + df::job* oldJob = (*i).second; + DFHack::Job::deleteJobStruct(oldJob); + if ( jobs.find(id) == jobs.end() ) + jobWorkers.erase(id); + } + prevJobs.clear(); + + //make copies of the jobs we looked at this tick in case they disappear next frame. + for ( unordered_map::iterator i = jobs.begin(); i != jobs.end(); i++ ) { + int32_t id = (*i).first; + df::job* oldJob = (*i).second; + df::job* jobCopy = DFHack::Job::cloneJobStruct(oldJob); + prevJobs[id] = jobCopy; + } + + return CR_OK; +} + +/*DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event e) { + return CR_OK; +}*/ + +DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { + commands.push_back(PluginCommand("autoSyndrome", "Automatically give units syndromes when they complete jobs, as configured in the raw files.\n", &autoSyndrome, false, + "autoSyndrome:\n" + " autoSyndrome 0 //disable\n" + " autoSyndrome 1 //enable\n" + " autoSyndrome disable //disable\n" + " autoSyndrome enable //enable\n" + "\n" + "autoSyndrome looks for recently completed jobs matching certain conditions, and if it finds one, then it will give the dwarf that finished that job the syndrome specified in the raw files.\n" + "\n" + "Requirements:\n" + " 1) The job must be a custom reaction.\n" + " 2) The job must produce a stone of some inorganic material.\n" + " 3) The material of one of the stones produced must have a token in its raw file of the form [CAUSE_SYNDROME:syndrome_name].\n" + "\n" + "If a syndrome with the tag [SYN_NAME:syndrome_name] exists, then the unit that completed the job will become afflicted with that syndrome as soon as the job is completed.\n")); + + + return CR_OK; +} + +command_result autoSyndrome(color_ostream& out, vector& parameters) { + if ( parameters.size() > 1 ) + return CR_WRONG_USAGE; + + if ( parameters.size() == 1 ) { + if ( parameters[0] == "enable" ) { + enabled = true; + } else if ( parameters[0] == "disable" ) { + enabled = false; + } else { + int32_t a = atoi(parameters[0].c_str()); + if ( a < 0 || a > 1 ) + return CR_WRONG_USAGE; + + enabled = (bool)a; + } + } + + out.print("autoSyndrome is %s\n", enabled ? "enabled" : "disabled"); + return CR_OK; +} + +int32_t processJob(color_ostream& out, int32_t jobId) { + df::job* job = prevJobs[jobId]; + if ( job == NULL ) { + out.print("Error %s line %d: couldn't find job %d.\n", __FILE__, __LINE__, jobId); + return -1; + } + + if ( job->job_type != df::job_type::CustomReaction ) + return 0; + + //find the custom reaction raws and see if we have any special tags there + //out.print("job: \"%s\"\n", job->reaction_name.c_str()); + + df::reaction* reaction = NULL; + for ( size_t a = 0; a < df::global::world->raws.reactions.size(); a++ ) { + df::reaction* candidate = df::global::world->raws.reactions[a]; + if ( candidate->code != job->reaction_name ) + continue; + reaction = candidate; + break; + } + if ( reaction == NULL ) { + out.print("%s, line %d: could not find reaction \"%s\".\n", __FILE__, __LINE__, job->reaction_name.c_str() ); + return -1; + } + + //find all of the products it makes. Look for a stone with a material with special tags. + bool foundIt = false; + for ( size_t a = 0; a < reaction->products.size(); a++ ) { + df::reaction_product_type type = reaction->products[a]->getType(); + //out.print("type = %d\n", (int32_t)type); + if ( type != df::enums::reaction_product_type::item ) + continue; + df::reaction_product_itemst* bob = (df::reaction_product_itemst*)reaction->products[a]; + //out.print("item_type = %d\n", (int32_t)bob->item_type); + if ( bob->item_type != df::enums::item_type::BOULDER ) + continue; + //for now don't worry about subtype + + df::inorganic_raw* inorganic = df::global::world->raws.inorganics[bob->mat_index]; + const char* helper = "CAUSE_SYNDROME:"; + for ( size_t b = 0; b < inorganic->str.size(); b++ ) { + //out.print("inorganic str = \"%s\"\n", inorganic->str[b]->c_str()); + size_t c = inorganic->str[b]->find(helper); + if ( c == string::npos ) + continue; + string tail = inorganic->str[b]->substr(c + strlen(helper), inorganic->str[b]->length() - strlen(helper) - 2); + //out.print("tail = %s\n", tail.c_str()); + + //find the syndrome with this name, and give apply it to the dwarf working on the job + //first find out who completed the job + if ( jobWorkers.find(jobId) == jobWorkers.end() ) { + out.print("%s, line %d: could not find job worker for jobs %d.\n", __FILE__, __LINE__, jobId); + return -1; + } + int32_t workerId = jobWorkers[jobId]; + + //find the syndrome + df::syndrome* syndrome = NULL; + for ( size_t d = 0; d < df::global::world->raws.syndromes.all.size(); d++ ) { + df::syndrome* candidate = df::global::world->raws.syndromes.all[d]; + if ( candidate->syn_name != tail ) + continue; + syndrome = candidate; + break; + } + if ( syndrome == NULL ) + return 0; + + if ( giveSyndrome(out, workerId, syndrome) < 0 ) + return -1; + //out.print("Gave syndrome.\n"); + } + } + if ( !foundIt ) + return 0; + + return -2; +} + +/* + * Heavily based on https://gist.github.com/4061959/ + **/ +int32_t giveSyndrome(color_ostream& out, int32_t workerId, df::syndrome* syndrome) { + int32_t index = df::unit::binsearch_index(df::global::world->units.all, workerId); + if ( index < 0 ) { + out.print("%s line %d: Couldn't find unit %d.\n", __FILE__, __LINE__, workerId); + return -1; + } + df::unit* unit = df::global::world->units.all[index]; + + df::unit_syndrome* unitSyndrome = new df::unit_syndrome(); + unitSyndrome->type = syndrome->id; + unitSyndrome->year = 0; + unitSyndrome->year_time = 0; + unitSyndrome->ticks = 1; + unitSyndrome->unk1 = 1; + unitSyndrome->flags = 0; //typecast + + for ( size_t a = 0; a < syndrome->ce.size(); a++ ) { + df::unit_syndrome::T_symptoms* symptom = new df::unit_syndrome::T_symptoms(); + symptom->unk1 = 0; + symptom->unk2 = 0; + symptom->ticks = 1; + symptom->flags = 2; //TODO: ??? + unitSyndrome->symptoms.push_back(symptom); + } + unit->syndromes.active.push_back(unitSyndrome); + return 0; +} + From 3a541e26bec867a87192c9b37d7f9bff01a1cc73 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 12 Dec 2012 23:01:05 -0500 Subject: [PATCH 04/56] autoSyndrome: automatically detects boiling rock syndromes better, and checks if each syndrome is applicable to the unit that finished the job. --- plugins/autoSyndrome.cpp | 153 +++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 71 deletions(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 28a785adc..77217c6a0 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -2,9 +2,10 @@ #include "Export.h" #include "DataDefs.h" #include "Core.h" -#include "df/job.h" +#include "df/caste_raw.h" +#include "df/creature_raw.h" #include "df/global_objects.h" -#include "df/ui.h" +#include "df/job.h" #include "df/job_type.h" #include "df/reaction.h" #include "df/reaction_product.h" @@ -12,6 +13,7 @@ #include "df/reaction_product_itemst.h" #include "df/syndrome.h" #include "df/unit_syndrome.h" +#include "df/ui.h" #include "df/unit.h" #include "df/general_ref.h" #include "df/general_ref_type.h" @@ -30,25 +32,6 @@ using namespace DFHack; /* Example usage: -////////////////////////////////////////////// -//in file interaction_duck.txt -interaction_duck - -[OBJECT:INTERACTION] - -[INTERACTION:DUMMY_DUCK_INTERACTION] - [I_SOURCE:CREATURE_ACTION] - [I_TARGET:A:CREATURE] - [IT_LOCATION:CONTEXT_CREATURE] - [IT_MANUAL_INPUT:target] - [IT_IMMUNE_CREATURE:BIRD_DUCK:MALE] - [I_EFFECT:ADD_SYNDROME] - [IE_TARGET:A] - [IE_IMMEDIATE] - [SYNDROME] - [SYN_NAME:chronicDuckSyndrome] - [CE_BODY_TRANSFORMATION:PROB:100:START:0] - [CE:CREATURE:BIRD_DUCK:MALE] ////////////////////////////////////////////// //In file inorganic_duck.txt inorganic_stone_duck @@ -60,8 +43,11 @@ inorganic_stone_duck [STATE_NAME_ADJ:ALL_SOLID:drakium][DISPLAY_COLOR:0:7:0][TILE:'.'] [IS_STONE] [SOLID_DENSITY:1][MELTING_POINT:25000] -[CAUSE_SYNDROME:chronicDuckSyndrome] -[BOILING_POINT:50000] +[BOILING_POINT:9999] //This is the critical line: boiling point must be <= 10000 +[SYNDROME] + [SYN_NAME:Chronic Duck Syndrome] + [CE_BODY_TRANSFORMATION:PROB:100:START:0] + [CE:CREATURE:BIRD_DUCK:MALE] //even though we don't have SYN_INHALED, the plugin will add it /////////////////////////////////////////////// //In file building_duck.txt building_duck @@ -117,6 +103,29 @@ command_result autoSyndrome(color_ostream& out, vector& parameters); int32_t processJob(color_ostream& out, int32_t id); int32_t giveSyndrome(color_ostream& out, int32_t workerId, df::syndrome* syndrome); +DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { + commands.push_back(PluginCommand("autoSyndrome", "Automatically give units syndromes when they complete jobs, as configured in the raw files.\n", &autoSyndrome, false, + "autoSyndrome:\n" + " autoSyndrome 0 //disable\n" + " autoSyndrome 1 //enable\n" + " autoSyndrome disable //disable\n" + " autoSyndrome enable //enable\n" + "\n" + "autoSyndrome looks for recently completed jobs matching certain conditions, and if it finds one, then it will give the dwarf that finished that job the syndrome specified in the raw files.\n" + "\n" + "Requirements:\n" + " 1) The job must be a custom reaction.\n" + " 2) The job must produce a stone of some inorganic material.\n" + " 3) The stone must have a boiling temperature less than or equal to 10000.\n" + "\n" + "When these conditions are met, the unit that completed the job will immediately become afflicted with all applicable syndromes associated with the inorganic material of the stone, or stones. It should correctly check for whether the creature or caste is affected or immune, but it ignores syn_affected_class tags.\n" + "Multiple syndromes per stone, or multiple boiling rocks produced with the same reaction should work fine.\n" + )); + + + return CR_OK; +} + DFhackCExport command_result plugin_shutdown(color_ostream& out) { return CR_OK; } @@ -204,27 +213,6 @@ DFhackCExport command_result plugin_onupdate(color_ostream& out) { return CR_OK; }*/ -DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { - commands.push_back(PluginCommand("autoSyndrome", "Automatically give units syndromes when they complete jobs, as configured in the raw files.\n", &autoSyndrome, false, - "autoSyndrome:\n" - " autoSyndrome 0 //disable\n" - " autoSyndrome 1 //enable\n" - " autoSyndrome disable //disable\n" - " autoSyndrome enable //enable\n" - "\n" - "autoSyndrome looks for recently completed jobs matching certain conditions, and if it finds one, then it will give the dwarf that finished that job the syndrome specified in the raw files.\n" - "\n" - "Requirements:\n" - " 1) The job must be a custom reaction.\n" - " 2) The job must produce a stone of some inorganic material.\n" - " 3) The material of one of the stones produced must have a token in its raw file of the form [CAUSE_SYNDROME:syndrome_name].\n" - "\n" - "If a syndrome with the tag [SYN_NAME:syndrome_name] exists, then the unit that completed the job will become afflicted with that syndrome as soon as the job is completed.\n")); - - - return CR_OK; -} - command_result autoSyndrome(color_ostream& out, vector& parameters) { if ( parameters.size() > 1 ) return CR_WRONG_USAGE; @@ -273,7 +261,22 @@ int32_t processJob(color_ostream& out, int32_t jobId) { return -1; } - //find all of the products it makes. Look for a stone with a material with special tags. + if ( jobWorkers.find(jobId) == jobWorkers.end() ) { + out.print("%s, line %d: couldn't find worker for job %d.\n", __FILE__, __LINE__, jobId); + return -1; + } + int32_t workerId = jobWorkers[jobId]; + int32_t workerIndex = df::unit::binsearch_index(df::global::world->units.all, workerId); + if ( workerIndex < 0 ) { + out.print("%s line %d: Couldn't find unit %d.\n", __FILE__, __LINE__, workerId); + return -1; + } + df::unit* unit = df::global::world->units.all[workerIndex]; + df::creature_raw* creature = df::global::world->raws.creatures.all[unit->race]; + std::string& creature_name = creature->creature_id; + std::string& creature_caste = creature->caste[unit->caste]->caste_id; + + //find all of the products it makes. Look for a stone with a low boiling point. bool foundIt = false; for ( size_t a = 0; a < reaction->products.size(); a++ ) { df::reaction_product_type type = reaction->products[a]->getType(); @@ -286,39 +289,47 @@ int32_t processJob(color_ostream& out, int32_t jobId) { continue; //for now don't worry about subtype + //must be a boiling rock syndrome df::inorganic_raw* inorganic = df::global::world->raws.inorganics[bob->mat_index]; - const char* helper = "CAUSE_SYNDROME:"; - for ( size_t b = 0; b < inorganic->str.size(); b++ ) { - //out.print("inorganic str = \"%s\"\n", inorganic->str[b]->c_str()); - size_t c = inorganic->str[b]->find(helper); - if ( c == string::npos ) - continue; - string tail = inorganic->str[b]->substr(c + strlen(helper), inorganic->str[b]->length() - strlen(helper) - 2); - //out.print("tail = %s\n", tail.c_str()); + if ( inorganic->material.heat.boiling_point > 10000 ) + continue; - //find the syndrome with this name, and give apply it to the dwarf working on the job - //first find out who completed the job - if ( jobWorkers.find(jobId) == jobWorkers.end() ) { - out.print("%s, line %d: could not find job worker for jobs %d.\n", __FILE__, __LINE__, jobId); - return -1; + for ( size_t b = 0; b < inorganic->material.syndrome.size(); b++ ) { + //add each syndrome to the guy who did the job + df::syndrome* syndrome = inorganic->material.syndrome[b]; + //check that the syndrome applies to that guy + bool applies = syndrome->syn_affected_creature_1.size() == 0; + if ( applies ) { + //out.print("No syn_affected_creature.\n"); } - int32_t workerId = jobWorkers[jobId]; - - //find the syndrome - df::syndrome* syndrome = NULL; - for ( size_t d = 0; d < df::global::world->raws.syndromes.all.size(); d++ ) { - df::syndrome* candidate = df::global::world->raws.syndromes.all[d]; - if ( candidate->syn_name != tail ) + for ( size_t c = 0; c < syndrome->syn_affected_creature_1.size(); c++ ) { + if ( creature_name != *syndrome->syn_affected_creature_1[c] ) continue; - syndrome = candidate; - break; + if ( *syndrome->syn_affected_creature_2[c] == "ALL" || + *syndrome->syn_affected_creature_2[c] == creature_caste ) { + applies = true; + break; + } + } + if ( !applies ) { + //out.print("Not in syn_affected_creature.\n"); + continue; + } + for ( size_t c = 0; c < syndrome->syn_immune_creature_1.size(); c++ ) { + if ( creature_name != *syndrome->syn_immune_creature_1[c] ) + continue; + if ( *syndrome->syn_immune_creature_2[c] == "ALL" || + *syndrome->syn_immune_creature_2[c] == creature_caste ) { + applies = false; + break; + } + } + if ( !applies ) { + //out.print("Creature is immune.\n"); + continue; } - if ( syndrome == NULL ) - return 0; - if ( giveSyndrome(out, workerId, syndrome) < 0 ) return -1; - //out.print("Gave syndrome.\n"); } } if ( !foundIt ) From a28fc65e6d21578e31d25f3fa08133977cb5388d Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 13 Dec 2012 11:13:56 -0500 Subject: [PATCH 05/56] autoSyndrome now deals with creature_class. --- plugins/autoSyndrome.cpp | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 77217c6a0..a37d806fe 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -118,7 +118,7 @@ DFhackCExport command_result plugin_init(color_ostream& out, vectorunits.all[workerIndex]; df::creature_raw* creature = df::global::world->raws.creatures.all[unit->race]; + df::caste_raw* caste = creature->caste[unit->caste]; std::string& creature_name = creature->creature_id; - std::string& creature_caste = creature->caste[unit->caste]->caste_id; + std::string& creature_caste = caste->caste_id; //find all of the products it makes. Look for a stone with a low boiling point. bool foundIt = false; @@ -298,10 +299,40 @@ int32_t processJob(color_ostream& out, int32_t jobId) { //add each syndrome to the guy who did the job df::syndrome* syndrome = inorganic->material.syndrome[b]; //check that the syndrome applies to that guy - bool applies = syndrome->syn_affected_creature_1.size() == 0; + bool applies = syndrome->syn_affected_class.size() == 0; if ( applies ) { + //out.print("No syn_affected_class.\n"); + } + for ( size_t c = 0; c < syndrome->syn_affected_class.size(); c++ ) { + if ( applies ) + break; + for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { + if ( *syndrome->syn_affected_class[c] == *caste->creature_class[d] ) { + applies = true; + break; + } + } + } + if ( syndrome->syn_affected_creature_1.size() != 0 ) { + applies = false; + } else { //out.print("No syn_affected_creature.\n"); } + for ( size_t c = 0; c < syndrome->syn_immune_class.size(); c++ ) { + if ( !applies ) + break; + for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { + if ( *syndrome->syn_immune_class[c] == *caste->creature_class[d] ) { + applies = false; + break; + } + } + } + + if ( syndrome->syn_affected_creature_1.size() != syndrome->syn_affected_creature_2.size() ) { + out.print("%s, line %d: different affected creature/caste sizes.\n", __FILE__, __LINE__); + return -1; + } for ( size_t c = 0; c < syndrome->syn_affected_creature_1.size(); c++ ) { if ( creature_name != *syndrome->syn_affected_creature_1[c] ) continue; From af7f11fdfac5b15227a5d65e757dc53926056fc4 Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 13 Dec 2012 11:21:51 -0500 Subject: [PATCH 06/56] autoSyndrome: made the syndrome logic make more sense. --- plugins/autoSyndrome.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index a37d806fe..1298764de 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -299,10 +299,17 @@ int32_t processJob(color_ostream& out, int32_t jobId) { //add each syndrome to the guy who did the job df::syndrome* syndrome = inorganic->material.syndrome[b]; //check that the syndrome applies to that guy - bool applies = syndrome->syn_affected_class.size() == 0; - if ( applies ) { - //out.print("No syn_affected_class.\n"); - } + /* + * If there is no affected class or affected creature, then anybody who isn't immune is fair game. + * + * Otherwise, it works like this: + * add all the affected class creatures + * remove all the immune class creatures + * add all the affected creatures + * remove all the immune creatures + * you're affected if and only if you're in the remaining list after all of that + **/ + bool applies = syndrome->syn_affected_class.size() == 0 && syndrome->syn_affected_creature_1.size() == 0; for ( size_t c = 0; c < syndrome->syn_affected_class.size(); c++ ) { if ( applies ) break; @@ -313,11 +320,6 @@ int32_t processJob(color_ostream& out, int32_t jobId) { } } } - if ( syndrome->syn_affected_creature_1.size() != 0 ) { - applies = false; - } else { - //out.print("No syn_affected_creature.\n"); - } for ( size_t c = 0; c < syndrome->syn_immune_class.size(); c++ ) { if ( !applies ) break; @@ -342,10 +344,6 @@ int32_t processJob(color_ostream& out, int32_t jobId) { break; } } - if ( !applies ) { - //out.print("Not in syn_affected_creature.\n"); - continue; - } for ( size_t c = 0; c < syndrome->syn_immune_creature_1.size(); c++ ) { if ( creature_name != *syndrome->syn_immune_creature_1[c] ) continue; @@ -356,7 +354,6 @@ int32_t processJob(color_ostream& out, int32_t jobId) { } } if ( !applies ) { - //out.print("Creature is immune.\n"); continue; } if ( giveSyndrome(out, workerId, syndrome) < 0 ) From 747723187f342a983a35fa3aad8ae90741486f7d Mon Sep 17 00:00:00 2001 From: expwnent Date: Fri, 14 Dec 2012 21:05:38 -0500 Subject: [PATCH 07/56] EventManager: first draft. --- library/CMakeLists.txt | 2 ++ library/Core.cpp | 3 +++ library/include/modules/Job.h | 2 +- library/modules/Job.cpp | 4 ++-- plugins/CMakeLists.txt | 1 + plugins/eventExample.cpp | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 plugins/eventExample.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 6f33d5c8a..615c223c2 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -111,6 +111,7 @@ include/modules/Burrows.h include/modules/Constructions.h include/modules/Units.h include/modules/Engravings.h +include/modules/EventManager.h include/modules/Gui.h include/modules/Items.h include/modules/Job.h @@ -133,6 +134,7 @@ modules/Burrows.cpp modules/Constructions.cpp modules/Units.cpp modules/Engravings.cpp +modules/EventManager.cpp modules/Gui.cpp modules/Items.cpp modules/Job.cpp diff --git a/library/Core.cpp b/library/Core.cpp index 26c0acbb0..a9164128f 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -44,6 +44,7 @@ using namespace std; #include "VersionInfo.h" #include "PluginManager.h" #include "ModuleFactory.h" +#include "modules/EventManager.h" #include "modules/Gui.h" #include "modules/World.h" #include "modules/Graphic.h" @@ -1238,6 +1239,8 @@ static int buildings_timer = 0; void Core::onUpdate(color_ostream &out) { + EventManager::manageEvents(out); + // convert building reagents if (buildings_do_onupdate && (++buildings_timer & 1)) buildings_onUpdate(out); diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index 853813073..e865273d9 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -47,7 +47,7 @@ namespace DFHack { namespace Job { // Duplicate the job structure. It is not linked into any DF lists. - DFHACK_EXPORT df::job *cloneJobStruct(df::job *job); + DFHACK_EXPORT df::job *cloneJobStruct(df::job *job, bool keepWorkerData=false); // Delete a cloned structure. DFHACK_EXPORT void deleteJobStruct(df::job *job); diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index def3b4192..c0e18f44a 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -54,7 +54,7 @@ using namespace std; using namespace DFHack; using namespace df::enums; -df::job *DFHack::Job::cloneJobStruct(df::job *job) +df::job *DFHack::Job::cloneJobStruct(df::job *job, bool keepWorkerData) { CHECK_NULL_POINTER(job); @@ -75,7 +75,7 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job) { df::general_ref *ref = pnew->references[i]; - if (virtual_cast(ref)) + if (!keepWorkerData && virtual_cast(ref)) vector_erase_at(pnew->references, i); else pnew->references[i] = ref->clone(); diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 8aeeee8c3..16fffd422 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -130,6 +130,7 @@ if (BUILD_SUPPORTED) #DFHACK_PLUGIN(versionosd versionosd.cpp) DFHACK_PLUGIN(misery misery.cpp) #DFHACK_PLUGIN(dfstream dfstream.cpp LINK_LIBRARIES clsocket dfhack-tinythread) + DFHACK_PLUGIN(eventExample eventExample.cpp) endif() diff --git a/plugins/eventExample.cpp b/plugins/eventExample.cpp new file mode 100644 index 000000000..ea8e2cfec --- /dev/null +++ b/plugins/eventExample.cpp @@ -0,0 +1,32 @@ + +#include "Console.h" +#include "Core.h" +#include "Export.h" +#include "modules/EventManager.h" +#include "DataDefs.h" + +using namespace DFHack; + +DFHACK_PLUGIN("eventExample"); + +void jobInitiated(color_ostream& out, void* job); +void jobCompleted(color_ostream& out, void* job); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + EventManager::EventHandler initiateHandler(jobInitiated); + EventManager::EventHandler completeHandler(jobCompleted); + + EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, NULL); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, NULL); + + return CR_OK; +} + +void jobInitiated(color_ostream& out, void* job) { + out.print("Job initiated! 0x%X\n", job); +} + +void jobCompleted(color_ostream& out, void* job) { + out.print("Job completed! 0x%X\n", job); +} + From cf619a519eb031531609604fd29a6e60a0893691 Mon Sep 17 00:00:00 2001 From: expwnent Date: Fri, 14 Dec 2012 22:14:38 -0500 Subject: [PATCH 08/56] EventManager: made event handlers unregister when plugins are unloaded. Also changed PluginManager so that plugins can call core.getPluginManager() during plugin_init. --- library/Core.cpp | 3 +++ library/PluginManager.cpp | 30 ++++++++++++++++++------------ library/include/PluginManager.h | 1 + plugins/eventExample.cpp | 6 ++++-- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index a9164128f..5c397dd05 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -905,6 +905,7 @@ bool Core::Init() cerr << "Initializing Plugins.\n"; // create plugin manager plug_mgr = new PluginManager(this); + plug_mgr->init(this); IODATA *temp = new IODATA; temp->core = this; temp->plug_mgr = plug_mgr; @@ -1254,6 +1255,8 @@ void Core::onUpdate(color_ostream &out) void Core::onStateChange(color_ostream &out, state_change_event event) { + EventManager::onStateChange(out, event); + buildings_onStateChange(out, event); plug_mgr->OnStateChange(out, event); diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 0c80639b4..86bab66cd 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -22,6 +22,7 @@ must not be misrepresented as being the original software. distribution. */ +#include "modules/EventManager.h" #include "Internal.h" #include "Core.h" #include "MemAccess.h" @@ -270,6 +271,7 @@ bool Plugin::unload(color_ostream &con) // if we are actually loaded if(state == PS_LOADED) { + EventManager::unregisterAll(this); // notify the plugin about an attempt to shutdown if (plugin_onstatechange && plugin_onstatechange(con, SC_BEGIN_UNLOAD) == CR_NOT_FOUND) @@ -598,6 +600,22 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn) } PluginManager::PluginManager(Core * core) +{ + cmdlist_mutex = new mutex(); + eval_ruby = NULL; +} + +PluginManager::~PluginManager() +{ + for(size_t i = 0; i < all_plugins.size();i++) + { + delete all_plugins[i]; + } + all_plugins.clear(); + delete cmdlist_mutex; +} + +void PluginManager::init(Core * core) { #ifdef LINUX_BUILD string path = core->getHackPath() + "plugins/"; @@ -606,8 +624,6 @@ PluginManager::PluginManager(Core * core) string path = core->getHackPath() + "plugins\\"; const string searchstr = ".plug.dll"; #endif - cmdlist_mutex = new mutex(); - eval_ruby = NULL; vector filez; getdir(path, filez); for(size_t i = 0; i < filez.size();i++) @@ -622,16 +638,6 @@ PluginManager::PluginManager(Core * core) } } -PluginManager::~PluginManager() -{ - for(size_t i = 0; i < all_plugins.size();i++) - { - delete all_plugins[i]; - } - all_plugins.clear(); - delete cmdlist_mutex; -} - Plugin *PluginManager::getPluginByName (const std::string & name) { for(size_t i = 0; i < all_plugins.size(); i++) diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 9ef16703a..62a195867 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -205,6 +205,7 @@ namespace DFHack friend class Plugin; PluginManager(Core * core); ~PluginManager(); + void init(Core* core); void OnUpdate(color_ostream &out); void OnStateChange(color_ostream &out, state_change_event event); void registerCommands( Plugin * p ); diff --git a/plugins/eventExample.cpp b/plugins/eventExample.cpp index ea8e2cfec..3b898ae7b 100644 --- a/plugins/eventExample.cpp +++ b/plugins/eventExample.cpp @@ -2,6 +2,7 @@ #include "Console.h" #include "Core.h" #include "Export.h" +#include "PluginManager.h" #include "modules/EventManager.h" #include "DataDefs.h" @@ -15,9 +16,10 @@ void jobCompleted(color_ostream& out, void* job); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { EventManager::EventHandler initiateHandler(jobInitiated); EventManager::EventHandler completeHandler(jobCompleted); + Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); - EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, NULL); - EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, NULL); + EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, me); return CR_OK; } From 155a4d044c45cf58ccb0bcbbc8168793faa0bb60 Mon Sep 17 00:00:00 2001 From: expwnent Date: Fri, 14 Dec 2012 23:29:28 -0500 Subject: [PATCH 09/56] EventManager: fiddled with time events. Made it possible to register for time events before a world is loaded. Also added some files I forgot to add to the previous commit. --- library/include/modules/EventManager.h | 51 ++++++ library/modules/EventManager.cpp | 216 +++++++++++++++++++++++++ plugins/eventExample.cpp | 9 ++ 3 files changed, 276 insertions(+) create mode 100644 library/include/modules/EventManager.h create mode 100644 library/modules/EventManager.cpp diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h new file mode 100644 index 000000000..a357bbb81 --- /dev/null +++ b/library/include/modules/EventManager.h @@ -0,0 +1,51 @@ +#pragma once +#ifndef EVENT_MANAGER_H_INCLUDED +#define EVENT_MANAGER_H_INCLUDED + +#include "Core.h" +#include "Export.h" +#include "ColorText.h" +#include "PluginManager.h" +#include "Console.h" + +namespace DFHack { + namespace EventManager { + namespace EventType { + enum EventType { + NONE, + TICK, + TICK_TILE, + JOB_INITIATED, + JOB_COMPLETED, + LIFE, + CREATURE, + ITEM, + TILE, + EVENT_MAX=TILE + }; + } + + struct EventHandler { + void (*eventHandler)(color_ostream&, void*); //called when the event happens + + EventHandler(void (*eventHandlerIn)(color_ostream&, void*)): eventHandler(eventHandlerIn) { + } + + bool operator==(EventHandler& handle) const { + return eventHandler == handle.eventHandler; + } + bool operator!=(EventHandler& handle) const { + return !( *this == handle); + } + }; + + DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin); + DFHACK_EXPORT void registerTick(EventHandler handler, int32_t when, Plugin* plugin); + DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin); + DFHACK_EXPORT void unregisterAll(Plugin* plugin); + void manageEvents(color_ostream& out); + void onStateChange(color_ostream& out, state_change_event event); + } +} + +#endif diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp new file mode 100644 index 000000000..c9a312841 --- /dev/null +++ b/library/modules/EventManager.cpp @@ -0,0 +1,216 @@ +#include "Core.h" +#include "modules/EventManager.h" +#include "modules/Job.h" +#include "modules/World.h" + +#include "df/job.h" +#include "df/global_objects.h" +#include "df/job_list_link.h" +#include "df/world.h" + +//#include +#include +//#include +using namespace std; +using namespace DFHack; +using namespace EventManager; + +/* + * TODO: + * error checking + **/ + +//map > tickQueue; +multimap tickQueue; + +multimap handlers[EventType::EVENT_MAX]; + +const uint32_t ticksPerYear = 403200; + +void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin) { + handlers[e].insert(pair(plugin, handler)); +} + +void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plugin* plugin) { + uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + + DFHack::World::ReadCurrentTick(); + if ( !Core::getInstance().isWorldLoaded() ) { + tick = 0; + } + + tickQueue.insert(pair(tick+(uint32_t)when, handler)); + handlers[EventType::TICK].insert(pair(plugin,handler)); + return; +} + +void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handler, Plugin* plugin) { + for ( multimap::iterator i = handlers[e].find(plugin); i != handlers[e].end(); i++ ) { + if ( (*i).first != plugin ) + break; + EventHandler handle = (*i).second; + if ( handle == handler ) { + handlers[e].erase(i); + break; + } + } + return; +} + +void DFHack::EventManager::unregisterAll(Plugin* plugin) { + for ( auto i = handlers[EventType::TICK].find(plugin); i != handlers[EventType::TICK].end(); i++ ) { + if ( (*i).first != plugin ) + break; + + //shenanigans to avoid concurrent modification + EventHandler getRidOf = (*i).second; + bool didSomething; + do { + didSomething = false; + for ( auto j = tickQueue.begin(); j != tickQueue.end(); j++ ) { + EventHandler candidate = (*j).second; + if ( getRidOf != candidate ) + continue; + tickQueue.erase(j); + didSomething = true; + break; + } + } while(didSomething); + } + for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) { + handlers[a].erase(plugin); + } + return; +} + +static void manageTickEvent(color_ostream& out); +static void manageJobInitiatedEvent(color_ostream& out); +static void manageJobCompletedEvent(color_ostream& out); + +uint32_t lastTick = 0; +int32_t lastJobId = -1; +static map prevJobs; + +void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { + if ( event == DFHack::SC_MAP_UNLOADED ) { + lastTick = 0; + lastJobId = -1; + for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { + Job::deleteJobStruct((*i).second); + } + prevJobs.clear(); + tickQueue.clear(); + } else if ( event == DFHack::SC_MAP_LOADED ) { + uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + + DFHack::World::ReadCurrentTick(); + multimap newTickQueue; + for ( auto i = tickQueue.begin(); i != tickQueue.end(); i++ ) { + newTickQueue.insert(pair(tick + (*i).first, (*i).second)); + } + tickQueue.clear(); + + tickQueue.insert(newTickQueue.begin(), newTickQueue.end()); + } +} + +void DFHack::EventManager::manageEvents(color_ostream& out) { + if ( !Core::getInstance().isWorldLoaded() ) { + return; + } + uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + + DFHack::World::ReadCurrentTick(); + if ( tick <= lastTick ) + return; + lastTick = tick; + + manageTickEvent(out); + manageJobInitiatedEvent(out); + manageJobCompletedEvent(out); + + return; +} + +static void manageTickEvent(color_ostream& out) { + uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + + DFHack::World::ReadCurrentTick(); + while ( !tickQueue.empty() ) { + if ( tick < (*tickQueue.begin()).first ) + break; + EventHandler handle = (*tickQueue.begin()).second; + tickQueue.erase(tickQueue.begin()); + handle.eventHandler(out, (void*)tick); + } + +} + +static void manageJobInitiatedEvent(color_ostream& out) { + if ( handlers[EventType::JOB_INITIATED].empty() ) + return; + + if ( lastJobId == -1 ) { + lastJobId = *df::global::job_next_id - 1; + return; + } + + if ( lastJobId+1 == *df::global::job_next_id ) { + return; //no new jobs + } + + for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) { + if ( link->item == NULL ) + continue; + if ( link->item->id <= lastJobId ) + continue; + for ( multimap::iterator i = handlers[EventType::JOB_INITIATED].begin(); i != handlers[EventType::JOB_INITIATED].end(); i++ ) { + (*i).second.eventHandler(out, (void*)link->item); + } + } + + lastJobId = *df::global::job_next_id - 1; +} + + +static void manageJobCompletedEvent(color_ostream& out) { + if ( handlers[EventType::JOB_COMPLETED].empty() ) { + return; + } + + map nowJobs; + for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) { + if ( link->item == NULL ) + continue; + nowJobs[link->item->id] = link->item; + } + + for ( map::iterator i = prevJobs.begin(); i != prevJobs.end(); i++ ) { + if ( nowJobs.find((*i).first) != nowJobs.end() ) + continue; + + //recently finished or cancelled job! + for ( multimap::iterator j = handlers[EventType::JOB_COMPLETED].begin(); j != handlers[EventType::JOB_COMPLETED].end(); j++ ) { + (*j).second.eventHandler(out, (void*)(*i).second); + } + } + + //erase old jobs, copy over possibly altered jobs + for ( map::iterator i = prevJobs.begin(); i != prevJobs.end(); i++ ) { + Job::deleteJobStruct((*i).second); + } + prevJobs.clear(); + + //create new jobs + for ( map::iterator j = nowJobs.begin(); j != nowJobs.end(); j++ ) { + /*map::iterator i = prevJobs.find((*j).first); + if ( i != prevJobs.end() ) { + continue; + }*/ + + df::job* newJob = Job::cloneJobStruct((*j).second, true); + prevJobs[newJob->id] = newJob; + } + + /*//get rid of old pointers to deallocated jobs + for ( size_t a = 0; a < toDelete.size(); a++ ) { + prevJobs.erase(a); + }*/ +} + diff --git a/plugins/eventExample.cpp b/plugins/eventExample.cpp index 3b898ae7b..c2774f422 100644 --- a/plugins/eventExample.cpp +++ b/plugins/eventExample.cpp @@ -12,14 +12,20 @@ DFHACK_PLUGIN("eventExample"); void jobInitiated(color_ostream& out, void* job); void jobCompleted(color_ostream& out, void* job); +void timePassed(color_ostream& out, void* ptr); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { EventManager::EventHandler initiateHandler(jobInitiated); EventManager::EventHandler completeHandler(jobCompleted); + EventManager::EventHandler timeHandler(timePassed); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, me); + EventManager::registerTick(timeHandler, 1, me); + EventManager::registerTick(timeHandler, 2, me); + EventManager::registerTick(timeHandler, 4, me); + EventManager::registerTick(timeHandler, 8, me); return CR_OK; } @@ -32,3 +38,6 @@ void jobCompleted(color_ostream& out, void* job) { out.print("Job completed! 0x%X\n", job); } +void timePassed(color_ostream& out, void* ptr) { + out.print("Time: %d\n", (int32_t)(ptr)); +} From b0314755e0c03d66b6c11abccdffd1b5583d606e Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 15 Dec 2012 14:40:11 -0500 Subject: [PATCH 10/56] EventManager: added unit death event. --- library/include/modules/EventManager.h | 9 +++----- library/modules/EventManager.cpp | 31 ++++++++++++++++++++++++-- plugins/eventExample.cpp | 7 ++++++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index a357bbb81..0794b29e3 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -14,14 +14,11 @@ namespace DFHack { enum EventType { NONE, TICK, - TICK_TILE, JOB_INITIATED, JOB_COMPLETED, - LIFE, - CREATURE, - ITEM, - TILE, - EVENT_MAX=TILE + UNIT_DEATH, + //ITEM_CREATED, + EVENT_MAX=UNIT_DEATH }; } diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index c9a312841..7e97cc3a4 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -6,6 +6,7 @@ #include "df/job.h" #include "df/global_objects.h" #include "df/job_list_link.h" +#include "df/unit.h" #include "df/world.h" //#include @@ -85,10 +86,12 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) { static void manageTickEvent(color_ostream& out); static void manageJobInitiatedEvent(color_ostream& out); static void manageJobCompletedEvent(color_ostream& out); +static void manageUnitDeathEvent(color_ostream& out); -uint32_t lastTick = 0; -int32_t lastJobId = -1; +static uint32_t lastTick = 0; +static int32_t lastJobId = -1; static map prevJobs; +static set livingUnits; void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { if ( event == DFHack::SC_MAP_UNLOADED ) { @@ -99,6 +102,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } prevJobs.clear(); tickQueue.clear(); + livingUnits.clear(); } else if ( event == DFHack::SC_MAP_LOADED ) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); @@ -125,6 +129,7 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { manageTickEvent(out); manageJobInitiatedEvent(out); manageJobCompletedEvent(out); + manageUnitDeathEvent(out); return; } @@ -214,3 +219,25 @@ static void manageJobCompletedEvent(color_ostream& out) { }*/ } +static void manageUnitDeathEvent(color_ostream& out) { + if ( handlers[EventType::UNIT_DEATH].empty() ) { + return; + } + + for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) { + df::unit* unit = df::global::world->units.active[a]; + if ( unit->counters.death_id == -1 ) { + livingUnits.insert(unit->id); + continue; + } + //dead: if dead since last check, trigger events + if ( livingUnits.find(unit->id) == livingUnits.end() ) + continue; + + for ( auto i = handlers[EventType::UNIT_DEATH].begin(); i != handlers[EventType::UNIT_DEATH].end(); i++ ) { + (*i).second.eventHandler(out, (void*)unit->id); + } + livingUnits.erase(unit->id); + } +} + diff --git a/plugins/eventExample.cpp b/plugins/eventExample.cpp index c2774f422..2951c7f82 100644 --- a/plugins/eventExample.cpp +++ b/plugins/eventExample.cpp @@ -13,11 +13,13 @@ DFHACK_PLUGIN("eventExample"); void jobInitiated(color_ostream& out, void* job); void jobCompleted(color_ostream& out, void* job); void timePassed(color_ostream& out, void* ptr); +void unitDeath(color_ostream& out, void* ptr); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { EventManager::EventHandler initiateHandler(jobInitiated); EventManager::EventHandler completeHandler(jobCompleted); EventManager::EventHandler timeHandler(timePassed); + EventManager::EventHandler deathHandler(unitDeath); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); @@ -26,6 +28,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector Date: Sat, 15 Dec 2012 16:49:13 -0500 Subject: [PATCH 11/56] EventManager: added item creation event. --- library/include/modules/EventManager.h | 4 +-- library/modules/EventManager.cpp | 42 ++++++++++++++++++++++++-- plugins/eventExample.cpp | 18 +++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 0794b29e3..31ac01c18 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -17,8 +17,8 @@ namespace DFHack { JOB_INITIATED, JOB_COMPLETED, UNIT_DEATH, - //ITEM_CREATED, - EVENT_MAX=UNIT_DEATH + ITEM_CREATED, + EVENT_MAX }; } diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 7e97cc3a4..63455b664 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -3,8 +3,9 @@ #include "modules/Job.h" #include "modules/World.h" -#include "df/job.h" #include "df/global_objects.h" +#include "df/item.h" +#include "df/job.h" #include "df/job_list_link.h" #include "df/unit.h" #include "df/world.h" @@ -87,11 +88,13 @@ static void manageTickEvent(color_ostream& out); static void manageJobInitiatedEvent(color_ostream& out); static void manageJobCompletedEvent(color_ostream& out); static void manageUnitDeathEvent(color_ostream& out); +static void manageItemCreationEvent(color_ostream& out); static uint32_t lastTick = 0; static int32_t lastJobId = -1; static map prevJobs; static set livingUnits; +static int32_t nextItem; void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { if ( event == DFHack::SC_MAP_UNLOADED ) { @@ -103,6 +106,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event prevJobs.clear(); tickQueue.clear(); livingUnits.clear(); + nextItem = -1; } else if ( event == DFHack::SC_MAP_LOADED ) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); @@ -113,6 +117,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event tickQueue.clear(); tickQueue.insert(newTickQueue.begin(), newTickQueue.end()); + + nextItem = *df::global::item_next_id; } } @@ -130,7 +136,8 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { manageJobInitiatedEvent(out); manageJobCompletedEvent(out); manageUnitDeathEvent(out); - + manageItemCreationEvent(out); + return; } @@ -241,3 +248,34 @@ static void manageUnitDeathEvent(color_ostream& out) { } } +static void manageItemCreationEvent(color_ostream& out) { + if ( handlers[EventType::ITEM_CREATED].empty() ) { + return; + } + + if ( nextItem >= *df::global::item_next_id ) { + return; + } + + size_t index = df::item::binsearch_index(df::global::world->items.all, nextItem, false); + for ( size_t a = index; a < df::global::world->items.all.size(); a++ ) { + df::item* item = df::global::world->items.all[a]; + //invaders + if ( item->flags.bits.foreign ) + continue; + //traders who bring back your items? + if ( item->flags.bits.trader ) + continue; + //migrants + if ( item->flags.bits.owned ) + continue; + //spider webs don't count + if ( item->flags.bits.spider_web ) + continue; + for ( auto i = handlers[EventType::ITEM_CREATED].begin(); i != handlers[EventType::ITEM_CREATED].end(); i++ ) { + (*i).second.eventHandler(out, (void*)item->id); + } + } + nextItem = *df::global::item_next_id; +} + diff --git a/plugins/eventExample.cpp b/plugins/eventExample.cpp index 2951c7f82..04657f1fd 100644 --- a/plugins/eventExample.cpp +++ b/plugins/eventExample.cpp @@ -6,6 +6,9 @@ #include "modules/EventManager.h" #include "DataDefs.h" +#include "df/item.h" +#include "df/world.h" + using namespace DFHack; DFHACK_PLUGIN("eventExample"); @@ -14,12 +17,14 @@ void jobInitiated(color_ostream& out, void* job); void jobCompleted(color_ostream& out, void* job); void timePassed(color_ostream& out, void* ptr); void unitDeath(color_ostream& out, void* ptr); +void itemCreate(color_ostream& out, void* ptr); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { EventManager::EventHandler initiateHandler(jobInitiated); EventManager::EventHandler completeHandler(jobCompleted); EventManager::EventHandler timeHandler(timePassed); EventManager::EventHandler deathHandler(unitDeath); + EventManager::EventHandler itemHandler(itemCreate); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); @@ -29,6 +34,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vectoritems.all, (int32_t)ptr); + if ( item_index == -1 ) { + out.print("%s, %d: Error.\n", __FILE__, __LINE__); + } + df::item* item = df::global::world->items.all[item_index]; + df::item_type type = item->getType(); + df::coord pos = item->pos; + out.print("Item created: %d, %s, at (%d,%d,%d)\n", (int32_t)(ptr), ENUM_KEY_STR(item_type, type).c_str(), pos.x, pos.y, pos.z); +} + From 935058f0a5edbdc52476d4a5e33d766f6fb650bd Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 15 Dec 2012 17:43:41 -0500 Subject: [PATCH 12/56] EventManager: moved files around, made eventExample not run by default, and got rid of the silly NONE event type. --- library/include/modules/EventManager.h | 1 - plugins/CMakeLists.txt | 1 - plugins/devel/CMakeLists.txt | 1 + plugins/{ => devel}/eventExample.cpp | 13 ++++++++++++- 4 files changed, 13 insertions(+), 3 deletions(-) rename plugins/{ => devel}/eventExample.cpp (87%) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 31ac01c18..1a6f15245 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -12,7 +12,6 @@ namespace DFHack { namespace EventManager { namespace EventType { enum EventType { - NONE, TICK, JOB_INITIATED, JOB_COMPLETED, diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 16fffd422..8aeeee8c3 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -130,7 +130,6 @@ if (BUILD_SUPPORTED) #DFHACK_PLUGIN(versionosd versionosd.cpp) DFHACK_PLUGIN(misery misery.cpp) #DFHACK_PLUGIN(dfstream dfstream.cpp LINK_LIBRARIES clsocket dfhack-tinythread) - DFHACK_PLUGIN(eventExample eventExample.cpp) endif() diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 134d5cb67..80d627fa9 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -18,6 +18,7 @@ DFHACK_PLUGIN(stripcaged stripcaged.cpp) DFHACK_PLUGIN(rprobe rprobe.cpp) DFHACK_PLUGIN(nestboxes nestboxes.cpp) DFHACK_PLUGIN(vshook vshook.cpp) +DFHACK_PLUGIN(eventExample eventExample.cpp) IF(UNIX) DFHACK_PLUGIN(ref-index ref-index.cpp) ENDIF() diff --git a/plugins/eventExample.cpp b/plugins/devel/eventExample.cpp similarity index 87% rename from plugins/eventExample.cpp rename to plugins/devel/eventExample.cpp index 04657f1fd..2099be110 100644 --- a/plugins/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -9,7 +9,10 @@ #include "df/item.h" #include "df/world.h" +#include + using namespace DFHack; +using namespace std; DFHACK_PLUGIN("eventExample"); @@ -19,7 +22,15 @@ void timePassed(color_ostream& out, void* ptr); void unitDeath(color_ostream& out, void* ptr); void itemCreate(color_ostream& out, void* ptr); +command_result eventExample(color_ostream& out, vector& parameters); + + DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + commands.push_back(PluginCommand("eventExample", "Sets up a few event triggers.",eventExample)); + return CR_OK; +} + +command_result eventExample(color_ostream& out, vector& parameters) { EventManager::EventHandler initiateHandler(jobInitiated); EventManager::EventHandler completeHandler(jobCompleted); EventManager::EventHandler timeHandler(timePassed); @@ -35,7 +46,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector Date: Sat, 15 Dec 2012 18:08:59 -0500 Subject: [PATCH 13/56] EventManager: Allowed absolute time registration. --- library/include/modules/EventManager.h | 2 +- library/modules/EventManager.cpp | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 1a6f15245..c1c09da7d 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -36,7 +36,7 @@ namespace DFHack { }; DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin); - DFHACK_EXPORT void registerTick(EventHandler handler, int32_t when, Plugin* plugin); + DFHACK_EXPORT void registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false); DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin); DFHACK_EXPORT void unregisterAll(Plugin* plugin); void manageEvents(color_ostream& out); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 63455b664..553a69668 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1,4 +1,5 @@ #include "Core.h" +#include "Console.h" #include "modules/EventManager.h" #include "modules/Job.h" #include "modules/World.h" @@ -33,11 +34,17 @@ void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handlers[e].insert(pair(plugin, handler)); } -void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plugin* plugin) { +void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); if ( !Core::getInstance().isWorldLoaded() ) { tick = 0; + if ( absolute ) { + Core::getInstance().getConsole().print("Warning: absolute flag will not be honored.\n"); + } + } + if ( absolute ) { + tick = 0; } tickQueue.insert(pair(tick+(uint32_t)when, handler)); From 3e5537e3213d2c6d85ec783314b07cbf8efa4137 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 15 Dec 2012 18:47:51 -0500 Subject: [PATCH 14/56] autoSyndrome: made it use EventManager. --- plugins/autoSyndrome.cpp | 151 ++++++++++++--------------------------- 1 file changed, 47 insertions(+), 104 deletions(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 1298764de..0f8b1ced3 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -2,6 +2,11 @@ #include "Export.h" #include "DataDefs.h" #include "Core.h" + +#include "modules/EventManager.h" +#include "modules/Job.h" +#include "modules/Maps.h" + #include "df/caste_raw.h" #include "df/creature_raw.h" #include "df/global_objects.h" @@ -18,8 +23,6 @@ #include "df/general_ref.h" #include "df/general_ref_type.h" #include "df/general_ref_unit_workerst.h" -#include "modules/Maps.h" -#include "modules/Job.h" #include #include @@ -92,15 +95,12 @@ Next, start a new fort in a new world, build a duck workshop, then have someone */ const int32_t ticksPerYear = 403200; -int32_t lastRun = 0; -unordered_map prevJobs; -unordered_map jobWorkers; bool enabled = true; DFHACK_PLUGIN("autoSyndrome"); command_result autoSyndrome(color_ostream& out, vector& parameters); -int32_t processJob(color_ostream& out, int32_t id); +void processJob(color_ostream& out, void* jobPtr); int32_t giveSyndrome(color_ostream& out, int32_t workerId, df::syndrome* syndrome); DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { @@ -123,6 +123,9 @@ DFhackCExport command_result plugin_init(color_ostream& out, vectorgetPluginByName("autoSyndrome"); + EventManager::EventHandler handle(processJob); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me); return CR_OK; } @@ -130,85 +133,6 @@ DFhackCExport command_result plugin_shutdown(color_ostream& out) { return CR_OK; } -DFhackCExport command_result plugin_onupdate(color_ostream& out) { - if ( !enabled ) - return CR_OK; - if(DFHack::Maps::IsValid() == false) { - return CR_OK; - } - - //don't run more than once per tick - int32_t time = (*df::global::cur_year)*ticksPerYear + (*df::global::cur_year_tick); - if ( time <= lastRun ) - return CR_OK; - lastRun = time; - - //keep track of all queued jobs. When one completes (and is not cancelled), check if it's a boiling rock job, and if so, give the worker the appropriate syndrome - unordered_map jobs; - df::job_list_link* link = &df::global::world->job_list; - for( ; link != NULL; link = link->next ) { - df::job* item = link->item; - if ( item == NULL ) - continue; - //-1 usually means it hasn't been assigned yet. - if ( item->completion_timer < 0 ) - continue; - //if the completion timer is more than one, then the job will never disappear next tick unless it's cancelled - if ( item->completion_timer > 1 ) - continue; - - //only consider jobs that have been started - int32_t workerId = -1; - for ( size_t a = 0; a < item->references.size(); a++ ) { - if ( item->references[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) - continue; - if ( workerId != -1 ) { - out.print("%s, line %d: Found two workers on the same job.\n", __FILE__, __LINE__); - } - workerId = ((df::general_ref_unit_workerst*)item->references[a])->unit_id; - } - if ( workerId == -1 ) - continue; - - jobs[item->id] = item; - jobWorkers[item->id] = workerId; - } - - //if it's not on the job list anymore, and its completion timer was 0, then it probably finished and was not cancelled, so process the job. - for ( unordered_map::iterator i = prevJobs.begin(); i != prevJobs.end(); i++ ) { - int32_t id = (*i).first; - df::job* completion = (*i).second; - if ( jobs.find(id) != jobs.end() ) - continue; - if ( completion->completion_timer > 0 ) - continue; - if ( processJob(out, id) < 0 ) { - //enabled = false; - return CR_FAILURE; - } - } - - //delete obselete job copies - for ( unordered_map::iterator i = prevJobs.begin(); i != prevJobs.end(); i++ ) { - int32_t id = (*i).first; - df::job* oldJob = (*i).second; - DFHack::Job::deleteJobStruct(oldJob); - if ( jobs.find(id) == jobs.end() ) - jobWorkers.erase(id); - } - prevJobs.clear(); - - //make copies of the jobs we looked at this tick in case they disappear next frame. - for ( unordered_map::iterator i = jobs.begin(); i != jobs.end(); i++ ) { - int32_t id = (*i).first; - df::job* oldJob = (*i).second; - df::job* jobCopy = DFHack::Job::cloneJobStruct(oldJob); - prevJobs[id] = jobCopy; - } - - return CR_OK; -} - /*DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event e) { return CR_OK; }*/ @@ -217,6 +141,7 @@ command_result autoSyndrome(color_ostream& out, vector& parameters) { if ( parameters.size() > 1 ) return CR_WRONG_USAGE; + bool wasEnabled = enabled; if ( parameters.size() == 1 ) { if ( parameters[0] == "enable" ) { enabled = true; @@ -232,22 +157,31 @@ command_result autoSyndrome(color_ostream& out, vector& parameters) { } out.print("autoSyndrome is %s\n", enabled ? "enabled" : "disabled"); + if ( enabled == wasEnabled ) + return CR_OK; + + Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome"); + if ( enabled ) { + EventManager::EventHandler handle(processJob); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me); + } else { + EventManager::unregisterAll(me); + } return CR_OK; } -int32_t processJob(color_ostream& out, int32_t jobId) { - df::job* job = prevJobs[jobId]; +void processJob(color_ostream& out, void* jobPtr) { + df::job* job = (df::job*)jobPtr; if ( job == NULL ) { - out.print("Error %s line %d: couldn't find job %d.\n", __FILE__, __LINE__, jobId); - return -1; + out.print("Error %s line %d: null job.\n", __FILE__, __LINE__); + return; } + if ( job->completion_timer > 0 ) + return; if ( job->job_type != df::job_type::CustomReaction ) - return 0; + return; - //find the custom reaction raws and see if we have any special tags there - //out.print("job: \"%s\"\n", job->reaction_name.c_str()); - df::reaction* reaction = NULL; for ( size_t a = 0; a < df::global::world->raws.reactions.size(); a++ ) { df::reaction* candidate = df::global::world->raws.reactions[a]; @@ -258,18 +192,27 @@ int32_t processJob(color_ostream& out, int32_t jobId) { } if ( reaction == NULL ) { out.print("%s, line %d: could not find reaction \"%s\".\n", __FILE__, __LINE__, job->reaction_name.c_str() ); - return -1; + return; } - - if ( jobWorkers.find(jobId) == jobWorkers.end() ) { - out.print("%s, line %d: couldn't find worker for job %d.\n", __FILE__, __LINE__, jobId); - return -1; + + int32_t workerId = -1; + for ( size_t a = 0; a < job->references.size(); a++ ) { + if ( job->references[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) + continue; + if ( workerId != -1 ) { + out.print("%s, line %d: Found two workers on the same job.\n", __FILE__, __LINE__); + } + workerId = ((df::general_ref_unit_workerst*)job->references[a])->unit_id; + if (workerId == -1) { + out.print("%s, line %d: invalid worker.\n", __FILE__, __LINE__); + continue; + } } - int32_t workerId = jobWorkers[jobId]; + int32_t workerIndex = df::unit::binsearch_index(df::global::world->units.all, workerId); if ( workerIndex < 0 ) { out.print("%s line %d: Couldn't find unit %d.\n", __FILE__, __LINE__, workerId); - return -1; + return; } df::unit* unit = df::global::world->units.all[workerIndex]; df::creature_raw* creature = df::global::world->raws.creatures.all[unit->race]; @@ -333,7 +276,7 @@ int32_t processJob(color_ostream& out, int32_t jobId) { if ( syndrome->syn_affected_creature_1.size() != syndrome->syn_affected_creature_2.size() ) { out.print("%s, line %d: different affected creature/caste sizes.\n", __FILE__, __LINE__); - return -1; + return; } for ( size_t c = 0; c < syndrome->syn_affected_creature_1.size(); c++ ) { if ( creature_name != *syndrome->syn_affected_creature_1[c] ) @@ -357,13 +300,13 @@ int32_t processJob(color_ostream& out, int32_t jobId) { continue; } if ( giveSyndrome(out, workerId, syndrome) < 0 ) - return -1; + return; } } if ( !foundIt ) - return 0; + return; - return -2; + return; } /* From 75db99a3c79c6f410c8db3e13117ff9ef5911422 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 15 Dec 2012 18:49:45 -0500 Subject: [PATCH 15/56] autoSyndrome: deleted an unused constant. --- plugins/autoSyndrome.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 0f8b1ced3..f2c86bdfd 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -94,7 +94,6 @@ reaction_duck Next, start a new fort in a new world, build a duck workshop, then have someone become a duck. */ -const int32_t ticksPerYear = 403200; bool enabled = true; DFHACK_PLUGIN("autoSyndrome"); From 3951d4d204ddaeaad7f393fadae3a98f27c69c4d Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 16 Dec 2012 15:39:39 -0500 Subject: [PATCH 16/56] EventManager: made it safe to register/unregister while events are being triggered. --- library/modules/EventManager.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 553a69668..d8ea0d5de 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -173,13 +173,14 @@ static void manageJobInitiatedEvent(color_ostream& out) { if ( lastJobId+1 == *df::global::job_next_id ) { return; //no new jobs } + multimap copy(handlers[EventType::JOB_INITIATED].begin(), handlers[EventType::JOB_INITIATED].end()); for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) { if ( link->item == NULL ) continue; if ( link->item->id <= lastJobId ) continue; - for ( multimap::iterator i = handlers[EventType::JOB_INITIATED].begin(); i != handlers[EventType::JOB_INITIATED].end(); i++ ) { + for ( auto i = copy.begin(); i != copy.end(); i++ ) { (*i).second.eventHandler(out, (void*)link->item); } } @@ -193,6 +194,7 @@ static void manageJobCompletedEvent(color_ostream& out) { return; } + multimap copy(handlers[EventType::JOB_COMPLETED].begin(), handlers[EventType::JOB_COMPLETED].end()); map nowJobs; for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) { if ( link->item == NULL ) @@ -205,7 +207,7 @@ static void manageJobCompletedEvent(color_ostream& out) { continue; //recently finished or cancelled job! - for ( multimap::iterator j = handlers[EventType::JOB_COMPLETED].begin(); j != handlers[EventType::JOB_COMPLETED].end(); j++ ) { + for ( auto j = copy.begin(); j != copy.end(); j++ ) { (*j).second.eventHandler(out, (void*)(*i).second); } } @@ -238,6 +240,7 @@ static void manageUnitDeathEvent(color_ostream& out) { return; } + multimap copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) { df::unit* unit = df::global::world->units.active[a]; if ( unit->counters.death_id == -1 ) { @@ -248,7 +251,7 @@ static void manageUnitDeathEvent(color_ostream& out) { if ( livingUnits.find(unit->id) == livingUnits.end() ) continue; - for ( auto i = handlers[EventType::UNIT_DEATH].begin(); i != handlers[EventType::UNIT_DEATH].end(); i++ ) { + for ( auto i = copy.begin(); i != copy.end(); i++ ) { (*i).second.eventHandler(out, (void*)unit->id); } livingUnits.erase(unit->id); @@ -264,6 +267,7 @@ static void manageItemCreationEvent(color_ostream& out) { return; } + multimap copy(handlers[EventType::ITEM_CREATED].begin(), handlers[EventType::ITEM_CREATED].end()); size_t index = df::item::binsearch_index(df::global::world->items.all, nextItem, false); for ( size_t a = index; a < df::global::world->items.all.size(); a++ ) { df::item* item = df::global::world->items.all[a]; @@ -279,7 +283,7 @@ static void manageItemCreationEvent(color_ostream& out) { //spider webs don't count if ( item->flags.bits.spider_web ) continue; - for ( auto i = handlers[EventType::ITEM_CREATED].begin(); i != handlers[EventType::ITEM_CREATED].end(); i++ ) { + for ( auto i = copy.begin(); i != copy.end(); i++ ) { (*i).second.eventHandler(out, (void*)item->id); } } From 78aab90f3aa8a11bcdf741328684d8ed4fec4481 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 16 Dec 2012 16:27:08 -0500 Subject: [PATCH 17/56] EventManager: whitespace. --- library/modules/EventManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index d8ea0d5de..187a95819 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -188,7 +188,6 @@ static void manageJobInitiatedEvent(color_ostream& out) { lastJobId = *df::global::job_next_id - 1; } - static void manageJobCompletedEvent(color_ostream& out) { if ( handlers[EventType::JOB_COMPLETED].empty() ) { return; From 01e5e938257ada98c2582b4b11ab84b8cb0f5035 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 16 Dec 2012 23:26:50 -0500 Subject: [PATCH 18/56] Renamed Maps::canWalkBetween to Maps::canPathBetween and added Maps::canWalkBetween, which does what it says. --- library/LuaApi.cpp | 2 +- library/include/modules/Maps.h | 1 + library/modules/Maps.cpp | 46 +++++++++++++++++- plugins/devel/CMakeLists.txt | 1 + plugins/devel/walkBetween.cpp | 87 ++++++++++++++++++++++++++++++++++ plugins/fix-armory.cpp | 2 +- 6 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 plugins/devel/walkBetween.cpp diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 0151ed404..720ccd21f 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1309,7 +1309,7 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = { WRAPM(Maps, enableBlockUpdates), WRAPM(Maps, getGlobalInitFeature), WRAPM(Maps, getLocalInitFeature), - WRAPM(Maps, canWalkBetween), + WRAPM(Maps, canPathBetween), WRAPM(Maps, spawnFlow), WRAPN(hasTileAssignment, hasTileAssignment), WRAPN(getTileAssignment, getTileAssignment), diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 632e8ec13..2655268d7 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -308,6 +308,7 @@ extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block, /// remove a block event from the block by address extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which ); +DFHACK_EXPORT bool canPathBetween(df::coord pos1, df::coord pos2); DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2); } } diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 482b950ba..0db69ea82 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -521,7 +521,7 @@ bool Maps::ReadGeology(vector > *layer_mats, vector return true; } -bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) +bool Maps::canPathBetween(df::coord pos1, df::coord pos2) { auto block1 = getTileBlock(pos1); auto block2 = getTileBlock(pos2); @@ -535,6 +535,50 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) return tile1 && tile1 == tile2; } +bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) +{ + bool b = canPathBetween(pos1, pos2); + if ( !b ) + return false; + + int32_t dx = pos1.x-pos2.x; + int32_t dy = pos1.y-pos2.y; + int32_t dz = pos1.z-pos2.z; + + if ( dx*dx > 1 || dy*dy > 1 || dz*dz > 1 ) + return false; + + if ( dz == 0 ) + return true; + + df::tiletype* type1 = Maps::getTileType(pos1); + df::tiletype* type2 = Maps::getTileType(pos2); + + df::tiletype_shape shape1 = ENUM_ATTR(tiletype,shape,*type1); + df::tiletype_shape shape2 = ENUM_ATTR(tiletype,shape,*type2); + + if ( pos2.z < pos1.z ) { + df::tiletype_shape temp = shape1; + shape1 = shape2; + shape2 = temp; + } + if ( dx == 0 && dy == 0 ) { + if ( shape1 == tiletype_shape::STAIR_UPDOWN && shape2 == shape1 ) + return true; + if ( shape1 == tiletype_shape::STAIR_UPDOWN && shape2 == tiletype_shape::STAIR_DOWN ) + return true; + if ( shape1 == tiletype_shape::STAIR_UP && shape2 == tiletype_shape::STAIR_UPDOWN ) + return true; + return false; + } + + //diagonal up: has to be a ramp + if ( shape1 == tiletype_shape::RAMP && shape2 == tiletype_shape::RAMP ) + return true; + + return false; +} + #define COPY(a,b) memcpy(&a,&b,sizeof(a)) MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 134d5cb67..41fcd8130 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -21,3 +21,4 @@ DFHACK_PLUGIN(vshook vshook.cpp) IF(UNIX) DFHACK_PLUGIN(ref-index ref-index.cpp) ENDIF() +DFHACK_PLUGIN(walkBetween walkBetween.cpp) diff --git a/plugins/devel/walkBetween.cpp b/plugins/devel/walkBetween.cpp new file mode 100644 index 000000000..9dafe4bb8 --- /dev/null +++ b/plugins/devel/walkBetween.cpp @@ -0,0 +1,87 @@ + +#include "Core.h" +#include +#include +#include + +// DF data structure definition headers +#include "DataDefs.h" +#include "df/world.h" + +#include "modules/Gui.h" +#include "modules/Maps.h" + +using namespace DFHack; +using namespace df::enums; + + + +command_result walkBetween (color_ostream &out, std::vector & parameters); + +// A plugin must be able to return its name and version. +// The name string provided must correspond to the filename - skeleton.plug.so or skeleton.plug.dll in this case +DFHACK_PLUGIN("walkBetween"); + +// Mandatory init function. If you have some global state, create it here. +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + // Fill the command list with your commands. + commands.push_back(PluginCommand( + "walkBetween", "Do nothing, look pretty.", + walkBetween, false, /* true means that the command can't be used from non-interactive user interface */ + // Extended help string. Used by CR_WRONG_USAGE and the help command: + " This command does nothing at all.\n" + )); + return CR_OK; +} + +// This is called right before the plugin library is removed from memory. +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + // You *MUST* kill all threads you created before this returns. + // If everything fails, just return CR_FAILURE. Your plugin will be + // in a zombie state, but things won't crash. + return CR_OK; +} + +// Called to notify the plugin about important state changes. +// Invoked with DF suspended, and always before the matching plugin_onupdate. +// More event codes may be added in the future. +/* +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_GAME_LOADED: + // initialize from the world just loaded + break; + case SC_GAME_UNLOADED: + // cleanup + break; + default: + break; + } + return CR_OK; +} +*/ + +// Whatever you put here will be done in each game step. Don't abuse it. +// It's optional, so you can just comment it out like this if you don't need it. +/* +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + // whetever. You don't need to suspend DF execution here. + return CR_OK; +} +*/ + +df::coord prev; + +// A command! It sits around and looks pretty. And it's nice and friendly. +command_result walkBetween (color_ostream &out, std::vector & parameters) +{ + df::coord bob = Gui::getCursorPos(); + out.print("(%d,%d,%d), (%d,%d,%d): canWalkBetween = %d, canPathBetween = %d\n", prev.x, prev.y, prev.z, bob.x, bob.y, bob.z, Maps::canWalkBetween(prev, bob), Maps::canPathBetween(prev,bob)); + + prev = bob; + return CR_OK; +} diff --git a/plugins/fix-armory.cpp b/plugins/fix-armory.cpp index b937d40e8..e54639960 100644 --- a/plugins/fix-armory.cpp +++ b/plugins/fix-armory.cpp @@ -475,7 +475,7 @@ static bool try_store_item(df::building *target, df::item *item) df::coord tpos(target->centerx, target->centery, target->z); df::coord ipos = Items::getPosition(item); - if (!Maps::canWalkBetween(tpos, ipos)) + if (!Maps::canPathBetween(tpos, ipos)) return false; // Check if the target has enough space left From d2be8f18e131ac5d2c763703b8fe62c53a420e51 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 16 Dec 2012 23:30:35 -0500 Subject: [PATCH 19/56] canWalkBetween: forgot a case with stairs. --- library/modules/Maps.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 0db69ea82..4b468defc 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -569,6 +569,8 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) return true; if ( shape1 == tiletype_shape::STAIR_UP && shape2 == tiletype_shape::STAIR_UPDOWN ) return true; + if ( shape1 == tiletype_shape::STAIR_UP && shape2 == tiletype_shape::STAIR_DOWN ) + return true; return false; } From 1a6a09281b8b3e5d6666bec376bfe526d91b47a3 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 16 Dec 2012 23:37:15 -0500 Subject: [PATCH 20/56] canWalkBetween: forgot a case with ramps. --- library/modules/Maps.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 4b468defc..ff02bcf60 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -551,17 +551,18 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) if ( dz == 0 ) return true; + if ( pos2.z < pos1.z ) { + df::coord temp = pos1; + pos1 = pos2; + pos2 = temp; + } + df::tiletype* type1 = Maps::getTileType(pos1); df::tiletype* type2 = Maps::getTileType(pos2); df::tiletype_shape shape1 = ENUM_ATTR(tiletype,shape,*type1); df::tiletype_shape shape2 = ENUM_ATTR(tiletype,shape,*type2); - if ( pos2.z < pos1.z ) { - df::tiletype_shape temp = shape1; - shape1 = shape2; - shape2 = temp; - } if ( dx == 0 && dy == 0 ) { if ( shape1 == tiletype_shape::STAIR_UPDOWN && shape2 == shape1 ) return true; @@ -575,8 +576,12 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) } //diagonal up: has to be a ramp - if ( shape1 == tiletype_shape::RAMP && shape2 == tiletype_shape::RAMP ) - return true; + if ( shape1 == tiletype_shape::RAMP && shape2 == tiletype_shape::RAMP ) { + df::coord up = df::coord(pos1.x,pos1.y,pos1.z+1); + df::tiletype* typeUp = Maps::getTileType(up); + df::tiletype_shape shapeUp = ENUM_ATTR(tiletype,shape,*typeUp); + return shapeUp == tiletype_shape::RAMP_TOP; + } return false; } From 22837af8d7440f625e90d46714120c66643c508f Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 00:25:14 -0500 Subject: [PATCH 21/56] canWalkBetween: fixed bug involving ramps. --- library/modules/Maps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index ff02bcf60..f736d385a 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -576,7 +576,7 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) } //diagonal up: has to be a ramp - if ( shape1 == tiletype_shape::RAMP && shape2 == tiletype_shape::RAMP ) { + if ( shape1 == tiletype_shape::RAMP /*&& shape2 == tiletype_shape::RAMP*/ ) { df::coord up = df::coord(pos1.x,pos1.y,pos1.z+1); df::tiletype* typeUp = Maps::getTileType(up); df::tiletype_shape shapeUp = ENUM_ATTR(tiletype,shape,*typeUp); From 555c754636aa8825a7561f634e7fa45528d20df3 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 18 Dec 2012 18:34:38 -0500 Subject: [PATCH 22/56] EventManager: added construction and building events. --- library/include/modules/EventManager.h | 2 + library/modules/EventManager.cpp | 118 +++++++++++++++++++++++-- plugins/devel/eventExample.cpp | 14 +++ 3 files changed, 127 insertions(+), 7 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index c1c09da7d..1c610564e 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -17,6 +17,8 @@ namespace DFHack { JOB_COMPLETED, UNIT_DEATH, ITEM_CREATED, + BUILDING, + CONSTRUCTION, EVENT_MAX }; } diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 187a95819..f2d7e0261 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -4,6 +4,8 @@ #include "modules/Job.h" #include "modules/World.h" +#include "df/building.h" +#include "df/construction.h" #include "df/global_objects.h" #include "df/item.h" #include "df/job.h" @@ -11,9 +13,10 @@ #include "df/unit.h" #include "df/world.h" -//#include #include -//#include +#include +#include + using namespace std; using namespace DFHack; using namespace EventManager; @@ -21,11 +24,13 @@ using namespace EventManager; /* * TODO: * error checking + * consider a typedef instead of a struct for EventHandler **/ //map > tickQueue; multimap tickQueue; +//TODO: consider unordered_map of pairs, or unordered_map of unordered_set, or whatever multimap handlers[EventType::EVENT_MAX]; const uint32_t ticksPerYear = 403200; @@ -96,13 +101,31 @@ static void manageJobInitiatedEvent(color_ostream& out); static void manageJobCompletedEvent(color_ostream& out); static void manageUnitDeathEvent(color_ostream& out); static void manageItemCreationEvent(color_ostream& out); +static void manageBuildingEvent(color_ostream& out); +static void manageConstructionEvent(color_ostream& out); +//tick event static uint32_t lastTick = 0; + +//job initiated static int32_t lastJobId = -1; -static map prevJobs; -static set livingUnits; + +//job completed +static unordered_map prevJobs; + +//unit death +static unordered_set livingUnits; + +//item creation static int32_t nextItem; +//building +static int32_t nextBuilding; +static unordered_set buildings; + +//construction +static unordered_set constructions; + void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { if ( event == DFHack::SC_MAP_UNLOADED ) { lastTick = 0; @@ -114,6 +137,9 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event tickQueue.clear(); livingUnits.clear(); nextItem = -1; + nextBuilding = -1; + buildings.clear(); + constructions.clear(); } else if ( event == DFHack::SC_MAP_LOADED ) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); @@ -126,6 +152,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event tickQueue.insert(newTickQueue.begin(), newTickQueue.end()); nextItem = *df::global::item_next_id; + nextBuilding = *df::global::building_next_id; + constructions.insert(df::global::world->constructions.begin(), df::global::world->constructions.end()); } } @@ -144,6 +172,8 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { manageJobCompletedEvent(out); manageUnitDeathEvent(out); manageItemCreationEvent(out); + manageBuildingEvent(out); + manageConstructionEvent(out); return; } @@ -201,7 +231,7 @@ static void manageJobCompletedEvent(color_ostream& out) { nowJobs[link->item->id] = link->item; } - for ( map::iterator i = prevJobs.begin(); i != prevJobs.end(); i++ ) { + for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { if ( nowJobs.find((*i).first) != nowJobs.end() ) continue; @@ -212,13 +242,13 @@ static void manageJobCompletedEvent(color_ostream& out) { } //erase old jobs, copy over possibly altered jobs - for ( map::iterator i = prevJobs.begin(); i != prevJobs.end(); i++ ) { + for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { Job::deleteJobStruct((*i).second); } prevJobs.clear(); //create new jobs - for ( map::iterator j = nowJobs.begin(); j != nowJobs.end(); j++ ) { + for ( auto j = nowJobs.begin(); j != nowJobs.end(); j++ ) { /*map::iterator i = prevJobs.find((*j).first); if ( i != prevJobs.end() ) { continue; @@ -289,3 +319,77 @@ static void manageItemCreationEvent(color_ostream& out) { nextItem = *df::global::item_next_id; } +static void manageBuildingEvent(color_ostream& out) { + /* + * TODO: could be faster + * consider looking at jobs: building creation / destruction + **/ + if ( handlers[EventType::ITEM_CREATED].empty() ) + return; + + multimap copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); + //first alert people about new buildings + for ( int32_t a = nextBuilding; a < *df::global::building_next_id; a++ ) { + int32_t index = df::building::binsearch_index(df::global::world->buildings.all, a); + if ( index == -1 ) { + out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a); + } + buildings.insert(a); + for ( auto b = copy.begin(); b != copy.end(); b++ ) { + EventHandler bob = (*b).second; + bob.eventHandler(out, (void*)a); + } + } + nextBuilding = *df::global::building_next_id; + + //now alert people about destroyed buildings + unordered_set toDelete; + for ( auto a = buildings.begin(); a != buildings.end(); a++ ) { + int32_t id = *a; + int32_t index = df::building::binsearch_index(df::global::world->buildings.all,id); + if ( index != -1 ) + continue; + toDelete.insert(id); + + for ( auto b = copy.begin(); b != copy.end(); b++ ) { + EventHandler bob = (*b).second; + bob.eventHandler(out, (void*)id); + } + } + + for ( auto a = toDelete.begin(); a != toDelete.end(); a++ ) { + int32_t id = *a; + buildings.erase(id); + } +} + +static void manageConstructionEvent(color_ostream& out) { + if ( handlers[EventType::CONSTRUCTION].empty() ) + return; + + unordered_set constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end()); + + multimap copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end()); + for ( auto a = constructions.begin(); a != constructions.end(); a++ ) { + df::construction* construction = *a; + if ( constructionsNow.find(construction) != constructionsNow.end() ) + continue; + for ( auto b = copy.begin(); b != copy.end(); b++ ) { + EventHandler handle = (*b).second; + handle.eventHandler(out, (void*)construction); + } + } + + for ( auto a = constructionsNow.begin(); a != constructionsNow.end(); a++ ) { + df::construction* construction = *a; + if ( constructions.find(construction) != constructions.end() ) + continue; + for ( auto b = copy.begin(); b != copy.end(); b++ ) { + EventHandler handle = (*b).second; + handle.eventHandler(out, (void*)construction); + } + } + + constructions.clear(); + constructions.insert(constructionsNow.begin(), constructionsNow.end()); +} diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index 2099be110..d93396d29 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -21,6 +21,8 @@ void jobCompleted(color_ostream& out, void* job); void timePassed(color_ostream& out, void* ptr); void unitDeath(color_ostream& out, void* ptr); void itemCreate(color_ostream& out, void* ptr); +void building(color_ostream& out, void* ptr); +void construction(color_ostream& out, void* ptr); command_result eventExample(color_ostream& out, vector& parameters); @@ -36,7 +38,10 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::EventHandler timeHandler(timePassed); EventManager::EventHandler deathHandler(unitDeath); EventManager::EventHandler itemHandler(itemCreate); + EventManager::EventHandler buildingHandler(building); + EventManager::EventHandler constructionHandler(construction); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); + EventManager::unregisterAll(me); EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, me); @@ -46,6 +51,8 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::registerTick(timeHandler, 8, me); EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, me); EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, me); + EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, me); + EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, me); out.print("Events registered.\n"); return CR_OK; } @@ -77,3 +84,10 @@ void itemCreate(color_ostream& out, void* ptr) { out.print("Item created: %d, %s, at (%d,%d,%d)\n", (int32_t)(ptr), ENUM_KEY_STR(item_type, type).c_str(), pos.x, pos.y, pos.z); } +void building(color_ostream& out, void* ptr) { + out.print("Building created/destroyed: %d\n", (int32_t)ptr); +} + +void construction(color_ostream& out, void* ptr) { + out.print("Construction created/destroyed: 0x%X\n", ptr); +} From a93c0223a2999944b788092791df342d9131b6e3 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 18 Dec 2012 20:28:30 -0500 Subject: [PATCH 23/56] EventManager: unstable. Temp commit. --- library/include/modules/Buildings.h | 4 ++ library/include/modules/Constructions.h | 2 + library/modules/Buildings.cpp | 82 ++++++++++++++++++++++++- library/modules/Constructions.cpp | 5 ++ library/modules/EventManager.cpp | 22 +++++-- 5 files changed, 110 insertions(+), 5 deletions(-) diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 266aadcb8..5f3a2b48d 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -25,7 +25,9 @@ distribution. #pragma once #include "Export.h" #include "DataDefs.h" +#include "Types.h" #include "df/building.h" +#include "df/building_type.h" #include "df/civzone_type.h" #include "df/furnace_type.h" #include "df/workshop_type.h" @@ -178,5 +180,7 @@ DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector +#include +#include +#include #include +#include #include -#include using namespace std; +#include "ColorText.h" #include "VersionInfo.h" #include "MemAccess.h" #include "Types.h" @@ -77,6 +82,14 @@ using df::global::building_next_id; using df::global::process_jobs; using df::building_def; +struct CoordHash { + size_t operator()(const df::coord pos) const { + return pos.x*65537 + pos.y*17 + pos.z; + } +}; + +static unordered_map locationToBuilding; + static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile) { if (!extent.extents) @@ -222,6 +235,30 @@ df::building *Buildings::findAtTile(df::coord pos) if (!occ || !occ->bits.building) return NULL; + auto a = locationToBuilding.find(pos); + if ( a == locationToBuilding.end() ) { + cerr << __FILE__ << ", " << __LINE__ << ": can't find building at " << pos.x << ", " << pos.y << ", " <buildings.all, id); + if ( index == -1 ) { + cerr << __FILE__ << ", " << __LINE__ << ": can't find building at " << pos.x << ", " << pos.y << ", " <buildings.all[index]; + if (!building->isSettingOccupancy()) + return NULL; + + if (building->room.extents && building->isExtentShaped()) + { + auto etile = getExtentTile(building->room, pos); + if (!etile || !*etile) + return NULL; + } + return building; + + /* auto &vec = df::building::get_vector(); for (size_t i = 0; i < vec.size(); i++) { @@ -246,6 +283,7 @@ df::building *Buildings::findAtTile(df::coord pos) } return NULL; + */ } bool Buildings::findCivzonesAt(std::vector *pvec, df::coord pos) @@ -1063,3 +1101,45 @@ bool Buildings::deconstruct(df::building *bld) return true; } +void Buildings::updateBuildings(color_ostream& out, void* ptr) { + static unordered_map corner1; + static unordered_map corner2; + out.print("Updating buildings, %s %d\n", __FILE__, __LINE__); + int32_t id = (int32_t)ptr; + + if ( corner1.find(id) == corner1.end() ) { + //new building: mark stuff + int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); + if ( index == -1 ) { + out.print("%s, line %d: Couldn't find new building id=%d.\n", __FILE__, __LINE__, id); + exit(1); + } + df::building* building = df::global::world->buildings.all[index]; + df::coord p1(min(building->x1, building->x2), min(building->y1,building->y2), building->z); + df::coord p2(max(building->x1, building->x2), max(building->y1,building->y2), building->z); + + corner1[id] = p1; + corner2[id] = p2; + + for ( int32_t x = p1.x; x <= p2.x; x++ ) { + for ( int32_t y = p1.y; y <= p2.y; y++ ) { + df::coord pt(x,y,building->z); + locationToBuilding[pt] = id; + } + } + } else { + //existing building: destroy it + df::coord p1 = corner1[id]; + df::coord p2 = corner2[id]; + + for ( int32_t x = p1.x; x <= p2.x; x++ ) { + for ( int32_t y = p1.y; y <= p2.y; y++ ) { + df::coord pt(x,y,p1.z); + locationToBuilding.erase(pt); + } + } + + corner1.erase(id); + corner2.erase(id); + } +} diff --git a/library/modules/Constructions.cpp b/library/modules/Constructions.cpp index 16c1f1b89..2153ce6a2 100644 --- a/library/modules/Constructions.cpp +++ b/library/modules/Constructions.cpp @@ -169,3 +169,8 @@ bool Constructions::designateRemove(df::coord pos, bool *immediate) return false; } + + +void Constructions::updateConstructions(color_ostream& out, void* ptr) { + //do stuff +} diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index f2d7e0261..b057adb01 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1,5 +1,7 @@ #include "Core.h" #include "Console.h" +#include "modules/Buildings.h" +#include "modules/Constructions.h" #include "modules/EventManager.h" #include "modules/Job.h" #include "modules/World.h" @@ -127,6 +129,16 @@ static unordered_set buildings; static unordered_set constructions; void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { + static bool doOnce = false; + if ( !doOnce ) { + //TODO: put this somewhere else + doOnce = true; + EventHandler buildingHandler(Buildings::updateBuildings); + EventHandler constructionHandler(Constructions::updateConstructions); + DFHack::EventManager::registerListener(EventType::BUILDING, buildingHandler, NULL); + DFHack::EventManager::registerListener(EventType::CONSTRUCTION, constructionHandler, NULL); + out.print("Registered listeners.\n %d", __LINE__); + } if ( event == DFHack::SC_MAP_UNLOADED ) { lastTick = 0; lastJobId = -1; @@ -151,9 +163,9 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event tickQueue.insert(newTickQueue.begin(), newTickQueue.end()); - nextItem = *df::global::item_next_id; - nextBuilding = *df::global::building_next_id; - constructions.insert(df::global::world->constructions.begin(), df::global::world->constructions.end()); + nextItem = 0; + nextBuilding = 0; + lastTick = 0; } } @@ -324,7 +336,7 @@ static void manageBuildingEvent(color_ostream& out) { * TODO: could be faster * consider looking at jobs: building creation / destruction **/ - if ( handlers[EventType::ITEM_CREATED].empty() ) + if ( handlers[EventType::BUILDING].empty() ) return; multimap copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); @@ -361,6 +373,8 @@ static void manageBuildingEvent(color_ostream& out) { int32_t id = *a; buildings.erase(id); } + + out.print("Sent building event.\n %d", __LINE__); } static void manageConstructionEvent(color_ostream& out) { From 7972902c81408526d77821d31bb12d355f85a8ed Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 19 Dec 2012 20:30:37 -0500 Subject: [PATCH 24/56] stepBetween: named a few things better, and fixed a lot. --- library/include/modules/Maps.h | 2 +- library/modules/Maps.cpp | 99 ++++++++++++++++--- plugins/devel/CMakeLists.txt | 2 +- .../{walkBetween.cpp => stepBetween.cpp} | 12 +-- 4 files changed, 95 insertions(+), 20 deletions(-) rename plugins/devel/{walkBetween.cpp => stepBetween.cpp} (87%) diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 2655268d7..29d3d69ba 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -309,7 +309,7 @@ extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block, extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which ); DFHACK_EXPORT bool canPathBetween(df::coord pos1, df::coord pos2); -DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2); +DFHACK_EXPORT bool canStepBetween(df::coord pos1, df::coord pos2); } } #endif diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index f736d385a..6517331ee 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -30,10 +30,12 @@ distribution. #include #include #include +#include using namespace std; #include "modules/Maps.h" #include "modules/MapCache.h" +#include "ColorText.h" #include "Error.h" #include "VersionInfo.h" #include "MemAccess.h" @@ -59,6 +61,7 @@ using namespace std; #include "df/z_level_flags.h" #include "df/region_map_entry.h" #include "df/flow_info.h" +#include "df/building_type.h" using namespace DFHack; using namespace df::enums; @@ -535,28 +538,35 @@ bool Maps::canPathBetween(df::coord pos1, df::coord pos2) return tile1 && tile1 == tile2; } -bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) +bool Maps::canStepBetween(df::coord pos1, df::coord pos2) { - bool b = canPathBetween(pos1, pos2); - if ( !b ) - return false; - - int32_t dx = pos1.x-pos2.x; - int32_t dy = pos1.y-pos2.y; - int32_t dz = pos1.z-pos2.z; + color_ostream& out = Core::getInstance().getConsole(); + int32_t dx = pos2.x-pos1.x; + int32_t dy = pos2.y-pos1.y; + int32_t dz = pos2.z-pos1.z; if ( dx*dx > 1 || dy*dy > 1 || dz*dz > 1 ) return false; - if ( dz == 0 ) - return true; - if ( pos2.z < pos1.z ) { df::coord temp = pos1; pos1 = pos2; pos2 = temp; } + df::map_block* block1 = getTileBlock(pos1); + df::map_block* block2 = getTileBlock(pos2); + + if ( !block1 || !block2 ) + return false; + + if ( !index_tile(block1->walkable,pos1) || !index_tile(block2->walkable,pos2) ) { + return false; + } + + if ( dz == 0 ) + return true; + df::tiletype* type1 = Maps::getTileType(pos1); df::tiletype* type2 = Maps::getTileType(pos2); @@ -564,6 +574,11 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) df::tiletype_shape shape2 = ENUM_ATTR(tiletype,shape,*type2); if ( dx == 0 && dy == 0 ) { + //check for forbidden hatches and floors and such + df::enums::tile_building_occ::tile_building_occ upOcc = index_tile(block2->occupancy,pos2).bits.building; + if ( upOcc == df::enums::tile_building_occ::Impassable || upOcc == df::enums::tile_building_occ::Obstacle || upOcc == df::enums::tile_building_occ::Floored ) + return false; + if ( shape1 == tiletype_shape::STAIR_UPDOWN && shape2 == shape1 ) return true; if ( shape1 == tiletype_shape::STAIR_UPDOWN && shape2 == tiletype_shape::STAIR_DOWN ) @@ -572,15 +587,75 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) return true; if ( shape1 == tiletype_shape::STAIR_UP && shape2 == tiletype_shape::STAIR_DOWN ) return true; + if ( shape1 == tiletype_shape::RAMP && shape2 == tiletype_shape::RAMP_TOP ) { + //it depends + //there has to be a wall next to the ramp + bool foundWall = false; + for ( int32_t x = -1; x <= 1; x++ ) { + for ( int32_t y = -1; y <= 1; y++ ) { + if ( x == 0 && y == 0 ) + continue; + df::tiletype* type = Maps::getTileType(df::coord(pos1.x+x,pos1.y+y,pos1.z)); + df::tiletype_shape shape1 = ENUM_ATTR(tiletype,shape,*type); + if ( shape1 == tiletype_shape::WALL ) { + foundWall = true; + x = 2; + break; + } + } + } + if ( !foundWall ) + return false; //unusable ramp + + //there has to be an unforbidden hatch above the ramp + if ( index_tile(block2->occupancy,pos2).bits.building != df::enums::tile_building_occ::Dynamic ) + return false; + //note that forbidden hatches have Floored occupancy. unforbidden ones have dynamic occupancy + df::building* building = Buildings::findAtTile(pos2); + if ( building == NULL ) { + out << __FILE__ << ", line " << __LINE__ << ": couldn't find hatch.\n"; + return false; + } + if ( building->getType() != df::enums::building_type::Hatch ) { + return false; + } + return true; + } return false; } //diagonal up: has to be a ramp if ( shape1 == tiletype_shape::RAMP /*&& shape2 == tiletype_shape::RAMP*/ ) { df::coord up = df::coord(pos1.x,pos1.y,pos1.z+1); + bool foundWall = false; + for ( int32_t x = -1; x <= 1; x++ ) { + for ( int32_t y = -1; y <= 1; y++ ) { + if ( x == 0 && y == 0 ) + continue; + df::tiletype* type = Maps::getTileType(df::coord(pos1.x+x,pos1.y+y,pos1.z)); + df::tiletype_shape shape1 = ENUM_ATTR(tiletype,shape,*type); + if ( shape1 == tiletype_shape::WALL ) { + foundWall = true; + x = 2; + break; + } + } + } + if ( !foundWall ) + return false; //unusable ramp df::tiletype* typeUp = Maps::getTileType(up); df::tiletype_shape shapeUp = ENUM_ATTR(tiletype,shape,*typeUp); - return shapeUp == tiletype_shape::RAMP_TOP; + if ( shapeUp != tiletype_shape::RAMP_TOP ) + return false; + + df::map_block* blockUp = getTileBlock(up); + if ( !blockUp ) + return false; + + df::enums::tile_building_occ::tile_building_occ occupancy = index_tile(blockUp->occupancy,up).bits.building; + if ( occupancy == df::enums::tile_building_occ::Obstacle || occupancy == df::enums::tile_building_occ::Floored || occupancy == df::enums::tile_building_occ::Impassable ) + return false; + return true; } return false; diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 41fcd8130..8b0778f86 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -21,4 +21,4 @@ DFHACK_PLUGIN(vshook vshook.cpp) IF(UNIX) DFHACK_PLUGIN(ref-index ref-index.cpp) ENDIF() -DFHACK_PLUGIN(walkBetween walkBetween.cpp) +DFHACK_PLUGIN(stepBetween stepBetween.cpp) diff --git a/plugins/devel/walkBetween.cpp b/plugins/devel/stepBetween.cpp similarity index 87% rename from plugins/devel/walkBetween.cpp rename to plugins/devel/stepBetween.cpp index 9dafe4bb8..c1bc3fa1a 100644 --- a/plugins/devel/walkBetween.cpp +++ b/plugins/devel/stepBetween.cpp @@ -16,19 +16,19 @@ using namespace df::enums; -command_result walkBetween (color_ostream &out, std::vector & parameters); +command_result stepBetween (color_ostream &out, std::vector & parameters); // A plugin must be able to return its name and version. // The name string provided must correspond to the filename - skeleton.plug.so or skeleton.plug.dll in this case -DFHACK_PLUGIN("walkBetween"); +DFHACK_PLUGIN("stepBetween"); // Mandatory init function. If you have some global state, create it here. DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { // Fill the command list with your commands. commands.push_back(PluginCommand( - "walkBetween", "Do nothing, look pretty.", - walkBetween, false, /* true means that the command can't be used from non-interactive user interface */ + "stepBetween", "Do nothing, look pretty.", + stepBetween, false, /* true means that the command can't be used from non-interactive user interface */ // Extended help string. Used by CR_WRONG_USAGE and the help command: " This command does nothing at all.\n" )); @@ -77,10 +77,10 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) df::coord prev; // A command! It sits around and looks pretty. And it's nice and friendly. -command_result walkBetween (color_ostream &out, std::vector & parameters) +command_result stepBetween (color_ostream &out, std::vector & parameters) { df::coord bob = Gui::getCursorPos(); - out.print("(%d,%d,%d), (%d,%d,%d): canWalkBetween = %d, canPathBetween = %d\n", prev.x, prev.y, prev.z, bob.x, bob.y, bob.z, Maps::canWalkBetween(prev, bob), Maps::canPathBetween(prev,bob)); + out.print("(%d,%d,%d), (%d,%d,%d): canWalkBetween = %d, canPathBetween = %d\n", prev.x, prev.y, prev.z, bob.x, bob.y, bob.z, Maps::canStepBetween(prev, bob), Maps::canPathBetween(prev,bob)); prev = bob; return CR_OK; From a4dc79565a95bf6e73e0145e28b03e646c051730 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 22 Dec 2012 00:13:07 -0500 Subject: [PATCH 25/56] AutoSyndrome: allowed for triggering DFHack commands from in game reactions. --- plugins/autoSyndrome.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index f2c86bdfd..6e5b7f636 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -234,12 +234,32 @@ void processJob(color_ostream& out, void* jobPtr) { //must be a boiling rock syndrome df::inorganic_raw* inorganic = df::global::world->raws.inorganics[bob->mat_index]; - if ( inorganic->material.heat.boiling_point > 10000 ) - continue; + if ( inorganic->material.heat.boiling_point > 10000 ) { + //continue; + } for ( size_t b = 0; b < inorganic->material.syndrome.size(); b++ ) { //add each syndrome to the guy who did the job df::syndrome* syndrome = inorganic->material.syndrome[b]; + bool foundCommand = false; + string commandStr; + vector args; + for ( size_t c = 0; c < syndrome->syn_class.size(); c++ ) { + std::string* clazz = syndrome->syn_class[c]; + out.print("Class = %s\n", clazz->c_str()); + if ( foundCommand ) { + if ( commandStr == "" ) + commandStr = *clazz; + else + args.push_back(*clazz); + } else if ( *clazz == "command" ) { + foundCommand = true; + } + } + if ( commandStr != "" ) { + out.print("Running command thingy."); + Core::getInstance().runCommand(out, commandStr, args); + } //check that the syndrome applies to that guy /* * If there is no affected class or affected creature, then anybody who isn't immune is fair game. From 0572e87d7b14c0be2982ce39bbde6b93f237390d Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 31 Dec 2012 13:50:44 -0500 Subject: [PATCH 26/56] SkyEternal: allocates new z-levels of sky as needed, or on request. --- plugins/CMakeLists.txt | 1 + plugins/skyEternal.cpp | 152 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 plugins/skyEternal.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 91d578215..aa4f95056 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -127,6 +127,7 @@ if (BUILD_SUPPORTED) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) DFHACK_PLUGIN(misery misery.cpp) + DFHACK_PLUGIN(skyEternal skyEternal.cpp) endif() diff --git a/plugins/skyEternal.cpp b/plugins/skyEternal.cpp new file mode 100644 index 000000000..04a176a8d --- /dev/null +++ b/plugins/skyEternal.cpp @@ -0,0 +1,152 @@ + +#include "Core.h" +#include "Console.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" + +#include "modules/World.h" + +#include "df/construction.h" +#include "df/game_mode.h" +#include "df/map_block.h" +#include "df/world.h" +#include "df/z_level_flags.h" + +#include +#include +#include + +using namespace std; + +using namespace DFHack; +using namespace df::enums; + +command_result skyEternal (color_ostream &out, std::vector & parameters); + +DFHACK_PLUGIN("skyEternal"); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "skyEternal", + "Creates new sky levels on request, or as you construct up.", + skyEternal, false, + "Usage:\n" + " skyEternal\n" + " creates one more z-level\n" + " skyEternal [n]\n" + " creates n more z-level(s)\n" + " skyEternal enable\n" + " enables monitoring of constructions\n" + " skyEternal disable\n" + " disable monitoring of constructions\n" + "\n" + "If construction monitoring is enabled, then the plugin will automatically create new sky z-levels as you construct upward.\n" + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +/* +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_GAME_LOADED: + // initialize from the world just loaded + break; + case SC_GAME_UNLOADED: + // cleanup + break; + default: + break; + } + return CR_OK; +} +*/ + +static size_t constructionSize = 0; +static bool enabled = false; + +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + if ( !enabled ) + return CR_OK; + if ( !Core::getInstance().isMapLoaded() ) + return CR_OK; + { + t_gamemodes mode; + if ( !Core::getInstance().getWorld()->ReadGameMode(mode) ) + return CR_FAILURE; + if ( mode.g_mode != df::enums::game_mode::DWARF ) + return CR_OK; + } + + if ( df::global::world->constructions.size() == constructionSize ) + return CR_OK; + int32_t zNow = df::global::world->map.z_count_block; + vector vec; + for ( size_t a = constructionSize; a < df::global::world->constructions.size(); a++ ) { + df::construction* construct = df::global::world->constructions[a]; + if ( construct->pos.z+2 < zNow ) + continue; + skyEternal(out, vec); + zNow = df::global::world->map.z_count_block; + ///break; + } + constructionSize = df::global::world->constructions.size(); + + return CR_OK; +} + +command_result skyEternal (color_ostream &out, std::vector & parameters) +{ + if ( parameters.size() > 1 ) + return CR_WRONG_USAGE; + if ( parameters.size() == 0 ) { + vector vec; + vec.push_back("1"); + return skyEternal(out, vec); + } + if (parameters[0] == "enable") { + enabled = true; + return CR_OK; + } + if (parameters[0] == "disable") { + enabled = false; + constructionSize = 0; + return CR_OK; + } + int32_t howMany = 0; + howMany = atoi(parameters[0].c_str()); + df::world* world = df::global::world; + CoreSuspender suspend; + int32_t x_count_block = world->map.x_count_block; + int32_t y_count_block = world->map.y_count_block; + for ( int32_t count = 0; count < howMany; count++ ) { + //change the size of the pointer stuff + int32_t z_count_block = world->map.z_count_block; + df::map_block**** block_index = world->map.block_index; + for ( int32_t a = 0; a < x_count_block; a++ ) { + for ( int32_t b = 0; b < y_count_block; b++ ) { + df::map_block** column = new df::map_block*[z_count_block+1]; + memcpy(column, block_index[a][b], z_count_block*sizeof(df::map_block*)); + column[z_count_block] = NULL; + delete[] block_index[a][b]; + block_index[a][b] = column; + } + } + df::z_level_flags* flags = new df::z_level_flags[z_count_block+1]; + memcpy(flags, world->map.z_level_flags, z_count_block*sizeof(df::z_level_flags)); + flags[z_count_block].whole = 0; + flags[z_count_block].bits.update = 1; + world->map.z_count_block++; + world->map.z_count++; + } + + return CR_OK; +} From 4e99841862735d8544bcade9f99010c02ac741c5 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 1 Jan 2013 22:22:31 -0500 Subject: [PATCH 27/56] EventManager: made Buildings module keep track of buildings so that it can do findAtTile in constant time. --- library/include/modules/Buildings.h | 1 + library/include/modules/Constructions.h | 2 -- library/modules/Buildings.cpp | 14 +++++++++++--- library/modules/Constructions.cpp | 5 ----- library/modules/EventManager.cpp | 12 +++++++----- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 5f3a2b48d..4e162be69 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -181,6 +181,7 @@ DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector corner1; +static unordered_map corner2; + +void Buildings::clearBuildings(color_ostream& out) { + corner1.clear(); + corner2.clear(); + locationToBuilding.clear(); +} + void Buildings::updateBuildings(color_ostream& out, void* ptr) { - static unordered_map corner1; - static unordered_map corner2; - out.print("Updating buildings, %s %d\n", __FILE__, __LINE__); + //out.print("Updating buildings, %s %d\n", __FILE__, __LINE__); int32_t id = (int32_t)ptr; if ( corner1.find(id) == corner1.end() ) { diff --git a/library/modules/Constructions.cpp b/library/modules/Constructions.cpp index 2153ce6a2..16c1f1b89 100644 --- a/library/modules/Constructions.cpp +++ b/library/modules/Constructions.cpp @@ -169,8 +169,3 @@ bool Constructions::designateRemove(df::coord pos, bool *immediate) return false; } - - -void Constructions::updateConstructions(color_ostream& out, void* ptr) { - //do stuff -} diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index b057adb01..24bd6aeec 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -134,10 +134,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event //TODO: put this somewhere else doOnce = true; EventHandler buildingHandler(Buildings::updateBuildings); - EventHandler constructionHandler(Constructions::updateConstructions); DFHack::EventManager::registerListener(EventType::BUILDING, buildingHandler, NULL); - DFHack::EventManager::registerListener(EventType::CONSTRUCTION, constructionHandler, NULL); - out.print("Registered listeners.\n %d", __LINE__); + //out.print("Registered listeners.\n %d", __LINE__); } if ( event == DFHack::SC_MAP_UNLOADED ) { lastTick = 0; @@ -152,6 +150,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event nextBuilding = -1; buildings.clear(); constructions.clear(); + + Buildings::clearBuildings(out); } else if ( event == DFHack::SC_MAP_LOADED ) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); @@ -344,7 +344,9 @@ static void manageBuildingEvent(color_ostream& out) { for ( int32_t a = nextBuilding; a < *df::global::building_next_id; a++ ) { int32_t index = df::building::binsearch_index(df::global::world->buildings.all, a); if ( index == -1 ) { - out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a); + //out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a); + //the tricky thing is that when the game first starts, it's ok to skip buildings, but otherwise, if you skip buildings, something is probably wrong. TODO: make this smarter + continue; } buildings.insert(a); for ( auto b = copy.begin(); b != copy.end(); b++ ) { @@ -374,7 +376,7 @@ static void manageBuildingEvent(color_ostream& out) { buildings.erase(id); } - out.print("Sent building event.\n %d", __LINE__); + //out.print("Sent building event.\n %d", __LINE__); } static void manageConstructionEvent(color_ostream& out) { From b320fb25f3ff04458f007f4fa2986cd6f57b384e Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 1 Jan 2013 23:56:31 -0500 Subject: [PATCH 28/56] AutoSyndrome: added smart arguments for location, worker id, and reaction id. --- plugins/autoSyndrome.cpp | 34 +++++++++++++++++++++++++++++----- plugins/devel/CMakeLists.txt | 1 + plugins/devel/printArgs.cpp | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 plugins/devel/printArgs.cpp diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 6e5b7f636..c18055455 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -180,7 +180,7 @@ void processJob(color_ostream& out, void* jobPtr) { if ( job->job_type != df::job_type::CustomReaction ) return; - + df::reaction* reaction = NULL; for ( size_t a = 0; a < df::global::world->raws.reactions.size(); a++ ) { df::reaction* candidate = df::global::world->raws.reactions[a]; @@ -250,14 +250,38 @@ void processJob(color_ostream& out, void* jobPtr) { if ( foundCommand ) { if ( commandStr == "" ) commandStr = *clazz; - else - args.push_back(*clazz); - } else if ( *clazz == "command" ) { + else { + stringstream bob; + if ( *clazz == "\\LOCATION" ) { + bob << job->pos.x; + args.push_back(bob.str()); + bob.str(""); + bob.clear(); + + bob << job->pos.y; + args.push_back(bob.str()); + bob.str(""); + bob.clear(); + + bob << job->pos.z; + args.push_back(bob.str()); + bob.str(""); + bob.clear(); + } else if ( *clazz == "\\WORKER_ID" ) { + bob << workerId; + args.push_back(bob.str()); + } else if ( *clazz == "\\REACTION_INDEX" ) { + bob << reaction->index; + args.push_back(bob.str()); + } else { + args.push_back(*clazz); + } + } + } else if ( *clazz == "\\COMMAND" ) { foundCommand = true; } } if ( commandStr != "" ) { - out.print("Running command thingy."); Core::getInstance().runCommand(out, commandStr, args); } //check that the syndrome applies to that guy diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 80d627fa9..c57596e9c 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -19,6 +19,7 @@ DFHACK_PLUGIN(rprobe rprobe.cpp) DFHACK_PLUGIN(nestboxes nestboxes.cpp) DFHACK_PLUGIN(vshook vshook.cpp) DFHACK_PLUGIN(eventExample eventExample.cpp) +DFHACK_PLUGIN(printArgs printArgs.cpp) IF(UNIX) DFHACK_PLUGIN(ref-index ref-index.cpp) ENDIF() diff --git a/plugins/devel/printArgs.cpp b/plugins/devel/printArgs.cpp new file mode 100644 index 000000000..051c7b1dc --- /dev/null +++ b/plugins/devel/printArgs.cpp @@ -0,0 +1,32 @@ + +#include "Console.h" +#include "Core.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" + +#include + +using namespace DFHack; +using namespace std; + +command_result printArgs (color_ostream &out, std::vector & parameters); + +DFHACK_PLUGIN("printArgs"); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "printArgs", "Print the arguments given.", + printArgs, false + )); + return CR_OK; +} + +command_result printArgs (color_ostream &out, std::vector & parameters) +{ + for ( size_t a = 0; a < parameters.size(); a++ ) { + out << "Argument " << (a+1) << ": \"" << parameters[a] << "\"" << endl; + } + return CR_OK; +} From c3b2ae21377037dff478d4dbd3ef0c6d89a6b405 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 11:07:56 -0500 Subject: [PATCH 29/56] EventManager: allowed plugins to specify how often they need events to be checked, in the event that monitoring is necessary. --- library/include/modules/EventManager.h | 4 +- library/modules/EventManager.cpp | 67 ++++++++++++++++++++++---- plugins/devel/eventExample.cpp | 12 ++--- 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 1c610564e..a06439fcd 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -37,9 +37,9 @@ namespace DFHack { } }; - DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin); + DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin); DFHACK_EXPORT void registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false); - DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin); + DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin); DFHACK_EXPORT void unregisterAll(Plugin* plugin); void manageEvents(color_ostream& out); void onStateChange(color_ostream& out, state_change_event event); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 24bd6aeec..2725d80a3 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -34,11 +34,16 @@ multimap tickQueue; //TODO: consider unordered_map of pairs, or unordered_map of unordered_set, or whatever multimap handlers[EventType::EVENT_MAX]; +multimap pluginFrequencies[EventType::EVENT_MAX]; +map eventFrequency[EventType::EVENT_MAX]; +uint32_t eventLastTick[EventType::EVENT_MAX]; const uint32_t ticksPerYear = 403200; -void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin) { +void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin) { handlers[e].insert(pair(plugin, handler)); + eventFrequency[e][freq]++; + pluginFrequencies[e].insert(pair(plugin, freq)); } void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute) { @@ -59,7 +64,7 @@ void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plug return; } -void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handler, Plugin* plugin) { +void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin) { for ( multimap::iterator i = handlers[e].find(plugin); i != handlers[e].end(); i++ ) { if ( (*i).first != plugin ) break; @@ -69,6 +74,16 @@ void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handl break; } } + if ( eventFrequency[e].find(freq) == eventFrequency[e].end() ) { + Core::getInstance().getConsole().print("%s, line %d: Error: incorrect frequency on deregister.\n", __FILE__, __LINE__); + return; + } + eventFrequency[e][freq]--; + if ( eventFrequency[e][freq] == 0 ) { + eventFrequency[e].erase(eventFrequency[e].find(freq)); + } else if ( eventFrequency[e][freq] < 0 ) { + Core::getInstance().getConsole().print("%s, line %d: Error: incorrect frequency on deregister.\n", __FILE__, __LINE__); + } return; } @@ -95,6 +110,21 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) { for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) { handlers[a].erase(plugin); } + + for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) { + for ( auto b = pluginFrequencies[a].begin(); b != pluginFrequencies[a].end(); b++ ) { + if ( (*b).first != plugin ) + continue; + int32_t freq = (*b).second; + eventFrequency[a][freq]--; + if ( eventFrequency[a][freq] < 0 ) { + Core::getInstance().getConsole().print("%s, line %d: Error: incorrect frequency on deregister.\n", __FILE__, __LINE__); + eventFrequency[a].erase(eventFrequency[a].find(freq)); + } else if ( eventFrequency[a][freq] == 0 ) { + eventFrequency[a].erase(eventFrequency[a].find(freq)); + } + } + } return; } @@ -134,7 +164,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event //TODO: put this somewhere else doOnce = true; EventHandler buildingHandler(Buildings::updateBuildings); - DFHack::EventManager::registerListener(EventType::BUILDING, buildingHandler, NULL); + DFHack::EventManager::registerListener(EventType::BUILDING, buildingHandler, 100, NULL); //out.print("Registered listeners.\n %d", __LINE__); } if ( event == DFHack::SC_MAP_UNLOADED ) { @@ -175,17 +205,36 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { } uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); + if ( tick <= lastTick ) return; lastTick = tick; manageTickEvent(out); - manageJobInitiatedEvent(out); - manageJobCompletedEvent(out); - manageUnitDeathEvent(out); - manageItemCreationEvent(out); - manageBuildingEvent(out); - manageConstructionEvent(out); + if ( tick - eventLastTick[EventType::JOB_INITIATED] >= (*eventFrequency[EventType::JOB_INITIATED].begin()).first ) { + manageJobInitiatedEvent(out); + eventLastTick[EventType::JOB_INITIATED] = tick; + } + if ( tick - eventLastTick[EventType::JOB_COMPLETED] >= (*eventFrequency[EventType::JOB_COMPLETED].begin()).first ) { + manageJobCompletedEvent(out); + eventLastTick[EventType::JOB_COMPLETED] = tick; + } + if ( tick - eventLastTick[EventType::UNIT_DEATH] >= (*eventFrequency[EventType::UNIT_DEATH].begin()).first ) { + manageUnitDeathEvent(out); + eventLastTick[EventType::UNIT_DEATH] = tick; + } + if ( tick - eventLastTick[EventType::ITEM_CREATED] >= (*eventFrequency[EventType::ITEM_CREATED].begin()).first ) { + manageItemCreationEvent(out); + eventLastTick[EventType::ITEM_CREATED] = tick; + } + if ( tick - eventLastTick[EventType::BUILDING] >= (*eventFrequency[EventType::BUILDING].begin()).first ) { + manageBuildingEvent(out); + eventLastTick[EventType::BUILDING] = tick; + } + if ( tick - eventLastTick[EventType::CONSTRUCTION] >= (*eventFrequency[EventType::CONSTRUCTION].begin()).first ) { + manageConstructionEvent(out); + eventLastTick[EventType::CONSTRUCTION] = tick; + } return; } diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index d93396d29..e6a3dd22f 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -43,16 +43,16 @@ command_result eventExample(color_ostream& out, vector& parameters) { Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); EventManager::unregisterAll(me); - EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); - EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, me); + EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, 10, me); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, 5, me); EventManager::registerTick(timeHandler, 1, me); EventManager::registerTick(timeHandler, 2, me); EventManager::registerTick(timeHandler, 4, me); EventManager::registerTick(timeHandler, 8, me); - EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, me); - EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, me); - EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, me); - EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, me); + EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, 500, me); + EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, 1000, me); + EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, 500, me); + EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, 100, me); out.print("Events registered.\n"); return CR_OK; } From f680ef7ee34da948e39bcdf35824da1c661856ec Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 11:30:06 -0500 Subject: [PATCH 30/56] Made git ignore vim swap files. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9f2b009c6..041624475 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,6 @@ dfhack/python/dist build/CPack*Config.cmake /cmakeall.bat + +# swap files for vim +*.swp From 5e2877be23a01c9817a6c626a826c1919522ccdb Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 13:44:56 -0500 Subject: [PATCH 31/56] AutoSyndrome: added options for worker only (vs all in building), allow multiple targets, and allow multiple syndromes. --- plugins/autoSyndrome.cpp | 210 +++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 72 deletions(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index c18055455..2c8023035 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -7,6 +7,7 @@ #include "modules/Job.h" #include "modules/Maps.h" +#include "df/building.h" #include "df/caste_raw.h" #include "df/creature_raw.h" #include "df/global_objects.h" @@ -21,6 +22,7 @@ #include "df/ui.h" #include "df/unit.h" #include "df/general_ref.h" +#include "df/general_ref_building_holderst.h" #include "df/general_ref_type.h" #include "df/general_ref_unit_workerst.h" @@ -124,7 +126,7 @@ DFhackCExport command_result plugin_init(color_ostream& out, vectorgetPluginByName("autoSyndrome"); EventManager::EventHandler handle(processJob); - EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, 5, me); return CR_OK; } @@ -162,13 +164,81 @@ command_result autoSyndrome(color_ostream& out, vector& parameters) { Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome"); if ( enabled ) { EventManager::EventHandler handle(processJob); - EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, 5, me); } else { EventManager::unregisterAll(me); } return CR_OK; } +bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df::unit* unit) { + df::creature_raw* creature = df::global::world->raws.creatures.all[unit->race]; + df::caste_raw* caste = creature->caste[unit->caste]; + std::string& creature_name = creature->creature_id; + std::string& creature_caste = caste->caste_id; + //check that the syndrome applies to that guy + /* + * If there is no affected class or affected creature, then anybody who isn't immune is fair game. + * + * Otherwise, it works like this: + * add all the affected class creatures + * remove all the immune class creatures + * add all the affected creatures + * remove all the immune creatures + * you're affected if and only if you're in the remaining list after all of that + **/ + bool applies = syndrome->syn_affected_class.size() == 0 && syndrome->syn_affected_creature.size() == 0; + for ( size_t c = 0; c < syndrome->syn_affected_class.size(); c++ ) { + if ( applies ) + break; + for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { + if ( *syndrome->syn_affected_class[c] == *caste->creature_class[d] ) { + applies = true; + break; + } + } + } + for ( size_t c = 0; c < syndrome->syn_immune_class.size(); c++ ) { + if ( !applies ) + break; + for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { + if ( *syndrome->syn_immune_class[c] == *caste->creature_class[d] ) { + applies = false; + break; + } + } + } + + if ( syndrome->syn_affected_creature.size() != syndrome->syn_affected_caste.size() ) { + out.print("%s, line %d: different affected creature/caste sizes.\n", __FILE__, __LINE__); + return false; + } + for ( size_t c = 0; c < syndrome->syn_affected_creature.size(); c++ ) { + if ( creature_name != *syndrome->syn_affected_creature[c] ) + continue; + if ( *syndrome->syn_affected_caste[c] == "ALL" || + *syndrome->syn_affected_caste[c] == creature_caste ) { + applies = true; + break; + } + } + for ( size_t c = 0; c < syndrome->syn_immune_creature.size(); c++ ) { + if ( creature_name != *syndrome->syn_immune_creature[c] ) + continue; + if ( *syndrome->syn_immune_caste[c] == "ALL" || + *syndrome->syn_immune_caste[c] == creature_caste ) { + applies = false; + break; + } + } + if ( !applies ) { + return false; + } + if ( giveSyndrome(out, workerId, syndrome) < 0 ) + return false; + return true; +} + void processJob(color_ostream& out, void* jobPtr) { df::job* job = (df::job*)jobPtr; if ( job == NULL ) { @@ -195,13 +265,13 @@ void processJob(color_ostream& out, void* jobPtr) { } int32_t workerId = -1; - for ( size_t a = 0; a < job->references.size(); a++ ) { - if ( job->references[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) + for ( size_t a = 0; a < job->general_refs.size(); a++ ) { + if ( job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) continue; if ( workerId != -1 ) { out.print("%s, line %d: Found two workers on the same job.\n", __FILE__, __LINE__); } - workerId = ((df::general_ref_unit_workerst*)job->references[a])->unit_id; + workerId = ((df::general_ref_unit_workerst*)job->general_refs[a])->unit_id; if (workerId == -1) { out.print("%s, line %d: invalid worker.\n", __FILE__, __LINE__); continue; @@ -214,13 +284,32 @@ void processJob(color_ostream& out, void* jobPtr) { return; } df::unit* unit = df::global::world->units.all[workerIndex]; - df::creature_raw* creature = df::global::world->raws.creatures.all[unit->race]; - df::caste_raw* caste = creature->caste[unit->caste]; - std::string& creature_name = creature->creature_id; - std::string& creature_caste = caste->caste_id; + //find the building that made it + int32_t buildingId = -1; + for ( size_t a = 0; a < job->general_refs.size(); a++ ) { + if ( job->general_refs[a]->getType() != df::enums::general_ref_type::BUILDING_HOLDER ) + continue; + if ( buildingId != -1 ) { + out.print("%s, line %d: Found two buildings for the same job.\n", __FILE__, __LINE__); + } + buildingId = ((df::general_ref_building_holderst*)job->general_refs[a])->building_id; + if (buildingId == -1) { + out.print("%s, line %d: invalid building.\n", __FILE__, __LINE__); + continue; + } + } + df::building* building; + { + int32_t index = df::building::binsearch_index(df::global::world->buildings.all, buildingId); + if ( index == -1 ) { + out.print("%s, line %d: error: couldn't find building %d.\n", __FILE__, __LINE__, buildingId); + return; + } + building = df::global::world->buildings.all[index]; + } //find all of the products it makes. Look for a stone with a low boiling point. - bool foundIt = false; + bool appliedSomething = false; for ( size_t a = 0; a < reaction->products.size(); a++ ) { df::reaction_product_type type = reaction->products[a]->getType(); //out.print("type = %d\n", (int32_t)type); @@ -234,23 +323,34 @@ void processJob(color_ostream& out, void* jobPtr) { //must be a boiling rock syndrome df::inorganic_raw* inorganic = df::global::world->raws.inorganics[bob->mat_index]; - if ( inorganic->material.heat.boiling_point > 10000 ) { - //continue; + if ( inorganic->material.heat.boiling_point > 9000 ) { + continue; } for ( size_t b = 0; b < inorganic->material.syndrome.size(); b++ ) { //add each syndrome to the guy who did the job df::syndrome* syndrome = inorganic->material.syndrome[b]; + bool workerOnly = false; + bool allowMultipleSyndromes = false; + bool allowMultipleTargets = false; bool foundCommand = false; string commandStr; vector args; for ( size_t c = 0; c < syndrome->syn_class.size(); c++ ) { std::string* clazz = syndrome->syn_class[c]; - out.print("Class = %s\n", clazz->c_str()); if ( foundCommand ) { - if ( commandStr == "" ) - commandStr = *clazz; - else { + if ( commandStr == "" ) { + if ( *clazz == "\\WORKER_ONLY" ) { + workerOnly = true; + } else if ( *clazz == "\\ALLOW_MULTIPLE_SYNDROMES" ) { + allowMultipleSyndromes = true; + } else if ( *clazz == "\\ALLOW_MULTIPLE_TARGETS" ) { + allowMultipleTargets = true; + } + else { + commandStr = *clazz; + } + } else { stringstream bob; if ( *clazz == "\\LOCATION" ) { bob << job->pos.x; @@ -284,70 +384,36 @@ void processJob(color_ostream& out, void* jobPtr) { if ( commandStr != "" ) { Core::getInstance().runCommand(out, commandStr, args); } - //check that the syndrome applies to that guy - /* - * If there is no affected class or affected creature, then anybody who isn't immune is fair game. - * - * Otherwise, it works like this: - * add all the affected class creatures - * remove all the immune class creatures - * add all the affected creatures - * remove all the immune creatures - * you're affected if and only if you're in the remaining list after all of that - **/ - bool applies = syndrome->syn_affected_class.size() == 0 && syndrome->syn_affected_creature_1.size() == 0; - for ( size_t c = 0; c < syndrome->syn_affected_class.size(); c++ ) { - if ( applies ) - break; - for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { - if ( *syndrome->syn_affected_class[c] == *caste->creature_class[d] ) { - applies = true; - break; - } - } - } - for ( size_t c = 0; c < syndrome->syn_immune_class.size(); c++ ) { - if ( !applies ) - break; - for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { - if ( *syndrome->syn_immune_class[c] == *caste->creature_class[d] ) { - applies = false; - break; - } - } - } - if ( syndrome->syn_affected_creature_1.size() != syndrome->syn_affected_creature_2.size() ) { - out.print("%s, line %d: different affected creature/caste sizes.\n", __FILE__, __LINE__); - return; + //only one syndrome per reaction will be applied, unless multiples are allowed. + if ( appliedSomething && !allowMultipleSyndromes ) + continue; + + if ( maybeApply(out, syndrome, workerId, unit) ) { + appliedSomething = true; + continue; } - for ( size_t c = 0; c < syndrome->syn_affected_creature_1.size(); c++ ) { - if ( creature_name != *syndrome->syn_affected_creature_1[c] ) + + if ( workerOnly ) + continue; + + //now try applying it to everybody inside the building + for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) { + df::unit* unit = df::global::world->units.active[a]; + if ( unit->pos.z != building->z ) continue; - if ( *syndrome->syn_affected_creature_2[c] == "ALL" || - *syndrome->syn_affected_creature_2[c] == creature_caste ) { - applies = true; - break; - } - } - for ( size_t c = 0; c < syndrome->syn_immune_creature_1.size(); c++ ) { - if ( creature_name != *syndrome->syn_immune_creature_1[c] ) + if ( unit->pos.x < building->x1 || unit->pos.x > building->x2 ) + continue; + if ( unit->pos.y < building->y1 || unit->pos.y > building->y2 ) continue; - if ( *syndrome->syn_immune_creature_2[c] == "ALL" || - *syndrome->syn_immune_creature_2[c] == creature_caste ) { - applies = false; - break; + if ( maybeApply(out, syndrome, unit->id, unit) ) { + appliedSomething = true; + if ( !allowMultipleTargets ) + break; } } - if ( !applies ) { - continue; - } - if ( giveSyndrome(out, workerId, syndrome) < 0 ) - return; } } - if ( !foundIt ) - return; return; } From 38ef75418ae5147dcc84ed2f808f8eb0b76850a6 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 14:09:16 -0500 Subject: [PATCH 32/56] AutoSyndrome: added an option to delete boiling rocks as they are created (on by default). --- library/xml | 2 +- plugins/autoSyndrome.cpp | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 22b01b80a..fbf671a7d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 22b01b80ad1f0e82c609dec56f09be1a46788921 +Subproject commit fbf671a7d5aacb41cb44059eb16a1ee9cad419be diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 2c8023035..2fa9fb681 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -11,6 +11,8 @@ #include "df/caste_raw.h" #include "df/creature_raw.h" #include "df/global_objects.h" +#include "df/item.h" +#include "df/item_boulderst.h" #include "df/job.h" #include "df/job_type.h" #include "df/reaction.h" @@ -334,6 +336,7 @@ void processJob(color_ostream& out, void* jobPtr) { bool allowMultipleSyndromes = false; bool allowMultipleTargets = false; bool foundCommand = false; + bool destroyRock = true; string commandStr; vector args; for ( size_t c = 0; c < syndrome->syn_class.size(); c++ ) { @@ -346,6 +349,8 @@ void processJob(color_ostream& out, void* jobPtr) { allowMultipleSyndromes = true; } else if ( *clazz == "\\ALLOW_MULTIPLE_TARGETS" ) { allowMultipleTargets = true; + } else if ( *clazz == "\\PRESERVE_ROCK" ) { + destroyRock = false; } else { commandStr = *clazz; @@ -385,13 +390,35 @@ void processJob(color_ostream& out, void* jobPtr) { Core::getInstance().runCommand(out, commandStr, args); } + if ( destroyRock ) { + //find the rock and kill it before it can boil and cause problems and ugliness + for ( size_t c = 0; c < df::global::world->items.all.size(); c++ ) { + df::item* item = df::global::world->items.all[c]; + if ( item->pos.z != building->z ) + continue; + if ( item->pos.x < building->x1 || item->pos.x > building->x2 ) + continue; + if ( item->pos.y < building->y1 || item->pos.y > building->y2 ) + continue; + if ( item->getType() != df::enums::item_type::BOULDER ) + continue; + //make sure it's the right type of boulder + df::item_boulderst* boulder = (df::item_boulderst*)item; + if ( boulder->mat_index != bob->mat_index ) + continue; + + boulder->flags.bits.garbage_collect = true; + boulder->flags.bits.forbid = true; + boulder->flags.bits.hidden = true; + } + } + //only one syndrome per reaction will be applied, unless multiples are allowed. if ( appliedSomething && !allowMultipleSyndromes ) continue; if ( maybeApply(out, syndrome, workerId, unit) ) { appliedSomething = true; - continue; } if ( workerOnly ) From 03650dbdbdc2a3fff47df77dfd8be17b1c5201d5 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 17:32:11 -0500 Subject: [PATCH 33/56] True transformation. Temp commit. --- plugins/CMakeLists.txt | 1 + plugins/trueTransformation.cpp | 85 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 plugins/trueTransformation.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1c89d9ab7..077f0e4ad 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -132,6 +132,7 @@ if (BUILD_SUPPORTED) #DFHACK_PLUGIN(versionosd versionosd.cpp) DFHACK_PLUGIN(misery misery.cpp) #DFHACK_PLUGIN(dfstream dfstream.cpp LINK_LIBRARIES clsocket dfhack-tinythread) + DFHACK_PLUGIN(trueTransformation trueTransformation.cpp) endif() diff --git a/plugins/trueTransformation.cpp b/plugins/trueTransformation.cpp new file mode 100644 index 000000000..6cd6f16ea --- /dev/null +++ b/plugins/trueTransformation.cpp @@ -0,0 +1,85 @@ + +#include "Core.h" +#include "Console.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" + +using namespace DFHack; + +DFHACK_PLUGIN("trueTransformation"); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "skeleton", "Do nothing, look pretty.", + skeleton, false, /* true means that the command can't be used from non-interactive user interface */ + // Extended help string. Used by CR_WRONG_USAGE and the help command: + " This command does nothing at all.\n" + "Example:\n" + " skeleton\n" + " Does nothing.\n" + )); + return CR_OK; +} + +// This is called right before the plugin library is removed from memory. +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + // You *MUST* kill all threads you created before this returns. + // If everything fails, just return CR_FAILURE. Your plugin will be + // in a zombie state, but things won't crash. + return CR_OK; +} + +// Called to notify the plugin about important state changes. +// Invoked with DF suspended, and always before the matching plugin_onupdate. +// More event codes may be added in the future. +/* +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_GAME_LOADED: + // initialize from the world just loaded + break; + case SC_GAME_UNLOADED: + // cleanup + break; + default: + break; + } + return CR_OK; +} +*/ + +// Whatever you put here will be done in each game step. Don't abuse it. +// It's optional, so you can just comment it out like this if you don't need it. +/* +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + // whetever. You don't need to suspend DF execution here. + return CR_OK; +} +*/ + +// A command! It sits around and looks pretty. And it's nice and friendly. +command_result skeleton (color_ostream &out, std::vector & parameters) +{ + // It's nice to print a help message you get invalid options + // from the user instead of just acting strange. + // This can be achieved by adding the extended help string to the + // PluginCommand registration as show above, and then returning + // CR_WRONG_USAGE from the function. The same string will also + // be used by 'help your-command'. + if (!parameters.empty()) + return CR_WRONG_USAGE; + // Commands are called from threads other than the DF one. + // Suspend this thread until DF has time for us. If you + // use CoreSuspender, it'll automatically resume DF when + // execution leaves the current scope. + CoreSuspender suspend; + // Actually do something here. Yay. + out.print("Hello! I do nothing, remember?\n"); + // Give control back to DF. + return CR_OK; +} From 4e4e382b8f7e359fef6723bebf4cd3e36a578aac Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 18:30:15 -0500 Subject: [PATCH 34/56] EventManager: added syndrome event. --- library/include/modules/EventManager.h | 9 ++++++++ library/modules/EventManager.cpp | 32 ++++++++++++++++++++++++++ library/xml | 2 +- plugins/devel/eventExample.cpp | 10 +++++++- 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index a06439fcd..40d6603a7 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -19,6 +19,7 @@ namespace DFHack { ITEM_CREATED, BUILDING, CONSTRUCTION, + SYNDROME, EVENT_MAX }; } @@ -36,6 +37,14 @@ namespace DFHack { return !( *this == handle); } }; + + struct SyndromeData { + int32_t unitId; + int32_t syndromeIndex; + SyndromeData(int32_t unitId_in, int32_t syndromeIndex_in): unitId(unitId_in), syndromeIndex(syndromeIndex_in) { + + } + }; DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin); DFHACK_EXPORT void registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 2725d80a3..6a355aa36 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -13,6 +13,7 @@ #include "df/job.h" #include "df/job_list_link.h" #include "df/unit.h" +#include "df/unit_syndrome.h" #include "df/world.h" #include @@ -135,6 +136,7 @@ static void manageUnitDeathEvent(color_ostream& out); static void manageItemCreationEvent(color_ostream& out); static void manageBuildingEvent(color_ostream& out); static void manageConstructionEvent(color_ostream& out); +static void manageSyndromeEvent(color_ostream& out); //tick event static uint32_t lastTick = 0; @@ -235,6 +237,10 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { manageConstructionEvent(out); eventLastTick[EventType::CONSTRUCTION] = tick; } + if ( tick - eventLastTick[EventType::SYNDROME] >= (*eventFrequency[EventType::SYNDROME].begin()).first ) { + manageSyndromeEvent(out); + eventLastTick[EventType::SYNDROME] = tick; + } return; } @@ -458,3 +464,29 @@ static void manageConstructionEvent(color_ostream& out) { constructions.clear(); constructions.insert(constructionsNow.begin(), constructionsNow.end()); } + +static void manageSyndromeEvent(color_ostream& out) { + if ( handlers[EventType::SYNDROME].empty() ) + return; + + multimap copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end()); + for ( auto a = df::global::world->units.active.begin(); a != df::global::world->units.active.end(); a++ ) { + df::unit* unit = *a; + if ( unit->flags1.bits.dead ) + continue; + for ( size_t b = 0; b < unit->syndromes.active.size(); b++ ) { + df::unit_syndrome* syndrome = unit->syndromes.active[b]; + uint32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time; + out.print("start time = %d, time = %d\n", startTime, eventLastTick[EventType::SYNDROME]); + if ( startTime < eventLastTick[EventType::SYNDROME] ) + continue; + + SyndromeData data(unit->id, b); + for ( auto c = copy.begin(); c != copy.end(); c++ ) { + EventHandler handle = (*c).second; + handle.eventHandler(out, (void*)&data); + } + } + } +} + diff --git a/library/xml b/library/xml index 22b01b80a..fbf671a7d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 22b01b80ad1f0e82c609dec56f09be1a46788921 +Subproject commit fbf671a7d5aacb41cb44059eb16a1ee9cad419be diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index e6a3dd22f..aaaa35a09 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -23,10 +23,10 @@ void unitDeath(color_ostream& out, void* ptr); void itemCreate(color_ostream& out, void* ptr); void building(color_ostream& out, void* ptr); void construction(color_ostream& out, void* ptr); +void syndrome(color_ostream& out, void* ptr); command_result eventExample(color_ostream& out, vector& parameters); - DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand("eventExample", "Sets up a few event triggers.",eventExample)); return CR_OK; @@ -40,6 +40,7 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::EventHandler itemHandler(itemCreate); EventManager::EventHandler buildingHandler(building); EventManager::EventHandler constructionHandler(construction); + EventManager::EventHandler syndromeHandler(syndrome); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); EventManager::unregisterAll(me); @@ -53,6 +54,7 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, 1000, me); EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, 500, me); EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, 100, me); + EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, 1, me); out.print("Events registered.\n"); return CR_OK; } @@ -91,3 +93,9 @@ void building(color_ostream& out, void* ptr) { void construction(color_ostream& out, void* ptr) { out.print("Construction created/destroyed: 0x%X\n", ptr); } + +void syndrome(color_ostream& out, void* ptr) { + EventManager::SyndromeData* data = (EventManager::SyndromeData*)ptr; + out.print("Syndrome started: unit %d, syndrome %d.\n", data->unitId, data->syndromeIndex); +} + From d4afa4b6e45c3b8b76e0c1d551ce566b71607dd7 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 19:21:25 -0500 Subject: [PATCH 35/56] True Transformation: turn into something temporarily, then permanently transform into another creature. If you try to do that with a syndrome, you can't do it recursively. --- plugins/trueTransformation.cpp | 133 +++++++++++++++++---------------- 1 file changed, 69 insertions(+), 64 deletions(-) diff --git a/plugins/trueTransformation.cpp b/plugins/trueTransformation.cpp index 6cd6f16ea..fd1e1a61b 100644 --- a/plugins/trueTransformation.cpp +++ b/plugins/trueTransformation.cpp @@ -5,81 +5,86 @@ #include "Export.h" #include "PluginManager.h" +#include "modules/EventManager.h" + +#include "df/caste_raw.h" +#include "df/creature_raw.h" +#include "df/syndrome.h" +#include "df/unit.h" +#include "df/unit_syndrome.h" +#include "df/world.h" + +#include + using namespace DFHack; +using namespace std; DFHACK_PLUGIN("trueTransformation"); +void syndromeHandler(color_ostream& out, void* ptr); + DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - commands.push_back(PluginCommand( - "skeleton", "Do nothing, look pretty.", - skeleton, false, /* true means that the command can't be used from non-interactive user interface */ - // Extended help string. Used by CR_WRONG_USAGE and the help command: - " This command does nothing at all.\n" - "Example:\n" - " skeleton\n" - " Does nothing.\n" - )); - return CR_OK; -} + EventManager::EventHandler syndrome(syndromeHandler); + Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("trueTransformation"); + EventManager::registerListener(EventManager::EventType::SYNDROME, syndrome, 1, me); -// This is called right before the plugin library is removed from memory. -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - // You *MUST* kill all threads you created before this returns. - // If everything fails, just return CR_FAILURE. Your plugin will be - // in a zombie state, but things won't crash. return CR_OK; } -// Called to notify the plugin about important state changes. -// Invoked with DF suspended, and always before the matching plugin_onupdate. -// More event codes may be added in the future. -/* -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) { - case SC_GAME_LOADED: - // initialize from the world just loaded - break; - case SC_GAME_UNLOADED: - // cleanup - break; - default: - break; +void syndromeHandler(color_ostream& out, void* ptr) { + EventManager::SyndromeData* data = (EventManager::SyndromeData*)ptr; + //out.print("Syndrome started: unit %d, syndrome %d.\n", data->unitId, data->syndromeIndex); + + int32_t index = df::unit::binsearch_index(df::global::world->units.active, data->unitId); + if ( index < 0 ) { + out.print("%s, line %d: couldn't find unit.\n", __FILE__, __LINE__); + return; } - return CR_OK; -} -*/ + df::unit* unit = df::global::world->units.active[index]; + df::unit_syndrome* unit_syndrome = unit->syndromes.active[data->syndromeIndex]; + df::syndrome* syndrome = df::global::world->raws.syndromes.all[unit_syndrome->type]; + + bool foundIt = false; + int32_t raceId = -1; + df::creature_raw* creatureRaw = NULL; + int32_t casteId = -1; + for ( size_t a = 0; a < syndrome->syn_class.size(); a++ ) { + if ( *syndrome->syn_class[a] == "\\PERMANENT" ) { + foundIt = true; + } + if ( foundIt && raceId == -1 ) { + //find the race with the name + string& name = *syndrome->syn_class[a]; + for ( size_t b = 0; b < df::global::world->raws.creatures.all.size(); b++ ) { + df::creature_raw* creature = df::global::world->raws.creatures.all[b]; + if ( creature->creature_id != name ) + continue; + raceId = b; + creatureRaw = creature; + break; + } + continue; + } + if ( foundIt && raceId != -1 ) { + string& name = *syndrome->syn_class[a]; + for ( size_t b = 0; b < creatureRaw->caste.size(); b++ ) { + df::caste_raw* caste = creatureRaw->caste[b]; + if ( caste->caste_id != name ) + continue; + casteId = b; + break; + } + break; + } + } + out.print("foundIt = %d, raceId = %d, casteId = %d\n", (int32_t)foundIt, raceId, casteId); + if ( !foundIt || raceId == -1 || casteId == -1 ) + return; -// Whatever you put here will be done in each game step. Don't abuse it. -// It's optional, so you can just comment it out like this if you don't need it. -/* -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) -{ - // whetever. You don't need to suspend DF execution here. - return CR_OK; + unit->enemy.normal_race = raceId; + unit->enemy.normal_caste = casteId; + out.print("Did the thing.\n"); + //that's it! } -*/ -// A command! It sits around and looks pretty. And it's nice and friendly. -command_result skeleton (color_ostream &out, std::vector & parameters) -{ - // It's nice to print a help message you get invalid options - // from the user instead of just acting strange. - // This can be achieved by adding the extended help string to the - // PluginCommand registration as show above, and then returning - // CR_WRONG_USAGE from the function. The same string will also - // be used by 'help your-command'. - if (!parameters.empty()) - return CR_WRONG_USAGE; - // Commands are called from threads other than the DF one. - // Suspend this thread until DF has time for us. If you - // use CoreSuspender, it'll automatically resume DF when - // execution leaves the current scope. - CoreSuspender suspend; - // Actually do something here. Yay. - out.print("Hello! I do nothing, remember?\n"); - // Give control back to DF. - return CR_OK; -} From 6d2773856a22f3ec4fabe3047222a6fce02d6931 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 19:23:40 -0500 Subject: [PATCH 36/56] EventManager: fixed a few things. --- library/modules/EventManager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 6a355aa36..b3f22b109 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -159,6 +159,7 @@ static unordered_set buildings; //construction static unordered_set constructions; +static bool gameLoaded; void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { static bool doOnce = false; @@ -184,6 +185,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event constructions.clear(); Buildings::clearBuildings(out); + gameLoaded = false; } else if ( event == DFHack::SC_MAP_LOADED ) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); @@ -198,11 +200,12 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event nextItem = 0; nextBuilding = 0; lastTick = 0; + gameLoaded = true; } } void DFHack::EventManager::manageEvents(color_ostream& out) { - if ( !Core::getInstance().isWorldLoaded() ) { + if ( !gameLoaded ) { return; } uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear @@ -478,7 +481,7 @@ static void manageSyndromeEvent(color_ostream& out) { df::unit_syndrome* syndrome = unit->syndromes.active[b]; uint32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time; out.print("start time = %d, time = %d\n", startTime, eventLastTick[EventType::SYNDROME]); - if ( startTime < eventLastTick[EventType::SYNDROME] ) + if ( startTime <= eventLastTick[EventType::SYNDROME] ) continue; SyndromeData data(unit->id, b); From a8b0a7e6958be517ea487202ca0e81b2edf9cf0e Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 19:25:24 -0500 Subject: [PATCH 37/56] EventManager: got rid of silly print statement. --- library/modules/EventManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index b3f22b109..8611417b0 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -480,7 +480,6 @@ static void manageSyndromeEvent(color_ostream& out) { for ( size_t b = 0; b < unit->syndromes.active.size(); b++ ) { df::unit_syndrome* syndrome = unit->syndromes.active[b]; uint32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time; - out.print("start time = %d, time = %d\n", startTime, eventLastTick[EventType::SYNDROME]); if ( startTime <= eventLastTick[EventType::SYNDROME] ) continue; From 5865579b23a0f28e86ddf344084a84a469233e34 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 19:26:37 -0500 Subject: [PATCH 38/56] EventManager: got rid of print statement. --- library/modules/EventManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index b3f22b109..8611417b0 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -480,7 +480,6 @@ static void manageSyndromeEvent(color_ostream& out) { for ( size_t b = 0; b < unit->syndromes.active.size(); b++ ) { df::unit_syndrome* syndrome = unit->syndromes.active[b]; uint32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time; - out.print("start time = %d, time = %d\n", startTime, eventLastTick[EventType::SYNDROME]); if ( startTime <= eventLastTick[EventType::SYNDROME] ) continue; From 285ee3b399f6505b67ce305f952cf52e6f2b46fd Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 19:45:08 -0500 Subject: [PATCH 39/56] True transformation: got rid of debug print statements. --- plugins/trueTransformation.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/trueTransformation.cpp b/plugins/trueTransformation.cpp index fd1e1a61b..6da6b7e74 100644 --- a/plugins/trueTransformation.cpp +++ b/plugins/trueTransformation.cpp @@ -78,13 +78,11 @@ void syndromeHandler(color_ostream& out, void* ptr) { break; } } - out.print("foundIt = %d, raceId = %d, casteId = %d\n", (int32_t)foundIt, raceId, casteId); if ( !foundIt || raceId == -1 || casteId == -1 ) return; unit->enemy.normal_race = raceId; unit->enemy.normal_caste = casteId; - out.print("Did the thing.\n"); //that's it! } From 123f34bf85f6eb3b9331f563b3d4abd4438047f9 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 21:06:48 -0500 Subject: [PATCH 40/56] Ignore swap files. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9f2b009c6..b4a578ec0 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,6 @@ dfhack/python/dist build/CPack*Config.cmake /cmakeall.bat + +# vim swap files +*.swp From 910e398a7bb3d7deb7f13cdbcd74acef6325a7d7 Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 3 Jan 2013 15:52:56 -0500 Subject: [PATCH 41/56] EventManager: added invasion event. --- library/include/modules/EventManager.h | 1 + library/modules/EventManager.cpp | 27 ++++++++++++++++++++++++++ plugins/devel/eventExample.cpp | 7 +++++++ 3 files changed, 35 insertions(+) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 40d6603a7..1f7758ddf 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -20,6 +20,7 @@ namespace DFHack { BUILDING, CONSTRUCTION, SYNDROME, + INVASION, EVENT_MAX }; } diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 8611417b0..77248038f 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -12,6 +12,7 @@ #include "df/item.h" #include "df/job.h" #include "df/job_list_link.h" +#include "df/ui.h" #include "df/unit.h" #include "df/unit_syndrome.h" #include "df/world.h" @@ -137,6 +138,7 @@ static void manageItemCreationEvent(color_ostream& out); static void manageBuildingEvent(color_ostream& out); static void manageConstructionEvent(color_ostream& out); static void manageSyndromeEvent(color_ostream& out); +static void manageInvasionEvent(color_ostream& out); //tick event static uint32_t lastTick = 0; @@ -161,6 +163,9 @@ static unordered_set buildings; static unordered_set constructions; static bool gameLoaded; +//invasion +static int32_t nextInvasion; + void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { static bool doOnce = false; if ( !doOnce ) { @@ -186,6 +191,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event Buildings::clearBuildings(out); gameLoaded = false; + nextInvasion = -1; } else if ( event == DFHack::SC_MAP_LOADED ) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); @@ -200,6 +206,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event nextItem = 0; nextBuilding = 0; lastTick = 0; + nextInvasion = df::global::ui->invasions.next_id; gameLoaded = true; } } @@ -244,6 +251,10 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { manageSyndromeEvent(out); eventLastTick[EventType::SYNDROME] = tick; } + if ( tick - eventLastTick[EventType::INVASION] >= (*eventFrequency[EventType::INVASION].begin()).first ) { + manageInvasionEvent(out); + eventLastTick[EventType::INVASION] = tick; + } return; } @@ -492,3 +503,19 @@ static void manageSyndromeEvent(color_ostream& out) { } } +static void manageInvasionEvent(color_ostream& out) { + if ( handlers[EventType::INVASION].empty() ) + return; + + multimap copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end()); + + if ( df::global::ui->invasions.next_id <= nextInvasion ) + return; + nextInvasion = df::global::ui->invasions.next_id; + + for ( auto a = copy.begin(); a != copy.end(); a++ ) { + EventHandler handle = (*a).second; + handle.eventHandler(out, (void*)nextInvasion); + } +} + diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index aaaa35a09..3bc84879a 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -24,6 +24,7 @@ void itemCreate(color_ostream& out, void* ptr); void building(color_ostream& out, void* ptr); void construction(color_ostream& out, void* ptr); void syndrome(color_ostream& out, void* ptr); +void invasion(color_ostream& out, void* ptr); command_result eventExample(color_ostream& out, vector& parameters); @@ -41,6 +42,7 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::EventHandler buildingHandler(building); EventManager::EventHandler constructionHandler(construction); EventManager::EventHandler syndromeHandler(syndrome); + EventManager::EventHandler invasionHandler(invasion); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); EventManager::unregisterAll(me); @@ -55,6 +57,7 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, 500, me); EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, 100, me); EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, 1, me); + EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, 1, me); out.print("Events registered.\n"); return CR_OK; } @@ -99,3 +102,7 @@ void syndrome(color_ostream& out, void* ptr) { out.print("Syndrome started: unit %d, syndrome %d.\n", data->unitId, data->syndromeIndex); } +void invasion(color_ostream& out, void* ptr) { + out.print("New invasion! %d\n", (int32_t)ptr); +} + From 9e74ae58f25db9380bfa27e4cd1fa01eab173215 Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 3 Jan 2013 19:07:05 -0500 Subject: [PATCH 42/56] EventManager: Fixed a problem with deregistering event frequencies. --- library/modules/EventManager.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 77248038f..6df6106de 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -120,13 +120,23 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) { int32_t freq = (*b).second; eventFrequency[a][freq]--; if ( eventFrequency[a][freq] < 0 ) { - Core::getInstance().getConsole().print("%s, line %d: Error: incorrect frequency on deregister.\n", __FILE__, __LINE__); + Core::getInstance().getConsole().print("%s, line %d: Error: incorrect frequency on deregister: %d, %d.\n", __FILE__, __LINE__, a, freq); eventFrequency[a].erase(eventFrequency[a].find(freq)); } else if ( eventFrequency[a][freq] == 0 ) { eventFrequency[a].erase(eventFrequency[a].find(freq)); } } } + //now delete the frequencies from the thing + for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) { + for ( auto b = pluginFrequencies[a].begin(); b != pluginFrequencies[a].end(); b++ ) { + if ( (*b).first != plugin ) + continue; + pluginFrequencies[a].erase(b); + a--; + break; + } + } return; } From 715f191c264009c3a30b89e1011620b930ebaa6a Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 3 Jan 2013 19:31:29 -0500 Subject: [PATCH 43/56] EventManager: made the frequency part of EventHandler. --- library/include/modules/EventManager.h | 9 ++-- library/modules/EventManager.cpp | 74 ++++++++------------------ plugins/devel/eventExample.cpp | 34 ++++++------ 3 files changed, 45 insertions(+), 72 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 1f7758ddf..9cca6e0e0 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -27,12 +27,13 @@ namespace DFHack { struct EventHandler { void (*eventHandler)(color_ostream&, void*); //called when the event happens + int32_t freq; - EventHandler(void (*eventHandlerIn)(color_ostream&, void*)): eventHandler(eventHandlerIn) { + EventHandler(void (*eventHandlerIn)(color_ostream&, void*), int32_t freqIn): eventHandler(eventHandlerIn), freq(freqIn) { } bool operator==(EventHandler& handle) const { - return eventHandler == handle.eventHandler; + return eventHandler == handle.eventHandler && freq == handle.freq; } bool operator!=(EventHandler& handle) const { return !( *this == handle); @@ -47,9 +48,9 @@ namespace DFHack { } }; - DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin); + DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin); DFHACK_EXPORT void registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false); - DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin); + DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin); DFHACK_EXPORT void unregisterAll(Plugin* plugin); void manageEvents(color_ostream& out); void onStateChange(color_ostream& out, state_change_event event); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 6df6106de..ff48263fc 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -36,16 +36,12 @@ multimap tickQueue; //TODO: consider unordered_map of pairs, or unordered_map of unordered_set, or whatever multimap handlers[EventType::EVENT_MAX]; -multimap pluginFrequencies[EventType::EVENT_MAX]; -map eventFrequency[EventType::EVENT_MAX]; uint32_t eventLastTick[EventType::EVENT_MAX]; const uint32_t ticksPerYear = 403200; -void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin) { +void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin) { handlers[e].insert(pair(plugin, handler)); - eventFrequency[e][freq]++; - pluginFrequencies[e].insert(pair(plugin, freq)); } void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute) { @@ -66,7 +62,7 @@ void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plug return; } -void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handler, int32_t freq, Plugin* plugin) { +void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handler, Plugin* plugin) { for ( multimap::iterator i = handlers[e].find(plugin); i != handlers[e].end(); i++ ) { if ( (*i).first != plugin ) break; @@ -76,16 +72,6 @@ void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handl break; } } - if ( eventFrequency[e].find(freq) == eventFrequency[e].end() ) { - Core::getInstance().getConsole().print("%s, line %d: Error: incorrect frequency on deregister.\n", __FILE__, __LINE__); - return; - } - eventFrequency[e][freq]--; - if ( eventFrequency[e][freq] == 0 ) { - eventFrequency[e].erase(eventFrequency[e].find(freq)); - } else if ( eventFrequency[e][freq] < 0 ) { - Core::getInstance().getConsole().print("%s, line %d: Error: incorrect frequency on deregister.\n", __FILE__, __LINE__); - } return; } @@ -112,31 +98,6 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) { for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) { handlers[a].erase(plugin); } - - for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) { - for ( auto b = pluginFrequencies[a].begin(); b != pluginFrequencies[a].end(); b++ ) { - if ( (*b).first != plugin ) - continue; - int32_t freq = (*b).second; - eventFrequency[a][freq]--; - if ( eventFrequency[a][freq] < 0 ) { - Core::getInstance().getConsole().print("%s, line %d: Error: incorrect frequency on deregister: %d, %d.\n", __FILE__, __LINE__, a, freq); - eventFrequency[a].erase(eventFrequency[a].find(freq)); - } else if ( eventFrequency[a][freq] == 0 ) { - eventFrequency[a].erase(eventFrequency[a].find(freq)); - } - } - } - //now delete the frequencies from the thing - for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) { - for ( auto b = pluginFrequencies[a].begin(); b != pluginFrequencies[a].end(); b++ ) { - if ( (*b).first != plugin ) - continue; - pluginFrequencies[a].erase(b); - a--; - break; - } - } return; } @@ -181,8 +142,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event if ( !doOnce ) { //TODO: put this somewhere else doOnce = true; - EventHandler buildingHandler(Buildings::updateBuildings); - DFHack::EventManager::registerListener(EventType::BUILDING, buildingHandler, 100, NULL); + EventHandler buildingHandler(Buildings::updateBuildings, 100); + DFHack::EventManager::registerListener(EventType::BUILDING, buildingHandler, NULL); //out.print("Registered listeners.\n %d", __LINE__); } if ( event == DFHack::SC_MAP_UNLOADED ) { @@ -231,37 +192,48 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { if ( tick <= lastTick ) return; lastTick = tick; + + int32_t eventFrequency[EventType::EVENT_MAX]; + for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) { + int32_t min = 1000000000; + for ( auto b = handlers[a].begin(); b != handlers[a].end(); b++ ) { + EventHandler bob = (*b).second; + if ( bob.freq < min ) + min = bob.freq; + } + eventFrequency[a] = min; + } manageTickEvent(out); - if ( tick - eventLastTick[EventType::JOB_INITIATED] >= (*eventFrequency[EventType::JOB_INITIATED].begin()).first ) { + if ( tick - eventLastTick[EventType::JOB_INITIATED] >= eventFrequency[EventType::JOB_INITIATED] ) { manageJobInitiatedEvent(out); eventLastTick[EventType::JOB_INITIATED] = tick; } - if ( tick - eventLastTick[EventType::JOB_COMPLETED] >= (*eventFrequency[EventType::JOB_COMPLETED].begin()).first ) { + if ( tick - eventLastTick[EventType::JOB_COMPLETED] >= eventFrequency[EventType::JOB_COMPLETED] ) { manageJobCompletedEvent(out); eventLastTick[EventType::JOB_COMPLETED] = tick; } - if ( tick - eventLastTick[EventType::UNIT_DEATH] >= (*eventFrequency[EventType::UNIT_DEATH].begin()).first ) { + if ( tick - eventLastTick[EventType::UNIT_DEATH] >= eventFrequency[EventType::UNIT_DEATH] ) { manageUnitDeathEvent(out); eventLastTick[EventType::UNIT_DEATH] = tick; } - if ( tick - eventLastTick[EventType::ITEM_CREATED] >= (*eventFrequency[EventType::ITEM_CREATED].begin()).first ) { + if ( tick - eventLastTick[EventType::ITEM_CREATED] >= eventFrequency[EventType::ITEM_CREATED] ) { manageItemCreationEvent(out); eventLastTick[EventType::ITEM_CREATED] = tick; } - if ( tick - eventLastTick[EventType::BUILDING] >= (*eventFrequency[EventType::BUILDING].begin()).first ) { + if ( tick - eventLastTick[EventType::BUILDING] >= eventFrequency[EventType::BUILDING] ) { manageBuildingEvent(out); eventLastTick[EventType::BUILDING] = tick; } - if ( tick - eventLastTick[EventType::CONSTRUCTION] >= (*eventFrequency[EventType::CONSTRUCTION].begin()).first ) { + if ( tick - eventLastTick[EventType::CONSTRUCTION] >= eventFrequency[EventType::CONSTRUCTION] ) { manageConstructionEvent(out); eventLastTick[EventType::CONSTRUCTION] = tick; } - if ( tick - eventLastTick[EventType::SYNDROME] >= (*eventFrequency[EventType::SYNDROME].begin()).first ) { + if ( tick - eventLastTick[EventType::SYNDROME] >= eventFrequency[EventType::SYNDROME] ) { manageSyndromeEvent(out); eventLastTick[EventType::SYNDROME] = tick; } - if ( tick - eventLastTick[EventType::INVASION] >= (*eventFrequency[EventType::INVASION].begin()).first ) { + if ( tick - eventLastTick[EventType::INVASION] >= eventFrequency[EventType::INVASION] ) { manageInvasionEvent(out); eventLastTick[EventType::INVASION] = tick; } diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index 3bc84879a..3a2970c5b 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -34,30 +34,30 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector& parameters) { - EventManager::EventHandler initiateHandler(jobInitiated); - EventManager::EventHandler completeHandler(jobCompleted); - EventManager::EventHandler timeHandler(timePassed); - EventManager::EventHandler deathHandler(unitDeath); - EventManager::EventHandler itemHandler(itemCreate); - EventManager::EventHandler buildingHandler(building); - EventManager::EventHandler constructionHandler(construction); - EventManager::EventHandler syndromeHandler(syndrome); - EventManager::EventHandler invasionHandler(invasion); + EventManager::EventHandler initiateHandler(jobInitiated, 10); + EventManager::EventHandler completeHandler(jobCompleted, 5); + EventManager::EventHandler timeHandler(timePassed, 1); + EventManager::EventHandler deathHandler(unitDeath, 500); + EventManager::EventHandler itemHandler(itemCreate, 1000); + EventManager::EventHandler buildingHandler(building, 500); + EventManager::EventHandler constructionHandler(construction, 100); + EventManager::EventHandler syndromeHandler(syndrome, 1); + EventManager::EventHandler invasionHandler(invasion, 1000); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); EventManager::unregisterAll(me); - EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, 10, me); - EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, 5, me); + EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, me); EventManager::registerTick(timeHandler, 1, me); EventManager::registerTick(timeHandler, 2, me); EventManager::registerTick(timeHandler, 4, me); EventManager::registerTick(timeHandler, 8, me); - EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, 500, me); - EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, 1000, me); - EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, 500, me); - EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, 100, me); - EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, 1, me); - EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, 1, me); + EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, me); + EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, me); + EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, me); + EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, me); + EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, me); + EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, me); out.print("Events registered.\n"); return CR_OK; } From ec03d567d28bd6fd5d4f656d7d42e3c2e1e246de Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 3 Jan 2013 22:47:27 -0500 Subject: [PATCH 44/56] EventManager: use WORLD_LOADED instead of MAP_LOADED. --- library/modules/EventManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index ff48263fc..3af096004 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -146,7 +146,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event DFHack::EventManager::registerListener(EventType::BUILDING, buildingHandler, NULL); //out.print("Registered listeners.\n %d", __LINE__); } - if ( event == DFHack::SC_MAP_UNLOADED ) { + if ( event == DFHack::SC_WORLD_UNLOADED ) { lastTick = 0; lastJobId = -1; for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { @@ -163,7 +163,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event Buildings::clearBuildings(out); gameLoaded = false; nextInvasion = -1; - } else if ( event == DFHack::SC_MAP_LOADED ) { + } else if ( event == DFHack::SC_WORLD_LOADED ) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); multimap newTickQueue; From bc5cdf88777e6655857317b60ee2ff655dafb298 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 11:37:12 -0500 Subject: [PATCH 45/56] Auto syndrome: got rid of allow multiple syndromes option that didn't make any sense, and made sure that if allowing multiple targets it doesn't attach the syndrome to the worker twice. --- plugins/autoSyndrome.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 2fa9fb681..574bcf853 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -119,7 +119,7 @@ DFhackCExport command_result plugin_init(color_ostream& out, vectorunits.all[workerIndex]; + df::unit* worker = df::global::world->units.all[workerIndex]; //find the building that made it int32_t buildingId = -1; for ( size_t a = 0; a < job->general_refs.size(); a++ ) { @@ -333,7 +333,6 @@ void processJob(color_ostream& out, void* jobPtr) { //add each syndrome to the guy who did the job df::syndrome* syndrome = inorganic->material.syndrome[b]; bool workerOnly = false; - bool allowMultipleSyndromes = false; bool allowMultipleTargets = false; bool foundCommand = false; bool destroyRock = true; @@ -345,8 +344,6 @@ void processJob(color_ostream& out, void* jobPtr) { if ( commandStr == "" ) { if ( *clazz == "\\WORKER_ONLY" ) { workerOnly = true; - } else if ( *clazz == "\\ALLOW_MULTIPLE_SYNDROMES" ) { - allowMultipleSyndromes = true; } else if ( *clazz == "\\ALLOW_MULTIPLE_TARGETS" ) { allowMultipleTargets = true; } else if ( *clazz == "\\PRESERVE_ROCK" ) { @@ -414,10 +411,10 @@ void processJob(color_ostream& out, void* jobPtr) { } //only one syndrome per reaction will be applied, unless multiples are allowed. - if ( appliedSomething && !allowMultipleSyndromes ) + if ( appliedSomething && !allowMultipleTargets ) continue; - if ( maybeApply(out, syndrome, workerId, unit) ) { + if ( maybeApply(out, syndrome, workerId, worker) ) { appliedSomething = true; } @@ -427,6 +424,8 @@ void processJob(color_ostream& out, void* jobPtr) { //now try applying it to everybody inside the building for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) { df::unit* unit = df::global::world->units.active[a]; + if ( unit == worker ) + continue; if ( unit->pos.z != building->z ) continue; if ( unit->pos.x < building->x1 || unit->pos.x > building->x2 ) From 5fc466ef7e4967c883efd23678860ddb76b4b0b1 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 12:50:10 -0500 Subject: [PATCH 46/56] Work now: also update dig on pause. --- plugins/workNow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/workNow.cpp b/plugins/workNow.cpp index 906df33dc..5058259f5 100644 --- a/plugins/workNow.cpp +++ b/plugins/workNow.cpp @@ -42,6 +42,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan return CR_OK; *df::global::process_jobs = true; + *df::global::process_dig = true; return CR_OK; } From e1ad933068994c3441fa3e7bafed3dcbbfba1666 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 12:58:06 -0500 Subject: [PATCH 47/56] True transformation: make it compile. --- plugins/trueTransformation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/trueTransformation.cpp b/plugins/trueTransformation.cpp index 6da6b7e74..6c4245da8 100644 --- a/plugins/trueTransformation.cpp +++ b/plugins/trueTransformation.cpp @@ -25,9 +25,9 @@ void syndromeHandler(color_ostream& out, void* ptr); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - EventManager::EventHandler syndrome(syndromeHandler); + EventManager::EventHandler syndrome(syndromeHandler, 1); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("trueTransformation"); - EventManager::registerListener(EventManager::EventType::SYNDROME, syndrome, 1, me); + EventManager::registerListener(EventManager::EventType::SYNDROME, syndrome, me); return CR_OK; } From 9eafc7443d2f2fb23af5c194b8f3d4b658dabcb0 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 13:15:10 -0500 Subject: [PATCH 48/56] Update submodules. --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 22b01b80a..fbf671a7d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 22b01b80ad1f0e82c609dec56f09be1a46788921 +Subproject commit fbf671a7d5aacb41cb44059eb16a1ee9cad419be From cf3ac485184d4b0bd18eb19ea91fb88ec8d791d7 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 13:35:49 -0500 Subject: [PATCH 49/56] Merge in autoSyndrome, trueTransformation, ... --- .gitignore | 2 +- plugins/CMakeLists.txt | 2 + plugins/autoSyndrome.cpp | 477 +++++++++++++++++++++++++++++++++ plugins/devel/CMakeLists.txt | 1 + plugins/devel/printArgs.cpp | 32 +++ plugins/trueTransformation.cpp | 88 ++++++ 6 files changed, 601 insertions(+), 1 deletion(-) create mode 100644 plugins/autoSyndrome.cpp create mode 100644 plugins/devel/printArgs.cpp create mode 100644 plugins/trueTransformation.cpp diff --git a/.gitignore b/.gitignore index 041624475..b4a578ec0 100644 --- a/.gitignore +++ b/.gitignore @@ -58,5 +58,5 @@ build/CPack*Config.cmake /cmakeall.bat -# swap files for vim +# vim swap files *.swp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1c89d9ab7..a2f99c206 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -132,6 +132,8 @@ if (BUILD_SUPPORTED) #DFHACK_PLUGIN(versionosd versionosd.cpp) DFHACK_PLUGIN(misery misery.cpp) #DFHACK_PLUGIN(dfstream dfstream.cpp LINK_LIBRARIES clsocket dfhack-tinythread) + DFHACK_PLUGIN(autoSyndrome autoSyndrome.cpp) + DFHACK_PLUGIN(trueTransformation trueTransformation.cpp) endif() diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp new file mode 100644 index 000000000..884c7749b --- /dev/null +++ b/plugins/autoSyndrome.cpp @@ -0,0 +1,477 @@ +#include "PluginManager.h" +#include "Export.h" +#include "DataDefs.h" +#include "Core.h" + +#include "modules/EventManager.h" +#include "modules/Job.h" +#include "modules/Maps.h" + +#include "df/building.h" +#include "df/caste_raw.h" +#include "df/creature_raw.h" +#include "df/global_objects.h" +#include "df/item.h" +#include "df/item_boulderst.h" +#include "df/job.h" +#include "df/job_type.h" +#include "df/reaction.h" +#include "df/reaction_product.h" +#include "df/reaction_product_type.h" +#include "df/reaction_product_itemst.h" +#include "df/syndrome.h" +#include "df/unit_syndrome.h" +#include "df/ui.h" +#include "df/unit.h" +#include "df/general_ref.h" +#include "df/general_ref_building_holderst.h" +#include "df/general_ref_type.h" +#include "df/general_ref_unit_workerst.h" + +#include +#include +#include +#include + +using namespace std; +using namespace DFHack; + +/* +Example usage: + +////////////////////////////////////////////// +//In file inorganic_duck.txt +inorganic_stone_duck + +[OBJECT:INORGANIC] + +[INORGANIC:DUCK_ROCK] +[USE_MATERIAL_TEMPLATE:STONE_TEMPLATE] +[STATE_NAME_ADJ:ALL_SOLID:drakium][DISPLAY_COLOR:0:7:0][TILE:'.'] +[IS_STONE] +[SOLID_DENSITY:1][MELTING_POINT:25000] +[BOILING_POINT:9999] //This is the critical line: boiling point must be <= 10000 +[SYNDROME] + [SYN_NAME:Chronic Duck Syndrome] + [CE_BODY_TRANSFORMATION:PROB:100:START:0] + [CE:CREATURE:BIRD_DUCK:MALE] //even though we don't have SYN_INHALED, the plugin will add it +/////////////////////////////////////////////// +//In file building_duck.txt +building_duck + +[OBJECT:BUILDING] + +[BUILDING_WORKSHOP:DUCK_WORKSHOP] + [NAME:Duck Workshop] + [NAME_COLOR:7:0:1] + [DIM:1:1] + [WORK_LOCATION:1:1] + [BLOCK:1:0:0:0] + [TILE:0:1:236] + [COLOR:0:1:0:0:1] + [TILE:1:1:' '] + [COLOR:1:1:0:0:0] + [TILE:2:1:8] + [COLOR:2:1:0:0:1] + [TILE:3:1:8] + [COLOR:3:2:0:4:1] + [BUILD_ITEM:1:NONE:NONE:NONE:NONE] + [BUILDMAT] + [WORTHLESS_STONE_ONLY] + [CAN_USE_ARTIFACT] +/////////////////////////////////////////////// +//In file reaction_duck.txt +reaction_duck + +[OBJECT:REACTION] + +[REACTION:DUCKIFICATION] +[NAME:become a duck] +[BUILDING:DUCK_WORKSHOP:NONE] +[PRODUCT:100:100:STONE:NO_SUBTYPE:STONE:DUCK_ROCK] +////////////////////////////////////////////// +//Add the following lines to your entity in entity_default.txt (or wherever it is) + [PERMITTED_BUILDING:DUCK_WORKSHOP] + [PERMITTED_REACTION:DUCKIFICATION] +////////////////////////////////////////////// + +Next, start a new fort in a new world, build a duck workshop, then have someone become a duck. +*/ + +bool enabled = true; + +DFHACK_PLUGIN("autoSyndrome"); + +command_result autoSyndrome(color_ostream& out, vector& parameters); +void processJob(color_ostream& out, void* jobPtr); +int32_t giveSyndrome(color_ostream& out, int32_t workerId, df::syndrome* syndrome); + +DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { + commands.push_back(PluginCommand("autoSyndrome", "Automatically give units syndromes when they complete jobs, as configured in the raw files.\n", &autoSyndrome, false, + "autoSyndrome:\n" + " autoSyndrome 0 //disable\n" + " autoSyndrome 1 //enable\n" + " autoSyndrome disable //disable\n" + " autoSyndrome enable //enable\n" + "\n" + "autoSyndrome looks for recently completed jobs matching certain conditions, and if it finds one, then it will give the dwarf that finished that job the syndrome specified in the raw files.\n" + "\n" + "Requirements:\n" + " 1) The job must be a custom reaction.\n" + " 2) The job must produce a stone of some inorganic material.\n" + " 3) The stone must have a boiling temperature less than or equal to 9000.\n" + "\n" + "When these conditions are met, the unit that completed the job will immediately become afflicted with all applicable syndromes associated with the inorganic material of the stone, or stones. It should correctly check for whether the creature or caste is affected or immune, and it should also correctly account for affected and immune creature classes.\n" + "Multiple syndromes per stone, or multiple boiling rocks produced with the same reaction should work fine.\n" + )); + + + Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome"); + EventManager::EventHandler handle(processJob, 5); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown(color_ostream& out) { + return CR_OK; +} + +/*DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event e) { + return CR_OK; +}*/ + +command_result autoSyndrome(color_ostream& out, vector& parameters) { + if ( parameters.size() > 1 ) + return CR_WRONG_USAGE; + + bool wasEnabled = enabled; + if ( parameters.size() == 1 ) { + if ( parameters[0] == "enable" ) { + enabled = true; + } else if ( parameters[0] == "disable" ) { + enabled = false; + } else { + int32_t a = atoi(parameters[0].c_str()); + if ( a < 0 || a > 1 ) + return CR_WRONG_USAGE; + + enabled = (bool)a; + } + } + + out.print("autoSyndrome is %s\n", enabled ? "enabled" : "disabled"); + if ( enabled == wasEnabled ) + return CR_OK; + + Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome"); + if ( enabled ) { + EventManager::EventHandler handle(processJob, 5); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me); + } else { + EventManager::unregisterAll(me); + } + return CR_OK; +} + +bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df::unit* unit) { + df::creature_raw* creature = df::global::world->raws.creatures.all[unit->race]; + df::caste_raw* caste = creature->caste[unit->caste]; + std::string& creature_name = creature->creature_id; + std::string& creature_caste = caste->caste_id; + //check that the syndrome applies to that guy + /* + * If there is no affected class or affected creature, then anybody who isn't immune is fair game. + * + * Otherwise, it works like this: + * add all the affected class creatures + * remove all the immune class creatures + * add all the affected creatures + * remove all the immune creatures + * you're affected if and only if you're in the remaining list after all of that + **/ + bool applies = syndrome->syn_affected_class.size() == 0 && syndrome->syn_affected_creature.size() == 0; + for ( size_t c = 0; c < syndrome->syn_affected_class.size(); c++ ) { + if ( applies ) + break; + for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { + if ( *syndrome->syn_affected_class[c] == *caste->creature_class[d] ) { + applies = true; + break; + } + } + } + for ( size_t c = 0; c < syndrome->syn_immune_class.size(); c++ ) { + if ( !applies ) + break; + for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { + if ( *syndrome->syn_immune_class[c] == *caste->creature_class[d] ) { + applies = false; + break; + } + } + } + + if ( syndrome->syn_affected_creature.size() != syndrome->syn_affected_caste.size() ) { + out.print("%s, line %d: different affected creature/caste sizes.\n", __FILE__, __LINE__); + return false; + } + for ( size_t c = 0; c < syndrome->syn_affected_creature.size(); c++ ) { + if ( creature_name != *syndrome->syn_affected_creature[c] ) + continue; + if ( *syndrome->syn_affected_caste[c] == "ALL" || + *syndrome->syn_affected_caste[c] == creature_caste ) { + applies = true; + break; + } + } + for ( size_t c = 0; c < syndrome->syn_immune_creature.size(); c++ ) { + if ( creature_name != *syndrome->syn_immune_creature[c] ) + continue; + if ( *syndrome->syn_immune_caste[c] == "ALL" || + *syndrome->syn_immune_caste[c] == creature_caste ) { + applies = false; + break; + } + } + if ( !applies ) { + return false; + } + if ( giveSyndrome(out, workerId, syndrome) < 0 ) + return false; + return true; +} + +void processJob(color_ostream& out, void* jobPtr) { + df::job* job = (df::job*)jobPtr; + if ( job == NULL ) { + out.print("Error %s line %d: null job.\n", __FILE__, __LINE__); + return; + } + if ( job->completion_timer > 0 ) + return; + + if ( job->job_type != df::job_type::CustomReaction ) + return; + + df::reaction* reaction = NULL; + for ( size_t a = 0; a < df::global::world->raws.reactions.size(); a++ ) { + df::reaction* candidate = df::global::world->raws.reactions[a]; + if ( candidate->code != job->reaction_name ) + continue; + reaction = candidate; + break; + } + if ( reaction == NULL ) { + out.print("%s, line %d: could not find reaction \"%s\".\n", __FILE__, __LINE__, job->reaction_name.c_str() ); + return; + } + + int32_t workerId = -1; + for ( size_t a = 0; a < job->general_refs.size(); a++ ) { + if ( job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) + continue; + if ( workerId != -1 ) { + out.print("%s, line %d: Found two workers on the same job.\n", __FILE__, __LINE__); + } + workerId = ((df::general_ref_unit_workerst*)job->general_refs[a])->unit_id; + if (workerId == -1) { + out.print("%s, line %d: invalid worker.\n", __FILE__, __LINE__); + continue; + } + } + + int32_t workerIndex = df::unit::binsearch_index(df::global::world->units.all, workerId); + if ( workerIndex < 0 ) { + out.print("%s line %d: Couldn't find unit %d.\n", __FILE__, __LINE__, workerId); + return; + } + df::unit* worker = df::global::world->units.all[workerIndex]; + //find the building that made it + int32_t buildingId = -1; + for ( size_t a = 0; a < job->general_refs.size(); a++ ) { + if ( job->general_refs[a]->getType() != df::enums::general_ref_type::BUILDING_HOLDER ) + continue; + if ( buildingId != -1 ) { + out.print("%s, line %d: Found two buildings for the same job.\n", __FILE__, __LINE__); + } + buildingId = ((df::general_ref_building_holderst*)job->general_refs[a])->building_id; + if (buildingId == -1) { + out.print("%s, line %d: invalid building.\n", __FILE__, __LINE__); + continue; + } + } + df::building* building; + { + int32_t index = df::building::binsearch_index(df::global::world->buildings.all, buildingId); + if ( index == -1 ) { + out.print("%s, line %d: error: couldn't find building %d.\n", __FILE__, __LINE__, buildingId); + return; + } + building = df::global::world->buildings.all[index]; + } + + //find all of the products it makes. Look for a stone with a low boiling point. + bool appliedSomething = false; + for ( size_t a = 0; a < reaction->products.size(); a++ ) { + df::reaction_product_type type = reaction->products[a]->getType(); + //out.print("type = %d\n", (int32_t)type); + if ( type != df::enums::reaction_product_type::item ) + continue; + df::reaction_product_itemst* bob = (df::reaction_product_itemst*)reaction->products[a]; + //out.print("item_type = %d\n", (int32_t)bob->item_type); + if ( bob->item_type != df::enums::item_type::BOULDER ) + continue; + //for now don't worry about subtype + + //must be a boiling rock syndrome + df::inorganic_raw* inorganic = df::global::world->raws.inorganics[bob->mat_index]; + if ( inorganic->material.heat.boiling_point > 9000 ) { + continue; + } + + for ( size_t b = 0; b < inorganic->material.syndrome.size(); b++ ) { + //add each syndrome to the guy who did the job + df::syndrome* syndrome = inorganic->material.syndrome[b]; + bool workerOnly = false; + bool allowMultipleTargets = false; + bool foundCommand = false; + bool destroyRock = true; + string commandStr; + vector args; + for ( size_t c = 0; c < syndrome->syn_class.size(); c++ ) { + std::string* clazz = syndrome->syn_class[c]; + if ( foundCommand ) { + if ( commandStr == "" ) { + if ( *clazz == "\\WORKER_ONLY" ) { + workerOnly = true; + } else if ( *clazz == "\\ALLOW_MULTIPLE_TARGETS" ) { + allowMultipleTargets = true; + } else if ( *clazz == "\\PRESERVE_ROCK" ) { + destroyRock = false; + } + else { + commandStr = *clazz; + } + } else { + stringstream bob; + if ( *clazz == "\\LOCATION" ) { + bob << job->pos.x; + args.push_back(bob.str()); + bob.str(""); + bob.clear(); + + bob << job->pos.y; + args.push_back(bob.str()); + bob.str(""); + bob.clear(); + + bob << job->pos.z; + args.push_back(bob.str()); + bob.str(""); + bob.clear(); + } else if ( *clazz == "\\WORKER_ID" ) { + bob << workerId; + args.push_back(bob.str()); + } else if ( *clazz == "\\REACTION_INDEX" ) { + bob << reaction->index; + args.push_back(bob.str()); + } else { + args.push_back(*clazz); + } + } + } else if ( *clazz == "\\COMMAND" ) { + foundCommand = true; + } + } + if ( commandStr != "" ) { + Core::getInstance().runCommand(out, commandStr, args); + } + + if ( destroyRock ) { + //find the rock and kill it before it can boil and cause problems and ugliness + for ( size_t c = 0; c < df::global::world->items.all.size(); c++ ) { + df::item* item = df::global::world->items.all[c]; + if ( item->pos.z != building->z ) + continue; + if ( item->pos.x < building->x1 || item->pos.x > building->x2 ) + continue; + if ( item->pos.y < building->y1 || item->pos.y > building->y2 ) + continue; + if ( item->getType() != df::enums::item_type::BOULDER ) + continue; + //make sure it's the right type of boulder + df::item_boulderst* boulder = (df::item_boulderst*)item; + if ( boulder->mat_index != bob->mat_index ) + continue; + + boulder->flags.bits.garbage_collect = true; + boulder->flags.bits.forbid = true; + boulder->flags.bits.hidden = true; + } + } + + //only one syndrome per reaction will be applied, unless multiples are allowed. + if ( appliedSomething && !allowMultipleTargets ) + continue; + + if ( maybeApply(out, syndrome, workerId, worker) ) { + appliedSomething = true; + } + + if ( workerOnly ) + continue; + + //now try applying it to everybody inside the building + for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) { + df::unit* unit = df::global::world->units.active[a]; + if ( unit == worker ) + continue; + if ( unit->pos.z != building->z ) + continue; + if ( unit->pos.x < building->x1 || unit->pos.x > building->x2 ) + continue; + if ( unit->pos.y < building->y1 || unit->pos.y > building->y2 ) + continue; + if ( maybeApply(out, syndrome, unit->id, unit) ) { + appliedSomething = true; + if ( !allowMultipleTargets ) + break; + } + } + } + } + + return; +} + +/* + * Heavily based on https://gist.github.com/4061959/ + **/ +int32_t giveSyndrome(color_ostream& out, int32_t workerId, df::syndrome* syndrome) { + int32_t index = df::unit::binsearch_index(df::global::world->units.all, workerId); + if ( index < 0 ) { + out.print("%s line %d: Couldn't find unit %d.\n", __FILE__, __LINE__, workerId); + return -1; + } + df::unit* unit = df::global::world->units.all[index]; + + df::unit_syndrome* unitSyndrome = new df::unit_syndrome(); + unitSyndrome->type = syndrome->id; + unitSyndrome->year = 0; + unitSyndrome->year_time = 0; + unitSyndrome->ticks = 1; + unitSyndrome->unk1 = 1; + unitSyndrome->flags = 0; //typecast + + for ( size_t a = 0; a < syndrome->ce.size(); a++ ) { + df::unit_syndrome::T_symptoms* symptom = new df::unit_syndrome::T_symptoms(); + symptom->unk1 = 0; + symptom->unk2 = 0; + symptom->ticks = 1; + symptom->flags = 2; //TODO: ??? + unitSyndrome->symptoms.push_back(symptom); + } + unit->syndromes.active.push_back(unitSyndrome); + return 0; +} + diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index c2d67c628..8a1edb9cc 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -19,6 +19,7 @@ DFHACK_PLUGIN(rprobe rprobe.cpp) DFHACK_PLUGIN(nestboxes nestboxes.cpp) DFHACK_PLUGIN(vshook vshook.cpp) DFHACK_PLUGIN(eventExample eventExample.cpp) +DFHACK_PLUGIN(printArgs printArgs.cpp) IF(UNIX) DFHACK_PLUGIN(ref-index ref-index.cpp) ENDIF() diff --git a/plugins/devel/printArgs.cpp b/plugins/devel/printArgs.cpp new file mode 100644 index 000000000..051c7b1dc --- /dev/null +++ b/plugins/devel/printArgs.cpp @@ -0,0 +1,32 @@ + +#include "Console.h" +#include "Core.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" + +#include + +using namespace DFHack; +using namespace std; + +command_result printArgs (color_ostream &out, std::vector & parameters); + +DFHACK_PLUGIN("printArgs"); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "printArgs", "Print the arguments given.", + printArgs, false + )); + return CR_OK; +} + +command_result printArgs (color_ostream &out, std::vector & parameters) +{ + for ( size_t a = 0; a < parameters.size(); a++ ) { + out << "Argument " << (a+1) << ": \"" << parameters[a] << "\"" << endl; + } + return CR_OK; +} diff --git a/plugins/trueTransformation.cpp b/plugins/trueTransformation.cpp new file mode 100644 index 000000000..6c4245da8 --- /dev/null +++ b/plugins/trueTransformation.cpp @@ -0,0 +1,88 @@ + +#include "Core.h" +#include "Console.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" + +#include "modules/EventManager.h" + +#include "df/caste_raw.h" +#include "df/creature_raw.h" +#include "df/syndrome.h" +#include "df/unit.h" +#include "df/unit_syndrome.h" +#include "df/world.h" + +#include + +using namespace DFHack; +using namespace std; + +DFHACK_PLUGIN("trueTransformation"); + +void syndromeHandler(color_ostream& out, void* ptr); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + EventManager::EventHandler syndrome(syndromeHandler, 1); + Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("trueTransformation"); + EventManager::registerListener(EventManager::EventType::SYNDROME, syndrome, me); + + return CR_OK; +} + +void syndromeHandler(color_ostream& out, void* ptr) { + EventManager::SyndromeData* data = (EventManager::SyndromeData*)ptr; + //out.print("Syndrome started: unit %d, syndrome %d.\n", data->unitId, data->syndromeIndex); + + int32_t index = df::unit::binsearch_index(df::global::world->units.active, data->unitId); + if ( index < 0 ) { + out.print("%s, line %d: couldn't find unit.\n", __FILE__, __LINE__); + return; + } + df::unit* unit = df::global::world->units.active[index]; + df::unit_syndrome* unit_syndrome = unit->syndromes.active[data->syndromeIndex]; + df::syndrome* syndrome = df::global::world->raws.syndromes.all[unit_syndrome->type]; + + bool foundIt = false; + int32_t raceId = -1; + df::creature_raw* creatureRaw = NULL; + int32_t casteId = -1; + for ( size_t a = 0; a < syndrome->syn_class.size(); a++ ) { + if ( *syndrome->syn_class[a] == "\\PERMANENT" ) { + foundIt = true; + } + if ( foundIt && raceId == -1 ) { + //find the race with the name + string& name = *syndrome->syn_class[a]; + for ( size_t b = 0; b < df::global::world->raws.creatures.all.size(); b++ ) { + df::creature_raw* creature = df::global::world->raws.creatures.all[b]; + if ( creature->creature_id != name ) + continue; + raceId = b; + creatureRaw = creature; + break; + } + continue; + } + if ( foundIt && raceId != -1 ) { + string& name = *syndrome->syn_class[a]; + for ( size_t b = 0; b < creatureRaw->caste.size(); b++ ) { + df::caste_raw* caste = creatureRaw->caste[b]; + if ( caste->caste_id != name ) + continue; + casteId = b; + break; + } + break; + } + } + if ( !foundIt || raceId == -1 || casteId == -1 ) + return; + + unit->enemy.normal_race = raceId; + unit->enemy.normal_caste = casteId; + //that's it! +} + From 4920293c2db40454c101bdfcb524aaad6816d37d Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 16:30:48 -0500 Subject: [PATCH 50/56] Infinite sky: get it to compile. --- plugins/skyEternal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/skyEternal.cpp b/plugins/skyEternal.cpp index 04a176a8d..3ab353b4f 100644 --- a/plugins/skyEternal.cpp +++ b/plugins/skyEternal.cpp @@ -80,7 +80,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) return CR_OK; { t_gamemodes mode; - if ( !Core::getInstance().getWorld()->ReadGameMode(mode) ) + if ( !World::ReadGameMode(mode) ) return CR_FAILURE; if ( mode.g_mode != df::enums::game_mode::DWARF ) return CR_OK; From 151ff0f29659db1b9d39e480a98e6bf674db77ef Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 16:34:33 -0500 Subject: [PATCH 51/56] Infinite sky: rename from sky eternal. --- plugins/CMakeLists.txt | 2 +- plugins/{skyEternal.cpp => infiniteSky.cpp} | 22 ++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) rename plugins/{skyEternal.cpp => infiniteSky.cpp} (90%) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6b8986dc3..22be1c555 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -135,7 +135,7 @@ if (BUILD_SUPPORTED) #DFHACK_PLUGIN(dfstream dfstream.cpp LINK_LIBRARIES clsocket dfhack-tinythread) DFHACK_PLUGIN(autoSyndrome autoSyndrome.cpp) DFHACK_PLUGIN(trueTransformation trueTransformation.cpp) - DFHACK_PLUGIN(skyEternal skyEternal.cpp) + DFHACK_PLUGIN(infiniteSky infiniteSky.cpp) endif() diff --git a/plugins/skyEternal.cpp b/plugins/infiniteSky.cpp similarity index 90% rename from plugins/skyEternal.cpp rename to plugins/infiniteSky.cpp index 3ab353b4f..f8690b5d0 100644 --- a/plugins/skyEternal.cpp +++ b/plugins/infiniteSky.cpp @@ -22,24 +22,24 @@ using namespace std; using namespace DFHack; using namespace df::enums; -command_result skyEternal (color_ostream &out, std::vector & parameters); +command_result infiniteSky (color_ostream &out, std::vector & parameters); -DFHACK_PLUGIN("skyEternal"); +DFHACK_PLUGIN("infiniteSky"); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand( - "skyEternal", + "infiniteSky", "Creates new sky levels on request, or as you construct up.", - skyEternal, false, + infiniteSky, false, "Usage:\n" - " skyEternal\n" + " infiniteSky\n" " creates one more z-level\n" - " skyEternal [n]\n" + " infiniteSky [n]\n" " creates n more z-level(s)\n" - " skyEternal enable\n" + " infiniteSky enable\n" " enables monitoring of constructions\n" - " skyEternal disable\n" + " infiniteSky disable\n" " disable monitoring of constructions\n" "\n" "If construction monitoring is enabled, then the plugin will automatically create new sky z-levels as you construct upward.\n" @@ -94,7 +94,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) df::construction* construct = df::global::world->constructions[a]; if ( construct->pos.z+2 < zNow ) continue; - skyEternal(out, vec); + infiniteSky(out, vec); zNow = df::global::world->map.z_count_block; ///break; } @@ -103,14 +103,14 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) return CR_OK; } -command_result skyEternal (color_ostream &out, std::vector & parameters) +command_result infiniteSky (color_ostream &out, std::vector & parameters) { if ( parameters.size() > 1 ) return CR_WRONG_USAGE; if ( parameters.size() == 0 ) { vector vec; vec.push_back("1"); - return skyEternal(out, vec); + return infiniteSky(out, vec); } if (parameters[0] == "enable") { enabled = true; From 4d57a053fc9ed9772e3ee20d541e571e7ca3329e Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 16:51:29 -0500 Subject: [PATCH 52/56] Infinite sky: added helpful print statements. --- plugins/infiniteSky.cpp | 54 ++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/plugins/infiniteSky.cpp b/plugins/infiniteSky.cpp index f8690b5d0..b86ea867a 100644 --- a/plugins/infiniteSky.cpp +++ b/plugins/infiniteSky.cpp @@ -71,6 +71,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan static size_t constructionSize = 0; static bool enabled = false; +void doInfiniteSky(color_ostream& out, int32_t howMany); DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { @@ -89,12 +90,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if ( df::global::world->constructions.size() == constructionSize ) return CR_OK; int32_t zNow = df::global::world->map.z_count_block; - vector vec; for ( size_t a = constructionSize; a < df::global::world->constructions.size(); a++ ) { df::construction* construct = df::global::world->constructions[a]; if ( construct->pos.z+2 < zNow ) continue; - infiniteSky(out, vec); + doInfiniteSky(out, 1); zNow = df::global::world->map.z_count_block; ///break; } @@ -103,26 +103,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) return CR_OK; } -command_result infiniteSky (color_ostream &out, std::vector & parameters) -{ - if ( parameters.size() > 1 ) - return CR_WRONG_USAGE; - if ( parameters.size() == 0 ) { - vector vec; - vec.push_back("1"); - return infiniteSky(out, vec); - } - if (parameters[0] == "enable") { - enabled = true; - return CR_OK; - } - if (parameters[0] == "disable") { - enabled = false; - constructionSize = 0; - return CR_OK; - } - int32_t howMany = 0; - howMany = atoi(parameters[0].c_str()); +void doInfiniteSky(color_ostream& out, int32_t howMany) { df::world* world = df::global::world; CoreSuspender suspend; int32_t x_count_block = world->map.x_count_block; @@ -138,6 +119,9 @@ command_result infiniteSky (color_ostream &out, std::vector & para column[z_count_block] = NULL; delete[] block_index[a][b]; block_index[a][b] = column; + + //deal with map_block_column stuff even though it'd probably be fine + } } df::z_level_flags* flags = new df::z_level_flags[z_count_block+1]; @@ -148,5 +132,31 @@ command_result infiniteSky (color_ostream &out, std::vector & para world->map.z_count++; } +} + +command_result infiniteSky (color_ostream &out, std::vector & parameters) +{ + if ( parameters.size() > 1 ) + return CR_WRONG_USAGE; + if ( parameters.size() == 0 ) { + vector vec; + vec.push_back("1"); + return infiniteSky(out, vec); + } + if (parameters[0] == "enable") { + enabled = true; + out.print("Construction monitoring enabled.\n"); + return CR_OK; + } + if (parameters[0] == "disable") { + enabled = false; + out.print("Construction monitoring disabled.\n"); + constructionSize = 0; + return CR_OK; + } + int32_t howMany = 0; + howMany = atoi(parameters[0].c_str()); + out.print("InfiniteSky: creating %d new z-levels of sky.\n", howMany); + doInfiniteSky(out, howMany); return CR_OK; } From 47b20ea301c267d69e4271691b21593acbd3193e Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 17:32:23 -0500 Subject: [PATCH 53/56] Infinite sky: added glyphs and made it print status when no arguments given. --- plugins/infiniteSky.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/plugins/infiniteSky.cpp b/plugins/infiniteSky.cpp index b86ea867a..1c99df6d0 100644 --- a/plugins/infiniteSky.cpp +++ b/plugins/infiniteSky.cpp @@ -10,6 +10,7 @@ #include "df/construction.h" #include "df/game_mode.h" #include "df/map_block.h" +#include "df/map_block_column.h" #include "df/world.h" #include "df/z_level_flags.h" @@ -114,14 +115,32 @@ void doInfiniteSky(color_ostream& out, int32_t howMany) { df::map_block**** block_index = world->map.block_index; for ( int32_t a = 0; a < x_count_block; a++ ) { for ( int32_t b = 0; b < y_count_block; b++ ) { - df::map_block** column = new df::map_block*[z_count_block+1]; - memcpy(column, block_index[a][b], z_count_block*sizeof(df::map_block*)); - column[z_count_block] = NULL; + df::map_block** blockColumn = new df::map_block*[z_count_block+1]; + memcpy(blockColumn, block_index[a][b], z_count_block*sizeof(df::map_block*)); + blockColumn[z_count_block] = NULL; delete[] block_index[a][b]; - block_index[a][b] = column; + block_index[a][b] = blockColumn; //deal with map_block_column stuff even though it'd probably be fine - + df::map_block_column* column = world->map.column_index[a][b]; + if ( !column ) { + out.print("%s, line %d: column is null (%d, %d).\n", __FILE__, __LINE__, a, b); + continue; + } + df::map_block_column::T_unmined_glyphs* glyphs = new df::map_block_column::T_unmined_glyphs; + glyphs->x[0] = 0; + glyphs->x[1] = 1; + glyphs->x[2] = 2; + glyphs->x[3] = 3; + glyphs->y[0] = 0; + glyphs->y[1] = 0; + glyphs->y[2] = 0; + glyphs->y[3] = 0; + glyphs->tile[0] = 'e'; + glyphs->tile[1] = 'x'; + glyphs->tile[2] = 'p'; + glyphs->tile[3] = '^'; + column->unmined_glyphs.push_back(glyphs); } } df::z_level_flags* flags = new df::z_level_flags[z_count_block+1]; @@ -139,9 +158,8 @@ command_result infiniteSky (color_ostream &out, std::vector & para if ( parameters.size() > 1 ) return CR_WRONG_USAGE; if ( parameters.size() == 0 ) { - vector vec; - vec.push_back("1"); - return infiniteSky(out, vec); + out.print("Construction monitoring is %s.\n", enabled ? "enabled" : "disabled"); + return CR_OK; } if (parameters[0] == "enable") { enabled = true; @@ -156,7 +174,7 @@ command_result infiniteSky (color_ostream &out, std::vector & para } int32_t howMany = 0; howMany = atoi(parameters[0].c_str()); - out.print("InfiniteSky: creating %d new z-levels of sky.\n", howMany); + out.print("InfiniteSky: creating %d new z-level%s of sky.\n", howMany, howMany == 1 ? "" : "s" ); doInfiniteSky(out, howMany); return CR_OK; } From f8abd5c595e5fa25b210258279478ef06830b9c6 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 5 Jan 2013 18:09:50 -0500 Subject: [PATCH 54/56] StepBetween: renamed stuff back to the way it was. Thought I had already done that. --- library/LuaApi.cpp | 2 +- library/include/modules/Maps.h | 2 +- library/modules/Maps.cpp | 2 +- plugins/devel/stepBetween.cpp | 2 +- plugins/fix-armory.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 720ccd21f..0151ed404 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1309,7 +1309,7 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = { WRAPM(Maps, enableBlockUpdates), WRAPM(Maps, getGlobalInitFeature), WRAPM(Maps, getLocalInitFeature), - WRAPM(Maps, canPathBetween), + WRAPM(Maps, canWalkBetween), WRAPM(Maps, spawnFlow), WRAPN(hasTileAssignment, hasTileAssignment), WRAPN(getTileAssignment, getTileAssignment), diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 29d3d69ba..265c89a9f 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -308,7 +308,7 @@ extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block, /// remove a block event from the block by address extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which ); -DFHACK_EXPORT bool canPathBetween(df::coord pos1, df::coord pos2); +DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2); DFHACK_EXPORT bool canStepBetween(df::coord pos1, df::coord pos2); } } diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 6517331ee..5c0608765 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -524,7 +524,7 @@ bool Maps::ReadGeology(vector > *layer_mats, vector return true; } -bool Maps::canPathBetween(df::coord pos1, df::coord pos2) +bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) { auto block1 = getTileBlock(pos1); auto block2 = getTileBlock(pos2); diff --git a/plugins/devel/stepBetween.cpp b/plugins/devel/stepBetween.cpp index c1bc3fa1a..32d5be31b 100644 --- a/plugins/devel/stepBetween.cpp +++ b/plugins/devel/stepBetween.cpp @@ -80,7 +80,7 @@ df::coord prev; command_result stepBetween (color_ostream &out, std::vector & parameters) { df::coord bob = Gui::getCursorPos(); - out.print("(%d,%d,%d), (%d,%d,%d): canWalkBetween = %d, canPathBetween = %d\n", prev.x, prev.y, prev.z, bob.x, bob.y, bob.z, Maps::canStepBetween(prev, bob), Maps::canPathBetween(prev,bob)); + out.print("(%d,%d,%d), (%d,%d,%d): canStepBetween = %d, canWalkBetween = %d\n", prev.x, prev.y, prev.z, bob.x, bob.y, bob.z, Maps::canStepBetween(prev, bob), Maps::canWalkBetween(prev,bob)); prev = bob; return CR_OK; diff --git a/plugins/fix-armory.cpp b/plugins/fix-armory.cpp index e54639960..b937d40e8 100644 --- a/plugins/fix-armory.cpp +++ b/plugins/fix-armory.cpp @@ -475,7 +475,7 @@ static bool try_store_item(df::building *target, df::item *item) df::coord tpos(target->centerx, target->centery, target->z); df::coord ipos = Items::getPosition(item); - if (!Maps::canPathBetween(tpos, ipos)) + if (!Maps::canWalkBetween(tpos, ipos)) return false; // Check if the target has enough space left From 6fd9ce339d657ce00e45dc0414f703e95d609b0c Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 7 Jan 2013 12:09:39 -0500 Subject: [PATCH 55/56] Autosyndrome: fixed the rules on when syndromes apply. --- plugins/autoSyndrome.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 884c7749b..79e8b35ec 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -184,8 +184,8 @@ bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df * * Otherwise, it works like this: * add all the affected class creatures - * remove all the immune class creatures * add all the affected creatures + * remove all the immune class creatures * remove all the immune creatures * you're affected if and only if you're in the remaining list after all of that **/ @@ -206,7 +206,7 @@ bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df for ( size_t d = 0; d < caste->creature_class.size(); d++ ) { if ( *syndrome->syn_immune_class[c] == *caste->creature_class[d] ) { applies = false; - break; + return false; } } } @@ -230,7 +230,7 @@ bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df if ( *syndrome->syn_immune_caste[c] == "ALL" || *syndrome->syn_immune_caste[c] == creature_caste ) { applies = false; - break; + return false; } } if ( !applies ) { From 0b6858c7ffdc5dd36c991d7e2fec4c528d7d0753 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 7 Jan 2013 12:36:11 -0500 Subject: [PATCH 56/56] Update news. --- NEWS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS b/NEWS index ed6290261..5aec19361 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,8 @@ DFHack future Internals: - support for displaying active keybindings properly. - support for reusable widgets in lua screen library. + - Maps::canStepBetween: returns whether you can walk between two tiles in one step. + - EventManager: monitors various in game events centrally so that individual plugins don't have to monitor the same things redundantly. Notable bugfixes: - autobutcher can be re-enabled again after being stopped. - stopped Dwarf Manipulator from unmasking vampires. @@ -59,6 +61,11 @@ DFHack future saving you from having to trawl through long lists of materials each time you place one. Dfusion plugin: Reworked to make use of lua modules, now all the scripts can be used from other scripts. + Auto syndrome plugin: a way of automatically applying boiling rock syndromes and calling dfhack commands controlled by raws. + Infinite sky plugin: create new z-levels automatically or on request. + True transformation plugin: a better way of doing permanent transformations that allows later transformations. + Work now plugin: makes the game assign jobs every time you pause. + DFHack v0.34.11-r2