Merge remote-tracking branch 'origin/scriptOrganization' into scriptOrganization

develop
expwnent 2014-06-28 22:57:47 -04:00
commit 19310c32b3
14 changed files with 438 additions and 412 deletions

11
NEWS

@ -1,11 +1,12 @@
DFHack future DFHack future
Internals: Internals:
LuaApi EventManager: added ON_REPORT and UNIT_ATTACK events and exposed them to Lua.
support queuing dfhack.timeout events before a world is loaded.
New scripts: New scripts:
add-syndrome.lua add-syndrome.lua
add a syndrome to a unit. many configurable options add a syndrome to a unit. many configurable options
attackTrigger.lua
run commands when a unit attacks another with weapons of a given type or weapons of a given material
blooddel.lua blooddel.lua
makes it so civs don't bring barrels full of blood ichor or goo makes it so civs don't bring barrels full of blood ichor or goo
feeding-timers.lua feeding-timers.lua
@ -24,12 +25,6 @@ DFHack future
adds and removes syndromes to units when they equip and unequip items adds and removes syndromes to units when they equip and unequip items
moddableGods.lua moddableGods.lua
makes raw moddable gods possible makes raw moddable gods possible
onReport.lua
callbacks when a report happens
onReportExample.lua
onStrike.lua
callbacks when a unit injures another
onStrikeExample.lua
printArgs.lua printArgs.lua
projectileExpansion.lua projectileExpansion.lua
adds extra functionality to projectiles adds extra functionality to projectiles

@ -208,6 +208,12 @@ log-region
# patch the material objects in memory to fix cloth stockpiles # patch the material objects in memory to fix cloth stockpiles
fix/cloth-stockpile enable fix/cloth-stockpile enable
# civs don't bring blood
#:lua dfhack.onStateChange.onLoadBloodDel = function(state) if state == SC_WORLD_LOADED then dfhack.run_command('repeat -time 1 months -command fix/blooddel') end end
# run growth bug regularly
#:lua dfhack.onStateChange.onLoadGrowthBug = function(state) if state == SC_WORLD_LOADED then dfhack.run_command('repeat -time 1 months -command fix/growthbug') end end
####################################################### #######################################################
# Apply binary patches at runtime # # Apply binary patches at runtime #
####################################################### #######################################################

