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

  • embark
  • lever
  • stripcaged
  • +
  • undump-buildings
  • create-items
  • soundsense-season
  • @@ -2867,6 +2868,10 @@ alternatively pass cage IDs as arguments:

    stripcaged weapons 25321 34228 +
    +

    undump-buildings

    +

    Undesignates building base materials for dumping.

    +

    create-items

    Spawn arbitrary items under the cursor.

    diff --git a/Readme.rst b/Readme.rst index cd9101aab..8cc5aaa1b 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2057,6 +2057,10 @@ alternatively pass cage IDs as arguments:: stripcaged weapons 25321 34228 +undump-buildings +================ +Undesignates building base materials for dumping. + create-items ============ Spawn arbitrary items under the cursor. From c3d45c3a1efb2b4e9db590c3bafaf0c5fdcdbcc7 Mon Sep 17 00:00:00 2001 From: Quietust Date: Wed, 23 Apr 2014 08:03:10 -0500 Subject: [PATCH 10/18] Add Job::getName(job *), also available from Lua --- library/LuaApi.cpp | 1 + library/include/modules/Job.h | 1 + library/modules/Job.cpp | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 8c76a36f7..4288802ea 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1337,6 +1337,7 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = { WRAPM(Job,checkDesignationsNow), WRAPM(Job,isSuitableItem), WRAPM(Job,isSuitableMaterial), + WRAPM(Job,getName), WRAPN(is_equal, jobEqual), WRAPN(is_item_equal, jobItemEqual), { NULL, NULL } diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index b13d9c5a9..4b3950ebd 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -81,6 +81,7 @@ namespace DFHack DFHACK_EXPORT bool isSuitableItem(df::job_item *item, df::item_type itype, int isubtype); DFHACK_EXPORT bool isSuitableMaterial(df::job_item *item, int mat_type, int mat_index); + DFHACK_EXPORT std::string getName(df::job *job); } DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b); diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index a86a82d8a..8750db4c7 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -53,6 +53,7 @@ using namespace std; #include "df/general_ref.h" #include "df/general_ref_unit_workerst.h" #include "df/general_ref_building_holderst.h" +#include "df/interface_button_building_new_jobst.h" using namespace DFHack; using namespace df::enums; @@ -476,3 +477,25 @@ bool Job::isSuitableMaterial(df::job_item *item, int mat_type, int mat_index) return minfo.isValid() && iinfo.matches(*item, &minfo); } + +std::string Job::getName(df::job *job) +{ + CHECK_NULL_POINTER(job); + + std::string desc; + auto button = df::allocate(); + button->reaction_name = job->reaction_name; + button->hist_figure_id = job->hist_figure_id; + button->job_type = job->job_type; + button->item_type = job->item_type; + button->item_subtype = job->item_subtype; + button->mat_type = job->mat_type; + button->mat_index = job->mat_index; + button->item_category = job->item_category; + button->material_category = job->material_category; + + button->getLabel(&desc); + delete button; + + return desc; +} From 57fbb1004b0c79fcd350a5a3a064f05b1f26c7ba Mon Sep 17 00:00:00 2001 From: Quietust Date: Wed, 23 Apr 2014 08:23:34 -0500 Subject: [PATCH 11/18] Assorted cleanup --- library/modules/EventManager.cpp | 9 +++------ library/modules/Items.cpp | 2 +- library/modules/Job.cpp | 24 +++++++----------------- library/modules/Maps.cpp | 12 ++++++------ 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index bfc7c4907..88f4d56d6 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -30,6 +30,7 @@ using namespace std; using namespace DFHack; using namespace EventManager; +using namespace df::enums; /* * TODO: @@ -322,12 +323,8 @@ static void manageJobInitiatedEvent(color_ostream& out) { //helper function for manageJobCompletedEvent static int32_t getWorkerID(df::job* job) { - for ( size_t a = 0; a < job->general_refs.size(); a++ ) { - if ( job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) - continue; - return ((df::general_ref_unit_workerst*)job->general_refs[a])->unit_id; - } - return -1; + auto ref = findRef(job->general_refs, general_ref_type::UNIT_WORKER); + return ref ? ref->getID() : -1; } /* diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 6673e6f1b..b243e5d85 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -534,7 +534,7 @@ bool Items::setOwner(df::item *item, df::unit *unit) { df::general_ref *ref = item->general_refs[i]; - if (!strict_virtual_cast(ref)) + if (ref->getType() != general_ref_type::UNIT_ITEMOWNER) continue; if (auto cur = ref->getUnit()) diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index 8750db4c7..1cfc0fa78 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -93,7 +93,7 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job, bool keepEverything) pnew->job_items[a] = new df::job_item(*pnew->job_items[a]); for ( size_t a = 0; a < job->general_refs.size(); a++ ) - if ( keepEverything || job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) + if ( keepEverything || job->general_refs[a]->getType() != general_ref_type::UNIT_WORKER ) pnew->general_refs.push_back(job->general_refs[a]->clone()); return pnew; @@ -265,28 +265,18 @@ df::building *DFHack::Job::getHolder(df::job *job) { CHECK_NULL_POINTER(job); - for (size_t i = 0; i < job->general_refs.size(); i++) - { - VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->general_refs[i]); - if (ref) - return ref->getBuilding(); - } + auto ref = getGeneralRef(job, general_ref_type::BUILDING_HOLDER); - return NULL; + return ref ? ref->getBuilding() : NULL; } df::unit *DFHack::Job::getWorker(df::job *job) { CHECK_NULL_POINTER(job); - for (size_t i = 0; i < job->general_refs.size(); i++) - { - VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]); - if (ref) - return ref->getUnit(); - } + auto ref = getGeneralRef(job, general_ref_type::UNIT_WORKER); - return NULL; + return ref ? ref->getUnit() : NULL; } void DFHack::Job::setJobCooldown(df::building *workshop, df::unit *worker, int cooldown) @@ -326,8 +316,8 @@ bool DFHack::Job::removeWorker(df::job *job, int cooldown) for (size_t i = 0; i < job->general_refs.size(); i++) { - VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]); - if (!ref) + df::general_ref *ref = job->general_refs[i]; + if (ref->getType() != general_ref_type::UNIT_WORKER) continue; auto worker = ref->getUnit(); diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 655371091..fd1ccc523 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -584,8 +584,8 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2) if ( dx == 0 && dy == 0 ) { //check for forbidden hatches and floors and such - df::enums::tile_building_occ::tile_building_occ upOcc = index_tile(block2->occupancy,pos2).bits.building; - if ( upOcc == df::enums::tile_building_occ::Impassable || upOcc == df::enums::tile_building_occ::Obstacle || upOcc == df::enums::tile_building_occ::Floored ) + df::tile_building_occ upOcc = index_tile(block2->occupancy,pos2).bits.building; + if ( upOcc == tile_building_occ::Impassable || upOcc == tile_building_occ::Obstacle || upOcc == tile_building_occ::Floored ) return false; if ( shape1 == tiletype_shape::STAIR_UPDOWN && shape2 == shape1 ) @@ -617,7 +617,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2) return false; //unusable ramp //there has to be an unforbidden hatch above the ramp - if ( index_tile(block2->occupancy,pos2).bits.building != df::enums::tile_building_occ::Dynamic ) + if ( index_tile(block2->occupancy,pos2).bits.building != tile_building_occ::Dynamic ) return false; //note that forbidden hatches have Floored occupancy. unforbidden ones have dynamic occupancy df::building* building = Buildings::findAtTile(pos2); @@ -625,7 +625,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2) out << __FILE__ << ", line " << __LINE__ << ": couldn't find hatch.\n"; return false; } - if ( building->getType() != df::enums::building_type::Hatch ) { + if ( building->getType() != building_type::Hatch ) { return false; } return true; @@ -661,8 +661,8 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2) if ( !blockUp ) return false; - df::enums::tile_building_occ::tile_building_occ occupancy = index_tile(blockUp->occupancy,up).bits.building; - if ( occupancy == df::enums::tile_building_occ::Obstacle || occupancy == df::enums::tile_building_occ::Floored || occupancy == df::enums::tile_building_occ::Impassable ) + df::tile_building_occ occupancy = index_tile(blockUp->occupancy,up).bits.building; + if ( occupancy == tile_building_occ::Obstacle || occupancy == tile_building_occ::Floored || occupancy == tile_building_occ::Impassable ) return false; return true; } From 0f4684f29dc77c0d2d79ee1ec99cf2cb9300b896 Mon Sep 17 00:00:00 2001 From: Quietust Date: Fri, 25 Apr 2014 11:04:21 -0500 Subject: [PATCH 12/18] Add Items::getItemBaseValue and Items::getValue, available from Lua --- Lua API.html | 9 + Lua API.rst | 12 ++ library/LuaApi.cpp | 2 + library/include/modules/Items.h | 6 + library/modules/Items.cpp | 297 ++++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+) diff --git a/Lua API.html b/Lua API.html index bd17cc644..d73384241 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1252,6 +1252,9 @@ the flags in the job item.

  • dfhack.job.isSuitableMaterial(job_item, mat_type, mat_index)

    Likewise, if replacing material.

  • +
  • dfhack.job.getName(job)

    +

    Returns the job's description, as seen in the Units and Jobs screens.

    +
  • @@ -1420,6 +1423,12 @@ Returns false in case of error.

  • dfhack.items.getSubtypeDef(item_type, subtype)

    Returns the raw definition for the given item type and subtype, or nil if invalid.

  • +
  • dfhack.items.getItemBaseValue(item_type, subtype, material, mat_index)

    +

    Calculates the base value for an item of the specified type and material.

    +
  • +
  • dfhack.items.getValue(item)

    +

    Calculates the Basic Value of an item, as seen in the View Item screen.

    +
  • diff --git a/Lua API.rst b/Lua API.rst index 072d9265f..9afbb1e78 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -979,6 +979,10 @@ Job module Likewise, if replacing material. +* ``dfhack.job.getName(job)`` + + Returns the job's description, as seen in the Units and Jobs screens. + Units module ------------ @@ -1187,6 +1191,14 @@ Items module Returns the raw definition for the given item type and subtype, or *nil* if invalid. +* ``dfhack.items.getItemBaseValue(item_type, subtype, material, mat_index)`` + + Calculates the base value for an item of the specified type and material. + +* ``dfhack.items.getValue(item)`` + + Calculates the Basic Value of an item, as seen in the View Item screen. + Maps module ----------- diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 4288802ea..3a374445f 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1476,6 +1476,8 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = { WRAPM(Items, isCasteMaterial), WRAPM(Items, getSubtypeCount), WRAPM(Items, getSubtypeDef), + WRAPM(Items, getItemBaseValue), + WRAPM(Items, getValue), WRAPN(moveToGround, items_moveToGround), WRAPN(moveToContainer, items_moveToContainer), WRAPN(moveToBuilding, items_moveToBuilding), diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 34ca98162..e5b6eb4df 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -173,5 +173,11 @@ DFHACK_EXPORT bool remove(MapExtras::MapCache &mc, df::item *item, bool no_uncat /// Detaches the items from its current location and turns it into a projectile DFHACK_EXPORT df::proj_itemst *makeProjectile(MapExtras::MapCache &mc, df::item *item); + +/// Gets value of base-quality item with specified type and material +DFHACK_EXPORT int getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_subtype); + +/// Gets the value of a specific item, ignoring civ values and trade agreements +DFHACK_EXPORT int getValue(df::item *item); } } diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index b243e5d85..d766b4166 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1022,3 +1022,300 @@ df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item) return proj; } + +int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_subtype) +{ + int value = 0; + switch (item_type) + { + case item_type::BAR: + case item_type::SMALLGEM: + case item_type::BLOCKS: + case item_type::SKIN_TANNED: + value = 5; + break; + + case item_type::ROUGH: + case item_type::BOULDER: + case item_type::WOOD: + value = 3; + break; + + case item_type::DOOR: + case item_type::FLOODGATE: + case item_type::BED: + case item_type::CHAIR: + case item_type::CHAIN: + case item_type::FLASK: + case item_type::GOBLET: + case item_type::INSTRUMENT: + case item_type::TOY: + case item_type::CAGE: + case item_type::BARREL: + case item_type::BUCKET: + case item_type::ANIMALTRAP: + case item_type::TABLE: + case item_type::COFFIN: + case item_type::BOX: + case item_type::BIN: + case item_type::ARMORSTAND: + case item_type::WEAPONRACK: + case item_type::CABINET: + case item_type::FIGURINE: + case item_type::AMULET: + case item_type::SCEPTER: + case item_type::CROWN: + case item_type::RING: + case item_type::EARRING: + case item_type::BRACELET: + case item_type::GEM: + case item_type::ANVIL: + case item_type::TOTEM: + case item_type::BACKPACK: + case item_type::QUIVER: + case item_type::BALLISTAARROWHEAD: + case item_type::PIPE_SECTION: + case item_type::HATCH_COVER: + case item_type::GRATE: + case item_type::QUERN: + case item_type::MILLSTONE: + case item_type::SPLINT: + case item_type::CRUTCH: + case item_type::SLAB: + case item_type::BOOK: + value = 10; + break; + + case item_type::WINDOW: + case item_type::STATUE: + value = 25; + break; + + case item_type::CORPSE: + case item_type::CORPSEPIECE: + case item_type::REMAINS: + return 0; + + case item_type::WEAPON: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.weapons.size()) + value = world->raws.itemdefs.weapons[item_subtype]->value; + else + value = 10; + break; + + case item_type::ARMOR: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.armor.size()) + value = world->raws.itemdefs.armor[item_subtype]->value; + else + value = 10; + break; + + case item_type::SHOES: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.shoes.size()) + value = world->raws.itemdefs.shoes[item_subtype]->value; + else + value = 5; + break; + + case item_type::SHIELD: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.shields.size()) + value = world->raws.itemdefs.shields[item_subtype]->value; + else + value = 10; + break; + + case item_type::HELM: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.helms.size()) + value = world->raws.itemdefs.helms[item_subtype]->value; + else + value = 10; + break; + + case item_type::GLOVES: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.gloves.size()) + value = world->raws.itemdefs.gloves[item_subtype]->value; + else + value = 5; + break; + + case item_type::AMMO: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.ammo.size()) + value = world->raws.itemdefs.ammo[item_subtype]->value; + else + value = 1; + break; + + case item_type::MEAT: + case item_type::PLANT: + case item_type::LEAVES: + case item_type::CHEESE: + value = 2; + break; + + case item_type::FISH: + case item_type::FISH_RAW: + case item_type::EGG: + value = 2; + if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + { + auto creature = world->raws.creatures.all[mat_type]; + if (mat_subtype >= 0 && mat_type < creature->caste.size()) + { + auto caste = creature->caste[mat_subtype]; + mat_type = caste->misc.bone_mat; + mat_subtype = caste->misc.bone_matidx; + } + } + break; + + case item_type::VERMIN: + value = 0; + if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + { + auto creature = world->raws.creatures.all[mat_type]; + if (mat_subtype >= 0 && mat_type < creature->caste.size()) + value = creature->caste[mat_subtype]->misc.petvalue; + } + value /= 2; + if (!value) + return 1; + return value; + + case item_type::PET: + if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + { + auto creature = world->raws.creatures.all[mat_type]; + if (mat_subtype >= 0 && mat_type < creature->caste.size()) + return creature->caste[mat_subtype]->misc.petvalue; + } + return 0; + + case item_type::SEEDS: + case item_type::DRINK: + case item_type::POWDER_MISC: + case item_type::LIQUID_MISC: + case item_type::COIN: + case item_type::GLOB: + case item_type::ORTHOPEDIC_CAST: + value = 1; + break; + + case item_type::THREAD: + value = 6; + break; + + case item_type::CLOTH: + value = 7; + break; + + case item_type::PANTS: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.pants.size()) + value = world->raws.itemdefs.pants[item_subtype]->value; + else + value = 10; + break; + + case item_type::CATAPULTPARTS: + case item_type::BALLISTAPARTS: + case item_type::TRAPPARTS: + value = 30; + break; + + case item_type::SIEGEAMMO: + case item_type::TRACTION_BENCH: + value = 20; + break; + + case item_type::TRAPCOMP: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.trapcomps.size()) + value = world->raws.itemdefs.trapcomps[item_subtype]->value; + else + value = 10; + break; + + case item_type::FOOD: + return 10; + +// case item_type::ROCK: + default: + return 0; + + case item_type::TOOL: + if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.tools.size()) + value = world->raws.itemdefs.tools[item_subtype]->value; + else + value = 10; + break; + } + + MaterialInfo mat; + if (mat.decode(mat_type, mat_subtype)) + value *= mat.material->material_value; + return value; +} + +int Items::getValue(df::item *item) +{ + CHECK_NULL_POINTER(item); + + int16_t item_type = item->getType(); + int16_t item_subtype = item->getSubtype(); + int16_t mat_type = item->getMaterial(); + int32_t mat_subtype = item->getMaterialIndex(); + + // Get base value for item type, subtype, and material + int value = getItemBaseValue(item_type, item_subtype, mat_type, mat_subtype); + + // Ignore entity value modifications + + // Improve value based on quality + int quality = item->getQuality(); + value *= (quality + 1); + if (quality == 5) + value *= 2; + + // Add improvement values + int impValue = item->getThreadDyeValue(NULL) + item->getImprovementsValue(NULL); + if (item_type == item_type::AMMO) // Ammo improvements are worth less + impValue /= 30; + value += impValue; + + // Degrade value due to wear + switch (item->getWear()) + { + case 1: + value = value * 3 / 4; + break; + case 2: + value = value / 2; + break; + case 3: + value = value / 4; + break; + } + + // Ignore value bonuses from magic, since that never actually happens + + // Artifacts have 10x value + if (item->flags.bits.artifact_mood) + value *= 10; + + // Boost value from stack size + value *= item->getStackSize(); + // ...but not for coins + if (item_type == item_type::COIN) + { + value /= 500; + if (!value) + value = 1; + } + + // Handle vermin swarms + if (item_type == item_type::VERMIN || item_type == item_type::PET) + { + int divisor = world->raws.creatures.all[mat_type]->caste[mat_subtype]->misc.petvalue_divisor; + if (divisor > 1) + value /= divisor; + } + return value; +} \ No newline at end of file From 98325757e2511dc3dfbb48aeb6ca61f63857cddb Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 30 Apr 2014 20:44:22 +0400 Subject: [PATCH 13/18] Fix signed-unsigned mismatch warnings in the new item value code. Unsigned comparison allows catching negatives in the same check too. Also, it didn't check the values in getValue. --- library/modules/Items.cpp | 41 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index d766b4166..8a3d0e128 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1097,49 +1097,49 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat return 0; case item_type::WEAPON: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.weapons.size()) + if (size_t(item_subtype) < world->raws.itemdefs.weapons.size()) value = world->raws.itemdefs.weapons[item_subtype]->value; else value = 10; break; case item_type::ARMOR: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.armor.size()) + if (size_t(item_subtype) < world->raws.itemdefs.armor.size()) value = world->raws.itemdefs.armor[item_subtype]->value; else value = 10; break; case item_type::SHOES: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.shoes.size()) + if (size_t(item_subtype) < world->raws.itemdefs.shoes.size()) value = world->raws.itemdefs.shoes[item_subtype]->value; else value = 5; break; case item_type::SHIELD: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.shields.size()) + if (size_t(item_subtype) < world->raws.itemdefs.shields.size()) value = world->raws.itemdefs.shields[item_subtype]->value; else value = 10; break; case item_type::HELM: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.helms.size()) + if (size_t(item_subtype) < world->raws.itemdefs.helms.size()) value = world->raws.itemdefs.helms[item_subtype]->value; else value = 10; break; case item_type::GLOVES: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.gloves.size()) + if (size_t(item_subtype) < world->raws.itemdefs.gloves.size()) value = world->raws.itemdefs.gloves[item_subtype]->value; else value = 5; break; case item_type::AMMO: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.ammo.size()) + if (size_t(item_subtype) < world->raws.itemdefs.ammo.size()) value = world->raws.itemdefs.ammo[item_subtype]->value; else value = 1; @@ -1156,10 +1156,10 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat case item_type::FISH_RAW: case item_type::EGG: value = 2; - if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + if (size_t(mat_type) < world->raws.creatures.all.size()) { auto creature = world->raws.creatures.all[mat_type]; - if (mat_subtype >= 0 && mat_type < creature->caste.size()) + if (size_t(mat_subtype) < creature->caste.size()) { auto caste = creature->caste[mat_subtype]; mat_type = caste->misc.bone_mat; @@ -1170,10 +1170,10 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat case item_type::VERMIN: value = 0; - if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + if (size_t(mat_type) < world->raws.creatures.all.size()) { auto creature = world->raws.creatures.all[mat_type]; - if (mat_subtype >= 0 && mat_type < creature->caste.size()) + if (size_t(mat_subtype) < creature->caste.size()) value = creature->caste[mat_subtype]->misc.petvalue; } value /= 2; @@ -1182,10 +1182,10 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat return value; case item_type::PET: - if (mat_type >= 0 && mat_type < world->raws.creatures.all.size()) + if (size_t(mat_type) < world->raws.creatures.all.size()) { auto creature = world->raws.creatures.all[mat_type]; - if (mat_subtype >= 0 && mat_type < creature->caste.size()) + if (size_t(mat_subtype) < creature->caste.size()) return creature->caste[mat_subtype]->misc.petvalue; } return 0; @@ -1209,7 +1209,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat break; case item_type::PANTS: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.pants.size()) + if (size_t(item_subtype) < world->raws.itemdefs.pants.size()) value = world->raws.itemdefs.pants[item_subtype]->value; else value = 10; @@ -1227,7 +1227,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat break; case item_type::TRAPCOMP: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.trapcomps.size()) + if (size_t(item_subtype) < world->raws.itemdefs.trapcomps.size()) value = world->raws.itemdefs.trapcomps[item_subtype]->value; else value = 10; @@ -1241,7 +1241,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat return 0; case item_type::TOOL: - if (item_subtype >= 0 && item_subtype < world->raws.itemdefs.tools.size()) + if (size_t(item_subtype) < world->raws.itemdefs.tools.size()) value = world->raws.itemdefs.tools[item_subtype]->value; else value = 10; @@ -1313,9 +1313,12 @@ int Items::getValue(df::item *item) // Handle vermin swarms if (item_type == item_type::VERMIN || item_type == item_type::PET) { - int divisor = world->raws.creatures.all[mat_type]->caste[mat_subtype]->misc.petvalue_divisor; - if (divisor > 1) - value /= divisor; + int divisor = 1; + auto creature = vector_get(world->raws.creatures.all, mat_type); + if (creature && size_t(mat_subtype) < creature->caste.size()) + divisor = creature->caste[mat_subtype]->misc.petvalue_divisor; + if (divisor > 1) + value /= divisor; } return value; } \ No newline at end of file From 415cdad48999fe0c1bfaa2f70a479e37a4574207 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 30 Apr 2014 21:28:02 +0400 Subject: [PATCH 14/18] Make using new for allocating DF objects with vtables a compile-time error. When done from plugins, it doesn't correctly initialize the vtable because of some weird things MSVC does, so the only safe way is to use df::allocate(). For consistency, it is also enforced for code in the main library. It reveals the issue in the digging invaders plugin, first found by warmist. This change is linked to a modification in df-structures codegen. --- library/include/df/custom/viewscreen.methods.inc | 1 + library/modules/Items.cpp | 8 +++++++- library/xml | 2 +- plugins/diggingInvaders/assignJob.cpp | 8 ++++---- 4 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 library/include/df/custom/viewscreen.methods.inc diff --git a/library/include/df/custom/viewscreen.methods.inc b/library/include/df/custom/viewscreen.methods.inc new file mode 100644 index 000000000..c5d277716 --- /dev/null +++ b/library/include/df/custom/viewscreen.methods.inc @@ -0,0 +1 @@ +friend struct df::interfacest; diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 8a3d0e128..38d63d867 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -997,16 +997,22 @@ df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item) if (!ref) return NULL; + auto proj = df::allocate(); + if (!proj) { + delete ref; + return NULL; + } + if (!detachItem(mc, item)) { delete ref; + delete proj; return NULL; } item->pos = pos; item->flags.bits.in_job = true; - auto proj = new df::proj_itemst(); proj->link = new df::proj_list_link(); proj->link->item = proj; proj->id = (*proj_next_id)++; diff --git a/library/xml b/library/xml index 9c280e13a..1558b2373 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9c280e13ab2e2421b8b8cab4a0c146fffc355a3c +Subproject commit 1558b2373fcf8fa63928167e007a7f9a709888b6 diff --git a/plugins/diggingInvaders/assignJob.cpp b/plugins/diggingInvaders/assignJob.cpp index 9472fb0e7..eff2de4ad 100644 --- a/plugins/diggingInvaders/assignJob.cpp +++ b/plugins/diggingInvaders/assignJob.cpp @@ -88,10 +88,10 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_mapjob_type = df::enums::job_type::DestroyBuilding; //job->flags.bits.special = 1; - df::general_ref_building_holderst* buildingRef = new df::general_ref_building_holderst; + df::general_ref_building_holderst* buildingRef = df::allocate(); buildingRef->building_id = building->id; job->general_refs.push_back(buildingRef); - df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst; + df::general_ref_unit_workerst* workerRef = df::allocate(); workerRef->unit_id = firstInvader->id; job->general_refs.push_back(workerRef); getRidOfOldJob(firstInvader); @@ -118,7 +118,7 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_mapjob_type = df::enums::job_type::RemoveConstruction; - df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst; + df::general_ref_unit_workerst* workerRef = df::allocate(); workerRef->unit_id = firstInvader->id; job->general_refs.push_back(workerRef); job->pos = pt2; @@ -189,7 +189,7 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_mappos = workHere; firstInvader->path.dest = goHere; location = goHere; - df::general_ref_unit_workerst* ref = new df::general_ref_unit_workerst; + df::general_ref_unit_workerst* ref = df::allocate(); ref->unit_id = firstInvader->id; job->general_refs.push_back(ref); firstInvader->job.hunt_target = NULL; From 401c8f3026577701725c0aff0da022142a1aa59c Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 8 Mar 2014 10:09:53 +0200 Subject: [PATCH 15/18] gm-editor: Added enum printing and selection. gm-editor: a bugfix for some entries not having fields --- scripts/gui/gm-editor.lua | 49 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua index 269d09c31..d6d4c0e17 100644 --- a/scripts/gui/gm-editor.lua +++ b/scripts/gui/gm-editor.lua @@ -11,6 +11,7 @@ local keybindings={ insert={key="CUSTOM_ALT_I",desc="Insert a new value to the vector"}, delete={key="CUSTOM_ALT_D",desc="Delete selected entry"}, help={key="HELP",desc="Show this help"}, + NOT_USED={key="SEC_SELECT",desc="Choose an enum value from a list"}, --not a binding... } function getTargetFromScreens() local my_trg @@ -75,6 +76,7 @@ function GmEditorUi:init(args) local helpPage=widgets.Panel{ subviews={widgets.Label{text=helptext,frame = {l=1,t=1,yalign=0}}}} local mainList=widgets.List{view_id="list_main",choices={},frame = {l=1,t=3,yalign=0},on_submit=self:callback("editSelected"), + on_submit2=self:callback("editSelectedEnum"), text_pen=dfhack.pen.parse{fg=COLOR_DARKGRAY,bg=0},cursor_pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}} local mainPage=widgets.Panel{ subviews={ @@ -155,6 +157,29 @@ end function GmEditorUi:currentTarget() return self.stack[#self.stack] end +function GmEditorUi:editSelectedEnum(index,choice) + local trg=self:currentTarget() + local trg_key=trg.keys[index] + if trg.target._field==nil then qerror("not an enum") end + local enum=trg.target:_field(trg_key)._type + + if enum._kind=="enum-type" then + local list={} + for i=enum._first_item, enum._last_item do + table.insert(list,{text=tostring(enum[i]),value=i}) + end + guiScript.start(function() + local ret,idx,choice=guiScript.showListPrompt("Choose item:",nil,3,list) + if ret then + trg.target[trg_key]=choice.value + self:updateTarget(true) + end + end) + + else + qerror("not an enum") + end +end function GmEditorUi:editSelected(index,choice) local trg=self:currentTarget() local trg_key=trg.keys[index] @@ -206,7 +231,6 @@ function GmEditorUi:set(key,input) self:updateTarget(true) end function GmEditorUi:onInput(keys) - if keys.LEAVESCREEN then if self.subviews.pages:getSelected()==2 then self.subviews.pages:setSelected(1) @@ -233,6 +257,27 @@ function GmEditorUi:onInput(keys) self.super.onInput(self,keys) end +function getStringValue(trg,field) + local obj=trg.target + + local text=tostring(obj[field]) + pcall(function() + if obj._field ~= nil then + local enum=obj:_field(field)._type + if enum._kind=="enum-type" then + text=text.."("..tostring(enum[obj[field]])..")" + end + end + end) + return text + + --[[local ok,ret=pcall(function() return trg.target[field]._enum end) + if ok then + print(trg.target[field]._kind,ok,ret) + text=text.."("..tostring(ret[field])..")" + end + return text]] +end function GmEditorUi:updateTarget(preserve_pos,reindex) local trg=self:currentTarget() if reindex then @@ -244,7 +289,7 @@ function GmEditorUi:updateTarget(preserve_pos,reindex) self.subviews.lbl_current_item:itemById('name').text=tostring(trg.target) local t={} for k,v in pairs(trg.keys) do - table.insert(t,{text={{text=string.format("%-25s",tostring(v))},{gap=1,text=tostring(trg.target[v]),}}}) + table.insert(t,{text={{text=string.format("%-25s",tostring(v))},{gap=1,text=getStringValue(trg,v)}}}) end local last_pos if preserve_pos then From 9733b30ed6260bb7c316fe8a6bd7c4948a9190b4 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 26 Apr 2014 21:50:52 +0300 Subject: [PATCH 16/18] gui/gm-editor: Fixed enum editing. And added free pointer/reinterpret options --- scripts/gui/gm-editor.lua | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua index d6d4c0e17..4d0b09c6a 100644 --- a/scripts/gui/gm-editor.lua +++ b/scripts/gui/gm-editor.lua @@ -2,6 +2,7 @@ local gui = require 'gui' local dialog = require 'gui.dialogs' local widgets =require 'gui.widgets' +local guiScript = require 'gui.script' local args={...} local keybindings={ @@ -10,6 +11,7 @@ local keybindings={ lua_set={key="CUSTOM_ALT_S",desc="Set by using a lua function"}, insert={key="CUSTOM_ALT_I",desc="Insert a new value to the vector"}, delete={key="CUSTOM_ALT_D",desc="Delete selected entry"}, + reinterpret={key="CUSTOM_ALT_R",desc="Open selected entry as something else"}, help={key="HELP",desc="Show this help"}, NOT_USED={key="SEC_SELECT",desc="Choose an enum value from a list"}, --not a binding... } @@ -180,6 +182,14 @@ function GmEditorUi:editSelectedEnum(index,choice) qerror("not an enum") end end +function GmEditorUi:openReinterpret(key) + local trg=self:currentTarget() + dialog.showInputPrompt(tostring(trg_key),"Enter new type:",COLOR_WHITE, + "",function(choice) + local ntype=df[tp] + self:pushTarget(df.reinterpret_cast(ntype,trg.target[key])) + end) +end function GmEditorUi:editSelected(index,choice) local trg=self:currentTarget() local trg_key=trg.keys[index] @@ -242,17 +252,17 @@ function GmEditorUi:onInput(keys) local _,stoff=df.sizeof(trg.target) local size,off=df.sizeof(trg.target:_field(self:getSelectedKey())) dialog.showMessage("Offset",string.format("Size hex=%x,%x dec=%d,%d\nRelative hex=%x dec=%d",size,off,size,off,off-stoff,off-stoff),COLOR_WHITE) - --elseif keys.CUSTOM_ALT_F then --filter? + elseif keys[keybindings.find.key] then self:find() elseif keys[keybindings.lua_set.key] then self:set(self:getSelectedKey()) - --elseif keys.CUSTOM_I then - -- self:insertSimple() elseif keys[keybindings.insert.key] then --insert self:insertNew() elseif keys[keybindings.delete.key] then --delete self:deleteSelected(self:getSelectedKey()) + elseif keys[keybindings.reinterpret.key] then + self:openReinterpret(self:getSelectedKey()) end self.super.onInput(self,keys) @@ -270,13 +280,6 @@ function getStringValue(trg,field) end end) return text - - --[[local ok,ret=pcall(function() return trg.target[field]._enum end) - if ok then - print(trg.target[field]._kind,ok,ret) - text=text.."("..tostring(ret[field])..")" - end - return text]] end function GmEditorUi:updateTarget(preserve_pos,reindex) local trg=self:currentTarget() @@ -337,6 +340,8 @@ if #args~=0 then show_editor(t) end dialog.showInputPrompt("Gm Editor", "Object to edit:", COLOR_GRAY, "",thunk) + elseif args[1]=="free" then + show_editor(df.reinterpret_cast(df[args[2]],args[3])) else local t=load("return "..args[1])() show_editor(t) From 94904eb66b9c2207ea26bd3a178e8611251db32d Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 4 May 2014 18:27:18 +0300 Subject: [PATCH 17/18] Updated NEWS. --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index b43ced4f0..0bd107f07 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ DFHack future Internals: - support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua). + - eventful now has a more friendly way of making custom sidebars + - new plugin: building-hacks. Allows to add custom functionality and/or animations to buildings. New commands: - move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands From cca801463019cfa62ec2205158addc8ed3b4dbc6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 4 May 2014 19:53:48 +0400 Subject: [PATCH 18/18] Mention more clearly how the data structures are defined in the Lua API. --- Lua API.html | 7 ++++--- Lua API.rst | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Lua API.html b/Lua API.html index 1d61b1797..1a2888eb0 100644 --- a/Lua API.html +++ b/Lua API.html @@ -455,9 +455,10 @@ It does not describe all of the utility functions implemented by Lua files located in hack/lua/...

    DF data structure wrapper

    -

    DF structures described by the xml files in library/xml are exported -to lua code as a tree of objects and functions under the df global, -which broadly maps to the df namespace in C++.

    +

    Data structures of the game are defined in XML files located in library/xml +(and online at http://github.com/DFHack/df-structures), and automatically exported +to lua code as a tree of objects and functions under the df global, which +also broadly maps to the df namespace in the headers generated for C++.

    WARNING: The wrapper provides almost raw access to the memory of the game, so mistakes in manipulating objects are as likely to crash the game as equivalent plain C++ code would be. E.g. NULL diff --git a/Lua API.rst b/Lua API.rst index 013e8f18d..3f2cf64ad 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -25,9 +25,10 @@ implemented by Lua files located in hack/lua/... DF data structure wrapper ========================= -DF structures described by the xml files in library/xml are exported -to lua code as a tree of objects and functions under the ``df`` global, -which broadly maps to the ``df`` namespace in C++. +Data structures of the game are defined in XML files located in library/xml +(and online at http://github.com/DFHack/df-structures), and automatically exported +to lua code as a tree of objects and functions under the ``df`` global, which +also broadly maps to the ``df`` namespace in the headers generated for C++. **WARNING**: The wrapper provides almost raw access to the memory of the game, so mistakes in manipulating objects are as likely to