Merge branch 'develop' of https://github.com/DFHack/dfhack into develop

develop
Japa 2015-06-11 21:51:03 +05:30
commit eef9f49ae8
18 changed files with 863 additions and 68 deletions

@ -77,6 +77,8 @@ scamtank scamtank
Mason11987 Mason11987
James Logsdon jlogsdon
melkor217 melkor217
acwatkins acwatkins
Wes Malone wesQ3
======================= ==================== ===========================
And these are the cool people who made **Stonesense**.

10
NEWS

@ -7,22 +7,28 @@ DFHack Future
Scripts can be enabled with the built-in enable/disable commands
A new function, reqscript(), is available as a safer alternative to script_environment()
New internal commands
kill-lua: Interrupt running Lua scripts
New plugins
New scripts
burial: sets all unowned coffins to allow burial ("-pets" to allow pets too)
fix-ster: changes fertility/sterility of animals or dwarves
view-item-info: adds information and customisable descriptions to item viewscreens
item-descriptions: holds a default description for every item type and subtype
warn-starving: check for starving, thirsty, or very drowsy units and pause with warning if any are found
New tweaks
New features
dwarfmonitor date format can be modified (see Readme)
Fixes
Plugins with vmethod hooks can now be reloaded on OS X
Lua's os.system() now works on OS X
Fixed default arguments in Lua gametype detection functions
gui/hack-wish now properly assigns quality to items.
gui/gm-editor handles lua tables properly
steam-engine: fixed a crash on arena load
tweak fps-min fixed
workflow: Fixed some issues with stuck jobs
- Note: Existing stuck jobs must be cancelled and re-added
Misc Improvements
dwarfmonitor date format can be modified (see Readme)
dwarfmonitor weather display can be configured separately
"keybinding list" accepts a context
nyan: Can now be stopped with dfhack-run
quicksave: Restricted to fortress mode

@ -1692,7 +1692,7 @@ Records dwarf activity to measure fort efficiency.
Options:
``dwarfmonitor enable <mode>``:
Start monitoring ``mode``. ``mode`` can be "work", "misery", or "all".
Start monitoring ``mode``. ``mode`` can be "work", "misery", "weather", or "all".
``dwarfmonitor disable <mode>``:
Stop monitoring ``mode`` (see above)
``dwarfmonitor stats``:
@ -2095,6 +2095,12 @@ Tools:
* ``sand``: Displays an indicator when sand is present in the currently-selected area, similar to the default clay/stone indicators.
* ``sticky``: Maintains the selected local area while navigating the world map
kill-lua
--------
Interrupts any currently-running Lua scripts. By default, scripts can only be
interrupted every 256 instructions. Use ``kill-lua force`` to interrupt
the next instruction.
petcapRemover
-------------
This plugin allows you to remove or raise the pet population cap. In vanilla
@ -2629,6 +2635,14 @@ of items. Individual descriptions can be added or overridden by a similar
script ``raw/scripts/more-item-descriptions.lua``. Both work as sparse lists,
so missing items simply go undescribed if not defined in the fallback.
warn-starving
=============
If any (live) units are starving, very thirsty, or very drowsy, the game will
be paused and a warning shown and logged to the console. Use with the
``repeat`` command for regular checks.
Use ``warn-starving all`` to display a list of all problematic units.
========
modtools
========