@ -1694,6 +1694,12 @@ int dfhack_timeout(lua_State *L)
luaL_checktype(L, 3, LUA_TFUNCTION); luaL_checktype(L, 3, LUA_TFUNCTION);
lua_settop(L, 3); lua_settop(L, 3);
if (mode > 0 && !Core::getInstance().isWorldLoaded())
{
lua_pushnil(L);
return 1;
}
// Compute timeout value // Compute timeout value
switch (mode) switch (mode)
{ {

@ -9,8 +9,10 @@
#include "Console.h" #include "Console.h"
#include "DataDefs.h" #include "DataDefs.h"
#include <df/coord.h> #include "df/coord.h"
#include <df/unit_inventory_item.h> #include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/unit_wound.h"
namespace DFHack { namespace DFHack {
namespace EventManager { namespace EventManager {
@ -26,6 +28,8 @@ namespace DFHack {
SYNDROME, SYNDROME,
INVASION, INVASION,
INVENTORY_CHANGE, INVENTORY_CHANGE,
REPORT,
UNIT_ATTACK,
EVENT_MAX EVENT_MAX
}; };
} }
@ -69,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) {} 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 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 int32_t 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, Plugin* plugin);

@ -1,74 +0,0 @@
--onReport.lua
--author expwnent
--contains the "ON_REPORT" event: triggered when there is a new report in df.global.world.status.reports
--example
--local onReport = require 'onReport'
--onReport.triggers.someName = function (reportId)
-- --do stuff with that id
--end
local _ENV = mkmodule('onReport')
local utils = require 'utils'
local repeatUtil = require 'repeatUtil'
lastReport = lastReport or -1
triggers = triggers or {}
monitorFrequency = monitorFrequency or nil
eventToDwarf = eventToDwarf or {}
function updateEventToDwarf(reportId)
if not eventToDwarf[reportId] then
eventToDwarf[reportId] = {}
end
for _,unit in ipairs(df.global.world.units.all) do
for _,reportType in ipairs(unit.reports.log) do
for _,report in ipairs(reportType) do
if report == reportId then
eventToDwarf[reportId][unit.id] = true
end
end
end
end
end
function monitor()
local reports = df.global.world.status.reports
if df.global.world.status.next_report_id-1 <= lastReport then
return
end
-- if #reports == 0 or reports[#reports-1].id <= lastReport then
-- return
-- end
_,_,start = utils.binsearch(reports,lastReport,"id")
while start < #reports and reports[start].id <= lastReport do
start = start+1
end
for i=start,#reports-1,1 do
updateEventToDwarf(reports[i].id)
for _,callBack in pairs(triggers) do
callBack(reports[i].id)
end
lastReport = reports[i].id
end
end
monitorEvery = function(n)
if n <= 0 then
print('cannot monitor onReport every '..n..' ticks.')
return
end
if monitorFrequency and monitorFrequency < n then
print('NOT decreasing frequency of onReport monitoring from every '..monitorFrequency..' ticks to every '..n..' ticks')
return
end
print('monitor onReport every '..n..' ticks')
monitorFrequency = n
repeatUtil.scheduleEvery('onReportMonitoring', n, 'ticks', monitor)
end
monitorEvery(1)
return _ENV

@ -1,255 +0,0 @@
local _ENV = mkmodule('onStrike')
local onReport = require 'onReport'
local utils = require 'utils'
debug = debug or true
triggers = triggers or {}
function getReportString(reportId)
local report = df.report.find(reportId)
local result = report.text
local i = 1
local report2 = df.report.find(reportId+i)
while report2 and report2.flags.continuation do
result = result .. ' ' .. report2.text
i = i+1
report2 = df.report.find(reportId+i)
end
return result
end
onReport.triggers.onStrike = function(reportId)
local report = df.report.find(reportId)
if report["type"] ~= df.announcement_type.COMBAT_STRIKE_DETAILS then
return
end
if report.flags.continuation then
return
end
-- print('\n')
local fighters = {}
for unitId,_ in pairs(onReport.eventToDwarf[reportId]) do
table.insert(fighters,unitId)
end
local reportString = getReportString(reportId)
if #fighters ~= 2 then
if debug then
local ok = string.find(reportString,' skids along ')
if not ok then
print('onStrike: #fighters = ' .. #fighters)
print(reportString)
df.global.pause_state = true
end
end
return
end
local info = {}
local count = 0
fighters[1] = df.unit.find(fighters[1])
fighters[2] = df.unit.find(fighters[2])
local function getWound(fighterA,fighterB)
local wound
for i=#fighterB.body.wounds-1,0,-1 do
local w = fighterB.body.wounds[i]
if w.unit_id == fighterA.id and w.age <= 1 then
wound = w
break
end
end
--[[name,_ = tryParse(reportString,getNames(fighterA))
if not name then
wound = nil
end
--]]
return wound
end
local wound1 = getWound(fighters[1],fighters[2])
local wound2 = getWound(fighters[2],fighters[1])
local flying = string.find(reportString,'The flying')
local name1
local name2
if flying then
name1 = findAny(reportString,getNames(fighters[2]))
name2 = findAny(reportString,getNames(fighters[1]))
else
name1 = tryParse(reportString,getNames(fighters[1]))
name2 = tryParse(reportString,getNames(fighters[2]))
end
if name1 and wound1 and name2 and wound2 then
if debug then
print('ambiguous wounds: ' .. reportString)
print('fighter1 = ' .. fighters[1].id)
print('fighter2 = ' .. fighters[2].id)
df.global.pause_state = true
end
return
elseif not wound1 and not wound2 then
local ok = fighters[1].flags1.dead or fighters[2].flags1.dead or string.find(reportString,' grabs ') or string.find(reportString,'snatches at') or string.find(reportString,'glances away!') or string.find(reportString, ' shakes ')
if not ok and debug then
print('neither wound works: ' .. reportString)
print('fighter1 = ' .. fighters[1].id)
print('fighter2 = ' .. fighters[2].id)
df.global.pause_state = true
end
return
elseif not name1 and not name2 and not string.find(reportString,'The flying ') then
if debug then
print('WTF?')
print('fighter1 = ' .. fighters[1].id)
print('fighter2 = ' .. fighters[2].id)
df.global.pause_state = true
end
return
elseif name1 and wound1 then
else
local temp = fighters[1]
fighters[1] = fighters[2]
fighters[2] = temp
end
local wound = wound1 or wound2
--is it a weapon attack?
local isWeaponAttack
if getWeapon(fighters[1]) and string.find(reportString,getWeaponString(fighters[1])) then
isWeaponAttack = true
else
isWeaponAttack = false
end
isWeaponAttack = isWeaponAttack or flying
local weapon
if isWeaponAttack then
weapon = getWeapon(fighters[1])
end
-- print('triggers')
for _,trigger in pairs(triggers) do
-- print('trigger')
trigger(fighters[1],fighters[2],weapon,wound)
end
end
function myConcat(table1,table2)
local result = {}
for _,v in pairs(table1) do
table.insert(result,v)
end
for _,v in pairs(table2) do
table.insert(result,v)
end
return result
end
function getUnitAttackStrings(unit)
local result = {}
for _,attack in ipairs(unit.body.body_plan.attacks) do
table.insert(result,attack.verb_3rd..' ')
end
return result
end
function getUnitAttack(unit,parsedAttack)
for _,attack in ipairs(unit.body.body_plan.attacks) do
if attack.verb_3rd..' ' == parsedAttack then
return result
end
end
return nil
end
function getWeapon(unit)
function dumb(item)
-- print('\n')
-- print(item)
-- printall(item)
if item.mode ~= df.unit_inventory_item.T_mode.Weapon then
--print('item.mode ' .. item.mode .. ' /= Weapon ' .. df.unit_inventory_item.T_mode.Weapon)
return false
end
if item.item._type ~= df.item_weaponst then
--print('item.item._type ' .. item.item._type .. ' /= df.item_weaponst ' .. df.item_weaponst)
return false
end
return true
end
for _,item in ipairs(unit.inventory) do
if dumb(item) then
return item.item
end
end
return nil
end
function getWeaponAttackStrings(unit)
local result = {}
local weapon = getWeapon(unit)
if not weapon then
print('no weapon')
return result
end
for _,attack in ipairs(weapon.subtype.attacks) do
table.insert(result,attack.verb_3rd..' ')
end
return result
end
function getWeaponAttack(unit,parsedAttack)
local weapon = getWeapon(unit)
if not weapon then
return nil
end
for _,attack in ipairs(weapon.subtype.attacks) do
if attack.verb_3rd..' ' == parsedAttack then
return attack
end
end
return nil
end
function getNames(unit)
local result = {}
table.insert(result,unit.name.first_name .. ' ')
table.insert(result,'The '..dfhack.units.getProfessionName(unit)..' ')
table.insert(result,'the '..dfhack.units.getProfessionName(unit)..' ')
table.insert(result,'The Stray '..dfhack.units.getProfessionName(unit)..' ')
table.insert(result,'the stray '..dfhack.units.getProfessionName(unit)..' ')
return result
end
function getWeaponString(unit,suffix)
local weapon = getWeapon(unit)
if not weapon then
return ''
end
local material = getMaterialString(weapon)
return material .. ' ' .. weapon.subtype.name .. (suffix or '')
end
function getMaterialString(item)
local material = dfhack.matinfo.decode(item.mat_type,item.mat_index)
return material.material.state_name[df.matter_state.Solid]
end
function findAny(parseString,strs)
for _,str in ipairs(strs) do
if string.find(parseString,str) then
return str
end
end
return nil
end
function tryParse(parseString,strs)
for _,str in ipairs(strs) do
if string.sub(parseString,1,#str) == str then
--print('\n"' .. str .. '" matches "' .. parseString .. '"\n')
return str,string.sub(parseString,#str+1,#parseString)
end
--print('\n"' .. str .. '" doesn\'t match "' .. parseString .. '"\n')
end
return nil,nil
end
return _ENV

@ -1,5 +1,6 @@
#include "Core.h" #include "Core.h"
#include "Console.h" #include "Console.h"
#include "VTableInterpose.h"
#include "modules/Buildings.h" #include "modules/Buildings.h"
#include "modules/Constructions.h" #include "modules/Constructions.h"
#include "modules/EventManager.h" #include "modules/EventManager.h"
@ -7,6 +8,7 @@
#include "modules/Job.h" #include "modules/Job.h"
#include "modules/World.h" #include "modules/World.h"
#include "df/announcement_type.h"
#include "df/building.h" #include "df/building.h"
#include "df/construction.h" #include "df/construction.h"
#include "df/general_ref.h" #include "df/general_ref.h"
@ -14,13 +16,20 @@
#include "df/general_ref_unit_workerst.h" #include "df/general_ref_unit_workerst.h"
#include "df/global_objects.h" #include "df/global_objects.h"
#include "df/item.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.h"
#include "df/job_list_link.h" #include "df/job_list_link.h"
#include "df/report.h"
#include "df/ui.h" #include "df/ui.h"
#include "df/unit.h" #include "df/unit.h"
#include "df/unit_flags1.h" #include "df/unit_flags1.h"
#include "df/unit_inventory_item.h" #include "df/unit_inventory_item.h"
#include "df/unit_report_type.h"
#include "df/unit_syndrome.h" #include "df/unit_syndrome.h"
#include "df/unit_wound.h"
#include "df/world.h" #include "df/world.h"
#include <map> #include <map>
@ -116,6 +125,8 @@ static void manageConstructionEvent(color_ostream& out);
static void manageSyndromeEvent(color_ostream& out); static void manageSyndromeEvent(color_ostream& out);
static void manageInvasionEvent(color_ostream& out); static void manageInvasionEvent(color_ostream& out);
static void manageEquipmentEvent(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&); typedef void (*eventManager_t)(color_ostream&);
@ -130,6 +141,8 @@ static const eventManager_t eventManager[] = {
manageSyndromeEvent, manageSyndromeEvent,
manageInvasionEvent, manageInvasionEvent,
manageEquipmentEvent, manageEquipmentEvent,
manageReportEvent,
manageUnitAttackEvent,
}; };
//job initiated //job initiated
@ -162,6 +175,12 @@ static int32_t nextInvasion;
//static unordered_map<int32_t, vector<df::unit_inventory_item> > equipmentLog; //static unordered_map<int32_t, vector<df::unit_inventory_item> > equipmentLog;
static unordered_map<int32_t, vector<InventoryItem> > equipmentLog; 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) { void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) {
static bool doOnce = false; static bool doOnce = false;
// const string eventNames[] = {"world loaded", "world unloaded", "map loaded", "map unloaded", "viewscreen changed", "core initialized", "begin unload", "paused", "unpaused"}; // const string eventNames[] = {"world loaded", "world unloaded", "map loaded", "map unloaded", "viewscreen changed", "core initialized", "begin unload", "paused", "unpaused"};
@ -186,6 +205,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
equipmentLog.clear(); equipmentLog.clear();
Buildings::clearBuildings(out); Buildings::clearBuildings(out);
lastReport = -1;
lastReportUnitAttack = -1;
gameLoaded = false; gameLoaded = false;
} else if ( event == DFHack::SC_MAP_LOADED ) { } else if ( event == DFHack::SC_MAP_LOADED ) {
/* /*
@ -235,6 +256,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
lastSyndromeTime = startTime; lastSyndromeTime = startTime;
} }
} }
lastReport = -1;
lastReportUnitAttack = -1;
for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) { for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) {
eventLastTick[a] = -1;//-1000000; eventLastTick[a] = -1;//-1000000;
} }
@ -694,3 +717,140 @@ static void manageEquipmentEvent(color_ostream& out) {
} }
} }
static void manageReportEvent(color_ostream& out) {
multimap<Plugin*,EventHandler> copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end());
std::vector<df::report*>& reports = df::global::world->status.reports;
if (reports.size() == 0) {
return;
}
size_t a = df::report::binsearch_index(reports, lastReport, 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 <= lastReport) {
a++;
}
for ( ; a < reports.size(); a++ ) {
df::report* report = reports[a];
for ( auto b = copy.begin(); b != copy.end(); b++ ) {
EventHandler handle = (*b).second;
handle.eventHandler(out, (void*)report->id);
}
lastReport = report->id;
}
}
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 "Core.h"
#include "Export.h" #include "Export.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "modules/EventManager.h"
#include "DataDefs.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/construction.h"
#include "df/coord.h" #include "df/coord.h"
#include "df/item.h" #include "df/item.h"
#include "df/item_actual.h"
#include "df/job.h" #include "df/job.h"
#include "df/unit.h"
#include "df/unit_wound.h"
#include "df/world.h" #include "df/world.h"
#include <vector> #include <vector>
#include <set>
using namespace DFHack; using namespace DFHack;
using namespace std; using namespace std;
@ -28,6 +36,9 @@ void building(color_ostream& out, void* ptr);
void construction(color_ostream& out, void* ptr); void construction(color_ostream& out, void* ptr);
void syndrome(color_ostream& out, void* ptr); void syndrome(color_ostream& out, void* ptr);
void invasion(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); 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; 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) { 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 initiateHandler(jobInitiated, 1);
EventManager::EventHandler completeHandler(jobCompleted, 0); EventManager::EventHandler completeHandler(jobCompleted, 0);
EventManager::EventHandler timeHandler(timePassed, 1); EventManager::EventHandler timeHandler(timePassed, 1);
@ -46,6 +80,7 @@ command_result eventExample(color_ostream& out, vector<string>& parameters) {
EventManager::EventHandler constructionHandler(construction, 100); EventManager::EventHandler constructionHandler(construction, 100);
EventManager::EventHandler syndromeHandler(syndrome, 1); EventManager::EventHandler syndromeHandler(syndrome, 1);
EventManager::EventHandler invasionHandler(invasion, 1000); EventManager::EventHandler invasionHandler(invasion, 1000);
EventManager::EventHandler unitAttackHandler(unitAttack, 1);
EventManager::unregisterAll(plugin_self); EventManager::unregisterAll(plugin_self);
EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, plugin_self); EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, 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::CONSTRUCTION, constructionHandler, plugin_self);
EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, plugin_self); EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, plugin_self);
EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, 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, 1, plugin_self);
EventManager::registerTick(timeHandler, 2, plugin_self); EventManager::registerTick(timeHandler, 2, plugin_self);
EventManager::registerTick(timeHandler, 4, 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); 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());
}
}

@ -1,36 +1,32 @@
#include "Core.h" #include "Core.h"
#include "Error.h" #include "Error.h"
#include <Console.h> #include "Console.h"
#include <Export.h> #include "Export.h"
#include <PluginManager.h> #include "LuaTools.h"
#include <string.h> #include "MiscUtils.h"
#include <stdexcept> #include "PluginManager.h"
#include "VTableInterpose.h"
#include <VTableInterpose.h>
#include "df/building.h"
#include "df/building_workshopst.h" #include "df/building_workshopst.h"
#include "df/construction.h"
#include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/item.h" #include "df/item.h"
#include "df/item_actual.h" #include "df/item_actual.h"
#include "df/unit_wound.h" #include "df/job.h"
#include "df/world.h" #include "df/proj_itemst.h"
#include "df/proj_unitst.h"
#include "df/reaction.h" #include "df/reaction.h"
#include "df/reaction_reagent_itemst.h" #include "df/reaction_reagent_itemst.h"
#include "df/reaction_product_itemst.h" #include "df/reaction_product_itemst.h"
#include "df/unit.h"
#include "df/proj_itemst.h" #include "df/unit_inventory_item.h"
#include "df/proj_unitst.h" #include "df/unit_wound.h"
#include "df/world.h"
#include "MiscUtils.h"
#include "LuaTools.h"
#include "modules/EventManager.h" #include "modules/EventManager.h"
#include "df/job.h" #include <string.h>
#include "df/building.h" #include <stdexcept>
#include "df/construction.h"
using std::vector; using std::vector;
using std::string; using std::string;
@ -124,6 +120,8 @@ static void handle_job_complete(color_ostream &out,df::job*){};
static void handle_constructions(color_ostream &out,df::construction*){}; static void handle_constructions(color_ostream &out,df::construction*){};
static void handle_syndrome(color_ostream &out,int32_t,int32_t){}; 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_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(onBuildingCreatedDestroyed, handle_int32t, int32_t);
DEFINE_LUA_EVENT_1(onJobInitiated,handle_job_init,df::job*); DEFINE_LUA_EVENT_1(onJobInitiated,handle_job_init,df::job*);
DEFINE_LUA_EVENT_1(onJobCompleted,handle_job_complete,df::job*); DEFINE_LUA_EVENT_1(onJobCompleted,handle_job_complete,df::job*);
@ -133,6 +131,8 @@ DEFINE_LUA_EVENT_1(onConstructionCreatedDestroyed, handle_constructions, df::con
DEFINE_LUA_EVENT_2(onSyndrome, handle_syndrome, int32_t,int32_t); DEFINE_LUA_EVENT_2(onSyndrome, handle_syndrome, int32_t,int32_t);
DEFINE_LUA_EVENT_1(onInvasion,handle_int32t,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_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_PLUGIN_LUA_EVENTS {
DFHACK_LUA_EVENT(onWorkshopFillSidebarMenu), DFHACK_LUA_EVENT(onWorkshopFillSidebarMenu),
DFHACK_LUA_EVENT(postWorkshopFillSidebarMenu), DFHACK_LUA_EVENT(postWorkshopFillSidebarMenu),
@ -152,6 +152,8 @@ DFHACK_PLUGIN_LUA_EVENTS {
DFHACK_LUA_EVENT(onSyndrome), DFHACK_LUA_EVENT(onSyndrome),
DFHACK_LUA_EVENT(onInvasion), DFHACK_LUA_EVENT(onInvasion),
DFHACK_LUA_EVENT(onInventoryChange), DFHACK_LUA_EVENT(onInventoryChange),
DFHACK_LUA_EVENT(onReport),
DFHACK_LUA_EVENT(onUnitAttack),
DFHACK_LUA_END DFHACK_LUA_END
}; };
@ -212,6 +214,13 @@ static void ev_mng_inventory(color_ostream& out, void* ptr)
} }
onInventoryChange(out,unitId,itemId,item_old,item_new); onInventoryChange(out,unitId,itemId,item_old,item_new);
} }
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); std::vector<int> enabledEventManagerEvents(EventManager::EventType::EVENT_MAX,-1);
typedef void (*handler_t) (color_ostream&,void*); typedef void (*handler_t) (color_ostream&,void*);
static const handler_t eventHandlers[] = { static const handler_t eventHandlers[] = {
@ -225,6 +234,8 @@ static const handler_t eventHandlers[] = {
ev_mng_syndrome, ev_mng_syndrome,
ev_mng_invasion, ev_mng_invasion,
ev_mng_inventory, ev_mng_inventory,
ev_mng_report,
ev_mng_unitAttack,
}; };
static void enableEvent(int evType,int freq) static void enableEvent(int evType,int freq)
{ {

@ -133,5 +133,5 @@ local function invertTable(tbl)
return ret return ret
end end
eventType=invertTable{[0]="TICK","JOB_INITIATED","JOB_COMPLETED","UNIT_DEATH","ITEM_CREATED", eventType=invertTable{[0]="TICK","JOB_INITIATED","JOB_COMPLETED","UNIT_DEATH","ITEM_CREATED",
"BUILDING","CONSTRUCTION","SYNDROME","INVASION","INVENTORY_CHANGE","EVENT_MAX"} "BUILDING","CONSTRUCTION","SYNDROME","INVASION","INVENTORY_CHANGE","REPORT","UNIT_ATTACK","EVENT_MAX"}
return _ENV return _ENV

@ -20,8 +20,7 @@
using namespace DFHack; using namespace DFHack;
using namespace std; using namespace std;
static bool enabled = false; DFHACK_PLUGIN_IS_ENABLED(enabled);
DFHACK_PLUGIN("syndromeTrigger"); DFHACK_PLUGIN("syndromeTrigger");
void syndromeHandler(color_ostream& out, void* ptr); void syndromeHandler(color_ostream& out, void* ptr);
@ -43,34 +42,42 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
return CR_OK; return CR_OK;
} }
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable)
{
if ( enable == enabled )
return CR_OK;
enabled = enable;
EventManager::unregisterAll(plugin_self);
if ( enabled ) {
EventManager::EventHandler handle(syndromeHandler, 1);
EventManager::registerListener(EventManager::EventType::SYNDROME, handle, plugin_self);
}
return CR_OK;
}
command_result syndromeTrigger(color_ostream& out, vector<string>& parameters) { command_result syndromeTrigger(color_ostream& out, vector<string>& parameters) {
if ( parameters.size() > 1 ) if ( parameters.size() > 1 )
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
bool wasEnabled = enabled; bool enable;
if ( parameters.size() == 1 ) { if ( parameters.size() == 1 ) {
if ( parameters[0] == "enable" ) { if ( parameters[0] == "enable" ) {
enabled = true; enable = true;
} else if ( parameters[0] == "disable" ) { } else if ( parameters[0] == "disable" ) {
enabled = false; enable = false;
} else { } else {
int32_t a = atoi(parameters[0].c_str()); int32_t a = atoi(parameters[0].c_str());
if ( a < 0 || a > 1 ) if ( a < 0 || a > 1 )
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
enabled = (bool)a; enable = (bool)a;
} }
} }
plugin_enable(out, enable);
out.print("syndromeTrigger is %s\n", enabled ? "enabled" : "disabled"); out.print("syndromeTrigger is %s\n", enabled ? "enabled" : "disabled");
if ( enabled == wasEnabled )
return CR_OK;
EventManager::unregisterAll(plugin_self);
if ( enabled ) {
EventManager::EventHandler handle(syndromeHandler, 1);
EventManager::registerListener(EventManager::EventType::SYNDROME, handle, plugin_self);
}
return CR_OK; return CR_OK;
} }

@ -0,0 +1,141 @@
--attackTrigger.lua
--author expwnent
--triggers scripts when a unit attacks another with a weapon type, a weapon of a particular material
local eventful = require 'plugins.eventful'
eventful.enableEvent(eventful.eventType.UNIT_ATTACK,1)
itemTriggers = itemTriggers or {}
materialTriggers = materialTriggers or {}
itemMaterialTriggers = itemMaterialTriggers or {}
local function processTrigger(command, attacker, defender)
local command2 = {}
for i,arg in ipairs(command) do
if arg == '\\ATTACKER' then
command2[i] = '' .. attacker.id
elseif arg == '\\DEFENDER' then
command2[i] = '' .. defender.id
else
command2[i] = arg
end
end
print(dfhack.run_command(table.unpack(command2)))
end
eventful.onUnitAttack.attackTrigger = function(attacker,defender,wound)
attacker = df.unit.find(attacker)
defender = df.unit.find(defender)
if not attacker then
return
end
local attackerWeapon
for _,item in ipairs(attacker.inventory) do
if item.mode == df.unit_inventory_item.T_mode.Weapon then
attackerWeapon = item.item
break
end
end
if not attackerWeapon then
return
end
local weaponType = attackerWeapon.subtype.subtype --attackerWeapon.subtype.id
if itemTriggers[weaponType] then
for _,command in pairs(itemTriggers[weaponType]) do
processTrigger(command,attacker,defender)
end
end
-- if materialTriggers[attackerWeapon.mat_type] and materialTriggers[attackerWeapon.mat_type][attackerWeapon.mat_index] then
if materialTriggers[attackerWeapon.mat_index] then
for _,command in pairs(materialTriggers[attackerWeapon.mat_index]) do
processTrigger(command,attacker,defender)
end
end
-- if itemMaterialTriggers[weaponType] and itemMaterialTriggers[weaponType][attackerWeapon.mat_type] and itemMaterialTriggers[weaponType][attackerWeapon.mat_type][attackerWeapon.mat_index] then
-- for _,command in pairs(itemMaterialTriggers[weaponType][attackerWeapon.mat_type][attackerWeapon.mat_index]) do
-- processTrigger(command,attacker,defender)
-- end
-- end
end
local args = {...}
local i = 1
local command
local weaponType
local material
while i <= #args do
if command then
table.insert(command, args[i])
i = i+1
else
if args[i] == '-weaponType' then
weaponType = args[i+1]
i = i+2
elseif args[i] == '-material' then
material = args[i+1]
i = i+2
elseif args[i] == '-command' then
command = {}
i = i+1
elseif args[i] == '-clear' then
itemTriggers = {}
materialTriggers = {}
i = i+1
else
error('Invalid arguments to attackTrigger.lua.')
end
end
end
if weaponType then
local temp
for _,itemdef in ipairs(df.global.world.raws.itemdefs.weapons) do
if itemdef.id == weaponType then
temp = itemdef.subtype
break
end
end
if not temp then
error 'Could not find weapon type.'
end
weaponType = temp
end
if material then
local i = 0
while true do
local mat = dfhack.matinfo.decode(0,i)
if not mat then
error 'Could not find material.'
end
if mat.inorganic.id == material then
material = mat.index
break
end
i = i+1
end
end
if material then
if not materialTriggers[material] then
materialTriggers[material] = {}
end
table.insert(materialTriggers[material],command)
-- table.insert(materialTriggers[material],function()
-- print(dfhack.run_command(command))
-- end)
elseif weaponType then
if not itemTriggers[weaponType] then
itemTriggers[weaponType] = {}
end
table.insert(itemTriggers[weaponType],command)
end

@ -1,18 +0,0 @@
local onReport = require 'onReport'
onReport.triggers.onReportExample = function(reportId)
-- print('report '..reportId..' happened!')
local report = df.report.find(reportId)
if not report then
return
end
-- printall(report)
-- print('\n')
print(reportId .. ': ' .. df.announcement_type[report["type"]])
for unitId,_ in pairs(onReport.eventToDwarf[reportId]) do
print('relevant dwarf: ' .. unitId)
end
end

@ -1,16 +0,0 @@
local onStrike = require 'onStrike'
local eventful = require 'plugins.eventful'
--print(onStrike)
--onStrike.triggers.onStrikeExample = function(information)
-- print(information.attacker .. ' attacks ' .. information.defender .. ': ' .. information.announcement)
onStrike.triggers.onStrikeExample = function(attacker, defender, weapon, wound)
if weapon then
print(attacker.id..' weapon attacks '..defender.id .. ' with ' .. weapon.id)
--df.global.pause_state = true
else
print(attacker.id..' attacks '..defender.id)
end
end