diff --git a/NEWS b/NEWS index 03332493d..d0d714acc 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, and it checks periodically to see when buildings change outsideness 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/build/package-release.bat b/build/package-release.bat index 99e94b0e5..510aaa6df 100644 --- a/build/package-release.bat +++ b/build/package-release.bat @@ -3,4 +3,3 @@ call "%VS100COMNTOOLS%vsvars32.bat" cd VC2010 msbuild /m /p:Platform=Win32 /p:Configuration=Release PACKAGE.vcxproj cd .. -exit %ERRORLEVEL% \ No newline at end of file diff --git a/plugins/outsideOnly.cpp b/plugins/outsideOnly.cpp index 7707053ca..95aec686f 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,155 @@ #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; +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& 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 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; } -// 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_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& 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& 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; } @@ -45,19 +176,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); + } + } } diff --git a/plugins/stonesense b/plugins/stonesense index 0d41614ff..c0a2a9a2a 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 0d41614ff3dae9245e786ad667b0e463fe0dea3e +Subproject commit c0a2a9a2a6909cc79f9a564c8039caf8103010a8