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
Internals:
LuaApi
support queuing dfhack.timeout events before a world is loaded.
EventManager: added ON_REPORT and UNIT_ATTACK events and exposed them to Lua.
New scripts:
add-syndrome.lua
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
makes it so civs don't bring barrels full of blood ichor or goo
feeding-timers.lua
@ -24,12 +25,6 @@ DFHack future
adds and removes syndromes to units when they equip and unequip items
moddableGods.lua
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
projectileExpansion.lua
adds extra functionality to projectiles

@ -208,6 +208,12 @@ log-region
# patch the material objects in memory to fix cloth stockpiles
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 #
#######################################################

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

@ -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 {
@ -26,6 +28,8 @@ namespace DFHack {
SYNDROME,
INVASION,
INVENTORY_CHANGE,
REPORT,
UNIT_ATTACK,
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) {}
};
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,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 "Console.h"
#include "VTableInterpose.h"
#include "modules/Buildings.h"
#include "modules/Constructions.h"
#include "modules/EventManager.h"
@ -7,6 +8,7 @@
#include "modules/Job.h"
#include "modules/World.h"
#include "df/announcement_type.h"
#include "df/building.h"
#include "df/construction.h"
#include "df/general_ref.h"
@ -14,13 +16,20 @@
#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"
#include "df/ui.h"
#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>
@ -116,6 +125,8 @@ static void manageConstructionEvent(color_ostream& out);
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&);
@ -130,6 +141,8 @@ static const eventManager_t eventManager[] = {
manageSyndromeEvent,
manageInvasionEvent,
manageEquipmentEvent,
manageReportEvent,
manageUnitAttackEvent,
};
//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<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"};
@ -186,6 +205,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
equipmentLog.clear();
Buildings::clearBuildings(out);
lastReport = -1;
lastReportUnitAttack = -1;
gameLoaded = false;
} else if ( event == DFHack::SC_MAP_LOADED ) {
/*
@ -235,6 +256,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
lastSyndromeTime = startTime;
}
}
lastReport = -1;
lastReportUnitAttack = -1;
for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) {
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 "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());
}
}

@ -1,36 +1,32 @@
#include "Core.h"
#include "Error.h"
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
#include <string.h>
#include <stdexcept>
#include <VTableInterpose.h>
#include "Console.h"
#include "Export.h"
#include "LuaTools.h"
#include "MiscUtils.h"
#include "PluginManager.h"
#include "VTableInterpose.h"
#include "df/building.h"
#include "df/building_workshopst.h"
#include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/construction.h"
#include "df/item.h"
#include "df/item_actual.h"
#include "df/unit_wound.h"
#include "df/world.h"
#include "df/job.h"
#include "df/proj_itemst.h"
#include "df/proj_unitst.h"
#include "df/reaction.h"
#include "df/reaction_reagent_itemst.h"
#include "df/reaction_product_itemst.h"
#include "df/proj_itemst.h"
#include "df/proj_unitst.h"
#include "MiscUtils.h"
#include "LuaTools.h"
#include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/unit_wound.h"
#include "df/world.h"
#include "modules/EventManager.h"
#include "df/job.h"
#include "df/building.h"
#include "df/construction.h"
#include <string.h>
#include <stdexcept>
using std::vector;
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_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*);
@ -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_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),
@ -152,6 +152,8 @@ DFHACK_PLUGIN_LUA_EVENTS {
DFHACK_LUA_EVENT(onSyndrome),
DFHACK_LUA_EVENT(onInvasion),
DFHACK_LUA_EVENT(onInventoryChange),
DFHACK_LUA_EVENT(onReport),
DFHACK_LUA_EVENT(onUnitAttack),
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);
}
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[] = {
@ -225,6 +234,8 @@ static const handler_t eventHandlers[] = {
ev_mng_syndrome,
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","EVENT_MAX"}
"BUILDING","CONSTRUCTION","SYNDROME","INVASION","INVENTORY_CHANGE","REPORT","UNIT_ATTACK","EVENT_MAX"}
return _ENV

@ -20,8 +20,7 @@
using namespace DFHack;
using namespace std;
static bool enabled = false;
DFHACK_PLUGIN_IS_ENABLED(enabled);
DFHACK_PLUGIN("syndromeTrigger");
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;
}
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) {
if ( parameters.size() > 1 )
return CR_WRONG_USAGE;
bool wasEnabled = enabled;
bool enable;
if ( parameters.size() == 1 ) {
if ( parameters[0] == "enable" ) {
enabled = true;
enable = true;
} else if ( parameters[0] == "disable" ) {
enabled = false;
enable = false;
} else {
int32_t a = atoi(parameters[0].c_str());
if ( a < 0 || a > 1 )
return CR_WRONG_USAGE;
enabled = (bool)a;
enable = (bool)a;
}
}
plugin_enable(out, enable);
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;
}

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