Converted outsideOnly plugin to Lua script.
parent
c3c025c034
commit
0aa2dc149f
@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
#include "Console.h"
|
|
||||||
#include "Core.h"
|
|
||||||
#include "DataDefs.h"
|
|
||||||
#include "Export.h"
|
|
||||||
#include "PluginManager.h"
|
|
||||||
|
|
||||||
#include "modules/Buildings.h"
|
|
||||||
#include "modules/EventManager.h"
|
|
||||||
#include "modules/Maps.h"
|
|
||||||
#include "modules/Once.h"
|
|
||||||
|
|
||||||
#include "df/coord.h"
|
|
||||||
#include "df/building.h"
|
|
||||||
#include "df/building_def.h"
|
|
||||||
#include "df/map_block.h"
|
|
||||||
#include "df/tile_designation.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
//TODO: check if building becomes inside/outside later
|
|
||||||
|
|
||||||
using namespace DFHack;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
DFHACK_PLUGIN_IS_ENABLED(enabled);
|
|
||||||
DFHACK_PLUGIN("outsideOnly");
|
|
||||||
|
|
||||||
static map<std::string, int32_t> registeredBuildings;
|
|
||||||
const int32_t OUTSIDE_ONLY = 1;
|
|
||||||
const int32_t EITHER = 0;
|
|
||||||
const int32_t INSIDE_ONLY = -1;
|
|
||||||
int32_t checkEvery = -1;
|
|
||||||
|
|
||||||
void buildingCreated(color_ostream& out, void* data);
|
|
||||||
void checkBuildings(color_ostream& out, void* data);
|
|
||||||
command_result outsideOnly(color_ostream& out, vector<string>& parameters);
|
|
||||||
|
|
||||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
|
||||||
{
|
|
||||||
commands.push_back(PluginCommand("outsideOnly", "Register buildings as inside/outside only. If the player attempts to construct them in an inapproprate place, the building plan automatically deconstructs.\n", &outsideOnly, false,
|
|
||||||
"outsideOnly:\n"
|
|
||||||
" outsideOnly outside [custom building name]\n"
|
|
||||||
" registers [custom building name] as outside-only\n"
|
|
||||||
" outsideOnly inside [custom building name]\n"
|
|
||||||
" registers [custom building name] as inside-only\n"
|
|
||||||
" outsideOnly either [custom building name]\n"
|
|
||||||
" unregisters [custom building name]\n"
|
|
||||||
" outsideOnly checkEvery [n]\n"
|
|
||||||
" checks for buildings that were previously in appropriate inside/outsideness but are not anymore every [n] ticks. If [n] is negative, disables checking.\n"
|
|
||||||
" outsideOnly clear\n"
|
|
||||||
" unregisters all custom buildings\n"
|
|
||||||
" enable outsideOnly\n"
|
|
||||||
" enables the plugin. Plugin must be enabled to function!\n"
|
|
||||||
" disable outsideOnly\n"
|
|
||||||
" disables the plugin\n"
|
|
||||||
" outsideOnly clear outside BUILDING_1 BUILDING_2 inside BUILDING_3\n"
|
|
||||||
" equivalent to:\n"
|
|
||||||
" outsideOnly clear\n"
|
|
||||||
" outsideOnly outside BUILDING_1\n"
|
|
||||||
" outsideOnly outside BUILDING_2\n"
|
|
||||||
" outsideOnly inside BUILDING_3\n"
|
|
||||||
));
|
|
||||||
return CR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
|
||||||
{
|
|
||||||
switch (event) {
|
|
||||||
case SC_WORLD_UNLOADED:
|
|
||||||
registeredBuildings.clear();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return CR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) {
|
|
||||||
if ( enabled == enable )
|
|
||||||
return CR_OK;
|
|
||||||
enabled = enable;
|
|
||||||
EventManager::unregisterAll(plugin_self);
|
|
||||||
if ( enabled ) {
|
|
||||||
EventManager::EventHandler handler(buildingCreated,1);
|
|
||||||
EventManager::registerListener(EventManager::EventType::BUILDING, handler, plugin_self);
|
|
||||||
checkBuildings(out, 0);
|
|
||||||
}
|
|
||||||
return CR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy(df::building* building) {
|
|
||||||
if ( Buildings::deconstruct(building) )
|
|
||||||
return;
|
|
||||||
building->flags.bits.almost_deleted = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkBuildings(color_ostream& out, void* data) {
|
|
||||||
if ( !enabled )
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<df::building*>& buildings = df::global::world->buildings.all;
|
|
||||||
for ( size_t a = 0; a < buildings.size(); a++ ) {
|
|
||||||
df::building* building = buildings[a];
|
|
||||||
if ( building == NULL )
|
|
||||||
continue;
|
|
||||||
if ( building->getCustomType() < 0 )
|
|
||||||
continue;
|
|
||||||
df::coord pos(building->centerx,building->centery,building->z);
|
|
||||||
df::tile_designation* des = Maps::getTileDesignation(pos);
|
|
||||||
bool outside = des->bits.outside;
|
|
||||||
df::building_def* def = df::global::world->raws.buildings.all[building->getCustomType()];
|
|
||||||
int32_t type = registeredBuildings[def->code];
|
|
||||||
|
|
||||||
if ( type == EITHER ) {
|
|
||||||
registeredBuildings.erase(def->code);
|
|
||||||
} else if ( type == OUTSIDE_ONLY ) {
|
|
||||||
if ( outside )
|
|
||||||
continue;
|
|
||||||
destroy(building);
|
|
||||||
} else if ( type == INSIDE_ONLY ) {
|
|
||||||
if ( !outside )
|
|
||||||
continue;
|
|
||||||
destroy(building);
|
|
||||||
} else {
|
|
||||||
if ( DFHack::Once::doOnce("outsideOnly invalid setting") ) {
|
|
||||||
out.print("Error: outsideOnly: building has invalid setting: %s %d\n", def->code.c_str(), type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( checkEvery < 0 )
|
|
||||||
return;
|
|
||||||
EventManager::EventHandler timeHandler(checkBuildings,-1);
|
|
||||||
EventManager::registerTick(timeHandler, checkEvery, plugin_self);
|
|
||||||
}
|
|
||||||
|
|
||||||
command_result outsideOnly(color_ostream& out, vector<string>& parameters) {
|
|
||||||
int32_t status = 2;
|
|
||||||
for ( size_t a = 0; a < parameters.size(); a++ ) {
|
|
||||||
if ( parameters[a] == "clear" ) {
|
|
||||||
registeredBuildings.clear();
|
|
||||||
} else if ( parameters[a] == "outside" ) {
|
|
||||||
status = OUTSIDE_ONLY;
|
|
||||||
} else if ( parameters[a] == "inside" ) {
|
|
||||||
status = INSIDE_ONLY;
|
|
||||||
} else if ( parameters[a] == "either" ) {
|
|
||||||
status = EITHER;
|
|
||||||
} else if ( parameters[a] == "checkEvery" ) {
|
|
||||||
if (a+1 >= parameters.size()) {
|
|
||||||
out.printerr("You must specify how often to check.\n");
|
|
||||||
return CR_WRONG_USAGE;
|
|
||||||
}
|
|
||||||
checkEvery = atoi(parameters[a].c_str());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( status == 2 ) {
|
|
||||||
out.printerr("Error: you need to tell outsideOnly whether the building is inside only, outside-only or either.\n");
|
|
||||||
return CR_WRONG_USAGE;
|
|
||||||
}
|
|
||||||
registeredBuildings[parameters[a]] = status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.print("outsideOnly is %s\n", enabled ? "enabled" : "disabled");
|
|
||||||
if ( enabled ) {
|
|
||||||
|
|
||||||
}
|
|
||||||
return CR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void buildingCreated(color_ostream& out, void* data) {
|
|
||||||
int32_t id = (int32_t)data;
|
|
||||||
df::building* building = df::building::find(id);
|
|
||||||
if ( building == NULL )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( building->getCustomType() < 0 )
|
|
||||||
return;
|
|
||||||
//check if it was created inside or outside
|
|
||||||
df::coord pos(building->centerx,building->centery,building->z);
|
|
||||||
df::tile_designation* des = Maps::getTileDesignation(pos);
|
|
||||||
bool outside = des->bits.outside;
|
|
||||||
|
|
||||||
df::building_def* def = df::global::world->raws.buildings.all[building->getCustomType()];
|
|
||||||
int32_t type = registeredBuildings[def->code];
|
|
||||||
if ( type == EITHER ) {
|
|
||||||
registeredBuildings.erase(def->code);
|
|
||||||
} else if ( type == OUTSIDE_ONLY ) {
|
|
||||||
if ( outside )
|
|
||||||
return;
|
|
||||||
Buildings::deconstruct(building);
|
|
||||||
} else if ( type == INSIDE_ONLY ) {
|
|
||||||
if ( !outside )
|
|
||||||
return;
|
|
||||||
Buildings::deconstruct(building);
|
|
||||||
} else {
|
|
||||||
if ( DFHack::Once::doOnce("outsideOnly invalid setting") ) {
|
|
||||||
out.print("Error: outsideOnly: building has invalid setting: %s %d\n", def->code.c_str(), type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
|||||||
|
--outsideOnly.lua
|
||||||
|
--author expwnent
|
||||||
|
--enables outside only and inside only buildings
|
||||||
|
|
||||||
|
local eventful = require 'plugins.eventful'
|
||||||
|
local utils = require 'utils'
|
||||||
|
|
||||||
|
buildingType = buildingType or utils.invert({'EITHER','OUTSIDE_ONLY','INSIDE_ONLY'})
|
||||||
|
registeredBuildings = registeredBuildings or {}
|
||||||
|
checkEvery = checkEvery or 100
|
||||||
|
timeoutId = timeoutId or nil
|
||||||
|
|
||||||
|
eventful.enableEvent(eventful.eventType.UNLOAD,1)
|
||||||
|
eventful.onUnload.outsideOnly = function()
|
||||||
|
registeredBuildings = {}
|
||||||
|
checkEvery = 100
|
||||||
|
end
|
||||||
|
|
||||||
|
local function destroy(building)
|
||||||
|
if #building.jobs > 0 and building.jobs[0] and building.jobs[0].job_type == df.job_type.DestroyBuilding then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local b = dfhack.buildings.deconstruct(building)
|
||||||
|
if b then
|
||||||
|
--TODO: print an error message to the user so they know
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- building.flags.almost_deleted = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkBuildings()
|
||||||
|
local toDestroy = {}
|
||||||
|
local function forEach(building)
|
||||||
|
if building:getCustomType() < 0 then
|
||||||
|
--TODO: support builtin building types if someone wants
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local pos = df.coord:new()
|
||||||
|
pos.x = building.centerx
|
||||||
|
pos.y = building.centery
|
||||||
|
pos.z = building.z
|
||||||
|
local outside = dfhack.maps.getTileBlock(pos).designation[pos.x%16][pos.y%16].outside
|
||||||
|
local def = df.global.world.raws.buildings.all[building:getCustomType()]
|
||||||
|
local btype = registeredBuildings[def.code]
|
||||||
|
if btype then
|
||||||
|
-- print('outside: ' .. outside==true .. ', type: ' .. btype)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not btype or btype == buildingType.EITHER then
|
||||||
|
registeredBuildings[def.code] = nil
|
||||||
|
return
|
||||||
|
elseif btype == buildingType.OUTSIDE_ONLY then
|
||||||
|
if outside then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if not outside then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(toDestroy,building)
|
||||||
|
end
|
||||||
|
for _,building in ipairs(df.global.world.buildings.all) do
|
||||||
|
forEach(building)
|
||||||
|
end
|
||||||
|
for _,building in ipairs(toDestroy) do
|
||||||
|
destroy(building)
|
||||||
|
end
|
||||||
|
if timeoutId then
|
||||||
|
dfhack.timeout_active(timeoutId,nil)
|
||||||
|
timeoutId = nil
|
||||||
|
end
|
||||||
|
timeoutId = dfhack.timeout(checkEvery, 'ticks', checkBuildings)
|
||||||
|
end
|
||||||
|
|
||||||
|
eventful.enableEvent(eventful.eventType.BUILDING, 100)
|
||||||
|
eventful.onBuildingCreatedDestroyed.outsideOnly = function(buildingId)
|
||||||
|
checkBuildings()
|
||||||
|
end
|
||||||
|
|
||||||
|
local args = utils.processArgs(...)
|
||||||
|
if args.help then
|
||||||
|
--print help message
|
||||||
|
end
|
||||||
|
|
||||||
|
if args.clear then
|
||||||
|
registeredBuildings = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if args.checkEvery then
|
||||||
|
if not tonumber(args.checkEvery) then
|
||||||
|
error('Invalid checkEvery.')
|
||||||
|
end
|
||||||
|
checkEvery = tonumber(args.checkEvery)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not args.building then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not args['type'] then
|
||||||
|
print 'outside-only: please specify type'
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not buildingType[args['type']] then
|
||||||
|
error('Invalid building type: ' .. args['type'])
|
||||||
|
end
|
||||||
|
|
||||||
|
registeredBuildings[args.building] = buildingType[args['type']]
|
||||||
|
|
||||||
|
checkBuildings()
|
||||||
|
|
Loading…
Reference in New Issue