-
+
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 13be972d0..378b32667 100644
--- a/Readme.rst
+++ b/Readme.rst
@@ -1626,10 +1626,31 @@ Fortress activity management
seedwatch
---------
-Tool for turning cooking of seeds and plants on/off depending on how much you
-have of them.
+Watches the numbers of seeds available and enables/disables seed and plant cooking.
-See 'seedwatch help' for detailed description.
+Each plant type can be assigned a limit. If their number falls below that limit,
+the plants and seeds of that type will be excluded from cookery.
+If the number rises above the limit + 20, then cooking will be allowed.
+
+The plugin needs a fortress to be loaded and will deactivate automatically otherwise.
+You have to reactivate with 'seedwatch start' after you load the game.
+
+Options:
+
+:all: Adds all plants from the abbreviation list to the watch list.
+:start: Start watching.
+:stop: Stop watching.
+:info: Display whether seedwatch is watching, and the watch list.
+:clear: Clears the watch list.
+
+Examples:
+
+``seedwatch MUSHROOM_HELMET_PLUMP 30``
+ add ``MUSHROOM_HELMET_PLUMP`` to the watch list, limit = 30
+``seedwatch MUSHROOM_HELMET_PLUMP``
+ removes ``MUSHROOM_HELMET_PLUMP`` from the watch list.
+``seedwatch all 30``
+ adds all plants from the abbreviation list to the watch list, the limit being 30.
zone
----
@@ -2149,8 +2170,8 @@ as an offset for the pattern: instead of starting at the cursor, it will start
The script takes the plan filename, starting from the root df folder (where
Dwarf Fortress.exe is found).
-drainaquifer
-============
+drain-aquifer
+=============
Remove all 'aquifer' tag from the map blocks. Irreversible.
deathcause
@@ -2655,6 +2676,19 @@ Enable the automelt plugin in your dfhack.init with::
When querying a stockpile an option will appear to toggle automelt for this stockpile.
Any items placed in this stockpile will be designated to be melted.
+Track Stop Menu
+===============
+
+The `q` menu of track stops is completely blank by default. To enable one::
+
+ enable trackstop
+
+This allows you to view and/or change the track stop's friction and dump direction settings.
+It re-uses the keybindings from the track stop building interface:
+
+* BUILDING_TRACK_STOP_FRICTION_UP
+* BUILDING_TRACK_STOP_FRICTION_DOWN
+* BUILDING_TRACK_STOP_DUMP
gui/advfort
===========
diff --git a/dfhack.init-example b/dfhack.init-example
index f619401f6..033d5e3b9 100644
--- a/dfhack.init-example
+++ b/dfhack.init-example
@@ -189,7 +189,7 @@ enable search
enable automaterial
# Other interface improvement tools
-enable dwarfmonitor mousequery automelt autotrade buildingplan resume zone
+enable dwarfmonitor mousequery automelt autotrade buildingplan resume trackstop zone stocks autochop
# allow the fortress bookkeeper to queue jobs through the manager
stockflow enable
diff --git a/library/Core.cpp b/library/Core.cpp
index b6ae13ae1..18801f4d9 100644
--- a/library/Core.cpp
+++ b/library/Core.cpp
@@ -61,6 +61,7 @@ using namespace DFHack;
#include "df/world_data.h"
#include "df/interfacest.h"
#include "df/viewscreen_dwarfmodest.h"
+#include "df/viewscreen_game_cleanerst.h"
#include "df/viewscreen_loadgamest.h"
#include "df/viewscreen_savegamest.h"
#include
@@ -1027,12 +1028,15 @@ bool Core::Init()
cerr << "Initializing Console.\n";
// init the console.
- bool is_text_mode = false;
- if(init && init->display.flag.is_set(init_display_flags::TEXT))
+ bool is_text_mode = (init && init->display.flag.is_set(init_display_flags::TEXT));
+ if (is_text_mode || getenv("DFHACK_DISABLE_CONSOLE"))
{
- is_text_mode = true;
con.init(true);
cerr << "Console is not available. Use dfhack-run to send commands.\n";
+ if (!is_text_mode)
+ {
+ cout << "Console disabled.\n";
+ }
}
else if(con.init(false))
cerr << "Console is running.\n";
@@ -1280,6 +1284,7 @@ void Core::doUpdate(color_ostream &out, bool first_update)
}
bool is_load_save =
+ strict_virtual_cast(screen) ||
strict_virtual_cast(screen) ||
strict_virtual_cast(screen);
diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp
index 93e75e280..f9187ff09 100644
--- a/library/DataDefs.cpp
+++ b/library/DataDefs.cpp
@@ -67,10 +67,7 @@ void *type_identity::allocate() {
bool type_identity::copy(void *tgt, const void *src) {
if (can_allocate() && tgt && src)
- {
- do_copy(tgt, src);
- return true;
- }
+ return do_copy(tgt, src);
else
return false;
}
diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp
index 79aa3bcf1..4d8decbed 100644
--- a/library/DataStaticsFields.cpp
+++ b/library/DataStaticsFields.cpp
@@ -39,6 +39,14 @@ namespace df {
stl_bit_vector_identity identity_traits >::identity;
bit_array_identity identity_traits >::identity;
+ static void *fstream_allocator_fn(void *out, const void *in) {
+ if (out) { /* *(T*)out = *(const T*)in;*/ return NULL; }
+ else if (in) { delete (std::fstream*)in; return (std::fstream*)in; }
+ else return new std::fstream();
+ }
+ opaque_identity identity_traits::identity(
+ sizeof(std::fstream), fstream_allocator_fn, "fstream");
+
buffer_container_identity buffer_container_identity::base_instance;
#undef NUMBER_IDENTITY_TRAITS
diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp
index 877609e56..410c25f88 100644
--- a/library/LuaTypes.cpp
+++ b/library/LuaTypes.cpp
@@ -1256,8 +1256,11 @@ static void MakePrimitiveMetatable(lua_State *state, type_identity *type)
// Index the fields
lua_newtable(state);
- EnableMetaField(state, base+2, "value", type);
- AssociateId(state, base+3, 1, "value");
+ if (type->type() != IDTYPE_OPAQUE)
+ {
+ EnableMetaField(state, base+2, "value", type);
+ AssociateId(state, base+3, 1, "value");
+ }
// Add the iteration metamethods
PushStructMethod(state, base+1, base+3, meta_struct_next);
diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h
index 3591be045..3bb62a2c1 100644
--- a/library/include/DataDefs.h
+++ b/library/include/DataDefs.h
@@ -61,7 +61,8 @@ namespace DFHack
IDTYPE_STRUCT,
IDTYPE_CLASS,
IDTYPE_BUFFER,
- IDTYPE_STL_PTR_VECTOR
+ IDTYPE_STL_PTR_VECTOR,
+ IDTYPE_OPAQUE
};
typedef void *(*TAllocateFn)(void*,const void*);
@@ -78,7 +79,7 @@ namespace DFHack
virtual bool can_allocate() { return true; }
virtual void *do_allocate() { return do_allocate_pod(); }
- virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
+ virtual bool do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); return true; }
virtual bool do_destroy(void *obj) { return do_destroy_pod(obj); }
public:
@@ -116,7 +117,7 @@ namespace DFHack
virtual bool can_allocate() { return (allocator != NULL); }
virtual void *do_allocate() { return allocator(NULL,NULL); }
- virtual void do_copy(void *tgt, const void *src) { allocator(tgt,src); }
+ virtual bool do_copy(void *tgt, const void *src) { return allocator(tgt,src) == tgt; }
virtual bool do_destroy(void *obj) { return allocator(NULL,obj) == obj; }
public:
virtual bool isPrimitive() { return false; }
@@ -166,7 +167,7 @@ namespace DFHack
protected:
virtual bool can_allocate() { return true; }
virtual void *do_allocate() { return do_allocate_pod(); }
- virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
+ virtual bool do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); return true; }
virtual bool do_destroy(void *obj) { return do_destroy_pod(obj); }
public:
@@ -199,7 +200,7 @@ namespace DFHack
protected:
virtual bool can_allocate() { return true; }
virtual void *do_allocate();
- virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
+ virtual bool do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); return true; }
virtual bool do_destroy(void *obj) { return do_destroy_pod(obj); }
public:
diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h
index e684c6abd..e6dfc6a18 100644
--- a/library/include/DataIdentity.h
+++ b/library/include/DataIdentity.h
@@ -65,6 +65,17 @@ namespace DFHack
virtual identity_type type() { return IDTYPE_PRIMITIVE; }
};
+ class DFHACK_EXPORT opaque_identity : public constructed_identity {
+ std::string name;
+
+ public:
+ opaque_identity(size_t size, TAllocateFn alloc, const std::string &name)
+ : constructed_identity(size, alloc), name(name) {};
+
+ virtual std::string getFullName() { return name; }
+ virtual identity_type type() { return IDTYPE_OPAQUE; }
+ };
+
class DFHACK_EXPORT pointer_identity : public primitive_identity {
type_identity *target;
@@ -170,6 +181,7 @@ namespace df
{
using DFHack::function_identity_base;
using DFHack::primitive_identity;
+ using DFHack::opaque_identity;
using DFHack::pointer_identity;
using DFHack::container_identity;
using DFHack::ptr_container_identity;
@@ -488,6 +500,11 @@ namespace df
static stl_string_identity *get() { return &identity; }
};
+ template<> struct DFHACK_EXPORT identity_traits {
+ static opaque_identity identity;
+ static opaque_identity *get() { return &identity; }
+ };
+
template<> struct DFHACK_EXPORT identity_traits {
static ptr_string_identity identity;
static ptr_string_identity *get() { return &identity; }
diff --git a/library/include/df/custom/file_compressorst.methods.inc b/library/include/df/custom/file_compressorst.methods.inc
new file mode 100644
index 000000000..ad61c7bc7
--- /dev/null
+++ b/library/include/df/custom/file_compressorst.methods.inc
@@ -0,0 +1,12 @@
+file_compressorst& operator=(const file_compressorst &in) {
+ compressed = in.compressed;
+ /* fstream cannot be assigned */
+ in_buffer = in.in_buffer;
+ in_buffersize = in.in_buffersize;
+ in_buffer_amount_loaded = in.in_buffer_amount_loaded;
+ in_buffer_position = in.in_buffer_position;
+ out_buffer = in.out_buffer;
+ out_buffersize = in.out_buffersize;
+ out_buffer_amount_written = in.out_buffer_amount_written;
+ return *this;
+}
diff --git a/library/lua/syndrome-util.lua b/library/lua/syndrome-util.lua
index 90e699de7..083c90371 100644
--- a/library/lua/syndrome-util.lua
+++ b/library/lua/syndrome-util.lua
@@ -119,7 +119,7 @@ function isValidTarget(unit,syndrome)
end
for caste,creature in ipairs(syndrome.syn_affected_creature) do
local affectedCreature = creature.value
- local affectedCaste = syndrome.syn_affectedCaste[caste].value
+ local affectedCaste = syndrome.syn_affected_caste[caste].value
if affectedCreature == unitRaceName and (affectedCaste == unitCasteName or affectedCaste == "ALL") then
affected = true
end
diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp
index 9d2ebf2dd..463cea227 100644
--- a/library/modules/Units.cpp
+++ b/library/modules/Units.cpp
@@ -154,7 +154,7 @@ void Units::CopyCreature(df::unit * source, t_unit & furball)
// profession
furball.profession = source->profession;
// happiness
- furball.happiness = source->status.happiness;
+ furball.happiness = 100;//source->status.happiness;
// physical attributes
memcpy(&furball.strength, source->body.physical_attrs, sizeof(source->body.physical_attrs));
diff --git a/library/xml b/library/xml
index 93606b34b..af94d10e9 160000
--- a/library/xml
+++ b/library/xml
@@ -1 +1 @@
-Subproject commit 93606b34b78a94b201704adb5c9ae28af304cdb1
+Subproject commit af94d10e9a3b17ea36b7e896bb950c6fc5369e31
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index 230194b46..bdf8d1402 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -110,7 +110,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(digFlood digFlood.cpp)
add_subdirectory(diggingInvaders)
DFHACK_PLUGIN(drybuckets drybuckets.cpp)
- DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp)
+ #DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp)
DFHACK_PLUGIN(embark-tools embark-tools.cpp)
DFHACK_PLUGIN(eventful eventful.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(fastdwarf fastdwarf.cpp)
@@ -132,7 +132,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua)
DFHACK_PLUGIN(manipulator manipulator.cpp)
DFHACK_PLUGIN(mode mode.cpp)
- DFHACK_PLUGIN(misery misery.cpp)
+ #DFHACK_PLUGIN(misery misery.cpp)
DFHACK_PLUGIN(mousequery mousequery.cpp)
DFHACK_PLUGIN(petcapRemover petcapRemover.cpp)
DFHACK_PLUGIN(plants plants.cpp)
@@ -156,6 +156,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(stocks stocks.cpp)
DFHACK_PLUGIN(strangemood strangemood.cpp)
DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h)
+ DFHACK_PLUGIN(trackstop trackstop.cpp)
# DFHACK_PLUGIN(treefarm treefarm.cpp)
DFHACK_PLUGIN(tubefill tubefill.cpp)
add_subdirectory(tweak)
diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp
index 05855a749..23ae71969 100644
--- a/plugins/autolabor.cpp
+++ b/plugins/autolabor.cpp
@@ -819,7 +819,7 @@ static void assign_labor(unit_labor::unit_labor labor,
// bias by happiness
- value += dwarfs[dwarf]->status.happiness;
+ //value += dwarfs[dwarf]->status.happiness;
values[dwarf] = value;
diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp
index 58cbdd20c..cfd348455 100644
--- a/plugins/automelt.cpp
+++ b/plugins/automelt.cpp
@@ -7,6 +7,7 @@
#include "df/building_def.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/building_stockpilest.h"
+#include "modules/Buildings.h"
#include "modules/Items.h"
#include "df/ui.h"
#include "modules/Maps.h"
@@ -23,10 +24,36 @@ DFHACK_PLUGIN("automelt");
static const string PERSISTENCE_KEY = "automelt/stockpiles";
-static void mark_all_in_stockpiles(vector &stockpiles)
+static int mark_item(df::item *item, df::item_flags bad_flags, int32_t stockpile_id)
{
- std::vector &items = world->items.other[items_other_id::IN_PLAY];
+ if (item->flags.whole & bad_flags.whole)
+ return 0;
+
+ if (item->isAssignedToThisStockpile(stockpile_id)) {
+ size_t marked_count = 0;
+ std::vector contents;
+ Items::getContainedItems(item, &contents);
+ for (auto child = contents.begin(); child != contents.end(); child++)
+ {
+ marked_count += mark_item(*child, bad_flags, stockpile_id);
+ }
+
+ return marked_count;
+ }
+
+ if (!can_melt(item))
+ return 0;
+
+ if (is_set_to_melt(item))
+ return 0;
+
+ insert_into_vector(world->items.other[items_other_id::ANY_MELT_DESIGNATED], &df::item::id, item);
+ item->flags.bits.melt = true;
+ return 1;
+}
+static void mark_all_in_stockpiles(vector &stockpiles)
+{
// Precompute a bitmask with the bad flags
df::item_flags bad_flags;
bad_flags.whole = 0;
@@ -39,27 +66,16 @@ static void mark_all_in_stockpiles(vector &stockpiles)
#undef F
size_t marked_count = 0;
- for (size_t i = 0; i < items.size(); i++)
+ for (auto it = stockpiles.begin(); it != stockpiles.end(); it++)
{
- df::item *item = items[i];
- if (item->flags.whole & bad_flags.whole)
- continue;
-
- if (!can_melt(item))
- continue;
-
- if (is_set_to_melt(item))
+ if (!it->isValid())
continue;
- auto &melting_items = world->items.other[items_other_id::ANY_MELT_DESIGNATED];
- for (auto it = stockpiles.begin(); it != stockpiles.end(); it++)
+ auto spid = it->getId();
+ Buildings::StockpileIterator stored;
+ for (stored.begin(it->getStockpile()); !stored.done(); ++stored)
{
- if (!it->inStockpile(item))
- continue;
-
- ++marked_count;
- insert_into_vector(melting_items, &df::item::id, item);
- item->flags.bits.melt = true;
+ marked_count += mark_item(*stored, bad_flags, spid);
}
}
diff --git a/plugins/autotrade.cpp b/plugins/autotrade.cpp
index ac733c1c2..eff1b5d64 100644
--- a/plugins/autotrade.cpp
+++ b/plugins/autotrade.cpp
@@ -455,7 +455,9 @@ struct tradeview_hook : public df::viewscreen_tradegoodsst
{
for (int i = 0; i < trader_selected.size(); i++)
{
- trader_selected[i] = 1;
+ // Only mark containers, not their contents.
+ // Granted, this behaves poorly with the search plugin...
+ trader_selected[i] = !trader_items[i]->flags.bits.in_inventory;
}
}
else if (input->count(interface_key::CUSTOM_U))
@@ -469,7 +471,8 @@ struct tradeview_hook : public df::viewscreen_tradegoodsst
{
for (int i = 0; i < broker_selected.size(); i++)
{
- broker_selected[i] = 1;
+ // Only mark containers, not their contents.
+ broker_selected[i] = !broker_items[i]->flags.bits.in_inventory;
}
}
else if (input->count(interface_key::CUSTOM_SHIFT_U))
diff --git a/plugins/digFlood.cpp b/plugins/digFlood.cpp
index abe144a63..1646f4deb 100644
--- a/plugins/digFlood.cpp
+++ b/plugins/digFlood.cpp
@@ -72,7 +72,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector
#include "ColorText.h"
+#include "uicommon.h"
#include "df/viewscreen_choose_start_sitest.h"
#include "df/interface_key.h"
using namespace DFHack;
-struct EmbarkTool
-{
- std::string id;
- std::string name;
- std::string desc;
- bool enabled;
- df::interface_key toggle_key;
-};
-
-EmbarkTool embark_tools[] = {
- {"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map",
- false, df::interface_key::CUSTOM_A},
- {"nano", "Nano embark", "Allows the embark size to be decreased below 2x2",
- false, df::interface_key::CUSTOM_N},
- {"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site",
- false, df::interface_key::CUSTOM_S},
- {"sticky", "Stable position", "Maintains the selected local area while navigating the world map",
- false, df::interface_key::CUSTOM_P},
-};
-#define NUM_TOOLS int(sizeof(embark_tools) / sizeof(EmbarkTool))
-
-command_result embark_tools_cmd (color_ostream &out, std::vector & parameters);
-
-void OutputString (int8_t color, int &x, int y, const std::string &text);
-
-bool tool_exists (std::string tool_name);
-bool tool_enabled (std::string tool_name);
-bool tool_enable (std::string tool_name, bool enable_state);
-void tool_update (std::string tool_name);
-
-class embark_tools_settings : public dfhack_viewscreen
-{
-public:
- embark_tools_settings () { };
- ~embark_tools_settings () { };
- void help () { };
- std::string getFocusString () { return "embark-tools/options"; };
- void render ()
- {
- parent->render();
- int x;
- auto dim = Screen::getWindowSize();
- int width = 50,
- height = 4 + 1 + NUM_TOOLS, // Padding + lower row
- min_x = (dim.x - width) / 2,
- max_x = (dim.x + width) / 2,
- min_y = (dim.y - height) / 2,
- max_y = min_y + height;
- Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_DARKGREY), min_x, min_y, max_x, max_y);
- Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), min_x + 1, min_y + 1, max_x - 1, max_y - 1);
- x = min_x + 2;
- OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::SELECT));
- OutputString(COLOR_WHITE, x, max_y - 2, "/");
- OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
- OutputString(COLOR_WHITE, x, max_y - 2, ": Done");
- for (int i = 0, y = min_y + 2; i < NUM_TOOLS; i++, y++)
- {
- EmbarkTool t = embark_tools[i];
- x = min_x + 2;
- OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t.toggle_key));
- OutputString(COLOR_WHITE, x, y, ": " + t.name + (t.enabled ? ": Enabled" : ": Disabled"));
- }
- };
- void feed (std::set * input)
- {
- if (input->count(df::interface_key::SELECT) || input->count(df::interface_key::LEAVESCREEN))
- {
- Screen::dismiss(this);
- return;
- }
- for (auto iter = input->begin(); iter != input->end(); iter++)
- {
- df::interface_key key = *iter;
- for (int i = 0; i < NUM_TOOLS; i++)
- {
- if (embark_tools[i].toggle_key == key)
- {
- embark_tools[i].enabled = !embark_tools[i].enabled;
- }
- }
- }
- };
-};
-
-/*
- * Logic
- */
+#define FOR_ITER_TOOLS(iter) for(auto iter = tools.begin(); iter != tools.end(); iter++)
void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen)
{
@@ -159,238 +74,317 @@ void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy)
update_embark_sidebar(screen);
}
-std::string sand_indicator = "";
-bool sand_dirty = true; // Flag set when update is needed
-void sand_update (df::viewscreen_choose_start_sitest * screen)
-{
- CoreSuspendClaimer suspend;
- buffered_color_ostream out;
- Core::getInstance().runCommand(out, "prospect");
- auto fragments = out.fragments();
- sand_indicator = "";
- for (auto iter = fragments.begin(); iter != fragments.end(); iter++)
- {
- std::string fragment = iter->second;
- if (fragment.find("SAND_") != std::string::npos)
- {
- sand_indicator = "Sand";
- break;
- }
- }
- sand_dirty = false;
-}
+typedef df::viewscreen_choose_start_sitest start_sitest;
+typedef std::set ikey_set;
-int sticky_pos[] = {0, 0, 3, 3};
-bool sticky_moved = false;
-void sticky_save (df::viewscreen_choose_start_sitest * screen)
+class EmbarkTool
{
- sticky_pos[0] = screen->location.embark_pos_min.x;
- sticky_pos[1] = screen->location.embark_pos_max.x;
- sticky_pos[2] = screen->location.embark_pos_min.y;
- sticky_pos[3] = screen->location.embark_pos_max.y;
-}
-
-void sticky_apply (df::viewscreen_choose_start_sitest * screen)
-{
- if (screen->finder.finder_state != -1)
- {
- // Site finder is active - don't override default local position
- return;
- }
- screen->location.embark_pos_min.x = sticky_pos[0];
- screen->location.embark_pos_max.x = sticky_pos[1];
- screen->location.embark_pos_min.y = sticky_pos[2];
- screen->location.embark_pos_max.y = sticky_pos[3];
- update_embark_sidebar(screen);
-}
+protected:
+ bool enabled;
+public:
+ EmbarkTool()
+ :enabled(false)
+ { }
+ virtual bool getEnabled() { return enabled; }
+ virtual void setEnabled(bool state) { enabled = state; }
+ virtual void toggleEnabled() { setEnabled(!enabled); }
+ virtual std::string getId() = 0;
+ virtual std::string getName() = 0;
+ virtual std::string getDesc() = 0;
+ virtual df::interface_key getToggleKey() = 0;
+ virtual void before_render(start_sitest* screen) { };
+ virtual void after_render(start_sitest* screen) { };
+ virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) { };
+ virtual void after_feed(start_sitest* screen, ikey_set* input) { };
+};
+std::vector tools;
/*
- * Viewscreen hooks
- */
-void OutputString (int8_t color, int &x, int y, const std::string &text)
+class SampleTool : public EmbarkTool
{
- Screen::paintString(Screen::Pen(' ', color, 0), x, y, text);
- x += text.length();
-}
+ virtual std::string getId() { return "id"; }
+ virtual std::string getName() { return "Name"; }
+ virtual std::string getDesc() { return "Description"; }
+ virtual df::interface_key getToggleKey() { return df::interface_key::KEY; }
+ virtual void before_render(start_sitest* screen) { }
+ virtual void after_render(start_sitest* screen) { }
+ virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) { };
+ virtual void after_feed(start_sitest* screen, ikey_set* input) { };
+};
-struct choose_start_site_hook : df::viewscreen_choose_start_sitest
-{
- typedef df::viewscreen_choose_start_sitest interpose_base;
+*/
- DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set *input))
+class EmbarkAnywhere : public EmbarkTool
+{
+public:
+ virtual std::string getId() { return "anywhere"; }
+ virtual std::string getName() { return "Embark anywhere"; }
+ virtual std::string getDesc() { return "Allows embarking anywhere on the world map"; }
+ virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_A; }
+ virtual void after_render(start_sitest* screen)
{
- bool prevent_default = false;
- if (tool_enabled("anywhere"))
+ auto dim = Screen::getWindowSize();
+ int x = 20, y = dim.y - 2;
+ if (screen->page >= 0 && screen->page <= 4)
{
- for (auto iter = input->begin(); iter != input->end(); iter++)
- {
- df::interface_key key = *iter;
- if (key == df::interface_key::SETUP_EMBARK)
- {
- prevent_default = true;
- this->in_embark_normal = 1;
- }
- }
+ OutputString(COLOR_WHITE, x, y, ": Embark!");
}
-
- if (input->count(df::interface_key::CUSTOM_S))
+ }
+ virtual void before_feed(start_sitest* screen, ikey_set *input, bool &cancel)
+ {
+ if (input->count(df::interface_key::SETUP_EMBARK))
{
- Screen::show(new embark_tools_settings);
- return;
+ cancel = true;
+ screen->in_embark_normal = 1;
}
+ };
+};
- if (tool_enabled("nano"))
+class NanoEmbark : public EmbarkTool
+{
+public:
+ virtual std::string getId() { return "nano"; }
+ virtual std::string getName() { return "Nano embark"; }
+ virtual std::string getDesc() { return "Allows the embark size to be decreased below 2x2"; }
+ virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_N; }
+ virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel)
+ {
+ for (auto iter = input->begin(); iter != input->end(); iter++)
{
- for (auto iter = input->begin(); iter != input->end(); iter++)
+ df::interface_key key = *iter;
+ bool is_resize = true;
+ int dx = 0, dy = 0;
+ switch (key)
{
- df::interface_key key = *iter;
- bool is_resize = true;
- int dx = 0, dy = 0;
- switch (key)
- {
- case df::interface_key::SETUP_LOCAL_Y_UP:
- dy = 1;
- break;
- case df::interface_key::SETUP_LOCAL_Y_DOWN:
- dy = -1;
- break;
- case df::interface_key::SETUP_LOCAL_X_UP:
- dx = 1;
- break;
- case df::interface_key::SETUP_LOCAL_X_DOWN:
- dx = -1;
- break;
- default:
- is_resize = false;
- }
- if (is_resize)
- {
- prevent_default = true;
- resize_embark(this, dx, dy);
- }
+ case df::interface_key::SETUP_LOCAL_Y_UP:
+ dy = 1;
+ break;
+ case df::interface_key::SETUP_LOCAL_Y_DOWN:
+ dy = -1;
+ break;
+ case df::interface_key::SETUP_LOCAL_X_UP:
+ dx = 1;
+ break;
+ case df::interface_key::SETUP_LOCAL_X_DOWN:
+ dx = -1;
+ break;
+ default:
+ is_resize = false;
+ }
+ if (is_resize)
+ {
+ cancel = true;
+ resize_embark(screen, dx, dy);
+ return;
}
}
+ };
+};
- if (tool_enabled("sticky"))
+class SandIndicator : public EmbarkTool
+{
+protected:
+ bool dirty;
+ std::string indicator;
+ void update_indicator()
+ {
+ CoreSuspendClaimer suspend;
+ buffered_color_ostream out;
+ Core::getInstance().runCommand(out, "prospect");
+ auto fragments = out.fragments();
+ indicator = "";
+ for (auto iter = fragments.begin(); iter != fragments.end(); iter++)
{
- for (auto iter = input->begin(); iter != input->end(); iter++)
+ std::string fragment = iter->second;
+ if (fragment.find("SAND_") != std::string::npos)
{
- df::interface_key key = *iter;
- bool is_motion = false;
- int dx = 0, dy = 0;
- switch (key)
- {
- case df::interface_key::CURSOR_UP:
- case df::interface_key::CURSOR_DOWN:
- case df::interface_key::CURSOR_LEFT:
- case df::interface_key::CURSOR_RIGHT:
- case df::interface_key::CURSOR_UPLEFT:
- case df::interface_key::CURSOR_UPRIGHT:
- case df::interface_key::CURSOR_DOWNLEFT:
- case df::interface_key::CURSOR_DOWNRIGHT:
- case df::interface_key::CURSOR_UP_FAST:
- case df::interface_key::CURSOR_DOWN_FAST:
- case df::interface_key::CURSOR_LEFT_FAST:
- case df::interface_key::CURSOR_RIGHT_FAST:
- case df::interface_key::CURSOR_UPLEFT_FAST:
- case df::interface_key::CURSOR_UPRIGHT_FAST:
- case df::interface_key::CURSOR_DOWNLEFT_FAST:
- case df::interface_key::CURSOR_DOWNRIGHT_FAST:
- is_motion = true;
- break;
- default:
- is_motion = false;
- }
- if (is_motion && !sticky_moved)
- {
- sticky_save(this);
- sticky_moved = true;
- }
+ indicator = "Sand";
+ break;
}
}
-
- if (tool_enabled("sand"))
+ dirty = false;
+ }
+public:
+ SandIndicator()
+ :EmbarkTool(),
+ dirty(true),
+ indicator("")
+ { }
+ virtual void setEnabled(bool state)
+ {
+ EmbarkTool::setEnabled(state);
+ dirty = true;
+ }
+ virtual std::string getId() { return "sand"; }
+ virtual std::string getName() { return "Sand indicator"; }
+ virtual std::string getDesc() { return "Displays an indicator when sand is present on the given embark site"; }
+ virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_S; }
+ virtual void after_render(start_sitest* screen)
+ {
+ if (dirty)
+ update_indicator();
+ auto dim = Screen::getWindowSize();
+ int x = dim.x - 28,
+ y = 13;
+ if (screen->page == 0)
{
- sand_dirty = true;
+ OutputString(COLOR_YELLOW, x, y, indicator);
}
- if (!prevent_default)
- INTERPOSE_NEXT(feed)(input);
}
+ virtual void after_feed(start_sitest* screen, ikey_set* input)
+ {
+ dirty = true;
+ };
+};
- DEFINE_VMETHOD_INTERPOSE(void, render, ())
+class StablePosition : public EmbarkTool
+{
+protected:
+ int prev_position[4];
+ bool moved_position;
+ void save_position(start_sitest* screen)
+ {
+ prev_position[0] = screen->location.embark_pos_min.x;
+ prev_position[1] = screen->location.embark_pos_max.x;
+ prev_position[2] = screen->location.embark_pos_min.y;
+ prev_position[3] = screen->location.embark_pos_max.y;
+ }
+ void restore_position(start_sitest* screen)
{
- if (tool_enabled("sticky") && sticky_moved)
+ if (screen->finder.finder_state != -1)
{
- sticky_apply(this);
- sticky_moved = false;
+ // Site finder is active - don't override default local position
+ return;
}
-
- INTERPOSE_NEXT(render)();
-
- auto dim = Screen::getWindowSize();
- int x = 1,
- y = dim.y - 5;
- OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S));
- OutputString(COLOR_WHITE, x, y, ": Enabled: ");
- std::list parts;
- for (int i = 0; i < NUM_TOOLS; i++)
+ screen->location.embark_pos_min.x = prev_position[0];
+ screen->location.embark_pos_max.x = prev_position[1];
+ screen->location.embark_pos_min.y = prev_position[2];
+ screen->location.embark_pos_max.y = prev_position[3];
+ update_embark_sidebar(screen);
+ }
+public:
+ StablePosition()
+ :EmbarkTool(),
+ moved_position(false)
+ {
+ prev_position[0] = 0;
+ prev_position[1] = 0;
+ prev_position[2] = 3;
+ prev_position[3] = 3;
+ }
+ virtual std::string getId() { return "sticky"; }
+ virtual std::string getName() { return "Stable position"; }
+ virtual std::string getDesc() { return "Maintains the selected local area while navigating the world map"; }
+ virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_P; }
+ virtual void before_render(start_sitest* screen) {
+ if (moved_position)
{
- if (embark_tools[i].enabled)
- {
- parts.push_back(embark_tools[i].name);
- parts.push_back(", ");
- }
+ restore_position(screen);
+ moved_position = false;
}
- if (parts.size())
+ }
+ virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) {
+ for (auto iter = input->begin(); iter != input->end(); iter++)
{
- parts.pop_back(); // Remove trailing comma
- for (auto iter = parts.begin(); iter != parts.end(); iter++)
+ df::interface_key key = *iter;
+ bool is_motion = false;
+ int dx = 0, dy = 0;
+ switch (key)
{
- OutputString(COLOR_LIGHTMAGENTA, x, y, *iter);
+ case df::interface_key::CURSOR_UP:
+ case df::interface_key::CURSOR_DOWN:
+ case df::interface_key::CURSOR_LEFT:
+ case df::interface_key::CURSOR_RIGHT:
+ case df::interface_key::CURSOR_UPLEFT:
+ case df::interface_key::CURSOR_UPRIGHT:
+ case df::interface_key::CURSOR_DOWNLEFT:
+ case df::interface_key::CURSOR_DOWNRIGHT:
+ case df::interface_key::CURSOR_UP_FAST:
+ case df::interface_key::CURSOR_DOWN_FAST:
+ case df::interface_key::CURSOR_LEFT_FAST:
+ case df::interface_key::CURSOR_RIGHT_FAST:
+ case df::interface_key::CURSOR_UPLEFT_FAST:
+ case df::interface_key::CURSOR_UPRIGHT_FAST:
+ case df::interface_key::CURSOR_DOWNLEFT_FAST:
+ case df::interface_key::CURSOR_DOWNRIGHT_FAST:
+ is_motion = true;
+ break;
+ }
+ if (is_motion && !moved_position)
+ {
+ save_position(screen);
+ moved_position = true;
}
}
- else
+ };
+};
+
+class embark_tools_settings : public dfhack_viewscreen
+{
+public:
+ embark_tools_settings () { };
+ ~embark_tools_settings () { };
+ void help () { };
+ std::string getFocusString () { return "embark-tools/options"; };
+ void render ()
+ {
+ parent->render();
+ int x, y;
+ auto dim = Screen::getWindowSize();
+ int width = 50,
+ height = 4 + 1 + tools.size(), // Padding + lower row
+ min_x = (dim.x - width) / 2,
+ max_x = (dim.x + width) / 2,
+ min_y = (dim.y - height) / 2,
+ max_y = min_y + height;
+ Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_DARKGREY), min_x, min_y, max_x, max_y);
+ Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), min_x + 1, min_y + 1, max_x - 1, max_y - 1);
+ x = min_x + 2;
+ y = max_y - 2;
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::SELECT));
+ OutputString(COLOR_WHITE, x, y, "/");
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
+ OutputString(COLOR_WHITE, x, y, ": Done");
+ y = min_y + 2;
+ FOR_ITER_TOOLS(iter)
{
- OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)");
+ EmbarkTool* t = *iter;
+ x = min_x + 2;
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t->getToggleKey()));
+ OutputString(COLOR_WHITE, x, y, ": " + t->getName() +
+ (t->getEnabled() ? ": Enabled" : ": Disabled"));
+ y++;
}
-
- if (tool_enabled("anywhere"))
+ };
+ void feed (std::set * input)
+ {
+ if (input->count(df::interface_key::SELECT) || input->count(df::interface_key::LEAVESCREEN))
{
- x = 20; y = dim.y - 2;
- if (this->page >= 0 && this->page <= 4)
- {
- // Only display on five map pages, not on site finder or notes
- OutputString(COLOR_WHITE, x, y, ": Embark!");
- }
+ Screen::dismiss(this);
+ return;
}
- if (tool_enabled("sand"))
+ for (auto iter = input->begin(); iter != input->end(); iter++)
{
- if (sand_dirty)
- {
- sand_update(this);
- }
- x = dim.x - 28; y = 13;
- if (this->page == 0)
+ df::interface_key key = *iter;
+ FOR_ITER_TOOLS(iter)
{
- OutputString(COLOR_YELLOW, x, y, sand_indicator);
+ EmbarkTool* t = *iter;
+ if (t->getToggleKey() == key)
+ {
+ t->toggleEnabled();
+ }
}
}
- }
+ };
};
-IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
-IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
-
-/*
- * Tool management
- */
-
bool tool_exists (std::string tool_name)
{
- for (int i = 0; i < NUM_TOOLS; i++)
+ FOR_ITER_TOOLS(iter)
{
- if (embark_tools[i].id == tool_name)
+ EmbarkTool* tool = *iter;
+ if (tool->getId() == tool_name)
return true;
}
return false;
@@ -398,10 +392,11 @@ bool tool_exists (std::string tool_name)
bool tool_enabled (std::string tool_name)
{
- for (int i = 0; i < NUM_TOOLS; i++)
+ FOR_ITER_TOOLS(iter)
{
- if (embark_tools[i].id == tool_name)
- return embark_tools[i].enabled;
+ EmbarkTool* tool = *iter;
+ if (tool->getId() == tool_name)
+ return tool->getEnabled();
}
return false;
}
@@ -409,42 +404,122 @@ bool tool_enabled (std::string tool_name)
bool tool_enable (std::string tool_name, bool enable_state)
{
int n = 0;
- for (int i = 0; i < NUM_TOOLS; i++)
+ FOR_ITER_TOOLS(iter)
{
- if (embark_tools[i].id == tool_name || tool_name == "all")
+ EmbarkTool* tool = *iter;
+ if (tool->getId() == tool_name || tool_name == "all")
{
- embark_tools[i].enabled = enable_state;
- tool_update(tool_name);
+ tool->setEnabled(enable_state);
n++;
}
}
return (bool)n;
}
-void tool_update (std::string tool_name)
+struct choose_start_site_hook : df::viewscreen_choose_start_sitest
{
- // Called whenever a tool is enabled/disabled
- if (tool_name == "sand")
+ typedef df::viewscreen_choose_start_sitest interpose_base;
+
+ void display_tool_status()
+ {
+ auto dim = Screen::getWindowSize();
+ int x = 1,
+ y = dim.y - 5;
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S));
+ OutputString(COLOR_WHITE, x, y, ": Enabled: ");
+ std::list parts;
+ FOR_ITER_TOOLS(iter)
+ {
+ EmbarkTool* tool = *iter;
+ if (tool->getEnabled())
+ {
+ parts.push_back(tool->getName());
+ parts.push_back(", ");
+ }
+ }
+ if (parts.size())
+ {
+ parts.pop_back(); // Remove trailing comma
+ for (auto iter = parts.begin(); iter != parts.end(); iter++)
+ {
+ OutputString(COLOR_LIGHTMAGENTA, x, y, *iter);
+ }
+ }
+ else
+ {
+ OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)");
+ }
+ }
+
+ void display_settings()
{
- sand_dirty = true;
+ Screen::show(new embark_tools_settings);
}
-}
-/*
- * Plugin management
- */
+ inline bool is_valid_page()
+ {
+ return (page >= 0 && page <= 4);
+ }
+
+ DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set *input))
+ {
+ bool cancel = false;
+ FOR_ITER_TOOLS(iter)
+ {
+ EmbarkTool* tool = *iter;
+ if (tool->getEnabled())
+ tool->before_feed(this, input, cancel);
+ }
+ if (cancel)
+ return;
+ INTERPOSE_NEXT(feed)(input);
+ if (input->count(df::interface_key::CUSTOM_S) && is_valid_page())
+ display_settings();
+ FOR_ITER_TOOLS(iter)
+ {
+ EmbarkTool* tool = *iter;
+ if (tool->getEnabled())
+ tool->after_feed(this, input);
+ }
+ }
+ DEFINE_VMETHOD_INTERPOSE(void, render, ())
+ {
+ FOR_ITER_TOOLS(iter)
+ {
+ EmbarkTool* tool = *iter;
+ if (tool->getEnabled())
+ tool->before_render(this);
+ }
+ INTERPOSE_NEXT(render)();
+ display_tool_status();
+ FOR_ITER_TOOLS(iter)
+ {
+ EmbarkTool* tool = *iter;
+ if (tool->getEnabled())
+ tool->after_render(this);
+ }
+ }
+};
+IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
+IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
DFHACK_PLUGIN("embark-tools");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
+command_result embark_tools_cmd (color_ostream &out, std::vector & parameters);
+
DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands)
{
+ tools.push_back(new EmbarkAnywhere);
+ tools.push_back(new NanoEmbark);
+ tools.push_back(new SandIndicator);
+ tools.push_back(new StablePosition);
std::string help = "";
help += "embark-tools (enable/disable) tool [tool...]\n"
"Tools:\n";
- for (int i = 0; i < NUM_TOOLS; i++)
+ FOR_ITER_TOOLS(iter)
{
- help += (" " + embark_tools[i].id + ": " + embark_tools[i].desc + "\n");
+ help += (" " + (*iter)->getId() + ": " + (*iter)->getDesc() + "\n");
}
commands.push_back(PluginCommand(
"embark-tools",
@@ -504,10 +579,11 @@ command_result embark_tools_cmd (color_ostream &out, std::vector &
if (is_enabled)
{
out << "Tool status:" << std::endl;
- for (int i = 0; i < NUM_TOOLS; i++)
+ FOR_ITER_TOOLS(iter)
{
- EmbarkTool t = embark_tools[i];
- out << t.name << " (" << t.id << "): " << (t.enabled ? "Enabled" : "Disabled") << std::endl;
+ EmbarkTool* t = *iter;
+ out << t->getName() << " (" << t->getId() << "): "
+ << (t->getEnabled() ? "Enabled" : "Disabled") << std::endl;
}
}
else
diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp
index b03fda561..0bd9d9698 100644
--- a/plugins/fastdwarf.cpp
+++ b/plugins/fastdwarf.cpp
@@ -113,8 +113,15 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
action->data.move.timer = 1;
break;
case unit_action_type::Attack:
+ // Attacks are executed when timer1 reaches zero, which will be
+ // on the following tick.
action->data.attack.timer1 = 1;
- action->data.attack.timer2 = 1;
+ // Attack actions are completed, and new ones generated, when
+ // timer2 reaches zero. If set to 1 this never seems to occur.
+ // Setting to zero makes next tick generate a new attack action
+ // every time, thereby allowing target enemy/body part re-selection
+ // take place.
+ action->data.attack.timer2 = 0;
break;
case unit_action_type::Hold:
action->data.hold.timer = 1;
diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp
index ebd23a9d7..3a39d115b 100644
--- a/plugins/manipulator.cpp
+++ b/plugins/manipulator.cpp
@@ -270,7 +270,7 @@ struct UnitInfo
enum altsort_mode {
ALTSORT_NAME,
ALTSORT_PROFESSION_OR_SQUAD,
- ALTSORT_HAPPINESS,
+ ALTSORT_STRESS,
ALTSORT_ARRIVAL,
ALTSORT_MAX
};
@@ -311,12 +311,17 @@ bool sortBySquad (const UnitInfo *d1, const UnitInfo *d2)
return descending ? gt : !gt;
}
-bool sortByHappiness (const UnitInfo *d1, const UnitInfo *d2)
+bool sortByStress (const UnitInfo *d1, const UnitInfo *d2)
{
+ if (!d1->unit->status.current_soul)
+ return !descending;
+ if (!d2->unit->status.current_soul)
+ return descending;
+
if (descending)
- return (d1->unit->status.happiness > d2->unit->status.happiness);
+ return (d1->unit->status.current_soul->personality.stress_level > d2->unit->status.current_soul->personality.stress_level);
else
- return (d1->unit->status.happiness < d2->unit->status.happiness);
+ return (d1->unit->status.current_soul->personality.stress_level < d2->unit->status.current_soul->personality.stress_level);
}
bool sortByArrival (const UnitInfo *d1, const UnitInfo *d2)
@@ -363,11 +368,11 @@ bool sortBySkill (const UnitInfo *d1, const UnitInfo *d2)
else
return d1->unit->status.labors[sort_labor] < d2->unit->status.labors[sort_labor];
}
- return sortByName(d1, d2);
+ return false;
}
enum display_columns {
- DISP_COLUMN_HAPPINESS,
+ DISP_COLUMN_STRESS,
DISP_COLUMN_NAME,
DISP_COLUMN_PROFESSION_OR_SQUAD,
DISP_COLUMN_LABORS,
@@ -509,13 +514,13 @@ void viewscreen_unitlaborsst::calcSize()
// 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_STRESS] = 6;
+ col_maxwidth[DISP_COLUMN_STRESS] = 6;
col_minwidth[DISP_COLUMN_NAME] = 16;
col_maxwidth[DISP_COLUMN_NAME] = 16; // adjusted in the loop below
col_minwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] = 10;
col_maxwidth[DISP_COLUMN_PROFESSION_OR_SQUAD] = 10; // adjusted in the loop below
- col_minwidth[DISP_COLUMN_LABORS] = num_columns*3/5; // 60%
+ col_minwidth[DISP_COLUMN_LABORS] = 1;
col_maxwidth[DISP_COLUMN_LABORS] = NUM_COLUMNS;
// get max_name/max_prof from strings length
@@ -778,10 +783,10 @@ void viewscreen_unitlaborsst::feed(set *events)
switch (click_header)
{
- case DISP_COLUMN_HAPPINESS:
+ case DISP_COLUMN_STRESS:
if (enabler->mouse_lbut || enabler->mouse_rbut)
{
- input_sort = ALTSORT_HAPPINESS;
+ input_sort = ALTSORT_STRESS;
if (enabler->mouse_lbut)
events->insert(interface_key::SECONDSCROLL_PAGEUP);
if (enabler->mouse_rbut)
@@ -825,7 +830,7 @@ void viewscreen_unitlaborsst::feed(set *events)
switch (click_body)
{
- case DISP_COLUMN_HAPPINESS:
+ case DISP_COLUMN_STRESS:
// do nothing
break;
@@ -917,7 +922,7 @@ void viewscreen_unitlaborsst::feed(set *events)
descending = events->count(interface_key::SECONDSCROLL_UP);
sort_skill = columns[input_column].skill;
sort_labor = columns[input_column].labor;
- std::sort(units.begin(), units.end(), sortBySkill);
+ std::stable_sort(units.begin(), units.end(), sortBySkill);
}
if (events->count(interface_key::SECONDSCROLL_PAGEUP) || events->count(interface_key::SECONDSCROLL_PAGEDOWN))
@@ -926,16 +931,16 @@ void viewscreen_unitlaborsst::feed(set *events)
switch (input_sort)
{
case ALTSORT_NAME:
- std::sort(units.begin(), units.end(), sortByName);
+ std::stable_sort(units.begin(), units.end(), sortByName);
break;
case ALTSORT_PROFESSION_OR_SQUAD:
- std::sort(units.begin(), units.end(), show_squad ? sortBySquad : sortByProfession);
+ std::stable_sort(units.begin(), units.end(), show_squad ? sortBySquad : sortByProfession);
break;
- case ALTSORT_HAPPINESS:
- std::sort(units.begin(), units.end(), sortByHappiness);
+ case ALTSORT_STRESS:
+ std::stable_sort(units.begin(), units.end(), sortByStress);
break;
case ALTSORT_ARRIVAL:
- std::sort(units.begin(), units.end(), sortByArrival);
+ std::stable_sort(units.begin(), units.end(), sortByArrival);
break;
}
}
@@ -947,9 +952,9 @@ void viewscreen_unitlaborsst::feed(set *events)
altsort = ALTSORT_PROFESSION_OR_SQUAD;
break;
case ALTSORT_PROFESSION_OR_SQUAD:
- altsort = ALTSORT_HAPPINESS;
+ altsort = ALTSORT_STRESS;
break;
- case ALTSORT_HAPPINESS:
+ case ALTSORT_STRESS:
altsort = ALTSORT_ARRIVAL;
break;
case ALTSORT_ARRIVAL:
@@ -1000,7 +1005,7 @@ void viewscreen_unitlaborsst::render()
Screen::clear();
Screen::drawBorder(" Dwarf Manipulator - Manage Labors ");
- Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_HAPPINESS], 2, "Hap.");
+ Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_STRESS], 2, "Stress");
Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_NAME], 2, "Name");
Screen::paintString(Screen::Pen(' ', 7, 0), col_offsets[DISP_COLUMN_PROFESSION_OR_SQUAD], 2, show_squad ? "Squad" : "Profession");
@@ -1043,23 +1048,22 @@ void viewscreen_unitlaborsst::render()
df::unit *unit = cur->unit;
int8_t fg = 15, bg = 0;
- int happy = cur->unit->status.happiness;
- string happiness = stl_sprintf("%4i", happy);
- if (happy == 0) // miserable
+ int stress_lvl = unit->status.current_soul ? unit->status.current_soul->personality.stress_level : 0;
+ // cap at 6 digits
+ if (stress_lvl < -99999) stress_lvl = -99999;
+ if (stress_lvl > 999999) stress_lvl = 999999;
+ string stress = stl_sprintf("%6i", stress_lvl);
+ if (stress_lvl >= 500000)
fg = 13; // 5:1
- else if (happy <= 25) // very unhappy
+ else if (stress_lvl >= 250000)
fg = 12; // 4:1
- else if (happy <= 50) // unhappy
- fg = 4; // 4:0
- else if (happy < 75) // fine
+ else if (stress_lvl >= 100000)
fg = 14; // 6:1
- else if (happy < 125) // quite content
- fg = 6; // 6:0
- else if (happy < 150) // happy
+ else if (stress_lvl >= 0)
fg = 2; // 2:0
- else // ecstatic
+ else
fg = 10; // 2:1
- Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_HAPPINESS], 4 + row, happiness);
+ Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_STRESS], 4 + row, stress);
fg = 15;
if (row_offset == sel_row)
@@ -1232,8 +1236,8 @@ void viewscreen_unitlaborsst::render()
case ALTSORT_PROFESSION_OR_SQUAD:
OutputString(15, x, y, show_squad ? "Squad" : "Profession");
break;
- case ALTSORT_HAPPINESS:
- OutputString(15, x, y, "Happiness");
+ case ALTSORT_STRESS:
+ OutputString(15, x, y, "Stress Level");
break;
case ALTSORT_ARRIVAL:
OutputString(15, x, y, "Arrival");
diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp
index 395e12c14..8a213a6e3 100644
--- a/plugins/rendermax/renderer_light.cpp
+++ b/plugins/rendermax/renderer_light.cpp
@@ -673,6 +673,8 @@ void lightingEngineViewscreen::doOcupancyAndLights()
}
//df::tile_occupancy o = b->OccupancyAt(gpos);
df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,type);
+ bool is_wall=!ENUM_ATTR(tiletype_shape,passable_high,shape);
+ bool is_floor=!ENUM_ATTR(tiletype_shape,passable_low,shape);
df::tiletype_shape_basic basic_shape = ENUM_ATTR(tiletype_shape, basic_shape, shape);
df::tiletype_material tileMat= ENUM_ATTR(tiletype,material,type);
@@ -685,7 +687,7 @@ void lightingEngineViewscreen::doOcupancyAndLights()
{
curCell=rgbf(0,0,0);
}
- else if(shape==df::tiletype_shape::WALL)
+ else if(is_wall)
{
if(tileMat==df::tiletype_material::FROZEN_LIQUID)
applyMaterial(tile,matIce);
@@ -700,8 +702,7 @@ void lightingEngineViewscreen::doOcupancyAndLights()
{
applyMaterial(tile,matLava,(float)d.bits.flow_size/7.0f,(float)d.bits.flow_size/7.0f);
}
- else if(shape==df::tiletype_shape::EMPTY || shape==df::tiletype_shape::RAMP_TOP
- || shape==df::tiletype_shape::STAIR_DOWN || shape==df::tiletype_shape::STAIR_UPDOWN)
+ else if(!is_floor)
{
if(bDown)
{
@@ -749,20 +750,6 @@ void lightingEngineViewscreen::doOcupancyAndLights()
}
}
- //plants
- for(int i=0;iplants.size();i++)
- {
- df::plant* cPlant=block->plants[i];
- if (cPlant->grow_counter <180000) //todo maybe smaller light/oclusion?
- continue;
- df::coord2d pos=cPlant->pos;
- pos=worldToViewportCoord(pos,vp,window2d);
- int tile=getIndex(pos.x,pos.y);
- if(isInRect(pos,vp))
- {
- applyMaterial(tile,419,cPlant->material);
- }
- }
//blood and other goo
for(int i=0;iblock_events.size();i++)
{
diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl
index f04731e3b..8a670718e 100755
--- a/plugins/ruby/codegen.pl
+++ b/plugins/ruby/codegen.pl
@@ -595,6 +595,9 @@ sub get_field_align {
$al = get_field_align($tg);
} elsif ($meta eq 'bytes') {
$al = $field->getAttribute('alignment') || 1;
+ } elsif ($meta eq 'primitive') {
+ my $subtype = $field->getAttribute('ld:subtype');
+ if ($subtype eq 'stl-fstream' and $os eq 'windows') { $al = 8; }
}
return $al;
@@ -735,6 +738,14 @@ sub sizeof {
print "sizeof stl-string on $os\n";
}
print "sizeof stl-string\n";
+ } elsif ($subtype eq 'stl-fstream') { if ($os eq 'linux') {
+ return 284;
+ } elsif ($os eq 'windows') {
+ return 184;
+ } else {
+ print "sizeof stl-fstream on $os\n";
+ }
+ print "sizeof stl-fstream\n";
} else {
print "sizeof primitive $subtype\n";
}
@@ -1011,6 +1022,7 @@ sub render_item_primitive {
my $subtype = $item->getAttribute('ld:subtype');
if ($subtype eq 'stl-string') {
push @lines_rb, "stl_string";
+ } elsif ($subtype eq 'stl-fstream') {
} else {
print "no render primitive $subtype\n";
}
diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp
index ea4becd6a..93d6501f0 100644
--- a/plugins/ruby/ruby.cpp
+++ b/plugins/ruby/ruby.cpp
@@ -543,6 +543,18 @@ static VALUE rb_dfprint_str(VALUE self, VALUE s)
return Qnil;
}
+static VALUE rb_dfprint_color(VALUE self, VALUE c, VALUE s)
+{
+ if (r_console) {
+ color_value old_col = r_console->color();
+ r_console->color(color_value(rb_num2ulong(c)));
+ r_console->print("%s", rb_string_value_ptr(&s));
+ r_console->color(old_col);
+ } else
+ console_proxy->print("%s", rb_string_value_ptr(&s));
+ return Qnil;
+}
+
static VALUE rb_dfprint_err(VALUE self, VALUE s)
{
printerr("%s", rb_string_value_ptr(&s));
@@ -1072,6 +1084,7 @@ static void ruby_bind_dfhack(void) {
rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1);
rb_define_singleton_method(rb_cDFHack, "dfhack_run", RUBY_METHOD_FUNC(rb_dfhack_run), 1);
rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1);
+ rb_define_singleton_method(rb_cDFHack, "print_color", RUBY_METHOD_FUNC(rb_dfprint_color), 2);
rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1);
rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1);
rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1);
diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb
index edce8ac84..af957c76b 100644
--- a/plugins/ruby/ruby.rb
+++ b/plugins/ruby/ruby.rb
@@ -179,7 +179,7 @@ module DFHack
if not onlylastpart
out << name.first_name if name.first_name != ''
if name.nickname != ''
- case respond_to?(:d_init) && d_init.nickname_dwarf
+ case respond_to?(:d_init) && d_init.nickname[gametype]
when :REPLACE_ALL; return "`#{name.nickname}'"
when :REPLACE_FIRST; out.pop
end
diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp
index e1cedbfe3..74c717816 100644
--- a/plugins/strangemood.cpp
+++ b/plugins/strangemood.cpp
@@ -631,7 +631,11 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
// If no mood type was specified, pick one randomly
if (type == mood_type::None)
{
- if (rng.df_trandom(100) > unit->status.happiness)
+ if (soul && (
+ (soul->personality.stress_level >= 500000) ||
+ (soul->personality.stress_level >= 250000 && !rng.df_trandom(2)) ||
+ (soul->personality.stress_level >= 100000 && !rng.df_trandom(10))
+ ))
{
switch (rng.df_trandom(2))
{
@@ -691,7 +695,6 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
unit->relations.mood_copy = unit->mood;
Gui::showAutoAnnouncement(announcement_type::STRANGE_MOOD, unit->pos, msg, color, bright);
- unit->status.happiness = 100;
// TODO: make sure unit drops any wrestle items
unit->job.mood_timeout = 50000;
unit->flags1.bits.has_mood = true;
@@ -1144,7 +1147,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
{
if ((job->job_type == job_type::StrangeMoodBrooding) && (rng.df_trandom(2)))
{
- switch (rng.df_trandom(3))
+ switch (rng.df_trandom(2))
{
case 0:
job->job_items.push_back(item = new df::job_item());
@@ -1159,10 +1162,6 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
item->flags2.bits.body_part = true;
item->quantity = 1;
break;
- case 2:
- // in older versions, they would request additional skulls
- // in 0.34.11, the request becomes "nothing"
- break;
}
}
else
diff --git a/plugins/trackstop.cpp b/plugins/trackstop.cpp
new file mode 100644
index 000000000..4676a1b52
--- /dev/null
+++ b/plugins/trackstop.cpp
@@ -0,0 +1,301 @@
+/*
+ * Trackstop plugin.
+ * Shows track stop friction and direction in its 'q' menu.
+ */
+
+#include "uicommon.h"
+#include "LuaTools.h"
+
+#include "df/building_rollersst.h"
+#include "df/building_trapst.h"
+#include "df/viewscreen_dwarfmodest.h"
+
+#include "modules/Gui.h"
+
+using namespace DFHack;
+using namespace std;
+
+using df::global::world;
+using df::global::ui;
+using df::building_rollersst;
+using df::building_trapst;
+using df::enums::trap_type::trap_type;
+using df::enums::screw_pump_direction::screw_pump_direction;
+
+DFHACK_PLUGIN("trackstop");
+
+#define AUTOENABLE false
+DFHACK_PLUGIN_IS_ENABLED(enabled);
+
+
+/*
+ * Interface hooks
+ */
+struct trackstop_hook : public df::viewscreen_dwarfmodest {
+ typedef df::viewscreen_dwarfmodest interpose_base;
+
+ enum Friction {
+ Lowest = 10,
+ Low = 50,
+ Medium = 500,
+ High = 10000,
+ Highest = 50000
+ };
+
+ building_trapst *get_selected_trackstop() {
+ if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || ui->main.mode != ui_sidebar_mode::QueryBuilding) {
+ return nullptr;
+ }
+
+ building_trapst *ts = virtual_cast(world->selected_building);
+ if (ts && ts->trap_type == trap_type::TrackStop && ts->construction_stage) {
+ return ts;
+ }
+
+ return nullptr;
+ }
+
+ bool handleInput(set *input) {
+ building_trapst *ts = get_selected_trackstop();
+ if (!ts) {
+ return false;
+ }
+
+ if (input->count(interface_key::BUILDING_TRACK_STOP_DUMP)) {
+ // Change track stop dump direction.
+ // There might be a more elegant way to do this.
+
+ if (!ts->use_dump) {
+ // No -> North
+ ts->use_dump = 1;
+ ts->dump_x_shift = 0;
+ ts->dump_y_shift = -1;
+ } else if (ts->dump_x_shift == 0 && ts->dump_y_shift == -1) {
+ // North -> South
+ ts->dump_x_shift = 0;
+ ts->dump_y_shift = 1;
+ } else if (ts->dump_x_shift == 0 && ts->dump_y_shift == 1) {
+ // South -> East
+ ts->dump_x_shift = 1;
+ ts->dump_y_shift = 0;
+ } else if (ts->dump_x_shift == 1 && ts->dump_y_shift == 0) {
+ // East -> West
+ ts->dump_x_shift = -1;
+ ts->dump_y_shift = 0;
+ } else {
+ // West (or Elsewhere) -> No
+ ts->use_dump = 0;
+ ts->dump_x_shift = 0;
+ ts->dump_y_shift = 0;
+ }
+
+ return true;
+ } else if (input->count(interface_key::BUILDING_TRACK_STOP_FRICTION_UP)) {
+ ts->friction = (
+ (ts->friction < Friction::Lowest)? Friction::Lowest:
+ (ts->friction < Friction::Low)? Friction::Low:
+ (ts->friction < Friction::Medium)? Friction::Medium:
+ (ts->friction < Friction::High)? Friction::High:
+ (ts->friction < Friction::Highest)? Friction::Highest:
+ ts->friction
+ );
+
+ return true;
+ } else if (input->count(interface_key::BUILDING_TRACK_STOP_FRICTION_DOWN)) {
+ ts->friction = (
+ (ts->friction > Friction::Highest)? Friction::Highest:
+ (ts->friction > Friction::High)? Friction::High:
+ (ts->friction > Friction::Medium)? Friction::Medium:
+ (ts->friction > Friction::Low)? Friction::Low:
+ (ts->friction > Friction::Lowest)? Friction::Lowest:
+ ts->friction
+ );
+
+ return true;
+ }
+
+ return false;
+ }
+
+ DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) {
+ if (!handleInput(input)) {
+ INTERPOSE_NEXT(feed)(input);
+ }
+ }
+
+ DEFINE_VMETHOD_INTERPOSE(void, render, ()) {
+ INTERPOSE_NEXT(render)();
+
+ building_trapst *ts = get_selected_trackstop();
+ if (ts) {
+ auto dims = Gui::getDwarfmodeViewDims();
+ int left_margin = dims.menu_x1 + 1;
+ int x = left_margin;
+ int y = dims.y1 + 1;
+
+ OutputString(COLOR_WHITE, x, y, "Track Stop", true, left_margin);
+
+ y += 3;
+ OutputString(COLOR_WHITE, x, y, "Friction: ", false);
+ OutputString(COLOR_WHITE, x, y, (
+ (ts->friction <= Friction::Lowest)? "Lowest":
+ (ts->friction <= Friction::Low)? "Low":
+ (ts->friction <= Friction::Medium)? "Medium":
+ (ts->friction <= Friction::High)? "High":
+ "Highest"
+ ), true, left_margin);
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_FRICTION_DOWN));
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_FRICTION_UP));
+ OutputString(COLOR_WHITE, x, y, ": Change Friction", true, left_margin);
+
+ y += 1;
+
+ OutputString(COLOR_WHITE, x, y, "Dump on arrival: ", false);
+ OutputString(COLOR_WHITE, x, y, (
+ (!ts->use_dump)? "No":
+ (ts->dump_x_shift == 0 && ts->dump_y_shift == -1)? "North":
+ (ts->dump_x_shift == 0 && ts->dump_y_shift == 1)? "South":
+ (ts->dump_x_shift == 1 && ts->dump_y_shift == 0)? "East":
+ (ts->dump_x_shift == -1 && ts->dump_y_shift == 0)? "West":
+ "Elsewhere"
+ ), true, left_margin);
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_DUMP));
+ OutputString(COLOR_WHITE, x, y, ": Activate/change direction", true, left_margin);
+ }
+ }
+};
+
+struct roller_hook : public df::viewscreen_dwarfmodest {
+ typedef df::viewscreen_dwarfmodest interpose_base;
+
+ enum Speed {
+ Lowest = 10000,
+ Low = 20000,
+ Medium = 30000,
+ High = 40000,
+ Highest = 50000
+ };
+
+ building_rollersst *get_selected_roller() {
+ if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || ui->main.mode != ui_sidebar_mode::QueryBuilding) {
+ return nullptr;
+ }
+
+ building_rollersst *roller = virtual_cast(world->selected_building);
+ if (roller && roller->construction_stage) {
+ return roller;
+ }
+
+ return nullptr;
+ }
+
+ bool handleInput(set *input) {
+ building_rollersst *roller = get_selected_roller();
+ if (!roller) {
+ return false;
+ }
+
+ if (input->count(interface_key::BUILDING_ORIENT_NONE)) {
+ // Flip roller orientation.
+ // Long rollers can only be oriented along their length.
+ // Todo: Only add 1 to 1x1 rollers: x ^= ((x&1)<<1)|1
+ // Todo: This could have been elegant without all the casting,
+ // but as an enum it might be better off listing each case.
+ roller->direction = (df::enums::screw_pump_direction::screw_pump_direction)(((int8_t)roller->direction) ^ 2);
+ return true;
+ } else if (input->count(interface_key::BUILDING_ROLLERS_SPEED_UP)) {
+ if (roller->speed < Speed::Highest) {
+ roller->speed += Speed::Lowest;
+ }
+
+ return true;
+ } else if (input->count(interface_key::BUILDING_ROLLERS_SPEED_DOWN)) {
+ if (roller->speed > Speed::Lowest) {
+ roller->speed -= Speed::Lowest;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) {
+ if (!handleInput(input)) {
+ INTERPOSE_NEXT(feed)(input);
+ }
+ }
+
+ DEFINE_VMETHOD_INTERPOSE(void, render, ()) {
+ INTERPOSE_NEXT(render)();
+
+ building_rollersst *roller = get_selected_roller();
+ if (roller) {
+ auto dims = Gui::getDwarfmodeViewDims();
+ int left_margin = dims.menu_x1 + 1;
+ int x = left_margin;
+ int y = dims.y1 + 6;
+
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ORIENT_NONE));
+ OutputString(COLOR_WHITE, x, y, ": Rolls ", false);
+ OutputString(COLOR_WHITE, x, y, (
+ (roller->direction == screw_pump_direction::FromNorth)? "Southward":
+ (roller->direction == screw_pump_direction::FromEast)? "Westward":
+ (roller->direction == screw_pump_direction::FromSouth)? "Northward":
+ (roller->direction == screw_pump_direction::FromWest)? "Eastward":
+ ""
+ ), true, left_margin);
+
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ROLLERS_SPEED_DOWN));
+ OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ROLLERS_SPEED_UP));
+ OutputString(COLOR_WHITE, x, y, ": ");
+ OutputString(COLOR_WHITE, x, y, (
+ (roller->speed <= Speed::Lowest)? "Lowest":
+ (roller->speed <= Speed::Low)? "Low":
+ (roller->speed <= Speed::Medium)? "Medium":
+ (roller->speed <= Speed::High)? "High":
+ "Highest"
+ ));
+ OutputString(COLOR_WHITE, x, y, " Speed", true, left_margin);
+ }
+ }
+};
+
+IMPLEMENT_VMETHOD_INTERPOSE(trackstop_hook, feed);
+IMPLEMENT_VMETHOD_INTERPOSE(trackstop_hook, render);
+IMPLEMENT_VMETHOD_INTERPOSE(roller_hook, feed);
+IMPLEMENT_VMETHOD_INTERPOSE(roller_hook, render);
+
+
+DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) {
+ // Accept the "enable trackstop" / "disable trackstop" commands.
+ if (enable != enabled) {
+ // Check for global variables that, if missing, result in total failure.
+ // Missing enabler and ui_menu_width also produce visible effects, but not nearly as severe.
+ // This could be moved to the plugin_init step, but that's louder for no real benefit.
+ if (!(gps && ui && world)) {
+ out.printerr("trackstop: Missing required global variables.\n");
+ return CR_FAILURE;
+ }
+
+ if (!INTERPOSE_HOOK(trackstop_hook, feed).apply(enable) ||
+ !INTERPOSE_HOOK(trackstop_hook, render).apply(enable) ||
+ !INTERPOSE_HOOK(roller_hook, feed).apply(enable) ||
+ !INTERPOSE_HOOK(roller_hook, render).apply(enable)) {
+ out.printerr("Could not %s trackstop hooks!\n", enable? "insert": "remove");
+ return CR_FAILURE;
+ }
+
+ enabled = enable;
+ }
+
+ return CR_OK;
+}
+
+DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) {
+ return plugin_enable(out, AUTOENABLE);
+}
+
+DFhackCExport command_result plugin_shutdown(color_ostream &out) {
+ return plugin_enable(out, false);
+}
diff --git a/plugins/tweak/tweak.cpp b/plugins/tweak/tweak.cpp
index def0d83d5..d49fbe2da 100644
--- a/plugins/tweak/tweak.cpp
+++ b/plugins/tweak/tweak.cpp
@@ -363,6 +363,7 @@ struct dimension_cloth_hook : df::item_clothst {
IMPLEMENT_VMETHOD_INTERPOSE(dimension_cloth_hook, subtractDimension);
+/*
// Unit updates are executed based on an action divisor variable,
// which is computed from the alive unit count and has range 10-100.
static int adjust_unit_divisor(int value) {
@@ -541,6 +542,7 @@ struct military_training_ct_hook : df::activity_event_combat_trainingst {
}
}
};
+*/
/*
IMPLEMENT_VMETHOD_INTERPOSE(military_training_ct_hook, process);
diff --git a/scripts/devel/find-offsets.lua b/scripts/devel/find-offsets.lua
index d8b3406f9..ae99adbca 100644
--- a/scripts/devel/find-offsets.lua
+++ b/scripts/devel/find-offsets.lua
@@ -131,15 +131,24 @@ local function zoomed_searcher(startn, end_or_sz)
end
local finder_searches = {}
-local function exec_finder(finder, names)
+local function exec_finder(finder, names, validators)
if type(names) ~= 'table' then
names = { names }
end
+ if type(validators) ~= 'table' then
+ validators = { validators }
+ end
local search = force_scan['all']
- for _,v in ipairs(names) do
+ for k,v in ipairs(names) do
if force_scan[v] or not is_known(v) then
table.insert(finder_searches, v)
search = true
+ elseif validators[k] then
+ if not validators[k](df.global[v]) then
+ dfhack.printerr('Validation failed for '..v..', will try to find again')
+ table.insert(finder_searches, v)
+ search = true
+ end
end
end
if search then
@@ -505,6 +514,7 @@ end
local function is_valid_world(world)
if not ms.is_valid_vector(world.units.all, 4)
+ or not ms.is_valid_vector(world.units.active, 4)
or not ms.is_valid_vector(world.units.bad, 4)
or not ms.is_valid_vector(world.history.figures, 4)
or not ms.is_valid_vector(world.features.map_features, 4)
@@ -776,11 +786,6 @@ end
--
local function find_current_weather()
- print('\nPlease load the save previously processed with prepare-save.')
- if not utils.prompt_yes_no('Proceed?', true) then
- return
- end
-
local zone
if os_type == 'windows' then
zone = zoomed_searcher('crime_next_id', 512)
@@ -839,7 +844,14 @@ local function find_ui_selected_unit()
end
for i,unit in ipairs(df.global.world.units.active) do
- dfhack.units.setNickname(unit, i)
+ -- This function does a lot of things and accesses histfigs, souls and so on:
+ --dfhack.units.setNickname(unit, i)
+
+ -- Instead use just a simple bit of code that only requires the start of the
+ -- unit to be valid. It may not work properly with vampires or reset later
+ -- if unpaused, but is sufficient for this script and won't crash:
+ unit.name.nickname = tostring(i)
+ unit.name.has_name = true
end
local addr = searcher:find_menu_cursor([[
@@ -1506,17 +1518,23 @@ 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_enabler, 'enabler')
-exec_finder(find_gps, 'gps')
+exec_finder(find_d_init, 'd_init', is_valid_d_init)
+exec_finder(find_enabler, 'enabler', is_valid_enabler)
+exec_finder(find_gps, 'gps', is_valid_gps)
print('\nCompound globals (need loaded world):\n')
-exec_finder(find_world, 'world')
-exec_finder(find_ui, 'ui')
+print('\nPlease load the save previously processed with prepare-save.')
+if not utils.prompt_yes_no('Proceed?', true) then
+ searcher:reset()
+ return
+end
+
+exec_finder(find_world, 'world', is_valid_world)
+exec_finder(find_ui, 'ui', is_valid_ui)
exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus')
exec_finder(find_ui_build_selector, 'ui_build_selector')
-exec_finder(find_init, 'init')
+exec_finder(find_init, 'init', is_valid_init)
print('\nPrimitive globals:\n')
diff --git a/scripts/drain-aquifer.lua b/scripts/drain-aquifer.lua
new file mode 100644
index 000000000..0c153acaf
--- /dev/null
+++ b/scripts/drain-aquifer.lua
@@ -0,0 +1,33 @@
+-- Remove all aquifers from the map
+
+local function drain()
+ local layers = {}
+ local layer_count = 0
+ local tile_count = 0
+
+ for k, block in ipairs(df.global.world.map.map_blocks) do
+ if block.flags.has_aquifer then
+ block.flags.has_aquifer = false
+ block.flags.check_aquifer = false
+
+ for x, row in ipairs(block.designation) do
+ for y, tile in ipairs(row) do
+ if tile.water_table then
+ tile.water_table = false
+ tile_count = tile_count + 1
+ end
+ end
+ end
+
+ if not layers[block.map_pos.z] then
+ layers[block.map_pos.z] = true
+ layer_count = layer_count + 1
+ end
+ end
+ end
+
+ print("Cleared "..tile_count.." aquifer tile"..((tile_count ~= 1) and "s" or "")..
+ " in "..layer_count.." layer"..((layer_count ~= 1) and "s" or "")..".")
+end
+
+drain(...)
diff --git a/scripts/drainaquifer.rb b/scripts/drainaquifer.rb
deleted file mode 100644
index 169ea94fe..000000000
--- a/scripts/drainaquifer.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# remove all aquifers from the map
-
-count = 0
-df.each_map_block { |b|
- if b.designation[0][0].water_table or b.designation[8][8].water_table
- count += 1
- df.each_map_block_z(b.map_pos.z) { |bz|
- bz.designation.each { |dx| dx.each { |dy| dy.water_table = false } }
- }
- end
-}
-
-puts "cleared #{count} aquifer#{'s' if count > 1}"
diff --git a/scripts/forum-dwarves.lua b/scripts/forum-dwarves.lua
index 0b8828dfc..21f8d1abc 100644
--- a/scripts/forum-dwarves.lua
+++ b/scripts/forum-dwarves.lua
@@ -31,7 +31,7 @@ target file:
will be displayed if the script is successful.
character encoding:
The text will likely be using system-default encoding, and as such
- will likely NOT display special characters (eg:,,) correctly. To
+ will likely NOT display special characters (eg:È,ı,Á) correctly. To
fix this, you need to modify the character set that you are reading
the document with. 'Notepad++' is a freely available program which
can do this using the following steps:
@@ -99,23 +99,16 @@ local function format_for_forum(strin)
end
if flerb == 'textviewer' then
- print(scrn)
- printall(scrn)
- local lines = scrn.formatted_text
+ local lines = scrn.src_text
local line = ""
if lines ~= nil then
local log = io.open('forumdwarves.txt', 'a')
log:write("[color=silver]")
for n,x in ipairs(lines) do
- print(x)
- printall(x)
- print(x.text)
- printall(x.text)
- if (x ~= nil) and (x.text ~= nil) then
- log:write(format_for_forum(x.text), ' ')
- --log:write(x[0],'\n')
- end
+ if x ~= nil and x.value ~= nil then
+ log:write(format_for_forum(x.value), ' ')
+ end
end
log:write("[/color]\n")
log:close()
diff --git a/scripts/repeat.lua b/scripts/repeat.lua
index fe9afb026..0c08d8fa8 100644
--- a/scripts/repeat.lua
+++ b/scripts/repeat.lua
@@ -24,13 +24,13 @@ if args.help then
print this help message
repeat -cancel bob
cancels the repetition with the name bob
- repeat -name jim -time delay -timeUnits units -printResult true -command printArgs 3 1 2
+ repeat -name jim -time delay -timeUnits units -printResult true -command [ printArgs 3 1 2 ]
-name sets the name for the purposes of cancelling and making sure you don't schedule the same repeating event twice
if not specified, it's set to the first argument after -command
-time delay -timeUnits units
delay is some positive integer
units is some valid time unit for dfhack.timeout(delay,timeUnits,function)
- -command ...
+ -command [ ... ]
specify the command to be run
]])
return
diff --git a/scripts/superdwarf.rb b/scripts/superdwarf.rb
index 93e299362..4aaa08231 100644
--- a/scripts/superdwarf.rb
+++ b/scripts/superdwarf.rb
@@ -16,9 +16,12 @@ when 'add'
$superdwarf_ids.each { |id|
if u = df.unit_find(id) and not u.flags1.dead
# faster walk/work
- if u.counters.job_counter > 0
- u.counters.job_counter = 0
- end
+ u.actions.each { |a|
+ case a.type
+ when :Move, :Climb, :Job, :Job2
+ a.data.send(a.type.to_s.downcase).timer = 0
+ end
+ }
# no sleep
if u.counters2.sleepiness_timer > 10000