diff --git a/Contributors.rst b/Contributors.rst index 8f4d6d635..647f0554e 100644 --- a/Contributors.rst +++ b/Contributors.rst @@ -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**. diff --git a/NEWS b/NEWS index c72b77835..e84fff67c 100644 --- a/NEWS +++ b/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 diff --git a/Readme.rst b/Readme.rst index 3870635ce..bd97755f8 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1692,7 +1692,7 @@ Records dwarf activity to measure fort efficiency. Options: ``dwarfmonitor enable ``: - Start monitoring ``mode``. ``mode`` can be "work", "misery", or "all". + Start monitoring ``mode``. ``mode`` can be "work", "misery", "weather", or "all". ``dwarfmonitor disable ``: 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 ======== diff --git a/library/Core.cpp b/library/Core.cpp index 2696e9526..f99c12039 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -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) diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 94c67828b..8c0e5ef1a 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -24,6 +24,7 @@ distribution. #include "Internal.h" +#include #include #include #include @@ -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); diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 6a7bf068e..e6510eacf 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -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; diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 042870273..9121660ec 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -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 */ diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 3e9097c54..ab032c4e3 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -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 *keys); + virtual bool key_conflict(df::interface_key key); virtual void onShow(); virtual void onDismiss(); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 8af680bb6..1ac50c98d 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -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}) - return env +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, ...) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index eaa3b7ad2..d338ab538 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -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(); diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 4866c80c1..517414766 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -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; diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 4d9b452ce..5d6ff4b7c 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -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> 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 \n" " Start monitoring \n" - " can be \"work\", \"misery\", or \"all\"\n" + " can be \"work\", \"misery\", \"weather\", or \"all\"\n" "dwarfmonitor disable \n" " as above\n\n" "dwarfmonitor stats\n" diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index d5964da7b..113f4c89d 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -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,8 +272,11 @@ 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 { @@ -281,7 +284,11 @@ public: if (isActuallyResumed()) resume_delay = DAY_TICKS; - actual_job->flags.bits.suspend = true; + if (!actual_job->flags.bits.suspend) + { + actual_job->flags.bits.suspend = true; + actual_job->unk_v4020_1 = -1; + } } want_resumed = resume; diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua index 4f7405121..7b104501d 100644 --- a/scripts/gui/gm-editor.lua +++ b/scripts/gui/gm-editor.lua @@ -212,14 +212,14 @@ function GmEditorUi:editSelected(index,choice) dialog.showInputPrompt(tostring(trg_key),"Enter new value:",COLOR_WHITE, tostring(trg.target[trg_key]),self:callback("commitEdit",trg_key)) - elseif trg_type=='boolean' then - trg.target[trg_key]= not trg.target[trg_key] + 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 diff --git a/scripts/item-descriptions.lua b/scripts/item-descriptions.lua index 7d1b3d4e8..9ce970c95 100644 --- a/scripts/item-descriptions.lua +++ b/scripts/item-descriptions.lua @@ -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."} } diff --git a/scripts/teleport.lua b/scripts/teleport.lua index ffe798f5d..64a456219 100644 --- a/scripts/teleport.lua +++ b/scripts/teleport.lua @@ -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 diff --git a/scripts/view-item-info.lua b/scripts/view-item-info.lua index bb8594656..9ea539ca2 100644 --- a/scripts/view-item-info.lua +++ b/scripts/view-item-info.lua @@ -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", diff --git a/scripts/warn-starving.lua b/scripts/warn-starving.lua new file mode 100644 index 000000000..40d1ffa93 --- /dev/null +++ b/scripts/warn-starving.lua @@ -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