@ -753,6 +753,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
" cls - Clear the console.\n"
" fpause - Force DF to pause.\n"
" die - Force DF to close immediately\n"
" kill-lua - Stop an active Lua script\n"
" keybinding - Modify bindings of commands to keys\n"
" script FILENAME - Run the commands specified in a file.\n"
" sc-script - Automatically run specified scripts on state change events\n"
@ -870,6 +871,17 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
{
_exit(666);
}
else if(first == "kill-lua")
{
bool force = false;
for (auto it = parts.begin(); it != parts.end(); ++it)
{
if (*it == "force")
force = true;
}
if (!Lua::Interrupt(force))
con.printerr("Failed to register hook - use 'kill-lua force' to force\n");
}
else if(first == "script")
{
if(parts.size() == 1)

@ -24,6 +24,7 @@ distribution.
#include "Internal.h"
#include <csignal>
#include <string>
#include <vector>
#include <map>
@ -354,6 +355,35 @@ static int dfhack_lineedit(lua_State *S)
* Exception handling
*/
volatile std::sig_atomic_t lstop = 0;
static void interrupt_hook (lua_State *L, lua_Debug *ar);
static void interrupt_init (lua_State *L)
{
lua_sethook(L, interrupt_hook, LUA_MASKCOUNT, 256);
}
static void interrupt_hook (lua_State *L, lua_Debug *ar)
{
if (lstop)
{
lstop = 0;
interrupt_init(L); // Restore default settings if necessary
luaL_error(L, "interrupted!");
}
}
bool DFHack::Lua::Interrupt (bool force)
{
lua_State *L = Lua::Core::State;
if (L->hook != interrupt_hook && !force)
return false;
if (force)
lua_sethook(L, interrupt_hook, LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT, 1);
lstop = 1;
return true;
}
static int DFHACK_EXCEPTION_META_TOKEN = 0;
static void error_tostring(lua_State *L, bool keep_old = false)
@ -1561,6 +1591,8 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
if (!state)
state = luaL_newstate();
interrupt_init(state);
luaL_openlibs(state);
AttachDFGlobals(state);

@ -230,7 +230,6 @@ bool Plugin::load(color_ostream &con)
plugin_check_symbol("plugin_self")
plugin_check_symbol("plugin_init")
plugin_check_symbol("plugin_globals")
plugin_check_symbol("plugin_dev")
const char ** plug_name =(const char ** ) LookupPlugin(plug, "name");
const char ** plug_version =(const char ** ) LookupPlugin(plug, "version");
Plugin **plug_self = (Plugin**)LookupPlugin(plug, "plugin_self");
@ -243,7 +242,7 @@ bool Plugin::load(color_ostream &con)
return false;
}
bool *plug_dev = (bool*)LookupPlugin(plug, "plugin_dev");
if (*plug_dev && getenv("DFHACK_NO_DEV_PLUGINS"))
if (plug_dev && *plug_dev && getenv("DFHACK_NO_DEV_PLUGINS"))
{
con.print("Skipping dev plugin: %s\n", *plug_name);
plugin_abort_load;

@ -263,6 +263,17 @@ namespace DFHack {namespace Lua {
bool (*init)(color_ostream&, lua_State*, void*),
void *arg);
/**
* Attempt to interrupt the currently-executing lua function by raising a lua error
* from a lua debug hook, similar to how SIGINT is handled in the lua interpreter (depends/lua/src/lua.c).
* The flag set here will only be checked every 256 instructions by default.
* Returns false if another debug hook is set and 'force' is false.
*
* force: Overwrite any existing debug hooks and interrupt the next instruction
*/
DFHACK_EXPORT bool Interrupt (bool force=false);
/**
* Push utility functions
*/

@ -334,6 +334,7 @@ namespace DFHack
static int do_notify(lua_State *L);
static int do_input(lua_State *L);
bool allow_options;
public:
dfhack_lua_viewscreen(lua_State *L, int table_idx);
virtual ~dfhack_lua_viewscreen();
@ -348,6 +349,7 @@ namespace DFHack
virtual void help();
virtual void resize(int w, int h);
virtual void feed(std::set<df::interface_key> *keys);
virtual bool key_conflict(df::interface_key key);
virtual void onShow();
virtual void onDismiss();

@ -392,6 +392,9 @@ function Script:init(path)
self.mtime = dfhack.filesystem.mtime(path)
self._flags = {}
end
function Script:needs_update()
return (not self.env) or self.mtime ~= dfhack.filesystem.mtime(self.path)
end
function Script:get_flags()
if self.flags_mtime ~= dfhack.filesystem.mtime(self.path) then
self.flags_mtime = dfhack.filesystem.mtime(self.path)
@ -463,14 +466,24 @@ function dfhack.enable_script(name, state)
end
function dfhack.reqscript(name)
_, env = dfhack.run_script_with_env(nil, name, {module=true})
return env
return dfhack.script_environment(name, true)
end
reqscript = dfhack.reqscript
function dfhack.script_environment(name)
_, env = dfhack.run_script_with_env(nil, name, {module=true, module_strict=false})
function dfhack.script_environment(name, strict)
local path = dfhack.findScript(name)
if not scripts[path] or scripts[path]:needs_update() then
local _, env = dfhack.run_script_with_env(nil, name, {
module=true,
module_strict=strict and true or false -- ensure that this key is present if 'strict' is nil
})
return env
else
if strict and not scripts[path]:get_flags().module then
error(('%s: %s'):format(name, valid_script_flags.module.error))
end
return scripts[path].env
end
end
function dfhack.run_script_with_env(envVars, name, flags, ...)

@ -1336,6 +1336,7 @@ int Items::getValue(df::item *item)
int32_t Items::createItem(df::item_type item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_index, df::unit* unit) {
//based on Quietust's plugins/createitem.cpp
CHECK_NULL_POINTER(unit);
df::map_block* block = Maps::getTileBlock(unit->pos.x, unit->pos.y, unit->pos.z);
CHECK_NULL_POINTER(block);
df::reaction_product_itemst* prod = df::allocate<df::reaction_product_itemst>();

@ -659,6 +659,9 @@ void dfhack_lua_viewscreen::update_focus(lua_State *L, int idx)
lua_getfield(L, idx, "text_input_mode");
text_input_mode = lua_toboolean(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "allow_options");
allow_options = lua_toboolean(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "focus_path");
auto str = lua_tostring(L, -1);
@ -824,6 +827,13 @@ void dfhack_lua_viewscreen::logic()
safe_call_lua(do_notify, 1, 0);
}
bool dfhack_lua_viewscreen::key_conflict(df::interface_key key)
{
if (key == df::interface_key::OPTIONS)
return !allow_options;
return dfhack_viewscreen::key_conflict(key);
}
void dfhack_lua_viewscreen::help()
{
if (Screen::isDismissed(this)) return;

@ -77,6 +77,7 @@ static dwarfmonitor_configst dwarfmonitor_config;
static bool monitor_jobs = false;
static bool monitor_misery = true;
static bool monitor_date = true;
static bool monitor_weather = true;
static map<df::unit *, deque<activity_type>> work_history;
static int misery[] = { 0, 0, 0, 0, 0, 0, 0 };
@ -1753,10 +1754,12 @@ struct dwarf_monitor_hook : public df::viewscreen_dwarfmodest
OutputString(COLOR_GREY, x, y, date_str.str());
x = 1;
y = gps->dimy - 1;
bool clear = false,
rain = false,
}
if (monitor_weather)
{
int x = 1;
int y = gps->dimy - 1;
bool rain = false,
snow = false;
if (current_weather)
{
@ -1767,24 +1770,18 @@ struct dwarf_monitor_hook : public df::viewscreen_dwarfmodest
{
switch ((*current_weather)[i][j])
{
case weather_type::None:
clear = true;
break;
case weather_type::Rain:
rain = true;
break;
case weather_type::Snow:
snow = true;
break;
default:
break;
}
}
}
}
if (clear && (rain || snow))
{
OutputString(COLOR_YELLOW, x, y, "Clear");
++x;
}
if (rain)
{
OutputString(COLOR_LIGHTBLUE, x, y, "Rain");
@ -1824,6 +1821,11 @@ static bool set_monitoring_mode(const string &mode, const bool &state)
mode_recognized = true;
monitor_date = state;
}
else if (mode == "weather" || mode == "all")
{
mode_recognized = true;
monitor_weather = state;
}
return mode_recognized;
}
@ -1952,7 +1954,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector <Plugin
dwarfmonitor_cmd, false,
"dwarfmonitor enable <mode>\n"
" Start monitoring <mode>\n"
" <mode> can be \"work\", \"misery\", or \"all\"\n"
" <mode> can be \"work\", \"misery\", \"weather\", or \"all\"\n"
"dwarfmonitor disable <mode>\n"
" <mode> as above\n\n"
"dwarfmonitor stats\n"

@ -254,7 +254,7 @@ public:
if (!resume_time)
want_resumed = false;
else if (world->frame_counter >= resume_time)
actual_job->flags.bits.suspend = false;
set_resumed(true);
}
}
@ -262,7 +262,7 @@ public:
{
actual_job = job;
job->flags.bits.repeat = true;
job->flags.bits.suspend = true;
set_resumed(false);
resume_delay = std::min(DAY_TICKS*MONTH_DAYS, 5*resume_delay/3);
resume_time = world->frame_counter + resume_delay;
@ -272,16 +272,23 @@ public:
{
if (resume)
{
if (world->frame_counter >= resume_time)
if (world->frame_counter >= resume_time && actual_job->flags.bits.suspend)
{
actual_job->unk_v4020_1 = -1;
actual_job->flags.bits.suspend = false;
}
}
else
{
resume_time = 0;
if (isActuallyResumed())
resume_delay = DAY_TICKS;
if (!actual_job->flags.bits.suspend)
{
actual_job->flags.bits.suspend = true;
actual_job->unk_v4020_1 = -1;
}
}
want_resumed = resume;

@ -215,11 +215,11 @@ function GmEditorUi:editSelected(index,choice)
elseif trg_type == 'boolean' then
trg.target[trg_key] = not trg.target[trg_key]
self:updateTarget(true)
elseif trg_type=='userdata' then
elseif trg_type == 'userdata' or trg_type == 'table' then
self:pushTarget(trg.target[trg_key])
else
print("Unknow type:"..trg_type)
print("Subtype:"..tostring(trg.target[trg_key]._kind))
print("Unknown type:"..trg_type)
pcall(function() print("Subtype:"..tostring(trg.target[trg_key]._kind)) end)
end
end
end

@ -1,42 +1,69 @@
-- Holds custom descriptions for view-item-info
-- By PeridexisErrant
-- Each line near the bottom has 53 characters of room until
-- it starts clipping over the UI in an ugly fashion.
-- For proper spacing, 50 characters is the maximum.
-- Descriptions which aren't pushed down the page by
-- barrel contents or such line up with the UI on the
-- 11th line down. There is room for a 10th long line
-- without clipping, but stopping at 9 leaves enough space
-- for ideal legibility.
-- The following people contributed descriptions:
-- Raideau, PeridexisErrant, /u/Puffin4Tom, /u/KroyMortlach
-- /u/genieus, /u/TeamsOnlyMedic, /u/johny5w, /u/DerTanni
-- /u/schmee101, /u/coaldiamond, /u/stolencatkarma, /u/sylth01
-- /u/MperorM, /u/SockHoarder, /u/_enclave_, WesQ3
-- /u/Xen0nex, /u/Jurph
if not moduleMode then
print("scripts/item-descriptions.lua is a content library; calling it does nothing.")
end
--[[
local help --[[
This script has a single function: to return a custom description for every
vanilla item in the game.
Having this as a separate script to "view-item-info.lua" allows either mods
to partially or fully replace the content.
If "raw/scripts/item-descriptions.lua" exists, it will entirely replace this one.
Instead, use "raw/scripts/more-item-descriptions.lua" to add content, or replace
Instead, mods should use "raw/scripts/more-item-descriptions.lua" to add content or replace
descriptions on a case-by-case basis. If an item description cannot be found in
the latter script, view-item-info will fall back to the former.
Lines should be about 70 characters for consistent presentation and to fit
on-screen. Logical sections can be separated by an empty line ("").
Text blocks should use identical indentation to make formatting clearly visible.
All described vanilla item IDs:
"ANVIL", "ARMORSTAND", "BARREL", "BED", "BIN", "BLOCKS", "BOX", "BUCKET", "ITEM_AMMO_ARROWS", "ITEM_AMMO_BOLTS", "ITEM_ARMOR_LEATHER", "ITEM_WEAPON_PICK", "ITEM_WEAPON_AXE_BATTLE", "ITEM_WEAPON_AXE_TRAINING", "SLAB", "TRAPPARTS"
Remaining item IDs:
"AMULET", "ANIMALTRAP", "BACKPACK", "BALLISTAARROWHEAD", "BALLISTAPARTS", "BAR", "BOOK", "BOULDER", "BRACELET", "CABINET", "CAGE", "CATAPULTPARTS", "CHAIN", "CHAIR", "CLOTH", "COFFIN", "COIN", "CROWN", "CRUTCH", "DOOR", "EARRING", "FIGURINE", "FLASK", "FLOODGATE", "GOBLET", "GRATE", "HATCH_COVER", "ITEM_ARMOR_BREASTPLATE", "ITEM_ARMOR_CAPE", "ITEM_ARMOR_CLOAK", "ITEM_ARMOR_COAT", "ITEM_ARMOR_DRESS", "ITEM_ARMOR_MAIL_SHIRT", "ITEM_ARMOR_ROBE", "ITEM_ARMOR_SHIRT", "ITEM_ARMOR_TOGA", "ITEM_ARMOR_TUNIC", "ITEM_ARMOR_VEST", "ITEM_FOOD_BISCUITS", "ITEM_FOOD_ROAST", "ITEM_FOOD_STEW", "ITEM_GLOVES_GAUNTLETS", "ITEM_GLOVES_GLOVES", "ITEM_GLOVES_MITTENS", "ITEM_HELM_CAP", "ITEM_HELM_HELM", "ITEM_HELM_HOOD", "ITEM_HELM_MASK", "ITEM_HELM_SCARF_HEAD", "ITEM_HELM_TURBAN", "ITEM_HELM_VEIL_FACE", "ITEM_HELM_VEIL_HEAD", "ITEM_INSTRUMENT_DRUM", "ITEM_INSTRUMENT_FLUTE", "ITEM_INSTRUMENT_HARP", "ITEM_INSTRUMENT_PICCOLO", "ITEM_INSTRUMENT_TRUMPET", "ITEM_PANTS_BRAIES", "ITEM_PANTS_GREAVES", "ITEM_PANTS_LEGGINGS", "ITEM_PANTS_LOINCLOTH", "ITEM_PANTS_PANTS", "ITEM_PANTS_SKIRT", "ITEM_PANTS_SKIRT_LONG", "ITEM_PANTS_SKIRT_SHORT", "ITEM_PANTS_THONG", "ITEM_SHIELD_BUCKLER", "ITEM_SHIELD_SHIELD", "ITEM_SHOES_BOOTS", "ITEM_SHOES_BOOTS_LOW", "ITEM_SHOES_CHAUSSE", "ITEM_SHOES_SANDAL", "ITEM_SHOES_SHOES", "ITEM_SHOES_SOCKS", "ITEM_SIEGEAMMO_BALLISTA", "ITEM_TOOL_BOWL", "ITEM_TOOL_CAULDRON", "ITEM_TOOL_FORK_CARVING", "ITEM_TOOL_HIVE", "ITEM_TOOL_HONEYCOMB", "ITEM_TOOL_JUG", "ITEM_TOOL_KNIFE_BONING", "ITEM_TOOL_KNIFE_CARVING", "ITEM_TOOL_KNIFE_MEAT_CLEAVER", "ITEM_TOOL_KNIFE_SLICING", "ITEM_TOOL_LADLE", "ITEM_TOOL_LARGE_POT", "ITEM_TOOL_MINECART", "ITEM_TOOL_MORTAR", "ITEM_TOOL_NEST_BOX", "ITEM_TOOL_PESTLE", "ITEM_TOOL_POUCH", "ITEM_TOOL_SCALE_SHARD", "ITEM_TOOL_STEPLADDER", "ITEM_TOOL_WHEELBARROW", "ITEM_TOY_AXE", "ITEM_TOY_BOAT", "ITEM_TOY_HAMMER", "ITEM_TOY_MINIFORGE", "ITEM_TOY_PUZZLEBOX", "ITEM_TRAPCOMP_ENORMOUSCORKSCREW", "ITEM_TRAPCOMP_GIANTAXEBLADE", "ITEM_TRAPCOMP_LARGESERRATEDDISC", "ITEM_TRAPCOMP_MENACINGSPIKE", "ITEM_TRAPCOMP_SPIKEDBALL", "ITEM_WEAPON_AXE_GREAT", "ITEM_WEAPON_BLOWGUN", "ITEM_WEAPON_BOW", "ITEM_WEAPON_CROSSBOW", "ITEM_WEAPON_DAGGER_LARGE", "ITEM_WEAPON_FLAIL", "ITEM_WEAPON_HALBERD", "ITEM_WEAPON_HAMMER_WAR", "ITEM_WEAPON_MACE", "ITEM_WEAPON_MAUL", "ITEM_WEAPON_MORNINGSTAR", "ITEM_WEAPON_PIKE", "ITEM_WEAPON_SCIMITAR", "ITEM_WEAPON_SCOURGE", "ITEM_WEAPON_SPEAR", "ITEM_WEAPON_SPEAR_TRAINING", "ITEM_WEAPON_SWORD_2H", "ITEM_WEAPON_SWORD_LONG", "ITEM_WEAPON_SWORD_SHORT", "ITEM_WEAPON_SWORD_SHORT_TRAINING", "ITEM_WEAPON_WHIP", "MEAT", "MILLSTONE", "ORTHOPEDIC_CAST", "PIPE_SECTION", "QUERN", "QUIVER", "RING", "ROCK", "ROUGH", "SCEPTER", "SKIN_TANNED", "SMALLGEM", "SPLINT", "STATUE", "TABLE", "THREAD", "TOTEM", "TRACTION_BENCH", "WEAPONRACK", "WINDOW", "WOOD"
]]
-- see http://dwarffortresswiki.org/index.php/cv:Item_token
descriptions = {
AMULET = { "An item of jewellery worn around the neck for it's aesthetic value.",
"An amulet does not interfere with wearing other equipment."},
ANIMALTRAP = {
"This tiny trap is used by your trappers to catch vermin. Some dwarves",
"like vermin as pets - if your cats don't get them first. May be built",
"from wood or metal. Catching vermin requires trap to be set with bait."},
ANVIL = { "An essential component of the forge."},
ARMORSTAND = {
"A rack for the storage of military equipment, specifically armor.",
"It is required by some nobles, and can be used to create a barracks."},
"Barracks may be designated and assigned from them. Military squads",
"may use their assigned barracks for training, storage, or sleeping,",
"depending on the settings. Some nobles demand an armor stand of",
"their own."},
BACKPACK = {"A backpack can be used by militia to carry rations in the field.",
"In Adventure mode, backpacks can be used to carry more equipment."},
BALLISTAARROWHEAD = {
"The arrowtip used to create metal ballista arrows in a siege workshop."},
BALLISTAPARTS = {
"Three of these can be used to construct a Ballista."},
BAR = { "A small ingot made of metal, fuel, ash, or soap, made to facilitate",
"stacking and storage. Aside from the uses unique to each material, bars",
"of any type can also be used as building materials in place of blocks.",
"",
"Metal bars are used with fuel at a Metalsmith's Forge to make metal",
"goods and decorations. Fuel is used at furnaces and workshops requiring",
"intense heat, with the exception of magma furnaces or a Wood Furnace.",
"Soap is used by hospitals to greatly reduce the chance of infected",
"wounds, and rarely used by individual dwarves to clean themselves or",
"surrounding tiles. Ash is processed at an Ashery to make potash or lye,",
"potash is used as farm plot fertilizer or made into pearlash, and",
"pearlash is used to make clear or crystal glass products."},
BARREL = { "A hollow cylinder with a removable lid. It is used to hold liquids,",
"food, and seeds. It can be made from metal or wood, and is replaceable",
"with a rock pot. A barrel (or rock pot) is needed to brew drinks."},
@ -62,32 +89,559 @@ descriptions = {
"dwarves to give water to other dwarves, to store lye, and are",
"required to build wells and certain workshops. They can be made",
"from wood or metal."},
BOOK = { "It's a book. Some books contain the secrets of life and death."},
BOULDER = { "Mining may yield loose stones for industry. There are four categories:",
"non-economic stones for building materials, ores for metal industry,",
"gems, and special-purpose economic stones like flux, coal and lignite."},
BRACELET = {"A bracelet is an item of jewellery worn on the hands."},
CABINET = { "A place for dwarves to store old clothing, used by any dwarf whose room",
"overlaps the cabinet. May be built from wood, stone, metal, or glass."},
CAGE = { "A cage can be made of glass, metal or wood. All materials are equally",
"strong as cages can not be broken, however the weight of the material",
"affects the time taken to move cages. Cages can be combined with a",
"mechanism to create a cage trap. Cages can also be built as furniture,",
"after which they can store an infinite number of animals or prisoners."},
CATAPULTPARTS = {
"Three of these can be used to construct a Catapult."},
CHAIN = { "A chain made of metal. A chain or rope is required to build a well.",
"Due to the marvels of dwarven engineering, a single chain can be used",
"for a well of any depth. Chains are also used to create restraints",
"for prisoners or animals."},
CHAIR = { "Furniture used for sitting. Named a chair if made from wood,",
"or a throne if made from stone, glass, or metal. Offices may be",
"designated and assigned from them. Dwarves will complain if there",
"aren't enough chairs in the dining room."},
CLOTH = { "A piece of fabric made from threads of plant fiber, yarn, silk or",
"adamantine. Cloth may be dyed. It is used at a Clothier's Workshop to",
"make clothing, bags, rope, and decorative sewn images. At a",
"Craftsdwarf's Workshop, it can be made into trade goods. Hospitals",
"use cloth for wound dressing - though expensive cloth confers no",
"benefit. Specific types of cloth can be required by a strange mood."},
COFFIN = { "A final resting place for dwarves. Must be built and assigned before",
"burial can occur. Named a coffin when made from stone or glass,",
"casket when made from wood, and sarcophagus when made from metal.",
"Tombs may be designated and assigned from them. Corpses contained in",
"coffins may still reanimate."},
COIN = { "A metal coin, which represents value. Surprisingly useless in trade."},
CROWN = { "A crown may be worn as headgear, or on top of a helmet. Although",
"usually just decorative or symbolic, crowns sometimes deflect attacks."},
CRUTCH = { "Item used in health-care. May be made from wood or metal. Given to",
"dwarves who receive injuries that impair or prevent normal movement.",
"Requires one hand."},
DOOR = { "A barrier that covers a hole in a wall and controls horizontal passage.",
"Intelligent creatures can open and close doors as needed. Doors can",
"also block the flow of liquids as long as they remain closed. May be",
"set as 'locked' to prevent all passage or 'tightly closed' to prevent",
"animal passage. Creatures cannot manually open or close doors that are",
"mechanically controlled. May be linked via mechanisms to devices such",
"as pressure plates and levers. Will become stuck open if an item",
"occupies its tile."},
EARRING = { "Earrings are decorative jewellery. Eleven can be worn on each ear."},
FIGURINE = {"A small piece of art carved in the likeness of a creature."},
FLASK = { "A drink container that is worn on the body, keeping the hands free.",
"Soldiers and adventurers can carry any drink of their choice in this",
"container. Called a flask when made from metal, or a vial when",
"made from glass."},
FLOODGATE = {
"A mechanical gate used to control the flow of water. Floodgates are",
"initially closed when installed, and must be linked to a lever or",
"pressure plate in order to be either opened or closed. Furniture for",
"blocking horizontal passage. It is built as a solid tile and may be",
"linked via mechanism to devices such as pressure plates and levers.",
"When activated, the floodgate will disappear. Will become stuck open",
"if an item occupies its tile."},
GOBLET = { "A small drink container that is held in one hand."},
GRATE = { "A barrier with small openings, grates block solid objects and",
"creatures - but not line of sight, liquids, or projectiles. Grates can",
"be installed vertically on a floor, or horizontally over open space.",
"Grates can be retracted if they are mechanically linked to a lever or",
"pressure plate. Grates are not stable enough to support constructions."},
HATCH_COVER = {
"A barrier that covers a hole in the floor. A hatch cover acts like a",
"door, but placed over a vertical opening. Hatches can also cover carved",
"stairways and ramps. Hatches can be linked to mechanical controls.",
"Creatures cannot manually open or close hatches that are mechanically",
"controlled. They may also be set as 'locked' to prevent all passage or",
"'tightly closed' to prevent animal passage."},
ITEM_AMMO_ARROWS = {
"Ammunition for bows, which are primarily used by elves."},
ITEM_AMMO_BOLTS = {
"Ammunition for crossbows, which are a dwarf's preferred ranged weapon.",
"It is not recommended to store bolts in bins, due to pickup bugs."},
ITEM_ARMOR_BREASTPLATE = {
"A breastplate is a piece of plate armor that covers the upper body and",
"the lower body. It is usually worn in the armor layer."},
ITEM_ARMOR_CAPE = {
"A (cool-looking) cape. Protects the chest."},
ITEM_ARMOR_CLOAK = {
"A cloth cloak. Protects the face, neck, chest, arms and upper legs."},
ITEM_ARMOR_COAT = {
"A heavy cloth coat. Protects the face, neck, chest, arms and upper legs."},
ITEM_ARMOR_DRESS = {
"A cloth dress. Protects the face, neck, chest, arms and legs."},
ITEM_ARMOR_LEATHER = {
"Leather armor is light and covers both arms and legs",
"in addition to body"},
ITEM_WEAPON_PICK = {
"The most important item for a beginning fortress, a pick can",
"get a party underground. Also crucial mining for stone or",
"metals, expansion of living space, and so on.", "",
"A pick is also useful as a weapon, though putting miners in the",
"military causes equipment clashes."},
ITEM_ARMOR_MAIL_SHIRT = {
"A chainmail shirt. Protects the face, neck, chest,",
"upper arms and upper legs."},
ITEM_ARMOR_ROBE = {
"A cloth robe. Protects the face, neck, chest, arms and legs."},
ITEM_ARMOR_SHIRT = {
"A cloth shirt. Protects the neck, chest and arms."},
ITEM_ARMOR_TOGA = {
"A cloth toga. Protects the face, neck, chest, upper arms and upper legs."},
ITEM_ARMOR_TUNIC = {
"A cloth tunic. Protects the neck, chest and upper arms."},
ITEM_ARMOR_VEST = {
"A cloth vest. Protects the chest."},
ITEM_FOOD_BISCUITS = {
"Biscuits are the lowest tier of meals that can be prepared by your",
"dwarves. They are made in a kitchen with the 'Prepare Easy Meal' order",
"and use two ingredients. Preparing easy meals is the easiest way to,",
"get experience for you cooks, but the larger volume produced means more",
"hauling to take them to storage."},
ITEM_FOOD_ROAST = {
"Roasts are the highest tier of meals that can be prepared by your ",
"dwarves. They are made in a kitchen with the 'Prepare Lavish Meal'",
"order, and use four ingredients. As there are more ingredients, there",
"is a better chance that a dwarf will like at least one ingredient."},
ITEM_FOOD_STEW = {
"Stews are the middle tier of meals that can be prepared by your ",
"dwarves. They are made in a kitchen with the 'Prepare Fine Meal' order,",
"and use three ingredients. They provide more food than Biscuits,",
"but are less valuable than Roasts."},
ITEM_GLOVES_GAUNTLETS = {
"Gauntlets are armor worn on any body part that can grasp, which for",
"dwarves are the hands. They are similar to mittens and gloves, but",
"act as an armor layer and provide much more protection. Like other",
"armor, gauntlets can be made of metal, shell, or bone."},
ITEM_GLOVES_GLOVES = {
"Gloves cover the hands, wrapping each finger and thumb individually",
"to preserve the wearer's dexterity at the cost of some warmth"},
ITEM_GLOVES_MITTENS = {
"Mittens cover the fingers together and thumb separately, preserving",
"the ability to grasp but keeping fingers together for more warmth in",
"cold climates"},
ITEM_HELM_CAP = {
"A cap covers only the crown of the head. It prevents heat loss through",
"a bald pate and protects the skull from falling objects and",
"downward strikes."},
ITEM_HELM_HELM = {
"A helm covers the entire face and head. It protects the wearer from",
"falling objects and a variety of weapon strikes from all directions.",
"Every other type of head covering, save a hood, is worn under a helm",
"for padding."},
ITEM_HELM_HOOD = {
"A hood is a soft loose covering for the head and sides of the face.",
"It shields the wearer from cold breezes and can cushion blows from",
"any direction. It is pulled over the wearer's other headgear, providing",
"a final outer layer of protection from the elements."},
ITEM_HELM_MASK = {
"A mask hides the wearer's face from view and protects it from all but",
"the most accurate piercing attacks. Some can be carved to present the",
"enemy with a more fearsome visage, or to show no face at all.",
"Masks are worn underneath other layers of headgear."},
ITEM_HELM_SCARF_HEAD = {
"A head scarf is a loose wrap of cloth or leather that is typically",
"worn in hot climates to protect the head from the rays of the sun.",
"It provides light cushioning against some blows."},
ITEM_HELM_TURBAN = {
"A turban is a length of cloth or leather that is wrapped many times",
"around the head to shield the wearer from the sun's rays and provide",
"several layers of insulation. A turban can be pinned or clasped in",
"place, or simply folded and tucked into a stable configuration."},
ITEM_HELM_VEIL_FACE = {
"A face veil is a soft covering that protects the lower half of the",
"wearer's face, leaving only the eyes to gaze out. It can prevent",
"noxious fluids from splashing into the wearer's mouth.",
"It is worn under every other layer of headgear."},
ITEM_HELM_VEIL_HEAD = {
"A veil for the whole head is a wall of sheer cloth or finely-punched",
"leather extending from above the brow to below the chin, and often hung",
"from a more solid cloth headpiece that covers the crown and cheeks.",
"It admits some light but almost entirely obscures the wearer's face."},
ITEM_INSTRUMENT_DRUM = {
"Short, wide, and round, this cylindrical percussion instrument can",
"play music when banged by one's hands. It is only useful to trade."},
ITEM_INSTRUMENT_FLUTE = {
"This long cylindrical woodwind instrument can make a wide array of",
"tones and music when blown into. It is only useful to trade."},
ITEM_INSTRUMENT_HARP = {
"Vaguely triangular in shape, this stringed instrument can play a",
"variety of notes by plucking the strings with one's fingers.",
"It is only useful to trade."},
ITEM_INSTRUMENT_PICCOLO = {
"Similar to a flute, but smaller and with a higher tone, a piccolo is",
"a cylindrical woodwind instrument. It is only useful to trade."},
ITEM_INSTRUMENT_TRUMPET = {
"A dwarven brass instrument - which need not be made of brass.",
"It is only useful to trade."},
ITEM_PANTS_BRAIES = {
"Braies are undergarments that cover from the waist to the knees.",
"Dwarves cannot craft braies, so they must be obtained through",
"other means."},
ITEM_PANTS_GREAVES = {
"Greaves are plated armor meant to protect the lower legs, though",
"they are equipped as pants."},
ITEM_PANTS_LEGGINGS = {
"Leggings are garments that cover everything from the waist to the",
"ankles, though with a tighter fit than other trousers."},
ITEM_PANTS_LOINCLOTH = {
"Loincloths are draped undergarments meant to cover little more than",
"the 'geldables'. Dwarves cannot craft loincloths, so they must be",
"obtained through other means."},
ITEM_PANTS_PANTS = {
"Trousers are a garment that covers everything from the waist to the",
"ankles. They keep the legs and lower body warm."},
ITEM_PANTS_SKIRT = {
"A skirt is a cone-shaped garment that hangs from the waist, covering",
"part of the legs. Its use is more for modesty than protection.",
"Dwarves cannot craft skirts, so they must be obtained through other means."},
ITEM_PANTS_SKIRT_LONG = {
"A skirt is a cone-shaped garment that hangs from the waist, covering",
"part of the legs. Its use is more for modesty than protection. Long",
"skirts fulfil this purpose well. Dwarves cannot craft long skirts,",
"so they must be obtained through other means."},
ITEM_PANTS_SKIRT_SHORT = {
"A skirt is a cone-shaped garment that hangs from the waist, covering",
"part of the legs. Its use is more for modesty than protection, though",
"short skirts offer less in the way of modesty. Dwarves cannot craft",
"short skirts, so they must be obtained through other means."},
ITEM_PANTS_THONG = {
"Thongs are strapped undergarments meant to cover little more than",
"the 'geldables'. Dwarves cannot craft thongs, so they must be obtained",
"through other means."},
ITEM_SHIELD_BUCKLER = {
"A smaller and less protective type of shield. A buckler can be used",
"to block attacks, and with skill anything from a goblin axe",
"to Dragonfire can be deflected."},
ITEM_SHIELD_SHIELD = {
"Larger and more defensive than a buckler, a full-sized shield can be",
"used to block attacks. With skill anything from a goblin axe to",
"Dragonfire can be deflected."},
ITEM_SHOES_BOOTS = {
"Boots are more protective kind of shoe, covering from the foot up to",
"the knee."},
ITEM_SHOES_BOOTS_LOW = {
"Low boots are more protective kind of shoe, covering from the foot up",
"to just past the ankle."},
ITEM_SHOES_CHAUSSE = {
"Chausses are chainmail armor meant to protect the legs, though these",
"are equipped as footwear. Dwarves cannot craft chausses, so they",
"must be obtained through other means."},
ITEM_SHOES_SANDAL = {
"Sandals are open footwear consisting of soles and some number of",
"straps. Dwarves cannot craft sandals, so they must be obtained",
"through other means."},
ITEM_SHOES_SHOES = {
"Shoes are closed footwear meant to protect the feet from rough terrain",
"and the elements."},
ITEM_SHOES_SOCKS = {
"Socks are tubular articles of clothing, worn on each foot along with",
"shoes or other footwear."},
ITEM_SIEGEAMMO_BALLISTA = {
"Ballista ammunition, for an enormous siege weapon."},
ITEM_TOOL_BOWL = {
"Bowls are used to contain individual servings of meals.",
"At the moment, dwarves have no use for these."},
ITEM_TOOL_CAULDRON = {
"Cauldrons are large metal pots used to cook meals like soups or stews",
"over an open fire. At the moment, dwarves have no use for these."},
ITEM_TOOL_FORK_CARVING = {
"A carving fork typically has only two prongs and is exceptionally long.",
"It is used to hold down a piece of cooked meat while using a knife."},
ITEM_TOOL_HIVE = {
" Hives are structures that house colonies of honey bees. To be",
"productive, they need to be constructed on an above-ground tile with",
"an accessible honey bee colony somewhere on the map. Some time after",
"bees are 'installed' by a beekeeper, the hive will be ready to harvest",
"or split into new colonies."},
ITEM_TOOL_HONEYCOMB = {
"Honeycomb is an intermediate product of beekeeping, produced along",
"with royal jelly when a beekeeper harvests a suitable hive. It must",
"be processed by a Presser at a Screw Press to produce honey, which may",
"be used in cooking or made into mead and a wax cake, which can be used",
"to make low-value crafts."},
ITEM_TOOL_JUG = {
"Jugs are small food storage containers that hold royal jelly, honey,",
"or oil. They are used by beekeepers when harvesting suitable hives and",
"by pressers when processing honeycomb or seed pastes at a screw press."},
ITEM_TOOL_KNIFE_BONING = {
"A boning knife has a sharp point and narrow blade. It is an excellent",
"all-around kitchen knife and decent weapon in a pinch."},
ITEM_TOOL_KNIFE_CARVING = {
"A carving knife is for cutting thin slices of cooked meat for serving.",
"It may be useful as an improvised weapon."},
ITEM_TOOL_KNIFE_MEAT_CLEAVER = {
"A meat cleaver is a heavy square-bladed knife for cutting bone and",
"meat alike."},
ITEM_TOOL_KNIFE_SLICING = {
"A slicing knife is for cutting thin slices of cooked meat."},
ITEM_TOOL_LADLE = {
"A ladle is a large spoon with a long handle and a deep bowl,",
"intended for serving out portions of soups and stews."},
ITEM_TOOL_LARGE_POT = {
"Large pots are storage containers made of any hard material. They",
"function identically to barrels when brewing or storing food, while",
"being much lighter than barrels made of the same material.",
"Unfortunately, they cannot be used when making dwarven syrup or when",
"building asheries and dyer's workshops."},
ITEM_TOOL_MINECART = {
"A minecart is a tool for hauling, and can be made from wood or metal.",
"",
"Minecart systems are the most efficient and most complicated way to",
"move items, and can do anything from improving industrial efficiency,",
"to transporting magma or launching hundreds of weapons at enemies.",
"Misuse may result in horrific injury to drivers and pedestrians."},
ITEM_TOOL_MORTAR = {
"Half of a mortar and pestle, the mortar is a bowl in which to grind",
"up plants or other reagents."},
ITEM_TOOL_NEST_BOX = {
"A place for birds to lay eggs. Must be built before use.",
"Forbid eggs to hatch into chicks before a dwarf picks them up."},
ITEM_TOOL_PESTLE = {
"Half of a mortar and pestle, the pestle is a stick used to grind up",
"plants or other reagents."},
ITEM_TOOL_POUCH = {
"A small bag used to carry a variety of tools."},
ITEM_TOOL_STEPLADDER = {
"A small stepladder. If you have one of these, you can use zones to",
"assign your dwarves to pick fruit from certain trees."},
ITEM_TOOL_WHEELBARROW = {
"A small hand-cart with long handles and a single wheel, this",
"wheelbarrow makes heavy hauling jobs much more manageable."},
ITEM_TOY_AXE = {
"A small toy axe without an edge. Useless except as a trade good."},
ITEM_TOY_BOAT = {
"A tiny model of a boat. Only good for trade."},
ITEM_TOY_HAMMER = {
"A toy hammer. Its only use is to sell."},
ITEM_TOY_MINIFORGE = {
"A model of a blacksmith's forge that dwarf children love.",
"Only useful as a trade good."},
ITEM_TOY_PUZZLEBOX = {
"A perplexing toy that dwarves of all ages enjoy.",
"Its only use is as a trade good."},
ITEM_TRAPCOMP_ENORMOUSCORKSCREW = {
"A massive screw-like object. Can be used to make a pump,",
"or as a component in a trap."},
ITEM_TRAPCOMP_GIANTAXEBLADE = {
"This massive blade is typically made of metal and can be used in weapon",
"traps, swinging once to slice anyone unfortunate enough to activate it."},
ITEM_TRAPCOMP_LARGESERRATEDDISC = {
"Serrated discs are typically made of metal and can be used in weapon",
"traps, in which they eviscerate its victims with three powerful slicing",
"attacks. Such traps have a tendency to sever multiple body parts and",
"make a gigantic mess."},
ITEM_TRAPCOMP_MENACINGSPIKE = {
"Menacing spikes are made of wood or metal and can be used in weapon",
"traps or upright spike traps, in which they impale the victim. They",
"are especially effective against unarmored foes or, in an upright",
"spike trap, anyone falling from great heights."},
ITEM_TRAPCOMP_SPIKEDBALL = {
"This trap component menaces with spikes of wood or metal. It hits three",
"times with its spikes, but does not penetrate as deeply as a menacing",
"spike. Compared to other trap components, spiked balls are slightly",
"more effective against heavily armored foes. They also make for a",
"surprisingly valuable trade good, on par with serrated discs."},
ITEM_WEAPON_AXE_BATTLE = {
"A battle axe is an edged weapon: essentially a sharp blade",
"mounted along the end of a short and heavy handle.", "",
"mounted along the end of a short and heavy handle.",
"",
"Dwarves can forge battle axes out of any weapon-grade metal,",
"though those with superior edge properties are more effective.", "",
"though those with superior edge properties are more effective.",
"",
"A battle axe may also be used as a tool for chopping down trees."},
ITEM_WEAPON_AXE_GREAT = {
"This is an axe nearly twice as large as a battle axe. Its size",
"makes it unsuitable for a dwarf, but those who can wield it find",
"its increased size and weight contribute to its effectiveness."},
ITEM_WEAPON_AXE_TRAINING = {
"As a battleaxe made from wood, this practise weapon is useful for",
"training recruits. Thanks to good craftsdwarfship, it can also",
"be used to cut down trees."},
ITEM_WEAPON_BLOWGUN = {
"A very simple ranged weapon: blow into one end of the long narrow",
"tube, and project a pellet or dart into the body of one's prey.",
"If the prey approaches, this blowgun makes a useless melee weapon."},
ITEM_WEAPON_BOW = {
"Bows are the preferred ranged weapon for elves and goblins, and",
"shoot arrows as projectiles. As they are a foreign weapon, they",
"cannot be made in your fort. In melee, bowmen will use their bow as",
"a weapon, training the swordsman skill."},
ITEM_WEAPON_CROSSBOW = {
"The favoured ranged weapon of choice for any dwarf, crossbows can be",
"made of wood, bones or metal, and shoot bolts as projectiles. Hunters",
"or marks-dwarves that run out of ammunition will use their crossbow",
"as a melee weapon, training the hammerdwarf skill."},
ITEM_WEAPON_DAGGER_LARGE = {
"A large dagger is a edge weapon that is essentially just a bit smaller",
"than a short sword. It's used for stabbing rather than slashing. Large",
"daggers use and train the knife user skill, and are common weapons for",
"kobold and goblin thieves. As foreign weapons dwarves cannot forge",
"large daggers."},
ITEM_WEAPON_FLAIL = {
"A flail is a blunt weapon that consists of a rounded weight attached to",
"a handle by a length of chain. Flails are the same size as a morningstar,",
"but have a contact area twice as large as a much larger maul. As foreign",
"weapons dwarves cannot forge flails. Flails use and train the",
"macedwarf skill."},
ITEM_WEAPON_HALBERD = {
"A halberd is a foreign weapon, and cannot be made by your dwarves.",
"Even the largest and strongest dwarves cannot use a halberd, making",
"them useless in military terms. They can however be placed in a weapon",
"trap or melted down to provide metal bars, redeeming them."},
ITEM_WEAPON_HAMMER_WAR = {
"A war hammer is a blunt weapon that is essentially a hammer with a long",
"handle. War hammers use and train the hammerdwarf skill. Dwarves can",
"forge war hammers out of any weapons-grade metal, though those with",
"higher densities tend to cause more damage."},
ITEM_WEAPON_MACE = {
"A mace is a blunt weapon that consists of a rounded or flanged weight",
"mounted on the end of a handle. Despite similarities to a morningstar",
"in appearance, a mace is 60% larger and has twice the contact area.",
"Maces use and train the macedwarf skill. Dwarves can forge maces out of",
"any weapons-grade metal, though those with higher densities",
"(like silver) cause more damage."},
ITEM_WEAPON_MAUL = {
"A maul is a blunt weapon that is essentially a very large war hammer,",
"similar to a sledgehammer. Mauls are more than three times larger than",
"standard war hammers, with a similar 'bash' attack. Mauls also have",
"ten times the contact area and greatly reduced penetration, which",
"reduces their effectiveness. Mauls use and train the hammerdwarf",
"skill. Being foreign weapons, dwarves cannot forge mauls."},
ITEM_WEAPON_MORNINGSTAR = {
"A morningstar is an edged weapon that consists of a spiked ball mounted",
"on the end of a handle. Despite similarities to a mace in appearance,",
"a morningstar's size and contact area are closer to those of a war",
"hammer. Specifically, a morningstar is 25% larger than a war hammer",
"with the same contact area, and uses piercing damage to inflict internal",
"injuries. Morningstars use and train the macedwarf skill."},
ITEM_WEAPON_PICK = {
"The most important item for a beginning fortress, a pick can",
"get a party underground. Also crucial mining for stone or",
"metals, expansion of living space, and so on.",
"",
"A pick is also useful as a weapon, though putting miners in the",
"military causes equipment clashes."},
ITEM_WEAPON_PIKE = {
"A pike is a weapon that is essentially a very long spear.",
"Pikes use and train the Pikedwarf skill. As foreign weapons,",
"dwarves cannot forge pikes."},
ITEM_WEAPON_SCIMITAR = {
"A scimitar is an edged weapon with a curved blade that is very similar",
"to a short sword. Scimitars use and train the swordsdwarf skill.",
"As foreign weapons dwarves cannot forge scimitars."},
ITEM_WEAPON_SCOURGE = {
"A scourge is an edge weapon that consists of a spike or bladed weight",
"on the end of a flexible length of material that can be swung at",
"high speed. Scourges are similar to whips, though the whip is a blunt",
"weapon with an even smaller contact area.Scourges use and train the",
"lasher skill. As foreign weapons dwarves cannot forge scourges."},
ITEM_WEAPON_SPEAR = {
"A pole weapon consisting of a shaft, usually of wood, with a pointed",
"head made of metal or just the sharpened end of the shaft itself.",
"With the ability to pin opponents, spears are most effective with axe",
"or macedwarves for combo attacks. "},
ITEM_WEAPON_SPEAR_TRAINING = {
"A wooden training spear, this has no sharp edges and thus presents",
"little risk of injury. Military dwarves can become",
"attached to them, and refuse to swap them for weapons that cause",
"actual injury to your enemies."},
ITEM_WEAPON_SWORD_2H = {
"An enormous sword taller than many humans. Victims may be split in",
"two by a single blow, though no dwarf is large enough to wield a",
"greatsword and do so. As foreign weapons, dwarves cannot forge them."},
ITEM_WEAPON_SWORD_LONG = {
"A longsword is a classic weapon, consisting of a short handle and a",
"long sharp blade. Most dwarves are large enough to use a longsword,",
"but as foreign weapons cannot forge them."},
ITEM_WEAPON_SWORD_SHORT = {
"A sword just the right size for dwarves, though small dwarves may",
"need both hands. Shortswords can be made from metal at a forge."},
ITEM_WEAPON_SWORD_SHORT_TRAINING = {
"A wooden training sword, this has no sharp edges and thus presents",
"little risk of injury. Military dwarves can become",
"attached to them, and refuse to swap them for weapons that cause",
"actual injury to your enemies."},
ITEM_WEAPON_WHIP = {
"A highly effective weapon known to cause large amounts of pain.",
"It cannot be forged by dwarves."},
MEAT = { "Butchering an animal gives meat, the amount depending on the size",
"of the butchered animal. Along with plants, meat is the",
"backbone of every food industry."},
MILLSTONE = {
"A large grinding stone, used in a mill to produce flour, sugar, and",
"dyes much faster than a quern. It is too large to be operated by hand,",
"and must be powered for operation. Millstones are made of stone."},
ORTHOPEDIC_CAST = {
"Casts are made from plaster, and are used to keep broken bones in",
"place until they are healed. Applying a cast requires a bucket,",
"cloth and a water source."},
PIPE_SECTION = {
"An enormous piece of pipe, it is a part of a screw pump."},
QUERN = { "A hand-operated mill for plants, grains, and seeds. It mills plants",
"much slower than a millstone. Must be built from stone."},
QUIVER = { "Item used to hold ammunition, made out of leather. Hunting dwarves",
"and crossbow dwarves will automatically grab one to store their ammo."},
RING = { "A ring is an item of jewellery, which does not interfere with",
"wearing other equipment. Eleven rings can be worn on each finger",
"or toe, for a maximum of 220 rings."},
ROCK = { "A small rock, sharpened as a weapon in Adventure mode."},
ROUGH = { "Rough gemstones and raw glass are cut by a Gem Cutter at a Jeweler's",
"Workshop into small decorative gems. Sometimes, the gem-cutting job",
"results in a craft or large gem that is useless except as a very",
"valuable trade good."},
SCEPTER = { "A scepter is a short, ornamental rod or wand typically associated",
"with royalty. It's only use is as a trade good."},
SKIN_TANNED = {
"The tanned hide of animals is flexible enough to be made into an",
"assortment of goods for military and civilian use. Leather can also",
"be used to decorate items with sewn images at a Leather Works. Armor",
"and shields made from leather are not terribly effective, but are",
"still better than nothing at all."},
SLAB = { "A memorial stone, used to quiet restless ghost when engraved with",
"the name of the deceased and built."},
SMALLGEM = {"Cut gemstones and the odd gizzard stone (a product of butchering",
"certain species of animals) are used by a Gem Setter to decorate items",
"at a Jeweler's Workshop."},
SPLINT = { "Splints are used to immobilise fractured limbs. They are made out of",
"wood or metal, and allow dwarves to leave the hospital and continue",
"their normal jobs. Splints are applied with the bonedoctor skill."},
STATUE = { "A large piece of art carved in the likeness of a creature. Statues",
"can be installed on any open floor space, but cannot share the space",
"with creatures. Statues can be used as the focal point of a",
"recreational statue garden. Dwarves will admire or revile as they",
"pass, depending on the statue and the individual's preferences."},
TABLE = { "A flat-topped piece of furniture useful as a work-surface for a",
"scribe or a dining-surface for a hungry dwarf. Typically found in",
"shops, dinning rooms, and offices. Dining rooms may be designated and",
"assigned from them, though dwarves will complain if there are too few."},
THREAD = { "A small bundle of processed material, ready to be woven into cloth.",
"Thread made from animal hair will not be used to make cloth. Thread",
"can also be used by doctors to sew wounds shut. It is sourced from",
"shearing, plant processing, trade, or web gathering. It can be dyed",
"for additional value before being woven."},
TOTEM = { "A carved and polished skull."},
TRACTION_BENCH = {
"A special hospital bed made to secure dwarves with complex or",
"overlapping fractures until healed. Patients may need several months",
"or more in a traction bench to heal. Constructed from a table,",
"a mechanism, and a rope or chain."},
TRAPPARTS = {
"Used to build traps, levers and other machines."}
"Used to build traps, levers and other machines."},
WEAPONRACK = {
"Furniture used for training. Barracks may be designated and assigned",
"from them. Military squads may use their assigned barracks for",
"training, storage, or sleeping, depending on the settings."},
WINDOW = { "Furniture used for ambiance. Either made in a glass furnace from glass",
"or built on site using three cut gems. While it is treated as a wall,",
"it does not support constructions. Passing dwarves will admire them."},
WOOD = { "A porous and fibrous structural tissue found in the stems and roots",
"of trees and underground fungus. Wood is renewable and essential for",
"numerous industries. It can be made into charcoal, ash for further",
"processing, furniture, crafts, tools, some trap components, training",
"gear, and (ineffective) weapons and armor. Elves take serious offence",
"when wood or wooden items are offered in trade."}
}

@ -36,7 +36,8 @@ if args.showunitid or args.showpos then
end
else
local unit = args.unit and df.unit.find(args.unit) or dfhack.gui.getSelectedUnit(true)
local pos = not(not args.x or not args.y or not args.z) and {x=args.x,y=args.y,z=args.z} or df.global.cursor
local pos = not(not args.x or not args.y or not args.z) and {x=args.x,y=args.y,z=args.z} or {x=df.global.cursor.x,y=df.global.cursor.y,z=df.global.cursor.z}
if not unit then qerror('A unit needs to be selected or specified. Use teleport -showunitid to get a unit\'s ID.') end
if not pos.x or pos.x==-30000 then qerror('A position needs to be highlighted or specified. Use teleport -showpos to get a position\'s exact xyz values.') end
teleport(unit,pos)
end

@ -72,12 +72,12 @@ function GetMatPropertiesStringList (item)
append(list,"Temperature: "..deg_C.."\248C ("..deg_U.."U)")
append(list,"Color: "..df.global.world.raws.language.colors[mat.state_color.Solid].name)
local function GetStrainDescription (number)
if tonumber(number) >= 50000 then return "very elastic"
elseif tonumber(number) < 50000 then return "elastic"
elseif tonumber(number) < 15001 then return "medium"
elseif tonumber(number) < 5001 then return "stiff"
if tonumber(number) < 1 then return "crystalline"
elseif tonumber(number) < 1000 then return "very stiff"
elseif tonumber(number) < 1 then return "crystalline"
elseif tonumber(number) < 5001 then return "stiff"
elseif tonumber(number) < 15001 then return "medium"
elseif tonumber(number) < 50000 then return "elastic"
elseif tonumber(number) >= 50000 then return "very elastic"
else return "unknown" end
end
local mat_properties_for = {"BAR", "SMALLGEM", "BOULDER", "ROUGH",

@ -0,0 +1,129 @@
-- Pauses the game with a warning if a creature is starving, dehydrated, or very drowsy.
-- By Meneth32, PeridexisErrant, Lethosor
--@ module = true
starvingUnits = starvingUnits or {}
dehydratedUnits = dehydratedUnits or {}
sleepyUnits = sleepyUnits or {}
function clear()
starvingUnits = {}
dehydratedUnits = {}
sleepyUnits = {}
end
local gui = require 'gui'
local utils = require 'utils'
local units = df.global.world.units.active
local args = utils.invert({...})
if args.all or args.clear then
clear()
end
warning = defclass(warning, gui.FramedScreen)
warning.ATTRS = {
frame_style = gui.GREY_LINE_FRAME,
frame_title = 'Warning',
frame_width = 20,
frame_height = 18,
frame_inset = 1,
focus_path = 'warn-starving',
}
function warning:init(args)
self.start = 1
self.messages = args.messages
self.frame_height = math.min(18, #self.messages)
self.max_start = #self.messages - self.frame_height + 1
for _, msg in pairs(self.messages) do
self.frame_width = math.max(self.frame_width, #msg + 2)
end
self.frame_width = math.min(df.global.gps.dimx - 2, self.frame_width)
end
function warning:onRenderBody(p)
for i = self.start, math.min(self.start + self.frame_height - 1, #self.messages) do
p:string(self.messages[i]):newline()
end
if #self.messages > self.frame_height then
if self.start > 1 then
p:seek(self.frame_width - 1, 0):string(string.char(24), COLOR_LIGHTCYAN) -- up
end
if self.start < self.max_start then
p:seek(self.frame_width - 1, self.frame_height - 1):string(string.char(25), COLOR_LIGHTCYAN) -- down
end
end
end
function warning:onInput(keys)
if keys.LEAVESCREEN or keys.SELECT then
self:dismiss()
elseif keys.CURSOR_UP or keys.STANDARDSCROLL_UP then
self.start = math.max(1, self.start - 1)
elseif keys.CURSOR_DOWN or keys.STANDARDSCROLL_DOWN then
self.start = math.min(self.start + 1, self.max_start)
end
end
local function findRaceCaste(unit)
local rraw = df.creature_raw.find(unit.race)
return rraw, safe_index(rraw, 'caste', unit.caste)
end
local function getSexString(sex)
local sexStr = ""
if sex==0 then sexStr=string.char(12)
elseif sex==1 then sexStr=string.char(11)
end
return string.char(40)..sexStr..string.char(41)
end
local function nameOrSpeciesAndNumber(unit)
if unit.name.has_name then
return dfhack.TranslateName(dfhack.units.getVisibleName(unit))..' '..getSexString(unit.sex),true
else
return 'Unit #'..unit.id..' ('..df.creature_raw.find(unit.race).caste[unit.caste].caste_name[0]..' '..getSexString(unit.sex)..')',false
end
end
local function checkVariable(var, limit, description, map, unit)
local rraw = findRaceCaste(unit)
local species = rraw.name[0]
local profname = dfhack.units.getProfessionName(unit)
if #profname == 0 then profname = nil end
local name = nameOrSpeciesAndNumber(unit)
if var > limit then
if not map[unit.id] then
map[unit.id] = true
return name .. ", " .. (profname or species) .. " is " .. description .. "!"
end
else
map[unit.id] = false
end
return nil
end
function doCheck()
local messages = {}
for i=#units-1, 0, -1 do
local unit = units[i]
local rraw = findRaceCaste(unit)
if rraw and not unit.flags1.dead and not dfhack.units.isOpposedToLife(unit) then
table.insert(messages, checkVariable(unit.counters2.hunger_timer, 75000, 'starving', starvingUnits, unit))
table.insert(messages, checkVariable(unit.counters2.thirst_timer, 50000, 'dehydrated', dehydratedUnits, unit))
table.insert(messages, checkVariable(unit.counters2.sleepiness_timer, 150000, 'very drowsy', sleepyUnits, unit))
end
end
if #messages > 0 then
dfhack.color(COLOR_LIGHTMAGENTA)
for _, msg in pairs(messages) do
print(dfhack.df2console(msg))
end
dfhack.color()
df.global.pause_state = true
warning{messages=messages}:show()
end
end
if not moduleMode then doCheck() end