develop
Timothy Collett 2013-03-14 13:35:28 -04:00
commit c93cb693c8
17 changed files with 940 additions and 342 deletions

@ -59,7 +59,7 @@ endif()
# set up versioning. # set up versioning.
set(DF_VERSION "0.34.11") set(DF_VERSION "0.34.11")
SET(DFHACK_RELEASE "r2" CACHE STRING "Current release revision.") SET(DFHACK_RELEASE "r3" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}") add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")

@ -402,10 +402,16 @@ ul.auto-toc {
<li><a class="reference internal" href="#plugins" id="id52">Plugins</a><ul> <li><a class="reference internal" href="#plugins" id="id52">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id53">burrows</a></li> <li><a class="reference internal" href="#burrows" id="id53">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id54">sort</a></li> <li><a class="reference internal" href="#sort" id="id54">sort</a></li>
<li><a class="reference internal" href="#eventful" id="id55">Eventful</a><ul>
<li><a class="reference internal" href="#list-of-events" id="id56">List of events</a></li>
<li><a class="reference internal" href="#functions" id="id57">Functions</a></li>
<li><a class="reference internal" href="#examples" id="id58">Examples</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#scripts" id="id55">Scripts</a><ul> </ul>
<li><a class="reference internal" href="#save-init-script" id="id56">Save init script</a></li> </li>
<li><a class="reference internal" href="#scripts" id="id59">Scripts</a><ul>
<li><a class="reference internal" href="#save-init-script" id="id60">Save init script</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -3038,9 +3044,93 @@ set is the same as used by the command line.</p>
<p>Does not export any native functions as of now. Instead, it <p>Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items.</p> calls lua code to perform the actual ordering of list items.</p>
</div> </div>
<div class="section" id="eventful">
<h2><a class="toc-backref" href="#id55">Eventful</a></h2>
<p>This plugin exports some events to lua thus allowing to run lua functions
on DF world events.</p>
<div class="section" id="list-of-events">
<h3><a class="toc-backref" href="#id56">List of events</a></h3>
<ol class="arabic">
<li><p class="first"><tt class="docutils literal">onReactionComplete(reaction,unit,input_items,input_reagents,output_items,call_native)</tt></p>
<p>Auto activates if detects reactions starting with <tt class="docutils literal">LUA_HOOK_</tt>. Is called when reaction finishes.</p>
</li>
<li><p class="first"><tt class="docutils literal">onItemContaminateWound(item,unit,wound,number1,number2)</tt></p>
<p>Is called when item tries to contaminate wound (e.g. stuck in).</p>
</li>
<li><p class="first"><tt class="docutils literal">onProjItemCheckMovement(projectile)</tt></p>
<p>Is called when projectile moves.</p>
</li>
<li><p class="first"><tt class="docutils literal">onProjItemCheckImpact(projectile,somebool)</tt></p>
<p>Is called when projectile hits something.</p>
</li>
<li><p class="first"><tt class="docutils literal">onProjUnitCheckMovement(projectile)</tt></p>
<p>Is called when projectile moves.</p>
</li>
<li><p class="first"><tt class="docutils literal">onProjUnitCheckImpact(projectile,somebool)</tt></p>
<p>Is called when projectile hits something.</p>
</li>
<li><p class="first"><tt class="docutils literal">onWorkshopFillSidebarMenu(workshop,callnative)</tt></p>
<p>Is called when viewing a workshop in 'q' mode, to populate reactions, useful for custom viewscreens for shops.</p>
</li>
<li><p class="first"><tt class="docutils literal">postWorkshopFillSidebarMenu(workshop)</tt></p>
<p>Is called after calling (or not) native fillSidebarMenu(). Useful for job button
tweaking (e.g. adding custom reactions)</p>
</li>
</ol>
</div>
<div class="section" id="functions">
<h3><a class="toc-backref" href="#id57">Functions</a></h3>
<ol class="arabic">
<li><p class="first"><tt class="docutils literal">registerReaction(reaction_name,callback)</tt></p>
<p>Simplified way of using onReactionComplete; the callback is function (same params as event).</p>
</li>
<li><p class="first"><tt class="docutils literal">removeNative(shop_name)</tt></p>
<p>Removes native choice list from the building.</p>
</li>
<li><p class="first"><tt class="docutils literal">addReactionToShop(reaction_name,shop_name)</tt></p>
<p>Add a custom reaction to the building.</p>
</li>
</ol>
</div>
<div class="section" id="examples">
<h3><a class="toc-backref" href="#id58">Examples</a></h3>
<p>Spawn dragon breath on each item attempt to contaminate wound:</p>
<pre class="literal-block">
b=require &quot;plugins.eventful&quot;
b.onItemContaminateWound.one=function(item,unit,un_wound,x,y)
local flw=dfhack.maps.spawnFlow(unit.pos,6,0,0,50000)
end
</pre>
<p>Reaction complete example:</p>
<pre class="literal-block">
b=require &quot;plugins.eventful&quot;
b.onReactionComplete.one=function(reaction,unit,in_items,in_reag,out_items,call_native)
local pos=copyall(unit.pos)
-- spawn dragonbreath after 100 ticks
dfhack.timeout(100,&quot;ticks&quot;,function() dfhack.maps.spawnFlow(pos,6,0,0,50000) end)
--do not call real item creation code
call_native.value=false
end
</pre>
<p>Grenade example:</p>
<pre class="literal-block">
b=require &quot;plugins.eventful&quot;
b.onProjItemCheckImpact.one=function(projectile)
-- you can check if projectile.item e.g. has correct material
dfhack.maps.spawnFlow(projectile.cur_pos,6,0,0,50000)
end
</pre>
<p>Integrated tannery:</p>
<pre class="literal-block">
b=require &quot;plugins.eventful&quot;
b.addReactionToShop(&quot;TAN_A_HIDE&quot;,&quot;LEATHERWORKS&quot;)
</pre>
</div>
</div>
</div> </div>
<div class="section" id="scripts"> <div class="section" id="scripts">
<h1><a class="toc-backref" href="#id55">Scripts</a></h1> <h1><a class="toc-backref" href="#id59">Scripts</a></h1>
<p>Any files with the .lua extension placed into hack/scripts/* <p>Any files with the .lua extension placed into hack/scripts/*
are automatically used by the DFHack core as commands. The are automatically used by the DFHack core as commands. The
matching command name consists of the name of the file sans matching command name consists of the name of the file sans
@ -3071,7 +3161,7 @@ The <tt class="docutils literal">name</tt> argument should be the name stem, as
</ul> </ul>
<p>Note that this function lets errors propagate to the caller.</p> <p>Note that this function lets errors propagate to the caller.</p>
<div class="section" id="save-init-script"> <div class="section" id="save-init-script">
<h2><a class="toc-backref" href="#id56">Save init script</a></h2> <h2><a class="toc-backref" href="#id60">Save init script</a></h2>
<p>If a save directory contains a file called <tt class="docutils literal">raw/init.lua</tt>, it is <p>If a save directory contains a file called <tt class="docutils literal">raw/init.lua</tt>, it is
automatically loaded and executed every time the save is loaded. It automatically loaded and executed every time the save is loaded. It
can also define the following functions to be called by dfhack:</p> can also define the following functions to be called by dfhack:</p>

@ -2967,51 +2967,85 @@ on DF world events.
List of events List of events
-------------- --------------
1. onReactionComplete(reaction,unit,input_items,input_reagents,output_items,call_native) - auto activates if detects reactions starting with ``LUA_HOOK_``. Is called when reaction finishes. 1. ``onReactionComplete(reaction,unit,input_items,input_reagents,output_items,call_native)``
2. onItemContaminateWound(item,unit,wound,number1,number2) - Is called when item tries to contaminate wound (e.g. stuck in)
3. onProjItemCheckMovement(projectile) - is called when projectile moves Auto activates if detects reactions starting with ``LUA_HOOK_``. Is called when reaction finishes.
4. onProjItemCheckImpact(projectile,somebool) - is called when projectile hits something
5. onProjUnitCheckMovement(projectile) - is called when projectile moves 2. ``onItemContaminateWound(item,unit,wound,number1,number2)``
6. onProjUnitCheckImpact(projectile,somebool) - is called when projectile hits something
7. onWorkshopFillSidebarMenu(workshop,callnative) - is called when viewing a workshop in 'q' mode, to populate reactions, usefull for custom viewscreens for shops Is called when item tries to contaminate wound (e.g. stuck in).
8. postWorkshopFillSidebarMenu(workshop) - is called after calling (or not) native fillSidebarMenu(). Usefull for job button tweaking (e.g. adding custom reactions)
3. ``onProjItemCheckMovement(projectile)``
Is called when projectile moves.
4. ``onProjItemCheckImpact(projectile,somebool)``
Is called when projectile hits something.
5. ``onProjUnitCheckMovement(projectile)``
Is called when projectile moves.
6. ``onProjUnitCheckImpact(projectile,somebool)``
Is called when projectile hits something.
7. ``onWorkshopFillSidebarMenu(workshop,callnative)``
Is called when viewing a workshop in 'q' mode, to populate reactions, useful for custom viewscreens for shops.
8. ``postWorkshopFillSidebarMenu(workshop)``
Is called after calling (or not) native fillSidebarMenu(). Useful for job button
tweaking (e.g. adding custom reactions)
Functions Functions
--------- ---------
1. registerReaction(reaction_name,callback) - simplified way of using onReactionComplete, the callback is function (same params as event) 1. ``registerReaction(reaction_name,callback)``
2. removeNative(shop_name) - removes native choice list from the building
3. addReactionToShop(reaction_name,shop_name) - add a custom reaction to the building Simplified way of using onReactionComplete; the callback is function (same params as event).
2. ``removeNative(shop_name)``
Removes native choice list from the building.
3. ``addReactionToShop(reaction_name,shop_name)``
Add a custom reaction to the building.
Examples Examples
-------- --------
Spawn dragon breath on each item attempt to contaminate wound: Spawn dragon breath on each item attempt to contaminate wound::
::
b=require "plugins.eventful" b=require "plugins.eventful"
b.onItemContaminateWound.one=function(item,unit,un_wound,x,y) b.onItemContaminateWound.one=function(item,unit,un_wound,x,y)
local flw=dfhack.maps.spawnFlow(unit.pos,6,0,0,50000) local flw=dfhack.maps.spawnFlow(unit.pos,6,0,0,50000)
end end
Reaction complete example: Reaction complete example::
::
b=require "plugins.eventful" b=require "plugins.eventful"
b.onReactionComplete.one=function(reaction,unit,in_items,in_reag,out_items,call_native) b.onReactionComplete.one=function(reaction,unit,in_items,in_reag,out_items,call_native)
local pos=copyall(unit.pos) local pos=copyall(unit.pos)
dfhack.timeout(100,"ticks",function() dfhack.maps.spawnFlow(pos,6,0,0,50000) end) -- spawn dragonbreath after 100 ticks -- spawn dragonbreath after 100 ticks
call_native.value=false --do not call real item creation code dfhack.timeout(100,"ticks",function() dfhack.maps.spawnFlow(pos,6,0,0,50000) end)
--do not call real item creation code
call_native.value=false
end end
Granade example: Grenade example::
::
b=require "plugins.eventful" b=require "plugins.eventful"
b.onProjItemCheckImpact.one=function(projectile) b.onProjItemCheckImpact.one=function(projectile)
-- you can check if projectile.item e.g. has correct material -- you can check if projectile.item e.g. has correct material
dfhack.maps.spawnFlow(projectile.cur_pos,6,0,0,50000) dfhack.maps.spawnFlow(projectile.cur_pos,6,0,0,50000)
end end
Integrated tannery: Integrated tannery::
::
b=require "plugins.eventful" b=require "plugins.eventful"
b.addReactionToShop("TAN_A_HIDE","LEATHERWORKS") b.addReactionToShop("TAN_A_HIDE","LEATHERWORKS")

@ -1,10 +1,15 @@
DFHack future DFHack future
Is not yet known.
DFHack v0.34.11-r3
Internals: Internals:
- support for displaying active keybindings properly. - support for displaying active keybindings properly.
- support for reusable widgets in lua screen library. - support for reusable widgets in lua screen library.
- Maps::canStepBetween: returns whether you can walk between two tiles in one step. - Maps::canStepBetween: returns whether you can walk between two tiles in one step.
- EventManager: monitors various in game events centrally so that individual plugins don't have to monitor the same things redundantly. - EventManager: monitors various in game events centrally so that individual plugins
don't have to monitor the same things redundantly.
Notable bugfixes: Notable bugfixes:
- autobutcher can be re-enabled again after being stopped. - autobutcher can be re-enabled again after being stopped.
- stopped Dwarf Manipulator from unmasking vampires. - stopped Dwarf Manipulator from unmasking vampires.
@ -28,6 +33,7 @@ DFHack future
- stripcaged: mark items inside cages for dumping, eg caged goblin weapons. - stripcaged: mark items inside cages for dumping, eg caged goblin weapons.
- soundsense-season: writes the correct season to gamelog.txt on world load. - soundsense-season: writes the correct season to gamelog.txt on world load.
- create-items: spawn items - create-items: spawn items
- fix/cloth-stockpile: fixes bug 5739; needs to be run after savegame load every time.
New GUI scripts: New GUI scripts:
- gui/guide-path: displays the cached path for minecart Guide orders. - gui/guide-path: displays the cached path for minecart Guide orders.
- gui/workshop-job: displays inputs of a workshop job and allows tweaking them. - gui/workshop-job: displays inputs of a workshop job and allows tweaking them.

File diff suppressed because it is too large Load Diff

@ -449,6 +449,26 @@ Options:
:bees: turn colonies into honey bee colonies :bees: turn colonies into honey bee colonies
createitem
----------
Allows creating new items of arbitrary types and made of arbitrary materials.
Any items created are spawned at the feet of the selected unit.
Specify the item and material information as you would indicate them in custom reaction raws, with the following differences:
* Separate the item and material with a space rather than a colon
* If the item has no subtype, omit the :NONE
* If the item is REMAINS, FISH, FISH_RAW, VERMIN, PET, or EGG, specify a CREATURE:CASTE pair instead of a material token.
Corpses, body parts, and prepared meals cannot be created using this tool.
Examples:
``createitem GLOVES:ITEM_GLOVES_GAUNTLETS INORGANIC:STEEL 2``
Create 2 pairs of steel gauntlets.
``createitem WOOD PLANT_MAT:TOWER_CAP:WOOD``
Create tower-cap logs.
``createitem FISH FISH_SHAD:MALE 5``
Create a stack of 5 cleaned shad, ready to eat.
deramp (by zilpin) deramp (by zilpin)
------------------ ------------------
Removes all ramps designated for removal from the map. This is useful for replicating the old channel digging designation. Removes all ramps designated for removal from the map. This is useful for replicating the old channel digging designation.
@ -1773,6 +1793,12 @@ Scripts in this subdirectory fix various bugs and issues, some of them obscure.
Diagnoses and fixes issues with nonexistant 'items occupying site', usually Diagnoses and fixes issues with nonexistant 'items occupying site', usually
caused by autodump bugs or other hacking mishaps. caused by autodump bugs or other hacking mishaps.
* fix/cloth-stockpile
Fixes erratic behavior of cloth stockpiles by scanning material objects
in memory and patching up some invalid reference fields. Needs to be run
every time a save game is loaded; putting ``fix/cloth-stockpile enable``
in ``dfhack.init`` makes it run automatically.
gui/* gui/*
===== =====
@ -2032,6 +2058,17 @@ Exemples::
create-items bar CREATURE:CAT:SOAP create-items bar CREATURE:CAT:SOAP
create-items bar adamantine create-items bar adamantine
soundsense-season
=================
It is a well known issue that Soundsense cannot detect the correct
current season when a savegame is loaded and has to play random
season music until a season switch occurs.
This script registers a hook that prints the appropriate string
to gamelog.txt on every map load to fix this. For best results
call the script from ``dfhack.init``.
======================= =======================
In-game interface tools In-game interface tools
======================= =======================
@ -2185,8 +2222,25 @@ To use, bind to a key (the example config uses Alt-L) and activate in the 'k' mo
.. image:: images/liquids.png .. image:: images/liquids.png
While active, use the suggested keys to switch the usual liquids parameters, and Enter This script is a gui front-end to the liquids plugin and works similar to it,
to select the target area and apply changes. allowing you to add or remove water & magma, and create obsidian walls & floors.
Note that there is **no undo support**, and that bugs in this plugin have been
known to create pathfinding problems and heat traps.
The ``b`` key changes how the affected area is selected. The default *Rectangle*
mode works by selecting two corners like any ordinary designation. The ``p``
key chooses between adding water, magma, obsidian walls & floors, or just
tweaking flags.
When painting liquids, it is possible to select the desired level with ``+-``,
and choose between setting it exactly, only increasing or only decreasing
with ``s``.
In addition, ``f`` allows disabling or enabling the flowing water computations
for an area, and ``r`` operates on the "permanent flow" property that makes
rivers power water wheels even when full and technically not flowing.
After setting up the desired operations using the described keys, use ``Enter`` to apply them.
gui/mechanisms gui/mechanisms
@ -2430,6 +2484,21 @@ keybinding. (e.g. keybinding set Ctrl-T gui/advfort). Possible arguments:
* job - selects that job (e.g. Dig or FellTree) * job - selects that job (e.g. Dig or FellTree)
gui/companion-order
=======================
A script to issue orders for companions. Select companions with lower case chars, issue orders with upper
case. Must be in look or talk mode to issue command on tile.
* move - orders selected companions to move to location. If companions are following they will move no more than 3 tiles from you.
* equip - try to equip items on the ground.
* pick-up - try to take items into hand (also wield)
* unequip - remove and drop equipment
* unwield - drop held items
* wait - temporarely remove from party
* follow - rejoin the party after "wait"
* leave - remove from party (can be rejoined by talking)
gui/gm-editor gui/gm-editor
============= =============
@ -2442,7 +2511,7 @@ There are three ways to open this editor:
* using gui/gm-editor <lua command> - executes lua command and opens editor on * using gui/gm-editor <lua command> - executes lua command and opens editor on
it's results (e.g. gui/gm-editor "df.global.world.items.all" shows all items) it's results (e.g. gui/gm-editor "df.global.world.items.all" shows all items)
* using gui/gm-edito dialog - shows an in game dialog to input lua command. Works * using gui/gm-editor dialog - shows an in game dialog to input lua command. Works
the same as version above. the same as version above.
This editor allows to change and modify almost anything in df. Press '?' for an This editor allows to change and modify almost anything in df. Press '?' for an

@ -373,7 +373,8 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std::
if (possible.size() == 1) if (possible.size() == 1)
{ {
completed = possible[0]; completed = possible[0];
fprintf(stderr, "Autocompleted %s to %s\n", first.c_str(), completed.c_str()); //fprintf(stderr, "Autocompleted %s to %s\n", , );
con.printerr("%s is not recognized. Did you mean %s?\n", first.c_str(), completed.c_str());
return true; return true;
} }
@ -382,7 +383,8 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std::
std::string out; std::string out;
for (size_t i = 0; i < possible.size(); i++) for (size_t i = 0; i < possible.size(); i++)
out += " " + possible[i]; out += " " + possible[i];
con.print("Possible completions:%s\n", out.c_str()); con.printerr("%s is not recognized. Possible completions:%s\n", first.c_str(), out.c_str());
return true;
} }
return false; return false;
@ -717,7 +719,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
else if (plug_mgr->eval_ruby && fileExists(filename + ".rb")) else if (plug_mgr->eval_ruby && fileExists(filename + ".rb"))
res = runRubyScript(con, plug_mgr, first, parts); res = runRubyScript(con, plug_mgr, first, parts);
else if (try_autocomplete(con, first, completed)) else if (try_autocomplete(con, first, completed))
return runCommand(con, completed, parts); return CR_NOT_IMPLEMENTED;// runCommand(con, completed, parts);
else else
con.printerr("%s is not a recognized command.\n", first.c_str()); con.printerr("%s is not a recognized command.\n", first.c_str());
} }

@ -1,5 +1,7 @@
-- Simple binary patch with IDA dif file support. -- Simple binary patch with IDA dif file support.
local _ENV = mkmodule('binpatch')
local function load_patch(name) local function load_patch(name)
local filename = name local filename = name
if not string.match(filename, '[./\\]') then if not string.match(filename, '[./\\]') then

@ -1 +1 @@
Subproject commit c7e2c28febd6dca06ff7e9951090982fbbee12b5 Subproject commit 4d2afc3a0bcebdb17415dc2827b44fd35986a368

@ -136,6 +136,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(autoSyndrome autoSyndrome.cpp) DFHACK_PLUGIN(autoSyndrome autoSyndrome.cpp)
DFHACK_PLUGIN(trueTransformation trueTransformation.cpp) DFHACK_PLUGIN(trueTransformation trueTransformation.cpp)
DFHACK_PLUGIN(infiniteSky infiniteSky.cpp) DFHACK_PLUGIN(infiniteSky infiniteSky.cpp)
DFHACK_PLUGIN(createitem createitem.cpp)
endif() endif()

@ -0,0 +1,257 @@
// Create arbitrary items
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "MiscUtils.h"
#include "modules/Maps.h"
#include "modules/Gui.h"
#include "modules/Items.h"
#include "modules/Materials.h"
#include "DataDefs.h"
#include "df/game_type.h"
#include "df/world.h"
#include "df/ui.h"
#include "df/unit.h"
#include "df/historical_entity.h"
#include "df/world_site.h"
#include "df/item.h"
#include "df/creature_raw.h"
#include "df/caste_raw.h"
#include "df/reaction_reagent.h"
#include "df/reaction_product_itemst.h"
using namespace std;
using namespace DFHack;
using df::global::world;
using df::global::ui;
using df::global::gametype;
DFHACK_PLUGIN("createitem");
command_result df_createitem (color_ostream &out, vector <string> & parameters);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector<PluginCommand> &commands)
{
commands.push_back(PluginCommand("createitem", "Create arbitrary item at the selected unit's feet.", df_createitem));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool glove2 = false)
{
vector<df::item *> out_items;
vector<df::reaction_reagent *> in_reag;
vector<df::item *> in_items;
bool is_gloves = (prod->item_type == df::item_type::GLOVES);
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);
if (!out_items.size())
return false;
for (size_t i = 0; i < out_items.size(); i++)
{
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)
if (out_items[i]->getGloveHandedness() > 0)
is_gloves = false;
else
out_items[i]->setGloveHandedness(glove2 ? 2 : 1);
}
}
if (is_gloves && !glove2)
return makeItem(prod, unit, true);
return true;
}
command_result df_createitem (color_ostream &out, vector <string> & parameters)
{
string item_str, material_str;
df::item_type item_type = df::item_type::NONE;
int16_t item_subtype = -1;
int16_t mat_type = -1;
int32_t mat_index = -1;
int count = 1;
if ((parameters.size() < 2) || (parameters.size() > 3))
{
out.print("Syntax: createitem <item> <material> [count]\n"
" <item> - Item token for what you wish to create, as specified in custom\n"
" reactions. If the item has no subtype, omit the :NONE.\n"
" <material> - 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];
if (parameters.size() == 3)
{
stringstream ss(parameters[2]);
ss >> count;
if (count < 1)
{
out.printerr("You cannot produce less than one item!\n");
return CR_FAILURE;
}
}
ItemTypeInfo item;
MaterialInfo material;
vector<string> tokens;
if (!item.find(item_str))
{
out.printerr("Unrecognized item type!\n");
return CR_FAILURE;
}
item_type = item.type;
item_subtype = item.subtype;
switch (item.type)
{
case df::item_type::INSTRUMENT:
case df::item_type::TOY:
case df::item_type::WEAPON:
case df::item_type::ARMOR:
case df::item_type::SHOES:
case df::item_type::SHIELD:
case df::item_type::HELM:
case df::item_type::GLOVES:
case df::item_type::AMMO:
case df::item_type::PANTS:
case df::item_type::SIEGEAMMO:
case df::item_type::TRAPCOMP:
case df::item_type::TOOL:
if (item_subtype == -1)
{
out.printerr("You must specify a subtype!\n");
return CR_FAILURE;
}
default:
if (!material.find(material_str))
{
out.printerr("Unrecognized material!\n");
return CR_FAILURE;
}
mat_type = material.type;
mat_index = material.index;
break;
case df::item_type::REMAINS:
case df::item_type::FISH:
case df::item_type::FISH_RAW:
case df::item_type::VERMIN:
case df::item_type::PET:
case df::item_type::EGG:
split_string(&tokens, material_str, ":");
if (tokens.size() != 2)
{
out.printerr("You must specify a creature ID and caste for this item type!\n");
return CR_FAILURE;
}
for (size_t i = 0; i < world->raws.creatures.all.size(); i++)
{
df::creature_raw *creature = world->raws.creatures.all[i];
if (creature->creature_id == tokens[0])
{
for (size_t j = 0; j < creature->caste.size(); j++)
{
df::caste_raw *caste = creature->caste[j];
if (creature->caste[j]->caste_id == tokens[1])
{
mat_type = i;
mat_index = j;
break;
}
}
if (mat_type == -1)
{
out.printerr("The creature you specified has no such caste!\n");
return CR_FAILURE;
}
}
}
if (mat_type == -1)
{
out.printerr("Unrecognized creature ID!\n");
return CR_FAILURE;
}
break;
case df::item_type::CORPSE:
case df::item_type::CORPSEPIECE:
case df::item_type::FOOD:
out.printerr("Cannot create that type of item!\n");
return CR_FAILURE;
break;
}
CoreSuspender suspend;
df::unit *unit = Gui::getSelectedUnit(out, true);
if (!unit)
{
out.printerr("No unit selected!\n");
return CR_FAILURE;
}
if (!Maps::IsValid())
{
out.printerr("Map is not available.\n");
return CR_FAILURE;
}
df::map_block *block = Maps::getTileBlock(unit->pos.x, unit->pos.y, unit->pos.z);
if (block == NULL)
{
out.printerr("Unit does not reside in a valid map block, somehow?\n");
return CR_FAILURE;
}
df::reaction_product_itemst *prod = df::allocate<df::reaction_product_itemst>();
prod->item_type = item_type;
prod->item_subtype = item_subtype;
prod->mat_type = mat_type;
prod->mat_index = mat_index;
prod->probability = 100;
prod->count = count;
switch (item_type)
{
case df::item_type::BAR:
case df::item_type::POWDER_MISC:
case df::item_type::LIQUID_MISC:
case df::item_type::DRINK:
prod->product_dimension = 150;
break;
case df::item_type::THREAD:
prod->product_dimension = 15000;
break;
case df::item_type::CLOTH:
prod->product_dimension = 10000;
break;
default:
prod->product_dimension = 1;
break;
}
if (!makeItem(prod, unit))
{
out.printerr("Failed to create item!\n");
return CR_FAILURE;
}
return CR_OK;
}

@ -60,7 +60,7 @@ struct ProductInfo {
MaterialSource material; MaterialSource material;
bool isValid() { bool isValid() {
return (material.mat_type >= 0 || material.reagent); return true;//due to mat_type being -1 = any
} }
}; };

@ -178,6 +178,7 @@ end
function BinaryPlugin:move_to_df() function BinaryPlugin:move_to_df()
local _,addr=df.sizeof(self.data) local _,addr=df.sizeof(self.data)
markAsExecutable(addr) markAsExecutable(addr)
return addr
end end
function BinaryPlugin:print_data() function BinaryPlugin:print_data()
local out="" local out=""

@ -9,9 +9,18 @@ FriendshipRainbow.name="FriendshipRainbow"
-- os independant... I think... -- os independant... I think...
FriendshipRainbow.ATTRS{filename="hack/lua/plugins/dfusion/friendship.o",name="FriendshipRainbow",race_data=DEFAULT_NIL} FriendshipRainbow.ATTRS{filename="hack/lua/plugins/dfusion/friendship.o",name="FriendshipRainbow",race_data=DEFAULT_NIL}
FriendshipRainbow.class_status="valid, not installed" FriendshipRainbow.class_status="valid, not installed"
function FriendshipRainbow:findall_needles(codesg,needle) -- todo move to memscan.lua
local cidx,caddr=codesg.uint8_t:find(needle)
local ret={}
while cidx~=nil do
table.insert(ret,{cidx,caddr})
cidx,caddr=codesg.uint8_t:find(needle,cidx+1)
end
return ret
end
function FriendshipRainbow:find_one(codesg,needle,crace) function FriendshipRainbow:find_one(codesg,needle,crace)
dfu.concatTables(needle,dfu.dwordToTable(crace)) dfu.concatTables(needle,dfu.dwordToTable(crace))
return codesg.uint8_t:findall(needle) return self:findall_needles(codesg,needle)
end end
function FriendshipRainbow:find_all() function FriendshipRainbow:find_all()
local code=ms.get_code_segment() local code=ms.get_code_segment()

@ -843,7 +843,7 @@ sub render_item_number {
} elsif ($subtype eq 'uint8_t') { } elsif ($subtype eq 'uint8_t') {
push @lines_rb, 'number 8, false'; push @lines_rb, 'number 8, false';
} elsif ($subtype eq 'int8_t') { } elsif ($subtype eq 'int8_t') {
push @lines_rb, 'number 8, false'; push @lines_rb, 'number 8, true';
} elsif ($subtype eq 'bool') { } elsif ($subtype eq 'bool') {
push @lines_rb, 'number 8, true'; push @lines_rb, 'number 8, true';
$initvalue ||= 'nil'; $initvalue ||= 'nil';

@ -251,9 +251,15 @@ struct stable_cursor_hook : df::viewscreen_dwarfmodest
// Force update of ui state // Force update of ui state
set<df::interface_key> tmp; set<df::interface_key> tmp;
if (last_cursor.z < 2)
tmp.insert(interface_key::CURSOR_UP_Z);
else
tmp.insert(interface_key::CURSOR_DOWN_Z); tmp.insert(interface_key::CURSOR_DOWN_Z);
INTERPOSE_NEXT(feed)(&tmp); INTERPOSE_NEXT(feed)(&tmp);
tmp.clear(); tmp.clear();
if (last_cursor.z < 2)
tmp.insert(interface_key::CURSOR_DOWN_Z);
else
tmp.insert(interface_key::CURSOR_UP_Z); tmp.insert(interface_key::CURSOR_UP_Z);
INTERPOSE_NEXT(feed)(&tmp); INTERPOSE_NEXT(feed)(&tmp);
} }
@ -809,14 +815,18 @@ struct military_training_ct_hook : df::activity_event_combat_trainingst {
spar++; spar++;
} }
#if 0
color_ostream_proxy out(Core::getInstance().getConsole()); color_ostream_proxy out(Core::getInstance().getConsole());
#endif
// If the xp gap is low, sometimes replace with sparring // If the xp gap is low, sometimes replace with sparring
if ((maxv - minv) < 64*15 && spar == units.size() && if ((maxv - minv) < 64*15 && spar == units.size() &&
random_int(45) >= 30 + (maxv-minv)/64) random_int(45) >= 30 + (maxv-minv)/64)
{ {
#if 0
out.print("Replacing %s demonstration (xp %d-%d, gap %d) with sparring.\n", out.print("Replacing %s demonstration (xp %d-%d, gap %d) with sparring.\n",
ENUM_KEY_STR(job_skill, sd->skill).c_str(), minv, maxv, maxv-minv); ENUM_KEY_STR(job_skill, sd->skill).c_str(), minv, maxv, maxv-minv);
#endif
if (auto spar = df::allocate<df::activity_event_sparringst>()) if (auto spar = df::allocate<df::activity_event_sparringst>())
{ {
@ -838,18 +848,22 @@ struct military_training_ct_hook : df::activity_event_combat_trainingst {
// If the teacher has less xp than somebody else, switch // If the teacher has less xp than somebody else, switch
if (best >= 0 && maxv > cur_xp) if (best >= 0 && maxv > cur_xp)
{ {
#if 0
out.print("Replacing %s teacher %d (%d xp) with %d (%d xp); xp gap %d.\n", out.print("Replacing %s teacher %d (%d xp) with %d (%d xp); xp gap %d.\n",
ENUM_KEY_STR(job_skill, sd->skill).c_str(), ENUM_KEY_STR(job_skill, sd->skill).c_str(),
sd->unit_id, cur_xp, units[best], maxv, maxv-minv); sd->unit_id, cur_xp, units[best], maxv, maxv-minv);
#endif
sd->hist_figure_id = sd->participants.histfigs[best]; sd->hist_figure_id = sd->participants.histfigs[best];
sd->unit_id = units[best]; sd->unit_id = units[best];
} }
else else
{ {
#if 0
out.print("Not changing %s demonstration (xp %d-%d, gap %d).\n", out.print("Not changing %s demonstration (xp %d-%d, gap %d).\n",
ENUM_KEY_STR(job_skill, sd->skill).c_str(), ENUM_KEY_STR(job_skill, sd->skill).c_str(),
minv, maxv, maxv-minv); minv, maxv, maxv-minv);
#endif
} }
} }
} }

@ -43,7 +43,7 @@ for k,v in ipairs({...}) do --setting parsing
end end
mode=mode or 0 mode=mode or 0
last_building=last_building or {}
function Disclaimer(tlb) function Disclaimer(tlb)
local dsc={"The Gathering Against ",{text="Goblin ",pen=dfhack.pen.parse{fg=COLOR_GREEN,bg=0}}, "Oppresion ", local dsc={"The Gathering Against ",{text="Goblin ",pen=dfhack.pen.parse{fg=COLOR_GREEN,bg=0}}, "Oppresion ",
@ -73,6 +73,31 @@ function showHelp()
Disclaimer(helptext) Disclaimer(helptext)
dialog.showMessage("Help!?!",helptext) dialog.showMessage("Help!?!",helptext)
end end
--[[ Util functions ]]--
function advGlobalPos()
local map=df.global.world.map
local wd=df.global.world.world_data
local adv=df.global.world.units.active[0]
--wd.adv_region_x*16+wd.adv_emb_x,wd.adv_region_y*16+wd.adv_emb_y
--return wd.adv_region_x*16+wd.adv_emb_x,wd.adv_region_y*16+wd.adv_emb_y
--return wd.adv_region_x*16+wd.adv_emb_x+adv.pos.x/16,wd.adv_region_y*16+wd.adv_emb_y+adv.pos.y/16
--print(map.region_x,map.region_y,adv.pos.x,adv.pos.y)
--print(map.region_x+adv.pos.x/48, map.region_y+adv.pos.y/48,wd.adv_region_x*16+wd.adv_emb_x,wd.adv_region_y*16+wd.adv_emb_y)
return math.floor(map.region_x+adv.pos.x/48), math.floor(map.region_y+adv.pos.y/48)
end
function inSite()
local tx,ty=advGlobalPos()
--print(tx,ty)
for k,v in pairs(df.global.world.world_data.sites) do
local tp={v.pos.x,v.pos.y}
if tx>=tp[1]*16+v.rgn_min_x and tx<=tp[1]*16+v.rgn_max_x and
ty>=tp[2]*16+v.rgn_min_y and ty<=tp[2]*16+v.rgn_max_y then
--print(k)
return v
end
end
end
--[[ low level job management ]]-- --[[ low level job management ]]--
function getLastJobLink() function getLastJobLink()
local st=df.global.world.job_list local st=df.global.world.job_list
@ -343,7 +368,12 @@ function BuildingChosen(inp_args,type_id,subtype_id,custom_id)
if inp_args then if inp_args then
args.pos=inp_args.pos or args.pos args.pos=inp_args.pos or args.pos
end end
last_building.type=args.type
last_building.subtype=args.subtype
last_building.custom=args.custom
if chooseBuildingWidthHeightDir(args) then if chooseBuildingWidthHeightDir(args) then
return return
end end
--if settings.build_by_items then --if settings.build_by_items then
@ -458,8 +488,10 @@ function getItemsUncollected(job)
end end
return ret return ret
end end
function AddItem(tbl,item,recurse) function AddItem(tbl,item,recurse,skip_add)
if not skip_add then
table.insert(tbl,item) table.insert(tbl,item)
end
if recurse then if recurse then
local subitems=dfhack.items.getContainedItems(item) local subitems=dfhack.items.getContainedItems(item)
if subitems~=nil then if subitems~=nil then
@ -488,6 +520,8 @@ function EnumItems(args)
for k,v in pairs(args.unit.inventory) do for k,v in pairs(args.unit.inventory) do
if args.inv[v.mode] then if args.inv[v.mode] then
AddItem(ret,v.item,args.deep) AddItem(ret,v.item,args.deep)
elseif args.deep then
AddItem(ret,v.item,args.deep,true)
end end
end end
end end
@ -711,10 +745,7 @@ function AssignJobItems(args)
return true return true
--]=] --]=]
end end
function AssignJobToBuild(args) function CheckAndFinishBuilding(args,bld)
local bld=dfhack.buildings.findAtTile(args.pos)
args.job_type=df.job_type.ConstructBuilding
if bld~=nil then
for idx,job in pairs(bld.jobs) do for idx,job in pairs(bld.jobs) do
if job.job_type==df.job_type.ConstructBuilding then if job.job_type==df.job_type.ConstructBuilding then
args.job=job args.job=job
@ -735,11 +766,30 @@ function AssignJobToBuild(args)
local ok,msg=makeJob(args) local ok,msg=makeJob(args)
return ok,msg return ok,msg
end end
end
function AssignJobToBuild(args)
local bld=dfhack.buildings.findAtTile(args.pos)
args.job_type=df.job_type.ConstructBuilding
if bld~=nil then
CheckAndFinishBuilding(args,bld)
else else
bdialog.BuildingDialog{on_select=dfhack.curry(BuildingChosen,args),hide_none=true}:show() bdialog.BuildingDialog{on_select=dfhack.curry(BuildingChosen,args),hide_none=true}:show()
end end
return true return true
end end
function BuildLast(args)
local bld=dfhack.buildings.findAtTile(args.pos)
args.job_type=df.job_type.ConstructBuilding
if bld~=nil then
CheckAndFinishBuilding(args,bld)
else
--bdialog.BuildingDialog{on_select=dfhack.curry(BuildingChosen,args),hide_none=true}:show()
if last_building and last_building.type then
BuildingChosen(args,last_building.type,last_building.subtype,last_building.custom)
end
end
return true
end
function CancelJob(unit) function CancelJob(unit)
local c_job=unit.job.current_job local c_job=unit.job.current_job
if c_job then if c_job then
@ -789,6 +839,7 @@ actions={
{"RemoveStairs" ,df.job_type.RemoveStairs,{IsStairs,NotConstruct}}, {"RemoveStairs" ,df.job_type.RemoveStairs,{IsStairs,NotConstruct}},
--{"HandleLargeCreature" ,df.job_type.HandleLargeCreature,{isUnit},{SetCreatureRef}}, --{"HandleLargeCreature" ,df.job_type.HandleLargeCreature,{isUnit},{SetCreatureRef}},
{"Build" ,AssignJobToBuild,{NoConstructedBuilding}}, {"Build" ,AssignJobToBuild,{NoConstructedBuilding}},
{"BuildLast" ,BuildLast,{NoConstructedBuilding}},
{"Clean" ,df.job_type.Clean,{}}, {"Clean" ,df.job_type.Clean,{}},
} }
@ -827,6 +878,15 @@ function usetool:init(args)
visible=false, visible=false,
text={ text={
{id="text1",gap=1,key=keybinds.workshop.key,key_sep="()", text="Workshop menu",pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}}} {id="text1",gap=1,key=keybinds.workshop.key,key_sep="()", text="Workshop menu",pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}}}
},
wid.Label{
view_id="siteLabel",
frame = {t=1,xalign=-1,yalign=0},
visible=false,
text={
{id="text1", text="Site:"},{id="site", text="name"}
}
} }
} }
end end
@ -948,7 +1008,7 @@ function usetool:openPutWindow(building)
local adv=df.global.world.units.active[0] local adv=df.global.world.units.active[0]
local items=EnumItems{pos=adv.pos,unit=adv, local items=EnumItems{pos=adv.pos,unit=adv,
inv={[df.unit_inventory_item.T_mode.Hauled]=true,[df.unit_inventory_item.T_mode.Worn]=true, inv={[df.unit_inventory_item.T_mode.Hauled]=true,--[df.unit_inventory_item.T_mode.Worn]=true,
[df.unit_inventory_item.T_mode.Weapon]=true,},deep=true} [df.unit_inventory_item.T_mode.Weapon]=true,},deep=true}
local choices={} local choices={}
for k,v in pairs(items) do for k,v in pairs(items) do
@ -1040,7 +1100,12 @@ function usetool:armCleanTrap(building)
LoadStoneTrap, LoadStoneTrap,
LoadWeaponTrap, LoadWeaponTrap,
]] ]]
if building.trap_type==df.trap_type.Lever then
--link
return
end
--building.trap_type==df.trap_type.PressurePlate then
--settings/link
local args={unit=adv,post_actions={AssignBuildingRef,AssignJobItems},pos=adv.pos,from_pos=adv.pos,job_type=df.job_type.CleanTrap} local args={unit=adv,post_actions={AssignBuildingRef,AssignJobItems},pos=adv.pos,from_pos=adv.pos,job_type=df.job_type.CleanTrap}
if building.trap_type==df.trap_type.CageTrap then if building.trap_type==df.trap_type.CageTrap then
args.job_type=df.job_type.LoadCageTrap args.job_type=df.job_type.LoadCageTrap
@ -1157,7 +1222,7 @@ MODES={
input=usetool.operatePump, input=usetool.operatePump,
}, },
[df.building_type.Trap]={ [df.building_type.Trap]={
name="Arm/Clean Trap", name="Interact",
input=usetool.armCleanTrap, input=usetool.armCleanTrap,
}, },
[df.building_type.Hive]={ [df.building_type.Hive]={
@ -1178,20 +1243,7 @@ function usetool:shopInput(keys)
self:openShopWindowButtoned(self.in_shop) self:openShopWindowButtoned(self.in_shop)
end end
end end
function advGlobalPos()
local wd=df.global.world.world_data
return wd.adv_region_x*16+wd.adv_emb_x,wd.adv_region_y*16+wd.adv_emb_y
end
function inSite()
local tx,ty=advGlobalPos()
for k,v in pairs(df.global.world.world_data.sites) do
local tp={v.pos.x,v.pos.y}
if tx>=tp[1]*16+v.rgn_min_x and tx<=tp[1]*16+v.rgn_max_x and
ty>=tp[2]*16+v.rgn_min_y and ty<=tp[2]*16+v.rgn_max_y then
return v
end
end
end
function usetool:setupFields() function usetool:setupFields()
local adv=df.global.world.units.active[0] local adv=df.global.world.units.active[0]
local civ_id=df.global.world.units.active[0].civ_id local civ_id=df.global.world.units.active[0].civ_id
@ -1271,6 +1323,7 @@ function usetool:fieldInput(keys)
end end
function usetool:onInput(keys) function usetool:onInput(keys)
local adv=df.global.world.units.active[0] local adv=df.global.world.units.active[0]
if keys.LEAVESCREEN then if keys.LEAVESCREEN then
if df.global.cursor.x~=-30000 then if df.global.cursor.x~=-30000 then
self:sendInputToParent("LEAVESCREEN") self:sendInputToParent("LEAVESCREEN")
@ -1298,6 +1351,14 @@ function usetool:onInput(keys)
self:fieldInput(keys) self:fieldInput(keys)
end end
end end
local site=inSite()
if site then
self.subviews.siteLabel.visible=true
self.subviews.siteLabel:itemById("site").text=dfhack.TranslateName(site.name)
else
self.subviews.siteLabel.visible=false
end
end end
function usetool:isOnBuilding() function usetool:isOnBuilding()
local adv=df.global.world.units.active[0] local adv=df.global.world.units.active[0]