From 763a301b4f928bf20c78fb67f864e1623409c710 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 23 Apr 2012 21:30:53 +0400 Subject: [PATCH] Add a few more lua api functions, documentation, and unit sort orders. Units::getProfessionName appears to work correctly for everything except nobles. --- LUA_API.rst | 97 ++++++++++++- Lua API.html | 89 ++++++++++-- library/LuaApi.cpp | 4 + library/include/modules/Translation.h | 2 + library/include/modules/Units.h | 6 +- library/lua/dfhack.lua | 56 +++++--- library/modules/Translation.cpp | 23 ++- library/modules/Units.cpp | 200 +++++++++++++++++++++----- plugins/lua/sort/units.lua | 52 +++++-- 9 files changed, 449 insertions(+), 80 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 050714c72..32716fbf8 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -594,41 +594,44 @@ C++ function wrappers ===================== Thin wrappers around C++ functions, similar to the ones for virtual methods. +One notable difference is that these explicit wrappers allow argument count +adjustment according to the usual lua rules, so trailing false/nil arguments +can be omitted. -* ``dfhack.TranslateName(name,in_english,only_last_name)`` +* ``dfhack.TranslateName(name[,in_english,only_last_name])`` Convert a language_name or only the last name part to string. Gui module ---------- -* ``dfhack.gui.getSelectedWorkshopJob(silent)`` +* ``dfhack.gui.getSelectedWorkshopJob([silent])`` When a job is selected in *'q'* mode, returns the job, else prints error unless silent and returns *nil*. -* ``dfhack.gui.getSelectedJob(silent)`` +* ``dfhack.gui.getSelectedJob([silent])`` Returns the job selected in a workshop or unit/jobs screen. -* ``dfhack.gui.getSelectedUnit(silent)`` +* ``dfhack.gui.getSelectedUnit([silent])`` Returns the unit selected via *'v'*, *'k'*, unit/jobs, or a full-screen item view of a cage or suchlike. -* ``dfhack.gui.getSelectedItem(silent)`` +* ``dfhack.gui.getSelectedItem([silent])`` Returns the item selected via *'v'* ->inventory, *'k'*, *'t'*, or a full-screen item view of a container. Note that in the last case, the highlighted *contained item* is returned, not the container itself. -* ``dfhack.gui.showAnnouncement(text,color,is_bright)`` +* ``dfhack.gui.showAnnouncement(text,color[,is_bright])`` Adds a regular announcement with given text, color, and brightness. The is_bright boolean actually seems to invert the brightness. -* ``dfhack.gui.showPopupAnnouncement(text,color,is_bright)`` +* ``dfhack.gui.showPopupAnnouncement(text,color[,is_bright])`` Pops up a titan-style modal announcement window. @@ -688,6 +691,10 @@ Units module Returns the language_name object visible in game, accounting for false identities. +* ``dfhack.units.getIdentity(unit)`` + + Returns the false identity of the unit if it has one, or *nil*. + * ``dfhack.units.getNemesis(unit)`` Returns the nemesis record of the unit if it has one, or *nil*. @@ -716,6 +723,19 @@ Units module Adds or removes the unit from the burrow. +* ``dfhack.units.getAge(unit[,true_age])`` + + Returns the age of the unit in years as a floating-point value. + If ``true_age`` is true, ignores false identities. + +* ``dfhack.units.getProfessionName(unit[,plural])`` + + Retrieves the profession name using custom profession or raws. + +* ``dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])`` + + Retrieves the profession name for the given race/caste using raws. + Items module ------------ @@ -856,3 +876,66 @@ Features: Invokes all listeners contained in the event in an arbitrary order using ``dfhack.safecall``. + +======= +Plugins +======= + +DFHack plugins may export native functions and events +to lua contexts. They are automatically imported by +``mkmodule('plugins.')``; this means that a lua +module file is still necessary for ``require`` to read. + +The following plugins have lua support. + +burrows +======= + +Implements extended burrow manipulations. + +Events: + +* ``onBurrowRename.foo = function(burrow)`` + + Emitted when a burrow might have been renamed either through + the game UI, or ``renameBurrow()``. + +* ``onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)`` + + Emitted when a tile might have been dug out. Only tracked if the + auto-growing burrows feature is enabled. + +Native functions: + +* ``renameBurrow(burrow,name)`` + + Renames the burrow, emitting ``onBurrowRename`` and updating auto-grow state properly. + +* ``findByName(burrow,name)`` + + Finds a burrow by name, using the same rules as the plugin command line interface. + Namely, trailing ``'+'`` characters marking auto-grow burrows are ignored. + +* ``copyUnits(target,source,enable)`` + + Applies units from ``source`` burrow to ``target``. The ``enable`` + parameter specifies if they are to be added or removed. + +* ``copyTiles(target,source,enable)`` + + Applies tiles from ``source`` burrow to ``target``. The ``enable`` + parameter specifies if they are to be added or removed. + +* ``setTilesByKeyword(target,keyword,enable)`` + + Adds or removes tiles matching a predefined keyword. The keyword + set is the same as used by the command line. + +The lua module file also re-exports or wraps some of the +functions implemented by the dfhack core for convenience. + +sort +==== + +Does not export any native functions as of now. Instead, it +calls lua code to perform the actual ordering of list items. diff --git a/Lua API.html b/Lua API.html index cbf35f0eb..916c6eef8 100644 --- a/Lua API.html +++ b/Lua API.html @@ -350,6 +350,11 @@ ul.auto-toc { +
  • Plugins +
  • @@ -842,37 +847,40 @@ Accept dfhack_material_category auto-assign table.

    C++ function wrappers

    -

    Thin wrappers around C++ functions, similar to the ones for virtual methods.

    +

    Thin wrappers around C++ functions, similar to the ones for virtual methods. +One notable difference is that these explicit wrappers allow argument count +adjustment according to the usual lua rules, so trailing false/nil arguments +can be omitted.

      -
    • dfhack.TranslateName(name,in_english,only_last_name)

      +
    • dfhack.TranslateName(name[,in_english,only_last_name])

      Convert a language_name or only the last name part to string.

    Gui module

      -
    • dfhack.gui.getSelectedWorkshopJob(silent)

      +
    • dfhack.gui.getSelectedWorkshopJob([silent])

      When a job is selected in 'q' mode, returns the job, else prints error unless silent and returns nil.

    • -
    • dfhack.gui.getSelectedJob(silent)

      +
    • dfhack.gui.getSelectedJob([silent])

      Returns the job selected in a workshop or unit/jobs screen.

    • -
    • dfhack.gui.getSelectedUnit(silent)

      +
    • dfhack.gui.getSelectedUnit([silent])

      Returns the unit selected via 'v', 'k', unit/jobs, or a full-screen item view of a cage or suchlike.

    • -
    • dfhack.gui.getSelectedItem(silent)

      +
    • dfhack.gui.getSelectedItem([silent])

      Returns the item selected via 'v' ->inventory, 'k', 't', or a full-screen item view of a container. Note that in the last case, the highlighted contained item is returned, not the container itself.

    • -
    • dfhack.gui.showAnnouncement(text,color,is_bright)

      +
    • dfhack.gui.showAnnouncement(text,color[,is_bright])

      Adds a regular announcement with given text, color, and brightness. The is_bright boolean actually seems to invert the brightness.

    • -
    • dfhack.gui.showPopupAnnouncement(text,color,is_bright)

      +
    • dfhack.gui.showPopupAnnouncement(text,color[,is_bright])

      Pops up a titan-style modal announcement window.

    @@ -923,6 +931,9 @@ a lua list containing them.

  • dfhack.units.getVisibleName(unit)

    Returns the language_name object visible in game, accounting for false identities.

  • +
  • dfhack.units.getIdentity(unit)

    +

    Returns the false identity of the unit if it has one, or nil.

    +
  • dfhack.units.getNemesis(unit)

    Returns the nemesis record of the unit if it has one, or nil.

  • @@ -944,6 +955,16 @@ a lua list containing them.

  • dfhack.units.setInBurrow(unit,burrow,enable)

    Adds or removes the unit from the burrow.

  • +
  • dfhack.units.getAge(unit[,true_age])

    +

    Returns the age of the unit in years as a floating-point value. +If true_age is true, ignores false identities.

    +
  • +
  • dfhack.units.getProfessionName(unit[,plural])

    +

    Retrieves the profession name using custom profession or raws.

    +
  • +
  • dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])

    +

    Retrieves the profession name for the given race/caste using raws.

    +
  • @@ -1061,6 +1082,58 @@ order using dfhack.safecall.

    +
    +

    Plugins

    +

    DFHack plugins may export native functions and events +to lua contexts. They are automatically imported by +mkmodule('plugins.<name>'); this means that a lua +module file is still necessary for require to read.

    +

    The following plugins have lua support.

    +
    +

    burrows

    +

    Implements extended burrow manipulations.

    +

    Events:

    +
      +
    • onBurrowRename.foo = function(burrow)

      +

      Emitted when a burrow might have been renamed either through +the game UI, or renameBurrow().

      +
    • +
    • onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)

      +

      Emitted when a tile might have been dug out. Only tracked if the +auto-growing burrows feature is enabled.

      +
    • +
    +

    Native functions:

    +
      +
    • renameBurrow(burrow,name)

      +

      Renames the burrow, emitting onBurrowRename and updating auto-grow state properly.

      +
    • +
    • findByName(burrow,name)

      +

      Finds a burrow by name, using the same rules as the plugin command line interface. +Namely, trailing '+' characters marking auto-grow burrows are ignored.

      +
    • +
    • copyUnits(target,source,enable)

      +

      Applies units from source burrow to target. The enable +parameter specifies if they are to be added or removed.

      +
    • +
    • copyTiles(target,source,enable)

      +

      Applies tiles from source burrow to target. The enable +parameter specifies if they are to be added or removed.

      +
    • +
    • setTilesByKeyword(target,keyword,enable)

      +

      Adds or removes tiles matching a predefined keyword. The keyword +set is the same as used by the command line.

      +
    • +
    +

    The lua module file also re-exports or wraps some of the +functions implemented by the dfhack core for convenience.

    +
    +
    +

    sort

    +

    Does not export any native functions as of now. Instead, it +calls lua code to perform the actual ordering of list items.

    +
    +
    diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 00192c058..71df50e22 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -58,6 +58,7 @@ distribution. #include "df/unit.h" #include "df/item.h" #include "df/material.h" +#include "df/assumed_identity.h" #include "df/nemesis_record.h" #include "df/historical_figure.h" #include "df/plant_raw.h" @@ -608,6 +609,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getContainer), WRAPM(Units, setNickname), WRAPM(Units, getVisibleName), + WRAPM(Units, getIdentity), WRAPM(Units, getNemesis), WRAPM(Units, isDead), WRAPM(Units, isAlive), @@ -616,6 +618,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isInBurrow), WRAPM(Units, setInBurrow), WRAPM(Units, getAge), + WRAPM(Units, getProfessionName), + WRAPM(Units, getCasteProfessionName), { NULL, NULL } }; diff --git a/library/include/modules/Translation.h b/library/include/modules/Translation.h index 6d74431e3..f75bf6339 100644 --- a/library/include/modules/Translation.h +++ b/library/include/modules/Translation.h @@ -52,6 +52,8 @@ DFHACK_EXPORT bool copyName(df::language_name * address, df::language_name * tar DFHACK_EXPORT void setNickname(df::language_name *name, std::string nick); +DFHACK_EXPORT std::string capitalize(const std::string &str, bool all_words = false); + // translate a name using the loaded dictionaries DFHACK_EXPORT std::string TranslateName (const df::language_name * name, bool inEnglish = true, bool onlyLastPart = false); diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 775cc5181..8e4fe2ade 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -37,6 +37,7 @@ namespace df { struct nemesis_record; struct burrow; + struct assumed_identity; } /** @@ -201,6 +202,7 @@ DFHACK_EXPORT df::item *getContainer(df::unit *unit); DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick); DFHACK_EXPORT df::language_name *getVisibleName(df::unit *unit); +DFHACK_EXPORT df::assumed_identity *getIdentity(df::unit *unit); DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit); DFHACK_EXPORT bool isDead(df::unit *unit); @@ -214,8 +216,10 @@ DFHACK_EXPORT void clearBurrowMembers(df::burrow *burrow); DFHACK_EXPORT bool isInBurrow(df::unit *unit, df::burrow *burrow); DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable); -DFHACK_EXPORT double getAge(df::unit *unit); +DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false); +DFHACK_EXPORT std::string getProfessionName(df::unit *unit, bool plural = false); +DFHACK_EXPORT std::string getCasteProfessionName(int race, int caste, df::profession pid, bool plural = false); } } #endif diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 5c10b002c..100aea59c 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -125,6 +125,23 @@ function xyz2pos(x,y,z) end end +function safe_index(obj,idx,...) + if obj == nil or idx == nil then + return nil + end + if type(idx) == 'number' and (idx < 0 or idx >= #obj) then + return nil + end + obj = obj[idx] + if select('#',...) > 0 then + return safe_index(obj,...) + else + return obj + end +end + +-- String conversions + function dfhack.event:__tostring() return "" end @@ -168,12 +185,14 @@ function dfhack.interpreter(prompt,hfile,env) end local prompt_str = "["..(prompt or 'lua').."]# " + local prompt_cont = string.rep(' ',#prompt_str-4)..">>> " local prompt_env = {} - local t_prompt + local cmdlinelist = {} + local t_prompt = nil local vcnt = 1 setmetatable(prompt_env, { __index = env or _G }) - local cmdlinelist={} + while true do local cmdline = dfhack.lineedit(t_prompt or prompt_str, hfile) @@ -182,26 +201,29 @@ function dfhack.interpreter(prompt,hfile,env) elseif cmdline ~= '' then local pfix = string.sub(cmdline,1,1) - if pfix == '!' or pfix == '=' then + if not t_prompt and (pfix == '!' or pfix == '=') then cmdline = 'return '..string.sub(cmdline,2) + else + pfix = nil end - table.insert(cmdlinelist,cmdline) - local code,err = load(table.concat(cmdlinelist,'\n'), '=(interactive)', 't', prompt_env) + + table.insert(cmdlinelist,cmdline) + cmdline = table.concat(cmdlinelist,'\n') + + local code,err = load(cmdline, '=(interactive)', 't', prompt_env) if code == nil then - if err:sub(-5)=="" then - t_prompt="[cont]" - - else - dfhack.printerr(err) - cmdlinelist={} - t_prompt=nil - end + if not pfix and err:sub(-5)=="" then + t_prompt=prompt_cont + else + dfhack.printerr(err) + cmdlinelist={} + t_prompt=nil + end else - - cmdlinelist={} - t_prompt=nil - + cmdlinelist={} + t_prompt=nil + local data = table.pack(safecall(code)) if data[1] and data.n > 1 then diff --git a/library/modules/Translation.cpp b/library/modules/Translation.cpp index 6b055a4ac..db1305161 100644 --- a/library/modules/Translation.cpp +++ b/library/modules/Translation.cpp @@ -81,15 +81,32 @@ bool Translation::copyName(df::language_name * source, df::language_name * targe return true; } +std::string Translation::capitalize(const std::string &str, bool all_words) +{ + string upper = str; + + if (!upper.empty()) + { + upper[0] = toupper(upper[0]); + + if (all_words) + { + for (size_t i = 1; i < upper.size(); i++) + if (isspace(upper[i-1])) + upper[i] = toupper(upper[i]); + } + } + + return upper; +} + void addNameWord (string &out, const string &word) { if (word.empty()) return; - string upper = word; - upper[0] = toupper(upper[0]); if (out.length() > 0) out.append(" "); - out.append(upper); + out.append(Translation::capitalize(word)); } void Translation::setNickname(df::language_name *name, std::string nick) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index e9699edbe..a691dc5d9 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -57,6 +57,8 @@ using namespace std; #include "df/historical_figure_info.h" #include "df/assumed_identity.h" #include "df/burrow.h" +#include "df/creature_raw.h" +#include "df/caste_raw.h" using namespace DFHack; using namespace df::enums; @@ -529,6 +531,23 @@ df::item *Units::getContainer(df::unit *unit) return NULL; } +static df::assumed_identity *getFigureIdentity(df::historical_figure *figure) +{ + if (figure && figure->info && figure->info->reputation) + return df::assumed_identity::find(figure->info->reputation->cur_identity); + + return NULL; +} + +df::assumed_identity *Units::getIdentity(df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + + df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id); + + return getFigureIdentity(figure); +} + void Units::setNickname(df::unit *unit, std::string nick) { CHECK_NULL_POINTER(unit); @@ -547,25 +566,19 @@ void Units::setNickname(df::unit *unit, std::string nick) { Translation::setNickname(&figure->name, nick); - // v0.34.01: added the vampire's assumed identity - if (figure->info && figure->info->reputation) + if (auto identity = getFigureIdentity(figure)) { - auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity); + auto id_hfig = df::historical_figure::find(identity->histfig_id); - if (identity) + if (id_hfig) { - auto id_hfig = df::historical_figure::find(identity->histfig_id); - - if (id_hfig) - { - // Even DF doesn't do this bit, because it's apparently - // only used for demons masquerading as gods, so you - // can't ever change their nickname in-game. - Translation::setNickname(&id_hfig->name, nick); - } - else - Translation::setNickname(&identity->name, nick); + // Even DF doesn't do this bit, because it's apparently + // only used for demons masquerading as gods, so you + // can't ever change their nickname in-game. + Translation::setNickname(&id_hfig->name, nick); } + else + Translation::setNickname(&identity->name, nick); } } } @@ -574,25 +587,14 @@ df::language_name *Units::getVisibleName(df::unit *unit) { CHECK_NULL_POINTER(unit); - df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id); - - if (figure) + if (auto identity = getIdentity(unit)) { - // v0.34.01: added the vampire's assumed identity - if (figure->info && figure->info->reputation) - { - auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity); + auto id_hfig = df::historical_figure::find(identity->histfig_id); - if (identity) - { - auto id_hfig = df::historical_figure::find(identity->histfig_id); + if (id_hfig) + return &id_hfig->name; - if (id_hfig) - return &id_hfig->name; - - return &identity->name; - } - } + return &identity->name; } return &unit->name; @@ -734,7 +736,7 @@ void DFHack::Units::setInBurrow(df::unit *unit, df::burrow *burrow, bool enable) } } -double DFHack::Units::getAge(df::unit *unit) +double DFHack::Units::getAge(df::unit *unit, bool true_age) { using df::global::cur_year; using df::global::cur_year_tick; @@ -748,5 +750,139 @@ double DFHack::Units::getAge(df::unit *unit) double birth_time = unit->relations.birth_year + unit->relations.birth_time/year_ticks; double cur_time = *cur_year + *cur_year_tick / year_ticks; + if (!true_age && unit->relations.curse_year >= 0) + { + if (auto identity = getIdentity(unit)) + { + if (identity->histfig_id < 0) + birth_time = identity->birth_year + identity->birth_second/year_ticks; + } + } + return cur_time - birth_time; } + +std::string DFHack::Units::getProfessionName(df::unit *unit, bool plural) +{ + std::string prof = unit->custom_profession; + + if (prof.empty()) + prof = getCasteProfessionName(unit->race, unit->caste, unit->profession, plural); + + return prof; +} + +std::string DFHack::Units::getCasteProfessionName(int race, int casteid, df::profession pid, bool plural) +{ + std::string prof, race_prefix; + + if (pid < 0 || !is_valid_enum_item(pid)) + return ""; + + bool use_race_prefix = (race >= 0 && race != df::global::ui->race_id); + + if (auto creature = df::creature_raw::find(race)) + { + if (auto caste = vector_get(creature->caste, casteid)) + { + race_prefix = caste->caste_name[0]; + + if (plural) + prof = caste->caste_profession_name.plural[pid]; + else + prof = caste->caste_profession_name.singular[pid]; + + if (prof.empty()) + { + switch (pid) + { + case profession::CHILD: + prof = caste->child_name[plural ? 1 : 0]; + if (!prof.empty()) + use_race_prefix = false; + break; + + case profession::BABY: + prof = caste->baby_name[plural ? 1 : 0]; + if (!prof.empty()) + use_race_prefix = false; + break; + + default: + break; + } + } + } + + if (race_prefix.empty()) + race_prefix = creature->name[0]; + + if (prof.empty()) + { + if (plural) + prof = creature->profession_name.plural[pid]; + else + prof = creature->profession_name.singular[pid]; + + if (prof.empty()) + { + switch (pid) + { + case profession::CHILD: + prof = creature->general_child_name[plural ? 1 : 0]; + if (!prof.empty()) + use_race_prefix = false; + break; + + case profession::BABY: + prof = creature->general_baby_name[plural ? 1 : 0]; + if (!prof.empty()) + use_race_prefix = false; + break; + + default: + break; + } + } + } + } + + if (race_prefix.empty()) + race_prefix = "Animal"; + + if (prof.empty()) + { + switch (pid) + { + case profession::TRAINED_WAR: + prof = "War " + (use_race_prefix ? race_prefix : "Peasant"); + use_race_prefix = false; + break; + + case profession::TRAINED_HUNTER: + prof = "Hunting " + (use_race_prefix ? race_prefix : "Peasant"); + use_race_prefix = false; + break; + + case profession::STANDARD: + if (!use_race_prefix) + prof = "Peasant"; + break; + + default: + if (auto caption = ENUM_ATTR(profession, caption, pid)) + prof = caption; + else + prof = ENUM_KEY_STR(profession, pid); + } + } + + if (use_race_prefix) + { + if (!prof.empty()) + race_prefix += " "; + prof = race_prefix + prof; + } + + return Translation::capitalize(prof, true); +} diff --git a/plugins/lua/sort/units.lua b/plugins/lua/sort/units.lua index 84beff8ed..7a332d094 100644 --- a/plugins/lua/sort/units.lua +++ b/plugins/lua/sort/units.lua @@ -13,8 +13,9 @@ orders.exists = { orders.name = { key = function(unit) - if unit.name.has_name then - return dfhack.TranslateName(unit.name) + local name = dfhack.units.getVisibleName(unit) + if name and name.has_name then + return dfhack.TranslateName(name) end end, compare = utils.compare_name @@ -29,28 +30,55 @@ orders.age = { -- This assumes that units are added to active in arrival order orders.arrival = { key_table = function(units) - local tmp={} - for i,v in ipairs(units) do - tmp[v.id] = i + local size = units.n or #units + local lookup={} + for i=1,size do + if units[i] then + lookup[units[i].id] = i + end end local idx={} for i,v in ipairs(df.global.world.units.active) do - local ix = tmp[v.id] - if ix ~= nil then - idx[ix] = i + if lookup[v.id] then + idx[lookup[v.id]] = i end end return idx end } +local function findRaceCaste(unit) + local rraw = df.creature_raw.find(unit.race) + return rraw, safe_index(rraw, 'caste', unit.caste) +end + orders.profession = { key = function(unit) - local cp = unit.custom_profession - if cp == '' then - cp = df.profession.attrs[unit.profession].caption + local cp = dfhack.units.getProfessionName(unit) + if cp and cp ~= '' then + return string.lower(cp) + end + end +} + +orders.profession_class = { + key = function(unit) + local pid = unit.profession + local parent = df.profession.attrs[pid].parent + if parent >= 0 then + return parent + else + return pid + end + end +} + +orders.race = { + key = function(unit) + local rraw = findRaceCaste(unit) + if rraw then + return rraw.name[0] end - return cp end }