Added UNIT_ATTACK event to EventManager and exposed it to Lua.

develop
expwnent 2014-06-28 02:31:34 -04:00
parent c635632ae7
commit 8e7e87ac73
5 changed files with 221 additions and 8 deletions

@ -9,8 +9,10 @@
#include "Console.h"
#include "DataDefs.h"
#include <df/coord.h>
#include <df/unit_inventory_item.h>
#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);

@ -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 <map>
@ -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<int32_t, vector<InventoryItem> > 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<Plugin*,EventHandler> 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<Plugin*,EventHandler> copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end());
std::vector<df::report*>& 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<int32_t> 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<int32_t,std::vector<int32_t>> 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<df::unit_report_type>::first_item_value; b <= df::enum_traits<df::unit_report_type>::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<int32_t, map<int32_t, int32_t> > 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<int32_t>& 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());
}
}
}
}

@ -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 <vector>
#include <set>
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<string>& parameters);
@ -36,7 +47,30 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector<PluginC
return CR_OK;
}
/*
//df::item::contaminateWound(df::unit*,df::unit_wound*,uint8_t,int16_t);
struct my_contaminate : df::item_actual {
typedef df::item_actual interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, contaminateWound, (df::unit* unit, df::unit_wound* wound, uint8_t unk1, int16_t unk2)) {
INTERPOSE_NEXT(contaminateWound)(unit,wound,unk1,unk2);
CoreSuspendClaimer suspend;
Core::getInstance().print("contaminateWound: item=%d, unit=%d, wound attacker = %d, unk1 = %d, unk2 = %d\n", this->id, unit->id, wound->unit_id, (int32_t)unk1, (int32_t)unk2);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(my_contaminate, contaminateWound);
*/
command_result eventExample(color_ostream& out, vector<string>& 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<string>& 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<string>& 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<int32_t> 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());
}
}

@ -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<int> 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)
{

@ -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