diff --git a/Lua API.rst b/Lua API.rst index d58847375..ab5cd1683 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -3329,21 +3329,26 @@ 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: + 3. consume -- how much machine power is needed to work. Disables reactions if not supplied enough and needs_power=1 + 4. produce -- how much machine power is produced. + 5. needs_power -- if produced in network < consumed stop working, default true + 6. gears -- a table or ``{x=?,y=?}`` of connection points for machines + 7. action -- a table of number (how much ticks to skip) and a function which gets called on shop update + 8. 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) - 8. 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 + 9. 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 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) +``getPower(building)`` returns two number - produced and consumed power if building can be modified and returns nothing otherwise + +``setPower(building,produced,consumed)`` sets current productiona and consumption for a building. + Examples -------- diff --git a/NEWS b/NEWS index cf1883735..26f184f0e 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ DFHack Future Fixes Made PRELOAD_LIB more extensible on Linux add-spatter/eventful: Fixed crash on world load + building-hacks: made buildings produce/consume correct amount of power Gave add-thought a proper subthought arg. fix-armory compiles and is available again (albeit with issues) gui/gm-editor: Added search option (accessible with "s") @@ -24,6 +25,7 @@ DFHack Future tradereq-pet-gender: Displays pet genders on the trade request screen Removed Misc Improvements + building-hacks: Added a way to allow building to work even if it consumes more power than is available. Added setPower/getPower functions. catsplosion: Can now trigger pregnancies in (most) other creatures exportlegends: 'info' and 'all' exports legends_plus xml with more data for legends utilities remotefortressreader: Exposes more information diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 62349e519..678675258 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -19,6 +19,8 @@ #include "df/coord.h" #include "df/tile_building_occ.h" #include "df/building_drawbuffer.h" +#include "df/general_ref_creaturest.h" // needed for power information storage +#include "modules/Buildings.h" #include @@ -42,6 +44,7 @@ struct workshop_hack_data //machine stuff df::machine_tile_set connections; df::power_info powerInfo; + bool needs_power; //animation std::vector > frames; bool machine_timing; //6 frames used in vanilla @@ -60,6 +63,7 @@ DFHACK_PLUGIN_LUA_EVENTS { DFHACK_LUA_EVENT(onUpdateAction), DFHACK_LUA_END }; + struct work_hook : df::building_workshopst{ typedef df::building_workshopst interpose_base; @@ -77,6 +81,54 @@ struct work_hook : df::building_workshopst{ { return getBuildStage() >= getMaxBuildStage(); } + bool get_current_power(df::power_info* info) + { + if (workshop_hack_data* def = find_def()) + { + df::general_ref_creaturest* ref = static_cast(DFHack::Buildings::getGeneralRef(this, general_ref_type::CREATURE)); + if (ref) + { + info->produced = ref->anon_1; + info->consumed = ref->anon_2; + return true; + } + else + { + info->produced = def->powerInfo.produced; + info->consumed = def->powerInfo.consumed; + return true; + } + //try getting ref, if not return from def + } + return false; + } + void set_current_power(int produced, int consumed) + { + if(machine.machine_id != -1) //if connected to machine, update the machine network production + { + df::machine* target_machine = df::machine::find(machine.machine_id); + if (target_machine) + { + df::power_info old_power; + get_current_power(&old_power); + target_machine->min_power += consumed - old_power.consumed; + target_machine->cur_power += produced - old_power.produced; + } + } + df::general_ref_creaturest* ref = static_cast(DFHack::Buildings::getGeneralRef(this, general_ref_type::CREATURE)); + if (ref) + { + ref->anon_1 = produced; + ref->anon_2 = consumed; + } + else + { + ref = df::allocate(); + ref->anon_1 = produced; + ref->anon_2 = consumed; + general_refs.push_back(ref); + } + } DEFINE_VMETHOD_INTERPOSE(uint32_t,getImpassableOccupancy,()) { if(auto def = find_def()) @@ -89,13 +141,12 @@ struct work_hook : df::building_workshopst{ 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; - } - + if (auto def = find_def()) + { + df::power_info power; + get_current_power(info); + return; + } INTERPOSE_NEXT(getPowerInfo)(info); } DEFINE_VMETHOD_INTERPOSE(df::machine_info*, getMachineInfo, ()) @@ -108,7 +159,9 @@ struct work_hook : df::building_workshopst{ DEFINE_VMETHOD_INTERPOSE(bool, isPowerSource, ()) { workshop_hack_data* def=find_def(); - if (def && def->powerInfo.produced>0) + df::power_info power; + get_current_power(&power); + if (def && power.produced>0) return true; return INTERPOSE_NEXT(isPowerSource)(); @@ -164,7 +217,11 @@ struct work_hook : df::building_workshopst{ { if (auto def = find_def()) { - if(def->powerInfo.consumed==0) + if (!def->needs_power) + return false; + df::power_info power; + get_current_power(&power); + if (power.consumed == 0) return false; if(machine.machine_id==-1) return true; @@ -257,6 +314,9 @@ IMPLEMENT_VMETHOD_INTERPOSE(work_hook, isUnpowered); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, canBeRoomSubset); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, updateAction); IMPLEMENT_VMETHOD_INTERPOSE(work_hook, drawBuilding); + + + void clear_mapping() { hacked_workshops.clear(); @@ -318,9 +378,10 @@ static int addBuilding(lua_State* L) 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); //table of machine connection points - luaL_checktype(L,5,LUA_TTABLE); - lua_pushvalue(L,5); + luaL_checktype(L,6,LUA_TTABLE); + lua_pushvalue(L,6); lua_pushnil(L); while (lua_next(L, -2) != 0) { lua_getfield(L,-1,"x"); @@ -337,12 +398,12 @@ static int addBuilding(lua_State* L) } lua_pop(L,1); //updates - newDefinition.skip_updates=luaL_optinteger(L,6,0); + newDefinition.skip_updates=luaL_optinteger(L,7,0); //animation - if(!lua_isnil(L,7)) + if(!lua_isnil(L,8)) { - loadFrames(L,newDefinition,7); - newDefinition.frame_skip=luaL_optinteger(L,8,-1); + 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) @@ -350,12 +411,41 @@ static int addBuilding(lua_State* L) else newDefinition.machine_timing=false; } - newDefinition.room_subset=luaL_optinteger(L,9,-1); + newDefinition.room_subset=luaL_optinteger(L,10,-1); hacked_workshops[newDefinition.myType]=newDefinition; return 0; } +static void setPower(df::building_workshopst* workshop, int power_produced, int power_consumed) +{ + work_hook* ptr = static_cast(workshop); + if (workshop_hack_data* def = ptr->find_def())//check if it's really hacked workshop + { + ptr->set_current_power(power_produced, power_consumed); + } +} +static int getPower(lua_State*L) +{ + auto workshop = Lua::CheckDFObject(L, 1); + work_hook* ptr = static_cast(workshop); + if (!ptr) + return 0; + if (workshop_hack_data* def = ptr->find_def())//check if it's really hacked workshop + { + df::power_info info; + ptr->get_current_power(&info); + lua_pushinteger(L, info.produced); + lua_pushinteger(L, info.consumed); + return 2; + } + return 0; +} +DFHACK_PLUGIN_LUA_FUNCTIONS{ + DFHACK_LUA_FUNCTION(setPower), + DFHACK_LUA_END +}; DFHACK_PLUGIN_LUA_COMMANDS{ DFHACK_LUA_COMMAND(addBuilding), + DFHACK_LUA_COMMAND(getPower), DFHACK_LUA_END }; static void enable_hooks(bool enable) diff --git a/plugins/lua/building-hacks.lua b/plugins/lua/building-hacks.lua index 00a7fe3f2..4cd12ad07 100644 --- a/plugins/lua/building-hacks.lua +++ b/plugins/lua/building-hacks.lua @@ -3,6 +3,8 @@ 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) from here: registerBuilding{ name -- custom workshop id e.g. SOAPMAKER << required! @@ -85,6 +87,7 @@ function registerBuilding(args) end local consume=args.consume or 0 local produce=args.produce or 0 + local needs_power=args.needs_power or 1 local gears=args.gears or {} local action=args.action --could be nil local updateSkip=0 @@ -103,7 +106,7 @@ function registerBuilding(args) frames=processFrames(shop_def,animate.frames) end local roomSubset=args.canBeRoomSubset or -1 - addBuilding(shop_id,fix_impassible,consume,produce,gears,updateSkip,frames,frameLength,roomSubset) + addBuilding(shop_id,fix_impassible,consume,produce,needs_power,gears,updateSkip,frames,frameLength,roomSubset) end return _ENV \ No newline at end of file