Merge branch 'master' of git://github.com/quietust/dfhack

develop
Warmist 2013-03-05 00:29:22 +02:00
commit bb85d361eb
10 changed files with 798 additions and 292 deletions

@ -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="#burrows" id="id53">burrows</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>
</li>
<li><a class="reference internal" href="#scripts" id="id55">Scripts</a><ul>
<li><a class="reference internal" href="#save-init-script" id="id56">Save init script</a></li>
</ul>
</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>
</li>
</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
calls lua code to perform the actual ordering of list items.</p>
</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 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/*
are automatically used by the DFHack core as commands. The
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>
<p>Note that this function lets errors propagate to the caller.</p>
<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
automatically loaded and executed every time the save is loaded. It
can also define the following functions to be called by dfhack:</p>

@ -2967,51 +2967,85 @@ on DF world 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.
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
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, usefull for custom viewscreens for shops
8. postWorkshopFillSidebarMenu(workshop) - is called after calling (or not) native fillSidebarMenu(). Usefull for job button tweaking (e.g. adding custom reactions)
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.
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.
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
---------
1. registerReaction(reaction_name,callback) - 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
1. ``registerReaction(reaction_name,callback)``
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
--------
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)
local flw=dfhack.maps.spawnFlow(unit.pos,6,0,0,50000)
end
Reaction complete example:
::
Reaction complete example::
b=require "plugins.eventful"
b.onReactionComplete.one=function(reaction,unit,in_items,in_reag,out_items,call_native)
local pos=copyall(unit.pos)
dfhack.timeout(100,"ticks",function() dfhack.maps.spawnFlow(pos,6,0,0,50000) end) -- spawn dragonbreath after 100 ticks
call_native.value=false --do not call real item creation code
-- spawn dragonbreath after 100 ticks
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
Granade example:
::
Grenade example::
b=require "plugins.eventful"
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
Integrated tannery:
::
Integrated tannery::
b=require "plugins.eventful"
b.addReactionToShop("TAN_A_HIDE","LEATHERWORKS")

@ -4,7 +4,8 @@ DFHack future
- support for displaying active keybindings properly.
- support for reusable widgets in lua screen library.
- 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:
- autobutcher can be re-enabled again after being stopped.
- stopped Dwarf Manipulator from unmasking vampires.
@ -28,6 +29,7 @@ DFHack future
- stripcaged: mark items inside cages for dumping, eg caged goblin weapons.
- soundsense-season: writes the correct season to gamelog.txt on world load.
- create-items: spawn items
- fix/cloth-stockpile: fixes bug 5739; needs to be run after savegame load every time.
New GUI scripts:
- gui/guide-path: displays the cached path for minecart Guide orders.
- gui/workshop-job: displays inputs of a workshop job and allows tweaking them.

File diff suppressed because it is too large Load Diff

@ -449,6 +449,26 @@ Options:
: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)
------------------
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
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/*
=====
@ -2032,6 +2058,17 @@ Exemples::
create-items bar CREATURE:CAT:SOAP
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
=======================
@ -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
While active, use the suggested keys to switch the usual liquids parameters, and Enter
to select the target area and apply changes.
This script is a gui front-end to the liquids plugin and works similar to it,
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

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

@ -136,6 +136,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(autoSyndrome autoSyndrome.cpp)
DFHACK_PLUGIN(trueTransformation trueTransformation.cpp)
DFHACK_PLUGIN(infiniteSky infiniteSky.cpp)
DFHACK_PLUGIN(createitem createitem.cpp)
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;
}

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

@ -251,10 +251,16 @@ struct stable_cursor_hook : df::viewscreen_dwarfmodest
// Force update of ui state
set<df::interface_key> tmp;
tmp.insert(interface_key::CURSOR_DOWN_Z);
if (last_cursor.z < 2)
tmp.insert(interface_key::CURSOR_UP_Z);
else
tmp.insert(interface_key::CURSOR_DOWN_Z);
INTERPOSE_NEXT(feed)(&tmp);
tmp.clear();
tmp.insert(interface_key::CURSOR_UP_Z);
if (last_cursor.z < 2)
tmp.insert(interface_key::CURSOR_DOWN_Z);
else
tmp.insert(interface_key::CURSOR_UP_Z);
INTERPOSE_NEXT(feed)(&tmp);
}
else if (!is_default && cur_cursor.isValid())
@ -809,14 +815,18 @@ struct military_training_ct_hook : df::activity_event_combat_trainingst {
spar++;
}
#if 0
color_ostream_proxy out(Core::getInstance().getConsole());
#endif
// If the xp gap is low, sometimes replace with sparring
if ((maxv - minv) < 64*15 && spar == units.size() &&
random_int(45) >= 30 + (maxv-minv)/64)
{
#if 0
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);
#endif
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 (best >= 0 && maxv > cur_xp)
{
#if 0
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(),
sd->unit_id, cur_xp, units[best], maxv, maxv-minv);
#endif
sd->hist_figure_id = sd->participants.histfigs[best];
sd->unit_id = units[best];
}
else
{
#if 0
out.print("Not changing %s demonstration (xp %d-%d, gap %d).\n",
ENUM_KEY_STR(job_skill, sd->skill).c_str(),
minv, maxv, maxv-minv);
#endif
}
}
}