From ab4a38415a52dd494d3187931c6f1b48682829ad Mon Sep 17 00:00:00 2001
From: Eric Wald
Date: Wed, 24 Apr 2013 11:22:19 -0600
Subject: [PATCH 001/107] Building base material undump script.
Based on an idea in https://github.com/peterix/dfhack/issues/119
---
scripts/undump-buildings.lua | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 scripts/undump-buildings.lua
diff --git a/scripts/undump-buildings.lua b/scripts/undump-buildings.lua
new file mode 100644
index 000000000..aa2af0397
--- /dev/null
+++ b/scripts/undump-buildings.lua
@@ -0,0 +1,29 @@
+-- Undesignates building base materials for dumping.
+function undump_buildings()
+ local buildings = df.global.world.buildings.all
+ local undumped = 0
+ for i = 0, #buildings - 1 do
+ local building = buildings[i]
+ -- Zones and stockpiles don't have the contained_items field.
+ if not (df.building_civzonest:is_instance(building) or
+ df.building_stockpilest:is_instance(building)) then
+ local items = building.contained_items
+ for j = 0, #items - 1 do
+ local contained = items[j]
+ if contained.use_mode == 2 and contained.item.flags.dump then
+ -- print(building, contained.item)
+ undumped = undumped + 1
+ contained.item.flags.dump = false
+ end
+ end
+ end
+ end
+
+ if undumped > 0 then
+ local s = "s"
+ if undumped == 1 then s = "" end
+ print("Undumped "..undumped.." item"..s..".")
+ end
+end
+
+undump_buildings()
From f41525665047e58b40cf3e9409fe5a2c02e9723a Mon Sep 17 00:00:00 2001
From: Eric Wald
Date: Sun, 5 May 2013 16:48:12 -0600
Subject: [PATCH 002/107] Better way to check for the contained_items field.
---
scripts/undump-buildings.lua | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/scripts/undump-buildings.lua b/scripts/undump-buildings.lua
index aa2af0397..fc1511343 100644
--- a/scripts/undump-buildings.lua
+++ b/scripts/undump-buildings.lua
@@ -5,8 +5,7 @@ function undump_buildings()
for i = 0, #buildings - 1 do
local building = buildings[i]
-- Zones and stockpiles don't have the contained_items field.
- if not (df.building_civzonest:is_instance(building) or
- df.building_stockpilest:is_instance(building)) then
+ if df.building_actual:is_instance(building) then
local items = building.contained_items
for j = 0, #items - 1 do
local contained = items[j]
From 9e85aa4280cf28efa75a8529b48dcada196ee6ca Mon Sep 17 00:00:00 2001
From: Tareq A Khandaker
Date: Mon, 6 May 2013 23:58:21 -0400
Subject: [PATCH 003/107] Ignore Mac OS X .DS_Store files
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index b4a578ec0..76e60f329 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,3 +60,6 @@ build/CPack*Config.cmake
# vim swap files
*.swp
+
+# Mac OS X .DS_Store files
+.DS_Store
From 6546ed2d5a7d92c5eed5b65a9301fe51c5d542cc Mon Sep 17 00:00:00 2001
From: jj
Date: Wed, 30 Oct 2013 14:31:33 +0100
Subject: [PATCH 004/107] scripts/digfort: better csv handling
---
scripts/digfort.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/digfort.rb b/scripts/digfort.rb
index 5def15c47..cf3625357 100644
--- a/scripts/digfort.rb
+++ b/scripts/digfort.rb
@@ -8,7 +8,7 @@ if df.cursor.x == -30000
end
tiles = planfile.lines.map { |l|
- l.sub(/#.*/, '').split(';').map { |t| t.strip }
+ l.sub(/#.*/, '').split(/[;,]/).map { |t| t = t.strip ; ((t[0] == ?") ? t[1..-2] : t) }
}
x = x0 = df.cursor.x
From 9dc9a3f33a1b109f0544d8c05142166d76aaed23 Mon Sep 17 00:00:00 2001
From: jj
Date: Wed, 30 Oct 2013 15:25:16 +0100
Subject: [PATCH 005/107] digfort: add start() comment
---
NEWS | 3 +++
Readme.rst | 7 ++++++-
scripts/digfort.rb | 42 +++++++++++++++++++++++++++++++++++++-----
3 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/NEWS b/NEWS
index 2a7508f5f..e52e76998 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@ DFHack future
- Is not yet known.
+ Misc improvements:
+ - digfort: improved csv parsing, add start() comment handling
+
DFHack v0.34.11-r4
New commands:
diff --git a/Readme.rst b/Readme.rst
index a2b400206..fe7fb741e 100644
--- a/Readme.rst
+++ b/Readme.rst
@@ -2171,7 +2171,12 @@ 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.
+One comment in the file may contain the phrase ``start(3,5)``. It is interpreted
+as an offset for the pattern: instead of starting at the cursor, it will start
+3 tiles left and 5 tiles up from the cursor.
+
+The script takes the plan filename, starting from the root df folder (where
+Dwarf Fortress.exe is found).
invasion-now
============
diff --git a/scripts/digfort.rb b/scripts/digfort.rb
index cf3625357..74db423d3 100644
--- a/scripts/digfort.rb
+++ b/scripts/digfort.rb
@@ -4,15 +4,47 @@ raise "usage: digfort " if not $script_args[0]
planfile = File.read($script_args[0])
if df.cursor.x == -30000
- raise "place the game cursor to the top-left corner of the design"
+ puts "place the game cursor to the top-left corner of the design"
+ throw :script_finished
end
-tiles = planfile.lines.map { |l|
- l.sub(/#.*/, '').split(/[;,]/).map { |t| t = t.strip ; ((t[0] == ?") ? t[1..-2] : t) }
+# a sample CSV file
+# empty lines are ignored
+# a special comment with start(dx, dy) means the actual patterns starts at cursor.x-dx, cursor.y-dy
+# the CSV file should be saved in the main DF directory, alongside of Dwarf Fortress.exe
+sample_csv = <
Date: Wed, 30 Oct 2013 14:19:52 -0500
Subject: [PATCH 006/107] Update "plants" plugin
* Add "createplant" command, creates a shrub or sapling at the cursor.
* Put help text in command definitions so CR_WRONG_USAGE works properly
---
plugins/plants.cpp | 165 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 127 insertions(+), 38 deletions(-)
diff --git a/plugins/plants.cpp b/plugins/plants.cpp
index 89a3257fa..9b845edc6 100644
--- a/plugins/plants.cpp
+++ b/plugins/plants.cpp
@@ -22,25 +22,8 @@ using df::global::world;
const uint32_t sapling_to_tree_threshold = 120 * 28 * 12 * 3; // 3 years
-command_result df_grow (color_ostream &out, vector & parameters);
-command_result df_immolate (color_ostream &out, vector & parameters);
-command_result df_extirpate (color_ostream &out, vector & parameters);
-
DFHACK_PLUGIN("plants");
-DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands)
-{
- commands.push_back(PluginCommand("grow", "Grows saplings into trees (with active cursor, only the targetted one).", df_grow));
- commands.push_back(PluginCommand("immolate", "Set plants on fire (under cursor, 'shrubs', 'trees' or 'all').", df_immolate));
- commands.push_back(PluginCommand("extirpate", "Kill plants (same mechanics as immolate).", df_extirpate));
- return CR_OK;
-}
-
-DFhackCExport command_result plugin_shutdown ( color_ostream &out )
-{
- return CR_OK;
-}
-
enum do_what
{
do_immolate,
@@ -84,19 +67,8 @@ static bool getoptions( vector & parameters, bool & shrubs, bool & tree
*/
static command_result immolations (color_ostream &out, do_what what, bool shrubs, bool trees, bool help)
{
- static const char * what1 = "destroys";
- static const char * what2 = "burns";
if(help)
- {
- out.print("Without any options, this command %s a plant under the cursor.\n"
- "Options:\n"
- "shrubs - affect all shrubs\n"
- "trees - affect all trees\n"
- "all - affect all plants\n",
- what == do_immolate? what2 : what1
- );
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
CoreSuspender suspend;
if (!Maps::IsValid())
{
@@ -165,26 +137,26 @@ command_result df_immolate (color_ostream &out, vector & parameters)
bool shrubs = false, trees = false, help = false;
if(getoptions(parameters,shrubs,trees,help))
{
- return immolations(out,do_immolate,shrubs,trees, help);
+ return immolations(out,do_immolate,shrubs,trees,help);
}
else
{
out.printerr("Invalid parameter!\n");
- return CR_FAILURE;
+ return CR_WRONG_USAGE;
}
}
command_result df_extirpate (color_ostream &out, vector & parameters)
{
bool shrubs = false, trees = false, help = false;
- if(getoptions(parameters,shrubs,trees, help))
+ if(getoptions(parameters,shrubs,trees,help))
{
- return immolations(out,do_extirpate,shrubs,trees, help);
+ return immolations(out,do_extirpate,shrubs,trees,help);
}
else
{
out.printerr("Invalid parameter!\n");
- return CR_FAILURE;
+ return CR_WRONG_USAGE;
}
}
@@ -193,10 +165,7 @@ command_result df_grow (color_ostream &out, vector & parameters)
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
- {
- out.print("This command turns all living saplings into full-grown trees.\n");
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
}
CoreSuspender suspend;
@@ -245,3 +214,123 @@ command_result df_grow (color_ostream &out, vector & parameters)
return CR_OK;
}
+command_result df_createplant (color_ostream &out, vector & parameters)
+{
+ if ((parameters.size() != 1) || (parameters[0] == "help" || parameters[0] == "?"))
+ return CR_WRONG_USAGE;
+
+ CoreSuspender suspend;
+
+ if (!Maps::IsValid())
+ {
+ out.printerr("Map is not available!\n");
+ return CR_FAILURE;
+ }
+
+ int32_t x,y,z;
+ if(!Gui::getCursorCoords(x,y,z))
+ {
+ out.printerr("No cursor detected - please place the cursor over the location in which you wish to create a new plant.\n");
+ return CR_FAILURE;
+ }
+ df::map_block *map = Maps::getTileBlock(x, y, z);
+ if (!map)
+ {
+ out.printerr("Invalid location selected!\n");
+ return CR_FAILURE;
+ }
+ int tx = x & 15, ty = y & 15;
+ int mat = tileMaterial(map->tiletype[tx][ty]);
+ if ((tileShape(map->tiletype[tx][ty]) != tiletype_shape::FLOOR) || ((mat != tiletype_material::SOIL) && (mat != tiletype_material::GRASS_DARK) && (mat != tiletype_material::GRASS_LIGHT)))
+ {
+ out.printerr("Plants can only be placed on dirt or grass floors!\n");
+ return CR_FAILURE;
+ }
+
+ int plant_id = -1;
+ df::plant_raw *plant_raw = NULL;
+ for (size_t i = 0; i < world->raws.plants.all.size(); i++)
+ {
+ plant_raw = world->raws.plants.all[i];
+ if (plant_raw->id == parameters[0])
+ {
+ plant_id = i;
+ break;
+ }
+ }
+ if (plant_id == -1)
+ {
+ out.printerr("Invalid plant ID specified!\n");
+ return CR_FAILURE;
+ }
+ if (plant_raw->flags.is_set(plant_raw_flags::GRASS))
+ {
+ out.printerr("You cannot plant grass using this command.\n");
+ return CR_FAILURE;
+ }
+
+ df::plant *plant = new df::plant;
+ if (plant_raw->flags.is_set(plant_raw_flags::TREE))
+ plant->hitpoints = 400000;
+ else
+ {
+ plant->hitpoints = 100000;
+ plant->flags.bits.is_shrub = 1;
+ }
+ // for now, always set "watery" for WET-permitted plants, even if they're spawned away from water
+ // the proper method would be to actually look for nearby water features, but it's not clear exactly how that works
+ if (plant_raw->flags.is_set(plant_raw_flags::WET))
+ plant->flags.bits.watery = 1;
+ plant->material = plant_id;
+ plant->pos.x = x;
+ plant->pos.y = y;
+ plant->pos.z = z;
+ plant->update_order = rand() % 10;
+ plant->temperature_tile_tick = -1;
+ plant->temperature_tile = 60001;
+ plant->min_safe_temp = 9900;
+ plant->max_safe_temp = 60001;
+
+ world->plants.all.push_back(plant);
+ switch (plant->flags.whole & 3)
+ {
+ case 0: world->plants.tree_dry.push_back(plant); break;
+ case 1: world->plants.tree_wet.push_back(plant); break;
+ case 2: world->plants.shrub_dry.push_back(plant); break;
+ case 3: world->plants.shrub_wet.push_back(plant); break;
+ }
+ map->plants.push_back(plant);
+ if (plant->flags.bits.is_shrub)
+ map->tiletype[tx][ty] = tiletype::Shrub;
+ else
+ map->tiletype[tx][ty] = tiletype::Sapling;
+
+ return CR_OK;
+}
+
+DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands)
+{
+ commands.push_back(PluginCommand("grow", "Grows saplings into trees (with active cursor, only the targetted one).", df_grow,
+ "This command turns all living saplings on the map into full-grown trees.\n"));
+ commands.push_back(PluginCommand("immolate", "Set plants on fire (under cursor, 'shrubs', 'trees' or 'all').", df_immolate,
+ "Without any options, this command burns a plant under the cursor.\n"
+ "Options:\n"
+ "shrubs - affect all shrubs\n"
+ "trees - affect all trees\n"
+ "all - affect all plants\n"));
+ commands.push_back(PluginCommand("extirpate", "Kill plants (same mechanics as immolate).", df_extirpate,
+ "Without any options, this command destroys a plant under the cursor.\n"
+ "Options:\n"
+ "shrubs - affect all shrubs\n"
+ "trees - affect all trees\n"
+ "all - affect all plants\n"));
+ commands.push_back(PluginCommand("createplant", "Create a new plant at the cursor.", df_createplant,
+ "Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n"
+ "Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n"));
+ return CR_OK;
+}
+
+DFhackCExport command_result plugin_shutdown ( color_ostream &out )
+{
+ return CR_OK;
+}
\ No newline at end of file
From ff939e36bbb8e46d6a1f7405908bbd0ae00754d3 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Wed, 30 Oct 2013 14:25:35 -0500
Subject: [PATCH 007/107] Missed parameter in plugin command init
---
plugins/plants.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/plugins/plants.cpp b/plugins/plants.cpp
index 9b845edc6..9be4c193a 100644
--- a/plugins/plants.cpp
+++ b/plugins/plants.cpp
@@ -310,21 +310,21 @@ command_result df_createplant (color_ostream &out, vector & parameters)
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands)
{
- commands.push_back(PluginCommand("grow", "Grows saplings into trees (with active cursor, only the targetted one).", df_grow,
+ commands.push_back(PluginCommand("grow", "Grows saplings into trees (with active cursor, only the targetted one).", df_grow, false,
"This command turns all living saplings on the map into full-grown trees.\n"));
- commands.push_back(PluginCommand("immolate", "Set plants on fire (under cursor, 'shrubs', 'trees' or 'all').", df_immolate,
+ commands.push_back(PluginCommand("immolate", "Set plants on fire (under cursor, 'shrubs', 'trees' or 'all').", df_immolate, false,
"Without any options, this command burns a plant under the cursor.\n"
"Options:\n"
"shrubs - affect all shrubs\n"
"trees - affect all trees\n"
"all - affect all plants\n"));
- commands.push_back(PluginCommand("extirpate", "Kill plants (same mechanics as immolate).", df_extirpate,
+ commands.push_back(PluginCommand("extirpate", "Kill plants (same mechanics as immolate).", df_extirpate, false,
"Without any options, this command destroys a plant under the cursor.\n"
"Options:\n"
"shrubs - affect all shrubs\n"
"trees - affect all trees\n"
"all - affect all plants\n"));
- commands.push_back(PluginCommand("createplant", "Create a new plant at the cursor.", df_createplant,
+ commands.push_back(PluginCommand("createplant", "Create a new plant at the cursor.", df_createplant, false,
"Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n"
"Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n"));
return CR_OK;
From 463bb8d498259ad0c0ec5d81378892f2c9b96779 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Wed, 30 Oct 2013 15:58:14 -0500
Subject: [PATCH 008/107] Update plugins to use CR_WRONG_USAGE where
appropriate
---
plugins/autotrade.cpp | 2 +-
plugins/buildingplan.cpp | 2 +-
plugins/createitem.cpp | 19 +++++++---------
plugins/lair.cpp | 3 ++-
plugins/liquids.cpp | 25 ++++++++------------
plugins/probe.cpp | 12 +++++++---
plugins/regrass.cpp | 3 ++-
plugins/reveal.cpp | 49 +++++++++++++++++-----------------------
plugins/seedwatch.cpp | 11 ++++++---
plugins/showmood.cpp | 3 ++-
plugins/stocks.cpp | 2 +-
plugins/tubefill.cpp | 11 ++++-----
12 files changed, 68 insertions(+), 74 deletions(-)
diff --git a/plugins/autotrade.cpp b/plugins/autotrade.cpp
index 2d3cf0ed2..0d11af9f0 100644
--- a/plugins/autotrade.cpp
+++ b/plugins/autotrade.cpp
@@ -626,7 +626,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector & parameters);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands)
{
- commands.push_back(PluginCommand("createitem", "Create arbitrary item at the selected unit's feet.", df_createitem));
+ commands.push_back(PluginCommand("createitem", "Create arbitrary item at the selected unit's feet.", df_createitem, false,
+ "Syntax: createitem - [count]\n"
+ "
- - Item token for what you wish to create, as specified in custom\n"
+ " reactions. If the item has no subtype, omit the :NONE.\n"
+ " - The material you want the item to be made of, as specified\n"
+ " in custom reactions. For REMAINS, FISH, FISH_RAW, VERMIN,\n"
+ " PET, and EGG, replace this with a creature ID and caste.\n"
+ " [count] - How many of the item you wish to create.\n"));
return CR_OK;
}
@@ -91,17 +98,7 @@ command_result df_createitem (color_ostream &out, vector & parameters)
int count = 1;
if ((parameters.size() < 2) || (parameters.size() > 3))
- {
- out.print("Syntax: createitem
- [count]\n"
- "
- - Item token for what you wish to create, as specified in custom\n"
- " reactions. If the item has no subtype, omit the :NONE.\n"
- " - The material you want the item to be made of, as specified\n"
- " in custom reactions. For REMAINS, FISH, FISH_RAW, VERMIN,\n"
- " PET, and EGG, replace this with a creature ID and caste.\n"
- " [count] - How many of the item you wish to create.\n"
- );
return CR_WRONG_USAGE;
- }
item_str = parameters[0];
material_str = parameters[1];
diff --git a/plugins/lair.cpp b/plugins/lair.cpp
index 0c2912761..cafbbcc33 100644
--- a/plugins/lair.cpp
+++ b/plugins/lair.cpp
@@ -58,6 +58,7 @@ command_result lair(color_ostream &out, std::vector & params)
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands)
{
- commands.push_back(PluginCommand("lair","Mark the map as a monster lair, preventing item scatter ('lair reset' reverts that).",lair));
+ commands.push_back(PluginCommand("lair","Mark the map as a monster lair, preventing item scatter.",lair, false,
+ "Usage: 'lair' to mark entire map as monster lair, 'lair reset' to undo the operation.\n"));
return CR_OK;
}
diff --git a/plugins/liquids.cpp b/plugins/liquids.cpp
index 15ae84c9b..712955b86 100644
--- a/plugins/liquids.cpp
+++ b/plugins/liquids.cpp
@@ -59,12 +59,16 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector & parameters)
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
- {
- out.print( "This tool allows placing magma, water and other similar things.\n"
- "It is interactive and further help is available when you run it.\n"
- "The settings will be remembered until dfhack is closed and you can call\n"
- "'liquids-here' (mapped to a hotkey) to paint liquids at the cursor position\n"
- "without the need to go back to the dfhack console.\n");
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
}
if (!Maps::IsValid())
@@ -375,11 +372,7 @@ command_result df_liquids_here (color_ostream &out, vector & parameters
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
- {
- out << "This command is supposed to be mapped to a hotkey." << endl;
- out << "It will use the current/last parameters set in liquids." << endl;
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
}
out.print("Run liquids-here with these parameters: ");
diff --git a/plugins/probe.cpp b/plugins/probe.cpp
index 353caea0f..28b62c24e 100644
--- a/plugins/probe.cpp
+++ b/plugins/probe.cpp
@@ -46,13 +46,19 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector & parameters);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands)
{
- commands.push_back(PluginCommand("regrass", "Regrows surface grass.", df_regrass));
+ commands.push_back(PluginCommand("regrass", "Regrows surface grass.", df_regrass, false,
+ "Specify parameter 'max' to set all grass types to full density, otherwise only one type of grass will be restored per tile.\n"));
return CR_OK;
}
diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp
index 2c51295a8..91ab75686 100644
--- a/plugins/reveal.cpp
+++ b/plugins/reveal.cpp
@@ -76,12 +76,23 @@ DFHACK_PLUGIN("reveal");
DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands)
{
- commands.push_back(PluginCommand("reveal","Reveal the map. 'reveal hell' will also reveal hell. 'reveal demon' won't pause.",reveal));
- commands.push_back(PluginCommand("unreveal","Revert the map to its previous state.",unreveal));
- commands.push_back(PluginCommand("revtoggle","Reveal/unreveal depending on state.",revtoggle));
- commands.push_back(PluginCommand("revflood","Hide all, reveal all tiles reachable from cursor position.",revflood));
- commands.push_back(PluginCommand("revforget", "Forget the current reveal data, allowing to use reveal again.",revforget));
- commands.push_back(PluginCommand("nopause","Disable pausing (doesn't affect pause forced by reveal).",nopause));
+ commands.push_back(PluginCommand("reveal","Reveal the map. 'reveal hell' will also reveal hell. 'reveal demon' won't pause.",reveal,false,
+ "Reveals the map, by default ignoring hell.\n"
+ "Options:\n"
+ "hell - also reveal hell, while forcing the game to pause.\n"
+ "demon - reveal hell, do not pause.\n"));
+ commands.push_back(PluginCommand("unreveal","Revert the map to its previous state.",unreveal,false,
+ "Reverts the previous reveal operation, hiding the map again.\n"));
+ commands.push_back(PluginCommand("revtoggle","Reveal/unreveal depending on state.",revtoggle,false,
+ "Toggles between reveal and unreveal.\n"));
+ commands.push_back(PluginCommand("revflood","Hide all, reveal all tiles reachable from cursor position.",revflood,false,
+ "This command hides the whole map. Then, starting from the cursor,\n"
+ "reveals all accessible tiles. Allows repairing parma-revealed maps.\n"));
+ commands.push_back(PluginCommand("revforget", "Forget the current reveal data, allowing to use reveal again.",revforget,false,
+ "Forget the current reveal data, allowing to use reveal again.\n"));
+ commands.push_back(PluginCommand("nopause","Disable pausing (doesn't affect pause forced by reveal).",nopause,false,
+ "Disable pausing (doesn't affect pause forced by reveal).\n"
+ "Activate with 'nopause 1', deactivate with 'nopause 0'.\n"));
return CR_OK;
}
@@ -160,14 +171,7 @@ command_result reveal(color_ostream &out, vector & params)
if(params[i]=="hell")
no_hell = false;
else if(params[i] == "help" || params[i] == "?")
- {
- out.print("Reveals the map, by default ignoring hell.\n"
- "Options:\n"
- "hell - also reveal hell, while forcing the game to pause.\n"
- "demon - reveal hell, do not pause.\n"
- );
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
}
if(params.size() && params[0] == "hell")
{
@@ -254,10 +258,7 @@ command_result unreveal(color_ostream &out, vector & params)
for(size_t i = 0; i < params.size();i++)
{
if(params[i] == "help" || params[i] == "?")
- {
- out.print("Reverts the previous reveal operation, hiding the map again.\n");
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
}
if(!revealed)
{
@@ -330,12 +331,7 @@ command_result revflood(color_ostream &out, vector & params)
for(size_t i = 0; i < params.size();i++)
{
if(params[i] == "help" || params[i] == "?")
- {
- out.print("This command hides the whole map. Then, starting from the cursor,\n"
- "reveals all accessible tiles. Allows repairing parma-revealed maps.\n"
- );
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
}
CoreSuspender suspend;
uint32_t x_max,y_max,z_max;
@@ -482,10 +478,7 @@ command_result revforget(color_ostream &out, vector & params)
for(size_t i = 0; i < params.size();i++)
{
if(params[i] == "help" || params[i] == "?")
- {
- out.print("Forget the current reveal data, allowing to use reveal again.\n");
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
}
if(!revealed)
{
diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp
index 19fa9b154..e0c2daeae 100755
--- a/plugins/seedwatch.cpp
+++ b/plugins/seedwatch.cpp
@@ -130,11 +130,15 @@ command_result df_seedwatch(color_ostream &out, vector& parameters)
{
case 0:
printHelp(out);
- break;
+ return CR_WRONG_USAGE;
+
case 1:
par = parameters[0];
- if(par == "help") printHelp(out);
- else if(par == "?") printHelp(out);
+ if ((par == "help") || (par == "?"))
+ {
+ printHelp(out);
+ return CR_WRONG_USAGE;
+ }
else if(par == "start")
{
running = true;
@@ -239,6 +243,7 @@ command_result df_seedwatch(color_ostream &out, vector& parameters)
break;
default:
printHelp(out);
+ return CR_WRONG_USAGE;
break;
}
diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp
index 7a3662f82..ba6df034a 100644
--- a/plugins/showmood.cpp
+++ b/plugins/showmood.cpp
@@ -292,7 +292,8 @@ DFHACK_PLUGIN("showmood");
DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands)
{
- commands.push_back(PluginCommand("showmood", "Shows items needed for current strange mood.", df_showmood));
+ commands.push_back(PluginCommand("showmood", "Shows items needed for current strange mood.", df_showmood, false,
+ "Run this command without any parameters to display information on the currently active Strange Mood."));
return CR_OK;
}
diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp
index 10594fe9e..7873595ff 100644
--- a/plugins/stocks.cpp
+++ b/plugins/stocks.cpp
@@ -1002,7 +1002,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands)
{
- commands.push_back(PluginCommand("tubefill","Fill in all the adamantine tubes again.",tubefill));
+ commands.push_back(PluginCommand("tubefill","Fill in all the adamantine tubes again.",tubefill, false,
+ "Replenishes mined out adamantine and hollow adamantine tubes.\n"
+ "May cause !!FUN!!\n"));
return CR_OK;
}
@@ -39,12 +41,7 @@ command_result tubefill(color_ostream &out, std::vector & params)
for(size_t i = 0; i < params.size();i++)
{
if(params[i] == "help" || params[i] == "?")
- {
- out.print("Replenishes mined out adamantine and hollow adamantine tubes.\n"
- "May cause !!FUN!!\n"
- );
- return CR_OK;
- }
+ return CR_WRONG_USAGE;
}
CoreSuspender suspend;
From 883d89bb68d458877645669be6a155e4e45cc447 Mon Sep 17 00:00:00 2001
From: jj
Date: Sat, 2 Nov 2013 18:54:29 +0100
Subject: [PATCH 009/107] plants: move all commands as "plant" subcommands,
update NEWS/Readme
---
NEWS | 4 +-
Readme.rst | 32 ++++++++------
plugins/plants.cpp | 108 +++++++++++++++++++++++++++------------------
3 files changed, 87 insertions(+), 57 deletions(-)
diff --git a/NEWS b/NEWS
index 2a7508f5f..d3745b8e7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
DFHack future
- - Is not yet known.
+ New commands:
+ - move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
+ - 'plant create' - spawn a new shrub under the cursor
DFHack v0.34.11-r4
diff --git a/Readme.rst b/Readme.rst
index a2b400206..4a425c775 100644
--- a/Readme.rst
+++ b/Readme.rst
@@ -679,26 +679,30 @@ 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.
+plant
+-----
+A tool for creating shrubs, growing, or getting rid of them.
-Options:
+Subcommands:
+ :create: Create a new shrub/sapling.
+ :grow: Make saplings grow into trees.
+ :extirpate: Kills trees and shrubs, turning them into ashes instantly.
+ :immolate: Similar to extirpate, but sets the plants on fire instead. The
+fires can and *will* spread ;)
+
+``create`` creates a new sapling under the cursor. Takes a raw ID as
+argument (e.g. TOWER_CAP). The cursor must be located on a dirt or grass
+floor tile.
+
+``grow`` works on the sapling under the cursor, and turns it into a tree.
+Works on all shrubs of the map if the cursor is hidden.
+``extirpate`` and ``immolate`` work only on the plant under the cursor.
+For mass effects, use one of the additional 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 ;)
diff --git a/plugins/plants.cpp b/plugins/plants.cpp
index 9be4c193a..46bafad5f 100644
--- a/plugins/plants.cpp
+++ b/plugins/plants.cpp
@@ -65,10 +65,8 @@ static bool getoptions( vector & parameters, bool & shrubs, bool & tree
* And he cursed the plants and trees for their bloodless wood, turning them into ash and smoldering ruin.
* Armok was pleased and great temples were built by the dwarves, for they shared his hatred for trees and plants.
*/
-static command_result immolations (color_ostream &out, do_what what, bool shrubs, bool trees, bool help)
+static command_result immolations (color_ostream &out, do_what what, bool shrubs, bool trees)
{
- if(help)
- return CR_WRONG_USAGE;
CoreSuspender suspend;
if (!Maps::IsValid())
{
@@ -132,32 +130,32 @@ static command_result immolations (color_ostream &out, do_what what, bool shrubs
return CR_OK;
}
-command_result df_immolate (color_ostream &out, vector & parameters)
+command_result df_immolate (color_ostream &out, vector & parameters, do_what what)
{
bool shrubs = false, trees = false, help = false;
- if(getoptions(parameters,shrubs,trees,help))
+ if (getoptions(parameters, shrubs, trees, help) && !help)
{
- return immolations(out,do_immolate,shrubs,trees,help);
+ return immolations(out, what, shrubs, trees);
}
- else
- {
- out.printerr("Invalid parameter!\n");
- return CR_WRONG_USAGE;
- }
-}
-command_result df_extirpate (color_ostream &out, vector & parameters)
-{
- bool shrubs = false, trees = false, help = false;
- if(getoptions(parameters,shrubs,trees,help))
- {
- return immolations(out,do_extirpate,shrubs,trees,help);
- }
+ string mode;
+ if (what == do_immolate)
+ mode = "Set plants on fire";
else
- {
+ mode = "Kill plants";
+
+ if (!help)
out.printerr("Invalid parameter!\n");
- return CR_WRONG_USAGE;
- }
+
+ out << "Usage:\n" <<
+ mode << " (under cursor, 'shrubs', 'trees' or 'all').\n"
+ "Without any options, this command acts on the plant under the cursor.\n"
+ "Options:\n"
+ "shrubs - affect all shrubs\n"
+ "trees - affect all trees\n"
+ "all - affect all plants\n";
+
+ return CR_OK;
}
command_result df_grow (color_ostream &out, vector & parameters)
@@ -165,8 +163,14 @@ command_result df_grow (color_ostream &out, vector & parameters)
for(size_t i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
- return CR_WRONG_USAGE;
+ {
+ out << "Usage:\n"
+ "This command turns all living saplings on the map into full-grown trees.\n"
+ "With active cursor, work on the targetted one only.\n";
+ return CR_OK;
+ }
}
+
CoreSuspender suspend;
if (!Maps::IsValid())
@@ -217,7 +221,13 @@ command_result df_grow (color_ostream &out, vector & parameters)
command_result df_createplant (color_ostream &out, vector & parameters)
{
if ((parameters.size() != 1) || (parameters[0] == "help" || parameters[0] == "?"))
- return CR_WRONG_USAGE;
+ {
+ out << "Usage:\n"
+ "Create a new plant at the cursor.\n"
+ "Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n"
+ "Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n";
+ return CR_OK;
+ }
CoreSuspender suspend;
@@ -308,29 +318,43 @@ command_result df_createplant (color_ostream &out, vector & parameters)
return CR_OK;
}
+command_result df_plant (color_ostream &out, vector & parameters)
+{
+ if (parameters.size() >= 1)
+ {
+ if (parameters[0] == "grow") {
+ parameters.erase(parameters.begin());
+ return df_grow(out, parameters);
+ } else
+ if (parameters[0] == "immolate") {
+ parameters.erase(parameters.begin());
+ return df_immolate(out, parameters, do_immolate);
+ } else
+ if (parameters[0] == "extirpate") {
+ parameters.erase(parameters.begin());
+ return df_immolate(out, parameters, do_extirpate);
+ } else
+ if (parameters[0] == "create") {
+ parameters.erase(parameters.begin());
+ return df_createplant(out, parameters);
+ }
+ }
+ return CR_WRONG_USAGE;
+}
+
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands)
{
- commands.push_back(PluginCommand("grow", "Grows saplings into trees (with active cursor, only the targetted one).", df_grow, false,
- "This command turns all living saplings on the map into full-grown trees.\n"));
- commands.push_back(PluginCommand("immolate", "Set plants on fire (under cursor, 'shrubs', 'trees' or 'all').", df_immolate, false,
- "Without any options, this command burns a plant under the cursor.\n"
- "Options:\n"
- "shrubs - affect all shrubs\n"
- "trees - affect all trees\n"
- "all - affect all plants\n"));
- commands.push_back(PluginCommand("extirpate", "Kill plants (same mechanics as immolate).", df_extirpate, false,
- "Without any options, this command destroys a plant under the cursor.\n"
- "Options:\n"
- "shrubs - affect all shrubs\n"
- "trees - affect all trees\n"
- "all - affect all plants\n"));
- commands.push_back(PluginCommand("createplant", "Create a new plant at the cursor.", df_createplant, false,
- "Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n"
- "Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n"));
+ commands.push_back(PluginCommand("plant", "Plant creation and removal.", df_plant, false,
+ "Command to create, grow or remove plants on the map. For more details, check the subcommand help :\n"
+ "plant grow help - Grows saplings into trees.\n"
+ "plant immolate help - Set plants on fire.\n"
+ "plant extirpate help - Kill plants.\n"
+ "plant create help - Create a new plant.\n"));
+
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
-}
\ No newline at end of file
+}
From 4d2e5b80bf4c48403fb5491929d45e46652aeca1 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Mon, 4 Nov 2013 14:55:31 -0600
Subject: [PATCH 010/107] Use df::allocate here for proper compatibility with
40d and earlier
---
plugins/plants.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/plants.cpp b/plugins/plants.cpp
index 9be4c193a..c0cd427a9 100644
--- a/plugins/plants.cpp
+++ b/plugins/plants.cpp
@@ -269,7 +269,7 @@ command_result df_createplant (color_ostream &out, vector & parameters)
return CR_FAILURE;
}
- df::plant *plant = new df::plant;
+ df::plant *plant = df::allocate();
if (plant_raw->flags.is_set(plant_raw_flags::TREE))
plant->hitpoints = 400000;
else
From 04dce1aa7f8c162eed8c2d98271f3d0f15b4a7e5 Mon Sep 17 00:00:00 2001
From: jj
Date: Tue, 5 Nov 2013 00:31:32 +0100
Subject: [PATCH 011/107] MaterialInfo: fix decoding for COAL subtypes
---
library/modules/Materials.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp
index 5bda6a9bc..60a35fa2b 100644
--- a/library/modules/Materials.cpp
+++ b/library/modules/Materials.cpp
@@ -190,6 +190,13 @@ bool MaterialInfo::find(const std::vector &items)
}
else if (items.size() == 2)
{
+ if (items[0] == "COAL" && findBuiltin(items[0])) {
+ if (items[1] == "COKE")
+ this->index = 0;
+ else if (items[1] == "CHARCOAL")
+ this->index = 1;
+ return true;
+ }
if (items[1] == "NONE" && findBuiltin(items[0]))
return true;
if (findPlant(items[0], items[1]))
From b2819ea8693c479f6d578579a98579e73ef47aec Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Thu, 7 Nov 2013 11:40:26 +0400
Subject: [PATCH 012/107] Fix wrong argument iteration bounds in
dfhack.matinfo.find().
---
library/LuaApi.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 3f93e912d..bdc0c39e5 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -565,7 +565,7 @@ static int dfhack_matinfo_find(lua_State *state)
{
std::vector tokens;
- for (int i = 1; i < argc; i++)
+ for (int i = 1; i <= argc; i++)
tokens.push_back(luaL_checkstring(state, i));
info.find(tokens);
From 7ce5831257c241eee4c3e87873f6e9aa37a271a6 Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Thu, 7 Nov 2013 11:58:11 +0400
Subject: [PATCH 013/107] Get rid of the std exception.
---
plugins/eventful.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/plugins/eventful.cpp b/plugins/eventful.cpp
index f93c15edc..97527c0ed 100644
--- a/plugins/eventful.cpp
+++ b/plugins/eventful.cpp
@@ -1,4 +1,5 @@
#include "Core.h"
+#include "Error.h"
#include
#include
#include
@@ -228,8 +229,8 @@ static void enableEvent(int evType,int freq)
{
if (freq < 0)
return;
- if (evType < 0 || evType >= EventManager::EventType::EVENT_MAX || evType == EventManager::EventType::TICK)
- throw std::runtime_error("invalid event type to enable");
+ CHECK_INVALID_ARGUMENT(evType >= 0 && evType < EventManager::EventType::EVENT_MAX &&
+ evType != EventManager::EventType::TICK);
EventManager::EventHandler::callback_t fun_ptr = eventHandlers[evType];
EventManager::EventType::EventType typeToEnable=static_cast(evType);
From 53bd1125153f5625249aebca85f37632a3688da7 Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Thu, 7 Nov 2013 12:27:53 +0400
Subject: [PATCH 014/107] Hide fake historical figures from legends xml export.
---
library/modules/World.cpp | 40 +++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/library/modules/World.cpp b/library/modules/World.cpp
index 9ae4266b2..0f54e68eb 100644
--- a/library/modules/World.cpp
+++ b/library/modules/World.cpp
@@ -42,11 +42,14 @@ using namespace std;
#include "MiscUtils.h"
+#include "VTableInterpose.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"
+#include "df/viewscreen_legendsst.h"
using namespace DFHack;
using namespace df::enums;
@@ -154,14 +157,51 @@ static PersistentDataItem dataFromHFig(df::historical_figure *hfig)
return PersistentDataItem(hfig->id, hfig->name.first_name, &hfig->name.nickname, hfig->name.words);
}
+// Hide fake histfigs from legends xml export
+static bool in_export_xml = false;
+
+struct hide_fake_histfigs_hook : df::viewscreen_legendsst {
+ typedef df::viewscreen_legendsst interpose_base;
+
+ DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input))
+ {
+ if (input->count(interface_key::LEGENDS_EXPORT_XML))
+ {
+ auto &figs = df::historical_figure::get_vector();
+
+ auto it = figs.begin();
+ while (it != figs.end() && (*it)->id <= -100)
+ ++it;
+
+ // Move our histfigs to a temporary vector
+ std::vector fakes(figs.begin(), it);
+ figs.erase(figs.begin(), it);
+ in_export_xml = true;
+
+ INTERPOSE_NEXT(feed)(input);
+
+ in_export_xml = false;
+ figs.insert(figs.begin(), fakes.begin(), fakes.end());
+ }
+ else
+ INTERPOSE_NEXT(feed)(input);
+ }
+};
+
+IMPLEMENT_VMETHOD_INTERPOSE_PRIO(hide_fake_histfigs_hook, feed, -10000);
+
void World::ClearPersistentCache()
{
next_persistent_id = 0;
persistent_index.clear();
+
+ INTERPOSE_HOOK(hide_fake_histfigs_hook, feed).apply(Core::getInstance().isWorldLoaded());
}
static bool BuildPersistentCache()
{
+ if (in_export_xml)
+ return false;
if (next_persistent_id)
return true;
if (!Core::getInstance().isWorldLoaded())
From b872cfc40fd9393c716dbd1873ded7d6c22c2479 Mon Sep 17 00:00:00 2001
From: Lethosor
Date: Wed, 27 Nov 2013 10:11:19 -0500
Subject: [PATCH 015/107] Update Compatibility section
OS X has been supported since 0.34.11r3
---
Readme.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Readme.rst b/Readme.rst
index a2b400206..e1365cbf1 100644
--- a/Readme.rst
+++ b/Readme.rst
@@ -28,15 +28,15 @@ 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.
+DFHack works on Windows XP, Vista, 7, any modern Linux distribution, or OS X
+10.6.8-10.9.
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.
+It is also possible to use the Windows DFHack with Wine under Linux and OS X.
====================
Installation/Removal
From 6e6d830ba6ed61fe3999709dfa52d2fd5269181f Mon Sep 17 00:00:00 2001
From: jj
Date: Tue, 24 Dec 2013 14:08:39 +0100
Subject: [PATCH 016/107] compile.rst: add a note on VS2010 SP1
---
Compile.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Compile.rst b/Compile.rst
index ddff30aeb..87164ed0c 100644
--- a/Compile.rst
+++ b/Compile.rst
@@ -174,6 +174,8 @@ 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.
+You'll also need the Visual Studio 2010 SP1 update.
+
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/
From 540bcc1f468773e4ef1964384d4a775088d15b44 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Fri, 27 Dec 2013 12:53:33 -0600
Subject: [PATCH 017/107] Enhance createitem, can now place items into
containers or buildings
---
plugins/createitem.cpp | 151 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 148 insertions(+), 3 deletions(-)
diff --git a/plugins/createitem.cpp b/plugins/createitem.cpp
index e1bb50b82..6e9cd6ea1 100644
--- a/plugins/createitem.cpp
+++ b/plugins/createitem.cpp
@@ -7,6 +7,7 @@
#include "MiscUtils.h"
#include "modules/Maps.h"
+#include "modules/MapCache.h"
#include "modules/Gui.h"
#include "modules/Items.h"
#include "modules/Materials.h"
@@ -22,6 +23,7 @@
#include "df/creature_raw.h"
#include "df/caste_raw.h"
#include "df/reaction_reagent.h"
+#include "df/reaction_reagent_itemst.h"
#include "df/reaction_product_itemst.h"
using namespace std;
@@ -33,18 +35,27 @@ using df::global::gametype;
DFHACK_PLUGIN("createitem");
+int dest_container = -1, dest_building = -1;
+
command_result df_createitem (color_ostream &out, vector & parameters);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands)
{
- commands.push_back(PluginCommand("createitem", "Create arbitrary item at the selected unit's feet.", df_createitem, false,
+ commands.push_back(PluginCommand("createitem", "Create arbitrary items.", df_createitem, false,
"Syntax: createitem
- [count]\n"
"
- - Item token for what you wish to create, as specified in custom\n"
" reactions. If the item has no subtype, omit the :NONE.\n"
" - The material you want the item to be made of, as specified\n"
" in custom reactions. For REMAINS, FISH, FISH_RAW, VERMIN,\n"
" PET, and EGG, replace this with a creature ID and caste.\n"
- " [count] - How many of the item you wish to create.\n"));
+ " [count] - How many of the item you wish to create.\n"
+ "\n"
+ "By default, items are created at the feet of the selected unit.\n"
+ "\n"
+ "Syntax: createitem \n"
+ " - Where to put subsequently created items.\n"
+ " Valid values are 'floor', 'item', and 'building'.\n"
+ ));
return CR_OK;
}
@@ -61,6 +72,14 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it
bool is_gloves = (prod->item_type == df::item_type::GLOVES);
bool is_shoes = (prod->item_type == df::item_type::SHOES);
+ df::item *container = NULL;
+ df::building *building = NULL;
+ df::reaction_reagent_itemst *reagent = NULL;
+ if (dest_container != -1)
+ container = df::item::find(dest_container);
+ if (dest_building != -1)
+ building = df::building::find(dest_building);
+
prod->produce(unit, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE,
df::historical_entity::find(unit->civ_id),
((*gametype == df::game_type::DWARF_MAIN) || (*gametype == df::game_type::DWARF_RECLAIM)) ? df::world_site::find(ui->site_id) : NULL);
@@ -70,9 +89,28 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it
// otherwise, make a second set because shoes are normally made in pairs
if (is_shoes && out_items.size() == prod->count * 2)
is_shoes = false;
+
+ MapExtras::MapCache mc;
+
for (size_t i = 0; i < out_items.size(); i++)
{
- out_items[i]->moveToGround(unit->pos.x, unit->pos.y, unit->pos.z);
+ bool on_ground = true;
+ if (container)
+ {
+ on_ground = false;
+ out_items[i]->flags.bits.removed = 1;
+ if (!Items::moveToContainer(mc, out_items[i], container))
+ out_items[i]->moveToGround(container->pos.x, container->pos.y, container->pos.z);
+ }
+ if (building)
+ {
+ on_ground = false;
+ out_items[i]->flags.bits.removed = 1;
+ if (!Items::moveToBuilding(mc, out_items[i], (df::building_actual *)building, 0))
+ out_items[i]->moveToGround(building->centerx, building->centery, building->z);
+ }
+ if (on_ground)
+ out_items[i]->moveToGround(unit->pos.x, unit->pos.y, unit->pos.z);
if (is_gloves)
{
// if the reaction creates gloves without handedness, then create 2 sets (left and right)
@@ -97,8 +135,104 @@ command_result df_createitem (color_ostream &out, vector & parameters)
int32_t mat_index = -1;
int count = 1;
+ if (parameters.size() == 1)
+ {
+ if (parameters[0] == "floor")
+ {
+ dest_container = -1;
+ dest_building = -1;
+ out.print("Items created will be placed on the floor.\n");
+ return CR_OK;
+ }
+ else if (parameters[0] == "item")
+ {
+ dest_building = -1;
+ df::item *item = Gui::getSelectedItem(out);
+ if (!item)
+ {
+ out.printerr("You must select a container!\n");
+ return CR_FAILURE;
+ }
+ switch (item->getType())
+ {
+ case df::item_type::FLASK:
+ case df::item_type::BARREL:
+ case df::item_type::BUCKET:
+ case df::item_type::ANIMALTRAP:
+ case df::item_type::BOX:
+ case df::item_type::BIN:
+ case df::item_type::BACKPACK:
+ case df::item_type::QUIVER:
+ break;
+ case df::item_type::TOOL:
+ if (item->hasToolUse(df::tool_uses::LIQUID_CONTAINER))
+ break;
+ if (item->hasToolUse(df::tool_uses::FOOD_STORAGE))
+ break;
+ if (item->hasToolUse(df::tool_uses::SMALL_OBJECT_STORAGE))
+ break;
+ if (item->hasToolUse(df::tool_uses::TRACK_CART))
+ break;
+ default:
+ out.printerr("The selected item cannot be used for item storage!\n");
+ return CR_FAILURE;
+ }
+ dest_container = item->id;
+ string name;
+ item->getItemDescription(&name, 0);
+ out.print("Items created will be placed inside %s.\n", name.c_str());
+ return CR_OK;
+ }
+ else if (parameters[0] == "building")
+ {
+ dest_container = -1;
+ df::building *building = Gui::getSelectedBuilding(out);
+ if (!building)
+ {
+ out.printerr("You must select a building!\n");
+ return CR_FAILURE;
+ }
+ switch (building->getType())
+ {
+ case df::building_type::Coffin:
+ case df::building_type::Furnace:
+ case df::building_type::TradeDepot:
+ case df::building_type::Shop:
+ case df::building_type::Box:
+ case df::building_type::Weaponrack:
+ case df::building_type::Armorstand:
+ case df::building_type::Workshop:
+ case df::building_type::Cabinet:
+ case df::building_type::SiegeEngine:
+ case df::building_type::Trap:
+ case df::building_type::AnimalTrap:
+ case df::building_type::Cage:
+ case df::building_type::Wagon:
+ case df::building_type::NestBox:
+ case df::building_type::Hive:
+ break;
+ default:
+ out.printerr("The selected building cannot be used for item storage!\n");
+ return CR_FAILURE;
+ }
+ if (building->getBuildStage() != building->getMaxBuildStage())
+ {
+ out.printerr("The selected building has not yet been fully constructed!\n");
+ return CR_FAILURE;
+ }
+ dest_building = building->id;
+ string name;
+ building->getName(&name);
+ out.print("Items created will be placed inside %s.\n", name.c_str());
+ return CR_OK;
+ }
+ else
+ return CR_WRONG_USAGE;
+ }
+
if ((parameters.size() < 2) || (parameters.size() > 3))
return CR_WRONG_USAGE;
+
item_str = parameters[0];
material_str = parameters[1];
@@ -250,6 +384,17 @@ command_result df_createitem (color_ostream &out, vector & parameters)
break;
}
+ if ((dest_container != -1) && !df::item::find(dest_container))
+ {
+ dest_container = -1;
+ out.printerr("Previously selected container no longer exists - item will be placed on the floor.\n");
+ }
+ if ((dest_building != -1) && !df::building::find(dest_building))
+ {
+ dest_building = -1;
+ out.printerr("Previously selected building no longer exists - item will be placed on the floor.\n");
+ }
+
bool result = makeItem(prod, unit);
delete prod;
if (!result)
From 69fc2bec6df2b1246bd81cabe776264411d2e607 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Fri, 27 Dec 2013 13:01:34 -0600
Subject: [PATCH 018/107] A bit of cleanup
---
plugins/createitem.cpp | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/plugins/createitem.cpp b/plugins/createitem.cpp
index 6e9cd6ea1..e75e82584 100644
--- a/plugins/createitem.cpp
+++ b/plugins/createitem.cpp
@@ -23,7 +23,6 @@
#include "df/creature_raw.h"
#include "df/caste_raw.h"
#include "df/reaction_reagent.h"
-#include "df/reaction_reagent_itemst.h"
#include "df/reaction_product_itemst.h"
using namespace std;
@@ -50,11 +49,13 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector\n"
- " - Where to put subsequently created items.\n"
- " Valid values are 'floor', 'item', and 'building'.\n"
+ "To use this command, you must select which unit will create the items.\n"
+ "By default, items created will be placed at that unit's feet.\n"
+ "To change this, type 'createitem '.\n"
+ "Valid destinations:\n"
+ "* floor - Place items on floor beneath maker's feet.\n"
+ "* item - Place items inside selected container.\n"
+ "* building - Place items inside selected building.\n"
));
return CR_OK;
}
@@ -74,7 +75,6 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it
df::item *container = NULL;
df::building *building = NULL;
- df::reaction_reagent_itemst *reagent = NULL;
if (dest_container != -1)
container = df::item::find(dest_container);
if (dest_building != -1)
From 91a7280ac34c651c4b231cf82751594a8036477d Mon Sep 17 00:00:00 2001
From: Quietust
Date: Fri, 3 Jan 2014 16:42:24 -0600
Subject: [PATCH 019/107] Need to include tool_uses.h
---
plugins/createitem.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/plugins/createitem.cpp b/plugins/createitem.cpp
index e75e82584..42f242074 100644
--- a/plugins/createitem.cpp
+++ b/plugins/createitem.cpp
@@ -24,6 +24,7 @@
#include "df/caste_raw.h"
#include "df/reaction_reagent.h"
#include "df/reaction_product_itemst.h"
+#include "df/tool_uses.h"
using namespace std;
using namespace DFHack;
From 6af362db399a84fe0598da71de81d0df8d344205 Mon Sep 17 00:00:00 2001
From: jj
Date: Sat, 4 Jan 2014 01:55:48 +0100
Subject: [PATCH 020/107] add scripts/devel/spawn-unit-helper.rb
---
scripts/devel/spawn-unit-helper.rb | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 scripts/devel/spawn-unit-helper.rb
diff --git a/scripts/devel/spawn-unit-helper.rb b/scripts/devel/spawn-unit-helper.rb
new file mode 100644
index 000000000..999fe7e45
--- /dev/null
+++ b/scripts/devel/spawn-unit-helper.rb
@@ -0,0 +1,29 @@
+# setup stuff to allow arena creature spawn after a mode change
+
+df.world.arena_spawn.race.clear
+df.world.arena_spawn.caste.clear
+
+df.world.raws.creatures.all.length.times { |r_idx|
+ df.world.raws.creatures.all[r_idx].caste.length.times { |c_idx|
+ df.world.arena_spawn.race << r_idx
+ df.world.arena_spawn.caste << c_idx
+ }
+}
+
+df.world.arena_spawn.creature_cnt[df.world.arena_spawn.race.length-1] = 0
+
+puts <
Date: Sat, 4 Jan 2014 15:04:56 +0100
Subject: [PATCH 021/107] ruby: codegen unit.caste_tg helper
---
plugins/ruby/codegen.pl | 30 ++++++++++++++++++++++++------
1 file changed, 24 insertions(+), 6 deletions(-)
diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl
index a4bebb7e6..f04731e3b 100755
--- a/plugins/ruby/codegen.pl
+++ b/plugins/ruby/codegen.pl
@@ -296,11 +296,32 @@ sub render_struct_field_refs {
sub render_field_reftarget {
my ($parent, $field, $name, $reftg) = @_;
- my $aux = $field->getAttribute('aux-value');
- return if ($aux); # TODO
-
my $tg = $global_types{$reftg};
return if (!$tg);
+
+ my $tgname = "${name}_tg";
+ $tgname =~ s/_id(.?.?)_tg/_tg$1/;
+
+ my $aux = $field->getAttribute('aux-value');
+ if ($aux) {
+ # minimal support (aims is unit.caste_tg)
+ return if $aux !~ /^\$\$\.[^_][\w\.]+$/;
+ $aux =~ s/\$\$\.//;
+
+ for my $codehelper ($tg->findnodes('child::code-helper')) {
+ if ($codehelper->getAttribute('name') eq 'find-instance') {
+ my $helper = $codehelper->textContent;
+ $helper =~ s/\$global/df/;
+ $helper =~ s/\$\$/$aux/;
+ $helper =~ s/\$/$name/;
+ if ($helper =~ /^[\w\.\[\]]+$/) {
+ push @lines_rb, "def $tgname ; $helper ; end";
+ }
+ }
+ }
+ return;
+ }
+
my $tgvec = $tg->getAttribute('instance-vector');
return if (!$tgvec);
my $idx = $tg->getAttribute('key-field');
@@ -308,9 +329,6 @@ sub render_field_reftarget {
$tgvec =~ s/^\$global/df/;
return if $tgvec !~ /^[\w\.]+$/;
- my $tgname = "${name}_tg";
- $tgname =~ s/_id(.?.?)_tg/_tg$1/;
-
for my $othername (map { $_->getAttribute('name') } $parent->findnodes('child::ld:field')) {
$tgname .= '_' if ($othername and $tgname eq $othername);
}
From 04f88ef8fb93be0027f8a3f9fdebb09265ca2f1b Mon Sep 17 00:00:00 2001
From: jj
Date: Sun, 5 Jan 2014 02:29:01 +0100
Subject: [PATCH 022/107] showmood: fix count of gotten items
---
plugins/showmood.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp
index ba6df034a..57bfd9658 100644
--- a/plugins/showmood.cpp
+++ b/plugins/showmood.cpp
@@ -169,7 +169,7 @@ command_result df_showmood (color_ostream &out, vector & parameters)
// total amount of stuff fetched so far
int count_got = 0;
for (size_t i = 0; i < job->items.size(); i++)
- count_got += job->items[i]->item->getTotalDimension();
+ count_got += 1; // XXX thread may need job->items[i]->item->getTotalDimension()
for (size_t i = 0; i < job->job_items.size(); i++)
{
From 24fbf570e62880eb87b786d6cc53bbfe124106f6 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Sat, 18 Jan 2014 22:45:42 -0600
Subject: [PATCH 023/107] Add "strangemood" plugin, lets you trigger a strange
mood
---
plugins/CMakeLists.txt | 1 +
plugins/strangemood.cpp | 1238 +++++++++++++++++++++++++++++++++++++++
2 files changed, 1239 insertions(+)
create mode 100644 plugins/strangemood.cpp
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index 4de3c68bf..29acba8a1 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -162,6 +162,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(treefarm treefarm.cpp)
DFHACK_PLUGIN(cleanconst cleanconst.cpp)
DFHACK_PLUGIN(3dveins 3dveins.cpp)
+ DFHACK_PLUGIN(strangemood strangemood.cpp)
endif()
# this is the skeleton plugin. If you want to make your own, make a copy and then change it
diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp
new file mode 100644
index 000000000..5dd12c627
--- /dev/null
+++ b/plugins/strangemood.cpp
@@ -0,0 +1,1238 @@
+// Triggers a strange mood using (mostly) the same logic used in-game
+
+#include "Core.h"
+#include "Console.h"
+#include "Export.h"
+#include "PluginManager.h"
+#include "modules/Gui.h"
+#include "modules/Units.h"
+#include "modules/Items.h"
+#include "modules/Job.h"
+#include "modules/Translation.h"
+
+#include "DataDefs.h"
+#include "df/d_init.h"
+#include "df/world.h"
+#include "df/ui.h"
+#include "df/unit.h"
+#include "df/unit_soul.h"
+#include "df/unit_skill.h"
+#include "df/unit_preference.h"
+#include "df/map_block.h"
+#include "df/job.h"
+#include "df/job_item.h"
+#include "df/historical_entity.h"
+#include "df/entity_raw.h"
+#include "df/builtin_mats.h"
+#include "df/general_ref_unit_workerst.h"
+
+using std::string;
+using std::vector;
+using namespace DFHack;
+using namespace df::enums;
+
+using df::global::world;
+using df::global::ui;
+using df::global::d_init;
+using df::global::created_item_count;
+using df::global::created_item_type;
+using df::global::created_item_subtype;
+using df::global::created_item_mattype;
+using df::global::created_item_matindex;
+
+bool isUnitMoodable (df::unit *unit)
+{
+ if (!Units::isCitizen(unit))
+ return false;
+ if (!unit->status2.limbs_grasp_count)
+ return false;
+ if (unit->mood != mood_type::None)
+ return false;
+ if (!ENUM_ATTR(profession,moodable,unit->profession))
+ return false;
+ return true;
+}
+
+df::job_skill getMoodSkill (df::unit *unit)
+{
+ if (!unit->status.current_soul)
+ return job_skill::STONECRAFT;
+ df::historical_entity *civ = df::historical_entity::find(unit->civ_id);
+ df::unit_soul *soul = unit->status.current_soul;
+ vector skills;
+ df::skill_rating level = skill_rating::Dabbling;
+ for (size_t i = 0; i < soul->skills.size(); i++)
+ {
+ df::unit_skill *skill = soul->skills[i];
+ switch (skill->id)
+ {
+ case job_skill::MINING:
+ case job_skill::CARPENTRY:
+ case job_skill::DETAILSTONE:
+ case job_skill::MASONRY:
+ case job_skill::TANNER:
+ case job_skill::WEAVING:
+ case job_skill::CLOTHESMAKING:
+ case job_skill::FORGE_WEAPON:
+ case job_skill::FORGE_ARMOR:
+ case job_skill::FORGE_FURNITURE:
+ case job_skill::CUTGEM:
+ case job_skill::ENCRUSTGEM:
+ case job_skill::WOODCRAFT:
+ case job_skill::STONECRAFT:
+ case job_skill::METALCRAFT:
+ case job_skill::GLASSMAKER:
+ case job_skill::LEATHERWORK:
+ case job_skill::BONECARVE:
+ case job_skill::BOWYER:
+ case job_skill::MECHANICS:
+ if (skill->rating > level)
+ {
+ skills.clear();
+ level = skill->rating;
+ }
+ if (skill->rating == level)
+ skills.push_back(skill->id);
+ break;
+ }
+ }
+ if (!skills.size() && civ)
+ {
+ if (civ->entity_raw->jobs.permitted_skill[job_skill::WOODCRAFT])
+ skills.push_back(job_skill::WOODCRAFT);
+ if (civ->entity_raw->jobs.permitted_skill[job_skill::STONECRAFT])
+ skills.push_back(job_skill::STONECRAFT);
+ if (civ->entity_raw->jobs.permitted_skill[job_skill::BONECARVE])
+ skills.push_back(job_skill::BONECARVE);
+ }
+ if (!skills.size())
+ skills.push_back(job_skill::STONECRAFT);
+ return skills[rand() % skills.size()];
+}
+
+int getCreatedMetalBars (int32_t idx)
+{
+ for (size_t i = 0; i < created_item_type->size(); i++)
+ {
+ if (created_item_type->at(i) == item_type::BAR &&
+ created_item_subtype->at(i) == -1 &&
+ created_item_mattype->at(i) == 0 &&
+ created_item_matindex->at(i) == idx)
+ return created_item_count->at(i);
+ }
+ return 0;
+}
+
+void selectWord (const df::world_raws::T_language::T_word_table &table, int32_t &word, df::enum_field &part, int mode)
+{
+ if (table.parts[mode].size())
+ {
+ int offset = rand() % table.parts[mode].size();
+ word = table.words[mode][offset];
+ part = table.parts[mode][offset];
+ }
+ else
+ {
+ word = rand() % world->raws.language.words.size();
+ part = (df::part_of_speech)(rand() % 9);
+ Core::getInstance().getConsole().printerr("Impoverished Word Selector");
+ }
+}
+
+void generateName(df::language_name &output, int language, int mode, const df::world_raws::T_language::T_word_table &table1, const df::world_raws::T_language::T_word_table &table2)
+{
+ for (int i = 0; i < 100; i++)
+ {
+ if (mode != 8 && mode != 9)
+ {
+ output = df::language_name();
+ if (language == -1)
+ language = rand() % world->raws.language.translations.size();
+ output.unknown = mode;
+ output.language = language;
+ }
+ output.has_name = 1;
+ if (output.language == -1)
+ output.language = rand() % world->raws.language.translations.size();
+ int r, r2, r3;
+ switch (mode)
+ {
+ case 0: case 9: case 10:
+ if (mode != 9)
+ {
+ int32_t word; df::enum_field part;
+ output.first_name.clear();
+ selectWord(table1, word, part, 2);
+ if (word >= 0 && word < world->raws.language.words.size())
+ output.first_name = *world->raws.language.translations[language]->words[word];
+ }
+ if (mode != 10)
+ {
+ case 4: case 37: // this is not a typo
+ if (rand() % 2)
+ {
+ selectWord(table2, output.words[0], output.parts_of_speech[0], 0);
+ selectWord(table1, output.words[1], output.parts_of_speech[1], 1);
+ }
+ else
+ {
+ selectWord(table1, output.words[0], output.parts_of_speech[0], 0);
+ selectWord(table2, output.words[1], output.parts_of_speech[1], 1);
+ }
+ }
+ break;
+
+ case 1: case 13: case 20:
+ r = rand() % 3;
+ if (r == 0 || r == 1)
+ {
+ if (rand() % 2)
+ {
+ selectWord(table2, output.words[0], output.parts_of_speech[0], 0);
+ selectWord(table1, output.words[1], output.parts_of_speech[1], 1);
+ }
+ else
+ {
+ selectWord(table1, output.words[0], output.parts_of_speech[0], 0);
+ selectWord(table2, output.words[1], output.parts_of_speech[1], 1);
+ }
+ }
+ if (r == 1 || r == 2)
+ {
+ case 3: case 8: case 11: // this is not a typo either
+ r2 = rand() % 2;
+ if (r2)
+ selectWord(table1, output.words[5], output.parts_of_speech[5], 2);
+ else
+ selectWord(table2, output.words[5], output.parts_of_speech[5], 2);
+ r3 = rand() % 3;
+ if (rand() % 50)
+ r3 = rand() % 2;
+ switch (r3)
+ {
+ case 0:
+ case 2:
+ if (r3 == 2)
+ r2 = rand() % 2;
+ if (r2)
+ selectWord(table2, output.words[6], output.parts_of_speech[6], 5);
+ else
+ selectWord(table1, output.words[6], output.parts_of_speech[6], 5);
+ if (r3 == 0)
+ break;
+ r2 = -r2;
+ case 1:
+ if (r2)
+ selectWord(table1, output.words[2], output.parts_of_speech[2], 3);
+ else
+ selectWord(table2, output.words[2], output.parts_of_speech[2], 3);
+ if (!(rand() % 100))
+ selectWord(table1, output.words[3], output.parts_of_speech[3], 3);
+ break;
+ }
+ }
+ if (rand() % 100)
+ {
+ if (rand() % 2)
+ selectWord(table1, output.words[4], output.parts_of_speech[4], 4);
+ else
+ selectWord(table2, output.words[4], output.parts_of_speech[4], 4);
+ }
+ if ((mode == 3) && (output.parts_of_speech[5] == part_of_speech::Noun) && (output.words[5] != -1) && (world->raws.language.words[output.words[5]]->forms[1].length()))
+ output.parts_of_speech[5] = part_of_speech::NounPlural;
+ break;
+
+ case 2: case 5: case 6: case 12: case 14: case 15: case 16: case 17: case 18: case 19:
+ case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30:
+ case 31: case 32: case 33: case 34: case 35: case 36: case 38: case 39:
+ selectWord(table1, output.words[5], output.parts_of_speech[5], 2);
+ r3 = rand() % 3;
+ if (rand() % 50)
+ r3 = rand() % 2;
+ switch (r3)
+ {
+ case 0:
+ case 2:
+ selectWord(table2, output.words[6], output.parts_of_speech[6], 5);
+ if (r3 == 0)
+ break;
+ case 1:
+ selectWord(table2, output.words[2], output.parts_of_speech[2], 3);
+ if (!(rand() % 100))
+ selectWord(table2, output.words[3], output.parts_of_speech[3], 3);
+ break;
+ }
+ if (rand() % 100)
+ selectWord(table2, output.words[4], output.parts_of_speech[4], 4);
+ break;
+
+ case 7:
+ r = rand() % 3;
+ if (r == 0 || r == 1)
+ {
+ selectWord(table2, output.words[0], output.parts_of_speech[0], 0);
+ selectWord(table1, output.words[1], output.parts_of_speech[1], 1);
+ }
+ if (r == 1 || r == 2)
+ {
+ r2 = rand() % 2;
+ if (r == 2 || r2 == 1)
+ selectWord(table1, output.words[5], output.parts_of_speech[5], 2);
+ else
+ selectWord(table2, output.words[5], output.parts_of_speech[5], 2);
+ r3 = rand() % 3;
+ if (rand() % 50)
+ r3 = rand() % 2;
+ switch (r3)
+ {
+ case 0:
+ case 2:
+ selectWord(table1, output.words[6], output.parts_of_speech[6], 5);
+ if (r3 == 0)
+ break;
+ case 1:
+ selectWord(table2, output.words[2], output.parts_of_speech[2], 3);
+ if (!(rand() % 100))
+ selectWord(table2, output.words[3], output.parts_of_speech[3], 3);
+ break;
+ }
+ }
+ if (rand() % 100)
+ selectWord(table2, output.words[4], output.parts_of_speech[4], 4);
+ break;
+ }
+ if (output.words[2] != -1 && output.words[3] != -1 &&
+ world->raws.language.words[output.words[3]]->adj_dist < world->raws.language.words[output.words[2]]->adj_dist)
+ {
+ std::swap(output.words[2], output.words[3]);
+ std::swap(output.parts_of_speech[2], output.parts_of_speech[3]);
+ }
+ bool next = false;
+ if ((output.parts_of_speech[5] == df::part_of_speech::NounPlural) && (output.parts_of_speech[6] == df::part_of_speech::NounPlural))
+ next = true;
+ if (output.words[0] != -1)
+ {
+ if (output.words[6] == -1) next = true;
+ if (output.words[4] == -1) next = true;
+ if (output.words[2] == -1) next = true;
+ if (output.words[3] == -1) next = true;
+ if (output.words[5] == -1) next = true;
+ }
+ if (output.words[1] != -1)
+ {
+ if (output.words[6] == -1) next = true;
+ if (output.words[4] == -1) next = true;
+ if (output.words[2] == -1) next = true;
+ if (output.words[3] == -1) next = true;
+ if (output.words[5] == -1) next = true;
+ }
+ if (output.words[4] != -1)
+ {
+ if (output.words[6] == -1) next = true;
+ if (output.words[2] == -1) next = true;
+ if (output.words[3] == -1) next = true;
+ if (output.words[5] == -1) next = true;
+ }
+ if (output.words[2] != -1)
+ {
+ if (output.words[6] == -1) next = true;
+ if (output.words[3] == -1) next = true;
+ if (output.words[5] == -1) next = true;
+ }
+ if (output.words[3] != -1)
+ {
+ if (output.words[6] == -1) next = true;
+ if (output.words[5] == -1) next = true;
+ }
+ if (output.words[5] != -1)
+ {
+ if (output.words[6] == -1) next = true;
+ }
+ if (!next)
+ return;
+ }
+}
+
+command_result df_strangemood (color_ostream &out, vector & parameters)
+{
+ if (!Translation::IsValid())
+ {
+ out.printerr("Translation data unavailable!\n");
+ return CR_FAILURE;
+ }
+ bool force = false;
+ df::unit *unit = NULL;
+ df::mood_type type = mood_type::None;
+
+ for (size_t i = 0; i < parameters.size(); i++)
+ {
+ if(parameters[i] == "help" || parameters[i] == "?")
+ return CR_WRONG_USAGE;
+ else if(parameters[i] == "-force")
+ force = true;
+ else if(parameters[i] == "-unit")
+ {
+ unit = DFHack::Gui::getSelectedUnit(out);
+ if (!unit)
+ return CR_FAILURE;
+ }
+ else if (parameters[i] == "-fey")
+ type = mood_type::Fey;
+ else if (parameters[i] == "-secretive")
+ type = mood_type::Secretive;
+ else if (parameters[i] == "-possessed")
+ type = mood_type::Possessed;
+ else if (parameters[i] == "-fell")
+ type = mood_type::Fell;
+ else if (parameters[i] == "-macabre")
+ type = mood_type::Macabre;
+ else
+ return CR_WRONG_USAGE;
+ }
+
+ CoreSuspender suspend;
+
+ // First, check if moods are enabled at all
+ if (!d_init->flags4.is_set(d_init_flags4::ARTIFACTS))
+ {
+ out.printerr("ARTIFACTS are not enabled!\n");
+ return CR_FAILURE;
+ }
+ if (*df::global::debug_nomoods)
+ {
+ out.printerr("Strange moods disabled via debug flag!\n");
+ return CR_FAILURE;
+ }
+ if (ui->mood_cooldown && !force)
+ {
+ out.printerr("Last strange mood happened too recently!\n");
+ return CR_FAILURE;
+ }
+
+ // Also, make sure there isn't a mood already running
+ for (size_t i = 0; i < world->units.active.size(); i++)
+ {
+ df::unit *cur = world->units.active[i];
+ if (Units::isCitizen(cur) && cur->flags1.bits.has_mood)
+ {
+ ui->mood_cooldown = 1000;
+ out.printerr("A strange mood is already in progress!\n");
+ return CR_FAILURE;
+ }
+ }
+
+ // See which units are eligible to enter moods
+ vector moodable_units;
+ bool mood_available = false;
+ for (size_t i = 0; i < world->units.active.size(); i++)
+ {
+ df::unit *cur = world->units.active[i];
+ if (!isUnitMoodable(cur))
+ continue;
+ if (cur->flags1.bits.has_mood)
+ {
+ ui->mood_cooldown = 1000;
+ out.printerr("A strange mood is already in progress!\n");
+ return CR_FAILURE;
+ }
+ if (!cur->flags1.bits.had_mood)
+ mood_available = true;
+ moodable_units.push_back(cur);
+ }
+ if (!mood_available)
+ {
+ out.printerr("No dwarves are available to enter a mood!\n");
+ return CR_FAILURE;
+ }
+
+ // If unit was manually selected, redo checks explicitly
+ if (unit)
+ {
+ if (!isUnitMoodable(unit))
+ {
+ out.printerr("Selected unit is not eligible to enter a strange mood!\n");
+ return CR_FAILURE;
+ }
+ if (unit->flags1.bits.had_mood)
+ {
+ out.printerr("Selected unit has already had a strange mood!\n");
+ return CR_FAILURE;
+ }
+ }
+
+ // Obey in-game mood limits
+ if (!force)
+ {
+ if (moodable_units.size() < 20)
+ {
+ out.printerr("Fortress is not eligible for a strange mood at this time - not enough moodable units.\n");
+ return CR_FAILURE;
+ }
+ int num_items = 0;
+ for (size_t i = 0; i < created_item_count->size(); i++)
+ num_items += created_item_count->at(i);
+
+ int num_revealed_tiles = 0;
+ for (size_t i = 0; i < world->map.map_blocks.size(); i++)
+ {
+ df::map_block *blk = world->map.map_blocks[i];
+ for (int x = 0; x < 16; x++)
+ for (int y = 0; y < 16; y++)
+ if (blk->designation[x][y].bits.subterranean && !blk->designation[x][y].bits.hidden)
+ num_revealed_tiles++;
+ }
+ if (num_revealed_tiles / 2304 < ui->tasks.num_artifacts)
+ {
+ out.printerr("Fortress is not eligible for a strange mood at this time - not enough subterranean tiles revealed.\n");
+ return CR_FAILURE;
+ }
+ if (num_items / 200 < ui->tasks.num_artifacts)
+ {
+ out.printerr("Fortress is not eligible for a strange mood at this time - not enough items created\n");
+ return CR_FAILURE;
+ }
+ }
+
+ // Randomly select a unit to enter a mood
+ if (!unit)
+ {
+ vector tickets;
+ for (size_t i = 0; i < moodable_units.size(); i++)
+ {
+ df::unit *cur = moodable_units[i];
+ if (cur->flags1.bits.had_mood)
+ continue;
+ if (cur->relations.dragger_id != -1)
+ continue;
+ if (cur->relations.draggee_id != -1)
+ continue;
+ for (int j = 0; j < 5; j++)
+ tickets.push_back(i);
+ switch (cur->profession)
+ {
+ case profession::WOODWORKER:
+ case profession::CARPENTER:
+ case profession::BOWYER:
+ case profession::STONEWORKER:
+ case profession::MASON:
+ for (int j = 0; j < 5; j++)
+ tickets.push_back(i);
+ break;
+ case profession::METALSMITH:
+ case profession::WEAPONSMITH:
+ case profession::ARMORER:
+ case profession::BLACKSMITH:
+ case profession::METALCRAFTER:
+ case profession::JEWELER:
+ case profession::GEM_CUTTER:
+ case profession::GEM_SETTER:
+ case profession::CRAFTSMAN:
+ case profession::WOODCRAFTER:
+ case profession::STONECRAFTER:
+ case profession::LEATHERWORKER:
+ case profession::BONE_CARVER:
+ case profession::WEAVER:
+ case profession::CLOTHIER:
+ case profession::GLASSMAKER:
+ for (int j = 0; j < 15; j++)
+ tickets.push_back(i);
+ break;
+ }
+ }
+ if (!tickets.size())
+ {
+ out.printerr("No units are eligible to enter a mood!\n");
+ return CR_FAILURE;
+ }
+ unit = moodable_units[tickets[rand() % tickets.size()]];
+ }
+ df::unit_soul *soul = unit->status.current_soul;
+
+ // Cancel selected unit's current job
+ if (unit->job.current_job)
+ {
+ // TODO: cancel job
+ out.printerr("Chosen unit '%s' has active job, cannot start mood!\n", Translation::TranslateName(&unit->name, false).c_str());
+ return CR_FAILURE;
+ }
+
+ ui->mood_cooldown = 1000;
+ // If no mood type was specified, pick one randomly
+ if (type == mood_type::None)
+ {
+ if (rand() % 100 > unit->status.happiness)
+ {
+ switch (rand() % 2)
+ {
+ case 0: type = mood_type::Fell; break;
+ case 1: type = mood_type::Macabre; break;
+ }
+ }
+ else
+ {
+ switch (rand() % 3)
+ {
+ case 0: type = mood_type::Fey; break;
+ case 1: type = mood_type::Secretive; break;
+ case 2: type = mood_type::Possessed; break;
+ }
+ }
+ }
+
+ // Display announcement and start setting up the mood job
+ int color = 0;
+ bool bright = false;
+ string msg = Translation::TranslateName(&unit->name, false) + ", " + Units::getProfessionName(unit);
+
+ switch (type)
+ {
+ case mood_type::Fey:
+ color = 7;
+ bright = true;
+ msg += " is taken by a fey mood!";
+ break;
+ case mood_type::Secretive:
+ color = 7;
+ bright = false;
+ msg += " withdraws from society...";
+ break;
+ case mood_type::Possessed:
+ color = 5;
+ bright = true;
+ msg += " has been possessed!";
+ break;
+ case mood_type::Macabre:
+ color = 0;
+ bright = true;
+ msg += " begins to stalk and brood...";
+ break;
+ case mood_type::Fell:
+ color = 5;
+ bright = false;
+ msg += " looses a roaring laughter, fell and terrible!";
+ break;
+ default:
+ out.printerr("Invalid mood type selected?\n");
+ return CR_FAILURE;
+ }
+
+ unit->mood = type;
+ 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;
+ unit->flags1.bits.had_mood = true;
+ unit->job.mood_skill = getMoodSkill(unit);
+ df::job *job = new df::job();
+ Job::linkIntoWorld(job);
+
+ // Choose the job type
+ if (unit->mood == mood_type::Fell)
+ job->job_type = job_type::StrangeMoodFell;
+ else if (unit->mood == mood_type::Macabre)
+ job->job_type = job_type::StrangeMoodBrooding;
+ else
+ {
+ switch (unit->job.mood_skill)
+ {
+ case job_skill::MINING:
+ case job_skill::MASONRY:
+ job->job_type = job_type::StrangeMoodMason;
+ break;
+ case job_skill::CARPENTRY:
+ job->job_type = job_type::StrangeMoodCarpenter;
+ break;
+ case job_skill::DETAILSTONE:
+ case job_skill::WOODCRAFT:
+ case job_skill::STONECRAFT:
+ case job_skill::BONECARVE:
+ job->job_type = job_type::StrangeMoodCrafter;
+ break;
+ case job_skill::TANNER:
+ case job_skill::LEATHERWORK:
+ job->job_type = job_type::StrangeMoodTanner;
+ break;
+ case job_skill::WEAVING:
+ case job_skill::CLOTHESMAKING:
+ job->job_type = job_type::StrangeMoodWeaver;
+ break;
+ case job_skill::FORGE_WEAPON:
+ case job_skill::FORGE_ARMOR:
+ case job_skill::FORGE_FURNITURE:
+ case job_skill::METALCRAFT:
+ job->job_type = job_type::StrangeMoodForge;
+ break;
+ case job_skill::CUTGEM:
+ case job_skill::ENCRUSTGEM:
+ job->job_type = job_type::StrangeMoodJeweller;
+ break;
+ case job_skill::GLASSMAKER:
+ job->job_type = job_type::StrangeMoodGlassmaker;
+ break;
+ case job_skill::BOWYER:
+ job->job_type = job_type::StrangeMoodBowyer;
+ break;
+ case job_skill::MECHANICS:
+ job->job_type = job_type::StrangeMoodMechanics;
+ break;
+ }
+ }
+ // Check which types of glass are available - we'll need this information later
+ bool have_glass[3] = {false, false, false};
+ for (size_t i = 0; i < created_item_type->size(); i++)
+ {
+ if (created_item_type->at(i) == item_type::ROUGH)
+ {
+ switch (created_item_mattype->at(i))
+ {
+ case builtin_mats::GLASS_GREEN:
+ have_glass[0] = true;
+ break;
+ case builtin_mats::GLASS_CLEAR:
+ have_glass[1] = true;
+ break;
+ case builtin_mats::GLASS_CRYSTAL:
+ have_glass[2] = true;
+ break;
+ }
+ }
+ }
+
+ // The dwarf will want 1-3 of the base material
+ int base_item_count = 1 + (rand() % 3);
+ if ((unit->job.mood_skill == job_skill::CUTGEM || unit->job.mood_skill == job_skill::ENCRUSTGEM) && (rand() % 2))
+ base_item_count = 1;
+
+ // Choose the base material
+ df::job_item *item;
+ if (job->job_type == job_type::StrangeMoodFell)
+ {
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::CORPSE;
+ item->flags1.bits.allow_buryable = true;
+ item->flags1.bits.murdered = true;
+ item->quantity = 1;
+ item->vector_id = job_item_vector_id::ANY_MURDERED;
+ }
+ else if (job->job_type == job_type::StrangeMoodBrooding)
+ {
+ switch (rand() % 3)
+ {
+ case 0:
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::REMAINS;
+ item->flags1.bits.allow_buryable = true;
+ item->quantity = 1;
+ break;
+ case 1:
+ job->job_items.push_back(item = new df::job_item());
+ item->flags1.bits.allow_buryable = true;
+ item->flags2.bits.bone = true;
+ item->flags2.bits.body_part = true;
+ item->quantity = 1;
+ break;
+ case 2:
+ job->job_items.push_back(item = new df::job_item());
+ item->flags1.bits.allow_buryable = true;
+ item->flags2.bits.totemable = true;
+ item->flags2.bits.body_part = true;
+ item->quantity = base_item_count;
+ break;
+ }
+ }
+ else
+ {
+ df::item *filter;
+ bool found_pref;
+ switch (unit->job.mood_skill)
+ {
+ case job_skill::MINING:
+ case job_skill::DETAILSTONE:
+ case job_skill::MASONRY:
+ case job_skill::STONECRAFT:
+ case job_skill::MECHANICS:
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::BOULDER;
+ item->quantity = base_item_count;
+ item->flags3.bits.hard = true;
+ found_pref = false;
+ if (soul)
+ {
+ for (size_t i = 0; i < soul->preferences.size(); i++)
+ {
+ df::unit_preference *pref = soul->preferences[i];
+ if (pref->active == 1 &&
+ pref->type == df::unit_preference::T_type::LikeMaterial &&
+ pref->mattype == builtin_mats::INORGANIC)
+ {
+ item->mat_type = pref->mattype;
+ found_pref = true;
+ break;
+ }
+ }
+ }
+ if (!found_pref)
+ item->mat_type = builtin_mats::INORGANIC;
+ break;
+
+ case job_skill::CARPENTRY:
+ case job_skill::WOODCRAFT:
+ case job_skill::BOWYER:
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::WOOD;
+ item->quantity = base_item_count;
+ break;
+
+ case job_skill::TANNER:
+ case job_skill::LEATHERWORK:
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::SKIN_TANNED;
+ item->quantity = base_item_count;
+ break;
+
+ case job_skill::WEAVING:
+ case job_skill::CLOTHESMAKING:
+ filter = NULL;
+ // TODO: do proper search through world->items.other[items_other_id::ANY_GENERIC35] for item_type CLOTH, mat_type 0, flags2.deep_material, and min_dimension 10000
+ for (size_t i = 0; i < world->items.other[items_other_id::ANY_GENERIC35].size(); i++)
+ {
+ filter = world->items.other[items_other_id::ANY_GENERIC35][i];
+ if (filter->getType() != item_type::CLOTH)
+ {
+ filter = NULL;
+ continue;
+ }
+ if (filter->getMaterial() != 0)
+ {
+ filter = NULL;
+ continue;
+ }
+ if (filter->getTotalDimension() < 10000)
+ {
+ filter = NULL;
+ continue;
+ }
+ MaterialInfo mat(filter->getMaterial(), filter->getMaterialIndex());
+ if (!mat.inorganic->flags.is_set(inorganic_flags::DEEP_SPECIAL))
+ {
+ filter = NULL;
+ continue;
+ }
+ }
+ if (filter)
+ {
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::CLOTH;
+ item->mat_type = filter->getMaterial();
+ item->mat_index = filter->getMaterialIndex();
+ item->quantity = base_item_count * 10000;
+ item->min_dimension = 10000;
+ }
+ else
+ {
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::CLOTH;
+ bool found_pref = false;
+ if (soul)
+ {
+ for (size_t i = 0; i < soul->preferences.size(); i++)
+ {
+ df::unit_preference *pref = soul->preferences[i];
+ if (pref->active == 1 &&
+ pref->type == df::unit_preference::T_type::LikeMaterial)
+ {
+ MaterialInfo mat(pref->mattype, pref->matindex);
+ if (mat.material->flags.is_set(material_flags::SILK))
+ item->flags2.bits.silk = true;
+ else if (mat.material->flags.is_set(material_flags::THREAD_PLANT))
+ item->flags2.bits.plant = true;
+ else if (mat.material->flags.is_set(material_flags::YARN))
+ item->flags2.bits.yarn = true;
+ else
+ continue;
+ found_pref = true;
+ break;
+ }
+ }
+ }
+ if (!found_pref)
+ {
+ switch (rand() % 3)
+ {
+ case 0:
+ item->flags2.bits.silk = true;
+ break;
+ case 1:
+ item->flags2.bits.plant = true;
+ break;
+ case 2:
+ item->flags2.bits.yarn = true;
+ break;
+ }
+ }
+ item->quantity = base_item_count * 10000;
+ item->min_dimension = 10000;
+ }
+ break;
+
+ case job_skill::FORGE_WEAPON:
+ case job_skill::FORGE_ARMOR:
+ case job_skill::FORGE_FURNITURE:
+ case job_skill::METALCRAFT:
+ filter = NULL;
+ // TODO: do proper search through world->items.other[items_other_id::ANY_GENERIC35] for item_type BAR, mat_type 0, and flags2.deep_material
+ for (size_t i = 0; i < world->items.other[items_other_id::ANY_GENERIC35].size(); i++)
+ {
+ filter = world->items.other[items_other_id::ANY_GENERIC35][i];
+ if (filter->getType() != item_type::BAR)
+ {
+ filter = NULL;
+ continue;
+ }
+ if (filter->getMaterial() != 0)
+ {
+ filter = NULL;
+ continue;
+ }
+ MaterialInfo mat(filter->getMaterial(), filter->getMaterialIndex());
+ if (!mat.inorganic->flags.is_set(inorganic_flags::DEEP_SPECIAL))
+ {
+ filter = NULL;
+ continue;
+ }
+ }
+ if (filter)
+ {
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::BAR;
+ item->mat_type = filter->getMaterial();
+ item->mat_index = filter->getMaterialIndex();
+ item->quantity = base_item_count;
+ }
+ else
+ {
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::BAR;
+ item->mat_type = 0;
+ vector mats;
+ if (soul)
+ {
+ for (size_t i = 0; i < soul->preferences.size(); i++)
+ {
+ df::unit_preference *pref = soul->preferences[i];
+ if (pref->active == 1 &&
+ pref->type == df::unit_preference::T_type::LikeMaterial &&
+ pref->mattype == 0 && getCreatedMetalBars(pref->matindex) > 0)
+ mats.push_back(pref->matindex);
+ }
+ }
+ if (mats.size())
+ item->mat_index = mats[rand() & mats.size()];
+ item->quantity = base_item_count;
+ }
+ break;
+
+ case job_skill::CUTGEM:
+ case job_skill::ENCRUSTGEM:
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::ROUGH;
+ item->mat_type = 0;
+ item->quantity = base_item_count;
+ break;
+
+ case job_skill::GLASSMAKER:
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::ROUGH;
+ found_pref = false;
+ if (soul)
+ {
+ for (size_t i = 0; i < soul->preferences.size(); i++)
+ {
+ df::unit_preference *pref = soul->preferences[i];
+ if (pref->active == 1 &&
+ pref->type == df::unit_preference::T_type::LikeMaterial &&
+ ((pref->mattype == builtin_mats::GLASS_GREEN) ||
+ (pref->mattype == builtin_mats::GLASS_CLEAR && have_glass[1]) ||
+ (pref->mattype == builtin_mats::GLASS_CRYSTAL && have_glass[2])))
+ {
+ item->mat_type = pref->mattype;
+ item->mat_index = pref->matindex;
+ found_pref = true;
+ }
+ }
+ }
+ if (!found_pref)
+ {
+ vector mats;
+ mats.push_back(builtin_mats::GLASS_GREEN);
+ if (have_glass[1])
+ mats.push_back(builtin_mats::GLASS_CLEAR);
+ if (have_glass[2])
+ mats.push_back(builtin_mats::GLASS_CRYSTAL);
+ item->mat_type = mats[rand() % mats.size()];
+ }
+ item->quantity = base_item_count;
+ break;
+
+ case job_skill::BONECARVE:
+ found_pref = false;
+ if (soul)
+ {
+ for (size_t i = 0; i < soul->preferences.size(); i++)
+ {
+ df::unit_preference *pref = soul->preferences[i];
+ if (pref->active == 1 &&
+ pref->type == df::unit_preference::T_type::LikeMaterial)
+ {
+ MaterialInfo mat(pref->mattype, pref->matindex);
+ if (mat.material->flags.is_set(material_flags::BONE))
+ {
+ job->job_items.push_back(item = new df::job_item());
+ item->flags2.bits.bone = true;
+ item->flags2.bits.body_part = true;
+ found_pref = true;
+ break;
+ }
+ else if (mat.material->flags.is_set(material_flags::SHELL))
+ {
+ job->job_items.push_back(item = new df::job_item());
+ item->flags2.bits.shell = true;
+ item->flags2.bits.body_part = true;
+ found_pref = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!found_pref)
+ {
+ job->job_items.push_back(item = new df::job_item());
+ item->flags2.bits.bone = true;
+ item->flags2.bits.body_part = true;
+ found_pref = true;
+ }
+ item->quantity = base_item_count;
+ break;
+ }
+ }
+
+ // Choose additional mood materials
+ if (!(((unit->job.mood_skill == job_skill::CUTGEM || unit->job.mood_skill == job_skill::ENCRUSTGEM) && base_item_count == 1) || job->job_type == job_type::StrangeMoodFell))
+ {
+ int extra_items = (ui->tasks.num_artifacts * 20 + moodable_units.size()) / 20;
+ if (extra_items > 0)
+ extra_items = std::min(rand() % (extra_items + 1), 7);
+ df::item_type avoid_type = item_type::NONE;
+ int avoid_glass = 0;
+ switch (unit->job.mood_skill)
+ {
+ case job_skill::MINING:
+ case job_skill::DETAILSTONE:
+ case job_skill::MASONRY:
+ case job_skill::STONECRAFT:
+ avoid_type = item_type::BLOCKS;
+ break;
+ case job_skill::CARPENTRY:
+ case job_skill::WOODCRAFT:
+ case job_skill::BOWYER:
+ avoid_type = item_type::WOOD;
+ break;
+ case job_skill::TANNER:
+ case job_skill::LEATHERWORK:
+ avoid_type = item_type::SKIN_TANNED;
+ break;
+ case job_skill::WEAVING:
+ case job_skill::CLOTHESMAKING:
+ avoid_type = item_type::CLOTH;
+ break;
+ case job_skill::FORGE_WEAPON:
+ case job_skill::FORGE_ARMOR:
+ case job_skill::FORGE_FURNITURE:
+ case job_skill::METALCRAFT:
+ avoid_type = item_type::BAR;
+ break;
+ case job_skill::CUTGEM:
+ case job_skill::ENCRUSTGEM:
+ avoid_type = item_type::SMALLGEM;
+ case job_skill::GLASSMAKER:
+ avoid_glass = 1;
+ break;
+ }
+ for (size_t i = 0; i < extra_items; i++)
+ {
+ if ((job->job_type == job_type::StrangeMoodBrooding) && (rand() % 2))
+ {
+ switch (rand() % 3)
+ {
+ case 0:
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type::REMAINS;
+ item->flags1.bits.allow_buryable = true;
+ item->quantity = 1;
+ break;
+ case 1:
+ job->job_items.push_back(item = new df::job_item());
+ item->flags1.bits.allow_buryable = true;
+ item->flags2.bits.bone = true;
+ 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
+ {
+ df::item_type item_type;
+ int16_t mat_type;
+ df::job_item_flags2 flags2;
+ do
+ {
+ item_type = item_type::NONE;
+ mat_type = -1;
+ flags2.whole = 0;
+ switch (rand() % 10)
+ {
+ case 0:
+ item_type = item_type::WOOD;
+ mat_type = -1;
+ break;
+ case 1:
+ item_type = item_type::BAR;
+ mat_type = builtin_mats::INORGANIC;
+ break;
+ case 2:
+ item_type = item_type::SMALLGEM;
+ mat_type = -1;
+ break;
+ case 3:
+ item_type = item_type::BLOCKS;
+ mat_type = builtin_mats::INORGANIC;
+ break;
+ case 4:
+ item_type = item_type::ROUGH;
+ mat_type = builtin_mats::INORGANIC;
+ break;
+ case 5:
+ item_type = item_type::BOULDER;
+ mat_type = builtin_mats::INORGANIC;
+ break;
+ case 6:
+ flags2.bits.bone = true;
+ flags2.bits.body_part = true;
+ break;
+ case 7:
+ item_type = item_type::SKIN_TANNED;
+ mat_type = -1;
+ break;
+ case 8:
+ item_type = item_type::CLOTH;
+ mat_type = -1;
+ switch (rand() % 3)
+ {
+ case 0:
+ flags2.bits.plant = true;
+ break;
+ case 1:
+ flags2.bits.silk = true;
+ break;
+ case 2:
+ flags2.bits.yarn = true;
+ break;
+ }
+ break;
+ case 9:
+ item_type = item_type::ROUGH;
+ switch (rand() % 3)
+ {
+ case 0:
+ mat_type = builtin_mats::GLASS_GREEN;
+ break;
+ case 1:
+ mat_type = builtin_mats::GLASS_CLEAR;
+ break;
+ case 2:
+ mat_type = builtin_mats::GLASS_CRYSTAL;
+ break;
+ }
+ break;
+ }
+ item = job->job_items[0];
+ if (item->item_type == item_type && item->mat_type == mat_type)
+ continue;
+ if (item_type == avoid_type)
+ continue;
+ if (avoid_glass && ((mat_type == builtin_mats::GLASS_GREEN) || (mat_type == builtin_mats::GLASS_CLEAR) || (mat_type == builtin_mats::GLASS_CRYSTAL)))
+ continue;
+ if ((mat_type == builtin_mats::GLASS_GREEN) && !have_glass[0])
+ continue;
+ if ((mat_type == builtin_mats::GLASS_CLEAR) && !have_glass[1])
+ continue;
+ if ((mat_type == builtin_mats::GLASS_CRYSTAL) && !have_glass[2])
+ continue;
+ break;
+ } while (1);
+ job->job_items.push_back(item = new df::job_item());
+ item->item_type = item_type;
+ item->mat_type = mat_type;
+ item->flags2.whole = flags2.whole;
+ item->quantity = 1;
+ }
+ }
+ }
+
+ // Attach the Strange Mood job to the dwarf
+ unit->path.dest.x = -30000;
+ unit->path.dest.y = -30000;
+ unit->path.dest.z = -30000;
+ unit->path.unk_ae = -1;
+ unit->path.path.x.clear();
+ unit->path.path.y.clear();
+ unit->path.path.z.clear();
+ job->flags.bits.special = true;
+ df::general_ref *ref = df::allocate();
+ ref->setID(unit->id);
+ job->general_refs.push_back(ref);
+ unit->job.current_job = job;
+ job->unk10b_cntdn = 0;
+
+ // Generate the artifact's name
+ if (type == mood_type::Fell || type == mood_type::Macabre)
+ generateName(unit->status.artifact_name, unit->name.language, 1, world->raws.language.word_table[0][2], world->raws.language.word_table[1][2]);
+ else
+ {
+ generateName(unit->status.artifact_name, unit->name.language, 1, world->raws.language.word_table[0][1], world->raws.language.word_table[1][1]);
+ if (!(rand() % 100))
+ unit->status.artifact_name = unit->name;
+ }
+ unit->unk_18e = 0;
+ return CR_OK;
+}
+
+DFHACK_PLUGIN("strangemood");
+
+DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands)
+{
+ commands.push_back(PluginCommand("strangemood", "Force a strange mood to happen.\n", df_strangemood, false,
+ "Options:\n"
+ " -force - Ignore standard mood preconditions.\n"
+ " -unit - Use the selected unit instead of picking one randomly.\n"
+ " -fey - Force the mood to be a fey mood.\n"
+ " -secretive - Force the mood to be a secretive mood.\n"
+ " -possessed - Force the mood to be a possession.\n"
+ " -fell - Force the mood to be a fell mood.\n"
+ " -macabre - Force the mood to be a macabre mood.\n"
+ ));
+
+ return CR_OK;
+}
+
+DFhackCExport command_result plugin_shutdown ( color_ostream &out )
+{
+ return CR_OK;
+}
From baf377afba8b17c6c286c8bfa00f0f441493bb83 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Sat, 18 Jan 2014 22:56:56 -0600
Subject: [PATCH 024/107] Meant to move this, not copy it
---
plugins/strangemood.cpp | 6 ------
1 file changed, 6 deletions(-)
diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp
index 5dd12c627..f3327c382 100644
--- a/plugins/strangemood.cpp
+++ b/plugins/strangemood.cpp
@@ -429,12 +429,6 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
df::unit *cur = world->units.active[i];
if (!isUnitMoodable(cur))
continue;
- if (cur->flags1.bits.has_mood)
- {
- ui->mood_cooldown = 1000;
- out.printerr("A strange mood is already in progress!\n");
- return CR_FAILURE;
- }
if (!cur->flags1.bits.had_mood)
mood_available = true;
moodable_units.push_back(cur);
From 89b3c7bb6774a99900a97b478bd685e1d9c81b6e Mon Sep 17 00:00:00 2001
From: Quietust
Date: Sun, 19 Jan 2014 20:21:41 -0600
Subject: [PATCH 025/107] Change syntax for specifying mood type, also allow
overriding mood skill
---
plugins/strangemood.cpp | 126 ++++++++++++++++++++++++++++++++--------
1 file changed, 103 insertions(+), 23 deletions(-)
diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp
index f3327c382..9b64a525c 100644
--- a/plugins/strangemood.cpp
+++ b/plugins/strangemood.cpp
@@ -363,6 +363,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
bool force = false;
df::unit *unit = NULL;
df::mood_type type = mood_type::None;
+ df::job_skill skill = job_skill::NONE;
for (size_t i = 0; i < parameters.size(); i++)
{
@@ -376,18 +377,89 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
if (!unit)
return CR_FAILURE;
}
- else if (parameters[i] == "-fey")
- type = mood_type::Fey;
- else if (parameters[i] == "-secretive")
- type = mood_type::Secretive;
- else if (parameters[i] == "-possessed")
- type = mood_type::Possessed;
- else if (parameters[i] == "-fell")
- type = mood_type::Fell;
- else if (parameters[i] == "-macabre")
- type = mood_type::Macabre;
+ else if (parameters[i] == "-type")
+ {
+ i++;
+ if (i == parameters.size())
+ {
+ out.printerr("No mood type specified!\n");
+ return CR_WRONG_USAGE;
+ }
+ if (parameters[i] == "fey")
+ type = mood_type::Fey;
+ else if (parameters[i] == "secretive")
+ type = mood_type::Secretive;
+ else if (parameters[i] == "possessed")
+ type = mood_type::Possessed;
+ else if (parameters[i] == "fell")
+ type = mood_type::Fell;
+ else if (parameters[i] == "macabre")
+ type = mood_type::Macabre;
+ else
+ {
+ out.printerr("Mood type '%s' not recognized!\n", parameters[i].c_str());
+ return CR_WRONG_USAGE;
+ }
+ }
+ else if (parameters[i] == "-skill")
+ {
+ i++;
+ if (i == parameters.size())
+ {
+ out.printerr("No mood skill specified!\n");
+ return CR_WRONG_USAGE;
+ }
+ else if (parameters[i] == "miner")
+ skill = job_skill::MINING;
+ else if (parameters[i] == "carpenter")
+ skill = job_skill::CARPENTRY;
+ else if (parameters[i] == "engraver")
+ skill = job_skill::DETAILSTONE;
+ else if (parameters[i] == "mason")
+ skill = job_skill::MASONRY;
+ else if (parameters[i] == "tanner")
+ skill = job_skill::TANNER;
+ else if (parameters[i] == "weaver")
+ skill = job_skill::WEAVING;
+ else if (parameters[i] == "clothier")
+ skill = job_skill::CLOTHESMAKING;
+ else if (parameters[i] == "weaponsmith")
+ skill = job_skill::FORGE_WEAPON;
+ else if (parameters[i] == "armorsmith")
+ skill = job_skill::FORGE_ARMOR;
+ else if (parameters[i] == "metalsmith")
+ skill = job_skill::FORGE_FURNITURE;
+ else if (parameters[i] == "gemcutter")
+ skill = job_skill::CUTGEM;
+ else if (parameters[i] == "gemsetter")
+ skill = job_skill::ENCRUSTGEM;
+ else if (parameters[i] == "woodcrafter")
+ skill = job_skill::WOODCRAFT;
+ else if (parameters[i] == "stonecrafter")
+ skill = job_skill::STONECRAFT;
+ else if (parameters[i] == "metalcrafter")
+ skill = job_skill::METALCRAFT;
+ else if (parameters[i] == "glassmaker")
+ skill = job_skill::GLASSMAKER;
+ else if (parameters[i] == "leatherworker")
+ skill = job_skill::LEATHERWORK;
+ else if (parameters[i] == "bonecarver")
+ skill = job_skill::BONECARVE;
+ else if (parameters[i] == "bowyer")
+ skill = job_skill::BOWYER;
+ else if (parameters[i] == "mechanic")
+ skill = job_skill::MECHANICS;
+ else
+ {
+ out.printerr("Mood skill '%s' not recognized!\n", parameters[i].c_str());
+ return CR_WRONG_USAGE;
+ }
+ }
else
+ {
+ out.printerr("Unrecognized parameter: %s\n", parameters[i].c_str());
return CR_WRONG_USAGE;
+ }
}
CoreSuspender suspend;
@@ -619,7 +691,10 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
unit->job.mood_timeout = 50000;
unit->flags1.bits.has_mood = true;
unit->flags1.bits.had_mood = true;
- unit->job.mood_skill = getMoodSkill(unit);
+ if (skill == job_skill::NONE)
+ skill = getMoodSkill(unit);
+
+ unit->job.mood_skill = skill;
df::job *job = new df::job();
Job::linkIntoWorld(job);
@@ -630,7 +705,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
job->job_type = job_type::StrangeMoodBrooding;
else
{
- switch (unit->job.mood_skill)
+ switch (skill)
{
case job_skill::MINING:
case job_skill::MASONRY:
@@ -697,7 +772,8 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
// The dwarf will want 1-3 of the base material
int base_item_count = 1 + (rand() % 3);
- if ((unit->job.mood_skill == job_skill::CUTGEM || unit->job.mood_skill == job_skill::ENCRUSTGEM) && (rand() % 2))
+ // Gem Cutters and Gem Setters have a 50% chance of using only one base item
+ if (((skill == job_skill::CUTGEM) || (skill == job_skill::ENCRUSTGEM)) && (rand() % 2))
base_item_count = 1;
// Choose the base material
@@ -741,7 +817,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
{
df::item *filter;
bool found_pref;
- switch (unit->job.mood_skill)
+ switch (skill)
{
case job_skill::MINING:
case job_skill::DETAILSTONE:
@@ -1014,14 +1090,18 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
}
// Choose additional mood materials
- if (!(((unit->job.mood_skill == job_skill::CUTGEM || unit->job.mood_skill == job_skill::ENCRUSTGEM) && base_item_count == 1) || job->job_type == job_type::StrangeMoodFell))
+ // Gem cutters/setters using a single gem require nothing else, and fell moods need only their corpse
+ if (!(
+ (((skill == job_skill::CUTGEM) || (skill == job_skill::ENCRUSTGEM)) && base_item_count == 1) ||
+ (job->job_type == job_type::StrangeMoodFell)
+ ))
{
int extra_items = (ui->tasks.num_artifacts * 20 + moodable_units.size()) / 20;
if (extra_items > 0)
extra_items = std::min(rand() % (extra_items + 1), 7);
df::item_type avoid_type = item_type::NONE;
int avoid_glass = 0;
- switch (unit->job.mood_skill)
+ switch (skill)
{
case job_skill::MINING:
case job_skill::DETAILSTONE:
@@ -1214,13 +1294,13 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector - Force the mood to be of a specific type.\n"
+ " Valid types: fey, secretive, possessed, fell, macabre\n"
+ " -skill - Force the mood to use a specific skill.\n"
+ " Skill name must be lowercase and without spaces.\n"
+ " Example: miner, gemcutter, metalcrafter, bonecarver, mason\n"
));
return CR_OK;
From 7f14e109fa239c0919a150d6ab28964a774fc24d Mon Sep 17 00:00:00 2001
From: Quietust
Date: Sun, 19 Jan 2014 20:31:15 -0600
Subject: [PATCH 026/107] Use DF-compatible RNG for better randomness
---
plugins/strangemood.cpp | 98 +++++++++++++++++++++--------------------
1 file changed, 50 insertions(+), 48 deletions(-)
diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp
index 9b64a525c..00da1f4f5 100644
--- a/plugins/strangemood.cpp
+++ b/plugins/strangemood.cpp
@@ -9,6 +9,7 @@
#include "modules/Items.h"
#include "modules/Job.h"
#include "modules/Translation.h"
+#include "modules/Random.h"
#include "DataDefs.h"
#include "df/d_init.h"
@@ -40,6 +41,8 @@ using df::global::created_item_subtype;
using df::global::created_item_mattype;
using df::global::created_item_matindex;
+Random::MersenneRNG rng;
+
bool isUnitMoodable (df::unit *unit)
{
if (!Units::isCitizen(unit))
@@ -107,7 +110,7 @@ df::job_skill getMoodSkill (df::unit *unit)
}
if (!skills.size())
skills.push_back(job_skill::STONECRAFT);
- return skills[rand() % skills.size()];
+ return skills[rng.df_trandom(skills.size())];
}
int getCreatedMetalBars (int32_t idx)
@@ -127,14 +130,14 @@ void selectWord (const df::world_raws::T_language::T_word_table &table, int32_t
{
if (table.parts[mode].size())
{
- int offset = rand() % table.parts[mode].size();
+ int offset = rng.df_trandom(table.parts[mode].size());
word = table.words[mode][offset];
part = table.parts[mode][offset];
}
else
{
- word = rand() % world->raws.language.words.size();
- part = (df::part_of_speech)(rand() % 9);
+ word = rng.df_trandom(world->raws.language.words.size());
+ part = (df::part_of_speech)(rng.df_trandom(9));
Core::getInstance().getConsole().printerr("Impoverished Word Selector");
}
}
@@ -147,13 +150,13 @@ void generateName(df::language_name &output, int language, int mode, const df::w
{
output = df::language_name();
if (language == -1)
- language = rand() % world->raws.language.translations.size();
+ language = rng.df_trandom(world->raws.language.translations.size());
output.unknown = mode;
output.language = language;
}
output.has_name = 1;
if (output.language == -1)
- output.language = rand() % world->raws.language.translations.size();
+ output.language = rng.df_trandom(world->raws.language.translations.size());
int r, r2, r3;
switch (mode)
{
@@ -169,7 +172,7 @@ void generateName(df::language_name &output, int language, int mode, const df::w
if (mode != 10)
{
case 4: case 37: // this is not a typo
- if (rand() % 2)
+ if (rng.df_trandom(2))
{
selectWord(table2, output.words[0], output.parts_of_speech[0], 0);
selectWord(table1, output.words[1], output.parts_of_speech[1], 1);
@@ -183,10 +186,10 @@ void generateName(df::language_name &output, int language, int mode, const df::w
break;
case 1: case 13: case 20:
- r = rand() % 3;
+ r = rng.df_trandom(3);
if (r == 0 || r == 1)
{
- if (rand() % 2)
+ if (rng.df_trandom(2))
{
selectWord(table2, output.words[0], output.parts_of_speech[0], 0);
selectWord(table1, output.words[1], output.parts_of_speech[1], 1);
@@ -200,20 +203,20 @@ void generateName(df::language_name &output, int language, int mode, const df::w
if (r == 1 || r == 2)
{
case 3: case 8: case 11: // this is not a typo either
- r2 = rand() % 2;
+ r2 = rng.df_trandom(2);
if (r2)
selectWord(table1, output.words[5], output.parts_of_speech[5], 2);
else
selectWord(table2, output.words[5], output.parts_of_speech[5], 2);
- r3 = rand() % 3;
- if (rand() % 50)
- r3 = rand() % 2;
+ r3 = rng.df_trandom(3);
+ if (rng.df_trandom(50))
+ r3 = rng.df_trandom(2);
switch (r3)
{
case 0:
case 2:
if (r3 == 2)
- r2 = rand() % 2;
+ r2 = rng.df_trandom(2);
if (r2)
selectWord(table2, output.words[6], output.parts_of_speech[6], 5);
else
@@ -226,14 +229,14 @@ void generateName(df::language_name &output, int language, int mode, const df::w
selectWord(table1, output.words[2], output.parts_of_speech[2], 3);
else
selectWord(table2, output.words[2], output.parts_of_speech[2], 3);
- if (!(rand() % 100))
+ if (!(rng.df_trandom(100)))
selectWord(table1, output.words[3], output.parts_of_speech[3], 3);
break;
}
}
- if (rand() % 100)
+ if (rng.df_trandom(100))
{
- if (rand() % 2)
+ if (rng.df_trandom(2))
selectWord(table1, output.words[4], output.parts_of_speech[4], 4);
else
selectWord(table2, output.words[4], output.parts_of_speech[4], 4);
@@ -246,9 +249,9 @@ void generateName(df::language_name &output, int language, int mode, const df::w
case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30:
case 31: case 32: case 33: case 34: case 35: case 36: case 38: case 39:
selectWord(table1, output.words[5], output.parts_of_speech[5], 2);
- r3 = rand() % 3;
- if (rand() % 50)
- r3 = rand() % 2;
+ r3 = rng.df_trandom(3);
+ if (rng.df_trandom(50))
+ r3 = rng.df_trandom(2);
switch (r3)
{
case 0:
@@ -258,16 +261,16 @@ void generateName(df::language_name &output, int language, int mode, const df::w
break;
case 1:
selectWord(table2, output.words[2], output.parts_of_speech[2], 3);
- if (!(rand() % 100))
+ if (!(rng.df_trandom(100)))
selectWord(table2, output.words[3], output.parts_of_speech[3], 3);
break;
}
- if (rand() % 100)
+ if (rng.df_trandom(100))
selectWord(table2, output.words[4], output.parts_of_speech[4], 4);
break;
case 7:
- r = rand() % 3;
+ r = rng.df_trandom(3);
if (r == 0 || r == 1)
{
selectWord(table2, output.words[0], output.parts_of_speech[0], 0);
@@ -275,14 +278,14 @@ void generateName(df::language_name &output, int language, int mode, const df::w
}
if (r == 1 || r == 2)
{
- r2 = rand() % 2;
+ r2 = rng.df_trandom(2);
if (r == 2 || r2 == 1)
selectWord(table1, output.words[5], output.parts_of_speech[5], 2);
else
selectWord(table2, output.words[5], output.parts_of_speech[5], 2);
- r3 = rand() % 3;
- if (rand() % 50)
- r3 = rand() % 2;
+ r3 = rng.df_trandom(3);
+ if (rng.df_trandom(50))
+ r3 = rng.df_trandom(2);
switch (r3)
{
case 0:
@@ -292,12 +295,12 @@ void generateName(df::language_name &output, int language, int mode, const df::w
break;
case 1:
selectWord(table2, output.words[2], output.parts_of_speech[2], 3);
- if (!(rand() % 100))
+ if (!(rng.df_trandom(100)))
selectWord(table2, output.words[3], output.parts_of_speech[3], 3);
break;
}
}
- if (rand() % 100)
+ if (rng.df_trandom(100))
selectWord(table2, output.words[4], output.parts_of_speech[4], 4);
break;
}
@@ -610,7 +613,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
out.printerr("No units are eligible to enter a mood!\n");
return CR_FAILURE;
}
- unit = moodable_units[tickets[rand() % tickets.size()]];
+ unit = moodable_units[tickets[rng.df_trandom(tickets.size())]];
}
df::unit_soul *soul = unit->status.current_soul;
@@ -626,9 +629,9 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
// If no mood type was specified, pick one randomly
if (type == mood_type::None)
{
- if (rand() % 100 > unit->status.happiness)
+ if (rng.df_trandom(100) > unit->status.happiness)
{
- switch (rand() % 2)
+ switch (rng.df_trandom(2))
{
case 0: type = mood_type::Fell; break;
case 1: type = mood_type::Macabre; break;
@@ -636,7 +639,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
}
else
{
- switch (rand() % 3)
+ switch (rng.df_trandom(3))
{
case 0: type = mood_type::Fey; break;
case 1: type = mood_type::Secretive; break;
@@ -771,9 +774,9 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
}
// The dwarf will want 1-3 of the base material
- int base_item_count = 1 + (rand() % 3);
+ int base_item_count = 1 + rng.df_trandom(3);
// Gem Cutters and Gem Setters have a 50% chance of using only one base item
- if (((skill == job_skill::CUTGEM) || (skill == job_skill::ENCRUSTGEM)) && (rand() % 2))
+ if (((skill == job_skill::CUTGEM) || (skill == job_skill::ENCRUSTGEM)) && (rng.df_trandom(2)))
base_item_count = 1;
// Choose the base material
@@ -789,7 +792,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
}
else if (job->job_type == job_type::StrangeMoodBrooding)
{
- switch (rand() % 3)
+ switch (rng.df_trandom(3))
{
case 0:
job->job_items.push_back(item = new df::job_item());
@@ -930,7 +933,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
}
if (!found_pref)
{
- switch (rand() % 3)
+ switch (rng.df_trandom(3))
{
case 0:
item->flags2.bits.silk = true;
@@ -1000,7 +1003,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
}
}
if (mats.size())
- item->mat_index = mats[rand() & mats.size()];
+ item->mat_index = mats[rng.df_trandom(mats.size())];
item->quantity = base_item_count;
}
break;
@@ -1042,7 +1045,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
mats.push_back(builtin_mats::GLASS_CLEAR);
if (have_glass[2])
mats.push_back(builtin_mats::GLASS_CRYSTAL);
- item->mat_type = mats[rand() % mats.size()];
+ item->mat_type = mats[rng.df_trandom(mats.size())];
}
item->quantity = base_item_count;
break;
@@ -1096,9 +1099,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
(job->job_type == job_type::StrangeMoodFell)
))
{
- int extra_items = (ui->tasks.num_artifacts * 20 + moodable_units.size()) / 20;
- if (extra_items > 0)
- extra_items = std::min(rand() % (extra_items + 1), 7);
+ int extra_items = std::min(rng.df_trandom((ui->tasks.num_artifacts * 20 + moodable_units.size()) / 20 + 1), 7);
df::item_type avoid_type = item_type::NONE;
int avoid_glass = 0;
switch (skill)
@@ -1137,9 +1138,9 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
}
for (size_t i = 0; i < extra_items; i++)
{
- if ((job->job_type == job_type::StrangeMoodBrooding) && (rand() % 2))
+ if ((job->job_type == job_type::StrangeMoodBrooding) && (rng.df_trandom(2)))
{
- switch (rand() % 3)
+ switch (rng.df_trandom(3))
{
case 0:
job->job_items.push_back(item = new df::job_item());
@@ -1170,7 +1171,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
item_type = item_type::NONE;
mat_type = -1;
flags2.whole = 0;
- switch (rand() % 10)
+ switch (rng.df_trandom(10))
{
case 0:
item_type = item_type::WOOD;
@@ -1207,7 +1208,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
case 8:
item_type = item_type::CLOTH;
mat_type = -1;
- switch (rand() % 3)
+ switch (rng.df_trandom(3))
{
case 0:
flags2.bits.plant = true;
@@ -1222,7 +1223,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
break;
case 9:
item_type = item_type::ROUGH;
- switch (rand() % 3)
+ switch (rng.df_trandom(3))
{
case 0:
mat_type = builtin_mats::GLASS_GREEN;
@@ -1281,7 +1282,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
else
{
generateName(unit->status.artifact_name, unit->name.language, 1, world->raws.language.word_table[0][1], world->raws.language.word_table[1][1]);
- if (!(rand() % 100))
+ if (!rng.df_trandom(100))
unit->status.artifact_name = unit->name;
}
unit->unk_18e = 0;
@@ -1302,6 +1303,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector
Date: Sat, 25 Jan 2014 03:21:56 -0500
Subject: [PATCH 027/107] DFHack compiles under OS X now.
---
library/modules/MapCache.cpp | 2 +-
plugins/eventful.cpp | 1 +
plugins/zone.cpp | 18 +++++++++---------
3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/library/modules/MapCache.cpp b/library/modules/MapCache.cpp
index 6ca696485..db3b4cdd9 100644
--- a/library/modules/MapCache.cpp
+++ b/library/modules/MapCache.cpp
@@ -1143,7 +1143,7 @@ MapExtras::MapCache::MapCache()
memset(biomes[i].layer_stone, -1, sizeof(biomes[i].layer_stone));
- for (size_t j = 0; j < std::min(BiomeInfo::MAX_LAYERS,layer_mats[i].size()); j++)
+ for (size_t j = 0; j < std::min(BiomeInfo::MAX_LAYERS,layer_mats[i].size()); j++)
{
biomes[i].layer_stone[j] = layer_mats[i][j];
diff --git a/plugins/eventful.cpp b/plugins/eventful.cpp
index f93c15edc..1bcf737cb 100644
--- a/plugins/eventful.cpp
+++ b/plugins/eventful.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
diff --git a/plugins/zone.cpp b/plugins/zone.cpp
index 0271cb75f..4def386b7 100644
--- a/plugins/zone.cpp
+++ b/plugins/zone.cpp
@@ -3839,12 +3839,12 @@ void unbutcherRace(int race)
static bool autobutcher_isEnabled() { return enable_autobutcher; }
static bool autowatch_isEnabled() { return enable_autobutcher_autowatch; }
-static size_t autobutcher_getSleep(color_ostream &out)
+static unsigned autobutcher_getSleep(color_ostream &out)
{
return sleep_autobutcher;
}
-static void autobutcher_setSleep(color_ostream &out, size_t ticks)
+static void autobutcher_setSleep(color_ostream &out, unsigned ticks)
{
sleep_autobutcher = ticks;
if(config_autobutcher.isValid())
@@ -3892,7 +3892,7 @@ static void autowatch_setEnabled(color_ostream &out, bool enable)
// set all data for a watchlist race in one go
// if race is not already on watchlist it will be added
// params: (id, fk, mk, fa, ma, watched)
-static void autobutcher_setWatchListRace(color_ostream &out, size_t id, size_t fk, size_t mk, size_t fa, size_t ma, bool watched)
+static void autobutcher_setWatchListRace(color_ostream &out, unsigned id, unsigned fk, unsigned mk, unsigned fa, unsigned ma, bool watched)
{
int watched_index = getWatchedIndex(id);
if(watched_index != -1)
@@ -3921,7 +3921,7 @@ static void autobutcher_setWatchListRace(color_ostream &out, size_t id, size_t f
}
// remove entry from watchlist
-static void autobutcher_removeFromWatchList(color_ostream &out, size_t id)
+static void autobutcher_removeFromWatchList(color_ostream &out, unsigned id)
{
int watched_index = getWatchedIndex(id);
if(watched_index != -1)
@@ -3940,7 +3940,7 @@ static void autobutcher_sortWatchList(color_ostream &out)
}
// set default target values for new races
-static void autobutcher_setDefaultTargetNew(color_ostream &out, size_t fk, size_t mk, size_t fa, size_t ma)
+static void autobutcher_setDefaultTargetNew(color_ostream &out, unsigned fk, unsigned mk, unsigned fa, unsigned ma)
{
default_fk = fk;
default_mk = mk;
@@ -3956,9 +3956,9 @@ static void autobutcher_setDefaultTargetNew(color_ostream &out, size_t fk, size_
}
// set default target values for ALL races (update watchlist and set new default)
-static void autobutcher_setDefaultTargetAll(color_ostream &out, size_t fk, size_t mk, size_t fa, size_t ma)
+static void autobutcher_setDefaultTargetAll(color_ostream &out, unsigned fk, unsigned mk, unsigned fa, unsigned ma)
{
- for(size_t i=0; ifk = fk;
@@ -3970,12 +3970,12 @@ static void autobutcher_setDefaultTargetAll(color_ostream &out, size_t fk, size_
autobutcher_setDefaultTargetNew(out, fk, mk, fa, ma);
}
-static void autobutcher_butcherRace(color_ostream &out, size_t id)
+static void autobutcher_butcherRace(color_ostream &out, unsigned id)
{
butcherRace(id);
}
-static void autobutcher_unbutcherRace(color_ostream &out, size_t id)
+static void autobutcher_unbutcherRace(color_ostream &out, unsigned id)
{
unbutcherRace(id);
}
From 5c28d7eaf6aa591c048b26be81662245b6dd0342 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Sat, 25 Jan 2014 09:15:43 -0600
Subject: [PATCH 028/107] Not enough mood tickets given out
---
plugins/strangemood.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp
index 00da1f4f5..aa111d189 100644
--- a/plugins/strangemood.cpp
+++ b/plugins/strangemood.cpp
@@ -575,6 +575,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
continue;
if (cur->relations.draggee_id != -1)
continue;
+ tickets.push_back(i);
for (int j = 0; j < 5; j++)
tickets.push_back(i);
switch (cur->profession)
From 56f935aea6471e4c274ae326af2b16855df739e6 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Sun, 26 Jan 2014 18:55:52 -0600
Subject: [PATCH 029/107] Add bugfix for #6420
---
plugins/strangemood.cpp | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp
index aa111d189..2886a024f 100644
--- a/plugins/strangemood.cpp
+++ b/plugins/strangemood.cpp
@@ -984,7 +984,8 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
item->item_type = item_type::BAR;
item->mat_type = filter->getMaterial();
item->mat_index = filter->getMaterialIndex();
- item->quantity = base_item_count;
+ item->quantity = base_item_count * 150;
+ item->min_dimension = 150;
}
else
{
@@ -1005,7 +1006,8 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
}
if (mats.size())
item->mat_index = mats[rng.df_trandom(mats.size())];
- item->quantity = base_item_count;
+ item->quantity = base_item_count * 150;
+ item->min_dimension = 150;
}
break;
@@ -1258,6 +1260,16 @@ command_result df_strangemood (color_ostream &out, vector & parameters)
item->mat_type = mat_type;
item->flags2.whole = flags2.whole;
item->quantity = 1;
+ if (item_type == item_type::BAR)
+ {
+ item->quantity *= 150;
+ item->min_dimension = 150;
+ }
+ if (item_type == item_type::CLOTH)
+ {
+ item->quantity *= 10000;
+ item->min_dimension = 10000;
+ }
}
}
}
From c0d3e9189ddddd31b6b7fc20f1be8c4581e4af16 Mon Sep 17 00:00:00 2001
From: jj
Date: Mon, 27 Jan 2014 12:40:21 +0100
Subject: [PATCH 030/107] showmood: fix item count for real
---
plugins/showmood.cpp | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp
index 57bfd9658..2a6d6b17c 100644
--- a/plugins/showmood.cpp
+++ b/plugins/showmood.cpp
@@ -169,7 +169,13 @@ command_result df_showmood (color_ostream &out, vector & parameters)
// total amount of stuff fetched so far
int count_got = 0;
for (size_t i = 0; i < job->items.size(); i++)
- count_got += 1; // XXX thread may need job->items[i]->item->getTotalDimension()
+ {
+ df::item_type type = job->job_items[i]->item_type;
+ if (type == item_type::BAR || type == item_type::CLOTH)
+ count_got += job->items[i]->item->getTotalDimension();
+ else
+ count_got += 1;
+ }
for (size_t i = 0; i < job->job_items.size(); i++)
{
From 6b6164c0995828d60915bf068cfcedecf5d48a51 Mon Sep 17 00:00:00 2001
From: jj
Date: Mon, 27 Jan 2014 14:13:31 +0100
Subject: [PATCH 031/107] call dfhack.init even in TEXT mode
---
library/Core.cpp | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/library/Core.cpp b/library/Core.cpp
index fad01e4b2..0f0b18ae7 100644
--- a/library/Core.cpp
+++ b/library/Core.cpp
@@ -811,6 +811,15 @@ bool Core::loadScriptFile(color_ostream &out, string fname, bool silent)
}
}
+// Load dfhack.init in a dedicated thread (non-interactive console mode)
+void fInitthread(void * iodata)
+{
+ IODATA * iod = ((IODATA*) iodata);
+ Core * core = iod->core;
+ Console & con = core->getConsole();
+ core->loadScriptFile(con, "dfhack.init", true);
+}
+
// A thread function... for the interactive console.
void fIOthread(void * iodata)
{
@@ -822,7 +831,7 @@ void fIOthread(void * iodata)
main_history.load("dfhack.history");
Console & con = core->getConsole();
- if(plug_mgr == 0 || core == 0)
+ if (plug_mgr == 0)
{
con.printerr("Something horrible happened in Core's constructor...\n");
return;
@@ -1008,16 +1017,24 @@ bool Core::Init()
IODATA *temp = new IODATA;
temp->core = this;
temp->plug_mgr = plug_mgr;
+
+ HotkeyMutex = new mutex();
+ HotkeyCond = new condition_variable();
+
if (!is_text_mode)
{
cerr << "Starting IO thread.\n";
// create IO thread
thread * IO = new thread(fIOthread, (void *) temp);
}
+ else
+ {
+ cerr << "Starting dfhack.init thread.\n";
+ thread * init = new thread(fInitthread, (void *) temp);
+ }
+
cerr << "Starting DF input capture thread.\n";
// set up hotkey capture
- HotkeyMutex = new mutex();
- HotkeyCond = new condition_variable();
thread * HK = new thread(fHKthread, (void *) temp);
screen_window = new Windows::top_level_window();
screen_window->addChild(new Windows::dfhack_dummy(5,10));
From 6ad8d128ba452047389c542542fc942f79f30da7 Mon Sep 17 00:00:00 2001
From: jj
Date: Mon, 27 Jan 2014 15:10:12 +0100
Subject: [PATCH 032/107] use non-interactive console when calling dfhack.init
in TEXT mode
---
library/Core.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/library/Core.cpp b/library/Core.cpp
index 0f0b18ae7..96fd3903f 100644
--- a/library/Core.cpp
+++ b/library/Core.cpp
@@ -816,8 +816,8 @@ void fInitthread(void * iodata)
{
IODATA * iod = ((IODATA*) iodata);
Core * core = iod->core;
- Console & con = core->getConsole();
- core->loadScriptFile(con, "dfhack.init", true);
+ color_ostream_proxy out(core->getConsole());
+ core->loadScriptFile(out, "dfhack.init", true);
}
// A thread function... for the interactive console.
From 7df9957941225b056d2db2e6bf622c4c115365d6 Mon Sep 17 00:00:00 2001
From: jj
Date: Mon, 27 Jan 2014 15:22:39 +0100
Subject: [PATCH 033/107] dfhack-run: prevent duplicate "not a recognized
command" error message
---
library/dfhack-run.cpp | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/library/dfhack-run.cpp b/library/dfhack-run.cpp
index f79e49a3c..69188f2d6 100644
--- a/library/dfhack-run.cpp
+++ b/library/dfhack-run.cpp
@@ -79,13 +79,10 @@ int main (int argc, char *argv[])
command_result rv = client.run_command(argv[1], args);
- if (rv != CR_OK) {
- if (rv == CR_NOT_IMPLEMENTED)
- out.printerr("%s is not a recognized command.\n", argv[1]);
+ out.flush();
+ if (rv != CR_OK)
return 1;
- }
- out.flush();
return 0;
}
From 8800cf6f40aa90302f3cb412df2152aaed1a411c Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Mon, 10 Feb 2014 18:54:52 +0400
Subject: [PATCH 034/107] Export functions for converting between UTF-8 and
CP437 to lua.
---
Lua API.html | 6 ++++++
Lua API.rst | 8 ++++++++
library/LuaApi.cpp | 5 +++++
3 files changed, 19 insertions(+)
diff --git a/Lua API.html b/Lua API.html
index 4aecf48a0..2a214259c 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -1119,6 +1119,12 @@ can be omitted.
dfhack.TranslateName(name[,in_english,only_last_name])
Convert a language_name or only the last name part to string.
+dfhack.df2utf(string)
+Convert a string from DF's CP437 encoding to UTF-8.
+
+dfhack.utf2df(string)
+Convert a string from UTF-8 to DF's CP437 encoding.
+
diff --git a/Lua API.rst b/Lua API.rst
index 1fd5147b5..6c769f004 100644
--- a/Lua API.rst
+++ b/Lua API.rst
@@ -812,6 +812,14 @@ can be omitted.
Convert a language_name or only the last name part to string.
+* ``dfhack.df2utf(string)``
+
+ Convert a string from DF's CP437 encoding to UTF-8.
+
+* ``dfhack.utf2df(string)``
+
+ Convert a string from UTF-8 to DF's CP437 encoding.
+
Gui module
----------
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index bdc0c39e5..c64ad6634 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -1279,6 +1279,9 @@ static std::string getHackPath() { return Core::getInstance().getHackPath(); }
static bool isWorldLoaded() { return Core::getInstance().isWorldLoaded(); }
static bool isMapLoaded() { return Core::getInstance().isMapLoaded(); }
+static std::string df2utf(std::string s) { return DF2UTF(s); }
+static std::string utf2df(std::string s) { return UTF2DF(s); }
+
static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP(getOSType),
WRAP(getDFVersion),
@@ -1288,6 +1291,8 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP(isWorldLoaded),
WRAP(isMapLoaded),
WRAPM(Translation, TranslateName),
+ WRAP(df2utf),
+ WRAP(utf2df),
{ NULL, NULL }
};
From 7bdb687e4ae5747b0a05f717c3ee5bee51cb1021 Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Mon, 10 Feb 2014 20:09:06 +0400
Subject: [PATCH 035/107] Support calling a lua function via a protobuf
request.
Previously the only way to call lua code was to call scripts
and parse their output to the stream, which is cumbersome.
---
NEWS | 3 ++
library/RemoteTools.cpp | 86 ++++++++++++++++++++++++++++++++
library/dfhack-run.cpp | 47 +++++++++++++++--
library/include/RemoteTools.h | 8 +++
library/proto/CoreProtocol.proto | 7 +++
5 files changed, 146 insertions(+), 5 deletions(-)
diff --git a/NEWS b/NEWS
index a672f2766..854c9353e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,8 @@
DFHack future
+ Internals:
+ - support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua).
+
New commands:
- move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- 'plant create' - spawn a new shrub under the cursor
diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp
index 718d1b181..17f5258e1 100644
--- a/library/RemoteTools.cpp
+++ b/library/RemoteTools.cpp
@@ -55,6 +55,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "modules/Units.h"
#include "modules/World.h"
+#include "LuaTools.h"
+
#include "DataDefs.h"
#include "df/ui.h"
#include "df/ui_advmode.h"
@@ -656,6 +658,8 @@ CoreService::CoreService() {
addMethod("CoreSuspend", &CoreService::CoreSuspend, SF_DONT_SUSPEND);
addMethod("CoreResume", &CoreService::CoreResume, SF_DONT_SUSPEND);
+ addMethod("RunLua", &CoreService::RunLua);
+
// Functions:
addFunction("GetVersion", GetVersion, SF_DONT_SUSPEND);
addFunction("GetDFVersion", GetDFVersion, SF_DONT_SUSPEND);
@@ -730,3 +734,85 @@ command_result CoreService::CoreResume(color_ostream &stream, const EmptyMessage
cnt->set_value(--suspend_depth);
return CR_OK;
}
+
+namespace {
+ struct LuaFunctionData {
+ command_result rv;
+ const dfproto::CoreRunLuaRequest *in;
+ StringListMessage *out;
+ };
+}
+
+command_result CoreService::RunLua(color_ostream &stream,
+ const dfproto::CoreRunLuaRequest *in,
+ StringListMessage *out)
+{
+ auto L = Lua::Core::State;
+ LuaFunctionData data = { CR_FAILURE, in, out };
+
+ lua_pushcfunction(L, doRunLuaFunction);
+ lua_pushlightuserdata(L, &data);
+
+ if (!Lua::Core::SafeCall(stream, 1, 0))
+ return CR_FAILURE;
+
+ return data.rv;
+}
+
+int CoreService::doRunLuaFunction(lua_State *L)
+{
+ color_ostream &out = *Lua::GetOutput(L);
+ auto &args = *(LuaFunctionData*)lua_touserdata(L, 1);
+
+ // Verify module name
+ std::string module = args.in->module();
+ size_t len = module.size();
+
+ bool valid = false;
+
+ if (len > 4)
+ {
+ if (module.substr(0,4) == "rpc.")
+ valid = true;
+ else if ((module[len-4] == '.' || module[len-4] == '-') && module.substr(len-3) != "rpc")
+ valid = true;
+ }
+
+ if (!valid)
+ {
+ args.rv = CR_WRONG_USAGE;
+ out.printerr("Only modules named rpc.* or *.rpc or *-rpc may be called.\n");
+ return 0;
+ }
+
+ // Prepare function and arguments
+ lua_settop(L, 0);
+
+ if (!Lua::PushModulePublic(out, L, module.c_str(), args.in->function().c_str())
+ || lua_isnil(L, 1))
+ {
+ args.rv = CR_NOT_FOUND;
+ return 0;
+ }
+
+ luaL_checkstack(L, args.in->arguments_size(), "too many arguments");
+
+ for (int i = 0; i < args.in->arguments_size(); i++)
+ lua_pushstring(L, args.in->arguments(i).c_str());
+
+ // Call
+ lua_call(L, args.in->arguments_size(), LUA_MULTRET);
+
+ // Store results
+ int nresults = lua_gettop(L);
+
+ for (int i = 1; i <= nresults; i++)
+ {
+ size_t len;
+ const char *data = lua_tolstring(L, i, &len);
+ args.out->add_value(std::string(data, len));
+ }
+
+ args.rv = CR_OK;
+ return 0;
+}
diff --git a/library/dfhack-run.cpp b/library/dfhack-run.cpp
index 69188f2d6..abe34f34d 100644
--- a/library/dfhack-run.cpp
+++ b/library/dfhack-run.cpp
@@ -72,12 +72,49 @@ int main (int argc, char *argv[])
if (!client.connect())
return 2;
- // Call the command
- std::vector args;
- for (int i = 2; i < argc; i++)
- args.push_back(argv[i]);
+ command_result rv;
- command_result rv = client.run_command(argv[1], args);
+ if (strcmp(argv[1], "--lua") == 0)
+ {
+ if (argc <= 3)
+ {
+ fprintf(stderr, "Usage: dfhack-run --lua [args...]\n");
+ return 2;
+ }
+
+ RemoteFunction run_call;
+
+ if (!run_call.bind(&client, "RunLua"))
+ {
+ fprintf(stderr, "No RunLua protocol function found.");
+ return 3;
+ }
+
+ run_call.in()->set_module(argv[2]);
+ run_call.in()->set_function(argv[3]);
+ for (int i = 4; i < argc; i++)
+ run_call.in()->add_arguments(argv[i]);
+
+ rv = run_call();
+
+ out.flush();
+
+ if (rv == CR_OK)
+ {
+ for (int i = 0; i < run_call.out()->value_size(); i++)
+ printf("%s%s", (i>0?"\t":""), run_call.out()->value(i).c_str());
+ printf("\n");
+ }
+ }
+ else
+ {
+ // Call the command
+ std::vector args;
+ for (int i = 2; i < argc; i++)
+ args.push_back(argv[i]);
+
+ rv = client.run_command(argv[1], args);
+ }
out.flush();
diff --git a/library/include/RemoteTools.h b/library/include/RemoteTools.h
index e70cefc00..ead1c0aa1 100644
--- a/library/include/RemoteTools.h
+++ b/library/include/RemoteTools.h
@@ -48,6 +48,8 @@ namespace DFHack
DFHACK_EXPORT void strVectorToRepeatedField(RepeatedPtrField *pf,
const std::vector &vec);
+ using dfproto::StringListMessage;
+
/**
* Represent bitfield bits as a repeated string field.
*/
@@ -131,6 +133,8 @@ namespace DFHack
class CoreService : public RPCService {
int suspend_depth;
+
+ static int doRunLuaFunction(lua_State *L);
public:
CoreService();
~CoreService();
@@ -144,5 +148,9 @@ namespace DFHack
// For batching
command_result CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
command_result CoreResume(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
+
+ command_result RunLua(color_ostream &stream,
+ const dfproto::CoreRunLuaRequest *in,
+ StringListMessage *out);
};
}
diff --git a/library/proto/CoreProtocol.proto b/library/proto/CoreProtocol.proto
index 92d7c48d9..e53078875 100644
--- a/library/proto/CoreProtocol.proto
+++ b/library/proto/CoreProtocol.proto
@@ -81,3 +81,10 @@ message CoreRunCommandRequest {
// RPC CoreSuspend : EmptyMessage -> IntMessage
// RPC CoreResume : EmptyMessage -> IntMessage
+
+// RPC RunLua : CoreRunLuaRequest -> StringListMessage
+message CoreRunLuaRequest {
+ required string module = 1;
+ required string function = 2;
+ repeated string arguments = 3;
+}
From 67b7681ba62c1a95181bae2e34a6eafdd79511e1 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Thu, 13 Feb 2014 11:36:45 -0600
Subject: [PATCH 036/107] Fix reaction errors, make quantities more consistent
---
plugins/raw/reaction_spatter.txt | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/plugins/raw/reaction_spatter.txt b/plugins/raw/reaction_spatter.txt
index 95b9f1d33..062e82287 100644
--- a/plugins/raw/reaction_spatter.txt
+++ b/plugins/raw/reaction_spatter.txt
@@ -77,8 +77,8 @@ Reaction name must start with 'SPATTER_ADD_':
[NAME:coat weapon with GCS venom]
[BUILDING:GREASING_STATION:NONE]
[SKILL:DYER]
- [REAGENT:extract:150:LIQUID_MISC:NONE:CAVE_SPIDER_GIANT:POISON]
- [MIN_DIMENSION:150]
+ [REAGENT:extract:100:LIQUID_MISC:NONE:CREATURE_MAT:SPIDER_CAVE_GIANT:POISON]
+ [MIN_DIMENSION:100]
[REACTION_CLASS:CREATURE_EXTRACT]
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
[CONTAINS:extract]
@@ -97,7 +97,7 @@ Reaction name must start with 'SPATTER_ADD_':
[NAME:coat ammo with GCS venom]
[BUILDING:GREASING_STATION:NONE]
[SKILL:DYER]
- [REAGENT:extract:50:LIQUID_MISC:NONE:CAVE_SPIDER_GIANT:POISON]
+ [REAGENT:extract:50:LIQUID_MISC:NONE:CREATURE_MAT:SPIDER_CAVE_GIANT:POISON]
[MIN_DIMENSION:50]
[REACTION_CLASS:CREATURE_EXTRACT]
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
@@ -117,8 +117,8 @@ Reaction name must start with 'SPATTER_ADD_':
[NAME:coat weapon with GDS venom]
[BUILDING:GREASING_STATION:NONE]
[SKILL:DYER]
- [REAGENT:extract:150:LIQUID_MISC:NONE:SCORPION_DESERT_GIANT:POISON]
- [MIN_DIMENSION:150]
+ [REAGENT:extract:100:LIQUID_MISC:NONE:CREATURE_MAT:SCORPION_DESERT_GIANT:POISON]
+ [MIN_DIMENSION:100]
[REACTION_CLASS:CREATURE_EXTRACT]
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
[CONTAINS:extract]
@@ -137,7 +137,7 @@ Reaction name must start with 'SPATTER_ADD_':
[NAME:coat ammo with GDS venom]
[BUILDING:GREASING_STATION:NONE]
[SKILL:DYER]
- [REAGENT:extract:50:LIQUID_MISC:NONE:SCORPION_DESERT_GIANT:POISON]
+ [REAGENT:extract:50:LIQUID_MISC:NONE:CREATURE_MAT:SCORPION_DESERT_GIANT:POISON]
[MIN_DIMENSION:50]
[REACTION_CLASS:CREATURE_EXTRACT]
[REAGENT:extract container:1:NONE:NONE:NONE:NONE]
From 73a112e06c68a73dc74e9f601cc3074883d550f4 Mon Sep 17 00:00:00 2001
From: Quietust
Date: Fri, 14 Feb 2014 11:06:33 -0600
Subject: [PATCH 037/107] Update tubefill to skip hollow tubes by default, and
update docs
---
Compile.html | 94 ++++++++++++++++++++++++++++++--------------
Contributors.html | 36 ++++++++++++-----
Readme.html | 23 +++++++++--
Readme.rst | 9 ++++-
plugins/tubefill.cpp | 28 ++++++++++---
5 files changed, 139 insertions(+), 51 deletions(-)
diff --git a/Compile.html b/Compile.html
index 99fdf98e7..7ae0df1c1 100644
--- a/Compile.html
+++ b/Compile.html
@@ -3,13 +3,13 @@
-
+
Building DFHACK