diff --git a/NEWS b/NEWS index 03332493d..0c32baa51 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,12 @@ DFHack future -The future has not yet happened. Stay tuned! + Internals: + New scripts: + New commands: + New tweaks: + New plugins: + Misc improvements: + - outsideOnly: now buildings have to be registered as inside or outside only DFHack v0.34.11-r5 diff --git a/Readme.rst b/Readme.rst index fde2b5c04..40cbeaf8f 100644 --- a/Readme.rst +++ b/Readme.rst @@ -954,7 +954,7 @@ Again, note that plugins AND scripts can be executed this way, and arguments wil outsideOnly ----------- -This plugin makes it so that buildings whose names begin with ``OUTSIDE_ONLY`` cannot be built inside. If the player attempts to do so, the building will automatically be deconstructed. +This plugin makes custom buildings either inside-only or outside-only. If you attempt to build one in an inappropriate location, the building plan will immediately deconstruct. Try `help outsideOnly` for details. syndromeTrigger --------------- diff --git a/plugins/outsideOnly.cpp b/plugins/outsideOnly.cpp index 7707053ca..f7a301cb7 100644 --- a/plugins/outsideOnly.cpp +++ b/plugins/outsideOnly.cpp @@ -8,6 +8,7 @@ #include "modules/Buildings.h" #include "modules/EventManager.h" #include "modules/Maps.h" +#include "modules/Once.h" #include "df/coord.h" #include "df/building.h" @@ -15,25 +16,94 @@ #include "df/map_block.h" #include "df/tile_designation.h" +#include #include +//TODO: check if building becomes inside/outside later using namespace DFHack; using namespace std; +DFHACK_PLUGIN_IS_ENABLED(enabled); DFHACK_PLUGIN("outsideOnly"); +static map registeredBuildings; +const int32_t OUTSIDE_ONLY = 1; +const int32_t EITHER = 0; +const int32_t INSIDE_ONLY = -1; + void buildingCreated(color_ostream& out, void* data); +command_result outsideOnly(color_ostream& out, vector& parameters); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - EventManager::EventHandler handler(buildingCreated,1); - EventManager::registerListener(EventManager::EventType::BUILDING, handler, plugin_self); + 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 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; } -// This is called right before the plugin library is removed from memory. -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + switch (event) { + case SC_GAME_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; + if ( enabled ) { + EventManager::EventHandler handler(buildingCreated,1); + EventManager::registerListener(EventManager::EventType::BUILDING, handler, plugin_self); + } else { + EventManager::unregisterAll(plugin_self); + } +} + +command_result outsideOnly(color_ostream& out, vector& 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 ( status == 2 ) { + out.print("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"); return CR_OK; } @@ -45,19 +115,27 @@ void buildingCreated(color_ostream& out, void* data) { if ( building->getCustomType() < 0 ) return; - string prefix("OUTSIDE_ONLY"); - df::building_def* def = df::global::world->raws.buildings.all[building->getCustomType()]; - if (def->code.compare(0, prefix.size(), prefix)) { - return; - } - - //now, just check if it was created inside, and if so, scuttle it + //check if it was created inside or outside df::coord pos(building->centerx,building->centery,building->z); - df::tile_designation* des = Maps::getTileDesignation(pos); - if ( des->bits.outside ) - return; + bool outside = des->bits.outside; - Buildings::deconstruct(building); + 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); + } + } }