summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LUA_API.rst13
-rw-r--r--Lua API.html11
-rw-r--r--library/LuaApi.cpp2
-rw-r--r--library/modules/Units.cpp71
m---------library/xml0
5 files changed, 85 insertions, 12 deletions
diff --git a/LUA_API.rst b/LUA_API.rst
index b0f1a9f3..9fb37375 100644
--- a/LUA_API.rst
+++ b/LUA_API.rst
@@ -726,7 +726,7 @@ Units module
* ``dfhack.units.isDead(unit)``
- The unit is completely dead and passive.
+ The unit is completely dead and passive, or a ghost.
* ``dfhack.units.isAlive(unit)``
@@ -734,7 +734,16 @@ Units module
* ``dfhack.units.isSane(unit)``
- The unit is capable of rational action, i.e. not dead, insane or zombie.
+ The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf.
+
+* ``dfhack.units.isDwarf(unit)``
+
+ The unit is of the correct race of the fortress.
+
+* ``dfhack.units.isCitizen(unit)``
+
+ The unit is an alive sane citizen of the fortress; wraps the
+ same checks the game uses to decide game-over by extinction.
* ``dfhack.units.getAge(unit[,true_age])``
diff --git a/Lua API.html b/Lua API.html
index 4b763af2..1576652d 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -960,13 +960,20 @@ a lua list containing them.</p>
<p>Returns the nemesis record of the unit if it has one, or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isDead(unit)</tt></p>
-<p>The unit is completely dead and passive.</p>
+<p>The unit is completely dead and passive, or a ghost.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isAlive(unit)</tt></p>
<p>The unit isn't dead or undead.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.units.isSane(unit)</tt></p>
-<p>The unit is capable of rational action, i.e. not dead, insane or zombie.</p>
+<p>The unit is capable of rational action, i.e. not dead, insane, zombie, or active werewolf.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.units.isDwarf(unit)</tt></p>
+<p>The unit is of the correct race of the fortress.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">dfhack.units.isCitizen(unit)</tt></p>
+<p>The unit is an alive sane citizen of the fortress; wraps the
+same checks the game uses to decide game-over by extinction.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.units.getAge(unit[,true_age])</span></tt></p>
<p>Returns the age of the unit in years as a floating-point value.
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 6a550db8..8e8a3c4e 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -715,6 +715,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDead),
WRAPM(Units, isAlive),
WRAPM(Units, isSane),
+ WRAPM(Units, isDwarf),
+ WRAPM(Units, isCitizen),
WRAPM(Units, getAge),
WRAPM(Units, getProfessionName),
WRAPM(Units, getCasteProfessionName),
diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp
index dd26109e..ee383cc0 100644
--- a/library/modules/Units.cpp
+++ b/library/modules/Units.cpp
@@ -613,12 +613,45 @@ df::nemesis_record *Units::getNemesis(df::unit *unit)
return NULL;
}
+static bool casteFlagSet(int race, int caste, df::caste_raw_flags flag)
+{
+ auto creature = df::creature_raw::find(race);
+ if (!creature)
+ return false;
+
+ auto craw = vector_get(creature->caste, caste);
+ if (!craw)
+ return false;
+
+ return craw->flags.is_set(flag);
+}
+
+static bool isCrazed(df::unit *unit)
+{
+ if (unit->flags3.bits.scuttle)
+ return false;
+ if (unit->curse.rem_tags1.bits.CRAZED)
+ return false;
+ if (unit->curse.add_tags1.bits.CRAZED)
+ return true;
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CRAZED);
+}
+
+static bool isOpposedToLife(df::unit *unit)
+{
+ if (unit->curse.rem_tags1.bits.OPPOSED_TO_LIFE)
+ return false;
+ if (unit->curse.add_tags1.bits.OPPOSED_TO_LIFE)
+ return true;
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CANNOT_UNDEAD);
+}
bool DFHack::Units::isDead(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
- return unit->flags1.bits.dead;
+ return unit->flags1.bits.dead ||
+ unit->flags3.bits.ghostly;
}
bool DFHack::Units::isAlive(df::unit *unit)
@@ -636,8 +669,11 @@ bool DFHack::Units::isSane(df::unit *unit)
if (unit->flags1.bits.dead ||
unit->flags3.bits.ghostly ||
- unit->curse.add_tags1.bits.OPPOSED_TO_LIFE ||
- unit->curse.add_tags1.bits.CRAZED)
+ isOpposedToLife(unit) ||
+ unit->unknown8.unk2)
+ return false;
+
+ if (unit->unknown8.normal_race == unit->unknown8.were_race && isCrazed(unit))
return false;
switch (unit->mood)
@@ -657,19 +693,38 @@ bool DFHack::Units::isCitizen(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
+ // Copied from the conditions used to decide game over,
+ // except that the game appears to let melancholy/raving
+ // dwarves count as citizens.
+
+ if (!isDwarf(unit) || !isSane(unit))
+ return false;
+
+ if (unit->flags1.bits.marauder ||
+ unit->flags1.bits.invader_origin ||
+ unit->flags1.bits.active_invader ||
+ unit->flags1.bits.forest ||
+ unit->flags1.bits.merchant ||
+ unit->flags1.bits.diplomat)
+ return false;
+
+ if (unit->flags1.bits.tame)
+ return true;
+
return unit->civ_id == ui->civ_id &&
- !unit->flags1.bits.merchant &&
- !unit->flags1.bits.diplomat &&
+ unit->civ_id != -1 &&
+ !unit->flags2.bits.underworld &&
!unit->flags2.bits.resident &&
- !unit->flags1.bits.dead &&
- !unit->flags3.bits.ghostly;
+ !unit->flags2.bits.visitor_uninvited &&
+ !unit->flags2.bits.visitor;
}
bool DFHack::Units::isDwarf(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
- return unit->race == ui->race_id;
+ return unit->race == ui->race_id ||
+ unit->unknown8.normal_race == ui->race_id;
}
double DFHack::Units::getAge(df::unit *unit, bool true_age)
diff --git a/library/xml b/library/xml
-Subproject 234d0f57a927f306f2052fc2f45d38b3201ddee
+Subproject d991d47b0f6709205c4e333bb86edb3dcf65642