summaryrefslogtreecommitdiff
path: root/library/modules/Units.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'library/modules/Units.cpp')
-rw-r--r--library/modules/Units.cpp159
1 files changed, 156 insertions, 3 deletions
diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp
index 874dabc3..6a672b58 100644
--- a/library/modules/Units.cpp
+++ b/library/modules/Units.cpp
@@ -63,11 +63,15 @@ using namespace std;
#include "df/burrow.h"
#include "df/creature_raw.h"
#include "df/caste_raw.h"
+#include "df/game_mode.h"
+#include "df/unit_misc_trait.h"
+#include "df/unit_skill.h"
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::ui;
+using df::global::gamemode;
bool Units::isValid()
{
@@ -626,8 +630,9 @@ static bool casteFlagSet(int race, int caste, df::caste_raw_flags flag)
return craw->flags.is_set(flag);
}
-static bool isCrazed(df::unit *unit)
+bool Units::isCrazed(df::unit *unit)
{
+ CHECK_NULL_POINTER(unit);
if (unit->flags3.bits.scuttle)
return false;
if (unit->curse.rem_tags1.bits.CRAZED)
@@ -637,13 +642,54 @@ static bool isCrazed(df::unit *unit)
return casteFlagSet(unit->race, unit->caste, caste_raw_flags::CRAZED);
}
-static bool isOpposedToLife(df::unit *unit)
+bool Units::isOpposedToLife(df::unit *unit)
{
+ CHECK_NULL_POINTER(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);
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::OPPOSED_TO_LIFE);
+}
+
+bool Units::hasExtravision(df::unit *unit)
+{
+ CHECK_NULL_POINTER(unit);
+ if (unit->curse.rem_tags1.bits.EXTRAVISION)
+ return false;
+ if (unit->curse.add_tags1.bits.EXTRAVISION)
+ return true;
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::EXTRAVISION);
+}
+
+bool Units::isBloodsucker(df::unit *unit)
+{
+ CHECK_NULL_POINTER(unit);
+ if (unit->curse.rem_tags1.bits.BLOODSUCKER)
+ return false;
+ if (unit->curse.add_tags1.bits.BLOODSUCKER)
+ return true;
+ return casteFlagSet(unit->race, unit->caste, caste_raw_flags::BLOODSUCKER);
+}
+
+df::unit_misc_trait *Units::getMiscTrait(df::unit *unit, df::misc_trait_type type, bool create)
+{
+ CHECK_NULL_POINTER(unit);
+
+ auto &vec = unit->status.misc_traits;
+ for (size_t i = 0; i < vec.size(); i++)
+ if (vec[i]->id == type)
+ return vec[i];
+
+ if (create)
+ {
+ auto obj = new df::unit_misc_trait();
+ obj->id = type;
+ vec.push_back(obj);
+ return obj;
+ }
+
+ return NULL;
}
bool DFHack::Units::isDead(df::unit *unit)
@@ -753,6 +799,113 @@ double DFHack::Units::getAge(df::unit *unit, bool true_age)
return cur_time - birth_time;
}
+inline int adjust_skill_rating(int &rating, bool is_adventure, int value, int dwarf3_4, int dwarf1_2, int adv9_10, int adv3_4, int adv1_2)
+{
+ if (is_adventure)
+ {
+ if (value >= adv1_2) rating >>= 1;
+ else if (value >= adv3_4) rating = rating*3/4;
+ else if (value >= adv9_10) rating = rating*9/10;
+ }
+ else
+ {
+ if (value >= dwarf1_2) rating >>= 1;
+ else if (value >= dwarf3_4) return rating*3/4;
+ }
+}
+
+int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id)
+{
+ CHECK_NULL_POINTER(unit);
+
+ /*
+ * This is 100% reverse-engineered from DF code.
+ */
+
+ if (!unit->status.current_soul)
+ return 0;
+
+ // Retrieve skill from unit soul:
+
+ df::enum_field<df::job_skill,int16_t> key(skill_id);
+ auto skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, key);
+
+ int rating = 0;
+ if (skill)
+ rating = std::max(0, int(skill->rating) - skill->rusty);
+
+ // Apply special states
+
+ if (unit->counters.soldier_mood == df::unit::T_counters::None)
+ {
+ if (unit->counters.nausea > 0) rating >>= 1;
+ if (unit->counters.winded > 0) rating >>= 1;
+ if (unit->counters.stunned > 0) rating >>= 1;
+ if (unit->counters.dizziness > 0) rating >>= 1;
+ if (unit->counters2.fever > 0) rating >>= 1;
+ }
+
+ if (unit->counters.soldier_mood != df::unit::T_counters::MartialTrance)
+ {
+ if (!unit->flags3.bits.ghostly && !unit->flags3.bits.scuttle &&
+ !unit->flags2.bits.vision_good && !unit->flags2.bits.vision_damaged &&
+ !hasExtravision(unit))
+ {
+ rating >>= 2;
+ }
+ if (unit->counters.pain >= 100 && unit->mood == -1)
+ {
+ rating >>= 1;
+ }
+ if (unit->counters2.exhaustion >= 2000)
+ {
+ rating = rating*3/4;
+ if (unit->counters2.exhaustion >= 4000)
+ {
+ rating = rating*3/4;
+ if (unit->counters2.exhaustion >= 6000)
+ rating = rating*3/4;
+ }
+ }
+ }
+
+ // Hunger etc timers
+
+ bool is_adventure = (gamemode && *gamemode == game_mode::ADVENTURE);
+
+ if (!unit->flags3.bits.scuttle && isBloodsucker(unit))
+ {
+ using namespace df::enums::misc_trait_type;
+
+ if (auto trait = getMiscTrait(unit, TimeSinceSuckedBlood))
+ {
+ adjust_skill_rating(
+ rating, is_adventure, trait->value,
+ 302400, 403200, // dwf 3/4; 1/2
+ 1209600, 1209600, 2419200 // adv 9/10; 3/4; 1/2
+ );
+ }
+ }
+
+ adjust_skill_rating(
+ rating, is_adventure, unit->counters2.thirst_timer,
+ 50000, 50000, 115200, 172800, 345600
+ );
+ adjust_skill_rating(
+ rating, is_adventure, unit->counters2.hunger_timer,
+ 75000, 75000, 172800, 1209600, 2592000
+ );
+ if (is_adventure && unit->counters2.sleepiness_timer >= 846000)
+ rating >>= 2;
+ else
+ adjust_skill_rating(
+ rating, is_adventure, unit->counters2.sleepiness_timer,
+ 150000, 150000, 172800, 259200, 345600
+ );
+
+ return rating;
+}
+
static bool noble_pos_compare(const Units::NoblePosition &a, const Units::NoblePosition &b)
{
if (a.position->precedence < b.position->precedence)