|  |  |  | @ -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 <map> | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -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<std::vector<int> > frames; | 
		
	
		
			
				|  |  |  |  |     bool machine_timing; //with machine timing only 4 frames are possible
 | 
		
	
		
			
				|  |  |  |  |     std::vector<std::vector<graphic_tile> > 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<int32_t,workshop_hack_data> 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<graphic_tile> &cur_frame=def->frames[frame]; | 
		
	
		
			
				|  |  |  |  |             for(int i=0;i<cur_frame.size();i++) | 
		
	
		
			
				|  |  |  |  |             { | 
		
	
		
			
				|  |  |  |  |                 if(cur_frame[i].tile>=0) | 
		
	
		
			
				|  |  |  |  |                 { | 
		
	
		
			
				|  |  |  |  |                     int tx=i % w; | 
		
	
		
			
				|  |  |  |  |                     int ty=i / w; | 
		
	
		
			
				|  |  |  |  |                     db->tile[tx][ty]=cur_frame[i].tile; | 
		
	
		
			
				|  |  |  |  |                     db->back[tx][ty]=cur_frame[i].back; | 
		
	
		
			
				|  |  |  |  |                     db->bright[tx][ty]=cur_frame[i].bright; | 
		
	
		
			
				|  |  |  |  |                     db->fore[tx][ty]=cur_frame[i].fore; | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | }; | 
		
	
		
			
				|  |  |  |  | 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<graphic_tile> 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) | 
		
	
		
			
				|  |  |  |  | { | 
		
	
	
		
			
				
					|  |  |  | 
 |