From 6d123687241f76775156faed5ebb48c4242236a0 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 19 Aug 2012 22:45:47 -0400 Subject: [PATCH 001/114] Added digtype to dig.cpp. Digtype allows designation of all veins of a given type. --- plugins/dig.cpp | 131 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/plugins/dig.cpp b/plugins/dig.cpp index 21c199102..c1b6f6331 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -24,7 +24,7 @@ command_result diglx (color_ostream &out, vector & parameters); command_result digauto (color_ostream &out, vector & parameters); command_result digexp (color_ostream &out, vector & parameters); command_result digcircle (color_ostream &out, vector & parameters); - +command_result digtype (color_ostream &out, vector & parameters); DFHACK_PLUGIN("dig"); @@ -57,6 +57,18 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector & parameters) { return CR_NOT_IMPLEMENTED; } + +command_result digtype (color_ostream &out, vector & parameters) +{ + //mostly copy-pasted from digv + CoreSuspender suspend; + if ( parameters.size() > 1 ) + { + out.printerr("Too many parameters.\n"); + return CR_FAILURE; + } + + uint32_t targetDigType; + if ( parameters.size() == 1 ) + { + string parameter = parameters[0]; + if ( parameter == "clear" ) + targetDigType = tile_dig_designation::No; + else if ( parameter == "dig" ) + targetDigType = tile_dig_designation::Default; + else if ( parameter == "updown" ) + targetDigType = tile_dig_designation::UpDownStair; + else if ( parameter == "channel" ) + targetDigType = tile_dig_designation::Channel; + else if ( parameter == "ramp" ) + targetDigType = tile_dig_designation::Ramp; + else if ( parameter == "down" ) + targetDigType = tile_dig_designation::DownStair; + else if ( parameter == "up" ) + targetDigType = tile_dig_designation::UpStair; + else + { + out.printerr("Invalid parameter.\n"); + return CR_FAILURE; + } + } + else + { + targetDigType = -1; + } + + if (!Maps::IsValid()) + { + out.printerr("Map is not available!\n"); + return CR_FAILURE; + } + + int32_t cx, cy, cz; + uint32_t xMax,yMax,zMax; + Maps::getSize(xMax,yMax,zMax); + uint32_t tileXMax = xMax * 16; + uint32_t tileYMax = yMax * 16; + Gui::getCursorCoords(cx,cy,cz); + if (cx == -30000) + { + out.printerr("Cursor is not active. Point the cursor at a vein.\n"); + return CR_FAILURE; + } + DFHack::DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz); + MapExtras::MapCache * mCache = new MapExtras::MapCache; + df::tile_designation baseDes = mCache->designationAt(xy); + df::tiletype tt = mCache->tiletypeAt(xy); + int16_t veinmat = mCache->veinMaterialAt(xy); + if( veinmat == -1 ) + { + out.printerr("This tile is not a vein.\n"); + delete mCache; + return CR_FAILURE; + } + out.print("(%d,%d,%d) tiletype: %d, veinmat: %d, designation: 0x%x ... DIGGING!\n", cx,cy,cz, tt, veinmat, baseDes.whole); + + if ( targetDigType != -1 ) + { + baseDes.bits.dig = (tile_dig_designation::tile_dig_designation)targetDigType; + } + else + { + if ( baseDes.bits.dig == tile_dig_designation::No ) + { + baseDes.bits.dig = tile_dig_designation::Default; + } + } + + for( uint32_t z = 0; z < zMax; z++ ) + { + for( uint32_t x = 1; x < tileXMax-1; x++ ) + { + for( uint32_t y = 1; y < tileYMax-1; y++ ) + { + DFHack::DFCoord current(x,y,z); + int16_t vmat2 = mCache->veinMaterialAt(current); + if ( vmat2 != veinmat ) + continue; + tt = mCache->tiletypeAt(current); + if (!DFHack::isWallTerrain(tt)) + continue; + + //designate it for digging + df::tile_designation des = mCache->designationAt(current); + if ( !mCache->testCoord(current) ) + { + out.printerr("testCoord failed at (%d,%d,%d)\n", x, y, z); + delete mCache; + return CR_FAILURE; + } + + df::tile_designation designation = mCache->designationAt(current); + designation.bits.dig = baseDes.bits.dig; + mCache->setDesignationAt(current, designation); + } + } + } + + mCache->WriteAll(); + delete mCache; + return CR_OK; +} + From eac2f3f5d1fb93fb47b8a060892d1eb38af33ac5 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 21 Aug 2012 01:33:04 -0400 Subject: [PATCH 002/114] Added misery plugin: multiply effects of negative thoughts. --- plugins/CMakeLists.txt | 1 + plugins/misery.cpp | 174 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 plugins/misery.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2c54aebad..670c9ce70 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -115,6 +115,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) + DFHACK_PLUGIN(misery misery.cpp) endif() diff --git a/plugins/misery.cpp b/plugins/misery.cpp new file mode 100644 index 000000000..f17a45599 --- /dev/null +++ b/plugins/misery.cpp @@ -0,0 +1,174 @@ +#include "PluginManager.h" +#include "Export.h" + +#include "DataDefs.h" +#include "df/world.h" +#include "df/ui.h" +#include "df/unit.h" +#include "df/unit_thought.h" +#include "df/unit_thought_type.h" + +#include +#include +#include +using namespace std; + +using namespace DFHack; + +static int factor = 1; +static map processedThoughtCountTable; + +//keep track of fake thoughts so you can remove them if requested +static vector > fakeThoughts; + +DFHACK_PLUGIN("misery"); + +command_result misery(color_ostream& out, vector& parameters); + +DFhackCExport command_result plugin_shutdown(color_ostream& out) { + factor = 1; + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream& out) { + static bool wasLoaded = false; + if ( factor == 1 || !df::global::world || !df::global::world->map.block_index ) { + if ( wasLoaded ) { + //we just unloaded the game: clear all data + factor = 1; + processedThoughtCountTable.clear(); + fakeThoughts.clear(); + wasLoaded = false; + } + return CR_OK; + } + + if ( !wasLoaded ) { + wasLoaded = true; + } + + int32_t race_id = df::global::ui->race_id; + int32_t civ_id = df::global::ui->civ_id; + for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { + df::unit* unit = df::global::world->units.all[a]; //TODO: consider units.active + //living, native units only + if ( unit->race != race_id || unit->civ_id != civ_id ) + continue; + if ( unit->flags1.bits.dead ) + continue; + + int processedThoughtCount; + map::iterator i = processedThoughtCountTable.find(unit->id); + if ( i == processedThoughtCountTable.end() ) { + processedThoughtCount = unit->status.recent_events.size(); + processedThoughtCountTable[unit->id] = processedThoughtCount; + } else { + processedThoughtCount = (*i).second; + } + + if ( processedThoughtCount == unit->status.recent_events.size() ) { + continue; + } else if ( processedThoughtCount > unit->status.recent_events.size() ) { + processedThoughtCount = unit->status.recent_events.size(); + } + + //don't reprocess any old thoughts + vector newThoughts; + for ( size_t b = processedThoughtCount; b < unit->status.recent_events.size(); b++ ) { + df::unit_thought* oldThought = unit->status.recent_events[b]; + const char* bob = ENUM_ATTR(unit_thought_type, value, oldThought->type); + if ( bob[0] != '-' ) { + //out.print("unit %4d: old thought value = %s\n", unit->id, bob); + continue; + } + /*out.print("unit %4d: Duplicating thought type %d (%s), value %s, age %d, subtype %d, severity %d\n", + unit->id, + oldThought->type.value, + ENUM_ATTR(unit_thought_type, caption, (oldThought->type)), + //df::enum_traits::attr_table[oldThought->type].caption + bob, + oldThought->age, + oldThought->subtype, + oldThought->severity + );*/ + //add duplicate thoughts to the temp list + for ( size_t c = 0; c < factor; c++ ) { + df::unit_thought* thought = new df::unit_thought; + thought->type = unit->status.recent_events[b]->type; + thought->age = unit->status.recent_events[b]->age; + thought->subtype = unit->status.recent_events[b]->subtype; + thought->severity = unit->status.recent_events[b]->severity; + newThoughts.push_back(thought); + } + } + for ( size_t b = 0; b < newThoughts.size(); b++ ) { + fakeThoughts.push_back(std::pair(a, unit->status.recent_events.size())); + unit->status.recent_events.push_back(newThoughts[b]); + } + processedThoughtCountTable[unit->id] = unit->status.recent_events.size(); + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { + commands.push_back(PluginCommand("misery", "increase the intensity of negative dwarven thoughts", + &misery, false, + "misery: When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).\n" + "Usage:\n" + " misery enable n\n" + " enable misery with optional magnitude n. If specified, n must be positive.\n" + " misery n\n" + " same as \"misery enable n\"\n" + " misery enable\n" + " same as \"misery enable 2\"\n" + " misery disable\n" + " stop adding new negative thoughts. This will not remove existing duplicated thoughts. Equivalent to \"misery 1\"\n" + " misery clear\n" + " remove fake thoughts added in this session of DF. Saving makes them permanent! Does not change factor.\n\n" + )); + return CR_OK; +} + +command_result misery(color_ostream &out, vector& parameters) { + if ( !df::global::world || !df::global::world->map.block_index ) { + out.printerr("misery can only be enabled in fortress mode with a fully-loaded game.\n"); + return CR_FAILURE; + } + + if ( parameters.size() < 1 || parameters.size() > 2 ) { + return CR_WRONG_USAGE; + } + + if ( parameters[0] == "disable" ) { + if ( parameters.size() > 1 ) { + return CR_WRONG_USAGE; + } + factor = 1; + return CR_OK; + } else if ( parameters[0] == "enable" ) { + factor = 2; + if ( parameters.size() == 2 ) { + factor = atoi(parameters[1].c_str()); + if ( factor < 1 ) { + out.printerr("Second argument must be a positive integer.\n"); + return CR_WRONG_USAGE; + } + } + } else if ( parameters[0] == "clear" ) { + for ( size_t a = 0; a < fakeThoughts.size(); a++ ) { + int dorfIndex = fakeThoughts[a].first; + int thoughtIndex = fakeThoughts[a].second; + df::global::world->units.all[dorfIndex]->status.recent_events[thoughtIndex]->age = 1000000; + } + fakeThoughts.clear(); + } else { + int a = atoi(parameters[0].c_str()); + if ( a < 1 ) { + return CR_WRONG_USAGE; + } + factor = a; + } + + return CR_OK; +} From 76cd941084f952a226a0a7777fe72b085f790707 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 20 Aug 2012 22:18:39 -0400 Subject: [PATCH 003/114] Added teledwarf: teleports dwarves to their destination instantly. --- plugins/fastdwarf.cpp | 96 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 16 deletions(-) diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 5a2e2f206..c51684ae6 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -23,33 +23,88 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } -static int enable_fastdwarf = false; +static bool enable_fastdwarf = false; +static bool enable_teledwarf = false; DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { // check run conditions - if(!world || !world->map.block_index || !enable_fastdwarf) + if(!world || !world->map.block_index) { - // give up if we shouldn't be running' + enable_fastdwarf = enable_teledwarf = false; return CR_OK; } int32_t race = ui->race_id; int32_t civ = ui->civ_id; - - for (size_t i = 0; i < world->units.all.size(); i++) - { - df::unit *unit = world->units.all[i]; - - if (unit->race == race && unit->civ_id == civ && unit->counters.job_counter > 0) - unit->counters.job_counter = 0; - // could also patch the unit->job.current_job->completion_timer + + if ( enable_fastdwarf ) { + for (size_t i = 0; i < world->units.all.size(); i++) + { + df::unit *unit = world->units.all[i]; + + if (unit->race == race && unit->civ_id == civ && unit->counters.job_counter > 0) + unit->counters.job_counter = 0; + // could also patch the unit->job.current_job->completion_timer + } + } + if ( enable_teledwarf ) { + for (size_t i = 0; i < world->units.all.size(); i++) + { + df::unit *unit = world->units.all[i]; + + if (unit->race != race || unit->civ_id != civ || unit->path.dest.x == -30000) + continue; + if (unit->relations.draggee_id != -1 || unit->relations.dragger_id != -1) + continue; + if (unit->relations.following != 0) + continue; + + //move immediately to destination + unit->pos.x = unit->path.dest.x; + unit->pos.y = unit->path.dest.y; + unit->pos.z = unit->path.dest.z; + } } return CR_OK; } static command_result fastdwarf (color_ostream &out, vector & parameters) { - if (parameters.size() == 1 && (parameters[0] == "0" || parameters[0] == "1")) + if (parameters.size() == 1) { + if ( parameters[0] == "0" ) { + enable_fastdwarf = true; + enable_teledwarf = false; + } else if ( parameters[0] == "1" ) { + enable_fastdwarf = false; + enable_teledwarf = false; + } else { + out.print("Incorrect usage.\n"); + return CR_OK; + } + } else if (parameters.size() == 2) { + if ( parameters[0] == "0" ) { + enable_fastdwarf = false; + } else if ( parameters[0] == "1" ) { + enable_fastdwarf = true; + } else { + out.print("Incorrect usage.\n"); + return CR_OK; + } + if ( parameters[1] == "0" ) { + enable_teledwarf = false; + } else if ( parameters[1] == "1" ) { + enable_teledwarf = true; + } else { + out.print("Incorrect usage.\n"); + return CR_OK; + } + } else if (parameters.size() == 0) { + //print status + } else { + out.print("Incorrect usage.\n"); + return CR_OK; + } + /*if (parameters.size() == 1 && (parameters[0] == "0" || parameters[0] == "1")) { if (parameters[0] == "0") enable_fastdwarf = 0; @@ -62,7 +117,7 @@ static command_result fastdwarf (color_ostream &out, vector & parameter out.print("Makes your minions move at ludicrous speeds.\n" "Activate with 'fastdwarf 1', deactivate with 'fastdwarf 0'.\n" "Current state: %d.\n", enable_fastdwarf); - } + }*/ return CR_OK; } @@ -70,8 +125,17 @@ static command_result fastdwarf (color_ostream &out, vector & parameter DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand("fastdwarf", - "enable/disable fastdwarf (parameter=0/1)", - fastdwarf)); - + "enable/disable fastdwarf and teledwarf (parameters=0/1)", + fastdwarf, false, + "fastdwarf: controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly and perform tasks quickly. Teledwarf makes dwarves move instantaneously, but do jobs at the same speed.\n" + "Usage:\n" + " fastdwarf 0 0: disable both speedydwarf and teledwarf\n" + " fastdwarf 0 1: disable speedydwarf, enable teledwarf\n" + " fastdwarf 1 0: enable speedydwarf, disable teledwarf\n" + " fastdwarf 1 1: enable speedydwarf, enable teledwarf\n" + " fastdwarf 0: disable speedydwarf, disable teledwarf\n" + " fastdwarf 1: enable speedydwarf, disable teledwarf\n" + )); + return CR_OK; } From 15adb17559992fd6feb87d26235c1280bf8c009c Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 23 Aug 2012 22:21:09 -0400 Subject: [PATCH 004/114] Converted tabs to spaces. --- plugins/CMakeLists.txt | 2 +- plugins/misery.cpp | 272 ++++++++++++++++++++--------------------- 2 files changed, 137 insertions(+), 137 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 670c9ce70..55a7c250a 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -115,7 +115,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) - DFHACK_PLUGIN(misery misery.cpp) + DFHACK_PLUGIN(misery misery.cpp) endif() diff --git a/plugins/misery.cpp b/plugins/misery.cpp index f17a45599..260eedd84 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -26,149 +26,149 @@ DFHACK_PLUGIN("misery"); command_result misery(color_ostream& out, vector& parameters); DFhackCExport command_result plugin_shutdown(color_ostream& out) { - factor = 1; - return CR_OK; + factor = 1; + return CR_OK; } DFhackCExport command_result plugin_onupdate(color_ostream& out) { - static bool wasLoaded = false; - if ( factor == 1 || !df::global::world || !df::global::world->map.block_index ) { - if ( wasLoaded ) { - //we just unloaded the game: clear all data - factor = 1; - processedThoughtCountTable.clear(); - fakeThoughts.clear(); - wasLoaded = false; - } - return CR_OK; - } - - if ( !wasLoaded ) { - wasLoaded = true; - } - - int32_t race_id = df::global::ui->race_id; - int32_t civ_id = df::global::ui->civ_id; - for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { - df::unit* unit = df::global::world->units.all[a]; //TODO: consider units.active - //living, native units only - if ( unit->race != race_id || unit->civ_id != civ_id ) - continue; - if ( unit->flags1.bits.dead ) - continue; - - int processedThoughtCount; - map::iterator i = processedThoughtCountTable.find(unit->id); - if ( i == processedThoughtCountTable.end() ) { - processedThoughtCount = unit->status.recent_events.size(); - processedThoughtCountTable[unit->id] = processedThoughtCount; - } else { - processedThoughtCount = (*i).second; - } - - if ( processedThoughtCount == unit->status.recent_events.size() ) { - continue; - } else if ( processedThoughtCount > unit->status.recent_events.size() ) { - processedThoughtCount = unit->status.recent_events.size(); - } - - //don't reprocess any old thoughts - vector newThoughts; - for ( size_t b = processedThoughtCount; b < unit->status.recent_events.size(); b++ ) { - df::unit_thought* oldThought = unit->status.recent_events[b]; - const char* bob = ENUM_ATTR(unit_thought_type, value, oldThought->type); - if ( bob[0] != '-' ) { - //out.print("unit %4d: old thought value = %s\n", unit->id, bob); - continue; - } - /*out.print("unit %4d: Duplicating thought type %d (%s), value %s, age %d, subtype %d, severity %d\n", - unit->id, - oldThought->type.value, - ENUM_ATTR(unit_thought_type, caption, (oldThought->type)), - //df::enum_traits::attr_table[oldThought->type].caption - bob, - oldThought->age, - oldThought->subtype, - oldThought->severity - );*/ - //add duplicate thoughts to the temp list - for ( size_t c = 0; c < factor; c++ ) { - df::unit_thought* thought = new df::unit_thought; - thought->type = unit->status.recent_events[b]->type; - thought->age = unit->status.recent_events[b]->age; - thought->subtype = unit->status.recent_events[b]->subtype; - thought->severity = unit->status.recent_events[b]->severity; - newThoughts.push_back(thought); - } - } - for ( size_t b = 0; b < newThoughts.size(); b++ ) { - fakeThoughts.push_back(std::pair(a, unit->status.recent_events.size())); - unit->status.recent_events.push_back(newThoughts[b]); - } - processedThoughtCountTable[unit->id] = unit->status.recent_events.size(); - } - - return CR_OK; + static bool wasLoaded = false; + if ( factor == 1 || !df::global::world || !df::global::world->map.block_index ) { + if ( wasLoaded ) { + //we just unloaded the game: clear all data + factor = 1; + processedThoughtCountTable.clear(); + fakeThoughts.clear(); + wasLoaded = false; + } + return CR_OK; + } + + if ( !wasLoaded ) { + wasLoaded = true; + } + + int32_t race_id = df::global::ui->race_id; + int32_t civ_id = df::global::ui->civ_id; + for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { + df::unit* unit = df::global::world->units.all[a]; //TODO: consider units.active + //living, native units only + if ( unit->race != race_id || unit->civ_id != civ_id ) + continue; + if ( unit->flags1.bits.dead ) + continue; + + int processedThoughtCount; + map::iterator i = processedThoughtCountTable.find(unit->id); + if ( i == processedThoughtCountTable.end() ) { + processedThoughtCount = unit->status.recent_events.size(); + processedThoughtCountTable[unit->id] = processedThoughtCount; + } else { + processedThoughtCount = (*i).second; + } + + if ( processedThoughtCount == unit->status.recent_events.size() ) { + continue; + } else if ( processedThoughtCount > unit->status.recent_events.size() ) { + processedThoughtCount = unit->status.recent_events.size(); + } + + //don't reprocess any old thoughts + vector newThoughts; + for ( size_t b = processedThoughtCount; b < unit->status.recent_events.size(); b++ ) { + df::unit_thought* oldThought = unit->status.recent_events[b]; + const char* bob = ENUM_ATTR(unit_thought_type, value, oldThought->type); + if ( bob[0] != '-' ) { + //out.print("unit %4d: old thought value = %s\n", unit->id, bob); + continue; + } + /*out.print("unit %4d: Duplicating thought type %d (%s), value %s, age %d, subtype %d, severity %d\n", + unit->id, + oldThought->type.value, + ENUM_ATTR(unit_thought_type, caption, (oldThought->type)), + //df::enum_traits::attr_table[oldThought->type].caption + bob, + oldThought->age, + oldThought->subtype, + oldThought->severity + );*/ + //add duplicate thoughts to the temp list + for ( size_t c = 0; c < factor; c++ ) { + df::unit_thought* thought = new df::unit_thought; + thought->type = unit->status.recent_events[b]->type; + thought->age = unit->status.recent_events[b]->age; + thought->subtype = unit->status.recent_events[b]->subtype; + thought->severity = unit->status.recent_events[b]->severity; + newThoughts.push_back(thought); + } + } + for ( size_t b = 0; b < newThoughts.size(); b++ ) { + fakeThoughts.push_back(std::pair(a, unit->status.recent_events.size())); + unit->status.recent_events.push_back(newThoughts[b]); + } + processedThoughtCountTable[unit->id] = unit->status.recent_events.size(); + } + + return CR_OK; } DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { - commands.push_back(PluginCommand("misery", "increase the intensity of negative dwarven thoughts", - &misery, false, - "misery: When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).\n" - "Usage:\n" - " misery enable n\n" - " enable misery with optional magnitude n. If specified, n must be positive.\n" - " misery n\n" - " same as \"misery enable n\"\n" - " misery enable\n" - " same as \"misery enable 2\"\n" - " misery disable\n" - " stop adding new negative thoughts. This will not remove existing duplicated thoughts. Equivalent to \"misery 1\"\n" - " misery clear\n" - " remove fake thoughts added in this session of DF. Saving makes them permanent! Does not change factor.\n\n" - )); - return CR_OK; + commands.push_back(PluginCommand("misery", "increase the intensity of negative dwarven thoughts", + &misery, false, + "misery: When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).\n" + "Usage:\n" + " misery enable n\n" + " enable misery with optional magnitude n. If specified, n must be positive.\n" + " misery n\n" + " same as \"misery enable n\"\n" + " misery enable\n" + " same as \"misery enable 2\"\n" + " misery disable\n" + " stop adding new negative thoughts. This will not remove existing duplicated thoughts. Equivalent to \"misery 1\"\n" + " misery clear\n" + " remove fake thoughts added in this session of DF. Saving makes them permanent! Does not change factor.\n\n" + )); + return CR_OK; } command_result misery(color_ostream &out, vector& parameters) { - if ( !df::global::world || !df::global::world->map.block_index ) { - out.printerr("misery can only be enabled in fortress mode with a fully-loaded game.\n"); - return CR_FAILURE; - } - - if ( parameters.size() < 1 || parameters.size() > 2 ) { - return CR_WRONG_USAGE; - } - - if ( parameters[0] == "disable" ) { - if ( parameters.size() > 1 ) { - return CR_WRONG_USAGE; - } - factor = 1; - return CR_OK; - } else if ( parameters[0] == "enable" ) { - factor = 2; - if ( parameters.size() == 2 ) { - factor = atoi(parameters[1].c_str()); - if ( factor < 1 ) { - out.printerr("Second argument must be a positive integer.\n"); - return CR_WRONG_USAGE; - } - } - } else if ( parameters[0] == "clear" ) { - for ( size_t a = 0; a < fakeThoughts.size(); a++ ) { - int dorfIndex = fakeThoughts[a].first; - int thoughtIndex = fakeThoughts[a].second; - df::global::world->units.all[dorfIndex]->status.recent_events[thoughtIndex]->age = 1000000; - } - fakeThoughts.clear(); - } else { - int a = atoi(parameters[0].c_str()); - if ( a < 1 ) { - return CR_WRONG_USAGE; - } - factor = a; - } - - return CR_OK; + if ( !df::global::world || !df::global::world->map.block_index ) { + out.printerr("misery can only be enabled in fortress mode with a fully-loaded game.\n"); + return CR_FAILURE; + } + + if ( parameters.size() < 1 || parameters.size() > 2 ) { + return CR_WRONG_USAGE; + } + + if ( parameters[0] == "disable" ) { + if ( parameters.size() > 1 ) { + return CR_WRONG_USAGE; + } + factor = 1; + return CR_OK; + } else if ( parameters[0] == "enable" ) { + factor = 2; + if ( parameters.size() == 2 ) { + factor = atoi(parameters[1].c_str()); + if ( factor < 1 ) { + out.printerr("Second argument must be a positive integer.\n"); + return CR_WRONG_USAGE; + } + } + } else if ( parameters[0] == "clear" ) { + for ( size_t a = 0; a < fakeThoughts.size(); a++ ) { + int dorfIndex = fakeThoughts[a].first; + int thoughtIndex = fakeThoughts[a].second; + df::global::world->units.all[dorfIndex]->status.recent_events[thoughtIndex]->age = 1000000; + } + fakeThoughts.clear(); + } else { + int a = atoi(parameters[0].c_str()); + if ( a < 1 ) { + return CR_WRONG_USAGE; + } + factor = a; + } + + return CR_OK; } From e12e7ddd40677f6048993bbbe12886f3982c0b43 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 27 Aug 2012 14:42:00 -0400 Subject: [PATCH 005/114] Fixed some memory leaks. --- plugins/dig.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/dig.cpp b/plugins/dig.cpp index c1b6f6331..5e2bb9f89 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -963,7 +963,7 @@ command_result digexp (color_ostream &out, vector & parameters) mx.setDesignationAt(pos,des); } } - mx.WriteAll(); + mx.WriteAll(); } else for(uint32_t x = 0; x < x_max; x++) { @@ -1141,6 +1141,7 @@ command_result digv (color_ostream &out, vector & parameters) } } MCache->WriteAll(); + delete MCache; return CR_OK; } @@ -1354,6 +1355,7 @@ command_result digl (color_ostream &out, vector & parameters) } } MCache->WriteAll(); + delete MCache; return CR_OK; } From 1a5f05768f9abee0b932f712794045eef526cb2a Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 27 Aug 2012 15:06:41 -0400 Subject: [PATCH 006/114] Made it not run every tick. --- plugins/misery.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/misery.cpp b/plugins/misery.cpp index 260eedd84..9e0535d5d 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -20,6 +20,8 @@ static map processedThoughtCountTable; //keep track of fake thoughts so you can remove them if requested static vector > fakeThoughts; +static int count; +const int maxCount = 1000; DFHACK_PLUGIN("misery"); @@ -46,6 +48,12 @@ DFhackCExport command_result plugin_onupdate(color_ostream& out) { if ( !wasLoaded ) { wasLoaded = true; } + + if ( count < maxCount ) { + count++; + return CR_OK; + } + count = 0; int32_t race_id = df::global::ui->race_id; int32_t civ_id = df::global::ui->civ_id; @@ -172,3 +180,4 @@ command_result misery(color_ostream &out, vector& parameters) { return CR_OK; } + From d69a7c63e3fae75a9acb0de3e1038c9507dc4ed3 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 27 Aug 2012 16:05:23 -0400 Subject: [PATCH 007/114] Tabs to spaces. --- plugins/misery.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/misery.cpp b/plugins/misery.cpp index 9e0535d5d..a0c0a2390 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -50,8 +50,8 @@ DFhackCExport command_result plugin_onupdate(color_ostream& out) { } if ( count < maxCount ) { - count++; - return CR_OK; + count++; + return CR_OK; } count = 0; From a2bb3223440a0c50768a53e6cd01a2ac450cc175 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 24 Sep 2012 15:40:35 +0200 Subject: [PATCH 008/114] ruby: item_find now works in z/stocks --- plugins/ruby/item.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index 032c0d8cf..fc840f7ad 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -4,9 +4,14 @@ module DFHack # arg similar to unit.rb/unit_find; no arg = 'k' menu def item_find(what=:selected, y=nil, z=nil) if what == :selected - if curview._rtti_classname == :viewscreen_itemst + case curview._rtti_classname + when :viewscreen_itemst ref = curview.entry_ref[curview.cursor_pos] ref.item_tg if ref.kind_of?(GeneralRefItem) + when :viewscreen_storesst # z/stocks + if curview.in_group_mode == 0 and curview.in_right_list == 1 + curview.items[curview.item_cursor] + end else case ui.main.mode when :LookAround From 627f0368306273e21abe35d938d8fd7d43583553 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 24 Sep 2012 19:13:33 +0400 Subject: [PATCH 009/114] Implement a special command parsing mode with one verbatim argument. Intended for script expressions, e.g. rb_eval. --- README.rst | 87 +++++++++----- Readme.html | 304 +++++++++++++++++++++++++++++------------------ library/Core.cpp | 28 ++++- 3 files changed, 277 insertions(+), 142 deletions(-) diff --git a/README.rst b/README.rst index 73147d061..5202b92a2 100644 --- a/README.rst +++ b/README.rst @@ -148,6 +148,19 @@ for context ``foo/bar/baz``, possible matches are any of ``@foo/bar/baz``, ``@fo Commands ======== +DFHack command syntax consists of a command name, followed by arguments separated +by whitespace. To include whitespace in an argument, quote it in double quotes. +To include a double quote character, use ``\"`` inside double quotes. + +If the first non-whitespace character of a line is ``#``, the line is treated +as a comment, i.e. a silent no-op command. + +If the first non-whitespace character is ``:``, the command is parsed in a special +alternative mode: first, non-whitespace characters immediately following the ``:`` +are used as the command name; the remaining part of the line, starting with the first +non-whitespace character *after* the command name, is used verbatim as the first argument. +This is intended for commands like ``rb_eval`` that evaluate script language statements. + Almost all the commands support using the 'help ' built-in command to retrieve further help without having to look at this document. Alternatively, some accept a 'help'/'?' option on their command line. @@ -1636,8 +1649,8 @@ digfort A script to designate an area for digging according to a plan in csv format. This script, inspired from quickfort, can designate an area for digging. -Your plan should be stored in a .csv file like this: -:: +Your plan should be stored in a .csv file like this:: + # this is a comment d;d;u;d;d;skip this tile;d d;d;d;i @@ -1656,8 +1669,8 @@ superdwarf ========== Similar to fastdwarf, per-creature. -To make any creature superfast, target it ingame using 'v' and: -:: +To make any creature superfast, target it ingame using 'v' and:: + superdwarf add Other options available: ``del``, ``clear``, ``list``. @@ -1748,16 +1761,6 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter re-entering the mechanisms ui. -Power Meter -=========== - -Front-end to the power-meter plugin implemented by the gui/power-meter script. Bind to a -key and activate after selecting Pressure Plate in the build menu. - -The script follows the general look and feel of the regular pressure plate build -configuration page, but configures parameters relevant to the modded power meter building. - - Rename ====== @@ -1786,11 +1789,25 @@ The script lists other rooms owned by the same owner, or by the unit selected in list, and allows unassigning them. +============= +Behavior Mods +============= + +These plugins, when activated via configuration UI or by detecting certain +structures in RAWs, modify the game engine behavior concerning the target +objects to add features not otherwise present. + + Siege Engine ============ -Front-end to the siege-engine plugin implemented by the gui/siege-engine script. Bind to a -key and activate after selecting a siege engine in 'q' mode. +The siege-engine plugin enables siege engines to be linked to stockpiles, and +aimed at an arbitrary rectangular area across Z levels, instead of the original +four directions. Also, catapults can be ordered to load arbitrary objects, not +just stones. + +The configuration front-end to the plugin is implemented by the gui/siege-engine +script. Bind it to a key and activate after selecting a siege engine in 'q' mode. The main mode displays the current target, selected ammo item type, linked stockpiles and the allowed operator skill range. The map tile color is changed to signify if it can be @@ -1799,7 +1816,7 @@ yellow for partially blocked. Pressing 'r' changes into the target selection mode, which works by highlighting two points with Enter like all designations. When a target area is set, the engine projectiles are -aimed at that area, or units within it, instead of the vanilla four directions. +aimed at that area, or units within it. After setting the target in this way for one engine, you can 'paste' the same area into others just by pressing 'p' in the main page of this script. The area to paste is kept until you quit @@ -1813,17 +1830,23 @@ menu. .. admonition:: DISCLAIMER - Siege engines are a very interesting feature, but currently nearly useless + Siege engines are a very interesting feature, but sadly almost useless in the current state because they haven't been updated since 2D and can only aim in four directions. This is an attempt to bring them more up to date until Toady has time to work on it. Actual improvements, e.g. like making siegers bring their own, are something only Toady can do. -========= -RAW hacks -========= +Power Meter +=========== + +The power-meter plugin implements a modified pressure plate that detects power being +supplied to gear boxes built in the four adjacent N/S/W/E tiles. + +The configuration front-end is implemented by the gui/power-meter script. Bind it to a +key and activate after selecting Pressure Plate in the build menu. -These plugins detect certain structures in RAWs, and enhance them in various ways. +The script follows the general look and feel of the regular pressure plate build +configuration page, but configures parameters relevant to the modded power meter building. Steam Engine @@ -1855,7 +1878,7 @@ The magma version also needs magma. .. admonition:: ISSUE Since this building is a machine, and machine collapse - code cannot be modified, it would collapse over true open space. + code cannot be hooked, it would collapse over true open space. As a loophole, down stair provides support to machines, while being passable, so use them. @@ -1866,7 +1889,7 @@ is extracted from the workshop raws. .. admonition:: ISSUE Like with collapse above, part of the code involved in - machine connection cannot be modified. As a result, the workshop + machine connection cannot be hooked. As a result, the workshop can only immediately connect to machine components built AFTER it. This also means that engines cannot be chained without intermediate short axles that can be built later than both of the engines. @@ -1879,8 +1902,13 @@ on repeat). A furnace operator will come, possibly bringing a bar of fuel, and perform it. As a result, a "boiling water" item will appear in the 't' view of the workshop. -**NOTE**: The completion of the job will actually consume one unit -of the appropriate liquids from below the workshop. +.. note:: + + The completion of the job will actually consume one unit + of the appropriate liquids from below the workshop. This means + that you cannot just raise 7 units of magma with a piston and + have infinite power. However, liquid consumption should be slow + enough that water can be supplied by a pond zone bucket chain. Every such item gives 100 power, up to a limit of 300 for coal, and 500 for a magma engine. The building can host twice that @@ -1928,9 +1956,12 @@ Add Spatter =========== This plugin makes reactions with names starting with ``SPATTER_ADD_`` -produce contaminants on the items instead of improvements. +produce contaminants on the items instead of improvements. The produced +contaminants are immune to being washed away by water or destroyed by +the ``clean items`` command. -Intended to give some use to all those poisons that can be bought from caravans. +The plugin is intended to give some use to all those poisons that can +be bought from caravans. :) To be really useful this needs patches from bug 808, ``tweak fix-dimensions`` and ``tweak advmode-contained``. diff --git a/Readme.html b/Readme.html index be6856fb6..7aebeb13a 100644 --- a/Readme.html +++ b/Readme.html @@ -432,72 +432,78 @@ access DF memory and allow for easier development of new tools.

  • Visualizer and data export
  • -
  • Job management
      -
    • job
    • -
    • job-material
    • -
    • job-duplicate
    • -
    • workflow
        -
      • Function
      • -
      • Constraint examples
      • +
      • Job management
      • -
      • Fortress activity management
          -
        • seedwatch
        • -
        • zone
            -
          • Usage with single units
          • -
          • Usage with filters
          • -
          • Mass-renaming
          • -
          • Cage zones
          • -
          • Examples
          • +
          • Fortress activity management
          • -
          • Other
          • -
          • Scripts
              -
            • fix/*
            • -
            • gui/*
            • -
            • quicksave
            • -
            • setfps
            • -
            • growcrops
            • -
            • removebadthoughts
            • -
            • slayrace
            • -
            • magmasource
            • +
            • Scripts
            • -
            • In-game interface tools
                -
              • Dwarf Manipulator
              • -
              • Liquids
              • -
              • Mechanisms
              • -
              • Power Meter
              • -
              • Rename
              • -
              • Room List
              • -
              • Siege Engine
              • +
              • In-game interface tools
              • -
              • RAW hacks
                  -
                • Steam Engine @@ -627,6 +633,16 @@ for context foo/bar/baz, possible matches are

                  Commands

                  +

                  DFHack command syntax consists of a command name, followed by arguments separated +by whitespace. To include whitespace in an argument, quote it in double quotes. +To include a double quote character, use \" inside double quotes.

                  +

                  If the first non-whitespace character of a line is #, the line is treated +as a comment, i.e. a silent no-op command.

                  +

                  If the first non-whitespace character is :, the command is parsed in a special +alternative mode: first, non-whitespace characters immediately following the : +are used as the command name; the remaining part of the line, starting with the first +non-whitespace character after the command name, is used verbatim as the first argument. +This is intended for commands like rb_eval that evaluate script language statements.

                  Almost all the commands support using the 'help <command-name>' built-in command to retrieve further help without having to look at this document. Alternatively, some accept a 'help'/'?' option on their command line.

                  @@ -1782,15 +1798,16 @@ thread: mapexport

                  Export the current loaded map as a file. This will be eventually usable with visualizers.

                  -

                  dwarfexport -----------= -Export dwarves to RuneSmith-compatible XML.

                  +
                  +
                  +

                  dwarfexport

                  +

                  Export dwarves to RuneSmith-compatible XML.

                  -

                  Job management

                  +

                  Job management

                  -

                  job

                  +

                  job

                  Command for general job query and manipulation.

                  Options:
                  @@ -1809,7 +1826,7 @@ in a workshop, or the unit/jobs screen.
                  -

                  job-material

                  +

                  job-material

                  Alter the material of the selected job.

                  Invoked as:

                  @@ -1827,7 +1844,7 @@ over the first available choice with the matching material.
                • -

                  job-duplicate

                  +

                  job-duplicate

                  Duplicate the selected job in a workshop:
                    @@ -1838,7 +1855,7 @@ instantly duplicates the job.
                  -

                  workflow

                  +

                  workflow

                  Manage control of repeat jobs.

                  Usage:

                  @@ -1862,7 +1879,7 @@ Otherwise, enables or disables any of the following options:

                  -

                  Function

                  +

                  Function

                  When the plugin is enabled, it protects all repeat jobs from removal. If they do disappear due to any cause, they are immediately re-added to their workshop and suspended.

                  @@ -1873,7 +1890,7 @@ the amount has to drop before jobs are resumed; this is intended to reduce the frequency of jobs being toggled.

                  -

                  Constraint examples

                  +

                  Constraint examples

                  Keep metal bolts within 900-1000, and wood/bone within 150-200.

                   workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100
                  @@ -1912,15 +1929,15 @@ command.
                   
                  -

                  Fortress activity management

                  +

                  Fortress activity management

                  -

                  seedwatch

                  +

                  seedwatch

                  Tool for turning cooking of seeds and plants on/off depending on how much you have of them.

                  See 'seedwatch help' for detailed description.

                  -

                  zone

                  +

                  zone

                  Helps a bit with managing activity zones (pens, pastures and pits) and cages.

                  Options:

                  @@ -2019,7 +2036,7 @@ for war/hunt). Negatable.
                  -

                  Usage with single units

                  +

                  Usage with single units

                  One convenient way to use the zone tool is to bind the command 'zone assign' to a hotkey, maybe also the command 'zone set'. Place the in-game cursor over a pen/pasture or pit, use 'zone set' to mark it. Then you can select units @@ -2028,7 +2045,7 @@ and use 'zone assign' to assign them to their new home. Allows pitting your own dwarves, by the way.

                  -

                  Usage with filters

                  +

                  Usage with filters

                  All filters can be used together with the 'assign' command.

                  Restrictions: It's not possible to assign units who are inside built cages or chained because in most cases that won't be desirable anyways. @@ -2046,14 +2063,14 @@ are not properly added to your own stocks; slaughtering them should work).

                  Most filters can be negated (e.g. 'not grazer' -> race is not a grazer).

                  -

                  Mass-renaming

                  +

                  Mass-renaming

                  Using the 'nick' command you can set the same nickname for multiple units. If used without 'assign', 'all' or 'count' it will rename all units in the current default target zone. Combined with 'assign', 'all' or 'count' (and further optional filters) it will rename units matching the filter conditions.

                  -

                  Cage zones

                  +

                  Cage zones

                  Using the 'tocages' command you can assign units to a set of cages, for example a room next to your butcher shop(s). They will be spread evenly among available cages to optimize hauling to and butchering from them. For this to work you need @@ -2064,7 +2081,7 @@ would make no sense, but can be used together with 'nick' or 'remnick' and all the usual filters.

                  -

                  Examples

                  +

                  Examples

                  zone assign all own ALPACA minage 3 maxage 10
                  Assign all own alpacas who are between 3 and 10 years old to the selected @@ -2090,7 +2107,7 @@ on the current default zone.
                  -

                  autonestbox

                  +

                  autonestbox

                  Assigns unpastured female egg-layers to nestbox zones. Requires that you create pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox must be in the top left corner. Only 1 unit will be assigned per pen, regardless @@ -2119,7 +2136,7 @@ frames between runs.

                  -

                  autobutcher

                  +

                  autobutcher

                  Assigns lifestock for slaughter once it reaches a specific count. Requires that you add the target race(s) to a watch list. Only tame units will be processed.

                  Named units will be completely ignored (to protect specific animals from @@ -2227,7 +2244,7 @@ autobutcher.bat

                  -

                  autolabor

                  +

                  autolabor

                  Automatically manage dwarf labors.

                  When enabled, autolabor periodically checks your dwarves and enables or disables labors. It tries to keep as many dwarves as possible busy but @@ -2241,14 +2258,14 @@ while it is enabled.

                  -

                  Other

                  +

                  Other

                  -

                  catsplosion

                  +

                  catsplosion

                  Makes cats just multiply. It is not a good idea to run this more than once or twice.

                  -

                  dfusion

                  +

                  dfusion

                  This is the DFusion lua plugin system by warmist/darius, running as a DFHack plugin.

                  See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=69682.15

                  Confirmed working DFusion plugins:

                  @@ -2272,7 +2289,7 @@ twice.

                  -

                  Scripts

                  +

                  Scripts

                  Lua or ruby scripts placed in the hack/scripts/ directory are considered for execution as if they were native DFHack commands. They are listed at the end of the 'ls' command output.

                  @@ -2281,7 +2298,7 @@ only be listed by ls if called as 'ls -a'. This is intended as a way to hide scripts that are obscure, developer-oriented, or should be used as keybindings.

                  Some notable scripts:

                  -

                  fix/*

                  +

                  fix/*

                  Scripts in this subdirectory fix various bugs and issues, some of them obscure.

                  • fix/dead-units

                    @@ -2307,22 +2324,29 @@ caused by autodump bugs or other hacking mishaps.

                  -

                  gui/*

                  +

                  gui/*

                  Scripts that implement dialogs inserted into the main game window are put in this directory.

                  -

                  quicksave

                  +

                  quicksave

                  If called in dwarf mode, makes DF immediately auto-save the game by setting a flag normally used in seasonal auto-save.

                  -

                  setfps

                  +

                  setfps

                  Run setfps <number> to set the FPS cap at runtime, in case you want to watch combat in slow motion or something :)

                  +
                  +

                  siren

                  +

                  Wakes up sleeping units, cancels breaks and stops parties either everywhere, +or in the burrows given as arguments. In return, adds bad thoughts about +noise and tiredness. Also, the units with interrupted breaks will go on +break again a lot sooner.

                  +
                  -

                  growcrops

                  +

                  growcrops

                  Instantly grow seeds inside farming plots.

                  With no argument, this command list the various seed types currently in use in your farming plots. @@ -2334,7 +2358,7 @@ growcrops plump 40

                  -

                  removebadthoughts

                  +

                  removebadthoughts

                  This script remove negative thoughts from your dwarves. Very useful against tantrum spirals.

                  With a selected unit in 'v' mode, will clear this unit's mind, otherwise @@ -2347,7 +2371,7 @@ you unpause.

                  it removed.

                  -

                  slayrace

                  +

                  slayrace

                  Kills any unit of a given race.

                  With no argument, lists the available races.

                  With the special argument 'him', targets only the selected creature.

                  @@ -2373,7 +2397,7 @@ slayrace elve magma
                  -

                  magmasource

                  +

                  magmasource

                  Create an infinite magma source on a tile.

                  This script registers a map tile as a magma source, and every 12 game ticks that tile receives 1 new unit of flowing magma.

                  @@ -2387,13 +2411,50 @@ magmasource here To remove all placed sources, call magmasource stop.

                  With no argument, this command shows an help message and list existing sources.

                  +
                  +

                  digfort

                  +

                  A script to designate an area for digging according to a plan in csv format.

                  +

                  This script, inspired from quickfort, can designate an area for digging. +Your plan should be stored in a .csv file like this:

                  +
                  +# this is a comment
                  +d;d;u;d;d;skip this tile;d
                  +d;d;d;i
                  +
                  +

                  Available tile shapes are named after the 'dig' menu shortcuts: +d for dig, u for upstairs, d downstairs, i updown, +h channel, r upward ramp, x remove designation. +Unrecognized characters are ignored (eg the 'skip this tile' in the sample).

                  +

                  Empty lines and data after a # are ignored as comments. +To skip a row in your design, use a single ;.

                  +

                  The script takes the plan filename, starting from the root df folder.

                  +
                  +
                  +

                  superdwarf

                  +

                  Similar to fastdwarf, per-creature.

                  +

                  To make any creature superfast, target it ingame using 'v' and:

                  +
                  +superdwarf add
                  +
                  +

                  Other options available: del, clear, list.

                  +

                  This plugin also shortens the 'sleeping' and 'on break' periods of targets.

                  +
                  +
                  +

                  drainaquifer

                  +

                  Remove all 'aquifer' tag from the map blocks. Irreversible.

                  +
                  +
                  +

                  deathcause

                  +

                  Focus a body part ingame, and this script will display the cause of death of +the creature.

                  +
                  -

                  In-game interface tools

                  +

                  In-game interface tools

                  These tools work by displaying dialogs or overlays in the game window, and are mostly implemented by lua scripts.

                  -

                  Dwarf Manipulator

                  +

                  Dwarf Manipulator

                  Implemented by the manipulator plugin. To activate, open the unit screen and press 'l'.

                  This tool implements a Dwarf Therapist-like interface within the game UI. The @@ -2428,13 +2489,13 @@ cursor onto that cell instead of toggling it. directly to the main dwarf mode screen.

                  -

                  Liquids

                  +

                  Liquids

                  Implemented by the gui/liquids script. To use, bind to a key and activate in the 'k' mode.

                  While active, use the suggested keys to switch the usual liquids parameters, and Enter to select the target area and apply changes.

                  -

                  Mechanisms

                  +

                  Mechanisms

                  Implemented by the gui/mechanims script. To use, bind to a key and activate in the 'q' mode.

                  Lists mechanisms connected to the building, and their links. Navigating the list centers the view on the relevant linked buildings.

                  @@ -2442,15 +2503,8 @@ the view on the relevant linked buildings.

                  focus on the current one. Shift-Enter has an effect equivalent to pressing Enter, and then re-entering the mechanisms ui.

                  -
                  -

                  Power Meter

                  -

                  Front-end to the power-meter plugin implemented by the gui/power-meter script. Bind to a -key and activate after selecting Pressure Plate in the build menu.

                  -

                  The script follows the general look and feel of the regular pressure plate build -configuration page, but configures parameters relevant to the modded power meter building.

                  -
                  -

                  Rename

                  +

                  Rename

                  Backed by the rename plugin, the gui/rename script allows entering the desired name via a simple dialog in the game ui.

                    @@ -2466,23 +2520,33 @@ It is also possible to rename zones from the 'i' menu.

                    The building or unit are automatically assumed when in relevant ui state.

                  -

                  Room List

                  +

                  Room List

                  Implemented by the gui/room-list script. To use, bind to a key and activate in the 'q' mode, either immediately or after opening the assign owner page.

                  The script lists other rooms owned by the same owner, or by the unit selected in the assign list, and allows unassigning them.

                  +
                  +
                  +

                  Behavior Mods

                  +

                  These plugins, when activated via configuration UI or by detecting certain +structures in RAWs, modify the game engine behavior concerning the target +objects to add features not otherwise present.

                  -

                  Siege Engine

                  -

                  Front-end to the siege-engine plugin implemented by the gui/siege-engine script. Bind to a -key and activate after selecting a siege engine in 'q' mode.

                  +

                  Siege Engine

                  +

                  The siege-engine plugin enables siege engines to be linked to stockpiles, and +aimed at an arbitrary rectangular area across Z levels, instead of the original +four directions. Also, catapults can be ordered to load arbitrary objects, not +just stones.

                  +

                  The configuration front-end to the plugin is implemented by the gui/siege-engine +script. Bind it to a key and activate after selecting a siege engine in 'q' mode.

                  The main mode displays the current target, selected ammo item type, linked stockpiles and the allowed operator skill range. The map tile color is changed to signify if it can be hit by the selected engine: green for fully reachable, blue for out of range, red for blocked, yellow for partially blocked.

                  Pressing 'r' changes into the target selection mode, which works by highlighting two points with Enter like all designations. When a target area is set, the engine projectiles are -aimed at that area, or units within it, instead of the vanilla four directions.

                  +aimed at that area, or units within it.

                  After setting the target in this way for one engine, you can 'paste' the same area into others just by pressing 'p' in the main page of this script. The area to paste is kept until you quit DF, or select another area manually.

                  @@ -2492,22 +2556,27 @@ the script. Shift-ESC retains the current viewport, and also exits from the 'q' menu.

                  DISCLAIMER

                  -

                  Siege engines are a very interesting feature, but currently nearly useless +

                  Siege engines are a very interesting feature, but sadly almost useless in the current state because they haven't been updated since 2D and can only aim in four directions. This is an attempt to bring them more up to date until Toady has time to work on it. Actual improvements, e.g. like making siegers bring their own, are something only Toady can do.

                  +
                  +

                  Power Meter

                  +

                  The power-meter plugin implements a modified pressure plate that detects power being +supplied to gear boxes built in the four adjacent N/S/W/E tiles.

                  +

                  The configuration front-end is implemented by the gui/power-meter script. Bind it to a +key and activate after selecting Pressure Plate in the build menu.

                  +

                  The script follows the general look and feel of the regular pressure plate build +configuration page, but configures parameters relevant to the modded power meter building.

                  -
                  -

                  RAW hacks

                  -

                  These plugins detect certain structures in RAWs, and enhance them in various ways.

                  -

                  Steam Engine

                  +

                  Steam Engine

                  The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real steam engines.

                  -

                  Rationale

                  +

                  Rationale

                  The vanilla game contains only water wheels and windmills as sources of power, but windmills give relatively little power, and water wheels require flowing water, which must either be a real river and thus immovable and @@ -2518,14 +2587,14 @@ it can be done just by combining existing features of the game engine in a new way with some glue code and a bit of custom logic.

                  -

                  Construction

                  +

                  Construction

                  The workshop needs water as its input, which it takes via a passable floor tile below it, like usual magma workshops do. The magma version also needs magma.

                  ISSUE

                  Since this building is a machine, and machine collapse -code cannot be modified, it would collapse over true open space. +code cannot be hooked, it would collapse over true open space. As a loophole, down stair provides support to machines, while being passable, so use them.

                  @@ -2535,20 +2604,26 @@ is extracted from the workshop raws.

                  ISSUE

                  Like with collapse above, part of the code involved in -machine connection cannot be modified. As a result, the workshop +machine connection cannot be hooked. As a result, the workshop can only immediately connect to machine components built AFTER it. This also means that engines cannot be chained without intermediate short axles that can be built later than both of the engines.

                  -

                  Operation

                  +

                  Operation

                  In order to operate the engine, queue the Stoke Boiler job (optionally on repeat). A furnace operator will come, possibly bringing a bar of fuel, and perform it. As a result, a "boiling water" item will appear in the 't' view of the workshop.

                  -

                  NOTE: The completion of the job will actually consume one unit -of the appropriate liquids from below the workshop.

                  +
                  +

                  Note

                  +

                  The completion of the job will actually consume one unit +of the appropriate liquids from below the workshop. This means +that you cannot just raise 7 units of magma with a piston and +have infinite power. However, liquid consumption should be slow +enough that water can be supplied by a pond zone bucket chain.

                  +

                  Every such item gives 100 power, up to a limit of 300 for coal, and 500 for a magma engine. The building can host twice that amount of items to provide longer autonomous running. When the @@ -2567,7 +2642,7 @@ decrease it by further 4%, and also decrease the whole steam use rate by 10%.

                  -

                  Explosions

                  +

                  Explosions

                  The engine must be constructed using barrel, pipe and piston from fire-safe, or in the magma version magma-safe metals.

                  During operation weak parts get gradually worn out, and @@ -2576,7 +2651,7 @@ toppled during operation by a building destroyer, or a tantruming dwarf.

                  -

                  Save files

                  +

                  Save files

                  It should be safe to load and view engine-using fortresses from a DF version without DFHack installed, except that in such case the engines won't work. However actually making modifications @@ -2587,10 +2662,13 @@ being generated.

                  -

                  Add Spatter

                  +

                  Add Spatter

                  This plugin makes reactions with names starting with SPATTER_ADD_ -produce contaminants on the items instead of improvements.

                  -

                  Intended to give some use to all those poisons that can be bought from caravans.

                  +produce contaminants on the items instead of improvements. The produced +contaminants are immune to being washed away by water or destroyed by +the clean items command.

                  +

                  The plugin is intended to give some use to all those poisons that can +be bought from caravans. :)

                  To be really useful this needs patches from bug 808, tweak fix-dimensions and tweak advmode-contained.

                  diff --git a/library/Core.cpp b/library/Core.cpp index 735359a7f..8a8d39e06 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -126,8 +126,34 @@ struct Core::Private void Core::cheap_tokenise(string const& input, vector &output) { string *cur = NULL; + size_t i = 0; - for (size_t i = 0; i < input.size(); i++) { + // Check the first non-space character + while (i < input.size() && isspace(input[i])) i++; + + // Special verbatim argument mode? + if (i < input.size() && input[i] == ':') + { + // Read the command + std::string cmd; + i++; + while (i < input.size() && !isspace(input[i])) + cmd.push_back(input[i++]); + if (!cmd.empty()) + output.push_back(cmd); + + // Find the argument + while (i < input.size() && isspace(input[i])) i++; + + if (i < input.size()) + output.push_back(input.substr(i)); + + return; + } + + // Otherwise, parse in the regular quoted mode + for (; i < input.size(); i++) + { unsigned char c = input[i]; if (isspace(c)) { cur = NULL; From 821271383ed024dbcef469043ade36790cde89fe Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 24 Sep 2012 20:01:53 +0400 Subject: [PATCH 010/114] Add one more bad thought from siren for civilians. Originally a Tax Collector thought, but the message sounds appropriate. --- scripts/siren.lua | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/scripts/siren.lua b/scripts/siren.lua index 5371e3d7b..30d3aa07f 100644 --- a/scripts/siren.lua +++ b/scripts/siren.lua @@ -19,6 +19,8 @@ for _,v in ipairs(args) do table.insert(burrows, b) end +local in_siege = false + function is_in_burrows(pos) if #burrows == 0 then return true @@ -75,10 +77,21 @@ function stop_break(unit) end end +-- Check siege for thought purpose +for _,v in ipairs(df.global.ui.invasions.list) do + if v.flags.siege and v.flags.active then + in_siege = true + break + end +end + -- Stop rest for _,v in ipairs(df.global.world.units.active) do local x,y,z = dfhack.units.getPosition(v) - if x and not dfhack.units.isDead(v) and is_in_burrows(xyz2pos(x,y,z)) then + if x and dfhack.units.isCitizen(v) and is_in_burrows(xyz2pos(x,y,z)) then + if not in_siege and v.military.squad_index < 0 then + add_thought(v, df.unit_thought_type.LackProtection) + end wake_unit(v) stop_break(v) end From a622ee52e9e7063303ca04cc2b8ed1ae81d28a2d Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 24 Sep 2012 19:11:42 +0200 Subject: [PATCH 011/114] ruby: fix building_alloc Trap, and init Floodgates as closed --- plugins/ruby/building.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index 3f8842b44..59f715515 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -51,12 +51,14 @@ module DFHack subtype = WorkshopType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Workshop subtype = FurnaceType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Furnace subtype = CivzoneType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Civzone + subtype = TrapType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Trap bld.setSubtype(subtype) bld.setCustomType(custom) case type when :Furnace; bld.melt_remainder[world.raws.inorganics.length] = 0 when :Coffin; bld.initBurialFlags when :Trap; bld.unk_cc = 500 if bld.trap_type == :PressurePlate + when :Floodgate; bld.gate_flags.closed = true end bld end From f628c3732ae62c324c91553333ebd4e51f486090 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 24 Sep 2012 19:19:34 +0200 Subject: [PATCH 012/114] ruby: avoid smoothing grass/trees --- plugins/ruby/map.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb index 9629c3f9e..371614704 100644 --- a/plugins/ruby/map.rb +++ b/plugins/ruby/map.rb @@ -198,7 +198,7 @@ module DFHack def dig(mode=:Default) if mode == :Smooth - if tilemat != :SOIL and caption !~ /smooth|pillar|fortification/i and # XXX caption.. + if (tilemat == :STONE or tilemat == :MINERAL) and caption !~ /smooth|pillar|fortification/i and # XXX caption.. designation.smooth == 0 and (designation.hidden or not df.world.job_list.find { |j| # the game removes 'smooth' designation as soon as it assigns a job, if we # re-set it the game may queue another :DetailWall that will carve a fortification From ac9e78a6130d896510f186cc8f8ed5046cac45be Mon Sep 17 00:00:00 2001 From: warmist Date: Wed, 26 Sep 2012 08:39:07 +0300 Subject: [PATCH 013/114] Fixed dfhack.interpreter argument order The argument order was incorrect. See: https://github.com/angavrilov/dfhack/blob/master/library/lua/dfhack.lua#L222 --- LUA_API.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LUA_API.rst b/LUA_API.rst index b098aa054..f532d2212 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -488,7 +488,7 @@ Input & Output lock. Using an explicit ``dfhack.with_suspend`` will prevent this, forcing the function to block on input with lock held. -* ``dfhack.interpreter([prompt[,env[,history_filename]]])`` +* ``dfhack.interpreter([prompt[,history_filename[,env]]])`` Starts an interactive lua interpreter, using the specified prompt string, global environment and command-line history file. From d975ce513509d12c1f69752270ce65de4f80d7aa Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 26 Sep 2012 15:08:54 +0200 Subject: [PATCH 014/114] ruby: add onstatechange_register_once --- plugins/ruby/ruby.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index aeae101d3..81b73b7ce 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -119,6 +119,14 @@ module DFHack @onstatechange_list.delete b end + # same as onstatechange_register, but auto-unregisters if the block returns true + def onstatechange_register_once + handle = onstatechange_register { |st| + onstatechange_unregister(handle) if yield(st) + } + end + + # this method is called by dfhack every 'onstatechange' def onstatechange(newstate) @onstatechange_list ||= [] From 7cc29250f8622a317da464a2b7356a246082abc0 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 26 Sep 2012 18:13:01 +0200 Subject: [PATCH 015/114] fix for updated viewlist_petst --- library/modules/Gui.cpp | 2 +- plugins/ruby/unit.rb | 14 ++++++++++---- plugins/sort.cpp | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index eaeef9bf4..21156ac0a 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -766,7 +766,7 @@ static df::unit *getAnyUnit(df::viewscreen *top) { case df::viewscreen_petst::List: if (!vector_get(screen->is_vermin, screen->cursor)) - return (df::unit*)vector_get(screen->animal, screen->cursor); + return vector_get(screen->animal, screen->cursor).unit; return NULL; case df::viewscreen_petst::SelectTrainer: diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb index 5289229a6..139a375b2 100644 --- a/plugins/ruby/unit.rb +++ b/plugins/ruby/unit.rb @@ -12,9 +12,15 @@ module DFHack ref.unit_tg if ref.kind_of?(GeneralRefUnit) when :viewscreen_unitlistst v = curview - # TODO fix xml to use enums everywhere - page = DFHack::ViewscreenUnitlistst_TPage.int(v.page) - v.units[page][v.cursor_pos[page]] + v.units[v.page][v.cursor_pos[v.page]] + when :viewscreen_petst + v = curview + case v.mode + when :List + v.animal[v.cursor].unit if !v.is_vermin[v.cursor] + when :SelectTrainer + v.trainer_unit[v.trainer_cursor] + end else case ui.main.mode when :ViewUnits @@ -48,7 +54,7 @@ module DFHack } end - def unit_iscitizen(u) + def unit_iscitizen(u) u.race == ui.race_id and u.civ_id == ui.civ_id and !u.flags1.dead and !u.flags1.merchant and !u.flags1.forest and !u.flags1.diplomat and !u.flags2.resident and !u.flags3.ghostly and !u.curse.add_tags1.OPPOSED_TO_LIFE and !u.curse.add_tags1.CRAZED and diff --git a/plugins/sort.cpp b/plugins/sort.cpp index 4b2bf7bbd..95ae109a8 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -356,7 +356,7 @@ DEFINE_SORT_HANDLER(unit_sorters, pet, "/List", animals) std::vector units; for (size_t i = 0; i < animals->animal.size(); i++) - units.push_back(animals->is_vermin[i] ? NULL : (df::unit*)animals->animal[i]); + units.push_back(animals->is_vermin[i] ? NULL : animals->animal[i].unit); if (compute_order(*pout, L, top, &order, units)) { From 164c61b6383d704a9a5cc52ed482e79ede5cd515 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 27 Sep 2012 10:43:42 +0400 Subject: [PATCH 016/114] Forbid stuff from bins in siege-engine, if forbid ammo standing order set. --- library/include/DataDefs.h | 3 +++ plugins/siege-engine.cpp | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 61d5dec41..0966c7f31 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -707,6 +707,9 @@ namespace DFHack { // Global object pointers #include "df/global_objects.h" +#define DF_GLOBAL_VALUE(name,defval) (df::global::name ? *df::global::name : defval) +#define DF_GLOBAL_FIELD(name,fname,defval) (df::global::name ? df::global::name->fname : defval) + // A couple of headers that have to be included at once #include "df/coord2d.h" #include "df/coord.h" diff --git a/plugins/siege-engine.cpp b/plugins/siege-engine.cpp index 2e362afec..7c880351e 100644 --- a/plugins/siege-engine.cpp +++ b/plugins/siege-engine.cpp @@ -1573,6 +1573,8 @@ struct projectile_hook : df::proj_itemst { if (next_pos.z == cur_pos.z && !isPassableTile(next_pos)) start_z = 49000; + bool forbid_ammo = DF_GLOBAL_VALUE(standing_orders_forbid_used_ammo, false); + MapExtras::MapCache mc; std::vector contents; Items::getContainedItems(item, &contents); @@ -1581,6 +1583,9 @@ struct projectile_hook : df::proj_itemst { { auto child = contents[i]; + if (forbid_ammo) + child->flags.bits.forbid = true; + // Liquids are vaporized so that they cover nearby units if (child->isLiquid()) { From 7e0365857d4af487ad07dfb43a85ed7b789075aa Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 27 Sep 2012 11:19:28 +0400 Subject: [PATCH 017/114] Edit docs a bit more. --- Lua API.html | 2 +- README.rst | 43 ++++++++++++++++++++++++++------------ Readme.html | 51 ++++++++++++++++++++++++++++++++------------- dfhack.init-example | 2 +- 4 files changed, 69 insertions(+), 29 deletions(-) diff --git a/Lua API.html b/Lua API.html index f30bb6ec5..36be1ba4a 100644 --- a/Lua API.html +++ b/Lua API.html @@ -781,7 +781,7 @@ running coroutine and let the C++ code release the core suspend lock. Using an explicit dfhack.with_suspend will prevent this, forcing the function to block on input with lock held.

                  -
                • dfhack.interpreter([prompt[,env[,history_filename]]])

                  +
                • dfhack.interpreter([prompt[,history_filename[,env]]])

                  Starts an interactive lua interpreter, using the specified prompt string, global environment and command-line history file.

                  If the interactive console is not accessible, returns nil, error.

                  diff --git a/README.rst b/README.rst index 5202b92a2..35e44e5c1 100644 --- a/README.rst +++ b/README.rst @@ -159,6 +159,11 @@ If the first non-whitespace character is ``:``, the command is parsed in a speci alternative mode: first, non-whitespace characters immediately following the ``:`` are used as the command name; the remaining part of the line, starting with the first non-whitespace character *after* the command name, is used verbatim as the first argument. +The following two command lines are exactly equivalent: + + * ``:foo a b "c d" e f`` + * ``foo "a b \"c d\" e f"`` + This is intended for commands like ``rb_eval`` that evaluate script language statements. Almost all the commands support using the 'help ' built-in command @@ -616,14 +621,20 @@ Options: Pre-embark estimate ................... -If called during the embark selection screen, displays an estimate of layer -stone availability. If the 'all' option is specified, also estimates veins. -The estimate is computed either for 1 embark tile of the blinking biome, or -for all tiles of the embark rectangle. +If prospect is called during the embark selection screen, it displays an estimate of +layer stone availability. + +.. note:: + + The results of pre-embark prospect are an *estimate*, and can at best be expected + to be somewhere within +/- 30% of the true amount; sometimes it does a lot worse. + Especially, it is not clear how to precisely compute how many soil layers there + will be in a given embark tile, so it can report a whole extra layer, or omit one + that is actually present. Options: - :all: processes all tiles, even hidden ones. + :all: Also estimate vein mineral amounts. reveal ------ @@ -951,9 +962,9 @@ accidentally placed a construction on top of a valuable mineral floor. tweak ----- -Contains various tweaks for minor bugs (currently just one). +Contains various tweaks for minor bugs. -Options: +One-shot subcommands: :clear-missing: Remove the missing status from the selected unit. This allows engraving slabs for ghostly, but not yet @@ -982,6 +993,9 @@ Options: and are not flagged as tame), but you are allowed to mark them for slaughter. Grabbing wagons results in some funny spam, then they are scuttled. + +Subcommands that persist until disabled or DF quit: + :stable-cursor: Saves the exact cursor position between t/q/k/d/etc menus of dwarfmode. :patrol-duty: Makes Train orders not count as patrol duty to stop unhappy thoughts. Does NOT fix the problem when soldiers go off-duty (i.e. civilian). @@ -1557,8 +1571,9 @@ siren Wakes up sleeping units, cancels breaks and stops parties either everywhere, or in the burrows given as arguments. In return, adds bad thoughts about -noise and tiredness. Also, the units with interrupted breaks will go on -break again a lot sooner. +noise, tiredness and lack of protection. Also, the units with interrupted +breaks will go on break again a lot sooner. The script is intended for +emergencies, e.g. when a siege appears, and all your military is partying. growcrops ========= @@ -1712,14 +1727,15 @@ Use the arrow keys or number pad to move the cursor around, holding Shift to move 10 tiles at a time. Press the Z-Up (<) and Z-Down (>) keys to move quickly between labor/skill -categories. +categories. The numpad Z-Up and Z-Down keys seek to the first or last unit +in the list. Press Enter to toggle the selected labor for the selected unit, or Shift+Enter to toggle all labors within the selected category. Press the ``+-`` keys to sort the unit list according to the currently selected skill/labor, and press the ``*/`` keys to sort the unit list by Name, Profession, -or Happiness (using Tab to select which sort method to use here). +Happiness, or Arrival order (using Tab to select which sort method to use here). With a unit selected, you can press the "v" key to view its properties (and possibly set a custom nickname or profession) or the "c" key to exit @@ -1776,7 +1792,7 @@ via a simple dialog in the game ui. * ``gui/rename unit-profession`` changes the selected unit's custom profession name. -The ``building`` or ``unit`` are automatically assumed when in relevant ui state. +The ``building`` or ``unit`` options are automatically assumed when in relevant ui state. Room List @@ -1816,7 +1832,8 @@ yellow for partially blocked. Pressing 'r' changes into the target selection mode, which works by highlighting two points with Enter like all designations. When a target area is set, the engine projectiles are -aimed at that area, or units within it. +aimed at that area, or units within it (this doesn't actually change the original aiming +code, instead the projectile trajectory parameters are rewritten as soon as it appears). After setting the target in this way for one engine, you can 'paste' the same area into others just by pressing 'p' in the main page of this script. The area to paste is kept until you quit diff --git a/Readme.html b/Readme.html index 7aebeb13a..3610ecd80 100644 --- a/Readme.html +++ b/Readme.html @@ -642,7 +642,14 @@ as a comment, i.e. a silent no-op command.

                  alternative mode: first, non-whitespace characters immediately following the : are used as the command name; the remaining part of the line, starting with the first non-whitespace character after the command name, is used verbatim as the first argument. -This is intended for commands like rb_eval that evaluate script language statements.

                  +The following two command lines are exactly equivalent:

                  +
                  +
                    +
                  • :foo a b "c d" e f
                  • +
                  • foo "a b \"c d\" e f"
                  • +
                  +
                  +

                  This is intended for commands like rb_eval that evaluate script language statements.

                  Almost all the commands support using the 'help <command-name>' built-in command to retrieve further help without having to look at this document. Alternatively, some accept a 'help'/'?' option on their command line.

                  @@ -1190,17 +1197,23 @@ the visible part of the map is scanned.

                  Pre-embark estimate

                  -

                  If called during the embark selection screen, displays an estimate of layer -stone availability. If the 'all' option is specified, also estimates veins. -The estimate is computed either for 1 embark tile of the blinking biome, or -for all tiles of the embark rectangle.

                  +

                  If prospect is called during the embark selection screen, it displays an estimate of +layer stone availability.

                  +
                  +

                  Note

                  +

                  The results of pre-embark prospect are an estimate, and can at best be expected +to be somewhere within +/- 30% of the true amount; sometimes it does a lot worse. +Especially, it is not clear how to precisely compute how many soil layers there +will be in a given embark tile, so it can report a whole extra layer, or omit one +that is actually present.

                  +

                  Options:

                  - +
                  all:processes all tiles, even hidden ones.
                  all:Also estimate vein mineral amounts.
                  @@ -1670,8 +1683,8 @@ accidentally placed a construction on top of a valuable mineral floor.

                  tweak

                  -

                  Contains various tweaks for minor bugs (currently just one).

                  -

                  Options:

                  +

                  Contains various tweaks for minor bugs.

                  +

                  One-shot subcommands:

                  @@ -1707,6 +1720,13 @@ and are not flagged as tame), but you are allowed to mark them for slaughter. Grabbing wagons results in some funny spam, then they are scuttled. + +
                  +

                  Subcommands that persist until disabled or DF quit:

                  + +++ + +
                  stable-cursor:Saves the exact cursor position between t/q/k/d/etc menus of dwarfmode.
                  patrol-duty:Makes Train orders not count as patrol duty to stop unhappy thoughts. @@ -2342,8 +2362,9 @@ combat in slow motion or something :)

                  siren

                  Wakes up sleeping units, cancels breaks and stops parties either everywhere, or in the burrows given as arguments. In return, adds bad thoughts about -noise and tiredness. Also, the units with interrupted breaks will go on -break again a lot sooner.

                  +noise, tiredness and lack of protection. Also, the units with interrupted +breaks will go on break again a lot sooner. The script is intended for +emergencies, e.g. when a siege appears, and all your military is partying.

                  growcrops

                  @@ -2466,12 +2487,13 @@ denote skills not controlled by labors.

                  Use the arrow keys or number pad to move the cursor around, holding Shift to move 10 tiles at a time.

                  Press the Z-Up (<) and Z-Down (>) keys to move quickly between labor/skill -categories.

                  +categories. The numpad Z-Up and Z-Down keys seek to the first or last unit +in the list.

                  Press Enter to toggle the selected labor for the selected unit, or Shift+Enter to toggle all labors within the selected category.

                  Press the +- keys to sort the unit list according to the currently selected skill/labor, and press the */ keys to sort the unit list by Name, Profession, -or Happiness (using Tab to select which sort method to use here).

                  +Happiness, or Arrival order (using Tab to select which sort method to use here).

                  With a unit selected, you can press the "v" key to view its properties (and possibly set a custom nickname or profession) or the "c" key to exit Manipulator and zoom to its position within your fortress.

                  @@ -2517,7 +2539,7 @@ It is also possible to rename zones from the 'i' menu.

                • gui/rename unit-profession changes the selected unit's custom profession name.

                • -

                  The building or unit are automatically assumed when in relevant ui state.

                  +

                  The building or unit options are automatically assumed when in relevant ui state.

                  Room List

                  @@ -2546,7 +2568,8 @@ hit by the selected engine: green for fully reachable, blue for out of range, re yellow for partially blocked.

                  Pressing 'r' changes into the target selection mode, which works by highlighting two points with Enter like all designations. When a target area is set, the engine projectiles are -aimed at that area, or units within it.

                  +aimed at that area, or units within it (this doesn't actually change the original aiming +code, instead the projectile trajectory parameters are rewritten as soon as it appears).

                  After setting the target in this way for one engine, you can 'paste' the same area into others just by pressing 'p' in the main page of this script. The area to paste is kept until you quit DF, or select another area manually.

                  diff --git a/dfhack.init-example b/dfhack.init-example index 83c3641b6..0bf344609 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -18,7 +18,7 @@ keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave # gui/rename script keybinding add Ctrl-Shift-N gui/rename -keybinding add Ctrl-Shift-P "gui/rename unit-profession" +keybinding add Alt-Shift-P "gui/rename unit-profession" ############################## # Generic adv mode bindings # From bd7cd690aa4260d4df9625a124a2a15480bdd848 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 28 Sep 2012 13:32:41 +0400 Subject: [PATCH 018/114] Implement the common suggestion of Shift-Enter in trade screens. --- README.rst | 3 ++- Readme.html | 3 +++ dfhack.init-example | 5 ++++ plugins/manipulator.cpp | 1 + plugins/tweak.cpp | 51 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 35e44e5c1..7e8678c9f 100644 --- a/README.rst +++ b/README.rst @@ -1013,7 +1013,8 @@ Subcommands that persist until disabled or DF quit: in advmode. The issue is that the screen tries to force you to select the contents separately from the container. This forcefully skips child reagents. - +:fast-trade: Makes Shift-Enter in the Move Goods to Depot and Trade screens select + the current item (fully, in case of a stack), and scroll down one line. Mode switch and reclaim ======================= diff --git a/Readme.html b/Readme.html index 3610ecd80..6155b72ce 100644 --- a/Readme.html +++ b/Readme.html @@ -1753,6 +1753,9 @@ in advmode. The issue is that the screen tries to force you to select the contents separately from the container. This forcefully skips child reagents.
                  fast-trade:Makes Shift-Enter in the Move Goods to Depot and Trade screens select +the current item (fully, in case of a stack), and scroll down one line.
                  diff --git a/dfhack.init-example b/dfhack.init-example index 0bf344609..8a3ec8f29 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -45,6 +45,7 @@ keybinding add Shift-R "job-material RHYOLITE" keybinding add Shift-I "job-material CINNABAR" keybinding add Shift-B "job-material COBALTITE" keybinding add Shift-O "job-material OBSIDIAN" +keybinding add Shift-T "job-material ORTHOCLASE" keybinding add Shift-G "job-material GLASS_GREEN" # browse linked mechanisms @@ -90,3 +91,7 @@ tweak fix-dimensions # make reactions requiring containers usable in advmode - the issue is # that the screen asks for those reagents to be selected directly tweak advmode-contained + +# support Shift-Enter in Trade and Move Goods to Depot screens for faster +# selection; it selects the current item or stack and scrolls down one line +tweak fast-trade diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 0b0b99d48..f3c6664a1 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -523,6 +523,7 @@ void viewscreen_unitlaborsst::feed(set *events) { events->insert(interface_key::LEAVESCREEN); parent->feed(events); + events->clear(); } return; } diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index fb286e0d7..6d0591d13 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -43,6 +43,8 @@ #include "df/reaction.h" #include "df/reaction_reagent_itemst.h" #include "df/reaction_reagent_flags.h" +#include "df/viewscreen_layer_assigntradest.h" +#include "df/viewscreen_tradegoodsst.h" #include @@ -109,6 +111,9 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector *input)) + { + if (layer_objects[1]->active && input->count(interface_key::SELECT_ALL)) + { + set tmp; tmp.insert(interface_key::SELECT); + INTERPOSE_NEXT(feed)(&tmp); + tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN); + INTERPOSE_NEXT(feed)(&tmp); + } + else + INTERPOSE_NEXT(feed)(input); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_assign_hook, feed); + +struct fast_trade_select_hook : df::viewscreen_tradegoodsst { + typedef df::viewscreen_tradegoodsst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (!(is_unloading || !has_traders || in_edit_count) + && input->count(interface_key::SELECT_ALL)) + { + set tmp; tmp.insert(interface_key::SELECT); + INTERPOSE_NEXT(feed)(&tmp); + if (in_edit_count) + INTERPOSE_NEXT(feed)(&tmp); + tmp.clear(); tmp.insert(interface_key::STANDARDSCROLL_DOWN); + INTERPOSE_NEXT(feed)(&tmp); + } + else + INTERPOSE_NEXT(feed)(input); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(fast_trade_select_hook, feed); + static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) { if (vector_get(parameters, 1) == "disable") @@ -653,6 +699,11 @@ static command_result tweak(color_ostream &out, vector ¶meters) { enable_hook(out, INTERPOSE_HOOK(advmode_contained_hook, feed), parameters); } + else if (cmd == "fast-trade") + { + enable_hook(out, INTERPOSE_HOOK(fast_trade_assign_hook, feed), parameters); + enable_hook(out, INTERPOSE_HOOK(fast_trade_select_hook, feed), parameters); + } else return CR_WRONG_USAGE; From 9784ca559144ec24f513dee23002bf49100ba897 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 28 Sep 2012 16:08:42 +0400 Subject: [PATCH 019/114] Mention the new tweak in NEWS. --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 8f7c407e7..ffce93d23 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,7 @@ DFHack v0.34.11-r2 (UNRELEASED) - tweak fast-heat: speeds up item heating & cooling, thus making stable-temp act faster. - tweak fix-dimensions: fixes subtracting small amounts from stacked liquids etc. - tweak advmode-contained: fixes UI bug in custom reactions with container inputs in advmode. + - tweak fast-trade: Shift-Enter for selecting items quckly in Trade and Move to Depot screens. New scripts: - fixnaked: removes thoughts about nakedness. - setfps: set FPS cap at runtime, in case you want slow motion or speed-up. From ba763b773d64c17a58ccd64f2b0581b6619c81e6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 28 Sep 2012 18:01:17 +0400 Subject: [PATCH 020/114] New tweaks for the military assign to position screen. --- NEWS | 2 + README.rst | 6 ++ dfhack.init-example | 11 ++++ plugins/lua/sort/items.lua | 8 ++- plugins/tweak.cpp | 112 +++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index ffce93d23..7bcd7513b 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ DFHack v0.34.11-r2 (UNRELEASED) - tweak fix-dimensions: fixes subtracting small amounts from stacked liquids etc. - tweak advmode-contained: fixes UI bug in custom reactions with container inputs in advmode. - tweak fast-trade: Shift-Enter for selecting items quckly in Trade and Move to Depot screens. + - tweak military-stable-assign: Stop rightmost list of military->Positions from jumping to top. + - tweak military-color-assigned: In same list, color already assigned units in brown & green. New scripts: - fixnaked: removes thoughts about nakedness. - setfps: set FPS cap at runtime, in case you want slow motion or speed-up. diff --git a/README.rst b/README.rst index 7e8678c9f..5801e6b45 100644 --- a/README.rst +++ b/README.rst @@ -1015,6 +1015,12 @@ Subcommands that persist until disabled or DF quit: reagents. :fast-trade: Makes Shift-Enter in the Move Goods to Depot and Trade screens select the current item (fully, in case of a stack), and scroll down one line. +:military-stable-assign: Preserve list order and cursor position when assigning to squad, + i.e. stop the rightmost list of the Positions page of the military + screen from constantly resetting to the top. +:military-color-assigned: Color squad candidates already assigned to other squads in brown/green + to make them stand out more in the list. + Mode switch and reclaim ======================= diff --git a/dfhack.init-example b/dfhack.init-example index 8a3ec8f29..2e656a609 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -48,6 +48,12 @@ keybinding add Shift-O "job-material OBSIDIAN" keybinding add Shift-T "job-material ORTHOCLASE" keybinding add Shift-G "job-material GLASS_GREEN" +# sort units and items +keybinding add Alt-Shift-N "sort-units name" "sort-items description" +keybinding add Alt-Shift-R "sort-units arrival" +keybinding add Alt-Shift-T "sort-units profession" "sort-items type material" +keybinding add Alt-Shift-Q "sort-units squad_position" "sort-items quality" + # browse linked mechanisms keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms @@ -95,3 +101,8 @@ tweak advmode-contained # support Shift-Enter in Trade and Move Goods to Depot screens for faster # selection; it selects the current item or stack and scrolls down one line tweak fast-trade + +# stop the right list in military->positions from resetting to top all the time +tweak military-stable-assign +# in same list, color units already assigned to squads in brown & green +tweak military-color-assigned diff --git a/plugins/lua/sort/items.lua b/plugins/lua/sort/items.lua index 13b62ff9b..5c6d4a5ba 100644 --- a/plugins/lua/sort/items.lua +++ b/plugins/lua/sort/items.lua @@ -23,12 +23,18 @@ orders.description = { end } -orders.quality = { +orders.base_quality = { key = function(item) return item:getQuality() end } +orders.quality = { + key = function(item) + return item:getOverallQuality() + end +} + orders.improvement = { key = function(item) return item:getImprovementQuality() diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 6d0591d13..939c94b3b 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -45,6 +45,7 @@ #include "df/reaction_reagent_flags.h" #include "df/viewscreen_layer_assigntradest.h" #include "df/viewscreen_tradegoodsst.h" +#include "df/viewscreen_layer_militaryst.h" #include @@ -61,6 +62,7 @@ using df::global::ui_menu_width; using df::global::ui_area_map_width; using namespace DFHack::Gui; +using Screen::Pen; static command_result tweak(color_ostream &out, vector & parameters); @@ -114,6 +116,13 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector *input)) + { + if (inPositionsMode() && !layer_objects[0]->active) + { + auto pos_list = layer_objects[1]; + auto plist = layer_objects[2]; + auto &cand = positions.candidates; + + // Save the candidate list and cursors + std::vector copy = cand; + int cursor = plist->getListCursor(); + int pos_cursor = pos_list->getListCursor(); + + INTERPOSE_NEXT(feed)(input); + + if (inPositionsMode() && !layer_objects[0]->active) + { + bool is_select = input->count(interface_key::SELECT); + + // Resort the candidate list and restore cursor + // on add to squad OR scroll in the position list. + if (!plist->active || is_select) + { + // Since we don't know the actual sorting order, preserve + // the ordering of the items in the list before keypress. + // This does the right thing even if the list was sorted + // with sort-units. + std::set prev, next; + prev.insert(copy.begin(), copy.end()); + next.insert(cand.begin(), cand.end()); + std::vector out; + + // (old-before-cursor) (new) |cursor| (old-after-cursor) + for (int i = 0; i < cursor && i < (int)copy.size(); i++) + if (next.count(copy[i])) out.push_back(copy[i]); + for (size_t i = 0; i < cand.size(); i++) + if (!prev.count(cand[i])) out.push_back(cand[i]); + int new_cursor = out.size(); + for (int i = cursor; i < (int)copy.size(); i++) + if (next.count(copy[i])) out.push_back(copy[i]); + + cand.swap(out); + plist->setListLength(cand.size()); + if (new_cursor < (int)cand.size()) + plist->setListCursor(new_cursor); + } + + // Preserve the position list index on remove from squad + if (pos_list->active && is_select) + pos_list->setListCursor(pos_cursor); + } + } + else + INTERPOSE_NEXT(feed)(input); + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + + if (inPositionsMode()) + { + auto plist = layer_objects[2]; + int x1 = plist->getX1(), y1 = plist->getY1(); + int x2 = plist->getX2(), y2 = plist->getY2(); + int i1 = plist->getFirstVisible(), i2 = plist->getLastVisible(); + + for (int y = y1, i = i1; i <= i2; i++, y++) + { + auto unit = vector_get(positions.candidates, i); + if (!unit || unit->military.squad_index < 0) + continue; + + for (int x = x1; x <= x2; x++) + { + Pen cur_tile = Screen::readTile(x, y); + if (!cur_tile.valid()) continue; + cur_tile.fg = (cur_tile.fg == COLOR_GREY ? COLOR_BROWN : COLOR_GREEN); + Screen::paintTile(cur_tile, x, y); + } + } + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(military_assign_hook, render); + static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) { if (vector_get(parameters, 1) == "disable") @@ -704,6 +808,14 @@ static command_result tweak(color_ostream &out, vector ¶meters) enable_hook(out, INTERPOSE_HOOK(fast_trade_assign_hook, feed), parameters); enable_hook(out, INTERPOSE_HOOK(fast_trade_select_hook, feed), parameters); } + else if (cmd == "military-stable-assign") + { + enable_hook(out, INTERPOSE_HOOK(military_assign_hook, feed), parameters); + } + else if (cmd == "military-color-assigned") + { + enable_hook(out, INTERPOSE_HOOK(military_assign_hook, render), parameters); + } else return CR_WRONG_USAGE; From 783b218eec3bc1b1c4c33f57700db39fc92069a4 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 28 Sep 2012 18:50:01 +0400 Subject: [PATCH 021/114] Try a different color scheme when highlighting squad members in tweak. --- plugins/tweak.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 939c94b3b..d54c4a5ec 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -622,6 +622,7 @@ struct military_assign_hook : df::viewscreen_layer_militaryst { int x1 = plist->getX1(), y1 = plist->getY1(); int x2 = plist->getX2(), y2 = plist->getY2(); int i1 = plist->getFirstVisible(), i2 = plist->getLastVisible(); + int si = plist->getListCursor(); for (int y = y1, i = i1; i <= i2; i++, y++) { @@ -633,7 +634,7 @@ struct military_assign_hook : df::viewscreen_layer_militaryst { { Pen cur_tile = Screen::readTile(x, y); if (!cur_tile.valid()) continue; - cur_tile.fg = (cur_tile.fg == COLOR_GREY ? COLOR_BROWN : COLOR_GREEN); + cur_tile.fg = (i == si) ? COLOR_BROWN : COLOR_GREEN; Screen::paintTile(cur_tile, x, y); } } From c4f26d0aa819e6038968d152c98198e48e52e8e1 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 28 Sep 2012 10:44:35 +0200 Subject: [PATCH 022/114] manipulator: make up/down keys wrap around --- plugins/manipulator.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index f3c6664a1..d676dc2a1 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -534,6 +534,8 @@ void viewscreen_unitlaborsst::feed(set *events) if (do_refresh_names) refreshNames(); + int old_sel_row = sel_row; + if (events->count(interface_key::CURSOR_UP) || events->count(interface_key::CURSOR_UPLEFT) || events->count(interface_key::CURSOR_UPRIGHT)) sel_row--; if (events->count(interface_key::CURSOR_UP_FAST) || events->count(interface_key::CURSOR_UPLEFT_FAST) || events->count(interface_key::CURSOR_UPRIGHT_FAST)) @@ -553,9 +555,20 @@ void viewscreen_unitlaborsst::feed(set *events) } if (sel_row < 0) - sel_row = 0; + { + if (old_sel_row == 0 && events->count(interface_key::CURSOR_UP)) + sel_row = units.size() - 1; + else + sel_row = 0; + } + if (sel_row > units.size() - 1) - sel_row = units.size() - 1; + { + if (old_sel_row == units.size()-1 && events->count(interface_key::CURSOR_DOWN)) + sel_row = 0; + else + sel_row = units.size() - 1; + } if (sel_row < first_row) first_row = sel_row; From ca0097b91089a7012b3aa3106721623545803dd4 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 28 Sep 2012 13:52:54 +0200 Subject: [PATCH 023/114] manipulator: better screen width usage --- plugins/manipulator.cpp | 104 ++++++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 25 deletions(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index d676dc2a1..7451f1373 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -439,6 +439,7 @@ void viewscreen_unitlaborsst::refreshNames() cur->transname = Translation::TranslateName(&unit->name, true); cur->profession = Units::getProfessionName(unit); } + calcSize(); } void viewscreen_unitlaborsst::calcSize() @@ -448,39 +449,92 @@ void viewscreen_unitlaborsst::calcSize() num_rows = units.size(); int num_columns = gps->dimx - DISP_COLUMN_MAX - 1; + + // min/max width of columns + int col_minwidth[DISP_COLUMN_MAX]; + int col_maxwidth[DISP_COLUMN_MAX]; + col_minwidth[DISP_COLUMN_HAPPINESS] = 4; + col_maxwidth[DISP_COLUMN_HAPPINESS] = 4; + col_minwidth[DISP_COLUMN_NAME] = 0; + col_maxwidth[DISP_COLUMN_NAME] = 0; + col_minwidth[DISP_COLUMN_PROFESSION] = 0; + col_maxwidth[DISP_COLUMN_PROFESSION] = 0; + col_minwidth[DISP_COLUMN_LABORS] = num_columns*3/5; // 60% + col_maxwidth[DISP_COLUMN_LABORS] = NUM_COLUMNS; + + // get max_name/max_prof from strings length + for (size_t i = 0; i < units.size(); i++) + { + if (col_maxwidth[DISP_COLUMN_NAME] < units[i]->name.size()) + col_maxwidth[DISP_COLUMN_NAME] = units[i]->name.size(); + if (col_maxwidth[DISP_COLUMN_PROFESSION] < units[i]->profession.size()) + col_maxwidth[DISP_COLUMN_PROFESSION] = units[i]->profession.size(); + } + + // check how much room we have + int width_min = 0, width_max = 0; for (int i = 0; i < DISP_COLUMN_MAX; i++) - col_widths[i] = 0; - while (num_columns > 0) { - num_columns--; - // need at least 4 digits for happiness - if (col_widths[DISP_COLUMN_HAPPINESS] < 4) + width_min += col_minwidth[i]; + width_max += col_maxwidth[i]; + } + + if (width_max <= num_columns) + { + // lots of space, distribute leftover (except last column) + int col_margin = (num_columns - width_max) / (DISP_COLUMN_MAX-1); + int col_margin_r = (num_columns - width_max) % (DISP_COLUMN_MAX-1); + for (int i = DISP_COLUMN_MAX-1; i>=0; i--) { - col_widths[DISP_COLUMN_HAPPINESS]++; - continue; + col_widths[i] = col_maxwidth[i]; + + if (i < DISP_COLUMN_MAX-1) + { + col_widths[i] += col_margin; + + if (col_margin_r) + { + col_margin_r--; + col_widths[i]++; + } + } } - // of remaining, 20% for Name, 20% for Profession, 60% for Labors - switch (num_columns % 5) + } + else if (width_min <= num_columns) + { + // constrained, give between min and max to every column + int space = num_columns - width_min; + // max size columns not yet seen may consume + int next_consume_max = width_max - width_min; + + for (int i = 0; i < DISP_COLUMN_MAX; i++) { - case 0: case 2: case 4: - col_widths[DISP_COLUMN_LABORS]++; - break; - case 1: - col_widths[DISP_COLUMN_NAME]++; - break; - case 3: - col_widths[DISP_COLUMN_PROFESSION]++; - break; + // divide evenly remaining space + int col_margin = space / (DISP_COLUMN_MAX-i); + + // take more if the columns after us cannot + next_consume_max -= (col_maxwidth[i]-col_minwidth[i]); + if (col_margin < space-next_consume_max) + col_margin = space - next_consume_max; + + // no more than maxwidth + if (col_margin > col_maxwidth[i] - col_minwidth[i]) + col_margin = col_maxwidth[i] - col_minwidth[i]; + + col_widths[i] = col_minwidth[i] + col_margin; + + space -= col_margin; } } - - while (col_widths[DISP_COLUMN_LABORS] > NUM_COLUMNS) + else { - col_widths[DISP_COLUMN_LABORS]--; - if (col_widths[DISP_COLUMN_LABORS] & 1) - col_widths[DISP_COLUMN_NAME]++; - else - col_widths[DISP_COLUMN_PROFESSION]++; + // should not happen, min screen is 80x25 + int space = num_columns; + for (int i = 0; i < DISP_COLUMN_MAX; i++) + { + col_widths[i] = space / (DISP_COLUMN_MAX-i); + space -= col_widths[i]; + } } for (int i = 0; i < DISP_COLUMN_MAX; i++) From 4a495c2c7fa9cef78628a3026a434322ca7418d1 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 28 Sep 2012 18:55:13 +0200 Subject: [PATCH 024/114] manipulator: make cursor_down_z go to beginning of next group --- plugins/manipulator.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 7451f1373..b04c80799 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -648,11 +648,13 @@ void viewscreen_unitlaborsst::feed(set *events) } if ((sel_column != NUM_COLUMNS - 1) && events->count(interface_key::CURSOR_DOWN_Z)) { - // go to end of current column group; if already at the end, go to the end of the next one - sel_column++; + // go to beginning of next group int cur = columns[sel_column].group; - while ((sel_column < NUM_COLUMNS - 1) && columns[sel_column + 1].group == cur) - sel_column++; + int next = sel_column+1; + while ((next < NUM_COLUMNS) && (columns[next].group == cur)) + next++; + if ((next < NUM_COLUMNS) && (columns[next].group != cur)) + sel_column = next; } if (sel_column < 0) From 687657f2da175db827109384737d6a2220cf1c32 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 28 Sep 2012 18:58:58 +0200 Subject: [PATCH 025/114] manipulator: show full group when navigating labor groups --- plugins/manipulator.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index b04c80799..953cf2ba9 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -662,6 +662,17 @@ void viewscreen_unitlaborsst::feed(set *events) if (sel_column > NUM_COLUMNS - 1) sel_column = NUM_COLUMNS - 1; + if (events->count(interface_key::CURSOR_DOWN_Z) || events->count(interface_key::CURSOR_UP_Z)) + { + // when moving by group, ensure the whole group is shown onscreen + int endgroup_column = sel_column; + while ((endgroup_column < NUM_COLUMNS-1) && columns[endgroup_column+1].group == columns[sel_column].group) + endgroup_column++; + + if (first_column < endgroup_column - col_widths[DISP_COLUMN_LABORS] + 1) + first_column = endgroup_column - col_widths[DISP_COLUMN_LABORS] + 1; + } + if (sel_column < first_column) first_column = sel_column; if (first_column < sel_column - col_widths[DISP_COLUMN_LABORS] + 1) From 53c6ee4673153aa8f8ad6f2c082d8a100e8f75c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 28 Sep 2012 21:57:12 +0200 Subject: [PATCH 026/114] Track structures and stonesense --- library/xml | 2 +- plugins/stonesense | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index e6a59bfeb..9773484b8 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e6a59bfebd56e209a0719c173f93a33262751528 +Subproject commit 9773484b8430417ad488423ddeae3832f011ce9d diff --git a/plugins/stonesense b/plugins/stonesense index 2a62ba5ed..75df76626 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 2a62ba5ed2607f4dbf0473e77502d4e19c19678e +Subproject commit 75df766263b23182820a1e07b330e64f87d5c9b7 From 79ac1a81b9c2751c162fc413c91ef0ee84766d01 Mon Sep 17 00:00:00 2001 From: expwnent Date: Fri, 28 Sep 2012 16:36:37 -0400 Subject: [PATCH 027/114] It was backwards when calling fastdwarf with just one argument. Also made it print the current status of fastdwarf when no arguments are given. --- plugins/fastdwarf.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index c51684ae6..400bff23b 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -72,10 +72,10 @@ static command_result fastdwarf (color_ostream &out, vector & parameter { if (parameters.size() == 1) { if ( parameters[0] == "0" ) { - enable_fastdwarf = true; + enable_fastdwarf = false; enable_teledwarf = false; } else if ( parameters[0] == "1" ) { - enable_fastdwarf = false; + enable_fastdwarf = true; enable_teledwarf = false; } else { out.print("Incorrect usage.\n"); @@ -100,6 +100,7 @@ static command_result fastdwarf (color_ostream &out, vector & parameter } } else if (parameters.size() == 0) { //print status + out.print("Current state: fast = %d, teleport = %d.\n", enable_fastdwarf, enable_teledwarf); } else { out.print("Incorrect usage.\n"); return CR_OK; From 634c281f00dfb6c5a4170044062ceff00ceb82c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 28 Sep 2012 22:44:14 +0200 Subject: [PATCH 028/114] Track clsocket --- depends/clsocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/clsocket b/depends/clsocket index d0b2d0750..178a4916d 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit d0b2d0750dc2d529a152eba4f3f519f69ff7eab0 +Subproject commit 178a4916da838e46e46e769e566e47fff6eff8f8 From 44a74ddc38abf86d093db2e7ff2aa3af24bf8173 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 28 Sep 2012 22:58:26 +0200 Subject: [PATCH 029/114] ruby: remove unused var --- plugins/ruby/ruby.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index e291a770f..7bd6d13e8 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -205,8 +205,6 @@ DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_ch static command_result df_rubyeval(color_ostream &out, std::vector & parameters) { - command_result ret; - if (parameters.size() == 1 && (parameters[0] == "help" || parameters[0] == "?")) { out.print("This command executes an arbitrary ruby statement.\n"); From 1bde32fa5b72a070a81c142b4044f8fd355e2145 Mon Sep 17 00:00:00 2001 From: expwnent Date: Fri, 28 Sep 2012 19:06:22 -0400 Subject: [PATCH 030/114] Fixed a tile occupancy problem with teleporting dwarves. --- plugins/fastdwarf.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 400bff23b..4c235394b 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -2,6 +2,7 @@ #include "Console.h" #include "Export.h" #include "PluginManager.h" +#include "modules/MapCache.h" #include "DataDefs.h" #include "df/ui.h" @@ -48,6 +49,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } } if ( enable_teledwarf ) { + MapExtras::MapCache *MCache = new MapExtras::MapCache(); for (size_t i = 0; i < world->units.all.size(); i++) { df::unit *unit = world->units.all[i]; @@ -59,11 +61,19 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (unit->relations.following != 0) continue; + MapExtras::Block* block = MCache->BlockAtTile(unit->pos); + df::coord2d pos(unit->pos.x % 16, unit->pos.y % 16); + df::tile_occupancy occ = block->OccupancyAt(pos); + occ.bits.unit = 0; + block->setOccupancyAt(pos, occ); + //move immediately to destination unit->pos.x = unit->path.dest.x; unit->pos.y = unit->path.dest.y; unit->pos.z = unit->path.dest.z; } + MCache->WriteAll(); + delete MCache; } return CR_OK; } From d41c4849f96eef155b41eac29d887ce926bd1781 Mon Sep 17 00:00:00 2001 From: expwnent Date: Fri, 28 Sep 2012 19:17:12 -0400 Subject: [PATCH 031/114] Fixed unit_grounded in teleporting dwarf tile. --- plugins/fastdwarf.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 4c235394b..aea14e7f0 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -65,6 +65,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) df::coord2d pos(unit->pos.x % 16, unit->pos.y % 16); df::tile_occupancy occ = block->OccupancyAt(pos); occ.bits.unit = 0; + occ.bits.unit_grounded = 0; block->setOccupancyAt(pos, occ); //move immediately to destination From 12f5e6028ce91e67143c7dad317ffe1430603193 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 29 Sep 2012 01:38:32 +0200 Subject: [PATCH 032/114] manipulator: avoid NULL deref with unassigned job in unit list --- plugins/manipulator.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 953cf2ba9..feceb08ec 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -383,8 +383,12 @@ viewscreen_unitlaborsst::viewscreen_unitlaborsst(vector &src, int cur for (size_t i = 0; i < src.size(); i++) { - UnitInfo *cur = new UnitInfo; df::unit *unit = src[i]; + if (!unit) + continue; + + UnitInfo *cur = new UnitInfo; + cur->unit = unit; cur->allowEdit = true; cur->active_index = active_idx[unit]; From cd18660f0edfb131d9ae1f3fe30a7e34f6339070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 29 Sep 2012 02:58:30 +0200 Subject: [PATCH 033/114] Add documentation for added plugins - digtype, misery, changes in fastdwarf --- NEWS | 3 +++ README.rst | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 7bcd7513b..ee6dd6592 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,9 @@ DFHack v0.34.11-r2 (UNRELEASED) - liquids: can paint permaflow, i.e. what makes rivers power water wheels. - prospect: pre-embark prospector accounts for caves & magma sea in its estimate. - rename: supports renaming stockpiles, workshops, traps, siege engines. + - fastdwarf: now has an additional option to make dwarves teleport to their destination. + - misery: multiplies every negative thought gained (2x by default). + - digtype: designates every tile of the same type of vein on the map for 'digging' (any dig designation). New tweaks: - tweak stable-cursor: keeps exact cursor position between d/k/t/q/v etc menus. - tweak patrol-duty: makes Train orders reduce patrol timer, like the binary patch does. diff --git a/README.rst b/README.rst index 5801e6b45..5cf79cdf5 100644 --- a/README.rst +++ b/README.rst @@ -193,11 +193,14 @@ by 'reveal hell'. This is nice for digging under rivers. fastdwarf --------- -Makes your minions move at ludicrous speeds. - - * Activate with 'fastdwarf 1' - * Deactivate with 'fastdwarf 0' +Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly and perform tasks quickly. Teledwarf makes dwarves move instantaneously, but do jobs at the same speed. + * 'fastdwarf 0 0' disables both + * 'fastdwarf 0 1' disables speedydwarf and enables teledwarf + * 'fastdwarf 1 0' enables speedydwarf and disables teledwarf + * 'fastdwarf 1 1' enables both + * 'fastdwarf 0' disables both + * 'fastdwarf 1' enables speedydwarf and disables teledwarf Game interface ============== @@ -804,6 +807,22 @@ Examples: * 'digcircle' = Do it again. +digtype +------- +For every tile on the map of the same vein type as the selected tile, this command designates it to have the same designation as the selected tile. If the selected tile has no designation, they will be dig designated. +If an argument is given, the designation of the selected tile is ignored, and all appropriate tiles are set to the specified designation. + +Options: + + :dig: + :channel: + :ramp: + :updown: up/down stairs + :up: up stairs + :down: down stairs + :clear: clear designation + + filltraffic ----------- Set traffic designations using flood-fill starting at the cursor. @@ -1510,6 +1529,17 @@ Confirmed working DFusion plugins: * This is currently working only on Windows. * The game will be suspended while you're using dfusion. Don't panic when it doen't respond. +misery +------ +When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default). + +Usage: + +:misery enable n: enable misery with optional magnitude n. If specified, n must be positive. +:misery n: same as "misery enable n" +:misery enable: same as "misery enable 2" +:misery disable: stop adding new negative thoughts. This will not remove existing duplicated thoughts. Equivalent to "misery 1" +:misery clear: remove fake thoughts added in this session of DF. Saving makes them permanent! Does not change factor. ======= Scripts From 09ddccea6cf539fb00098b2e6a34245c35ea815c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 29 Sep 2012 03:06:39 +0200 Subject: [PATCH 034/114] Purge html files, add docutils cmake module --- CMake/Modules/FindDocutils.cmake | 3 + CMakeLists.txt | 1 + Compile.html | 546 ------ Lua API.html | 2092 ----------------------- Readme.html | 2704 ------------------------------ 5 files changed, 4 insertions(+), 5342 deletions(-) create mode 100644 CMake/Modules/FindDocutils.cmake delete mode 100644 Compile.html delete mode 100644 Lua API.html delete mode 100644 Readme.html diff --git a/CMake/Modules/FindDocutils.cmake b/CMake/Modules/FindDocutils.cmake new file mode 100644 index 000000000..8103628df --- /dev/null +++ b/CMake/Modules/FindDocutils.cmake @@ -0,0 +1,3 @@ +FIND_PROGRAM(RST2HTML_EXECUTABLE NAMES rst2html rst2html.py) +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Docutils DEFAULT_MSG RST2HTML_EXECUTABLE) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index dfb13cd5d..9bd4ad974 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,7 @@ include_directories(depends/clsocket/src) add_subdirectory(depends) +find_package(Docutils) # build the lib itself IF(BUILD_LIBRARY) diff --git a/Compile.html b/Compile.html deleted file mode 100644 index e17e57e22..000000000 --- a/Compile.html +++ /dev/null @@ -1,546 +0,0 @@ - - - - - - -Building DFHACK - - - -
                  -

                  Building DFHACK

                  - - -
                  -

                  Linux

                  -

                  On Linux, DFHack acts as a library that shadows parts of the SDL API using LD_PRELOAD.

                  -
                  -

                  How to get the code

                  -

                  DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. -Having a 'git' package installed is the minimal requirement, but some sort of git gui or git integration for your favorite text editor/IDE will certainly help.

                  -

                  The code resides here: https://github.com/peterix/dfhack

                  -

                  If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:

                  -
                  -git clone git://github.com/peterix/dfhack.git
                  -cd dfhack
                  -git submodule init
                  -git submodule update
                  -
                  -

                  If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).

                  -
                  -
                  -

                  Dependencies

                  -

                  DFHack is meant to be installed into an existing DF folder, so get one ready.

                  -

                  For building, you need a 32-bit version of GCC. For example, to build DFHack on -a 64-bit distribution like Arch, you'll need the multilib development tools and libraries.

                  -

                  Before you can build anything, you'll also need cmake. It is advisable to also get -ccmake on distributions that split the cmake package into multiple parts.

                  -

                  For the code generation parts, you need perl and the XML::LibXML and XML::LibXSLT perl packages. -You should be able to find them in your distro repositories (on Arch linux 'perl-xml-libxml' and 'perl-xml-libxslt').

                  -
                  -
                  -

                  Build

                  -

                  Building is fairly straightforward. Enter the build folder and start the build like this:

                  -
                  -cd build
                  -cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/home/user/DF
                  -make install
                  -
                  -

                  Obviously, replace the install path with path to your DF. This will build the library -along with the normal set of plugins and install them into your DF folder.

                  -

                  Alternatively, you can use ccmake instead of cmake:

                  -
                  -cd build
                  -ccmake ..
                  -make install
                  -
                  -

                  This will show a curses-based interface that lets you set all of the -extra options.

                  -

                  You can also use a cmake-friendly IDE like KDevelop 4 or the cmake-gui -program.

                  -
                  -
                  -
                  -

                  Windows

                  -

                  On Windows, DFHack replaces the SDL library distributed with DF.

                  -
                  -

                  How to get the code

                  -

                  DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. -You will need some sort of Windows port of git, or a GUI. Some examples:

                  -
                  - -
                  -

                  The code resides here: https://github.com/peterix/dfhack

                  -

                  If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:

                  -
                  -git clone git://github.com/peterix/dfhack.git
                  -cd dfhack
                  -git submodule init
                  -git submodule update
                  -
                  -

                  The tortoisegit GUI should have the equivalent options included.

                  -

                  If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).

                  -
                  -
                  -

                  Dependencies

                  -

                  First, you need cmake. Get the win32 installer version from the official -site: http://www.cmake.org/cmake/resources/software.html

                  -

                  It has the usual installer wizard. Make sure you let it add its binary folder -to your binary search PATH so the tool can be later run from anywhere.

                  -

                  You'll need a copy of Microsoft Visual C++ 2010. The Express version is sufficient. -Grab it from Microsoft's site.

                  -

                  For the code generation parts, you'll need perl and XML::LibXML. You can install them like this:

                  -
                    -
                  • download and install strawberry perl from http://strawberryperl.com/
                  • -
                  • reboot so that the system can pick up the new binary path
                  • -
                  • open a cmd.exe window and run "cpan XML::LibXML" (obviously without the quotes). This can take a while to complete.
                  • -
                  • Same with "cpan XML::LibXSLT".
                  • -
                  -

                  If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.

                  -
                  -
                  -

                  Build

                  -

                  There are several different batch files in the build folder along with a script that's used for picking the DF path.

                  -

                  First, run set_df_path.vbs and point the dialog that pops up at your DF folder that you want to use for development. -Next, run one of the scripts with generate prefix. These create the MSVC solution file(s):

                  -
                    -
                  • all will create a solution with everything enabled (and the kitchen sink).
                  • -
                  • gui will pop up the cmake gui and let you pick and choose what to build. This is probably what you want most of the time. Set the options you are interested in, then hit configure, then generate. More options can appear after the configure step.
                  • -
                  • minimal will create a minimal solution with just the bare necessities - the main library and standard plugins.
                  • -
                  -

                  Then you can either open the solution with MSVC or use one of the msbuild scripts:

                  -
                    -
                  • Scripts with build prefix will only build.
                  • -
                  • Scripts with install prefix will build DFHack and install it to the previously selected DF path.
                  • -
                  • Scripts with package prefix will build and create a .zip package of DFHack.
                  • -
                  -

                  When you open the solution in MSVC, make sure you never use the Debug builds. Those aren't -binary-compatible with DF. If you try to use a debug build with DF, you'll only get crashes. -So pick either Release or RelWithDebInfo build and build the INSTALL target.

                  -

                  The debug scripts actually do RelWithDebInfo builds.

                  -
                  -
                  -
                  -

                  Build types

                  -

                  cmake allows you to pick a build type by changing this -variable: CMAKE_BUILD_TYPE

                  -
                  -cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
                  -
                  -

                  Without specifying a build type or 'None', cmake uses the -CMAKE_CXX_FLAGS variable for building.

                  -

                  Valid and useful build types include 'Release', 'Debug' and -'RelWithDebInfo'. 'Debug' is not available on Windows.

                  -
                  -
                  -

                  Using the library as a developer

                  -

                  Currently, the most direct way to use the library is to write a plugin that can be loaded by it. -All the plugins can be found in the 'plugins' folder. There's no in-depth documentation -on how to write one yet, but it should be easy enough to copy one and just follow the pattern.

                  -

                  Other than through plugins, it is possible to use DFHack via remote access interface, or by writing Lua scripts.

                  -

                  The most important parts of DFHack are the Core, Console, Modules and Plugins.

                  -
                    -
                  • Core acts as the centerpiece of DFHack - it acts as a filter between DF and SDL and synchronizes the various plugins with DF.
                  • -
                  • Console is a thread-safe console that can be used to invoke commands exported by Plugins.
                  • -
                  • Modules actually describe the way to access information in DF's memory. You can get them from the Core. Most modules are split into two parts: high-level and low-level. Higl-level is mostly method calls, low-level publicly visible pointers to DF's data structures.
                  • -
                  • Plugins are the tools that use all the other stuff to make things happen. A plugin can have a list of commands that it exports and an onupdate function that will be called each DF game tick.
                  • -
                  -

                  Rudimentary API documentation can be built using doxygen (see build options with ccmake or cmake-gui).

                  -

                  DFHack consists of variously licensed code, but invariably weak copyleft. -The main license is zlib/libpng, some bits are MIT licensed, and some are BSD licensed.

                  -

                  Feel free to add your own extensions and plugins. Contributing back to -the dfhack repository is welcome and the right thing to do :)

                  -
                  -

                  DF data structure definitions

                  -

                  DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.

                  -

                  Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.

                  -

                  Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.

                  -
                  -
                  -

                  Remote access interface

                  -

                  DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The dfhack-run command uses this interface to invoke ordinary console commands.

                  -

                  Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.

                  -

                  Protocol client implementations exist for Java and C#.

                  -
                  -
                  -

                  Contributing to DFHack

                  -

                  Several things should be kept in mind when contributing to DFHack.

                  -
                  -

                  Coding style

                  -

                  DFhack uses ANSI formatting and four spaces as indentation. Line -endings are UNIX. The files use UTF-8 encoding. Code not following this -won't make me happy, because I'll have to fix it. There's a good chance -I'll make you fix it ;)

                  -
                  -
                  -

                  How to get new code into DFHack

                  -

                  You can send patches or make a clone of the github repo and ask me on -the IRC channel to pull your code in. I'll review it and see if there -are any problems. I'll fix them if they are minor.

                  -

                  Fixes are higher in priority. If you want to work on something, but -don't know what, check out http://github.com/peterix/dfhack/issues -- -this is also a good place to dump new ideas and/or bugs that need -fixing.

                  -
                  -
                  -

                  Memory research

                  -

                  If you want to do memory research, you'll need some tools and some knowledge. -In general, you'll need a good memory viewer and optionally something -to look at machine code without getting crazy :)

                  -

                  Good windows tools include:

                  -
                    -
                  • Cheat Engine
                  • -
                  • IDA Pro (the free version)
                  • -
                  -

                  Good linux tools:

                  -
                    -
                  • angavrilov's df-structures gui (visit us on IRC for details).
                  • -
                  • edb (Evan's Debugger)
                  • -
                  • IDA Pro running under wine.
                  • -
                  • Some of the tools residing in the legacy dfhack branch.
                  • -
                  -

                  Using publicly known information and analyzing the game's data is preferred.

                  -
                  -
                  -
                  -
                  - - diff --git a/Lua API.html b/Lua API.html deleted file mode 100644 index 36be1ba4a..000000000 --- a/Lua API.html +++ /dev/null @@ -1,2092 +0,0 @@ - - - - - - -DFHack Lua API - - - -
                  -

                  DFHack Lua API

                  - - -

                  The current version of DFHack has extensive support for -the Lua scripting language, providing access to:

                  -
                    -
                  1. Raw data structures used by the game.
                  2. -
                  3. Many C++ functions for high-level access to these -structures, and interaction with dfhack itself.
                  4. -
                  5. Some functions exported by C++ plugins.
                  6. -
                  -

                  Lua code can be used both for writing scripts, which -are treated by DFHack command line prompt almost as -native C++ commands, and invoked by plugins written in c++.

                  -

                  This document describes native API available to Lua in detail. -It does not describe all of the utility functions -implemented by Lua files located in hack/lua/...

                  -
                  -

                  DF data structure wrapper

                  -

                  DF structures described by the xml files in library/xml are exported -to lua code as a tree of objects and functions under the df global, -which broadly maps to the df namespace in C++.

                  -

                  WARNING: The wrapper provides almost raw access to the memory -of the game, so mistakes in manipulating objects are as likely to -crash the game as equivalent plain C++ code would be. E.g. NULL -pointer access is safely detected, but dangling pointers aren't.

                  -

                  Objects managed by the wrapper can be broadly classified into the following groups:

                  -
                    -
                  1. Typed object pointers (references).

                    -

                    References represent objects in DF memory with a known type.

                    -

                    In addition to fields and methods defined by the wrapped type, -every reference has some built-in properties and methods.

                    -
                  2. -
                  3. Untyped pointers

                    -

                    Represented as lightuserdata.

                    -

                    In assignment to a pointer NULL can be represented either as -nil, or a NULL lightuserdata; reading a NULL pointer field -returns nil.

                    -
                  4. -
                  5. Named types

                    -

                    Objects in the df tree that represent identity of struct, class, -enum and bitfield types. They host nested named types, static -methods, builtin properties & methods, and, for enums and bitfields, -the bi-directional mapping between key names and values.

                    -
                  6. -
                  7. The global object

                    -

                    df.global corresponds to the df::global namespace, and -behaves as a mix between a named type and a reference, containing -both nested types and fields corresponding to global symbols.

                    -
                  8. -
                  -

                  In addition to the global object and top-level types the df -global also contains a few global builtin utility functions.

                  -
                  -

                  Typed object references

                  -

                  The underlying primitive lua object is userdata with a metatable. -Every structured field access produces a new userdata instance.

                  -

                  All typed objects have the following built-in features:

                  -
                    -
                  • ref1 == ref2, tostring(ref)

                    -

                    References implement equality by type & pointer value, and string conversion.

                    -
                  • -
                  • pairs(ref)

                    -

                    Returns an iterator for the sequence of actual C++ field names -and values. Fields are enumerated in memory order. Methods and -lua wrapper properties are not included in the iteration.

                    -

                    WARNING: a few of the data structures (like ui_look_list) -contain unions with pointers to different types with vtables. -Using pairs on such structs is an almost sure way to crash with -an access violation.

                    -
                  • -
                  • ref._kind

                    -

                    Returns one of: primitive, struct, container, -or bitfield, as appropriate for the referenced object.

                    -
                  • -
                  • ref._type

                    -

                    Returns the named type object or a string that represents -the referenced object type.

                    -
                  • -
                  • ref:sizeof()

                    -

                    Returns size, address

                    -
                  • -
                  • ref:new()

                    -

                    Allocates a new instance of the same type, and copies data -from the current object.

                    -
                  • -
                  • ref:delete()

                    -

                    Destroys the object with the C++ delete operator. -If destructor is not available, returns false.

                    -

                    WARNING: the lua reference object remains as a dangling -pointer, like a raw C++ pointer would.

                    -
                  • -
                  • ref:assign(object)

                    -

                    Assigns data from object to ref. Object must either be another -ref of a compatible type, or a lua table; in the latter case -special recursive assignment rules are applied.

                    -
                  • -
                  • ref:_displace(index[,step])

                    -

                    Returns a new reference with the pointer adjusted by index*step. -Step defaults to the natural object size.

                    -
                  • -
                  -
                  -

                  Primitive references

                  -

                  References of the _kind 'primitive' are used for objects -that don't fit any of the other reference types. Such -references can only appear as a value of a pointer field, -or as a result of calling the _field() method.

                  -

                  They behave as structs with one field value of the right type.

                  -

                  To make working with numeric buffers easier, they also allow -numeric indices. Note that other than excluding negative values -no bound checking is performed, since buffer length is not available. -Index 0 is equivalent to the value field.

                  -
                  -
                  -

                  Struct references

                  -

                  Struct references are used for class and struct objects.

                  -

                  They implement the following features:

                  -
                    -
                  • ref.field, ref.field = value

                    -

                    Valid fields of the structure may be accessed by subscript.

                    -

                    Primitive typed fields, i.e. numbers & strings, are converted -to/from matching lua values. The value of a pointer is a reference -to the target, or nil/NULL. Complex types are represented by -a reference to the field within the structure; unless recursive -lua table assignment is used, such fields can only be read.

                    -

                    NOTE: In case of inheritance, superclass fields have precedence -over the subclass, but fields shadowed in this way can still -be accessed as ref['subclasstype.field']. -This shadowing order is necessary because vtable-based classes -are automatically exposed in their exact type, and the reverse -rule would make access to superclass fields unreliable.

                    -
                  • -
                  • ref._field(field)

                    -

                    Returns a reference to a valid field. That is, unlike regular -subscript, it returns a reference to the field within the structure -even for primitive typed fields and pointers.

                    -
                  • -
                  • ref:vmethod(args...)

                    -

                    Named virtual methods are also exposed, subject to the same -shadowing rules.

                    -
                  • -
                  • pairs(ref)

                    -

                    Enumerates all real fields (but not methods) in memory -(= declaration) order.

                    -
                  • -
                  -
                  -
                  -

                  Container references

                  -

                  Containers represent vectors and arrays, possibly resizable.

                  -

                  A container field can associate an enum to the container -reference, which allows accessing elements using string keys -instead of numerical indices.

                  -

                  Implemented features:

                  -
                    -
                  • ref._enum

                    -

                    If the container has an associated enum, returns the matching -named type object.

                    -
                  • -
                  • #ref

                    -

                    Returns the length of the container.

                    -
                  • -
                  • ref[index]

                    -

                    Accesses the container element, using either a 0-based numerical -index, or, if an enum is associated, a valid enum key string.

                    -

                    Accessing an invalid index is an error, but some container types -may return a default value, or auto-resize instead for convenience. -Currently this relaxed mode is implemented by df-flagarray aka BitArray.

                    -
                  • -
                  • ref._field(index)

                    -

                    Like with structs, returns a pointer to the array element, if possible. -Flag and bit arrays cannot return such pointer, so it fails with an error.

                    -
                  • -
                  • pairs(ref), ipairs(ref)

                    -

                    If the container has no associated enum, both behave identically, -iterating over numerical indices in order. Otherwise, ipairs still -uses numbers, while pairs tries to substitute enum keys whenever -possible.

                    -
                  • -
                  • ref:resize(new_size)

                    -

                    Resizes the container if supported, or fails with an error.

                    -
                  • -
                  • ref:insert(index,item)

                    -

                    Inserts a new item at the specified index. To add at the end, -use #ref, or just '#' as index.

                    -
                  • -
                  • ref:erase(index)

                    -

                    Removes the element at the given valid index.

                    -
                  • -
                  -
                  -
                  -

                  Bitfield references

                  -

                  Bitfields behave like special fixed-size containers. -Consider them to be something in between structs and -fixed-size vectors.

                  -

                  The _enum property points to the bitfield type. -Numerical indices correspond to the shift value, -and if a subfield occupies multiple bits, the -ipairs order would have a gap.

                  -

                  Since currently there is no API to allocate a bitfield -object fully in GC-managed lua heap, consider using the -lua table assignment feature outlined below in order to -pass bitfield values to dfhack API functions that need -them, e.g. matinfo:matches{metal=true}.

                  -
                  -
                  -
                  -

                  Named types

                  -

                  Named types are exposed in the df tree with names identical -to the C++ version, except for the :: vs . difference.

                  -

                  All types and the global object have the following features:

                  -
                    -
                  • type._kind

                    -

                    Evaluates to one of struct-type, class-type, enum-type, -bitfield-type or global.

                    -
                  • -
                  • type._identity

                    -

                    Contains a lightuserdata pointing to the underlying -DFHack::type_instance object.

                    -
                  • -
                  -

                  Types excluding the global object also support:

                  -
                    -
                  • type:sizeof()

                    -

                    Returns the size of an object of the type.

                    -
                  • -
                  • type:new()

                    -

                    Creates a new instance of an object of the type.

                    -
                  • -
                  • type:is_instance(object)

                    -

                    Returns true if object is same or subclass type, or a reference -to an object of same or subclass type. It is permissible to pass -nil, NULL or non-wrapper value as object; in this case the -method returns nil.

                    -
                  • -
                  -

                  In addition to this, enum and bitfield types contain a -bi-directional mapping between key strings and values, and -also map _first_item and _last_item to the min and -max values.

                  -

                  Struct and class types with instance-vector attribute in the -xml have a type.find(key) function that wraps the find -method provided in C++.

                  -
                  -
                  -

                  Global functions

                  -

                  The df table itself contains the following functions and values:

                  -
                    -
                  • NULL, df.NULL

                    -

                    Contains the NULL lightuserdata.

                    -
                  • -
                  • df.isnull(obj)

                    -

                    Evaluates to true if obj is nil or NULL; false otherwise.

                    -
                  • -
                  • df.isvalid(obj[,allow_null])

                    -

                    For supported objects returns one of type, voidptr, ref.

                    -

                    If allow_null is true, and obj is nil or NULL, returns null.

                    -

                    Otherwise returns nil.

                    -
                  • -
                  • df.sizeof(obj)

                    -

                    For types and refs identical to obj:sizeof(). -For lightuserdata returns nil, address

                    -
                  • -
                  • df.new(obj), df.delete(obj), df.assign(obj, obj2)

                    -

                    Equivalent to using the matching methods of obj.

                    -
                  • -
                  • df._displace(obj,index[,step])

                    -

                    For refs equivalent to the method, but also works with -lightuserdata (step is mandatory then).

                    -
                  • -
                  • df.is_instance(type,obj)

                    -

                    Equivalent to the method, but also allows a reference as proxy for its type.

                    -
                  • -
                  • df.new(ptype[,count])

                    -

                    Allocate a new instance, or an array of built-in types. -The ptype argument is a string from the following list: -string, int8_t, uint8_t, int16_t, uint16_t, -int32_t, uint32_t, int64_t, uint64_t, bool, -float, double. All of these except string can be -used with the count argument to allocate an array.

                    -
                  • -
                  • df.reinterpret_cast(type,ptr)

                    -

                    Converts ptr to a ref of specified type. The type may be anything -acceptable to df.is_instance. Ptr may be nil, a ref, -a lightuserdata, or a number.

                    -

                    Returns nil if NULL, or a ref.

                    -
                  • -
                  -
                  -
                  -

                  Recursive table assignment

                  -

                  Recursive assignment is invoked when a lua table is assigned -to a C++ object or field, i.e. one of:

                  -
                    -
                  • ref:assign{...}
                  • -
                  • ref.field = {...}
                  • -
                  -

                  The general mode of operation is that all fields of the table -are assigned to the fields of the target structure, roughly -emulating the following code:

                  -
                  -function rec_assign(ref,table)
                  -    for key,value in pairs(table) do
                  -        ref[key] = value
                  -    end
                  -end
                  -
                  -

                  Since assigning a table to a field using = invokes the same -process, it is recursive.

                  -

                  There are however some variations to this process depending -on the type of the field being assigned to:

                  -
                    -
                  1. If the table contains an assign field, it is -applied first, using the ref:assign(value) method. -It is never assigned as a usual field.

                    -
                  2. -
                  3. When a table is assigned to a non-NULL pointer field -using the ref.field = {...} syntax, it is applied -to the target of the pointer instead.

                    -

                    If the pointer is NULL, the table is checked for a new field:

                    -
                      -
                    1. If it is nil or false, assignment fails with an error.
                    2. -
                    3. If it is true, the pointer is initialized with a newly -allocated object of the declared target type of the pointer.
                    4. -
                    5. Otherwise, table.new must be a named type, or an -object of a type compatible with the pointer. The pointer -is initialized with the result of calling table.new:new().
                    6. -
                    -

                    After this auto-vivification process, assignment proceeds -as if the pointer wasn't NULL.

                    -

                    Obviously, the new field inside the table is always skipped -during the actual per-field assignment processing.

                    -
                  4. -
                  5. If the target of the assignment is a container, a separate -rule set is used:

                    -
                      -
                    1. If the table contains neither assign nor resize -fields, it is interpreted as an ordinary 1-based lua -array. The container is resized to the #-size of the -table, and elements are assigned in numeric order:

                      -
                      -ref:resize(#table);
                      -for i=1,#table do ref[i-1] = table[i] end
                      -
                      -
                    2. -
                    3. Otherwise, resize must be true, false, or -an explicit number. If it is not false, the container -is resized. After that the usual struct-like 'pairs' -assignment is performed.

                      -

                      In case resize is true, the size is computed -by scanning the table for the largest numeric key.

                      -
                    4. -
                    -

                    This means that in order to reassign only one element of -a container using this system, it is necessary to use:

                    -
                    -{ resize=false, [idx]=value }
                    -
                    -
                  6. -
                  -

                  Since nil inside a table is indistinguishable from missing key, -it is necessary to use df.NULL as a null pointer value.

                  -

                  This system is intended as a way to define a nested object -tree using pure lua data structures, and then materialize it in -C++ memory in one go. Note that if pointer auto-vivification -is used, an error in the middle of the recursive walk would -not destroy any objects allocated in this way, so the user -should be prepared to catch the error and do the necessary -cleanup.

                  -
                  -
                  -
                  -

                  DFHack API

                  -

                  DFHack utility functions are placed in the dfhack global tree.

                  -
                  -

                  Native utilities

                  -
                  -

                  Input & Output

                  -
                    -
                  • dfhack.print(args...)

                    -

                    Output tab-separated args as standard lua print would do, -but without a newline.

                    -
                  • -
                  • print(args...), dfhack.println(args...)

                    -

                    A replacement of the standard library print function that -works with DFHack output infrastructure.

                    -
                  • -
                  • dfhack.printerr(args...)

                    -

                    Same as println; intended for errors. Uses red color and logs to stderr.log.

                    -
                  • -
                  • dfhack.color([color])

                    -

                    Sets the current output color. If color is nil or -1, resets to default. -Returns the previous color value.

                    -
                  • -
                  • dfhack.is_interactive()

                    -

                    Checks if the thread can access the interactive console and returns true or false.

                    -
                  • -
                  • dfhack.lineedit([prompt[,history_filename]])

                    -

                    If the thread owns the interactive console, shows a prompt -and returns the entered string. Otherwise returns nil, error.

                    -

                    Depending on the context, this function may actually yield the -running coroutine and let the C++ code release the core suspend -lock. Using an explicit dfhack.with_suspend will prevent -this, forcing the function to block on input with lock held.

                    -
                  • -
                  • dfhack.interpreter([prompt[,history_filename[,env]]])

                    -

                    Starts an interactive lua interpreter, using the specified prompt -string, global environment and command-line history file.

                    -

                    If the interactive console is not accessible, returns nil, error.

                    -
                  • -
                  -
                  -
                  -

                  Exception handling

                  -
                    -
                  • dfhack.error(msg[,level[,verbose]])

                    -

                    Throws a dfhack exception object with location and stack trace. -The verbose parameter controls whether the trace is printed by default.

                    -
                  • -
                  • qerror(msg[,level])

                    -

                    Calls dfhack.error() with verbose being false. Intended to -be used for user-caused errors in scripts, where stack traces are not -desirable.

                    -
                  • -
                  • dfhack.pcall(f[,args...])

                    -

                    Invokes f via xpcall, using an error function that attaches -a stack trace to the error. The same function is used by SafeCall -in C++, and dfhack.safecall.

                    -
                  • -
                  • safecall(f[,args...]), dfhack.safecall(f[,args...])

                    -

                    Just like pcall, but also prints the error using printerr before -returning. Intended as a convenience function.

                    -
                  • -
                  • dfhack.saferesume(coroutine[,args...])

                    -

                    Compares to coroutine.resume like dfhack.safecall vs pcall.

                    -
                  • -
                  • dfhack.exception

                    -

                    Metatable of error objects used by dfhack. The objects have the -following properties:

                    -
                    -
                    err.where
                    -

                    The location prefix string, or nil.

                    -
                    -
                    err.message
                    -

                    The base message string.

                    -
                    -
                    err.stacktrace
                    -

                    The stack trace string, or nil.

                    -
                    -
                    err.cause
                    -

                    A different exception object, or nil.

                    -
                    -
                    err.thread
                    -

                    The coroutine that has thrown the exception.

                    -
                    -
                    err.verbose
                    -

                    Boolean, or nil; specifies if where and stacktrace should be printed.

                    -
                    -
                    tostring(err), or err:tostring([verbose])
                    -

                    Converts the exception to string.

                    -
                    -
                    -
                  • -
                  • dfhack.exception.verbose

                    -

                    The default value of the verbose argument of err:tostring().

                    -
                  • -
                  -
                  -
                  -

                  Miscellaneous

                  -
                    -
                  • dfhack.VERSION

                    -

                    DFHack version string constant.

                    -
                  • -
                  • dfhack.curry(func,args...), or curry(func,args...)

                    -

                    Returns a closure that invokes the function with args combined -both from the curry call and the closure call itself. I.e. -curry(func,a,b)(c,d) equals func(a,b,c,d).

                    -
                  • -
                  -
                  -
                  -

                  Locking and finalization

                  -
                    -
                  • dfhack.with_suspend(f[,args...])

                    -

                    Calls f with arguments after grabbing the DF core suspend lock. -Suspending is necessary for accessing a consistent state of DF memory.

                    -

                    Returned values and errors are propagated through after releasing -the lock. It is safe to nest suspends.

                    -

                    Every thread is allowed only one suspend per DF frame, so it is best -to group operations together in one big critical section. A plugin -can choose to run all lua code inside a C++-side suspend lock.

                    -
                  • -
                  • dfhack.call_with_finalizer(num_cleanup_args,always,cleanup_fn[,cleanup_args...],fn[,args...])

                    -

                    Invokes fn with args, and after it returns or throws an -error calls cleanup_fn with cleanup_args. Any return values from -fn are propagated, and errors are re-thrown.

                    -

                    The num_cleanup_args integer specifies the number of cleanup_args, -and the always boolean specifies if cleanup should be called in any case, -or only in case of an error.

                    -
                  • -
                  • dfhack.with_finalize(cleanup_fn,fn[,args...])

                    -

                    Calls fn with arguments, then finalizes with cleanup_fn. -Implemented using call_with_finalizer(0,true,...).

                    -
                  • -
                  • dfhack.with_onerror(cleanup_fn,fn[,args...])

                    -

                    Calls fn with arguments, then finalizes with cleanup_fn on any thrown error. -Implemented using call_with_finalizer(0,false,...).

                    -
                  • -
                  • dfhack.with_temp_object(obj,fn[,args...])

                    -

                    Calls fn(obj,args...), then finalizes with obj:delete().

                    -
                  • -
                  -
                  -
                  -

                  Persistent configuration storage

                  -

                  This api is intended for storing configuration options in the world itself. -It probably should be restricted to data that is world-dependent.

                  -

                  Entries are identified by a string key, but it is also possible to manage -multiple entries with the same key; their identity is determined by entry_id. -Every entry has a mutable string value, and an array of 7 mutable ints.

                  -
                    -
                  • dfhack.persistent.get(key), entry:get()

                    -

                    Retrieves a persistent config record with the given string key, -or refreshes an already retrieved entry. If there are multiple -entries with the same key, it is undefined which one is retrieved -by the first version of the call.

                    -

                    Returns entry, or nil if not found.

                    -
                  • -
                  • dfhack.persistent.delete(key), entry:delete()

                    -

                    Removes an existing entry. Returns true if succeeded.

                    -
                  • -
                  • dfhack.persistent.get_all(key[,match_prefix])

                    -

                    Retrieves all entries with the same key, or starting with key..'/'. -Calling get_all('',true) will match all entries.

                    -

                    If none found, returns nil; otherwise returns an array of entries.

                    -
                  • -
                  • dfhack.persistent.save({key=str1, ...}[,new]), entry:save([new])

                    -

                    Saves changes in an entry, or creates a new one. Passing true as -new forces creation of a new entry even if one already exists; -otherwise the existing one is simply updated. -Returns entry, did_create_new

                    -
                  • -
                  -

                  Since the data is hidden in data structures owned by the DF world, -and automatically stored in the save game, these save and retrieval -functions can just copy values in memory without doing any actual I/O. -However, currently every entry has a 180+-byte dead-weight overhead.

                  -
                  -
                  -

                  Material info lookup

                  -

                  A material info record has fields:

                  -
                    -
                  • type, index, material

                    -

                    DF material code pair, and a reference to the material object.

                    -
                  • -
                  • mode

                    -

                    One of 'builtin', 'inorganic', 'plant', 'creature'.

                    -
                  • -
                  • inorganic, plant, creature

                    -

                    If the material is of the matching type, contains a reference to the raw object.

                    -
                  • -
                  • figure

                    -

                    For a specific creature material contains a ref to the historical figure.

                    -
                  • -
                  -

                  Functions:

                  -
                    -
                  • dfhack.matinfo.decode(type,index)

                    -

                    Looks up material info for the given number pair; if not found, returs nil.

                    -
                  • -
                  • ....decode(matinfo), ....decode(item), ....decode(obj)

                    -

                    Uses matinfo.type/matinfo.index, item getter vmethods, -or obj.mat_type/obj.mat_index to get the code pair.

                    -
                  • -
                  • dfhack.matinfo.find(token[,token...])

                    -

                    Looks up material by a token string, or a pre-split string token sequence.

                    -
                  • -
                  • dfhack.matinfo.getToken(...), info:getToken()

                    -

                    Applies decode and constructs a string token.

                    -
                  • -
                  • info:toString([temperature[,named]])

                    -

                    Returns the human-readable name at the given temperature.

                    -
                  • -
                  • info:getCraftClass()

                    -

                    Returns the classification used for craft skills.

                    -
                  • -
                  • info:matches(obj)

                    -

                    Checks if the material matches job_material_category or job_item. -Accept dfhack_material_category auto-assign table.

                    -
                  • -
                  -
                  -
                  -
                  -

                  C++ function wrappers

                  -

                  Thin wrappers around C++ functions, similar to the ones for virtual methods. -One notable difference is that these explicit wrappers allow argument count -adjustment according to the usual lua rules, so trailing false/nil arguments -can be omitted.

                  -
                    -
                  • dfhack.getOSType()

                    -

                    Returns the OS type string from symbols.xml.

                    -
                  • -
                  • dfhack.getDFVersion()

                    -

                    Returns the DF version string from symbols.xml.

                    -
                  • -
                  • dfhack.getDFPath()

                    -

                    Returns the DF directory path.

                    -
                  • -
                  • dfhack.getHackPath()

                    -

                    Returns the dfhack directory path, i.e. ".../df/hack/".

                    -
                  • -
                  • dfhack.getTickCount()

                    -

                    Returns the tick count in ms, exactly as DF ui uses.

                    -
                  • -
                  • dfhack.isWorldLoaded()

                    -

                    Checks if the world is loaded.

                    -
                  • -
                  • dfhack.isMapLoaded()

                    -

                    Checks if the world and map are loaded.

                    -
                  • -
                  • dfhack.TranslateName(name[,in_english,only_last_name])

                    -

                    Convert a language_name or only the last name part to string.

                    -
                  • -
                  -
                  -

                  Gui module

                  -
                    -
                  • dfhack.gui.getCurViewscreen([skip_dismissed])

                    -

                    Returns the topmost viewscreen. If skip_dismissed is true, -ignores screens already marked to be removed.

                    -
                  • -
                  • dfhack.gui.getFocusString(viewscreen)

                    -

                    Returns a string representation of the current focus position -in the ui. The string has a "screen/foo/bar/baz..." format.

                    -
                  • -
                  • dfhack.gui.getCurFocus([skip_dismissed])

                    -

                    Returns the focus string of the current viewscreen.

                    -
                  • -
                  • dfhack.gui.getSelectedWorkshopJob([silent])

                    -

                    When a job is selected in 'q' mode, returns the job, else -prints error unless silent and returns nil.

                    -
                  • -
                  • dfhack.gui.getSelectedJob([silent])

                    -

                    Returns the job selected in a workshop or unit/jobs screen.

                    -
                  • -
                  • dfhack.gui.getSelectedUnit([silent])

                    -

                    Returns the unit selected via 'v', 'k', unit/jobs, or -a full-screen item view of a cage or suchlike.

                    -
                  • -
                  • dfhack.gui.getSelectedItem([silent])

                    -

                    Returns the item selected via 'v' ->inventory, 'k', 't', or -a full-screen item view of a container. Note that in the -last case, the highlighted contained item is returned, not -the container itself.

                    -
                  • -
                  • dfhack.gui.getSelectedBuilding([silent])

                    -

                    Returns the building selected via 'q', 't', 'k' or 'i'.

                    -
                  • -
                  • dfhack.gui.showAnnouncement(text,color[,is_bright])

                    -

                    Adds a regular announcement with given text, color, and brightness. -The is_bright boolean actually seems to invert the brightness.

                    -
                  • -
                  • dfhack.gui.showZoomAnnouncement(type,pos,text,color[,is_bright])

                    -

                    Like above, but also specifies a position you can zoom to from the announcement menu.

                    -
                  • -
                  • dfhack.gui.showPopupAnnouncement(text,color[,is_bright])

                    -

                    Pops up a titan-style modal announcement window.

                    -
                  • -
                  • dfhack.gui.showAutoAnnouncement(type,pos,text,color[,is_bright])

                    -

                    Uses the type to look up options from announcements.txt, and calls the -above operations accordingly. If enabled, pauses and zooms to position.

                    -
                  • -
                  -
                  -
                  -

                  Job module

                  -
                    -
                  • dfhack.job.cloneJobStruct(job)

                    -

                    Creates a deep copy of the given job.

                    -
                  • -
                  • dfhack.job.printJobDetails(job)

                    -

                    Prints info about the job.

                    -
                  • -
                  • dfhack.job.printItemDetails(jobitem,idx)

                    -

                    Prints info about the job item.

                    -
                  • -
                  • dfhack.job.getHolder(job)

                    -

                    Returns the building holding the job.

                    -
                  • -
                  • dfhack.job.getWorker(job)

                    -

                    Returns the unit performing the job.

                    -
                  • -
                  • dfhack.job.checkBuildingsNow()

                    -

                    Instructs the game to check buildings for jobs next frame and assign workers.

                    -
                  • -
                  • dfhack.job.checkDesignationsNow()

                    -

                    Instructs the game to check designations for jobs next frame and assign workers.

                    -
                  • -
                  • dfhack.job.is_equal(job1,job2)

                    -

                    Compares important fields in the job and nested item structures.

                    -
                  • -
                  • dfhack.job.is_item_equal(job_item1,job_item2)

                    -

                    Compares important fields in the job item structures.

                    -
                  • -
                  • dfhack.job.listNewlyCreated(first_id)

                    -

                    Returns the current value of df.global.job_next_id, and -if there are any jobs with first_id <= id < job_next_id, -a lua list containing them.

                    -
                  • -
                  -
                  -
                  -

                  Units module

                  -
                    -
                  • dfhack.units.getPosition(unit)

                    -

                    Returns true x,y,z of the unit, or nil if invalid; may be not equal to unit.pos if caged.

                    -
                  • -
                  • dfhack.units.getContainer(unit)

                    -

                    Returns the container (cage) item or nil.

                    -
                  • -
                  • dfhack.units.setNickname(unit,nick)

                    -

                    Sets the unit's nickname properly.

                    -
                  • -
                  • dfhack.units.getVisibleName(unit)

                    -

                    Returns the language_name object visible in game, accounting for false identities.

                    -
                  • -
                  • dfhack.units.getIdentity(unit)

                    -

                    Returns the false identity of the unit if it has one, or nil.

                    -
                  • -
                  • dfhack.units.getNemesis(unit)

                    -

                    Returns the nemesis record of the unit if it has one, or nil.

                    -
                  • -
                  • dfhack.units.isHidingCurse(unit)

                    -

                    Checks if the unit hides improved attributes from its curse.

                    -
                  • -
                  • dfhack.units.getPhysicalAttrValue(unit, attr_type)

                    -
                  • -
                  • dfhack.units.getMentalAttrValue(unit, attr_type)

                    -

                    Computes the effective attribute value, including curse effect.

                    -
                  • -
                  • dfhack.units.isCrazed(unit)

                    -
                  • -
                  • dfhack.units.isOpposedToLife(unit)

                    -
                  • -
                  • dfhack.units.hasExtravision(unit)

                    -
                  • -
                  • dfhack.units.isBloodsucker(unit)

                    -

                    Simple checks of caste attributes that can be modified by curses.

                    -
                  • -
                  • dfhack.units.getMiscTrait(unit, type[, create])

                    -

                    Finds (or creates if requested) a misc trait object with the given id.

                    -
                  • -
                  • dfhack.units.isDead(unit)

                    -

                    The unit is completely dead and passive, or a ghost.

                    -
                  • -
                  • dfhack.units.isAlive(unit)

                    -

                    The unit isn't dead or undead.

                    -
                  • -
                  • dfhack.units.isSane(unit)

                    -

                    The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf.

                    -
                  • -
                  • dfhack.units.isDwarf(unit)

                    -

                    The unit is of the correct race of the fortress.

                    -
                  • -
                  • dfhack.units.isCitizen(unit)

                    -

                    The unit is an alive sane citizen of the fortress; wraps the -same checks the game uses to decide game-over by extinction.

                    -
                  • -
                  • dfhack.units.getAge(unit[,true_age])

                    -

                    Returns the age of the unit in years as a floating-point value. -If true_age is true, ignores false identities.

                    -
                  • -
                  • dfhack.units.getEffectiveSkill(unit, skill)

                    -

                    Computes the effective rating for the given skill, taking into account exhaustion, pain etc.

                    -
                  • -
                  • dfhack.units.computeMovementSpeed(unit)

                    -

                    Computes number of frames * 100 it takes the unit to move in its current state of mind and body.

                    -
                  • -
                  • dfhack.units.getNoblePositions(unit)

                    -

                    Returns a list of tables describing noble position assignments, or nil. -Every table has fields entity, assignment and position.

                    -
                  • -
                  • dfhack.units.getProfessionName(unit[,ignore_noble,plural])

                    -

                    Retrieves the profession name using custom profession, noble assignments -or raws. The ignore_noble boolean disables the use of noble positions.

                    -
                  • -
                  • dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])

                    -

                    Retrieves the profession name for the given race/caste using raws.

                    -
                  • -
                  • dfhack.units.getProfessionColor(unit[,ignore_noble])

                    -

                    Retrieves the color associated with the profession, using noble assignments -or raws. The ignore_noble boolean disables the use of noble positions.

                    -
                  • -
                  • dfhack.units.getCasteProfessionColor(race,caste,prof_id)

                    -

                    Retrieves the profession color for the given race/caste using raws.

                    -
                  • -
                  -
                  -
                  -

                  Items module

                  -
                    -
                  • dfhack.items.getPosition(item)

                    -

                    Returns true x,y,z of the item, or nil if invalid; may be not equal to item.pos if in inventory.

                    -
                  • -
                  • dfhack.items.getDescription(item, type[, decorate])

                    -

                    Returns the string description of the item, as produced by the getItemDescription -method. If decorate is true, also adds markings for quality and improvements.

                    -
                  • -
                  • dfhack.items.getGeneralRef(item, type)

                    -

                    Searches for a general_ref with the given type.

                    -
                  • -
                  • dfhack.items.getSpecificRef(item, type)

                    -

                    Searches for a specific_ref with the given type.

                    -
                  • -
                  • dfhack.items.getOwner(item)

                    -

                    Returns the owner unit or nil.

                    -
                  • -
                  • dfhack.items.setOwner(item,unit)

                    -

                    Replaces the owner of the item. If unit is nil, removes ownership. -Returns false in case of error.

                    -
                  • -
                  • dfhack.items.getContainer(item)

                    -

                    Returns the container item or nil.

                    -
                  • -
                  • dfhack.items.getContainedItems(item)

                    -

                    Returns a list of items contained in this one.

                    -
                  • -
                  • dfhack.items.moveToGround(item,pos)

                    -

                    Move the item to the ground at position. Returns false if impossible.

                    -
                  • -
                  • dfhack.items.moveToContainer(item,container)

                    -

                    Move the item to the container. Returns false if impossible.

                    -
                  • -
                  • dfhack.items.moveToBuilding(item,building,use_mode)

                    -

                    Move the item to the building. Returns false if impossible.

                    -
                  • -
                  • dfhack.items.moveToInventory(item,unit,use_mode,body_part)

                    -

                    Move the item to the unit inventory. Returns false if impossible.

                    -
                  • -
                  • dfhack.items.remove(item[, no_uncat])

                    -

                    Removes the item, and marks it for garbage collection unless no_uncat is true.

                    -
                  • -
                  • dfhack.items.makeProjectile(item)

                    -

                    Turns the item into a projectile, and returns the new object, or nil if impossible.

                    -
                  • -
                  -
                  -
                  -

                  Maps module

                  -
                    -
                  • dfhack.maps.getSize()

                    -

                    Returns map size in blocks: x, y, z

                    -
                  • -
                  • dfhack.maps.getTileSize()

                    -

                    Returns map size in tiles: x, y, z

                    -
                  • -
                  • dfhack.maps.getBlock(x,y,z)

                    -

                    Returns a map block object for given x,y,z in local block coordinates.

                    -
                  • -
                  • dfhack.maps.isValidTilePos(coords), or isValidTilePos(x,y,z)``

                    -

                    Checks if the given df::coord or x,y,z in local tile coordinates are valid.

                    -
                  • -
                  • dfhack.maps.getTileBlock(coords), or getTileBlock(x,y,z)

                    -

                    Returns a map block object for given df::coord or x,y,z in local tile coordinates.

                    -
                  • -
                  • dfhack.maps.ensureTileBlock(coords), or ensureTileBlock(x,y,z)

                    -

                    Like getTileBlock, but if the block is not allocated, try creating it.

                    -
                  • -
                  • dfhack.maps.getRegionBiome(region_coord2d), or getRegionBiome(x,y)

                    -

                    Returns the biome info struct for the given global map region.

                    -
                  • -
                  • dfhack.maps.enableBlockUpdates(block[,flow,temperature])

                    -

                    Enables updates for liquid flow or temperature, unless already active.

                    -
                  • -
                  • dfhack.maps.spawnFlow(pos,type,mat_type,mat_index,dimension)

                    -

                    Spawns a new flow (i.e. steam/mist/dust/etc) at the given pos, and with -the given parameters. Returns it, or nil if unsuccessful.

                    -
                  • -
                  • dfhack.maps.getGlobalInitFeature(index)

                    -

                    Returns the global feature object with the given index.

                    -
                  • -
                  • dfhack.maps.getLocalInitFeature(region_coord2d,index)

                    -

                    Returns the local feature object with the given region coords and index.

                    -
                  • -
                  • dfhack.maps.getTileBiomeRgn(coords), or getTileBiomeRgn(x,y,z)

                    -

                    Returns x, y for use with getRegionBiome.

                    -
                  • -
                  • dfhack.maps.canWalkBetween(pos1, pos2)

                    -

                    Checks if a dwarf may be able to walk between the two tiles, -using a pathfinding cache maintained by the game. Note that -this cache is only updated when the game is unpaused, and thus -can get out of date if doors are forbidden or unforbidden, or -tools like liquids or tiletypes are used. It also cannot possibly -take into account anything that depends on the actual units, like -burrows, or the presence of invaders.

                    -
                  • -
                  -
                  -
                  -

                  Burrows module

                  -
                    -
                  • dfhack.burrows.findByName(name)

                    -

                    Returns the burrow pointer or nil.

                    -
                  • -
                  • dfhack.burrows.clearUnits(burrow)

                    -

                    Removes all units from the burrow.

                    -
                  • -
                  • dfhack.burrows.isAssignedUnit(burrow,unit)

                    -

                    Checks if the unit is in the burrow.

                    -
                  • -
                  • dfhack.burrows.setAssignedUnit(burrow,unit,enable)

                    -

                    Adds or removes the unit from the burrow.

                    -
                  • -
                  • dfhack.burrows.clearTiles(burrow)

                    -

                    Removes all tiles from the burrow.

                    -
                  • -
                  • dfhack.burrows.listBlocks(burrow)

                    -

                    Returns a table of map block pointers.

                    -
                  • -
                  • dfhack.burrows.isAssignedTile(burrow,tile_coord)

                    -

                    Checks if the tile is in burrow.

                    -
                  • -
                  • dfhack.burrows.setAssignedTile(burrow,tile_coord,enable)

                    -

                    Adds or removes the tile from the burrow. Returns false if invalid coords.

                    -
                  • -
                  • dfhack.burrows.isAssignedBlockTile(burrow,block,x,y)

                    -

                    Checks if the tile within the block is in burrow.

                    -
                  • -
                  • dfhack.burrows.setAssignedBlockTile(burrow,block,x,y,enable)

                    -

                    Adds or removes the tile from the burrow. Returns false if invalid coords.

                    -
                  • -
                  -
                  -
                  -

                  Buildings module

                  -
                    -
                  • dfhack.buildings.setOwner(item,unit)

                    -

                    Replaces the owner of the building. If unit is nil, removes ownership. -Returns false in case of error.

                    -
                  • -
                  • dfhack.buildings.getSize(building)

                    -

                    Returns width, height, centerx, centery.

                    -
                  • -
                  • dfhack.buildings.findAtTile(pos), or findAtTile(x,y,z)

                    -

                    Scans the buildings for the one located at the given tile. -Does not work on civzones. Warning: linear scan if the map -tile indicates there are buildings at it.

                    -
                  • -
                  • dfhack.buildings.findCivzonesAt(pos), or findCivzonesAt(x,y,z)

                    -

                    Scans civzones, and returns a lua sequence of those that touch -the given tile, or nil if none.

                    -
                  • -
                  • dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)

                    -

                    Computes correct dimensions for the specified building type and orientation, -using width and height for flexible dimensions. -Returns is_flexible, width, height, center_x, center_y.

                    -
                  • -
                  • dfhack.buildings.checkFreeTiles(pos,size[,extents,change_extents,allow_occupied])

                    -

                    Checks if the rectangle defined by pos and size, and possibly extents, -can be used for placing a building. If change_extents is true, bad tiles -are removed from extents. If allow_occupied, the occupancy test is skipped.

                    -
                  • -
                  • dfhack.buildings.countExtentTiles(extents,defval)

                    -

                    Returns the number of tiles included by extents, or defval.

                    -
                  • -
                  • dfhack.buildings.containsTile(building, x, y[, room])

                    -

                    Checks if the building contains the specified tile, either directly, or as room.

                    -
                  • -
                  • dfhack.buildings.hasSupport(pos,size)

                    -

                    Checks if a bridge constructed at specified position would have -support from terrain, and thus won't collapse if retracted.

                    -
                  • -
                  -

                  Low-level building creation functions;

                  -
                    -
                  • dfhack.buildings.allocInstance(pos, type, subtype, custom)

                    -

                    Creates a new building instance of given type, subtype and custom type, -at specified position. Returns the object, or nil in case of an error.

                    -
                  • -
                  • dfhack.buildings.setSize(building, width, height, direction)

                    -

                    Configures an object returned by allocInstance, using specified -parameters wherever appropriate. If the building has fixed size along -any dimension, the corresponding input parameter will be ignored. -Returns false if the building cannot be placed, or true, width, -height, rect_area, true_area. Returned width and height are the -final values used by the building; true_area is less than rect_area -if any tiles were removed from designation.

                    -
                  • -
                  • dfhack.buildings.constructAbstract(building)

                    -

                    Links a fully configured object created by allocInstance into the -world. The object must be an abstract building, i.e. a stockpile or civzone. -Returns true, or false if impossible.

                    -
                  • -
                  • dfhack.buildings.constructWithItems(building, items)

                    -

                    Links a fully configured object created by allocInstance into the -world for construction, using a list of specific items as material. -Returns true, or false if impossible.

                    -
                  • -
                  • dfhack.buildings.constructWithFilters(building, job_items)

                    -

                    Links a fully configured object created by allocInstance into the -world for construction, using a list of job_item filters as inputs. -Returns true, or false if impossible. Filter objects are claimed -and possibly destroyed in any case. -Use a negative quantity field value to auto-compute the amount -from the size of the building.

                    -
                  • -
                  • dfhack.buildings.deconstruct(building)

                    -

                    Destroys the building, or queues a deconstruction job. -Returns true if the building was destroyed and deallocated immediately.

                    -
                  • -
                  -

                  More high-level functions are implemented in lua and can be loaded by -require('dfhack.buildings'). See hack/lua/dfhack/buildings.lua.

                  -

                  Among them are:

                  -
                    -
                  • dfhack.buildings.getFiltersByType(argtable,type,subtype,custom)

                    -

                    Returns a sequence of lua structures, describing input item filters -suitable for the specified building type, or nil if unknown or invalid. -The returned sequence is suitable for use as the job_items argument -of constructWithFilters. -Uses tables defined in buildings.lua.

                    -

                    Argtable members material (the default name), bucket, barrel, -chain, mechanism, screw, pipe, anvil, weapon are used to -augment the basic attributes with more detailed information if the -building has input items with the matching name (see the tables for naming details). -Note that it is impossible to override any properties this way, only supply those that -are not mentioned otherwise; one exception is that flags2.non_economic -is automatically cleared if an explicit material is specified.

                    -
                  • -
                  • dfhack.buildings.constructBuilding{...}

                    -

                    Creates a building in one call, using options contained -in the argument table. Returns the building, or nil, error.

                    -

                    NOTE: Despite the name, unless the building is abstract, -the function creates it in an 'unconstructed' stage, with -a queued in-game job that will actually construct it. I.e. -the function replicates programmatically what can be done -through the construct building menu in the game ui, except -that it does less environment constraint checking.

                    -

                    The following options can be used:

                    -
                      -
                    • pos = coordinates, or x = ..., y = ..., z = ...

                      -

                      Mandatory. Specifies the left upper corner of the building.

                      -
                    • -
                    • type = df.building_type.FOO, subtype = ..., custom = ...

                      -

                      Mandatory. Specifies the type of the building. Obviously, subtype -and custom are only expected if the type requires them.

                      -
                    • -
                    • fields = { ... }

                      -

                      Initializes fields of the building object after creation with df.assign.

                      -
                    • -
                    • width = ..., height = ..., direction = ...

                      -

                      Sets size and orientation of the building. If it is -fixed-size, specified dimensions are ignored.

                      -
                    • -
                    • full_rectangle = true

                      -

                      For buildings like stockpiles or farm plots that can normally -accomodate individual tile exclusion, forces an error if any -tiles within the specified width*height are obstructed.

                      -
                    • -
                    • items = { item, item ... }, or filters = { {...}, {...}... }

                      -

                      Specifies explicit items or item filters to use in construction. -It is the job of the user to ensure they are correct for the building type.

                      -
                    • -
                    • abstract = true

                      -

                      Specifies that the building is abstract and does not require construction. -Required for stockpiles and civzones; an error otherwise.

                      -
                    • -
                    • material = {...}, mechanism = {...}, ...

                      -

                      If none of items, filter, or abstract is used, -the function uses getFiltersByType to compute the input -item filters, and passes the argument table through. If no filters -can be determined this way, constructBuilding throws an error.

                      -
                    • -
                    -
                  • -
                  -
                  -
                  -

                  Constructions module

                  -
                    -
                  • dfhack.constructions.designateNew(pos,type,item_type,mat_index)

                    -

                    Designates a new construction at given position. If there already is -a planned but not completed construction there, changes its type. -Returns true, or false if obstructed. -Note that designated constructions are technically buildings.

                    -
                  • -
                  • dfhack.constructions.designateRemove(pos), or designateRemove(x,y,z)

                    -

                    If there is a construction or a planned construction at the specified -coordinates, designates it for removal, or instantly cancels the planned one. -Returns true, was_only_planned if removed; or false if none found.

                    -
                  • -
                  -
                  -
                  -

                  Screen API

                  -

                  The screen module implements support for drawing to the tiled screen of the game. -Note that drawing only has any effect when done from callbacks, so it can only -be feasibly used in the core context.

                  -

                  Basic painting functions:

                  -
                    -
                  • dfhack.screen.getWindowSize()

                    -

                    Returns width, height of the screen.

                    -
                  • -
                  • dfhack.screen.getMousePos()

                    -

                    Returns x,y of the tile the mouse is over.

                    -
                  • -
                  • dfhack.screen.inGraphicsMode()

                    -

                    Checks if [GRAPHICS:YES] was specified in init.

                    -
                  • -
                  • dfhack.screen.paintTile(pen,x,y[,char,tile])

                    -

                    Paints a tile using given parameters. Pen is a table with following possible fields:

                    -
                    -
                    ch
                    -

                    Provides the ordinary tile character, as either a 1-character string or a number. -Can be overridden with the char function parameter.

                    -
                    -
                    fg
                    -

                    Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).

                    -
                    -
                    bg
                    -

                    Background color for the ordinary tile. Defaults to COLOR_BLACK (0).

                    -
                    -
                    bold
                    -

                    Bright/bold text flag. If nil, computed based on (fg & 8); fg is masked to 3 bits. -Otherwise should be true/false.

                    -
                    -
                    tile
                    -

                    Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.

                    -
                    -
                    tile_color = true
                    -

                    Specifies that the tile should be shaded with fg/bg.

                    -
                    -
                    tile_fg, tile_bg
                    -

                    If specified, overrides tile_color and supplies shading colors directly.

                    -
                    -
                    -

                    Returns false if coordinates out of bounds, or other error.

                    -
                  • -
                  • dfhack.screen.readTile(x,y)

                    -

                    Retrieves the contents of the specified tile from the screen buffers. -Returns a pen, or nil if invalid or TrueType.

                    -
                  • -
                  • dfhack.screen.paintString(pen,x,y,text)

                    -

                    Paints the string starting at x,y. Uses the string characters -in sequence to override the ch field of pen.

                    -

                    Returns true if painting at least one character succeeded.

                    -
                  • -
                  • dfhack.screen.fillRect(pen,x1,y1,x2,y2)

                    -

                    Fills the rectangle specified by the coordinates with the given pen. -Returns true if painting at least one character succeeded.

                    -
                  • -
                  • dfhack.screen.findGraphicsTile(pagename,x,y)

                    -

                    Finds a tile from a graphics set (i.e. the raws used for creatures), -if in graphics mode and loaded.

                    -

                    Returns: tile, tile_grayscale, or nil if not found. -The values can then be used for the tile field of pen structures.

                    -
                  • -
                  • dfhack.screen.clear()

                    -

                    Fills the screen with blank background.

                    -
                  • -
                  • dfhack.screen.invalidate()

                    -

                    Requests repaint of the screen by setting a flag. Unlike other -functions in this section, this may be used at any time.

                    -
                  • -
                  -

                  In order to actually be able to paint to the screen, it is necessary -to create and register a viewscreen (basically a modal dialog) with -the game.

                  -

                  NOTE: As a matter of policy, in order to avoid user confusion, all -interface screens added by dfhack should bear the "DFHack" signature.

                  -

                  Screens are managed with the following functions:

                  -
                    -
                  • dfhack.screen.show(screen[,below])

                    -

                    Displays the given screen, possibly placing it below a different one. -The screen must not be already shown. Returns true if success.

                    -
                  • -
                  • dfhack.screen.dismiss(screen[,to_first])

                    -

                    Marks the screen to be removed when the game enters its event loop. -If to_first is true, all screens up to the first one will be deleted.

                    -
                  • -
                  • dfhack.screen.isDismissed(screen)

                    -

                    Checks if the screen is already marked for removal.

                    -
                  • -
                  -

                  Apart from a native viewscreen object, these functions accept a table -as a screen. In this case, show creates a new native viewscreen -that delegates all processing to methods stored in that table.

                  -

                  NOTE: Lua-implemented screens are only supported in the core context.

                  -

                  Supported callbacks and fields are:

                  -
                    -
                  • screen._native

                    -

                    Initialized by show with a reference to the backing viewscreen -object, and removed again when the object is deleted.

                    -
                  • -
                  • function screen:onShow()

                    -

                    Called by dfhack.screen.show if successful.

                    -
                  • -
                  • function screen:onDismiss()

                    -

                    Called by dfhack.screen.dismiss if successful.

                    -
                  • -
                  • function screen:onDestroy()

                    -

                    Called from the destructor when the viewscreen is deleted.

                    -
                  • -
                  • function screen:onResize(w, h)

                    -

                    Called before onRender or onIdle when the window size has changed.

                    -
                  • -
                  • function screen:onRender()

                    -

                    Called when the viewscreen should paint itself. This is the only context -where the above painting functions work correctly.

                    -

                    If omitted, the screen is cleared; otherwise it should do that itself. -In order to make a see-through dialog, call self._native.parent:render().

                    -
                  • -
                  • function screen:onIdle()

                    -

                    Called every frame when the screen is on top of the stack.

                    -
                  • -
                  • function screen:onHelp()

                    -

                    Called when the help keybinding is activated (usually '?').

                    -
                  • -
                  • function screen:onInput(keys)

                    -

                    Called when keyboard or mouse events are available. -If any keys are pressed, the keys argument is a table mapping them to true. -Note that this refers to logical keybingings computed from real keys via -options; if multiple interpretations exist, the table will contain multiple keys.

                    -

                    The table also may contain special keys:

                    -
                    -
                    _STRING
                    -

                    Maps to an integer in range 0-255. Duplicates a separate "STRING_A???" code for convenience.

                    -
                    -
                    _MOUSE_L, _MOUSE_R
                    -

                    If the left or right mouse button is pressed.

                    -
                    -
                    -

                    If this method is omitted, the screen is dismissed on receival of the LEAVESCREEN key.

                    -
                  • -
                  • function screen:onGetSelectedUnit()

                    -
                  • -
                  • function screen:onGetSelectedItem()

                    -
                  • -
                  • function screen:onGetSelectedJob()

                    -
                  • -
                  • function screen:onGetSelectedBuilding()

                    -

                    Implement these to provide a return value for the matching -dfhack.gui.getSelected... function.

                    -
                  • -
                  -
                  -
                  -

                  Internal API

                  -

                  These functions are intended for the use by dfhack developers, -and are only documented here for completeness:

                  -
                    -
                  • dfhack.internal.scripts

                    -

                    The table used by dfhack.run_script() to give every script its own -global environment, persistent between calls to the script.

                    -
                  • -
                  • dfhack.internal.getAddress(name)

                    -

                    Returns the global address name, or nil.

                    -
                  • -
                  • dfhack.internal.setAddress(name, value)

                    -

                    Sets the global address name. Returns the value of getAddress before the change.

                    -
                  • -
                  • dfhack.internal.getVTable(name)

                    -

                    Returns the pre-extracted vtable address name, or nil.

                    -
                  • -
                  • dfhack.internal.getRebaseDelta()

                    -

                    Returns the ASLR rebase offset of the DF executable.

                    -
                  • -
                  • dfhack.internal.getMemRanges()

                    -

                    Returns a sequence of tables describing virtual memory ranges of the process.

                    -
                  • -
                  • dfhack.internal.patchMemory(dest,src,count)

                    -

                    Like memmove below, but works even if dest is read-only memory, e.g. code. -If destination overlaps a completely invalid memory region, or another error -occurs, returns false.

                    -
                  • -
                  • dfhack.internal.memmove(dest,src,count)

                    -

                    Wraps the standard memmove function. Accepts both numbers and refs as pointers.

                    -
                  • -
                  • dfhack.internal.memcmp(ptr1,ptr2,count)

                    -

                    Wraps the standard memcmp function.

                    -
                  • -
                  • dfhack.internal.memscan(haystack,count,step,needle,nsize)

                    -

                    Searches for needle of nsize bytes in haystack, -using count steps of step bytes. -Returns: step_idx, sum_idx, found_ptr, or nil if not found.

                    -
                  • -
                  • dfhack.internal.diffscan(old_data, new_data, start_idx, end_idx, eltsize[, oldval, newval, delta])

                    -

                    Searches for differences between buffers at ptr1 and ptr2, as integers of size eltsize. -The oldval, newval or delta arguments may be used to specify additional constraints. -Returns: found_index, or nil if end reached.

                    -
                  • -
                  -
                  -
                  -
                  -

                  Core interpreter context

                  -

                  While plugins can create any number of interpreter instances, -there is one special context managed by dfhack core. It is the -only context that can receive events from DF and plugins.

                  -

                  Core context specific functions:

                  -
                    -
                  • dfhack.is_core_context

                    -

                    Boolean value; true in the core context.

                    -
                  • -
                  • dfhack.timeout(time,mode,callback)

                    -

                    Arranges for the callback to be called once the specified -period of time passes. The mode argument specifies the -unit of time used, and may be one of 'frames' (raw FPS), -'ticks' (unpaused FPS), 'days', 'months', -'years' (in-game time). All timers other than -'frames' are cancelled when the world is unloaded, -and cannot be queued until it is loaded again. -Returns the timer id, or nil if unsuccessful due to -world being unloaded.

                    -
                  • -
                  • dfhack.timeout_active(id[,new_callback])

                    -

                    Returns the active callback with the given id, or nil -if inactive or nil id. If called with 2 arguments, replaces -the current callback with the given value, if still active. -Using timeout_active(id,nil) cancels the timer.

                    -
                  • -
                  • dfhack.onStateChange.foo = function(code)

                    -

                    Event. Receives the same codes as plugin_onstatechange in C++.

                    -
                  • -
                  -
                  -

                  Event type

                  -

                  An event is a native object transparently wrapping a lua table, -and implementing a __call metamethod. When it is invoked, it loops -through the table with next and calls all contained values. -This is intended as an extensible way to add listeners.

                  -

                  This type itself is available in any context, but only the -core context has the actual events defined by C++ code.

                  -

                  Features:

                  -
                    -
                  • dfhack.event.new()

                    -

                    Creates a new instance of an event.

                    -
                  • -
                  • event[key] = function

                    -

                    Sets the function as one of the listeners. Assign nil to remove it.

                    -

                    NOTE: The df.NULL key is reserved for the use by -the C++ owner of the event; it is an error to try setting it.

                    -
                  • -
                  • #event

                    -

                    Returns the number of non-nil listeners.

                    -
                  • -
                  • pairs(event)

                    -

                    Iterates over all listeners in the table.

                    -
                  • -
                  • event(args...)

                    -

                    Invokes all listeners contained in the event in an arbitrary -order using dfhack.safecall.

                    -
                  • -
                  -
                  -
                  -
                  -
                  -

                  Lua Modules

                  -

                  DFHack sets up the lua interpreter so that the built-in require -function can be used to load shared lua code from hack/lua/. -The dfhack namespace reference itself may be obtained via -require('dfhack'), although it is initially created as a -global by C++ bootstrap code.

                  -

                  The following module management functions are provided:

                  -
                    -
                  • mkmodule(name)

                    -

                    Creates an environment table for the module. Intended to be used as:

                    -
                    -local _ENV = mkmodule('foo')
                    -...
                    -return _ENV
                    -
                    -

                    If called the second time, returns the same table; thus providing reload support.

                    -
                  • -
                  • reload(name)

                    -

                    Reloads a previously require-d module "name" from the file. -Intended as a help for module development.

                    -
                  • -
                  • dfhack.BASE_G

                    -

                    This variable contains the root global environment table, which is -used as a base for all module and script environments. Its contents -should be kept limited to the standard Lua library and API described -in this document.

                    -
                  • -
                  -
                  -

                  Global environment

                  -

                  A number of variables and functions are provided in the base global -environment by the mandatory init file dfhack.lua:

                  -
                    -
                  • Color constants

                    -

                    These are applicable both for dfhack.color() and color fields -in DF functions or structures:

                    -

                    COLOR_RESET, COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, -COLOR_RED, COLOR_MAGENTA, COLOR_BROWN, COLOR_GREY, COLOR_DARKGREY, -COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED, -COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE

                    -
                  • -
                  • dfhack.onStateChange event codes

                    -

                    Available only in the core context, as is the event itself:

                    -

                    SC_WORLD_LOADED, SC_WORLD_UNLOADED, SC_MAP_LOADED, -SC_MAP_UNLOADED, SC_VIEWSCREEN_CHANGED, SC_CORE_INITIALIZED

                    -
                  • -
                  • Functions already described above

                    -

                    safecall, qerror, mkmodule, reload

                    -
                  • -
                  • printall(obj)

                    -

                    If the argument is a lua table or DF object reference, prints all fields.

                    -
                  • -
                  • copyall(obj)

                    -

                    Returns a shallow copy of the table or reference as a lua table.

                    -
                  • -
                  • pos2xyz(obj)

                    -

                    The object must have fields x, y and z. Returns them as 3 values. -If obj is nil, or x is -30000 (the usual marker for undefined -coordinates), returns nil.

                    -
                  • -
                  • xyz2pos(x,y,z)

                    -

                    Returns a table with x, y and z as fields.

                    -
                  • -
                  • safe_index(obj,index...)

                    -

                    Walks a sequence of dereferences, which may be represented by numbers or strings. -Returns nil if any of obj or indices is nil, or a numeric index is out of array bounds.

                    -
                  • -
                  -
                  -
                  -

                  utils

                  -
                    -
                  • utils.compare(a,b)

                    -

                    Comparator function; returns -1 if a<b, 1 if a>b, 0 otherwise.

                    -
                  • -
                  • utils.compare_name(a,b)

                    -

                    Comparator for names; compares empty string last.

                    -
                  • -
                  • utils.is_container(obj)

                    -

                    Checks if obj is a container ref.

                    -
                  • -
                  • utils.make_index_sequence(start,end)

                    -

                    Returns a lua sequence of numbers in start..end.

                    -
                  • -
                  • utils.make_sort_order(data, ordering)

                    -

                    Computes a sorted permutation of objects in data, as a table of integer -indices into the data sequence. Uses data.n as input length -if present.

                    -

                    The ordering argument is a sequence of ordering specs, represented -as lua tables with following possible fields:

                    -
                    -
                    ord.key = function(value)
                    -

                    Computes comparison key from input data value. Not called on nil. -If omitted, the comparison key is the value itself.

                    -
                    -
                    ord.key_table = function(data)
                    -

                    Computes a key table from the data table in one go.

                    -
                    -
                    ord.compare = function(a,b)
                    -

                    Comparison function. Defaults to utils.compare above. -Called on non-nil keys; nil sorts last.

                    -
                    -
                    ord.nil_first = true/false
                    -

                    If true, nil keys are sorted first instead of last.

                    -
                    -
                    ord.reverse = true/false
                    -

                    If true, sort non-nil keys in descending order.

                    -
                    -
                    -

                    For every comparison during sorting the specs are applied in -order until an unambiguous decision is reached. Sorting is stable.

                    -

                    Example of sorting a sequence by field foo:

                    -
                    -local spec = { key = function(v) return v.foo end }
                    -local order = utils.make_sort_order(data, { spec })
                    -local output = {}
                    -for i = 1,#order do output[i] = data[order[i]] end
                    -
                    -

                    Separating the actual reordering of the sequence in this -way enables applying the same permutation to multiple arrays. -This function is used by the sort plugin.

                    -
                  • -
                  • utils.assign(tgt, src)

                    -

                    Does a recursive assignment of src into tgt. -Uses df.assign if tgt is a native object ref; otherwise -recurses into lua tables.

                    -
                  • -
                  • utils.clone(obj, deep)

                    -

                    Performs a shallow, or semi-deep copy of the object as a lua table tree. -The deep mode recurses into lua tables and subobjects, except pointers -to other heap objects. -Null pointers are represented as df.NULL. Zero-based native containers -are converted to 1-based lua sequences.

                    -
                  • -
                  • utils.clone_with_default(obj, default, force)

                    -

                    Copies the object, using the default lua table tree -as a guide to which values should be skipped as uninteresting. -The force argument makes it always return a non-nil value.

                    -
                  • -
                  • utils.sort_vector(vector,field,cmpfun)

                    -

                    Sorts a native vector or lua sequence using the comparator function. -If field is not nil, applies the comparator to the field instead -of the whole object.

                    -
                  • -
                  • utils.binsearch(vector,key,field,cmpfun,min,max)

                    -

                    Does a binary search in a native vector or lua sequence for -key, using cmpfun and field like sort_vector. -If min and max are specified, they are used as the -search subrange bounds.

                    -

                    If found, returns item, true, idx. Otherwise returns -nil, false, insert_idx, where insert_idx is the correct -insertion point.

                    -
                  • -
                  • utils.insert_sorted(vector,item,field,cmpfun)

                    -

                    Does a binary search, and inserts item if not found. -Returns did_insert, vector[idx], idx.

                    -
                  • -
                  • utils.insert_or_update(vector,item,field,cmpfun)

                    -

                    Like insert_sorted, but also assigns the item into -the vector cell if insertion didn't happen.

                    -

                    As an example, you can use this to set skill values:

                    -
                    -utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id')
                    -
                    -

                    (For an explanation of new=true, see table assignment in the wrapper section)

                    -
                  • -
                  • utils.prompt_yes_no(prompt, default)

                    -

                    Presents a yes/no prompt to the user. If default is not nil, -allows just pressing Enter to submit the default choice. -If the user enters 'abort', throws an error.

                    -
                  • -
                  • utils.prompt_input(prompt, checkfun, quit_str)

                    -

                    Presents a prompt to input data, until a valid string is entered. -Once checkfun(input) returns true, ..., passes the values -through. If the user enters the quit_str (defaults to '~~~'), -throws an error.

                    -
                  • -
                  • utils.check_number(text)

                    -

                    A prompt_input checkfun that verifies a number input.

                    -
                  • -
                  -
                  -
                  -

                  dumper

                  -

                  A third-party lua table dumper module from -http://lua-users.org/wiki/DataDumper. Defines one -function:

                  -
                    -
                  • dumper.DataDumper(value, varname, fastmode, ident, indent_step)

                    -

                    Returns value converted to a string. The indent_step -argument specifies the indentation step size in spaces. For -the other arguments see the original documentation link above.

                    -
                  • -
                  -
                  -
                  -

                  class

                  -

                  Implements a trivial single-inheritance class system.

                  -
                    -
                  • Foo = defclass(Foo[, ParentClass])

                    -

                    Defines or updates class Foo. The Foo = defclass(Foo) syntax -is needed so that when the module or script is reloaded, the -class identity will be preserved through the preservation of -global variable values.

                    -

                    The defclass function is defined as a stub in the global -namespace, and using it will auto-load the class module.

                    -
                  • -
                  • Class.super

                    -

                    This class field is set by defclass to the parent class, and -allows a readable Class.super.method(self, ...) syntax for -calling superclass methods.

                    -
                  • -
                  • Class.ATTRS { foo = xxx, bar = yyy }

                    -

                    Declares certain instance fields to be attributes, i.e. auto-initialized -from fields in the table used as the constructor argument. If omitted, -they are initialized with the default values specified in this declaration.

                    -

                    If the default value should be nil, use ATTRS { foo = DEFAULT_NIL }.

                    -
                  • -
                  • new_obj = Class{ foo = arg, bar = arg, ... }

                    -

                    Calling the class as a function creates and initializes a new instance. -Initialization happens in this order:

                    -
                      -
                    1. An empty instance table is created, and its metatable set.
                    2. -
                    3. The preinit method is called via invoke_before (see below) -with the table used as argument to the class. This method is intended -for validating and tweaking that argument table.
                    4. -
                    5. Declared ATTRS are initialized from the argument table or their default values.
                    6. -
                    7. The init method is called via invoke_after with the argument table. -This is the main constructor method.
                    8. -
                    9. The postinit method is called via invoke_after with the argument table. -Place code that should be called after the object is fully constructed here.
                    10. -
                    -
                  • -
                  -

                  Predefined instance methods:

                  -
                    -
                  • instance:assign{ foo = xxx }

                    -

                    Assigns all values in the input table to the matching instance fields.

                    -
                  • -
                  • instance:callback(method_name, [args...])

                    -

                    Returns a closure that invokes the specified method of the class, -properly passing in self, and optionally a number of initial arguments too. -The arguments given to the closure are appended to these.

                    -
                  • -
                  • instance:invoke_before(method_name, args...)

                    -

                    Navigates the inheritance chain of the instance starting from the most specific -class, and invokes the specified method with the arguments if it is defined in -that specific class. Equivalent to the following definition in every class:

                    -
                    -function Class:invoke_before(method, ...)
                    -  if rawget(Class, method) then
                    -    rawget(Class, method)(self, ...)
                    -  end
                    -  Class.super.invoke_before(method, ...)
                    -end
                    -
                    -
                  • -
                  • instance:invoke_after(method_name, args...)

                    -

                    Like invoke_before, only the method is called after the recursive call to super, -i.e. invocations happen in the parent to child order.

                    -

                    These two methods are inspired by the Common Lisp before and after methods, and -are intended for implementing similar protocols for certain things. The class -library itself uses them for constructors.

                    -
                  • -
                  -

                  To avoid confusion, these methods cannot be redefined.

                  -
                  -
                  -
                  -

                  Plugins

                  -

                  DFHack plugins may export native functions and events -to lua contexts. They are automatically imported by -mkmodule('plugins.<name>'); this means that a lua -module file is still necessary for require to read.

                  -

                  The following plugins have lua support.

                  -
                  -

                  burrows

                  -

                  Implements extended burrow manipulations.

                  -

                  Events:

                  -
                    -
                  • onBurrowRename.foo = function(burrow)

                    -

                    Emitted when a burrow might have been renamed either through -the game UI, or renameBurrow().

                    -
                  • -
                  • onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype,worker)

                    -

                    Emitted when a tile might have been dug out. Only tracked if the -auto-growing burrows feature is enabled.

                    -
                  • -
                  -

                  Native functions:

                  -
                    -
                  • renameBurrow(burrow,name)

                    -

                    Renames the burrow, emitting onBurrowRename and updating auto-grow state properly.

                    -
                  • -
                  • findByName(burrow,name)

                    -

                    Finds a burrow by name, using the same rules as the plugin command line interface. -Namely, trailing '+' characters marking auto-grow burrows are ignored.

                    -
                  • -
                  • copyUnits(target,source,enable)

                    -

                    Applies units from source burrow to target. The enable -parameter specifies if they are to be added or removed.

                    -
                  • -
                  • copyTiles(target,source,enable)

                    -

                    Applies tiles from source burrow to target. The enable -parameter specifies if they are to be added or removed.

                    -
                  • -
                  • setTilesByKeyword(target,keyword,enable)

                    -

                    Adds or removes tiles matching a predefined keyword. The keyword -set is the same as used by the command line.

                    -
                  • -
                  -

                  The lua module file also re-exports functions from dfhack.burrows.

                  -
                  -
                  -

                  sort

                  -

                  Does not export any native functions as of now. Instead, it -calls lua code to perform the actual ordering of list items.

                  -
                  -
                  -
                  -

                  Scripts

                  -

                  Any files with the .lua extension placed into hack/scripts/* -are automatically used by the DFHack core as commands. The -matching command name consists of the name of the file sans -the extension.

                  -

                  If the first line of the script is a one-line comment, it is -used by the built-in ls and help commands.

                  -

                  NOTE: Scripts placed in subdirectories still can be accessed, but -do not clutter the ls command list; thus it is preferred -for obscure developer-oriented scripts and scripts used by tools. -When calling such scripts, always use '/' as the separator for -directories, e.g. devel/lua-example.

                  -

                  Scripts are re-read from disk every time they are used -(this may be changed later to check the file change time); however -the global variable values persist in memory between calls. -Every script gets its own separate environment for global -variables.

                  -

                  Arguments are passed in to the scripts via the ... built-in -quasi-variable; when the script is called by the DFHack core, -they are all guaranteed to be non-nil strings.

                  -

                  DFHack core invokes the scripts in the core context (see above); -however it is possible to call them from any lua code (including -from other scripts) in any context, via the same function the core uses:

                  -
                    -
                  • dfhack.run_script(name[,args...])

                    -

                    Run a lua script in hack/scripts/, as if it was started from dfhack command-line. -The name argument should be the name stem, as would be used on the command line.

                    -
                  • -
                  -

                  Note that this function lets errors propagate to the caller.

                  -
                  -
                  - - diff --git a/Readme.html b/Readme.html deleted file mode 100644 index 6155b72ce..000000000 --- a/Readme.html +++ /dev/null @@ -1,2704 +0,0 @@ - - - - - - -DFHack Readme - - - -
                  -

                  DFHack Readme

                  - -
                  -

                  Introduction

                  -

                  DFHack is a Dwarf Fortress memory access library and a set of basic -tools that use it. Tools come in the form of plugins or (not yet) -external tools. It is an attempt to unite the various ways tools -access DF memory and allow for easier development of new tools.

                  -
                  -

                  Contents

                  - -
                  -
                  -
                  -

                  Getting DFHack

                  -

                  The project is currently hosted on github, for both source and -binaries at http://github.com/peterix/dfhack

                  -

                  Releases can be downloaded from here: https://github.com/peterix/dfhack/downloads

                  -

                  All new releases are announced in the bay12 thread: http://tinyurl.com/dfhack-ng

                  -
                  -
                  -

                  Compatibility

                  -

                  DFHack works on Windows XP, Vista, 7 or any modern Linux distribution. -OSX is not supported due to lack of developers with a Mac.

                  -

                  Currently, versions 0.34.08 - 0.34.11 are supported. If you need DFHack -for older versions, look for older releases.

                  -

                  On Windows, you have to use the SDL version of DF.

                  -

                  It is possible to use the Windows DFHack under wine/OSX.

                  -
                  -
                  -

                  Installation/Removal

                  -

                  Installing DFhack involves copying files into your DF folder. -Copy the files from a release archive so that:

                  -
                  -
                    -
                  • On Windows, SDL.dll is replaced
                  • -
                  • On Linux, the 'dfhack' script is placed in the same folder as the 'df' script
                  • -
                  -
                  -

                  Uninstalling is basically the same, in reverse:

                  -
                  -
                    -
                  • On Windows, first delete SDL.dll and rename SDLreal.dll to SDL.dll. Then -remove the other DFHack files
                  • -
                  • On Linux, Remove the DFHack files.
                  • -
                  -
                  -

                  The stonesense plugin might require some additional libraries on Linux.

                  -

                  If any of the plugins or dfhack itself refuses to load, check the stderr.log -file created in your DF folder.

                  -
                  -
                  -

                  Using DFHack

                  -

                  DFHack basically extends what DF can do with something similar to the drop-down -console found in Quake engine games. On Windows, this is a separate command line -window. On linux, the terminal used to launch the dfhack script is taken over -(so, make sure you start from a terminal). Basic interaction with dfhack -involves entering commands into the console. For some basic instroduction, -use the 'help' command. To list all possible commands, use the 'ls' command. -Many commands have their own help or detailed description. You can use -'command help' or 'command ?' to show that.

                  -

                  The command line has some nice line editing capabilities, including history -that's preserved between different runs of DF (use up/down keys to go through -the history).

                  -

                  The second way to interact with DFHack is to bind the available commands -to in-game hotkeys. The old way to do this is via the hotkey/zoom menu (normally -opened with the 'h' key). Binding the commands is done by assigning a command as -a hotkey name (with 'n').

                  -

                  A new and more flexible way is the keybinding command in the dfhack console. -However, bindings created this way are not automatically remembered between runs -of the game, so it becomes necessary to use the dfhack.init file to ensure that -they are re-created every time it is loaded.

                  -

                  Interactive commands like 'liquids' cannot be used as hotkeys.

                  -

                  Most of the commands come from plugins. Those reside in 'hack/plugins/'.

                  -
                  -
                  -

                  Something doesn't work, help!

                  -

                  First, don't panic :) Second, dfhack keeps a few log files in DF's folder -- stderr.log and stdout.log. You can look at those and possibly find out what's -happening. -If you found a bug, you can either report it in the bay12 DFHack thread, -the issues tracker on github, contact me (peterix@gmail.com) or visit the -#dfhack IRC channel on freenode.

                  -
                  -
                  -

                  The init file

                  -

                  If your DF folder contains a file named dfhack.init, its contents will be run -every time you start DF. This allows setting up keybindings. An example file -is provided as dfhack.init-example - you can tweak it and rename to dfhack.init -if you want to use this functionality.

                  -
                  -

                  Setting keybindings

                  -

                  To set keybindings, use the built-in keybinding command. Like any other -command it can be used at any time from the console, but it is also meaningful -in the DFHack init file.

                  -

                  Currently it supports any combination of Ctrl/Alt/Shift with F1-F9, or A-Z.

                  -

                  Possible ways to call the command:

                  - --- - - - - - - - - - - - - - -
                  keybinding list <key>:
                   List bindings active for the key combination.
                  keybinding clear <key> <key>...:
                   Remove bindings for the specified keys.
                  keybinding add <key> "cmdline" "cmdline"...:
                   Add bindings for the specified -key.
                  keybinding set <key> "cmdline" "cmdline"...:
                   Clear, and then add bindings for -the specified key.
                  -

                  The <key> parameter above has the following case-sensitive syntax:

                  -
                  -[Ctrl-][Alt-][Shift-]KEY[@context]
                  -
                  -

                  where the KEY part can be F1-F9 or A-Z, and [] denote optional parts.

                  -

                  When multiple commands are bound to the same key combination, DFHack selects -the first applicable one. Later 'add' commands, and earlier entries within one -'add' command have priority. Commands that are not specifically intended for use -as a hotkey are always considered applicable.

                  -

                  The context part in the key specifier above can be used to explicitly restrict -the UI state where the binding would be applicable. If called without parameters, -the keybinding command among other things prints the current context string. -Only bindings with a context tag that either matches the current context fully, -or is a prefix ending at a '/' boundary would be considered for execution, i.e. -for context foo/bar/baz, possible matches are any of @foo/bar/baz, @foo/bar, -@foo or none.

                  -
                  -
                  -
                  -

                  Commands

                  -

                  DFHack command syntax consists of a command name, followed by arguments separated -by whitespace. To include whitespace in an argument, quote it in double quotes. -To include a double quote character, use \" inside double quotes.

                  -

                  If the first non-whitespace character of a line is #, the line is treated -as a comment, i.e. a silent no-op command.

                  -

                  If the first non-whitespace character is :, the command is parsed in a special -alternative mode: first, non-whitespace characters immediately following the : -are used as the command name; the remaining part of the line, starting with the first -non-whitespace character after the command name, is used verbatim as the first argument. -The following two command lines are exactly equivalent:

                  -
                  -
                    -
                  • :foo a b "c d" e f
                  • -
                  • foo "a b \"c d\" e f"
                  • -
                  -
                  -

                  This is intended for commands like rb_eval that evaluate script language statements.

                  -

                  Almost all the commands support using the 'help <command-name>' built-in command -to retrieve further help without having to look at this document. Alternatively, -some accept a 'help'/'?' option on their command line.

                  -
                  -

                  Game progress

                  -
                  -

                  die

                  -

                  Instantly kills DF without saving.

                  -
                  -
                  -

                  forcepause

                  -

                  Forces DF to pause. This is useful when your FPS drops below 1 and you lose -control of the game.

                  -
                  -
                    -
                  • Activate with 'forcepause 1'
                  • -
                  • Deactivate with 'forcepause 0'
                  • -
                  -
                  -
                  -
                  -

                  nopause

                  -

                  Disables pausing (both manual and automatic) with the exception of pause forced -by 'reveal hell'. This is nice for digging under rivers.

                  -
                  -
                  -

                  fastdwarf

                  -

                  Makes your minions move at ludicrous speeds.

                  -
                  -
                    -
                  • Activate with 'fastdwarf 1'
                  • -
                  • Deactivate with 'fastdwarf 0'
                  • -
                  -
                  -
                  -
                  -
                  -

                  Game interface

                  -
                  -

                  follow

                  -

                  Makes the game view follow the currently highlighted unit after you exit from -current menu/cursor mode. Handy for watching dwarves running around. Deactivated -by moving the view manually.

                  -
                  -
                  -

                  tidlers

                  -

                  Toggle between all possible positions where the idlers count can be placed.

                  -
                  -
                  -

                  twaterlvl

                  -

                  Toggle between displaying/not displaying liquid depth as numbers.

                  -
                  -
                  -

                  copystock

                  -

                  Copies the parameters of the currently highlighted stockpile to the custom -stockpile settings and switches to custom stockpile placement mode, effectively -allowing you to copy/paste stockpiles easily.

                  -
                  -
                  -

                  rename

                  -

                  Allows renaming various things.

                  -

                  Options:

                  -
                  - --- - - - - - - - - - - - - - - - - -
                  rename squad <index> "name":
                   Rename squad by index to 'name'.
                  rename hotkey <index> "name":
                   Rename hotkey by index. This allows assigning -longer commands to the DF hotkeys.
                  rename unit "nickname":
                   Rename a unit/creature highlighted in the DF user -interface.
                  rename unit-profession "custom profession":
                   Change proffession name of the -highlighted unit/creature.
                  rename building "name":
                   Set a custom name for the selected building. -The building must be one of stockpile, workshop, furnace, trap, -siege engine or an activity zone.
                  -
                  -
                  -
                  -
                  -

                  Adventure mode

                  -
                  -

                  adv-bodyswap

                  -

                  This allows taking control over your followers and other creatures in adventure -mode. For example, you can make them pick up new arms and armor and equip them -properly.

                  -

                  Usage:

                  -
                  -
                    -
                  • When viewing unit details, body-swaps into that unit.
                  • -
                  • In the main adventure mode screen, reverts transient swap.
                  • -
                  -
                  -
                  -
                  -

                  advtools

                  -

                  A package of different adventure mode tools (currently just one)

                  -

                  Usage:

                  -
                  - --- - - - - - - - -
                  list-equipped [all]:
                   List armor and weapons equipped by your companions. -If all is specified, also lists non-metal clothing.
                  metal-detector [all-types] [non-trader]:
                   Reveal metal armor and weapons in -shops. The options disable the checks -on item type and being in shop.
                  -
                  -
                  -
                  -
                  -

                  Map modification

                  -
                  -

                  changelayer

                  -

                  Changes material of the geology layer under cursor to the specified inorganic -RAW material. Can have impact on all surrounding regions, not only your embark! -By default changing stone to soil and vice versa is not allowed. By default -changes only the layer at the cursor position. Note that one layer can stretch -across lots of z levels. By default changes only the geology which is linked -to the biome under the cursor. That geology might be linked to other biomes -as well, though. Mineral veins and gem clusters will stay on the map. Use -'changevein' for them.

                  -

                  tl;dr: You will end up with changing quite big areas in one go, especially if -you use it in lower z levels. Use with care.

                  -

                  Options:

                  -
                  - --- - - - - - - - - - - - -
                  all_biomes:Change selected layer for all biomes on your map. -Result may be undesirable since the same layer can AND WILL -be on different z-levels for different biomes. Use the tool -'probe' to get an idea how layers and biomes are distributed -on your map.
                  all_layers:Change all layers on your map (only for the selected biome -unless 'all_biomes' is added). -Candy mountain, anyone? Will make your map quite boring, -but tidy.
                  force:Allow changing stone to soil and vice versa. !!THIS CAN HAVE -WEIRD EFFECTS, USE WITH CARE!! -Note that soil will not be magically replaced with stone. -You will, however, get a stone floor after digging so it -will allow the floor to be engraved. -Note that stone will not be magically replaced with soil. -You will, however, get a soil floor after digging so it -could be helpful for creating farm plots on maps with no -soil.
                  verbose:Give some details about what is being changed.
                  trouble:Give some advice about known problems.
                  -
                  -

                  Examples:

                  -
                  -
                  -
                  changelayer GRANITE
                  -
                  Convert layer at cursor position into granite.
                  -
                  changelayer SILTY_CLAY force
                  -
                  Convert layer at cursor position into clay even if it's stone.
                  -
                  changelayer MARBLE all_biomes all_layers
                  -
                  Convert all layers of all biomes which are not soil into marble.
                  -
                  -
                  -
                  -

                  Note

                  -
                    -
                  • If you use changelayer and nothing happens, try to pause/unpause the game -for a while and try to move the cursor to another tile. Then try again. -If that doesn't help try temporarily changing some other layer, undo your -changes and try again for the layer you want to change. Saving -and reloading your map might also help.
                  • -
                  • You should be fine if you only change single layers without the use -of 'force'. Still it's advisable to save your game before messing with -the map.
                  • -
                  • When you force changelayer to convert soil to stone you might experience -weird stuff (flashing tiles, tiles changed all over place etc). -Try reverting the changes manually or even better use an older savegame. -You did save your game, right?
                  • -
                  -
                  -
                  -
                  -

                  changevein

                  -

                  Changes material of the vein under cursor to the specified inorganic RAW -material. Only affects tiles within the current 16x16 block - for veins and -large clusters, you will need to use this command multiple times.

                  -

                  Example:

                  -
                  -
                  -
                  changevein NATIVE_PLATINUM
                  -
                  Convert vein at cursor position into platinum ore.
                  -
                  -
                  -
                  -
                  -

                  changeitem

                  -

                  Allows changing item material and base quality. By default the item currently -selected in the UI will be changed (you can select items in the 'k' list -or inside containers/inventory). By default change is only allowed if materials -is of the same subtype (for example wood<->wood, stone<->stone etc). But since -some transformations work pretty well and may be desired you can override this -with 'force'. Note that some attributes will not be touched, possibly resulting -in weirdness. To get an idea how the RAW id should look like, check some items -with 'info'. Using 'force' might create items which are not touched by -crafters/haulers.

                  -

                  Options:

                  -
                  - --- - - - - - - - - - - - -
                  info:Don't change anything, print some info instead.
                  here:Change all items at the cursor position. Requires in-game cursor.
                  material, m:Change material. Must be followed by valid material RAW id.
                  quality, q:Change base quality. Must be followed by number (0-5).
                  force:Ignore subtypes, force change to new material.
                  -
                  -

                  Examples:

                  -
                  -
                  -
                  changeitem m INORGANIC:GRANITE here
                  -
                  Change material of all items under the cursor to granite.
                  -
                  changeitem q 5
                  -
                  Change currently selected item to masterpiece quality.
                  -
                  -
                  -
                  -
                  -

                  colonies

                  -

                  Allows listing all the vermin colonies on the map and optionally turning them into honey bee colonies.

                  -

                  Options:

                  -
                  - --- - - - -
                  bees:turn colonies into honey bee colonies
                  -
                  -
                  -
                  -

                  deramp (by zilpin)

                  -

                  Removes all ramps designated for removal from the map. This is useful for replicating the old channel digging designation. -It also removes any and all 'down ramps' that can remain after a cave-in (you don't have to designate anything for that to happen).

                  -
                  -
                  -

                  feature

                  -

                  Enables management of map features.

                  -
                    -
                  • Discovering a magma feature (magma pool, volcano, magma sea, or curious -underground structure) permits magma workshops and furnaces to be built.
                  • -
                  • Discovering a cavern layer causes plants (trees, shrubs, and grass) from -that cavern to grow within your fortress.
                  • -
                  -

                  Options:

                  -
                  - --- - - - - - - - -
                  list:Lists all map features in your current embark by index.
                  show X:Marks the selected map feature as discovered.
                  hide X:Marks the selected map feature as undiscovered.
                  -
                  -
                  -
                  -

                  liquids

                  -

                  Allows adding magma, water and obsidian to the game. It replaces the normal -dfhack command line and can't be used from a hotkey. Settings will be remembered -as long as dfhack runs. Intended for use in combination with the command -liquids-here (which can be bound to a hotkey).

                  -

                  For more information, refer to the command's internal help.

                  -
                  -

                  Note

                  -

                  Spawning and deleting liquids can F up pathing data and -temperatures (creating heat traps). You've been warned.

                  -
                  -
                  -
                  -

                  liquids-here

                  -

                  Run the liquid spawner with the current/last settings made in liquids (if no -settings in liquids were made it paints a point of 7/7 magma by default).

                  -

                  Intended to be used as keybinding. Requires an active in-game cursor.

                  -
                  -
                  -

                  tiletypes

                  -

                  Can be used for painting map tiles and is an interactive command, much like -liquids.

                  -

                  The tool works with two set of options and a brush. The brush determines which -tiles will be processed. First set of options is the filter, which can exclude -some of the tiles from the brush by looking at the tile properties. The second -set of options is the paint - this determines how the selected tiles are -changed.

                  -

                  Both paint and filter can have many different properties including things like -general shape (WALL, FLOOR, etc.), general material (SOIL, STONE, MINERAL, -etc.), state of 'designated', 'hidden' and 'light' flags.

                  -

                  The properties of filter and paint can be partially defined. This means that -you can for example do something like this:

                  -
                  -filter material STONE
                  -filter shape FORTIFICATION
                  -paint shape FLOOR
                  -
                  -

                  This will turn all stone fortifications into floors, preserving the material.

                  -

                  Or this:

                  -
                  -filter shape FLOOR
                  -filter material MINERAL
                  -paint shape WALL
                  -
                  -

                  Turning mineral vein floors back into walls.

                  -

                  The tool also allows tweaking some tile flags:

                  -

                  Or this:

                  -
                  -paint hidden 1
                  -paint hidden 0
                  -
                  -

                  This will hide previously revealed tiles (or show hidden with the 0 option).

                  -

                  Any paint or filter option (or the entire paint or filter) can be disabled entirely by using the ANY keyword:

                  -
                  -paint hidden ANY
                  -paint shape ANY
                  -filter material any
                  -filter shape any
                  -filter any
                  -
                  -
                  -
                  You can use several different brushes for painting tiles:
                  -
                    -
                  • Point. (point)
                  • -
                  • Rectangular range. (range)
                  • -
                  • A column ranging from current cursor to the first solid tile above. (column)
                  • -
                  • DF map block - 16x16 tiles, in a regular grid. (block)
                  • -
                  -
                  -
                  -

                  Example:

                  -
                  -range 10 10 1
                  -
                  -

                  This will change the brush to a rectangle spanning 10x10 tiles on one z-level. -The range starts at the position of the cursor and goes to the east, south and -up.

                  -

                  For more details, see the 'help' command while using this.

                  -
                  -
                  -

                  tiletypes-commands

                  -

                  Runs tiletypes commands, separated by ;. This makes it possible to change -tiletypes modes from a hotkey.

                  -
                  -
                  -

                  tiletypes-here

                  -

                  Apply the current tiletypes options at the in-game cursor position, including -the brush. Can be used from a hotkey.

                  -
                  -
                  -

                  tiletypes-here-point

                  -

                  Apply the current tiletypes options at the in-game cursor position to a single -tile. Can be used from a hotkey.

                  -
                  -
                  -

                  tubefill

                  -

                  Fills all the adamantine veins again. Veins that were empty will be filled in -too, but might still trigger a demon invasion (this is a known bug).

                  -
                  -
                  -

                  extirpate

                  -

                  A tool for getting rid of trees and shrubs. By default, it only kills -a tree/shrub under the cursor. The plants are turned into ashes instantly.

                  -

                  Options:

                  -
                  - --- - - - - - - - -
                  shrubs:affect all shrubs on the map
                  trees:affect all trees on the map
                  all:affect every plant!
                  -
                  -
                  -
                  -

                  grow

                  -

                  Makes all saplings present on the map grow into trees (almost) instantly.

                  -
                  -
                  -

                  immolate

                  -

                  Very similar to extirpate, but additionally sets the plants on fire. The fires -can and will spread ;)

                  -
                  -
                  -

                  regrass

                  -

                  Regrows grass. Not much to it ;)

                  -
                  -
                  -

                  weather

                  -

                  Prints the current weather map by default.

                  -

                  Also lets you change the current weather to 'clear sky', 'rainy' or 'snowing'.

                  -

                  Options:

                  -
                  - --- - - - - - - - -
                  snow:make it snow everywhere.
                  rain:make it rain.
                  clear:clear the sky.
                  -
                  -
                  -
                  -
                  -

                  Map inspection

                  -
                  -

                  cursecheck

                  -

                  Checks a single map tile or the whole map/world for cursed creatures (ghosts, -vampires, necromancers, werebeasts, zombies).

                  -

                  With an active in-game cursor only the selected tile will be observed. -Without a cursor the whole map will be checked.

                  -

                  By default cursed creatures will be only counted in case you just want to find -out if you have any of them running around in your fort. Dead and passive -creatures (ghosts who were put to rest, killed vampires, ...) are ignored. -Undead skeletons, corpses, bodyparts and the like are all thrown into the curse -category "zombie". Anonymous zombies and resurrected body parts will show -as "unnamed creature".

                  -

                  Options:

                  -
                  - --- - - - - - - - - - -
                  detail:Print full name, date of birth, date of curse and some status -info (some vampires might use fake identities in-game, though).
                  nick:Set the type of curse as nickname (does not always show up -in-game, some vamps don't like nicknames).
                  all:Include dead and passive cursed creatures (can result in a quite -long list after having FUN with necromancers).
                  verbose:Print all curse tags (if you really want to know it all).
                  -
                  -

                  Examples:

                  -
                  -
                  -
                  cursecheck detail all
                  -
                  Give detailed info about all cursed creatures including deceased ones (no -in-game cursor).
                  -
                  cursecheck nick
                  -
                  Give a nickname all living/active cursed creatures on the map(no in-game -cursor).
                  -
                  -
                  -
                  -

                  Note

                  -
                    -
                  • If you do a full search (with the option "all") former ghosts will show up -with the cursetype "unknown" because their ghostly flag is not set -anymore. But if you happen to find a living/active creature with cursetype -"unknown" please report that in the dfhack thread on the modding forum or -per irc. This is likely to happen with mods which introduce new types -of curses, for example.
                  • -
                  -
                  -
                  -
                  -

                  flows

                  -

                  A tool for checking how many tiles contain flowing liquids. If you suspect that -your magma sea leaks into HFS, you can use this tool to be sure without -revealing the map.

                  -
                  -
                  -

                  probe

                  -

                  Can be used to determine tile properties like temperature.

                  -
                  -
                  -

                  prospect

                  -

                  Prints a big list of all the present minerals and plants. By default, only -the visible part of the map is scanned.

                  -

                  Options:

                  -
                  - --- - - - - - - - -
                  all:Scan the whole map, as if it was revealed.
                  value:Show material value in the output. Most useful for gems.
                  hell:Show the Z range of HFS tubes. Implies 'all'.
                  -
                  -
                  -

                  Pre-embark estimate

                  -

                  If prospect is called during the embark selection screen, it displays an estimate of -layer stone availability.

                  -
                  -

                  Note

                  -

                  The results of pre-embark prospect are an estimate, and can at best be expected -to be somewhere within +/- 30% of the true amount; sometimes it does a lot worse. -Especially, it is not clear how to precisely compute how many soil layers there -will be in a given embark tile, so it can report a whole extra layer, or omit one -that is actually present.

                  -
                  -

                  Options:

                  -
                  - --- - - - -
                  all:Also estimate vein mineral amounts.
                  -
                  -
                  -
                  -
                  -

                  reveal

                  -

                  This reveals the map. By default, HFS will remain hidden so that the demons -don't spawn. You can use 'reveal hell' to reveal everything. With hell revealed, -you won't be able to unpause until you hide the map again. If you really want -to unpause with hell revealed, use 'reveal demons'.

                  -

                  Reveal also works in adventure mode, but any of its effects are negated once -you move. When you use it this way, you don't need to run 'unreveal'.

                  -
                  -
                  -

                  unreveal

                  -

                  Reverts the effects of 'reveal'.

                  -
                  -
                  -

                  revtoggle

                  -

                  Switches between 'reveal' and 'unreveal'.

                  -
                  -
                  -

                  revflood

                  -

                  This command will hide the whole map and then reveal all the tiles that have -a path to the in-game cursor.

                  -
                  -
                  -

                  revforget

                  -

                  When you use reveal, it saves information about what was/wasn't visible before -revealing everything. Unreveal uses this information to hide things again. -This command throws away the information. For example, use in cases where -you abandoned with the fort revealed and no longer want the data.

                  -
                  -
                  -

                  showmood

                  -

                  Shows all items needed for the currently active strange mood.

                  -
                  -
                  -
                  -

                  Designations

                  -
                  -

                  burrow

                  -

                  Miscellaneous burrow control. Allows manipulating burrows and automated burrow -expansion while digging.

                  -

                  Options:

                  -
                  -
                  -
                  enable feature ...
                  -
                  Enable features of the plugin.
                  -
                  disable feature ...
                  -
                  Disable features of the plugin.
                  -
                  clear-unit burrow burrow ...
                  -
                  Remove all units from the burrows.
                  -
                  clear-tiles burrow burrow ...
                  -
                  Remove all tiles from the burrows.
                  -
                  set-units target-burrow src-burrow ...
                  -
                  Clear target, and adds units from source burrows.
                  -
                  add-units target-burrow src-burrow ...
                  -
                  Add units from the source burrows to the target.
                  -
                  remove-units target-burrow src-burrow ...
                  -
                  Remove units in source burrows from the target.
                  -
                  set-tiles target-burrow src-burrow ...
                  -
                  Clear target and adds tiles from the source burrows.
                  -
                  add-tiles target-burrow src-burrow ...
                  -
                  Add tiles from the source burrows to the target.
                  -
                  remove-tiles target-burrow src-burrow ...
                  -

                  Remove tiles in source burrows from the target.

                  -

                  For these three options, in place of a source burrow it is -possible to use one of the following keywords: ABOVE_GROUND, -SUBTERRANEAN, INSIDE, OUTSIDE, LIGHT, DARK, HIDDEN, REVEALED

                  -
                  -
                  -
                  -

                  Features:

                  -
                  - --- - - - -
                  auto-grow:When a wall inside a burrow with a name ending in '+' is dug -out, the burrow is extended to newly-revealed adjacent walls. -This final '+' may be omitted in burrow name args of commands above. -Digging 1-wide corridors with the miner inside the burrow is SLOW.
                  -
                  -
                  -
                  -

                  digv

                  -

                  Designates a whole vein for digging. Requires an active in-game cursor placed -over a vein tile. With the 'x' option, it will traverse z-levels (putting stairs -between the same-material tiles).

                  -
                  -
                  -

                  digvx

                  -

                  A permanent alias for 'digv x'.

                  -
                  -
                  -

                  digl

                  -

                  Designates layer stone for digging. Requires an active in-game cursor placed -over a layer stone tile. With the 'x' option, it will traverse z-levels -(putting stairs between the same-material tiles). With the 'undo' option it -will remove the dig designation instead (if you realize that digging out a 50 -z-level deep layer was not such a good idea after all).

                  -
                  -
                  -

                  diglx

                  -

                  A permanent alias for 'digl x'.

                  -
                  -
                  -

                  digexp

                  -

                  This command can be used for exploratory mining.

                  -

                  See: http://df.magmawiki.com/index.php/DF2010:Exploratory_mining

                  -

                  There are two variables that can be set: pattern and filter.

                  -

                  Patterns:

                  -
                  - --- - - - - - - - - - - - - - -
                  diag5:diagonals separated by 5 tiles
                  diag5r:diag5 rotated 90 degrees
                  ladder:A 'ladder' pattern
                  ladderr:ladder rotated 90 degrees
                  clear:Just remove all dig designations
                  cross:A cross, exactly in the middle of the map.
                  -
                  -

                  Filters:

                  -
                  - --- - - - - - - - -
                  all:designate whole z-level
                  hidden:designate only hidden tiles of z-level (default)
                  designated:Take current designation and apply pattern to it.
                  -
                  -

                  After you have a pattern set, you can use 'expdig' to apply it again.

                  -

                  Examples:

                  -
                  -
                  -
                  designate the diagonal 5 patter over all hidden tiles:
                  -
                    -
                  • expdig diag5 hidden
                  • -
                  -
                  -
                  apply last used pattern and filter:
                  -
                    -
                  • expdig
                  • -
                  -
                  -
                  Take current designations and replace them with the ladder pattern:
                  -
                    -
                  • expdig ladder designated
                  • -
                  -
                  -
                  -
                  -
                  -
                  -

                  digcircle

                  -

                  A command for easy designation of filled and hollow circles. -It has several types of options.

                  -

                  Shape:

                  -
                  - --- - - - - - - - -
                  hollow:Set the circle to hollow (default)
                  filled:Set the circle to filled
                  #:Diameter in tiles (default = 0, does nothing)
                  -
                  -

                  Action:

                  -
                  - --- - - - - - - - -
                  set:Set designation (default)
                  unset:Unset current designation
                  invert:Invert designations already present
                  -
                  -

                  Designation types:

                  -
                  - --- - - - - - - - - - - - - - -
                  dig:Normal digging designation (default)
                  ramp:Ramp digging
                  ustair:Staircase up
                  dstair:Staircase down
                  xstair:Staircase up/down
                  chan:Dig channel
                  -
                  -

                  After you have set the options, the command called with no options -repeats with the last selected parameters.

                  -

                  Examples:

                  -
                    -
                  • 'digcircle filled 3' = Dig a filled circle with radius = 3.
                  • -
                  • 'digcircle' = Do it again.
                  • -
                  -
                  -
                  -

                  filltraffic

                  -

                  Set traffic designations using flood-fill starting at the cursor.

                  -

                  Traffic Type Codes:

                  -
                  - --- - - - - - - - - - -
                  H:High Traffic
                  N:Normal Traffic
                  L:Low Traffic
                  R:Restricted Traffic
                  -
                  -

                  Other Options:

                  -
                  - --- - - - - - - - -
                  X:Fill accross z-levels.
                  B:Include buildings and stockpiles.
                  P:Include empty space.
                  -
                  -

                  Example:

                  -
                  -'filltraffic H' - When used in a room with doors, it will set traffic to HIGH in just that room.
                  -
                  -
                  -

                  alltraffic

                  -

                  Set traffic designations for every single tile of the map (useful for resetting traffic designations).

                  -

                  Traffic Type Codes:

                  -
                  - --- - - - - - - - - - -
                  H:High Traffic
                  N:Normal Traffic
                  L:Low Traffic
                  R:Restricted Traffic
                  -
                  -

                  Example:

                  -
                  -'alltraffic N' - Set traffic to 'normal' for all tiles.
                  -
                  -
                  -

                  getplants

                  -

                  This tool allows plant gathering and tree cutting by RAW ID. Specify the types -of trees to cut down and/or shrubs to gather by their plant names, separated -by spaces.

                  -

                  Options:

                  -
                  - --- - - - - - - - - - -
                  -t:Select trees only (exclude shrubs)
                  -s:Select shrubs only (exclude trees)
                  -c:Clear designations instead of setting them
                  -x:Apply selected action to all plants except those specified (invert -selection)
                  -
                  -

                  Specifying both -t and -s will have no effect. If no plant IDs are specified, -all valid plant IDs will be listed.

                  -
                  -
                  -
                  -

                  Cleanup and garbage disposal

                  -
                  -

                  clean

                  -

                  Cleans all the splatter that get scattered all over the map, items and -creatures. In an old fortress, this can significantly reduce FPS lag. It can -also spoil your !!FUN!!, so think before you use it.

                  -

                  Options:

                  -
                  - --- - - - - - - - -
                  map:Clean the map tiles. By default, it leaves mud and snow alone.
                  units:Clean the creatures. Will also clean hostiles.
                  items:Clean all the items. Even a poisoned blade.
                  -
                  -

                  Extra options for 'map':

                  -
                  - --- - - - - - -
                  mud:Remove mud in addition to the normal stuff.
                  snow:Also remove snow coverings.
                  -
                  -
                  -
                  -

                  spotclean

                  -

                  Works like 'clean map snow mud', but only for the tile under the cursor. Ideal -if you want to keep that bloody entrance 'clean map' would clean up.

                  -
                  -
                  -

                  autodump

                  -

                  This utility lets you quickly move all items designated to be dumped. -Items are instantly moved to the cursor position, the dump flag is unset, -and the forbid flag is set, as if it had been dumped normally. -Be aware that any active dump item tasks still point at the item.

                  -

                  Cursor must be placed on a floor tile so the items can be dumped there.

                  -

                  Options:

                  -
                  - --- - - - - - - - - - - - -
                  destroy:Destroy instead of dumping. Doesn't require a cursor.
                  destroy-here:Destroy items only under the cursor.
                  visible:Only process items that are not hidden.
                  hidden:Only process hidden items.
                  forbidden:Only process forbidden items (default: only unforbidden).
                  -
                  -
                  -
                  -

                  autodump-destroy-here

                  -

                  Destroy items marked for dumping under cursor. Identical to autodump -destroy-here, but intended for use as keybinding.

                  -
                  -
                  -

                  autodump-destroy-item

                  -

                  Destroy the selected item. The item may be selected in the 'k' list, or inside -a container. If called again before the game is resumed, cancels destroy.

                  -
                  -
                  -

                  cleanowned

                  -

                  Confiscates items owned by dwarfs. By default, owned food on the floor -and rotten items are confistacted and dumped.

                  -

                  Options:

                  -
                  - --- - - - - - - - - - - - -
                  all:confiscate all owned items
                  scattered:confiscated and dump all items scattered on the floor
                  x:confiscate/dump items with wear level 'x' and more
                  X:confiscate/dump items with wear level 'X' and more
                  dryrun:a dry run. combine with other options to see what will happen -without it actually happening.
                  -
                  -

                  Example:

                  -
                  -
                  -
                  cleanowned scattered X
                  -
                  This will confiscate rotten and dropped food, garbage on the floors and any -worn items with 'X' damage and above.
                  -
                  -
                  -
                  -
                  -
                  -

                  Bugfixes

                  -
                  -

                  drybuckets

                  -

                  This utility removes water from all buckets in your fortress, allowing them to be safely used for making lye.

                  -
                  -
                  -

                  fixdiplomats

                  -

                  Up to version 0.31.12, Elves only sent Diplomats to your fortress to propose -tree cutting quotas due to a bug; once that bug was fixed, Elves stopped caring -about excess tree cutting. This command adds a Diplomat position to all Elven -civilizations, allowing them to negotiate tree cutting quotas (and allowing you -to violate them and potentially start wars) in case you haven't already modified -your raws accordingly.

                  -
                  -
                  -

                  fixmerchants

                  -

                  This command adds the Guild Representative position to all Human civilizations, -allowing them to make trade agreements (just as they did back in 0.28.181.40d -and earlier) in case you haven't already modified your raws accordingly.

                  -
                  -
                  -

                  fixveins

                  -

                  Removes invalid references to mineral inclusions and restores missing ones. -Use this if you broke your embark with tools like tiletypes, or if you -accidentally placed a construction on top of a valuable mineral floor.

                  -
                  -
                  -

                  tweak

                  -

                  Contains various tweaks for minor bugs.

                  -

                  One-shot subcommands:

                  - --- - - - - - - - - - -
                  clear-missing:Remove the missing status from the selected unit. -This allows engraving slabs for ghostly, but not yet -found, creatures.
                  clear-ghostly:Remove the ghostly status from the selected unit and mark -it as dead. This allows getting rid of bugged ghosts -which do not show up in the engraving slab menu at all, -even after using clear-missing. It works, but is -potentially very dangerous - so use with care. Probably -(almost certainly) it does not have the same effects like -a proper burial. You've been warned.
                  fixmigrant:Remove the resident/merchant flag from the selected unit. -Intended to fix bugged migrants/traders who stay at the -map edge and don't enter your fort. Only works for -dwarves (or generally the player's race in modded games). -Do NOT abuse this for 'real' caravan merchants (if you -really want to kidnap them, use 'tweak makeown' instead, -otherwise they will have their clothes set to forbidden etc).
                  makeown:Force selected unit to become a member of your fort. -Can be abused to grab caravan merchants and escorts, even if -they don't belong to the player's race. Foreign sentients -(humans, elves) can be put to work, but you can't assign rooms -to them and they don't show up in DwarfTherapist because the -game treats them like pets. Grabbing draft animals from -a caravan can result in weirdness (animals go insane or berserk -and are not flagged as tame), but you are allowed to mark them -for slaughter. Grabbing wagons results in some funny spam, then -they are scuttled.
                  -

                  Subcommands that persist until disabled or DF quit:

                  - --- - - - - - - - - - - - - - - - - - - - -
                  stable-cursor:Saves the exact cursor position between t/q/k/d/etc menus of dwarfmode.
                  patrol-duty:Makes Train orders not count as patrol duty to stop unhappy thoughts. -Does NOT fix the problem when soldiers go off-duty (i.e. civilian).
                  readable-build-plate:
                   Fixes rendering of creature weight limits in pressure plate build menu.
                  stable-temp:Fixes performance bug 6012 by squashing jitter in temperature updates. -In very item-heavy forts with big stockpiles this can improve FPS by 50-100%
                  fast-heat:Further improves temperature update performance by ensuring that 1 degree -of item temperature is crossed in no more than specified number of frames -when updating from the environment temperature. This reduces the time it -takes for stable-temp to stop updates again when equilibrium is disturbed.
                  fix-dimensions:Fixes subtracting small amount of thread/cloth/liquid from a stack -by splitting the stack and subtracting from the remaining single item. -This is a necessary addition to the binary patch in bug 808.
                  advmode-contained:
                   Works around bug 6202, i.e. custom reactions with container inputs -in advmode. The issue is that the screen tries to force you to select -the contents separately from the container. This forcefully skips child -reagents.
                  fast-trade:Makes Shift-Enter in the Move Goods to Depot and Trade screens select -the current item (fully, in case of a stack), and scroll down one line.
                  -
                  -
                  -
                  -

                  Mode switch and reclaim

                  -
                  -

                  lair

                  -

                  This command allows you to mark the map as 'monster lair', preventing item -scatter on abandon. When invoked as 'lair reset', it does the opposite.

                  -

                  Unlike reveal, this command doesn't save the information about tiles - you -won't be able to restore state of real monster lairs using 'lair reset'.

                  -

                  Options:

                  -
                  - --- - - - - - -
                  lair:Mark the map as monster lair
                  lair reset:Mark the map as ordinary (not lair)
                  -
                  -
                  -
                  -

                  mode

                  -

                  This command lets you see and change the game mode directly. -Not all combinations are good for every situation and most of them will -produce undesirable results. There are a few good ones though.

                  -
                  -

                  Example

                  -

                  You are in fort game mode, managing your fortress and paused. -You switch to the arena game mode, assume control of a creature and then -switch to adventure game mode(1). -You just lost a fortress and gained an adventurer. -You could also do this. -You are in fort game mode, managing your fortress and paused at the esc menu. -You switch to the adventure game mode, then use Dfusion to assume control of a creature and then -save or retire. -You just created a returnable mountain home and gained an adventurer.

                  -
                  -

                  I take no responsibility of anything that happens as a result of using this tool

                  -
                  -
                  -
                  -

                  Visualizer and data export

                  -
                  -

                  ssense / stonesense

                  -

                  An isometric visualizer that runs in a second window. This requires working -graphics acceleration and at least a dual core CPU (otherwise it will slow -down DF).

                  -

                  All the data resides in the 'stonesense' directory. For detailed instructions, -see stonesense/README.txt

                  -

                  Compatible with Windows > XP SP3 and most modern Linux distributions.

                  -

                  Older versions, support and extra graphics can be found in the bay12 forum -thread: http://www.bay12forums.com/smf/index.php?topic=43260.0

                  -

                  Some additional resources: -http://df.magmawiki.com/index.php/Utility:Stonesense/Content_repository

                  -
                  -
                  -

                  mapexport

                  -

                  Export the current loaded map as a file. This will be eventually usable -with visualizers.

                  -
                  -
                  -

                  dwarfexport

                  -

                  Export dwarves to RuneSmith-compatible XML.

                  -
                  -
                  -
                  -

                  Job management

                  -
                  -

                  job

                  -

                  Command for general job query and manipulation.

                  -
                  -
                  Options:
                  -
                  -
                  no extra options
                  -
                  Print details of the current job. The job can be selected -in a workshop, or the unit/jobs screen.
                  -
                  list
                  -
                  Print details of all jobs in the selected workshop.
                  -
                  item-material <item-idx> <material[:subtoken]>
                  -
                  Replace the exact material id in the job item.
                  -
                  item-type <item-idx> <type[:subtype]>
                  -
                  Replace the exact item type id in the job item.
                  -
                  -
                  -
                  -
                  -
                  -

                  job-material

                  -

                  Alter the material of the selected job.

                  -

                  Invoked as:

                  -
                  -job-material <inorganic-token>
                  -
                  -

                  Intended to be used as a keybinding:

                  -
                  -
                    -
                  • In 'q' mode, when a job is highlighted within a workshop or furnace, -changes the material of the job. Only inorganic materials can be used -in this mode.
                  • -
                  • In 'b' mode, during selection of building components positions the cursor -over the first available choice with the matching material.
                  • -
                  -
                  -
                  -
                  -

                  job-duplicate

                  -
                  -
                  Duplicate the selected job in a workshop:
                  -
                    -
                  • In 'q' mode, when a job is highlighted within a workshop or furnace building, -instantly duplicates the job.
                  • -
                  -
                  -
                  -
                  -
                  -

                  workflow

                  -

                  Manage control of repeat jobs.

                  -

                  Usage:

                  -
                  -
                  -
                  workflow enable [option...], workflow disable [option...]
                  -

                  If no options are specified, enables or disables the plugin. -Otherwise, enables or disables any of the following options:

                  -
                    -
                  • drybuckets: Automatically empty abandoned water buckets.
                  • -
                  • auto-melt: Resume melt jobs when there are objects to melt.
                  • -
                  -
                  -
                  workflow jobs
                  -
                  List workflow-controlled jobs (if in a workshop, filtered by it).
                  -
                  workflow list
                  -
                  List active constraints, and their job counts.
                  -
                  workflow count <constraint-spec> <cnt-limit> [cnt-gap], workflow amount <constraint-spec> <cnt-limit> [cnt-gap]
                  -
                  Set a constraint. The first form counts each stack as only 1 item.
                  -
                  workflow unlimit <constraint-spec>
                  -
                  Delete a constraint.
                  -
                  -
                  -
                  -

                  Function

                  -

                  When the plugin is enabled, it protects all repeat jobs from removal. -If they do disappear due to any cause, they are immediately re-added to their -workshop and suspended.

                  -

                  In addition, when any constraints on item amounts are set, repeat jobs that -produce that kind of item are automatically suspended and resumed as the item -amount goes above or below the limit. The gap specifies how much below the limit -the amount has to drop before jobs are resumed; this is intended to reduce -the frequency of jobs being toggled.

                  -
                  -
                  -

                  Constraint examples

                  -

                  Keep metal bolts within 900-1000, and wood/bone within 150-200.

                  -
                  -workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100
                  -workflow amount AMMO:ITEM_AMMO_BOLTS/WOOD,BONE 200 50
                  -
                  -

                  Keep the number of prepared food & drink stacks between 90 and 120

                  -
                  -workflow count FOOD 120 30
                  -workflow count DRINK 120 30
                  -
                  -

                  Make sure there are always 25-30 empty bins/barrels/bags.

                  -
                  -workflow count BIN 30
                  -workflow count BARREL 30
                  -workflow count BOX/CLOTH,SILK,YARN 30
                  -
                  -

                  Make sure there are always 15-20 coal and 25-30 copper bars.

                  -
                  -workflow count BAR//COAL 20
                  -workflow count BAR//COPPER 30
                  -
                  -

                  Collect 15-20 sand bags and clay boulders.

                  -
                  -workflow count POWDER_MISC/SAND 20
                  -workflow count BOULDER/CLAY 20
                  -
                  -

                  Make sure there are always 80-100 units of dimple dye.

                  -
                  -  workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20
                  -
                  -In order for this to work, you have to set the material of the PLANT input
                  -on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material'
                  -command.
                  -
                  -
                  -
                  -
                  -
                  -

                  Fortress activity management

                  -
                  -

                  seedwatch

                  -

                  Tool for turning cooking of seeds and plants on/off depending on how much you -have of them.

                  -

                  See 'seedwatch help' for detailed description.

                  -
                  -
                  -

                  zone

                  -

                  Helps a bit with managing activity zones (pens, pastures and pits) and cages.

                  -

                  Options:

                  -
                  - --- - - - - - - - - - - - - - - - - - - - - - - - - - -
                  set:Set zone or cage under cursor as default for future assigns.
                  assign:Assign unit(s) to the pen or pit marked with the 'set' command. -If no filters are set a unit must be selected in the in-game ui. -Can also be followed by a valid zone id which will be set -instead.
                  unassign:Unassign selected creature from it's zone.
                  nick:Mass-assign nicknames, must be followed by the name you want -to set.
                  remnick:Mass-remove nicknames.
                  tocages:Assign unit(s) to cages inside a pasture.
                  uinfo:Print info about unit(s). If no filters are set a unit must -be selected in the in-game ui.
                  zinfo:Print info about zone(s). If no filters are set zones under -the cursor are listed.
                  verbose:Print some more info.
                  filters:Print list of valid filter options.
                  examples:Print some usage examples.
                  not:Negates the next filter keyword.
                  -
                  -

                  Filters:

                  -
                  - --- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                  all:Process all units (to be used with additional filters).
                  count:Must be followed by a number. Process only n units (to be used -with additional filters).
                  unassigned:Not assigned to zone, chain or built cage.
                  minage:Minimum age. Must be followed by number.
                  maxage:Maximum age. Must be followed by number.
                  race:Must be followed by a race RAW ID (e.g. BIRD_TURKEY, ALPACA, -etc). Negatable.
                  caged:In a built cage. Negatable.
                  own:From own civilization. Negatable.
                  merchant:Is a merchant / belongs to a merchant. Should only be used for -pitting, not for stealing animals (slaughter should work).
                  war:Trained war creature. Negatable.
                  hunting:Trained hunting creature. Negatable.
                  tamed:Creature is tame. Negatable.
                  trained:Creature is trained. Finds war/hunting creatures as well as -creatures who have a training level greater than 'domesticated'. -If you want to specifically search for war/hunting creatures use -'war' or 'hunting' Negatable.
                  trainablewar:Creature can be trained for war (and is not already trained for -war/hunt). Negatable.
                  trainablehunt:Creature can be trained for hunting (and is not already trained -for war/hunt). Negatable.
                  male:Creature is male. Negatable.
                  female:Creature is female. Negatable.
                  egglayer:Race lays eggs. Negatable.
                  grazer:Race is a grazer. Negatable.
                  milkable:Race is milkable. Negatable.
                  -
                  -
                  -

                  Usage with single units

                  -

                  One convenient way to use the zone tool is to bind the command 'zone assign' to -a hotkey, maybe also the command 'zone set'. Place the in-game cursor over -a pen/pasture or pit, use 'zone set' to mark it. Then you can select units -on the map (in 'v' or 'k' mode), in the unit list or from inside cages -and use 'zone assign' to assign them to their new home. Allows pitting your -own dwarves, by the way.

                  -
                  -
                  -

                  Usage with filters

                  -

                  All filters can be used together with the 'assign' command.

                  -

                  Restrictions: It's not possible to assign units who are inside built cages -or chained because in most cases that won't be desirable anyways. -It's not possible to cage owned pets because in that case the owner -uncages them after a while which results in infinite hauling back and forth.

                  -

                  Usually you should always use the filter 'own' (which implies tame) unless you -want to use the zone tool for pitting hostiles. 'own' ignores own dwarves unless -you specify 'race DWARF' (so it's safe to use 'assign all own' to one big -pasture if you want to have all your animals at the same place). 'egglayer' and -'milkable' should be used together with 'female' unless you have a mod with -egg-laying male elves who give milk or whatever. Merchants and their animals are -ignored unless you specify 'merchant' (pitting them should be no problem, -but stealing and pasturing their animals is not a good idea since currently they -are not properly added to your own stocks; slaughtering them should work).

                  -

                  Most filters can be negated (e.g. 'not grazer' -> race is not a grazer).

                  -
                  -
                  -

                  Mass-renaming

                  -

                  Using the 'nick' command you can set the same nickname for multiple units. -If used without 'assign', 'all' or 'count' it will rename all units in the -current default target zone. Combined with 'assign', 'all' or 'count' (and -further optional filters) it will rename units matching the filter conditions.

                  -
                  -
                  -

                  Cage zones

                  -

                  Using the 'tocages' command you can assign units to a set of cages, for example -a room next to your butcher shop(s). They will be spread evenly among available -cages to optimize hauling to and butchering from them. For this to work you need -to build cages and then place one pen/pasture activity zone above them, covering -all cages you want to use. Then use 'zone set' (like with 'assign') and use -'zone tocages filter1 filter2 ...'. 'tocages' overwrites 'assign' because it -would make no sense, but can be used together with 'nick' or 'remnick' and all -the usual filters.

                  -
                  -
                  -

                  Examples

                  -
                  -
                  zone assign all own ALPACA minage 3 maxage 10
                  -
                  Assign all own alpacas who are between 3 and 10 years old to the selected -pasture.
                  -
                  zone assign all own caged grazer nick ineedgrass
                  -
                  Assign all own grazers who are sitting in cages on stockpiles (e.g. after -buying them from merchants) to the selected pasture and give them -the nickname 'ineedgrass'.
                  -
                  zone assign all own not grazer not race CAT
                  -
                  Assign all own animals who are not grazers, excluding cats.
                  -
                  zone assign count 5 own female milkable
                  -
                  Assign up to 5 own female milkable creatures to the selected pasture.
                  -
                  zone assign all own race DWARF maxage 2
                  -
                  Throw all useless kids into a pit :)
                  -
                  zone nick donttouchme
                  -
                  Nicknames all units in the current default zone or cage to 'donttouchme'. -Mostly intended to be used for special pastures or cages which are not marked -as rooms you want to protect from autobutcher.
                  -
                  zone tocages count 50 own tame male not grazer
                  -
                  Stuff up to 50 owned tame male animals who are not grazers into cages built -on the current default zone.
                  -
                  -
                  -
                  -
                  -

                  autonestbox

                  -

                  Assigns unpastured female egg-layers to nestbox zones. Requires that you create -pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox -must be in the top left corner. Only 1 unit will be assigned per pen, regardless -of the size. The age of the units is currently not checked, most birds grow up -quite fast. Egglayers who are also grazers will be ignored, since confining them -to a 1x1 pasture is not a good idea. Only tame and domesticated own units are -processed since pasturing half-trained wild egglayers could destroy your neat -nestbox zones when they revert to wild. When called without options autonestbox -will instantly run once.

                  -

                  Options:

                  -
                  - --- - - - - - - - -
                  start:Start running every X frames (df simulation ticks). -Default: X=6000, which would be every 60 seconds at 100fps.
                  stop:Stop running automatically.
                  sleep:Must be followed by number X. Changes the timer to sleep X -frames between runs.
                  -
                  -
                  -
                  -

                  autobutcher

                  -

                  Assigns lifestock for slaughter once it reaches a specific count. Requires that -you add the target race(s) to a watch list. Only tame units will be processed.

                  -

                  Named units will be completely ignored (to protect specific animals from -autobutcher you can give them nicknames with the tool 'rename unit' for single -units or with 'zone nick' to mass-rename units in pastures and cages).

                  -

                  Creatures trained for war or hunting will be ignored as well.

                  -

                  Creatures assigned to cages will be ignored if the cage is defined as a room -(to avoid butchering unnamed zoo animals).

                  -

                  Once you have too much adults, the oldest will be butchered first. -Once you have too much kids, the youngest will be butchered first. -If you don't set any target count the following default will be used: -1 male kid, 5 female kids, 1 male adult, 5 female adults.

                  -

                  Options:

                  -
                  - --- - - - - - - - - - - - - - - - - - - - - - - - - - - -
                  start:Start running every X frames (df simulation ticks). -Default: X=6000, which would be every 60 seconds at 100fps.
                  stop:Stop running automatically.
                  sleep:Must be followed by number X. Changes the timer to sleep -X frames between runs.
                  watch R:Start watching a race. R can be a valid race RAW id (ALPACA, -BIRD_TURKEY, etc) or a list of ids seperated by spaces or -the keyword 'all' which affects all races on your current -watchlist.
                  unwatch R:Stop watching race(s). The current target settings will be -remembered. R can be a list of ids or the keyword 'all'.
                  forget R:Stop watching race(s) and forget it's/their target settings. -R can be a list of ids or the keyword 'all'.
                  autowatch:Automatically adds all new races (animals you buy from merchants, -tame yourself or get from migrants) to the watch list using -default target count.
                  noautowatch:Stop auto-adding new races to the watchlist.
                  list:Print the current status and watchlist.
                  list_export:Print status and watchlist in a format which can be used -to import them to another savegame (see notes).
                  target fk mk fa ma R:
                   Set target count for specified race(s). -fk = number of female kids, -mk = number of male kids, -fa = number of female adults, -ma = number of female adults. -R can be a list of ids or the keyword 'all' or 'new'. -R = 'all': change target count for all races on watchlist -and set the new default for the future. R = 'new': don't touch -current settings on the watchlist, only set the new default -for future entries.
                  example:Print some usage examples.
                  -
                  -

                  Examples:

                  -

                  You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, -1 male) of the race alpaca. Once the kids grow up the oldest adults will get -slaughtered. Excess kids will get slaughtered starting with the youngest -to allow that the older ones grow into adults. Any unnamed cats will -be slaughtered as soon as possible.

                  -
                  -autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY
                  -autobutcher target 0 0 0 0 CAT
                  -autobutcher watch ALPACA BIRD_TURKEY CAT
                  -autobutcher start
                  -
                  -

                  Automatically put all new races onto the watchlist and mark unnamed tame units -for slaughter as soon as they arrive in your fort. Settings already made -for specific races will be left untouched.

                  -
                  -autobutcher target 0 0 0 0 new
                  -autobutcher autowatch
                  -autobutcher start
                  -
                  -

                  Stop watching the races alpaca and cat, but remember the target count -settings so that you can use 'unwatch' without the need to enter the -values again. Note: 'autobutcher unwatch all' works, but only makes sense -if you want to keep the plugin running with the 'autowatch' feature or manually -add some new races with 'watch'. If you simply want to stop it completely use -'autobutcher stop' instead.

                  -
                  -autobutcher unwatch ALPACA CAT
                  -
                  -

                  Note:

                  -

                  Settings and watchlist are stored in the savegame, so that you can have -different settings for each world. If you want to copy your watchlist to -another savegame you can use the command list_export:

                  -
                  -Load savegame where you made the settings.
                  -Start a CMD shell and navigate to the df directory. Type the following into the shell:
                  -dfhack-run autobutcher list_export > autobutcher.bat
                  -Load the savegame where you want to copy the settings to, run the batch file (from the shell):
                  -autobutcher.bat
                  -
                  -
                  -
                  -

                  autolabor

                  -

                  Automatically manage dwarf labors.

                  -

                  When enabled, autolabor periodically checks your dwarves and enables or -disables labors. It tries to keep as many dwarves as possible busy but -also tries to have dwarves specialize in specific skills.

                  -
                  -

                  Note

                  -

                  Warning: autolabor will override any manual changes you make to labors -while it is enabled.

                  -
                  -

                  For detailed usage information, see 'help autolabor'.

                  -
                  -
                  -
                  -

                  Other

                  -
                  -

                  catsplosion

                  -

                  Makes cats just multiply. It is not a good idea to run this more than once or -twice.

                  -
                  -
                  -

                  dfusion

                  -

                  This is the DFusion lua plugin system by warmist/darius, running as a DFHack plugin.

                  -

                  See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=69682.15

                  -

                  Confirmed working DFusion plugins:

                  - --- - - - -
                  simple_embark:allows changing the number of dwarves available on embark.
                  -
                  -

                  Note

                  -
                    -
                  • Some of the DFusion plugins aren't completely ported yet. This can lead to crashes.
                  • -
                  • This is currently working only on Windows.
                  • -
                  • The game will be suspended while you're using dfusion. Don't panic when it doen't respond.
                  • -
                  -
                  -
                  -
                  -
                  -
                  -

                  Scripts

                  -

                  Lua or ruby scripts placed in the hack/scripts/ directory are considered for -execution as if they were native DFHack commands. They are listed at the end -of the 'ls' command output.

                  -

                  Note: scripts in subdirectories of hack/scripts/ can still be called, but will -only be listed by ls if called as 'ls -a'. This is intended as a way to hide -scripts that are obscure, developer-oriented, or should be used as keybindings.

                  -

                  Some notable scripts:

                  -
                  -

                  fix/*

                  -

                  Scripts in this subdirectory fix various bugs and issues, some of them obscure.

                  -
                    -
                  • fix/dead-units

                    -

                    Removes uninteresting dead units from the unit list. Doesn't seem to give any -noticeable performance gain, but migrants normally stop if the unit list grows -to around 3000 units, and this script reduces it back.

                    -
                  • -
                  • fix/population-cap

                    -

                    Run this after every migrant wave to ensure your population cap is not exceeded. -The issue with the cap is that it is compared to the population number reported -by the last caravan, so once it drops below the cap, migrants continue to come -until that number is updated again.

                    -
                  • -
                  • fix/stable-temp

                    -

                    Instantly sets the temperature of all free-lying items to be in equilibrium with -the environment and stops temperature updates. In order to maintain this efficient -state however, use tweak stable-temp and tweak fast-heat.

                    -
                  • -
                  • fix/item-occupancy

                    -

                    Diagnoses and fixes issues with nonexistant 'items occupying site', usually -caused by autodump bugs or other hacking mishaps.

                    -
                  • -
                  -
                  -
                  -

                  gui/*

                  -

                  Scripts that implement dialogs inserted into the main game window are put in this -directory.

                  -
                  -
                  -

                  quicksave

                  -

                  If called in dwarf mode, makes DF immediately auto-save the game by setting a flag -normally used in seasonal auto-save.

                  -
                  -
                  -

                  setfps

                  -

                  Run setfps <number> to set the FPS cap at runtime, in case you want to watch -combat in slow motion or something :)

                  -
                  -
                  -

                  siren

                  -

                  Wakes up sleeping units, cancels breaks and stops parties either everywhere, -or in the burrows given as arguments. In return, adds bad thoughts about -noise, tiredness and lack of protection. Also, the units with interrupted -breaks will go on break again a lot sooner. The script is intended for -emergencies, e.g. when a siege appears, and all your military is partying.

                  -
                  -
                  -

                  growcrops

                  -

                  Instantly grow seeds inside farming plots.

                  -

                  With no argument, this command list the various seed types currently in -use in your farming plots. -With a seed type, the script will grow 100 of these seeds, ready to be -harvested. You can change the number with a 2nd argument.

                  -

                  For exemple, to grow 40 plump helmet spawn:

                  -
                  -growcrops plump 40
                  -
                  -
                  -
                  -

                  removebadthoughts

                  -

                  This script remove negative thoughts from your dwarves. Very useful against -tantrum spirals.

                  -

                  With a selected unit in 'v' mode, will clear this unit's mind, otherwise -clear all your fort's units minds.

                  -

                  Individual dwarf happiness may not increase right after this command is run, -but in the short term your dwarves will get much more joyful. -The thoughts are set to be very old, and the game will remove them soon when -you unpause.

                  -

                  With the optional -v parameter, the script will dump the negative thoughts -it removed.

                  -
                  -
                  -

                  slayrace

                  -

                  Kills any unit of a given race.

                  -

                  With no argument, lists the available races.

                  -

                  With the special argument 'him', targets only the selected creature.

                  -

                  Any non-dead non-caged unit of the specified race gets its blood_count -set to 0, which means immediate death at the next game tick. For creatures -such as vampires, also set animal.vanish_countdown to 2.

                  -

                  An alternate mode is selected by adding a 2nd argument to the command, -magma. In this case, a column of 7/7 magma is generated on top of the -targets until they die (Warning: do not call on magma-safe creatures. Also, -using this mode for birds is not recommanded.)

                  -

                  Will target any unit on a revealed tile of the map, including ambushers.

                  -

                  Ex:

                  -
                  -slayrace gob
                  -
                  -

                  To kill a single creature, select the unit with the 'v' cursor and:

                  -
                  -slayrace him
                  -
                  -

                  To purify all elves on the map with fire (may have side-effects):

                  -
                  -slayrace elve magma
                  -
                  -
                  -
                  -

                  magmasource

                  -

                  Create an infinite magma source on a tile.

                  -

                  This script registers a map tile as a magma source, and every 12 game ticks -that tile receives 1 new unit of flowing magma.

                  -

                  Place the game cursor where you want to create the source (must be a -flow-passable tile, and not too high in the sky) and call:

                  -
                  -magmasource here
                  -
                  -

                  To add more than 1 unit everytime, call the command again.

                  -

                  To delete one source, place the cursor over its tile and use delete-here. -To remove all placed sources, call magmasource stop.

                  -

                  With no argument, this command shows an help message and list existing sources.

                  -
                  -
                  -

                  digfort

                  -

                  A script to designate an area for digging according to a plan in csv format.

                  -

                  This script, inspired from quickfort, can designate an area for digging. -Your plan should be stored in a .csv file like this:

                  -
                  -# this is a comment
                  -d;d;u;d;d;skip this tile;d
                  -d;d;d;i
                  -
                  -

                  Available tile shapes are named after the 'dig' menu shortcuts: -d for dig, u for upstairs, d downstairs, i updown, -h channel, r upward ramp, x remove designation. -Unrecognized characters are ignored (eg the 'skip this tile' in the sample).

                  -

                  Empty lines and data after a # are ignored as comments. -To skip a row in your design, use a single ;.

                  -

                  The script takes the plan filename, starting from the root df folder.

                  -
                  -
                  -

                  superdwarf

                  -

                  Similar to fastdwarf, per-creature.

                  -

                  To make any creature superfast, target it ingame using 'v' and:

                  -
                  -superdwarf add
                  -
                  -

                  Other options available: del, clear, list.

                  -

                  This plugin also shortens the 'sleeping' and 'on break' periods of targets.

                  -
                  -
                  -

                  drainaquifer

                  -

                  Remove all 'aquifer' tag from the map blocks. Irreversible.

                  -
                  -
                  -

                  deathcause

                  -

                  Focus a body part ingame, and this script will display the cause of death of -the creature.

                  -
                  -
                  -
                  -

                  In-game interface tools

                  -

                  These tools work by displaying dialogs or overlays in the game window, and -are mostly implemented by lua scripts.

                  -
                  -

                  Dwarf Manipulator

                  -

                  Implemented by the manipulator plugin. To activate, open the unit screen and -press 'l'.

                  -

                  This tool implements a Dwarf Therapist-like interface within the game UI. The -far left column displays the unit's Happiness (color-coded based on its -value), and the right half of the screen displays each dwarf's labor settings -and skill levels (0-9 for Dabbling thru Professional, A-E for Great thru Grand -Master, and U-Z for Legendary thru Legendary+5). Cells with red backgrounds -denote skills not controlled by labors.

                  -

                  Use the arrow keys or number pad to move the cursor around, holding Shift to -move 10 tiles at a time.

                  -

                  Press the Z-Up (<) and Z-Down (>) keys to move quickly between labor/skill -categories. The numpad Z-Up and Z-Down keys seek to the first or last unit -in the list.

                  -

                  Press Enter to toggle the selected labor for the selected unit, or Shift+Enter -to toggle all labors within the selected category.

                  -

                  Press the +- keys to sort the unit list according to the currently selected -skill/labor, and press the */ keys to sort the unit list by Name, Profession, -Happiness, or Arrival order (using Tab to select which sort method to use here).

                  -

                  With a unit selected, you can press the "v" key to view its properties (and -possibly set a custom nickname or profession) or the "c" key to exit -Manipulator and zoom to its position within your fortress.

                  -

                  The following mouse shortcuts are also available:

                  -
                    -
                  • Click on a column header to sort the unit list. Left-click to sort it in one -direction (descending for happiness or labors/skills, ascending for name or -profession) and right-click to sort it in the opposite direction.
                  • -
                  • Left-click on a labor cell to toggle that labor. Right-click to move the -cursor onto that cell instead of toggling it.
                  • -
                  • Left-click on a unit's name or profession to view its properties.
                  • -
                  • Right-click on a unit's name or profession to zoom to it.
                  • -
                  -

                  Pressing ESC normally returns to the unit screen, but Shift-ESC would exit -directly to the main dwarf mode screen.

                  -
                  -
                  -

                  Liquids

                  -

                  Implemented by the gui/liquids script. To use, bind to a key and activate in the 'k' mode.

                  -

                  While active, use the suggested keys to switch the usual liquids parameters, and Enter -to select the target area and apply changes.

                  -
                  -
                  -

                  Mechanisms

                  -

                  Implemented by the gui/mechanims script. To use, bind to a key and activate in the 'q' mode.

                  -

                  Lists mechanisms connected to the building, and their links. Navigating the list centers -the view on the relevant linked buildings.

                  -

                  To exit, press ESC or Enter; ESC recenters on the original building, while Enter leaves -focus on the current one. Shift-Enter has an effect equivalent to pressing Enter, and then -re-entering the mechanisms ui.

                  -
                  -
                  -

                  Rename

                  -

                  Backed by the rename plugin, the gui/rename script allows entering the desired name -via a simple dialog in the game ui.

                  -
                    -
                  • gui/rename [building] in 'q' mode changes the name of a building.

                    -

                    The selected building must be one of stockpile, workshop, furnace, trap, or siege engine. -It is also possible to rename zones from the 'i' menu.

                    -
                  • -
                  • gui/rename [unit] with a unit selected changes the nickname.

                    -
                  • -
                  • gui/rename unit-profession changes the selected unit's custom profession name.

                    -
                  • -
                  -

                  The building or unit options are automatically assumed when in relevant ui state.

                  -
                  -
                  -

                  Room List

                  -

                  Implemented by the gui/room-list script. To use, bind to a key and activate in the 'q' mode, -either immediately or after opening the assign owner page.

                  -

                  The script lists other rooms owned by the same owner, or by the unit selected in the assign -list, and allows unassigning them.

                  -
                  -
                  -
                  -

                  Behavior Mods

                  -

                  These plugins, when activated via configuration UI or by detecting certain -structures in RAWs, modify the game engine behavior concerning the target -objects to add features not otherwise present.

                  -
                  -

                  Siege Engine

                  -

                  The siege-engine plugin enables siege engines to be linked to stockpiles, and -aimed at an arbitrary rectangular area across Z levels, instead of the original -four directions. Also, catapults can be ordered to load arbitrary objects, not -just stones.

                  -

                  The configuration front-end to the plugin is implemented by the gui/siege-engine -script. Bind it to a key and activate after selecting a siege engine in 'q' mode.

                  -

                  The main mode displays the current target, selected ammo item type, linked stockpiles and -the allowed operator skill range. The map tile color is changed to signify if it can be -hit by the selected engine: green for fully reachable, blue for out of range, red for blocked, -yellow for partially blocked.

                  -

                  Pressing 'r' changes into the target selection mode, which works by highlighting two points -with Enter like all designations. When a target area is set, the engine projectiles are -aimed at that area, or units within it (this doesn't actually change the original aiming -code, instead the projectile trajectory parameters are rewritten as soon as it appears).

                  -

                  After setting the target in this way for one engine, you can 'paste' the same area into others -just by pressing 'p' in the main page of this script. The area to paste is kept until you quit -DF, or select another area manually.

                  -

                  Pressing 't' switches to a mode for selecting a stockpile to take ammo from.

                  -

                  Exiting from the siege engine script via ESC reverts the view to the state prior to starting -the script. Shift-ESC retains the current viewport, and also exits from the 'q' mode to main -menu.

                  -
                  -

                  DISCLAIMER

                  -

                  Siege engines are a very interesting feature, but sadly almost useless in the current state -because they haven't been updated since 2D and can only aim in four directions. This is an -attempt to bring them more up to date until Toady has time to work on it. Actual improvements, -e.g. like making siegers bring their own, are something only Toady can do.

                  -
                  -
                  -
                  -

                  Power Meter

                  -

                  The power-meter plugin implements a modified pressure plate that detects power being -supplied to gear boxes built in the four adjacent N/S/W/E tiles.

                  -

                  The configuration front-end is implemented by the gui/power-meter script. Bind it to a -key and activate after selecting Pressure Plate in the build menu.

                  -

                  The script follows the general look and feel of the regular pressure plate build -configuration page, but configures parameters relevant to the modded power meter building.

                  -
                  -
                  -

                  Steam Engine

                  -

                  The steam-engine plugin detects custom workshops with STEAM_ENGINE in -their token, and turns them into real steam engines.

                  -
                  -

                  Rationale

                  -

                  The vanilla game contains only water wheels and windmills as sources of -power, but windmills give relatively little power, and water wheels require -flowing water, which must either be a real river and thus immovable and -limited in supply, or actually flowing and thus laggy.

                  -

                  Steam engines are an alternative to water reactors that actually makes -sense, and hopefully doesn't lag. Also, unlike e.g. animal treadmills, -it can be done just by combining existing features of the game engine -in a new way with some glue code and a bit of custom logic.

                  -
                  -
                  -

                  Construction

                  -

                  The workshop needs water as its input, which it takes via a -passable floor tile below it, like usual magma workshops do. -The magma version also needs magma.

                  -
                  -

                  ISSUE

                  -

                  Since this building is a machine, and machine collapse -code cannot be hooked, it would collapse over true open space. -As a loophole, down stair provides support to machines, while -being passable, so use them.

                  -
                  -

                  After constructing the building itself, machines can be connected -to the edge tiles that look like gear boxes. Their exact position -is extracted from the workshop raws.

                  -
                  -

                  ISSUE

                  -

                  Like with collapse above, part of the code involved in -machine connection cannot be hooked. As a result, the workshop -can only immediately connect to machine components built AFTER it. -This also means that engines cannot be chained without intermediate -short axles that can be built later than both of the engines.

                  -
                  -
                  -
                  -

                  Operation

                  -

                  In order to operate the engine, queue the Stoke Boiler job (optionally -on repeat). A furnace operator will come, possibly bringing a bar of fuel, -and perform it. As a result, a "boiling water" item will appear -in the 't' view of the workshop.

                  -
                  -

                  Note

                  -

                  The completion of the job will actually consume one unit -of the appropriate liquids from below the workshop. This means -that you cannot just raise 7 units of magma with a piston and -have infinite power. However, liquid consumption should be slow -enough that water can be supplied by a pond zone bucket chain.

                  -
                  -

                  Every such item gives 100 power, up to a limit of 300 for coal, -and 500 for a magma engine. The building can host twice that -amount of items to provide longer autonomous running. When the -boiler gets filled to capacity, all queued jobs are suspended; -once it drops back to 3+1 or 5+1 items, they are re-enabled.

                  -

                  While the engine is providing power, steam is being consumed. -The consumption speed includes a fixed 10% waste rate, and -the remaining 90% are applied proportionally to the actual -load in the machine. With the engine at nominal 300 power with -150 load in the system, it will consume steam for actual -300*(10% + 90%*150/300) = 165 power.

                  -

                  Masterpiece mechanism and chain will decrease the mechanical -power drawn by the engine itself from 10 to 5. Masterpiece -barrel decreases waste rate by 4%. Masterpiece piston and pipe -decrease it by further 4%, and also decrease the whole steam -use rate by 10%.

                  -
                  -
                  -

                  Explosions

                  -

                  The engine must be constructed using barrel, pipe and piston -from fire-safe, or in the magma version magma-safe metals.

                  -

                  During operation weak parts get gradually worn out, and -eventually the engine explodes. It should also explode if -toppled during operation by a building destroyer, or a -tantruming dwarf.

                  -
                  -
                  -

                  Save files

                  -

                  It should be safe to load and view engine-using fortresses -from a DF version without DFHack installed, except that in such -case the engines won't work. However actually making modifications -to them, or machines they connect to (including by pulling levers), -can easily result in inconsistent state once this plugin is -available again. The effects may be as weird as negative power -being generated.

                  -
                  -
                  -
                  -

                  Add Spatter

                  -

                  This plugin makes reactions with names starting with SPATTER_ADD_ -produce contaminants on the items instead of improvements. The produced -contaminants are immune to being washed away by water or destroyed by -the clean items command.

                  -

                  The plugin is intended to give some use to all those poisons that can -be bought from caravans. :)

                  -

                  To be really useful this needs patches from bug 808, tweak fix-dimensions -and tweak advmode-contained.

                  -
                  -
                  -
                  - - From 38f2ae399a443ffa6f215c5d3c8ff2329de333ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 29 Sep 2012 03:30:38 +0200 Subject: [PATCH 035/114] Generate html documentation as part of the normal build. --- CMakeLists.txt | 21 ++++++++++++++++++++- fixTexts.sh | 5 ----- testTexts.sh | 5 +++++ 3 files changed, 25 insertions(+), 6 deletions(-) delete mode 100755 fixTexts.sh create mode 100755 testTexts.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bd4ad974..ee3f1d2e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,11 +147,30 @@ add_subdirectory(depends) find_package(Docutils) +set (RST_FILES +README +COMPILE +LUA_API +) + +set (RST_PROCESSED_FILES "") +IF(RST2HTML_EXECUTABLE) + foreach(F ${RST_FILES}) + add_custom_command( + OUTPUT "${dfhack_BINARY_DIR}/${F}.html" + COMMAND ${RST2HTML_EXECUTABLE} "${dfhack_SOURCE_DIR}/${F}.rst" > "${dfhack_BINARY_DIR}/${F}.html" + COMMENT "Translating ${F} to html" + DEPENDS "${dfhack_SOURCE_DIR}/${F}.rst") + list (APPEND RST_PROCESSED_FILES "${dfhack_BINARY_DIR}/${F}.html") + endforeach() + add_custom_target(HTML_DOCS ALL DEPENDS ${RST_PROCESSED_FILES}) +ENDIF() + # build the lib itself IF(BUILD_LIBRARY) add_subdirectory (library) ## install the default documentation files - install(FILES LICENSE Readme.html Compile.html Lua\ API.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) + install(FILES LICENSE ${RST_PROCESSED_FILES} DESTINATION ${DFHACK_USERDOC_DESTINATION}) endif() #build the plugins diff --git a/fixTexts.sh b/fixTexts.sh deleted file mode 100755 index 669fa068b..000000000 --- a/fixTexts.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rst2html README.rst > Readme.html -rst2html COMPILE.rst > Compile.html -rst2html DEVEL.rst > Devel.html -rst2html LUA_API.rst > Lua\ API.html diff --git a/testTexts.sh b/testTexts.sh new file mode 100755 index 000000000..0189ae2a2 --- /dev/null +++ b/testTexts.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# this script is used for easy testing of the rst documentation files. +rst2html README.rst > README.html +rst2html COMPILE.rst > COMPILE.html +rst2html LUA_API.rst > LUA_API.html From eaddd0e646c57a8e72a3dd3c0d95871ec7b25b2e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 29 Sep 2012 12:47:41 +0400 Subject: [PATCH 036/114] Try shutting up a few random warnings. --- library/include/RemoteServer.h | 1 + library/modules/Maps.cpp | 2 +- library/modules/Screen.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/library/include/RemoteServer.h b/library/include/RemoteServer.h index d89b44959..7a6940bbe 100644 --- a/library/include/RemoteServer.h +++ b/library/include/RemoteServer.h @@ -65,6 +65,7 @@ namespace DFHack RPCService *owner, const char *name, int flags) : RPCFunctionBase(in, out), name(name), flags(flags), owner(owner), id(-1) {} + virtual ~ServerFunctionBase() {} RPCService *owner; int16_t id; diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index f1f40f19c..608068b8e 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -585,7 +585,7 @@ bool MapExtras::Block::Allocate() if (!block) return false; - delete item_counts; + delete[] item_counts; delete tiles; delete basemats; init(); diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 8057d17a2..e9d19c931 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -195,7 +195,7 @@ bool Screen::drawBorder(const std::string &title) if (!gps) return false; int dimx = gps->dimx, dimy = gps->dimy; - Pen border(0xDB, 8); + Pen border('\xDB', 8); Pen text(0, 0, 7); Pen signature(0, 0, 8); From 7a74088287ed94517b30e6e9eadfef908da20043 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 29 Sep 2012 13:01:11 +0400 Subject: [PATCH 037/114] Make backspace seek to top left corner in manipulator. --- plugins/manipulator.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index feceb08ec..164b13dd7 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -385,7 +385,11 @@ viewscreen_unitlaborsst::viewscreen_unitlaborsst(vector &src, int cur { df::unit *unit = src[i]; if (!unit) + { + if (cursor_pos > i) + cursor_pos--; continue; + } UnitInfo *cur = new UnitInfo; @@ -628,6 +632,9 @@ void viewscreen_unitlaborsst::feed(set *events) sel_row = units.size() - 1; } + if (events->count(interface_key::STRING_A000)) + sel_row = 0; + if (sel_row < first_row) first_row = sel_row; if (first_row < sel_row - num_rows + 1) @@ -661,6 +668,9 @@ void viewscreen_unitlaborsst::feed(set *events) sel_column = next; } + if (events->count(interface_key::STRING_A000)) + sel_column = 0; + if (sel_column < 0) sel_column = 0; if (sel_column > NUM_COLUMNS - 1) From cb2acc169c3dd2ddfc4af36c4829c6f50561c40b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 29 Sep 2012 13:17:28 +0400 Subject: [PATCH 038/114] Edit docs. --- .gitignore | 3 +++ NEWS | 1 + README.rst | 34 ++++++++++++++++++++++++++-------- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index bdb9474bb..9f2b009c6 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,9 @@ build/tools build/plugins build/depends build/install_manifest.txt +build/README.html +build/LUA_API.html +build/COMPILE.html #ignore Kdevelop stuff .kdev4 diff --git a/NEWS b/NEWS index ee6dd6592..20c51f415 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,7 @@ DFHack v0.34.11-r2 (UNRELEASED) - prospect: pre-embark prospector accounts for caves & magma sea in its estimate. - rename: supports renaming stockpiles, workshops, traps, siege engines. - fastdwarf: now has an additional option to make dwarves teleport to their destination. + New commands: - misery: multiplies every negative thought gained (2x by default). - digtype: designates every tile of the same type of vein on the map for 'digging' (any dig designation). New tweaks: diff --git a/README.rst b/README.rst index 5cf79cdf5..a36cba607 100644 --- a/README.rst +++ b/README.rst @@ -1746,6 +1746,14 @@ In-game interface tools These tools work by displaying dialogs or overlays in the game window, and are mostly implemented by lua scripts. +.. note:: + + In order to avoid user confusion, as a matter of policy all these tools + display the word "DFHack" on the screen somewhere while active. + + As an exception, the tweak plugin described above does not follow this + guideline because it arguably just fixes small usability bugs in the game UI. + Dwarf Manipulator ================= @@ -1765,7 +1773,7 @@ move 10 tiles at a time. Press the Z-Up (<) and Z-Down (>) keys to move quickly between labor/skill categories. The numpad Z-Up and Z-Down keys seek to the first or last unit -in the list. +in the list. Backspace seeks to the top left corner. Press Enter to toggle the selected labor for the selected unit, or Shift+Enter to toggle all labors within the selected category. @@ -1850,6 +1858,12 @@ These plugins, when activated via configuration UI or by detecting certain structures in RAWs, modify the game engine behavior concerning the target objects to add features not otherwise present. +.. admonition:: DISCLAIMER + + The plugins in this section have mostly been created for fun as an interesting + technical challenge, and do not represent any long-term plans to produce more + similar modifications of the game. + Siege Engine ============ @@ -1859,6 +1873,17 @@ aimed at an arbitrary rectangular area across Z levels, instead of the original four directions. Also, catapults can be ordered to load arbitrary objects, not just stones. +Rationale +--------- + +Siege engines are a very interesting feature, but sadly almost useless in the current state +because they haven't been updated since 2D and can only aim in four directions. This is an +attempt to bring them more up to date until Toady has time to work on it. Actual improvements, +e.g. like making siegers bring their own, are something only Toady can do. + +Configuration UI +---------------- + The configuration front-end to the plugin is implemented by the gui/siege-engine script. Bind it to a key and activate after selecting a siege engine in 'q' mode. @@ -1882,13 +1907,6 @@ Exiting from the siege engine script via ESC reverts the view to the state prior the script. Shift-ESC retains the current viewport, and also exits from the 'q' mode to main menu. -.. admonition:: DISCLAIMER - - Siege engines are a very interesting feature, but sadly almost useless in the current state - because they haven't been updated since 2D and can only aim in four directions. This is an - attempt to bring them more up to date until Toady has time to work on it. Actual improvements, - e.g. like making siegers bring their own, are something only Toady can do. - Power Meter =========== From 6fd002382f08ee1f39976865e143104d6249f0fd Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 29 Sep 2012 12:12:56 +0200 Subject: [PATCH 039/114] ruby: fix cmake dependency on codegen.out.xml --- plugins/ruby/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index e10ee38bf..b8c90d134 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -20,7 +20,9 @@ ENDIF(DL_RUBY AND NOT APPLE) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb - DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl + # cmake quirk: depending on codegen.out.xml or generate_headers only is not enough, needs both + # test by manually patching any library/xml/moo.xml, run make ruby-autogen-rb -j2, and check build/plugins/ruby/ruby-autogen.rb for patched xml data + DEPENDS generate_headers ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl COMMENT ruby-autogen.rb ) ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb) From 48da06ec371421a48f801f6da1add6e437ccfa06 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 29 Sep 2012 15:18:04 +0400 Subject: [PATCH 040/114] Add an API function for reading the nominal skill level. --- LUA_API.rst | 5 +++++ library/LuaApi.cpp | 1 + library/include/modules/Units.h | 1 + library/modules/Units.cpp | 21 ++++++++++++++++++--- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index f532d2212..bf7ee45a7 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -918,6 +918,11 @@ Units module Returns the age of the unit in years as a floating-point value. If ``true_age`` is true, ignores false identities. +* ``dfhack.units.getNominalSkill(unit, skill[, use_rust])`` + + Retrieves the nominal skill level for the given unit. If ``use_rust`` + is *true*, subtracts the rust penalty. + * ``dfhack.units.getEffectiveSkill(unit, skill)`` Computes the effective rating for the given skill, taking into account exhaustion, pain etc. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index b2d41dc19..dab99f48a 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -828,6 +828,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isDwarf), WRAPM(Units, isCitizen), WRAPM(Units, getAge), + WRAPM(Units, getNominalSkill), WRAPM(Units, getEffectiveSkill), WRAPM(Units, computeMovementSpeed), WRAPM(Units, getProfessionName), diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 65f0b58a0..2019eb652 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -233,6 +233,7 @@ DFHACK_EXPORT bool isDwarf(df::unit *unit); DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false); +DFHACK_EXPORT int getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust = false); DFHACK_EXPORT int getEffectiveSkill(df::unit *unit, df::job_skill skill_id); DFHACK_EXPORT int computeMovementSpeed(df::unit *unit); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 01b7b50f4..2644f4ab2 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -876,7 +876,7 @@ inline void adjust_skill_rating(int &rating, bool is_adventure, int value, int d } } -int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id) +int Units::getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust) { CHECK_NULL_POINTER(unit); @@ -892,9 +892,24 @@ int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id) df::enum_field key(skill_id); auto skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, key); - int rating = 0; if (skill) - rating = std::max(0, int(skill->rating) - skill->rusty); + { + int rating = int(skill->rating); + if (use_rust) + rating -= skill->rusty; + return std::max(0, rating); + } + + return 0; +} + +int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id) +{ + /* + * This is 100% reverse-engineered from DF code. + */ + + int rating = getNominalSkill(unit, skill_id, true); // Apply special states From 62cde96724622a08f9dd6d4bf83f1763a02fd9df Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 29 Sep 2012 17:21:49 +0400 Subject: [PATCH 041/114] Add a script to reclassify 'individual choice' weapons based on unit skill. --- NEWS | 1 + README.rst | 40 ++++++--- dfhack.init-example | 3 + library/lua/utils.lua | 21 +++++ library/modules/Gui.cpp | 31 +++++++ scripts/gui/choose-weapons.lua | 154 +++++++++++++++++++++++++++++++++ 6 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 scripts/gui/choose-weapons.lua diff --git a/NEWS b/NEWS index 20c51f415..fe7b5b667 100644 --- a/NEWS +++ b/NEWS @@ -56,6 +56,7 @@ DFHack v0.34.11-r2 (UNRELEASED) - gui/rename: renaming stockpiles, workshops and units via an in-game dialog. - gui/power-meter: front-end for the Power Meter plugin. - gui/siege-engine: front-end for the Siege Engine plugin. + - gui/choose-weapons: auto-choose matching weapons in the military equip screen. Autolabor plugin: - can set nonidle hauler percentage. - broker excluded from all labors when needed at depot. diff --git a/README.rst b/README.rst index a36cba607..7ca375039 100644 --- a/README.rst +++ b/README.rst @@ -1800,19 +1800,19 @@ Pressing ESC normally returns to the unit screen, but Shift-ESC would exit directly to the main dwarf mode screen. -Liquids -======= +gui/liquids +=========== -Implemented by the gui/liquids script. To use, bind to a key and activate in the 'k' mode. +To use, bind to a key and activate in the 'k' mode. While active, use the suggested keys to switch the usual liquids parameters, and Enter to select the target area and apply changes. -Mechanisms -========== +gui/mechanisms +============== -Implemented by the gui/mechanims script. To use, bind to a key and activate in the 'q' mode. +To use, bind to a key and activate in the 'q' mode. Lists mechanisms connected to the building, and their links. Navigating the list centers the view on the relevant linked buildings. @@ -1822,10 +1822,10 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter re-entering the mechanisms ui. -Rename -====== +gui/rename +========== -Backed by the rename plugin, the gui/rename script allows entering the desired name +Backed by the rename plugin, this script allows entering the desired name via a simple dialog in the game ui. * ``gui/rename [building]`` in 'q' mode changes the name of a building. @@ -1840,16 +1840,30 @@ via a simple dialog in the game ui. The ``building`` or ``unit`` options are automatically assumed when in relevant ui state. -Room List -========= +gui/room-list +============= -Implemented by the gui/room-list script. To use, bind to a key and activate in the 'q' mode, -either immediately or after opening the assign owner page. +To use, bind to a key and activate in the 'q' mode, either immediately or after opening +the assign owner page. The script lists other rooms owned by the same owner, or by the unit selected in the assign list, and allows unassigning them. +gui/choose-weapons +================== + +Bind to a key, and activate in the Equip->View/Customize page of the military screen. + +Depending on the cursor location, it rewrites all 'individual choice weapon' entries +in the selected squad or position to use a specific weapon type matching the assigned +unit's top skill. If the cursor is in the rightmost list over a weapon entry, it rewrites +only that entry, and does it even if it is not 'individual choice'. + +Rationale: individual choice seems to be unreliable when there is a weapon shortage, +and may lead to inappropriate weapons being selected. + + ============= Behavior Mods ============= diff --git a/dfhack.init-example b/dfhack.init-example index 2e656a609..8505f5acc 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -69,6 +69,9 @@ keybinding add Ctrl-Shift-M@dwarfmode/Build/Position/Trap gui/power-meter # siege engine control keybinding add Alt-A@dwarfmode/QueryBuilding/Some/SiegeEngine gui/siege-engine +# military weapon auto-select +keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons + ############################ # UI and game logic tweaks # ############################ diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 9fa473ed8..b46363cd7 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -361,6 +361,27 @@ function insert_or_update(vector,item,field,cmp) return added,cur,pos end +-- Binary search and erase +function erase_sorted_key(vector,key,field,cmp) + local cur,found,pos = binsearch(vector,key,field,cmp) + if found then + if df.isvalid(vector) then + vector:erase(pos) + else + table.remove(vector, pos) + end + end + return found,cur,pos +end + +function erase_sorted(vector,item,field,cmp) + local key = item + if field and item then + key = item[field] + end + return erase_sorted_key(vector,key,field,cmp) +end + -- Calls a method with a string temporary function call_with_string(obj,methodname,...) return dfhack.with_temp_object( diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 21156ac0a..2afb36205 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -347,6 +347,37 @@ DEFINE_GET_FOCUS_STRING_HANDLER(layer_military) break; } + case df::viewscreen_layer_militaryst::Equip: + { + focus += "/" + enum_item_key(screen->equip.mode); + + switch (screen->equip.mode) + { + case df::viewscreen_layer_militaryst::T_equip::Customize: + { + if (screen->equip.edit_mode < 0) + focus += "/View"; + else + focus += "/" + enum_item_key(screen->equip.edit_mode); + break; + } + case df::viewscreen_layer_militaryst::T_equip::Uniform: + break; + case df::viewscreen_layer_militaryst::T_equip::Priority: + { + if (screen->equip.prio_in_move >= 0) + focus += "/Move"; + else + focus += "/View"; + break; + } + } + + static const char *lists[] = { "/Squads", "/Positions", "/Choices" }; + focus += lists[cur_list]; + break; + } + default: break; } diff --git a/scripts/gui/choose-weapons.lua b/scripts/gui/choose-weapons.lua new file mode 100644 index 000000000..85ad62b6e --- /dev/null +++ b/scripts/gui/choose-weapons.lua @@ -0,0 +1,154 @@ +-- Rewrite individual choice weapons into specific types. + +local utils = require 'utils' +local dlg = require 'gui.dialogs' + +local defs = df.global.world.raws.itemdefs +local entity = df.global.ui.main.fortress_entity +local tasks = df.global.ui.tasks +local equipment = df.global.ui.equipment + +function find_best_weapon(unit,mode) + local best = nil + local skill = nil + local skill_level = nil + local count = 0 + local function try(id,iskill) + local slevel = dfhack.units.getNominalSkill(unit,iskill) + -- Choose most skill + if (skill ~= nil and slevel > skill_level) + or (skill == nil and slevel > 0) then + best,skill,skill_level,count = id,iskill,slevel,0 + end + -- Then most produced within same skill + if skill == iskill then + local cnt = tasks.created_weapons[id] + if cnt > count then + best,count = id,cnt + end + end + end + for _,id in ipairs(entity.resources.weapon_type) do + local def = defs.weapons[id] + if def.skill_ranged >= 0 then + if mode == nil or mode == 'ranged' then + try(id, def.skill_ranged) + end + else + if mode == nil or mode == 'melee' then + try(id, def.skill_melee) + end + end + end + return best +end + +function unassign_wrong_items(unit,position,spec,subtype) + for i=#spec.assigned-1,0,-1 do + local id = spec.assigned[i] + local item = df.item.find(id) + + if item.subtype.subtype ~= subtype then + spec.assigned:erase(i) + + -- TODO: somewhat unexplored area; maybe missing some steps + utils.erase_sorted(position.assigned_items,id) + if utils.erase_sorted(equipment.items_assigned.WEAPON,item,'id') then + utils.insert_sorted(equipment.items_unassigned.WEAPON,item,'id') + end + equipment.update.weapon = true + unit.military.pickup_flags.update = true + end + end +end + +local count = 0 + +function adjust_uniform_spec(unit,position,spec,force) + if not unit then return end + local best + if spec.indiv_choice.melee then + best = find_best_weapon(unit, 'melee') + elseif spec.indiv_choice.ranged then + best = find_best_weapon(unit, 'ranged') + elseif spec.indiv_choice.any or force then + best = find_best_weapon(unit, nil) + end + if best then + count = count + 1 + spec.item_filter.item_subtype = best + spec.indiv_choice.any = false + spec.indiv_choice.melee = false + spec.indiv_choice.ranged = false + unassign_wrong_items(unit, position, spec, best) + end +end + +function adjust_position(unit,position,force) + if not unit then + local fig = df.historical_figure.find(position.occupant) + if not fig then return end + unit = df.unit.find(fig.unit_id) + end + + for _,v in ipairs(position.uniform.weapon) do + adjust_uniform_spec(unit, position, v, force) + end +end + +function adjust_squad(squad, force) + for _,pos in ipairs(squad.positions) do + adjust_position(nil, pos, force) + end +end + +local args = {...} +local vs = dfhack.gui.getCurViewscreen() +local vstype = df.viewscreen_layer_militaryst +if not vstype:is_instance(vs) then + qerror('Call this from the military screen') +end + +if vs.page == vstype.T_page.Equip +and vs.equip.mode == vstype.T_equip.T_mode.Customize then + local slist = vs.layer_objects[0] + local squad = vs.equip.squads[slist:getListCursor()] + + if slist.active then + print('Adjusting squad.') + adjust_squad(squad) + else + local plist = vs.layer_objects[1] + local pidx = plist:getListCursor() + local pos = squad.positions[pidx] + local unit = vs.equip.units[pidx] + + if plist.active then + print('Adjusting position.') + adjust_position(unit, pos) + elseif unit and vs.equip.edit_mode < 0 then + local wlist = vs.layer_objects[2] + local idx = wlist:getListCursor() + local cat = vs.equip.assigned.category[idx] + + if wlist.active and cat == df.uniform_category.weapon then + print('Adjusting spec.') + adjust_uniform_spec(unit, pos, vs.equip.assigned.spec[idx], true) + end + end + end +else + qerror('Call this from the Equip page of military screen') +end + +if count > 1 then + dlg.showMessage( + 'Choose Weapons', + 'Updated '..count..' uniform entries.', COLOR_GREEN + ) +elseif count == 0 then + dlg.showMessage( + 'Choose Weapons', + 'Did not find any entries to update.', COLOR_YELLOW + ) +end From b1ad92e310e89c72db271ee013b9daf4cf1afba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 30 Sep 2012 02:52:30 +0200 Subject: [PATCH 042/114] Track structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 9773484b8..98d7ebf6a 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9773484b8430417ad488423ddeae3832f011ce9d +Subproject commit 98d7ebf6a7995a0150b4320adfe8d7131f8174cf From 8812238bf6358b267508082ff2aecfb87115ebe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 30 Sep 2012 04:03:37 +0200 Subject: [PATCH 043/114] Update license, add contributors file, bump release number --- CMakeLists.txt | 11 ++++++----- COMPILE.rst => Compile.rst | 0 LICENSE | 2 +- LUA_API.rst => LUA Api.rst | 0 README.rst => Readme.rst | 2 +- library/Core.cpp | 2 +- library/DataDefs.cpp | 2 +- library/Hooks-darwin.cpp | 2 +- library/Hooks-egg.cpp | 2 +- library/Hooks-linux.cpp | 2 +- library/Hooks-windows.cpp | 2 +- library/LuaApi.cpp | 4 ++-- library/LuaTools.cpp | 4 ++-- library/LuaTypes.cpp | 4 ++-- library/LuaWrapper.cpp | 4 ++-- library/MiscUtils.cpp | 2 +- library/PlugLoad-windows.cpp | 2 +- library/PluginManager.cpp | 2 +- library/Process-darwin.cpp | 2 +- library/Process-linux.cpp | 2 +- library/Process-windows.cpp | 2 +- library/TileTypes.cpp | 4 ++-- library/Types.cpp | 2 +- library/VTableInterpose.cpp | 2 +- library/VersionInfoFactory.cpp | 2 +- library/include/BitArray.h | 2 +- library/include/ColorText.h | 2 +- library/include/Console.h | 2 +- library/include/Core.h | 2 +- library/include/DFHack.h | 2 +- library/include/DataDefs.h | 2 +- library/include/DataFuncs.h | 2 +- library/include/DataIdentity.h | 2 +- library/include/Error.h | 2 +- library/include/Export.h | 2 +- library/include/Hooks.h | 2 +- library/include/Internal.h | 2 +- library/include/LuaTools.h | 2 +- library/include/LuaWrapper.h | 2 +- library/include/MemAccess.h | 2 +- library/include/MiscUtils.h | 2 +- library/include/Module.h | 2 +- library/include/ModuleFactory.h | 2 +- library/include/PluginManager.h | 2 +- library/include/Pragma.h | 2 +- library/include/RemoteClient.h | 2 +- library/include/RemoteServer.h | 2 +- library/include/RemoteTools.h | 2 +- library/include/TileTypes.h | 2 +- library/include/Types.h | 2 +- library/include/VTableInterpose.h | 2 +- library/include/VersionInfo.h | 2 +- library/include/VersionInfoFactory.h | 2 +- library/include/modules/Buildings.h | 2 +- library/include/modules/Burrows.h | 2 +- library/include/modules/Constructions.h | 2 +- library/include/modules/Engravings.h | 2 +- library/include/modules/Graphic.h | 2 +- library/include/modules/Gui.h | 2 +- library/include/modules/Items.h | 2 +- library/include/modules/Job.h | 2 +- library/include/modules/MapCache.h | 2 +- library/include/modules/Maps.h | 2 +- library/include/modules/Materials.h | 2 +- library/include/modules/Screen.h | 2 +- library/include/modules/Translation.h | 2 +- library/include/modules/Units.h | 2 +- library/include/modules/Vegetation.h | 2 +- library/include/modules/Windows.h | 2 +- library/include/modules/World.h | 2 +- library/include/modules/kitchen.h | 2 +- library/modules/Buildings.cpp | 2 +- library/modules/Burrows.cpp | 2 +- library/modules/Constructions.cpp | 2 +- library/modules/Engravings.cpp | 2 +- library/modules/Graphic.cpp | 2 +- library/modules/Gui.cpp | 2 +- library/modules/Items.cpp | 2 +- library/modules/Job.cpp | 2 +- library/modules/Maps.cpp | 2 +- library/modules/Materials.cpp | 2 +- library/modules/Screen.cpp | 2 +- library/modules/Translation.cpp | 2 +- library/modules/Units.cpp | 2 +- library/modules/Vegetation.cpp | 2 +- library/modules/Windows.cpp | 2 +- library/modules/World.cpp | 2 +- 87 files changed, 95 insertions(+), 94 deletions(-) rename COMPILE.rst => Compile.rst (100%) rename LUA_API.rst => LUA Api.rst (100%) rename README.rst => Readme.rst (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index ee3f1d2e6..c8ec900e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,7 +63,7 @@ set(DF_VERSION_MINOR "34") set(DF_VERSION_PATCH "11") set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}") -SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.") +SET(DFHACK_RELEASE "r2" CACHE STRING "Current release revision.") set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-${DFHACK_RELEASE}") add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}") @@ -148,11 +148,12 @@ add_subdirectory(depends) find_package(Docutils) set (RST_FILES -README -COMPILE -LUA_API +"Readme" +"Compile" +"LUA Api" +"Contributors" ) - + set (RST_PROCESSED_FILES "") IF(RST2HTML_EXECUTABLE) foreach(F ${RST_FILES}) diff --git a/COMPILE.rst b/Compile.rst similarity index 100% rename from COMPILE.rst rename to Compile.rst diff --git a/LICENSE b/LICENSE index 96ab022d9..2a9a5513b 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ License of dfhack https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/LUA_API.rst b/LUA Api.rst similarity index 100% rename from LUA_API.rst rename to LUA Api.rst diff --git a/README.rst b/Readme.rst similarity index 99% rename from README.rst rename to Readme.rst index 7ca375039..b5c0f335d 100644 --- a/README.rst +++ b/Readme.rst @@ -31,7 +31,7 @@ Compatibility DFHack works on Windows XP, Vista, 7 or any modern Linux distribution. OSX is not supported due to lack of developers with a Mac. -Currently, versions 0.34.08 - 0.34.11 are supported. If you need DFHack +Currently, version 0.34.11 is supported (and tested). If you need DFHack for older versions, look for older releases. On Windows, you have to use the SDL version of DF. diff --git a/library/Core.cpp b/library/Core.cpp index 8a8d39e06..1015194ad 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index fa2aacf78..93e75e280 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/Hooks-darwin.cpp b/library/Hooks-darwin.cpp index 190616b86..31e191e1b 100644 --- a/library/Hooks-darwin.cpp +++ b/library/Hooks-darwin.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/Hooks-egg.cpp b/library/Hooks-egg.cpp index 8abd0ae8a..842573eca 100644 --- a/library/Hooks-egg.cpp +++ b/library/Hooks-egg.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/Hooks-linux.cpp b/library/Hooks-linux.cpp index ef1a765cb..31c0323f6 100644 --- a/library/Hooks-linux.cpp +++ b/library/Hooks-linux.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/Hooks-windows.cpp b/library/Hooks-windows.cpp index a586a0e85..1e6679855 100644 --- a/library/Hooks-windows.cpp +++ b/library/Hooks-windows.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index dab99f48a..352e21ccf 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1,6 +1,6 @@ -/* +/* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 8fce0076f..6bf218ba0 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -1,6 +1,6 @@ -/* +/* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 9f2689fa9..877609e56 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -1,6 +1,6 @@ -/* +/* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 9f2fc2f16..3da79e932 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -1,6 +1,6 @@ -/* +/* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index b73d730c1..a05e7eba5 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/PlugLoad-windows.cpp b/library/PlugLoad-windows.cpp index 714960e64..df8397f96 100644 --- a/library/PlugLoad-windows.cpp +++ b/library/PlugLoad-windows.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index ceb644e60..0c80639b4 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/Process-darwin.cpp b/library/Process-darwin.cpp index 3893cfc5f..c5e4e4b85 100644 --- a/library/Process-darwin.cpp +++ b/library/Process-darwin.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/Process-linux.cpp b/library/Process-linux.cpp index 1fecbab78..f88279b3f 100644 --- a/library/Process-linux.cpp +++ b/library/Process-linux.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/Process-windows.cpp b/library/Process-windows.cpp index db58c4d33..966468fb5 100644 --- a/library/Process-windows.cpp +++ b/library/Process-windows.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/TileTypes.cpp b/library/TileTypes.cpp index 5564e5e3f..804d5e772 100644 --- a/library/TileTypes.cpp +++ b/library/TileTypes.cpp @@ -1,6 +1,6 @@ -/* +/* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/Types.cpp b/library/Types.cpp index 72f8bdb8c..1ba87f839 100644 --- a/library/Types.cpp +++ b/library/Types.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/VTableInterpose.cpp b/library/VTableInterpose.cpp index 48ae6109c..141450615 100644 --- a/library/VTableInterpose.cpp +++ b/library/VTableInterpose.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/VersionInfoFactory.cpp b/library/VersionInfoFactory.cpp index 4ac53dd1a..e8c0561cd 100644 --- a/library/VersionInfoFactory.cpp +++ b/library/VersionInfoFactory.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/BitArray.h b/library/include/BitArray.h index ff68ea1d1..a507a38a4 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/ColorText.h b/library/include/ColorText.h index 50d1f3623..006fa119d 100644 --- a/library/include/ColorText.h +++ b/library/include/ColorText.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Console.h b/library/include/Console.h index ba59324e9..8d848135a 100644 --- a/library/include/Console.h +++ b/library/include/Console.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Core.h b/library/include/Core.h index e1f1cf3fb..d2d7080da 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/DFHack.h b/library/include/DFHack.h index ac5d78cf0..d606df94b 100644 --- a/library/include/DFHack.h +++ b/library/include/DFHack.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 0966c7f31..6c5ae5e62 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h index 01a798e34..f34b9cc73 100644 --- a/library/include/DataFuncs.h +++ b/library/include/DataFuncs.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 21dc68d1a..0b88e0195 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Error.h b/library/include/Error.h index 448c1a4f2..4e3ff269c 100644 --- a/library/include/Error.h +++ b/library/include/Error.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Export.h b/library/include/Export.h index 8e00dbaed..c3d007117 100644 --- a/library/include/Export.h +++ b/library/include/Export.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Hooks.h b/library/include/Hooks.h index 325af43eb..7f522a33d 100644 --- a/library/include/Hooks.h +++ b/library/include/Hooks.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Internal.h b/library/include/Internal.h index 12b7852a1..f92814cd4 100644 --- a/library/include/Internal.h +++ b/library/include/Internal.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 3330e23e7..fddbdbc26 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h index 9e4490220..ab694c4a8 100644 --- a/library/include/LuaWrapper.h +++ b/library/include/LuaWrapper.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/MemAccess.h b/library/include/MemAccess.h index a226018a6..0bc027c5c 100644 --- a/library/include/MemAccess.h +++ b/library/include/MemAccess.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index bbc88a2bb..61dd69297 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Module.h b/library/include/Module.h index 7433fce49..639edea6a 100644 --- a/library/include/Module.h +++ b/library/include/Module.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/ModuleFactory.h b/library/include/ModuleFactory.h index 276ccabb3..1f3d4222a 100644 --- a/library/include/ModuleFactory.h +++ b/library/include/ModuleFactory.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 38f0e2e50..9ef16703a 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Pragma.h b/library/include/Pragma.h index e688810d2..b6f776725 100644 --- a/library/include/Pragma.h +++ b/library/include/Pragma.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/RemoteClient.h b/library/include/RemoteClient.h index 37aaea9b1..1e8264cec 100644 --- a/library/include/RemoteClient.h +++ b/library/include/RemoteClient.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/RemoteServer.h b/library/include/RemoteServer.h index 7a6940bbe..df2f42712 100644 --- a/library/include/RemoteServer.h +++ b/library/include/RemoteServer.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/RemoteTools.h b/library/include/RemoteTools.h index e87e8026b..e70cefc00 100644 --- a/library/include/RemoteTools.h +++ b/library/include/RemoteTools.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/TileTypes.h b/library/include/TileTypes.h index 55628b3df..d21fb3c17 100644 --- a/library/include/TileTypes.h +++ b/library/include/TileTypes.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/Types.h b/library/include/Types.h index 1f211649d..3b9bf00b6 100644 --- a/library/include/Types.h +++ b/library/include/Types.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/VTableInterpose.h b/library/include/VTableInterpose.h index aeb407a8c..b52b32270 100644 --- a/library/include/VTableInterpose.h +++ b/library/include/VTableInterpose.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/VersionInfo.h b/library/include/VersionInfo.h index 5ff20516e..2ea4170fa 100644 --- a/library/include/VersionInfo.h +++ b/library/include/VersionInfo.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/VersionInfoFactory.h b/library/include/VersionInfoFactory.h index 08b1ea76e..f69f37fee 100644 --- a/library/include/VersionInfoFactory.h +++ b/library/include/VersionInfoFactory.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 639df6865..266aadcb8 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Burrows.h b/library/include/modules/Burrows.h index e26a2dc20..49b7df3c1 100644 --- a/library/include/modules/Burrows.h +++ b/library/include/modules/Burrows.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Constructions.h b/library/include/modules/Constructions.h index f8a3e5c61..41959b2d7 100644 --- a/library/include/modules/Constructions.h +++ b/library/include/modules/Constructions.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Engravings.h b/library/include/modules/Engravings.h index ea5eae62c..bf30c62a8 100644 --- a/library/include/modules/Engravings.h +++ b/library/include/modules/Engravings.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Graphic.h b/library/include/modules/Graphic.h index 6c003b4bc..a4b2dad10 100644 --- a/library/include/modules/Graphic.h +++ b/library/include/modules/Graphic.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mr�zek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mr�zek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index b06408f68..147f27883 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 81c8e1285..367ce9b41 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index 9ce72c326..490d79a34 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index 262e70bbf..ac083075f 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 869b21580..3150acccf 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index fb5a6353c..e44ca89ed 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index fdad6c8ad..a2e64a515 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Translation.h b/library/include/modules/Translation.h index f75bf6339..03521797a 100644 --- a/library/include/modules/Translation.h +++ b/library/include/modules/Translation.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 2019eb652..ab1a5f63a 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Vegetation.h b/library/include/modules/Vegetation.h index 83a192b98..89ba5ff6c 100644 --- a/library/include/modules/Vegetation.h +++ b/library/include/modules/Vegetation.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/Windows.h b/library/include/modules/Windows.h index 7dbc9f1ad..700b88851 100644 --- a/library/include/modules/Windows.h +++ b/library/include/modules/Windows.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/World.h b/library/include/modules/World.h index 8b0fe3f59..1cd57e2f2 100644 --- a/library/include/modules/World.h +++ b/library/include/modules/World.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/include/modules/kitchen.h b/library/include/modules/kitchen.h index f9d9d3458..ca88e9163 100644 --- a/library/include/modules/kitchen.h +++ b/library/include/modules/kitchen.h @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 9e78edd3e..89106ca87 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Burrows.cpp b/library/modules/Burrows.cpp index 62f62779a..48a2ca3a8 100644 --- a/library/modules/Burrows.cpp +++ b/library/modules/Burrows.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Constructions.cpp b/library/modules/Constructions.cpp index 2d61c447a..16c1f1b89 100644 --- a/library/modules/Constructions.cpp +++ b/library/modules/Constructions.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Engravings.cpp b/library/modules/Engravings.cpp index bff17ef5f..c2e0e6fce 100644 --- a/library/modules/Engravings.cpp +++ b/library/modules/Engravings.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Graphic.cpp b/library/modules/Graphic.cpp index 2f490c233..4188df2b8 100644 --- a/library/modules/Graphic.cpp +++ b/library/modules/Graphic.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mr�zek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mr�zek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 2afb36205..b211f9f2b 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index b8c697a48..1118afcfe 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index b74a4b73f..df2009d0a 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 608068b8e..5ef4ce829 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index db9c9c7df..fbc441493 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index e9d19c931..0b9279d2d 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Translation.cpp b/library/modules/Translation.cpp index db1305161..6f4ca2b04 100644 --- a/library/modules/Translation.cpp +++ b/library/modules/Translation.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 2644f4ab2..6d71f5a15 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Vegetation.cpp b/library/modules/Vegetation.cpp index a76c3a2cd..1a6779553 100644 --- a/library/modules/Vegetation.cpp +++ b/library/modules/Vegetation.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/Windows.cpp b/library/modules/Windows.cpp index 218b6f7e0..4e749322f 100644 --- a/library/modules/Windows.cpp +++ b/library/modules/Windows.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any diff --git a/library/modules/World.cpp b/library/modules/World.cpp index 67b8c1236..a4c4c5604 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -1,6 +1,6 @@ /* https://github.com/peterix/dfhack -Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any From dd9be6cc7c81a3d577f6283a798c78b522013d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 30 Sep 2012 04:16:54 +0200 Subject: [PATCH 044/114] The usual derp with files not added: Contributors.rst --- CMakeLists.txt | 2 +- Contributors.rst | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 Contributors.rst diff --git a/CMakeLists.txt b/CMakeLists.txt index c8ec900e4..8e69111dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,7 +153,7 @@ set (RST_FILES "LUA Api" "Contributors" ) - + set (RST_PROCESSED_FILES "") IF(RST2HTML_EXECUTABLE) foreach(F ${RST_FILES}) diff --git a/Contributors.rst b/Contributors.rst new file mode 100644 index 000000000..112a847e7 --- /dev/null +++ b/Contributors.rst @@ -0,0 +1,62 @@ +Contributors +============ +The following is a list of people who have contributed to **DFHack**. +If you belong here and are missing please add yourself and send me (peterix) a pull request :-) + +- Petr Mrázek +- Alexander Gavrilov +- doomchild +- Quietust +- jj +- Warmist +- Robert Heinrich +- simon +- Kelly Martin +- mizipzor +- Simon Jackson +- belal +- RusAnon +- Raoul XQ +- Matthew Cline +- Mike Stewart +- Timothy Collett +- RossM +- Tom Prince +- Jared Adams +- expwnent +- Erik Youngren +- Espen Wiborg +- Tim Walberg +- Mikko Juola +- rampaging-poet +- U-glouglou\\simon +- Clayton Hughes +- zilpin +- Will Rogers +- NMLittle +- root +- reverb +- Zhentar +- Valentin Ochs +- Priit Laes +- kmartin +- Neil Little +- rout +- rofl0r +- harlanplayford +- gsvslto +- sami +- potato +- playfordh +- feng1st +- comestible +- Rumrusher +- Rinin +- Raoul van Putten +- John Shade +- John Beisley +- Feng +- Donald Ruegsegger +- Caldfir +- Antalia +- Angus Mezick \ No newline at end of file From e2d810ddc2bbedc1dcdc154b299f0dee7452f8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 30 Sep 2012 06:44:00 +0200 Subject: [PATCH 045/114] Put HTML back. --- CMakeLists.txt | 40 +- Compile.html | 554 +++++++++ Contributors.html | 391 +++++++ LUA Api.html | 2104 +++++++++++++++++++++++++++++++++ Readme.html | 2808 +++++++++++++++++++++++++++++++++++++++++++++ fixTexts.sh | 6 + testTexts.sh | 5 - 7 files changed, 5883 insertions(+), 25 deletions(-) create mode 100644 Compile.html create mode 100644 Contributors.html create mode 100644 LUA Api.html create mode 100644 Readme.html create mode 100755 fixTexts.sh delete mode 100755 testTexts.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e69111dd..c1ced670c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,31 +147,31 @@ add_subdirectory(depends) find_package(Docutils) -set (RST_FILES -"Readme" -"Compile" -"LUA Api" -"Contributors" -) - -set (RST_PROCESSED_FILES "") -IF(RST2HTML_EXECUTABLE) - foreach(F ${RST_FILES}) - add_custom_command( - OUTPUT "${dfhack_BINARY_DIR}/${F}.html" - COMMAND ${RST2HTML_EXECUTABLE} "${dfhack_SOURCE_DIR}/${F}.rst" > "${dfhack_BINARY_DIR}/${F}.html" - COMMENT "Translating ${F} to html" - DEPENDS "${dfhack_SOURCE_DIR}/${F}.rst") - list (APPEND RST_PROCESSED_FILES "${dfhack_BINARY_DIR}/${F}.html") - endforeach() - add_custom_target(HTML_DOCS ALL DEPENDS ${RST_PROCESSED_FILES}) -ENDIF() +#set (RST_FILES +#"Readme" +#"Compile" +#"LUA Api" +#"Contributors" +#) + +#set (RST_PROCESSED_FILES "") +#IF(RST2HTML_EXECUTABLE) +# foreach(F ${RST_FILES}) +# add_custom_command( +# OUTPUT "${dfhack_BINARY_DIR}/${F}.html" +# COMMAND ${RST2HTML_EXECUTABLE} "${dfhack_SOURCE_DIR}/${F}.rst" "${dfhack_BINARY_DIR}/${F}.html" +# COMMENT "Translating ${F} to html" +# DEPENDS "${dfhack_SOURCE_DIR}/${F}.rst") +# list (APPEND RST_PROCESSED_FILES "${dfhack_BINARY_DIR}/${F}.html") +# endforeach() +# add_custom_target(HTML_DOCS ALL DEPENDS ${RST_PROCESSED_FILES}) +#ENDIF() # build the lib itself IF(BUILD_LIBRARY) add_subdirectory (library) ## install the default documentation files - install(FILES LICENSE ${RST_PROCESSED_FILES} DESTINATION ${DFHACK_USERDOC_DESTINATION}) + install(FILES LICENSE "LUA Api.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) endif() #build the plugins diff --git a/Compile.html b/Compile.html new file mode 100644 index 000000000..41d409112 --- /dev/null +++ b/Compile.html @@ -0,0 +1,554 @@ + + + + + + +Building DFHACK + + + +
                  +

                  Building DFHACK

                  + + +
                  +

                  Linux

                  +

                  On Linux, DFHack acts as a library that shadows parts of the SDL API using LD_PRELOAD.

                  +
                  +

                  How to get the code

                  +

                  DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. +Having a 'git' package installed is the minimal requirement, but some sort of git gui or git integration for your favorite text editor/IDE will certainly help.

                  +

                  The code resides here: https://github.com/peterix/dfhack

                  +

                  If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:

                  +
                  +git clone git://github.com/peterix/dfhack.git
                  +cd dfhack
                  +git submodule init
                  +git submodule update
                  +
                  +

                  If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).

                  +
                  +
                  +

                  Dependencies

                  +

                  DFHack is meant to be installed into an existing DF folder, so get one ready.

                  +

                  For building, you need a 32-bit version of GCC. For example, to build DFHack on +a 64-bit distribution like Arch, you'll need the multilib development tools and libraries.

                  +

                  Before you can build anything, you'll also need cmake. It is advisable to also get +ccmake on distributions that split the cmake package into multiple parts.

                  +

                  For the code generation parts, you need perl and the XML::LibXML and XML::LibXSLT perl packages. +You should be able to find them in your distro repositories (on Arch linux 'perl-xml-libxml' and 'perl-xml-libxslt').

                  +
                  +
                  +

                  Build

                  +

                  Building is fairly straightforward. Enter the build folder and start the build like this:

                  +
                  +cd build
                  +cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/home/user/DF
                  +make install
                  +
                  +

                  Obviously, replace the install path with path to your DF. This will build the library +along with the normal set of plugins and install them into your DF folder.

                  +

                  Alternatively, you can use ccmake instead of cmake:

                  +
                  +cd build
                  +ccmake ..
                  +make install
                  +
                  +

                  This will show a curses-based interface that lets you set all of the +extra options.

                  +

                  You can also use a cmake-friendly IDE like KDevelop 4 or the cmake-gui +program.

                  +
                  +
                  +
                  +

                  Windows

                  +

                  On Windows, DFHack replaces the SDL library distributed with DF.

                  +
                  +

                  How to get the code

                  +

                  DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. +You will need some sort of Windows port of git, or a GUI. Some examples:

                  +
                  + +
                  +

                  The code resides here: https://github.com/peterix/dfhack

                  +

                  If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:

                  +
                  +git clone git://github.com/peterix/dfhack.git
                  +cd dfhack
                  +git submodule init
                  +git submodule update
                  +
                  +

                  The tortoisegit GUI should have the equivalent options included.

                  +

                  If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).

                  +
                  +
                  +

                  Dependencies

                  +

                  First, you need cmake. Get the win32 installer version from the official +site: http://www.cmake.org/cmake/resources/software.html

                  +

                  It has the usual installer wizard. Make sure you let it add its binary folder +to your binary search PATH so the tool can be later run from anywhere.

                  +

                  You'll need a copy of Microsoft Visual C++ 2010. The Express version is sufficient. +Grab it from Microsoft's site.

                  +

                  For the code generation parts, you'll need perl and XML::LibXML. You can install them like this:

                  +
                    +
                  • download and install strawberry perl from http://strawberryperl.com/
                  • +
                  • reboot so that the system can pick up the new binary path
                  • +
                  • open a cmd.exe window and run "cpan XML::LibXML" (obviously without the quotes). This can take a while to complete.
                  • +
                  • Same with "cpan XML::LibXSLT".
                  • +
                  +

                  If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.

                  +
                  +
                  +

                  Build

                  +

                  There are several different batch files in the build folder along with a script that's used for picking the DF path.

                  +

                  First, run set_df_path.vbs and point the dialog that pops up at your DF folder that you want to use for development. +Next, run one of the scripts with generate prefix. These create the MSVC solution file(s):

                  +
                    +
                  • all will create a solution with everything enabled (and the kitchen sink).
                  • +
                  • gui will pop up the cmake gui and let you pick and choose what to build. This is probably what you want most of the time. Set the options you are interested in, then hit configure, then generate. More options can appear after the configure step.
                  • +
                  • minimal will create a minimal solution with just the bare necessities - the main library and standard plugins.
                  • +
                  +

                  Then you can either open the solution with MSVC or use one of the msbuild scripts:

                  +
                    +
                  • Scripts with build prefix will only build.
                  • +
                  • Scripts with install prefix will build DFHack and install it to the previously selected DF path.
                  • +
                  • Scripts with package prefix will build and create a .zip package of DFHack.
                  • +
                  +

                  When you open the solution in MSVC, make sure you never use the Debug builds. Those aren't +binary-compatible with DF. If you try to use a debug build with DF, you'll only get crashes. +So pick either Release or RelWithDebInfo build and build the INSTALL target.

                  +

                  The debug scripts actually do RelWithDebInfo builds.

                  +
                  +
                  +
                  +

                  Build types

                  +

                  cmake allows you to pick a build type by changing this +variable: CMAKE_BUILD_TYPE

                  +
                  +cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
                  +
                  +

                  Without specifying a build type or 'None', cmake uses the +CMAKE_CXX_FLAGS variable for building.

                  +

                  Valid and useful build types include 'Release', 'Debug' and +'RelWithDebInfo'. 'Debug' is not available on Windows.

                  +
                  +
                  +

                  Using the library as a developer

                  +

                  Currently, the most direct way to use the library is to write a plugin that can be loaded by it. +All the plugins can be found in the 'plugins' folder. There's no in-depth documentation +on how to write one yet, but it should be easy enough to copy one and just follow the pattern.

                  +

                  Other than through plugins, it is possible to use DFHack via remote access interface, or by writing Lua scripts.

                  +

                  The most important parts of DFHack are the Core, Console, Modules and Plugins.

                  +
                    +
                  • Core acts as the centerpiece of DFHack - it acts as a filter between DF and SDL and synchronizes the various plugins with DF.
                  • +
                  • Console is a thread-safe console that can be used to invoke commands exported by Plugins.
                  • +
                  • Modules actually describe the way to access information in DF's memory. You can get them from the Core. Most modules are split into two parts: high-level and low-level. Higl-level is mostly method calls, low-level publicly visible pointers to DF's data structures.
                  • +
                  • Plugins are the tools that use all the other stuff to make things happen. A plugin can have a list of commands that it exports and an onupdate function that will be called each DF game tick.
                  • +
                  +

                  Rudimentary API documentation can be built using doxygen (see build options with ccmake or cmake-gui).

                  +

                  DFHack consists of variously licensed code, but invariably weak copyleft. +The main license is zlib/libpng, some bits are MIT licensed, and some are BSD licensed.

                  +

                  Feel free to add your own extensions and plugins. Contributing back to +the dfhack repository is welcome and the right thing to do :)

                  +
                  +

                  DF data structure definitions

                  +

                  DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.

                  +

                  Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.

                  +

                  Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.

                  +
                  +
                  +

                  Remote access interface

                  +

                  DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The dfhack-run command uses this interface to invoke ordinary console commands.

                  +

                  Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.

                  +

                  Protocol client implementations exist for Java and C#.

                  +
                  +
                  +

                  Contributing to DFHack

                  +

                  Several things should be kept in mind when contributing to DFHack.

                  +
                  +

                  Coding style

                  +

                  DFhack uses ANSI formatting and four spaces as indentation. Line +endings are UNIX. The files use UTF-8 encoding. Code not following this +won't make me happy, because I'll have to fix it. There's a good chance +I'll make you fix it ;)

                  +
                  +
                  +

                  How to get new code into DFHack

                  +

                  You can send patches or make a clone of the github repo and ask me on +the IRC channel to pull your code in. I'll review it and see if there +are any problems. I'll fix them if they are minor.

                  +

                  Fixes are higher in priority. If you want to work on something, but +don't know what, check out http://github.com/peterix/dfhack/issues -- +this is also a good place to dump new ideas and/or bugs that need +fixing.

                  +
                  +
                  +

                  Memory research

                  +

                  If you want to do memory research, you'll need some tools and some knowledge. +In general, you'll need a good memory viewer and optionally something +to look at machine code without getting crazy :)

                  +

                  Good windows tools include:

                  +
                    +
                  • Cheat Engine
                  • +
                  • IDA Pro (the free version)
                  • +
                  +

                  Good linux tools:

                  +
                    +
                  • angavrilov's df-structures gui (visit us on IRC for details).
                  • +
                  • edb (Evan's Debugger)
                  • +
                  • IDA Pro running under wine.
                  • +
                  • Some of the tools residing in the legacy dfhack branch.
                  • +
                  +

                  Using publicly known information and analyzing the game's data is preferred.

                  +
                  +
                  +
                  +
                  + + diff --git a/Contributors.html b/Contributors.html new file mode 100644 index 000000000..72cb8bdfc --- /dev/null +++ b/Contributors.html @@ -0,0 +1,391 @@ + + + + + + +Contributors + + + +
                  +

                  Contributors

                  + +

                  The following is a list of people who have contributed to DFHack. +If you belong here and are missing please add yourself and send me (peterix) a pull request :-)

                  + +
                  + + diff --git a/LUA Api.html b/LUA Api.html new file mode 100644 index 000000000..047ef9786 --- /dev/null +++ b/LUA Api.html @@ -0,0 +1,2104 @@ + + + + + + +DFHack Lua API + + + +
                  +

                  DFHack Lua API

                  + + +

                  The current version of DFHack has extensive support for +the Lua scripting language, providing access to:

                  +
                    +
                  1. Raw data structures used by the game.
                  2. +
                  3. Many C++ functions for high-level access to these +structures, and interaction with dfhack itself.
                  4. +
                  5. Some functions exported by C++ plugins.
                  6. +
                  +

                  Lua code can be used both for writing scripts, which +are treated by DFHack command line prompt almost as +native C++ commands, and invoked by plugins written in c++.

                  +

                  This document describes native API available to Lua in detail. +It does not describe all of the utility functions +implemented by Lua files located in hack/lua/...

                  +
                  +

                  DF data structure wrapper

                  +

                  DF structures described by the xml files in library/xml are exported +to lua code as a tree of objects and functions under the df global, +which broadly maps to the df namespace in C++.

                  +

                  WARNING: The wrapper provides almost raw access to the memory +of the game, so mistakes in manipulating objects are as likely to +crash the game as equivalent plain C++ code would be. E.g. NULL +pointer access is safely detected, but dangling pointers aren't.

                  +

                  Objects managed by the wrapper can be broadly classified into the following groups:

                  +
                    +
                  1. Typed object pointers (references).

                    +

                    References represent objects in DF memory with a known type.

                    +

                    In addition to fields and methods defined by the wrapped type, +every reference has some built-in properties and methods.

                    +
                  2. +
                  3. Untyped pointers

                    +

                    Represented as lightuserdata.

                    +

                    In assignment to a pointer NULL can be represented either as +nil, or a NULL lightuserdata; reading a NULL pointer field +returns nil.

                    +
                  4. +
                  5. Named types

                    +

                    Objects in the df tree that represent identity of struct, class, +enum and bitfield types. They host nested named types, static +methods, builtin properties & methods, and, for enums and bitfields, +the bi-directional mapping between key names and values.

                    +
                  6. +
                  7. The global object

                    +

                    df.global corresponds to the df::global namespace, and +behaves as a mix between a named type and a reference, containing +both nested types and fields corresponding to global symbols.

                    +
                  8. +
                  +

                  In addition to the global object and top-level types the df +global also contains a few global builtin utility functions.

                  +
                  +

                  Typed object references

                  +

                  The underlying primitive lua object is userdata with a metatable. +Every structured field access produces a new userdata instance.

                  +

                  All typed objects have the following built-in features:

                  +
                    +
                  • ref1 == ref2, tostring(ref)

                    +

                    References implement equality by type & pointer value, and string conversion.

                    +
                  • +
                  • pairs(ref)

                    +

                    Returns an iterator for the sequence of actual C++ field names +and values. Fields are enumerated in memory order. Methods and +lua wrapper properties are not included in the iteration.

                    +

                    WARNING: a few of the data structures (like ui_look_list) +contain unions with pointers to different types with vtables. +Using pairs on such structs is an almost sure way to crash with +an access violation.

                    +
                  • +
                  • ref._kind

                    +

                    Returns one of: primitive, struct, container, +or bitfield, as appropriate for the referenced object.

                    +
                  • +
                  • ref._type

                    +

                    Returns the named type object or a string that represents +the referenced object type.

                    +
                  • +
                  • ref:sizeof()

                    +

                    Returns size, address

                    +
                  • +
                  • ref:new()

                    +

                    Allocates a new instance of the same type, and copies data +from the current object.

                    +
                  • +
                  • ref:delete()

                    +

                    Destroys the object with the C++ delete operator. +If destructor is not available, returns false.

                    +

                    WARNING: the lua reference object remains as a dangling +pointer, like a raw C++ pointer would.

                    +
                  • +
                  • ref:assign(object)

                    +

                    Assigns data from object to ref. Object must either be another +ref of a compatible type, or a lua table; in the latter case +special recursive assignment rules are applied.

                    +
                  • +
                  • ref:_displace(index[,step])

                    +

                    Returns a new reference with the pointer adjusted by index*step. +Step defaults to the natural object size.

                    +
                  • +
                  +
                  +

                  Primitive references

                  +

                  References of the _kind 'primitive' are used for objects +that don't fit any of the other reference types. Such +references can only appear as a value of a pointer field, +or as a result of calling the _field() method.

                  +

                  They behave as structs with one field value of the right type.

                  +

                  To make working with numeric buffers easier, they also allow +numeric indices. Note that other than excluding negative values +no bound checking is performed, since buffer length is not available. +Index 0 is equivalent to the value field.

                  +
                  +
                  +

                  Struct references

                  +

                  Struct references are used for class and struct objects.

                  +

                  They implement the following features:

                  +
                    +
                  • ref.field, ref.field = value

                    +

                    Valid fields of the structure may be accessed by subscript.

                    +

                    Primitive typed fields, i.e. numbers & strings, are converted +to/from matching lua values. The value of a pointer is a reference +to the target, or nil/NULL. Complex types are represented by +a reference to the field within the structure; unless recursive +lua table assignment is used, such fields can only be read.

                    +

                    NOTE: In case of inheritance, superclass fields have precedence +over the subclass, but fields shadowed in this way can still +be accessed as ref['subclasstype.field']. +This shadowing order is necessary because vtable-based classes +are automatically exposed in their exact type, and the reverse +rule would make access to superclass fields unreliable.

                    +
                  • +
                  • ref._field(field)

                    +

                    Returns a reference to a valid field. That is, unlike regular +subscript, it returns a reference to the field within the structure +even for primitive typed fields and pointers.

                    +
                  • +
                  • ref:vmethod(args...)

                    +

                    Named virtual methods are also exposed, subject to the same +shadowing rules.

                    +
                  • +
                  • pairs(ref)

                    +

                    Enumerates all real fields (but not methods) in memory +(= declaration) order.

                    +
                  • +
                  +
                  +
                  +

                  Container references

                  +

                  Containers represent vectors and arrays, possibly resizable.

                  +

                  A container field can associate an enum to the container +reference, which allows accessing elements using string keys +instead of numerical indices.

                  +

                  Implemented features:

                  +
                    +
                  • ref._enum

                    +

                    If the container has an associated enum, returns the matching +named type object.

                    +
                  • +
                  • #ref

                    +

                    Returns the length of the container.

                    +
                  • +
                  • ref[index]

                    +

                    Accesses the container element, using either a 0-based numerical +index, or, if an enum is associated, a valid enum key string.

                    +

                    Accessing an invalid index is an error, but some container types +may return a default value, or auto-resize instead for convenience. +Currently this relaxed mode is implemented by df-flagarray aka BitArray.

                    +
                  • +
                  • ref._field(index)

                    +

                    Like with structs, returns a pointer to the array element, if possible. +Flag and bit arrays cannot return such pointer, so it fails with an error.

                    +
                  • +
                  • pairs(ref), ipairs(ref)

                    +

                    If the container has no associated enum, both behave identically, +iterating over numerical indices in order. Otherwise, ipairs still +uses numbers, while pairs tries to substitute enum keys whenever +possible.

                    +
                  • +
                  • ref:resize(new_size)

                    +

                    Resizes the container if supported, or fails with an error.

                    +
                  • +
                  • ref:insert(index,item)

                    +

                    Inserts a new item at the specified index. To add at the end, +use #ref, or just '#' as index.

                    +
                  • +
                  • ref:erase(index)

                    +

                    Removes the element at the given valid index.

                    +
                  • +
                  +
                  +
                  +

                  Bitfield references

                  +

                  Bitfields behave like special fixed-size containers. +Consider them to be something in between structs and +fixed-size vectors.

                  +

                  The _enum property points to the bitfield type. +Numerical indices correspond to the shift value, +and if a subfield occupies multiple bits, the +ipairs order would have a gap.

                  +

                  Since currently there is no API to allocate a bitfield +object fully in GC-managed lua heap, consider using the +lua table assignment feature outlined below in order to +pass bitfield values to dfhack API functions that need +them, e.g. matinfo:matches{metal=true}.

                  +
                  +
                  +
                  +

                  Named types

                  +

                  Named types are exposed in the df tree with names identical +to the C++ version, except for the :: vs . difference.

                  +

                  All types and the global object have the following features:

                  +
                    +
                  • type._kind

                    +

                    Evaluates to one of struct-type, class-type, enum-type, +bitfield-type or global.

                    +
                  • +
                  • type._identity

                    +

                    Contains a lightuserdata pointing to the underlying +DFHack::type_instance object.

                    +
                  • +
                  +

                  Types excluding the global object also support:

                  +
                    +
                  • type:sizeof()

                    +

                    Returns the size of an object of the type.

                    +
                  • +
                  • type:new()

                    +

                    Creates a new instance of an object of the type.

                    +
                  • +
                  • type:is_instance(object)

                    +

                    Returns true if object is same or subclass type, or a reference +to an object of same or subclass type. It is permissible to pass +nil, NULL or non-wrapper value as object; in this case the +method returns nil.

                    +
                  • +
                  +

                  In addition to this, enum and bitfield types contain a +bi-directional mapping between key strings and values, and +also map _first_item and _last_item to the min and +max values.

                  +

                  Struct and class types with instance-vector attribute in the +xml have a type.find(key) function that wraps the find +method provided in C++.

                  +
                  +
                  +

                  Global functions

                  +

                  The df table itself contains the following functions and values:

                  +
                    +
                  • NULL, df.NULL

                    +

                    Contains the NULL lightuserdata.

                    +
                  • +
                  • df.isnull(obj)

                    +

                    Evaluates to true if obj is nil or NULL; false otherwise.

                    +
                  • +
                  • df.isvalid(obj[,allow_null])

                    +

                    For supported objects returns one of type, voidptr, ref.

                    +

                    If allow_null is true, and obj is nil or NULL, returns null.

                    +

                    Otherwise returns nil.

                    +
                  • +
                  • df.sizeof(obj)

                    +

                    For types and refs identical to obj:sizeof(). +For lightuserdata returns nil, address

                    +
                  • +
                  • df.new(obj), df.delete(obj), df.assign(obj, obj2)

                    +

                    Equivalent to using the matching methods of obj.

                    +
                  • +
                  • df._displace(obj,index[,step])

                    +

                    For refs equivalent to the method, but also works with +lightuserdata (step is mandatory then).

                    +
                  • +
                  • df.is_instance(type,obj)

                    +

                    Equivalent to the method, but also allows a reference as proxy for its type.

                    +
                  • +
                  • df.new(ptype[,count])

                    +

                    Allocate a new instance, or an array of built-in types. +The ptype argument is a string from the following list: +string, int8_t, uint8_t, int16_t, uint16_t, +int32_t, uint32_t, int64_t, uint64_t, bool, +float, double. All of these except string can be +used with the count argument to allocate an array.

                    +
                  • +
                  • df.reinterpret_cast(type,ptr)

                    +

                    Converts ptr to a ref of specified type. The type may be anything +acceptable to df.is_instance. Ptr may be nil, a ref, +a lightuserdata, or a number.

                    +

                    Returns nil if NULL, or a ref.

                    +
                  • +
                  +
                  +
                  +

                  Recursive table assignment

                  +

                  Recursive assignment is invoked when a lua table is assigned +to a C++ object or field, i.e. one of:

                  +
                    +
                  • ref:assign{...}
                  • +
                  • ref.field = {...}
                  • +
                  +

                  The general mode of operation is that all fields of the table +are assigned to the fields of the target structure, roughly +emulating the following code:

                  +
                  +function rec_assign(ref,table)
                  +    for key,value in pairs(table) do
                  +        ref[key] = value
                  +    end
                  +end
                  +
                  +

                  Since assigning a table to a field using = invokes the same +process, it is recursive.

                  +

                  There are however some variations to this process depending +on the type of the field being assigned to:

                  +
                    +
                  1. If the table contains an assign field, it is +applied first, using the ref:assign(value) method. +It is never assigned as a usual field.

                    +
                  2. +
                  3. When a table is assigned to a non-NULL pointer field +using the ref.field = {...} syntax, it is applied +to the target of the pointer instead.

                    +

                    If the pointer is NULL, the table is checked for a new field:

                    +
                      +
                    1. If it is nil or false, assignment fails with an error.
                    2. +
                    3. If it is true, the pointer is initialized with a newly +allocated object of the declared target type of the pointer.
                    4. +
                    5. Otherwise, table.new must be a named type, or an +object of a type compatible with the pointer. The pointer +is initialized with the result of calling table.new:new().
                    6. +
                    +

                    After this auto-vivification process, assignment proceeds +as if the pointer wasn't NULL.

                    +

                    Obviously, the new field inside the table is always skipped +during the actual per-field assignment processing.

                    +
                  4. +
                  5. If the target of the assignment is a container, a separate +rule set is used:

                    +
                      +
                    1. If the table contains neither assign nor resize +fields, it is interpreted as an ordinary 1-based lua +array. The container is resized to the #-size of the +table, and elements are assigned in numeric order:

                      +
                      +ref:resize(#table);
                      +for i=1,#table do ref[i-1] = table[i] end
                      +
                      +
                    2. +
                    3. Otherwise, resize must be true, false, or +an explicit number. If it is not false, the container +is resized. After that the usual struct-like 'pairs' +assignment is performed.

                      +

                      In case resize is true, the size is computed +by scanning the table for the largest numeric key.

                      +
                    4. +
                    +

                    This means that in order to reassign only one element of +a container using this system, it is necessary to use:

                    +
                    +{ resize=false, [idx]=value }
                    +
                    +
                  6. +
                  +

                  Since nil inside a table is indistinguishable from missing key, +it is necessary to use df.NULL as a null pointer value.

                  +

                  This system is intended as a way to define a nested object +tree using pure lua data structures, and then materialize it in +C++ memory in one go. Note that if pointer auto-vivification +is used, an error in the middle of the recursive walk would +not destroy any objects allocated in this way, so the user +should be prepared to catch the error and do the necessary +cleanup.

                  +
                  +
                  +
                  +

                  DFHack API

                  +

                  DFHack utility functions are placed in the dfhack global tree.

                  +
                  +

                  Native utilities

                  +
                  +

                  Input & Output

                  +
                    +
                  • dfhack.print(args...)

                    +

                    Output tab-separated args as standard lua print would do, +but without a newline.

                    +
                  • +
                  • print(args...), dfhack.println(args...)

                    +

                    A replacement of the standard library print function that +works with DFHack output infrastructure.

                    +
                  • +
                  • dfhack.printerr(args...)

                    +

                    Same as println; intended for errors. Uses red color and logs to stderr.log.

                    +
                  • +
                  • dfhack.color([color])

                    +

                    Sets the current output color. If color is nil or -1, resets to default. +Returns the previous color value.

                    +
                  • +
                  • dfhack.is_interactive()

                    +

                    Checks if the thread can access the interactive console and returns true or false.

                    +
                  • +
                  • dfhack.lineedit([prompt[,history_filename]])

                    +

                    If the thread owns the interactive console, shows a prompt +and returns the entered string. Otherwise returns nil, error.

                    +

                    Depending on the context, this function may actually yield the +running coroutine and let the C++ code release the core suspend +lock. Using an explicit dfhack.with_suspend will prevent +this, forcing the function to block on input with lock held.

                    +
                  • +
                  • dfhack.interpreter([prompt[,history_filename[,env]]])

                    +

                    Starts an interactive lua interpreter, using the specified prompt +string, global environment and command-line history file.

                    +

                    If the interactive console is not accessible, returns nil, error.

                    +
                  • +
                  +
                  +
                  +

                  Exception handling

                  +
                    +
                  • dfhack.error(msg[,level[,verbose]])

                    +

                    Throws a dfhack exception object with location and stack trace. +The verbose parameter controls whether the trace is printed by default.

                    +
                  • +
                  • qerror(msg[,level])

                    +

                    Calls dfhack.error() with verbose being false. Intended to +be used for user-caused errors in scripts, where stack traces are not +desirable.

                    +
                  • +
                  • dfhack.pcall(f[,args...])

                    +

                    Invokes f via xpcall, using an error function that attaches +a stack trace to the error. The same function is used by SafeCall +in C++, and dfhack.safecall.

                    +
                  • +
                  • safecall(f[,args...]), dfhack.safecall(f[,args...])

                    +

                    Just like pcall, but also prints the error using printerr before +returning. Intended as a convenience function.

                    +
                  • +
                  • dfhack.saferesume(coroutine[,args...])

                    +

                    Compares to coroutine.resume like dfhack.safecall vs pcall.

                    +
                  • +
                  • dfhack.exception

                    +

                    Metatable of error objects used by dfhack. The objects have the +following properties:

                    +
                    +
                    err.where
                    +

                    The location prefix string, or nil.

                    +
                    +
                    err.message
                    +

                    The base message string.

                    +
                    +
                    err.stacktrace
                    +

                    The stack trace string, or nil.

                    +
                    +
                    err.cause
                    +

                    A different exception object, or nil.

                    +
                    +
                    err.thread
                    +

                    The coroutine that has thrown the exception.

                    +
                    +
                    err.verbose
                    +

                    Boolean, or nil; specifies if where and stacktrace should be printed.

                    +
                    +
                    tostring(err), or err:tostring([verbose])
                    +

                    Converts the exception to string.

                    +
                    +
                    +
                  • +
                  • dfhack.exception.verbose

                    +

                    The default value of the verbose argument of err:tostring().

                    +
                  • +
                  +
                  +
                  +

                  Miscellaneous

                  +
                    +
                  • dfhack.VERSION

                    +

                    DFHack version string constant.

                    +
                  • +
                  • dfhack.curry(func,args...), or curry(func,args...)

                    +

                    Returns a closure that invokes the function with args combined +both from the curry call and the closure call itself. I.e. +curry(func,a,b)(c,d) equals func(a,b,c,d).

                    +
                  • +
                  +
                  +
                  +

                  Locking and finalization

                  +
                    +
                  • dfhack.with_suspend(f[,args...])

                    +

                    Calls f with arguments after grabbing the DF core suspend lock. +Suspending is necessary for accessing a consistent state of DF memory.

                    +

                    Returned values and errors are propagated through after releasing +the lock. It is safe to nest suspends.

                    +

                    Every thread is allowed only one suspend per DF frame, so it is best +to group operations together in one big critical section. A plugin +can choose to run all lua code inside a C++-side suspend lock.

                    +
                  • +
                  • dfhack.call_with_finalizer(num_cleanup_args,always,cleanup_fn[,cleanup_args...],fn[,args...])

                    +

                    Invokes fn with args, and after it returns or throws an +error calls cleanup_fn with cleanup_args. Any return values from +fn are propagated, and errors are re-thrown.

                    +

                    The num_cleanup_args integer specifies the number of cleanup_args, +and the always boolean specifies if cleanup should be called in any case, +or only in case of an error.

                    +
                  • +
                  • dfhack.with_finalize(cleanup_fn,fn[,args...])

                    +

                    Calls fn with arguments, then finalizes with cleanup_fn. +Implemented using call_with_finalizer(0,true,...).

                    +
                  • +
                  • dfhack.with_onerror(cleanup_fn,fn[,args...])

                    +

                    Calls fn with arguments, then finalizes with cleanup_fn on any thrown error. +Implemented using call_with_finalizer(0,false,...).

                    +
                  • +
                  • dfhack.with_temp_object(obj,fn[,args...])

                    +

                    Calls fn(obj,args...), then finalizes with obj:delete().

                    +
                  • +
                  +
                  +
                  +

                  Persistent configuration storage

                  +

                  This api is intended for storing configuration options in the world itself. +It probably should be restricted to data that is world-dependent.

                  +

                  Entries are identified by a string key, but it is also possible to manage +multiple entries with the same key; their identity is determined by entry_id. +Every entry has a mutable string value, and an array of 7 mutable ints.

                  +
                    +
                  • dfhack.persistent.get(key), entry:get()

                    +

                    Retrieves a persistent config record with the given string key, +or refreshes an already retrieved entry. If there are multiple +entries with the same key, it is undefined which one is retrieved +by the first version of the call.

                    +

                    Returns entry, or nil if not found.

                    +
                  • +
                  • dfhack.persistent.delete(key), entry:delete()

                    +

                    Removes an existing entry. Returns true if succeeded.

                    +
                  • +
                  • dfhack.persistent.get_all(key[,match_prefix])

                    +

                    Retrieves all entries with the same key, or starting with key..'/'. +Calling get_all('',true) will match all entries.

                    +

                    If none found, returns nil; otherwise returns an array of entries.

                    +
                  • +
                  • dfhack.persistent.save({key=str1, ...}[,new]), entry:save([new])

                    +

                    Saves changes in an entry, or creates a new one. Passing true as +new forces creation of a new entry even if one already exists; +otherwise the existing one is simply updated. +Returns entry, did_create_new

                    +
                  • +
                  +

                  Since the data is hidden in data structures owned by the DF world, +and automatically stored in the save game, these save and retrieval +functions can just copy values in memory without doing any actual I/O. +However, currently every entry has a 180+-byte dead-weight overhead.

                  +
                  +
                  +

                  Material info lookup

                  +

                  A material info record has fields:

                  +
                    +
                  • type, index, material

                    +

                    DF material code pair, and a reference to the material object.

                    +
                  • +
                  • mode

                    +

                    One of 'builtin', 'inorganic', 'plant', 'creature'.

                    +
                  • +
                  • inorganic, plant, creature

                    +

                    If the material is of the matching type, contains a reference to the raw object.

                    +
                  • +
                  • figure

                    +

                    For a specific creature material contains a ref to the historical figure.

                    +
                  • +
                  +

                  Functions:

                  +
                    +
                  • dfhack.matinfo.decode(type,index)

                    +

                    Looks up material info for the given number pair; if not found, returs nil.

                    +
                  • +
                  • ....decode(matinfo), ....decode(item), ....decode(obj)

                    +

                    Uses matinfo.type/matinfo.index, item getter vmethods, +or obj.mat_type/obj.mat_index to get the code pair.

                    +
                  • +
                  • dfhack.matinfo.find(token[,token...])

                    +

                    Looks up material by a token string, or a pre-split string token sequence.

                    +
                  • +
                  • dfhack.matinfo.getToken(...), info:getToken()

                    +

                    Applies decode and constructs a string token.

                    +
                  • +
                  • info:toString([temperature[,named]])

                    +

                    Returns the human-readable name at the given temperature.

                    +
                  • +
                  • info:getCraftClass()

                    +

                    Returns the classification used for craft skills.

                    +
                  • +
                  • info:matches(obj)

                    +

                    Checks if the material matches job_material_category or job_item. +Accept dfhack_material_category auto-assign table.

                    +
                  • +
                  +
                  +
                  +
                  +

                  C++ function wrappers

                  +

                  Thin wrappers around C++ functions, similar to the ones for virtual methods. +One notable difference is that these explicit wrappers allow argument count +adjustment according to the usual lua rules, so trailing false/nil arguments +can be omitted.

                  +
                    +
                  • dfhack.getOSType()

                    +

                    Returns the OS type string from symbols.xml.

                    +
                  • +
                  • dfhack.getDFVersion()

                    +

                    Returns the DF version string from symbols.xml.

                    +
                  • +
                  • dfhack.getDFPath()

                    +

                    Returns the DF directory path.

                    +
                  • +
                  • dfhack.getHackPath()

                    +

                    Returns the dfhack directory path, i.e. ".../df/hack/".

                    +
                  • +
                  • dfhack.getTickCount()

                    +

                    Returns the tick count in ms, exactly as DF ui uses.

                    +
                  • +
                  • dfhack.isWorldLoaded()

                    +

                    Checks if the world is loaded.

                    +
                  • +
                  • dfhack.isMapLoaded()

                    +

                    Checks if the world and map are loaded.

                    +
                  • +
                  • dfhack.TranslateName(name[,in_english,only_last_name])

                    +

                    Convert a language_name or only the last name part to string.

                    +
                  • +
                  +
                  +

                  Gui module

                  +
                    +
                  • dfhack.gui.getCurViewscreen([skip_dismissed])

                    +

                    Returns the topmost viewscreen. If skip_dismissed is true, +ignores screens already marked to be removed.

                    +
                  • +
                  • dfhack.gui.getFocusString(viewscreen)

                    +

                    Returns a string representation of the current focus position +in the ui. The string has a "screen/foo/bar/baz..." format.

                    +
                  • +
                  • dfhack.gui.getCurFocus([skip_dismissed])

                    +

                    Returns the focus string of the current viewscreen.

                    +
                  • +
                  • dfhack.gui.getSelectedWorkshopJob([silent])

                    +

                    When a job is selected in 'q' mode, returns the job, else +prints error unless silent and returns nil.

                    +
                  • +
                  • dfhack.gui.getSelectedJob([silent])

                    +

                    Returns the job selected in a workshop or unit/jobs screen.

                    +
                  • +
                  • dfhack.gui.getSelectedUnit([silent])

                    +

                    Returns the unit selected via 'v', 'k', unit/jobs, or +a full-screen item view of a cage or suchlike.

                    +
                  • +
                  • dfhack.gui.getSelectedItem([silent])

                    +

                    Returns the item selected via 'v' ->inventory, 'k', 't', or +a full-screen item view of a container. Note that in the +last case, the highlighted contained item is returned, not +the container itself.

                    +
                  • +
                  • dfhack.gui.getSelectedBuilding([silent])

                    +

                    Returns the building selected via 'q', 't', 'k' or 'i'.

                    +
                  • +
                  • dfhack.gui.showAnnouncement(text,color[,is_bright])

                    +

                    Adds a regular announcement with given text, color, and brightness. +The is_bright boolean actually seems to invert the brightness.

                    +
                  • +
                  • dfhack.gui.showZoomAnnouncement(type,pos,text,color[,is_bright])

                    +

                    Like above, but also specifies a position you can zoom to from the announcement menu.

                    +
                  • +
                  • dfhack.gui.showPopupAnnouncement(text,color[,is_bright])

                    +

                    Pops up a titan-style modal announcement window.

                    +
                  • +
                  • dfhack.gui.showAutoAnnouncement(type,pos,text,color[,is_bright])

                    +

                    Uses the type to look up options from announcements.txt, and calls the +above operations accordingly. If enabled, pauses and zooms to position.

                    +
                  • +
                  +
                  +
                  +

                  Job module

                  +
                    +
                  • dfhack.job.cloneJobStruct(job)

                    +

                    Creates a deep copy of the given job.

                    +
                  • +
                  • dfhack.job.printJobDetails(job)

                    +

                    Prints info about the job.

                    +
                  • +
                  • dfhack.job.printItemDetails(jobitem,idx)

                    +

                    Prints info about the job item.

                    +
                  • +
                  • dfhack.job.getHolder(job)

                    +

                    Returns the building holding the job.

                    +
                  • +
                  • dfhack.job.getWorker(job)

                    +

                    Returns the unit performing the job.

                    +
                  • +
                  • dfhack.job.checkBuildingsNow()

                    +

                    Instructs the game to check buildings for jobs next frame and assign workers.

                    +
                  • +
                  • dfhack.job.checkDesignationsNow()

                    +

                    Instructs the game to check designations for jobs next frame and assign workers.

                    +
                  • +
                  • dfhack.job.is_equal(job1,job2)

                    +

                    Compares important fields in the job and nested item structures.

                    +
                  • +
                  • dfhack.job.is_item_equal(job_item1,job_item2)

                    +

                    Compares important fields in the job item structures.

                    +
                  • +
                  • dfhack.job.listNewlyCreated(first_id)

                    +

                    Returns the current value of df.global.job_next_id, and +if there are any jobs with first_id <= id < job_next_id, +a lua list containing them.

                    +
                  • +
                  +
                  +
                  +

                  Units module

                  +
                    +
                  • dfhack.units.getPosition(unit)

                    +

                    Returns true x,y,z of the unit, or nil if invalid; may be not equal to unit.pos if caged.

                    +
                  • +
                  • dfhack.units.getContainer(unit)

                    +

                    Returns the container (cage) item or nil.

                    +
                  • +
                  • dfhack.units.setNickname(unit,nick)

                    +

                    Sets the unit's nickname properly.

                    +
                  • +
                  • dfhack.units.getVisibleName(unit)

                    +

                    Returns the language_name object visible in game, accounting for false identities.

                    +
                  • +
                  • dfhack.units.getIdentity(unit)

                    +

                    Returns the false identity of the unit if it has one, or nil.

                    +
                  • +
                  • dfhack.units.getNemesis(unit)

                    +

                    Returns the nemesis record of the unit if it has one, or nil.

                    +
                  • +
                  • dfhack.units.isHidingCurse(unit)

                    +

                    Checks if the unit hides improved attributes from its curse.

                    +
                  • +
                  • dfhack.units.getPhysicalAttrValue(unit, attr_type)

                    +
                  • +
                  • dfhack.units.getMentalAttrValue(unit, attr_type)

                    +

                    Computes the effective attribute value, including curse effect.

                    +
                  • +
                  • dfhack.units.isCrazed(unit)

                    +
                  • +
                  • dfhack.units.isOpposedToLife(unit)

                    +
                  • +
                  • dfhack.units.hasExtravision(unit)

                    +
                  • +
                  • dfhack.units.isBloodsucker(unit)

                    +

                    Simple checks of caste attributes that can be modified by curses.

                    +
                  • +
                  • dfhack.units.getMiscTrait(unit, type[, create])

                    +

                    Finds (or creates if requested) a misc trait object with the given id.

                    +
                  • +
                  • dfhack.units.isDead(unit)

                    +

                    The unit is completely dead and passive, or a ghost.

                    +
                  • +
                  • dfhack.units.isAlive(unit)

                    +

                    The unit isn't dead or undead.

                    +
                  • +
                  • dfhack.units.isSane(unit)

                    +

                    The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf.

                    +
                  • +
                  • dfhack.units.isDwarf(unit)

                    +

                    The unit is of the correct race of the fortress.

                    +
                  • +
                  • dfhack.units.isCitizen(unit)

                    +

                    The unit is an alive sane citizen of the fortress; wraps the +same checks the game uses to decide game-over by extinction.

                    +
                  • +
                  • dfhack.units.getAge(unit[,true_age])

                    +

                    Returns the age of the unit in years as a floating-point value. +If true_age is true, ignores false identities.

                    +
                  • +
                  • dfhack.units.getNominalSkill(unit, skill[, use_rust])

                    +

                    Retrieves the nominal skill level for the given unit. If use_rust +is true, subtracts the rust penalty.

                    +
                  • +
                  • dfhack.units.getEffectiveSkill(unit, skill)

                    +

                    Computes the effective rating for the given skill, taking into account exhaustion, pain etc.

                    +
                  • +
                  • dfhack.units.computeMovementSpeed(unit)

                    +

                    Computes number of frames * 100 it takes the unit to move in its current state of mind and body.

                    +
                  • +
                  • dfhack.units.getNoblePositions(unit)

                    +

                    Returns a list of tables describing noble position assignments, or nil. +Every table has fields entity, assignment and position.

                    +
                  • +
                  • dfhack.units.getProfessionName(unit[,ignore_noble,plural])

                    +

                    Retrieves the profession name using custom profession, noble assignments +or raws. The ignore_noble boolean disables the use of noble positions.

                    +
                  • +
                  • dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])

                    +

                    Retrieves the profession name for the given race/caste using raws.

                    +
                  • +
                  • dfhack.units.getProfessionColor(unit[,ignore_noble])

                    +

                    Retrieves the color associated with the profession, using noble assignments +or raws. The ignore_noble boolean disables the use of noble positions.

                    +
                  • +
                  • dfhack.units.getCasteProfessionColor(race,caste,prof_id)

                    +

                    Retrieves the profession color for the given race/caste using raws.

                    +
                  • +
                  +
                  +
                  +

                  Items module

                  +
                    +
                  • dfhack.items.getPosition(item)

                    +

                    Returns true x,y,z of the item, or nil if invalid; may be not equal to item.pos if in inventory.

                    +
                  • +
                  • dfhack.items.getDescription(item, type[, decorate])

                    +

                    Returns the string description of the item, as produced by the getItemDescription +method. If decorate is true, also adds markings for quality and improvements.

                    +
                  • +
                  • dfhack.items.getGeneralRef(item, type)

                    +

                    Searches for a general_ref with the given type.

                    +
                  • +
                  • dfhack.items.getSpecificRef(item, type)

                    +

                    Searches for a specific_ref with the given type.

                    +
                  • +
                  • dfhack.items.getOwner(item)

                    +

                    Returns the owner unit or nil.

                    +
                  • +
                  • dfhack.items.setOwner(item,unit)

                    +

                    Replaces the owner of the item. If unit is nil, removes ownership. +Returns false in case of error.

                    +
                  • +
                  • dfhack.items.getContainer(item)

                    +

                    Returns the container item or nil.

                    +
                  • +
                  • dfhack.items.getContainedItems(item)

                    +

                    Returns a list of items contained in this one.

                    +
                  • +
                  • dfhack.items.moveToGround(item,pos)

                    +

                    Move the item to the ground at position. Returns false if impossible.

                    +
                  • +
                  • dfhack.items.moveToContainer(item,container)

                    +

                    Move the item to the container. Returns false if impossible.

                    +
                  • +
                  • dfhack.items.moveToBuilding(item,building,use_mode)

                    +

                    Move the item to the building. Returns false if impossible.

                    +
                  • +
                  • dfhack.items.moveToInventory(item,unit,use_mode,body_part)

                    +

                    Move the item to the unit inventory. Returns false if impossible.

                    +
                  • +
                  • dfhack.items.remove(item[, no_uncat])

                    +

                    Removes the item, and marks it for garbage collection unless no_uncat is true.

                    +
                  • +
                  • dfhack.items.makeProjectile(item)

                    +

                    Turns the item into a projectile, and returns the new object, or nil if impossible.

                    +
                  • +
                  +
                  +
                  +

                  Maps module

                  +
                    +
                  • dfhack.maps.getSize()

                    +

                    Returns map size in blocks: x, y, z

                    +
                  • +
                  • dfhack.maps.getTileSize()

                    +

                    Returns map size in tiles: x, y, z

                    +
                  • +
                  • dfhack.maps.getBlock(x,y,z)

                    +

                    Returns a map block object for given x,y,z in local block coordinates.

                    +
                  • +
                  • dfhack.maps.isValidTilePos(coords), or isValidTilePos(x,y,z)``

                    +

                    Checks if the given df::coord or x,y,z in local tile coordinates are valid.

                    +
                  • +
                  • dfhack.maps.getTileBlock(coords), or getTileBlock(x,y,z)

                    +

                    Returns a map block object for given df::coord or x,y,z in local tile coordinates.

                    +
                  • +
                  • dfhack.maps.ensureTileBlock(coords), or ensureTileBlock(x,y,z)

                    +

                    Like getTileBlock, but if the block is not allocated, try creating it.

                    +
                  • +
                  • dfhack.maps.getRegionBiome(region_coord2d), or getRegionBiome(x,y)

                    +

                    Returns the biome info struct for the given global map region.

                    +
                  • +
                  • dfhack.maps.enableBlockUpdates(block[,flow,temperature])

                    +

                    Enables updates for liquid flow or temperature, unless already active.

                    +
                  • +
                  • dfhack.maps.spawnFlow(pos,type,mat_type,mat_index,dimension)

                    +

                    Spawns a new flow (i.e. steam/mist/dust/etc) at the given pos, and with +the given parameters. Returns it, or nil if unsuccessful.

                    +
                  • +
                  • dfhack.maps.getGlobalInitFeature(index)

                    +

                    Returns the global feature object with the given index.

                    +
                  • +
                  • dfhack.maps.getLocalInitFeature(region_coord2d,index)

                    +

                    Returns the local feature object with the given region coords and index.

                    +
                  • +
                  • dfhack.maps.getTileBiomeRgn(coords), or getTileBiomeRgn(x,y,z)

                    +

                    Returns x, y for use with getRegionBiome.

                    +
                  • +
                  • dfhack.maps.canWalkBetween(pos1, pos2)

                    +

                    Checks if a dwarf may be able to walk between the two tiles, +using a pathfinding cache maintained by the game. Note that +this cache is only updated when the game is unpaused, and thus +can get out of date if doors are forbidden or unforbidden, or +tools like liquids or tiletypes are used. It also cannot possibly +take into account anything that depends on the actual units, like +burrows, or the presence of invaders.

                    +
                  • +
                  +
                  +
                  +

                  Burrows module

                  +
                    +
                  • dfhack.burrows.findByName(name)

                    +

                    Returns the burrow pointer or nil.

                    +
                  • +
                  • dfhack.burrows.clearUnits(burrow)

                    +

                    Removes all units from the burrow.

                    +
                  • +
                  • dfhack.burrows.isAssignedUnit(burrow,unit)

                    +

                    Checks if the unit is in the burrow.

                    +
                  • +
                  • dfhack.burrows.setAssignedUnit(burrow,unit,enable)

                    +

                    Adds or removes the unit from the burrow.

                    +
                  • +
                  • dfhack.burrows.clearTiles(burrow)

                    +

                    Removes all tiles from the burrow.

                    +
                  • +
                  • dfhack.burrows.listBlocks(burrow)

                    +

                    Returns a table of map block pointers.

                    +
                  • +
                  • dfhack.burrows.isAssignedTile(burrow,tile_coord)

                    +

                    Checks if the tile is in burrow.

                    +
                  • +
                  • dfhack.burrows.setAssignedTile(burrow,tile_coord,enable)

                    +

                    Adds or removes the tile from the burrow. Returns false if invalid coords.

                    +
                  • +
                  • dfhack.burrows.isAssignedBlockTile(burrow,block,x,y)

                    +

                    Checks if the tile within the block is in burrow.

                    +
                  • +
                  • dfhack.burrows.setAssignedBlockTile(burrow,block,x,y,enable)

                    +

                    Adds or removes the tile from the burrow. Returns false if invalid coords.

                    +
                  • +
                  +
                  +
                  +

                  Buildings module

                  +
                    +
                  • dfhack.buildings.setOwner(item,unit)

                    +

                    Replaces the owner of the building. If unit is nil, removes ownership. +Returns false in case of error.

                    +
                  • +
                  • dfhack.buildings.getSize(building)

                    +

                    Returns width, height, centerx, centery.

                    +
                  • +
                  • dfhack.buildings.findAtTile(pos), or findAtTile(x,y,z)

                    +

                    Scans the buildings for the one located at the given tile. +Does not work on civzones. Warning: linear scan if the map +tile indicates there are buildings at it.

                    +
                  • +
                  • dfhack.buildings.findCivzonesAt(pos), or findCivzonesAt(x,y,z)

                    +

                    Scans civzones, and returns a lua sequence of those that touch +the given tile, or nil if none.

                    +
                  • +
                  • dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)

                    +

                    Computes correct dimensions for the specified building type and orientation, +using width and height for flexible dimensions. +Returns is_flexible, width, height, center_x, center_y.

                    +
                  • +
                  • dfhack.buildings.checkFreeTiles(pos,size[,extents,change_extents,allow_occupied])

                    +

                    Checks if the rectangle defined by pos and size, and possibly extents, +can be used for placing a building. If change_extents is true, bad tiles +are removed from extents. If allow_occupied, the occupancy test is skipped.

                    +
                  • +
                  • dfhack.buildings.countExtentTiles(extents,defval)

                    +

                    Returns the number of tiles included by extents, or defval.

                    +
                  • +
                  • dfhack.buildings.containsTile(building, x, y[, room])

                    +

                    Checks if the building contains the specified tile, either directly, or as room.

                    +
                  • +
                  • dfhack.buildings.hasSupport(pos,size)

                    +

                    Checks if a bridge constructed at specified position would have +support from terrain, and thus won't collapse if retracted.

                    +
                  • +
                  +

                  Low-level building creation functions;

                  +
                    +
                  • dfhack.buildings.allocInstance(pos, type, subtype, custom)

                    +

                    Creates a new building instance of given type, subtype and custom type, +at specified position. Returns the object, or nil in case of an error.

                    +
                  • +
                  • dfhack.buildings.setSize(building, width, height, direction)

                    +

                    Configures an object returned by allocInstance, using specified +parameters wherever appropriate. If the building has fixed size along +any dimension, the corresponding input parameter will be ignored. +Returns false if the building cannot be placed, or true, width, +height, rect_area, true_area. Returned width and height are the +final values used by the building; true_area is less than rect_area +if any tiles were removed from designation.

                    +
                  • +
                  • dfhack.buildings.constructAbstract(building)

                    +

                    Links a fully configured object created by allocInstance into the +world. The object must be an abstract building, i.e. a stockpile or civzone. +Returns true, or false if impossible.

                    +
                  • +
                  • dfhack.buildings.constructWithItems(building, items)

                    +

                    Links a fully configured object created by allocInstance into the +world for construction, using a list of specific items as material. +Returns true, or false if impossible.

                    +
                  • +
                  • dfhack.buildings.constructWithFilters(building, job_items)

                    +

                    Links a fully configured object created by allocInstance into the +world for construction, using a list of job_item filters as inputs. +Returns true, or false if impossible. Filter objects are claimed +and possibly destroyed in any case. +Use a negative quantity field value to auto-compute the amount +from the size of the building.

                    +
                  • +
                  • dfhack.buildings.deconstruct(building)

                    +

                    Destroys the building, or queues a deconstruction job. +Returns true if the building was destroyed and deallocated immediately.

                    +
                  • +
                  +

                  More high-level functions are implemented in lua and can be loaded by +require('dfhack.buildings'). See hack/lua/dfhack/buildings.lua.

                  +

                  Among them are:

                  +
                    +
                  • dfhack.buildings.getFiltersByType(argtable,type,subtype,custom)

                    +

                    Returns a sequence of lua structures, describing input item filters +suitable for the specified building type, or nil if unknown or invalid. +The returned sequence is suitable for use as the job_items argument +of constructWithFilters. +Uses tables defined in buildings.lua.

                    +

                    Argtable members material (the default name), bucket, barrel, +chain, mechanism, screw, pipe, anvil, weapon are used to +augment the basic attributes with more detailed information if the +building has input items with the matching name (see the tables for naming details). +Note that it is impossible to override any properties this way, only supply those that +are not mentioned otherwise; one exception is that flags2.non_economic +is automatically cleared if an explicit material is specified.

                    +
                  • +
                  • dfhack.buildings.constructBuilding{...}

                    +

                    Creates a building in one call, using options contained +in the argument table. Returns the building, or nil, error.

                    +

                    NOTE: Despite the name, unless the building is abstract, +the function creates it in an 'unconstructed' stage, with +a queued in-game job that will actually construct it. I.e. +the function replicates programmatically what can be done +through the construct building menu in the game ui, except +that it does less environment constraint checking.

                    +

                    The following options can be used:

                    +
                      +
                    • pos = coordinates, or x = ..., y = ..., z = ...

                      +

                      Mandatory. Specifies the left upper corner of the building.

                      +
                    • +
                    • type = df.building_type.FOO, subtype = ..., custom = ...

                      +

                      Mandatory. Specifies the type of the building. Obviously, subtype +and custom are only expected if the type requires them.

                      +
                    • +
                    • fields = { ... }

                      +

                      Initializes fields of the building object after creation with df.assign.

                      +
                    • +
                    • width = ..., height = ..., direction = ...

                      +

                      Sets size and orientation of the building. If it is +fixed-size, specified dimensions are ignored.

                      +
                    • +
                    • full_rectangle = true

                      +

                      For buildings like stockpiles or farm plots that can normally +accomodate individual tile exclusion, forces an error if any +tiles within the specified width*height are obstructed.

                      +
                    • +
                    • items = { item, item ... }, or filters = { {...}, {...}... }

                      +

                      Specifies explicit items or item filters to use in construction. +It is the job of the user to ensure they are correct for the building type.

                      +
                    • +
                    • abstract = true

                      +

                      Specifies that the building is abstract and does not require construction. +Required for stockpiles and civzones; an error otherwise.

                      +
                    • +
                    • material = {...}, mechanism = {...}, ...

                      +

                      If none of items, filter, or abstract is used, +the function uses getFiltersByType to compute the input +item filters, and passes the argument table through. If no filters +can be determined this way, constructBuilding throws an error.

                      +
                    • +
                    +
                  • +
                  +
                  +
                  +

                  Constructions module

                  +
                    +
                  • dfhack.constructions.designateNew(pos,type,item_type,mat_index)

                    +

                    Designates a new construction at given position. If there already is +a planned but not completed construction there, changes its type. +Returns true, or false if obstructed. +Note that designated constructions are technically buildings.

                    +
                  • +
                  • dfhack.constructions.designateRemove(pos), or designateRemove(x,y,z)

                    +

                    If there is a construction or a planned construction at the specified +coordinates, designates it for removal, or instantly cancels the planned one. +Returns true, was_only_planned if removed; or false if none found.

                    +
                  • +
                  +
                  +
                  +

                  Screen API

                  +

                  The screen module implements support for drawing to the tiled screen of the game. +Note that drawing only has any effect when done from callbacks, so it can only +be feasibly used in the core context.

                  +

                  Basic painting functions:

                  +
                    +
                  • dfhack.screen.getWindowSize()

                    +

                    Returns width, height of the screen.

                    +
                  • +
                  • dfhack.screen.getMousePos()

                    +

                    Returns x,y of the tile the mouse is over.

                    +
                  • +
                  • dfhack.screen.inGraphicsMode()

                    +

                    Checks if [GRAPHICS:YES] was specified in init.

                    +
                  • +
                  • dfhack.screen.paintTile(pen,x,y[,char,tile])

                    +

                    Paints a tile using given parameters. Pen is a table with following possible fields:

                    +
                    +
                    ch
                    +

                    Provides the ordinary tile character, as either a 1-character string or a number. +Can be overridden with the char function parameter.

                    +
                    +
                    fg
                    +

                    Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).

                    +
                    +
                    bg
                    +

                    Background color for the ordinary tile. Defaults to COLOR_BLACK (0).

                    +
                    +
                    bold
                    +

                    Bright/bold text flag. If nil, computed based on (fg & 8); fg is masked to 3 bits. +Otherwise should be true/false.

                    +
                    +
                    tile
                    +

                    Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.

                    +
                    +
                    tile_color = true
                    +

                    Specifies that the tile should be shaded with fg/bg.

                    +
                    +
                    tile_fg, tile_bg
                    +

                    If specified, overrides tile_color and supplies shading colors directly.

                    +
                    +
                    +

                    Returns false if coordinates out of bounds, or other error.

                    +
                  • +
                  • dfhack.screen.readTile(x,y)

                    +

                    Retrieves the contents of the specified tile from the screen buffers. +Returns a pen, or nil if invalid or TrueType.

                    +
                  • +
                  • dfhack.screen.paintString(pen,x,y,text)

                    +

                    Paints the string starting at x,y. Uses the string characters +in sequence to override the ch field of pen.

                    +

                    Returns true if painting at least one character succeeded.

                    +
                  • +
                  • dfhack.screen.fillRect(pen,x1,y1,x2,y2)

                    +

                    Fills the rectangle specified by the coordinates with the given pen. +Returns true if painting at least one character succeeded.

                    +
                  • +
                  • dfhack.screen.findGraphicsTile(pagename,x,y)

                    +

                    Finds a tile from a graphics set (i.e. the raws used for creatures), +if in graphics mode and loaded.

                    +

                    Returns: tile, tile_grayscale, or nil if not found. +The values can then be used for the tile field of pen structures.

                    +
                  • +
                  • dfhack.screen.clear()

                    +

                    Fills the screen with blank background.

                    +
                  • +
                  • dfhack.screen.invalidate()

                    +

                    Requests repaint of the screen by setting a flag. Unlike other +functions in this section, this may be used at any time.

                    +
                  • +
                  +

                  In order to actually be able to paint to the screen, it is necessary +to create and register a viewscreen (basically a modal dialog) with +the game.

                  +

                  NOTE: As a matter of policy, in order to avoid user confusion, all +interface screens added by dfhack should bear the "DFHack" signature.

                  +

                  Screens are managed with the following functions:

                  +
                    +
                  • dfhack.screen.show(screen[,below])

                    +

                    Displays the given screen, possibly placing it below a different one. +The screen must not be already shown. Returns true if success.

                    +
                  • +
                  • dfhack.screen.dismiss(screen[,to_first])

                    +

                    Marks the screen to be removed when the game enters its event loop. +If to_first is true, all screens up to the first one will be deleted.

                    +
                  • +
                  • dfhack.screen.isDismissed(screen)

                    +

                    Checks if the screen is already marked for removal.

                    +
                  • +
                  +

                  Apart from a native viewscreen object, these functions accept a table +as a screen. In this case, show creates a new native viewscreen +that delegates all processing to methods stored in that table.

                  +

                  NOTE: Lua-implemented screens are only supported in the core context.

                  +

                  Supported callbacks and fields are:

                  +
                    +
                  • screen._native

                    +

                    Initialized by show with a reference to the backing viewscreen +object, and removed again when the object is deleted.

                    +
                  • +
                  • function screen:onShow()

                    +

                    Called by dfhack.screen.show if successful.

                    +
                  • +
                  • function screen:onDismiss()

                    +

                    Called by dfhack.screen.dismiss if successful.

                    +
                  • +
                  • function screen:onDestroy()

                    +

                    Called from the destructor when the viewscreen is deleted.

                    +
                  • +
                  • function screen:onResize(w, h)

                    +

                    Called before onRender or onIdle when the window size has changed.

                    +
                  • +
                  • function screen:onRender()

                    +

                    Called when the viewscreen should paint itself. This is the only context +where the above painting functions work correctly.

                    +

                    If omitted, the screen is cleared; otherwise it should do that itself. +In order to make a see-through dialog, call self._native.parent:render().

                    +
                  • +
                  • function screen:onIdle()

                    +

                    Called every frame when the screen is on top of the stack.

                    +
                  • +
                  • function screen:onHelp()

                    +

                    Called when the help keybinding is activated (usually '?').

                    +
                  • +
                  • function screen:onInput(keys)

                    +

                    Called when keyboard or mouse events are available. +If any keys are pressed, the keys argument is a table mapping them to true. +Note that this refers to logical keybingings computed from real keys via +options; if multiple interpretations exist, the table will contain multiple keys.

                    +

                    The table also may contain special keys:

                    +
                    +
                    _STRING
                    +

                    Maps to an integer in range 0-255. Duplicates a separate "STRING_A???" code for convenience.

                    +
                    +
                    _MOUSE_L, _MOUSE_R
                    +

                    If the left or right mouse button is pressed.

                    +
                    +
                    +

                    If this method is omitted, the screen is dismissed on receival of the LEAVESCREEN key.

                    +
                  • +
                  • function screen:onGetSelectedUnit()

                    +
                  • +
                  • function screen:onGetSelectedItem()

                    +
                  • +
                  • function screen:onGetSelectedJob()

                    +
                  • +
                  • function screen:onGetSelectedBuilding()

                    +

                    Implement these to provide a return value for the matching +dfhack.gui.getSelected... function.

                    +
                  • +
                  +
                  +
                  +

                  Internal API

                  +

                  These functions are intended for the use by dfhack developers, +and are only documented here for completeness:

                  +
                    +
                  • dfhack.internal.scripts

                    +

                    The table used by dfhack.run_script() to give every script its own +global environment, persistent between calls to the script.

                    +
                  • +
                  • dfhack.internal.getAddress(name)

                    +

                    Returns the global address name, or nil.

                    +
                  • +
                  • dfhack.internal.setAddress(name, value)

                    +

                    Sets the global address name. Returns the value of getAddress before the change.

                    +
                  • +
                  • dfhack.internal.getVTable(name)

                    +

                    Returns the pre-extracted vtable address name, or nil.

                    +
                  • +
                  • dfhack.internal.getRebaseDelta()

                    +

                    Returns the ASLR rebase offset of the DF executable.

                    +
                  • +
                  • dfhack.internal.getMemRanges()

                    +

                    Returns a sequence of tables describing virtual memory ranges of the process.

                    +
                  • +
                  • dfhack.internal.patchMemory(dest,src,count)

                    +

                    Like memmove below, but works even if dest is read-only memory, e.g. code. +If destination overlaps a completely invalid memory region, or another error +occurs, returns false.

                    +
                  • +
                  • dfhack.internal.memmove(dest,src,count)

                    +

                    Wraps the standard memmove function. Accepts both numbers and refs as pointers.

                    +
                  • +
                  • dfhack.internal.memcmp(ptr1,ptr2,count)

                    +

                    Wraps the standard memcmp function.

                    +
                  • +
                  • dfhack.internal.memscan(haystack,count,step,needle,nsize)

                    +

                    Searches for needle of nsize bytes in haystack, +using count steps of step bytes. +Returns: step_idx, sum_idx, found_ptr, or nil if not found.

                    +
                  • +
                  • dfhack.internal.diffscan(old_data, new_data, start_idx, end_idx, eltsize[, oldval, newval, delta])

                    +

                    Searches for differences between buffers at ptr1 and ptr2, as integers of size eltsize. +The oldval, newval or delta arguments may be used to specify additional constraints. +Returns: found_index, or nil if end reached.

                    +
                  • +
                  +
                  +
                  +
                  +

                  Core interpreter context

                  +

                  While plugins can create any number of interpreter instances, +there is one special context managed by dfhack core. It is the +only context that can receive events from DF and plugins.

                  +

                  Core context specific functions:

                  +
                    +
                  • dfhack.is_core_context

                    +

                    Boolean value; true in the core context.

                    +
                  • +
                  • dfhack.timeout(time,mode,callback)

                    +

                    Arranges for the callback to be called once the specified +period of time passes. The mode argument specifies the +unit of time used, and may be one of 'frames' (raw FPS), +'ticks' (unpaused FPS), 'days', 'months', +'years' (in-game time). All timers other than +'frames' are cancelled when the world is unloaded, +and cannot be queued until it is loaded again. +Returns the timer id, or nil if unsuccessful due to +world being unloaded.

                    +
                  • +
                  • dfhack.timeout_active(id[,new_callback])

                    +

                    Returns the active callback with the given id, or nil +if inactive or nil id. If called with 2 arguments, replaces +the current callback with the given value, if still active. +Using timeout_active(id,nil) cancels the timer.

                    +
                  • +
                  • dfhack.onStateChange.foo = function(code)

                    +

                    Event. Receives the same codes as plugin_onstatechange in C++.

                    +
                  • +
                  +
                  +

                  Event type

                  +

                  An event is a native object transparently wrapping a lua table, +and implementing a __call metamethod. When it is invoked, it loops +through the table with next and calls all contained values. +This is intended as an extensible way to add listeners.

                  +

                  This type itself is available in any context, but only the +core context has the actual events defined by C++ code.

                  +

                  Features:

                  +
                    +
                  • dfhack.event.new()

                    +

                    Creates a new instance of an event.

                    +
                  • +
                  • event[key] = function

                    +

                    Sets the function as one of the listeners. Assign nil to remove it.

                    +

                    NOTE: The df.NULL key is reserved for the use by +the C++ owner of the event; it is an error to try setting it.

                    +
                  • +
                  • #event

                    +

                    Returns the number of non-nil listeners.

                    +
                  • +
                  • pairs(event)

                    +

                    Iterates over all listeners in the table.

                    +
                  • +
                  • event(args...)

                    +

                    Invokes all listeners contained in the event in an arbitrary +order using dfhack.safecall.

                    +
                  • +
                  +
                  +
                  +
                  +
                  +

                  Lua Modules

                  +

                  DFHack sets up the lua interpreter so that the built-in require +function can be used to load shared lua code from hack/lua/. +The dfhack namespace reference itself may be obtained via +require('dfhack'), although it is initially created as a +global by C++ bootstrap code.

                  +

                  The following module management functions are provided:

                  +
                    +
                  • mkmodule(name)

                    +

                    Creates an environment table for the module. Intended to be used as:

                    +
                    +local _ENV = mkmodule('foo')
                    +...
                    +return _ENV
                    +
                    +

                    If called the second time, returns the same table; thus providing reload support.

                    +
                  • +
                  • reload(name)

                    +

                    Reloads a previously require-d module "name" from the file. +Intended as a help for module development.

                    +
                  • +
                  • dfhack.BASE_G

                    +

                    This variable contains the root global environment table, which is +used as a base for all module and script environments. Its contents +should be kept limited to the standard Lua library and API described +in this document.

                    +
                  • +
                  +
                  +

                  Global environment

                  +

                  A number of variables and functions are provided in the base global +environment by the mandatory init file dfhack.lua:

                  +
                    +
                  • Color constants

                    +

                    These are applicable both for dfhack.color() and color fields +in DF functions or structures:

                    +

                    COLOR_RESET, COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, +COLOR_RED, COLOR_MAGENTA, COLOR_BROWN, COLOR_GREY, COLOR_DARKGREY, +COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED, +COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE

                    +
                  • +
                  • dfhack.onStateChange event codes

                    +

                    Available only in the core context, as is the event itself:

                    +

                    SC_WORLD_LOADED, SC_WORLD_UNLOADED, SC_MAP_LOADED, +SC_MAP_UNLOADED, SC_VIEWSCREEN_CHANGED, SC_CORE_INITIALIZED

                    +
                  • +
                  • Functions already described above

                    +

                    safecall, qerror, mkmodule, reload

                    +
                  • +
                  • printall(obj)

                    +

                    If the argument is a lua table or DF object reference, prints all fields.

                    +
                  • +
                  • copyall(obj)

                    +

                    Returns a shallow copy of the table or reference as a lua table.

                    +
                  • +
                  • pos2xyz(obj)

                    +

                    The object must have fields x, y and z. Returns them as 3 values. +If obj is nil, or x is -30000 (the usual marker for undefined +coordinates), returns nil.

                    +
                  • +
                  • xyz2pos(x,y,z)

                    +

                    Returns a table with x, y and z as fields.

                    +
                  • +
                  • safe_index(obj,index...)

                    +

                    Walks a sequence of dereferences, which may be represented by numbers or strings. +Returns nil if any of obj or indices is nil, or a numeric index is out of array bounds.

                    +
                  • +
                  +
                  +
                  +

                  utils

                  +
                    +
                  • utils.compare(a,b)

                    +

                    Comparator function; returns -1 if a<b, 1 if a>b, 0 otherwise.

                    +
                  • +
                  • utils.compare_name(a,b)

                    +

                    Comparator for names; compares empty string last.

                    +
                  • +
                  • utils.is_container(obj)

                    +

                    Checks if obj is a container ref.

                    +
                  • +
                  • utils.make_index_sequence(start,end)

                    +

                    Returns a lua sequence of numbers in start..end.

                    +
                  • +
                  • utils.make_sort_order(data, ordering)

                    +

                    Computes a sorted permutation of objects in data, as a table of integer +indices into the data sequence. Uses data.n as input length +if present.

                    +

                    The ordering argument is a sequence of ordering specs, represented +as lua tables with following possible fields:

                    +
                    +
                    ord.key = function(value)
                    +

                    Computes comparison key from input data value. Not called on nil. +If omitted, the comparison key is the value itself.

                    +
                    +
                    ord.key_table = function(data)
                    +

                    Computes a key table from the data table in one go.

                    +
                    +
                    ord.compare = function(a,b)
                    +

                    Comparison function. Defaults to utils.compare above. +Called on non-nil keys; nil sorts last.

                    +
                    +
                    ord.nil_first = true/false
                    +

                    If true, nil keys are sorted first instead of last.

                    +
                    +
                    ord.reverse = true/false
                    +

                    If true, sort non-nil keys in descending order.

                    +
                    +
                    +

                    For every comparison during sorting the specs are applied in +order until an unambiguous decision is reached. Sorting is stable.

                    +

                    Example of sorting a sequence by field foo:

                    +
                    +local spec = { key = function(v) return v.foo end }
                    +local order = utils.make_sort_order(data, { spec })
                    +local output = {}
                    +for i = 1,#order do output[i] = data[order[i]] end
                    +
                    +

                    Separating the actual reordering of the sequence in this +way enables applying the same permutation to multiple arrays. +This function is used by the sort plugin.

                    +
                  • +
                  • utils.assign(tgt, src)

                    +

                    Does a recursive assignment of src into tgt. +Uses df.assign if tgt is a native object ref; otherwise +recurses into lua tables.

                    +
                  • +
                  • utils.clone(obj, deep)

                    +

                    Performs a shallow, or semi-deep copy of the object as a lua table tree. +The deep mode recurses into lua tables and subobjects, except pointers +to other heap objects. +Null pointers are represented as df.NULL. Zero-based native containers +are converted to 1-based lua sequences.

                    +
                  • +
                  • utils.clone_with_default(obj, default, force)

                    +

                    Copies the object, using the default lua table tree +as a guide to which values should be skipped as uninteresting. +The force argument makes it always return a non-nil value.

                    +
                  • +
                  • utils.sort_vector(vector,field,cmpfun)

                    +

                    Sorts a native vector or lua sequence using the comparator function. +If field is not nil, applies the comparator to the field instead +of the whole object.

                    +
                  • +
                  • utils.binsearch(vector,key,field,cmpfun,min,max)

                    +

                    Does a binary search in a native vector or lua sequence for +key, using cmpfun and field like sort_vector. +If min and max are specified, they are used as the +search subrange bounds.

                    +

                    If found, returns item, true, idx. Otherwise returns +nil, false, insert_idx, where insert_idx is the correct +insertion point.

                    +
                  • +
                  • utils.insert_sorted(vector,item,field,cmpfun)

                    +

                    Does a binary search, and inserts item if not found. +Returns did_insert, vector[idx], idx.

                    +
                  • +
                  • utils.insert_or_update(vector,item,field,cmpfun)

                    +

                    Like insert_sorted, but also assigns the item into +the vector cell if insertion didn't happen.

                    +

                    As an example, you can use this to set skill values:

                    +
                    +utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id')
                    +
                    +

                    (For an explanation of new=true, see table assignment in the wrapper section)

                    +
                  • +
                  • utils.prompt_yes_no(prompt, default)

                    +

                    Presents a yes/no prompt to the user. If default is not nil, +allows just pressing Enter to submit the default choice. +If the user enters 'abort', throws an error.

                    +
                  • +
                  • utils.prompt_input(prompt, checkfun, quit_str)

                    +

                    Presents a prompt to input data, until a valid string is entered. +Once checkfun(input) returns true, ..., passes the values +through. If the user enters the quit_str (defaults to '~~~'), +throws an error.

                    +
                  • +
                  • utils.check_number(text)

                    +

                    A prompt_input checkfun that verifies a number input.

                    +
                  • +
                  +
                  +
                  +

                  dumper

                  +

                  A third-party lua table dumper module from +http://lua-users.org/wiki/DataDumper. Defines one +function:

                  +
                    +
                  • dumper.DataDumper(value, varname, fastmode, ident, indent_step)

                    +

                    Returns value converted to a string. The indent_step +argument specifies the indentation step size in spaces. For +the other arguments see the original documentation link above.

                    +
                  • +
                  +
                  +
                  +

                  class

                  +

                  Implements a trivial single-inheritance class system.

                  +
                    +
                  • Foo = defclass(Foo[, ParentClass])

                    +

                    Defines or updates class Foo. The Foo = defclass(Foo) syntax +is needed so that when the module or script is reloaded, the +class identity will be preserved through the preservation of +global variable values.

                    +

                    The defclass function is defined as a stub in the global +namespace, and using it will auto-load the class module.

                    +
                  • +
                  • Class.super

                    +

                    This class field is set by defclass to the parent class, and +allows a readable Class.super.method(self, ...) syntax for +calling superclass methods.

                    +
                  • +
                  • Class.ATTRS { foo = xxx, bar = yyy }

                    +

                    Declares certain instance fields to be attributes, i.e. auto-initialized +from fields in the table used as the constructor argument. If omitted, +they are initialized with the default values specified in this declaration.

                    +

                    If the default value should be nil, use ATTRS { foo = DEFAULT_NIL }.

                    +
                  • +
                  • new_obj = Class{ foo = arg, bar = arg, ... }

                    +

                    Calling the class as a function creates and initializes a new instance. +Initialization happens in this order:

                    +
                      +
                    1. An empty instance table is created, and its metatable set.
                    2. +
                    3. The preinit method is called via invoke_before (see below) +with the table used as argument to the class. This method is intended +for validating and tweaking that argument table.
                    4. +
                    5. Declared ATTRS are initialized from the argument table or their default values.
                    6. +
                    7. The init method is called via invoke_after with the argument table. +This is the main constructor method.
                    8. +
                    9. The postinit method is called via invoke_after with the argument table. +Place code that should be called after the object is fully constructed here.
                    10. +
                    +
                  • +
                  +

                  Predefined instance methods:

                  +
                    +
                  • instance:assign{ foo = xxx }

                    +

                    Assigns all values in the input table to the matching instance fields.

                    +
                  • +
                  • instance:callback(method_name, [args...])

                    +

                    Returns a closure that invokes the specified method of the class, +properly passing in self, and optionally a number of initial arguments too. +The arguments given to the closure are appended to these.

                    +
                  • +
                  • instance:invoke_before(method_name, args...)

                    +

                    Navigates the inheritance chain of the instance starting from the most specific +class, and invokes the specified method with the arguments if it is defined in +that specific class. Equivalent to the following definition in every class:

                    +
                    +function Class:invoke_before(method, ...)
                    +  if rawget(Class, method) then
                    +    rawget(Class, method)(self, ...)
                    +  end
                    +  Class.super.invoke_before(method, ...)
                    +end
                    +
                    +
                  • +
                  • instance:invoke_after(method_name, args...)

                    +

                    Like invoke_before, only the method is called after the recursive call to super, +i.e. invocations happen in the parent to child order.

                    +

                    These two methods are inspired by the Common Lisp before and after methods, and +are intended for implementing similar protocols for certain things. The class +library itself uses them for constructors.

                    +
                  • +
                  +

                  To avoid confusion, these methods cannot be redefined.

                  +
                  +
                  +
                  +

                  Plugins

                  +

                  DFHack plugins may export native functions and events +to lua contexts. They are automatically imported by +mkmodule('plugins.<name>'); this means that a lua +module file is still necessary for require to read.

                  +

                  The following plugins have lua support.

                  +
                  +

                  burrows

                  +

                  Implements extended burrow manipulations.

                  +

                  Events:

                  +
                    +
                  • onBurrowRename.foo = function(burrow)

                    +

                    Emitted when a burrow might have been renamed either through +the game UI, or renameBurrow().

                    +
                  • +
                  • onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype,worker)

                    +

                    Emitted when a tile might have been dug out. Only tracked if the +auto-growing burrows feature is enabled.

                    +
                  • +
                  +

                  Native functions:

                  +
                    +
                  • renameBurrow(burrow,name)

                    +

                    Renames the burrow, emitting onBurrowRename and updating auto-grow state properly.

                    +
                  • +
                  • findByName(burrow,name)

                    +

                    Finds a burrow by name, using the same rules as the plugin command line interface. +Namely, trailing '+' characters marking auto-grow burrows are ignored.

                    +
                  • +
                  • copyUnits(target,source,enable)

                    +

                    Applies units from source burrow to target. The enable +parameter specifies if they are to be added or removed.

                    +
                  • +
                  • copyTiles(target,source,enable)

                    +

                    Applies tiles from source burrow to target. The enable +parameter specifies if they are to be added or removed.

                    +
                  • +
                  • setTilesByKeyword(target,keyword,enable)

                    +

                    Adds or removes tiles matching a predefined keyword. The keyword +set is the same as used by the command line.

                    +
                  • +
                  +

                  The lua module file also re-exports functions from dfhack.burrows.

                  +
                  +
                  +

                  sort

                  +

                  Does not export any native functions as of now. Instead, it +calls lua code to perform the actual ordering of list items.

                  +
                  +
                  +
                  +

                  Scripts

                  +

                  Any files with the .lua extension placed into hack/scripts/* +are automatically used by the DFHack core as commands. The +matching command name consists of the name of the file sans +the extension.

                  +

                  If the first line of the script is a one-line comment, it is +used by the built-in ls and help commands.

                  +

                  NOTE: Scripts placed in subdirectories still can be accessed, but +do not clutter the ls command list; thus it is preferred +for obscure developer-oriented scripts and scripts used by tools. +When calling such scripts, always use '/' as the separator for +directories, e.g. devel/lua-example.

                  +

                  Scripts are re-read from disk every time they are used +(this may be changed later to check the file change time); however +the global variable values persist in memory between calls. +Every script gets its own separate environment for global +variables.

                  +

                  Arguments are passed in to the scripts via the ... built-in +quasi-variable; when the script is called by the DFHack core, +they are all guaranteed to be non-nil strings.

                  +

                  DFHack core invokes the scripts in the core context (see above); +however it is possible to call them from any lua code (including +from other scripts) in any context, via the same function the core uses:

                  +
                    +
                  • dfhack.run_script(name[,args...])

                    +

                    Run a lua script in hack/scripts/, as if it was started from dfhack command-line. +The name argument should be the name stem, as would be used on the command line.

                    +
                  • +
                  +

                  Note that this function lets errors propagate to the caller.

                  +
                  +
                  + + diff --git a/Readme.html b/Readme.html new file mode 100644 index 000000000..caf1f6a0e --- /dev/null +++ b/Readme.html @@ -0,0 +1,2808 @@ + + + + + + +DFHack Readme + + + +
                  +

                  DFHack Readme

                  + +
                  +

                  Introduction

                  +

                  DFHack is a Dwarf Fortress memory access library and a set of basic +tools that use it. Tools come in the form of plugins or (not yet) +external tools. It is an attempt to unite the various ways tools +access DF memory and allow for easier development of new tools.

                  +
                  +

                  Contents

                  + +
                  +
                  +
                  +

                  Getting DFHack

                  +

                  The project is currently hosted on github, for both source and +binaries at http://github.com/peterix/dfhack

                  +

                  Releases can be downloaded from here: https://github.com/peterix/dfhack/downloads

                  +

                  All new releases are announced in the bay12 thread: http://tinyurl.com/dfhack-ng

                  +
                  +
                  +

                  Compatibility

                  +

                  DFHack works on Windows XP, Vista, 7 or any modern Linux distribution. +OSX is not supported due to lack of developers with a Mac.

                  +

                  Currently, version 0.34.11 is supported (and tested). If you need DFHack +for older versions, look for older releases.

                  +

                  On Windows, you have to use the SDL version of DF.

                  +

                  It is possible to use the Windows DFHack under wine/OSX.

                  +
                  +
                  +

                  Installation/Removal

                  +

                  Installing DFhack involves copying files into your DF folder. +Copy the files from a release archive so that:

                  +
                  +
                    +
                  • On Windows, SDL.dll is replaced
                  • +
                  • On Linux, the 'dfhack' script is placed in the same folder as the 'df' script
                  • +
                  +
                  +

                  Uninstalling is basically the same, in reverse:

                  +
                  +
                    +
                  • On Windows, first delete SDL.dll and rename SDLreal.dll to SDL.dll. Then +remove the other DFHack files
                  • +
                  • On Linux, Remove the DFHack files.
                  • +
                  +
                  +

                  The stonesense plugin might require some additional libraries on Linux.

                  +

                  If any of the plugins or dfhack itself refuses to load, check the stderr.log +file created in your DF folder.

                  +
                  +
                  +

                  Using DFHack

                  +

                  DFHack basically extends what DF can do with something similar to the drop-down +console found in Quake engine games. On Windows, this is a separate command line +window. On linux, the terminal used to launch the dfhack script is taken over +(so, make sure you start from a terminal). Basic interaction with dfhack +involves entering commands into the console. For some basic instroduction, +use the 'help' command. To list all possible commands, use the 'ls' command. +Many commands have their own help or detailed description. You can use +'command help' or 'command ?' to show that.

                  +

                  The command line has some nice line editing capabilities, including history +that's preserved between different runs of DF (use up/down keys to go through +the history).

                  +

                  The second way to interact with DFHack is to bind the available commands +to in-game hotkeys. The old way to do this is via the hotkey/zoom menu (normally +opened with the 'h' key). Binding the commands is done by assigning a command as +a hotkey name (with 'n').

                  +

                  A new and more flexible way is the keybinding command in the dfhack console. +However, bindings created this way are not automatically remembered between runs +of the game, so it becomes necessary to use the dfhack.init file to ensure that +they are re-created every time it is loaded.

                  +

                  Interactive commands like 'liquids' cannot be used as hotkeys.

                  +

                  Most of the commands come from plugins. Those reside in 'hack/plugins/'.

                  +
                  +
                  +

                  Something doesn't work, help!

                  +

                  First, don't panic :) Second, dfhack keeps a few log files in DF's folder +- stderr.log and stdout.log. You can look at those and possibly find out what's +happening. +If you found a bug, you can either report it in the bay12 DFHack thread, +the issues tracker on github, contact me (peterix@gmail.com) or visit the +#dfhack IRC channel on freenode.

                  +
                  +
                  +

                  The init file

                  +

                  If your DF folder contains a file named dfhack.init, its contents will be run +every time you start DF. This allows setting up keybindings. An example file +is provided as dfhack.init-example - you can tweak it and rename to dfhack.init +if you want to use this functionality.

                  +
                  +

                  Setting keybindings

                  +

                  To set keybindings, use the built-in keybinding command. Like any other +command it can be used at any time from the console, but it is also meaningful +in the DFHack init file.

                  +

                  Currently it supports any combination of Ctrl/Alt/Shift with F1-F9, or A-Z.

                  +

                  Possible ways to call the command:

                  + +++ + + + + + + + + + + + + + +
                  keybinding list <key>:
                   List bindings active for the key combination.
                  keybinding clear <key> <key>...:
                   Remove bindings for the specified keys.
                  keybinding add <key> "cmdline" "cmdline"...:
                   Add bindings for the specified +key.
                  keybinding set <key> "cmdline" "cmdline"...:
                   Clear, and then add bindings for +the specified key.
                  +

                  The <key> parameter above has the following case-sensitive syntax:

                  +
                  +[Ctrl-][Alt-][Shift-]KEY[@context]
                  +
                  +

                  where the KEY part can be F1-F9 or A-Z, and [] denote optional parts.

                  +

                  When multiple commands are bound to the same key combination, DFHack selects +the first applicable one. Later 'add' commands, and earlier entries within one +'add' command have priority. Commands that are not specifically intended for use +as a hotkey are always considered applicable.

                  +

                  The context part in the key specifier above can be used to explicitly restrict +the UI state where the binding would be applicable. If called without parameters, +the keybinding command among other things prints the current context string. +Only bindings with a context tag that either matches the current context fully, +or is a prefix ending at a '/' boundary would be considered for execution, i.e. +for context foo/bar/baz, possible matches are any of @foo/bar/baz, @foo/bar, +@foo or none.

                  +
                  +
                  +
                  +

                  Commands

                  +

                  DFHack command syntax consists of a command name, followed by arguments separated +by whitespace. To include whitespace in an argument, quote it in double quotes. +To include a double quote character, use \" inside double quotes.

                  +

                  If the first non-whitespace character of a line is #, the line is treated +as a comment, i.e. a silent no-op command.

                  +

                  If the first non-whitespace character is :, the command is parsed in a special +alternative mode: first, non-whitespace characters immediately following the : +are used as the command name; the remaining part of the line, starting with the first +non-whitespace character after the command name, is used verbatim as the first argument. +The following two command lines are exactly equivalent:

                  +
                  +
                    +
                  • :foo a b "c d" e f
                  • +
                  • foo "a b \"c d\" e f"
                  • +
                  +
                  +

                  This is intended for commands like rb_eval that evaluate script language statements.

                  +

                  Almost all the commands support using the 'help <command-name>' built-in command +to retrieve further help without having to look at this document. Alternatively, +some accept a 'help'/'?' option on their command line.

                  +
                  +

                  Game progress

                  +
                  +

                  die

                  +

                  Instantly kills DF without saving.

                  +
                  +
                  +

                  forcepause

                  +

                  Forces DF to pause. This is useful when your FPS drops below 1 and you lose +control of the game.

                  +
                  +
                    +
                  • Activate with 'forcepause 1'
                  • +
                  • Deactivate with 'forcepause 0'
                  • +
                  +
                  +
                  +
                  +

                  nopause

                  +

                  Disables pausing (both manual and automatic) with the exception of pause forced +by 'reveal hell'. This is nice for digging under rivers.

                  +
                  +
                  +

                  fastdwarf

                  +

                  Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly and perform tasks quickly. Teledwarf makes dwarves move instantaneously, but do jobs at the same speed.

                  +
                  +
                    +
                  • 'fastdwarf 0 0' disables both
                  • +
                  • 'fastdwarf 0 1' disables speedydwarf and enables teledwarf
                  • +
                  • 'fastdwarf 1 0' enables speedydwarf and disables teledwarf
                  • +
                  • 'fastdwarf 1 1' enables both
                  • +
                  • 'fastdwarf 0' disables both
                  • +
                  • 'fastdwarf 1' enables speedydwarf and disables teledwarf
                  • +
                  +
                  +
                  +
                  +
                  +

                  Game interface

                  +
                  +

                  follow

                  +

                  Makes the game view follow the currently highlighted unit after you exit from +current menu/cursor mode. Handy for watching dwarves running around. Deactivated +by moving the view manually.

                  +
                  +
                  +

                  tidlers

                  +

                  Toggle between all possible positions where the idlers count can be placed.

                  +
                  +
                  +

                  twaterlvl

                  +

                  Toggle between displaying/not displaying liquid depth as numbers.

                  +
                  +
                  +

                  copystock

                  +

                  Copies the parameters of the currently highlighted stockpile to the custom +stockpile settings and switches to custom stockpile placement mode, effectively +allowing you to copy/paste stockpiles easily.

                  +
                  +
                  +

                  rename

                  +

                  Allows renaming various things.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + + + + + + + + +
                  rename squad <index> "name":
                   Rename squad by index to 'name'.
                  rename hotkey <index> "name":
                   Rename hotkey by index. This allows assigning +longer commands to the DF hotkeys.
                  rename unit "nickname":
                   Rename a unit/creature highlighted in the DF user +interface.
                  rename unit-profession "custom profession":
                   Change proffession name of the +highlighted unit/creature.
                  rename building "name":
                   Set a custom name for the selected building. +The building must be one of stockpile, workshop, furnace, trap, +siege engine or an activity zone.
                  +
                  +
                  +
                  +
                  +

                  Adventure mode

                  +
                  +

                  adv-bodyswap

                  +

                  This allows taking control over your followers and other creatures in adventure +mode. For example, you can make them pick up new arms and armor and equip them +properly.

                  +

                  Usage:

                  +
                  +
                    +
                  • When viewing unit details, body-swaps into that unit.
                  • +
                  • In the main adventure mode screen, reverts transient swap.
                  • +
                  +
                  +
                  +
                  +

                  advtools

                  +

                  A package of different adventure mode tools (currently just one)

                  +

                  Usage:

                  +
                  + +++ + + + + + + + +
                  list-equipped [all]:
                   List armor and weapons equipped by your companions. +If all is specified, also lists non-metal clothing.
                  metal-detector [all-types] [non-trader]:
                   Reveal metal armor and weapons in +shops. The options disable the checks +on item type and being in shop.
                  +
                  +
                  +
                  +
                  +

                  Map modification

                  +
                  +

                  changelayer

                  +

                  Changes material of the geology layer under cursor to the specified inorganic +RAW material. Can have impact on all surrounding regions, not only your embark! +By default changing stone to soil and vice versa is not allowed. By default +changes only the layer at the cursor position. Note that one layer can stretch +across lots of z levels. By default changes only the geology which is linked +to the biome under the cursor. That geology might be linked to other biomes +as well, though. Mineral veins and gem clusters will stay on the map. Use +'changevein' for them.

                  +

                  tl;dr: You will end up with changing quite big areas in one go, especially if +you use it in lower z levels. Use with care.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + + + +
                  all_biomes:Change selected layer for all biomes on your map. +Result may be undesirable since the same layer can AND WILL +be on different z-levels for different biomes. Use the tool +'probe' to get an idea how layers and biomes are distributed +on your map.
                  all_layers:Change all layers on your map (only for the selected biome +unless 'all_biomes' is added). +Candy mountain, anyone? Will make your map quite boring, +but tidy.
                  force:Allow changing stone to soil and vice versa. !!THIS CAN HAVE +WEIRD EFFECTS, USE WITH CARE!! +Note that soil will not be magically replaced with stone. +You will, however, get a stone floor after digging so it +will allow the floor to be engraved. +Note that stone will not be magically replaced with soil. +You will, however, get a soil floor after digging so it +could be helpful for creating farm plots on maps with no +soil.
                  verbose:Give some details about what is being changed.
                  trouble:Give some advice about known problems.
                  +
                  +

                  Examples:

                  +
                  +
                  +
                  changelayer GRANITE
                  +
                  Convert layer at cursor position into granite.
                  +
                  changelayer SILTY_CLAY force
                  +
                  Convert layer at cursor position into clay even if it's stone.
                  +
                  changelayer MARBLE all_biomes all_layers
                  +
                  Convert all layers of all biomes which are not soil into marble.
                  +
                  +
                  +
                  +

                  Note

                  +
                    +
                  • If you use changelayer and nothing happens, try to pause/unpause the game +for a while and try to move the cursor to another tile. Then try again. +If that doesn't help try temporarily changing some other layer, undo your +changes and try again for the layer you want to change. Saving +and reloading your map might also help.
                  • +
                  • You should be fine if you only change single layers without the use +of 'force'. Still it's advisable to save your game before messing with +the map.
                  • +
                  • When you force changelayer to convert soil to stone you might experience +weird stuff (flashing tiles, tiles changed all over place etc). +Try reverting the changes manually or even better use an older savegame. +You did save your game, right?
                  • +
                  +
                  +
                  +
                  +

                  changevein

                  +

                  Changes material of the vein under cursor to the specified inorganic RAW +material. Only affects tiles within the current 16x16 block - for veins and +large clusters, you will need to use this command multiple times.

                  +

                  Example:

                  +
                  +
                  +
                  changevein NATIVE_PLATINUM
                  +
                  Convert vein at cursor position into platinum ore.
                  +
                  +
                  +
                  +
                  +

                  changeitem

                  +

                  Allows changing item material and base quality. By default the item currently +selected in the UI will be changed (you can select items in the 'k' list +or inside containers/inventory). By default change is only allowed if materials +is of the same subtype (for example wood<->wood, stone<->stone etc). But since +some transformations work pretty well and may be desired you can override this +with 'force'. Note that some attributes will not be touched, possibly resulting +in weirdness. To get an idea how the RAW id should look like, check some items +with 'info'. Using 'force' might create items which are not touched by +crafters/haulers.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + + + +
                  info:Don't change anything, print some info instead.
                  here:Change all items at the cursor position. Requires in-game cursor.
                  material, m:Change material. Must be followed by valid material RAW id.
                  quality, q:Change base quality. Must be followed by number (0-5).
                  force:Ignore subtypes, force change to new material.
                  +
                  +

                  Examples:

                  +
                  +
                  +
                  changeitem m INORGANIC:GRANITE here
                  +
                  Change material of all items under the cursor to granite.
                  +
                  changeitem q 5
                  +
                  Change currently selected item to masterpiece quality.
                  +
                  +
                  +
                  +
                  +

                  colonies

                  +

                  Allows listing all the vermin colonies on the map and optionally turning them into honey bee colonies.

                  +

                  Options:

                  +
                  + +++ + + + +
                  bees:turn colonies into honey bee colonies
                  +
                  +
                  +
                  +

                  deramp (by zilpin)

                  +

                  Removes all ramps designated for removal from the map. This is useful for replicating the old channel digging designation. +It also removes any and all 'down ramps' that can remain after a cave-in (you don't have to designate anything for that to happen).

                  +
                  +
                  +

                  feature

                  +

                  Enables management of map features.

                  +
                    +
                  • Discovering a magma feature (magma pool, volcano, magma sea, or curious +underground structure) permits magma workshops and furnaces to be built.
                  • +
                  • Discovering a cavern layer causes plants (trees, shrubs, and grass) from +that cavern to grow within your fortress.
                  • +
                  +

                  Options:

                  +
                  + +++ + + + + + + + +
                  list:Lists all map features in your current embark by index.
                  show X:Marks the selected map feature as discovered.
                  hide X:Marks the selected map feature as undiscovered.
                  +
                  +
                  +
                  +

                  liquids

                  +

                  Allows adding magma, water and obsidian to the game. It replaces the normal +dfhack command line and can't be used from a hotkey. Settings will be remembered +as long as dfhack runs. Intended for use in combination with the command +liquids-here (which can be bound to a hotkey).

                  +

                  For more information, refer to the command's internal help.

                  +
                  +

                  Note

                  +

                  Spawning and deleting liquids can F up pathing data and +temperatures (creating heat traps). You've been warned.

                  +
                  +
                  +
                  +

                  liquids-here

                  +

                  Run the liquid spawner with the current/last settings made in liquids (if no +settings in liquids were made it paints a point of 7/7 magma by default).

                  +

                  Intended to be used as keybinding. Requires an active in-game cursor.

                  +
                  +
                  +

                  tiletypes

                  +

                  Can be used for painting map tiles and is an interactive command, much like +liquids.

                  +

                  The tool works with two set of options and a brush. The brush determines which +tiles will be processed. First set of options is the filter, which can exclude +some of the tiles from the brush by looking at the tile properties. The second +set of options is the paint - this determines how the selected tiles are +changed.

                  +

                  Both paint and filter can have many different properties including things like +general shape (WALL, FLOOR, etc.), general material (SOIL, STONE, MINERAL, +etc.), state of 'designated', 'hidden' and 'light' flags.

                  +

                  The properties of filter and paint can be partially defined. This means that +you can for example do something like this:

                  +
                  +filter material STONE
                  +filter shape FORTIFICATION
                  +paint shape FLOOR
                  +
                  +

                  This will turn all stone fortifications into floors, preserving the material.

                  +

                  Or this:

                  +
                  +filter shape FLOOR
                  +filter material MINERAL
                  +paint shape WALL
                  +
                  +

                  Turning mineral vein floors back into walls.

                  +

                  The tool also allows tweaking some tile flags:

                  +

                  Or this:

                  +
                  +paint hidden 1
                  +paint hidden 0
                  +
                  +

                  This will hide previously revealed tiles (or show hidden with the 0 option).

                  +

                  Any paint or filter option (or the entire paint or filter) can be disabled entirely by using the ANY keyword:

                  +
                  +paint hidden ANY
                  +paint shape ANY
                  +filter material any
                  +filter shape any
                  +filter any
                  +
                  +
                  +
                  You can use several different brushes for painting tiles:
                  +
                    +
                  • Point. (point)
                  • +
                  • Rectangular range. (range)
                  • +
                  • A column ranging from current cursor to the first solid tile above. (column)
                  • +
                  • DF map block - 16x16 tiles, in a regular grid. (block)
                  • +
                  +
                  +
                  +

                  Example:

                  +
                  +range 10 10 1
                  +
                  +

                  This will change the brush to a rectangle spanning 10x10 tiles on one z-level. +The range starts at the position of the cursor and goes to the east, south and +up.

                  +

                  For more details, see the 'help' command while using this.

                  +
                  +
                  +

                  tiletypes-commands

                  +

                  Runs tiletypes commands, separated by ;. This makes it possible to change +tiletypes modes from a hotkey.

                  +
                  +
                  +

                  tiletypes-here

                  +

                  Apply the current tiletypes options at the in-game cursor position, including +the brush. Can be used from a hotkey.

                  +
                  +
                  +

                  tiletypes-here-point

                  +

                  Apply the current tiletypes options at the in-game cursor position to a single +tile. Can be used from a hotkey.

                  +
                  +
                  +

                  tubefill

                  +

                  Fills all the adamantine veins again. Veins that were empty will be filled in +too, but might still trigger a demon invasion (this is a known bug).

                  +
                  +
                  +

                  extirpate

                  +

                  A tool for getting rid of trees and shrubs. By default, it only kills +a tree/shrub under the cursor. The plants are turned into ashes instantly.

                  +

                  Options:

                  +
                  + +++ + + + + + + + +
                  shrubs:affect all shrubs on the map
                  trees:affect all trees on the map
                  all:affect every plant!
                  +
                  +
                  +
                  +

                  grow

                  +

                  Makes all saplings present on the map grow into trees (almost) instantly.

                  +
                  +
                  +

                  immolate

                  +

                  Very similar to extirpate, but additionally sets the plants on fire. The fires +can and will spread ;)

                  +
                  +
                  +

                  regrass

                  +

                  Regrows grass. Not much to it ;)

                  +
                  +
                  +

                  weather

                  +

                  Prints the current weather map by default.

                  +

                  Also lets you change the current weather to 'clear sky', 'rainy' or 'snowing'.

                  +

                  Options:

                  +
                  + +++ + + + + + + + +
                  snow:make it snow everywhere.
                  rain:make it rain.
                  clear:clear the sky.
                  +
                  +
                  +
                  +
                  +

                  Map inspection

                  +
                  +

                  cursecheck

                  +

                  Checks a single map tile or the whole map/world for cursed creatures (ghosts, +vampires, necromancers, werebeasts, zombies).

                  +

                  With an active in-game cursor only the selected tile will be observed. +Without a cursor the whole map will be checked.

                  +

                  By default cursed creatures will be only counted in case you just want to find +out if you have any of them running around in your fort. Dead and passive +creatures (ghosts who were put to rest, killed vampires, ...) are ignored. +Undead skeletons, corpses, bodyparts and the like are all thrown into the curse +category "zombie". Anonymous zombies and resurrected body parts will show +as "unnamed creature".

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + +
                  detail:Print full name, date of birth, date of curse and some status +info (some vampires might use fake identities in-game, though).
                  nick:Set the type of curse as nickname (does not always show up +in-game, some vamps don't like nicknames).
                  all:Include dead and passive cursed creatures (can result in a quite +long list after having FUN with necromancers).
                  verbose:Print all curse tags (if you really want to know it all).
                  +
                  +

                  Examples:

                  +
                  +
                  +
                  cursecheck detail all
                  +
                  Give detailed info about all cursed creatures including deceased ones (no +in-game cursor).
                  +
                  cursecheck nick
                  +
                  Give a nickname all living/active cursed creatures on the map(no in-game +cursor).
                  +
                  +
                  +
                  +

                  Note

                  +
                    +
                  • If you do a full search (with the option "all") former ghosts will show up +with the cursetype "unknown" because their ghostly flag is not set +anymore. But if you happen to find a living/active creature with cursetype +"unknown" please report that in the dfhack thread on the modding forum or +per irc. This is likely to happen with mods which introduce new types +of curses, for example.
                  • +
                  +
                  +
                  +
                  +

                  flows

                  +

                  A tool for checking how many tiles contain flowing liquids. If you suspect that +your magma sea leaks into HFS, you can use this tool to be sure without +revealing the map.

                  +
                  +
                  +

                  probe

                  +

                  Can be used to determine tile properties like temperature.

                  +
                  +
                  +

                  prospect

                  +

                  Prints a big list of all the present minerals and plants. By default, only +the visible part of the map is scanned.

                  +

                  Options:

                  +
                  + +++ + + + + + + + +
                  all:Scan the whole map, as if it was revealed.
                  value:Show material value in the output. Most useful for gems.
                  hell:Show the Z range of HFS tubes. Implies 'all'.
                  +
                  +
                  +

                  Pre-embark estimate

                  +

                  If prospect is called during the embark selection screen, it displays an estimate of +layer stone availability.

                  +
                  +

                  Note

                  +

                  The results of pre-embark prospect are an estimate, and can at best be expected +to be somewhere within +/- 30% of the true amount; sometimes it does a lot worse. +Especially, it is not clear how to precisely compute how many soil layers there +will be in a given embark tile, so it can report a whole extra layer, or omit one +that is actually present.

                  +
                  +

                  Options:

                  +
                  + +++ + + + +
                  all:Also estimate vein mineral amounts.
                  +
                  +
                  +
                  +
                  +

                  reveal

                  +

                  This reveals the map. By default, HFS will remain hidden so that the demons +don't spawn. You can use 'reveal hell' to reveal everything. With hell revealed, +you won't be able to unpause until you hide the map again. If you really want +to unpause with hell revealed, use 'reveal demons'.

                  +

                  Reveal also works in adventure mode, but any of its effects are negated once +you move. When you use it this way, you don't need to run 'unreveal'.

                  +
                  +
                  +

                  unreveal

                  +

                  Reverts the effects of 'reveal'.

                  +
                  +
                  +

                  revtoggle

                  +

                  Switches between 'reveal' and 'unreveal'.

                  +
                  +
                  +

                  revflood

                  +

                  This command will hide the whole map and then reveal all the tiles that have +a path to the in-game cursor.

                  +
                  +
                  +

                  revforget

                  +

                  When you use reveal, it saves information about what was/wasn't visible before +revealing everything. Unreveal uses this information to hide things again. +This command throws away the information. For example, use in cases where +you abandoned with the fort revealed and no longer want the data.

                  +
                  +
                  +

                  showmood

                  +

                  Shows all items needed for the currently active strange mood.

                  +
                  +
                  +
                  +

                  Designations

                  +
                  +

                  burrow

                  +

                  Miscellaneous burrow control. Allows manipulating burrows and automated burrow +expansion while digging.

                  +

                  Options:

                  +
                  +
                  +
                  enable feature ...
                  +
                  Enable features of the plugin.
                  +
                  disable feature ...
                  +
                  Disable features of the plugin.
                  +
                  clear-unit burrow burrow ...
                  +
                  Remove all units from the burrows.
                  +
                  clear-tiles burrow burrow ...
                  +
                  Remove all tiles from the burrows.
                  +
                  set-units target-burrow src-burrow ...
                  +
                  Clear target, and adds units from source burrows.
                  +
                  add-units target-burrow src-burrow ...
                  +
                  Add units from the source burrows to the target.
                  +
                  remove-units target-burrow src-burrow ...
                  +
                  Remove units in source burrows from the target.
                  +
                  set-tiles target-burrow src-burrow ...
                  +
                  Clear target and adds tiles from the source burrows.
                  +
                  add-tiles target-burrow src-burrow ...
                  +
                  Add tiles from the source burrows to the target.
                  +
                  remove-tiles target-burrow src-burrow ...
                  +

                  Remove tiles in source burrows from the target.

                  +

                  For these three options, in place of a source burrow it is +possible to use one of the following keywords: ABOVE_GROUND, +SUBTERRANEAN, INSIDE, OUTSIDE, LIGHT, DARK, HIDDEN, REVEALED

                  +
                  +
                  +
                  +

                  Features:

                  +
                  + +++ + + + +
                  auto-grow:When a wall inside a burrow with a name ending in '+' is dug +out, the burrow is extended to newly-revealed adjacent walls. +This final '+' may be omitted in burrow name args of commands above. +Digging 1-wide corridors with the miner inside the burrow is SLOW.
                  +
                  +
                  +
                  +

                  digv

                  +

                  Designates a whole vein for digging. Requires an active in-game cursor placed +over a vein tile. With the 'x' option, it will traverse z-levels (putting stairs +between the same-material tiles).

                  +
                  +
                  +

                  digvx

                  +

                  A permanent alias for 'digv x'.

                  +
                  +
                  +

                  digl

                  +

                  Designates layer stone for digging. Requires an active in-game cursor placed +over a layer stone tile. With the 'x' option, it will traverse z-levels +(putting stairs between the same-material tiles). With the 'undo' option it +will remove the dig designation instead (if you realize that digging out a 50 +z-level deep layer was not such a good idea after all).

                  +
                  +
                  +

                  diglx

                  +

                  A permanent alias for 'digl x'.

                  +
                  +
                  +

                  digexp

                  +

                  This command can be used for exploratory mining.

                  +

                  See: http://df.magmawiki.com/index.php/DF2010:Exploratory_mining

                  +

                  There are two variables that can be set: pattern and filter.

                  +

                  Patterns:

                  +
                  + +++ + + + + + + + + + + + + + +
                  diag5:diagonals separated by 5 tiles
                  diag5r:diag5 rotated 90 degrees
                  ladder:A 'ladder' pattern
                  ladderr:ladder rotated 90 degrees
                  clear:Just remove all dig designations
                  cross:A cross, exactly in the middle of the map.
                  +
                  +

                  Filters:

                  +
                  + +++ + + + + + + + +
                  all:designate whole z-level
                  hidden:designate only hidden tiles of z-level (default)
                  designated:Take current designation and apply pattern to it.
                  +
                  +

                  After you have a pattern set, you can use 'expdig' to apply it again.

                  +

                  Examples:

                  +
                  +
                  +
                  designate the diagonal 5 patter over all hidden tiles:
                  +
                    +
                  • expdig diag5 hidden
                  • +
                  +
                  +
                  apply last used pattern and filter:
                  +
                    +
                  • expdig
                  • +
                  +
                  +
                  Take current designations and replace them with the ladder pattern:
                  +
                    +
                  • expdig ladder designated
                  • +
                  +
                  +
                  +
                  +
                  +
                  +

                  digcircle

                  +

                  A command for easy designation of filled and hollow circles. +It has several types of options.

                  +

                  Shape:

                  +
                  + +++ + + + + + + + +
                  hollow:Set the circle to hollow (default)
                  filled:Set the circle to filled
                  #:Diameter in tiles (default = 0, does nothing)
                  +
                  +

                  Action:

                  +
                  + +++ + + + + + + + +
                  set:Set designation (default)
                  unset:Unset current designation
                  invert:Invert designations already present
                  +
                  +

                  Designation types:

                  +
                  + +++ + + + + + + + + + + + + + +
                  dig:Normal digging designation (default)
                  ramp:Ramp digging
                  ustair:Staircase up
                  dstair:Staircase down
                  xstair:Staircase up/down
                  chan:Dig channel
                  +
                  +

                  After you have set the options, the command called with no options +repeats with the last selected parameters.

                  +

                  Examples:

                  +
                    +
                  • 'digcircle filled 3' = Dig a filled circle with radius = 3.
                  • +
                  • 'digcircle' = Do it again.
                  • +
                  +
                  +
                  +

                  digtype

                  +

                  For every tile on the map of the same vein type as the selected tile, this command designates it to have the same designation as the selected tile. If the selected tile has no designation, they will be dig designated. +If an argument is given, the designation of the selected tile is ignored, and all appropriate tiles are set to the specified designation.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + + + + + + + +
                  dig:
                  channel:
                  ramp:
                  updown:up/down stairs
                  up:up stairs
                  down:down stairs
                  clear:clear designation
                  +
                  +
                  +
                  +

                  filltraffic

                  +

                  Set traffic designations using flood-fill starting at the cursor.

                  +

                  Traffic Type Codes:

                  +
                  + +++ + + + + + + + + + +
                  H:High Traffic
                  N:Normal Traffic
                  L:Low Traffic
                  R:Restricted Traffic
                  +
                  +

                  Other Options:

                  +
                  + +++ + + + + + + + +
                  X:Fill accross z-levels.
                  B:Include buildings and stockpiles.
                  P:Include empty space.
                  +
                  +

                  Example:

                  +
                  +'filltraffic H' - When used in a room with doors, it will set traffic to HIGH in just that room.
                  +
                  +
                  +

                  alltraffic

                  +

                  Set traffic designations for every single tile of the map (useful for resetting traffic designations).

                  +

                  Traffic Type Codes:

                  +
                  + +++ + + + + + + + + + +
                  H:High Traffic
                  N:Normal Traffic
                  L:Low Traffic
                  R:Restricted Traffic
                  +
                  +

                  Example:

                  +
                  +'alltraffic N' - Set traffic to 'normal' for all tiles.
                  +
                  +
                  +

                  getplants

                  +

                  This tool allows plant gathering and tree cutting by RAW ID. Specify the types +of trees to cut down and/or shrubs to gather by their plant names, separated +by spaces.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + +
                  -t:Select trees only (exclude shrubs)
                  -s:Select shrubs only (exclude trees)
                  -c:Clear designations instead of setting them
                  -x:Apply selected action to all plants except those specified (invert +selection)
                  +
                  +

                  Specifying both -t and -s will have no effect. If no plant IDs are specified, +all valid plant IDs will be listed.

                  +
                  +
                  +
                  +

                  Cleanup and garbage disposal

                  +
                  +

                  clean

                  +

                  Cleans all the splatter that get scattered all over the map, items and +creatures. In an old fortress, this can significantly reduce FPS lag. It can +also spoil your !!FUN!!, so think before you use it.

                  +

                  Options:

                  +
                  + +++ + + + + + + + +
                  map:Clean the map tiles. By default, it leaves mud and snow alone.
                  units:Clean the creatures. Will also clean hostiles.
                  items:Clean all the items. Even a poisoned blade.
                  +
                  +

                  Extra options for 'map':

                  +
                  + +++ + + + + + +
                  mud:Remove mud in addition to the normal stuff.
                  snow:Also remove snow coverings.
                  +
                  +
                  +
                  +

                  spotclean

                  +

                  Works like 'clean map snow mud', but only for the tile under the cursor. Ideal +if you want to keep that bloody entrance 'clean map' would clean up.

                  +
                  +
                  +

                  autodump

                  +

                  This utility lets you quickly move all items designated to be dumped. +Items are instantly moved to the cursor position, the dump flag is unset, +and the forbid flag is set, as if it had been dumped normally. +Be aware that any active dump item tasks still point at the item.

                  +

                  Cursor must be placed on a floor tile so the items can be dumped there.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + + + +
                  destroy:Destroy instead of dumping. Doesn't require a cursor.
                  destroy-here:Destroy items only under the cursor.
                  visible:Only process items that are not hidden.
                  hidden:Only process hidden items.
                  forbidden:Only process forbidden items (default: only unforbidden).
                  +
                  +
                  +
                  +

                  autodump-destroy-here

                  +

                  Destroy items marked for dumping under cursor. Identical to autodump +destroy-here, but intended for use as keybinding.

                  +
                  +
                  +

                  autodump-destroy-item

                  +

                  Destroy the selected item. The item may be selected in the 'k' list, or inside +a container. If called again before the game is resumed, cancels destroy.

                  +
                  +
                  +

                  cleanowned

                  +

                  Confiscates items owned by dwarfs. By default, owned food on the floor +and rotten items are confistacted and dumped.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + + + +
                  all:confiscate all owned items
                  scattered:confiscated and dump all items scattered on the floor
                  x:confiscate/dump items with wear level 'x' and more
                  X:confiscate/dump items with wear level 'X' and more
                  dryrun:a dry run. combine with other options to see what will happen +without it actually happening.
                  +
                  +

                  Example:

                  +
                  +
                  +
                  cleanowned scattered X
                  +
                  This will confiscate rotten and dropped food, garbage on the floors and any +worn items with 'X' damage and above.
                  +
                  +
                  +
                  +
                  +
                  +

                  Bugfixes

                  +
                  +

                  drybuckets

                  +

                  This utility removes water from all buckets in your fortress, allowing them to be safely used for making lye.

                  +
                  +
                  +

                  fixdiplomats

                  +

                  Up to version 0.31.12, Elves only sent Diplomats to your fortress to propose +tree cutting quotas due to a bug; once that bug was fixed, Elves stopped caring +about excess tree cutting. This command adds a Diplomat position to all Elven +civilizations, allowing them to negotiate tree cutting quotas (and allowing you +to violate them and potentially start wars) in case you haven't already modified +your raws accordingly.

                  +
                  +
                  +

                  fixmerchants

                  +

                  This command adds the Guild Representative position to all Human civilizations, +allowing them to make trade agreements (just as they did back in 0.28.181.40d +and earlier) in case you haven't already modified your raws accordingly.

                  +
                  +
                  +

                  fixveins

                  +

                  Removes invalid references to mineral inclusions and restores missing ones. +Use this if you broke your embark with tools like tiletypes, or if you +accidentally placed a construction on top of a valuable mineral floor.

                  +
                  +
                  +

                  tweak

                  +

                  Contains various tweaks for minor bugs.

                  +

                  One-shot subcommands:

                  + +++ + + + + + + + + + +
                  clear-missing:Remove the missing status from the selected unit. +This allows engraving slabs for ghostly, but not yet +found, creatures.
                  clear-ghostly:Remove the ghostly status from the selected unit and mark +it as dead. This allows getting rid of bugged ghosts +which do not show up in the engraving slab menu at all, +even after using clear-missing. It works, but is +potentially very dangerous - so use with care. Probably +(almost certainly) it does not have the same effects like +a proper burial. You've been warned.
                  fixmigrant:Remove the resident/merchant flag from the selected unit. +Intended to fix bugged migrants/traders who stay at the +map edge and don't enter your fort. Only works for +dwarves (or generally the player's race in modded games). +Do NOT abuse this for 'real' caravan merchants (if you +really want to kidnap them, use 'tweak makeown' instead, +otherwise they will have their clothes set to forbidden etc).
                  makeown:Force selected unit to become a member of your fort. +Can be abused to grab caravan merchants and escorts, even if +they don't belong to the player's race. Foreign sentients +(humans, elves) can be put to work, but you can't assign rooms +to them and they don't show up in DwarfTherapist because the +game treats them like pets. Grabbing draft animals from +a caravan can result in weirdness (animals go insane or berserk +and are not flagged as tame), but you are allowed to mark them +for slaughter. Grabbing wagons results in some funny spam, then +they are scuttled.
                  +

                  Subcommands that persist until disabled or DF quit:

                  + +++ + + + + + + + + + + + + + + + + + + + + + + + + + +
                  stable-cursor:Saves the exact cursor position between t/q/k/d/etc menus of dwarfmode.
                  patrol-duty:Makes Train orders not count as patrol duty to stop unhappy thoughts. +Does NOT fix the problem when soldiers go off-duty (i.e. civilian).
                  readable-build-plate:
                   Fixes rendering of creature weight limits in pressure plate build menu.
                  stable-temp:Fixes performance bug 6012 by squashing jitter in temperature updates. +In very item-heavy forts with big stockpiles this can improve FPS by 50-100%
                  fast-heat:Further improves temperature update performance by ensuring that 1 degree +of item temperature is crossed in no more than specified number of frames +when updating from the environment temperature. This reduces the time it +takes for stable-temp to stop updates again when equilibrium is disturbed.
                  fix-dimensions:Fixes subtracting small amount of thread/cloth/liquid from a stack +by splitting the stack and subtracting from the remaining single item. +This is a necessary addition to the binary patch in bug 808.
                  advmode-contained:
                   Works around bug 6202, i.e. custom reactions with container inputs +in advmode. The issue is that the screen tries to force you to select +the contents separately from the container. This forcefully skips child +reagents.
                  fast-trade:Makes Shift-Enter in the Move Goods to Depot and Trade screens select +the current item (fully, in case of a stack), and scroll down one line.
                  military-stable-assign:
                   Preserve list order and cursor position when assigning to squad, +i.e. stop the rightmost list of the Positions page of the military +screen from constantly resetting to the top.
                  military-color-assigned:
                   Color squad candidates already assigned to other squads in brown/green +to make them stand out more in the list.
                  +
                  +
                  +
                  +

                  Mode switch and reclaim

                  +
                  +

                  lair

                  +

                  This command allows you to mark the map as 'monster lair', preventing item +scatter on abandon. When invoked as 'lair reset', it does the opposite.

                  +

                  Unlike reveal, this command doesn't save the information about tiles - you +won't be able to restore state of real monster lairs using 'lair reset'.

                  +

                  Options:

                  +
                  + +++ + + + + + +
                  lair:Mark the map as monster lair
                  lair reset:Mark the map as ordinary (not lair)
                  +
                  +
                  +
                  +

                  mode

                  +

                  This command lets you see and change the game mode directly. +Not all combinations are good for every situation and most of them will +produce undesirable results. There are a few good ones though.

                  +
                  +

                  Example

                  +

                  You are in fort game mode, managing your fortress and paused. +You switch to the arena game mode, assume control of a creature and then +switch to adventure game mode(1). +You just lost a fortress and gained an adventurer. +You could also do this. +You are in fort game mode, managing your fortress and paused at the esc menu. +You switch to the adventure game mode, then use Dfusion to assume control of a creature and then +save or retire. +You just created a returnable mountain home and gained an adventurer.

                  +
                  +

                  I take no responsibility of anything that happens as a result of using this tool

                  +
                  +
                  +
                  +

                  Visualizer and data export

                  +
                  +

                  ssense / stonesense

                  +

                  An isometric visualizer that runs in a second window. This requires working +graphics acceleration and at least a dual core CPU (otherwise it will slow +down DF).

                  +

                  All the data resides in the 'stonesense' directory. For detailed instructions, +see stonesense/README.txt

                  +

                  Compatible with Windows > XP SP3 and most modern Linux distributions.

                  +

                  Older versions, support and extra graphics can be found in the bay12 forum +thread: http://www.bay12forums.com/smf/index.php?topic=43260.0

                  +

                  Some additional resources: +http://df.magmawiki.com/index.php/Utility:Stonesense/Content_repository

                  +
                  +
                  +

                  mapexport

                  +

                  Export the current loaded map as a file. This will be eventually usable +with visualizers.

                  +
                  +
                  +

                  dwarfexport

                  +

                  Export dwarves to RuneSmith-compatible XML.

                  +
                  +
                  +
                  +

                  Job management

                  +
                  +

                  job

                  +

                  Command for general job query and manipulation.

                  +
                  +
                  Options:
                  +
                  +
                  no extra options
                  +
                  Print details of the current job. The job can be selected +in a workshop, or the unit/jobs screen.
                  +
                  list
                  +
                  Print details of all jobs in the selected workshop.
                  +
                  item-material <item-idx> <material[:subtoken]>
                  +
                  Replace the exact material id in the job item.
                  +
                  item-type <item-idx> <type[:subtype]>
                  +
                  Replace the exact item type id in the job item.
                  +
                  +
                  +
                  +
                  +
                  +

                  job-material

                  +

                  Alter the material of the selected job.

                  +

                  Invoked as:

                  +
                  +job-material <inorganic-token>
                  +
                  +

                  Intended to be used as a keybinding:

                  +
                  +
                    +
                  • In 'q' mode, when a job is highlighted within a workshop or furnace, +changes the material of the job. Only inorganic materials can be used +in this mode.
                  • +
                  • In 'b' mode, during selection of building components positions the cursor +over the first available choice with the matching material.
                  • +
                  +
                  +
                  +
                  +

                  job-duplicate

                  +
                  +
                  Duplicate the selected job in a workshop:
                  +
                    +
                  • In 'q' mode, when a job is highlighted within a workshop or furnace building, +instantly duplicates the job.
                  • +
                  +
                  +
                  +
                  +
                  +

                  workflow

                  +

                  Manage control of repeat jobs.

                  +

                  Usage:

                  +
                  +
                  +
                  workflow enable [option...], workflow disable [option...]
                  +

                  If no options are specified, enables or disables the plugin. +Otherwise, enables or disables any of the following options:

                  +
                    +
                  • drybuckets: Automatically empty abandoned water buckets.
                  • +
                  • auto-melt: Resume melt jobs when there are objects to melt.
                  • +
                  +
                  +
                  workflow jobs
                  +
                  List workflow-controlled jobs (if in a workshop, filtered by it).
                  +
                  workflow list
                  +
                  List active constraints, and their job counts.
                  +
                  workflow count <constraint-spec> <cnt-limit> [cnt-gap], workflow amount <constraint-spec> <cnt-limit> [cnt-gap]
                  +
                  Set a constraint. The first form counts each stack as only 1 item.
                  +
                  workflow unlimit <constraint-spec>
                  +
                  Delete a constraint.
                  +
                  +
                  +
                  +

                  Function

                  +

                  When the plugin is enabled, it protects all repeat jobs from removal. +If they do disappear due to any cause, they are immediately re-added to their +workshop and suspended.

                  +

                  In addition, when any constraints on item amounts are set, repeat jobs that +produce that kind of item are automatically suspended and resumed as the item +amount goes above or below the limit. The gap specifies how much below the limit +the amount has to drop before jobs are resumed; this is intended to reduce +the frequency of jobs being toggled.

                  +
                  +
                  +

                  Constraint examples

                  +

                  Keep metal bolts within 900-1000, and wood/bone within 150-200.

                  +
                  +workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100
                  +workflow amount AMMO:ITEM_AMMO_BOLTS/WOOD,BONE 200 50
                  +
                  +

                  Keep the number of prepared food & drink stacks between 90 and 120

                  +
                  +workflow count FOOD 120 30
                  +workflow count DRINK 120 30
                  +
                  +

                  Make sure there are always 25-30 empty bins/barrels/bags.

                  +
                  +workflow count BIN 30
                  +workflow count BARREL 30
                  +workflow count BOX/CLOTH,SILK,YARN 30
                  +
                  +

                  Make sure there are always 15-20 coal and 25-30 copper bars.

                  +
                  +workflow count BAR//COAL 20
                  +workflow count BAR//COPPER 30
                  +
                  +

                  Collect 15-20 sand bags and clay boulders.

                  +
                  +workflow count POWDER_MISC/SAND 20
                  +workflow count BOULDER/CLAY 20
                  +
                  +

                  Make sure there are always 80-100 units of dimple dye.

                  +
                  +  workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20
                  +
                  +In order for this to work, you have to set the material of the PLANT input
                  +on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material'
                  +command.
                  +
                  +
                  +
                  +
                  +
                  +

                  Fortress activity management

                  +
                  +

                  seedwatch

                  +

                  Tool for turning cooking of seeds and plants on/off depending on how much you +have of them.

                  +

                  See 'seedwatch help' for detailed description.

                  +
                  +
                  +

                  zone

                  +

                  Helps a bit with managing activity zones (pens, pastures and pits) and cages.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + + + + + + + + + + + + + + + + + +
                  set:Set zone or cage under cursor as default for future assigns.
                  assign:Assign unit(s) to the pen or pit marked with the 'set' command. +If no filters are set a unit must be selected in the in-game ui. +Can also be followed by a valid zone id which will be set +instead.
                  unassign:Unassign selected creature from it's zone.
                  nick:Mass-assign nicknames, must be followed by the name you want +to set.
                  remnick:Mass-remove nicknames.
                  tocages:Assign unit(s) to cages inside a pasture.
                  uinfo:Print info about unit(s). If no filters are set a unit must +be selected in the in-game ui.
                  zinfo:Print info about zone(s). If no filters are set zones under +the cursor are listed.
                  verbose:Print some more info.
                  filters:Print list of valid filter options.
                  examples:Print some usage examples.
                  not:Negates the next filter keyword.
                  +
                  +

                  Filters:

                  +
                  + +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                  all:Process all units (to be used with additional filters).
                  count:Must be followed by a number. Process only n units (to be used +with additional filters).
                  unassigned:Not assigned to zone, chain or built cage.
                  minage:Minimum age. Must be followed by number.
                  maxage:Maximum age. Must be followed by number.
                  race:Must be followed by a race RAW ID (e.g. BIRD_TURKEY, ALPACA, +etc). Negatable.
                  caged:In a built cage. Negatable.
                  own:From own civilization. Negatable.
                  merchant:Is a merchant / belongs to a merchant. Should only be used for +pitting, not for stealing animals (slaughter should work).
                  war:Trained war creature. Negatable.
                  hunting:Trained hunting creature. Negatable.
                  tamed:Creature is tame. Negatable.
                  trained:Creature is trained. Finds war/hunting creatures as well as +creatures who have a training level greater than 'domesticated'. +If you want to specifically search for war/hunting creatures use +'war' or 'hunting' Negatable.
                  trainablewar:Creature can be trained for war (and is not already trained for +war/hunt). Negatable.
                  trainablehunt:Creature can be trained for hunting (and is not already trained +for war/hunt). Negatable.
                  male:Creature is male. Negatable.
                  female:Creature is female. Negatable.
                  egglayer:Race lays eggs. Negatable.
                  grazer:Race is a grazer. Negatable.
                  milkable:Race is milkable. Negatable.
                  +
                  +
                  +

                  Usage with single units

                  +

                  One convenient way to use the zone tool is to bind the command 'zone assign' to +a hotkey, maybe also the command 'zone set'. Place the in-game cursor over +a pen/pasture or pit, use 'zone set' to mark it. Then you can select units +on the map (in 'v' or 'k' mode), in the unit list or from inside cages +and use 'zone assign' to assign them to their new home. Allows pitting your +own dwarves, by the way.

                  +
                  +
                  +

                  Usage with filters

                  +

                  All filters can be used together with the 'assign' command.

                  +

                  Restrictions: It's not possible to assign units who are inside built cages +or chained because in most cases that won't be desirable anyways. +It's not possible to cage owned pets because in that case the owner +uncages them after a while which results in infinite hauling back and forth.

                  +

                  Usually you should always use the filter 'own' (which implies tame) unless you +want to use the zone tool for pitting hostiles. 'own' ignores own dwarves unless +you specify 'race DWARF' (so it's safe to use 'assign all own' to one big +pasture if you want to have all your animals at the same place). 'egglayer' and +'milkable' should be used together with 'female' unless you have a mod with +egg-laying male elves who give milk or whatever. Merchants and their animals are +ignored unless you specify 'merchant' (pitting them should be no problem, +but stealing and pasturing their animals is not a good idea since currently they +are not properly added to your own stocks; slaughtering them should work).

                  +

                  Most filters can be negated (e.g. 'not grazer' -> race is not a grazer).

                  +
                  +
                  +

                  Mass-renaming

                  +

                  Using the 'nick' command you can set the same nickname for multiple units. +If used without 'assign', 'all' or 'count' it will rename all units in the +current default target zone. Combined with 'assign', 'all' or 'count' (and +further optional filters) it will rename units matching the filter conditions.

                  +
                  +
                  +

                  Cage zones

                  +

                  Using the 'tocages' command you can assign units to a set of cages, for example +a room next to your butcher shop(s). They will be spread evenly among available +cages to optimize hauling to and butchering from them. For this to work you need +to build cages and then place one pen/pasture activity zone above them, covering +all cages you want to use. Then use 'zone set' (like with 'assign') and use +'zone tocages filter1 filter2 ...'. 'tocages' overwrites 'assign' because it +would make no sense, but can be used together with 'nick' or 'remnick' and all +the usual filters.

                  +
                  +
                  +

                  Examples

                  +
                  +
                  zone assign all own ALPACA minage 3 maxage 10
                  +
                  Assign all own alpacas who are between 3 and 10 years old to the selected +pasture.
                  +
                  zone assign all own caged grazer nick ineedgrass
                  +
                  Assign all own grazers who are sitting in cages on stockpiles (e.g. after +buying them from merchants) to the selected pasture and give them +the nickname 'ineedgrass'.
                  +
                  zone assign all own not grazer not race CAT
                  +
                  Assign all own animals who are not grazers, excluding cats.
                  +
                  zone assign count 5 own female milkable
                  +
                  Assign up to 5 own female milkable creatures to the selected pasture.
                  +
                  zone assign all own race DWARF maxage 2
                  +
                  Throw all useless kids into a pit :)
                  +
                  zone nick donttouchme
                  +
                  Nicknames all units in the current default zone or cage to 'donttouchme'. +Mostly intended to be used for special pastures or cages which are not marked +as rooms you want to protect from autobutcher.
                  +
                  zone tocages count 50 own tame male not grazer
                  +
                  Stuff up to 50 owned tame male animals who are not grazers into cages built +on the current default zone.
                  +
                  +
                  +
                  +
                  +

                  autonestbox

                  +

                  Assigns unpastured female egg-layers to nestbox zones. Requires that you create +pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox +must be in the top left corner. Only 1 unit will be assigned per pen, regardless +of the size. The age of the units is currently not checked, most birds grow up +quite fast. Egglayers who are also grazers will be ignored, since confining them +to a 1x1 pasture is not a good idea. Only tame and domesticated own units are +processed since pasturing half-trained wild egglayers could destroy your neat +nestbox zones when they revert to wild. When called without options autonestbox +will instantly run once.

                  +

                  Options:

                  +
                  + +++ + + + + + + + +
                  start:Start running every X frames (df simulation ticks). +Default: X=6000, which would be every 60 seconds at 100fps.
                  stop:Stop running automatically.
                  sleep:Must be followed by number X. Changes the timer to sleep X +frames between runs.
                  +
                  +
                  +
                  +

                  autobutcher

                  +

                  Assigns lifestock for slaughter once it reaches a specific count. Requires that +you add the target race(s) to a watch list. Only tame units will be processed.

                  +

                  Named units will be completely ignored (to protect specific animals from +autobutcher you can give them nicknames with the tool 'rename unit' for single +units or with 'zone nick' to mass-rename units in pastures and cages).

                  +

                  Creatures trained for war or hunting will be ignored as well.

                  +

                  Creatures assigned to cages will be ignored if the cage is defined as a room +(to avoid butchering unnamed zoo animals).

                  +

                  Once you have too much adults, the oldest will be butchered first. +Once you have too much kids, the youngest will be butchered first. +If you don't set any target count the following default will be used: +1 male kid, 5 female kids, 1 male adult, 5 female adults.

                  +

                  Options:

                  +
                  + +++ + + + + + + + + + + + + + + + + + + + + + + + + + + +
                  start:Start running every X frames (df simulation ticks). +Default: X=6000, which would be every 60 seconds at 100fps.
                  stop:Stop running automatically.
                  sleep:Must be followed by number X. Changes the timer to sleep +X frames between runs.
                  watch R:Start watching a race. R can be a valid race RAW id (ALPACA, +BIRD_TURKEY, etc) or a list of ids seperated by spaces or +the keyword 'all' which affects all races on your current +watchlist.
                  unwatch R:Stop watching race(s). The current target settings will be +remembered. R can be a list of ids or the keyword 'all'.
                  forget R:Stop watching race(s) and forget it's/their target settings. +R can be a list of ids or the keyword 'all'.
                  autowatch:Automatically adds all new races (animals you buy from merchants, +tame yourself or get from migrants) to the watch list using +default target count.
                  noautowatch:Stop auto-adding new races to the watchlist.
                  list:Print the current status and watchlist.
                  list_export:Print status and watchlist in a format which can be used +to import them to another savegame (see notes).
                  target fk mk fa ma R:
                   Set target count for specified race(s). +fk = number of female kids, +mk = number of male kids, +fa = number of female adults, +ma = number of female adults. +R can be a list of ids or the keyword 'all' or 'new'. +R = 'all': change target count for all races on watchlist +and set the new default for the future. R = 'new': don't touch +current settings on the watchlist, only set the new default +for future entries.
                  example:Print some usage examples.
                  +
                  +

                  Examples:

                  +

                  You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, +1 male) of the race alpaca. Once the kids grow up the oldest adults will get +slaughtered. Excess kids will get slaughtered starting with the youngest +to allow that the older ones grow into adults. Any unnamed cats will +be slaughtered as soon as possible.

                  +
                  +autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY
                  +autobutcher target 0 0 0 0 CAT
                  +autobutcher watch ALPACA BIRD_TURKEY CAT
                  +autobutcher start
                  +
                  +

                  Automatically put all new races onto the watchlist and mark unnamed tame units +for slaughter as soon as they arrive in your fort. Settings already made +for specific races will be left untouched.

                  +
                  +autobutcher target 0 0 0 0 new
                  +autobutcher autowatch
                  +autobutcher start
                  +
                  +

                  Stop watching the races alpaca and cat, but remember the target count +settings so that you can use 'unwatch' without the need to enter the +values again. Note: 'autobutcher unwatch all' works, but only makes sense +if you want to keep the plugin running with the 'autowatch' feature or manually +add some new races with 'watch'. If you simply want to stop it completely use +'autobutcher stop' instead.

                  +
                  +autobutcher unwatch ALPACA CAT
                  +
                  +

                  Note:

                  +

                  Settings and watchlist are stored in the savegame, so that you can have +different settings for each world. If you want to copy your watchlist to +another savegame you can use the command list_export:

                  +
                  +Load savegame where you made the settings.
                  +Start a CMD shell and navigate to the df directory. Type the following into the shell:
                  +dfhack-run autobutcher list_export > autobutcher.bat
                  +Load the savegame where you want to copy the settings to, run the batch file (from the shell):
                  +autobutcher.bat
                  +
                  +
                  +
                  +

                  autolabor

                  +

                  Automatically manage dwarf labors.

                  +

                  When enabled, autolabor periodically checks your dwarves and enables or +disables labors. It tries to keep as many dwarves as possible busy but +also tries to have dwarves specialize in specific skills.

                  +
                  +

                  Note

                  +

                  Warning: autolabor will override any manual changes you make to labors +while it is enabled.

                  +
                  +

                  For detailed usage information, see 'help autolabor'.

                  +
                  +
                  +
                  +

                  Other

                  +
                  +

                  catsplosion

                  +

                  Makes cats just multiply. It is not a good idea to run this more than once or +twice.

                  +
                  +
                  +

                  dfusion

                  +

                  This is the DFusion lua plugin system by warmist/darius, running as a DFHack plugin.

                  +

                  See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=69682.15

                  +

                  Confirmed working DFusion plugins:

                  + +++ + + + +
                  simple_embark:allows changing the number of dwarves available on embark.
                  +
                  +

                  Note

                  +
                    +
                  • Some of the DFusion plugins aren't completely ported yet. This can lead to crashes.
                  • +
                  • This is currently working only on Windows.
                  • +
                  • The game will be suspended while you're using dfusion. Don't panic when it doen't respond.
                  • +
                  +
                  +
                  +
                  +

                  misery

                  +

                  When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).

                  +

                  Usage:

                  + +++ + + + + + + + + + + + + +
                  misery enable n:
                   enable misery with optional magnitude n. If specified, n must be positive.
                  misery n:same as "misery enable n"
                  misery enable:same as "misery enable 2"
                  misery disable:stop adding new negative thoughts. This will not remove existing duplicated thoughts. Equivalent to "misery 1"
                  misery clear:remove fake thoughts added in this session of DF. Saving makes them permanent! Does not change factor.
                  +
                  +
                  +
                  +
                  +

                  Scripts

                  +

                  Lua or ruby scripts placed in the hack/scripts/ directory are considered for +execution as if they were native DFHack commands. They are listed at the end +of the 'ls' command output.

                  +

                  Note: scripts in subdirectories of hack/scripts/ can still be called, but will +only be listed by ls if called as 'ls -a'. This is intended as a way to hide +scripts that are obscure, developer-oriented, or should be used as keybindings.

                  +

                  Some notable scripts:

                  +
                  +

                  fix/*

                  +

                  Scripts in this subdirectory fix various bugs and issues, some of them obscure.

                  +
                    +
                  • fix/dead-units

                    +

                    Removes uninteresting dead units from the unit list. Doesn't seem to give any +noticeable performance gain, but migrants normally stop if the unit list grows +to around 3000 units, and this script reduces it back.

                    +
                  • +
                  • fix/population-cap

                    +

                    Run this after every migrant wave to ensure your population cap is not exceeded. +The issue with the cap is that it is compared to the population number reported +by the last caravan, so once it drops below the cap, migrants continue to come +until that number is updated again.

                    +
                  • +
                  • fix/stable-temp

                    +

                    Instantly sets the temperature of all free-lying items to be in equilibrium with +the environment and stops temperature updates. In order to maintain this efficient +state however, use tweak stable-temp and tweak fast-heat.

                    +
                  • +
                  • fix/item-occupancy

                    +

                    Diagnoses and fixes issues with nonexistant 'items occupying site', usually +caused by autodump bugs or other hacking mishaps.

                    +
                  • +
                  +
                  +
                  +

                  gui/*

                  +

                  Scripts that implement dialogs inserted into the main game window are put in this +directory.

                  +
                  +
                  +

                  quicksave

                  +

                  If called in dwarf mode, makes DF immediately auto-save the game by setting a flag +normally used in seasonal auto-save.

                  +
                  +
                  +

                  setfps

                  +

                  Run setfps <number> to set the FPS cap at runtime, in case you want to watch +combat in slow motion or something :)

                  +
                  +
                  +

                  siren

                  +

                  Wakes up sleeping units, cancels breaks and stops parties either everywhere, +or in the burrows given as arguments. In return, adds bad thoughts about +noise, tiredness and lack of protection. Also, the units with interrupted +breaks will go on break again a lot sooner. The script is intended for +emergencies, e.g. when a siege appears, and all your military is partying.

                  +
                  +
                  +

                  growcrops

                  +

                  Instantly grow seeds inside farming plots.

                  +

                  With no argument, this command list the various seed types currently in +use in your farming plots. +With a seed type, the script will grow 100 of these seeds, ready to be +harvested. You can change the number with a 2nd argument.

                  +

                  For exemple, to grow 40 plump helmet spawn:

                  +
                  +growcrops plump 40
                  +
                  +
                  +
                  +

                  removebadthoughts

                  +

                  This script remove negative thoughts from your dwarves. Very useful against +tantrum spirals.

                  +

                  With a selected unit in 'v' mode, will clear this unit's mind, otherwise +clear all your fort's units minds.

                  +

                  Individual dwarf happiness may not increase right after this command is run, +but in the short term your dwarves will get much more joyful. +The thoughts are set to be very old, and the game will remove them soon when +you unpause.

                  +

                  With the optional -v parameter, the script will dump the negative thoughts +it removed.

                  +
                  +
                  +

                  slayrace

                  +

                  Kills any unit of a given race.

                  +

                  With no argument, lists the available races.

                  +

                  With the special argument 'him', targets only the selected creature.

                  +

                  Any non-dead non-caged unit of the specified race gets its blood_count +set to 0, which means immediate death at the next game tick. For creatures +such as vampires, also set animal.vanish_countdown to 2.

                  +

                  An alternate mode is selected by adding a 2nd argument to the command, +magma. In this case, a column of 7/7 magma is generated on top of the +targets until they die (Warning: do not call on magma-safe creatures. Also, +using this mode for birds is not recommanded.)

                  +

                  Will target any unit on a revealed tile of the map, including ambushers.

                  +

                  Ex:

                  +
                  +slayrace gob
                  +
                  +

                  To kill a single creature, select the unit with the 'v' cursor and:

                  +
                  +slayrace him
                  +
                  +

                  To purify all elves on the map with fire (may have side-effects):

                  +
                  +slayrace elve magma
                  +
                  +
                  +
                  +

                  magmasource

                  +

                  Create an infinite magma source on a tile.

                  +

                  This script registers a map tile as a magma source, and every 12 game ticks +that tile receives 1 new unit of flowing magma.

                  +

                  Place the game cursor where you want to create the source (must be a +flow-passable tile, and not too high in the sky) and call:

                  +
                  +magmasource here
                  +
                  +

                  To add more than 1 unit everytime, call the command again.

                  +

                  To delete one source, place the cursor over its tile and use delete-here. +To remove all placed sources, call magmasource stop.

                  +

                  With no argument, this command shows an help message and list existing sources.

                  +
                  +
                  +

                  digfort

                  +

                  A script to designate an area for digging according to a plan in csv format.

                  +

                  This script, inspired from quickfort, can designate an area for digging. +Your plan should be stored in a .csv file like this:

                  +
                  +# this is a comment
                  +d;d;u;d;d;skip this tile;d
                  +d;d;d;i
                  +
                  +

                  Available tile shapes are named after the 'dig' menu shortcuts: +d for dig, u for upstairs, d downstairs, i updown, +h channel, r upward ramp, x remove designation. +Unrecognized characters are ignored (eg the 'skip this tile' in the sample).

                  +

                  Empty lines and data after a # are ignored as comments. +To skip a row in your design, use a single ;.

                  +

                  The script takes the plan filename, starting from the root df folder.

                  +
                  +
                  +

                  superdwarf

                  +

                  Similar to fastdwarf, per-creature.

                  +

                  To make any creature superfast, target it ingame using 'v' and:

                  +
                  +superdwarf add
                  +
                  +

                  Other options available: del, clear, list.

                  +

                  This plugin also shortens the 'sleeping' and 'on break' periods of targets.

                  +
                  +
                  +

                  drainaquifer

                  +

                  Remove all 'aquifer' tag from the map blocks. Irreversible.

                  +
                  +
                  +

                  deathcause

                  +

                  Focus a body part ingame, and this script will display the cause of death of +the creature.

                  +
                  +
                  +
                  +

                  In-game interface tools

                  +

                  These tools work by displaying dialogs or overlays in the game window, and +are mostly implemented by lua scripts.

                  +
                  +

                  Note

                  +

                  In order to avoid user confusion, as a matter of policy all these tools +display the word "DFHack" on the screen somewhere while active.

                  +

                  As an exception, the tweak plugin described above does not follow this +guideline because it arguably just fixes small usability bugs in the game UI.

                  +
                  +
                  +

                  Dwarf Manipulator

                  +

                  Implemented by the manipulator plugin. To activate, open the unit screen and +press 'l'.

                  +

                  This tool implements a Dwarf Therapist-like interface within the game UI. The +far left column displays the unit's Happiness (color-coded based on its +value), and the right half of the screen displays each dwarf's labor settings +and skill levels (0-9 for Dabbling thru Professional, A-E for Great thru Grand +Master, and U-Z for Legendary thru Legendary+5). Cells with red backgrounds +denote skills not controlled by labors.

                  +

                  Use the arrow keys or number pad to move the cursor around, holding Shift to +move 10 tiles at a time.

                  +

                  Press the Z-Up (<) and Z-Down (>) keys to move quickly between labor/skill +categories. The numpad Z-Up and Z-Down keys seek to the first or last unit +in the list. Backspace seeks to the top left corner.

                  +

                  Press Enter to toggle the selected labor for the selected unit, or Shift+Enter +to toggle all labors within the selected category.

                  +

                  Press the +- keys to sort the unit list according to the currently selected +skill/labor, and press the */ keys to sort the unit list by Name, Profession, +Happiness, or Arrival order (using Tab to select which sort method to use here).

                  +

                  With a unit selected, you can press the "v" key to view its properties (and +possibly set a custom nickname or profession) or the "c" key to exit +Manipulator and zoom to its position within your fortress.

                  +

                  The following mouse shortcuts are also available:

                  +
                    +
                  • Click on a column header to sort the unit list. Left-click to sort it in one +direction (descending for happiness or labors/skills, ascending for name or +profession) and right-click to sort it in the opposite direction.
                  • +
                  • Left-click on a labor cell to toggle that labor. Right-click to move the +cursor onto that cell instead of toggling it.
                  • +
                  • Left-click on a unit's name or profession to view its properties.
                  • +
                  • Right-click on a unit's name or profession to zoom to it.
                  • +
                  +

                  Pressing ESC normally returns to the unit screen, but Shift-ESC would exit +directly to the main dwarf mode screen.

                  +
                  +
                  +

                  gui/liquids

                  +

                  To use, bind to a key and activate in the 'k' mode.

                  +

                  While active, use the suggested keys to switch the usual liquids parameters, and Enter +to select the target area and apply changes.

                  +
                  +
                  +

                  gui/mechanisms

                  +

                  To use, bind to a key and activate in the 'q' mode.

                  +

                  Lists mechanisms connected to the building, and their links. Navigating the list centers +the view on the relevant linked buildings.

                  +

                  To exit, press ESC or Enter; ESC recenters on the original building, while Enter leaves +focus on the current one. Shift-Enter has an effect equivalent to pressing Enter, and then +re-entering the mechanisms ui.

                  +
                  +
                  +

                  gui/rename

                  +

                  Backed by the rename plugin, this script allows entering the desired name +via a simple dialog in the game ui.

                  +
                    +
                  • gui/rename [building] in 'q' mode changes the name of a building.

                    +

                    The selected building must be one of stockpile, workshop, furnace, trap, or siege engine. +It is also possible to rename zones from the 'i' menu.

                    +
                  • +
                  • gui/rename [unit] with a unit selected changes the nickname.

                    +
                  • +
                  • gui/rename unit-profession changes the selected unit's custom profession name.

                    +
                  • +
                  +

                  The building or unit options are automatically assumed when in relevant ui state.

                  +
                  +
                  +

                  gui/room-list

                  +

                  To use, bind to a key and activate in the 'q' mode, either immediately or after opening +the assign owner page.

                  +

                  The script lists other rooms owned by the same owner, or by the unit selected in the assign +list, and allows unassigning them.

                  +
                  +
                  +

                  gui/choose-weapons

                  +

                  Bind to a key, and activate in the Equip->View/Customize page of the military screen.

                  +

                  Depending on the cursor location, it rewrites all 'individual choice weapon' entries +in the selected squad or position to use a specific weapon type matching the assigned +unit's top skill. If the cursor is in the rightmost list over a weapon entry, it rewrites +only that entry, and does it even if it is not 'individual choice'.

                  +

                  Rationale: individual choice seems to be unreliable when there is a weapon shortage, +and may lead to inappropriate weapons being selected.

                  +
                  +
                  +
                  +

                  Behavior Mods

                  +

                  These plugins, when activated via configuration UI or by detecting certain +structures in RAWs, modify the game engine behavior concerning the target +objects to add features not otherwise present.

                  +
                  +

                  DISCLAIMER

                  +

                  The plugins in this section have mostly been created for fun as an interesting +technical challenge, and do not represent any long-term plans to produce more +similar modifications of the game.

                  +
                  +
                  +

                  Siege Engine

                  +

                  The siege-engine plugin enables siege engines to be linked to stockpiles, and +aimed at an arbitrary rectangular area across Z levels, instead of the original +four directions. Also, catapults can be ordered to load arbitrary objects, not +just stones.

                  +
                  +

                  Rationale

                  +

                  Siege engines are a very interesting feature, but sadly almost useless in the current state +because they haven't been updated since 2D and can only aim in four directions. This is an +attempt to bring them more up to date until Toady has time to work on it. Actual improvements, +e.g. like making siegers bring their own, are something only Toady can do.

                  +
                  +
                  +

                  Configuration UI

                  +

                  The configuration front-end to the plugin is implemented by the gui/siege-engine +script. Bind it to a key and activate after selecting a siege engine in 'q' mode.

                  +

                  The main mode displays the current target, selected ammo item type, linked stockpiles and +the allowed operator skill range. The map tile color is changed to signify if it can be +hit by the selected engine: green for fully reachable, blue for out of range, red for blocked, +yellow for partially blocked.

                  +

                  Pressing 'r' changes into the target selection mode, which works by highlighting two points +with Enter like all designations. When a target area is set, the engine projectiles are +aimed at that area, or units within it (this doesn't actually change the original aiming +code, instead the projectile trajectory parameters are rewritten as soon as it appears).

                  +

                  After setting the target in this way for one engine, you can 'paste' the same area into others +just by pressing 'p' in the main page of this script. The area to paste is kept until you quit +DF, or select another area manually.

                  +

                  Pressing 't' switches to a mode for selecting a stockpile to take ammo from.

                  +

                  Exiting from the siege engine script via ESC reverts the view to the state prior to starting +the script. Shift-ESC retains the current viewport, and also exits from the 'q' mode to main +menu.

                  +
                  +
                  +
                  +

                  Power Meter

                  +

                  The power-meter plugin implements a modified pressure plate that detects power being +supplied to gear boxes built in the four adjacent N/S/W/E tiles.

                  +

                  The configuration front-end is implemented by the gui/power-meter script. Bind it to a +key and activate after selecting Pressure Plate in the build menu.

                  +

                  The script follows the general look and feel of the regular pressure plate build +configuration page, but configures parameters relevant to the modded power meter building.

                  +
                  +
                  +

                  Steam Engine

                  +

                  The steam-engine plugin detects custom workshops with STEAM_ENGINE in +their token, and turns them into real steam engines.

                  +
                  +

                  Rationale

                  +

                  The vanilla game contains only water wheels and windmills as sources of +power, but windmills give relatively little power, and water wheels require +flowing water, which must either be a real river and thus immovable and +limited in supply, or actually flowing and thus laggy.

                  +

                  Steam engines are an alternative to water reactors that actually makes +sense, and hopefully doesn't lag. Also, unlike e.g. animal treadmills, +it can be done just by combining existing features of the game engine +in a new way with some glue code and a bit of custom logic.

                  +
                  +
                  +

                  Construction

                  +

                  The workshop needs water as its input, which it takes via a +passable floor tile below it, like usual magma workshops do. +The magma version also needs magma.

                  +
                  +

                  ISSUE

                  +

                  Since this building is a machine, and machine collapse +code cannot be hooked, it would collapse over true open space. +As a loophole, down stair provides support to machines, while +being passable, so use them.

                  +
                  +

                  After constructing the building itself, machines can be connected +to the edge tiles that look like gear boxes. Their exact position +is extracted from the workshop raws.

                  +
                  +

                  ISSUE

                  +

                  Like with collapse above, part of the code involved in +machine connection cannot be hooked. As a result, the workshop +can only immediately connect to machine components built AFTER it. +This also means that engines cannot be chained without intermediate +short axles that can be built later than both of the engines.

                  +
                  +
                  +
                  +

                  Operation

                  +

                  In order to operate the engine, queue the Stoke Boiler job (optionally +on repeat). A furnace operator will come, possibly bringing a bar of fuel, +and perform it. As a result, a "boiling water" item will appear +in the 't' view of the workshop.

                  +
                  +

                  Note

                  +

                  The completion of the job will actually consume one unit +of the appropriate liquids from below the workshop. This means +that you cannot just raise 7 units of magma with a piston and +have infinite power. However, liquid consumption should be slow +enough that water can be supplied by a pond zone bucket chain.

                  +
                  +

                  Every such item gives 100 power, up to a limit of 300 for coal, +and 500 for a magma engine. The building can host twice that +amount of items to provide longer autonomous running. When the +boiler gets filled to capacity, all queued jobs are suspended; +once it drops back to 3+1 or 5+1 items, they are re-enabled.

                  +

                  While the engine is providing power, steam is being consumed. +The consumption speed includes a fixed 10% waste rate, and +the remaining 90% are applied proportionally to the actual +load in the machine. With the engine at nominal 300 power with +150 load in the system, it will consume steam for actual +300*(10% + 90%*150/300) = 165 power.

                  +

                  Masterpiece mechanism and chain will decrease the mechanical +power drawn by the engine itself from 10 to 5. Masterpiece +barrel decreases waste rate by 4%. Masterpiece piston and pipe +decrease it by further 4%, and also decrease the whole steam +use rate by 10%.

                  +
                  +
                  +

                  Explosions

                  +

                  The engine must be constructed using barrel, pipe and piston +from fire-safe, or in the magma version magma-safe metals.

                  +

                  During operation weak parts get gradually worn out, and +eventually the engine explodes. It should also explode if +toppled during operation by a building destroyer, or a +tantruming dwarf.

                  +
                  +
                  +

                  Save files

                  +

                  It should be safe to load and view engine-using fortresses +from a DF version without DFHack installed, except that in such +case the engines won't work. However actually making modifications +to them, or machines they connect to (including by pulling levers), +can easily result in inconsistent state once this plugin is +available again. The effects may be as weird as negative power +being generated.

                  +
                  +
                  +
                  +

                  Add Spatter

                  +

                  This plugin makes reactions with names starting with SPATTER_ADD_ +produce contaminants on the items instead of improvements. The produced +contaminants are immune to being washed away by water or destroyed by +the clean items command.

                  +

                  The plugin is intended to give some use to all those poisons that can +be bought from caravans. :)

                  +

                  To be really useful this needs patches from bug 808, tweak fix-dimensions +and tweak advmode-contained.

                  +
                  +
                  +
                  + + diff --git a/fixTexts.sh b/fixTexts.sh new file mode 100755 index 000000000..e8d890448 --- /dev/null +++ b/fixTexts.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# this script is used for easy testing of the rst documentation files. +rst2html Readme.rst Readme.html +rst2html Compile.rst Compile.html +rst2html LUA\ Api.rst LUA\ Api.html +rst2html Contributors.rst > Contributors.html diff --git a/testTexts.sh b/testTexts.sh deleted file mode 100755 index 0189ae2a2..000000000 --- a/testTexts.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# this script is used for easy testing of the rst documentation files. -rst2html README.rst > README.html -rst2html COMPILE.rst > COMPILE.html -rst2html LUA_API.rst > LUA_API.html From bb39b2e45bdbe8fda2f99607e9ca56319a602843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 30 Sep 2012 06:55:52 +0200 Subject: [PATCH 046/114] Lua API, not LUA Api. --- CMakeLists.txt | 2 +- LUA Api.html => Lua API.html | 0 LUA Api.rst => Lua API.rst | 0 fixTexts.sh | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename LUA Api.html => Lua API.html (100%) rename LUA Api.rst => Lua API.rst (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1ced670c..14436dcff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,7 +171,7 @@ find_package(Docutils) IF(BUILD_LIBRARY) add_subdirectory (library) ## install the default documentation files - install(FILES LICENSE "LUA Api.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) + install(FILES LICENSE "Lua API.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) endif() #build the plugins diff --git a/LUA Api.html b/Lua API.html similarity index 100% rename from LUA Api.html rename to Lua API.html diff --git a/LUA Api.rst b/Lua API.rst similarity index 100% rename from LUA Api.rst rename to Lua API.rst diff --git a/fixTexts.sh b/fixTexts.sh index e8d890448..917199411 100755 --- a/fixTexts.sh +++ b/fixTexts.sh @@ -2,5 +2,5 @@ # this script is used for easy testing of the rst documentation files. rst2html Readme.rst Readme.html rst2html Compile.rst Compile.html -rst2html LUA\ Api.rst LUA\ Api.html +rst2html Lua\ API.rst Lua\ API.html rst2html Contributors.rst > Contributors.html From 47e04863eb1bd04c1914afb2e354d65088e81e7c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 30 Sep 2012 09:02:14 +0400 Subject: [PATCH 047/114] Stop fixTexts.sh from rebuilding html files that didn't change. --- fixTexts.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/fixTexts.sh b/fixTexts.sh index 917199411..689dab8e2 100755 --- a/fixTexts.sh +++ b/fixTexts.sh @@ -1,6 +1,17 @@ #!/bin/bash + +cd `dirname $0` + +function process() { + if [ "$1" -nt "$2" ]; then + rst2html "$1" "$2" + else + echo "$2 - up to date." + fi +} + # this script is used for easy testing of the rst documentation files. -rst2html Readme.rst Readme.html -rst2html Compile.rst Compile.html -rst2html Lua\ API.rst Lua\ API.html -rst2html Contributors.rst > Contributors.html +process Readme.rst Readme.html +process Compile.rst Compile.html +process Lua\ API.rst Lua\ API.html +process Contributors.rst Contributors.html From 4de245d6911cf0e53c01d89aed3a18b285ce2168 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 30 Sep 2012 17:08:03 +0200 Subject: [PATCH 048/114] ruby: install generated autogen after copying *.rb --- plugins/ruby/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index b8c90d134..9d821f941 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -34,8 +34,8 @@ ADD_DEPENDENCIES(ruby ruby-autogen-rb) INSTALL(FILES ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION}) -INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb DESTINATION hack/ruby) - INSTALL(DIRECTORY . DESTINATION hack/ruby FILES_MATCHING PATTERN "*.rb") + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb DESTINATION hack/ruby) From 67adf2dde3c505fa57b10463c19bda2b4780b5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 30 Sep 2012 23:24:24 +0200 Subject: [PATCH 049/114] Add stonesense contributors. --- Contributors.html | 15 +++++++++++++-- Contributors.rst | 16 ++++++++++++++-- fixTexts.sh | 10 +++++----- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Contributors.html b/Contributors.html index 72cb8bdfc..227525bfb 100644 --- a/Contributors.html +++ b/Contributors.html @@ -325,8 +325,8 @@ ul.auto-toc {

                  Contributors

                  -

                  The following is a list of people who have contributed to DFHack. -If you belong here and are missing please add yourself and send me (peterix) a pull request :-)

                  +

                  If you belong here and are missing, please add yourself and send me (peterix) a pull request :-)

                  +

                  The following is a list of people who have contributed to DFHack.

                  +

                  And those are the cool people who made stonesense.

                  +
                  diff --git a/Contributors.rst b/Contributors.rst index 112a847e7..b10877718 100644 --- a/Contributors.rst +++ b/Contributors.rst @@ -1,7 +1,8 @@ Contributors ============ +If you belong here and are missing, please add yourself and send me (peterix) a pull request :-) + The following is a list of people who have contributed to **DFHack**. -If you belong here and are missing please add yourself and send me (peterix) a pull request :-) - Petr Mrázek - Alexander Gavrilov @@ -59,4 +60,15 @@ If you belong here and are missing please add yourself and send me (peterix) a p - Donald Ruegsegger - Caldfir - Antalia -- Angus Mezick \ No newline at end of file +- Angus Mezick + +And those are the cool people who made **stonesense**. + +- Kris Parker +- Japa +- Jonas Ask +- Petr Mrázek +- Caldfir +- 8Z +- Alexander Gavrilov +- Timothy Collett diff --git a/fixTexts.sh b/fixTexts.sh index 917199411..d58fe997d 100755 --- a/fixTexts.sh +++ b/fixTexts.sh @@ -1,6 +1,6 @@ #!/bin/bash -# this script is used for easy testing of the rst documentation files. -rst2html Readme.rst Readme.html -rst2html Compile.rst Compile.html -rst2html Lua\ API.rst Lua\ API.html -rst2html Contributors.rst > Contributors.html +# regenerate documentation after editing the .rst files. Requires python and docutils. +rst2html --no-generator --no-datestamp Readme.rst Readme.html +rst2html --no-generator --no-datestamp Compile.rst Compile.html +rst2html --no-generator --no-datestamp Lua\ API.rst Lua\ API.html +rst2html --no-generator --no-datestamp Contributors.rst Contributors.html From d9cd427121d0edd2fb4f1983fe30ac629a03c2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 1 Oct 2012 01:22:52 +0200 Subject: [PATCH 050/114] Update NEWS --- NEWS | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index fe7b5b667..65339ad64 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,8 @@ -DFHack v0.34.11-r2 (UNRELEASED) +DFHack future + + Nothing yet! + +DFHack v0.34.11-r2 Internals: - full support for Mac OS X. From fbc2d85609808b69f27f5653d8898eabfbfce242 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 1 Oct 2012 11:14:00 +0200 Subject: [PATCH 051/114] ruby: rename building_isitemfree to item_isfree, add trader flag check --- plugins/ruby/building.rb | 12 +----------- plugins/ruby/item.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index 59f715515..08c12a841 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -352,23 +352,13 @@ module DFHack job end - # check item flags to see if it is suitable for use as a building material - def building_isitemfree(i) - !i.flags.in_job and - !i.flags.in_inventory and - !i.flags.removed and - !i.flags.in_building and - !i.flags.owned and - !i.flags.forbid - end - # exemple usage def buildbed(pos=cursor) raise 'where to ?' if pos.x < 0 item = world.items.all.find { |i| i.kind_of?(ItemBedst) and - building_isitemfree(i) + item_isfree(i) } raise 'no free bed, build more !' if not item diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index fc840f7ad..9a254d9e8 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -47,5 +47,17 @@ module DFHack raise "what what?" end end + + # check item flags to see if it is suitable for use as a job input material + def item_isfree(i) + !i.flags.trader and + !i.flags.in_job and + !i.flags.in_inventory and + !i.flags.removed and + !i.flags.in_building and + !i.flags.owned and + !i.flags.forbid + end + end end From 150704f1af024143575b92429b432ae0c22aea08 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 1 Oct 2012 16:05:07 +0200 Subject: [PATCH 052/114] ruby: raise when using unknown symbols in viewscreen.feed --- plugins/ruby/ruby-autogen-defs.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 64c08facb..4470c8022 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -785,6 +785,7 @@ module DFHack def isset(key) raise unless @_memaddr key = @_enum.int(key) if _enum + raise "unknown key #{key.inspect}" if key.kind_of?(::Symbol) DFHack.memory_stlset_isset(@_memaddr, key) end alias is_set? isset @@ -792,12 +793,14 @@ module DFHack def set(key) raise unless @_memaddr key = @_enum.int(key) if _enum + raise "unknown key #{key.inspect}" if key.kind_of?(::Symbol) DFHack.memory_stlset_set(@_memaddr, key) end def delete(key) raise unless @_memaddr key = @_enum.int(key) if _enum + raise "unknown key #{key.inspect}" if key.kind_of?(::Symbol) DFHack.memory_stlset_deletekey(@_memaddr, key) end From 83117fca40ded212ca0f419718f2a7ab2861c961 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 1 Oct 2012 17:57:38 -0500 Subject: [PATCH 053/114] Sync structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 2f76de54d..d9c765cce 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 2f76de54dacd32af567b177adfb9d037fdf62d9b +Subproject commit d9c765ccea1d98ac08d33316dbb12f62df271690 From 7440e80e6c6af23284403db3af103a5f35358f0f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 2 Oct 2012 13:49:31 +0400 Subject: [PATCH 054/114] Add an API function to retrieve interface key bindings for display. --- library/Core.cpp | 3 ++ library/LuaApi.cpp | 1 + library/include/modules/Screen.h | 3 ++ library/modules/Screen.cpp | 48 ++++++++++++++++++++++++++++++++ library/xml | 2 +- 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index 1015194ad..bc90abc6b 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -821,6 +821,8 @@ std::string Core::getHackPath() #endif } +void init_screen_module(Core *); + bool Core::Init() { if(started) @@ -866,6 +868,7 @@ bool Core::Init() // Init global object pointers df::global::InitGlobals(); + init_screen_module(this); cerr << "Initializing Console.\n"; // init the console. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 352e21ccf..aba6301d2 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1146,6 +1146,7 @@ static const LuaWrapper::FunctionReg dfhack_screen_module[] = { WRAPM(Screen, inGraphicsMode), WRAPM(Screen, clear), WRAPM(Screen, invalidate), + WRAPM(Screen, getKeyDisplay), { NULL, NULL } }; diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index a2e64a515..ccd7f2f8d 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -128,6 +128,9 @@ namespace DFHack DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL); DFHACK_EXPORT void dismiss(df::viewscreen *screen, bool to_first = false); DFHACK_EXPORT bool isDismissed(df::viewscreen *screen); + + /// Retrieve the string representation of the bound key. + DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key); } class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen { diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 0b9279d2d..29f718266 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -28,6 +28,7 @@ distribution. #include #include #include +#include using namespace std; #include "modules/Screen.h" @@ -64,6 +65,8 @@ using df::global::enabler; using Screen::Pen; +using std::string; + /* * Screen painting API. */ @@ -303,6 +306,51 @@ bool Screen::isDismissed(df::viewscreen *screen) return screen->breakdown_level != interface_breakdown_types::NONE; } +#ifdef _LINUX +// Link to the libgraphics class directly: +class DFHACK_EXPORT enabler_inputst { + public: + std::string GetKeyDisplay(int binding); +}; +#else +struct less_sz { + bool operator() (const string &a, const string &b) const { + if (a.size() < b.size()) return true; + if (a.size() > b.size()) return false; + return a < b; + } +}; +static std::map > *keydisplay = NULL; +#endif + +void init_screen_module(Core *core) +{ +#ifdef _LINUX + core = core; +#else + if (!core->vinfo->getAddress("keydisplay", keydisplay)) + keydisplay = NULL; +#endif +} + +string Screen::getKeyDisplay(df::interface_key key) +{ +#ifdef _LINUX + auto enabler = (enabler_inputst*)df::global::enabler; + if (enabler) + return enabler->GetKeyDisplay(key); +#else + if (keydisplay) + { + auto it = keydisplay->find(key); + if (it != keydisplay->end() && !it->second.empty()) + return *it->second.begin(); + } +#endif + + return "?"; +} + /* * Base DFHack viewscreen. */ diff --git a/library/xml b/library/xml index 2f76de54d..4c210ddb5 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 2f76de54dacd32af567b177adfb9d037fdf62d9b +Subproject commit 4c210ddb5338d3a519d03f768da828c33480e4fe From 9d5adf1b2fb3e79420966967528dcf96cb879eb2 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 2 Oct 2012 15:25:59 +0400 Subject: [PATCH 055/114] Update the lua screens to use the new key display string API function. --- library/lua/gui.lua | 18 ++++++++++++++++++ library/lua/gui/dialogs.lua | 7 +++---- scripts/gui/liquids.lua | 22 +++++++++++----------- scripts/gui/mechanisms.lua | 4 ++-- scripts/gui/power-meter.lua | 8 +++++--- scripts/gui/room-list.lua | 4 ++-- scripts/gui/siege-engine.lua | 32 +++++++++++++++++--------------- 7 files changed, 58 insertions(+), 37 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 6eaa98606..22066c077 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -93,6 +93,14 @@ function Painter.new(rect, pen) return Painter{ rect = rect, pen = pen } end +function Painter.new_xy(x1,y1,x2,y2,pen) + return Painter{ rect = mkdims_xy(x1,y1,x2,y2), pen = pen } +end + +function Painter.new_wh(x,y,w,h,pen) + return Painter{ rect = mkdims_wh(x,y,w,h), pen = pen } +end + function Painter:isValidPos() return self.x >= self.clip_x1 and self.x <= self.clip_x2 and self.y >= self.clip_y1 and self.y <= self.clip_y2 @@ -210,6 +218,16 @@ function Painter:string(text,pen,...) return self:advance(#text, nil) end +function Painter:key(code,pen,bg,...) + if type(code) == 'string' then + code = df.interface_key[code] + end + return self:string( + dscreen.getKeyDisplay(code), + pen or COLOR_LIGHTGREEN, bg or self.cur_pen.bg, ... + ) +end + ------------------------ -- Base screen object -- ------------------------ diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index b1a96a558..707b36d52 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -51,10 +51,9 @@ function MessageBox:onRenderBody(dc) end if self.on_accept then - local x,y = self.frame_rect.x1+1, self.frame_rect.y2+1 - dscreen.paintString({fg=COLOR_LIGHTGREEN},x,y,'ESC') - dscreen.paintString({fg=COLOR_GREY},x+3,y,'/') - dscreen.paintString({fg=COLOR_LIGHTGREEN},x+4,y,'y') + local fr = self.frame_rect + local dc2 = gui.Painter.new_xy(fr.x1+1,fr.y2+1,fr.x2-8,fr.y2+1) + dc2:key('LEAVESCREEN'):string('/'):key('MENU_CONFIRM') end end diff --git a/scripts/gui/liquids.lua b/scripts/gui/liquids.lua index cddb9f01d..322ae6952 100644 --- a/scripts/gui/liquids.lua +++ b/scripts/gui/liquids.lua @@ -17,8 +17,8 @@ local brushes = { } local paints = { - { tag = 'water', caption = 'Water', liquid = true, flow = true, key = 'w' }, - { tag = 'magma', caption = 'Magma', liquid = true, flow = true, key = 'l' }, + { tag = 'water', caption = 'Water', liquid = true, flow = true, key = 'D_LOOK_ARENA_WATER' }, + { tag = 'magma', caption = 'Magma', liquid = true, flow = true, key = 'D_LOOK_ARENA_MAGMA' }, { tag = 'obsidian', caption = 'Obsidian Wall' }, { tag = 'obsidian_floor', caption = 'Obsidian Floor' }, { tag = 'riversource', caption = 'River Source' }, @@ -64,7 +64,7 @@ function Toggle:render(dc) if item then dc:string(item.caption) if item.key then - dc:string(" ("):string(item.key, COLOR_LIGHTGREEN):string(")") + dc:string(" ("):key(item.key):string(")") end else dc:string('NONE', COLOR_RED) @@ -160,9 +160,9 @@ function LiquidsUI:onRenderBody(dc) dc:newline():pen(COLOR_GREY) - dc:newline(1):string("b", COLOR_LIGHTGREEN):string(": ") + dc:newline(1):key('CUSTOM_B'):string(": ") self.brush:render(dc) - dc:newline(1):string("p", COLOR_LIGHTGREEN):string(": ") + dc:newline(1):key('CUSTOM_P'):string(": ") self.paint:render(dc) local paint = self.paint:get() @@ -170,8 +170,8 @@ function LiquidsUI:onRenderBody(dc) dc:newline() if paint.liquid then dc:newline(1):string("Amount: "..self.amount) - dc:advance(1):string("("):string("-+", COLOR_LIGHTGREEN):string(")") - dc:newline(3):string("s", COLOR_LIGHTGREEN):string(": ") + dc:advance(1):string("("):key('SECONDSCROLL_UP'):key('SECONDSCROLL_DOWN'):string(")") + dc:newline(3):key('CUSTOM_S'):string(": ") self.set:render(dc) else dc:advance(0,2) @@ -179,17 +179,17 @@ function LiquidsUI:onRenderBody(dc) dc:newline() if paint.flow then - dc:newline(1):string("f", COLOR_LIGHTGREEN):string(": ") + dc:newline(1):key('CUSTOM_F'):string(": ") self.flow:render(dc) - dc:newline(1):string("r", COLOR_LIGHTGREEN):string(": ") + dc:newline(1):key('CUSTOM_R'):string(": ") self.permaflow:render(dc) else dc:advance(0,2) end dc:newline():newline(1):pen(COLOR_WHITE) - dc:string("Esc", COLOR_LIGHTGREEN):string(": Back, ") - dc:string("Enter", COLOR_LIGHTGREEN):string(": Paint") + dc:key('LEAVESCREEN'):string(": Back, ") + dc:key('SELECT'):string(": Paint") end function ensure_blocks(cursor, size, cb) diff --git a/scripts/gui/mechanisms.lua b/scripts/gui/mechanisms.lua index d1e8ec803..607625524 100644 --- a/scripts/gui/mechanisms.lua +++ b/scripts/gui/mechanisms.lua @@ -89,8 +89,8 @@ function MechanismList:onRenderBody(dc) end dc:newline():newline(1):pen(COLOR_WHITE) - dc:string("Esc", COLOR_LIGHTGREEN):string(": Back, ") - dc:string("Enter", COLOR_LIGHTGREEN):string(": Switch") + dc:key('LEAVESCREEN'):string(": Back, ") + dc:key('SELECT'):string(": Switch") end function MechanismList:changeSelected(delta) diff --git a/scripts/gui/power-meter.lua b/scripts/gui/power-meter.lua index 6c2f699ac..81b56e29f 100644 --- a/scripts/gui/power-meter.lua +++ b/scripts/gui/power-meter.lua @@ -34,7 +34,8 @@ function PowerMeter:onRenderBody(dc) dc:string("Excess power range:") - dc:newline(3):string("as", COLOR_LIGHTGREEN) + dc:newline(3):key('BUILDING_TRIGGER_MIN_WATER_DOWN') + dc:key('BUILDING_TRIGGER_MIN_WATER_UP') dc:string(": Min ") if self.min_power <= 0 then dc:string("(any)") @@ -42,7 +43,8 @@ function PowerMeter:onRenderBody(dc) dc:string(''..self.min_power) end - dc:newline(3):string("zx", COLOR_LIGHTGREEN) + dc:newline(3):key('BUILDING_TRIGGER_MAX_WATER_DOWN') + dc:key('BUILDING_TRIGGER_MAX_WATER_UP') dc:string(": Max ") if self.max_power < 0 then dc:string("(any)") @@ -51,7 +53,7 @@ function PowerMeter:onRenderBody(dc) end dc:newline():newline(1) - dc:string("i",COLOR_LIGHTGREEN):string(": ") + dc:key('CUSTOM_I'):string(": ") if self.invert then dc:string("Inverted") else diff --git a/scripts/gui/room-list.lua b/scripts/gui/room-list.lua index 0de82db5f..1f3e663da 100644 --- a/scripts/gui/room-list.lua +++ b/scripts/gui/room-list.lua @@ -185,10 +185,10 @@ function RoomList:onRenderBody(dc) end dc:newline():newline(1):pen(COLOR_WHITE) - dc:string("Esc", COLOR_LIGHTGREEN):string(": Back") + dc:key('LEAVESCREEN'):string(": Back") if can_modify(sel_item) then - dc:string(", "):string("Enter", COLOR_LIGHTGREEN) + dc:string(", "):key('SELECT') if sel_item.obj.owner == sel_item.owner then dc:string(": Unassign") else diff --git a/scripts/gui/siege-engine.lua b/scripts/gui/siege-engine.lua index f460bb211..c518a9cf8 100644 --- a/scripts/gui/siege-engine.lua +++ b/scripts/gui/siege-engine.lua @@ -204,14 +204,14 @@ function SiegeEngine:onRenderBody_main(dc) dc:string("None (default)") end - dc:newline(3):string("r",COLOR_LIGHTGREEN):string(": Rectangle") + dc:newline(3):key('CUSTOM_R'):string(": Rectangle") if last_target_min then - dc:string(", "):string("p",COLOR_LIGHTGREEN):string(": Paste") + dc:string(", "):key('CUSTOM_P'):string(": Paste") end dc:newline(3) if target_min then - dc:string("x",COLOR_LIGHTGREEN):string(": Clear, ") - dc:string("z",COLOR_LIGHTGREEN):string(": Zoom") + dc:key('CUSTOM_X'):string(": Clear, ") + dc:key('CUSTOM_Z'):string(": Zoom") end dc:newline():newline(1) @@ -219,7 +219,7 @@ function SiegeEngine:onRenderBody_main(dc) dc:string("Uses ballista arrows") else local item = plugin.getAmmoItem(self.building) - dc:string("u",COLOR_LIGHTGREEN):string(": Use ") + dc:key('CUSTOM_U'):string(": Use ") if item_choice_idx[item] then dc:string(item_choices[item_choice_idx[item]].caption) else @@ -228,18 +228,20 @@ function SiegeEngine:onRenderBody_main(dc) end dc:newline():newline(1) - dc:string("t",COLOR_LIGHTGREEN):string(": Take from stockpile"):newline(3) + dc:key('CUSTOM_T'):string(": Take from stockpile"):newline(3) local links = plugin.getStockpileLinks(self.building) local bottom = dc.height - 5 if links then - dc:string("d",COLOR_LIGHTGREEN):string(": Delete, ") - dc:string("o",COLOR_LIGHTGREEN):string(": Zoom"):newline() + dc:key('CUSTOM_D'):string(": Delete, ") + dc:key('CUSTOM_O'):string(": Zoom"):newline() self:renderStockpiles(dc, links, bottom-2-dc:localY()) dc:newline():newline() end local prof = self.building:getWorkshopProfile() or {} - dc:seek(1,math.max(dc:localY(),19)):string('ghjk',COLOR_LIGHTGREEN)dc:string(': ') + dc:seek(1,math.max(dc:localY(),19)) + dc:key('CUSTOM_G'):key('CUSTOM_H'):key('CUSTOM_J'):key('CUSTOM_K') + dc:string(': ') dc:string(df.skill_rating.attrs[prof.min_level or 0].caption):string('-') dc:string(df.skill_rating.attrs[math.min(LEGENDARY,prof.max_level or 3000)].caption) dc:newline():newline() @@ -357,7 +359,7 @@ function SiegeEngine:onRenderBody_aim(dc) dc:newline(2):string('ERROR', COLOR_RED) end - dc:newline():newline(1):string("Enter",COLOR_LIGHTGREEN) + dc:newline():newline(1):key('SELECT') if first then dc:string(": Finish rectangle") else @@ -367,7 +369,7 @@ function SiegeEngine:onRenderBody_aim(dc) local target_min, target_max = plugin.getTargetArea(self.building) if target_min then - dc:newline(1):string("z",COLOR_LIGHTGREEN):string(": Zoom to current target") + dc:newline(1):key('CUSTOM_Z'):string(": Zoom to current target") end if first then @@ -412,9 +414,9 @@ function SiegeEngine:onRenderBody_pile(dc) if plugin.isLinkedToPile(self.building, sel) then dc:string("Already taking from here"):newline():newline(2) - dc:string("d", COLOR_LIGHTGREEN):string(": Delete link") + dc:key('CUSTOM_D'):string(": Delete link") else - dc:string("Enter",COLOR_LIGHTGREEN):string(": Take from this pile") + dc:key('SELECT'):string(": Take from this pile") end elseif sel then dc:string(utils.getBuildingName(sel), COLOR_DARKGREY) @@ -460,8 +462,8 @@ function SiegeEngine:onRenderBody(dc) self.mode.render(dc) dc:seek(1, math.max(dc:localY(), 21)):pen(COLOR_WHITE) - dc:string("ESC", COLOR_LIGHTGREEN):string(": Back, ") - dc:string("c", COLOR_LIGHTGREEN):string(": Recenter") + dc:key('LEAVESCREEN'):string(": Back, ") + dc:key('CUSTOM_C'):string(": Recenter") end function SiegeEngine:onInput(keys) From abf503fcdca4dd6c07171f93e4c4bc3ed85759d2 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 2 Oct 2012 16:45:17 +0400 Subject: [PATCH 056/114] Fix the ListBox dialog to behave in a more consistent way. --- library/lua/gui.lua | 3 +- library/lua/gui/dialogs.lua | 112 +++++++++++++++++++++------------- library/lua/gui/dwarfmode.lua | 5 +- 3 files changed, 74 insertions(+), 46 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 22066c077..a5e4f7503 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -310,6 +310,7 @@ function Screen:onResize(w,h) end function Screen:updateLayout() + self:invoke_after('postUpdateLayout') end ------------------------ @@ -399,7 +400,7 @@ function FramedScreen:updateFrameSize() self.frame_opaque = (gw == 0 and gh == 0) end -function FramedScreen:updateLayout() +function FramedScreen:postUpdateLayout() self:updateFrameSize() end diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 707b36d52..553bb3a4b 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -170,10 +170,10 @@ ListBox = defclass(ListBox, MessageBox) ListBox.focus_path = 'ListBox' ListBox.ATTRS{ - selection = 0, + selection = 1, choices = {}, select_pen = DEFAULT_NIL, - on_input = DEFAULT_NIL + on_select = DEFAULT_NIL } function InputBox:preinit(info) @@ -181,84 +181,112 @@ function InputBox:preinit(info) end function ListBox:init(info) - self.page_top = 0 + self.page_top = 1 +end + +local function choice_text(entry) + if type(entry)=="table" then + return entry.caption or entry[1] + else + return entry + end end function ListBox:getWantedFrameSize() local mw, mh = ListBox.super.getWantedFrameSize(self) - return mw, mh+#self.choices + for _,v in ipairs(self.choices) do + local text = choice_text(v) + mw = math.max(mw, #text+2) + end + return mw, mh+#self.choices+1 end -function ListBox:onRenderBody(dc) - ListBox.super.onRenderBody(self, dc) - - dc:newline(1) +function ListBox:postUpdateLayout() + self.page_size = self.frame_rect.height - #self.text - 3 + self:moveCursor(0) +end - if self.selection>dc.height-3 then - self.page_top=self.selection-(dc.height-3) - elseif self.selection0 then - self.page_top=self.selection-1 - end - for i,entry in ipairs(self.choices) do - if type(entry)=="table" then - entry=entry[1] +function ListBox:moveCursor(delta) + local page = math.max(1, self.page_size) + local cnt = #self.choices + local off = self.selection+delta-1 + local ds = math.abs(delta) + + if ds > 1 then + if off >= cnt+ds-1 then + off = 0 + else + off = math.min(cnt-1, off) end - if i>self.page_top then - if i == self.selection then - dc:pen(self.select_pen or COLOR_LIGHTCYAN) - else - dc:pen(self.text_pen or COLOR_GREY) - end - dc:string(entry) - dc:newline(1) + if off <= -ds then + off = cnt-1 + else + off = math.max(0, off) end end + + self.selection = 1 + off % cnt + self.page_top = 1 + page * math.floor((self.selection-1) / page) end -function ListBox:moveCursor(delta) - local newsel=self.selection+delta - if #self.choices ~=0 then - if newsel<1 or newsel>#self.choices then - newsel=newsel % #self.choices +function ListBox:onRenderBody(dc) + ListBox.super.onRenderBody(self, dc) + + dc:newline(1):pen(self.select_pen or COLOR_CYAN) + + local choices = self.choices + local iend = math.min(#choices, self.page_top+self.page_size-1) + + for i = self.page_top,iend do + local text = choice_text(choices[i]) + if text then + dc.cur_pen.bold = (i == self.selection); + dc:string(text) + else + dc:string('?ERROR?', COLOR_LIGHTRED) end + dc:newline(1) end - self.selection=newsel end function ListBox:onInput(keys) if keys.SELECT then self:dismiss() + local choice=self.choices[self.selection] - if self.on_input then - self.on_input(self.selection,choice) + if self.on_select then + self.on_select(self.selection, choice) end - if choice and choice[2] then - choice[2](choice,self.selection) -- maybe reverse the arguments? + if choice then + local callback = choice.on_select or choice[2] + if callback then + callback(choice, self.selection) + end end elseif keys.LEAVESCREEN then self:dismiss() if self.on_cancel then self.on_cancel() end - elseif keys.CURSOR_UP then + elseif keys.STANDARDSCROLL_UP then self:moveCursor(-1) - elseif keys.CURSOR_DOWN then + elseif keys.STANDARDSCROLL_DOWN then self:moveCursor(1) - elseif keys.CURSOR_UP_FAST then - self:moveCursor(-10) - elseif keys.CURSOR_DOWN_FAST then - self:moveCursor(10) + elseif keys.STANDARDSCROLL_PAGEUP then + self:moveCursor(-self.page_size) + elseif keys.STANDARDSCROLL_PAGEDOWN then + self:moveCursor(self.page_size) end end -function showListPrompt(title, text, tcolor, choices, on_input, on_cancel, min_width) +function showListPrompt(title, text, tcolor, choices, on_select, on_cancel, min_width) ListBox{ frame_title = title, text = text, text_pen = tcolor, choices = choices, - on_input = on_input, + on_select = on_select, on_cancel = on_cancel, frame_width = min_width, }:show() diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index ba3cfbe6c..125e71220 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -253,7 +253,7 @@ end DwarfOverlay = defclass(DwarfOverlay, gui.Screen) -function DwarfOverlay:updateLayout() +function DwarfOverlay:postUpdateLayout() self.df_layout = getPanelLayout() end @@ -352,8 +352,7 @@ end MenuOverlay = defclass(MenuOverlay, DwarfOverlay) -function MenuOverlay:updateLayout() - MenuOverlay.super.updateLayout(self) +function MenuOverlay:postUpdateLayout() self.frame_rect = self.df_layout.menu end From bd3d3061ae72973fa0ddf5349bf6a9f59b662e28 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 2 Oct 2012 18:01:28 +0400 Subject: [PATCH 057/114] Add a module that wraps the dialogs as "blocking" coroutine functions. --- library/lua/gui/script.lua | 151 +++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 library/lua/gui/script.lua diff --git a/library/lua/gui/script.lua b/library/lua/gui/script.lua new file mode 100644 index 000000000..021a4fa52 --- /dev/null +++ b/library/lua/gui/script.lua @@ -0,0 +1,151 @@ +-- Support for scripted interaction sequences via coroutines. + +local _ENV = mkmodule('gui.script') + +local dlg = require('gui.dialogs') + +--[[ + Example: + + start(function() + sleep(100, 'frames') + print(showYesNoPrompt('test', 'print true?')) + end) +]] + +-- Table of running background scripts. +if not scripts then + scripts = {} + setmetatable(scripts, { __mode = 'k' }) +end + +local function do_resume(inst, ...) + inst.gen = inst.gen + 1 + return (dfhack.saferesume(inst.coro, ...)) +end + +-- Starts a new background script by calling the function. +function start(fn,...) + local coro = coroutine.create(fn) + local inst = { + coro = coro, + gen = 0, + } + scripts[coro] = inst + return do_resume(inst, ...) +end + +-- Checks if called from a background script +function in_script() + return scripts[coroutine.running()] ~= nil +end + +local function getinst() + local inst = scripts[coroutine.running()] + if not inst then + error('Not in a gui script coroutine.') + end + return inst +end + +local function invoke_resume(inst,gen,quiet,...) + local state = coroutine.status(inst.coro) + if state ~= 'suspended' then + if state ~= 'dead' then + dfhack.printerr(debug.traceback('resuming a non-waiting continuation')) + end + elseif inst.gen > gen then + if not quiet then + dfhack.printerr(debug.traceback('resuming an expired continuation')) + end + else + do_resume(inst, ...) + end +end + +-- Returns a callback that resumes the script from wait with given return values +function mkresume(...) + local inst = getinst() + return curry(invoke_resume, inst, inst.gen, false, ...) +end + +-- Like mkresume, but does not complain if already resumed from this wait +function qresume(...) + local inst = getinst() + return curry(invoke_resume, inst, inst.gen, true, ...) +end + +-- Wait until a mkresume callback is called, then return its arguments. +-- Once it returns, all mkresume callbacks created before are invalidated. +function wait() + getinst() -- check that the context is right + return coroutine.yield() +end + +-- Wraps dfhack.timeout for coroutines. +function sleep(time, quantity) + if dfhack.timeout(time, quantity, mkresume()) then + wait() + return true + else + return false + end +end + +-- Some dialog wrappers: + +function showMessage(title, text, tcolor) + dlg.MessageBox{ + frame_title = title, + text = text, + text_pen = tcolor, + on_close = qresume(nil) + }:show() + + return wait() +end + +function showYesNoPrompt(title, text, tcolor) + dlg.MessageBox{ + frame_title = title, + text = text, + text_pen = tcolor, + on_accept = mkresume(true), + on_cancel = mkresume(false), + on_close = qresume(nil) + }:show() + + return wait() +end + +function showInputPrompt(title, text, tcolor, input, min_width) + dlg.InputBox{ + frame_title = title, + text = text, + text_pen = tcolor, + input = input, + frame_width = min_width, + on_input = mkresume(true), + on_cancel = mkresume(false), + on_close = qresume(nil) + }:show() + + return wait() +end + +function showListPrompt(title, text, tcolor, choices, min_width) + dlg.ListBox{ + frame_title = title, + text = text, + text_pen = tcolor, + choices = choices, + frame_width = min_width, + on_select = mkresume(true), + on_cancel = mkresume(false), + on_close = qresume(nil) + }:show() + + return wait() +end + +return _ENV From 7962f24cce8f9049a0e280cd3f1d04c6c9520a65 Mon Sep 17 00:00:00 2001 From: Quietust Date: Tue, 2 Oct 2012 10:20:54 -0500 Subject: [PATCH 058/114] Display actual key bindings for Manipulator --- plugins/manipulator.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 164b13dd7..633098a10 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -1105,30 +1105,30 @@ void viewscreen_unitlaborsst::render() } int x = 2; - OutputString(10, x, gps->dimy - 3, "Enter"); // SELECT key + OutputString(10, x, gps->dimy - 3, Screen::getKeyDisplay(interface_key::SELECT)); OutputString(canToggle ? 15 : 8, x, gps->dimy - 3, ": Toggle labor, "); - OutputString(10, x, gps->dimy - 3, "Shift+Enter"); // SELECT_ALL key + OutputString(10, x, gps->dimy - 3, Screen::getKeyDisplay(interface_key::SELECT_ALL)); OutputString(canToggle ? 15 : 8, x, gps->dimy - 3, ": Toggle Group, "); - OutputString(10, x, gps->dimy - 3, "v"); // UNITJOB_VIEW key + OutputString(10, x, gps->dimy - 3, Screen::getKeyDisplay(interface_key::UNITJOB_VIEW)); OutputString(15, x, gps->dimy - 3, ": ViewCre, "); - OutputString(10, x, gps->dimy - 3, "c"); // UNITJOB_ZOOM_CRE key + OutputString(10, x, gps->dimy - 3, Screen::getKeyDisplay(interface_key::UNITJOB_ZOOM_CRE)); OutputString(15, x, gps->dimy - 3, ": Zoom-Cre"); x = 2; - OutputString(10, x, gps->dimy - 2, "Esc"); // LEAVESCREEN key + OutputString(10, x, gps->dimy - 2, Screen::getKeyDisplay(interface_key::LEAVESCREEN)); OutputString(15, x, gps->dimy - 2, ": Done, "); - OutputString(10, x, gps->dimy - 2, "+"); // SECONDSCROLL_DOWN key - OutputString(10, x, gps->dimy - 2, "-"); // SECONDSCROLL_UP key + OutputString(10, x, gps->dimy - 2, Screen::getKeyDisplay(interface_key::SECONDSCROLL_DOWN)); + OutputString(10, x, gps->dimy - 2, Screen::getKeyDisplay(interface_key::SECONDSCROLL_UP)); OutputString(15, x, gps->dimy - 2, ": Sort by Skill, "); - OutputString(10, x, gps->dimy - 2, "*"); // SECONDSCROLL_PAGEDOWN key - OutputString(10, x, gps->dimy - 2, "/"); // SECONDSCROLL_PAGEUP key + OutputString(10, x, gps->dimy - 2, Screen::getKeyDisplay(interface_key::SECONDSCROLL_PAGEDOWN)); + OutputString(10, x, gps->dimy - 2, Screen::getKeyDisplay(interface_key::SECONDSCROLL_PAGEUP)); OutputString(15, x, gps->dimy - 2, ": Sort by ("); - OutputString(10, x, gps->dimy - 2, "Tab"); // CHANGETAB key + OutputString(10, x, gps->dimy - 2, Screen::getKeyDisplay(interface_key::CHANGETAB)); OutputString(15, x, gps->dimy - 2, ") "); switch (altsort) { @@ -1182,7 +1182,7 @@ struct unitlist_hook : df::viewscreen_unitlistst if (units[page].size()) { int x = 2; - OutputString(12, x, gps->dimy - 2, "l"); // UNITVIEW_PRF_PROF key + OutputString(12, x, gps->dimy - 2, Screen::getKeyDisplay(interface_key::UNITVIEW_PRF_PROF)); OutputString(15, x, gps->dimy - 2, ": Manage labors (DFHack)"); } } From 33aead34b43799e440e58586a215df69c8c3c02e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 2 Oct 2012 19:53:16 +0400 Subject: [PATCH 059/114] Implement a more automated search mode based on keys for some globals. --- library/lua/memscan.lua | 7 +- scripts/devel/find-offsets.lua | 306 +++++++++++++++++++++++++++++---- 2 files changed, 279 insertions(+), 34 deletions(-) diff --git a/library/lua/memscan.lua b/library/lua/memscan.lua index 970f821c2..6764a0d58 100644 --- a/library/lua/memscan.lua +++ b/library/lua/memscan.lua @@ -358,7 +358,7 @@ end -- Interactive search utility -function DiffSearcher:find_interactive(prompt,data_type,condition_cb) +function DiffSearcher:find_interactive(prompt,data_type,condition_cb,iter_limit) enum = enum or {} -- Loop for restarting search from scratch @@ -374,6 +374,11 @@ function DiffSearcher:find_interactive(prompt,data_type,condition_cb) while true do print('') + if iter_limit and ccursor >= iter_limit then + dfhack.printerr(' Iteration limit reached without a solution.') + break + end + local ok, value, delta = condition_cb(ccursor) ccursor = ccursor + 1 diff --git a/scripts/devel/find-offsets.lua b/scripts/devel/find-offsets.lua index 07a058474..45f59e154 100644 --- a/scripts/devel/find-offsets.lua +++ b/scripts/devel/find-offsets.lua @@ -2,6 +2,7 @@ local utils = require 'utils' local ms = require 'memscan' +local gui = require 'gui' local is_known = dfhack.internal.getAddress @@ -53,6 +54,35 @@ end local searcher = ms.DiffSearcher.new(data) +local function get_screen(class, prompt) + if not is_known('gview') then + print('Please navigate to '..prompt) + if not utils.prompt_yes_no('Proceed?', true) then + return nil + end + return true + end + + while true do + local cs = dfhack.gui.getCurViewscreen(true) + if not df.is_instance(class, cs) then + print('Please navigate to '..prompt) + if not utils.prompt_yes_no('Proceed?', true) then + return nil + end + else + return cs + end + end +end + +local function screen_title() + return get_screen(df.viewscreen_titlest, 'the title screen') +end +local function screen_dwarfmode() + return get_screen(df.viewscreen_dwarfmodest, 'the main dwarf mode screen') +end + local function validate_offset(name,validator,addr,tname,...) local obj = data:object_by_field(addr,tname,...) if obj and not validator(obj) then @@ -136,13 +166,95 @@ local function list_index_choices(length_func) end end +local function can_feed() + return not force_scan['nofeed'] and is_known 'gview' +end + +local function dwarfmode_feed_input(...) + local screen = screen_dwarfmode() + if not df.isvalid(screen) then + qerror('could not retrieve dwarfmode screen') + end + for _,v in ipairs({...}) do + gui.simulateInput(screen, v) + end +end + +local function dwarfmode_to_top() + if not can_feed() then + return false + end + + local screen = screen_dwarfmode() + if not df.isvalid(screen) then + return false + end + + for i=0,10 do + if is_known 'ui' and df.global.ui.main.mode == df.ui_sidebar_mode.Default then + break + end + gui.simulateInput(screen, 'LEAVESCREEN') + end + + -- force pause just in case + screen.keyRepeat = 1 + return true +end + +local function feed_menu_choice(catnames,catkeys,enum) + return function (idx) + idx = idx % #catnames + 1 + dwarfmode_feed_input(catkeys[idx]) + if enum then + return true, enum[catnames[idx]] + else + return true, catnames[idx] + end + end +end + +local function feed_list_choice(count,upkey,downkey) + return function(idx) + if idx > 0 then + local ok, len + if type(count) == 'number' then + ok, len = true, count + else + ok, len = pcall(count) + end + if not ok then + len = 5 + elseif len > 10 then + len = 10 + end + + local hcnt = len-1 + local rix = 1 + (idx-1) % (hcnt*2) + + if rix >= hcnt then + dwarfmode_feed_input(upkey or 'SECONDSCROLL_UP') + return true, hcnt*2 - rix + else + dwarfmode_feed_input(donwkey or 'SECONDSCROLL_DOWN') + return true, rix + end + else + print(' Please select the first list item.') + if not utils.prompt_yes_no(' Proceed?') then + return false + end + return true, 0 + end + end +end + -- -- Cursor group -- local function find_cursor() - print('\nPlease navigate to the title screen to find cursor.') - if not utils.prompt_yes_no('Proceed?', true) then + if not screen_title() then return false end @@ -354,13 +466,35 @@ local function is_valid_world(world) end local function find_world() - local addr = searcher:find_menu_cursor([[ + local catnames = { + 'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins' + } + local catkeys = { + 'STOCKPILE_GRAVEYARD', 'STOCKPILE_REFUSE', 'STOCKPILE_STONE', 'STOCKPILE_WOOD', + 'STOCKPILE_GEM', 'STOCKPILE_BARBLOCK', 'STOCKPILE_CLOTH', 'STOCKPILE_LEATHER', + 'STOCKPILE_AMMO', 'STOCKPILE_COINS' + } + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_STOCKPILES') + + addr = searcher:find_interactive( + 'Auto-searching for world.', + 'int32_t', + feed_menu_choice(catnames, catkeys, df.stockpile_category), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for world. Please open the stockpile creation menu, and select different types as instructed below:]], - 'int32_t', - { 'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins' }, - df.stockpile_category - ) + 'int32_t', catnames, df.stockpile_category + ) + end + validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type') end @@ -385,14 +519,37 @@ local function is_valid_ui(ui) end local function find_ui() - local addr = searcher:find_menu_cursor([[ + local catnames = { + 'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps', + 'DesignateUpStair', 'DesignateDownStair', 'DesignateUpDownStair', + 'DesignateUpRamp', 'DesignateChopTrees' + } + local catkeys = { + 'DESIGNATE_DIG', 'DESIGNATE_CHANNEL', 'DESIGNATE_DIG_REMOVE_STAIRS_RAMPS', + 'DESIGNATE_STAIR_UP', 'DESIGNATE_STAIR_DOWN', 'DESIGNATE_STAIR_UPDOWN', + 'DESIGNATE_RAMP', 'DESIGNATE_CHOP' + } + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_DESIGNATE') + + addr = searcher:find_interactive( + 'Auto-searching for ui.', + 'int16_t', + feed_menu_choice(catnames, catkeys, df.ui_sidebar_mode), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui. Please open the designation menu, and switch modes as instructed below:]], - 'int16_t', - { 'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps', 'DesignateUpStair', - 'DesignateDownStair', 'DesignateUpDownStair', 'DesignateUpRamp', 'DesignateChopTrees' }, - df.ui_sidebar_mode - ) + 'int16_t', catnames, df.ui_sidebar_mode + ) + end + validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode') end @@ -421,14 +578,32 @@ local function is_valid_ui_sidebar_menus(usm) end local function find_ui_sidebar_menus() - local addr = searcher:find_menu_cursor([[ + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_BUILDJOB') + + addr = searcher:find_interactive([[ +Auto-searching for ui_sidebar_menus. Please select a Mason, +Craftsdwarfs, or Carpenters workshop, open the Add Job +menu, and move the cursor within:]], + 'int32_t', + feed_list_choice(7), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_sidebar_menus. Please switch to 'q' mode, select a Mason, Craftsdwarfs, or Carpenters workshop, open the Add Job menu, and move the cursor within:]], - 'int32_t', - { 0, 1, 2, 3, 4, 5, 6 }, - ordinal_names - ) + 'int32_t', + { 0, 1, 2, 3, 4, 5, 6 }, + ordinal_names + ) + end + validate_offset('ui_sidebar_menus', is_valid_ui_sidebar_menus, addr, df.ui_sidebar_menus, 'workshop_job', 'cursor') end @@ -455,14 +630,41 @@ local function is_valid_ui_build_selector(ubs) end local function find_ui_build_selector() - local addr = searcher:find_menu_cursor([[ + local addr + + if dwarfmode_to_top() then + addr = searcher:find_interactive([[ +Auto-searching for ui_build_selector. This requires mechanisms.]], + 'int32_t', + function(idx) + if idx == 0 then + dwarfmode_to_top() + dwarfmode_feed_input( + 'D_BUILDING', + 'HOTKEY_BUILDING_TRAP', + 'HOTKEY_BUILDING_TRAP_TRIGGER', + 'BUILDING_TRIGGER_ENABLE_CREATURE' + ) + else + dwarfmode_feed_input('BUILDING_TRIGGER_MIN_SIZE_DOWN') + end + return true, 50000 - 1000*idx + end, + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_build_selector. Please start constructing a pressure plate, and enable creatures. Then change the min weight as requested, remembering that the ui truncates the number, so when it shows "Min (5000df", it means 50000:]], - 'int32_t', - { 50000, 49000, 48000, 47000, 46000, 45000, 44000 } - ) + 'int32_t', + { 50000, 49000, 48000, 47000, 46000, 45000, 44000 } + ) + end + validate_offset('ui_build_selector', is_valid_ui_build_selector, addr, df.ui_build_selector, 'plate_info', 'unit_min') end @@ -601,13 +803,33 @@ end -- local function find_ui_unit_view_mode() - local addr = searcher:find_menu_cursor([[ + local catnames = { 'General', 'Inventory', 'Preferences', 'Wounds' } + local catkeys = { 'UNITVIEW_GEN', 'UNITVIEW_INV', 'UNITVIEW_PRF', 'UNITVIEW_WND' } + local addr + + if dwarfmode_to_top() and is_known('ui_selected_unit') then + dwarfmode_feed_input('D_VIEWUNIT') + + if df.global.ui_selected_unit < 0 then + df.global.ui_selected_unit = 0 + end + + addr = searcher:find_interactive( + 'Auto-searching for ui_unit_view_mode.', + 'int32_t', + feed_menu_choice(catnames, catkeys, df.ui_unit_view_mode.T_value), + 10 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_unit_view_mode. Having selected a unit with 'v', switch the pages as requested:]], - 'int32_t', - { 'General', 'Inventory', 'Preferences', 'Wounds' }, - df.ui_unit_view_mode.T_value - ) + 'int32_t', catnames, df.ui_unit_view_mode.T_value + ) + end + ms.found_offset('ui_unit_view_mode', addr) end @@ -620,14 +842,32 @@ local function look_item_list_count() end local function find_ui_look_cursor() - local addr = searcher:find_menu_cursor([[ + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_LOOK') + + addr = searcher:find_interactive([[ +Auto-searching for ui_look_cursor. Please select a tile +with at least 5 items or units on the ground, and move +the cursor as instructed:]], + 'int32_t', + feed_list_choice(look_item_list_count), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_look_cursor. Please activate the 'k' mode, find a tile with many items or units on the ground, and select list entries as instructed:]], - 'int32_t', - list_index_choices(look_item_list_count), - ordinal_names - ) + 'int32_t', + list_index_choices(look_item_list_count), + ordinal_names + ) + end + ms.found_offset('ui_look_cursor', addr) end @@ -989,10 +1229,10 @@ end print('\nInitial globals (need title screen):\n') +exec_finder(find_gview, 'gview') exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' }) exec_finder(find_announcements, 'announcements') exec_finder(find_d_init, 'd_init') -exec_finder(find_gview, 'gview') exec_finder(find_enabler, 'enabler') exec_finder(find_gps, 'gps') From 9f687f64a431e44f0c2f122589358d78b34d71b5 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 3 Oct 2012 12:58:05 +0400 Subject: [PATCH 060/114] Fix build. --- library/xml | 2 +- plugins/advtools.cpp | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/library/xml b/library/xml index 4c210ddb5..20c6d9c74 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 4c210ddb5338d3a519d03f768da828c33480e4fe +Subproject commit 20c6d9c743f1c5a20bb288f427b101e9b2a138d7 diff --git a/plugins/advtools.cpp b/plugins/advtools.cpp index d674f5528..d6224f5c2 100644 --- a/plugins/advtools.cpp +++ b/plugins/advtools.cpp @@ -30,6 +30,7 @@ #include "df/viewscreen_optionst.h" #include "df/viewscreen_dungeonmodest.h" #include "df/viewscreen_dungeon_monsterstatusst.h" +#include "df/nemesis_flags.h" #include @@ -683,17 +684,19 @@ command_result adv_bodyswap (color_ostream &out, std::vector & par // Permanently re-link everything if (permanent) { + using namespace df::enums::nemesis_flags; + ui_advmode->player_id = linear_index(world->nemesis.all, new_nemesis); // Flag 0 appears to be the 'active adventurer' flag, and // the player_id field above seems to be computed using it // when a savegame is loaded. // Also, unless this is set, it is impossible to retire. - real_nemesis->flags.set(0, false); - new_nemesis->flags.set(0, true); + real_nemesis->flags.set(ACTIVE_ADVENTURER, false); + new_nemesis->flags.set(ACTIVE_ADVENTURER, true); - real_nemesis->flags.set(1, true); // former retired adventurer - new_nemesis->flags.set(2, true); // blue color in legends + real_nemesis->flags.set(RETIRED_ADVENTURER, true); // former retired adventurer + new_nemesis->flags.set(ADVENTURER, true); // blue color in legends // Reassign companions and acquaintances if (!no_make_leader) From 3a522768a2d16d9cd3a0bfcf53f1c877450d4bdd Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 3 Oct 2012 19:07:04 +0400 Subject: [PATCH 061/114] Implement feed-based finders for the remaining applicable globals. --- scripts/devel/find-offsets.lua | 373 +++++++++++++++++++++++++++------ 1 file changed, 304 insertions(+), 69 deletions(-) diff --git a/scripts/devel/find-offsets.lua b/scripts/devel/find-offsets.lua index 45f59e154..2f7f1287c 100644 --- a/scripts/devel/find-offsets.lua +++ b/scripts/devel/find-offsets.lua @@ -180,6 +180,21 @@ local function dwarfmode_feed_input(...) end end +local function dwarfmode_step_frames(count) + local screen = screen_dwarfmode() + if not df.isvalid(screen) then + qerror('could not retrieve dwarfmode screen') + end + + for i = 1,(count or 1) do + gui.simulateInput(screen, 'D_ONESTEP') + if screen.keyRepeat ~= 1 then + qerror('Could not step one frame: . did not work') + end + screen:logic() + end +end + local function dwarfmode_to_top() if not can_feed() then return false @@ -241,9 +256,33 @@ local function feed_list_choice(count,upkey,downkey) end else print(' Please select the first list item.') - if not utils.prompt_yes_no(' Proceed?') then + if not utils.prompt_yes_no(' Proceed?', true) then + return false + end + return true, 0 + end + end +end + +local function feed_menu_bool(enter_seq, exit_seq) + return function(idx) + if idx == 0 then + if not utils.prompt_yes_no(' Proceed?', true) then + return false + end + return true, 0 + end + if idx == 5 then + print(' Please resize the game window.') + if not utils.prompt_yes_no(' Proceed?', true) then return false end + end + if idx%2 == 1 then + dwarfmode_feed_input(table.unpack(enter_seq)) + return true, 1 + else + dwarfmode_feed_input(table.unpack(exit_seq)) return true, 0 end end @@ -880,14 +919,32 @@ local function building_item_list_count() end local function find_ui_building_item_cursor() - local addr = searcher:find_menu_cursor([[ + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_BUILDITEM') + + addr = searcher:find_interactive([[ +Auto-searching for ui_building_item_cursor. Please highlight a +workshop, trade depot or other building with at least 5 contained +items, and select as instructed:]], + 'int32_t', + feed_list_choice(building_item_list_count), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_building_item_cursor. Please activate the 't' mode, find a cluttered workshop, trade depot, or other building with many contained items, and select as instructed:]], - 'int32_t', - list_index_choices(building_item_list_count), - ordinal_names - ) + 'int32_t', + list_index_choices(building_item_list_count), + ordinal_names + ) + end + ms.found_offset('ui_building_item_cursor', addr) end @@ -896,17 +953,37 @@ end -- local function find_ui_workshop_in_add() - local addr = searcher:find_menu_cursor([[ + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_BUILDJOB') + + addr = searcher:find_interactive([[ +Auto-searching for ui_workshop_in_add. Please select a +workshop, e.g. Carpenters or Masons.]], + 'int8_t', + feed_menu_bool( + { 'BUILDJOB_CANCEL', 'BUILDJOB_ADD' }, + { 'SELECT', 'SELECT', 'SELECT', 'SELECT', 'SELECT' } + ), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_workshop_in_add. Please activate the 'q' mode, find a workshop without jobs (or delete jobs), and do as instructed below. NOTE: If not done after first 3-4 steps, resize the game window.]], - 'int8_t', - { 1, 0 }, - { [1] = 'enter the add job menu', - [0] = 'add job, thus exiting the menu' } - ) + 'int8_t', + { 1, 0 }, + { [1] = 'enter the add job menu', + [0] = 'add job, thus exiting the menu' } + ) + end + ms.found_offset('ui_workshop_in_add', addr) end @@ -919,13 +996,30 @@ local function workshop_job_list_count() end local function find_ui_workshop_job_cursor() - local addr = searcher:find_menu_cursor([[ + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_BUILDJOB') + + addr = searcher:find_interactive([[ +Auto-searching for ui_workshop_job_cursor. Please highlight a +workshop with at least 5 contained jobs, and select as instructed:]], + 'int32_t', + feed_list_choice(workshop_job_list_count), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_workshop_job_cursor. Please activate the 'q' mode, find a workshop with many jobs, and select as instructed:]], - 'int32_t', - list_index_choices(workshop_job_list_count), - ordinal_names - ) + 'int32_t', + list_index_choices(workshop_job_list_count), + ordinal_names + ) + end + ms.found_offset('ui_workshop_job_cursor', addr) end @@ -934,17 +1028,39 @@ end -- local function find_ui_building_in_assign() - local addr = searcher:find_menu_cursor([[ + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_BUILDJOB') + + addr = searcher:find_interactive([[ +Auto-searching for ui_building_in_assign. Please select a room, +i.e. a bedroom, tomb, office, dining room or statue garden.]], + 'int8_t', + feed_menu_bool( + { { 'BUILDJOB_STATUE_ASSIGN', 'BUILDJOB_COFFIN_ASSIGN', + 'BUILDJOB_CHAIR_ASSIGN', 'BUILDJOB_TABLE_ASSIGN', + 'BUILDJOB_BED_ASSIGN' } }, + { 'LEAVESCREEN' } + ), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_building_in_assign. Please activate the 'q' mode, select a room building (e.g. a bedroom) and do as instructed below. NOTE: If not done after first 3-4 steps, resize the game window.]], - 'int8_t', - { 1, 0 }, - { [1] = 'enter the Assign owner menu', - [0] = 'press Esc to exit assign' } - ) + 'int8_t', + { 1, 0 }, + { [1] = 'enter the Assign owner menu', + [0] = 'press Esc to exit assign' } + ) + end + ms.found_offset('ui_building_in_assign', addr) end @@ -953,17 +1069,39 @@ end -- local function find_ui_building_in_resize() - local addr = searcher:find_menu_cursor([[ + local addr + + if dwarfmode_to_top() then + dwarfmode_feed_input('D_BUILDJOB') + + addr = searcher:find_interactive([[ +Auto-searching for ui_building_in_resize. Please select a room, +i.e. a bedroom, tomb, office, dining room or statue garden.]], + 'int8_t', + feed_menu_bool( + { { 'BUILDJOB_STATUE_SIZE', 'BUILDJOB_COFFIN_SIZE', + 'BUILDJOB_CHAIR_SIZE', 'BUILDJOB_TABLE_SIZE', + 'BUILDJOB_BED_SIZE' } }, + { 'LEAVESCREEN' } + ), + 20 + ) + end + + if not addr then + addr = searcher:find_menu_cursor([[ Searching for ui_building_in_resize. Please activate the 'q' mode, select a room building (e.g. a bedroom) and do as instructed below. NOTE: If not done after first 3-4 steps, resize the game window.]], - 'int8_t', - { 1, 0 }, - { [1] = 'enter the Resize room mode', - [0] = 'press Esc to exit resize' } - ) + 'int8_t', + { 1, 0 }, + { [1] = 'enter the Resize room mode', + [0] = 'press Esc to exit resize' } + ) + end + ms.found_offset('ui_building_in_resize', addr) end @@ -971,13 +1109,40 @@ end -- window_x -- +local function feed_window_xyz(dec,inc,step) + return function(idx) + if idx == 0 then + for i = 1,30 do dwarfmode_feed_input(dec) end + else + dwarfmode_feed_input(inc) + end + return true, nil, step + end +end + local function find_window_x() - local addr = searcher:find_counter([[ + local addr + + if dwarfmode_to_top() then + addr = searcher:find_interactive( + 'Auto-searching for window_x.', + 'int32_t', + feed_window_xyz('CURSOR_LEFT_FAST', 'CURSOR_RIGHT', 10), + 20 + ) + + dwarfmode_feed_input('D_HOTKEY1') + end + + if not addr then + addr = searcher:find_counter([[ Searching for window_x. Please exit to main dwarfmode menu, scroll to the LEFT edge, then do as instructed:]], - 'int32_t', 10, - 'Please press Right to scroll right one step.' - ) + 'int32_t', 10, + 'Please press Right to scroll right one step.' + ) + end + ms.found_offset('window_x', addr) end @@ -986,12 +1151,28 @@ end -- local function find_window_y() - local addr = searcher:find_counter([[ + local addr + + if dwarfmode_to_top() then + addr = searcher:find_interactive( + 'Auto-searching for window_y.', + 'int32_t', + feed_window_xyz('CURSOR_UP_FAST', 'CURSOR_DOWN', 10), + 20 + ) + + dwarfmode_feed_input('D_HOTKEY1') + end + + if not addr then + addr = searcher:find_counter([[ Searching for window_y. Please exit to main dwarfmode menu, scroll to the TOP edge, then do as instructed:]], - 'int32_t', 10, - 'Please press Down to scroll down one step.' - ) + 'int32_t', 10, + 'Please press Down to scroll down one step.' + ) + end + ms.found_offset('window_y', addr) end @@ -1000,14 +1181,30 @@ end -- local function find_window_z() - local addr = searcher:find_counter([[ + local addr + + if dwarfmode_to_top() then + addr = searcher:find_interactive( + 'Auto-searching for window_z.', + 'int32_t', + feed_window_xyz('CURSOR_UP_Z', 'CURSOR_DOWN_Z', -1), + 30 + ) + + dwarfmode_feed_input('D_HOTKEY1') + end + + if not addr then + addr = searcher:find_counter([[ Searching for window_z. Please exit to main dwarfmode menu, scroll to a Z level near surface, then do as instructed below. NOTE: If not done after first 3-4 steps, resize the game window.]], - 'int32_t', -1, - "Please press '>' to scroll one Z level down." - ) + 'int32_t', -1, + "Please press '>' to scroll one Z level down." + ) + end + ms.found_offset('window_z', addr) end @@ -1056,6 +1253,27 @@ function stop_autosave() end end +function step_n_frames(cnt, feed) + local world = df.global.world + local ctick = world.frame_counter + + if feed then + print(" Auto-stepping "..cnt.." frames.") + dwarfmode_step_frames(cnt) + return world.frame_counter-ctick + end + + local more = '' + while world.frame_counter-ctick < cnt do + print(" Please step the game "..(cnt-world.frame_counter+ctick)..more.." frames.") + more = ' more' + if not utils.prompt_yes_no(' Proceed?', true) then + return nil + end + end + return world.frame_counter-ctick +end + local function find_cur_year_tick() local zone if os_type == 'windows' then @@ -1070,12 +1288,21 @@ local function find_cur_year_tick() stop_autosave() - local addr = zone:find_counter([[ -Searching for cur_year_tick. Please exit to main dwarfmode -menu, then do as instructed below:]], - 'int32_t', 1, - "Please press '.' to step the game one frame." + local feed = dwarfmode_to_top() + local addr = zone:find_interactive( + 'Searching for cur_year_tick.', + 'int32_t', + function(idx) + if idx > 0 then + if not step_n_frames(1, feed) then + return false + end + end + return true, nil, 1 + end, + 20 ) + ms.found_offset('cur_year_tick', addr) end @@ -1083,20 +1310,6 @@ end -- cur_season_tick -- -function step_n_frames(cnt) - local world = df.global.world - local ctick = world.frame_counter - local more = '' - while world.frame_counter-ctick < cnt do - print(" Please step the game "..(cnt-world.frame_counter+ctick)..more.." frames.") - more = ' more' - if not utils.prompt_yes_no(' Done?', true) then - return nil - end - end - return world.frame_counter-ctick -end - local function find_cur_season_tick() if not (is_known 'cur_year_tick') then dfhack.printerr('Cannot search for cur_season_tick - prerequisites missing.') @@ -1105,13 +1318,14 @@ local function find_cur_season_tick() stop_autosave() + local feed = dwarfmode_to_top() local addr = searcher:find_interactive([[ Searching for cur_season_tick. Please exit to main dwarfmode menu, then do as instructed below:]], 'int32_t', function(ccursor) if ccursor > 0 then - if not step_n_frames(10) then + if not step_n_frames(10, feed) then return false end end @@ -1133,6 +1347,7 @@ local function find_cur_season() stop_autosave() + local feed = dwarfmode_to_top() local addr = searcher:find_interactive([[ Searching for cur_season. Please exit to main dwarfmode menu, then do as instructed below:]], @@ -1142,7 +1357,7 @@ menu, then do as instructed below:]], local cst = df.global.cur_season_tick df.global.cur_season_tick = 10079 df.global.cur_year_tick = df.global.cur_year_tick + (10079-cst)*10 - if not step_n_frames(10) then + if not step_n_frames(10, feed) then return false end end @@ -1203,7 +1418,7 @@ end -- local function find_pause_state() - local zone + local zone, addr if os_type == 'linux' or os_type == 'darwin' then zone = zoomed_searcher('ui_look_cursor', 32) elseif os_type == 'windows' then @@ -1213,13 +1428,33 @@ local function find_pause_state() stop_autosave() - local addr = zone:find_menu_cursor([[ + if dwarfmode_to_top() then + addr = zone:find_interactive( + 'Auto-searching for pause_state', + 'int8_t', + function(idx) + if idx%2 == 0 then + dwarfmode_feed_input('D_ONESTEP') + return true, 0 + else + screen_dwarfmode():logic() + return true, 1 + end + end, + 20 + ) + end + + if not addr then + addr = zone:find_menu_cursor([[ Searching for pause_state. Please do as instructed below:]], - 'int8_t', - { 1, 0 }, - { [1] = 'PAUSE the game', - [0] = 'UNPAUSE the game' } - ) + 'int8_t', + { 1, 0 }, + { [1] = 'PAUSE the game', + [0] = 'UNPAUSE the game' } + ) + end + ms.found_offset('pause_state', addr) end From faa131942c283cc7bcd809a469b2e22f411fa728 Mon Sep 17 00:00:00 2001 From: Quietust Date: Thu, 4 Oct 2012 20:14:50 -0500 Subject: [PATCH 062/114] Partial rewrite of 'fastdwarf' plugin: * add "fastdwarf 2" to use DF builtin "turbo speed" debug setting * use Units::isCitizen() instead of race/civ check * only scan active units, not all of them * do both fastdwarf and teledwarf in the same loop * teledwarf: don't use MapCache - it's faster to do it directly * teledwarf: don't clear both occupancy flags - check unit 'prone' flag * teledwarf: set proper unit occupancy flag at destination tile * teledwarf: if destination tile has standing unit, make dwarf lie down * cleanup 'fastdwarf' command * improve help text --- plugins/fastdwarf.cpp | 201 ++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 84 deletions(-) diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index aea14e7f0..f1ecb0baa 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -2,134 +2,165 @@ #include "Console.h" #include "Export.h" #include "PluginManager.h" -#include "modules/MapCache.h" +#include "modules/Units.h" +#include "modules/Maps.h" #include "DataDefs.h" -#include "df/ui.h" #include "df/world.h" #include "df/unit.h" +#include "df/map_block.h" using std::string; using std::vector; using namespace DFHack; using df::global::world; -using df::global::ui; // dfhack interface DFHACK_PLUGIN("fastdwarf"); +static bool enable_fastdwarf = false; +static bool enable_teledwarf = false; + DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { + if (df::global::debug_turbospeed) + *df::global::debug_turbospeed = false; return CR_OK; } -static bool enable_fastdwarf = false; -static bool enable_teledwarf = false; - DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { - // check run conditions - if(!world || !world->map.block_index) + // do we even need to do anything at all? + if (!enable_fastdwarf && !enable_teledwarf) + return CR_OK; + + // make sure the world is actually loaded + if (!world || !world->map.block_index) { enable_fastdwarf = enable_teledwarf = false; return CR_OK; } - int32_t race = ui->race_id; - int32_t civ = ui->civ_id; - - if ( enable_fastdwarf ) { - for (size_t i = 0; i < world->units.all.size(); i++) + + df::map_block *old_block, *new_block; + for (size_t i = 0; i < world->units.active.size(); i++) + { + df::unit *unit = world->units.active[i]; + // citizens only + if (!Units::isCitizen(unit)) + continue; + + if (enable_fastdwarf) { - df::unit *unit = world->units.all[i]; - - if (unit->race == race && unit->civ_id == civ && unit->counters.job_counter > 0) + + if (unit->counters.job_counter > 0) unit->counters.job_counter = 0; // could also patch the unit->job.current_job->completion_timer } - } - if ( enable_teledwarf ) { - MapExtras::MapCache *MCache = new MapExtras::MapCache(); - for (size_t i = 0; i < world->units.all.size(); i++) + if (enable_teledwarf) { - df::unit *unit = world->units.all[i]; - - if (unit->race != race || unit->civ_id != civ || unit->path.dest.x == -30000) + // don't do anything if the dwarf isn't going anywhere + if (unit->path.dest.x == -30000) continue; - if (unit->relations.draggee_id != -1 || unit->relations.dragger_id != -1) + + // skip dwarves that are dragging creatures or being dragged + if ((unit->relations.draggee_id != -1) || (unit->relations.dragger_id != -1)) continue; + + // skip dwarves that are following other units if (unit->relations.following != 0) continue; - - MapExtras::Block* block = MCache->BlockAtTile(unit->pos); - df::coord2d pos(unit->pos.x % 16, unit->pos.y % 16); - df::tile_occupancy occ = block->OccupancyAt(pos); - occ.bits.unit = 0; - occ.bits.unit_grounded = 0; - block->setOccupancyAt(pos, occ); - - //move immediately to destination + + old_block = Maps::getTileBlock(unit->pos.x, unit->pos.y, unit->pos.z); + new_block = Maps::getTileBlock(unit->path.dest.x, unit->path.dest.y, unit->path.dest.z); + // just to be safe, prevent the dwarf from being moved to an unallocated map block! + if (!old_block || !new_block) + continue; + + // clear appropriate occupancy flags at old tile + if (unit->flags1.bits.on_ground) + // this is technically wrong, but the game will recompute this as needed + old_block->occupancy[unit->pos.x & 0xF][unit->pos.y & 0xF].bits.unit_grounded = 0; + else + old_block->occupancy[unit->pos.x & 0xF][unit->pos.y & 0xF].bits.unit = 0; + + // if there's already somebody standing at the destination, then force the unit to lay down + if (new_block->occupancy[unit->path.dest.x & 0xF][unit->path.dest.y & 0xF].bits.unit) + unit->flags1.bits.on_ground = 1; + + // set appropriate occupancy flags at new tile + if (unit->flags1.bits.on_ground) + new_block->occupancy[unit->path.dest.x & 0xF][unit->path.dest.y & 0xF].bits.unit_grounded = 1; + else + new_block->occupancy[unit->path.dest.x & 0xF][unit->path.dest.y & 0xF].bits.unit = 1; + + // move unit to destination unit->pos.x = unit->path.dest.x; unit->pos.y = unit->path.dest.y; unit->pos.z = unit->path.dest.z; } - MCache->WriteAll(); - delete MCache; } return CR_OK; } static command_result fastdwarf (color_ostream &out, vector & parameters) { - if (parameters.size() == 1) { - if ( parameters[0] == "0" ) { - enable_fastdwarf = false; - enable_teledwarf = false; - } else if ( parameters[0] == "1" ) { - enable_fastdwarf = true; - enable_teledwarf = false; - } else { - out.print("Incorrect usage.\n"); - return CR_OK; + if (parameters.size() > 2) { + out.print("Incorrect usage.\n"); + return CR_FAILURE; + } + + if (parameters.size() <= 2) + { + if (parameters.size() == 2) + { + if (parameters[1] == "0") + enable_teledwarf = false; + else if (parameters[1] == "1") + enable_teledwarf = true; + else + { + out.print("Incorrect usage.\n"); + return CR_FAILURE; + } } - } else if (parameters.size() == 2) { - if ( parameters[0] == "0" ) { + else + enable_teledwarf = false; + if (parameters[0] == "0") + { enable_fastdwarf = false; - } else if ( parameters[0] == "1" ) { + if (df::global::debug_turbospeed) + *df::global::debug_turbospeed = false; + } + else if (parameters[0] == "1") + { enable_fastdwarf = true; - } else { - out.print("Incorrect usage.\n"); - return CR_OK; + if (df::global::debug_turbospeed) + *df::global::debug_turbospeed = false; } - if ( parameters[1] == "0" ) { - enable_teledwarf = false; - } else if ( parameters[1] == "1" ) { - enable_teledwarf = true; - } else { - out.print("Incorrect usage.\n"); - return CR_OK; + else if (parameters[0] == "2") + { + if (df::global::debug_turbospeed) + { + enable_fastdwarf = false; + *df::global::debug_turbospeed = true; + } + else + { + out.print("Speed level 2 not available.\n"); + return CR_FAILURE; + } } - } else if (parameters.size() == 0) { - //print status - out.print("Current state: fast = %d, teleport = %d.\n", enable_fastdwarf, enable_teledwarf); - } else { - out.print("Incorrect usage.\n"); - return CR_OK; - } - /*if (parameters.size() == 1 && (parameters[0] == "0" || parameters[0] == "1")) - { - if (parameters[0] == "0") - enable_fastdwarf = 0; else - enable_fastdwarf = 1; - out.print("fastdwarf %sactivated.\n", (enable_fastdwarf ? "" : "de")); + { + out.print("Incorrect usage.\n"); + return CR_FAILURE; + } } - else - { - out.print("Makes your minions move at ludicrous speeds.\n" - "Activate with 'fastdwarf 1', deactivate with 'fastdwarf 0'.\n" - "Current state: %d.\n", enable_fastdwarf); - }*/ + + out.print("Current state: fast = %d, teleport = %d.\n", + (df::global::debug_turbospeed && *df::global::debug_turbospeed) ? 2 : (enable_fastdwarf ? 1 : 0), + enable_teledwarf ? 1 : 0); return CR_OK; } @@ -139,14 +170,16 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector (tele)\n" + "Valid values for speed:\n" + " * 0 - Make dwarves move and work at standard speed.\n" + " * 1 - Make dwarves move and work at maximum speed.\n" + " * 2 - Make ALL creatures move and work at maximum speed.\n" + "Valid values for (tele):\n" + " * 0 - Disable dwarf teleportation (default)\n" + " * 1 - Make dwarves teleport to their destinations instantly.\n" )); return CR_OK; From 6b3d85eb0feb556a3dd73144d1e61009e2753e56 Mon Sep 17 00:00:00 2001 From: Quietust Date: Thu, 4 Oct 2012 20:17:33 -0500 Subject: [PATCH 063/114] Don't make teledwarf skip everything after it if there's a problem --- plugins/fastdwarf.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index f1ecb0baa..4964b542a 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -52,12 +52,12 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (enable_fastdwarf) { - if (unit->counters.job_counter > 0) unit->counters.job_counter = 0; // could also patch the unit->job.current_job->completion_timer } - if (enable_teledwarf) + + if (enable_teledwarf) do { // don't do anything if the dwarf isn't going anywhere if (unit->path.dest.x == -30000) @@ -73,7 +73,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) old_block = Maps::getTileBlock(unit->pos.x, unit->pos.y, unit->pos.z); new_block = Maps::getTileBlock(unit->path.dest.x, unit->path.dest.y, unit->path.dest.z); - // just to be safe, prevent the dwarf from being moved to an unallocated map block! + // make sure source and dest map blocks are valid if (!old_block || !new_block) continue; @@ -98,7 +98,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) unit->pos.x = unit->path.dest.x; unit->pos.y = unit->path.dest.y; unit->pos.z = unit->path.dest.z; - } + } while (0); } return CR_OK; } From ddcc2ee90da2cdd787a81aab09adfc118fd7a97d Mon Sep 17 00:00:00 2001 From: Quietust Date: Thu, 4 Oct 2012 20:20:35 -0500 Subject: [PATCH 064/114] Should use "break" in this construct, not "continue" --- plugins/fastdwarf.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 4964b542a..3f3905ecc 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -61,21 +61,21 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { // don't do anything if the dwarf isn't going anywhere if (unit->path.dest.x == -30000) - continue; + break; // skip dwarves that are dragging creatures or being dragged if ((unit->relations.draggee_id != -1) || (unit->relations.dragger_id != -1)) - continue; + break; // skip dwarves that are following other units if (unit->relations.following != 0) - continue; + break; old_block = Maps::getTileBlock(unit->pos.x, unit->pos.y, unit->pos.z); new_block = Maps::getTileBlock(unit->path.dest.x, unit->path.dest.y, unit->path.dest.z); // make sure source and dest map blocks are valid if (!old_block || !new_block) - continue; + break; // clear appropriate occupancy flags at old tile if (unit->flags1.bits.on_ground) From cbbb164dad740cdaa08fde9dec763b7259676996 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 5 Oct 2012 20:44:29 +0200 Subject: [PATCH 065/114] ruby: add MapTile distance_to and spiral_search --- plugins/ruby/map.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb index 371614704..7c616d722 100644 --- a/plugins/ruby/map.rb +++ b/plugins/ruby/map.rb @@ -240,5 +240,35 @@ module DFHack def spawn_magma(quantity=7) spawn_liquid(quantity, true) end + + # yield a serie of tiles until the block returns true, returns the matching tile + # the yielded tiles form a (squared) spiral centered here in the current zlevel + # eg for radius 4, yields (-4, -4), (-4, -3), .., (-4, 3), + # (-4, 4), (-3, 4), .., (4, 4), .., (4, -4), .., (-3, -4) + # then move on to radius 5 + def spiral_search(maxradius=100, minradius=0, step=1) + if minradius == 0 + return self if yield self + minradius += step + end + + sides = [[0, 1], [1, 0], [0, -1], [-1, 0]] + (minradius..maxradius).step(step) { |r| + sides.length.times { |s| + dxr, dyr = sides[(s-1) % sides.length] + dx, dy = sides[s] + (-r...r).step(step) { |v| + t = offset(dxr*r + dx*v, dyr*r + dy*v) + return t if t and yield t + } + } + } + nil + end + + # returns dx^2+dy^2+dz^2 + def distance_to(ot) + (x-ot.x)**2 + (y-ot.y)**2 + (z-ot.z)**2 + end end end From 5396a67465a374260f81baef03c03381d8a09059 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 6 Oct 2012 12:40:46 +0400 Subject: [PATCH 066/114] Some tweaking and edited NEWS. --- NEWS | 5 +++- .../include/df/custom/coord_path.methods.inc | 7 +++++ library/include/modules/MapCache.h | 8 ------ library/include/modules/Maps.h | 15 +++++++++++ library/modules/Maps.cpp | 6 ++--- plugins/fastdwarf.cpp | 27 ++++++++++--------- 6 files changed, 44 insertions(+), 24 deletions(-) diff --git a/NEWS b/NEWS index 65339ad64..e066b7431 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ DFHack future - Nothing yet! + Internals: + - support for displaying active keybindings properly. + Misc improvements: + - fastdwarf: new mode using debug flags, and some internal consistency fixes. DFHack v0.34.11-r2 diff --git a/library/include/df/custom/coord_path.methods.inc b/library/include/df/custom/coord_path.methods.inc index 9acebb82a..5421796e3 100644 --- a/library/include/df/custom/coord_path.methods.inc +++ b/library/include/df/custom/coord_path.methods.inc @@ -1,5 +1,12 @@ +bool empty() const { return x.empty(); } unsigned size() const { return x.size(); } +void clear() { + x.clear(); + y.clear(); + z.clear(); +} + coord operator[] (unsigned idx) const { if (idx >= x.size()) return coord(); diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index ac083075f..c1c478bc6 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -47,14 +47,6 @@ namespace MapExtras class DFHACK_EXPORT MapCache; -template inline R index_tile(T &v, df::coord2d p) { - return v[p.x&15][p.y&15]; -} - -inline bool is_valid_tile_coord(df::coord2d p) { - return (p.x & ~15) == 0 && (p.y & ~15) == 0; -} - class Block; class BlockInfo diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 3150acccf..632e8ec13 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -151,6 +151,21 @@ typedef uint8_t biome_indices40d [9]; */ typedef uint16_t t_temperatures [16][16]; +/** + * Index a tile array by a 2D coordinate, clipping it to mod 16 + */ +template inline R index_tile(T &v, df::coord2d p) { + return v[p.x&15][p.y&15]; +} + +/** + * Check if a 2D coordinate is in the 0-15 range. + */ +inline bool is_valid_tile_coord(df::coord2d p) { + return (p.x & ~15) == 0 && (p.y & ~15) == 0; +} + + /** * The Maps module * \ingroup grp_modules diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 5ef4ce829..482b950ba 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -454,7 +454,7 @@ df::coord2d Maps::getBlockTileBiomeRgn(df::map_block *block, df::coord2d pos) if (!block || !world->world_data) return df::coord2d(); - auto des = MapExtras::index_tile(block->designation,pos); + auto des = index_tile(block->designation,pos); unsigned idx = des.bits.biome; if (idx < 9) { @@ -529,8 +529,8 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) if (!block1 || !block2) return false; - auto tile1 = MapExtras::index_tile(block1->walkable, pos1); - auto tile2 = MapExtras::index_tile(block2->walkable, pos2); + auto tile1 = index_tile(block1->walkable, pos1); + auto tile2 = index_tile(block2->walkable, pos2); return tile1 && tile1 == tile2; } diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 3f3905ecc..28104b909 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -60,7 +60,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (enable_teledwarf) do { // don't do anything if the dwarf isn't going anywhere - if (unit->path.dest.x == -30000) + if (!unit->path.dest.isValid()) break; // skip dwarves that are dragging creatures or being dragged @@ -71,33 +71,36 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (unit->relations.following != 0) break; - old_block = Maps::getTileBlock(unit->pos.x, unit->pos.y, unit->pos.z); - new_block = Maps::getTileBlock(unit->path.dest.x, unit->path.dest.y, unit->path.dest.z); + // skip unconscious units + if (unit->counters.unconscious > 0) + break; + // make sure source and dest map blocks are valid - if (!old_block || !new_block) + auto old_occ = Maps::getTileOccupancy(unit->pos); + auto new_occ = Maps::getTileOccupancy(unit->path.dest); + if (!old_occ || !new_occ) break; // clear appropriate occupancy flags at old tile if (unit->flags1.bits.on_ground) // this is technically wrong, but the game will recompute this as needed - old_block->occupancy[unit->pos.x & 0xF][unit->pos.y & 0xF].bits.unit_grounded = 0; + old_occ->bits.unit_grounded = 0; else - old_block->occupancy[unit->pos.x & 0xF][unit->pos.y & 0xF].bits.unit = 0; + old_occ->bits.unit = 0; // if there's already somebody standing at the destination, then force the unit to lay down - if (new_block->occupancy[unit->path.dest.x & 0xF][unit->path.dest.y & 0xF].bits.unit) + if (new_occ->bits.unit) unit->flags1.bits.on_ground = 1; // set appropriate occupancy flags at new tile if (unit->flags1.bits.on_ground) - new_block->occupancy[unit->path.dest.x & 0xF][unit->path.dest.y & 0xF].bits.unit_grounded = 1; + new_occ->bits.unit_grounded = 1; else - new_block->occupancy[unit->path.dest.x & 0xF][unit->path.dest.y & 0xF].bits.unit = 1; + new_occ->bits.unit = 1; // move unit to destination - unit->pos.x = unit->path.dest.x; - unit->pos.y = unit->path.dest.y; - unit->pos.z = unit->path.dest.z; + unit->pos = unit->path.dest; + unit->path.path.clear(); } while (0); } return CR_OK; From 6fefd0907281b41bbcbe27df1bea113ab8c46c1b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 6 Oct 2012 12:51:34 +0400 Subject: [PATCH 067/114] Fix re-enabling autobutcher after being disabled. --- NEWS | 2 ++ plugins/zone.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index e066b7431..b36d2f121 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ DFHack future Internals: - support for displaying active keybindings properly. + Notable bugfixes: + - autobutcher can be re-enabled again after being stopped. Misc improvements: - fastdwarf: new mode using debug flags, and some internal consistency fixes. diff --git a/plugins/zone.cpp b/plugins/zone.cpp index c496f49b6..f32a77a2a 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -3408,10 +3408,10 @@ command_result start_autobutcher(color_ostream &out) auto pworld = Core::getInstance().getWorld(); enable_autobutcher = true; + if (!config_autobutcher.isValid()) { config_autobutcher = pworld->AddPersistentData("autobutcher/config"); - config_autobutcher.ival(0) = enable_autobutcher; config_autobutcher.ival(1) = sleep_autobutcher; config_autobutcher.ival(2) = enable_autobutcher_autowatch; config_autobutcher.ival(3) = default_fk; @@ -3420,6 +3420,8 @@ command_result start_autobutcher(color_ostream &out) config_autobutcher.ival(6) = default_ma; } + config_autobutcher.ival(0) = enable_autobutcher; + out << "Starting autobutcher." << endl; init_autobutcher(out); return CR_OK; From 696cc4a911a077463def422189444a3acf3e46db Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 6 Oct 2012 13:07:11 +0400 Subject: [PATCH 068/114] Stop autobutcher and autonestbox crashing if started without world. --- plugins/zone.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/plugins/zone.cpp b/plugins/zone.cpp index f32a77a2a..6c1af4768 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -3412,6 +3412,13 @@ command_result start_autobutcher(color_ostream &out) if (!config_autobutcher.isValid()) { config_autobutcher = pworld->AddPersistentData("autobutcher/config"); + + if (!config_autobutcher.isValid()) + { + out << "Cannot enable autobutcher without a world!" << endl; + return CR_OK; + } + config_autobutcher.ival(1) = sleep_autobutcher; config_autobutcher.ival(2) = enable_autobutcher_autowatch; config_autobutcher.ival(3) = default_fk; @@ -3431,11 +3438,6 @@ command_result init_autobutcher(color_ostream &out) { cleanup_autobutcher(out); auto pworld = Core::getInstance().getWorld(); - if(!pworld) - { - out << "Autobutcher has no world to read from!" << endl; - return CR_OK; - } config_autobutcher = pworld->GetPersistentData("autobutcher/config"); if(config_autobutcher.isValid()) @@ -3502,12 +3504,22 @@ command_result start_autonestbox(color_ostream &out) { auto pworld = Core::getInstance().getWorld(); enable_autonestbox = true; + if (!config_autobutcher.isValid()) { config_autonestbox = pworld->AddPersistentData("autonestbox/config"); - config_autonestbox.ival(0) = enable_autonestbox; + + if (!config_autobutcher.isValid()) + { + out << "Cannot enable autonestbox without a world!" << endl; + return CR_OK; + } + config_autonestbox.ival(1) = sleep_autonestbox; } + + config_autonestbox.ival(0) = enable_autonestbox; + out << "Starting autonestbox." << endl; init_autonestbox(out); return CR_OK; @@ -3517,11 +3529,6 @@ command_result init_autonestbox(color_ostream &out) { cleanup_autonestbox(out); auto pworld = Core::getInstance().getWorld(); - if(!pworld) - { - out << "Autonestbox has no world to read from!" << endl; - return CR_OK; - } config_autonestbox = pworld->GetPersistentData("autonestbox/config"); if(config_autonestbox.isValid()) From 459c69046bbbde9877b29858495f2593731983b4 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 6 Oct 2012 13:46:20 +0400 Subject: [PATCH 069/114] Dissolve the World module class into a namespace. It made accessing persistent data way too cumbersome. --- library/Core.cpp | 8 +- library/LuaApi.cpp | 14 ++-- library/include/Core.h | 4 - library/include/modules/World.h | 68 ++++++++--------- library/modules/World.cpp | 126 +++++++++----------------------- plugins/autolabor.cpp | 14 ++-- plugins/burrows.cpp | 4 +- plugins/follow.cpp | 5 +- plugins/mode.cpp | 8 +- plugins/power-meter.cpp | 6 +- plugins/rename.cpp | 6 +- plugins/reveal.cpp | 18 ++--- plugins/seedwatch.cpp | 6 +- plugins/siege-engine.cpp | 45 +++++------- plugins/weather.cpp | 9 +-- plugins/workflow.cpp | 16 ++-- plugins/zone.cpp | 22 ++---- 17 files changed, 134 insertions(+), 245 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index bc90abc6b..a8000070f 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -627,8 +627,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve } else if(first == "fpause") { - World * w = getWorld(); - w->SetPauseState(true); + World::SetPauseState(true); con.print("The game was forced to pause!\n"); } else if(first == "cls") @@ -1125,7 +1124,7 @@ void Core::doUpdate(color_ostream &out, bool first_update) last_world_data_ptr = new_wdata; last_local_map_ptr = new_mapdata; - getWorld()->ClearPersistentCache(); + World::ClearPersistentCache(); // and if the world is going away, we report the map change first if(had_map) @@ -1143,7 +1142,7 @@ void Core::doUpdate(color_ostream &out, bool first_update) if (isMapLoaded() != had_map) { - getWorld()->ClearPersistentCache(); + World::ClearPersistentCache(); onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); } } @@ -1681,7 +1680,6 @@ TYPE * Core::get##TYPE() \ return s_mods.p##TYPE;\ } -MODULE_GETTER(World); MODULE_GETTER(Materials); MODULE_GETTER(Notes); MODULE_GETTER(Graphic); diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index aba6301d2..0df96f066 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -258,7 +258,7 @@ static PersistentDataItem persistent_by_struct(lua_State *state, int idx) int id = lua_tointeger(state, -1); lua_pop(state, 1); - PersistentDataItem ref = Core::getInstance().getWorld()->GetPersistentData(id); + PersistentDataItem ref = World::GetPersistentData(id); if (ref.isValid()) { @@ -323,7 +323,7 @@ static PersistentDataItem get_persistent(lua_State *state) { const char *str = luaL_checkstring(state, 1); - return Core::getInstance().getWorld()->GetPersistentData(str); + return World::GetPersistentData(str); } } @@ -342,7 +342,7 @@ static int dfhack_persistent_delete(lua_State *state) auto ref = get_persistent(state); - bool ok = Core::getInstance().getWorld()->DeletePersistentData(ref); + bool ok = World::DeletePersistentData(ref); lua_pushboolean(state, ok); return 1; @@ -356,7 +356,7 @@ static int dfhack_persistent_get_all(lua_State *state) bool prefix = (lua_gettop(state)>=2 ? lua_toboolean(state,2) : false); std::vector data; - Core::getInstance().getWorld()->GetPersistentData(&data, str, prefix); + World::GetPersistentData(&data, str, prefix); if (data.empty()) { @@ -396,7 +396,7 @@ static int dfhack_persistent_save(lua_State *state) if (add) { - ref = Core::getInstance().getWorld()->AddPersistentData(str); + ref = World::AddPersistentData(str); added = true; } else if (lua_getmetatable(state, 1)) @@ -409,13 +409,13 @@ static int dfhack_persistent_save(lua_State *state) } else { - ref = Core::getInstance().getWorld()->GetPersistentData(str); + ref = World::GetPersistentData(str); } // Auto-add if not found if (!ref.isValid()) { - ref = Core::getInstance().getWorld()->AddPersistentData(str); + ref = World::AddPersistentData(str); if (!ref.isValid()) luaL_error(state, "cannot create persistent entry"); added = true; diff --git a/library/include/Core.h b/library/include/Core.h index d2d7080da..b3db50c74 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -54,7 +54,6 @@ namespace DFHack { class Process; class Module; - class World; class Materials; class Notes; struct VersionInfo; @@ -120,8 +119,6 @@ namespace DFHack /// Is everything OK? bool isValid(void) { return !errorstate; } - /// get the world module - World * getWorld(); /// get the materials module Materials * getMaterials(); /// get the notes module @@ -205,7 +202,6 @@ namespace DFHack // Module storage struct { - World * pWorld; Materials * pMaterials; Notes * pNotes; Graphic * pGraphic; diff --git a/library/include/modules/World.h b/library/include/modules/World.h index 1cd57e2f2..f4c31dcf3 100644 --- a/library/include/modules/World.h +++ b/library/include/modules/World.h @@ -55,8 +55,6 @@ namespace DFHack class DFContextShared; class DFHACK_EXPORT PersistentDataItem { - friend class World; - int id; std::string key_value; @@ -65,13 +63,17 @@ namespace DFHack public: static const int NumInts = 7; - bool isValid() { return id != 0; } - int entry_id() { return -id; } + bool isValid() const { return id != 0; } + int entry_id() const { return -id; } + + int raw_id() const { return id; } - const std::string &key() { return key_value; } + const std::string &key() const { return key_value; } std::string &val() { return *str_value; } + const std::string &val() const { return *str_value; } int &ival(int i) { return int_values[i]; } + int ival(int i) const { return int_values[i]; } PersistentDataItem() : id(0), str_value(0), int_values(0) {} PersistentDataItem(int id, const std::string &key, std::string *sv, int *iv) @@ -83,54 +85,42 @@ namespace DFHack * \ingroup grp_modules * \ingroup grp_world */ - class DFHACK_EXPORT World : public Module + namespace World { - public: - World(); - ~World(); - bool Start(); - bool Finish(); - ///true if paused, false if not - bool ReadPauseState(); + DFHACK_EXPORT bool ReadPauseState(); ///true if paused, false if not - void SetPauseState(bool paused); - - uint32_t ReadCurrentTick(); - uint32_t ReadCurrentYear(); - uint32_t ReadCurrentMonth(); - uint32_t ReadCurrentDay(); - uint8_t ReadCurrentWeather(); - void SetCurrentWeather(uint8_t weather); - bool ReadGameMode(t_gamemodes& rd); - bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous - std::string ReadWorldFolder(); + DFHACK_EXPORT void SetPauseState(bool paused); + + DFHACK_EXPORT uint32_t ReadCurrentTick(); + DFHACK_EXPORT uint32_t ReadCurrentYear(); + DFHACK_EXPORT uint32_t ReadCurrentMonth(); + DFHACK_EXPORT uint32_t ReadCurrentDay(); + DFHACK_EXPORT uint8_t ReadCurrentWeather(); + DFHACK_EXPORT void SetCurrentWeather(uint8_t weather); + DFHACK_EXPORT bool ReadGameMode(t_gamemodes& rd); + DFHACK_EXPORT bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous + DFHACK_EXPORT std::string ReadWorldFolder(); // Store data in fake historical figure names. // This ensures that the values are stored in save games. - PersistentDataItem AddPersistentData(const std::string &key); - PersistentDataItem GetPersistentData(const std::string &key); - PersistentDataItem GetPersistentData(int entry_id); + DFHACK_EXPORT PersistentDataItem AddPersistentData(const std::string &key); + DFHACK_EXPORT PersistentDataItem GetPersistentData(const std::string &key); + DFHACK_EXPORT PersistentDataItem GetPersistentData(int entry_id); // Calls GetPersistentData(key); if not found, adds and sets added to true. // The result can still be not isValid() e.g. if the world is not loaded. - PersistentDataItem GetPersistentData(const std::string &key, bool *added); + DFHACK_EXPORT PersistentDataItem GetPersistentData(const std::string &key, bool *added); // Lists all items with the given key. // If prefix is true, search for keys starting with key+"/". // GetPersistentData(&vec,"",true) returns all items. // Items have alphabetic order by key; same key ordering is undefined. - void GetPersistentData(std::vector *vec, - const std::string &key, bool prefix = false); + DFHACK_EXPORT void GetPersistentData(std::vector *vec, + const std::string &key, bool prefix = false); // Deletes the item; returns true if success. - bool DeletePersistentData(const PersistentDataItem &item); + DFHACK_EXPORT bool DeletePersistentData(const PersistentDataItem &item); - void ClearPersistentCache(); - - private: - struct Private; - Private *d; - - bool BuildPersistentCache(); - }; + DFHACK_EXPORT void ClearPersistentCache(); + } } #endif diff --git a/library/modules/World.cpp b/library/modules/World.cpp index a4c4c5604..65d0c7cb8 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -48,89 +48,34 @@ using namespace DFHack; using df::global::world; -Module* DFHack::createWorld() -{ - return new World(); -} - -struct World::Private -{ - Private() - { - Inited = PauseInited = StartedWeather = StartedMode = false; - next_persistent_id = 0; - } - bool Inited; - - bool PauseInited; - bool StartedWeather; - bool StartedMode; - - int next_persistent_id; - std::multimap persistent_index; - - Process * owner; -}; - +static int next_persistent_id = 0; +static std::multimap persistent_index; typedef std::pair T_persistent_item; -World::World() -{ - Core & c = Core::getInstance(); - d = new Private; - d->owner = c.p; - - if(df::global::pause_state) - d->PauseInited = true; - - if(df::global::current_weather) - d->StartedWeather = true; - if (df::global::gamemode && df::global::gametype) - d->StartedMode = true; - - d->Inited = true; -} - -World::~World() -{ - delete d; -} - -bool World::Start() -{ - return true; -} - -bool World::Finish() -{ - return true; -} - bool World::ReadPauseState() { - if(!d->PauseInited) return false; - return *df::global::pause_state; + return DF_GLOBAL_VALUE(pause_state, false); } void World::SetPauseState(bool paused) { - if (d->PauseInited) - *df::global::pause_state = paused; + bool dummy; + DF_GLOBAL_VALUE(pause_state, dummy) = paused; } uint32_t World::ReadCurrentYear() { - return *df::global::cur_year; + return DF_GLOBAL_VALUE(cur_year, 0); } uint32_t World::ReadCurrentTick() { - return *df::global::cur_year_tick; + return DF_GLOBAL_VALUE(cur_year_tick, 0); } bool World::ReadGameMode(t_gamemodes& rd) { - if(d->Inited && d->StartedMode) + if(df::global::gamemode && df::global::gametype) { rd.g_mode = (DFHack::GameMode)*df::global::gamemode; rd.g_type = (DFHack::GameType)*df::global::gametype; @@ -140,7 +85,7 @@ bool World::ReadGameMode(t_gamemodes& rd) } bool World::WriteGameMode(const t_gamemodes & wr) { - if(d->Inited && d->StartedMode) + if(df::global::gamemode && df::global::gametype) { *df::global::gamemode = wr.g_mode; *df::global::gametype = wr.g_type; @@ -173,24 +118,24 @@ specified by memory.xml gets me the current month/date. */ uint32_t World::ReadCurrentMonth() { - return this->ReadCurrentTick() / 1200 / 28; + return ReadCurrentTick() / 1200 / 28; } uint32_t World::ReadCurrentDay() { - return ((this->ReadCurrentTick() / 1200) % 28) + 1; + return ((ReadCurrentTick() / 1200) % 28) + 1; } uint8_t World::ReadCurrentWeather() { - if (d->Inited && d->StartedWeather) + if (df::global::current_weather) return (*df::global::current_weather)[2][2]; return 0; } void World::SetCurrentWeather(uint8_t weather) { - if (d->Inited && d->StartedWeather) + if (df::global::current_weather) memset(df::global::current_weather, weather, 25); } @@ -206,13 +151,13 @@ static PersistentDataItem dataFromHFig(df::historical_figure *hfig) void World::ClearPersistentCache() { - d->next_persistent_id = 0; - d->persistent_index.clear(); + next_persistent_id = 0; + persistent_index.clear(); } -bool World::BuildPersistentCache() +static bool BuildPersistentCache() { - if (d->next_persistent_id) + if (next_persistent_id) return true; if (!Core::getInstance().isWorldLoaded()) return false; @@ -220,20 +165,20 @@ bool World::BuildPersistentCache() std::vector &hfvec = df::historical_figure::get_vector(); // Determine the next entry id as min(-100, lowest_id-1) - d->next_persistent_id = -100; + next_persistent_id = -100; if (hfvec.size() > 0 && hfvec[0]->id <= -100) - d->next_persistent_id = hfvec[0]->id-1; + next_persistent_id = hfvec[0]->id-1; // Add the entries to the lookup table - d->persistent_index.clear(); + persistent_index.clear(); for (size_t i = 0; i < hfvec.size() && hfvec[i]->id <= -100; i++) { if (!hfvec[i]->name.has_name || hfvec[i]->name.first_name.empty()) continue; - d->persistent_index.insert(T_persistent_item(hfvec[i]->name.first_name, -hfvec[i]->id)); + persistent_index.insert(T_persistent_item(hfvec[i]->name.first_name, -hfvec[i]->id)); } return true; @@ -247,14 +192,14 @@ PersistentDataItem World::AddPersistentData(const std::string &key) std::vector &hfvec = df::historical_figure::get_vector(); df::historical_figure *hfig = new df::historical_figure(); - hfig->id = d->next_persistent_id--; + hfig->id = next_persistent_id--; hfig->name.has_name = true; hfig->name.first_name = key; memset(hfig->name.words, 0xFF, sizeof(hfig->name.words)); hfvec.insert(hfvec.begin(), hfig); - d->persistent_index.insert(T_persistent_item(key, -hfig->id)); + persistent_index.insert(T_persistent_item(key, -hfig->id)); return dataFromHFig(hfig); } @@ -264,8 +209,8 @@ PersistentDataItem World::GetPersistentData(const std::string &key) if (!BuildPersistentCache()) return PersistentDataItem(); - auto it = d->persistent_index.find(key); - if (it != d->persistent_index.end()) + auto it = persistent_index.find(key); + if (it != persistent_index.end()) return GetPersistentData(it->second); return PersistentDataItem(); @@ -305,24 +250,24 @@ void World::GetPersistentData(std::vector *vec, const std::s if (!BuildPersistentCache()) return; - auto eqrange = d->persistent_index.equal_range(key); + auto eqrange = persistent_index.equal_range(key); if (prefix) { if (key.empty()) { - eqrange.first = d->persistent_index.begin(); - eqrange.second = d->persistent_index.end(); + eqrange.first = persistent_index.begin(); + eqrange.second = persistent_index.end(); } else { std::string bound = key; if (bound[bound.size()-1] != '/') bound += "/"; - eqrange.first = d->persistent_index.lower_bound(bound); + eqrange.first = persistent_index.lower_bound(bound); bound[bound.size()-1]++; - eqrange.second = d->persistent_index.lower_bound(bound); + eqrange.second = persistent_index.lower_bound(bound); } } @@ -336,25 +281,26 @@ void World::GetPersistentData(std::vector *vec, const std::s bool World::DeletePersistentData(const PersistentDataItem &item) { - if (item.id > -100) + int id = item.raw_id(); + if (id > -100) return false; if (!BuildPersistentCache()) return false; std::vector &hfvec = df::historical_figure::get_vector(); - auto eqrange = d->persistent_index.equal_range(item.key_value); + auto eqrange = persistent_index.equal_range(item.key()); for (auto it2 = eqrange.first; it2 != eqrange.second; ) { auto it = it2; ++it2; - if (it->second != -item.id) + if (it->second != -id) continue; - d->persistent_index.erase(it); + persistent_index.erase(it); - int idx = binsearch_index(hfvec, item.id); + int idx = binsearch_index(hfvec, id); if (idx >= 0) { delete hfvec[idx]; diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index c39b126c9..42ccae4c2 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -547,9 +547,7 @@ static void reset_labor(df::unit_labor labor) static void init_state() { - auto pworld = Core::getInstance().getWorld(); - - config = pworld->GetPersistentData("autolabor/config"); + config = World::GetPersistentData("autolabor/config"); if (config.isValid() && config.ival(0) == -1) config.ival(0) = 0; @@ -558,7 +556,7 @@ static void init_state() if (!enable_autolabor) return; - auto cfg_haulpct = pworld->GetPersistentData("autolabor/haulpct"); + auto cfg_haulpct = World::GetPersistentData("autolabor/haulpct"); if (cfg_haulpct.isValid()) { hauler_pct = cfg_haulpct.ival(0); @@ -572,7 +570,7 @@ static void init_state() labor_infos.resize(ARRAY_COUNT(default_labor_infos)); std::vector items; - pworld->GetPersistentData(&items, "autolabor/labors/", true); + World::GetPersistentData(&items, "autolabor/labors/", true); for (auto p = items.begin(); p != items.end(); p++) { @@ -594,7 +592,7 @@ static void init_state() std::stringstream name; name << "autolabor/labors/" << i; - labor_infos[i].config = pworld->AddPersistentData(name.str()); + labor_infos[i].config = World::AddPersistentData(name.str()); labor_infos[i].is_exclusive = default_labor_infos[i].is_exclusive; labor_infos[i].active_dwarfs = 0; @@ -633,11 +631,9 @@ static void generate_labor_to_skill_map() static void enable_plugin(color_ostream &out) { - auto pworld = Core::getInstance().getWorld(); - if (!config.isValid()) { - config = pworld->AddPersistentData("autolabor/config"); + config = World::AddPersistentData("autolabor/config"); config.ival(0) = 0; } diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp index 0b66a7b9a..edcc01ecf 100644 --- a/plugins/burrows.cpp +++ b/plugins/burrows.cpp @@ -281,7 +281,7 @@ static void reset_tracking() static void init_map(color_ostream &out) { - auto config = Core::getInstance().getWorld()->GetPersistentData("burrows/config"); + auto config = World::GetPersistentData("burrows/config"); if (config.isValid()) { auto_grow = !!(config.ival(0) & 1); @@ -307,7 +307,7 @@ static void deinit_map(color_ostream &out) static PersistentDataItem create_config(color_ostream &out) { bool created; - auto rv = Core::getInstance().getWorld()->GetPersistentData("burrows/config", &created); + auto rv = World::GetPersistentData("burrows/config", &created); if (created && rv.isValid()) rv.ival(0) = 0; if (!rv.isValid()) diff --git a/plugins/follow.cpp b/plugins/follow.cpp index 96693df8d..5c14780a3 100644 --- a/plugins/follow.cpp +++ b/plugins/follow.cpp @@ -65,8 +65,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { if (!followedUnit) return CR_OK; //Don't do anything if we're not following a unit - DFHack::World *world = Core::getInstance().getWorld(); - if (world->ReadPauseState() && prevX==-1) return CR_OK; //Wait until the game is unpaused after first running "follow" to begin following + if (World::ReadPauseState() && prevX==-1) return CR_OK; //Wait until the game is unpaused after first running "follow" to begin following df::coord &unitPos = followedUnit->pos; @@ -120,7 +119,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) Gui::setViewCoords(x, y, z); //If, for some reason, the cursor is active and the screen is still moving, move the cursor along with the screen - if (c_x != -30000 && !world->ReadPauseState()) + if (c_x != -30000 && !World::ReadPauseState()) Gui::setCursorCoords(c_x - (prevX-x), c_y - (prevY-y), z); //Save this round's stuff for next time so we can monitor for changes made by the user diff --git a/plugins/mode.cpp b/plugins/mode.cpp index f9e6cd10c..d18cf7529 100644 --- a/plugins/mode.cpp +++ b/plugins/mode.cpp @@ -117,13 +117,9 @@ command_result mode (color_ostream &out_, vector & parameters) return CR_WRONG_USAGE; } - World *world; - { CoreSuspender suspend; - world = Core::getInstance().getWorld(); - world->Start(); - world->ReadGameMode(gm); + World::ReadGameMode(gm); } printCurrentModes(gm, out); @@ -202,7 +198,7 @@ command_result mode (color_ostream &out_, vector & parameters) { CoreSuspender suspend; - world->WriteGameMode(gm); + World::WriteGameMode(gm); } out << endl; diff --git a/plugins/power-meter.cpp b/plugins/power-meter.cpp index 17261adb2..8db6c6f30 100644 --- a/plugins/power-meter.cpp +++ b/plugins/power-meter.cpp @@ -177,8 +177,7 @@ static bool makePowerMeter(df::pressure_plate_info *info, int min_power, int max if (!enabled) { - auto pworld = Core::getInstance().getWorld(); - auto entry = pworld->GetPersistentData("power-meter/enabled", NULL); + auto entry = World::GetPersistentData("power-meter/enabled", NULL); if (!entry.isValid()) return false; @@ -202,8 +201,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan switch (event) { case SC_WORLD_LOADED: { - auto pworld = Core::getInstance().getWorld(); - bool enable = pworld->GetPersistentData("power-meter/enabled").isValid(); + bool enable = World::GetPersistentData("power-meter/enabled").isValid(); if (enable) { diff --git a/plugins/rename.cpp b/plugins/rename.cpp index ecebbb90c..bffb33111 100644 --- a/plugins/rename.cpp +++ b/plugins/rename.cpp @@ -215,8 +215,7 @@ static void init_buildings(bool enable) if (enable) { - auto pworld = Core::getInstance().getWorld(); - auto entry = pworld->GetPersistentData("rename/building_types"); + auto entry = World::GetPersistentData("rename/building_types"); if (entry.isValid()) { @@ -245,8 +244,7 @@ static bool renameBuilding(df::building *bld, std::string name) if (!name.empty() && !is_enabled_building(code)) { - auto pworld = Core::getInstance().getWorld(); - auto entry = pworld->GetPersistentData("rename/building_types", NULL); + auto entry = World::GetPersistentData("rename/building_types", NULL); if (!entry.isValid()) return false; diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index f35424309..8db7c9112 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -87,19 +87,18 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector ReadGameMode(gm); + World::ReadGameMode(gm); if(gm.g_mode == game_mode::DWARF) { // if the map is revealed and we're in fortress mode, force the game to pause. if(revealed == REVEALED) { - World->SetPauseState(true); + World::SetPauseState(true); } else if(nopause_state) { - World->SetPauseState(false); + World::SetPauseState(false); } } return CR_OK; @@ -185,14 +184,13 @@ command_result reveal(color_ostream &out, vector & params) CoreSuspender suspend; - World *World = Core::getInstance().getWorld(); if (!Maps::IsValid()) { out.printerr("Map is not available!\n"); return CR_FAILURE; } t_gamemodes gm; - World->ReadGameMode(gm); + World::ReadGameMode(gm); if(gm.g_mode == game_mode::ADVENTURE) { revealAdventure(out); @@ -234,7 +232,7 @@ command_result reveal(color_ostream &out, vector & params) if(pause) { revealed = REVEALED; - World->SetPauseState(true); + World::SetPauseState(true); } else revealed = DEMON_REVEALED; @@ -264,14 +262,13 @@ command_result unreveal(color_ostream &out, vector & params) } CoreSuspender suspend; - World *World = Core::getInstance().getWorld(); if (!Maps::IsValid()) { out.printerr("Map is not available!\n"); return CR_FAILURE; } t_gamemodes gm; - World->ReadGameMode(gm); + World::ReadGameMode(gm); if(gm.g_mode != game_mode::DWARF) { con.printerr("Only in fortress mode.\n"); @@ -337,7 +334,6 @@ command_result revflood(color_ostream &out, vector & params) } CoreSuspender suspend; uint32_t x_max,y_max,z_max; - World * World = Core::getInstance().getWorld(); if (!Maps::IsValid()) { out.printerr("Map is not available!\n"); @@ -349,7 +345,7 @@ command_result revflood(color_ostream &out, vector & params) return CR_FAILURE; } t_gamemodes gm; - World->ReadGameMode(gm); + World::ReadGameMode(gm); if(gm.g_type != game_type::DWARF_MAIN && gm.g_mode != game_mode::DWARF ) { out.printerr("Only in proper dwarf mode.\n"); diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index 676c02ed5..ae87e4381 100755 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -106,9 +106,8 @@ command_result df_seedwatch(color_ostream &out, vector& parameters) materialsReverser[world->raws.plants.all[i]->id] = i; } - World *w = Core::getInstance().getWorld(); t_gamemodes gm; - w->ReadGameMode(gm);// FIXME: check return value + World::ReadGameMode(gm);// FIXME: check return value // if game mode isn't fortress mode if(gm.g_mode != game_mode::DWARF || @@ -296,9 +295,8 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) return CR_OK; counter = 0; - World *w = Core::getInstance().getWorld(); t_gamemodes gm; - w->ReadGameMode(gm);// FIXME: check return value + World::ReadGameMode(gm);// FIXME: check return value // if game mode isn't fortress mode if(gm.g_mode != game_mode::DWARF || !(gm.g_type == game_type::DWARF_MAIN || gm.g_type == game_type::DWARF_RECLAIM)) diff --git a/plugins/siege-engine.cpp b/plugins/siege-engine.cpp index 7c880351e..324c924c4 100644 --- a/plugins/siege-engine.cpp +++ b/plugins/siege-engine.cpp @@ -347,10 +347,9 @@ static void load_engines() { clear_engines(); - auto pworld = Core::getInstance().getWorld(); std::vector vec; - pworld->GetPersistentData(&vec, "siege-engine/target/", true); + World::GetPersistentData(&vec, "siege-engine/target/", true); for (auto it = vec.begin(); it != vec.end(); ++it) { auto engine = find_engine(df::building::find(it->ival(0)), true); @@ -359,7 +358,7 @@ static void load_engines() engine->target.second = df::coord(it->ival(4), it->ival(5), it->ival(6)); } - pworld->GetPersistentData(&vec, "siege-engine/ammo/", true); + World::GetPersistentData(&vec, "siege-engine/ammo/", true); for (auto it = vec.begin(); it != vec.end(); ++it) { auto engine = find_engine(df::building::find(it->ival(0)), true); @@ -368,7 +367,7 @@ static void load_engines() engine->ammo_item_type = (df::item_type)it->ival(2); } - pworld->GetPersistentData(&vec, "siege-engine/stockpiles/", true); + World::GetPersistentData(&vec, "siege-engine/stockpiles/", true); for (auto it = vec.begin(); it != vec.end(); ++it) { auto engine = find_engine(df::building::find(it->ival(0)), true); @@ -377,14 +376,14 @@ static void load_engines() auto pile = df::building::find(it->ival(1)); if (!pile || pile->getType() != building_type::Stockpile) { - pworld->DeletePersistentData(*it); + World::DeletePersistentData(*it); continue;; } engine->stockpiles.insert(it->ival(1)); } - pworld->GetPersistentData(&vec, "siege-engine/profiles/", true); + World::GetPersistentData(&vec, "siege-engine/profiles/", true); for (auto it = vec.begin(); it != vec.end(); ++it) { auto engine = find_engine(df::building::find(it->ival(0)), true); @@ -393,7 +392,7 @@ static void load_engines() engine->profile.max_level = it->ival(2); } - pworld->GetPersistentData(&vec, "siege-engine/profile-workers/", true); + World::GetPersistentData(&vec, "siege-engine/profile-workers/", true); for (auto it = vec.begin(); it != vec.end(); ++it) { auto engine = find_engine(df::building::find(it->ival(0)), true); @@ -402,7 +401,7 @@ static void load_engines() auto unit = df::unit::find(it->ival(1)); if (!unit || !Units::isCitizen(unit)) { - pworld->DeletePersistentData(*it); + World::DeletePersistentData(*it); continue; } engine->profile.permitted_workers.push_back(it->ival(1)); @@ -434,9 +433,8 @@ static void clearTargetArea(df::building_siegeenginest *bld) if (auto engine = find_engine(bld)) engine->target = coord_range(); - auto pworld = Core::getInstance().getWorld(); auto key = stl_sprintf("siege-engine/target/%d", bld->id); - pworld->DeletePersistentData(pworld->GetPersistentData(key)); + World::DeletePersistentData(World::GetPersistentData(key)); } static bool setTargetArea(df::building_siegeenginest *bld, df::coord target_min, df::coord target_max) @@ -447,9 +445,8 @@ static bool setTargetArea(df::building_siegeenginest *bld, df::coord target_min, if (!enable_plugin()) return false; - auto pworld = Core::getInstance().getWorld(); auto key = stl_sprintf("siege-engine/target/%d", bld->id); - auto entry = pworld->GetPersistentData(key, NULL); + auto entry = World::GetPersistentData(key, NULL); if (!entry.isValid()) return false; @@ -491,9 +488,8 @@ static int setAmmoItem(lua_State *L) if (!is_valid_enum_item(item_type)) luaL_argerror(L, 2, "invalid item type"); - auto pworld = Core::getInstance().getWorld(); auto key = stl_sprintf("siege-engine/ammo/%d", engine->id); - auto entry = pworld->GetPersistentData(key, NULL); + auto entry = World::GetPersistentData(key, NULL); if (!entry.isValid()) return 0; @@ -523,9 +519,8 @@ static void forgetStockpileLink(EngineInfo *engine, int pile_id) { engine->stockpiles.erase(pile_id); - auto pworld = Core::getInstance().getWorld(); auto key = stl_sprintf("siege-engine/stockpiles/%d/%d", engine->id, pile_id); - pworld->DeletePersistentData(pworld->GetPersistentData(key)); + World::DeletePersistentData(World::GetPersistentData(key)); } static void update_stockpile_links(EngineInfo *engine) @@ -583,9 +578,8 @@ static bool addStockpileLink(df::building_siegeenginest *bld, df::building_stock if (!enable_plugin()) return false; - auto pworld = Core::getInstance().getWorld(); auto key = stl_sprintf("siege-engine/stockpiles/%d/%d", bld->id, pile->id); - auto entry = pworld->GetPersistentData(key, NULL); + auto entry = World::GetPersistentData(key, NULL); if (!entry.isValid()) return false; @@ -620,9 +614,8 @@ static df::workshop_profile *saveWorkshopProfile(df::building_siegeenginest *bld return NULL; // Save skill limits - auto pworld = Core::getInstance().getWorld(); auto key = stl_sprintf("siege-engine/profiles/%d", bld->id); - auto entry = pworld->GetPersistentData(key, NULL); + auto entry = World::GetPersistentData(key, NULL); if (!entry.isValid()) return NULL; @@ -637,18 +630,18 @@ static df::workshop_profile *saveWorkshopProfile(df::building_siegeenginest *bld auto &workers = engine->profile.permitted_workers; key = stl_sprintf("siege-engine/profile-workers/%d", bld->id); - pworld->GetPersistentData(&vec, key, true); + World::GetPersistentData(&vec, key, true); for (auto it = vec.begin(); it != vec.end(); ++it) { if (linear_index(workers, it->ival(1)) < 0) - pworld->DeletePersistentData(*it); + World::DeletePersistentData(*it); } for (size_t i = 0; i < workers.size(); i++) { key = stl_sprintf("siege-engine/profile-workers/%d/%d", bld->id, workers[i]); - entry = pworld->GetPersistentData(key, NULL); + entry = World::GetPersistentData(key, NULL); if (!entry.isValid()) continue; entry.ival(0) = engine->id; @@ -1802,8 +1795,7 @@ static bool enable_plugin() if (is_enabled) return true; - auto pworld = Core::getInstance().getWorld(); - auto entry = pworld->GetPersistentData("siege-engine/enabled", NULL); + auto entry = World::GetPersistentData("siege-engine/enabled", NULL); if (!entry.isValid()) return false; @@ -1828,8 +1820,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan case SC_MAP_LOADED: if (!gamemode || *gamemode == game_mode::DWARF) { - auto pworld = Core::getInstance().getWorld(); - bool enable = pworld->GetPersistentData("siege-engine/enabled").isValid(); + bool enable = World::GetPersistentData("siege-engine/enabled").isValid(); if (enable) { diff --git a/plugins/weather.cpp b/plugins/weather.cpp index 33fa45fd3..98476ef2c 100644 --- a/plugins/weather.cpp +++ b/plugins/weather.cpp @@ -84,7 +84,6 @@ command_result weather (color_ostream &con, vector & parameters) CoreSuspender suspend; - DFHack::World * w = Core::getInstance().getWorld(); if(!df::global::current_weather) { con << "Weather support seems broken :(" << std::endl; @@ -123,22 +122,22 @@ command_result weather (color_ostream &con, vector & parameters) if(rain) { con << "Here comes the rain." << std::endl; - w->SetCurrentWeather(weather_type::Rain); + World::SetCurrentWeather(weather_type::Rain); } if(snow) { con << "Snow everywhere!" << std::endl; - w->SetCurrentWeather(weather_type::Snow); + World::SetCurrentWeather(weather_type::Snow); } if(clear) { con << "Suddenly, sunny weather!" << std::endl; - w->SetCurrentWeather(weather_type::None); + World::SetCurrentWeather(weather_type::None); } if(val_override != -1) { con << "I have no damn idea what this is... " << val_override << std::endl; - w->SetCurrentWeather(val_override); + World::SetCurrentWeather(val_override); } // FIXME: weather lock needs map ID to work reliably... needs to be implemented. } diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 98258682e..2720baa83 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -438,9 +438,7 @@ static void start_protect(color_ostream &out) static void init_state(color_ostream &out) { - auto pworld = Core::getInstance().getWorld(); - - config = pworld->GetPersistentData("workflow/config"); + config = World::GetPersistentData("workflow/config"); if (config.isValid() && config.ival(0) == -1) config.ival(0) = 0; @@ -448,14 +446,14 @@ static void init_state(color_ostream &out) // Parse constraints std::vector items; - pworld->GetPersistentData(&items, "workflow/constraints"); + World::GetPersistentData(&items, "workflow/constraints"); for (int i = items.size()-1; i >= 0; i--) { if (get_constraint(out, items[i].val(), &items[i])) continue; out.printerr("Lost constraint %s\n", items[i].val().c_str()); - pworld->DeletePersistentData(items[i]); + World::DeletePersistentData(items[i]); } last_tick_frame_count = world->frame_counter; @@ -469,11 +467,9 @@ static void init_state(color_ostream &out) static void enable_plugin(color_ostream &out) { - auto pworld = Core::getInstance().getWorld(); - if (!config.isValid()) { - config = pworld->AddPersistentData("workflow/config"); + config = World::AddPersistentData("workflow/config"); config.ival(0) = 0; } @@ -729,7 +725,7 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str nct->config = *cfg; else { - nct->config = Core::getInstance().getWorld()->AddPersistentData("workflow/constraints"); + nct->config = World::AddPersistentData("workflow/constraints"); nct->init(str); } @@ -743,7 +739,7 @@ static void delete_constraint(ItemConstraint *cv) if (idx >= 0) vector_erase_at(constraints, idx); - Core::getInstance().getWorld()->DeletePersistentData(cv->config); + World::DeletePersistentData(cv->config); delete cv; } diff --git a/plugins/zone.cpp b/plugins/zone.cpp index 6c1af4768..542ce8a02 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -2769,10 +2769,7 @@ public: if(!rconfig.isValid()) { string keyname = "autobutcher/watchlist/" + getRaceName(raceId); - auto pworld = Core::getInstance().getWorld(); - rconfig = pworld->GetPersistentData(keyname); - if(!rconfig.isValid()) - rconfig = pworld->AddPersistentData(keyname); + rconfig = World::GetPersistentData(keyname, NULL); } if(rconfig.isValid()) { @@ -2795,7 +2792,7 @@ public: { if(!rconfig.isValid()) return; - Core::getInstance().getWorld()->DeletePersistentData(rconfig); + World::DeletePersistentData(rconfig); } void SortUnitsByAge() @@ -3405,13 +3402,11 @@ command_result autoButcher( color_ostream &out, bool verbose = false ) command_result start_autobutcher(color_ostream &out) { - auto pworld = Core::getInstance().getWorld(); - enable_autobutcher = true; if (!config_autobutcher.isValid()) { - config_autobutcher = pworld->AddPersistentData("autobutcher/config"); + config_autobutcher = World::AddPersistentData("autobutcher/config"); if (!config_autobutcher.isValid()) { @@ -3437,9 +3432,8 @@ command_result start_autobutcher(color_ostream &out) command_result init_autobutcher(color_ostream &out) { cleanup_autobutcher(out); - auto pworld = Core::getInstance().getWorld(); - config_autobutcher = pworld->GetPersistentData("autobutcher/config"); + config_autobutcher = World::GetPersistentData("autobutcher/config"); if(config_autobutcher.isValid()) { if (config_autobutcher.ival(0) == -1) @@ -3471,7 +3465,7 @@ command_result init_autobutcher(color_ostream &out) // read watchlist from save std::vector items; - pworld->GetPersistentData(&items, "autobutcher/watchlist/", true); + World::GetPersistentData(&items, "autobutcher/watchlist/", true); for (auto p = items.begin(); p != items.end(); p++) { string key = p->key(); @@ -3502,12 +3496,11 @@ command_result cleanup_autobutcher(color_ostream &out) command_result start_autonestbox(color_ostream &out) { - auto pworld = Core::getInstance().getWorld(); enable_autonestbox = true; if (!config_autobutcher.isValid()) { - config_autonestbox = pworld->AddPersistentData("autonestbox/config"); + config_autonestbox = World::AddPersistentData("autonestbox/config"); if (!config_autobutcher.isValid()) { @@ -3528,9 +3521,8 @@ command_result start_autonestbox(color_ostream &out) command_result init_autonestbox(color_ostream &out) { cleanup_autonestbox(out); - auto pworld = Core::getInstance().getWorld(); - config_autonestbox = pworld->GetPersistentData("autonestbox/config"); + config_autonestbox = World::GetPersistentData("autonestbox/config"); if(config_autonestbox.isValid()) { if (config_autonestbox.ival(0) == -1) From ab90e3eefe4966139d38f33bf629969c1cf8fa4e Mon Sep 17 00:00:00 2001 From: Quietust Date: Sun, 7 Oct 2012 17:35:41 -0500 Subject: [PATCH 070/114] Manipulator - take false identities into account --- plugins/manipulator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 633098a10..b3852437c 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -443,8 +443,8 @@ void viewscreen_unitlaborsst::refreshNames() UnitInfo *cur = units[i]; df::unit *unit = cur->unit; - cur->name = Translation::TranslateName(&unit->name, false); - cur->transname = Translation::TranslateName(&unit->name, true); + cur->name = Translation::TranslateName(Units::getVisibleName(unit), false); + cur->transname = Translation::TranslateName(Units::getVisibleName(unit), true); cur->profession = Units::getProfessionName(unit); } calcSize(); From 408f0cb06e682c437dbe82b64eb229d41b65fe44 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 8 Oct 2012 12:10:02 +0400 Subject: [PATCH 071/114] Add a small stand-alone utility for managing binary patches. --- Lua API.html | 6 + Lua API.rst | 8 + NEWS | 2 + Readme.html | 577 ++++++++++++++++++++++------------------- Readme.rst | 30 +++ library/CMakeLists.txt | 3 +- library/binpatch.cpp | 308 ++++++++++++++++++++++ 7 files changed, 660 insertions(+), 274 deletions(-) create mode 100644 library/binpatch.cpp diff --git a/Lua API.html b/Lua API.html index 047ef9786..06fa5418e 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1914,6 +1914,12 @@ utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id')

                  (For an explanation of new=true, see table assignment in the wrapper section)

                • +
                • utils.erase_sorted_key(vector,key,field,cmpfun)

                  +

                  Removes the item with the given key from the list. Returns: did_erase, vector[idx], idx.

                  +
                • +
                • utils.erase_sorted(vector,item,field,cmpfun)

                  +

                  Exactly like erase_sorted_key, but if field is specified, takes the key from item[field].

                  +
                • utils.prompt_yes_no(prompt, default)

                  Presents a yes/no prompt to the user. If default is not nil, allows just pressing Enter to submit the default choice. diff --git a/Lua API.rst b/Lua API.rst index bf7ee45a7..fbb4b7d82 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -1806,6 +1806,14 @@ utils (For an explanation of ``new=true``, see table assignment in the wrapper section) +* ``utils.erase_sorted_key(vector,key,field,cmpfun)`` + + Removes the item with the given key from the list. Returns: *did_erase, vector[idx], idx*. + +* ``utils.erase_sorted(vector,item,field,cmpfun)`` + + Exactly like ``erase_sorted_key``, but if field is specified, takes the key from ``item[field]``. + * ``utils.prompt_yes_no(prompt, default)`` Presents a yes/no prompt to the user. If ``default`` is not *nil*, diff --git a/NEWS b/NEWS index b36d2f121..5072cebc3 100644 --- a/NEWS +++ b/NEWS @@ -4,8 +4,10 @@ DFHack future - support for displaying active keybindings properly. Notable bugfixes: - autobutcher can be re-enabled again after being stopped. + - stopped Dwarf Manipulator from unmasking vampires. Misc improvements: - fastdwarf: new mode using debug flags, and some internal consistency fixes. + - added a small stand-alone utility for applying and removing binary patches. DFHack v0.34.11-r2 diff --git a/Readme.html b/Readme.html index caf1f6a0e..c1d2d0d3c 100644 --- a/Readme.html +++ b/Readme.html @@ -338,187 +338,190 @@ access DF memory and allow for easier development of new tools.

                • Getting DFHack
                • Compatibility
                • Installation/Removal
                • -
                • Using DFHack
                • -
                • Something doesn't work, help!
                • -
                • The init file
                • -

                  Something doesn't work, help!

                  +

                  Something doesn't work, help!

                  First, don't panic :) Second, dfhack keeps a few log files in DF's folder - stderr.log and stdout.log. You can look at those and possibly find out what's happening. @@ -596,13 +627,13 @@ the issues tracker on github, contact me ( -

                  The init file

                  +

                  The init file

                  If your DF folder contains a file named dfhack.init, its contents will be run every time you start DF. This allows setting up keybindings. An example file is provided as dfhack.init-example - you can tweak it and rename to dfhack.init if you want to use this functionality.

                  -

                  Setting keybindings

                  +

                  Setting keybindings

                  To set keybindings, use the built-in keybinding command. Like any other command it can be used at any time from the console, but it is also meaningful in the DFHack init file.

                  @@ -647,7 +678,7 @@ for context foo/bar/baz, possible matches are
                  -

                  Commands

                  +

                  Commands

                  DFHack command syntax consists of a command name, followed by arguments separated by whitespace. To include whitespace in an argument, quote it in double quotes. To include a double quote character, use \" inside double quotes.

                  @@ -669,13 +700,13 @@ The following two command lines are exactly equivalent:

                  to retrieve further help without having to look at this document. Alternatively, some accept a 'help'/'?' option on their command line.

                  -

                  Game progress

                  +

                  Game progress

                  -

                  die

                  +

                  die

                  Instantly kills DF without saving.

                  -

                  forcepause

                  +

                  forcepause

                  Forces DF to pause. This is useful when your FPS drops below 1 and you lose control of the game.

                  @@ -686,12 +717,12 @@ control of the game.

                  -

                  nopause

                  +

                  nopause

                  Disables pausing (both manual and automatic) with the exception of pause forced by 'reveal hell'. This is nice for digging under rivers.

                  -

                  fastdwarf

                  +

                  fastdwarf

                  Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly and perform tasks quickly. Teledwarf makes dwarves move instantaneously, but do jobs at the same speed.

                    @@ -706,29 +737,29 @@ by 'reveal hell'. This is nice for digging under rivers.

                  -

                  Game interface

                  +

                  Game interface

                  -

                  follow

                  +

                  follow

                  Makes the game view follow the currently highlighted unit after you exit from current menu/cursor mode. Handy for watching dwarves running around. Deactivated by moving the view manually.

                  -

                  tidlers

                  +

                  tidlers

                  Toggle between all possible positions where the idlers count can be placed.

                  -

                  twaterlvl

                  +

                  twaterlvl

                  Toggle between displaying/not displaying liquid depth as numbers.

                  -

                  copystock

                  +

                  copystock

                  Copies the parameters of the currently highlighted stockpile to the custom stockpile settings and switches to custom stockpile placement mode, effectively allowing you to copy/paste stockpiles easily.

                  -

                  rename

                  +

                  rename

                  Allows renaming various things.

                  Options:

                  @@ -762,9 +793,9 @@ siege engine or an activity zone.
                  -

                  Adventure mode

                  +

                  Adventure mode

                  -

                  adv-bodyswap

                  +

                  adv-bodyswap

                  This allows taking control over your followers and other creatures in adventure mode. For example, you can make them pick up new arms and armor and equip them properly.

                  @@ -777,7 +808,7 @@ properly.

                  -

                  advtools

                  +

                  advtools

                  A package of different adventure mode tools (currently just one)

                  Usage:

                  @@ -800,9 +831,9 @@ on item type and being in shop.
                  -

                  Map modification

                  +

                  Map modification

                  -

                  changelayer

                  +

                  changelayer

                  Changes material of the geology layer under cursor to the specified inorganic RAW material. Can have impact on all surrounding regions, not only your embark! By default changing stone to soil and vice versa is not allowed. By default @@ -877,7 +908,7 @@ You did save your game, right?

                  -

                  changevein

                  +

                  changevein

                  Changes material of the vein under cursor to the specified inorganic RAW material. Only affects tiles within the current 16x16 block - for veins and large clusters, you will need to use this command multiple times.

                  @@ -890,7 +921,7 @@ large clusters, you will need to use this command multiple times.

                  -

                  changeitem

                  +

                  changeitem

                  Allows changing item material and base quality. By default the item currently selected in the UI will be changed (you can select items in the 'k' list or inside containers/inventory). By default change is only allowed if materials @@ -930,7 +961,7 @@ crafters/haulers.

                  -

                  colonies

                  +

                  colonies

                  Allows listing all the vermin colonies on the map and optionally turning them into honey bee colonies.

                  Options:

                  @@ -945,12 +976,12 @@ crafters/haulers.

                  -

                  deramp (by zilpin)

                  +

                  deramp (by zilpin)

                  Removes all ramps designated for removal from the map. This is useful for replicating the old channel digging designation. It also removes any and all 'down ramps' that can remain after a cave-in (you don't have to designate anything for that to happen).

                  -

                  feature

                  +

                  feature

                  Enables management of map features.

                  • Discovering a magma feature (magma pool, volcano, magma sea, or curious @@ -975,7 +1006,7 @@ that cavern to grow within your fortress.
                  -

                  liquids

                  +

                  liquids

                  Allows adding magma, water and obsidian to the game. It replaces the normal dfhack command line and can't be used from a hotkey. Settings will be remembered as long as dfhack runs. Intended for use in combination with the command @@ -988,13 +1019,13 @@ temperatures (creating heat traps). You've been warned.

                  -

                  liquids-here

                  +

                  liquids-here

                  Run the liquid spawner with the current/last settings made in liquids (if no settings in liquids were made it paints a point of 7/7 magma by default).

                  Intended to be used as keybinding. Requires an active in-game cursor.

                  -

                  tiletypes

                  +

                  tiletypes

                  Can be used for painting map tiles and is an interactive command, much like liquids.

                  The tool works with two set of options and a brush. The brush determines which @@ -1055,27 +1086,27 @@ up.

                  For more details, see the 'help' command while using this.

                  -

                  tiletypes-commands

                  +

                  tiletypes-commands

                  Runs tiletypes commands, separated by ;. This makes it possible to change tiletypes modes from a hotkey.

                  -

                  tiletypes-here

                  +

                  tiletypes-here

                  Apply the current tiletypes options at the in-game cursor position, including the brush. Can be used from a hotkey.

                  -

                  tiletypes-here-point

                  +

                  tiletypes-here-point

                  Apply the current tiletypes options at the in-game cursor position to a single tile. Can be used from a hotkey.

                  -

                  tubefill

                  +

                  tubefill

                  Fills all the adamantine veins again. Veins that were empty will be filled in too, but might still trigger a demon invasion (this is a known bug).

                  -

                  extirpate

                  +

                  extirpate

                  A tool for getting rid of trees and shrubs. By default, it only kills a tree/shrub under the cursor. The plants are turned into ashes instantly.

                  Options:

                  @@ -1095,20 +1126,20 @@ a tree/shrub under the cursor. The plants are turned into ashes instantly.

                  -

                  grow

                  +

                  grow

                  Makes all saplings present on the map grow into trees (almost) instantly.

                  -

                  immolate

                  +

                  immolate

                  Very similar to extirpate, but additionally sets the plants on fire. The fires can and will spread ;)

                  -

                  regrass

                  +

                  regrass

                  Regrows grass. Not much to it ;)

                  -

                  weather

                  +

                  weather

                  Prints the current weather map by default.

                  Also lets you change the current weather to 'clear sky', 'rainy' or 'snowing'.

                  Options:

                  @@ -1129,9 +1160,9 @@ can and will spread ;)

                  -

                  Map inspection

                  +

                  Map inspection

                  -

                  cursecheck

                  +

                  cursecheck

                  Checks a single map tile or the whole map/world for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies).

                  With an active in-game cursor only the selected tile will be observed. @@ -1186,17 +1217,17 @@ of curses, for example.

                  -

                  flows

                  +

                  flows

                  A tool for checking how many tiles contain flowing liquids. If you suspect that your magma sea leaks into HFS, you can use this tool to be sure without revealing the map.

                  -

                  probe

                  +

                  probe

                  Can be used to determine tile properties like temperature.

                  -

                  prospect

                  +

                  prospect

                  Prints a big list of all the present minerals and plants. By default, only the visible part of the map is scanned.

                  Options:

                  @@ -1215,7 +1246,7 @@ the visible part of the map is scanned.

                  -

                  Pre-embark estimate

                  +

                  Pre-embark estimate

                  If prospect is called during the embark selection screen, it displays an estimate of layer stone availability.

                  @@ -1240,7 +1271,7 @@ that is actually present.

                  -

                  reveal

                  +

                  reveal

                  This reveals the map. By default, HFS will remain hidden so that the demons don't spawn. You can use 'reveal hell' to reveal everything. With hell revealed, you won't be able to unpause until you hide the map again. If you really want @@ -1249,34 +1280,34 @@ to unpause with hell revealed, use 'reveal demons'.

                  you move. When you use it this way, you don't need to run 'unreveal'.

                  -

                  unreveal

                  +

                  unreveal

                  Reverts the effects of 'reveal'.

                  -

                  revtoggle

                  +

                  revtoggle

                  Switches between 'reveal' and 'unreveal'.

                  -

                  revflood

                  +

                  revflood

                  This command will hide the whole map and then reveal all the tiles that have a path to the in-game cursor.

                  -

                  revforget

                  +

                  revforget

                  When you use reveal, it saves information about what was/wasn't visible before revealing everything. Unreveal uses this information to hide things again. This command throws away the information. For example, use in cases where you abandoned with the fort revealed and no longer want the data.

                  -

                  showmood

                  +

                  showmood

                  Shows all items needed for the currently active strange mood.

                  -

                  Designations

                  +

                  Designations

                  -

                  burrow

                  +

                  burrow

                  Miscellaneous burrow control. Allows manipulating burrows and automated burrow expansion while digging.

                  Options:

                  @@ -1324,17 +1355,17 @@ Digging 1-wide corridors with the miner inside the burrow is SLOW.
                  -

                  digv

                  +

                  digv

                  Designates a whole vein for digging. Requires an active in-game cursor placed over a vein tile. With the 'x' option, it will traverse z-levels (putting stairs between the same-material tiles).

                  -

                  digvx

                  +

                  digvx

                  A permanent alias for 'digv x'.

                  -

                  digl

                  +

                  digl

                  Designates layer stone for digging. Requires an active in-game cursor placed over a layer stone tile. With the 'x' option, it will traverse z-levels (putting stairs between the same-material tiles). With the 'undo' option it @@ -1342,11 +1373,11 @@ will remove the dig designation instead (if you realize that digging out a 50 z-level deep layer was not such a good idea after all).

                  -

                  diglx

                  +

                  diglx

                  A permanent alias for 'digl x'.

                  -

                  digexp

                  +

                  digexp

                  This command can be used for exploratory mining.

                  See: http://df.magmawiki.com/index.php/DF2010:Exploratory_mining

                  There are two variables that can be set: pattern and filter.

                  @@ -1409,7 +1440,7 @@ z-level deep layer was not such a good idea after all).

                  -

                  digcircle

                  +

                  digcircle

                  A command for easy designation of filled and hollow circles. It has several types of options.

                  Shape:

                  @@ -1472,7 +1503,7 @@ repeats with the last selected parameters.

                -

                digtype

                +

                digtype

                For every tile on the map of the same vein type as the selected tile, this command designates it to have the same designation as the selected tile. If the selected tile has no designation, they will be dig designated. If an argument is given, the designation of the selected tile is ignored, and all appropriate tiles are set to the specified designation.

                Options:

                @@ -1500,7 +1531,7 @@ If an argument is given, the designation of the selected tile is ignored, and al
                -

                filltraffic

                +

                filltraffic

                Set traffic designations using flood-fill starting at the cursor.

                Traffic Type Codes:

                @@ -1539,7 +1570,7 @@ If an argument is given, the designation of the selected tile is ignored, and al 'filltraffic H' - When used in a room with doors, it will set traffic to HIGH in just that room.
                -

                alltraffic

                +

                alltraffic

                Set traffic designations for every single tile of the map (useful for resetting traffic designations).

                Traffic Type Codes:

                @@ -1563,7 +1594,7 @@ If an argument is given, the designation of the selected tile is ignored, and al 'alltraffic N' - Set traffic to 'normal' for all tiles.
                -

                getplants

                +

                getplants

                This tool allows plant gathering and tree cutting by RAW ID. Specify the types of trees to cut down and/or shrubs to gather by their plant names, separated by spaces.

                @@ -1590,9 +1621,9 @@ all valid plant IDs will be listed.

                -

                Cleanup and garbage disposal

                +

                Cleanup and garbage disposal

                -

                clean

                +

                clean

                Cleans all the splatter that get scattered all over the map, items and creatures. In an old fortress, this can significantly reduce FPS lag. It can also spoil your !!FUN!!, so think before you use it.

                @@ -1626,12 +1657,12 @@ also spoil your !!FUN!!, so think before you use it.

                -

                spotclean

                +

                spotclean

                Works like 'clean map snow mud', but only for the tile under the cursor. Ideal if you want to keep that bloody entrance 'clean map' would clean up.

                -

                autodump

                +

                autodump

                This utility lets you quickly move all items designated to be dumped. Items are instantly moved to the cursor position, the dump flag is unset, and the forbid flag is set, as if it had been dumped normally. @@ -1658,17 +1689,17 @@ Be aware that any active dump item tasks still point at the item.

                -

                autodump-destroy-here

                +

                autodump-destroy-here

                Destroy items marked for dumping under cursor. Identical to autodump destroy-here, but intended for use as keybinding.

                -

                autodump-destroy-item

                +

                autodump-destroy-item

                Destroy the selected item. The item may be selected in the 'k' list, or inside a container. If called again before the game is resumed, cancels destroy.

                -

                cleanowned

                +

                cleanowned

                Confiscates items owned by dwarfs. By default, owned food on the floor and rotten items are confistacted and dumped.

                Options:

                @@ -1702,13 +1733,13 @@ worn items with 'X' damage and above.
                -

                Bugfixes

                +

                Bugfixes

                -

                drybuckets

                +

                drybuckets

                This utility removes water from all buckets in your fortress, allowing them to be safely used for making lye.

                -

                fixdiplomats

                +

                fixdiplomats

                Up to version 0.31.12, Elves only sent Diplomats to your fortress to propose tree cutting quotas due to a bug; once that bug was fixed, Elves stopped caring about excess tree cutting. This command adds a Diplomat position to all Elven @@ -1717,19 +1748,19 @@ to violate them and potentially start wars) in case you haven't already modified your raws accordingly.

                -

                fixmerchants

                +

                fixmerchants

                This command adds the Guild Representative position to all Human civilizations, allowing them to make trade agreements (just as they did back in 0.28.181.40d and earlier) in case you haven't already modified your raws accordingly.

                -

                fixveins

                +

                fixveins

                Removes invalid references to mineral inclusions and restores missing ones. Use this if you broke your embark with tools like tiletypes, or if you accidentally placed a construction on top of a valuable mineral floor.

                -

                tweak

                +

                tweak

                Contains various tweaks for minor bugs.

                One-shot subcommands:

                @@ -1817,9 +1848,9 @@ to make them stand out more in the list.
                -

                Mode switch and reclaim

                +

                Mode switch and reclaim

                -

                lair

                +

                lair

                This command allows you to mark the map as 'monster lair', preventing item scatter on abandon. When invoked as 'lair reset', it does the opposite.

                Unlike reveal, this command doesn't save the information about tiles - you @@ -1839,7 +1870,7 @@ won't be able to restore state of real monster lairs using 'lair reset'.

                -

                mode

                +

                mode

                This command lets you see and change the game mode directly. Not all combinations are good for every situation and most of them will produce undesirable results. There are a few good ones though.

                @@ -1859,9 +1890,9 @@ You just created a returnable mountain home and gained an adventurer.

                -

                Visualizer and data export

                +

                Visualizer and data export

                -

                ssense / stonesense

                +

                ssense / stonesense

                An isometric visualizer that runs in a second window. This requires working graphics acceleration and at least a dual core CPU (otherwise it will slow down DF).

                @@ -1874,19 +1905,19 @@ thread: http://df.magmawiki.com/index.php/Utility:Stonesense/Content_repository

                -

                mapexport

                +

                mapexport

                Export the current loaded map as a file. This will be eventually usable with visualizers.

                -

                dwarfexport

                +

                dwarfexport

                Export dwarves to RuneSmith-compatible XML.

                -

                Job management

                +

                Job management

                -

                job

                +

                job

                Command for general job query and manipulation.

                Options:
                @@ -1905,7 +1936,7 @@ in a workshop, or the unit/jobs screen.
                -

                job-material

                +

                job-material

                Alter the material of the selected job.

                Invoked as:

                @@ -1923,7 +1954,7 @@ over the first available choice with the matching material.
                 
                 
                -

                job-duplicate

                +

                job-duplicate

                Duplicate the selected job in a workshop:
                  @@ -1934,7 +1965,7 @@ instantly duplicates the job.
                -

                workflow

                +

                workflow

                Manage control of repeat jobs.

                Usage:

                @@ -1958,7 +1989,7 @@ Otherwise, enables or disables any of the following options:

                -

                Function

                +

                Function

                When the plugin is enabled, it protects all repeat jobs from removal. If they do disappear due to any cause, they are immediately re-added to their workshop and suspended.

                @@ -1969,7 +2000,7 @@ the amount has to drop before jobs are resumed; this is intended to reduce the frequency of jobs being toggled.

                -

                Constraint examples

                +

                Constraint examples

                Keep metal bolts within 900-1000, and wood/bone within 150-200.

                 workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100
                @@ -2008,15 +2039,15 @@ command.
                 
                -

                Fortress activity management

                +

                Fortress activity management

                -

                seedwatch

                +

                seedwatch

                Tool for turning cooking of seeds and plants on/off depending on how much you have of them.

                See 'seedwatch help' for detailed description.

                -

                zone

                +

                zone

                Helps a bit with managing activity zones (pens, pastures and pits) and cages.

                Options:

                @@ -2115,7 +2146,7 @@ for war/hunt). Negatable.
                -

                Usage with single units

                +

                Usage with single units

                One convenient way to use the zone tool is to bind the command 'zone assign' to a hotkey, maybe also the command 'zone set'. Place the in-game cursor over a pen/pasture or pit, use 'zone set' to mark it. Then you can select units @@ -2124,7 +2155,7 @@ and use 'zone assign' to assign them to their new home. Allows pitting your own dwarves, by the way.

                -

                Usage with filters

                +

                Usage with filters

                All filters can be used together with the 'assign' command.

                Restrictions: It's not possible to assign units who are inside built cages or chained because in most cases that won't be desirable anyways. @@ -2142,14 +2173,14 @@ are not properly added to your own stocks; slaughtering them should work).

                Most filters can be negated (e.g. 'not grazer' -> race is not a grazer).

                -

                Mass-renaming

                +

                Mass-renaming

                Using the 'nick' command you can set the same nickname for multiple units. If used without 'assign', 'all' or 'count' it will rename all units in the current default target zone. Combined with 'assign', 'all' or 'count' (and further optional filters) it will rename units matching the filter conditions.

                -

                Cage zones

                +

                Cage zones

                Using the 'tocages' command you can assign units to a set of cages, for example a room next to your butcher shop(s). They will be spread evenly among available cages to optimize hauling to and butchering from them. For this to work you need @@ -2160,7 +2191,7 @@ would make no sense, but can be used together with 'nick' or 'remnick' and all the usual filters.

                -

                Examples

                +

                Examples

                zone assign all own ALPACA minage 3 maxage 10
                Assign all own alpacas who are between 3 and 10 years old to the selected @@ -2186,7 +2217,7 @@ on the current default zone.
                -

                autonestbox

                +

                autonestbox

                Assigns unpastured female egg-layers to nestbox zones. Requires that you create pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox must be in the top left corner. Only 1 unit will be assigned per pen, regardless @@ -2215,7 +2246,7 @@ frames between runs.

                -

                autobutcher

                +

                autobutcher

                Assigns lifestock for slaughter once it reaches a specific count. Requires that you add the target race(s) to a watch list. Only tame units will be processed.

                Named units will be completely ignored (to protect specific animals from @@ -2323,7 +2354,7 @@ autobutcher.bat

                -

                autolabor

                +

                autolabor

                Automatically manage dwarf labors.

                When enabled, autolabor periodically checks your dwarves and enables or disables labors. It tries to keep as many dwarves as possible busy but @@ -2337,14 +2368,14 @@ while it is enabled.

                -

                Other

                +

                Other

                -

                catsplosion

                +

                catsplosion

                Makes cats just multiply. It is not a good idea to run this more than once or twice.

                -

                dfusion

                +

                dfusion

                This is the DFusion lua plugin system by warmist/darius, running as a DFHack plugin.

                See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=69682.15

                Confirmed working DFusion plugins:

                @@ -2366,7 +2397,7 @@ twice.

                -

                misery

                +

                misery

                When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).

                Usage:

                @@ -2390,7 +2421,7 @@ twice.

                -

                Scripts

                +

                Scripts

                Lua or ruby scripts placed in the hack/scripts/ directory are considered for execution as if they were native DFHack commands. They are listed at the end of the 'ls' command output.

                @@ -2399,7 +2430,7 @@ only be listed by ls if called as 'ls -a'. This is intended as a way to hide scripts that are obscure, developer-oriented, or should be used as keybindings.

                Some notable scripts:

                -

                fix/*

                +

                fix/*

                Scripts in this subdirectory fix various bugs and issues, some of them obscure.

                • fix/dead-units

                  @@ -2425,22 +2456,22 @@ caused by autodump bugs or other hacking mishaps.

                -

                gui/*

                +

                gui/*

                Scripts that implement dialogs inserted into the main game window are put in this directory.

                -

                quicksave

                +

                quicksave

                If called in dwarf mode, makes DF immediately auto-save the game by setting a flag normally used in seasonal auto-save.

                -

                setfps

                +

                setfps

                Run setfps <number> to set the FPS cap at runtime, in case you want to watch combat in slow motion or something :)

                -

                siren

                +

                siren

                Wakes up sleeping units, cancels breaks and stops parties either everywhere, or in the burrows given as arguments. In return, adds bad thoughts about noise, tiredness and lack of protection. Also, the units with interrupted @@ -2448,7 +2479,7 @@ breaks will go on break again a lot sooner. The script is intended for emergencies, e.g. when a siege appears, and all your military is partying.

                -

                growcrops

                +

                growcrops

                Instantly grow seeds inside farming plots.

                With no argument, this command list the various seed types currently in use in your farming plots. @@ -2460,7 +2491,7 @@ growcrops plump 40

                -

                removebadthoughts

                +

                removebadthoughts

                This script remove negative thoughts from your dwarves. Very useful against tantrum spirals.

                With a selected unit in 'v' mode, will clear this unit's mind, otherwise @@ -2473,7 +2504,7 @@ you unpause.

                it removed.

                -

                slayrace

                +

                slayrace

                Kills any unit of a given race.

                With no argument, lists the available races.

                With the special argument 'him', targets only the selected creature.

                @@ -2499,7 +2530,7 @@ slayrace elve magma
                -

                magmasource

                +

                magmasource

                Create an infinite magma source on a tile.

                This script registers a map tile as a magma source, and every 12 game ticks that tile receives 1 new unit of flowing magma.

                @@ -2514,7 +2545,7 @@ To remove all placed sources, call magmasource stop

                With no argument, this command shows an help message and list existing sources.

                -

                digfort

                +

                digfort

                A script to designate an area for digging according to a plan in csv format.

                This script, inspired from quickfort, can designate an area for digging. Your plan should be stored in a .csv file like this:

                @@ -2532,7 +2563,7 @@ To skip a row in your design, use a single ;.<

                The script takes the plan filename, starting from the root df folder.

                -

                superdwarf

                +

                superdwarf

                Similar to fastdwarf, per-creature.

                To make any creature superfast, target it ingame using 'v' and:

                @@ -2542,17 +2573,17 @@ superdwarf add
                 

                This plugin also shortens the 'sleeping' and 'on break' periods of targets.

                -

                drainaquifer

                +

                drainaquifer

                Remove all 'aquifer' tag from the map blocks. Irreversible.

                -

                deathcause

                +

                deathcause

                Focus a body part ingame, and this script will display the cause of death of the creature.

                -

                In-game interface tools

                +

                In-game interface tools

                These tools work by displaying dialogs or overlays in the game window, and are mostly implemented by lua scripts.

                @@ -2563,7 +2594,7 @@ display the word "DFHack" on the screen somewhere while active.

                guideline because it arguably just fixes small usability bugs in the game UI.

                -

                Dwarf Manipulator

                +

                Dwarf Manipulator

                Implemented by the manipulator plugin. To activate, open the unit screen and press 'l'.

                This tool implements a Dwarf Therapist-like interface within the game UI. The @@ -2599,13 +2630,13 @@ cursor onto that cell instead of toggling it. directly to the main dwarf mode screen.

                -

                gui/liquids

                +

                gui/liquids

                To use, bind to a key and activate in the 'k' mode.

                While active, use the suggested keys to switch the usual liquids parameters, and Enter to select the target area and apply changes.

                -

                gui/mechanisms

                +

                gui/mechanisms

                To use, bind to a key and activate in the 'q' mode.

                Lists mechanisms connected to the building, and their links. Navigating the list centers the view on the relevant linked buildings.

                @@ -2614,7 +2645,7 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter re-entering the mechanisms ui.

                -

                gui/rename

                +

                gui/rename

                Backed by the rename plugin, this script allows entering the desired name via a simple dialog in the game ui.

                  @@ -2630,14 +2661,14 @@ It is also possible to rename zones from the 'i' menu.

                  The building or unit options are automatically assumed when in relevant ui state.

                -

                gui/room-list

                +

                gui/room-list

                To use, bind to a key and activate in the 'q' mode, either immediately or after opening the assign owner page.

                The script lists other rooms owned by the same owner, or by the unit selected in the assign list, and allows unassigning them.

                -

                gui/choose-weapons

                +

                gui/choose-weapons

                Bind to a key, and activate in the Equip->View/Customize page of the military screen.

                Depending on the cursor location, it rewrites all 'individual choice weapon' entries in the selected squad or position to use a specific weapon type matching the assigned @@ -2648,7 +2679,7 @@ and may lead to inappropriate weapons being selected.

                -

                Behavior Mods

                +

                Behavior Mods

                These plugins, when activated via configuration UI or by detecting certain structures in RAWs, modify the game engine behavior concerning the target objects to add features not otherwise present.

                @@ -2659,20 +2690,20 @@ technical challenge, and do not represent any long-term plans to produce more similar modifications of the game.

                -

                Siege Engine

                +

                Siege Engine

                The siege-engine plugin enables siege engines to be linked to stockpiles, and aimed at an arbitrary rectangular area across Z levels, instead of the original four directions. Also, catapults can be ordered to load arbitrary objects, not just stones.

                -

                Rationale

                +

                Rationale

                Siege engines are a very interesting feature, but sadly almost useless in the current state because they haven't been updated since 2D and can only aim in four directions. This is an attempt to bring them more up to date until Toady has time to work on it. Actual improvements, e.g. like making siegers bring their own, are something only Toady can do.

                -

                Configuration UI

                +

                Configuration UI

                The configuration front-end to the plugin is implemented by the gui/siege-engine script. Bind it to a key and activate after selecting a siege engine in 'q' mode.

                The main mode displays the current target, selected ammo item type, linked stockpiles and @@ -2693,7 +2724,7 @@ menu.

                -

                Power Meter

                +

                Power Meter

                The power-meter plugin implements a modified pressure plate that detects power being supplied to gear boxes built in the four adjacent N/S/W/E tiles.

                The configuration front-end is implemented by the gui/power-meter script. Bind it to a @@ -2702,11 +2733,11 @@ key and activate after selecting Pressure Plate in the build menu.

                configuration page, but configures parameters relevant to the modded power meter building.

                -

                Steam Engine

                +

                Steam Engine

                The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real steam engines.

                -

                Rationale

                +

                Rationale

                The vanilla game contains only water wheels and windmills as sources of power, but windmills give relatively little power, and water wheels require flowing water, which must either be a real river and thus immovable and @@ -2717,7 +2748,7 @@ it can be done just by combining existing features of the game engine in a new way with some glue code and a bit of custom logic.

                -

                Construction

                +

                Construction

                The workshop needs water as its input, which it takes via a passable floor tile below it, like usual magma workshops do. The magma version also needs magma.

                @@ -2741,7 +2772,7 @@ short axles that can be built later than both of the engines.

                -

                Operation

                +

                Operation

                In order to operate the engine, queue the Stoke Boiler job (optionally on repeat). A furnace operator will come, possibly bringing a bar of fuel, and perform it. As a result, a "boiling water" item will appear @@ -2772,7 +2803,7 @@ decrease it by further 4%, and also decrease the whole steam use rate by 10%.

                -

                Explosions

                +

                Explosions

                The engine must be constructed using barrel, pipe and piston from fire-safe, or in the magma version magma-safe metals.

                During operation weak parts get gradually worn out, and @@ -2781,7 +2812,7 @@ toppled during operation by a building destroyer, or a tantruming dwarf.

                -

                Save files

                +

                Save files

                It should be safe to load and view engine-using fortresses from a DF version without DFHack installed, except that in such case the engines won't work. However actually making modifications @@ -2792,7 +2823,7 @@ being generated.

                -

                Add Spatter

                +

                Add Spatter

                This plugin makes reactions with names starting with SPATTER_ADD_ produce contaminants on the items instead of improvements. The produced contaminants are immune to being washed away by water or destroyed by diff --git a/Readme.rst b/Readme.rst index b5c0f335d..bed837f0b 100644 --- a/Readme.rst +++ b/Readme.rst @@ -88,6 +88,36 @@ Interactive commands like 'liquids' cannot be used as hotkeys. Most of the commands come from plugins. Those reside in 'hack/plugins/'. +Patched binaries +================ + +On linux and OSX, users of patched binaries may have to find the relevant +section in symbols.xml, and add a new line with the checksum of their +executable:: + + + +In order to find the correct value of the hash, look into stderr.log; +DFHack prints an error there if it does not recognize the hash. + +DFHack includes a small stand-alone utility for applying and removing +binary patches from the game executable. Use it from the regular operating +system console: + + * ``binpatch check "Dwarf Fortress.exe" patch.dif`` + + Checks and prints if the patch is currently applied. + + * ``binpatch apply "Dwarf Fortress.exe" patch.dif`` + + Applies the patch, unless it is already applied or in conflict. + + * ``binpatch remove "Dwarf Fortress.exe" patch.dif`` + + Removes the patch, unless it is already removed. + +The patches are expected to be encoded in text format used by IDA. + ============================= Something doesn't work, help! ============================= diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 536f4d34d..a6ce58877 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -249,6 +249,7 @@ ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp ${ ADD_DEPENDENCIES(dfhack-client dfhack) ADD_EXECUTABLE(dfhack-run dfhack-run.cpp) +ADD_EXECUTABLE(binpatch binpatch.cpp) IF(BUILD_EGGY) SET_TARGET_PROPERTIES(dfhack PROPERTIES OUTPUT_NAME "egg" ) @@ -329,7 +330,7 @@ install(FILES xml/symbols.xml install(FILES ../dfhack.init-example DESTINATION ${DFHACK_BINARY_DESTINATION}) -install(TARGETS dfhack-run dfhack-client +install(TARGETS dfhack-run dfhack-client binpatch LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION} RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION}) diff --git a/library/binpatch.cpp b/library/binpatch.cpp new file mode 100644 index 000000000..10188db8c --- /dev/null +++ b/library/binpatch.cpp @@ -0,0 +1,308 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2011 Petr Mrázek + +A thread-safe logging console with a line editor for windows. + +Based on linenoise win32 port, +copyright 2010, Jon Griffiths . +All rights reserved. +Based on linenoise, copyright 2010, Salvatore Sanfilippo . +The original linenoise can be found at: http://github.com/antirez/linenoise + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Redis nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +using std::cout; +using std::cerr; +using std::endl; + +typedef unsigned char patch_byte; + +struct BinaryPatch { + struct Byte { + unsigned offset; + patch_byte old_val, new_val; + }; + enum State { + Conflict = 0, + Unapplied = 1, + Applied = 2, + Partial = 3 + }; + + std::vector entries; + + bool loadDIF(std::string name); + State checkState(const patch_byte *ptr, size_t len); + + void apply(patch_byte *ptr, size_t len, bool newv); +}; + +inline bool is_hex(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +bool BinaryPatch::loadDIF(std::string name) +{ + entries.clear(); + + std::ifstream infile(name); + if(infile.bad()) + { + cerr << "Cannot open file: " << name << endl; + return false; + } + + std::string s; + while(std::getline(infile, s)) + { + // Parse lines that begin with "[0-9a-f]+:" + size_t idx = s.find(':'); + if (idx == std::string::npos || idx == 0 || idx > 8) + continue; + + bool ok = true; + for (size_t i = 0; i < idx; i++) + if (!is_hex(s[i])) + ok = false; + if (!ok) + continue; + + unsigned off, oval, nval; + int nchar = 0; + int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar); + + if (cnt < 3) + { + cerr << "Could not parse: " << s << endl; + return false; + } + + for (size_t i = nchar; i < s.size(); i++) + { + if (!isspace(s[i])) + { + cerr << "Garbage at end of line: " << s << endl; + return false; + } + } + + if (oval >= 256 || nval >= 256) + { + cerr << "Invalid byte values: " << s << endl; + return false; + } + + Byte bv = { off, patch_byte(oval), patch_byte(nval) }; + entries.push_back(bv); + } + + if (entries.empty()) + { + cerr << "No lines recognized." << endl; + return false; + } + + return true; +} + +BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len) +{ + int state = 0; + + for (size_t i = 0; i < entries.size(); i++) + { + Byte &bv = entries[i]; + + if (bv.offset >= len) + { + cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl; + return Conflict; + } + + patch_byte cv = ptr[bv.offset]; + if (bv.old_val == cv) + state |= Unapplied; + else if (bv.new_val == cv) + state |= Applied; + else + { + cerr << std::hex << bv.offset << ": " << bv.old_val << " " << bv.new_val + << ", but currently " << cv << std::dec << endl; + return Conflict; + } + } + + return State(state); +} + +void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv) +{ + for (size_t i = 0; i < entries.size(); i++) + { + Byte &bv = entries[i]; + assert (bv.offset < len); + + ptr[bv.offset] = (newv ? bv.new_val : bv.old_val); + } +} + +bool load_file(std::vector *pvec, std::string fname) +{ + FILE *f = fopen(fname.c_str(), "rb"); + if (!f) + { + cerr << "Cannot open file: " << fname << endl; + return false; + } + + fseek(f, 0, SEEK_END); + pvec->resize(ftell(f)); + fseek(f, 0, SEEK_SET); + size_t cnt = fread(pvec->data(), 1, pvec->size(), f); + fclose(f); + + return cnt == pvec->size(); +} + +bool save_file(const std::vector &pvec, std::string fname) +{ + FILE *f = fopen(fname.c_str(), "wb"); + if (!f) + { + cerr << "Cannot open file: " << fname << endl; + return false; + } + + size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f); + fclose(f); + + return cnt == pvec.size(); +} + +int main (int argc, char *argv[]) +{ + if (argc <= 3) + { + cerr << "Usage: binpatch check|apply|remove " << endl; + return 2; + } + + std::string cmd = argv[1]; + + if (cmd != "check" && cmd != "apply" && cmd != "remove") + { + cerr << "Invalid command: " << cmd << endl; + return 2; + } + + std::string exe_file = argv[2]; + std::vector bindata; + if (!load_file(&bindata, exe_file)) + return 2; + + BinaryPatch patch; + if (!patch.loadDIF(argv[3])) + return 2; + + BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size()); + if (state == BinaryPatch::Conflict) + return 1; + + if (cmd == "check") + { + switch (state) + { + case BinaryPatch::Unapplied: + cout << "Currently not applied." << endl; + break; + case BinaryPatch::Applied: + cout << "Currently applied." << endl; + break; + case BinaryPatch::Partial: + cout << "Currently partially applied." << endl; + break; + default: + break; + } + + return 0; + } + else if (cmd == "apply") + { + if (state == BinaryPatch::Applied) + { + cout << "Already applied." << endl; + return 0; + } + + patch.apply(bindata.data(), bindata.size(), true); + } + else if (cmd == "remove") + { + if (state == BinaryPatch::Unapplied) + { + cout << "Already removed." << endl; + return 0; + } + + patch.apply(bindata.data(), bindata.size(), false); + } + + std::string bak_file = exe_file + ".bak"; + remove(bak_file.c_str()); + + if (rename(exe_file.c_str(), bak_file.c_str()) != 0) + { + cerr << "Could not create backup." << endl; + return 1; + } + + if (!save_file(bindata, exe_file)) + return 1; + + cout << "Patched " << patch.entries.size() << " bytes." << endl; + return 0; +} From 28f0fed0aa1d339d60228969b0e6b174da27e42c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 8 Oct 2012 16:22:35 +0400 Subject: [PATCH 072/114] Redo the way binpatch backs up, so as not to lose the executable perms. --- library/binpatch.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/library/binpatch.cpp b/library/binpatch.cpp index 10188db8c..88ef2f004 100644 --- a/library/binpatch.cpp +++ b/library/binpatch.cpp @@ -291,10 +291,7 @@ int main (int argc, char *argv[]) patch.apply(bindata.data(), bindata.size(), false); } - std::string bak_file = exe_file + ".bak"; - remove(bak_file.c_str()); - - if (rename(exe_file.c_str(), bak_file.c_str()) != 0) + if (!save_file(bindata, exe_file + ".bak")) { cerr << "Could not create backup." << endl; return 1; From 7224c8746aa44249d1b1c12088e6d4aeefa4ac22 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 8 Oct 2012 16:47:52 +0400 Subject: [PATCH 073/114] Print the new md5 hash after modification in binpatch. --- depends/md5/md5wrapper.cpp | 9 +++------ depends/md5/md5wrapper.h | 6 +++++- library/CMakeLists.txt | 2 ++ library/binpatch.cpp | 11 ++++++++++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/depends/md5/md5wrapper.cpp b/depends/md5/md5wrapper.cpp index e12b65780..d9f857c5d 100644 --- a/depends/md5/md5wrapper.cpp +++ b/depends/md5/md5wrapper.cpp @@ -36,16 +36,14 @@ * internal hash function, calling * the basic methods from md5.h */ -std::string md5wrapper::hashit(std::string text) +std::string md5wrapper::hashit(unsigned char *data, size_t length) { MD5Context ctx; //init md5 MD5Init(&ctx); //update with our string - MD5Update(&ctx, - (unsigned char*)text.c_str(), - text.length()); + MD5Update(&ctx, data, length); //create the hash unsigned char buff[16] = ""; @@ -95,10 +93,9 @@ md5wrapper::~md5wrapper() */ std::string md5wrapper::getHashFromString(std::string text) { - return this->hashit(text); + return this->hashit((unsigned char*)text.data(), text.length()); } - /* * creates a MD5 hash from * a file specified in "filename" and diff --git a/depends/md5/md5wrapper.h b/depends/md5/md5wrapper.h index 1a41192a1..0b534b61d 100644 --- a/depends/md5/md5wrapper.h +++ b/depends/md5/md5wrapper.h @@ -31,7 +31,7 @@ class md5wrapper * internal hash function, calling * the basic methods from md5.h */ - std::string hashit(std::string text); + std::string hashit(unsigned char *data, size_t length); /* * converts the numeric giets to @@ -52,6 +52,10 @@ class md5wrapper */ std::string getHashFromString(std::string text); + std::string getHashFromBytes(const unsigned char *data, size_t size) { + return hashit(const_cast(data),size); + } + /* * creates a MD5 hash from * a file specified in "filename" and diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index a6ce58877..6f33d5c8a 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -249,7 +249,9 @@ ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp ${ ADD_DEPENDENCIES(dfhack-client dfhack) ADD_EXECUTABLE(dfhack-run dfhack-run.cpp) + ADD_EXECUTABLE(binpatch binpatch.cpp) +TARGET_LINK_LIBRARIES(binpatch dfhack-md5) IF(BUILD_EGGY) SET_TARGET_PROPERTIES(dfhack PROPERTIES OUTPUT_NAME "egg" ) diff --git a/library/binpatch.cpp b/library/binpatch.cpp index 88ef2f004..815ac5b92 100644 --- a/library/binpatch.cpp +++ b/library/binpatch.cpp @@ -53,6 +53,8 @@ POSSIBILITY OF SUCH DAMAGE. #include +#include + using std::cout; using std::cerr; using std::endl; @@ -222,6 +224,12 @@ bool save_file(const std::vector &pvec, std::string fname) return cnt == pvec.size(); } +std::string compute_hash(const std::vector &pvec) +{ + md5wrapper md5; + return md5.getHashFromBytes(pvec.data(), pvec.size()); +} + int main (int argc, char *argv[]) { if (argc <= 3) @@ -300,6 +308,7 @@ int main (int argc, char *argv[]) if (!save_file(bindata, exe_file)) return 1; - cout << "Patched " << patch.entries.size() << " bytes." << endl; + cout << "Patched " << patch.entries.size() + << " bytes, new hash: " << compute_hash(bindata) << endl; return 0; } From 5cf42fd6f8b61efe302738d20bb1585b2704a5f3 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 10 Oct 2012 14:57:45 +0200 Subject: [PATCH 074/114] ruby: add Matinfo#=== --- plugins/ruby/material.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/ruby/material.rb b/plugins/ruby/material.rb index 4a92118d6..ca0a64779 100644 --- a/plugins/ruby/material.rb +++ b/plugins/ruby/material.rb @@ -189,6 +189,10 @@ module DFHack end def to_s ; token ; end + + def ===(other) + other.mat_index == mat_index and other.mat_type == mat_type + end end class << self From 7c969f774f5ea86aaf9251c02e187014c73986c6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 10 Oct 2012 18:22:01 +0400 Subject: [PATCH 075/114] Split the liquipowder fix-dimensions hook into separate liquid and powder. The item_liquipowder vtable is completely optimized out by MSVC. --- plugins/tweak.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index d54c4a5ec..8488ba3e8 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -34,7 +34,8 @@ #include "df/ui_build_selector.h" #include "df/building_trapst.h" #include "df/item_actual.h" -#include "df/item_liquipowder.h" +#include "df/item_liquid_miscst.h" +#include "df/item_powder_miscst.h" #include "df/item_barst.h" #include "df/item_threadst.h" #include "df/item_clothst.h" @@ -402,8 +403,8 @@ static void correct_dimension(df::item_actual *self, int32_t &delta, int32_t dim if (copy) copy->categorize(true); } -struct dimension_lqp_hook : df::item_liquipowder { - typedef df::item_liquipowder interpose_base; +struct dimension_liquid_hook : df::item_liquid_miscst { + typedef df::item_liquid_miscst interpose_base; DEFINE_VMETHOD_INTERPOSE(bool, subtractDimension, (int32_t delta)) { @@ -412,7 +413,19 @@ struct dimension_lqp_hook : df::item_liquipowder { } }; -IMPLEMENT_VMETHOD_INTERPOSE(dimension_lqp_hook, subtractDimension); +IMPLEMENT_VMETHOD_INTERPOSE(dimension_liquid_hook, subtractDimension); + +struct dimension_powder_hook : df::item_powder_miscst { + typedef df::item_powder_miscst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(bool, subtractDimension, (int32_t delta)) + { + correct_dimension(this, delta, dimension); + return INTERPOSE_NEXT(subtractDimension)(delta); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(dimension_powder_hook, subtractDimension); struct dimension_bar_hook : df::item_barst { typedef df::item_barst interpose_base; @@ -795,7 +808,8 @@ static command_result tweak(color_ostream &out, vector ¶meters) } else if (cmd == "fix-dimensions") { - enable_hook(out, INTERPOSE_HOOK(dimension_lqp_hook, subtractDimension), parameters); + enable_hook(out, INTERPOSE_HOOK(dimension_liquid_hook, subtractDimension), parameters); + enable_hook(out, INTERPOSE_HOOK(dimension_powder_hook, subtractDimension), parameters); enable_hook(out, INTERPOSE_HOOK(dimension_bar_hook, subtractDimension), parameters); enable_hook(out, INTERPOSE_HOOK(dimension_thread_hook, subtractDimension), parameters); enable_hook(out, INTERPOSE_HOOK(dimension_cloth_hook, subtractDimension), parameters); From 41de37a5c116d14175de54e3dfa9367454967868 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 10 Oct 2012 18:29:59 +0400 Subject: [PATCH 076/114] Add a special workshop for add spatter reactions into example raws. --- plugins/raw/building_spatter.txt | 22 ++++++++++++++++++++++ plugins/raw/reaction_spatter.txt | 30 +++++++++++++++--------------- 2 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 plugins/raw/building_spatter.txt diff --git a/plugins/raw/building_spatter.txt b/plugins/raw/building_spatter.txt new file mode 100644 index 000000000..7bde8297b --- /dev/null +++ b/plugins/raw/building_spatter.txt @@ -0,0 +1,22 @@ +building_spatter + +[OBJECT:BUILDING] + +[BUILDING_WORKSHOP:GREASING_STATION] + [NAME:Greasing Station] + [NAME_COLOR:2:0:1] + [DIM:1:1] + [WORK_LOCATION:1:1] + [BUILD_LABOR:DYER] + [BUILD_KEY:CUSTOM_ALT_G] + [BLOCK:1:0] + [TILE:0:1:150] + [COLOR:0:1:0:0:1] + [TILE:1:1:150] + [COLOR:1:1:MAT] + [TILE:2:1:8] + [COLOR:2:1:MAT] + [TILE:3:1:8] + [COLOR:3:1:7:5:0] + [BUILD_ITEM:1:BUCKET:NONE:NONE:NONE][CAN_USE_ARTIFACT] + [BUILD_ITEM:1:NONE:NONE:NONE:NONE][BUILDMAT] diff --git a/plugins/raw/reaction_spatter.txt b/plugins/raw/reaction_spatter.txt index 81f9a2b62..95b9f1d33 100644 --- a/plugins/raw/reaction_spatter.txt +++ b/plugins/raw/reaction_spatter.txt @@ -7,7 +7,7 @@ Reaction name must start with 'SPATTER_ADD_': [REACTION:SPATTER_ADD_OBJECT_LIQUID] [NAME:coat object with liquid] [ADVENTURE_MODE_ENABLED] - [SKILL:WAX_WORKING] + [SKILL:DYER] [REAGENT:extract:150:LIQUID_MISC:NONE:NONE:NONE] [MIN_DIMENSION:150] [DOES_NOT_DETERMINE_PRODUCT_AMOUNT] @@ -28,10 +28,10 @@ Reaction name must start with 'SPATTER_ADD_': [REACTION:SPATTER_ADD_WEAPON_EXTRACT] [NAME:coat weapon with extract] - [BUILDING:CRAFTSMAN:NONE] - [SKILL:WAX_WORKING] - [REAGENT:extract:150:LIQUID_MISC:NONE:NONE:NONE] - [MIN_DIMENSION:150] + [BUILDING:GREASING_STATION:CUSTOM_W] + [SKILL:DYER] + [REAGENT:extract:100:LIQUID_MISC:NONE:NONE:NONE] + [MIN_DIMENSION:100] [REACTION_CLASS:CREATURE_EXTRACT] [DOES_NOT_DETERMINE_PRODUCT_AMOUNT] [REAGENT:extract container:1:NONE:NONE:NONE:NONE] @@ -51,8 +51,8 @@ Reaction name must start with 'SPATTER_ADD_': [REACTION:SPATTER_ADD_AMMO_EXTRACT] [NAME:coat ammo with extract] - [BUILDING:CRAFTSMAN:NONE] - [SKILL:WAX_WORKING] + [BUILDING:GREASING_STATION:CUSTOM_A] + [SKILL:DYER] [REAGENT:extract:50:LIQUID_MISC:NONE:NONE:NONE] [MIN_DIMENSION:50] [REACTION_CLASS:CREATURE_EXTRACT] @@ -75,8 +75,8 @@ Reaction name must start with 'SPATTER_ADD_': [REACTION:SPATTER_ADD_WEAPON_GCS] [NAME:coat weapon with GCS venom] - [BUILDING:CRAFTSMAN:NONE] - [SKILL:WAX_WORKING] + [BUILDING:GREASING_STATION:NONE] + [SKILL:DYER] [REAGENT:extract:150:LIQUID_MISC:NONE:CAVE_SPIDER_GIANT:POISON] [MIN_DIMENSION:150] [REACTION_CLASS:CREATURE_EXTRACT] @@ -95,8 +95,8 @@ Reaction name must start with 'SPATTER_ADD_': [REACTION:SPATTER_ADD_AMMO_GCS] [NAME:coat ammo with GCS venom] - [BUILDING:CRAFTSMAN:NONE] - [SKILL:WAX_WORKING] + [BUILDING:GREASING_STATION:NONE] + [SKILL:DYER] [REAGENT:extract:50:LIQUID_MISC:NONE:CAVE_SPIDER_GIANT:POISON] [MIN_DIMENSION:50] [REACTION_CLASS:CREATURE_EXTRACT] @@ -115,8 +115,8 @@ Reaction name must start with 'SPATTER_ADD_': [REACTION:SPATTER_ADD_WEAPON_GDS] [NAME:coat weapon with GDS venom] - [BUILDING:CRAFTSMAN:NONE] - [SKILL:WAX_WORKING] + [BUILDING:GREASING_STATION:NONE] + [SKILL:DYER] [REAGENT:extract:150:LIQUID_MISC:NONE:SCORPION_DESERT_GIANT:POISON] [MIN_DIMENSION:150] [REACTION_CLASS:CREATURE_EXTRACT] @@ -135,8 +135,8 @@ Reaction name must start with 'SPATTER_ADD_': [REACTION:SPATTER_ADD_AMMO_GDS] [NAME:coat ammo with GDS venom] - [BUILDING:CRAFTSMAN:NONE] - [SKILL:WAX_WORKING] + [BUILDING:GREASING_STATION:NONE] + [SKILL:DYER] [REAGENT:extract:50:LIQUID_MISC:NONE:SCORPION_DESERT_GIANT:POISON] [MIN_DIMENSION:50] [REACTION_CLASS:CREATURE_EXTRACT] From 46a24a83fb660d46aed0a8304fb68d00e675c785 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 10 Oct 2012 18:01:57 +0200 Subject: [PATCH 077/114] ruby: fix item_find(:selected) in item details screen --- plugins/ruby/item.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index 9a254d9e8..469ec7449 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -6,8 +6,12 @@ module DFHack if what == :selected case curview._rtti_classname when :viewscreen_itemst - ref = curview.entry_ref[curview.cursor_pos] - ref.item_tg if ref.kind_of?(GeneralRefItem) + if ref = curview.entry_ref[curview.cursor_pos] + ref.item_tg if ref.kind_of?(GeneralRefItem) + else + # not a container + curview.item + end when :viewscreen_storesst # z/stocks if curview.in_group_mode == 0 and curview.in_right_list == 1 curview.items[curview.item_cursor] From e3e01107b91c9b22524c1c0d1e93d3932b53f491 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 10 Oct 2012 18:56:23 +0200 Subject: [PATCH 078/114] fix deathcause for new df-structures --- scripts/deathcause.rb | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/scripts/deathcause.rb b/scripts/deathcause.rb index 178ebbc87..0ed54d81a 100644 --- a/scripts/deathcause.rb +++ b/scripts/deathcause.rb @@ -1,15 +1,13 @@ # show death cause of a creature -def display_event(e) - p e if $DEBUG - - str = "#{e.victim_tg.name} died in year #{e.year}" - str << " of #{e.death_cause}" if false - str << " killed by the #{e.slayer_race_tg.name[0]} #{e.slayer_tg.name}" if e.slayer != -1 +def display_death_event(e) + str = "The #{e.victim_hf_tg.race_tg.name[0]} #{e.victim_hf_tg.name} died in year #{e.year}" + str << " (cause: #{e.death_cause.to_s.downcase})," + str << " killed by the #{e.slayer_race_tg.name[0]} #{e.slayer_hf_tg.name}" if e.slayer_hf != -1 str << " using a #{df.world.raws.itemdefs.weapons[e.weapon.item_subtype].name}" if e.weapon.item_type == :WEAPON str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.bow_item_subtype].name}" if e.weapon.bow_item_type == :WEAPON - puts str + '.' + puts str.chomp(',') + '.' end item = df.item_find(:selected) @@ -21,14 +19,15 @@ end if !item or !item.kind_of?(DFHack::ItemBodyComponent) puts "Please select a corpse in the loo'k' menu" else - hfig = item.hist_figure_id - if hfig == -1 - puts "Not a historical figure, cannot find info" + hf = item.hist_figure_id + if hf == -1 + # TODO try to retrieve info from the unit (u = item.unit_tg) + puts "Not a historical figure, cannot death find info" else events = df.world.history.events (0...events.length).reverse_each { |i| - if events[i].kind_of?(DFHack::HistoryEventHistFigureDiedst) and events[i].victim == hfig - display_event(events[i]) + if events[i].kind_of?(DFHack::HistoryEventHistFigureDiedst) and events[i].victim_hf == hf + display_death_event(events[i]) break end } From b3b93f818d097f88cb2517df4de5fb64b1ac3d69 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 10 Oct 2012 19:25:30 +0200 Subject: [PATCH 079/114] slayrace: show number of creatures per race --- scripts/slayrace.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/slayrace.rb b/scripts/slayrace.rb index b1fc24e35..749d0189b 100644 --- a/scripts/slayrace.rb +++ b/scripts/slayrace.rb @@ -33,12 +33,14 @@ slayit = lambda { |u| end } -all_races = df.world.units.active.map { |u| - u.race_tg.creature_id if checkunit[u] -}.compact.uniq.sort +all_races = Hash.new(0) + +df.world.units.active.map { |u| + all_races[u.race_tg.creature_id] += 1 if checkunit[u] +} if !race - puts all_races + all_races.sort_by { |race, cnt| [cnt, race] }.each{ |race, cnt| puts " #{race} #{cnt}" } elsif race == 'him' if him = df.unit_find slayit[him] @@ -46,7 +48,7 @@ elsif race == 'him' puts "Choose target" end else - raw_race = df.match_rawname(race, all_races) + raw_race = df.match_rawname(race, all_races.keys) raise 'invalid race' if not raw_race race_nr = df.world.raws.creatures.all.index { |cr| cr.creature_id == raw_race } From b5f5d1f85b56fe0d7bd804fe881ce8a50310de10 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 10 Oct 2012 19:42:36 +0200 Subject: [PATCH 080/114] removebadthoughts: add --dry-run option --- NEWS | 1 + Readme.rst | 16 +++++++------ scripts/removebadthoughts.rb | 44 ++++++++++++++++++++++++------------ 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/NEWS b/NEWS index 65339ad64..192b93aed 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ DFHack future Nothing yet! + - removebadthoughts: add --dry-run option DFHack v0.34.11-r2 diff --git a/Readme.rst b/Readme.rst index b5c0f335d..7ce15015a 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1632,16 +1632,18 @@ removebadthoughts This script remove negative thoughts from your dwarves. Very useful against tantrum spirals. -With a selected unit in 'v' mode, will clear this unit's mind, otherwise -clear all your fort's units minds. +The script can target a single creature, when used with the ``him`` argument, +or the whole fort population, with ``all``. + +To show every bad thought present without actually removing them, run the +script with the ``-n`` or ``--dry-run`` argument. This can give a quick +hint on what bothers your dwarves the most. Individual dwarf happiness may not increase right after this command is run, but in the short term your dwarves will get much more joyful. -The thoughts are set to be very old, and the game will remove them soon when -you unpause. -With the optional ``-v`` parameter, the script will dump the negative thoughts -it removed. +Internals: the thoughts are set to be very old, so that the game remove them +quickly after you unpause. slayrace @@ -1650,7 +1652,7 @@ Kills any unit of a given race. With no argument, lists the available races. -With the special argument 'him', targets only the selected creature. +With the special argument ``him``, targets only the selected creature. Any non-dead non-caged unit of the specified race gets its ``blood_count`` set to 0, which means immediate death at the next game tick. For creatures diff --git a/scripts/removebadthoughts.rb b/scripts/removebadthoughts.rb index 99b742643..ff98f8f65 100644 --- a/scripts/removebadthoughts.rb +++ b/scripts/removebadthoughts.rb @@ -1,27 +1,43 @@ # remove bad thoughts for the selected unit or the whole fort -# with removebadthoughts -v, dump the bad thoughts types we removed -verbose = $script_args.delete('-v') +dry_run = $script_args.delete('--dry-run') || $script_args.delete('-n') -if u = df.unit_find(:selected) - targets = [u] -else - targets = df.unit_citizens -end +$script_args << 'all' if dry_run and $script_args.empty? seenbad = Hash.new(0) -targets.each { |u| +clear_mind = lambda { |u| u.status.recent_events.each { |e| next if DFHack::UnitThoughtType::Value[e.type].to_s[0, 1] != '-' seenbad[e.type] += 1 - e.age = 0x1000_0000 + e.age = 0x1000_0000 unless dry_run } } -if verbose - seenbad.sort_by { |k, v| v }.each { |k, v| puts " #{v} #{k}" } -end +summary = lambda { + seenbad.sort_by { |thought, cnt| cnt }.each { |thought, cnt| + puts " #{thought} #{cnt}" + } + count = seenbad.values.inject(0) { |sum, cnt| sum+cnt } + puts "Removed #{count} bad thought#{'s' if count != 1}." if count > 0 and not dry_run +} -count = seenbad.values.inject(0) { |s, v| s+v } -puts "removed #{count} bad thought#{'s' if count != 1}" +case $script_args[0] +when 'him' + if u = df.unit_find + clear_mind[u] + summary[] + else + puts 'Please select a dwarf ingame' + end + +when 'all' + df.unit_citizens.each { |uu| + clear_mind[uu] + } + summary[] + +else + puts "Usage: removebadthoughts [--dry-run] " + +end From 599577780add8e8c7caaba281b584c50dacdb9a0 Mon Sep 17 00:00:00 2001 From: ab9rf Date: Wed, 10 Oct 2012 23:46:01 -0500 Subject: [PATCH 081/114] Sync structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index d9c765cce..a5a8fd689 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit d9c765ccea1d98ac08d33316dbb12f62df271690 +Subproject commit a5a8fd68947b60fcb2d1c03b4f05bdf48cfcf7a5 From 010417c8122bdb16cc881ccba6e2d236a8c01491 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 11 Oct 2012 12:36:17 +0400 Subject: [PATCH 082/114] Compute detailed focus string for the hauling menu. --- library/modules/Gui.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index b211f9f2b..04a453c06 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -85,6 +85,8 @@ using namespace DFHack; #include "df/assign_trade_status.h" #include "df/announcement_flags.h" #include "df/announcements.h" +#include "df/stop_depart_condition.h" +#include "df/route_stockpile_link.h" using namespace df::enums; using df::global::gview; @@ -303,6 +305,45 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) focus += "/List"; break; + case Hauling: + if (ui->hauling.in_assign_vehicle) + { + auto vehicle = vector_get(ui->hauling.vehicles, ui->hauling.cursor_vehicle); + focus += "/AssignVehicle/" + std::string(vehicle ? "Some" : "None"); + } + else + { + int idx = ui->hauling.cursor_top; + auto route = vector_get(ui->hauling.view_routes, idx); + auto stop = vector_get(ui->hauling.view_stops, idx); + std::string tag = stop ? "Stop" : (route ? "Route" : "None"); + + if (ui->hauling.in_name) + focus += "/Rename/" + tag; + else if (ui->hauling.in_stop) + { + int sidx = ui->hauling.cursor_stop; + auto cond = vector_get(ui->hauling.stop_conditions, sidx); + auto link = vector_get(ui->hauling.stop_links, sidx); + + focus += "/DefineStop"; + + if (cond) + focus += "/Cond/" + enum_item_key(cond->mode); + else if (link) + { + focus += "/Link/"; + if (link->mode.bits.give) focus += "Give"; + if (link->mode.bits.take) focus += "Take"; + } + else + focus += "/None"; + } + else + focus += "/Select/" + tag; + } + break; + default: break; } From 5206236b014850518e2de5f47e4534bc1b10bd48 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 11 Oct 2012 15:10:19 +0400 Subject: [PATCH 083/114] Look through missing intermediate bases when interposing subclasses. --- library/VTableInterpose.cpp | 20 ++++++++++++++++++-- library/include/VTableInterpose.h | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/library/VTableInterpose.cpp b/library/VTableInterpose.cpp index 141450615..d8a07e830 100644 --- a/library/VTableInterpose.cpp +++ b/library/VTableInterpose.cpp @@ -281,9 +281,10 @@ VMethodInterposeLinkBase *VMethodInterposeLinkBase::get_first_interpose(virtual_ return item; } -void VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmptr) +bool VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmptr) { auto &children = cur->getChildren(); + bool found = false; for (size_t i = 0; i < children.size(); i++) { @@ -298,17 +299,32 @@ void VMethodInterposeLinkBase::find_child_hosts(virtual_identity *cur, void *vmp continue; child_next.insert(base); + found = true; } - else + else if (child->vtable_ptr) { void *cptr = child->get_vmethod_ptr(vmethod_idx); if (cptr != vmptr) continue; child_hosts.insert(child); + found = true; + find_child_hosts(child, vmptr); } + else + { + // If this vtable is not known, but any of the children + // have the same vmethod, this one definitely does too + if (find_child_hosts(child, vmptr)) + { + child_hosts.insert(child); + found = true; + } + } } + + return found; } void VMethodInterposeLinkBase::on_host_delete(virtual_identity *from) diff --git a/library/include/VTableInterpose.h b/library/include/VTableInterpose.h index b52b32270..bd3c23036 100644 --- a/library/include/VTableInterpose.h +++ b/library/include/VTableInterpose.h @@ -159,7 +159,7 @@ namespace DFHack void on_host_delete(virtual_identity *host); VMethodInterposeLinkBase *get_first_interpose(virtual_identity *id); - void find_child_hosts(virtual_identity *cur, void *vmptr); + bool find_child_hosts(virtual_identity *cur, void *vmptr); public: VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority); ~VMethodInterposeLinkBase(); From 2865e1373aaa366b1121806408726a9b8c087fa6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 11 Oct 2012 17:34:34 +0400 Subject: [PATCH 084/114] Experimental API for associating tile bitmasks with persistent data. Use block_square_event_world_constructionst objects with the same bogus negative id as the matching historical figure object. --- Lua API.html | 27 ++++++++ Lua API.rst | 34 ++++++++++ library/LuaApi.cpp | 52 ++++++++++++++- .../df/custom/tile_bitmask.methods.inc | 6 +- library/include/modules/World.h | 9 +++ library/modules/World.cpp | 65 +++++++++++++++++++ library/xml | 2 +- 7 files changed, 189 insertions(+), 6 deletions(-) diff --git a/Lua API.html b/Lua API.html index 06fa5418e..125687110 100644 --- a/Lua API.html +++ b/Lua API.html @@ -932,6 +932,21 @@ Returns entry, did_create_new

                and automatically stored in the save game, these save and retrieval functions can just copy values in memory without doing any actual I/O. However, currently every entry has a 180+-byte dead-weight overhead.

                +

                It is also possible to associate one bit per map tile with an entry, +using these two methods:

                +
                  +
                • entry:getTilemask(block[, create])

                  +

                  Retrieves the tile bitmask associated with this entry in the given map +block. If create is true, an empty mask is created if none exists; +otherwise the function returns nil, which must be assumed to be the same +as an all-zero mask.

                  +
                • +
                • entry:deleteTilemask(block)

                  +

                  Deletes the associated tile mask from the given map block.

                  +
                • +
                +

                Note that these masks are only saved in fortress mode, and also that deleting +the persistent entry will NOT delete the associated masks.

                Material info lookup

                @@ -1286,6 +1301,18 @@ tools like liquids or tiletypes are used. It also cannot possibly take into account anything that depends on the actual units, like burrows, or the presence of invaders.

                +
              • dfhack.maps.hasTileAssignment(tilemask)

                +

                Checks if the tile_bitmask object is not nil and contains any set bits; returns true or false.

                +
              • +
              • dfhack.maps.getTileAssignment(tilemask,x,y)

                +

                Checks if the tile_bitmask object is not nil and has the relevant bit set; returns true or false.

                +
              • +
              • dfhack.maps.setTileAssignment(tilemask,x,y,enable)

                +

                Sets the relevant bit in the tile_bitmask object to the enable argument.

                +
              • +
              • dfhack.maps.resetTileAssignment(tilemask[,enable])

                +

                Sets all bits in the mask to the enable argument.

                +
              • diff --git a/Lua API.rst b/Lua API.rst index fbb4b7d82..7ad6aec23 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -646,6 +646,24 @@ and automatically stored in the save game, these save and retrieval functions can just copy values in memory without doing any actual I/O. However, currently every entry has a 180+-byte dead-weight overhead. +It is also possible to associate one bit per map tile with an entry, +using these two methods: + +* ``entry:getTilemask(block[, create])`` + + Retrieves the tile bitmask associated with this entry in the given map + block. If ``create`` is *true*, an empty mask is created if none exists; + otherwise the function returns *nil*, which must be assumed to be the same + as an all-zero mask. + +* ``entry:deleteTilemask(block)`` + + Deletes the associated tile mask from the given map block. + +Note that these masks are only saved in fortress mode, and also that deleting +the persistent entry will **NOT** delete the associated masks. + + Material info lookup -------------------- @@ -1079,6 +1097,22 @@ Maps module take into account anything that depends on the actual units, like burrows, or the presence of invaders. +* ``dfhack.maps.hasTileAssignment(tilemask)`` + + Checks if the tile_bitmask object is not *nil* and contains any set bits; returns *true* or *false*. + +* ``dfhack.maps.getTileAssignment(tilemask,x,y)`` + + Checks if the tile_bitmask object is not *nil* and has the relevant bit set; returns *true* or *false*. + +* ``dfhack.maps.setTileAssignment(tilemask,x,y,enable)`` + + Sets the relevant bit in the tile_bitmask object to the *enable* argument. + +* ``dfhack.maps.resetTileAssignment(tilemask[,enable])`` + + Sets all bits in the mask to the *enable* argument. + Burrows module -------------- diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 0df96f066..ef571bcb7 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -30,6 +30,7 @@ distribution. #include "MemAccess.h" #include "Core.h" +#include "Error.h" #include "VersionInfo.h" #include "tinythread.h" // must be last due to MS stupidity @@ -311,12 +312,12 @@ static PersistentDataItem get_persistent(lua_State *state) if (lua_istable(state, 1)) { + Lua::StackUnwinder frame(state); + if (!lua_getmetatable(state, 1) || !lua_rawequal(state, -1, lua_upvalueindex(1))) luaL_argerror(state, 1, "invalid table type"); - lua_settop(state, 1); - return persistent_by_struct(state, 1); } else @@ -446,11 +447,38 @@ static int dfhack_persistent_save(lua_State *state) return 2; } +static int dfhack_persistent_getTilemask(lua_State *state) +{ + CoreSuspender suspend; + + lua_settop(state, 3); + auto ref = get_persistent(state); + auto block = Lua::CheckDFObject(state, 2); + bool create = lua_toboolean(state, 3); + + Lua::PushDFObject(state, World::getPersistentTilemask(ref, block, create)); + return 1; +} + +static int dfhack_persistent_deleteTilemask(lua_State *state) +{ + CoreSuspender suspend; + + lua_settop(state, 2); + auto ref = get_persistent(state); + auto block = Lua::CheckDFObject(state, 2); + + lua_pushboolean(state, World::deletePersistentTilemask(ref, block)); + return 1; +} + static const luaL_Reg dfhack_persistent_funcs[] = { { "get", dfhack_persistent_get }, { "delete", dfhack_persistent_delete }, { "get_all", dfhack_persistent_get_all }, { "save", dfhack_persistent_save }, + { "getTilemask", dfhack_persistent_getTilemask }, + { "deleteTilemask", dfhack_persistent_deleteTilemask }, { NULL, NULL } }; @@ -937,6 +965,22 @@ static const luaL_Reg dfhack_items_funcs[] = { /***** Maps module *****/ +static bool hasTileAssignment(df::tile_bitmask *bm) { + return bm && bm->has_assignments(); +} +static bool getTileAssignment(df::tile_bitmask *bm, int x, int y) { + return bm && bm->getassignment(x,y); +} +static void setTileAssignment(df::tile_bitmask *bm, int x, int y, bool val) { + CHECK_NULL_POINTER(bm); + bm->setassignment(x,y,val); +} +static void resetTileAssignment(df::tile_bitmask *bm, bool val) { + CHECK_NULL_POINTER(bm); + if (val) bm->set_all(); + else bm->clear(); +} + static const LuaWrapper::FunctionReg dfhack_maps_module[] = { WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock), WRAPM(Maps, enableBlockUpdates), @@ -944,6 +988,10 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = { WRAPM(Maps, getLocalInitFeature), WRAPM(Maps, canWalkBetween), WRAPM(Maps, spawnFlow), + WRAPN(hasTileAssignment, hasTileAssignment), + WRAPN(getTileAssignment, getTileAssignment), + WRAPN(setTileAssignment, setTileAssignment), + WRAPN(resetTileAssignment, resetTileAssignment), { NULL, NULL } }; diff --git a/library/include/df/custom/tile_bitmask.methods.inc b/library/include/df/custom/tile_bitmask.methods.inc index 18b312a08..e3414e334 100644 --- a/library/include/df/custom/tile_bitmask.methods.inc +++ b/library/include/df/custom/tile_bitmask.methods.inc @@ -16,7 +16,7 @@ inline bool getassignment( const df::coord2d &xy ) } inline bool getassignment( int x, int y ) { - return (bits[y] & (1 << x)); + return (bits[(y&15)] & (1 << (x&15))); } inline void setassignment( const df::coord2d &xy, bool bit ) { @@ -25,9 +25,9 @@ inline void setassignment( const df::coord2d &xy, bool bit ) inline void setassignment( int x, int y, bool bit ) { if(bit) - bits[y] |= (1 << x); + bits[(y&15)] |= (1 << (x&15)); else - bits[y] &= ~(1 << x); + bits[(y&15)] &= ~(1 << (x&15)); } bool has_assignments() { diff --git a/library/include/modules/World.h b/library/include/modules/World.h index f4c31dcf3..a945c4e72 100644 --- a/library/include/modules/World.h +++ b/library/include/modules/World.h @@ -37,6 +37,12 @@ distribution. #include "DataDefs.h" +namespace df +{ + struct tile_bitmask; + struct map_block; +} + namespace DFHack { typedef df::game_mode GameMode; @@ -120,6 +126,9 @@ namespace DFHack DFHACK_EXPORT bool DeletePersistentData(const PersistentDataItem &item); DFHACK_EXPORT void ClearPersistentCache(); + + DFHACK_EXPORT df::tile_bitmask *getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create = false); + DFHACK_EXPORT bool deletePersistentTilemask(const PersistentDataItem &item, df::map_block *block); } } #endif diff --git a/library/modules/World.cpp b/library/modules/World.cpp index 65d0c7cb8..f3283c3a3 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -38,13 +38,18 @@ using namespace std; #include "ModuleFactory.h" #include "Core.h" +#include "modules/Maps.h" + #include "MiscUtils.h" #include "DataDefs.h" #include "df/world.h" #include "df/historical_figure.h" +#include "df/map_block.h" +#include "df/block_square_event_world_constructionst.h" using namespace DFHack; +using namespace df::enums; using df::global::world; @@ -312,3 +317,63 @@ bool World::DeletePersistentData(const PersistentDataItem &item) return false; } + +df::tile_bitmask *World::getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create) +{ + if (!block) + return NULL; + + int id = item.raw_id(); + if (id > -100) + return NULL; + + for (size_t i = 0; i < block->block_events.size(); i++) + { + auto ev = block->block_events[i]; + if (ev->getType() != block_square_event_type::world_construction) + continue; + auto wcsev = strict_virtual_cast(ev); + if (!wcsev || wcsev->construction_id != id) + continue; + return &wcsev->tile_bitmask; + } + + if (!create) + return NULL; + + auto ev = df::allocate(); + if (!ev) + return NULL; + + ev->construction_id = id; + ev->tile_bitmask.clear(); + vector_insert_at(block->block_events, 0, (df::block_square_event*)ev); + + return &ev->tile_bitmask; +} + +bool World::deletePersistentTilemask(const PersistentDataItem &item, df::map_block *block) +{ + if (!block) + return false; + int id = item.raw_id(); + if (id > -100) + return false; + + bool found = false; + for (int i = block->block_events.size()-1; i >= 0; i--) + { + auto ev = block->block_events[i]; + if (ev->getType() != block_square_event_type::world_construction) + continue; + auto wcsev = strict_virtual_cast(ev); + if (!wcsev || wcsev->construction_id != id) + continue; + + delete wcsev; + vector_erase_at(block->block_events, i); + found = true; + } + + return found; +} diff --git a/library/xml b/library/xml index 20c6d9c74..aec106cdc 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 20c6d9c743f1c5a20bb288f427b101e9b2a138d7 +Subproject commit aec106cdc32083bbcc6d6dd27d9e069f5525ea92 From 5f9489a8432d638b2ecb2e264c592025a1e1dde6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 11 Oct 2012 19:32:41 +0400 Subject: [PATCH 085/114] Start making a script for viewing and changing minecart Guide paths. --- dfhack.init-example | 3 + scripts/gui/guide-path.lua | 201 +++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 scripts/gui/guide-path.lua diff --git a/dfhack.init-example b/dfhack.init-example index 8505f5acc..3ecbf4342 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -72,6 +72,9 @@ keybinding add Alt-A@dwarfmode/QueryBuilding/Some/SiegeEngine gui/siege-engine # military weapon auto-select keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons +# minecart Guide path +keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path + ############################ # UI and game logic tweaks # ############################ diff --git a/scripts/gui/guide-path.lua b/scripts/gui/guide-path.lua new file mode 100644 index 000000000..f3e27d917 --- /dev/null +++ b/scripts/gui/guide-path.lua @@ -0,0 +1,201 @@ +-- Show and manipulate the path used by Guide Cart orders. + +local utils = require 'utils' +local gui = require 'gui' +local guidm = require 'gui.dwarfmode' +local dlg = require 'gui.dialogs' + +local tile_attrs = df.tiletype.attrs + +function same_xyz(a,b) + return a and b and a.x == b.x and a.y == b.y and a.z == b.z +end + +GuidePathUI = defclass(GuidePathUI, guidm.MenuOverlay) + +GuidePathUI.focus_path = 'guide-path' + +GuidePathUI.ATTRS { + route = DEFAULT_NIL, + stop = DEFAULT_NIL, + order = DEFAULT_NIL, +} + +function GuidePathUI:init() + self.saved_mode = df.global.ui.main.mode + + for i=0,#self.route.stops-1 do + if self.route.stops[i] == self.stop then + self.stop_idx = i + break + end + end + + if not self.stop_idx then + error('Could not find stop index') + end + + self.next_stop = self.route.stops[(self.stop_idx+1)%#self.route.stops] +end + +function GuidePathUI:onShow() + -- with cursor, but without those ugly lines from native hauling mode + df.global.ui.main.mode = df.ui_sidebar_mode.Stockpiles +end + +function GuidePathUI:onDestroy() + df.global.ui.main.mode = self.saved_mode +end + +local function getTileType(cursor) + local block = dfhack.maps.getTileBlock(cursor) + if block then + return block.tiletype[cursor.x%16][cursor.y%16] + else + return 0 + end +end + +local function isTrackTile(tile) + return tile_attrs[tile].special == df.tiletype_special.TRACK +end + +local function paintMapTile(dc, vp, cursor, pos, ...) + if not same_xyz(cursor, pos) then + local stile = vp:tileToScreen(pos) + if stile.z == 0 then + dc:seek(stile.x,stile.y):char(...) + end + end +end + +local function get_path_point(gpath,i) + return xyz2pos(gpath.x[i], gpath.y[i], gpath.z[i]) +end + +local function blink_visible(delay) + return math.floor(dfhack.getTickCount()/delay) % 2 == 0 +end + +function GuidePathUI:renderPath(cursor) + local gpath = self.order.guide_path + local startp = self.stop.pos + local endp = self.next_stop.pos + local vp = self:getViewport() + local dc = gui.Painter.new(self.df_layout.map) + local visible = blink_visible(500) + + if visible then + paintMapTile(dc, vp, cursor, endp, '+', COLOR_LIGHTGREEN) + end + + local ok = nil + local pcnt = #gpath.x + if pcnt > 0 then + ok = true + + for i = 0,pcnt-1 do + local pt = get_path_point(gpath, i) + if i == 0 and not same_xyz(startp,pt) then + ok = false + end + if i == pcnt-1 and not same_xyz(endp,pt) then + ok = false + end + local tile = getTileType(pt) + if not isTrackTile(tile) then + ok = false + end + if visible then + local char = '+' + if i < pcnt-1 then + local npt = get_path_point(gpath, i+1) + if npt.x == pt.x+1 then + char = 26 + elseif npt.x == pt.x-1 then + char = 27 + elseif npt.y == pt.y+1 then + char = 25 + elseif npt.y == pt.y-1 then + char = 24 + end + end + local color = COLOR_LIGHTGREEN + if not ok then color = COLOR_LIGHTRED end + paintMapTile(dc, vp, cursor, pt, char, color) + end + end + end + + if blink_visible(120) then + paintMapTile(dc, vp, cursor, startp, 240, COLOR_LIGHTGREEN, COLOR_GREEN) + end + + return ok +end + +function GuidePathUI:onRenderBody(dc) + dc:clear():seek(1,1):pen(COLOR_WHITE):string("Guide Path") + + dc:seek(2,3) + + local cursor = guidm.getCursorPos() + local path_ok = self:renderPath(cursor) + + if path_ok == nil then + dc:string('No saved path', COLOR_DARKGREY) + elseif path_ok then + dc:string('Valid path', COLOR_GREEN) + else + dc:string('Invalid path', COLOR_RED) + end + + dc:newline():newline(1) + dc:key('CUSTOM_Z'):string(": Reset path, ",COLOR_GREY,nil,path_ok~=nil) + dc:key('CUSTOM_P'):string(": Find path",COLOR_GREY,nil,false) + dc:newline(1) + dc:key('CUSTOM_C'):string(": Zoom cur, ") + dc:key('CUSTOM_N'):string(": Zoom next") + + dc:newline():newline(1):string('At cursor:') + dc:newline():newline(2) + + local tile = getTileType(cursor) + if isTrackTile(tile) then + dc:string('Track '..tile_attrs[tile].direction, COLOR_GREEN) + else + dc:string('No track', COLOR_DARKGREY) + end + + dc:newline():newline(1) + dc:key('LEAVESCREEN'):string(": Back") +end + +function GuidePathUI:onInput(keys) + if keys.CUSTOM_C then + self:moveCursorTo(copyall(self.stop.pos)) + elseif keys.CUSTOM_N then + self:moveCursorTo(copyall(self.next_stop.pos)) + elseif keys.CUSTOM_Z then + local gpath = self.order.guide_path + gpath.x:resize(0) + gpath.y:resize(0) + gpath.z:resize(0) + elseif keys.LEAVESCREEN then + self:dismiss() + elseif self:propagateMoveKeys(keys) then + return + end +end + +if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/Hauling/DefineStop/Cond/Guide') then + qerror("This script requires the main dwarfmode view in 'h' mode over a Guide order") +end + +local hauling = df.global.ui.hauling +local route = hauling.view_routes[hauling.cursor_top] +local stop = hauling.view_stops[hauling.cursor_top] +local order = hauling.stop_conditions[hauling.cursor_stop] + +local list = GuidePathUI{ route = route, stop = stop, order = order } +list:show() From 424c37c0ea23cd9fb822ce61a3964e16502478f8 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 11 Oct 2012 17:51:49 +0200 Subject: [PATCH 086/114] ruby: fix codegen to handle composite vtables --- plugins/ruby/codegen.pl | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index a615ab964..8dae55597 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -232,7 +232,11 @@ sub render_global_class { render_struct_fields($type); my $vms = $type->findnodes('child::virtual-methods')->[0]; - render_class_vmethods($vms) if $vms; + if ($vms) + { + my $voff = render_class_vmethods_voff($parent); + render_class_vmethods($vms, $voff); + } }; push @lines_rb, "end\n"; } @@ -368,9 +372,29 @@ sub render_container_reftarget { } } +# return the size of the parent's vtables +sub render_class_vmethods_voff { + my ($name) = @_; + + return 0 if !$name; + + my $type = $global_types{$name}; + my $parent = $type->getAttribute('inherits-from'); + + my $voff = render_class_vmethods_voff($parent); + my $vms = $type->findnodes('child::virtual-methods')->[0]; + + for my $meth ($vms->findnodes('child::vmethod')) + { + $voff += 4 if $meth->getAttribute('is-destructor') and $os eq 'linux'; + $voff += 4; + } + + return $voff; +} + sub render_class_vmethods { - my ($vms) = @_; - my $voff = 0; + my ($vms, $voff) = @_; for my $meth ($vms->findnodes('child::vmethod')) { From 12eeb9e5c0f5b15bd9976e07f4ba09d34774d3f8 Mon Sep 17 00:00:00 2001 From: ab9rf Date: Fri, 12 Oct 2012 00:38:45 -0500 Subject: [PATCH 087/114] Correct wrong variable use in start_autonestbox. --- plugins/zone.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/zone.cpp b/plugins/zone.cpp index 542ce8a02..2c63b6af7 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -3498,11 +3498,11 @@ command_result start_autonestbox(color_ostream &out) { enable_autonestbox = true; - if (!config_autobutcher.isValid()) + if (!config_autonestbox.isValid()) { config_autonestbox = World::AddPersistentData("autonestbox/config"); - if (!config_autobutcher.isValid()) + if (!config_autonestbox.isValid()) { out << "Cannot enable autonestbox without a world!" << endl; return CR_OK; From 0547ee7f831e9e10442ea52bbe0ebfeb344f9f90 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 12 Oct 2012 11:12:31 +0200 Subject: [PATCH 088/114] ruby: add magic "nolock " prefix to run ruby without Suspending main df --- plugins/ruby/ruby.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 7bd6d13e8..13190f70d 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -118,18 +118,8 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } -// send a single ruby line to be evaluated by the ruby thread -DFhackCExport command_result plugin_eval_ruby( color_ostream &out, const char *command) +static command_result do_plugin_eval_ruby(color_ostream &out, const char *command) { - // if dlopen failed - if (!r_thread) - return CR_FAILURE; - - // wrap all ruby code inside a suspend block - // if we dont do that and rely on ruby code doing it, we'll deadlock in - // onupdate - CoreSuspender suspend; - command_result ret; // ensure ruby thread is idle @@ -159,6 +149,27 @@ DFhackCExport command_result plugin_eval_ruby( color_ostream &out, const char *c return ret; } +// send a single ruby line to be evaluated by the ruby thread +DFhackCExport command_result plugin_eval_ruby( color_ostream &out, const char *command) +{ + // if dlopen failed + if (!r_thread) + return CR_FAILURE; + + if (!strncmp(command, "nolock ", 7)) { + // debug only! + // run ruby commands without locking the main thread + // useful when the game is frozen after a segfault + return do_plugin_eval_ruby(out, command+7); + } else { + // wrap all ruby code inside a suspend block + // if we dont do that and rely on ruby code doing it, we'll deadlock in + // onupdate + CoreSuspender suspend; + return do_plugin_eval_ruby(out, command); + } +} + DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { if (!r_thread) From 1f88c0eeed1999a252b0b3f0dd40252a7b983f99 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 12 Oct 2012 11:42:42 +0200 Subject: [PATCH 089/114] ruby: codegen: avoid bad vector_reftg accessor --- plugins/ruby/codegen.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 8dae55597..ff69853af 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -279,7 +279,6 @@ sub render_struct_field_refs { my ($parent, $field, $name) = @_; my $reftg = $field->getAttribute('ref-target'); - render_field_reftarget($parent, $field, $name, $reftg) if ($reftg); my $refto = $field->getAttribute('refers-to'); render_field_refto($parent, $name, $refto) if ($refto); @@ -289,6 +288,8 @@ sub render_struct_field_refs { if ($meta and $meta eq 'container' and $item) { my $itemreftg = $item->getAttribute('ref-target'); render_container_reftarget($parent, $item, $name, $itemreftg) if $itemreftg; + } elsif ($reftg) { + render_field_reftarget($parent, $field, $name, $reftg); } } From c089534f7315190b6df7d6ce6390b99fc3adc6b0 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 12 Oct 2012 13:33:58 +0200 Subject: [PATCH 090/114] ruby: fix assigning value to pointer to number --- plugins/ruby/ruby-autogen-defs.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 4470c8022..d5bcb08f4 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -282,6 +282,10 @@ module DFHack DFHack.memory_read_int32(@_memaddr) & 0xffffffff end + def _setp(v) + DFHack.memory_write_int32(@_memaddr, v) + end + def _get addr = _getp return if addr == 0 @@ -294,7 +298,15 @@ module DFHack case v when Pointer; DFHack.memory_write_int32(@_memaddr, v._getp) when MemStruct; DFHack.memory_write_int32(@_memaddr, v._memaddr) - when Integer; DFHack.memory_write_int32(@_memaddr, v) + when Integer + if @_tg and @_tg.kind_of?(MemHack::Number) + if _getp == 0 + _setp(DFHack.malloc(@_tg._bits/8)) + end + @_tg._at(_getp)._set(v) + else + DFHack.memory_write_int32(@_memaddr, v) + end when nil; DFHack.memory_write_int32(@_memaddr, 0) else _get._set(v) end From 33bd8103de43d1a6cb1d8a0f48819e4d753a94ce Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 15 Oct 2012 15:30:00 +0400 Subject: [PATCH 091/114] Extract an abstract View superclass from Screen to handle widget trees. --- Lua API.rst | 12 ++ library/lua/dfhack.lua | 16 ++ library/lua/gui.lua | 283 ++++++++++++++++++++++++---------- library/lua/gui/dialogs.lua | 2 +- library/lua/gui/dwarfmode.lua | 18 ++- scripts/gui/guide-path.lua | 4 - scripts/gui/hello-world.lua | 7 +- scripts/gui/siege-engine.lua | 6 +- 8 files changed, 249 insertions(+), 99 deletions(-) diff --git a/Lua API.rst b/Lua API.rst index 7ad6aec23..bbee8646c 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -1727,6 +1727,18 @@ environment by the mandatory init file dfhack.lua: Returns a table with x, y and z as fields. +* ``same_xyz(a,b)`` + + Checks if ``a`` and ``b`` have the same x, y and z fields. + +* ``get_path_xyz(path,i)`` + + Returns ``path.x[i], path.y[i], path.z[i]``. + +* ``pos2xy(obj)``, ``xy2pos(x,y)``, ``same_xy(a,b)``, ``get_path_xy(a,b)`` + + Same as above, but for 2D coordinates. + * ``safe_index(obj,index...)`` Walks a sequence of dereferences, which may be represented by numbers or strings. diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index ce3be5a87..9fc94a025 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -157,6 +157,14 @@ function xyz2pos(x,y,z) end end +function same_xyz(a,b) + return a and b and a.x == b.x and a.y == b.y and a.z == b.z +end + +function get_path_xyz(path,i) + return path.x[i], path.y[i], path.z[i] +end + function pos2xy(pos) if pos then local x = pos.x @@ -174,6 +182,14 @@ function xy2pos(x,y) end end +function same_xy(a,b) + return a and b and a.x == b.x and a.y == b.y +end + +function get_path_xy(path,i) + return path.x[i], path.y[i] +end + function safe_index(obj,idx,...) if obj == nil or idx == nil then return nil diff --git a/library/lua/gui.lua b/library/lua/gui.lua index a5e4f7503..4f98e1bde 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -50,8 +50,8 @@ function mkdims_wh(x1,y1,w,h) end function inset(rect,dx1,dy1,dx2,dy2) return mkdims_xy( - rect.x1+dx1, rect.y1+dy1, - rect.x2-(dx2 or dx1), rect.y2-(dy2 or dy1) + rect.x1+dx1, rect.y1+(dy1 or dx1), + rect.x2-(dx2 or dx1), rect.y2-(dy2 or dy1 or dx1) ) end function is_in_rect(rect,x,y) @@ -68,45 +68,68 @@ local function to_pen(default, pen, bg, bold) end end ----------------------------- --- Clipped painter object -- ----------------------------- +function getKeyDisplay(code) + if type(code) == 'string' then + code = df.interface_key[code] + end + return dscreen.getKeyDisplay(code) +end -Painter = defclass(Painter, nil) +----------------------------------- +-- Clipped view rectangle object -- +----------------------------------- -function Painter:init(args) - local rect = args.rect or mkdims_wh(0,0,dscreen.getWindowSize()) - local crect = args.clip_rect or rect - self:assign{ - x = rect.x1, y = rect.y1, - x1 = rect.x1, clip_x1 = crect.x1, - y1 = rect.y1, clip_y1 = crect.y1, - x2 = rect.x2, clip_x2 = crect.x2, - y2 = rect.y2, clip_y2 = crect.y2, - width = rect.x2-rect.x1+1, - height = rect.y2-rect.y1+1, - cur_pen = to_pen(nil, args.pen or COLOR_GREY) - } +ViewRect = defclass(ViewRect, nil) + +function ViewRect:init(args) + if args.view_rect then + self:assign(args.view_rect) + else + local rect = args.rect or mkdims_wh(0,0,dscreen.getWindowSize()) + local crect = args.clip_rect or rect + self:assign{ + x1 = rect.x1, clip_x1 = crect.x1, + y1 = rect.y1, clip_y1 = crect.y1, + x2 = rect.x2, clip_x2 = crect.x2, + y2 = rect.y2, clip_y2 = crect.y2, + width = rect.x2-rect.x1+1, + height = rect.y2-rect.y1+1, + } + end + if args.clip_view then + local cr = args.clip_view + self:assign{ + clip_x1 = math.max(self.clip_x1, cr.clip_x1), + clip_y1 = math.max(self.clip_y1, cr.clip_y1), + clip_x2 = math.min(self.clip_x2, cr.clip_x2), + clip_y2 = math.min(self.clip_y2, cr.clip_y2), + } + end end -function Painter.new(rect, pen) - return Painter{ rect = rect, pen = pen } +function ViewRect:isDefunct() + return (self.clip_x1 > self.clip_x2 or self.clip_y1 > self.clip_y2) end -function Painter.new_xy(x1,y1,x2,y2,pen) - return Painter{ rect = mkdims_xy(x1,y1,x2,y2), pen = pen } +function ViewRect:inClipGlobalXY(x,y) + return x >= self.clip_x1 and x <= self.clip_x2 + and y >= self.clip_y1 and y <= self.clip_y2 end -function Painter.new_wh(x,y,w,h,pen) - return Painter{ rect = mkdims_wh(x,y,w,h), pen = pen } +function ViewRect:inClipLocalXY(x,y) + return (x+self.x1) >= self.clip_x1 and (x+self.x1) <= self.clip_x2 + and (y+self.y1) >= self.clip_y1 and (y+self.y1) <= self.clip_y2 end -function Painter:isValidPos() - return self.x >= self.clip_x1 and self.x <= self.clip_x2 - and self.y >= self.clip_y1 and self.y <= self.clip_y2 +function ViewRect:localXY(x,y) + return x-self.x1, y-self.y1 end -function Painter:viewport(x,y,w,h) +function ViewRect:globalXY(x,y) + return x+self.x1, y+self.y1 +end + +function ViewRect:viewport(x,y,w,h) if type(x) == 'table' then x,y,w,h = x.x1, x.y1, x.width, x.height end @@ -121,17 +144,57 @@ function Painter:viewport(x,y,w,h) clip_y1 = math.max(self.clip_y1, y1), clip_x2 = math.min(self.clip_x2, x2), clip_y2 = math.min(self.clip_y2, y2), - -- Pen - cur_pen = self.cur_pen } + return mkinstance(ViewRect, vp) +end + +---------------------------- +-- Clipped painter object -- +---------------------------- + +Painter = defclass(Painter, ViewRect) + +function Painter:init(args) + self.x = self.x1 + self.y = self.y1 + self.cur_pen = to_pen(nil, args.pen or COLOR_GREY) +end + +function Painter.new(rect, pen) + return Painter{ rect = rect, pen = pen } +end + +function Painter.new_view(view_rect, pen) + return Painter{ view_rect = view_rect, pen = pen } +end + +function Painter.new_xy(x1,y1,x2,y2,pen) + return Painter{ rect = mkdims_xy(x1,y1,x2,y2), pen = pen } +end + +function Painter.new_wh(x,y,w,h,pen) + return Painter{ rect = mkdims_wh(x,y,w,h), pen = pen } +end + +function Painter:isValidPos() + return self:inClipGlobalXY(self.x, self.y) +end + +function Painter:viewport(x,y,w,h) + local vp = ViewRect.viewport(x,y,w,h) + vp.cur_pen = self.cur_pen return mkinstance(Painter, vp):seek(0,0) end -function Painter:localX() +function Painter:cursor() + return self.x - self.x1, self.y - self.y1 +end + +function Painter:cursorX() return self.x - self.x1 end -function Painter:localY() +function Painter:cursorY() return self.y - self.y1 end @@ -219,25 +282,109 @@ function Painter:string(text,pen,...) end function Painter:key(code,pen,bg,...) - if type(code) == 'string' then - code = df.interface_key[code] - end return self:string( - dscreen.getKeyDisplay(code), + getKeyDisplay(code), pen or COLOR_LIGHTGREEN, bg or self.cur_pen.bg, ... ) end +-------------------------- +-- Abstract view object -- +-------------------------- + +View = defclass(View) + +View.ATTRS { + active = true, + hidden = false, +} + +function View:init(args) + self.subviews = {} +end + +function View:getWindowSize() + local rect = self.frame_body + return rect.width, rect.height +end + +function View:getMousePos() + local rect = self.frame_body + local x,y = dscreen.getMousePos() + if rect and rect:inClipGlobalXY(x,y) then + return rect:localXY(x,y) + end +end + +function View:computeFrame(parent_rect) + return mkdims_wh(0,0,parent_rect.width,parent_rect.height) +end + +function View:updateLayout(parent_rect) + if not parent_rect then + parent_rect = self.frame_parent_rect + else + self.frame_parent_rect = parent_rect + end + + local frame_rect,body_rect = self:computeFrame(parent_rect) + + self.frame_rect = frame_rect + self.frame_body = parent_rect:viewport(body_rect or frame_rect) + + for _,child in ipairs(self.subviews) do + child:updateLayout(frame_body) + end + + self:invoke_after('postUpdateLayout',frame_body) +end + +function View:renderSubviews(dc) + for _,child in ipairs(self.subviews) do + if not child.hidden then + child:render(dc) + end + end +end + +function View:render(dc) + local sub_dc = Painter{ + view_rect = self.frame_body, + clip_view = dc + } + + self:onRenderBody(sub_dc) + + self:renderSubviews(sub_dc) +end + +function View:onRenderBody(dc) +end + +function View:inputToSubviews(keys) + for _,child in ipairs(self.subviews) do + if child.active and child:onInput(keys) then + return true + end + end + + return false +end + +function View:onInput(keys) + return self:inputToSubviews(keys) +end + ------------------------ -- Base screen object -- ------------------------ -Screen = defclass(Screen) +Screen = defclass(Screen, View) Screen.text_input_mode = false function Screen:postinit() - self:updateLayout() + self:onResize(dscreen.getWindowSize()) end Screen.isDismissed = dscreen.isDismissed @@ -254,14 +401,6 @@ function Screen:invalidate() dscreen.invalidate() end -function Screen:getWindowSize() - return dscreen.getWindowSize() -end - -function Screen:getMousePos() - return dscreen.getMousePos() -end - function Screen:renderParent() if self._native and self._native.parent then self._native.parent:render() @@ -290,7 +429,7 @@ function Screen:onAboutToShow() end function Screen:onShow() - self:updateLayout() + self:onResize(dscreen.getWindowSize()) end function Screen:dismiss() @@ -306,11 +445,11 @@ function Screen:onDestroy() end function Screen:onResize(w,h) - self:updateLayout() + self:updateLayout(ViewRect{ rect = mkdims_wh(0,0,w,h) }) end -function Screen:updateLayout() - self:invoke_after('postUpdateLayout') +function Screen:onRender() + self:render(Painter.new()) end ------------------------ @@ -372,6 +511,7 @@ FramedScreen.ATTRS{ frame_title = DEFAULT_NIL, frame_width = DEFAULT_NIL, frame_height = DEFAULT_NIL, + frame_inset = 0, } local function hint_coord(gap,hint) @@ -388,52 +528,35 @@ function FramedScreen:getWantedFrameSize() return self.frame_width, self.frame_height end -function FramedScreen:updateFrameSize() - local sw, sh = dscreen.getWindowSize() - local iw, ih = sw-2, sh-2 +function FramedScreen:computeFrame(parent_rect) + local sw, sh = parent_rect.width, parent_rect.height + local ins = self.frame_inset + local thickness = 2+ins*2 + local iw, ih = sw-thickness, sh-thickness local fw, fh = self:getWantedFrameSize() local width = math.min(fw or iw, iw) local height = math.min(fh or ih, ih) local gw, gh = iw-width, ih-height local x1, y1 = hint_coord(gw,self.frame_xhint), hint_coord(gh,self.frame_yhint) - self.frame_rect = mkdims_wh(x1+1,y1+1,width,height) self.frame_opaque = (gw == 0 and gh == 0) + return mkdims_wh(x1,y1,width+thickness,height+thickness), + mkdims_wh(x1+1+ins,y1+1+ins,width,height) end -function FramedScreen:postUpdateLayout() - self:updateFrameSize() -end - -function FramedScreen:getWindowSize() - local rect = self.frame_rect - return rect.width, rect.height -end - -function FramedScreen:getMousePos() - local rect = self.frame_rect - local x,y = dscreen.getMousePos() - if is_in_rect(rect,x,y) then - return x-rect.x1, y-rect.y1 - end -end - -function FramedScreen:onRender() +function FramedScreen:render(dc) local rect = self.frame_rect - local x1,y1,x2,y2 = rect.x1-1, rect.y1-1, rect.x2+1, rect.y2+1 + local x1,y1,x2,y2 = rect.x1, rect.y1, rect.x2, rect.y2 if self.frame_opaque then - dscreen.clear() + dc:clear() else self:renderParent() - dscreen.fillRect(CLEAR_PEN,x1,y1,x2,y2) + dc:fill(rect, CLEAR_PEN) end paint_frame(x1,y1,x2,y2,self.frame_style,self.frame_title) - self:onRenderBody(Painter.new(rect)) -end - -function FramedScreen:onRenderBody(dc) + FramedScreen.super.render(self, dc) end return _ENV diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 553bb3a4b..75f7349d1 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -120,7 +120,7 @@ function InputBox:onRenderBody(dc) dc:newline(1) dc:pen(self.input_pen or COLOR_LIGHTCYAN) - dc:fill(1,dc:localY(),dc.width-2,dc:localY()) + dc:fill(1,dc:cursorY(),dc.width-2,dc:cursorY()) local cursor = '_' if math.floor(dfhack.getTickCount()/300) % 2 == 0 then diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index 125e71220..4056fbfcc 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -253,8 +253,9 @@ end DwarfOverlay = defclass(DwarfOverlay, gui.Screen) -function DwarfOverlay:postUpdateLayout() +function DwarfOverlay:updateLayout(parent_rect) self.df_layout = getPanelLayout() + DwarfOverlay.super.updateLayout(self, parent_rect) end function DwarfOverlay:getViewport(old_vp) @@ -352,12 +353,13 @@ end MenuOverlay = defclass(MenuOverlay, DwarfOverlay) -function MenuOverlay:postUpdateLayout() - self.frame_rect = self.df_layout.menu -end +MenuOverlay.ATTRS { + frame_inset = 0 +} -MenuOverlay.getWindowSize = gui.FramedScreen.getWindowSize -MenuOverlay.getMousePos = gui.FramedScreen.getMousePos +function MenuOverlay:computeFrame(parent_rect) + return self.df_layout.menu, gui.inset(self.df_layout.menu, self.frame_inset) +end function MenuOverlay:onAboutToShow(below) MenuOverlay.super.onAboutToShow(self,below) @@ -368,7 +370,7 @@ function MenuOverlay:onAboutToShow(below) end end -function MenuOverlay:onRender() +function MenuOverlay:render(dc) self:renderParent() local menu = self.df_layout.menu @@ -379,7 +381,7 @@ function MenuOverlay:onRender() menu.x1+1, menu.y2+1, "DFHack" ) - self:onRenderBody(gui.Painter.new(menu)) + MenuOverlay.super.render(self, dc) end end diff --git a/scripts/gui/guide-path.lua b/scripts/gui/guide-path.lua index f3e27d917..4d32a99c2 100644 --- a/scripts/gui/guide-path.lua +++ b/scripts/gui/guide-path.lua @@ -7,10 +7,6 @@ local dlg = require 'gui.dialogs' local tile_attrs = df.tiletype.attrs -function same_xyz(a,b) - return a and b and a.x == b.x and a.y == b.y and a.z == b.z -end - GuidePathUI = defclass(GuidePathUI, guidm.MenuOverlay) GuidePathUI.focus_path = 'guide-path' diff --git a/scripts/gui/hello-world.lua b/scripts/gui/hello-world.lua index c8cd3bd01..122f2cd38 100644 --- a/scripts/gui/hello-world.lua +++ b/scripts/gui/hello-world.lua @@ -7,12 +7,13 @@ local text = 'Woohoo, lua viewscreen :)' local screen = gui.FramedScreen{ frame_style = gui.GREY_LINE_FRAME, frame_title = 'Hello World', - frame_width = #text+6, - frame_height = 3, + frame_width = #text, + frame_height = 1, + frame_inset = 1, } function screen:onRenderBody(dc) - dc:seek(3,1):string(text, COLOR_LIGHTGREEN) + dc:string(text, COLOR_LIGHTGREEN) end function screen:onInput(keys) diff --git a/scripts/gui/siege-engine.lua b/scripts/gui/siege-engine.lua index c518a9cf8..9a5d76066 100644 --- a/scripts/gui/siege-engine.lua +++ b/scripts/gui/siege-engine.lua @@ -234,12 +234,12 @@ function SiegeEngine:onRenderBody_main(dc) if links then dc:key('CUSTOM_D'):string(": Delete, ") dc:key('CUSTOM_O'):string(": Zoom"):newline() - self:renderStockpiles(dc, links, bottom-2-dc:localY()) + self:renderStockpiles(dc, links, bottom-2-dc:cursorY()) dc:newline():newline() end local prof = self.building:getWorkshopProfile() or {} - dc:seek(1,math.max(dc:localY(),19)) + dc:seek(1,math.max(dc:cursorY(),19)) dc:key('CUSTOM_G'):key('CUSTOM_H'):key('CUSTOM_J'):key('CUSTOM_K') dc:string(': ') dc:string(df.skill_rating.attrs[prof.min_level or 0].caption):string('-') @@ -461,7 +461,7 @@ function SiegeEngine:onRenderBody(dc) self.mode.render(dc) - dc:seek(1, math.max(dc:localY(), 21)):pen(COLOR_WHITE) + dc:seek(1, math.max(dc:cursorY(), 21)):pen(COLOR_WHITE) dc:key('LEAVESCREEN'):string(": Back, ") dc:key('CUSTOM_C'):string(": Recenter") end From abfe2754fbf60199f116514a5d0498a8a90769a4 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 15 Oct 2012 20:03:18 +0400 Subject: [PATCH 092/114] Start implementing common widgets for lua screens. --- library/lua/gui.lua | 134 +++++++++++++++++++++++++--------- library/lua/gui/dialogs.lua | 43 ++++------- library/lua/gui/dwarfmode.lua | 9 ++- library/lua/gui/widgets.lua | 80 ++++++++++++++++++++ scripts/gui/guide-path.lua | 8 +- scripts/gui/power-meter.lua | 4 + 6 files changed, 209 insertions(+), 69 deletions(-) create mode 100644 library/lua/gui/widgets.lua diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 4f98e1bde..ea2a79da8 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -48,16 +48,74 @@ end function mkdims_wh(x1,y1,w,h) return { x1=x1, y1=y1, x2=x1+w-1, y2=y1+h-1, width=w, height=h } end -function inset(rect,dx1,dy1,dx2,dy2) - return mkdims_xy( - rect.x1+dx1, rect.y1+(dy1 or dx1), - rect.x2-(dx2 or dx1), rect.y2-(dy2 or dy1 or dx1) - ) -end function is_in_rect(rect,x,y) return x and y and x >= rect.x1 and x <= rect.x2 and y >= rect.y1 and y <= rect.y2 end +local function align_coord(gap,align,lv,rv) + if gap <= 0 then + return 0 + end + if not align then + if rv and not lv then + align = 1.0 + elseif lv and not rv then + align = 0.0 + else + align = 0.5 + end + end + return math.floor(gap*align) +end + +function compute_frame_rect(wavail,havail,spec,xgap,ygap) + if not spec then + return mkdims_wh(0,0,wavail,havail) + end + + local sw = wavail - (spec.l or 0) - (spec.r or 0) + local sh = havail - (spec.t or 0) - (spec.b or 0) + local rqw = math.min(sw, (spec.w or sw)+xgap) + local rqh = math.min(sh, (spec.h or sh)+ygap) + local ax = align_coord(sw - rqw, spec.xalign, spec.l, spec.r) + local ay = align_coord(sh - rqh, spec.yalign, spec.t, spec.b) + + local rect = mkdims_wh((spec.l or 0) + ax, (spec.t or 0) + ay, rqw, rqh) + rect.wgap = sw - rqw + rect.hgap = sh - rqh + return rect +end + +local function parse_inset(inset) + local l,r,t,b + if type(inset) == 'table' then + l,r = inset.l or inset.x, inset.r or inset.x + t,b = inset.t or inset.y, inset.b or inset.y + else + l = inset or 0 + t,r,b = l,l,l + end + return l,r,t,b +end + +function inset_frame(rect, inset, gap) + gap = gap or 0 + local l,t,r,b = parse_inset(inset) + return mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap) +end + +function compute_frame_body(wavail, havail, spec, inset, gap) + gap = gap or 0 + local l,t,r,b = parse_inset(inset) + local rect = compute_frame_rect(wavail, havail, spec, gap*2+l+r, gap*2+t+b) + local body = mkdims_xy(rect.x1+l+gap, rect.y1+t+gap, rect.x2-r-gap, rect.y2-b-gap) + return rect, body +end + +function blink_visible(delay) + return math.floor(dfhack.getTickCount()/delay) % 2 == 0 +end + local function to_pen(default, pen, bg, bold) if pen == nil then return default or {} @@ -296,13 +354,35 @@ View = defclass(View) View.ATTRS { active = true, - hidden = false, + visible = true, + view_id = DEFAULT_NIL, } function View:init(args) self.subviews = {} end +function View:addviews(list) + local sv = self.subviews + + for _,obj in ipairs(list) do + table.insert(sv, obj) + + local id = obj.view_id + if id and type(id) ~= 'number' and sv[id] == nil then + sv[id] = obj + end + end + + for _,dir in ipairs(list) do + for id,obj in pairs(dir) do + if id and type(id) ~= 'number' and sv[id] == nil then + sv[id] = obj + end + end + end +end + function View:getWindowSize() local rect = self.frame_body return rect.width, rect.height @@ -320,6 +400,12 @@ function View:computeFrame(parent_rect) return mkdims_wh(0,0,parent_rect.width,parent_rect.height) end +function View:updateSubviewLayout(frame_body) + for _,child in ipairs(self.subviews) do + child:updateLayout(frame_body) + end +end + function View:updateLayout(parent_rect) if not parent_rect then parent_rect = self.frame_parent_rect @@ -332,16 +418,14 @@ function View:updateLayout(parent_rect) self.frame_rect = frame_rect self.frame_body = parent_rect:viewport(body_rect or frame_rect) - for _,child in ipairs(self.subviews) do - child:updateLayout(frame_body) - end + self:updateSubviewLayout(self.frame_body) - self:invoke_after('postUpdateLayout',frame_body) + self:invoke_after('postUpdateLayout', self.frame_body) end function View:renderSubviews(dc) for _,child in ipairs(self.subviews) do - if not child.hidden then + if child.visible then child:render(dc) end end @@ -512,46 +596,28 @@ FramedScreen.ATTRS{ frame_width = DEFAULT_NIL, frame_height = DEFAULT_NIL, frame_inset = 0, + frame_background = CLEAR_PEN, } -local function hint_coord(gap,hint) - if hint and hint > 0 then - return math.min(hint,gap) - elseif hint and hint < 0 then - return math.max(0,gap-hint) - else - return math.floor(gap/2) - end -end - function FramedScreen:getWantedFrameSize() return self.frame_width, self.frame_height end function FramedScreen:computeFrame(parent_rect) local sw, sh = parent_rect.width, parent_rect.height - local ins = self.frame_inset - local thickness = 2+ins*2 - local iw, ih = sw-thickness, sh-thickness local fw, fh = self:getWantedFrameSize() - local width = math.min(fw or iw, iw) - local height = math.min(fh or ih, ih) - local gw, gh = iw-width, ih-height - local x1, y1 = hint_coord(gw,self.frame_xhint), hint_coord(gh,self.frame_yhint) - self.frame_opaque = (gw == 0 and gh == 0) - return mkdims_wh(x1,y1,width+thickness,height+thickness), - mkdims_wh(x1+1+ins,y1+1+ins,width,height) + return compute_frame_body(sw, sh, { w = fw, h = fh }, self.frame_inset, 1) end function FramedScreen:render(dc) local rect = self.frame_rect local x1,y1,x2,y2 = rect.x1, rect.y1, rect.x2, rect.y2 - if self.frame_opaque then + if rect.wgap <= 0 and rect.hgap <= 0 then dc:clear() else self:renderParent() - dc:fill(rect, CLEAR_PEN) + dc:fill(rect, self.frame_background) end paint_frame(x1,y1,x2,y2,self.frame_style,self.frame_title) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 75f7349d1..10a43c22e 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -3,6 +3,7 @@ local _ENV = mkmodule('gui.dialogs') local gui = require('gui') +local widgets = require('gui.widgets') local utils = require('utils') local dscreen = dfhack.screen @@ -101,8 +102,6 @@ InputBox = defclass(InputBox, MessageBox) InputBox.focus_path = 'InputBox' InputBox.ATTRS{ - input = '', - input_pen = DEFAULT_NIL, on_input = DEFAULT_NIL, } @@ -110,46 +109,36 @@ function InputBox:preinit(info) info.on_accept = nil end +function InputBox:init(info) + self:addviews{ + widgets.EditField{ + view_id = 'edit', + text = info.input, + text_pen = info.input_pen, + frame = { l = 1, r = 1, h = 1 }, + } + } +end + function InputBox:getWantedFrameSize() local mw, mh = InputBox.super.getWantedFrameSize(self) + self.subviews.edit.frame.t = mh return mw, mh+2 end -function InputBox:onRenderBody(dc) - InputBox.super.onRenderBody(self, dc) - - dc:newline(1) - dc:pen(self.input_pen or COLOR_LIGHTCYAN) - dc:fill(1,dc:cursorY(),dc.width-2,dc:cursorY()) - - local cursor = '_' - if math.floor(dfhack.getTickCount()/300) % 2 == 0 then - cursor = ' ' - end - local txt = self.input .. cursor - if #txt > dc.width-2 then - txt = string.char(27)..string.sub(txt, #txt-dc.width+4) - end - dc:string(txt) -end - function InputBox:onInput(keys) if keys.SELECT then self:dismiss() if self.on_input then - self.on_input(self.input) + self.on_input(self.subviews.edit.text) end elseif keys.LEAVESCREEN then self:dismiss() if self.on_cancel then self.on_cancel() end - elseif keys._STRING then - if keys._STRING == 0 then - self.input = string.sub(self.input, 1, #self.input-1) - else - self.input = self.input .. string.char(keys._STRING) - end + else + self:inputToSubviews(keys) end end diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index 4056fbfcc..0c1b0599c 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -354,11 +354,12 @@ end MenuOverlay = defclass(MenuOverlay, DwarfOverlay) MenuOverlay.ATTRS { - frame_inset = 0 + frame_inset = 0, + frame_background = CLEAR_PEN, } function MenuOverlay:computeFrame(parent_rect) - return self.df_layout.menu, gui.inset(self.df_layout.menu, self.frame_inset) + return self.df_layout.menu, gui.inset_frame(self.df_layout.menu, self.frame_inset) end function MenuOverlay:onAboutToShow(below) @@ -381,6 +382,10 @@ function MenuOverlay:render(dc) menu.x1+1, menu.y2+1, "DFHack" ) + if self.frame_background then + dc:fill(menu, self.frame_background) + end + MenuOverlay.super.render(self, dc) end end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua new file mode 100644 index 000000000..010ea5510 --- /dev/null +++ b/library/lua/gui/widgets.lua @@ -0,0 +1,80 @@ +-- Simple widgets for screens + +local _ENV = mkmodule('gui.widgets') + +local gui = require('gui') +local utils = require('utils') + +local dscreen = dfhack.screen + +------------ +-- Widget -- +------------ + +Widget = defclass(Widget, gui.View) + +Widget.ATTRS { + frame = DEFAULT_NIL, + frame_inset = DEFAULT_NIL, + frame_background = DEFAULT_NIL, +} + +function Widget:computeFrame(parent_rect) + local sw, sh = parent_rect.width, parent_rect.height + return gui.compute_frame_body(sw, sh, self.frame, self.frame_inset) +end + +function Widget:render(dc) + if self.frame_background then + dc:fill(self.frame_rect, self.frame_background) + end + + Widget.super.render(self, dc) +end + +---------------- +-- Edit field -- +---------------- + +EditField = defclass(EditField, Widget) + +EditField.ATTRS{ + text = '', + text_pen = DEFAULT_NIL, + on_char = DEFAULT_NIL, + on_change = DEFAULT_NIL, +} + +function EditField:onRenderBody(dc) + dc:pen(self.text_pen or COLOR_LIGHTCYAN):fill(0,0,dc.width-1,0) + + local cursor = '_' + if not self.active or gui.blink_visible(300) then + cursor = ' ' + end + local txt = self.text .. cursor + if #txt > dc.width then + txt = string.char(27)..string.sub(txt, #txt-dc.width+2) + end + dc:string(txt) +end + +function EditField:onInput(keys) + if keys._STRING then + local old = self.text + if keys._STRING == 0 then + self.text = string.sub(old, 1, #old-1) + else + local cv = string.char(keys._STRING) + if not self.on_char or self.on_char(cv, old) then + self.text = old .. cv + end + end + if self.on_change and self.text ~= old then + self.on_change(self.text, old) + end + return true + end +end + +return _ENV diff --git a/scripts/gui/guide-path.lua b/scripts/gui/guide-path.lua index 4d32a99c2..1546150b7 100644 --- a/scripts/gui/guide-path.lua +++ b/scripts/gui/guide-path.lua @@ -69,17 +69,13 @@ local function get_path_point(gpath,i) return xyz2pos(gpath.x[i], gpath.y[i], gpath.z[i]) end -local function blink_visible(delay) - return math.floor(dfhack.getTickCount()/delay) % 2 == 0 -end - function GuidePathUI:renderPath(cursor) local gpath = self.order.guide_path local startp = self.stop.pos local endp = self.next_stop.pos local vp = self:getViewport() local dc = gui.Painter.new(self.df_layout.map) - local visible = blink_visible(500) + local visible = gui.blink_visible(500) if visible then paintMapTile(dc, vp, cursor, endp, '+', COLOR_LIGHTGREEN) @@ -123,7 +119,7 @@ function GuidePathUI:renderPath(cursor) end end - if blink_visible(120) then + if gui.blink_visible(120) then paintMapTile(dc, vp, cursor, startp, 240, COLOR_LIGHTGREEN, COLOR_GREEN) end diff --git a/scripts/gui/power-meter.lua b/scripts/gui/power-meter.lua index 81b56e29f..ac30c9273 100644 --- a/scripts/gui/power-meter.lua +++ b/scripts/gui/power-meter.lua @@ -12,6 +12,10 @@ PowerMeter = defclass(PowerMeter, guidm.MenuOverlay) PowerMeter.focus_path = 'power-meter' +PowerMeter.ATTRS { + frame_background = false +} + function PowerMeter:init() self:assign{ min_power = 0, max_power = -1, invert = false, From d336abfd974c3a721bf71ba4be0156501877365c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 16 Oct 2012 14:18:35 +0400 Subject: [PATCH 093/114] Add label and list widgets, and switch stock dialogs to them. --- library/lua/dfhack.lua | 4 + library/lua/gui.lua | 18 +- library/lua/gui/dialogs.lua | 164 ++++---------- library/lua/gui/widgets.lua | 424 +++++++++++++++++++++++++++++++++++- library/lua/utils.lua | 18 ++ 5 files changed, 502 insertions(+), 126 deletions(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 9fc94a025..3f57e5722 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -125,6 +125,10 @@ end -- Misc functions +NEWLINE = "\n" +COMMA = "," +PERIOD = "." + function printall(table) local ok,f,t,k = pcall(pairs,table) if ok then diff --git a/library/lua/gui.lua b/library/lua/gui.lua index ea2a79da8..125b59544 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -116,7 +116,7 @@ function blink_visible(delay) return math.floor(dfhack.getTickCount()/delay) % 2 == 0 end -local function to_pen(default, pen, bg, bold) +function to_pen(default, pen, bg, bold) if pen == nil then return default or {} elseif type(pen) ~= 'table' then @@ -363,6 +363,8 @@ function View:init(args) end function View:addviews(list) + if not list then return end + local sv = self.subviews for _,obj in ipairs(list) do @@ -413,11 +415,15 @@ function View:updateLayout(parent_rect) self.frame_parent_rect = parent_rect end + self:invoke_before('preUpdateLayout', parent_rect) + local frame_rect,body_rect = self:computeFrame(parent_rect) self.frame_rect = frame_rect self.frame_body = parent_rect:viewport(body_rect or frame_rect) + self:invoke_after('postComputeFrame', self.frame_body) + self:updateSubviewLayout(self.frame_body) self:invoke_after('postUpdateLayout', self.frame_body) @@ -432,6 +438,8 @@ function View:renderSubviews(dc) end function View:render(dc) + self:onRenderFrame(dc, self.frame_rect) + local sub_dc = Painter{ view_rect = self.frame_body, clip_view = dc @@ -442,6 +450,9 @@ function View:render(dc) self:renderSubviews(sub_dc) end +function View:onRenderFrame(dc,rect) +end + function View:onRenderBody(dc) end @@ -609,8 +620,7 @@ function FramedScreen:computeFrame(parent_rect) return compute_frame_body(sw, sh, { w = fw, h = fh }, self.frame_inset, 1) end -function FramedScreen:render(dc) - local rect = self.frame_rect +function FramedScreen:onRenderFrame(dc, rect) local x1,y1,x2,y2 = rect.x1, rect.y1, rect.x2, rect.y2 if rect.wgap <= 0 and rect.hgap <= 0 then @@ -621,8 +631,6 @@ function FramedScreen:render(dc) end paint_frame(x1,y1,x2,y2,self.frame_style,self.frame_title) - - FramedScreen.super.render(self, dc) end return _ENV diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 10a43c22e..7d8058a93 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -14,47 +14,35 @@ MessageBox.focus_path = 'MessageBox' MessageBox.ATTRS{ frame_style = gui.GREY_LINE_FRAME, + frame_inset = 1, -- new attrs - text = {}, on_accept = DEFAULT_NIL, on_cancel = DEFAULT_NIL, on_close = DEFAULT_NIL, - text_pen = DEFAULT_NIL, } -function MessageBox:preinit(info) - if type(info.text) == 'string' then - info.text = utils.split_string(info.text, "\n") - end +function MessageBox:init(info) + self:addviews{ + widgets.Label{ + view_id = 'label', + text = info.text, + text_pen = info.text_pen, + frame = { l = 0, t = 0 }, + auto_height = true + } + } end function MessageBox:getWantedFrameSize() - local text = self.text - local w = #(self.frame_title or '') + 4 - w = math.max(w, 20) - w = math.max(self.frame_width or w, w) - for _, l in ipairs(text) do - w = math.max(w, #l) - end - local h = #text+1 - if h > 1 then - h = h+1 - end - return w+2, #text+2 + local label = self.subviews.label + local width = math.max(self.frame_width or 0, 20, #(self.frame_title or '') + 4) + return math.max(width, label:getTextWidth()), label:getTextHeight() end -function MessageBox:onRenderBody(dc) - if #self.text > 0 then - dc:newline(1):pen(self.text_pen or COLOR_GREY) - for _, l in ipairs(self.text or {}) do - dc:string(l):newline(1) - end - end - +function MessageBox:onRenderFrame(dc,rect) + MessageBox.super.onRenderFrame(self,dc,rect) if self.on_accept then - local fr = self.frame_rect - local dc2 = gui.Painter.new_xy(fr.x1+1,fr.y2+1,fr.x2-8,fr.y2+1) - dc2:key('LEAVESCREEN'):string('/'):key('MENU_CONFIRM') + dc:seek(rect.x1+2,rect.y2):key('LEAVESCREEN'):string('/'):key('MENU_CONFIRM') end end @@ -75,6 +63,8 @@ function MessageBox:onInput(keys) if self.on_cancel then self.on_cancel() end + else + self:inputToSubviews(keys) end end @@ -115,14 +105,14 @@ function InputBox:init(info) view_id = 'edit', text = info.input, text_pen = info.input_pen, - frame = { l = 1, r = 1, h = 1 }, + frame = { l = 0, r = 0, h = 1 }, } } end function InputBox:getWantedFrameSize() local mw, mh = InputBox.super.getWantedFrameSize(self) - self.subviews.edit.frame.t = mh + self.subviews.edit.frame.t = mh+1 return mw, mh+2 end @@ -165,107 +155,47 @@ ListBox.ATTRS{ on_select = DEFAULT_NIL } -function InputBox:preinit(info) +function ListBox:preinit(info) info.on_accept = nil end function ListBox:init(info) - self.page_top = 1 -end + local spen = gui.to_pen(COLOR_CYAN, info.select_pen, nil, false) + local cpen = gui.to_pen(COLOR_LIGHTCYAN, info.cursor_pen or info.select_pen, nil, true) -local function choice_text(entry) - if type(entry)=="table" then - return entry.caption or entry[1] - else - return entry - end + self:addviews{ + widgets.List{ + view_id = 'list', + selected = info.selected, + choices = info.choices, + text_pen = spen, + cursor_pen = cpen, + on_submit = function(sel,obj) + self:dismiss() + if self.on_select then self.on_select(sel, obj) end + local cb = obj.on_select or obj[2] + if cb then cb(obj, sel) end + end, + frame = { l = 0, r = 0 }, + } + } end function ListBox:getWantedFrameSize() - local mw, mh = ListBox.super.getWantedFrameSize(self) - for _,v in ipairs(self.choices) do - local text = choice_text(v) - mw = math.max(mw, #text+2) - end - return mw, mh+#self.choices+1 -end - -function ListBox:postUpdateLayout() - self.page_size = self.frame_rect.height - #self.text - 3 - self:moveCursor(0) -end - -function ListBox:moveCursor(delta) - local page = math.max(1, self.page_size) - local cnt = #self.choices - local off = self.selection+delta-1 - local ds = math.abs(delta) - - if ds > 1 then - if off >= cnt+ds-1 then - off = 0 - else - off = math.min(cnt-1, off) - end - if off <= -ds then - off = cnt-1 - else - off = math.max(0, off) - end - end - - self.selection = 1 + off % cnt - self.page_top = 1 + page * math.floor((self.selection-1) / page) -end - -function ListBox:onRenderBody(dc) - ListBox.super.onRenderBody(self, dc) - - dc:newline(1):pen(self.select_pen or COLOR_CYAN) - - local choices = self.choices - local iend = math.min(#choices, self.page_top+self.page_size-1) - - for i = self.page_top,iend do - local text = choice_text(choices[i]) - if text then - dc.cur_pen.bold = (i == self.selection); - dc:string(text) - else - dc:string('?ERROR?', COLOR_LIGHTRED) - end - dc:newline(1) - end + local mw, mh = InputBox.super.getWantedFrameSize(self) + local list = self.subviews.list + list.frame.t = mh+1 + return math.max(mw, list:getContentWidth()), mh+1+list:getContentHeight() end function ListBox:onInput(keys) - if keys.SELECT then - self:dismiss() - - local choice=self.choices[self.selection] - if self.on_select then - self.on_select(self.selection, choice) - end - - if choice then - local callback = choice.on_select or choice[2] - if callback then - callback(choice, self.selection) - end - end - elseif keys.LEAVESCREEN then + if keys.LEAVESCREEN then self:dismiss() if self.on_cancel then self.on_cancel() end - elseif keys.STANDARDSCROLL_UP then - self:moveCursor(-1) - elseif keys.STANDARDSCROLL_DOWN then - self:moveCursor(1) - elseif keys.STANDARDSCROLL_PAGEUP then - self:moveCursor(-self.page_size) - elseif keys.STANDARDSCROLL_PAGEDOWN then - self:moveCursor(self.page_size) + else + self:inputToSubviews(keys) end end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 010ea5510..f3796d0e3 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -7,6 +7,21 @@ local utils = require('utils') local dscreen = dfhack.screen +local function show_view(view,vis,act) + if view then + view.visible = vis + view.active = act + end +end + +local function getval(obj) + if type(obj) == 'function' then + return obj() + else + return obj + end +end + ------------ -- Widget -- ------------ @@ -24,12 +39,62 @@ function Widget:computeFrame(parent_rect) return gui.compute_frame_body(sw, sh, self.frame, self.frame_inset) end -function Widget:render(dc) +function Widget:onRenderFrame(dc, rect) if self.frame_background then - dc:fill(self.frame_rect, self.frame_background) + dc:fill(rect, self.frame_background) end +end + +----------- +-- Panel -- +----------- + +Panel = defclass(Panel, Widget) - Widget.super.render(self, dc) +Panel.ATTRS { + on_render = DEFAULT_NIL, +} + +function Panel:init(args) + self:addviews(args.subviews) +end + +function Panel:onRenderBody(dc) + if self.on_render then self.on_render(dc) end +end + +----------- +-- Pages -- +----------- + +Pages = defclass(Pages, Panel) + +function Pages:init(args) + for _,v in ipairs(self.subviews) do + show_view(v, false, false) + end + self:setSelected(args.selected or 1) +end + +function Pages:setSelected(idx) + if type(idx) ~= 'number' then + local key = idx + if type(idx) == 'string' then + key = self.subviews[key] + end + idx = utils.linear_index(self.subviews, key) + if not idx then + error('Unknown page: '..key) + end + end + + show_view(self.subviews[self.selected], false, false) + self.selected = math.min(math.max(1, idx), #self.subviews) + show_view(self.subviews[self.selected], true, true) +end + +function Pages:getSelected() + return self.selected, self.subviews[self.selected] end ---------------- @@ -43,6 +108,7 @@ EditField.ATTRS{ text_pen = DEFAULT_NIL, on_char = DEFAULT_NIL, on_change = DEFAULT_NIL, + on_submit = DEFAULT_NIL, } function EditField:onRenderBody(dc) @@ -60,7 +126,10 @@ function EditField:onRenderBody(dc) end function EditField:onInput(keys) - if keys._STRING then + if self.on_submit and keys.SELECT then + self.on_submit(self.text) + return true + elseif keys._STRING then local old = self.text if keys._STRING == 0 then self.text = string.sub(old, 1, #old-1) @@ -77,4 +146,351 @@ function EditField:onInput(keys) end end +----------- +-- Label -- +----------- + +function parse_label_text(obj) + local text = obj.text or {} + if type(text) ~= 'table' then + text = { text } + end + local curline = nil + local out = { } + local active = nil + local idtab = nil + for _,v in ipairs(text) do + local vv + if type(v) == 'string' then + vv = utils.split_string(v, NEWLINE) + else + vv = { v } + end + + for i = 1,#vv do + local cv = vv[i] + if i > 1 then + if not curline then + table.insert(out, {}) + end + curline = nil + end + if cv ~= '' then + if not curline then + curline = {} + table.insert(out, curline) + end + + if type(cv) == 'string' then + table.insert(curline, { text = cv }) + else + table.insert(curline, cv) + + if cv.on_activate then + active = active or {} + table.insert(active, cv) + end + + if cv.id then + idtab = idtab or {} + idtab[cv.id] = cv + end + end + end + end + end + obj.text_lines = out + obj.text_active = active + obj.text_ids = idtab +end + +function render_text(obj,dc,x0,y0,pen,dpen) + local width = 0 + for iline,line in ipairs(obj.text_lines) do + local x = 0 + if dc then + dc:seek(x+x0,y0+iline-1) + end + for _,token in ipairs(line) do + token.line = iline + token.x1 = x + + if token.gap then + x = x + token.gap + if dc then + dc:advance(token.gap) + end + end + + if token.text or token.key then + local text = getval(token.text) or '' + local keypen + + if dc then + if getval(token.disabled) then + dc:pen(getval(token.dpen) or dpen) + keypen = COLOR_GREEN + else + dc:pen(getval(token.pen) or pen) + keypen = COLOR_LIGHTGREEN + end + end + + x = x + #text + + if token.key then + local keystr = gui.getKeyDisplay(token.key) + local sep = token.key_sep or '' + + if sep == '()' then + if dc then + dc:string(text) + dc:string(' ('):string(keystr,keypen):string(')') + end + x = x + 3 + else + if dc then + dc:string(keystr,keypen):string(sep):string(text) + end + x = x + #sep + end + else + if dc then + dc:string(text) + end + end + end + + token.x2 = x + end + width = math.max(width, x) + end + obj.text_width = width +end + +function check_text_keys(self, keys) + if self.text_active then + for _,item in ipairs(self.text_active) do + if item.key and keys[item.key] and not getval(item.disabled) then + item.on_activate() + return true + end + end + end +end + +Label = defclass(Label, Widget) + +Label.ATTRS{ + text_pen = COLOR_WHITE, + text_dpen = COLOR_DARKGREY, + auto_height = true, +} + +function Label:init(args) + self:setText(args.text) +end + +function Label:setText(text) + self.text = text + parse_label_text(self) + + if self.auto_height then + self.frame = self.frame or {} + self.frame.h = self:getTextHeight() + end +end + +function Label:itemById(id) + if self.text_ids then + return self.text_ids[id] + end +end + +function Label:getTextHeight() + return #self.text_lines +end + +function Label:getTextWidth() + render_text(self) + return self.text_width +end + +function Label:onRenderBody(dc) + render_text(self,dc,0,0,self.text_pen,self.text_dpen) +end + +function Label:onInput(keys) + return check_text_keys(self, keys) +end + +---------- +-- List -- +---------- + +List = defclass(List, Widget) + +STANDARDSCROLL = { + STANDARDSCROLL_UP = -1, + STANDARDSCROLL_DOWN = 1, + STANDARDSCROLL_PAGEUP = '-page', + STANDARDSCROLL_PAGEDOWN = '+page', +} + +List.ATTRS{ + text_pen = COLOR_CYAN, + cursor_pen = COLOR_LIGHTCYAN, + cursor_dpen = DEFAULT_NIL, + inactive_pen = DEFAULT_NIL, + on_select = DEFAULT_NIL, + on_submit = DEFAULT_NIL, + row_height = 1, + scroll_keys = STANDARDSCROLL, +} + +function List:init(info) + self.page_top = 1 + self.page_size = 1 + self:setChoices(info.choices, info.selected) +end + +function List:setChoices(choices, selected) + self.choices = choices or {} + + for i,v in ipairs(self.choices) do + if type(v) ~= 'table' then + v = { text = v } + self.choices[i] = v + end + v.text = v.text or v.caption or v[1] + parse_label_text(v) + end + + self:setSelected(selected) +end + +function List:setSelected(selected) + self.selected = selected or self.selected or 1 + self:moveCursor(0, true) + return self.selected +end + +function List:getSelected() + return self.selected, self.choices[self.selected] +end + +function List:getContentWidth() + local width = 0 + for i,v in ipairs(self.choices) do + render_text(v) + local roww = v.text_width + if v.key then + roww = roww + 3 + #gui.getKeyDisplay(v.key) + end + width = math.max(width, roww) + end + return width +end + +function List:getContentHeight() + return #self.choices * self.row_height +end + +function List:postComputeFrame(body) + self.page_size = math.max(1, math.floor(body.height / self.row_height)) + self:moveCursor(0) +end + +function List:moveCursor(delta, force_cb) + local page = math.max(1, self.page_size) + local cnt = #self.choices + local off = self.selected+delta-1 + local ds = math.abs(delta) + + if ds > 1 then + if off >= cnt+ds-1 then + off = 0 + else + off = math.min(cnt-1, off) + end + if off <= -ds then + off = cnt-1 + else + off = math.max(0, off) + end + end + + self.selected = 1 + off % cnt + self.page_top = 1 + page * math.floor((self.selected-1) / page) + + if (force_cb or delta ~= 0) and self.on_select then + self.on_select(self:getSelected()) + end +end + +function List:onRenderBody(dc) + local choices = self.choices + local top = self.page_top + local iend = math.min(#choices, top+self.page_size-1) + + for i = top,iend do + local obj = choices[i] + local current = (i == self.selected) + local cur_pen = self.text_pen + local cur_dpen = cur_pen + + if current and active then + cur_pen = self.cursor_pen + cur_dpen = self.cursor_dpen or self.text_pen + elseif current then + cur_pen = self.inactive_pen or self.cursor_pen + cur_dpen = self.inactive_pen or self.text_pen + end + + local y = (i - top)*self.row_height + render_text(obj, dc, 0, y, cur_pen, cur_dpen) + + if obj.key then + local keystr = gui.getKeyDisplay(obj.key) + dc:seek(dc.width-2-#keystr,y):pen(self.text_pen) + dc:string('('):string(keystr,COLOR_LIGHTGREEN):string(')') + end + end +end + +function List:onInput(keys) + if self.on_submit and keys.SELECT then + self.on_submit(self:getSelected()) + return true + else + for k,v in pairs(self.scroll_keys) do + if keys[k] then + if v == '+page' then + v = self.page_size + elseif v == '-page' then + v = -self.page_size + end + + self:moveCursor(v) + return true + end + end + + for i,v in ipairs(self.choices) do + if v.key and keys[v.key] then + self:setSelected(i) + if self.on_submit then + self.on_submit(self:getSelected()) + end + return true + end + end + + local current = self.choices[self.selected] + if current then + return check_text_keys(current, keys) + end + end +end + return _ENV diff --git a/library/lua/utils.lua b/library/lua/utils.lua index b46363cd7..2507c9964 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -302,6 +302,24 @@ function sort_vector(vector,field,cmp) return vector end +-- Linear search + +function linear_index(vector,obj) + local min,max + if df.isvalid(vector) then + min,max = 0,#vector-1 + else + min,max = 1,#vector + end + for i=min,max do + if vector[i] == obj then + return i + end + end + return nil +end + + -- Binary search in a vector or lua table function binsearch(vector,key,field,cmp,min,max) if not(min and max) then From 023dc82564f9f896cffadfede284bb01d9fa7800 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 16 Oct 2012 18:33:00 +0400 Subject: [PATCH 094/114] Implement a material selection dialog. --- library/lua/class.lua | 14 +- library/lua/gui.lua | 4 +- library/lua/gui/materials.lua | 311 ++++++++++++++++++++++++++++++++++ library/lua/gui/script.lua | 12 ++ library/lua/gui/widgets.lua | 29 +++- 5 files changed, 361 insertions(+), 9 deletions(-) create mode 100644 library/lua/gui/materials.lua diff --git a/library/lua/class.lua b/library/lua/class.lua index 7b142e499..bcfff13e2 100644 --- a/library/lua/class.lua +++ b/library/lua/class.lua @@ -3,16 +3,16 @@ local _ENV = mkmodule('class') -- Metatable template for a class -class_obj = {} or class_obj +class_obj = class_obj or {} -- Methods shared by all classes -common_methods = {} or common_methods +common_methods = common_methods or {} -- Forbidden names for class fields and methods. reserved_names = { super = true, ATTRS = true } -- Attribute table metatable -attrs_meta = {} or attrs_meta +attrs_meta = attrs_meta or {} -- Create or updates a class; a class has metamethods and thus own metatable. function defclass(class,parent) @@ -133,6 +133,14 @@ function common_methods:callback(method, ...) return dfhack.curry(self[method], self, ...) end +function common_methods:cb_getfield(field) + return function() return self[field] end +end + +function common_methods:cb_setfield(field) + return function(val) self[field] = val end +end + function common_methods:assign(data) for k,v in pairs(data) do self[k] = v diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 125b59544..90eeb823a 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -377,7 +377,7 @@ function View:addviews(list) end for _,dir in ipairs(list) do - for id,obj in pairs(dir) do + for id,obj in pairs(dir.subviews) do if id and type(id) ~= 'number' and sv[id] == nil then sv[id] = obj end @@ -616,7 +616,7 @@ end function FramedScreen:computeFrame(parent_rect) local sw, sh = parent_rect.width, parent_rect.height - local fw, fh = self:getWantedFrameSize() + local fw, fh = self:getWantedFrameSize(parent_rect) return compute_frame_body(sw, sh, { w = fw, h = fh }, self.frame_inset, 1) end diff --git a/library/lua/gui/materials.lua b/library/lua/gui/materials.lua new file mode 100644 index 000000000..0cf3c21d9 --- /dev/null +++ b/library/lua/gui/materials.lua @@ -0,0 +1,311 @@ +-- Stock dialog for selecting materials + +local _ENV = mkmodule('gui.materials') + +local gui = require('gui') +local widgets = require('gui.widgets') +local utils = require('utils') + +ARROW = string.char(26) + +CREATURE_BASE = 19 +PLANT_BASE = 419 + +MaterialDialog = defclass(MaterialDialog, gui.FramedScreen) + +MaterialDialog.focus_path = 'MaterialDialog' + +MaterialDialog.ATTRS{ + prompt = 'Type or select a material from this list', + frame_style = gui.GREY_LINE_FRAME, + frame_inset = 1, + frame_title = 'Select Material', + -- new attrs + on_select = DEFAULT_NIL, + on_cancel = DEFAULT_NIL, + on_close = DEFAULT_NIL, +} + +function MaterialDialog:init(info) + self:addviews{ + widgets.Label{ + text = { + self.prompt, '\n\n', + 'Category: ', { text = self:cb_getfield('context_str'), pen = COLOR_CYAN } + }, + text_pen = COLOR_WHITE, + frame = { l = 0, t = 0 }, + }, + widgets.Label{ + view_id = 'back', + visible = false, + text = { { key = 'LEAVESCREEN', text = ': Back' } }, + frame = { r = 0, b = 0 }, + auto_width = true, + }, + widgets.List{ + view_id = 'list', + frame = { l = 0, r = 0, t = 6, b = 2 }, + on_submit = self:callback('onSubmitItem'), + }, + widgets.EditField{ + view_id = 'edit', + frame = { l = 2, t = 4 }, + on_change = self:callback('onFilterChoices'), + on_char = self:callback('onFilterChar'), + }, + widgets.Label{ + view_id = 'not_found', + text = 'No matching materials.', + text_pen = COLOR_LIGHTRED, + frame = { l = 2, r = 0, t = 6 }, + }, + widgets.Label{ + text = { { + key = 'SELECT', text = ': Select', + disabled = function() return self.subviews.not_found.visible end + } }, + frame = { l = 0, b = 0 }, + } + } + self:initBuiltinMode() +end + +function MaterialDialog:getWantedFrameSize(rect) + return math.max(40, #self.prompt), math.min(28, rect.height-8) +end + +function MaterialDialog:onDestroy() + if self.on_close then + self.on_close() + end +end + +function MaterialDialog:initBuiltinMode() + local choices = { + { text = ' none', mat_type = -1, mat_index = -1 }, + { text = ARROW..' inorganic', key = 'CUSTOM_SHIFT_I', + cb = self:callback('initInorganicMode') }, + { text = ARROW..' creature', key = 'CUSTOM_SHIFT_C', + cb = self:callback('initCreatureMode')}, + { text = ARROW..' plant', key = 'CUSTOM_SHIFT_P', + cb = self:callback('initPlantMode') }, + } + + self:addMaterials( + choices, + df.global.world.raws.mat_table.builtin, 0, -1, + df.builtin_mats._last_item + ) + + self:pushContext('Any material', choices) +end + +function MaterialDialog:initInorganicMode() + local choices = {} + + self:addMaterials( + choices, + df.global.world.raws.inorganics, 0, nil, + nil, 'material' + ) + + self:pushContext('Inorganic materials', choices) +end + +function MaterialDialog:initCreatureMode() + local choices = {} + + for i,v in ipairs(df.global.world.raws.creatures.all) do + self:addObjectChoice(choices, v, v.name[0], CREATURE_BASE, i) + end + + self:pushContext('Creature materials', choices) +end + +function MaterialDialog:initPlantMode() + local choices = {} + + for i,v in ipairs(df.global.world.raws.plants.all) do + self:addObjectChoice(choices, v, v.name, PLANT_BASE, i) + end + + self:pushContext('Plant materials', choices) +end + +function MaterialDialog:addObjectChoice(choices, obj, name, typ, index) + if #obj.material == 1 then + self:addMaterial(choices, obj.material[0], typ, index, true) + else + table.insert(choices, { + text = ARROW..' '..name, mat_type = typ, mat_index = index, + ctx = name, obj = obj, cb = self:callback('onSelectObj') + }) + end +end + +function MaterialDialog:onSelectObj(item) + local choices = {} + self:addMaterials(choices, item.obj.material, item.mat_type, item.mat_index) + self:pushContext(item.ctx, choices) +end + +function MaterialDialog:addMaterials(choices, vector, tid, index, maxid, field) + for i=0,(maxid or #vector-1) do + local mat = vector[i] + if mat and field then + mat = mat[field] + end + if mat then + local typ, idx + if index then + typ, idx = tid+i, index + else + typ, idx = tid, i + end + self:addMaterial(choices, mat, typ, idx) + end + end +end + +function MaterialDialog:addMaterial(choices, mat, typ, idx, pfix) + local state = 0 + if mat.heat.melting_point <= 10015 then + state = 1 + end + local name = mat.state_name[state] + name = string.gsub(name, '^frozen ','') + name = string.gsub(name, '^molten ','') + name = string.gsub(name, '^condensed ','') + local key + if pfix and mat.prefix ~= '' then + name = mat.prefix .. ' ' .. name + key = mat.prefix + end + table.insert(choices, { + text = ' '..name, + search_key = key, + material = mat, + mat_type = typ, mat_index = idx + }) +end + +function MaterialDialog:pushContext(name, choices) + if not self.back_stack then + self.back_stack = {} + self.subviews.back.visible = false + else + table.insert(self.back_stack, { + context_str = self.context_str, + all_choices = self.all_choices, + edit_text = self.subviews.edit.text, + choices = self.choices, + selected = self.subviews.list.selected, + }) + self.subviews.back.visible = true + end + + self.context_str = name + self.all_choices = choices + self.subviews.edit.text = '' + self:setChoices(choices, 1) +end + +function MaterialDialog:onGoBack() + local save = table.remove(self.back_stack) + self.subviews.back.visible = (#self.back_stack > 0) + + self.context_str = save.context_str + self.all_choices = save.all_choices + self.subviews.edit.text = save.edit_text + self:setChoices(save.choices, save.selected) +end + +function MaterialDialog:setChoices(choices, pos) + self.choices = choices + self.subviews.list:setChoices(self.choices, pos) + self.subviews.not_found.visible = (#self.choices == 0) +end + +function MaterialDialog:onFilterChoices(text) + local tokens = utils.split_string(text, ' ') + local choices = {} + + for i,v in ipairs(self.all_choices) do + local ok = true + local search_key = v.search_key or v.text + for _,key in ipairs(tokens) do + if key ~= '' and not string.match(search_key, '%f[^%s\x00]'..key) then + ok = false + break + end + end + if ok then + table.insert(choices, v) + end + end + + self:setChoices(choices) +end + +local bad_chars = { + ['%'] = true, ['.'] = true, ['+'] = true, ['*'] = true, + ['['] = true, [']'] = true, ['('] = true, [')'] = true, +} + +function MaterialDialog:onFilterChar(char, text) + if bad_chars[char] then + return false + end + if char == ' ' then + if #self.choices == 1 then + self.subviews.list:submit() + return false + end + return string.match(text, '%S$') + end + return true +end + +function MaterialDialog:submitMaterial(typ, index) + self:dismiss() + + if self.on_select then + local info = dfhack.matinfo.decode(typ, index) + self.on_select(info, typ, index) + end +end + +function MaterialDialog:onSubmitItem(idx, item) + if item.cb then + item:cb(idx) + else + self:submitMaterial(item.mat_type, item.mat_index) + end +end + +function MaterialDialog:onInput(keys) + if keys.LEAVESCREEN or keys.LEAVESCREEN_ALL then + if self.subviews.back.visible and not keys.LEAVESCREEN_ALL then + self:onGoBack() + else + self:dismiss() + if self.on_cancel then + self.on_cancel() + end + end + else + self:inputToSubviews(keys) + end +end + +function showMaterialPrompt(title, prompt, on_select, on_cancel) + MaterialDialog{ + frame_title = title, + prompt = prompt, + on_select = on_select, + on_cancel = on_cancel, + }:show() +end + +return _ENV diff --git a/library/lua/gui/script.lua b/library/lua/gui/script.lua index 021a4fa52..06079c426 100644 --- a/library/lua/gui/script.lua +++ b/library/lua/gui/script.lua @@ -148,4 +148,16 @@ function showListPrompt(title, text, tcolor, choices, min_width) return wait() end +function showMaterialPrompt(title, prompt) + require('gui.materials').MaterialDialog{ + frame_title = title, + prompt = prompt, + on_select = mkresume(true, + on_cancel = mkresume(false), + on_close = qresume(nil) + }:show() + + return wait() +end + return _ENV diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index f3796d0e3..eb2e8d05d 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -242,6 +242,8 @@ function render_text(obj,dc,x0,y0,pen,dpen) local keystr = gui.getKeyDisplay(token.key) local sep = token.key_sep or '' + x = x + #keystr + if sep == '()' then if dc then dc:string(text) @@ -285,6 +287,7 @@ Label.ATTRS{ text_pen = COLOR_WHITE, text_dpen = COLOR_DARKGREY, auto_height = true, + auto_width = false, } function Label:init(args) @@ -301,6 +304,13 @@ function Label:setText(text) end end +function Label:preUpdateLayout() + if self.auto_width then + self.frame = self.frame or {} + self.frame.w = self:getTextWidth() + end +end + function Label:itemById(id) if self.text_ids then return self.text_ids[id] @@ -404,6 +414,13 @@ end function List:moveCursor(delta, force_cb) local page = math.max(1, self.page_size) local cnt = #self.choices + + if cnt < 1 then + self.page_top = 1 + self.selected = 1 + return + end + local off = self.selected+delta-1 local ds = math.abs(delta) @@ -458,9 +475,15 @@ function List:onRenderBody(dc) end end +function List:submit() + if self.on_submit and #self.choices > 0 then + self.on_submit(self:getSelected()) + end +end + function List:onInput(keys) if self.on_submit and keys.SELECT then - self.on_submit(self:getSelected()) + self:submit() return true else for k,v in pairs(self.scroll_keys) do @@ -479,9 +502,7 @@ function List:onInput(keys) for i,v in ipairs(self.choices) do if v.key and keys[v.key] then self:setSelected(i) - if self.on_submit then - self.on_submit(self:getSelected()) - end + self:submit() return true end end From 7aae869d728dad27504518245028b5ec6105f98f Mon Sep 17 00:00:00 2001 From: Timothy Collett Date: Tue, 16 Oct 2012 15:58:34 -0400 Subject: [PATCH 095/114] Add OS X build instructions to the Compile file --- Compile.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Compile.rst b/Compile.rst index 8fca0e1f5..a71642060 100644 --- a/Compile.rst +++ b/Compile.rst @@ -63,6 +63,34 @@ extra options. You can also use a cmake-friendly IDE like KDevelop 4 or the cmake-gui program. +======== +Mac OS X +======== + +1. Download and unpack a copy of the latest DF +2. Install Xcode from Mac App Store +3. Open Xcode, go to Preferences > Downloads, and install the Command Line Tools. +4. Install MacPorts. +5. Install dependencies from MacPorts: + * sudo port install gcc45 +universal cmake +universal git-core +universal (This will take some time—maybe hours, depending on your machine.) + * At some point during this process, it may ask you to install a Java environment; let it do so. +6. Install perl dependencies + 1. sudo cpan (If this is the first time you've run cpan, you will need to go through the setup process. Just stick with the defaults for everything and you'll be fine.) + 2. install XML::LibXML + 3. install XML::LibXSLT +7. Get the dfhack source + 1. git clone https://github.com/danaris/dfhack.git + 2. cd dfhack + 3. git submodule init + 4. git submodule update +8. mkdir build-osx +9. cd build-osx +10. export CC=/opt/local/bin/gcc-mp-4.5 +11. export CXX=/opt/local/bin/g++-mp-4.5 +12. cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory +13. make +14. make install + ======= Windows ======= From 8fd1dd04bb72eecb8056f9b262a10e828c639bdd Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Tue, 16 Oct 2012 19:27:48 -0500 Subject: [PATCH 096/114] Display dfhack version number at end of baseline 'help' message. --- library/Core.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/Core.cpp b/library/Core.cpp index a8000070f..7766b3591 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -374,6 +374,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve " unload PLUGIN|all - Unload a plugin or all loaded plugins.\n" " reload PLUGIN|all - Reload a plugin or all loaded plugins.\n" ); + + con.print("\nDFHack version " DFHACK_VERSION ".\n"); } else if (parts.size() == 1) { From b14e4e97f543b5b2a107ce0a808f507c68716e41 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 17 Oct 2012 09:41:48 +0400 Subject: [PATCH 097/114] Natively support entry icons in the List widget. --- library/lua/gui/materials.lua | 17 +++++++++-------- library/lua/gui/widgets.lua | 23 +++++++++++++++++++++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/library/lua/gui/materials.lua b/library/lua/gui/materials.lua index 0cf3c21d9..17fcf2928 100644 --- a/library/lua/gui/materials.lua +++ b/library/lua/gui/materials.lua @@ -46,6 +46,7 @@ function MaterialDialog:init(info) widgets.List{ view_id = 'list', frame = { l = 0, r = 0, t = 6, b = 2 }, + icon_width = 2, on_submit = self:callback('onSubmitItem'), }, widgets.EditField{ @@ -83,12 +84,12 @@ end function MaterialDialog:initBuiltinMode() local choices = { - { text = ' none', mat_type = -1, mat_index = -1 }, - { text = ARROW..' inorganic', key = 'CUSTOM_SHIFT_I', + { text = 'none', mat_type = -1, mat_index = -1 }, + { icon = ARROW, text = 'inorganic', key = 'CUSTOM_SHIFT_I', cb = self:callback('initInorganicMode') }, - { text = ARROW..' creature', key = 'CUSTOM_SHIFT_C', + { icon = ARROW, text = 'creature', key = 'CUSTOM_SHIFT_C', cb = self:callback('initCreatureMode')}, - { text = ARROW..' plant', key = 'CUSTOM_SHIFT_P', + { icon = ARROW, text = 'plant', key = 'CUSTOM_SHIFT_P', cb = self:callback('initPlantMode') }, } @@ -138,8 +139,8 @@ function MaterialDialog:addObjectChoice(choices, obj, name, typ, index) self:addMaterial(choices, obj.material[0], typ, index, true) else table.insert(choices, { - text = ARROW..' '..name, mat_type = typ, mat_index = index, - ctx = name, obj = obj, cb = self:callback('onSelectObj') + icon = ARROW, text = name, mat_type = typ, mat_index = index, + obj = obj, cb = self:callback('onSelectObj') }) end end @@ -147,7 +148,7 @@ end function MaterialDialog:onSelectObj(item) local choices = {} self:addMaterials(choices, item.obj.material, item.mat_type, item.mat_index) - self:pushContext(item.ctx, choices) + self:pushContext(item.text, choices) end function MaterialDialog:addMaterials(choices, vector, tid, index, maxid, field) @@ -183,7 +184,7 @@ function MaterialDialog:addMaterial(choices, mat, typ, idx, pfix) key = mat.prefix end table.insert(choices, { - text = ' '..name, + text = name, search_key = key, material = mat, mat_type = typ, mat_index = idx diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index eb2e8d05d..16568aa16 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -222,6 +222,13 @@ function render_text(obj,dc,x0,y0,pen,dpen) end end + if token.tile then + x = x + 1 + if dc then + dc:char(nil, token.tile) + end + end + if token.text or token.key then local text = getval(token.text) or '' local keypen @@ -356,6 +363,7 @@ List.ATTRS{ on_submit = DEFAULT_NIL, row_height = 1, scroll_keys = STANDARDSCROLL, + icon_width = DEFAULT_NIL, } function List:init(info) @@ -399,7 +407,7 @@ function List:getContentWidth() end width = math.max(width, roww) end - return width + return width + (self.icon_width or 0) end function List:getContentHeight() @@ -449,6 +457,7 @@ function List:onRenderBody(dc) local choices = self.choices local top = self.page_top local iend = math.min(#choices, top+self.page_size-1) + local iw = self.icon_width for i = top,iend do local obj = choices[i] @@ -465,7 +474,17 @@ function List:onRenderBody(dc) end local y = (i - top)*self.row_height - render_text(obj, dc, 0, y, cur_pen, cur_dpen) + + if iw and obj.icon then + dc:seek(0, y) + if type(obj.icon) == 'table' then + dc:char(nil,obj.icon) + else + dc:string(obj.icon, obj.icon_pen or cur_pen) + end + end + + render_text(obj, dc, iw or 0, y, cur_pen, cur_dpen) if obj.key then local keystr = gui.getKeyDisplay(obj.key) From ad4f9908fb437a989f80ea6d334e0b44d5372c16 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 17 Oct 2012 10:41:50 +0400 Subject: [PATCH 098/114] Extract a generic filtered list widget from the materials dialog. --- library/lua/gui/dialogs.lua | 18 ++-- library/lua/gui/materials.lua | 81 +++--------------- library/lua/gui/script.lua | 3 +- library/lua/gui/widgets.lua | 149 ++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 78 deletions(-) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 7d8058a93..cb8d66176 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -149,8 +149,8 @@ ListBox = defclass(ListBox, MessageBox) ListBox.focus_path = 'ListBox' ListBox.ATTRS{ - selection = 1, - choices = {}, + with_filter = false, + cursor_pen = DEFAULT_NIL, select_pen = DEFAULT_NIL, on_select = DEFAULT_NIL } @@ -160,11 +160,16 @@ function ListBox:preinit(info) end function ListBox:init(info) - local spen = gui.to_pen(COLOR_CYAN, info.select_pen, nil, false) - local cpen = gui.to_pen(COLOR_LIGHTCYAN, info.cursor_pen or info.select_pen, nil, true) + local spen = gui.to_pen(COLOR_CYAN, self.select_pen, nil, false) + local cpen = gui.to_pen(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true) + + local list_widget = widgets.List + if self.with_filter then + list_widget = widgets.FilteredList + end self:addviews{ - widgets.List{ + list_widget{ view_id = 'list', selected = info.selected, choices = info.choices, @@ -199,7 +204,7 @@ function ListBox:onInput(keys) end end -function showListPrompt(title, text, tcolor, choices, on_select, on_cancel, min_width) +function showListPrompt(title, text, tcolor, choices, on_select, on_cancel, min_width, filter) ListBox{ frame_title = title, text = text, @@ -208,6 +213,7 @@ function showListPrompt(title, text, tcolor, choices, on_select, on_cancel, min_ on_select = on_select, on_cancel = on_cancel, frame_width = min_width, + with_filter = filter, }:show() end diff --git a/library/lua/gui/materials.lua b/library/lua/gui/materials.lua index 17fcf2928..e8f1e19ca 100644 --- a/library/lua/gui/materials.lua +++ b/library/lua/gui/materials.lua @@ -43,28 +43,17 @@ function MaterialDialog:init(info) frame = { r = 0, b = 0 }, auto_width = true, }, - widgets.List{ + widgets.FilteredList{ view_id = 'list', - frame = { l = 0, r = 0, t = 6, b = 2 }, + not_found_label = 'No matching materials', + frame = { l = 0, r = 0, t = 4, b = 2 }, icon_width = 2, on_submit = self:callback('onSubmitItem'), }, - widgets.EditField{ - view_id = 'edit', - frame = { l = 2, t = 4 }, - on_change = self:callback('onFilterChoices'), - on_char = self:callback('onFilterChar'), - }, - widgets.Label{ - view_id = 'not_found', - text = 'No matching materials.', - text_pen = COLOR_LIGHTRED, - frame = { l = 2, r = 0, t = 6 }, - }, widgets.Label{ text = { { key = 'SELECT', text = ': Select', - disabled = function() return self.subviews.not_found.visible end + disabled = function() return not self.subviews.list:canSubmit() end } }, frame = { l = 0, b = 0 }, } @@ -198,18 +187,15 @@ function MaterialDialog:pushContext(name, choices) else table.insert(self.back_stack, { context_str = self.context_str, - all_choices = self.all_choices, - edit_text = self.subviews.edit.text, - choices = self.choices, - selected = self.subviews.list.selected, + all_choices = self.subviews.list:getChoices(), + edit_text = self.subviews.list:getFilter(), + selected = self.subviews.list:getSelected(), }) self.subviews.back.visible = true end self.context_str = name - self.all_choices = choices - self.subviews.edit.text = '' - self:setChoices(choices, 1) + self.subviews.list:setChoices(choices, 1) end function MaterialDialog:onGoBack() @@ -217,55 +203,8 @@ function MaterialDialog:onGoBack() self.subviews.back.visible = (#self.back_stack > 0) self.context_str = save.context_str - self.all_choices = save.all_choices - self.subviews.edit.text = save.edit_text - self:setChoices(save.choices, save.selected) -end - -function MaterialDialog:setChoices(choices, pos) - self.choices = choices - self.subviews.list:setChoices(self.choices, pos) - self.subviews.not_found.visible = (#self.choices == 0) -end - -function MaterialDialog:onFilterChoices(text) - local tokens = utils.split_string(text, ' ') - local choices = {} - - for i,v in ipairs(self.all_choices) do - local ok = true - local search_key = v.search_key or v.text - for _,key in ipairs(tokens) do - if key ~= '' and not string.match(search_key, '%f[^%s\x00]'..key) then - ok = false - break - end - end - if ok then - table.insert(choices, v) - end - end - - self:setChoices(choices) -end - -local bad_chars = { - ['%'] = true, ['.'] = true, ['+'] = true, ['*'] = true, - ['['] = true, [']'] = true, ['('] = true, [')'] = true, -} - -function MaterialDialog:onFilterChar(char, text) - if bad_chars[char] then - return false - end - if char == ' ' then - if #self.choices == 1 then - self.subviews.list:submit() - return false - end - return string.match(text, '%S$') - end - return true + self.subviews.list:setChoices(save.all_choices) + self.subviews.list:setFilter(save.edit_text, save.selected) end function MaterialDialog:submitMaterial(typ, index) diff --git a/library/lua/gui/script.lua b/library/lua/gui/script.lua index 06079c426..e15f6c1b9 100644 --- a/library/lua/gui/script.lua +++ b/library/lua/gui/script.lua @@ -133,13 +133,14 @@ function showInputPrompt(title, text, tcolor, input, min_width) return wait() end -function showListPrompt(title, text, tcolor, choices, min_width) +function showListPrompt(title, text, tcolor, choices, min_width, filter) dlg.ListBox{ frame_title = title, text = text, text_pen = tcolor, choices = choices, frame_width = min_width, + with_filter = filter, on_select = mkresume(true), on_cancel = mkresume(false), on_close = qresume(nil) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 16568aa16..0a6d99ba6 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -22,6 +22,14 @@ local function getval(obj) end end +local function map_opttab(tab,idx) + if tab then + return tab[idx] + else + return idx + end +end + ------------ -- Widget -- ------------ @@ -393,6 +401,10 @@ function List:setSelected(selected) return self.selected end +function List:getChoices() + return self.choices +end + function List:getSelected() return self.selected, self.choices[self.selected] end @@ -533,4 +545,141 @@ function List:onInput(keys) end end +------------------- +-- Filtered List -- +------------------- + +FilteredList = defclass(FilteredList, Widget) + +function FilteredList:init(info) + self.edit = EditField{ + text_pen = info.cursor_pen, + frame = { l = info.icon_width, t = 0 }, + on_change = self:callback('onFilterChange'), + on_char = self:callback('onFilterChar'), + } + self.list = List{ + frame = { t = 2 }, + text_pen = info.text_pen, + cursor_pen = info.cursor_pen, + inactive_pen = info.inactive_pen, + row_height = info.row_height, + scroll_keys = info.scroll_keys, + icon_width = info.icon_width, + } + if info.on_select then + self.list.on_select = function() + return info.on_select(self:getSelected()) + end + end + if info.on_submit then + self.list.on_submit = function() + return info.on_submit(self:getSelected()) + end + end + self.not_found = Label{ + visible = false, + text = info.not_found_label or 'No matches', + text_pen = COLOR_LIGHTRED, + frame = { l = info.icon_width, t = 2 }, + } + self:addviews{ self.list, self.edit, self.not_found } + self:setChoices(info.choices, info.selected) +end + +function FilteredList:getChoices() + return self.choices +end + +function FilteredList:setChoices(choices, pos) + choices = choices or {} + self.choices = choices + self.edit.text = '' + self.list:setChoices(choices, pos) + self.not_found.visible = (#choices == 0) +end + +function FilteredList:submit() + return self.list:submit() +end + +function FilteredList:canSubmit() + return not self.not_found.visible +end + +function FilteredList:getSelected() + local i,v = self.list:getSelected() + return map_opttab(self.choice_index, i), v +end + +function FilteredList:getContentWidth() + return self.list:getContentWidth() +end + +function FilteredList:getContentHeight() + return self.list:getContentHeight() + 2 +end + +function FilteredList:getFilter() + return self.edit.text, self.list.choices +end + +function FilteredList:setFilter(filter, pos) + local choices = self.choices + local cidx = nil + + filter = filter or '' + self.edit.text = filter + + if filter ~= '' then + local tokens = utils.split_string(filter, ' ') + local ipos = pos + + choices = {} + cidx = {} + pos = nil + + for i,v in ipairs(self.choices) do + local ok = true + local search_key = v.search_key or v.text + for _,key in ipairs(tokens) do + if key ~= '' and not string.match(search_key, '%f[^%s\x00]'..key) then + ok = false + break + end + end + if ok then + table.insert(choices, v) + cidx[#choices] = i + if ipos == i then + pos = #choices + end + end + end + end + + self.choice_index = cidx + self.list:setChoices(choices, pos) + self.not_found.visible = (#choices == 0) +end + +function FilteredList:onFilterChange(text) + self:setFilter(text) +end + +local bad_chars = { + ['%'] = true, ['.'] = true, ['+'] = true, ['*'] = true, + ['['] = true, [']'] = true, ['('] = true, [')'] = true, +} + +function FilteredList:onFilterChar(char, text) + if bad_chars[char] then + return false + end + if char == ' ' then + return string.match(text, '%S$') + end + return true +end + return _ENV From 0046b093f761fb42f03fccb32a56efcf854c2b41 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 17 Oct 2012 11:49:11 +0400 Subject: [PATCH 099/114] Link visibility and event handling order. - Hidden widgets don't receive events. - Children handle events in top to bottom order. --- library/lua/gui.lua | 7 +++++-- library/lua/gui/widgets.lua | 11 +++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 90eeb823a..18b0d67d8 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -457,8 +457,11 @@ function View:onRenderBody(dc) end function View:inputToSubviews(keys) - for _,child in ipairs(self.subviews) do - if child.active and child:onInput(keys) then + local children = self.subviews + + for i=#children,1,-1 do + local child = children[i] + if child.visible and child.active and child:onInput(keys) then return true end end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 0a6d99ba6..05100c00b 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -7,10 +7,9 @@ local utils = require('utils') local dscreen = dfhack.screen -local function show_view(view,vis,act) +local function show_view(view,vis) if view then view.visible = vis - view.active = act end end @@ -79,7 +78,7 @@ Pages = defclass(Pages, Panel) function Pages:init(args) for _,v in ipairs(self.subviews) do - show_view(v, false, false) + v.visible = false end self:setSelected(args.selected or 1) end @@ -96,9 +95,9 @@ function Pages:setSelected(idx) end end - show_view(self.subviews[self.selected], false, false) + show_view(self.subviews[self.selected], false) self.selected = math.min(math.max(1, idx), #self.subviews) - show_view(self.subviews[self.selected], true, true) + show_view(self.subviews[self.selected], true) end function Pages:getSelected() @@ -583,7 +582,7 @@ function FilteredList:init(info) text_pen = COLOR_LIGHTRED, frame = { l = info.icon_width, t = 2 }, } - self:addviews{ self.list, self.edit, self.not_found } + self:addviews{ self.edit, self.list, self.not_found } self:setChoices(info.choices, info.selected) end From ffe70e9ee71731bf93f07a67ba70f37e774cec4e Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 17 Oct 2012 14:40:28 +0200 Subject: [PATCH 100/114] ruby: unregister onupdate callback on exception --- plugins/ruby/ruby.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 81b73b7ce..ab095e8d8 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -48,6 +48,7 @@ module DFHack end end rescue + df.onupdate_unregister self puts_err "onupdate cb #$!", $!.backtrace end From 5f4f540bfaf1dcff0c057b043c1c4e23cb7fd335 Mon Sep 17 00:00:00 2001 From: Quietust Date: Wed, 17 Oct 2012 07:57:14 -0500 Subject: [PATCH 101/114] Fix "fastdwarf" command to print syntax where appropriate, silence warning --- plugins/fastdwarf.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 28104b909..6292fc85e 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -42,7 +42,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) return CR_OK; } - df::map_block *old_block, *new_block; for (size_t i = 0; i < world->units.active.size(); i++) { df::unit *unit = world->units.active[i]; @@ -108,12 +107,10 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) static command_result fastdwarf (color_ostream &out, vector & parameters) { - if (parameters.size() > 2) { - out.print("Incorrect usage.\n"); - return CR_FAILURE; - } + if (parameters.size() > 2) + return CR_WRONG_USAGE; - if (parameters.size() <= 2) + if ((parameters.size() == 1) || (parameters.size() == 2)) { if (parameters.size() == 2) { @@ -122,10 +119,7 @@ static command_result fastdwarf (color_ostream &out, vector & parameter else if (parameters[1] == "1") enable_teledwarf = true; else - { - out.print("Incorrect usage.\n"); - return CR_FAILURE; - } + return CR_WRONG_USAGE; } else enable_teledwarf = false; @@ -155,10 +149,7 @@ static command_result fastdwarf (color_ostream &out, vector & parameter } } else - { - out.print("Incorrect usage.\n"); - return CR_FAILURE; - } + return CR_WRONG_USAGE; } out.print("Current state: fast = %d, teleport = %d.\n", From 4f7895f5717f074dd4f3058aa86fbfe88835aa51 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 17 Oct 2012 18:29:15 +0400 Subject: [PATCH 102/114] Support restricting the set of materials presented in the dialog. --- library/lua/gui/dialogs.lua | 1 + library/lua/gui/materials.lua | 103 +++++++++++++++++++++------------- 2 files changed, 66 insertions(+), 38 deletions(-) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index cb8d66176..079760604 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -173,6 +173,7 @@ function ListBox:init(info) view_id = 'list', selected = info.selected, choices = info.choices, + icon_width = info.icon_width, text_pen = spen, cursor_pen = cpen, on_submit = function(sel,obj) diff --git a/library/lua/gui/materials.lua b/library/lua/gui/materials.lua index e8f1e19ca..56b3eee5f 100644 --- a/library/lua/gui/materials.lua +++ b/library/lua/gui/materials.lua @@ -21,6 +21,11 @@ MaterialDialog.ATTRS{ frame_inset = 1, frame_title = 'Select Material', -- new attrs + none_caption = 'none', + use_inorganic = true, + use_creature = true, + use_plant = true, + mat_filter = DEFAULT_NIL, on_select = DEFAULT_NIL, on_cancel = DEFAULT_NIL, on_close = DEFAULT_NIL, @@ -73,20 +78,32 @@ end function MaterialDialog:initBuiltinMode() local choices = { - { text = 'none', mat_type = -1, mat_index = -1 }, - { icon = ARROW, text = 'inorganic', key = 'CUSTOM_SHIFT_I', - cb = self:callback('initInorganicMode') }, - { icon = ARROW, text = 'creature', key = 'CUSTOM_SHIFT_C', - cb = self:callback('initCreatureMode')}, - { icon = ARROW, text = 'plant', key = 'CUSTOM_SHIFT_P', - cb = self:callback('initPlantMode') }, + { text = self.none_caption, mat_type = -1, mat_index = -1 }, } - self:addMaterials( - choices, - df.global.world.raws.mat_table.builtin, 0, -1, - df.builtin_mats._last_item - ) + if self.use_inorganic then + table.insert(choices, { + icon = ARROW, text = 'inorganic', key = 'CUSTOM_SHIFT_I', + cb = self:callback('initInorganicMode') + }) + end + if self.use_creature then + table.insert(choices, { + icon = ARROW, text = 'creature', key = 'CUSTOM_SHIFT_C', + cb = self:callback('initCreatureMode') + }) + end + if self.use_plant then + table.insert(choices, { + icon = ARROW, text = 'plant', key = 'CUSTOM_SHIFT_P', + cb = self:callback('initPlantMode') + }) + end + + local table = df.global.world.raws.mat_table.builtin + for i=0,df.builtin_mats._last_item do + self:addMaterial(choices, table[i], i, -1, false, nil) + end self:pushContext('Any material', choices) end @@ -94,11 +111,9 @@ end function MaterialDialog:initInorganicMode() local choices = {} - self:addMaterials( - choices, - df.global.world.raws.inorganics, 0, nil, - nil, 'material' - ) + for i,mat in ipairs(df.global.world.raws.inorganics) do + self:addMaterial(choices, mat.material, 0, i, false, mat) + end self:pushContext('Inorganic materials', choices) end @@ -124,8 +139,26 @@ function MaterialDialog:initPlantMode() end function MaterialDialog:addObjectChoice(choices, obj, name, typ, index) - if #obj.material == 1 then - self:addMaterial(choices, obj.material[0], typ, index, true) + -- Check if any eligible children + local count = #obj.material + local idx = 0 + + if self.mat_filter then + count = 0 + for i,v in ipairs(obj.material) do + if self.mat_filter(v, obj, typ+i, index) then + count = count + 1 + if count > 1 then break end + idx = i + end + end + end + + -- Create an entry + if count < 1 then + return + elseif count == 1 then + self:addMaterial(choices, obj.material[idx], typ+idx, index, true, obj) else table.insert(choices, { icon = ARROW, text = name, mat_type = typ, mat_index = index, @@ -136,29 +169,19 @@ end function MaterialDialog:onSelectObj(item) local choices = {} - self:addMaterials(choices, item.obj.material, item.mat_type, item.mat_index) + for i,v in ipairs(item.obj.material) do + self:addMaterial(choices, v, item.mat_type+i, item.mat_index, false, item.obj) + end self:pushContext(item.text, choices) end -function MaterialDialog:addMaterials(choices, vector, tid, index, maxid, field) - for i=0,(maxid or #vector-1) do - local mat = vector[i] - if mat and field then - mat = mat[field] - end - if mat then - local typ, idx - if index then - typ, idx = tid+i, index - else - typ, idx = tid, i - end - self:addMaterial(choices, mat, typ, idx) - end +function MaterialDialog:addMaterial(choices, mat, typ, idx, pfix, parent) + -- Check the filter + if self.mat_filter and not self.mat_filter(mat, parent, typ, idx) then + return end -end -function MaterialDialog:addMaterial(choices, mat, typ, idx, pfix) + -- Find the material name local state = 0 if mat.heat.melting_point <= 10015 then state = 1 @@ -167,11 +190,14 @@ function MaterialDialog:addMaterial(choices, mat, typ, idx, pfix) name = string.gsub(name, '^frozen ','') name = string.gsub(name, '^molten ','') name = string.gsub(name, '^condensed ','') + + -- Add prefix if requested local key if pfix and mat.prefix ~= '' then name = mat.prefix .. ' ' .. name key = mat.prefix end + table.insert(choices, { text = name, search_key = key, @@ -239,10 +265,11 @@ function MaterialDialog:onInput(keys) end end -function showMaterialPrompt(title, prompt, on_select, on_cancel) +function showMaterialPrompt(title, prompt, on_select, on_cancel, mat_filter) MaterialDialog{ frame_title = title, prompt = prompt, + mat_filter = mat_filter, on_select = on_select, on_cancel = on_cancel, }:show() From 2d4935bc17039c63d9ffc229578f3d397ffc8b80 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 17 Oct 2012 19:16:18 +0400 Subject: [PATCH 103/114] Add lua API functions for verifying item and material vs job_item flags. --- Lua API.rst | 9 +++++++++ NEWS | 3 +++ library/LuaApi.cpp | 2 ++ library/include/modules/Job.h | 4 ++++ library/modules/Job.cpp | 26 ++++++++++++++++++++++++++ scripts/gui/guide-path.lua | 4 ++-- 6 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Lua API.rst b/Lua API.rst index bbee8646c..4d689ee2b 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -863,6 +863,15 @@ Job module if there are any jobs with ``first_id <= id < job_next_id``, a lua list containing them. +* ``dfhack.job.isSuitableItem(job_item, item_type, item_subtype)`` + + Does basic sanity checks to verify if the suggested item type matches + the flags in the job item. + +* ``dfhack.job.isSuitableMaterial(job_item, mat_type, mat_index)`` + + Likewise, if replacing material. + Units module ------------ diff --git a/NEWS b/NEWS index 40e9315c5..61e8a5b3e 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ DFHack future Internals: - support for displaying active keybindings properly. + - support for reusable widgets in lua screen library. Notable bugfixes: - autobutcher can be re-enabled again after being stopped. - stopped Dwarf Manipulator from unmasking vampires. @@ -9,6 +10,8 @@ DFHack future - fastdwarf: new mode using debug flags, and some internal consistency fixes. - added a small stand-alone utility for applying and removing binary patches. - removebadthoughts: add --dry-run option + New GUI scripts: + - gui/guide-path: displays the cached path for minecart Guide orders. DFHack v0.34.11-r2 diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index ef571bcb7..9f380c353 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -809,6 +809,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = { WRAPM(Job,getWorker), WRAPM(Job,checkBuildingsNow), WRAPM(Job,checkDesignationsNow), + WRAPM(Job,isSuitableItem), + WRAPM(Job,isSuitableMaterial), WRAPN(is_equal, jobEqual), WRAPN(is_item_equal, jobItemEqual), { NULL, NULL } diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index 490d79a34..853813073 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -32,6 +32,7 @@ distribution. #include "DataDefs.h" #include "df/job_item_ref.h" +#include "df/item_type.h" namespace df { @@ -69,6 +70,9 @@ namespace DFHack DFHACK_EXPORT bool attachJobItem(df::job *job, df::item *item, df::job_item_ref::T_role role, int filter_idx = -1, int insert_idx = -1); + + DFHACK_EXPORT bool isSuitableItem(df::job_item *item, df::item_type itype, int isubtype); + DFHACK_EXPORT bool isSuitableMaterial(df::job_item *item, int mat_type, int mat_index); } DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b); diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index df2009d0a..def3b4192 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -361,3 +361,29 @@ bool DFHack::Job::attachJobItem(df::job *job, df::item *item, return true; } + +bool Job::isSuitableItem(df::job_item *item, df::item_type itype, int isubtype) +{ + CHECK_NULL_POINTER(item); + + if (itype == item_type::NONE) + return true; + + ItemTypeInfo iinfo(itype, isubtype); + MaterialInfo minfo(item); + + return iinfo.isValid() && iinfo.matches(*item, &minfo); +} + +bool Job::isSuitableMaterial(df::job_item *item, int mat_type, int mat_index) +{ + CHECK_NULL_POINTER(item); + + if (mat_type == -1 && mat_index == -1) + return true; + + ItemTypeInfo iinfo(item); + MaterialInfo minfo(mat_type, mat_index); + + return minfo.isValid() && iinfo.matches(*item, &minfo); +} diff --git a/scripts/gui/guide-path.lua b/scripts/gui/guide-path.lua index 1546150b7..a807e032d 100644 --- a/scripts/gui/guide-path.lua +++ b/scripts/gui/guide-path.lua @@ -143,8 +143,8 @@ function GuidePathUI:onRenderBody(dc) end dc:newline():newline(1) - dc:key('CUSTOM_Z'):string(": Reset path, ",COLOR_GREY,nil,path_ok~=nil) - dc:key('CUSTOM_P'):string(": Find path",COLOR_GREY,nil,false) + dc:key('CUSTOM_Z'):string(": Reset path",COLOR_GREY,nil,path_ok~=nil) + --dc:key('CUSTOM_P'):string(": Find path",COLOR_GREY,nil,false) dc:newline(1) dc:key('CUSTOM_C'):string(": Zoom cur, ") dc:key('CUSTOM_N'):string(": Zoom next") From 2bbd00a8ecffa92eb20f50aa8f0f78dfe702ef36 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 17 Oct 2012 20:58:37 +0400 Subject: [PATCH 104/114] Add pairs and ipairs support for objects in df tree. --- library/LuaWrapper.cpp | 132 ++++++++++++++++++++++++++++++++++------- 1 file changed, 109 insertions(+), 23 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 3da79e932..75e3b697c 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -1265,6 +1265,51 @@ static void BuildTypeMetatable(lua_State *state, type_identity *type) * Recursive walk of scopes to construct the df... tree. */ +static int wtype_pnext(lua_State *L) +{ + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, lua_upvalueindex(1))) + return 2; + lua_pushnil(L); + return 1; +} + +static int wtype_pairs(lua_State *state) +{ + lua_pushvalue(state, lua_upvalueindex(1)); + lua_pushcclosure(state, wtype_pnext, 1); + lua_pushnil(state); + lua_pushnil(state); + return 3; +} + +static int wtype_inext(lua_State *L) +{ + int i = luaL_checkint(L, 2); + i++; /* next value */ + if (i <= lua_tointeger(L, lua_upvalueindex(2))) + { + lua_pushinteger(L, i); + lua_rawgeti(L, lua_upvalueindex(1), i); + return 2; + } + else + { + lua_pushnil(L); + return 1; + } +} + +static int wtype_ipairs(lua_State *state) +{ + lua_pushvalue(state, lua_upvalueindex(1)); + lua_pushvalue(state, lua_upvalueindex(3)); + lua_pushcclosure(state, wtype_inext, 2); + lua_pushnil(state); + lua_pushvalue(state, lua_upvalueindex(2)); + return 3; +} + static void RenderTypeChildren(lua_State *state, const std::vector &children); void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *name) @@ -1278,7 +1323,7 @@ void LuaWrapper::AssociateId(lua_State *state, int table, int val, const char *n lua_rawset(state, table); } -static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid) +static void FillEnumKeys(lua_State *state, int ix_meta, int ftable, enum_identity *eid) { const char *const *keys = eid->getKeys(); @@ -1296,11 +1341,17 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid) if (eid->getFirstItem() <= eid->getLastItem()) { + lua_pushvalue(state, base+1); + lua_pushinteger(state, eid->getFirstItem()-1); + lua_pushinteger(state, eid->getLastItem()); + lua_pushcclosure(state, wtype_ipairs, 3); + lua_setfield(state, ix_meta, "__ipairs"); + lua_pushinteger(state, eid->getFirstItem()); - lua_setfield(state, base+1, "_first_item"); + lua_setfield(state, ftable, "_first_item"); lua_pushinteger(state, eid->getLastItem()); - lua_setfield(state, base+1, "_last_item"); + lua_setfield(state, ftable, "_last_item"); } SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN); @@ -1321,7 +1372,7 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid) lua_setmetatable(state, ftable); } -static void FillBitfieldKeys(lua_State *state, int ftable, bitfield_identity *eid) +static void FillBitfieldKeys(lua_State *state, int ix_meta, int ftable, bitfield_identity *eid) { // Create a new table attached to ftable as __index lua_newtable(state); @@ -1338,11 +1389,17 @@ static void FillBitfieldKeys(lua_State *state, int ftable, bitfield_identity *ei i += bits[i].size-1; } + lua_pushvalue(state, base+1); + lua_pushinteger(state, -1); + lua_pushinteger(state, eid->getNumBits()-1); + lua_pushcclosure(state, wtype_ipairs, 3); + lua_setfield(state, ix_meta, "__ipairs"); + lua_pushinteger(state, 0); - lua_setfield(state, base+1, "_first_item"); + lua_setfield(state, ftable, "_first_item"); lua_pushinteger(state, eid->getNumBits()-1); - lua_setfield(state, base+1, "_last_item"); + lua_setfield(state, ftable, "_last_item"); SaveInTable(state, eid, &DFHACK_ENUM_TABLE_TOKEN); @@ -1355,7 +1412,12 @@ static void RenderType(lua_State *state, compound_identity *node) assert(node->getName()); std::string name = node->getFullName(); - int base = lua_gettop(state); + // Frame: + // base+1 - outer table + // base+2 - metatable of outer table + // base+3 - inner table + // base+4 - pairs table + Lua::StackUnwinder base(state); lua_newtable(state); if (!lua_checkstack(state, 20)) @@ -1365,51 +1427,59 @@ static void RenderType(lua_State *state, compound_identity *node) // metatable lua_newtable(state); + int ix_meta = base+2; lua_dup(state); lua_setmetatable(state, base+1); lua_pushstring(state, name.c_str()); - lua_setfield(state, base+2, "__metatable"); + lua_setfield(state, ix_meta, "__metatable"); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); - lua_setfield(state, base+2, "__tostring"); + lua_setfield(state, ix_meta, "__tostring"); lua_pushlightuserdata(state, node); - lua_rawsetp(state, base+2, &DFHACK_IDENTITY_FIELD_TOKEN); + lua_rawsetp(state, ix_meta, &DFHACK_IDENTITY_FIELD_TOKEN); // inner table lua_newtable(state); + int ftable = base+3; lua_dup(state); - lua_setfield(state, base+2, "__index"); + lua_setfield(state, ix_meta, "__index"); - int ftable = base+3; + // pairs table + lua_newtable(state); + int ptable = base+4; + + lua_pushvalue(state, ptable); + lua_pushcclosure(state, wtype_pairs, 1); + lua_setfield(state, ix_meta, "__pairs"); switch (node->type()) { case IDTYPE_STRUCT: lua_pushstring(state, "struct-type"); lua_setfield(state, ftable, "_kind"); - IndexStatics(state, base+2, base+3, (struct_identity*)node); + IndexStatics(state, ix_meta, ftable, (struct_identity*)node); break; case IDTYPE_CLASS: lua_pushstring(state, "class-type"); lua_setfield(state, ftable, "_kind"); - IndexStatics(state, base+2, base+3, (struct_identity*)node); + IndexStatics(state, ix_meta, ftable, (struct_identity*)node); break; case IDTYPE_ENUM: lua_pushstring(state, "enum-type"); lua_setfield(state, ftable, "_kind"); - FillEnumKeys(state, ftable, (enum_identity*)node); + FillEnumKeys(state, ix_meta, ftable, (enum_identity*)node); break; case IDTYPE_BITFIELD: lua_pushstring(state, "bitfield-type"); lua_setfield(state, ftable, "_kind"); - FillBitfieldKeys(state, ftable, (bitfield_identity*)node); + FillBitfieldKeys(state, ix_meta, ftable, (bitfield_identity*)node); break; case IDTYPE_GLOBAL: @@ -1425,14 +1495,14 @@ static void RenderType(lua_State *state, compound_identity *node) BuildTypeMetatable(state, node); lua_dup(state); - lua_setmetatable(state, base+3); + lua_setmetatable(state, ftable); lua_getfield(state, -1, "__newindex"); - lua_setfield(state, base+2, "__newindex"); + lua_setfield(state, ix_meta, "__newindex"); lua_getfield(state, -1, "__pairs"); - lua_setfield(state, base+2, "__pairs"); + lua_setfield(state, ix_meta, "__pairs"); - lua_pop(state, 3); + base += 1; return; } @@ -1454,17 +1524,25 @@ static void RenderType(lua_State *state, compound_identity *node) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME); lua_setfield(state, ftable, "is_instance"); - lua_pop(state, 2); + base += 1; } static void RenderTypeChildren(lua_State *state, const std::vector &children) { + // fieldtable pairstable | + int base = lua_gettop(state); + for (size_t i = 0; i < children.size(); i++) { RenderType(state, children[i]); lua_pushstring(state, children[i]->getName()); lua_swap(state); - lua_rawset(state, -3); + + // save in both tables + lua_pushvalue(state, -2); + lua_pushvalue(state, -2); + lua_rawset(state, base); + lua_rawset(state, base-1); } } @@ -1524,10 +1602,13 @@ static int DoAttach(lua_State *state) { // Assign df a metatable with read-only contents lua_newtable(state); + lua_newtable(state); // Render the type structure RenderTypeChildren(state, compound_identity::getTopScope()); + lua_swap(state); // -> pairstable fieldtable + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); lua_setfield(state, -2, "sizeof"); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); @@ -1558,7 +1639,12 @@ static int DoAttach(lua_State *state) lua_pushcfunction(state, meta_isnull); lua_setfield(state, -2, "isnull"); - freeze_table(state, false, "df"); + freeze_table(state, true, "df"); + + lua_swap(state); + lua_pushcclosure(state, wtype_pairs, 1); + lua_setfield(state, -2, "__pairs"); + lua_pop(state, 1); } return 1; From 1e2570f4cbbf8fe3ab0c3912418482e56f73a6cf Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 17 Oct 2012 21:42:06 +0400 Subject: [PATCH 105/114] Add an extremely simple item selection dialog by wrapping ListBox. --- library/lua/gui/dialogs.lua | 2 +- library/lua/gui/materials.lua | 66 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 079760604..5811e94e6 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -191,7 +191,7 @@ function ListBox:getWantedFrameSize() local mw, mh = InputBox.super.getWantedFrameSize(self) local list = self.subviews.list list.frame.t = mh+1 - return math.max(mw, list:getContentWidth()), mh+1+list:getContentHeight() + return math.max(mw, list:getContentWidth()), mh+1+math.min(20,list:getContentHeight()) end function ListBox:onInput(keys) diff --git a/library/lua/gui/materials.lua b/library/lua/gui/materials.lua index 56b3eee5f..baf641976 100644 --- a/library/lua/gui/materials.lua +++ b/library/lua/gui/materials.lua @@ -4,6 +4,7 @@ local _ENV = mkmodule('gui.materials') local gui = require('gui') local widgets = require('gui.widgets') +local dlg = require('gui.dialogs') local utils = require('utils') ARROW = string.char(26) @@ -275,4 +276,69 @@ function showMaterialPrompt(title, prompt, on_select, on_cancel, mat_filter) }:show() end +local itemdefs = df.global.world.raws.itemdefs +local itemtype_info = { + TRAPPARTS = { name = 'mechanisms' }, + WEAPON = { defs = itemdefs.weapons }, + TRAPCOMP = { defs = itemdefs.trapcomps }, + TOY = { defs = itemdefs.toys }, + TOOL = { defs = itemdefs.tools }, + INSTRUMENT = { defs = itemdefs.instruments }, + ARMOR = { defs = itemdefs.armor }, + AMMO = { defs = itemdefs.ammo }, + SIEGEAMMO = { defs = itemdefs.siege_ammo }, + GLOVES = { defs = itemdefs.gloves }, + SHOES = { defs = itemdefs.shoes }, + SHIELD = { defs = itemdefs.shields }, + HELM = { defs = itemdefs.helms }, + PANTS = { defs = itemdefs.pants }, + FOOD = { defs = itemdefs.food }, +} + +function ItemTypeDialog(args) + args.text = args.prompt or 'Type or select an item type' + args.text_pen = COLOR_WHITE + args.with_filter = true + args.icon_width = 2 + + local choices = { { + icon = '?', text = args.none_caption or 'none', item_type = -1, item_subtype = -1 + } } + local filter = args.item_filter + + for itype = 0,df.item_type._last_item do + local key = df.item_type[itype] + local info = itemtype_info[key] + + if not filter or filter(itype,-1) then + local name = key + local icon + if info and info.defs then + name = 'any '..name + icon = '+' + end + if info and info.name then + name = info.name + end + table.insert(choices, { + icon = icon, text = string.lower(name), item_type = itype, item_subtype = -1 + }) + end + + if info and info.defs then + for subtype,def in ipairs(info.defs) do + if not filter or filter(itype,subtype,def) then + table.insert(choices, { + icon = '\x1e', text = ' '..def.name, item_type = itype, item_subtype = subtype + }) + end + end + end + end + + args.choices = choices + + return dlg.ListBox(args) +end + return _ENV From 2b1d856214e30ff832e664141070a77b9a736c4c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 20 Oct 2012 17:06:33 +0400 Subject: [PATCH 106/114] Add a few utility functions to the lua api. --- Compile.html | 104 ++++++++++++++++++++++++-------- Compile.rst | 49 +++++++++------ Lua API.html | 33 +++++++++- Lua API.rst | 22 ++++++- Readme.html | 17 +++--- library/LuaApi.cpp | 25 ++++++++ library/include/modules/Items.h | 4 ++ library/modules/Items.cpp | 51 ++++++++++++---- library/xml | 2 +- 9 files changed, 244 insertions(+), 63 deletions(-) diff --git a/Compile.html b/Compile.html index 41d409112..e5f6ca679 100644 --- a/Compile.html +++ b/Compile.html @@ -334,20 +334,21 @@ ul.auto-toc {
              • Build
              • -
              • Windows
              • +
                +

                Mac OS X

                +
                  +
                1. Download and unpack a copy of the latest DF

                  +
                2. +
                3. Install Xcode from Mac App Store

                  +
                4. +
                5. Open Xcode, go to Preferences > Downloads, and install the Command Line Tools.

                  +
                6. +
                7. Install MacPorts.

                  +
                8. +
                9. Install dependencies from MacPorts:

                  +
                    +
                  • sudo port install gcc45 +universal cmake +universal git-core +universal

                    +

                    This will take some time—maybe hours, depending on your machine.

                    +
                  • +
                  • At some point during this process, it may ask you to install a Java environment; let it do so.

                    +
                  • +
                  +
                10. +
                11. Install perl dependencies

                  +
                  +
                    +
                  1. sudo cpan

                    +

                    If this is the first time you've run cpan, you will need to go through the setup +process. Just stick with the defaults for everything and you'll be fine.

                    +
                  2. +
                  3. install XML::LibXML

                    +
                  4. +
                  5. install XML::LibXSLT

                    +
                  6. +
                  +
                  +
                12. +
                13. Get the dfhack source:

                  +
                  +git clone https://github.com/danaris/dfhack.git
                  +cd dfhack
                  +git submodule init
                  +git submodule update
                  +
                  +
                14. +
                15. Build dfhack:

                  +
                  +mkdir build-osx
                  +cd build-osx
                  +export CC=/opt/local/bin/gcc-mp-4.5
                  +export CXX=/opt/local/bin/g++-mp-4.5
                  +cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory
                  +make
                  +make install
                  +
                  +
                16. +
                +
                -

                Windows

                +

                Windows

                On Windows, DFHack replaces the SDL library distributed with DF.

                -

                How to get the code

                +

                How to get the code

                DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. You will need some sort of Windows port of git, or a GUI. Some examples:

                @@ -428,7 +484,7 @@ git submodule update

                If you want to get really involved with the development, create an account on github, make a clone there and then use that as your remote repository instead. Detailed instructions are beyond the scope of this document. If you need help, join us on IRC (#dfhack channel on freenode).

                -

                Dependencies

                +

                Dependencies

                First, you need cmake. Get the win32 installer version from the official site: http://www.cmake.org/cmake/resources/software.html

                It has the usual installer wizard. Make sure you let it add its binary folder @@ -445,7 +501,7 @@ Grab it from Microsoft's site.

                If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.

                -

                Build

                +

                Build

                There are several different batch files in the build folder along with a script that's used for picking the DF path.

                First, run set_df_path.vbs and point the dialog that pops up at your DF folder that you want to use for development. Next, run one of the scripts with generate prefix. These create the MSVC solution file(s):

                @@ -467,7 +523,7 @@ So pick either Release or RelWithDebInfo build and build the INSTALL target.

                -

                Build types

                +

                Build types

                cmake allows you to pick a build type by changing this variable: CMAKE_BUILD_TYPE

                @@ -479,7 +535,7 @@ cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
                 'RelWithDebInfo'. 'Debug' is not available on Windows.

                -

                Using the library as a developer

                +

                Using the library as a developer

                Currently, the most direct way to use the library is to write a plugin that can be loaded by it. All the plugins can be found in the 'plugins' folder. There's no in-depth documentation on how to write one yet, but it should be easy enough to copy one and just follow the pattern.

                @@ -497,29 +553,29 @@ The main license is zlib/libpng, some bits are MIT licensed, and some are BSD li

                Feel free to add your own extensions and plugins. Contributing back to the dfhack repository is welcome and the right thing to do :)

                -

                DF data structure definitions

                +

                DF data structure definitions

                DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.

                Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.

                Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.

                -

                Remote access interface

                +

                Remote access interface

                DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The dfhack-run command uses this interface to invoke ordinary console commands.

                Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.

                Protocol client implementations exist for Java and C#.

                -

                Contributing to DFHack

                +

                Contributing to DFHack

                Several things should be kept in mind when contributing to DFHack.

                -

                Coding style

                +

                Coding style

                DFhack uses ANSI formatting and four spaces as indentation. Line endings are UNIX. The files use UTF-8 encoding. Code not following this won't make me happy, because I'll have to fix it. There's a good chance I'll make you fix it ;)

                -

                How to get new code into DFHack

                +

                How to get new code into DFHack

                You can send patches or make a clone of the github repo and ask me on the IRC channel to pull your code in. I'll review it and see if there are any problems. I'll fix them if they are minor.

                @@ -529,7 +585,7 @@ this is also a good place to dump new ideas and/or bugs that need fixing.

                -

                Memory research

                +

                Memory research

                If you want to do memory research, you'll need some tools and some knowledge. In general, you'll need a good memory viewer and optionally something to look at machine code without getting crazy :)

                diff --git a/Compile.rst b/Compile.rst index a71642060..3340fc97e 100644 --- a/Compile.rst +++ b/Compile.rst @@ -72,24 +72,39 @@ Mac OS X 3. Open Xcode, go to Preferences > Downloads, and install the Command Line Tools. 4. Install MacPorts. 5. Install dependencies from MacPorts: - * sudo port install gcc45 +universal cmake +universal git-core +universal (This will take some time—maybe hours, depending on your machine.) - * At some point during this process, it may ask you to install a Java environment; let it do so. + + * ``sudo port install gcc45 +universal cmake +universal git-core +universal`` + + This will take some time—maybe hours, depending on your machine. + + * At some point during this process, it may ask you to install a Java environment; let it do so. + 6. Install perl dependencies - 1. sudo cpan (If this is the first time you've run cpan, you will need to go through the setup process. Just stick with the defaults for everything and you'll be fine.) - 2. install XML::LibXML - 3. install XML::LibXSLT -7. Get the dfhack source - 1. git clone https://github.com/danaris/dfhack.git - 2. cd dfhack - 3. git submodule init - 4. git submodule update -8. mkdir build-osx -9. cd build-osx -10. export CC=/opt/local/bin/gcc-mp-4.5 -11. export CXX=/opt/local/bin/g++-mp-4.5 -12. cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory -13. make -14. make install + + 1. ``sudo cpan`` + + If this is the first time you've run cpan, you will need to go through the setup + process. Just stick with the defaults for everything and you'll be fine. + + 2. ``install XML::LibXML`` + 3. ``install XML::LibXSLT`` + +7. Get the dfhack source:: + + git clone https://github.com/danaris/dfhack.git + cd dfhack + git submodule init + git submodule update + +8. Build dfhack:: + + mkdir build-osx + cd build-osx + export CC=/opt/local/bin/gcc-mp-4.5 + export CXX=/opt/local/bin/g++-mp-4.5 + cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/path/to/DF/directory + make + make install ======= Windows diff --git a/Lua API.html b/Lua API.html index 125687110..9c18b6585 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1110,6 +1110,13 @@ above operations accordingly. If enabled, pauses and zooms to position.

                if there are any jobs with first_id <= id < job_next_id, a lua list containing them.

                +
              • dfhack.job.isSuitableItem(job_item, item_type, item_subtype)

                +

                Does basic sanity checks to verify if the suggested item type matches +the flags in the job item.

                +
              • +
              • dfhack.job.isSuitableMaterial(job_item, mat_type, mat_index)

                +

                Likewise, if replacing material.

                +
              • @@ -1250,6 +1257,15 @@ Returns false in case of error.

              • dfhack.items.makeProjectile(item)

                Turns the item into a projectile, and returns the new object, or nil if impossible.

              • +
              • dfhack.items.isCasteMaterial(item_type)

                +

                Returns true if this item type uses a creature/caste pair as its material.

                +
              • +
              • dfhack.items.getSubtypeCount(item_type)

                +

                Returns the number of raw-defined subtypes of the given item type, or -1 if not applicable.

                +
              • +
              • dfhack.items.getSubtypeDef(item_type, subtype)

                +

                Returns the raw definition for the given item type and subtype, or nil if invalid.

                +
              • @@ -1264,7 +1280,7 @@ Returns false in case of error.

              • dfhack.maps.getBlock(x,y,z)

                Returns a map block object for given x,y,z in local block coordinates.

              • -
              • dfhack.maps.isValidTilePos(coords), or isValidTilePos(x,y,z)``

                +
              • dfhack.maps.isValidTilePos(coords), or isValidTilePos(x,y,z)

                Checks if the given df::coord or x,y,z in local tile coordinates are valid.

              • dfhack.maps.getTileBlock(coords), or getTileBlock(x,y,z)

                @@ -1273,6 +1289,12 @@ Returns false in case of error.

              • dfhack.maps.ensureTileBlock(coords), or ensureTileBlock(x,y,z)

                Like getTileBlock, but if the block is not allocated, try creating it.

              • +
              • dfhack.maps.getTileType(coords), or getTileType(x,y,z)

                +

                Returns the tile type at the given coordinates, or nil if invalid.

                +
              • +
              • dfhack.maps.getTileFlags(coords), or getTileFlags(x,y,z)

                +

                Returns designation and occupancy references for the given coordinates, or nil, nil if invalid.

                +
              • dfhack.maps.getRegionBiome(region_coord2d), or getRegionBiome(x,y)

                Returns the biome info struct for the given global map region.

              • @@ -1838,6 +1860,15 @@ coordinates), returns nil.

              • xyz2pos(x,y,z)

                Returns a table with x, y and z as fields.

              • +
              • same_xyz(a,b)

                +

                Checks if a and b have the same x, y and z fields.

                +
              • +
              • get_path_xyz(path,i)

                +

                Returns path.x[i], path.y[i], path.z[i].

                +
              • +
              • pos2xy(obj), xy2pos(x,y), same_xy(a,b), get_path_xy(a,b)

                +

                Same as above, but for 2D coordinates.

                +
              • safe_index(obj,index...)

                Walks a sequence of dereferences, which may be represented by numbers or strings. Returns nil if any of obj or indices is nil, or a numeric index is out of array bounds.

                diff --git a/Lua API.rst b/Lua API.rst index 4d689ee2b..185672816 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -1043,6 +1043,18 @@ Items module Turns the item into a projectile, and returns the new object, or *nil* if impossible. +* ``dfhack.items.isCasteMaterial(item_type)`` + + Returns *true* if this item type uses a creature/caste pair as its material. + +* ``dfhack.items.getSubtypeCount(item_type)`` + + Returns the number of raw-defined subtypes of the given item type, or *-1* if not applicable. + +* ``dfhack.items.getSubtypeDef(item_type, subtype)`` + + Returns the raw definition for the given item type and subtype, or *nil* if invalid. + Maps module ----------- @@ -1059,7 +1071,7 @@ Maps module Returns a map block object for given x,y,z in local block coordinates. -* ``dfhack.maps.isValidTilePos(coords)``, or isValidTilePos(x,y,z)`` +* ``dfhack.maps.isValidTilePos(coords)``, or ``isValidTilePos(x,y,z)`` Checks if the given df::coord or x,y,z in local tile coordinates are valid. @@ -1071,6 +1083,14 @@ Maps module Like ``getTileBlock``, but if the block is not allocated, try creating it. +* ``dfhack.maps.getTileType(coords)``, or ``getTileType(x,y,z)`` + + Returns the tile type at the given coordinates, or *nil* if invalid. + +* ``dfhack.maps.getTileFlags(coords)``, or ``getTileFlags(x,y,z)`` + + Returns designation and occupancy references for the given coordinates, or *nil, nil* if invalid. + * ``dfhack.maps.getRegionBiome(region_coord2d)``, or ``getRegionBiome(x,y)`` Returns the biome info struct for the given global map region. diff --git a/Readme.html b/Readme.html index c1d2d0d3c..6df1afcad 100644 --- a/Readme.html +++ b/Readme.html @@ -2494,20 +2494,21 @@ growcrops plump 40

                removebadthoughts

                This script remove negative thoughts from your dwarves. Very useful against tantrum spirals.

                -

                With a selected unit in 'v' mode, will clear this unit's mind, otherwise -clear all your fort's units minds.

                +

                The script can target a single creature, when used with the him argument, +or the whole fort population, with all.

                +

                To show every bad thought present without actually removing them, run the +script with the -n or --dry-run argument. This can give a quick +hint on what bothers your dwarves the most.

                Individual dwarf happiness may not increase right after this command is run, -but in the short term your dwarves will get much more joyful. -The thoughts are set to be very old, and the game will remove them soon when -you unpause.

                -

                With the optional -v parameter, the script will dump the negative thoughts -it removed.

                +but in the short term your dwarves will get much more joyful.

                +

                Internals: the thoughts are set to be very old, so that the game remove them +quickly after you unpause.

              • slayrace

                Kills any unit of a given race.

                With no argument, lists the available races.

                -

                With the special argument 'him', targets only the selected creature.

                +

                With the special argument him, targets only the selected creature.

                Any non-dead non-caged unit of the specified race gets its blood_count set to 0, which means immediate death at the next game tick. For creatures such as vampires, also set animal.vanish_countdown to 2.

                diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 9f380c353..aa6c93c32 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -82,6 +82,7 @@ distribution. #include "df/flow_info.h" #include "df/unit_misc_trait.h" #include "df/proj_itemst.h" +#include "df/itemdef.h" #include #include @@ -937,6 +938,9 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = { WRAPM(Items, setOwner), WRAPM(Items, getContainer), WRAPM(Items, getDescription), + WRAPM(Items, isCasteMaterial), + WRAPM(Items, getSubtypeCount), + WRAPM(Items, getSubtypeDef), WRAPN(moveToGround, items_moveToGround), WRAPN(moveToContainer, items_moveToContainer), WRAPN(moveToBuilding, items_moveToBuilding), @@ -1018,6 +1022,25 @@ static int maps_ensureTileBlock(lua_State *L) return 1; } +static int maps_getTileType(lua_State *L) +{ + auto pos = CheckCoordXYZ(L, 1, true); + auto ptype = Maps::getTileType(pos); + if (ptype) + lua_pushinteger(L, *ptype); + else + lua_pushnil(L); + return 1; +} + +static int maps_getTileFlags(lua_State *L) +{ + auto pos = CheckCoordXYZ(L, 1, true); + Lua::PushDFObject(L, Maps::getTileDesignation(pos)); + Lua::PushDFObject(L, Maps::getTileOccupancy(pos)); + return 2; +} + static int maps_getRegionBiome(lua_State *L) { auto pos = CheckCoordXY(L, 1, true); @@ -1035,6 +1058,8 @@ static const luaL_Reg dfhack_maps_funcs[] = { { "isValidTilePos", maps_isValidTilePos }, { "getTileBlock", maps_getTileBlock }, { "ensureTileBlock", maps_ensureTileBlock }, + { "getTileType", maps_getTileType }, + { "getTileFlags", maps_getTileFlags }, { "getRegionBiome", maps_getRegionBiome }, { "getTileBiomeRgn", maps_getTileBiomeRgn }, { NULL, NULL } diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 367ce9b41..92329980a 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -123,6 +123,10 @@ struct dfh_item namespace Items { +DFHACK_EXPORT bool isCasteMaterial(df::item_type itype); +DFHACK_EXPORT int getSubtypeCount(df::item_type itype); +DFHACK_EXPORT df::itemdef *getSubtypeDef(df::item_type itype, int subtype); + /// Look for a particular item by ID DFHACK_EXPORT df::item * findItemByID(int32_t id); diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 1118afcfe..4e75c8a6b 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -109,30 +109,47 @@ using df::global::proj_next_id; ITEM(PANTS, pants, itemdef_pantsst) \ ITEM(FOOD, food, itemdef_foodst) -bool ItemTypeInfo::decode(df::item_type type_, int16_t subtype_) +int Items::getSubtypeCount(df::item_type itype) { using namespace df::enums::item_type; - type = type_; - subtype = subtype_; - custom = NULL; - df::world_raws::T_itemdefs &defs = df::global::world->raws.itemdefs; - switch (type_) { - case NONE: - return false; + switch (itype) { +#define ITEM(type,vec,tclass) \ + case type: \ + return defs.vec.size(); +ITEMDEF_VECTORS +#undef ITEM + + default: + return -1; + } +} + +df::itemdef *Items::getSubtypeDef(df::item_type itype, int subtype) +{ + using namespace df::enums::item_type; + df::world_raws::T_itemdefs &defs = df::global::world->raws.itemdefs; + + switch (itype) { #define ITEM(type,vec,tclass) \ case type: \ - custom = vector_get(defs.vec, subtype); \ - break; + return vector_get(defs.vec, subtype); ITEMDEF_VECTORS #undef ITEM default: - break; + return NULL; } +} + +bool ItemTypeInfo::decode(df::item_type type_, int16_t subtype_) +{ + type = type_; + subtype = subtype_; + custom = Items::getSubtypeDef(type_, subtype_); return isValid(); } @@ -171,6 +188,10 @@ ITEMDEF_VECTORS break; } + const char *name = ENUM_ATTR(item_type, caption, type); + if (name) + return name; + return toLower(ENUM_KEY_STR(item_type, type)); } @@ -219,6 +240,11 @@ ITEMDEF_VECTORS return (subtype >= 0); } +bool Items::isCasteMaterial(df::item_type itype) +{ + return ENUM_ATTR(item_type, is_caste_mat, itype); +} + bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat) { using namespace df::enums::item_type; @@ -226,6 +252,9 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat) if (!isValid()) return mat ? mat->matches(item) : false; + if (Items::isCasteMaterial(type) && mat && !mat->isNone()) + return false; + df::job_item_flags1 ok1, mask1, item_ok1, item_mask1; df::job_item_flags2 ok2, mask2, item_ok2, item_mask2; df::job_item_flags3 ok3, mask3, item_ok3, item_mask3; diff --git a/library/xml b/library/xml index aec106cdc..0ed2d1dc5 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit aec106cdc32083bbcc6d6dd27d9e069f5525ea92 +Subproject commit 0ed2d1dc5547737fbb838568fc66671098f2c11a From 5388ad475f85b162f4570bf1fbf410b620e5b553 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 20 Oct 2012 20:14:50 +0400 Subject: [PATCH 107/114] Fix a bug in lua wrapper caused by the recent pairs() addition. It inadvertently removed all functions like df.new from the df table. --- library/LuaWrapper.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 75e3b697c..1ce405c29 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -1641,10 +1641,13 @@ static int DoAttach(lua_State *state) freeze_table(state, true, "df"); - lua_swap(state); + // pairstable dftable dfmeta + + lua_pushvalue(state, -3); lua_pushcclosure(state, wtype_pairs, 1); lua_setfield(state, -2, "__pairs"); lua_pop(state, 1); + lua_remove(state, -2); } return 1; From 795961bfc4c1148a13483d739bda8fdf6923fb41 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 20 Oct 2012 20:31:45 +0400 Subject: [PATCH 108/114] Fix the treatment of non_economic in Materials/Items modules. non_economic == !(is boulder && is inorganic && is economic) --- library/modules/Items.cpp | 14 ++++++++++---- library/modules/Materials.cpp | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 4e75c8a6b..7af8c549b 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -256,11 +256,11 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat) return false; df::job_item_flags1 ok1, mask1, item_ok1, item_mask1; - df::job_item_flags2 ok2, mask2, item_ok2, item_mask2; + df::job_item_flags2 ok2, mask2, item_ok2, item_mask2, xmask2; df::job_item_flags3 ok3, mask3, item_ok3, item_mask3; ok1.whole = mask1.whole = item_ok1.whole = item_mask1.whole = 0; - ok2.whole = mask2.whole = item_ok2.whole = item_mask2.whole = 0; + ok2.whole = mask2.whole = item_ok2.whole = item_mask2.whole = xmask2.whole = 0; ok3.whole = mask3.whole = item_ok3.whole = item_mask3.whole = 0; if (mat) { @@ -281,12 +281,16 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat) RQ(1,not_bin); RQ(1,lye_bearing); RQ(2,dye); RQ(2,dyeable); RQ(2,dyed); RQ(2,glass_making); RQ(2,screw); - RQ(2,building_material); RQ(2,fire_safe); RQ(2,magma_safe); RQ(2,non_economic); + RQ(2,building_material); RQ(2,fire_safe); RQ(2,magma_safe); RQ(2,totemable); RQ(2,plaster_containing); RQ(2,body_part); RQ(2,lye_milk_free); RQ(2,blunt); RQ(2,unengraved); RQ(2,hair_wool); RQ(3,any_raw_material); RQ(3,non_pressed); RQ(3,food_storage); + // only checked if boulder + + xmask2.bits.non_economic = true; + // Compute the ok mask OK(1,solid); @@ -306,7 +310,7 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat) case BOULDER: OK(1,sharpenable); - OK(2,non_economic); + xmask2.bits.non_economic = false; case BAR: OK(3,any_raw_material); case BLOCKS: @@ -432,6 +436,8 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat) #undef OK #undef RQ + mask2.whole &= ~xmask2.whole; + return bits_match(item.flags1.whole, ok1.whole, mask1.whole) && bits_match(item.flags2.whole, ok2.whole, mask2.whole) && bits_match(item.flags3.whole, ok3.whole, mask3.whole) && diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index fbc441493..a94d49181 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -497,7 +497,7 @@ void MaterialInfo::getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &ma TEST(fire_safe, material->heat.melting_point > 11000); TEST(magma_safe, material->heat.melting_point > 12000); TEST(deep_material, FLAG(inorganic, inorganic_flags::SPECIAL)); - TEST(non_economic, inorganic && !(ui && ui->economic_stone[index])); + TEST(non_economic, !inorganic || !(ui && vector_get(ui->economic_stone, index))); TEST(plant, plant); TEST(silk, MAT_FLAG(SILK)); From 0c9f1e0af498678656e759725c25ee44ec17a31d Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 20 Oct 2012 21:01:22 +0400 Subject: [PATCH 109/114] Check the item type against job_item_vector_id when matching to jobs. This will prevent setting an invalid item type via the job command. --- library/include/modules/Items.h | 4 +++- library/modules/Items.cpp | 26 +++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 92329980a..f9871669a 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -40,6 +40,7 @@ distribution. #include "df/building_actual.h" #include "df/body_part_raw.h" #include "df/unit_inventory_item.h" +#include "df/job_item_vector_id.h" namespace df { @@ -86,7 +87,8 @@ namespace DFHack bool find(const std::string &token); - bool matches(const df::job_item &item, MaterialInfo *mat = NULL); + bool matches(df::job_item_vector_id vec_id); + bool matches(const df::job_item &item, MaterialInfo *mat = NULL, bool skip_vector = false); }; inline bool operator== (const ItemTypeInfo &a, const ItemTypeInfo &b) { diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 7af8c549b..0a78c727f 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -245,7 +245,28 @@ bool Items::isCasteMaterial(df::item_type itype) return ENUM_ATTR(item_type, is_caste_mat, itype); } -bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat) +bool ItemTypeInfo::matches(df::job_item_vector_id vec_id) +{ + auto other_id = ENUM_ATTR(job_item_vector_id, other, vec_id); + + auto explicit_item = ENUM_ATTR(items_other_id, item, other_id); + if (explicit_item != item_type::NONE && type != explicit_item) + return false; + + auto generic_item = ENUM_ATTR(items_other_id, generic_item, other_id); + if (generic_item.size > 0) + { + for (size_t i = 0; i < generic_item.size; i++) + if (generic_item.items[i] == type) + return true; + + return false; + } + + return true; +} + +bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat, bool skip_vector) { using namespace df::enums::item_type; @@ -255,6 +276,9 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat) if (Items::isCasteMaterial(type) && mat && !mat->isNone()) return false; + if (!skip_vector && !matches(item.vector_id)) + return false; + df::job_item_flags1 ok1, mask1, item_ok1, item_mask1; df::job_item_flags2 ok2, mask2, item_ok2, item_mask2, xmask2; df::job_item_flags3 ok3, mask3, item_ok3, item_mask3; From fbba4caab25b703a22157053f45ac627e64121b8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 20 Oct 2012 21:16:00 +0400 Subject: [PATCH 110/114] Suppress checking the cookable material flag if the item is a container. --- library/modules/Items.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 0a78c727f..cf99d9426 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -279,11 +279,11 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat, bool ski if (!skip_vector && !matches(item.vector_id)) return false; - df::job_item_flags1 ok1, mask1, item_ok1, item_mask1; + df::job_item_flags1 ok1, mask1, item_ok1, item_mask1, xmask1; df::job_item_flags2 ok2, mask2, item_ok2, item_mask2, xmask2; df::job_item_flags3 ok3, mask3, item_ok3, item_mask3; - ok1.whole = mask1.whole = item_ok1.whole = item_mask1.whole = 0; + ok1.whole = mask1.whole = item_ok1.whole = item_mask1.whole = xmask1.whole = 0; ok2.whole = mask2.whole = item_ok2.whole = item_mask2.whole = xmask2.whole = 0; ok3.whole = mask3.whole = item_ok3.whole = item_mask3.whole = 0; @@ -362,11 +362,13 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat, bool ski case CAGE: OK(1,milk); OK(1,milkable); + xmask1.bits.cookable = true; break; case BUCKET: case FLASK: OK(1,milk); + xmask1.bits.cookable = true; break; case TOOL: @@ -374,6 +376,7 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat, bool ski OK(1,milk); OK(2,lye_milk_free); OK(2,blunt); + xmask1.bits.cookable = true; if (VIRTUAL_CAST_VAR(def, df::itemdef_toolst, custom)) { df::tool_uses key(tool_uses::FOOD_STORAGE); @@ -389,11 +392,13 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat, bool ski OK(1,milk); OK(2,lye_milk_free); OK(3,food_storage); + xmask1.bits.cookable = true; break; case BOX: OK(1,bag); OK(1,sand_bearing); OK(1,milk); OK(2,dye); OK(2,plaster_containing); + xmask1.bits.cookable = true; break; case BIN: @@ -460,6 +465,7 @@ bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat, bool ski #undef OK #undef RQ + mask1.whole &= ~xmask1.whole; mask2.whole &= ~xmask2.whole; return bits_match(item.flags1.whole, ok1.whole, mask1.whole) && From dee0c97584607d39d0a263a0fdd9024786f0c6e3 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 20 Oct 2012 21:57:36 +0400 Subject: [PATCH 111/114] Add a gui script for viewing and changing job_item properties. --- NEWS | 1 + dfhack.init-example | 3 + library/lua/gui/materials.lua | 45 +++---- library/lua/gui/widgets.lua | 16 ++- library/xml | 2 +- scripts/gui/workshop-job.lua | 237 ++++++++++++++++++++++++++++++++++ 6 files changed, 271 insertions(+), 33 deletions(-) create mode 100644 scripts/gui/workshop-job.lua diff --git a/NEWS b/NEWS index 61e8a5b3e..5567258bc 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ DFHack future - removebadthoughts: add --dry-run option New GUI scripts: - gui/guide-path: displays the cached path for minecart Guide orders. + - gui/workshop-job: displays inputs of a workshop job and allows tweaking them. DFHack v0.34.11-r2 diff --git a/dfhack.init-example b/dfhack.init-example index 3ecbf4342..239d68a0f 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -75,6 +75,9 @@ keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons # minecart Guide path keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path +# workshop job details +# keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job + ############################ # UI and game logic tweaks # ############################ diff --git a/library/lua/gui/materials.lua b/library/lua/gui/materials.lua index baf641976..871c60014 100644 --- a/library/lua/gui/materials.lua +++ b/library/lua/gui/materials.lua @@ -238,8 +238,7 @@ function MaterialDialog:submitMaterial(typ, index) self:dismiss() if self.on_select then - local info = dfhack.matinfo.decode(typ, index) - self.on_select(info, typ, index) + self.on_select(typ, index) end end @@ -276,25 +275,6 @@ function showMaterialPrompt(title, prompt, on_select, on_cancel, mat_filter) }:show() end -local itemdefs = df.global.world.raws.itemdefs -local itemtype_info = { - TRAPPARTS = { name = 'mechanisms' }, - WEAPON = { defs = itemdefs.weapons }, - TRAPCOMP = { defs = itemdefs.trapcomps }, - TOY = { defs = itemdefs.toys }, - TOOL = { defs = itemdefs.tools }, - INSTRUMENT = { defs = itemdefs.instruments }, - ARMOR = { defs = itemdefs.armor }, - AMMO = { defs = itemdefs.ammo }, - SIEGEAMMO = { defs = itemdefs.siege_ammo }, - GLOVES = { defs = itemdefs.gloves }, - SHOES = { defs = itemdefs.shoes }, - SHIELD = { defs = itemdefs.shields }, - HELM = { defs = itemdefs.helms }, - PANTS = { defs = itemdefs.pants }, - FOOD = { defs = itemdefs.food }, -} - function ItemTypeDialog(args) args.text = args.prompt or 'Type or select an item type' args.text_pen = COLOR_WHITE @@ -307,26 +287,24 @@ function ItemTypeDialog(args) local filter = args.item_filter for itype = 0,df.item_type._last_item do - local key = df.item_type[itype] - local info = itemtype_info[key] + local attrs = df.item_type.attrs[itype] + local defcnt = dfhack.items.getSubtypeCount(itype) if not filter or filter(itype,-1) then - local name = key + local name = attrs.caption or df.item_type[itype] local icon - if info and info.defs then + if defcnt >= 0 then name = 'any '..name icon = '+' end - if info and info.name then - name = info.name - end table.insert(choices, { icon = icon, text = string.lower(name), item_type = itype, item_subtype = -1 }) end - if info and info.defs then - for subtype,def in ipairs(info.defs) do + if defcnt > 0 then + for subtype = 0,defcnt-1 do + local def = dfhack.items.getSubtypeDef(itype, subtype) if not filter or filter(itype,subtype,def) then table.insert(choices, { icon = '\x1e', text = ' '..def.name, item_type = itype, item_subtype = subtype @@ -338,6 +316,13 @@ function ItemTypeDialog(args) args.choices = choices + if args.on_select then + local cb = args.on_select + args.on_select = function(idx, obj) + return cb(obj.item_type, obj.item_subtype) + end + end + return dlg.ListBox(args) end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 05100c00b..e6a9a4d72 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -211,6 +211,11 @@ function parse_label_text(obj) obj.text_ids = idtab end +local function is_disabled(token) + return (token.disabled ~= nil and getval(token.disabled)) or + (token.enabled ~= nil and not getval(token.enabled)) +end + function render_text(obj,dc,x0,y0,pen,dpen) local width = 0 for iline,line in ipairs(obj.text_lines) do @@ -241,7 +246,7 @@ function render_text(obj,dc,x0,y0,pen,dpen) local keypen if dc then - if getval(token.disabled) then + if is_disabled(token) then dc:pen(getval(token.dpen) or dpen) keypen = COLOR_GREEN else @@ -287,7 +292,7 @@ end function check_text_keys(self, keys) if self.text_active then for _,item in ipairs(self.text_active) do - if item.key and keys[item.key] and not getval(item.disabled) then + if item.key and keys[item.key] and not is_disabled(item) then item.on_activate() return true end @@ -361,6 +366,13 @@ STANDARDSCROLL = { STANDARDSCROLL_PAGEDOWN = '+page', } +SECONDSCROLL = { + SECONDSCROLL_UP = -1, + SECONDSCROLL_DOWN = 1, + SECONDSCROLL_PAGEUP = '-page', + SECONDSCROLL_PAGEDOWN = '+page', +} + List.ATTRS{ text_pen = COLOR_CYAN, cursor_pen = COLOR_LIGHTCYAN, diff --git a/library/xml b/library/xml index 0ed2d1dc5..b9b2e8c6d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0ed2d1dc5547737fbb838568fc66671098f2c11a +Subproject commit b9b2e8c6d2141f13966ed965b3f3ffe924e527db diff --git a/scripts/gui/workshop-job.lua b/scripts/gui/workshop-job.lua new file mode 100644 index 000000000..973d5cc17 --- /dev/null +++ b/scripts/gui/workshop-job.lua @@ -0,0 +1,237 @@ +-- Show and modify properties of jobs in a workshop. + +local utils = require 'utils' +local gui = require 'gui' +local guidm = require 'gui.dwarfmode' +local guimat = require 'gui.materials' +local widgets = require 'gui.widgets' +local dlg = require 'gui.dialogs' + +JobDetails = defclass(JobDetails, guidm.MenuOverlay) + +JobDetails.focus_path = 'workshop-job' + +JobDetails.ATTRS { + job = DEFAULT_NIL, + frame_inset = 1, + frame_background = COLOR_BLACK, +} + +function JobDetails:init(args) + local status = { text = 'No worker', pen = COLOR_DARKGREY } + local worker = dfhack.job.getWorker(self.job) + if self.job.flags.suspend then + status = { text = 'Suspended', pen = COLOR_RED } + elseif worker then + status = { text = dfhack.TranslateName(dfhack.unit.getVisibleName(worker)), pen = COLOR_GREEN } + end + + self:addviews{ + widgets.Label{ + frame = { l = 0, t = 0 }, + text = { + { text = df.job_type.attrs[self.job.job_type].caption }, NEWLINE, NEWLINE, + ' ', status + } + }, + widgets.Label{ + frame = { l = 0, t = 4 }, + text = { + { key = 'CUSTOM_I', text = ': Input item, ', + enabled = self:callback('canChangeIType'), + on_activate = self:callback('onChangeIType') }, + { key = 'CUSTOM_M', text = ': Material', + enabled = self:callback('canChangeMat'), + on_activate = self:callback('onChangeMat') } + } + }, + widgets.List{ + view_id = 'list', + frame = { t = 6, b = 2 }, + row_height = 4, + scroll_keys = widgets.SECONDSCROLL, + }, + widgets.Label{ + frame = { l = 0, b = 0 }, + text = { + { key = 'LEAVESCREEN', text = ': Back', + on_activate = self:callback('dismiss') } + } + }, + } + + self:initListChoices() +end + +function describe_item_type(iobj) + local itemline = 'any item' + if iobj.item_type >= 0 then + itemline = df.item_type.attrs[iobj.item_type].caption or iobj.item_type + local def = dfhack.items.getSubtypeDef(iobj.item_type, iobj.item_subtype) + local count = dfhack.items.getSubtypeCount(iobj.item_type, iobj.item_subtype) + if def then + itemline = def.name + elseif count >= 0 then + itemline = 'any '..itemline + end + end + return itemline +end + +function is_caste_mat(iobj) + if iobj.item_type >= 0 then + return df.item_type.attrs[iobj.item_type].is_caste_mat + end + return false +end + +function describe_material(iobj) + local matline = 'any material' + if is_caste_mat(iobj) then + matline = 'material not applicable' + elseif iobj.mat_type >= 0 then + local info = dfhack.matinfo.decode(iobj.mat_type, iobj.mat_index) + if info then + matline = info:toString() + else + matline = iobj.mat_type..':'..iobj.mat_index + end + end + return matline +end + +function list_flags(list, bitfield) + for name,val in pairs(bitfield) do + if val then + table.insert(list, name) + end + end +end + +function JobDetails:initListChoices() + local items = {} + for i,ref in ipairs(self.job.items) do + local idx = ref.job_item_idx + if idx >= 0 then + items[idx] = (items[idx] or 0) + 1 + end + end + + local choices = {} + for i,iobj in ipairs(self.job.job_items) do + local head = 'Item '..(i+1)..': '..(items[idx] or 0)..' of '..iobj.quantity + if iobj.min_dimension > 0 then + head = head .. '(size '..iobj.min_dimension..')' + end + + local line1 = {} + local reaction = df.reaction.find(iobj.reaction_id) + if reaction and #iobj.contains > 0 then + for _,ri in ipairs(iobj.contains) do + table.insert(line1, 'has '..utils.call_with_string( + reaction.reagents[ri],'getDescription',iobj.reaction_id + )) + end + end + if iobj.metal_ore >= 0 then + local ore = dfhack.matinfo.decode(0, iobj.metal_ore) + if ore then + table.insert(line1, 'ore of '..ore:toString()) + end + end + if iobj.has_material_reaction_product ~= '' then + table.insert(line1, 'product '..iobj.has_material_reaction_product) + end + if iobj.reaction_class ~= '' then + table.insert(line1, 'class '..iobj.reaction_class) + end + if iobj.has_tool_use >= 0 then + table.insert(line1, 'has use '..df.tool_uses[iobj.has_tool_use]) + end + list_flags(line1, iobj.flags1) + list_flags(line1, iobj.flags2) + list_flags(line1, iobj.flags3) + if #line1 == 0 then + table.insert(line1, 'no flags') + end + + table.insert(choices, { + index = i, + iobj = iobj, + text = { + head, NEWLINE, + ' ', { text = curry(describe_item_type, iobj) }, NEWLINE, + ' ', { text = curry(describe_material, iobj) }, NEWLINE, + ' ', table.concat(line1, ', '), NEWLINE + } + }) + end + + self.subviews.list:setChoices(choices) +end + +function JobDetails:canChangeIType() + local idx, obj = self.subviews.list:getSelected() + return obj ~= nil +end + +function JobDetails:onChangeIType() + local idx, obj = self.subviews.list:getSelected() + guimat.ItemTypeDialog{ + prompt = 'Please select a new item type for input '..idx, + none_caption = 'any item', + item_filter = curry(dfhack.job.isSuitableItem, obj.iobj), + on_select = function(item_type, item_subtype) + obj.iobj.item_type = item_type + obj.iobj.item_subtype = item_subtype + end + }:show() +end + +function JobDetails:canChangeMat() + local idx, obj = self.subviews.list:getSelected() + return obj ~= nil and not is_caste_mat(obj.iobj) +end + +function JobDetails:onChangeMat() + local idx, obj = self.subviews.list:getSelected() + + if obj.iobj.item_type == -1 and obj.iobj.mat_type == -1 then + dlg.showMessage( + 'Bug Alert', + { 'Please set a specific item type first.\n\n', + 'Otherwise the material will be matched\n', + 'incorrectly due to a limitation in DF code.' }, + COLOR_YELLOW + ) + return + end + + guimat.MaterialDialog{ + prompt = 'Please select a new material for input '..idx, + none_caption = 'any material', + mat_filter = function(mat,parent,mat_type,mat_index) + return dfhack.job.isSuitableMaterial(obj.iobj, mat_type, mat_index) + end, + on_select = function(mat_type, mat_index) + if idx == 1 + and self.job.mat_type == obj.iobj.mat_type + and self.job.mat_index == obj.iobj.mat_index + and self.job.job_type ~= df.job_type.PrepareMeal + then + self.job.mat_type = mat_type + self.job.mat_index = mat_index + end + + obj.iobj.mat_type = mat_type + obj.iobj.mat_index = mat_index + end + }:show() +end + +if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Workshop/Job') then + qerror("This script requires a workshop job selected in the 'q' mode") +end + +local dlg = JobDetails{ job = dfhack.gui.getSelectedJob() } +dlg:show() From 1f7a01d6854ff528f7ffadb4fa31c2495ab83c9c Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 20 Oct 2012 22:35:39 +0200 Subject: [PATCH 112/114] follow rename unit.military.squad_index to squad_id in df-structures --- library/RemoteTools.cpp | 4 ++-- plugins/lua/sort/units.lua | 6 +++--- plugins/ruby/unit.rb | 2 +- plugins/tweak.cpp | 2 +- scripts/siren.lua | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index b371d60fa..718d1b181 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -292,9 +292,9 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit, if (!unit->custom_profession.empty()) info->set_custom_profession(unit->custom_profession); - if (unit->military.squad_index >= 0) + if (unit->military.squad_id >= 0) { - info->set_squad_id(unit->military.squad_index); + info->set_squad_id(unit->military.squad_id); info->set_squad_position(unit->military.squad_position); } } diff --git a/plugins/lua/sort/units.lua b/plugins/lua/sort/units.lua index d8ae83a38..c124e52d9 100644 --- a/plugins/lua/sort/units.lua +++ b/plugins/lua/sort/units.lua @@ -93,7 +93,7 @@ orders.race = { orders.squad = { key = function(unit) - local sidx = unit.military.squad_index + local sidx = unit.military.squad_id if sidx >= 0 then return sidx end @@ -102,7 +102,7 @@ orders.squad = { orders.squad_position = { key = function(unit) - local sidx = unit.military.squad_index + local sidx = unit.military.squad_id if sidx >= 0 then return sidx * 1000 + unit.military.squad_position end @@ -115,4 +115,4 @@ orders.happiness = { end } -return _ENV \ No newline at end of file +return _ENV diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb index 139a375b2..4fbf75d8d 100644 --- a/plugins/ruby/unit.rb +++ b/plugins/ruby/unit.rb @@ -92,7 +92,7 @@ module DFHack # filter 'attend meeting' not u.specific_refs.find { |s| s.type == :ACTIVITY } and # filter soldiers (TODO check schedule) - u.military.squad_index == -1 and + u.military.squad_id == -1 and # filter 'on break' not u.status.misc_traits.find { |t| t.id == :OnBreak } end diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 8488ba3e8..b4af2a4ba 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -640,7 +640,7 @@ struct military_assign_hook : df::viewscreen_layer_militaryst { for (int y = y1, i = i1; i <= i2; i++, y++) { auto unit = vector_get(positions.candidates, i); - if (!unit || unit->military.squad_index < 0) + if (!unit || unit->military.squad_id < 0) continue; for (int x = x1; x <= x2; x++) diff --git a/scripts/siren.lua b/scripts/siren.lua index 30d3aa07f..4fc574ed0 100644 --- a/scripts/siren.lua +++ b/scripts/siren.lua @@ -89,7 +89,7 @@ end for _,v in ipairs(df.global.world.units.active) do local x,y,z = dfhack.units.getPosition(v) if x and dfhack.units.isCitizen(v) and is_in_burrows(xyz2pos(x,y,z)) then - if not in_siege and v.military.squad_index < 0 then + if not in_siege and v.military.squad_id < 0 then add_thought(v, df.unit_thought_type.LackProtection) end wake_unit(v) From 46938625fdedbb23bf7b7ebd57bc77c21758dcf2 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 21 Oct 2012 21:45:51 +0400 Subject: [PATCH 113/114] Pass the hotkey keys to dwarfmode from overlays, and fix gui/workshop-job. --- dfhack.init-example | 2 +- library/lua/gui/dwarfmode.lua | 48 +++++++++++++++++++----- scripts/gui/workshop-job.lua | 69 ++++++++++++++++++++++++----------- 3 files changed, 87 insertions(+), 32 deletions(-) diff --git a/dfhack.init-example b/dfhack.init-example index 239d68a0f..27801b73d 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -76,7 +76,7 @@ keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path # workshop job details -# keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job +keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job ############################ # UI and game logic tweaks # diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index 0c1b0599c..cef52ae24 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -238,6 +238,19 @@ local function get_movement_delta(key, delta, big_step) end end +HOTKEY_KEYS = {} + +for i,v in ipairs(df.global.ui.main.hotkeys) do + HOTKEY_KEYS['D_HOTKEY'..(i+1)] = v +end + +local function get_hotkey_target(key) + local hk = HOTKEY_KEYS[key] + if hk and hk.cmd == df.ui_hotkey.T_cmd.Zoom then + return xyz2pos(hk.x, hk.y, hk.z) + end +end + function Viewport:scrollByKey(key) local dx, dy, dz = get_movement_delta(key, 10, 20) if dx then @@ -247,7 +260,12 @@ function Viewport:scrollByKey(key) self.z + dz ) else - return self + local hk_pos = get_hotkey_target(key) + if hk_pos then + return self:centerOn(hk_pos) + else + return self + end end end @@ -286,9 +304,11 @@ function DwarfOverlay:selectBuilding(building,cursor,viewport,gap) end function DwarfOverlay:propagateMoveKeys(keys) - for code,_ in pairs(MOVEMENT_KEYS) do - if keys[code] then - self:sendInputToParent(code) + for code,_ in pairs(keys) do + if MOVEMENT_KEYS[code] or HOTKEY_KEYS[code] then + if not HOTKEY_KEYS[code] or get_hotkey_target(code) then + self:sendInputToParent(code) + end return code end end @@ -305,8 +325,8 @@ function DwarfOverlay:simulateViewScroll(keys, anchor, no_clip_cursor) return 'A_MOVE_SAME_SQUARE' end - for code,_ in pairs(MOVEMENT_KEYS) do - if keys[code] then + for code,_ in pairs(keys) do + if MOVEMENT_KEYS[code] or HOTKEY_KEYS[code] then local vp = self:getViewport():scrollByKey(code) if (cursor and not no_clip_cursor) or no_clip_cursor == false then vp = vp:reveal(anchor,4,20,4,true) @@ -329,16 +349,26 @@ function DwarfOverlay:simulateCursorMovement(keys, anchor) return 'A_MOVE_SAME_SQUARE' end - for code,_ in pairs(MOVEMENT_KEYS) do - if keys[code] then + for code,_ in pairs(keys) do + if MOVEMENT_KEYS[code] then local dx, dy, dz = get_movement_delta(code, 1, 10) local ncur = xyz2pos(cx+dx, cy+dy, cz+dz) if dfhack.maps.isValidTilePos(ncur) then setCursorPos(ncur) self:getViewport():reveal(ncur,4,10,6,true):set() - return code end + + return code + elseif HOTKEY_KEYS[code] then + local pos = get_hotkey_target(code) + + if pos and dfhack.maps.isValidTilePos(pos) then + setCursorPos(pos) + self:getViewport():centerOn(pos):set() + end + + return code end end end diff --git a/scripts/gui/workshop-job.lua b/scripts/gui/workshop-job.lua index 973d5cc17..74e33595f 100644 --- a/scripts/gui/workshop-job.lua +++ b/scripts/gui/workshop-job.lua @@ -18,12 +18,14 @@ JobDetails.ATTRS { } function JobDetails:init(args) + self.building = dfhack.job.getHolder(self.job) + local status = { text = 'No worker', pen = COLOR_DARKGREY } local worker = dfhack.job.getWorker(self.job) if self.job.flags.suspend then status = { text = 'Suspended', pen = COLOR_RED } elseif worker then - status = { text = dfhack.TranslateName(dfhack.unit.getVisibleName(worker)), pen = COLOR_GREEN } + status = { text = dfhack.TranslateName(dfhack.units.getVisibleName(worker)), pen = COLOR_GREEN } end self:addviews{ @@ -63,6 +65,14 @@ function JobDetails:init(args) self:initListChoices() end +function JobDetails:onGetSelectedBuilding() + return self.building +end + +function JobDetails:onGetSelectedJob() + return self.job +end + function describe_item_type(iobj) local itemline = 'any item' if iobj.item_type >= 0 then @@ -79,10 +89,7 @@ function describe_item_type(iobj) end function is_caste_mat(iobj) - if iobj.item_type >= 0 then - return df.item_type.attrs[iobj.item_type].is_caste_mat - end - return false + return dfhack.items.isCasteMaterial(iobj.item_type) end function describe_material(iobj) @@ -119,7 +126,7 @@ function JobDetails:initListChoices() local choices = {} for i,iobj in ipairs(self.job.job_items) do - local head = 'Item '..(i+1)..': '..(items[idx] or 0)..' of '..iobj.quantity + local head = 'Item '..(i+1)..': '..(items[i] or 0)..' of '..iobj.quantity if iobj.min_dimension > 0 then head = head .. '(size '..iobj.min_dimension..')' end @@ -175,16 +182,22 @@ function JobDetails:canChangeIType() return obj ~= nil end +function JobDetails:setItemType(obj, item_type, item_subtype) + obj.iobj.item_type = item_type + obj.iobj.item_subtype = item_subtype + + if is_caste_mat(obj.iobj) then + self:setMaterial(obj, -1, -1) + end +end + function JobDetails:onChangeIType() local idx, obj = self.subviews.list:getSelected() guimat.ItemTypeDialog{ prompt = 'Please select a new item type for input '..idx, none_caption = 'any item', item_filter = curry(dfhack.job.isSuitableItem, obj.iobj), - on_select = function(item_type, item_subtype) - obj.iobj.item_type = item_type - obj.iobj.item_subtype = item_subtype - end + on_select = self:callback('setItemType', obj) }:show() end @@ -193,6 +206,20 @@ function JobDetails:canChangeMat() return obj ~= nil and not is_caste_mat(obj.iobj) end +function JobDetails:setMaterial(obj, mat_type, mat_index) + if obj.index == 0 + and self.job.mat_type == obj.iobj.mat_type + and self.job.mat_index == obj.iobj.mat_index + and self.job.job_type ~= df.job_type.PrepareMeal + then + self.job.mat_type = mat_type + self.job.mat_index = mat_index + end + + obj.iobj.mat_type = mat_type + obj.iobj.mat_index = mat_index +end + function JobDetails:onChangeMat() local idx, obj = self.subviews.list:getSelected() @@ -213,20 +240,18 @@ function JobDetails:onChangeMat() mat_filter = function(mat,parent,mat_type,mat_index) return dfhack.job.isSuitableMaterial(obj.iobj, mat_type, mat_index) end, - on_select = function(mat_type, mat_index) - if idx == 1 - and self.job.mat_type == obj.iobj.mat_type - and self.job.mat_index == obj.iobj.mat_index - and self.job.job_type ~= df.job_type.PrepareMeal - then - self.job.mat_type = mat_type - self.job.mat_index = mat_index - end + on_select = self:callback('setMaterial', obj) + }:show() +end - obj.iobj.mat_type = mat_type - obj.iobj.mat_index = mat_index +function JobDetails:onInput(keys) + if self:propagateMoveKeys(keys) then + if df.global.world.selected_building ~= self.building then + self:dismiss() end - }:show() + else + JobDetails.super.onInput(self, keys) + end end if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Workshop/Job') then From dfa3a520fd2e7243413d5dc38d253fd17e072c1e Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sun, 21 Oct 2012 16:34:13 -0500 Subject: [PATCH 114/114] sync structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index b9b2e8c6d..e06438924 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b9b2e8c6d2141f13966ed965b3f3ffe924e527db +Subproject commit e06438924929a8ecab751c0c233dad5767e91f7e