From 94b59613729d8b77080bdbee6756a22370776e89 Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 1 Jan 2014 16:09:41 +0200 Subject: [PATCH 1/6] New plugin: building-hacks. Allows workshops to block liquids and consume mechanical power, connect to machines. --- plugins/CMakeLists.txt | 1 + plugins/building-hacks.cpp | 234 +++++++++++++++++++++++++++++++++ plugins/lua/building-hacks.lua | 3 + 3 files changed, 238 insertions(+) create mode 100644 plugins/building-hacks.cpp create mode 100644 plugins/lua/building-hacks.lua diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 29acba8a1..7dfef3225 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -163,6 +163,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(cleanconst cleanconst.cpp) DFHACK_PLUGIN(3dveins 3dveins.cpp) DFHACK_PLUGIN(strangemood strangemood.cpp) + DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua) endif() # this is the skeleton plugin. If you want to make your own, make a copy and then change it diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp new file mode 100644 index 000000000..321fef1fa --- /dev/null +++ b/plugins/building-hacks.cpp @@ -0,0 +1,234 @@ +//most of the code is shamelessly stolen from steam-engine.cpp +#include "Core.h" +#include "Error.h" +#include +#include +#include + +#include "LuaTools.h" +#include +#include "MiscUtils.h" + +#include "df/building_doorst.h" +#include "df/building_workshopst.h" +#include "df/machine.h" +#include "df/machine_tile_set.h" +#include "df/power_info.h" +#include "df/world.h" +#include "df/buildings_other_id.h" +#include "df/coord.h" +#include "df/tile_building_occ.h" + +#include + +using namespace DFHack; +using namespace df::enums; +using df::global::world; + +DFHACK_PLUGIN("building-hacks"); +struct workshop_hack_data +{ + int32_t myType; + //machine stuff + df::machine_tile_set connections; + df::power_info powerInfo; + //animation + std::vector > frames; + bool machine_timing; //with machine timing only 4 frames are possible +}; +typedef std::map workshops_data_t; +workshops_data_t hacked_workshops; + + +struct work_hook : df::building_workshopst{ + typedef df::building_workshopst interpose_base; + + workshop_hack_data* find_def() + { + if (type == workshop_type::Custom) + { + auto it=hacked_workshops.find(this->getCustomType()); + if(it!=hacked_workshops.end()) + return &(it->second); + } + return NULL; + } + DEFINE_VMETHOD_INTERPOSE(uint32_t,getImpassableOccupancy,()) + { + if(find_def()) + return tile_building_occ::Impassable; + return INTERPOSE_NEXT(getImpassableOccupancy)(); + } + + DEFINE_VMETHOD_INTERPOSE(void, getPowerInfo, (df::power_info *info)) + { + if (auto def = find_def()) + { + info->produced = def->powerInfo.produced; + info->consumed = def->powerInfo.consumed; + return; + } + + INTERPOSE_NEXT(getPowerInfo)(info); + } + DEFINE_VMETHOD_INTERPOSE(df::machine_info*, getMachineInfo, ()) + { + if (find_def()) + return &machine; + + return INTERPOSE_NEXT(getMachineInfo)(); + } + DEFINE_VMETHOD_INTERPOSE(bool, isPowerSource, ()) + { + workshop_hack_data* def=find_def(); + if (def && def->powerInfo.produced>0) + return true; + + return INTERPOSE_NEXT(isPowerSource)(); + } + DEFINE_VMETHOD_INTERPOSE(void, categorize, (bool free)) + { + if (find_def()) + { + auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE]; + insert_into_vector(vec, &df::building::id, (df::building*)this); + } + + INTERPOSE_NEXT(categorize)(free); + } + + DEFINE_VMETHOD_INTERPOSE(void, uncategorize, ()) + { + if (find_def()) + { + auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE]; + erase_from_vector(vec, &df::building::id, id); + } + + INTERPOSE_NEXT(uncategorize)(); + } + DEFINE_VMETHOD_INTERPOSE(bool, canConnectToMachine, (df::machine_tile_set *info)) + { + if (auto def = find_def()) + { + int real_cx = centerx, real_cy = centery; + bool ok = false; + + for (size_t i = 0; i < def->connections.tiles.size(); i++) + { + // the original function connects to the center tile + centerx = x1 + def->connections.tiles[i].x; + centery = y1 + def->connections.tiles[i].y; + + if (!INTERPOSE_NEXT(canConnectToMachine)(info)) + continue; + + ok = true; + break; + } + + centerx = real_cx; centery = real_cy; + return ok; + } + else + return INTERPOSE_NEXT(canConnectToMachine)(info); + } + DEFINE_VMETHOD_INTERPOSE(bool, isUnpowered, ()) + { + if (auto def = find_def()) + { + if(machine.machine_id==-1) + return true; + df::machine* target_machine=df::machine::find(machine.machine_id); + if(target_machine && target_machine->flags.bits.active) + return false; + return true; + } + + return INTERPOSE_NEXT(isUnpowered)(); + } +}; +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, getImpassableOccupancy); + +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, getPowerInfo); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, getMachineInfo); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, isPowerSource); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, categorize); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, uncategorize); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, canConnectToMachine); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, isUnpowered); +//IMPLEMENT_VMETHOD_INTERPOSE(work_hook, updateAction); +// todo animations... +//IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, drawBuilding); +void clear_mapping() +{ + hacked_workshops.clear(); +} +//arguments: custom type, consumed power, produced power, list of connection points. +static int addBuilding(lua_State* L) +{ + workshop_hack_data newDefinition; + newDefinition.myType=luaL_checkint(L,1); + newDefinition.powerInfo.consumed=luaL_checkint(L,2); + newDefinition.powerInfo.produced=luaL_checkint(L,3); + luaL_checktype(L,4,LUA_TTABLE); + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + lua_getfield(L,-1,"x"); + int x=lua_tonumber(L,-1); + lua_pop(L,1); + lua_getfield(L,-1,"y"); + int y=lua_tonumber(L,-1); + lua_pop(L,1); + + newDefinition.connections.can_connect.push_back(-1);//TODO add this too... + newDefinition.connections.tiles.push_back(df::coord(x,y,0)); + hacked_workshops[newDefinition.myType]=newDefinition; + lua_pop(L,1); + } + lua_pop(L,1); + return 0; +} +DFHACK_PLUGIN_LUA_COMMANDS{ + DFHACK_LUA_COMMAND(addBuilding), + DFHACK_LUA_END +}; +static void enable_hooks(bool enable) +{ + INTERPOSE_HOOK(work_hook,getImpassableOccupancy).apply(enable); + //machine part + INTERPOSE_HOOK(work_hook,getPowerInfo).apply(enable); + INTERPOSE_HOOK(work_hook,getMachineInfo).apply(enable); + INTERPOSE_HOOK(work_hook,isPowerSource).apply(enable); + INTERPOSE_HOOK(work_hook,categorize).apply(enable); + INTERPOSE_HOOK(work_hook,uncategorize).apply(enable); + INTERPOSE_HOOK(work_hook,canConnectToMachine).apply(enable); + INTERPOSE_HOOK(work_hook,isUnpowered).apply(enable); +} +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_WORLD_LOADED: + enable_hooks(true); + break; + case SC_WORLD_UNLOADED: + enable_hooks(false); + clear_mapping(); + break; + default: + break; + } + + return CR_OK; +} +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + enable_hooks(true); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + plugin_onstatechange(out,SC_WORLD_UNLOADED); + return CR_OK; +} diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua new file mode 100644 index 000000000..3431764f8 --- /dev/null +++ b/plugins/lua/building-hacks.lua @@ -0,0 +1,3 @@ +local _ENV = mkmodule('plugins.building-hacks') + +return _ENV \ No newline at end of file From b1e7356e63d12d3bcd8d2d6148bc46156491822f Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 2 Jan 2014 15:19:46 +0200 Subject: [PATCH 2/6] Added animation and on update callback. --- plugins/building-hacks.cpp | 156 +++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 7 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 321fef1fa..60e6179cf 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -18,6 +18,7 @@ #include "df/buildings_other_id.h" #include "df/coord.h" #include "df/tile_building_occ.h" +#include "df/building_drawbuffer.h" #include @@ -26,6 +27,13 @@ using namespace df::enums; using df::global::world; DFHACK_PLUGIN("building-hacks"); +struct graphic_tile //could do just 31x31 and be done, but it's nicer to have flexible imho. +{ + int16_t tile; //originally uint8_t but we need to indicate non-animated tiles + int8_t fore; + int8_t back; + int8_t bright; +}; struct workshop_hack_data { int32_t myType; @@ -33,13 +41,22 @@ struct workshop_hack_data df::machine_tile_set connections; df::power_info powerInfo; //animation - std::vector > frames; - bool machine_timing; //with machine timing only 4 frames are possible + std::vector > frames; + bool machine_timing; //6 frames used in vanilla + int frame_skip; // e.g. 2 means have to ticks between frames + //updateCallback: + int skip_updates; }; typedef std::map workshops_data_t; workshops_data_t hacked_workshops; +static void handle_update_action(color_ostream &out,df::building_workshopst*){}; +DEFINE_LUA_EVENT_1(onUpdateAction,handle_update_action,df::building_workshopst*); +DFHACK_PLUGIN_LUA_EVENTS { + DFHACK_LUA_EVENT(onUpdateAction), + DFHACK_LUA_END +}; struct work_hook : df::building_workshopst{ typedef df::building_workshopst interpose_base; @@ -53,6 +70,10 @@ struct work_hook : df::building_workshopst{ } return NULL; } + inline bool is_fully_built() + { + return getBuildStage() >= getMaxBuildStage(); + } DEFINE_VMETHOD_INTERPOSE(uint32_t,getImpassableOccupancy,()) { if(find_def()) @@ -137,6 +158,8 @@ struct work_hook : df::building_workshopst{ { if (auto def = find_def()) { + if(def->powerInfo.consumed==0) + return false; if(machine.machine_id==-1) return true; df::machine* target_machine=df::machine::find(machine.machine_id); @@ -147,6 +170,65 @@ struct work_hook : df::building_workshopst{ return INTERPOSE_NEXT(isUnpowered)(); } + DEFINE_VMETHOD_INTERPOSE(void, updateAction, ()) + { + if(auto def = find_def()) + { + if(def->skip_updates!=0 && is_fully_built()) + { + df::world* world = df::global::world; + if(world->frame_counter % def->skip_updates == 0) + { + CoreSuspendClaimer suspend; + color_ostream_proxy out(Core::getInstance().getConsole()); + onUpdateAction(out,this); + } + } + } + INTERPOSE_NEXT(updateAction)(); + } + DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, int16_t unk)) + { + INTERPOSE_NEXT(drawBuilding)(db, unk); + + if (auto def = find_def()) + { + if (!is_fully_built() || def->frames.size()==0) + return; + int frame=0; + if(!def->machine_timing) + { + int frame_mod=def->frames.size()* def->frame_skip; + df::world* world = df::global::world; + frame=(world->frame_counter % frame_mod)/def->frame_skip; + } + else + { + if(machine.machine_id!=-1) + { + df::machine* target_machine=df::machine::find(machine.machine_id); + if(target_machine) + { + frame=target_machine->visual_phase % def->frames.size(); + } + } + } + int w=db->x2-db->x1; + std::vector &cur_frame=def->frames[frame]; + for(int i=0;i=0) + { + int tx=i % w; + int ty=i / w; + db->tile[tx][ty]=cur_frame[i].tile; + db->back[tx][ty]=cur_frame[i].back; + db->bright[tx][ty]=cur_frame[i].bright; + db->fore[tx][ty]=cur_frame[i].fore; + } + } + } + } }; IMPLEMENT_VMETHOD_INTERPOSE(work_hook, getImpassableOccupancy); @@ -157,21 +239,63 @@ IMPLEMENT_VMETHOD_INTERPOSE(work_hook, categorize); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, uncategorize); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, canConnectToMachine); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, isUnpowered); -//IMPLEMENT_VMETHOD_INTERPOSE(work_hook, updateAction); -// todo animations... -//IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, drawBuilding); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, updateAction); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, drawBuilding); void clear_mapping() { hacked_workshops.clear(); } -//arguments: custom type, consumed power, produced power, list of connection points. +static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) +{ + luaL_checktype(L,stack_pos,LUA_TTABLE); + lua_pushvalue(L,stack_pos); + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + luaL_checktype(L,-1,LUA_TTABLE); + lua_pushnil(L); + std::vector frame; + while (lua_next(L, -2) != 0) { + graphic_tile t; + lua_pushnumber(L,1); + lua_gettable(L,-2); + t.tile=lua_tonumber(L,-1); + lua_pop(L,1); + + lua_pushnumber(L,2); + lua_gettable(L,-2); + t.fore=lua_tonumber(L,-1); + lua_pop(L,1); + + lua_pushnumber(L,3); + lua_gettable(L,-2); + t.back=lua_tonumber(L,-1); + lua_pop(L,1); + + lua_pushnumber(L,4); + lua_gettable(L,-2); + t.bright=lua_tonumber(L,-1); + lua_pop(L,1); + + frame.push_back(t); + lua_pop(L,1); + } + lua_pop(L,1); + def.frames.push_back(frame); + } + lua_pop(L,1); + return ; +} +//arguments: custom type, consumed power, produced power, list of connection points, update skip(0/nil to disable) +// table of frames,frame to tick ratio (-1 for machine control) static int addBuilding(lua_State* L) { workshop_hack_data newDefinition; newDefinition.myType=luaL_checkint(L,1); newDefinition.powerInfo.consumed=luaL_checkint(L,2); newDefinition.powerInfo.produced=luaL_checkint(L,3); + //table of machine connection points luaL_checktype(L,4,LUA_TTABLE); + lua_pushvalue(L,4); lua_pushnil(L); while (lua_next(L, -2) != 0) { lua_getfield(L,-1,"x"); @@ -183,10 +307,25 @@ static int addBuilding(lua_State* L) newDefinition.connections.can_connect.push_back(-1);//TODO add this too... newDefinition.connections.tiles.push_back(df::coord(x,y,0)); - hacked_workshops[newDefinition.myType]=newDefinition; + lua_pop(L,1); } lua_pop(L,1); + //updates + newDefinition.skip_updates=luaL_optinteger(L,5,0); + //animation + if(!lua_isnil(L,6) && lua_gettop(L)>5) + { + loadFrames(L,newDefinition,6); + newDefinition.frame_skip=luaL_optinteger(L,7,-1); + if(newDefinition.frame_skip==0) + newDefinition.frame_skip=1; + if(newDefinition.frame_skip<0) + newDefinition.machine_timing=true; + else + newDefinition.machine_timing=false; + } + hacked_workshops[newDefinition.myType]=newDefinition; return 0; } DFHACK_PLUGIN_LUA_COMMANDS{ @@ -204,6 +343,9 @@ static void enable_hooks(bool enable) INTERPOSE_HOOK(work_hook,uncategorize).apply(enable); INTERPOSE_HOOK(work_hook,canConnectToMachine).apply(enable); INTERPOSE_HOOK(work_hook,isUnpowered).apply(enable); + //update n render + INTERPOSE_HOOK(work_hook,updateAction).apply(enable); + INTERPOSE_HOOK(work_hook,drawBuilding).apply(enable); } DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { From 834350012f780de948af4e6cf3171ab85ec8be07 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 7 Jan 2014 18:14:54 +0200 Subject: [PATCH 3/6] building-hacks: added lua convenience function, made impassible tile fix optional. --- plugins/building-hacks.cpp | 65 ++++++++++++-------- plugins/lua/building-hacks.lua | 106 +++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 26 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 60e6179cf..b9c9ea3c9 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -37,6 +37,7 @@ struct graphic_tile //could do just 31x31 and be done, but it's nicer to have fl struct workshop_hack_data { int32_t myType; + bool impassible_fix; //machine stuff df::machine_tile_set connections; df::power_info powerInfo; @@ -76,8 +77,11 @@ struct work_hook : df::building_workshopst{ } DEFINE_VMETHOD_INTERPOSE(uint32_t,getImpassableOccupancy,()) { - if(find_def()) - return tile_building_occ::Impassable; + if(auto def = find_def()) + { + if(def->impassible_fix) + return tile_building_occ::Impassable; + } return INTERPOSE_NEXT(getImpassableOccupancy)(); } @@ -213,7 +217,7 @@ struct work_hook : df::building_workshopst{ } } } - int w=db->x2-db->x1; + int w=db->x2-db->x1+1; std::vector &cur_frame=def->frames[frame]; for(int i=0;i5) + if(!lua_isnil(L,7)) { - loadFrames(L,newDefinition,6); - newDefinition.frame_skip=luaL_optinteger(L,7,-1); + loadFrames(L,newDefinition,7); + newDefinition.frame_skip=luaL_optinteger(L,8,-1); if(newDefinition.frame_skip==0) newDefinition.frame_skip=1; if(newDefinition.frame_skip<0) diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index 3431764f8..e0a5b1010 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -1,3 +1,109 @@ local _ENV = mkmodule('plugins.building-hacks') +--[[ + from native: + addBuilding(custom type,impassible fix (bool), consumed power, produced power, list of connection points, + update skip(0/nil to disable),table of frames,frame to tick ratio (-1 for machine control)) + from here: + registerBuilding{ + name -- custom workshop id e.g. SOAPMAKER << required! + fix_impassible -- make impassible tiles impassible to liquids too + consume -- how much machine power is needed to work + produce -- how much machine power is produced + gears -- a table or {x=?,y=?} of connection points for machines + action -- a table of number (how much ticks to skip) and a function which gets called on shop update + animate -- a table of + frames -- a table of + tables of 4 numbers (tile,fore,back,bright) OR + empty table (tile not modified) OR + {x= y= + 4 numbers like in first case} -- this generates full frame even, usefull for animations that change little (1-2 tiles) + frameLenght -- how many ticks does one frame take OR + isMechanical -- a bool that says to try to match to mechanical system (i.e. how gears are turning) + } +]] +_registeredStuff={} +local function unregall(state) + if state==SC_WORLD_UNLOADED then + onUpdateAction._library=nil + dfhack.onStateChange.building_hacks= nil + _registeredStuff={} + end +end +local function onUpdateLocal(workshop) + local f=_registeredStuff[workshop:getCustomType()] + if f then + f(workshop) + end +end +local function findCustomWorkshop(name) + local raws=df.global.world.raws.buildings.all + for k,v in ipairs(raws) do + if v.code==name then + return v + end + end +end +local function registerUpdateAction(shopId,callback) + _registeredStuff[shopId]=callback + onUpdateAction._library=onUpdateLocal + dfhack.onStateChange.building_hacks=unregall +end +local function generateFrame(tiles,w,h) + local mTiles={} + for k,v in ipairs(tiles) do + mTiles[v.x]=mTiles[v.x] or {} + mTiles[v.x][v.y]=v + end + local ret={} + for ty=0,h-1 do + for tx=0,w-1 do + if mTiles[tx] and mTiles[tx][ty] then + table.insert(ret,mTiles[tx][ty]) -- leaves x and y in but who cares + else + table.insert(ret,{}) + end + end + end + return ret +end +local function processFrames(shop_def,frames) + local w,h=shop_def.dim_x,shop_def.dim_y + for frame_id,frame in ipairs(frames) do + if frame[1].x~=nil then + frames[frame_id]=generateFrame(frame,w,h) + end + end + return frames +end +function registerBuilding(args) + local shop_def=findCustomWorkshop(args.name) + local shop_id=shop_def.id + local fix_impassible + if args.fix_impassible then + fix_impassible=1 + else + fix_impassible=0 + end + local consume=args.consume or 0 + local produce=args.produce or 0 + local gears=args.gears or {} + local action=args.action --could be nil + local updateSkip=0 + if action~=nil then + updateSkip=action[1] + registerUpdateAction(shop_id,action[2]) + end + local animate=args.animate + local frameLength=1 + local frames + if animate~=nil then + frameLength=animate.frameLength + if animate.isMechanical then + frameLength=-1 + end + frames=processFrames(shop_def,animate.frames) + end + + addBuilding(shop_id,fix_impassible,consume,produce,gears,updateSkip,frames,frameLength) +end return _ENV \ No newline at end of file From a52451a97a0a85bf27db8eaa3f54c2dd169d1f07 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 7 Jan 2014 18:15:49 +0200 Subject: [PATCH 4/6] eventful: added support for custom workshops and a convenience function to interpose side panel generation --- plugins/lua/eventful.lua | 23 ++++++++++++++++++++--- scripts/gui/advfort.lua | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/plugins/lua/eventful.lua b/plugins/lua/eventful.lua index a8840424b..2aa713583 100644 --- a/plugins/lua/eventful.lua +++ b/plugins/lua/eventful.lua @@ -25,13 +25,13 @@ local function getShopName(btype,bsubtype,bcustom) if typenames_shop[bsubtype]~=nil then return typenames_shop[bsubtype] else - return nil --todo add custom (not very useful) + return df.building_def_workshopst.find(bcustom).code end elseif btype==df.building_type.Furnace then if typenames_furnace[bsubtype]~=nil then return typenames_furnace[bsubtype] else - return nil --todo add custom (not very useful) + return df.building_def_furnacest.find(bcustom).code end end end @@ -77,9 +77,19 @@ local function onPostSidebar(workshop) wjob.choices_visible:insert("#",new_button) end end + if _registeredStuff.customSidebar and _registeredStuff.customSidebar[shop_id] then + _registeredStuff.customSidebar[shop_id](workshop) + end + end +end +local function customSidebarsCallback(workshop) + local shop_id=getShopName(workshop:getType(),workshop:getSubtype(),workshop:getCustomType()) + if shop_id then + if _registeredStuff.customSidebar and _registeredStuff.customSidebar[shop_id] then + _registeredStuff.customSidebar[shop_id](workshop) + end end end - function registerReaction(reaction_name,callback) _registeredStuff.reactionCallbacks=_registeredStuff.reactionCallbacks or {} _registeredStuff.reactionCallbacks[reaction_name]=callback @@ -87,6 +97,13 @@ function registerReaction(reaction_name,callback) dfhack.onStateChange.eventful=unregall end +function registerSidebar(shop_name,callback) + _registeredStuff.customSidebar=_registeredStuff.customSidebar or {} + _registeredStuff.customSidebar[shop_name]=callback + onWorkshopFillSidebarMenu._library=customSidebarsCallback + dfhack.onStateChange.eventful=unregall +end + function removeNative(shop_name,name) _registeredStuff.shopNonNative=_registeredStuff.shopNonNative or {} local shops=_registeredStuff.shopNonNative diff --git a/scripts/gui/advfort.lua b/scripts/gui/advfort.lua index e29042e54..ea93aa262 100644 --- a/scripts/gui/advfort.lua +++ b/scripts/gui/advfort.lua @@ -880,7 +880,7 @@ function usetool:init(args) wid.Label{ view_id="mainLabel", frame = {xalign=0,yalign=0}, - text={{key=keybinds.prevJob.key},{gap=1,text=dfhack.curry(usetool.getModeName,self)},{gap=1,key=keybinds.nextJob.key}, + text={{key=keybinds.prevJob.key},{gap=1,text=self:callback("getModeName")},{gap=1,key=keybinds.nextJob.key}, } }, From ba85f671ac1943f4d69d7f67bd2840aee4252602 Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 8 Jan 2014 23:21:00 +0200 Subject: [PATCH 5/6] docs: updated eventful lua api and added building-hacks docs. --- Lua API.rst | 52 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/Lua API.rst b/Lua API.rst index 6c769f004..53ff8a952 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -3098,7 +3098,7 @@ These events are straight from EventManager module. Each of them first needs to 9. ``onInventoryChange(unit_id,item_id,old_equip,new_equip)`` Gets called when someone picks up an item, puts one down, or changes the way they are holding it. If an item is picked up, old_equip will be null. If an item is dropped, new_equip will be null. If an item is re-equipped in a new way, then neither will be null. You absolutely must NOT alter either old_equip or new_equip or you might break other plugins. - + Functions --------- @@ -3118,6 +3118,10 @@ Functions Enable event checking for EventManager events. For event types use ``eventType`` table. Note that different types of events require different frequencies to be effective. The frequency is how many ticks EventManager will wait before checking if that type of event has happened. If multiple scripts or plugins use the same event type, the smallest frequency is the one that is used, so you might get events triggered more often than the frequency you use here. +5. ``registerSidebar(shop_name,callback)`` + + Enable callback when sidebar for ``shop_name`` is drawn. Usefull for custom workshop views e.g. using gui.dwarfmode lib. + Examples -------- Spawn dragon breath on each item attempt to contaminate wound:: @@ -3131,13 +3135,13 @@ Reaction complete example:: b=require "plugins.eventful" - b.onReactionComplete.one=function(reaction,unit,in_items,in_reag,out_items,call_native) + b.registerReaction("LUA_HOOK_LAY_BOMB",function(reaction,unit,in_items,in_reag,out_items,call_native) local pos=copyall(unit.pos) -- spawn dragonbreath after 100 ticks dfhack.timeout(100,"ticks",function() dfhack.maps.spawnFlow(pos,6,0,0,50000) end) --do not call real item creation code call_native.value=false - end + end) Grenade example:: @@ -3151,6 +3155,48 @@ Integrated tannery:: b=require "plugins.eventful" b.addReactionToShop("TAN_A_HIDE","LEATHERWORKS") + +Building-hacks +============== + +This plugin overwrites some methods in workshop df class so that mechanical workshops are possible. Although +plugin export a function it's recommended to use lua decorated function. + +Functions +--------- + +``registerBuilding(table)`` where table must contain name, as a workshop raw name, the rest are optional: + 1. name -- custom workshop id e.g. ``SOAPMAKER`` + 2. fix_impassible -- if true make impassible tiles impassible to liquids too + 3. consume -- how much machine power is needed to work. Disables reactions if not supplied enough + 4. produce -- how much machine power is produced. Use discouraged as there is no way to change this at runtime + 5. gears -- a table or ``{x=?,y=?}`` of connection points for machines + 6. action -- a table of number (how much ticks to skip) and a function which gets called on shop update + 7. animate -- a table of frames which can be a table of: + + a. tables of 4 numbers ``{tile,fore,back,bright}`` OR + b. empty table (tile not modified) OR + c. ``{x= y= + 4 numbers like in first case}``, this generates full frame useful for animations that change little (1-2 tiles) + +Animate table also might contain: + 1. frameLenght -- how many ticks does one frame take OR + 2. isMechanical -- a bool that says to try to match to mechanical system (i.e. how gears are turning) + +Examples +-------- + +Simple mechanical workshop:: + + require('plugins.building-hacks').registerBuilding{name="BONE_GRINDER", + consume=15, + gears={x=0,y=0}, --connection point + animate={ + isMechanical=true, --animate the same connection point as vanilla gear + frames={ + {{x=0,y=0,42,7,0,0}}, --first frame, 1 changed tile + {{x=0,y=0,15,7,0,0}} -- second frame, same + } + } ======= Scripts From 94904eb66b9c2207ea26bd3a178e8611251db32d Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 4 May 2014 18:27:18 +0300 Subject: [PATCH 6/6] Updated NEWS. --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index b43ced4f0..0bd107f07 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ DFHack future Internals: - support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua). + - eventful now has a more friendly way of making custom sidebars + - new plugin: building-hacks. Allows to add custom functionality and/or animations to buildings. New commands: - move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands