From 9a7370ff4409755144d7741d4573efa86e3954e4 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 17 Mar 2024 17:35:55 +0200 Subject: [PATCH 01/38] Add graphics support for building-hacks --- plugins/CMakeLists.txt | 2 +- plugins/building-hacks.cpp | 52 +++++++++++++++++++++++++------------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index bb1b140aea..ae27c18592 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -78,7 +78,7 @@ if(BUILD_SUPPORTED) dfhack_plugin(autoslab autoslab.cpp) dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) dfhack_plugin(burrow burrow.cpp LINK_LIBRARIES lua) - #dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) + dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) add_subdirectory(buildingplan) dfhack_plugin(changeitem changeitem.cpp) dfhack_plugin(changelayer changelayer.cpp) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 72c6e50f50..3aba621145 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -35,6 +35,9 @@ struct graphic_tile //could do just 31x31 and be done, but it's nicer to have fl int8_t fore; int8_t back; int8_t bright; + uint32_t graphics_tile; + uint32_t overlay_tile; + uint32_t unk_tile;//??? }; struct workshop_hack_data { @@ -258,9 +261,9 @@ struct work_hook : df::building_workshopst{ } INTERPOSE_NEXT(updateAction)(); } - DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, int16_t unk)) + DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (int32_t unk1,df::building_drawbuffer *db, int16_t unk2)) { - INTERPOSE_NEXT(drawBuilding)(db, unk); + INTERPOSE_NEXT(drawBuilding)(unk1,db, unk2); if (auto def = find_def()) { @@ -287,15 +290,22 @@ struct work_hook : df::building_workshopst{ std::vector &cur_frame=def->frames[frame]; for(size_t i=0;i=0) + int tx = i % w; + int ty = i / w; + const auto& cf = cur_frame[i]; + if(cf.tile>=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; + db->tile[tx][ty]= cf.tile; + db->back[tx][ty]= cf.back; + db->bright[tx][ty]= cf.bright; + db->fore[tx][ty]= cf.fore; } + if (cf.graphics_tile >= 0) + db->texpos1[tx][ty] = cf.graphics_tile; + if (cf.overlay_tile >= 0) + db->texpos2[tx][ty] = cf.overlay_tile; + if (cf.unk_tile >= 0) + db->texpos3[tx][ty] = cf.unk_tile; } } } @@ -330,8 +340,8 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) std::vector frame; while (lua_next(L, -2) != 0) { graphic_tile t; - lua_pushnumber(L,1); - lua_gettable(L,-2); + + lua_geti(L, -1, 1); if(lua_isnil(L,-1)) { t.tile=-1; @@ -342,21 +352,29 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) t.tile=lua_tonumber(L,-1); lua_pop(L,1); - lua_pushnumber(L,2); - lua_gettable(L,-2); + lua_geti(L, -1, 2); t.fore=lua_tonumber(L,-1); lua_pop(L,1); - lua_pushnumber(L,3); - lua_gettable(L,-2); + lua_geti(L, -1, 3); t.back=lua_tonumber(L,-1); lua_pop(L,1); - lua_pushnumber(L,4); - lua_gettable(L,-2); + lua_geti(L, -1, 4); t.bright=lua_tonumber(L,-1); lua_pop(L,1); + lua_geti(L, -1, 5); + t.graphics_tile = luaL_optinteger(L, -1,-1); + lua_pop(L, 1); + + lua_geti(L, -1, 6); + t.overlay_tile = luaL_optinteger(L, -1, -1); + lua_pop(L, 1); + + lua_geti(L, -1, 7); + t.unk_tile = luaL_optinteger(L, -1, -1); + lua_pop(L, 1); } frame.push_back(t); lua_pop(L,1); From c4266c55c6680b6f35323cc6ebf202c8529f090e Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 18 Mar 2024 17:51:21 +0200 Subject: [PATCH 02/38] Update to new structures and simplify api a bit --- plugins/building-hacks.cpp | 60 +++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 3aba621145..c3e97852b6 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -31,13 +31,16 @@ REQUIRE_GLOBAL(world); 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 + int16_t tile=-1; //originally uint8_t but we need to indicate non-animated tiles int8_t fore; int8_t back; int8_t bright; - uint32_t graphics_tile; - uint32_t overlay_tile; - uint32_t unk_tile;//??? + //index of texpos + uint32_t graphics_tile = -1; + uint32_t overlay_tile = -1; + uint32_t item_tile = -1; + //only for first line + uint32_t signpost_tile = -1; }; struct workshop_hack_data { @@ -301,11 +304,14 @@ struct work_hook : df::building_workshopst{ db->fore[tx][ty]= cf.fore; } if (cf.graphics_tile >= 0) - db->texpos1[tx][ty] = cf.graphics_tile; + db->building_one_texpos[tx][ty] = cf.graphics_tile; if (cf.overlay_tile >= 0) - db->texpos2[tx][ty] = cf.overlay_tile; - if (cf.unk_tile >= 0) - db->texpos3[tx][ty] = cf.unk_tile; + db->building_two_texpos[tx][ty] = cf.overlay_tile; + if (cf.item_tile >= 0) + db->item_texpos[tx][ty] = cf.item_tile; + //only first line has signpost graphics + if (cf.item_tile >= 0 && ty==0) + db->signpost_texpos[tx] = cf.signpost_tile; } } } @@ -331,37 +337,42 @@ void clear_mapping() } static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) { + const int max_idx = 31 * 31; + 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_geti(L, -1, 1); - if(lua_isnil(L,-1)) + std::vector frame(max_idx); + + for (int idx = 0; idx < max_idx; idx++) + { + auto& t = frame[idx]; + lua_geti(L, -1, idx); + //allow sparse indexing + if (lua_isnil(L, -1)) { - t.tile=-1; - lua_pop(L,1); + lua_pop(L, 1); + continue; } else { - t.tile=lua_tonumber(L,-1); + lua_geti(L, -1, 1); + //not sure why would anyone do nil tile, but for api consitency lets allow it + t.tile= luaL_optinteger(L,-1,-1); lua_pop(L,1); lua_geti(L, -1, 2); - t.fore=lua_tonumber(L,-1); + t.fore= luaL_optinteger(L,-1,0); lua_pop(L,1); lua_geti(L, -1, 3); - t.back=lua_tonumber(L,-1); + t.back= luaL_optinteger(L,-1,0); lua_pop(L,1); lua_geti(L, -1, 4); - t.bright=lua_tonumber(L,-1); + t.bright=luaL_optinteger(L,-1,0); lua_pop(L,1); lua_geti(L, -1, 5); @@ -373,13 +384,16 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) lua_pop(L, 1); lua_geti(L, -1, 7); - t.unk_tile = luaL_optinteger(L, -1, -1); + t.signpost_tile = luaL_optinteger(L, -1, -1); + lua_pop(L, 1); + + lua_geti(L, -1, 8); + t.item_tile = luaL_optinteger(L, -1, -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); From 6ade67ed3109d3e4d541453ed44d853ae2e5c2f1 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 18 Mar 2024 21:40:07 +0200 Subject: [PATCH 03/38] Fix bugs and refactor lua c api to use indexed access --- plugins/building-hacks.cpp | 42 +++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index c3e97852b6..8003e29b6a 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -264,9 +264,9 @@ struct work_hook : df::building_workshopst{ } INTERPOSE_NEXT(updateAction)(); } - DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (int32_t unk1,df::building_drawbuffer *db, int16_t unk2)) + DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (uint32_t curtick,df::building_drawbuffer *db, int16_t z_offset)) { - INTERPOSE_NEXT(drawBuilding)(unk1,db, unk2); + INTERPOSE_NEXT(drawBuilding)(curtick,db, z_offset); if (auto def = find_def()) { @@ -289,12 +289,11 @@ struct work_hook : df::building_workshopst{ } } } - int w=db->x2-db->x1+1; std::vector &cur_frame=def->frames[frame]; for(size_t i=0;i=0) { @@ -303,14 +302,14 @@ struct work_hook : df::building_workshopst{ db->bright[tx][ty]= cf.bright; db->fore[tx][ty]= cf.fore; } - if (cf.graphics_tile >= 0) + if (cf.graphics_tile != -1) db->building_one_texpos[tx][ty] = cf.graphics_tile; - if (cf.overlay_tile >= 0) + if (cf.overlay_tile != -1) db->building_two_texpos[tx][ty] = cf.overlay_tile; - if (cf.item_tile >= 0) + if (cf.item_tile != -1) db->item_texpos[tx][ty] = cf.item_tile; //only first line has signpost graphics - if (cf.item_tile >= 0 && ty==0) + if (cf.item_tile != -1 && ty==0) db->signpost_texpos[tx] = cf.signpost_tile; } } @@ -340,24 +339,26 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) const int max_idx = 31 * 31; 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); + + int frame_index = 1; + + while (lua_geti(L,stack_pos,frame_index) != LUA_TNIL) { //get frame[i] + luaL_checktype(L,-1,LUA_TTABLE); //ensure that it's a table std::vector frame(max_idx); for (int idx = 0; idx < max_idx; idx++) { auto& t = frame[idx]; - lua_geti(L, -1, idx); - //allow sparse indexing - if (lua_isnil(L, -1)) + lua_geti(L, -1, idx); //get tile at idx i.e. frame[i][idx] where idx=x+y*31 + + if (lua_isnil(L, -1))//allow sparse indexing { - lua_pop(L, 1); + lua_pop(L, 1); //pop current tile (nil in this case) continue; } else { + //load up tile, color, optionally graphics stuff lua_geti(L, -1, 1); //not sure why would anyone do nil tile, but for api consitency lets allow it t.tile= luaL_optinteger(L,-1,-1); @@ -390,13 +391,16 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) lua_geti(L, -1, 8); t.item_tile = luaL_optinteger(L, -1, -1); lua_pop(L, 1); + + lua_pop(L, 1); //pop current tile } frame.push_back(t); - lua_pop(L,1); } def.frames.push_back(frame); + frame_index++; + lua_pop(L, 1); //pop current frame } - lua_pop(L,1); + return ; } //arguments: custom type,impassible fix (bool), consumed power, produced power, list of connection points, update skip(0/nil to disable) From b295d578adb64d27bcc3f6e618478068b4eb1795 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 18 Mar 2024 21:40:32 +0200 Subject: [PATCH 04/38] Update plugin lua module --- plugins/lua/building-hacks.lua | 55 +++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index 7304dfae33..c1a3172649 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -15,19 +15,30 @@ local _ENV = mkmodule('plugins.building-hacks') action -- a table of number (how much ticks to skip) and a function which gets called on shop update canBeRoomSubset -- room is considered in to be part of the building defined by chairs etc... auto_gears -- find the gears automatically and animate them + auto_graphics -- insert gear graphics tiles for gear tiles gears -- a table or {x=?,y=?} of connection points for machines 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) + --NB: following table is 0 indexed + tables of 4 numbers (tile,fore,back,bright,graphics tile, overlay tile, sign tile, item tile) OR + {x= y= + 4 to 8 numbers like in first case} -- this generates full frame even, useful for animations that change little (1-2 tiles) + frameLength -- how many ticks does one frame take OR + isMechanical -- a bool that says to try to match to mechanical system (i.e. animate only when powered) } ]] _registeredStuff={} +--cache graphics tiles for mechanical gears +local graphics_cache +function reload_graphics_cache( ) + graphics_cache={} + graphics_cache[1]=dfhack.screen.findGraphicsTile('AXLES_GEARS',0,2) + graphics_cache[2]=dfhack.screen.findGraphicsTile('AXLES_GEARS',1,2) +end + + local function unregall(state) if state==SC_WORLD_UNLOADED then + graphics_cache=nil onUpdateAction._library=nil dfhack.onStateChange.building_hacks= nil _registeredStuff={} @@ -52,21 +63,12 @@ local function registerUpdateAction(shopId,callback) onUpdateAction._library=onUpdateLocal dfhack.onStateChange.building_hacks=unregall end +--take in tiles with {x=?, y=? ,...} and output a table flat sparse 31x31 table 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 + for k,v in ipairs(tiles) do + ret[v.x+v.y*31]=v end return ret end @@ -98,7 +100,8 @@ end local function lookup_color( shop_def,x,y,stage ) return shop_def.tile_color[0][stage][x][y],shop_def.tile_color[1][stage][x][y],shop_def.tile_color[2][stage][x][y] end -local function processFramesAuto( shop_def ,gears) --adds frames for all gear icons and inverted gear icons +--adds frames for all gear icons and inverted gear icons +local function processFramesAuto( shop_def ,gears,auto_graphics) local w,h=shop_def.dim_x,shop_def.dim_y local frames={{},{}} --two frames only local stage=shop_def.build_stages @@ -116,6 +119,12 @@ local function processFramesAuto( shop_def ,gears) --adds frames for all gear ic table.insert(frames[1],{x=v.x,y=v.y,tile,lookup_color(shop_def,v.x,v.y,stage)}) table.insert(frames[2],{x=v.x,y=v.y,tile_inv,lookup_color(shop_def,v.x,v.y,stage)}) + + --insert default gear graphics if auto graphics is on + if auto_graphics then + frames[1][#frames[1]][5]=graphics_cache[1] + frames[2][#frames[2]][5]=graphics_cache[2] + end end for frame_id,frame in ipairs(frames) do @@ -124,6 +133,11 @@ local function processFramesAuto( shop_def ,gears) --adds frames for all gear ic return frames end function registerBuilding(args) + + if graphics_cache==nil then + reload_graphics_cache() + end + local shop_def=findCustomWorkshop(args.name) local shop_id=shop_def.id --misc @@ -170,10 +184,11 @@ function registerBuilding(args) end end gears=findGears(shop_def) - frames=processFramesAuto(shop_def,gears) + frames=processFramesAuto(shop_def,gears,args.auto_graphics) end - + --finally call the c++ api addBuilding(shop_id,fix_impassible,consume,produce,needs_power,gears,updateSkip,frames,frameLength,roomSubset) + end return _ENV From c67d6853b6879da97fffeb5aa427862482de1116 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 19 Mar 2024 17:46:46 +0200 Subject: [PATCH 05/38] whitespace fixes --- plugins/building-hacks.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 8003e29b6a..2bf16fb2b3 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -341,16 +341,16 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) luaL_checktype(L,stack_pos,LUA_TTABLE); int frame_index = 1; - + while (lua_geti(L,stack_pos,frame_index) != LUA_TNIL) { //get frame[i] luaL_checktype(L,-1,LUA_TTABLE); //ensure that it's a table std::vector frame(max_idx); - + for (int idx = 0; idx < max_idx; idx++) { auto& t = frame[idx]; lua_geti(L, -1, idx); //get tile at idx i.e. frame[i][idx] where idx=x+y*31 - + if (lua_isnil(L, -1))//allow sparse indexing { lua_pop(L, 1); //pop current tile (nil in this case) From fe0bc2fe91b947b8bb60bbff92f7e8943e220471 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 19 Mar 2024 17:48:50 +0200 Subject: [PATCH 06/38] Fix uint32_t getting set to -1 --- plugins/building-hacks.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 2bf16fb2b3..00353bb166 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -22,13 +22,14 @@ #include "modules/Buildings.h" #include - +#include using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("building-hacks"); REQUIRE_GLOBAL(world); +constexpr uint32_t invalid_tile = std::numeric_limits::max(); struct graphic_tile //could do just 31x31 and be done, but it's nicer to have flexible imho. { int16_t tile=-1; //originally uint8_t but we need to indicate non-animated tiles @@ -36,11 +37,11 @@ struct graphic_tile //could do just 31x31 and be done, but it's nicer to have fl int8_t back; int8_t bright; //index of texpos - uint32_t graphics_tile = -1; - uint32_t overlay_tile = -1; - uint32_t item_tile = -1; + uint32_t graphics_tile = invalid_tile; + uint32_t overlay_tile = invalid_tile; + uint32_t item_tile = invalid_tile; //only for first line - uint32_t signpost_tile = -1; + uint32_t signpost_tile = invalid_tile; }; struct workshop_hack_data { @@ -302,14 +303,14 @@ struct work_hook : df::building_workshopst{ db->bright[tx][ty]= cf.bright; db->fore[tx][ty]= cf.fore; } - if (cf.graphics_tile != -1) + if (cf.graphics_tile != invalid_tile) db->building_one_texpos[tx][ty] = cf.graphics_tile; - if (cf.overlay_tile != -1) + if (cf.overlay_tile != invalid_tile) db->building_two_texpos[tx][ty] = cf.overlay_tile; - if (cf.item_tile != -1) + if (cf.item_tile != invalid_tile) db->item_texpos[tx][ty] = cf.item_tile; //only first line has signpost graphics - if (cf.item_tile != -1 && ty==0) + if (cf.item_tile != invalid_tile && ty==0) db->signpost_texpos[tx] = cf.signpost_tile; } } From 2cdeee8faee72f04a3e27a7b9bf969956af8ed7e Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 20 Mar 2024 22:37:23 +0200 Subject: [PATCH 07/38] Add event to vmethod interpose setTriggerState for lever/trap plate linking support --- plugins/building-hacks.cpp | 16 +++++++++++++--- plugins/lua/building-hacks.lua | 3 +++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 00353bb166..909c08040a 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -62,11 +62,12 @@ struct workshop_hack_data 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_NH_1(onUpdateAction,df::building_workshopst*); +DEFINE_LUA_EVENT_NH_2(onSetTriggerState,df::building_workshopst*,int32_t); -DEFINE_LUA_EVENT_1(onUpdateAction,handle_update_action,df::building_workshopst*); -DFHACK_PLUGIN_LUA_EVENTS { +DFHACK_PLUGIN_LUA_EVENTS{ DFHACK_LUA_EVENT(onUpdateAction), + DFHACK_LUA_EVENT(onSetTriggerState), DFHACK_LUA_END }; @@ -265,6 +266,13 @@ struct work_hook : df::building_workshopst{ } INTERPOSE_NEXT(updateAction)(); } + DEFINE_VMETHOD_INTERPOSE(void, setTriggerState,(int32_t state)) + { + CoreSuspendClaimer suspend; + color_ostream_proxy out(Core::getInstance().getConsole()); + onSetTriggerState(out, this,state); + INTERPOSE_NEXT(setTriggerState)(state); //pretty sure default workshop has nothing related to this, but to be future proof lets do it like this + } DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (uint32_t curtick,df::building_drawbuffer *db, int16_t z_offset)) { INTERPOSE_NEXT(drawBuilding)(curtick,db, z_offset); @@ -327,6 +335,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(work_hook, canConnectToMachine); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, isUnpowered); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, canBeRoomSubset); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, updateAction); +IMPLEMENT_VMETHOD_INTERPOSE(work_hook, setTriggerState); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, drawBuilding); @@ -499,6 +508,7 @@ static void enable_hooks(bool enable) INTERPOSE_HOOK(work_hook,canBeRoomSubset).apply(enable); //update n render INTERPOSE_HOOK(work_hook,updateAction).apply(enable); + INTERPOSE_HOOK(work_hook,setTriggerState).apply(enable); INTERPOSE_HOOK(work_hook,drawBuilding).apply(enable); } DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index c1a3172649..d329018bbd 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -72,6 +72,7 @@ local function generateFrame(tiles,w,h) end return ret end +--convert frames to flat arrays if needed 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 @@ -81,6 +82,7 @@ local function processFrames(shop_def,frames) end return frames end +--locate gears on the workshop from the raws definition local function findGears( shop_def ) --finds positions of all gears and inverted gears local w,h=shop_def.dim_x,shop_def.dim_y local stage=shop_def.build_stages @@ -97,6 +99,7 @@ local function findGears( shop_def ) --finds positions of all gears and inverted end return ret end +--helper for reading tile color info from raws local function lookup_color( shop_def,x,y,stage ) return shop_def.tile_color[0][stage][x][y],shop_def.tile_color[1][stage][x][y],shop_def.tile_color[2][stage][x][y] end From 010928c11114c2f69d882d104722f45d3def7794 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 24 Mar 2024 14:19:51 +0200 Subject: [PATCH 08/38] big refactor and api cleanup --- plugins/building-hacks.cpp | 168 +++++++++++++++++++++++---------- plugins/lua/building-hacks.lua | 105 ++++++--------------- 2 files changed, 145 insertions(+), 128 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 909c08040a..fc563181f4 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -19,6 +19,7 @@ #include "df/tile_building_occ.h" #include "df/building_drawbuffer.h" #include "df/general_ref_creaturest.h" // needed for power information storage +#include "df/building_def_workshopst.h" #include "modules/Buildings.h" #include @@ -45,19 +46,19 @@ 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; + bool impassible_fix = false; //machine stuff + bool is_machine = false; df::machine_tile_set connections; df::power_info powerInfo; bool needs_power; //animation std::vector > frames; - bool machine_timing; //6 frames used in vanilla + bool machine_timing=false; //6 frames used in vanilla int frame_skip; // e.g. 2 means have to ticks between frames //updateCallback: - int skip_updates; - int room_subset; //0 no, 1 yes, -1 default + int skip_updates=0; + int room_subset=-1; //0 no, 1 yes, -1 default }; typedef std::map workshops_data_t; workshops_data_t hacked_workshops; @@ -93,6 +94,7 @@ struct work_hook : df::building_workshopst{ if (workshop_hack_data* def = find_def()) { df::general_ref_creaturest* ref = static_cast(DFHack::Buildings::getGeneralRef(this, general_ref_type::CREATURE)); + //try getting ref, if not return from definition if (ref) { info->produced = ref->unk_1; @@ -105,7 +107,6 @@ struct work_hook : df::building_workshopst{ info->consumed = def->powerInfo.consumed; return true; } - //try getting ref, if not return from def } return false; } @@ -123,6 +124,7 @@ struct work_hook : df::building_workshopst{ } } df::general_ref_creaturest* ref = static_cast(DFHack::Buildings::getGeneralRef(this, general_ref_type::CREATURE)); + //if we have a setting then update it, else create a new ref for dynamic power tracking if (ref) { ref->unk_1 = produced; @@ -148,7 +150,8 @@ struct work_hook : df::building_workshopst{ DEFINE_VMETHOD_INTERPOSE(void, getPowerInfo, (df::power_info *info)) { - if (find_def()) + auto def = find_def(); + if (def && def->is_machine) { df::power_info power; get_current_power(info); @@ -158,7 +161,8 @@ struct work_hook : df::building_workshopst{ } DEFINE_VMETHOD_INTERPOSE(df::machine_info*, getMachineInfo, ()) { - if (find_def()) + auto def = find_def(); + if (def && def->is_machine) return &machine; return INTERPOSE_NEXT(getMachineInfo)(); @@ -175,7 +179,8 @@ struct work_hook : df::building_workshopst{ } DEFINE_VMETHOD_INTERPOSE(void, categorize, (bool free)) { - if (find_def()) + auto def = find_def(); + if (def && def->is_machine) { auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE]; insert_into_vector(vec, &df::building::id, (df::building*)this); @@ -186,7 +191,8 @@ struct work_hook : df::building_workshopst{ DEFINE_VMETHOD_INTERPOSE(void, uncategorize, ()) { - if (find_def()) + auto def = find_def(); + if (def && def->is_machine) { auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE]; erase_from_vector(vec, &df::building::id, id); @@ -196,8 +202,10 @@ struct work_hook : df::building_workshopst{ } DEFINE_VMETHOD_INTERPOSE(bool, canConnectToMachine, (df::machine_tile_set *info)) { - if (auto def = find_def()) + auto def = find_def(); + if (def && def->is_machine) { + int real_cx = centerx, real_cy = centery; bool ok = false; @@ -339,7 +347,26 @@ IMPLEMENT_VMETHOD_INTERPOSE(work_hook, setTriggerState); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, drawBuilding); - +int get_workshop_type(lua_State* L,int arg) +{ + size_t len; + int is_num; + int type; + type=lua_tointegerx(L, arg, &is_num); + if (is_num) + { + return type; + } + auto str = lua_tolstring(L, arg, &len); + const auto& raws = world->raws.buildings.workshops; + for(size_t i=0;icode == str) + return raws[i]->id; + } + luaL_argerror(L, arg, "expected int or string workshop id"); + return 0; +} void clear_mapping() { hacked_workshops.clear(); @@ -413,58 +440,91 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) return ; } -//arguments: 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) -static int addBuilding(lua_State* L) + +//fixImpassible(workshop_type,bool) - changes how impassible tiles work with liquids. False - default behaviour. True - blocks liquids. +static int fixImpassible(lua_State* L) +{ + int workshop_type = get_workshop_type(L, 1); + bool impassible_setting = lua_toboolean(L, 2); + + auto& def = hacked_workshops[workshop_type]; + def.impassible_fix = impassible_setting; + return 0; +} +//setMachineInfo(workshop_type,bool needs_power,int power_consumed=0,int power_produced=0,table [x=int,y=int] connection_points) -setups and enables machine (i.e. connected to gears, and co) behaviour of the building +static int setMachineInfo(lua_State* L) { - workshop_hack_data newDefinition; - newDefinition.myType=luaL_checkint(L,1); - newDefinition.impassible_fix=luaL_checkint(L,2); - newDefinition.powerInfo.consumed=luaL_checkint(L,3); - newDefinition.powerInfo.produced=luaL_checkint(L,4); - newDefinition.needs_power = luaL_optinteger(L, 5, 1); + int workshop_type = get_workshop_type(L, 1); + auto& def = hacked_workshops[workshop_type]; + def.is_machine = true; + + def.needs_power = lua_toboolean(L, 2); + def.powerInfo.consumed = luaL_optinteger(L, 3,0); + def.powerInfo.produced = luaL_optinteger(L, 4,0); + + //table of machine connection points - luaL_checktype(L,6,LUA_TTABLE); - lua_pushvalue(L,6); + luaL_checktype(L, 5, LUA_TTABLE); + lua_pushvalue(L, 5); 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); + 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); df::machine_conn_modes modes; modes.whole = -1; - newDefinition.connections.can_connect.push_back(modes);//TODO add this too... - newDefinition.connections.tiles.push_back(df::coord(x,y,0)); + def.connections.can_connect.push_back(modes);//TODO add this too... + def.connections.tiles.push_back(df::coord(x, y, 0)); - lua_pop(L,1); + lua_pop(L, 1); } - lua_pop(L,1); - //updates - newDefinition.skip_updates=luaL_optinteger(L,7,0); + lua_pop(L, 1); + return 0; +} +//setUpdateSkip(workshop_type,int skip_frames) - skips frames to lower onupdate event call rate, 0 to disable +static int setUpdateSkip(lua_State* L) +{ + int workshop_type = get_workshop_type(L, 1); + auto& def = hacked_workshops[workshop_type]; + + def.skip_updates = luaL_optinteger(L, 2, 0); + return 0; +} +//setAnimationInfo(workshop_type,table frames, [frame_skip]) - define animation and it's timing. If frame_skip is not set or set to -1, it will use machine timing (i.e. like gears/axels etc) +static int setAnimationInfo(lua_State* L) +{ + int workshop_type = get_workshop_type(L, 1); + auto& def = hacked_workshops[workshop_type]; //animation - if(!lua_isnil(L,8)) - { - loadFrames(L,newDefinition,8); - newDefinition.frame_skip=luaL_optinteger(L,9,-1); - if(newDefinition.frame_skip==0) - newDefinition.frame_skip=1; - if(newDefinition.frame_skip<0) - newDefinition.machine_timing=true; - else - newDefinition.machine_timing=false; - } - newDefinition.room_subset=luaL_optinteger(L,10,-1); - hacked_workshops[newDefinition.myType]=newDefinition; + loadFrames(L, def, 2); + def.frame_skip = luaL_optinteger(L, 3, -1); + if (def.frame_skip == 0) + def.frame_skip = 1; + if (def.frame_skip < 0) + def.machine_timing = true; + else + def.machine_timing = false; + return 0; +} +//setOwnableBuilding(workshop_type,bool is_ownable) +static int setOwnableBuilding(lua_State* L) +{ + int workshop_type = get_workshop_type(L, 1); + bool room_subset = lua_toboolean(L, 2); + + auto& def = hacked_workshops[workshop_type]; + def.room_subset = room_subset; return 0; } static void setPower(df::building_workshopst* workshop, int power_produced, int power_consumed) { work_hook* ptr = static_cast(workshop); - if (ptr->find_def()) // check if it's really hacked workshop + auto def = ptr->find_def(); + if (def && def->is_machine) // check if it's really hacked workshop { ptr->set_current_power(power_produced, power_consumed); } @@ -475,7 +535,8 @@ static int getPower(lua_State*L) work_hook* ptr = static_cast(workshop); if (!ptr) return 0; - if (ptr->find_def()) // check if it's really hacked workshop + auto def = ptr->find_def(); + if (def && def->is_machine) // check if it's really hacked workshop { df::power_info info; ptr->get_current_power(&info); @@ -490,8 +551,13 @@ DFHACK_PLUGIN_LUA_FUNCTIONS{ DFHACK_LUA_END }; DFHACK_PLUGIN_LUA_COMMANDS{ - DFHACK_LUA_COMMAND(addBuilding), + DFHACK_LUA_COMMAND(getPower), + DFHACK_LUA_COMMAND(setOwnableBuilding), + DFHACK_LUA_COMMAND(setAnimationInfo), + DFHACK_LUA_COMMAND(setUpdateSkip), + DFHACK_LUA_COMMAND(setMachineInfo), + DFHACK_LUA_COMMAND(fixImpassible), DFHACK_LUA_END }; static void enable_hooks(bool enable) diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index d329018bbd..0347920479 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -1,31 +1,19 @@ 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)) - getPower(bld) -- 2 or 0 returns, produced and consumed - setPower(bld,produced, consumed) + setOwnableBuilding(workshop_type,bool) + setAnimationInfo(workshop_type,table frames,int frameskip=-1) + setUpdateSkip(workshop_type,int=0) + setMachineInfo(workshop_type,bool need_power,int consume=0,int produce=0,table connection_points) + fixImpassible(workshop_type,bool) + getPower(building) -- 2 or 0 returns, produced and consumed + setPower(building,produced, consumed) 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 - needs_power -- needs power to be able to add jobs - action -- a table of number (how much ticks to skip) and a function which gets called on shop update - canBeRoomSubset -- room is considered in to be part of the building defined by chairs etc... - auto_gears -- find the gears automatically and animate them - auto_graphics -- insert gear graphics tiles for gear tiles - gears -- a table or {x=?,y=?} of connection points for machines - animate -- a table of - frames -- a table of - --NB: following table is 0 indexed - tables of 4 numbers (tile,fore,back,bright,graphics tile, overlay tile, sign tile, item tile) OR - {x= y= + 4 to 8 numbers like in first case} -- this generates full frame even, useful for animations that change little (1-2 tiles) - frameLength -- how many ticks does one frame take OR - isMechanical -- a bool that says to try to match to mechanical system (i.e. animate only when powered) - } + setMachineInfoAuto(name,int consume,int produce,bool need_power) + setAnimationInfoAuto(name,bool make_graphics_too) + setOnUpdate(name,int interval,callback) ]] + _registeredStuff={} --cache graphics tiles for mechanical gears local graphics_cache @@ -35,7 +23,7 @@ function reload_graphics_cache( ) graphics_cache[2]=dfhack.screen.findGraphicsTile('AXLES_GEARS',1,2) end - +--on world unload unreg callbacks and invalidate cache local function unregall(state) if state==SC_WORLD_UNLOADED then graphics_cache=nil @@ -44,6 +32,7 @@ local function unregall(state) _registeredStuff={} end end + local function onUpdateLocal(workshop) local f=_registeredStuff[workshop:getCustomType()] if f then @@ -135,63 +124,25 @@ local function processFramesAuto( shop_def ,gears,auto_graphics) end return frames end -function registerBuilding(args) - +function setMachineInfoAuto( name,need_power,consume,produce) + local shop_def=findCustomWorkshop(name) + local gears=findGears(shop_def) + setMachineInfo(name,need_power,consume,produce,gears) +end +function setAnimationInfoAuto( name,make_graphics_too,frame_length ) if graphics_cache==nil then reload_graphics_cache() end - - local shop_def=findCustomWorkshop(args.name) + local shop_def=findCustomWorkshop(name) + local gears=findGears(shop_def) + local frames=processFramesAuto(shop_def,gears,make_graphics_too) + setAnimationInfo(shop_def,frames,frame_length) +end +function setOnUpdate(name,interval,callback) + local shop_def=findCustomWorkshop(name) local shop_id=shop_def.id - --misc - local fix_impassible - if args.fix_impassible then - fix_impassible=1 - else - fix_impassible=0 - end - local roomSubset=args.canBeRoomSubset or -1 - --power - local consume=args.consume or 0 - local produce=args.produce or 0 - local needs_power=args.needs_power or 1 - local auto_gears=args.auto_gears or false - local updateSkip=0 - local action=args.action --could be nil - if action~=nil then - updateSkip=action[1] - registerUpdateAction(shop_id,action[2]) - end - --animations and connections next: - local gears - local frames - - local frameLength - local animate=args.animate - if not auto_gears then - gears=args.gears or {} - frameLength=1 - if animate~=nil then - frameLength=animate.frameLength - if animate.isMechanical then - frameLength=-1 - end - frames=processFrames(shop_def,animate.frames) - end - else - frameLength=-1 - if animate~=nil then - frameLength=animate.frameLength or frameLength - if animate.isMechanical then - frameLength=-1 - end - end - gears=findGears(shop_def) - frames=processFramesAuto(shop_def,gears,args.auto_graphics) - end - --finally call the c++ api - addBuilding(shop_id,fix_impassible,consume,produce,needs_power,gears,updateSkip,frames,frameLength,roomSubset) - + setUpdateSkip(name,interval) + registerUpdateAction(shop_id,callback) end return _ENV From 3138d3a7ed3fbcbb2d7ba4fa5ceb01f0564eaf25 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 24 Mar 2024 16:37:47 +0200 Subject: [PATCH 09/38] Doc update --- docs/dev/Lua API.rst | 168 ++++++++++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 64 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 64bea03c3a..a8afd2af4d 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6520,8 +6520,8 @@ The names of the functions are also available as the keys of the 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. +This plugin overwrites some methods in workshop df class to change and extend workshop +functionality. See exported function list for details. .. contents:: :local: @@ -6529,78 +6529,118 @@ possible. Although plugin export a function it's recommended to use lua decorate Functions --------- -``registerBuilding(table)`` where table must contain name, as a workshop raw name, -the rest are optional: - - :name: - custom workshop id e.g., ``SOAPMAKER`` - - .. note:: this is the only mandatory field. - - :fix_impassible: - if true make impassable tiles impassable to liquids too - :consume: - how much machine power is needed to work. - Disables reactions if not supplied enough and ``needs_power==1`` - :produce: - how much machine power is produced. - :needs_power: - if produced in network < consumed stop working, default true - :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 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) - - :canBeRoomSubset: - a flag if this building can be counted in room. 1 means it can, - 0 means it can't and -1 default building behaviour - :auto_gears: - a flag that automatically fills up gears and animations. - It looks over the building definition for gear icons and maps them. - - Animate table also might contain: - - :frameLength: - 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) - -``getPower(building)`` returns two number - produced and consumed power if building can be -modified and returns nothing otherwise - -``setPower(building,produced,consumed)`` sets current power production and -consumption for a building. +* ``setOwnableBuilding(workshop_type, enable)`` + + Set workshop to be included in zones (such as bedroom or inn). + + :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :enable: boolean value to enable or disable this functionality + +* ``fixImpassible(workshop_type, enable)`` + + Set workshop non walkable tiles to also block liquids (i.e. water and magma). + + :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :enable: boolean value to enable or disable this functionality + +* ``setMachineInfo(workshop_type, needs_power, power_consumed, power_produced, connection_points)`` + + Setup and enable machine-like functionality for the workshop. Note: due to implementation limitations + workshop only connects to other machines if the other machine is build later than this one. + + :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :needs_power: only function if it has sufficient power + :power_consumed: building consumes this amount of power + :power_produced: output this amount of power + :connection_points: a table of ``{x=?,y=?}`` points that can connect to other machines + +* ``setMachineInfoAuto(workshop_type, needs_power, power_consumed, power_produced)`` + + Same as ``setMachineInfo`` but fills out the ``connection_points`` table from raws placing connection + points on tiles which have the gear tile. + +* ``setAnimationInfo(workshop_type, frames, frame_skip)`` + + Animate workshop by replacing displayed tiles (or graphical tiles). There are two ways this works: + if ``frame_skip>=0`` then it shows each frame for ``frame_skip`` of frames or if ``frame_skip<0`` + Frames are synchronized to the machine this building is connected to. + + :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :frames: table of frames. Each frame is sparse flat table with ids from ``0`` to ``31*31-1``. + Each frame tile is table of integers from 4 to 8 members long. Tile members are as + follow: tile, foreground color, background color, bright, graphics tile, overlay tile, + signpost tile, item tile. + :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames + are synchronized with machine animation. + +* ``setAnimationInfoAuto(name, make_graphics_too, frame_skip)`` + + Animate workshop as with function above but generate frames automatically. This works by finding + tiles which have gears and animating them with alternating gear tiles. + + :name: custom workshop string id e.g. ``SOAPMAKER`` + :make_graphics_too: replace same tiles in graphics mode with tiles from vanilla df mechanism + :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames + are synchronized with machine animation. + +* ``setOnUpdate(name,interval,callback)`` + + Setup callback to be called every ``interval`` of ticks for each building of this type. Note: low interval + numbers and/or many workshops that use this might reduce DF performance. + + :name: custom workshop string id e.g. ``SOAPMAKER`` + :interval: how many ticks to skip between event triggers + :callback: function to call + +* ``getPower(building)`` + + Returns two number - produced and consumed power if building can be modified and returns nothing otherwise. + + :building: specific workshop that produces or consumes power + +* ``setPower(building,power_consumed, power_produced)`` + + Sets current power production and consumption for a building. Can be used to make buildings that + dynamically change power consumption and production. + + :building: specific workshop that produces or consumes power + :power_consumed: set building to consume this amount of power + :power_produced: output this amount of power + + +Events +------ + +This module exports two events. However only one is documented here and is intended to be used directly. To use +``onUpdateAction`` instead call ``setOnUpdate`` function. + +* ``onSetTriggerState(workshop,state)`` + + Notify when building is triggered from linked lever or trap. + + :workshop: object of type ``df.building_workshopst`` that is triggered. + :state: integer value of new state. 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 conn. 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 + local bld=require('plugins.building-hacks') + --work only powered, consume 15 power and one connection point at 0,0 + bld.setMachineInfo("BONE_GRINDER",true,15,0,{{x=0,y=0}}) + --set animation to switch between gear tiles at 0,0 + bld.setAnimationInfo("BONE_GRINDER",{ + {[0]={42,7,0,0}}, --first frame, 1 changed tile + {[0]={15,7,0,0}} -- second frame, same } - } + ) Or with auto_gears:: - require('plugins.building-hacks').registerBuilding{name="BONE_GRINDER", - consume=15, - auto_gears=true - } + local bld=require('plugins.building-hacks') + bld.setMachineInfoAuto("BONE_GRINDER",true,15) + bld.setAnimationInfoAuto("BONE_GRINDER",true) buildingplan ============ From cd5959539f4ae0918baee5e1412af2f78765d0d0 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 24 Mar 2024 16:38:13 +0200 Subject: [PATCH 10/38] Fix bad string compare --- plugins/building-hacks.cpp | 12 ++++++++---- plugins/lua/building-hacks.lua | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index fc563181f4..80ef84ef5c 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -358,11 +358,15 @@ int get_workshop_type(lua_State* L,int arg) return type; } auto str = lua_tolstring(L, arg, &len); - const auto& raws = world->raws.buildings.workshops; - for(size_t i=0;icode == str) - return raws[i]->id; + std::string lua_name(str, len); + const auto& raws = world->raws.buildings.workshops; + for (size_t i = 0; i < raws.size(); i++) + { + if (raws[i]->code == lua_name) + return raws[i]->id; + } } luaL_argerror(L, arg, "expected int or string workshop id"); return 0; diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index 0347920479..d38b682317 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -136,7 +136,7 @@ function setAnimationInfoAuto( name,make_graphics_too,frame_length ) local shop_def=findCustomWorkshop(name) local gears=findGears(shop_def) local frames=processFramesAuto(shop_def,gears,make_graphics_too) - setAnimationInfo(shop_def,frames,frame_length) + setAnimationInfo(name,frames,frame_length) end function setOnUpdate(name,interval,callback) local shop_def=findCustomWorkshop(name) From 16bc0b6ff6e330ac363c2df6925662a882a4b805 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 24 Mar 2024 16:39:23 +0200 Subject: [PATCH 11/38] whitespace fixes --- plugins/building-hacks.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 80ef84ef5c..e110e9c261 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -450,7 +450,7 @@ static int fixImpassible(lua_State* L) { int workshop_type = get_workshop_type(L, 1); bool impassible_setting = lua_toboolean(L, 2); - + auto& def = hacked_workshops[workshop_type]; def.impassible_fix = impassible_setting; return 0; @@ -465,8 +465,7 @@ static int setMachineInfo(lua_State* L) def.needs_power = lua_toboolean(L, 2); def.powerInfo.consumed = luaL_optinteger(L, 3,0); def.powerInfo.produced = luaL_optinteger(L, 4,0); - - + //table of machine connection points luaL_checktype(L, 5, LUA_TTABLE); lua_pushvalue(L, 5); @@ -494,7 +493,7 @@ static int setUpdateSkip(lua_State* L) { int workshop_type = get_workshop_type(L, 1); auto& def = hacked_workshops[workshop_type]; - + def.skip_updates = luaL_optinteger(L, 2, 0); return 0; } @@ -555,7 +554,6 @@ DFHACK_PLUGIN_LUA_FUNCTIONS{ DFHACK_LUA_END }; DFHACK_PLUGIN_LUA_COMMANDS{ - DFHACK_LUA_COMMAND(getPower), DFHACK_LUA_COMMAND(setOwnableBuilding), DFHACK_LUA_COMMAND(setAnimationInfo), From bd09cb59052f2581a333c52ed21b910b45b30c4c Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 25 Mar 2024 18:18:54 +0200 Subject: [PATCH 12/38] Add error on failing to find gears for auto-machines and auto-animate --- plugins/lua/building-hacks.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index d38b682317..461774d9d7 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -86,6 +86,9 @@ local function findGears( shop_def ) --finds positions of all gears and inverted end end end + if #ret==0 then + error(string.format("Could not find gears in a workshop (%s) that was marked for auto-gear finding",shop_def.code)) + end return ret end --helper for reading tile color info from raws From 60e21b9cd67bbae99fc690102d637dcf83245e98 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 29 Mar 2024 11:03:58 +0200 Subject: [PATCH 13/38] Add a way to change graphics tiles when auto generating connection points and animation --- docs/dev/Lua API.rst | 8 +++++--- plugins/lua/building-hacks.lua | 28 +++++++++++++++------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index a8afd2af4d..ae4846314e 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6554,10 +6554,11 @@ Functions :power_produced: output this amount of power :connection_points: a table of ``{x=?,y=?}`` points that can connect to other machines -* ``setMachineInfoAuto(workshop_type, needs_power, power_consumed, power_produced)`` +* ``setMachineInfoAuto(workshop_type, needs_power, power_consumed, power_produced, gear_tiles)`` Same as ``setMachineInfo`` but fills out the ``connection_points`` table from raws placing connection - points on tiles which have the gear tile. + points on tiles which have the gear tile. ``gear_tiles`` is an optional array of two tiles that are + counted as gears in the workshop ascii tile raws. * ``setAnimationInfo(workshop_type, frames, frame_skip)`` @@ -6573,7 +6574,7 @@ Functions :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames are synchronized with machine animation. -* ``setAnimationInfoAuto(name, make_graphics_too, frame_skip)`` +* ``setAnimationInfoAuto(name, make_graphics_too, frame_skip, gear_tiles)`` Animate workshop as with function above but generate frames automatically. This works by finding tiles which have gears and animating them with alternating gear tiles. @@ -6582,6 +6583,7 @@ Functions :make_graphics_too: replace same tiles in graphics mode with tiles from vanilla df mechanism :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames are synchronized with machine animation. + :gear_tiles: Optional array of 2 or 4 indexes. First two define ascii tiles and next two graphics tiles * ``setOnUpdate(name,interval,callback)`` diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index 461774d9d7..6b3015cba4 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -72,7 +72,8 @@ local function processFrames(shop_def,frames) return frames end --locate gears on the workshop from the raws definition -local function findGears( shop_def ) --finds positions of all gears and inverted gears +local function findGears( shop_def ,gear_tiles) --finds positions of all gears and inverted gears + gear_tiles=gear_tiles or {42,15} local w,h=shop_def.dim_x,shop_def.dim_y local stage=shop_def.build_stages local ret={} @@ -96,7 +97,8 @@ local function lookup_color( shop_def,x,y,stage ) return shop_def.tile_color[0][stage][x][y],shop_def.tile_color[1][stage][x][y],shop_def.tile_color[2][stage][x][y] end --adds frames for all gear icons and inverted gear icons -local function processFramesAuto( shop_def ,gears,auto_graphics) +local function processFramesAuto( shop_def ,gears,auto_graphics,gear_tiles) + gear_tiles=gear_tiles or {42,15,graphics_cache[1],graphics_cache[2]} local w,h=shop_def.dim_x,shop_def.dim_y local frames={{},{}} --two frames only local stage=shop_def.build_stages @@ -105,11 +107,11 @@ local function processFramesAuto( shop_def ,gears,auto_graphics) local tile,tile_inv if v.inverted then - tile=42 - tile_inv=15 + tile=gear_tiles[1] + tile_inv=gear_tiles[2] else - tile=15 - tile_inv=42 + tile=gear_tiles[2] + tile_inv=gear_tiles[1] end table.insert(frames[1],{x=v.x,y=v.y,tile,lookup_color(shop_def,v.x,v.y,stage)}) @@ -117,8 +119,8 @@ local function processFramesAuto( shop_def ,gears,auto_graphics) --insert default gear graphics if auto graphics is on if auto_graphics then - frames[1][#frames[1]][5]=graphics_cache[1] - frames[2][#frames[2]][5]=graphics_cache[2] + frames[1][#frames[1]][5]=gear_tiles[3] + frames[2][#frames[2]][5]=gear_tiles[4] end end @@ -127,18 +129,18 @@ local function processFramesAuto( shop_def ,gears,auto_graphics) end return frames end -function setMachineInfoAuto( name,need_power,consume,produce) +function setMachineInfoAuto( name,need_power,consume,produce,gear_tiles) local shop_def=findCustomWorkshop(name) - local gears=findGears(shop_def) + local gears=findGears(shop_def,gear_tiles) setMachineInfo(name,need_power,consume,produce,gears) end -function setAnimationInfoAuto( name,make_graphics_too,frame_length ) +function setAnimationInfoAuto( name,make_graphics_too,frame_length,gear_tiles ) if graphics_cache==nil then reload_graphics_cache() end local shop_def=findCustomWorkshop(name) - local gears=findGears(shop_def) - local frames=processFramesAuto(shop_def,gears,make_graphics_too) + local gears=findGears(shop_def,gear_tiles) + local frames=processFramesAuto(shop_def,gears,make_graphics_too,gear_tiles) setAnimationInfo(name,frames,frame_length) end function setOnUpdate(name,interval,callback) From 796f4802308eafa6e6b9e4c669c658250184bfad Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 29 Mar 2024 12:13:57 +0200 Subject: [PATCH 14/38] Fix repeated calling animation creating more and more frames --- plugins/building-hacks.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index e110e9c261..3085d824b6 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -382,6 +382,7 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) luaL_checktype(L,stack_pos,LUA_TTABLE); int frame_index = 1; + def.frames.clear(); while (lua_geti(L,stack_pos,frame_index) != LUA_TNIL) { //get frame[i] luaL_checktype(L,-1,LUA_TTABLE); //ensure that it's a table From 11ef74a4225d0a5a6bed911d4aa8e865b48816e0 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 29 Mar 2024 13:34:27 +0200 Subject: [PATCH 15/38] Fix graphics bug --- plugins/building-hacks.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 3085d824b6..4a7a0f912a 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -436,7 +436,6 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) lua_pop(L, 1); //pop current tile } - frame.push_back(t); } def.frames.push_back(frame); frame_index++; From 61920e3fa0d766f516541770f0b8a9830c136d67 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 1 Apr 2024 12:56:05 +0300 Subject: [PATCH 16/38] Update docs/dev/Lua API.rst Co-authored-by: Myk --- docs/dev/Lua API.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ae4846314e..787202ef6c 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6520,8 +6520,7 @@ The names of the functions are also available as the keys of the building-hacks ============== -This plugin overwrites some methods in workshop df class to change and extend workshop -functionality. See exported function list for details. +This plugin extends DF workshops to support custom powered buildings. .. contents:: :local: From 73f2d23dd7d144515517cf282476e4191c5a5fa1 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 2 Apr 2024 16:53:52 +0300 Subject: [PATCH 17/38] Switch map to unordered map --- plugins/building-hacks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 4a7a0f912a..f9dfd6575c 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -22,7 +22,7 @@ #include "df/building_def_workshopst.h" #include "modules/Buildings.h" -#include +#include #include using namespace DFHack; using namespace df::enums; @@ -60,7 +60,7 @@ struct workshop_hack_data int skip_updates=0; int room_subset=-1; //0 no, 1 yes, -1 default }; -typedef std::map workshops_data_t; +typedef std::unordered_map workshops_data_t; workshops_data_t hacked_workshops; DEFINE_LUA_EVENT_NH_1(onUpdateAction,df::building_workshopst*); From 3cd0bc89f503da25e35b161da8fcd09e76181b1d Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 2 Apr 2024 16:55:51 +0300 Subject: [PATCH 18/38] Just use 0 as invalid tile marker. It's used in df code like that --- plugins/building-hacks.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index f9dfd6575c..d1b37a67ff 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -30,7 +30,7 @@ using namespace df::enums; DFHACK_PLUGIN("building-hacks"); REQUIRE_GLOBAL(world); -constexpr uint32_t invalid_tile = std::numeric_limits::max(); +constexpr uint32_t invalid_tile = 0; struct graphic_tile //could do just 31x31 and be done, but it's nicer to have flexible imho. { int16_t tile=-1; //originally uint8_t but we need to indicate non-animated tiles @@ -419,19 +419,19 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) lua_pop(L,1); lua_geti(L, -1, 5); - t.graphics_tile = luaL_optinteger(L, -1,-1); + t.graphics_tile = luaL_optinteger(L, -1,invalid_tile); lua_pop(L, 1); lua_geti(L, -1, 6); - t.overlay_tile = luaL_optinteger(L, -1, -1); + t.overlay_tile = luaL_optinteger(L, -1, invalid_tile); lua_pop(L, 1); lua_geti(L, -1, 7); - t.signpost_tile = luaL_optinteger(L, -1, -1); + t.signpost_tile = luaL_optinteger(L, -1, invalid_tile); lua_pop(L, 1); lua_geti(L, -1, 8); - t.item_tile = luaL_optinteger(L, -1, -1); + t.item_tile = luaL_optinteger(L, -1, invalid_tile); lua_pop(L, 1); lua_pop(L, 1); //pop current tile From 6ce0bcc72f8258ec2356552dfc6bb2d04f2ed892 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 2 Apr 2024 20:02:53 +0300 Subject: [PATCH 19/38] Redo the categorize to prevent issue that scripts run after world has loaded --- plugins/building-hacks.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index d1b37a67ff..cffbb7b766 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -179,11 +179,21 @@ struct work_hook : df::building_workshopst{ } DEFINE_VMETHOD_INTERPOSE(void, categorize, (bool free)) { + /* + there are two ways to enter this: + a) we have plugin enabled and building added from script (thus def exists) and we are placing a new building + b) we are loading a game thus buildings are not added from script yet + */ auto def = find_def(); - if (def && def->is_machine) + //in "b" case this ref is used as indicator to signal that script added this building last time before we saved + df::general_ref_creaturest* ref = static_cast(DFHack::Buildings::getGeneralRef(this, general_ref_type::CREATURE)); + if( ref || (def && def->is_machine)) { auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE]; insert_into_vector(vec, &df::building::id, (df::building*)this); + //in "a" case we add a ref and set it's values to signal later that we are a modified workshop + if (!ref) + set_current_power(def->powerInfo.produced, def->powerInfo.consumed); } INTERPOSE_NEXT(categorize)(free); @@ -569,8 +579,7 @@ static void enable_hooks(bool enable) 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); INTERPOSE_HOOK(work_hook,canBeRoomSubset).apply(enable); @@ -595,14 +604,22 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan return CR_OK; } +//this is needed as all other methods depend on world being loaded but categorize happens when we are loading stuff in +static void init_categorize(bool enable) +{ + INTERPOSE_HOOK(work_hook, categorize).apply(enable); + INTERPOSE_HOOK(work_hook, uncategorize).apply(enable); +} DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { enable_hooks(true); + init_categorize(true); return CR_OK; } DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { plugin_onstatechange(out,SC_WORLD_UNLOADED); + init_categorize(false); return CR_OK; } From 6a482464601c3c1217120a68ceeb2391ad6964e9 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 2 Apr 2024 20:16:16 +0300 Subject: [PATCH 20/38] Redo plugin to auto-enable on first use and expose enabled state to dfhack core --- plugins/building-hacks.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index cffbb7b766..ec4e51b0ef 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -28,6 +28,8 @@ using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("building-hacks"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + REQUIRE_GLOBAL(world); constexpr uint32_t invalid_tile = 0; @@ -72,6 +74,8 @@ DFHACK_PLUGIN_LUA_EVENTS{ DFHACK_LUA_END }; +static void enable_hooks(bool enable); + struct work_hook : df::building_workshopst{ typedef df::building_workshopst interpose_base; @@ -463,6 +467,9 @@ static int fixImpassible(lua_State* L) auto& def = hacked_workshops[workshop_type]; def.impassible_fix = impassible_setting; + + enable_hooks(true); + return 0; } //setMachineInfo(workshop_type,bool needs_power,int power_consumed=0,int power_produced=0,table [x=int,y=int] connection_points) -setups and enables machine (i.e. connected to gears, and co) behaviour of the building @@ -496,6 +503,9 @@ static int setMachineInfo(lua_State* L) lua_pop(L, 1); } lua_pop(L, 1); + + enable_hooks(true); + return 0; } //setUpdateSkip(workshop_type,int skip_frames) - skips frames to lower onupdate event call rate, 0 to disable @@ -505,6 +515,9 @@ static int setUpdateSkip(lua_State* L) auto& def = hacked_workshops[workshop_type]; def.skip_updates = luaL_optinteger(L, 2, 0); + + enable_hooks(true); + return 0; } //setAnimationInfo(workshop_type,table frames, [frame_skip]) - define animation and it's timing. If frame_skip is not set or set to -1, it will use machine timing (i.e. like gears/axels etc) @@ -521,6 +534,9 @@ static int setAnimationInfo(lua_State* L) def.machine_timing = true; else def.machine_timing = false; + + enable_hooks(true); + return 0; } //setOwnableBuilding(workshop_type,bool is_ownable) @@ -531,6 +547,9 @@ static int setOwnableBuilding(lua_State* L) auto& def = hacked_workshops[workshop_type]; def.room_subset = room_subset; + + enable_hooks(true); + return 0; } static void setPower(df::building_workshopst* workshop, int power_produced, int power_consumed) @@ -574,6 +593,10 @@ DFHACK_PLUGIN_LUA_COMMANDS{ }; static void enable_hooks(bool enable) { + if (is_enabled == enable) + return; + is_enabled = enable; + INTERPOSE_HOOK(work_hook,getImpassableOccupancy).apply(enable); //machine part INTERPOSE_HOOK(work_hook,getPowerInfo).apply(enable); @@ -591,9 +614,6 @@ static void enable_hooks(bool 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(); From 2a1db17403bbb1fcbdbbb9e06052bc8abfabd803 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 2 Apr 2024 20:31:45 +0300 Subject: [PATCH 21/38] Fix graphics tile types --- plugins/building-hacks.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index ec4e51b0ef..2e6bff035a 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -32,7 +32,7 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(world); -constexpr uint32_t invalid_tile = 0; +constexpr int32_t invalid_tile = 0; struct graphic_tile //could do just 31x31 and be done, but it's nicer to have flexible imho. { int16_t tile=-1; //originally uint8_t but we need to indicate non-animated tiles @@ -40,11 +40,11 @@ struct graphic_tile //could do just 31x31 and be done, but it's nicer to have fl int8_t back; int8_t bright; //index of texpos - uint32_t graphics_tile = invalid_tile; - uint32_t overlay_tile = invalid_tile; - uint32_t item_tile = invalid_tile; + int32_t graphics_tile = invalid_tile; + int32_t overlay_tile = invalid_tile; + int32_t item_tile = invalid_tile; //only for first line - uint32_t signpost_tile = invalid_tile; + int32_t signpost_tile = invalid_tile; }; struct workshop_hack_data { From 1e68060fb2c82c5f5be1f3b68903257e538f2300 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 2 Apr 2024 20:32:47 +0300 Subject: [PATCH 22/38] docs: add correct tags to the plugin docs --- docs/plugins/building-hacks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index ae38cb3d99..020f9d96c6 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -3,7 +3,7 @@ building-hacks .. dfhack-tool:: :summary: Provides a Lua API for creating powered workshops. - :tags: unavailable + :tags: adventure fort gameplay buildings :no-command: See `building-hacks-api` for more details. From 6faacc02ed481ab04eaeb7b5c002087e2d9f9f93 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 5 Apr 2024 15:29:23 +0300 Subject: [PATCH 23/38] docs: add note about workshop ids --- docs/plugins/building-hacks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index 020f9d96c6..12f49fbbe9 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -3,7 +3,7 @@ building-hacks .. dfhack-tool:: :summary: Provides a Lua API for creating powered workshops. - :tags: adventure fort gameplay buildings + :tags: fort gameplay buildings :no-command: See `building-hacks-api` for more details. From aaf3f9c669c6756cb1c93da1aeaf4f5d22f0d26b Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 5 Apr 2024 15:36:55 +0300 Subject: [PATCH 24/38] remove boolean arg from fixImpassible and setOwnableBuilding and doc improvements --- docs/dev/Lua API.rst | 18 ++++++++++-------- plugins/building-hacks.cpp | 10 ++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 787202ef6c..f8c95ed57b 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6522,29 +6522,30 @@ building-hacks This plugin extends DF workshops to support custom powered buildings. +.. note:: when using numeric ids for workshops be aware that those id can change between worlds + .. contents:: :local: Functions --------- -* ``setOwnableBuilding(workshop_type, enable)`` +* ``setOwnableBuilding(workshop_type)`` Set workshop to be included in zones (such as bedroom or inn). :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id - :enable: boolean value to enable or disable this functionality -* ``fixImpassible(workshop_type, enable)`` +* ``fixImpassible(workshop_type)`` Set workshop non walkable tiles to also block liquids (i.e. water and magma). :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id - :enable: boolean value to enable or disable this functionality * ``setMachineInfo(workshop_type, needs_power, power_consumed, power_produced, connection_points)`` - Setup and enable machine-like functionality for the workshop. Note: due to implementation limitations + Setup and enable machine-like functionality for the workshop. All workshops of this type will have + this as default power consumption/production. Note: due to implementation limitations workshop only connects to other machines if the other machine is build later than this one. :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id @@ -6591,7 +6592,8 @@ Functions :name: custom workshop string id e.g. ``SOAPMAKER`` :interval: how many ticks to skip between event triggers - :callback: function to call + :callback: function to call. Function signature is ``func(workshop)`` where ``workshop`` is of type + ``df.building_workshopst`` * ``getPower(building)`` @@ -6599,9 +6601,9 @@ Functions :building: specific workshop that produces or consumes power -* ``setPower(building,power_consumed, power_produced)`` +* ``setPower(building, power_consumed, power_produced)`` - Sets current power production and consumption for a building. Can be used to make buildings that + Sets current power production and consumption for a specific workshop building. Can be used to make buildings that dynamically change power consumption and production. :building: specific workshop that produces or consumes power diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 2e6bff035a..f9812e99a2 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -459,14 +459,13 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) return ; } -//fixImpassible(workshop_type,bool) - changes how impassible tiles work with liquids. False - default behaviour. True - blocks liquids. +//fixImpassible(workshop_type) - changes how impassible tiles work with liquids. static int fixImpassible(lua_State* L) { int workshop_type = get_workshop_type(L, 1); - bool impassible_setting = lua_toboolean(L, 2); auto& def = hacked_workshops[workshop_type]; - def.impassible_fix = impassible_setting; + def.impassible_fix = true; enable_hooks(true); @@ -539,14 +538,13 @@ static int setAnimationInfo(lua_State* L) return 0; } -//setOwnableBuilding(workshop_type,bool is_ownable) +//setOwnableBuilding(workshop_type) static int setOwnableBuilding(lua_State* L) { int workshop_type = get_workshop_type(L, 1); - bool room_subset = lua_toboolean(L, 2); auto& def = hacked_workshops[workshop_type]; - def.room_subset = room_subset; + def.room_subset = true; enable_hooks(true); From a975fd9515533a5c5dd9e4d05fd4908bd7b382c0 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 5 Apr 2024 16:06:06 +0300 Subject: [PATCH 25/38] Allow passing numeric id to auto functions. Tidy up docs api some more. --- docs/dev/Lua API.rst | 35 +++++++++++++++++----------------- plugins/lua/building-hacks.lua | 26 ++++++++++++++++--------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index f8c95ed57b..b6e6356744 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6548,17 +6548,17 @@ Functions this as default power consumption/production. Note: due to implementation limitations workshop only connects to other machines if the other machine is build later than this one. - :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id - :needs_power: only function if it has sufficient power - :power_consumed: building consumes this amount of power - :power_produced: output this amount of power - :connection_points: a table of ``{x=?,y=?}`` points that can connect to other machines + :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :needs_power: only function if it has sufficient power + :power_consumed: building consumes this amount of power + :power_produced: output this amount of power + :connection_points: a table of ``{x=?,y=?}`` zero-based coordinates that can connect to other machines -* ``setMachineInfoAuto(workshop_type, needs_power, power_consumed, power_produced, gear_tiles)`` +* ``setMachineInfoAuto(workshop_type, needs_power, power_consumed, power_produced, [gear_tiles])`` Same as ``setMachineInfo`` but fills out the ``connection_points`` table from raws placing connection points on tiles which have the gear tile. ``gear_tiles`` is an optional array of two tiles that are - counted as gears in the workshop ascii tile raws. + counted as gears in the workshop ascii tile raws. The default gear tiles are ``42`` and ``15``. * ``setAnimationInfo(workshop_type, frames, frame_skip)`` @@ -6574,26 +6574,27 @@ Functions :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames are synchronized with machine animation. -* ``setAnimationInfoAuto(name, make_graphics_too, frame_skip, gear_tiles)`` +* ``setAnimationInfoAuto(workshop_type, make_graphics_too, [frame_length], [gear_tiles])`` Animate workshop as with function above but generate frames automatically. This works by finding tiles which have gears and animating them with alternating gear tiles. - :name: custom workshop string id e.g. ``SOAPMAKER`` + :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id :make_graphics_too: replace same tiles in graphics mode with tiles from vanilla df mechanism - :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames - are synchronized with machine animation. - :gear_tiles: Optional array of 2 or 4 indexes. First two define ascii tiles and next two graphics tiles + :frame_length: How many ticks to display one frame. If set to negative number (or skipped) frames + are synchronized with machine animation. + :gear_tiles: Optional array of 2 or 4 indexes. First two define ascii tiles and next two graphics tiles. + This overrides default gear tiles. -* ``setOnUpdate(name,interval,callback)`` +* ``setOnUpdate(workshop_type,interval,callback)`` Setup callback to be called every ``interval`` of ticks for each building of this type. Note: low interval numbers and/or many workshops that use this might reduce DF performance. - :name: custom workshop string id e.g. ``SOAPMAKER`` - :interval: how many ticks to skip between event triggers - :callback: function to call. Function signature is ``func(workshop)`` where ``workshop`` is of type - ``df.building_workshopst`` + :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :interval: how many ticks to skip between event triggers + :callback: function to call. Function signature is ``func(workshop)`` where ``workshop`` is of type + ``df.building_workshopst`` * ``getPower(building)`` diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index 6b3015cba4..860bcea7c2 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -1,11 +1,11 @@ local _ENV = mkmodule('plugins.building-hacks') --[[ from native: - setOwnableBuilding(workshop_type,bool) + setOwnableBuilding(workshop_type) setAnimationInfo(workshop_type,table frames,int frameskip=-1) setUpdateSkip(workshop_type,int=0) setMachineInfo(workshop_type,bool need_power,int consume=0,int produce=0,table connection_points) - fixImpassible(workshop_type,bool) + fixImpassible(workshop_type) getPower(building) -- 2 or 0 returns, produced and consumed setPower(building,produced, consumed) from here: @@ -39,13 +39,21 @@ local function onUpdateLocal(workshop) 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 +local function findCustomWorkshop(name_or_id) + if type(name_or_id)="string" then + local raws=df.global.world.raws.buildings.all + for k,v in ipairs(raws) do + if v.code==name_or_id then + return v + end end + error("Building def:"..name_or_id.." not found") + elseif type(name_or_id)=="number" then + return df.building_def.find(name_or_id) + else + error("Expected string or integer id for workshop definition") end + end local function registerUpdateAction(shopId,callback) _registeredStuff[shopId]=callback @@ -80,9 +88,9 @@ local function findGears( shop_def ,gear_tiles) --finds positions of all gears a for x=0,w-1 do for y=0,h-1 do local tile=shop_def.tile[stage][x][y] - if tile==42 then --gear icon + if tile==gear_tiles[1] then --gear icon table.insert(ret,{x=x,y=y,invert=false}) - elseif tile==15 then --inverted gear icon + elseif tile==gear_tiles[2] then --inverted gear icon table.insert(ret,{x=x,y=y,invert=true}) end end From e462846bd04787e624b2f7ea5976c1e8c272d347 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 5 Apr 2024 17:07:25 +0300 Subject: [PATCH 26/38] docs: more info about animation tiles --- docs/dev/Lua API.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index b6e6356744..946aad34ae 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6569,8 +6569,11 @@ Functions :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id :frames: table of frames. Each frame is sparse flat table with ids from ``0`` to ``31*31-1``. Each frame tile is table of integers from 4 to 8 members long. Tile members are as - follow: tile, foreground color, background color, bright, graphics tile, overlay tile, - signpost tile, item tile. + follow: ``tile``, ``foreground color``, ``background color``, ``bright``, + ``graphics tile``, ``overlay tile``, ``signpost tile``, ``item tile``. + First 4 are function same as ascii workshop definition. The latter 4 are graphics + layers. ``signpost tile`` is an optional row that sticks up over the workshop. + :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames are synchronized with machine animation. From 1439fcb1f2d7891d27d558ee5715d3fdee9269c6 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 5 Apr 2024 17:30:05 +0300 Subject: [PATCH 27/38] fix lua syntax error and whitespaces --- plugins/building-hacks.cpp | 2 +- plugins/lua/building-hacks.lua | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index f9812e99a2..66fa3e0632 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -600,7 +600,7 @@ static void enable_hooks(bool enable) 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,canConnectToMachine).apply(enable); INTERPOSE_HOOK(work_hook,isUnpowered).apply(enable); INTERPOSE_HOOK(work_hook,canBeRoomSubset).apply(enable); diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index 860bcea7c2..72707bee4f 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -40,7 +40,7 @@ local function onUpdateLocal(workshop) end end local function findCustomWorkshop(name_or_id) - if type(name_or_id)="string" then + if type(name_or_id) == "string" then local raws=df.global.world.raws.buildings.all for k,v in ipairs(raws) do if v.code==name_or_id then @@ -53,7 +53,6 @@ local function findCustomWorkshop(name_or_id) else error("Expected string or integer id for workshop definition") end - end local function registerUpdateAction(shopId,callback) _registeredStuff[shopId]=callback From 9e14ac46ad1e2f531336771a3a0a0e6eec54edd0 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 7 Jun 2024 10:15:23 +0300 Subject: [PATCH 28/38] update to new structures --- plugins/building-hacks.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 66fa3e0632..01442fdec0 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -101,8 +101,8 @@ struct work_hook : df::building_workshopst{ //try getting ref, if not return from definition if (ref) { - info->produced = ref->unk_1; - info->consumed = ref->unk_2; + info->produced = ref->race; + info->consumed = ref->caste; return true; } else @@ -131,18 +131,18 @@ struct work_hook : df::building_workshopst{ //if we have a setting then update it, else create a new ref for dynamic power tracking if (ref) { - ref->unk_1 = produced; - ref->unk_2 = consumed; + ref->race = produced; + ref->caste = consumed; } else { ref = df::allocate(); - ref->unk_1 = produced; - ref->unk_2 = consumed; + ref->race = produced; + ref->caste = consumed; general_refs.push_back(ref); } } - DEFINE_VMETHOD_INTERPOSE(uint32_t,getImpassableOccupancy,()) + DEFINE_VMETHOD_INTERPOSE(df::tile_building_occ,getImpassableOccupancy,()) { if(auto def = find_def()) { From e8763fec31619f6ddd63fb0a7723eb08d263107f Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 30 Dec 2024 12:14:04 +0200 Subject: [PATCH 29/38] docs: use bhacks instead of bld in examples --- docs/dev/Lua API.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 946aad34ae..4b297b67b0 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6633,11 +6633,11 @@ Examples Simple mechanical workshop:: - local bld=require('plugins.building-hacks') + local bhacks = require('plugins.building-hacks') --work only powered, consume 15 power and one connection point at 0,0 - bld.setMachineInfo("BONE_GRINDER",true,15,0,{{x=0,y=0}}) + bhacks.setMachineInfo("BONE_GRINDER",true,15,0,{{x=0,y=0}}) --set animation to switch between gear tiles at 0,0 - bld.setAnimationInfo("BONE_GRINDER",{ + bhacks.setAnimationInfo("BONE_GRINDER",{ {[0]={42,7,0,0}}, --first frame, 1 changed tile {[0]={15,7,0,0}} -- second frame, same } @@ -6645,9 +6645,9 @@ Simple mechanical workshop:: Or with auto_gears:: - local bld=require('plugins.building-hacks') - bld.setMachineInfoAuto("BONE_GRINDER",true,15) - bld.setAnimationInfoAuto("BONE_GRINDER",true) + local bhacks = require('plugins.building-hacks') + bhacks.setMachineInfoAuto("BONE_GRINDER",true,15) + bhacks.setAnimationInfoAuto("BONE_GRINDER",true) buildingplan ============ From 41a0df50dc1cd8e5340ca2bcfc12d4b322fe22ed Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 30 Dec 2024 15:19:12 +0200 Subject: [PATCH 30/38] Don't hold the CoreSuspend over INTERPOSE_NEXT --- plugins/building-hacks.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 01442fdec0..ae0ed77f9e 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -290,9 +290,11 @@ struct work_hook : df::building_workshopst{ } DEFINE_VMETHOD_INTERPOSE(void, setTriggerState,(int32_t state)) { - CoreSuspendClaimer suspend; - color_ostream_proxy out(Core::getInstance().getConsole()); - onSetTriggerState(out, this,state); + { + CoreSuspender suspend; + color_ostream_proxy out(Core::getInstance().getConsole()); + onSetTriggerState(out, this, state); + } INTERPOSE_NEXT(setTriggerState)(state); //pretty sure default workshop has nothing related to this, but to be future proof lets do it like this } DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (uint32_t curtick,df::building_drawbuffer *db, int16_t z_offset)) From 5360b9895c60d88dc1a44c054df9dd6d1a24de09 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 30 Dec 2024 15:21:19 +0200 Subject: [PATCH 31/38] swap getPower returns to be same as setPower docs: minor changes --- plugins/building-hacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index ae0ed77f9e..1be0778684 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -572,8 +572,8 @@ static int getPower(lua_State*L) { df::power_info info; ptr->get_current_power(&info); - lua_pushinteger(L, info.produced); lua_pushinteger(L, info.consumed); + lua_pushinteger(L, info.produced); return 2; } return 0; From b27a61a847b8780a8cdedd47eb1b46b6be0032ba Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 30 Dec 2024 19:49:47 +0200 Subject: [PATCH 32/38] docs: change wording on many thingies --- docs/dev/Lua API.rst | 53 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 4b297b67b0..e8191ae5e4 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6522,7 +6522,9 @@ building-hacks This plugin extends DF workshops to support custom powered buildings. -.. note:: when using numeric ids for workshops be aware that those id can change between worlds +.. note:: + When using numeric ids for workshops be aware that those id can change between worlds, + depending on what other custom types exist in the raws for that world. .. contents:: :local: @@ -6532,41 +6534,42 @@ Functions * ``setOwnableBuilding(workshop_type)`` - Set workshop to be included in zones (such as bedroom or inn). + Set workshop to be included in zones (such as a bedroom or tavern). - :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :workshop_type: custom workshop string id, e.g. ``SOAPMAKER`` or numeric id * ``fixImpassible(workshop_type)`` Set workshop non walkable tiles to also block liquids (i.e. water and magma). - :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :workshop_type: custom workshop string id, e.g. ``SOAPMAKER`` or numeric id * ``setMachineInfo(workshop_type, needs_power, power_consumed, power_produced, connection_points)`` Setup and enable machine-like functionality for the workshop. All workshops of this type will have - this as default power consumption/production. Note: due to implementation limitations - workshop only connects to other machines if the other machine is build later than this one. + this as their default power consumption/production. Note: due to implementation limitations, + workshops only connect to other machines if the other machines are planned after than this one. :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id - :needs_power: only function if it has sufficient power - :power_consumed: building consumes this amount of power - :power_produced: output this amount of power + :needs_power: true if the workshop should only be usable if it has sufficient power + :power_consumed: buildings of this type consume this amount of power by default + :power_produced: buildings of this type output this amount of power by default :connection_points: a table of ``{x=?,y=?}`` zero-based coordinates that can connect to other machines * ``setMachineInfoAuto(workshop_type, needs_power, power_consumed, power_produced, [gear_tiles])`` - Same as ``setMachineInfo`` but fills out the ``connection_points`` table from raws placing connection - points on tiles which have the gear tile. ``gear_tiles`` is an optional array of two tiles that are - counted as gears in the workshop ascii tile raws. The default gear tiles are ``42`` and ``15``. + Same as ``setMachineInfo`` but fills out the ``connection_points`` table based on the + building definition in the raws. It places connection points on tiles which have the gear + tile. ``gear_tiles`` is an optional array of two tiles that are counted as gears in the + workshop ascii tile raws. The default gear tiles are ``42`` and ``15``. * ``setAnimationInfo(workshop_type, frames, frame_skip)`` Animate workshop by replacing displayed tiles (or graphical tiles). There are two ways this works: if ``frame_skip>=0`` then it shows each frame for ``frame_skip`` of frames or if ``frame_skip<0`` - Frames are synchronized to the machine this building is connected to. + Frames are synchronized with the machines this building is connected to. - :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :workshop_type: custom workshop string id, e.g. ``SOAPMAKER`` or numeric id :frames: table of frames. Each frame is sparse flat table with ids from ``0`` to ``31*31-1``. Each frame tile is table of integers from 4 to 8 members long. Tile members are as follow: ``tile``, ``foreground color``, ``background color``, ``bright``, @@ -6577,38 +6580,40 @@ Functions :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames are synchronized with machine animation. -* ``setAnimationInfoAuto(workshop_type, make_graphics_too, [frame_length], [gear_tiles])`` +* ``setAnimationInfoAuto(workshop_type, make_graphics_too[, frame_length][, gear_tiles])`` Animate workshop as with function above but generate frames automatically. This works by finding tiles which have gears and animating them with alternating gear tiles. - :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :workshop_type: custom workshop string id, e.g. ``SOAPMAKER`` or numeric id :make_graphics_too: replace same tiles in graphics mode with tiles from vanilla df mechanism :frame_length: How many ticks to display one frame. If set to negative number (or skipped) frames are synchronized with machine animation. :gear_tiles: Optional array of 2 or 4 indexes. First two define ascii tiles and next two graphics tiles. This overrides default gear tiles. -* ``setOnUpdate(workshop_type,interval,callback)`` +* ``setOnUpdate(workshop_type, interval, callback)`` - Setup callback to be called every ``interval`` of ticks for each building of this type. Note: low interval - numbers and/or many workshops that use this might reduce DF performance. + Register callback to be called every ``interval`` ticks for each building of this + type. This can be very expensive if the interval is low and/or there are many + workshops of this type. Keep these callbacks light! - :workshop_type: custom workshop string id e.g. ``SOAPMAKER`` or numeric id + :workshop_type: custom workshop string id, e.g. ``SOAPMAKER`` or numeric id :interval: how many ticks to skip between event triggers :callback: function to call. Function signature is ``func(workshop)`` where ``workshop`` is of type ``df.building_workshopst`` * ``getPower(building)`` - Returns two number - produced and consumed power if building can be modified and returns nothing otherwise. + If this building is of a type registered with building-hacks, returns values for + consumed and produced power. Otherwise, returns ``nil``. :building: specific workshop that produces or consumes power * ``setPower(building, power_consumed, power_produced)`` - Sets current power production and consumption for a specific workshop building. Can be used to make buildings that - dynamically change power consumption and production. + Dynamically sets current power production and consumption for a specific workshop + (which must be of a type registered with building-hacks). :building: specific workshop that produces or consumes power :power_consumed: set building to consume this amount of power @@ -6621,7 +6626,7 @@ Events This module exports two events. However only one is documented here and is intended to be used directly. To use ``onUpdateAction`` instead call ``setOnUpdate`` function. -* ``onSetTriggerState(workshop,state)`` +* ``onSetTriggerState(workshop, state)`` Notify when building is triggered from linked lever or trap. From d43fb3aee84210f3c5896eae158d8a2b39f7251e Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 30 Dec 2024 19:50:43 +0200 Subject: [PATCH 33/38] change how frames are loaded --- plugins/building-hacks.cpp | 88 +++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 1be0778684..4b24ab22a1 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -391,9 +391,44 @@ void clear_mapping() { hacked_workshops.clear(); } +static void load_graphics_tile(lua_State* L,graphic_tile& t) +{ + lua_getfield(L, -1, "ch"); + t.tile = luaL_optinteger(L, -1, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "fg"); + t.fore = luaL_optinteger(L, -1, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "bg"); + t.back = luaL_optinteger(L, -1, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "bold"); + t.bright = luaL_optinteger(L, -1, 0); + lua_pop(L, 1); + + lua_getfield(L, -1, "tile"); + t.graphics_tile = luaL_optinteger(L, -1, invalid_tile); + lua_pop(L, 1); + + lua_getfield(L, -1, "tile_overlay"); + t.overlay_tile = luaL_optinteger(L, -1, invalid_tile); + lua_pop(L, 1); + + lua_getfield(L, -1, "tile_signpost"); + t.signpost_tile = luaL_optinteger(L, -1, invalid_tile); + lua_pop(L, 1); + + lua_getfield(L, -1, "tile_item"); + t.item_tile = luaL_optinteger(L, -1, invalid_tile); + lua_pop(L, 1); +} static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) { - const int max_idx = 31 * 31; + const int max_side = 31; + const int max_idx = max_side * max_side; luaL_checktype(L,stack_pos,LUA_TTABLE); @@ -403,56 +438,31 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) while (lua_geti(L,stack_pos,frame_index) != LUA_TNIL) { //get frame[i] luaL_checktype(L,-1,LUA_TTABLE); //ensure that it's a table std::vector frame(max_idx); - - for (int idx = 0; idx < max_idx; idx++) + for (int x = 1; x <= max_side; x++) { - auto& t = frame[idx]; - lua_geti(L, -1, idx); //get tile at idx i.e. frame[i][idx] where idx=x+y*31 - + lua_geti(L, -1, x); //get row at x if (lua_isnil(L, -1))//allow sparse indexing { lua_pop(L, 1); //pop current tile (nil in this case) continue; } - else - { - //load up tile, color, optionally graphics stuff - lua_geti(L, -1, 1); - //not sure why would anyone do nil tile, but for api consitency lets allow it - t.tile= luaL_optinteger(L,-1,-1); - lua_pop(L,1); - - lua_geti(L, -1, 2); - t.fore= luaL_optinteger(L,-1,0); - lua_pop(L,1); - - lua_geti(L, -1, 3); - t.back= luaL_optinteger(L,-1,0); - lua_pop(L,1); - lua_geti(L, -1, 4); - t.bright=luaL_optinteger(L,-1,0); - lua_pop(L,1); - - lua_geti(L, -1, 5); - t.graphics_tile = luaL_optinteger(L, -1,invalid_tile); - lua_pop(L, 1); - - lua_geti(L, -1, 6); - t.overlay_tile = luaL_optinteger(L, -1, invalid_tile); - lua_pop(L, 1); - - lua_geti(L, -1, 7); - t.signpost_tile = luaL_optinteger(L, -1, invalid_tile); - lua_pop(L, 1); + for (int y = 1; y <= max_side; y++) + { + lua_geti(L, -1, y); //get cell at y + if (lua_isnil(L, -1))//allow sparse indexing + { + lua_pop(L, 1); //pop current tile (nil in this case) + continue; + } - lua_geti(L, -1, 8); - t.item_tile = luaL_optinteger(L, -1, invalid_tile); - lua_pop(L, 1); + load_graphics_tile(L, frame[(x-1)+(y-1)*max_side]); lua_pop(L, 1); //pop current tile } + lua_pop(L, 1); //pop current row } + def.frames.push_back(frame); frame_index++; lua_pop(L, 1); //pop current frame From 3355f6b2dba192e7233e5871fb684794a9eca8f8 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 30 Dec 2024 19:57:36 +0200 Subject: [PATCH 34/38] saner frameskip logic --- docs/dev/Lua API.rst | 2 +- plugins/building-hacks.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index e8191ae5e4..6f9311f418 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6566,7 +6566,7 @@ Functions * ``setAnimationInfo(workshop_type, frames, frame_skip)`` Animate workshop by replacing displayed tiles (or graphical tiles). There are two ways this works: - if ``frame_skip>=0`` then it shows each frame for ``frame_skip`` of frames or if ``frame_skip<0`` + if ``frame_skip>0`` then it shows each frame for ``frame_skip`` of frames or if ``frame_skip<=0`` Frames are synchronized with the machines this building is connected to. :workshop_type: custom workshop string id, e.g. ``SOAPMAKER`` or numeric id diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 4b24ab22a1..e62991d82b 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -539,9 +539,7 @@ static int setAnimationInfo(lua_State* L) //animation loadFrames(L, def, 2); def.frame_skip = luaL_optinteger(L, 3, -1); - if (def.frame_skip == 0) - def.frame_skip = 1; - if (def.frame_skip < 0) + if (def.frame_skip <= 0) def.machine_timing = true; else def.machine_timing = false; From f49d203417de96366081848dbb8fddc1ad730734 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 30 Dec 2024 21:18:18 +0200 Subject: [PATCH 35/38] docs: update frame doc --- docs/dev/Lua API.rst | 13 ++++++------- plugins/building-hacks.cpp | 3 +-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 6f9311f418..77fe4ddf60 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6570,15 +6570,14 @@ Functions Frames are synchronized with the machines this building is connected to. :workshop_type: custom workshop string id, e.g. ``SOAPMAKER`` or numeric id - :frames: table of frames. Each frame is sparse flat table with ids from ``0`` to ``31*31-1``. - Each frame tile is table of integers from 4 to 8 members long. Tile members are as - follow: ``tile``, ``foreground color``, ``background color``, ``bright``, - ``graphics tile``, ``overlay tile``, ``signpost tile``, ``item tile``. + :frames: table of frames. Each frame is array of rows of tiles with ids from ``1`` to ``31``. + Each frame tile is table of with following optional members: ``ch``, ``fg``, + ``bg``, ``bold``, ``tile``, ``tile_overlay``, ``tile_signpost``, ``tile_item``. First 4 are function same as ascii workshop definition. The latter 4 are graphics - layers. ``signpost tile`` is an optional row that sticks up over the workshop. + layers. ``tile_signpost `` is only valid in first row and it shows up above the workshop. - :frame_skip: How many ticks to display one frame. If set to negative number (or skipped) frames - are synchronized with machine animation. + :frame_skip: How many ticks to display one frame. If set to negative number, zero or skipped frames + are synchronized with other connected machines animations. * ``setAnimationInfoAuto(workshop_type, make_graphics_too[, frame_length][, gear_tiles])`` diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index e62991d82b..a58881bf16 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -462,13 +462,12 @@ static void loadFrames(lua_State* L,workshop_hack_data& def,int stack_pos) } lua_pop(L, 1); //pop current row } - def.frames.push_back(frame); frame_index++; lua_pop(L, 1); //pop current frame } - return ; + return; } //fixImpassible(workshop_type) - changes how impassible tiles work with liquids. From 92fa5cf9baf940972ca79da40c5fd3baa623c845 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 30 Dec 2024 22:10:37 +0200 Subject: [PATCH 36/38] building-hacks.lua update --- docs/dev/Lua API.rst | 8 +++--- plugins/lua/building-hacks.lua | 48 ++++++++++++++-------------------- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 77fe4ddf60..ff635bdb3c 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6576,8 +6576,8 @@ Functions First 4 are function same as ascii workshop definition. The latter 4 are graphics layers. ``tile_signpost `` is only valid in first row and it shows up above the workshop. - :frame_skip: How many ticks to display one frame. If set to negative number, zero or skipped frames - are synchronized with other connected machines animations. + :frame_skip: How many ticks to display one frame. If set to negative number, zero or skipped, frames + are synchronized with machine animation. * ``setAnimationInfoAuto(workshop_type, make_graphics_too[, frame_length][, gear_tiles])`` @@ -6586,9 +6586,9 @@ Functions :workshop_type: custom workshop string id, e.g. ``SOAPMAKER`` or numeric id :make_graphics_too: replace same tiles in graphics mode with tiles from vanilla df mechanism - :frame_length: How many ticks to display one frame. If set to negative number (or skipped) frames + :frame_length: How many ticks to display one frame. If set to negative number, zero or skipped, frames are synchronized with machine animation. - :gear_tiles: Optional array of 2 or 4 indexes. First two define ascii tiles and next two graphics tiles. + :gear_tiles: Optional table with of 2 or 4 indexes. First two define ascii tiles and next two graphics tiles. This overrides default gear tiles. * ``setOnUpdate(workshop_type, interval, callback)`` diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index 72707bee4f..ed4a883f81 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -6,8 +6,8 @@ local _ENV = mkmodule('plugins.building-hacks') setUpdateSkip(workshop_type,int=0) setMachineInfo(workshop_type,bool need_power,int consume=0,int produce=0,table connection_points) fixImpassible(workshop_type) - getPower(building) -- 2 or 0 returns, produced and consumed - setPower(building,produced, consumed) + getPower(building) -- 2 or 0 returns, consumed and produced + setPower(building, consumed, produced) from here: setMachineInfoAuto(name,int consume,int produce,bool need_power) setAnimationInfoAuto(name,bool make_graphics_too) @@ -59,37 +59,28 @@ local function registerUpdateAction(shopId,callback) onUpdateAction._library=onUpdateLocal dfhack.onStateChange.building_hacks=unregall end ---take in tiles with {x=?, y=? ,...} and output a table flat sparse 31x31 table -local function generateFrame(tiles,w,h) +--take in tiles with {x=?, y=? ,...} and output a table in format needed for setAnimationInfo +local function generateFrame(tiles) local mTiles={} local ret={} for k,v in ipairs(tiles) do - ret[v.x+v.y*31]=v + ensure_key(ret, v.x)[v.y]=v end return ret end ---convert frames to flat arrays if needed -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 + --locate gears on the workshop from the raws definition local function findGears( shop_def ,gear_tiles) --finds positions of all gears and inverted gears - gear_tiles=gear_tiles or {42,15} + gear_tiles=gear_tiles or {ch=42,ch_alt=15} local w,h=shop_def.dim_x,shop_def.dim_y local stage=shop_def.build_stages local ret={} for x=0,w-1 do for y=0,h-1 do local tile=shop_def.tile[stage][x][y] - if tile==gear_tiles[1] then --gear icon + if tile==gear_tiles.ch then --gear icon table.insert(ret,{x=x,y=y,invert=false}) - elseif tile==gear_tiles[2] then --inverted gear icon + elseif tile==gear_tiles.ch_alt then --inverted gear icon table.insert(ret,{x=x,y=y,invert=true}) end end @@ -105,8 +96,7 @@ local function lookup_color( shop_def,x,y,stage ) end --adds frames for all gear icons and inverted gear icons local function processFramesAuto( shop_def ,gears,auto_graphics,gear_tiles) - gear_tiles=gear_tiles or {42,15,graphics_cache[1],graphics_cache[2]} - local w,h=shop_def.dim_x,shop_def.dim_y + gear_tiles=gear_tiles or {ch=42,ch_alt=15,tile=graphics_cache[1],tile_alt=graphics_cache[2]} local frames={{},{}} --two frames only local stage=shop_def.build_stages @@ -114,25 +104,25 @@ local function processFramesAuto( shop_def ,gears,auto_graphics,gear_tiles) local tile,tile_inv if v.inverted then - tile=gear_tiles[1] - tile_inv=gear_tiles[2] + tile=gear_tiles.ch + tile_inv=gear_tiles.ch_alt else - tile=gear_tiles[2] - tile_inv=gear_tiles[1] + tile=gear_tiles.ch_alt + tile_inv=gear_tiles.ch end - table.insert(frames[1],{x=v.x,y=v.y,tile,lookup_color(shop_def,v.x,v.y,stage)}) - table.insert(frames[2],{x=v.x,y=v.y,tile_inv,lookup_color(shop_def,v.x,v.y,stage)}) + table.insert(frames[1],{x=v.x,y=v.y,ch=tile,fg=lookup_color(shop_def,v.x,v.y,stage)}) + table.insert(frames[2],{x=v.x,y=v.y,ch=tile_inv,fg=lookup_color(shop_def,v.x,v.y,stage)}) --insert default gear graphics if auto graphics is on if auto_graphics then - frames[1][#frames[1]][5]=gear_tiles[3] - frames[2][#frames[2]][5]=gear_tiles[4] + frames[1][#frames[1]].tile=gear_tiles.tile + frames[2][#frames[2]].tile=gear_tiles.tile_alt end end for frame_id,frame in ipairs(frames) do - frames[frame_id]=generateFrame(frame,w,h) + frames[frame_id]=generateFrame(frame) end return frames end From 7a79fab1f6b67044353e3ac4061c62057b3ad27e Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 31 Dec 2024 11:27:06 +0200 Subject: [PATCH 37/38] docs: yet another day, yet another doc update. Also improved optional args for gear char/tile --- docs/dev/Lua API.rst | 42 +++++++++++++++++++++++----------- plugins/lua/building-hacks.lua | 20 +++++++++------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ff635bdb3c..f3bda1654a 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -6574,7 +6574,7 @@ Functions Each frame tile is table of with following optional members: ``ch``, ``fg``, ``bg``, ``bold``, ``tile``, ``tile_overlay``, ``tile_signpost``, ``tile_item``. First 4 are function same as ascii workshop definition. The latter 4 are graphics - layers. ``tile_signpost `` is only valid in first row and it shows up above the workshop. + layers. ``tile_signpost`` is only valid in first row and it shows up above the workshop. :frame_skip: How many ticks to display one frame. If set to negative number, zero or skipped, frames are synchronized with machine animation. @@ -6588,8 +6588,9 @@ Functions :make_graphics_too: replace same tiles in graphics mode with tiles from vanilla df mechanism :frame_length: How many ticks to display one frame. If set to negative number, zero or skipped, frames are synchronized with machine animation. - :gear_tiles: Optional table with of 2 or 4 indexes. First two define ascii tiles and next two graphics tiles. - This overrides default gear tiles. + :gear_tiles: Optional table with ``ch``, ``ch_alt``, ``tile``, ``tile_alt``. First two are ascii + gear tiles and are used to find tiles in workshop raw and animate them. Second two are + used to animate graphical tiles. * ``setOnUpdate(workshop_type, interval, callback)`` @@ -6638,20 +6639,35 @@ Examples Simple mechanical workshop:: local bhacks = require('plugins.building-hacks') - --work only powered, consume 15 power and one connection point at 0,0 - bhacks.setMachineInfo("BONE_GRINDER",true,15,0,{{x=0,y=0}}) - --set animation to switch between gear tiles at 0,0 - bhacks.setAnimationInfo("BONE_GRINDER",{ - {[0]={42,7,0,0}}, --first frame, 1 changed tile - {[0]={15,7,0,0}} -- second frame, same - } - ) + + --work only powered, consume 15 power and one connection point at 0, 0 + bhacks.setMachineInfo("BONE_GRINDER", true, 15, 0, {{x=0, y=0}}) + + --load custom graphical tiles for use if graphics is enabled + local tile1=dfhack.screen.findGraphicsTile('DRAGON_ENGINE_TILES', 0, 0) + local tile2=dfhack.screen.findGraphicsTile('DRAGON_ENGINE_TILES', 1, 0) + + local frames={} + --first frame - tile (1, 1) changed to character 42 + ensure_key(frames, 1, 1)[1]={ch=42, fg=7, bg=0, bold=0, tile=tile1} + --second frame - tile (1,1) changed to character 15 + ensure_key(frames, 2, 1)[1]={ch=15, fg=7, bg=0, bold=0, tile=tile2} + + --set animation to switch between gear tiles at 1,1 + bhacks.setAnimationInfo("BONE_GRINDER", frames) Or with auto_gears:: local bhacks = require('plugins.building-hacks') - bhacks.setMachineInfoAuto("BONE_GRINDER",true,15) - bhacks.setAnimationInfoAuto("BONE_GRINDER",true) + + --load custom graphical tiles for use if graphics is enabled + local tile1=dfhack.screen.findGraphicsTile('DRAGON_ENGINE_TILES', 0, 0) + local tile2=dfhack.screen.findGraphicsTile('DRAGON_ENGINE_TILES', 1, 0) + + --work only powered, consume 15 power and find connection point from building raws + bhacks.setMachineInfoAuto("BONE_GRINDER", true, 15) + --set animation to switch between default ascii gears and specific graphic tiles loaded above + bhacks.setAnimationInfoAuto("BONE_GRINDER", true, -1, {tile=tile1, tile_alt=tile2}) buildingplan ============ diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index ed4a883f81..45a3826da2 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -15,6 +15,10 @@ local _ENV = mkmodule('plugins.building-hacks') ]] _registeredStuff={} + +local CHAR_GEAR=42 +local CHAR_GEAR_ALT=15 + --cache graphics tiles for mechanical gears local graphics_cache function reload_graphics_cache( ) @@ -71,7 +75,7 @@ end --locate gears on the workshop from the raws definition local function findGears( shop_def ,gear_tiles) --finds positions of all gears and inverted gears - gear_tiles=gear_tiles or {ch=42,ch_alt=15} + gear_tiles=gear_tiles or {ch=CHAR_GEAR,ch_alt=CHAR_GEAR_ALT} local w,h=shop_def.dim_x,shop_def.dim_y local stage=shop_def.build_stages local ret={} @@ -96,7 +100,7 @@ local function lookup_color( shop_def,x,y,stage ) end --adds frames for all gear icons and inverted gear icons local function processFramesAuto( shop_def ,gears,auto_graphics,gear_tiles) - gear_tiles=gear_tiles or {ch=42,ch_alt=15,tile=graphics_cache[1],tile_alt=graphics_cache[2]} + gear_tiles=gear_tiles or {ch=CHAR_GEAR,ch_alt=CHAR_GEAR_ALT,tile=graphics_cache[1],tile_alt=graphics_cache[2]} local frames={{},{}} --two frames only local stage=shop_def.build_stages @@ -104,11 +108,11 @@ local function processFramesAuto( shop_def ,gears,auto_graphics,gear_tiles) local tile,tile_inv if v.inverted then - tile=gear_tiles.ch - tile_inv=gear_tiles.ch_alt + tile=gear_tiles.ch or CHAR_GEAR + tile_inv=gear_tiles.ch_alt or CHAR_GEAR_ALT else - tile=gear_tiles.ch_alt - tile_inv=gear_tiles.ch + tile=gear_tiles.ch_alt or CHAR_GEAR_ALT + tile_inv=gear_tiles.ch or CHAR_GEAR end table.insert(frames[1],{x=v.x,y=v.y,ch=tile,fg=lookup_color(shop_def,v.x,v.y,stage)}) @@ -116,8 +120,8 @@ local function processFramesAuto( shop_def ,gears,auto_graphics,gear_tiles) --insert default gear graphics if auto graphics is on if auto_graphics then - frames[1][#frames[1]].tile=gear_tiles.tile - frames[2][#frames[2]].tile=gear_tiles.tile_alt + frames[1][#frames[1]].tile=gear_tiles.tile or graphics_cache[1] + frames[2][#frames[2]].tile=gear_tiles.tile_alt or graphics_cache[2] end end From f2c038d1736164962beeef36e26ce6915dc4b0ec Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 1 Jan 2025 11:51:44 +0200 Subject: [PATCH 38/38] remove coresuspend from vmethod interpose --- plugins/building-hacks.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index a58881bf16..1f3ea3d6a8 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -290,11 +290,8 @@ struct work_hook : df::building_workshopst{ } DEFINE_VMETHOD_INTERPOSE(void, setTriggerState,(int32_t state)) { - { - CoreSuspender suspend; - color_ostream_proxy out(Core::getInstance().getConsole()); - onSetTriggerState(out, this, state); - } + color_ostream_proxy out(Core::getInstance().getConsole()); + onSetTriggerState(out, this, state); INTERPOSE_NEXT(setTriggerState)(state); //pretty sure default workshop has nothing related to this, but to be future proof lets do it like this } DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (uint32_t curtick,df::building_drawbuffer *db, int16_t z_offset))