diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index f1c54ac1d..f95a75e8d 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -9,8 +9,10 @@ #include "Console.h" #include "DataDefs.h" -#include -#include +#include "df/coord.h" +#include "df/unit.h" +#include "df/unit_inventory_item.h" +#include "df/unit_wound.h" namespace DFHack { namespace EventManager { @@ -27,6 +29,7 @@ namespace DFHack { INVASION, INVENTORY_CHANGE, REPORT, + UNIT_ATTACK, EVENT_MAX }; } @@ -70,6 +73,12 @@ namespace DFHack { InventoryChangeData(int32_t id_in, InventoryItem* old_in, InventoryItem* new_in): unitId(id_in), item_old(old_in), item_new(new_in) {} }; + struct UnitAttackData { + int32_t attacker; + int32_t defender; + int32_t wound; + }; + DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin); DFHACK_EXPORT int32_t registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false); DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index c1b49b637..fa25122c2 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1,5 +1,6 @@ #include "Core.h" #include "Console.h" +#include "VTableInterpose.h" #include "modules/Buildings.h" #include "modules/Constructions.h" #include "modules/EventManager.h" @@ -15,6 +16,10 @@ #include "df/general_ref_unit_workerst.h" #include "df/global_objects.h" #include "df/item.h" +#include "df/item_actual.h" +#include "df/item_constructed.h" +#include "df/item_crafted.h" +#include "df/item_weaponst.h" #include "df/job.h" #include "df/job_list_link.h" #include "df/report.h" @@ -22,7 +27,9 @@ #include "df/unit.h" #include "df/unit_flags1.h" #include "df/unit_inventory_item.h" +#include "df/unit_report_type.h" #include "df/unit_syndrome.h" +#include "df/unit_wound.h" #include "df/world.h" #include @@ -119,6 +126,7 @@ static void manageSyndromeEvent(color_ostream& out); static void manageInvasionEvent(color_ostream& out); static void manageEquipmentEvent(color_ostream& out); static void manageReportEvent(color_ostream& out); +static void manageUnitAttackEvent(color_ostream& out); typedef void (*eventManager_t)(color_ostream&); @@ -133,7 +141,8 @@ static const eventManager_t eventManager[] = { manageSyndromeEvent, manageInvasionEvent, manageEquipmentEvent, - manageReportEvent + manageReportEvent, + manageUnitAttackEvent, }; //job initiated @@ -169,6 +178,9 @@ static unordered_map > equipmentLog; //report static int32_t lastReport; +//unit attack +static int32_t lastReportUnitAttack; + void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { static bool doOnce = false; // const string eventNames[] = {"world loaded", "world unloaded", "map loaded", "map unloaded", "viewscreen changed", "core initialized", "begin unload", "paused", "unpaused"}; @@ -194,6 +206,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event Buildings::clearBuildings(out); lastReport = -1; + lastReportUnitAttack = -1; gameLoaded = false; } else if ( event == DFHack::SC_MAP_LOADED ) { /* @@ -244,6 +257,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } } lastReport = -1; + lastReportUnitAttack = -1; for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) { eventLastTick[a] = -1;//-1000000; } @@ -716,8 +730,6 @@ static void manageReportEvent(color_ostream& out) { } for ( ; a < reports.size(); a++ ) { df::report* report = reports[a]; - df::announcement_type type = report->type; - //starts with COMBAT_ or UNIT_PROJECTILE_ for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)report->id); @@ -726,3 +738,134 @@ static void manageReportEvent(color_ostream& out) { } } +/* +static void onAttackEventHelper(df::unit* attacker, df::unit* defender, df::unit_wound* wound) { + multimap copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); + UnitAttackData data; + data.defender = unit->id; + data.attacker = wound->unit_id; + data.wound = wound->id; + + for ( auto a = copy.begin(); a != copy.end(); a++ ) { + EventHandler handle = (*a).second; + handle.eventHandler(Core::getInstance().getConsole(), (void*)&data); + } +} +*/ + +static df::unit_wound* getWound(df::unit* attacker, df::unit* defender) { + for ( size_t a = 0; a < defender->body.wounds.size(); a++ ) { + df::unit_wound* wound = defender->body.wounds[a]; + if ( wound->age <= 1 && wound->unit_id == attacker->id ) { + return wound; + } + } + return NULL; +} + +static void manageUnitAttackEvent(color_ostream& out) { + multimap copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); + std::vector& reports = df::global::world->status.reports; + if (reports.size() == 0) { + return; + } + size_t a = df::report::binsearch_index(reports, lastReportUnitAttack, false); + //this may or may not be needed: I don't know if binsearch_index goes earlier or later if it can't hit the target exactly + while (a < reports.size() && reports[a]->id <= lastReportUnitAttack) { + a++; + } + std::set strikeReports; + for ( ; a < reports.size(); a++ ) { + df::report* report = reports[a]; + lastReportUnitAttack = report->id; + if ( report->flags.bits.continuation ) + continue; + df::announcement_type type = report->type; + if ( type == df::announcement_type::COMBAT_STRIKE_DETAILS ) { + strikeReports.insert(report->id); + } + } + + if ( strikeReports.empty() ) + return; + + //report id -> relevant units + std::map> reportToRelevantUnits; + for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { + df::unit* unit = df::global::world->units.all[a]; + for ( int16_t b = df::enum_traits::first_item_value; b <= df::enum_traits::last_item_value; b++ ) { + if ( b == df::unit_report_type::Sparring ) + continue; + for ( size_t c = 0; c < unit->reports.log[b].size(); c++ ) { + reportToRelevantUnits[unit->reports.log[b][c]].push_back(unit->id); + } + } + } + + map > alreadyDone; + for ( auto a = strikeReports.begin(); a != strikeReports.end(); a++ ) { + int32_t reportId = *a; + df::report* report = df::report::find(reportId); + if ( !report ) + continue; //TODO: error + std::string reportStr = report->text; + for ( int32_t b = reportId+1; ; b++ ) { + df::report* report2 = df::report::find(b); + if ( !report2 ) + break; + if ( report2->type != df::announcement_type::COMBAT_STRIKE_DETAILS ) + break; + if ( !report2->flags.bits.continuation ) + break; + reportStr = reportStr + report2->text; + } + + std::vector& relevantUnits = reportToRelevantUnits[report->id]; + if ( relevantUnits.size() != 2 ) { + continue; + } + + df::unit* unit1 = df::unit::find(relevantUnits[0]); + df::unit* unit2 = df::unit::find(relevantUnits[1]); + + df::unit_wound* wound1 = getWound(unit1,unit2); + df::unit_wound* wound2 = getWound(unit2,unit1); + + if ( wound1 && !alreadyDone[unit1->id][unit2->id] ) { + UnitAttackData data; + data.attacker = unit1->id; + data.defender = unit2->id; + data.wound = wound1->id; + + alreadyDone[data.attacker][data.defender] = 1; + for ( auto b = copy.begin(); b != copy.end(); b++ ) { + EventHandler handle = (*b).second; + handle.eventHandler(out, (void*)&data); + } + } + + if ( wound2 && !alreadyDone[unit1->id][unit2->id] ) { + UnitAttackData data; + data.attacker = unit2->id; + data.defender = unit1->id; + data.wound = wound2->id; + + alreadyDone[data.attacker][data.defender] = 1; + for ( auto b = copy.begin(); b != copy.end(); b++ ) { + EventHandler handle = (*b).second; + handle.eventHandler(out, (void*)&data); + } + } + + if ( !wound1 && !wound2 ) { + if ( unit1->flags1.bits.dead || unit2->flags1.bits.dead ) + continue; + if ( reportStr.find("severed part") ) + continue; + if ( Once::doOnce("EventManager neither wound") ) { + out.print("%s, %d: neither wound: %s\n", __FILE__, __LINE__, reportStr.c_str()); + } + } + } +} + diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index 80570fc27..5a827f18e 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -3,16 +3,24 @@ #include "Core.h" #include "Export.h" #include "PluginManager.h" -#include "modules/EventManager.h" #include "DataDefs.h" +#include "VTableInterpose.h" + +#include "modules/EventManager.h" +#include "df/body_part_raw.h" +#include "df/caste_body_info.h" #include "df/construction.h" #include "df/coord.h" #include "df/item.h" +#include "df/item_actual.h" #include "df/job.h" +#include "df/unit.h" +#include "df/unit_wound.h" #include "df/world.h" #include +#include using namespace DFHack; using namespace std; @@ -28,6 +36,9 @@ 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); +void unitAttack(color_ostream& out, void* ptr); + +//bool interposed = false; command_result eventExample(color_ostream& out, vector& parameters); @@ -36,7 +47,30 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vectorid, unit->id, wound->unit_id, (int32_t)unk1, (int32_t)unk2); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(my_contaminate, contaminateWound); +*/ + command_result eventExample(color_ostream& out, vector& parameters) { +/* + if ( !interposed ) { + interposed = true; + if ( !INTERPOSE_HOOK(my_contaminate, contaminateWound).apply() ) { + out.print("Error: could not interpose contaminateWound."); + } + } +*/ + EventManager::EventHandler initiateHandler(jobInitiated, 1); EventManager::EventHandler completeHandler(jobCompleted, 0); EventManager::EventHandler timeHandler(timePassed, 1); @@ -46,8 +80,9 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::EventHandler constructionHandler(construction, 100); EventManager::EventHandler syndromeHandler(syndrome, 1); EventManager::EventHandler invasionHandler(invasion, 1000); + EventManager::EventHandler unitAttackHandler(unitAttack, 1); EventManager::unregisterAll(plugin_self); - + EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, plugin_self); EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, plugin_self); EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, plugin_self); @@ -56,6 +91,7 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, plugin_self); EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, plugin_self); EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, plugin_self); + EventManager::registerListener(EventManager::EventType::UNIT_ATTACK, unitAttackHandler, plugin_self); EventManager::registerTick(timeHandler, 1, plugin_self); EventManager::registerTick(timeHandler, 2, plugin_self); EventManager::registerTick(timeHandler, 4, plugin_self); @@ -135,3 +171,20 @@ void invasion(color_ostream& out, void* ptr) { out.print("New invasion! %d\n", (int32_t)ptr); } +void unitAttack(color_ostream& out, void* ptr) { + EventManager::UnitAttackData* data = (EventManager::UnitAttackData*)ptr; + out.print("unit %d attacks unit %d\n", data->attacker, data->defender); + df::unit* defender = df::unit::find(data->defender); + int32_t woundIndex = df::unit_wound::binsearch_index(defender->body.wounds, data->wound); + df::unit_wound* wound = defender->body.wounds[woundIndex]; + set parts; + for ( auto a = wound->parts.begin(); a != wound->parts.end(); a++ ) { + parts.insert((*a)->body_part_id); + } + for ( auto a = parts.begin(); a != parts.end(); a++ ) { + int32_t body_part_id = (*a); + df::body_part_raw* part = defender->body.body_plan->body_parts[body_part_id]; + out.print(" %s\n", part->name_singular[0]->c_str()); + } +} + diff --git a/plugins/eventful.cpp b/plugins/eventful.cpp index b39947f57..5dbed2a5d 100644 --- a/plugins/eventful.cpp +++ b/plugins/eventful.cpp @@ -121,6 +121,7 @@ static void handle_constructions(color_ostream &out,df::construction*){}; static void handle_syndrome(color_ostream &out,int32_t,int32_t){}; static void handle_inventory_change(color_ostream& out,int32_t,int32_t,df::unit_inventory_item*,df::unit_inventory_item*){}; static void handle_report(color_ostream& out,int32_t){}; +static void handle_unitAttack(color_ostream& out,int32_t,int32_t,int32_t){}; DEFINE_LUA_EVENT_1(onBuildingCreatedDestroyed, handle_int32t, int32_t); DEFINE_LUA_EVENT_1(onJobInitiated,handle_job_init,df::job*); DEFINE_LUA_EVENT_1(onJobCompleted,handle_job_complete,df::job*); @@ -131,6 +132,7 @@ DEFINE_LUA_EVENT_2(onSyndrome, handle_syndrome, int32_t,int32_t); DEFINE_LUA_EVENT_1(onInvasion,handle_int32t,int32_t); DEFINE_LUA_EVENT_4(onInventoryChange,handle_inventory_change,int32_t,int32_t,df::unit_inventory_item*,df::unit_inventory_item*); DEFINE_LUA_EVENT_1(onReport,handle_report,int32_t); +DEFINE_LUA_EVENT_3(onUnitAttack,handle_unitAttack,int32_t,int32_t,int32_t) DFHACK_PLUGIN_LUA_EVENTS { DFHACK_LUA_EVENT(onWorkshopFillSidebarMenu), DFHACK_LUA_EVENT(postWorkshopFillSidebarMenu), @@ -151,6 +153,7 @@ DFHACK_PLUGIN_LUA_EVENTS { DFHACK_LUA_EVENT(onInvasion), DFHACK_LUA_EVENT(onInventoryChange), DFHACK_LUA_EVENT(onReport), + DFHACK_LUA_EVENT(onUnitAttack), DFHACK_LUA_END }; @@ -214,6 +217,10 @@ static void ev_mng_inventory(color_ostream& out, void* ptr) static void ev_mng_report(color_ostream& out, void* ptr) { onReport(out,(int32_t)ptr); } +static void ev_mng_unitAttack(color_ostream& out, void* ptr) { + EventManager::UnitAttackData* data = (EventManager::UnitAttackData*)ptr; + onUnitAttack(out,data->attacker,data->defender,data->wound); +} std::vector enabledEventManagerEvents(EventManager::EventType::EVENT_MAX,-1); typedef void (*handler_t) (color_ostream&,void*); static const handler_t eventHandlers[] = { @@ -228,6 +235,7 @@ static const handler_t eventHandlers[] = { ev_mng_invasion, ev_mng_inventory, ev_mng_report, + ev_mng_unitAttack, }; static void enableEvent(int evType,int freq) { diff --git a/plugins/lua/eventful.lua b/plugins/lua/eventful.lua index 0a73be09f..3c3865ce8 100644 --- a/plugins/lua/eventful.lua +++ b/plugins/lua/eventful.lua @@ -133,5 +133,5 @@ local function invertTable(tbl) return ret end eventType=invertTable{[0]="TICK","JOB_INITIATED","JOB_COMPLETED","UNIT_DEATH","ITEM_CREATED", - "BUILDING","CONSTRUCTION","SYNDROME","INVASION","INVENTORY_CHANGE","REPORT","EVENT_MAX"} + "BUILDING","CONSTRUCTION","SYNDROME","INVASION","INVENTORY_CHANGE","REPORT","UNIT_ATTACK","EVENT_MAX"} return _ENV