diff --git a/plugins/building-hacks.cpp b/plugins/building-hacks.cpp index 94940c24851..34a18a6e100 100644 --- a/plugins/building-hacks.cpp +++ b/plugins/building-hacks.cpp @@ -20,6 +20,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 @@ -46,19 +47,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; @@ -94,6 +95,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; @@ -106,7 +108,6 @@ struct work_hook : df::building_workshopst{ info->consumed = def->powerInfo.consumed; return true; } - //try getting ref, if not return from def } return false; } @@ -124,6 +125,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; @@ -149,7 +151,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); @@ -159,7 +162,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)(); @@ -176,7 +180,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); @@ -187,7 +192,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); @@ -197,8 +203,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; @@ -341,7 +349,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(); @@ -415,58 +442,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); } @@ -477,7 +537,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); @@ -492,8 +553,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 d329018bbdc..03479204793 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