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) {