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/dfhack.init-example b/dfhack.init-example index c5ec9800f..643334d64 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -59,10 +59,6 @@ keybinding add Ctrl-Shift-Z@dwarfmode/Default "stocks show" # open an overview window summarising some stocks (dfstatus) keybinding add Ctrl-Shift-I@dwarfmode/Default dfstatus -# Workflow -keybinding add Ctrl-W@dwarfmode/QueryBuilding/Some "gui/workflow" -keybinding add Ctrl-I "gui/workflow status" - # q->stockpile; p - copy & paste stockpiles keybinding add Alt-P copystock diff --git a/library/Core.cpp b/library/Core.cpp index 96fd3903f..15c7064c8 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -811,13 +811,23 @@ bool Core::loadScriptFile(color_ostream &out, string fname, bool silent) } } +static void run_dfhack_init(color_ostream &out, Core *core) +{ + if (!core->loadScriptFile(out, "dfhack.init", true)) + { + core->runCommand(out, "gui/no-dfhack-init"); + core->loadScriptFile(out, "dfhack.init-example", true); + } +} + // Load dfhack.init in a dedicated thread (non-interactive console mode) void fInitthread(void * iodata) { IODATA * iod = ((IODATA*) iodata); Core * core = iod->core; color_ostream_proxy out(core->getConsole()); - core->loadScriptFile(out, "dfhack.init", true); + + run_dfhack_init(out, core); } // A thread function... for the interactive console. @@ -837,7 +847,7 @@ void fIOthread(void * iodata) return; } - core->loadScriptFile(con, "dfhack.init", true); + run_dfhack_init(con, core); con.print("DFHack is ready. Have a nice day!\n" "Type in '?' or 'help' for general help, 'ls' to see all commands.\n"); diff --git a/library/Process-darwin.cpp b/library/Process-darwin.cpp index 30ed709b8..69ccefcea 100644 --- a/library/Process-darwin.cpp +++ b/library/Process-darwin.cpp @@ -222,7 +222,7 @@ void Process::getMemRanges( vector & ranges ) uintptr_t Process::getBase() { - return 0x1000000; + return 0x1000; } int Process::adjustOffset(int offset, bool /*to_file*/) diff --git a/library/VersionInfoFactory.cpp b/library/VersionInfoFactory.cpp index 7142233d8..1b66b583a 100644 --- a/library/VersionInfoFactory.cpp +++ b/library/VersionInfoFactory.cpp @@ -109,7 +109,7 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem) { mem->setOS(OS_APPLE); // this is wrong... I'm not going to do base image relocation on linux though. - mem->setBase(0x1000000); + mem->setBase(0x1000); } else { diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 3cc98e882..ca9d43d1e 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -83,7 +83,27 @@ dfhack.exception.__index = dfhack.exception -- Module loading +local function find_required_module_arg() + -- require -> module code -> mkmodule -> find_... + if debug.getinfo(4,'f').func == require then + return debug.getlocal(4, 1) + end + -- reload -> dofile -> module code -> mkmodule -> find_... + if debug.getinfo(5,'f').func == reload then + return debug.getlocal(5, 1) + end +end + function mkmodule(module,env) + -- Verify that the module name is correct + local _, rq_modname = find_required_module_arg() + if not rq_modname then + error('The mkmodule function must be used at the start of a module') + end + if rq_modname ~= module then + error('Found module '..module..' during require '..rq_modname) + end + -- Reuse the already loaded module table local pkg = package.loaded[module] if pkg == nil then pkg = {} @@ -92,6 +112,7 @@ function mkmodule(module,env) error("Not a table in package.loaded["..module.."]") end end + -- Inject the plugin-exported functions when appropriate local plugname = string.match(module,'^plugins%.([%w%-]+)$') if plugname then dfhack.open_plugin(pkg,plugname) diff --git a/library/xml b/library/xml index f74eeb5f7..c66ab3307 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit f74eeb5f705afa0d1e766d31090e62d4706126e5 +Subproject commit c66ab33071842bcfb7d37c3993f6a024923ca358 diff --git a/patches/v0.34.11 osx/hospital-overstocking.dif b/patches/v0.34.11 osx/hospital-overstocking.dif new file mode 100644 index 000000000..85aade42b --- /dev/null +++ b/patches/v0.34.11 osx/hospital-overstocking.dif @@ -0,0 +1,20 @@ +see linux patch for info + +- .text:00111776 jnz loc_111750 ++ .text:00111776 nop ++ .text:00111777 nop + +sub_660360 +osx stores pointer in edi instead of ebx +- .text:006612C3 mov eax, [eax+edx*4] ++ .text:006612C3 mov edi, [eax+edx*4] +- .text:006612CD mov eax, [eax+74h] ++ .text:006612CD mov eax, [edi+74h] + +---8<--- + +Dwarf_Fortress +00110776: 75 90 +00110777: D8 90 +006602C4: 04 3C +006602CE: 40 47 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 8436f2fe1..ad76889ce 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -169,6 +169,8 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(embark-tools embark-tools.cpp) DFHACK_PLUGIN(petcapRemover petcapRemover.cpp) + + add_subdirectory(rendermax) endif() # this is the skeleton plugin. If you want to make your own, make a copy and then change it @@ -176,7 +178,3 @@ OPTION(BUILD_SKELETON "Build the skeleton plugin." OFF) if(BUILD_SKELETON) add_subdirectory(skeleton) endif() -OPTION(BUILD_RENDERMAX "Build the rendermax alt-renderers plugin." OFF) -if(BUILD_RENDERMAX) - add_subdirectory(rendermax) -endif() diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 2953f83f3..6c888ee7e 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -191,6 +191,11 @@ void sticky_save (df::viewscreen_choose_start_sitest * screen) void sticky_apply (df::viewscreen_choose_start_sitest * screen) { + if (screen->finder.finder_state != -1) + { + // Site finder is active - don't override default local position + return; + } screen->embark_pos_min.x = sticky_pos[0]; screen->embark_pos_max.x = sticky_pos[1]; screen->embark_pos_min.y = sticky_pos[2]; diff --git a/plugins/isoworld b/plugins/isoworld index 29fad1ef8..aa3b1bd51 160000 --- a/plugins/isoworld +++ b/plugins/isoworld @@ -1 +1 @@ -Subproject commit 29fad1ef8e7e2aceb2e68c93a3993933168fe885 +Subproject commit aa3b1bd51f269c07b3235392fd7ed21fe9171f3f diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 4f22a44be..5d36dcd5b 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -26,6 +26,8 @@ #include "df/creature_graphics_role.h" #include "df/creature_raw.h" #include "df/caste_raw.h" +#include "df/historical_entity.h" +#include "df/entity_raw.h" using std::set; using std::vector; @@ -82,6 +84,14 @@ struct SkillColumn df::job_skill skill; // displayed rating char label[3]; // column header bool special; // specified labor is mutually exclusive with all other special labors + bool isValidLabor (df::historical_entity *entity = NULL) const + { + if (labor == unit_labor::NONE) + return false; + if (entity && entity->entity_raw && !entity->entity_raw->jobs.permitted_labor[labor]) + return false; + return true; + } }; #define NUM_COLUMNS (sizeof(columns) / sizeof(SkillColumn)) @@ -851,7 +861,7 @@ void viewscreen_unitlaborsst::feed(set *events) } UnitInfo *cur = units[input_row]; - if (events->count(interface_key::SELECT) && (cur->allowEdit) && (columns[input_column].labor != unit_labor::NONE)) + if (events->count(interface_key::SELECT) && (cur->allowEdit) && columns[input_column].isValidLabor(ui->main.fortress_entity)) { df::unit *unit = cur->unit; const SkillColumn &col = columns[input_column]; @@ -870,15 +880,17 @@ void viewscreen_unitlaborsst::feed(set *events) } unit->status.labors[col.labor] = newstatus; } - if (events->count(interface_key::SELECT_ALL) && (cur->allowEdit)) + if (events->count(interface_key::SELECT_ALL) && (cur->allowEdit) && columns[input_column].isValidLabor(ui->main.fortress_entity)) { df::unit *unit = cur->unit; const SkillColumn &col = columns[input_column]; - bool newstatus = (col.labor == unit_labor::NONE) ? true : !unit->status.labors[col.labor]; + bool newstatus = !unit->status.labors[col.labor]; for (int i = 0; i < NUM_COLUMNS; i++) { if (columns[i].group != col.group) continue; + if (!columns[i].isValidLabor(ui->main.fortress_entity)) + continue; if (columns[i].special) { if (newstatus) @@ -912,7 +924,7 @@ void viewscreen_unitlaborsst::feed(set *events) std::sort(units.begin(), units.end(), sortByName); break; case ALTSORT_PROFESSION_OR_SQUAD: - std::sort(units.begin(), units.end(), show_squad ? sortBySquad : sortByProfession); + std::sort(units.begin(), units.end(), show_squad ? sortBySquad : sortByProfession); break; case ALTSORT_HAPPINESS: std::sort(units.begin(), units.end(), sortByHappiness); @@ -1175,7 +1187,7 @@ void viewscreen_unitlaborsst::render() } - canToggle = (cur->allowEdit) && (columns[sel_column].labor != unit_labor::NONE); + canToggle = (cur->allowEdit) && columns[sel_column].isValidLabor(ui->main.fortress_entity); } int x = 2, y = dim.y - 3; @@ -1213,7 +1225,7 @@ void viewscreen_unitlaborsst::render() OutputString(15, x, y, "Name"); break; case ALTSORT_PROFESSION_OR_SQUAD: - OutputString(15, x, y, show_squad ? "Squad" : "Profession"); + OutputString(15, x, y, show_squad ? "Squad" : "Profession"); break; case ALTSORT_HAPPINESS: OutputString(15, x, y, "Happiness"); 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 a3cbb54ef..0d1954840 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit a3cbb54efd12873f9815ce8133665808566e8560 +Subproject commit 0d19548402932c970c8a0d45404808cbe8fe1bcd diff --git a/plugins/tubefill.cpp b/plugins/tubefill.cpp index 5e863d882..efdc6f1a9 100644 --- a/plugins/tubefill.cpp +++ b/plugins/tubefill.cpp @@ -13,6 +13,8 @@ #include "modules/Gui.h" #include "TileTypes.h" +#include "df/deep_vein_hollow.h" + using namespace DFHack; using namespace df::enums; using df::global::world; diff --git a/scripts/gui/no-dfhack-init.lua b/scripts/gui/no-dfhack-init.lua new file mode 100644 index 000000000..f6171d5c7 --- /dev/null +++ b/scripts/gui/no-dfhack-init.lua @@ -0,0 +1,31 @@ +-- Shows the warning about missing configuration file. + +local gui = require 'gui' +local dlg = require 'gui.dialogs' + +local dfhack_init = { text = 'dfhack.init', pen = COLOR_LIGHTCYAN } +local dfhack_init_example = { text = 'dfhack.init-example', pen = COLOR_LIGHTCYAN } + +local message = { + 'The ', dfhack_init, ' configuration file is missing. To customize', NEWLINE, + 'your DFHack installation, rename the ', dfhack_init_example, ' file', NEWLINE, + 'to ', dfhack_init, ' and edit it to suit your needs.', NEWLINE, NEWLINE, + 'For now, ', dfhack_init_example, ' will be used instead.' +} + +dfhack.print('\n') + +for k,v in ipairs(message) do + if type(v) == 'table' then + dfhack.color(v.pen) + dfhack.print(v.text) + else + dfhack.color(COLOR_YELLOW) + dfhack.print(v) + end +end + +dfhack.color(COLOR_RESET) +dfhack.print('\n\n') + +dlg.showMessage('DFHack is not configured', message, COLOR_YELLOW)