Replace Gnoll aptitude mechanic with locked stats
authorFloodkiller <floodkiller3228@gmail.com>
Sat, 6 May 2017 12:57:39 +0000 (07:57 -0500)
committergammafunk <gammafunk@gmail.com>
Sun, 16 Jul 2017 21:36:35 +0000 (16:36 -0500)
This commit removes the Gnoll aptitude system in order to replace it with
a different main feature: locked, low stats.

This version of Gnoll has +3 in every aptitude at all skill levels, unlike
the previous system which reduced the effective aptitude as more skill
levels were gained.  In order to compensate, Gnolls have a base stat line
of 5 Str/5 Int/5 Dex. Additionally, these stats cannot be changed by any
source, temporary or permanent. For example, Gnolls cannot gain stats
from backgrounds, potions of might/brilliance/agility, Cheibriados, or
changing forms. Likewise, they cannot lose stats from stat drain or have
their stats shifted by Jiyva. This is an alternative approach to
encouraging adaptability and flexibility through being a generalist over a
specialist.

Balancing mechanics from previous iterations of Gnoll (like lower MR
growth) have been temporarily reverted to start balance testing from a
clean slate. They may be re-introduced at a later time if necessary or
interesting.

16 files changed:
crawl-ref/docs/crawl_manual.rst
crawl-ref/source/aptitudes.h
crawl-ref/source/dat/descript/species.txt
crawl-ref/source/god-abil.cc
crawl-ref/source/item-name.cc
crawl-ref/source/jobs.cc
crawl-ref/source/mutation.cc
crawl-ref/source/output.cc
crawl-ref/source/player-stats.cc
crawl-ref/source/religion.cc
crawl-ref/source/sacrifice-data.h
crawl-ref/source/skill-menu.cc
crawl-ref/source/skills.cc
crawl-ref/source/species-data.h
crawl-ref/source/timed-effects.cc
crawl-ref/source/transform.cc

index 48ba493..d3d7943 100644 (file)
@@ -1711,17 +1711,19 @@ Vine Stalkers
   spells' fuel with each voracious bite.
 
 Gnolls
-  Gnolls are a race of caniform humanoids hailing from the arid deserts and
-  grasslands of the east. With a variety of lurid tales told of them, others
-  avoid the Gnolls. Despite their isolation, Gnolls are unusually drawn to
-  the Dungeon.
-
-  Gnolls are quick to learn, picking up the basics of any skill within a
-  short period of time. Their attention wanes quickly, though, and they find it
-  more and more difficult to train a given skill the more practised they become
-  at it. Thus, Gnolls often prefer to dabble in a wide variety of fresh
-  and interesting skills. Their powerful noses indicate to them where treasures
-  lay hidden in the Dungeon.
+  Gnolls are a race of caniform humanoids originally hailing from the arid
+  deserts and grasslands of the east. In recent history they have become
+  unusually attracted to the Dungeon, establishing tribes around and even inside
+  of it. With a variety of lurid tales told of them, others avoid the Gnolls.
+
+  Gnolls excel at learning quickly, allowing them to master any skill in a short
+  period of time. Unfortunately, the Dungeon has had an especially adverse
+  effect on their bodies over the generations compared to other species. Their
+  physical attributes have become severly impaired and are unable to be altered
+  in any way, even through magic or the power of the gods.
+
+  On the bright side, their powerful noses have adapted to the Dungeon's scents,
+  allowing them to easily locate where treasures lay hidden.
 
 The Undead
 ========================================
index 8abb713..07a08b2 100644 (file)
@@ -1524,42 +1524,42 @@ static const species_skill_aptitude species_skill_aptitudes[] =
     APT(SP_BARACHI,             SK_EVOCATIONS,      1),
 
     // SP_GNOLL
-    APT(SP_GNOLL,           SK_FIGHTING,        0),
-    APT(SP_GNOLL,           SK_SHORT_BLADES,    0),
-    APT(SP_GNOLL,           SK_LONG_BLADES,     0),
-    APT(SP_GNOLL,           SK_AXES,            0),
-    APT(SP_GNOLL,           SK_MACES_FLAILS,    0),
-    APT(SP_GNOLL,           SK_POLEARMS,        0),
-    APT(SP_GNOLL,           SK_STAVES,          0),
-    APT(SP_GNOLL,           SK_SLINGS,          0),
-    APT(SP_GNOLL,           SK_BOWS,            0),
-    APT(SP_GNOLL,           SK_CROSSBOWS,       0),
-    APT(SP_GNOLL,           SK_THROWING,        0),
-    APT(SP_GNOLL,           SK_ARMOUR,          0),
-    APT(SP_GNOLL,           SK_DODGING,         0),
-    APT(SP_GNOLL,           SK_STEALTH,         0),
+    APT(SP_GNOLL,           SK_FIGHTING,        3),
+    APT(SP_GNOLL,           SK_SHORT_BLADES,    3),
+    APT(SP_GNOLL,           SK_LONG_BLADES,     3),
+    APT(SP_GNOLL,           SK_AXES,            3),
+    APT(SP_GNOLL,           SK_MACES_FLAILS,    3),
+    APT(SP_GNOLL,           SK_POLEARMS,        3),
+    APT(SP_GNOLL,           SK_STAVES,          3),
+    APT(SP_GNOLL,           SK_SLINGS,          3),
+    APT(SP_GNOLL,           SK_BOWS,            3),
+    APT(SP_GNOLL,           SK_CROSSBOWS,       3),
+    APT(SP_GNOLL,           SK_THROWING,        3),
+    APT(SP_GNOLL,           SK_ARMOUR,          3),
+    APT(SP_GNOLL,           SK_DODGING,         3),
+    APT(SP_GNOLL,           SK_STEALTH,         3),
 #if TAG_MAJOR_VERSION == 34
     APT(SP_GNOLL,           SK_STABBING,      UNUSABLE_SKILL),
 #endif
-    APT(SP_GNOLL,           SK_SHIELDS,         0),
+    APT(SP_GNOLL,           SK_SHIELDS,         3),
 #if TAG_MAJOR_VERSION == 34
     APT(SP_GNOLL,           SK_TRAPS,         UNUSABLE_SKILL),
 #endif
-    APT(SP_GNOLL,           SK_UNARMED_COMBAT,  0),
-    APT(SP_GNOLL,           SK_SPELLCASTING,    0),
-    APT(SP_GNOLL,           SK_CONJURATIONS,    0),
-    APT(SP_GNOLL,           SK_HEXES,           0),
-    APT(SP_GNOLL,           SK_CHARMS,          0),
-    APT(SP_GNOLL,           SK_SUMMONINGS,      0),
-    APT(SP_GNOLL,           SK_NECROMANCY,      0),
-    APT(SP_GNOLL,           SK_TRANSLOCATIONS,  0),
-    APT(SP_GNOLL,           SK_TRANSMUTATIONS,  0),
-    APT(SP_GNOLL,           SK_FIRE_MAGIC,      0),
-    APT(SP_GNOLL,           SK_ICE_MAGIC,       0),
-    APT(SP_GNOLL,           SK_AIR_MAGIC,       0),
-    APT(SP_GNOLL,           SK_EARTH_MAGIC,     0),
-    APT(SP_GNOLL,           SK_POISON_MAGIC,    0),
-    APT(SP_GNOLL,           SK_INVOCATIONS,     0),
-    APT(SP_GNOLL,           SK_EVOCATIONS,      0),
+    APT(SP_GNOLL,           SK_UNARMED_COMBAT,  3),
+    APT(SP_GNOLL,           SK_SPELLCASTING,    3),
+    APT(SP_GNOLL,           SK_CONJURATIONS,    3),
+    APT(SP_GNOLL,           SK_HEXES,           3),
+    APT(SP_GNOLL,           SK_CHARMS,          3),
+    APT(SP_GNOLL,           SK_SUMMONINGS,      3),
+    APT(SP_GNOLL,           SK_NECROMANCY,      3),
+    APT(SP_GNOLL,           SK_TRANSLOCATIONS,  3),
+    APT(SP_GNOLL,           SK_TRANSMUTATIONS,  3),
+    APT(SP_GNOLL,           SK_FIRE_MAGIC,      3),
+    APT(SP_GNOLL,           SK_ICE_MAGIC,       3),
+    APT(SP_GNOLL,           SK_AIR_MAGIC,       3),
+    APT(SP_GNOLL,           SK_EARTH_MAGIC,     3),
+    APT(SP_GNOLL,           SK_POISON_MAGIC,    3),
+    APT(SP_GNOLL,           SK_INVOCATIONS,     3),
+    APT(SP_GNOLL,           SK_EVOCATIONS,      3),
 };
 COMPILE_CHECK(ARRAYSZ(species_skill_aptitudes) == NUM_SPECIES * NUM_SKILLS);
index de99679..3f18511 100644 (file)
@@ -55,9 +55,8 @@ They have low health, but their stone bodies are incredibly tough.
 %%%%
 Gnoll
 
-Gnolls are canine-like humanoids. They are quick to learn the basics of any
-skill, but struggle to master any. Their strong noses can sniff out treasure
-from afar.
+Gnolls are canine-like humanoids. They are quick to learn any skill, but are
+unable to alter their poor physical attributes.
 %%%%
 Halfling
 
index da89b06..0bbb1c4 100644 (file)
@@ -5136,6 +5136,11 @@ static mutation_type _random_valid_sacrifice(const vector<mutation_type> &muts)
             continue;
         }
 
+        // Gnolls can't get stat-changing mutations
+        if ((mut == MUT_DOPEY || mut == MUT_WEAK || mut == MUT_CLUMSY)
+            && you.species == SP_GNOLL)
+            continue;
+
         // The Grunt Algorithm
         // (choose a random element from a set of unknown size without building
         // an explicit list, by giving each one a chance to be chosen equal to
index 54a1606..1662673 100644 (file)
@@ -3669,6 +3669,12 @@ bool is_useless_item(const item_def &item, bool temp)
         case RING_STEALTH:
             return you.get_mutation_level(MUT_NO_STEALTH);
 
+        // Gnolls can't boost stats
+        case RING_STRENGTH:
+        case RING_INTELLIGENCE:
+        case RING_DEXTERITY:
+            return you.species == SP_GNOLL;
+
         default:
             return false;
         }
index 9eb626a..19aa10a 100644 (file)
@@ -70,23 +70,27 @@ void job_stat_init(job_type job)
 {
     you.hp_max_adj_perm = 0;
 
-    you.base_stats[STAT_STR] += _job_def(job).s;
-    you.base_stats[STAT_INT] += _job_def(job).i;
-    you.base_stats[STAT_DEX] += _job_def(job).d;
-
-    if (job == JOB_WANDERER)
+    // Gnolls don't get stats from background
+    if(!(you.species == SP_GNOLL))
     {
-        for (int i = 0; i < 12; i++)
+        you.base_stats[STAT_STR] += _job_def(job).s;
+        you.base_stats[STAT_INT] += _job_def(job).i;
+        you.base_stats[STAT_DEX] += _job_def(job).d;
+
+        if (job == JOB_WANDERER)
         {
-            const stat_type stat = static_cast<stat_type>(random2(NUM_STATS));
-            // Stats that are already high will be chosen half as often.
-            if (you.base_stats[stat] > 17 && coinflip())
+            for (int i = 0; i < 12; i++)
             {
-                i--;
-                continue;
+                const stat_type stat = static_cast<stat_type>(random2(NUM_STATS));
+                // Stats that are already high will be chosen half as often.
+                if (you.base_stats[stat] > 17 && coinflip())
+                {
+                    i--;
+                    continue;
+                }
+
+                you.base_stats[stat]++;
             }
-
-            you.base_stats[stat]++;
         }
     }
 }
index 135dd47..184dfb6 100644 (file)
@@ -1252,6 +1252,23 @@ bool physiology_mutation_conflict(mutation_type mutat)
     if (you.species == SP_GARGOYLE && mutat == MUT_POISON_RESISTANCE)
         return true;
 
+    // Gnolls can't get any stat-affecting mutations
+    if (you.species == SP_GNOLL)
+    {
+        if (mutat == MUT_STRONG
+            || mutat == MUT_CLEVER
+            || mutat == MUT_AGILE
+            || mutat == MUT_WEAK
+            || mutat == MUT_DOPEY
+            || mutat == MUT_CLUMSY
+            || mutat == MUT_THIN_SKELETAL_STRUCTURE
+            || mutat == MUT_ROUGH_BLACK_SCALES
+            || mutat == MUT_DETERIORATION)
+        {
+            return true;
+        }
+    }
+
     // We can't use is_useless_skill() here, since species that can still wear
     // body armour can sacrifice armour skill with Ru.
     if (species_apt(SK_ARMOUR) == UNUSABLE_SKILL
index e4b911c..fac9261 100644 (file)
@@ -797,6 +797,10 @@ static void _print_stats_hp(int x, int y)
 
 static short _get_stat_colour(stat_type stat)
 {
+    // If player is Gnoll, stats don't change; always return HUD_VALUE_COLOUR
+    if (you.species == SP_GNOLL)
+        return HUD_VALUE_COLOUR;
+
     if (you.duration[stat_zero_duration(stat)])
         return LIGHTRED;
 
index 402ce1f..6c3148b 100644 (file)
@@ -104,99 +104,107 @@ static void _handle_stat_change(stat_type stat);
  */
 bool attribute_increase()
 {
-    const string stat_gain_message = make_stringf("Your experience leads to a%s "
-                                                  "increase in your attributes!",
-                                                  you.species == SP_DEMIGOD ?
-                                                  " dramatic" : "n");
-    crawl_state.stat_gain_prompt = true;
-#ifdef TOUCH_UI
-    learned_something_new(HINT_CHOOSE_STAT);
-    Popup *pop = new Popup("Increase Attributes");
-    MenuEntry *status = new MenuEntry("", MEL_SUBTITLE);
-    pop->push_entry(new MenuEntry(stat_gain_message + " Increase:", MEL_TITLE));
-    pop->push_entry(status);
-    MenuEntry *me = new MenuEntry("Strength", MEL_ITEM, 0, 'S', false);
-    me->add_tile(tile_def(TILEG_FIGHTING_ON, TEX_GUI));
-    pop->push_entry(me);
-    me = new MenuEntry("Intelligence", MEL_ITEM, 0, 'I', false);
-    me->add_tile(tile_def(TILEG_SPELLCASTING_ON, TEX_GUI));
-    pop->push_entry(me);
-    me = new MenuEntry("Dexterity", MEL_ITEM, 0, 'D', false);
-    me->add_tile(tile_def(TILEG_DODGING_ON, TEX_GUI));
-    pop->push_entry(me);
-#else
-    mprf(MSGCH_INTRINSIC_GAIN, "%s", stat_gain_message.c_str());
-    learned_something_new(HINT_CHOOSE_STAT);
-    if (innate_stat(STAT_STR) != you.strength()
-        || innate_stat(STAT_INT) != you.intel()
-        || innate_stat(STAT_DEX) != you.dex())
+    // Gnolls don't get stat gains
+    if (you.species == SP_GNOLL)
     {
-        mprf(MSGCH_PROMPT, "Your base attributes are Str %d, Int %d, Dex %d.",
-             innate_stat(STAT_STR),
-             innate_stat(STAT_INT),
-             innate_stat(STAT_DEX));
+        return true;
     }
-    mprf(MSGCH_PROMPT, "Increase (S)trength, (I)ntelligence, or (D)exterity? ");
-#endif
-    mouse_control mc(MOUSE_MODE_PROMPT);
-
-    const int statgain = you.species == SP_DEMIGOD ? 2 : 1;
-
-    bool tried_lua = false;
-    int keyin;
-    while (true)
+    else
     {
-        // Calling a user-defined lua function here to let players reply to
-        // the prompt automatically. Either returning a string or using
-        // crawl.sendkeys will work.
-        if (!tried_lua && clua.callfn("choose_stat_gain", 0, 1))
+        const string stat_gain_message = make_stringf("Your experience leads to a%s "
+                                                      "increase in your attributes!",
+                                                      you.species == SP_DEMIGOD ?
+                                                      " dramatic" : "n");
+        crawl_state.stat_gain_prompt = true;
+#ifdef TOUCH_UI
+        learned_something_new(HINT_CHOOSE_STAT);
+        Popup *pop = new Popup("Increase Attributes");
+        MenuEntry *status = new MenuEntry("", MEL_SUBTITLE);
+        pop->push_entry(new MenuEntry(stat_gain_message + " Increase:", MEL_TITLE));
+        pop->push_entry(status);
+        MenuEntry *me = new MenuEntry("Strength", MEL_ITEM, 0, 'S', false);
+        me->add_tile(tile_def(TILEG_FIGHTING_ON, TEX_GUI));
+        pop->push_entry(me);
+        me = new MenuEntry("Intelligence", MEL_ITEM, 0, 'I', false);
+        me->add_tile(tile_def(TILEG_SPELLCASTING_ON, TEX_GUI));
+        pop->push_entry(me);
+        me = new MenuEntry("Dexterity", MEL_ITEM, 0, 'D', false);
+        me->add_tile(tile_def(TILEG_DODGING_ON, TEX_GUI));
+        pop->push_entry(me);
+#else
+        mprf(MSGCH_INTRINSIC_GAIN, "%s", stat_gain_message.c_str());
+        learned_something_new(HINT_CHOOSE_STAT);
+        if (innate_stat(STAT_STR) != you.strength()
+            || innate_stat(STAT_INT) != you.intel()
+            || innate_stat(STAT_DEX) != you.dex())
         {
-            string result;
-            clua.fnreturns(">s", &result);
-            keyin = result[0];
+            mprf(MSGCH_PROMPT, "Your base attributes are Str %d, Int %d, Dex %d.",
+                 innate_stat(STAT_STR),
+                 innate_stat(STAT_INT),
+                 innate_stat(STAT_DEX));
         }
-        else
+        mprf(MSGCH_PROMPT, "Increase (S)trength, (I)ntelligence, or (D)exterity? ");
+#endif
+        mouse_control mc(MOUSE_MODE_PROMPT);
+
+        const int statgain = you.species == SP_DEMIGOD ? 2 : 1;
+
+        bool tried_lua = false;
+        int keyin;
+        while (true)
         {
+            // Calling a user-defined lua function here to let players reply to
+            // the prompt automatically. Either returning a string or using
+            // crawl.sendkeys will work.
+            if (!tried_lua && clua.callfn("choose_stat_gain", 0, 1))
+            {
+                string result;
+                clua.fnreturns(">s", &result);
+                keyin = result[0];
+            }
+            else
+            {
 #ifdef TOUCH_UI
-            keyin = pop->pop();
+                keyin = pop->pop();
 #else
-            keyin = getchm();
+                keyin = getchm();
 #endif
-        }
-        tried_lua = true;
-
-        switch (keyin)
-        {
-        CASE_ESCAPE
-            // It is unsafe to save the game here; continue with the turn
-            // normally, when the player reloads, the game will re-prompt
-            // for their level-up stat gain.
-            if (crawl_state.seen_hups)
-                return false;
-            break;
-
-        case 's':
-        case 'S':
-            for (int i = 0; i < statgain; i++)
-                modify_stat(STAT_STR, 1, false);
-            return true;
-
-        case 'i':
-        case 'I':
-            for (int i = 0; i < statgain; i++)
-                modify_stat(STAT_INT, 1, false);
-            return true;
+            }
+            tried_lua = true;
 
-        case 'd':
-        case 'D':
-            for (int i = 0; i < statgain; i++)
-                modify_stat(STAT_DEX, 1, false);
-            return true;
+            switch (keyin)
+            {
+            CASE_ESCAPE
+                // It is unsafe to save the game here; continue with the turn
+                // normally, when the player reloads, the game will re-prompt
+                // for their level-up stat gain.
+                if (crawl_state.seen_hups)
+                    return false;
+                break;
+
+            case 's':
+            case 'S':
+                for (int i = 0; i < statgain; i++)
+                    modify_stat(STAT_STR, 1, false);
+                return true;
+
+            case 'i':
+            case 'I':
+                for (int i = 0; i < statgain; i++)
+                    modify_stat(STAT_INT, 1, false);
+                return true;
+
+            case 'd':
+            case 'D':
+                for (int i = 0; i < statgain; i++)
+                    modify_stat(STAT_DEX, 1, false);
+                return true;
 #ifdef TOUCH_UI
-        default:
-            status->text = "Please choose an option below"; // too naggy?
+            default:
+                status->text = "Please choose an option below"; // too naggy?
 #endif
-        }
+            }
+        } 
     }
 }
 
@@ -339,6 +347,10 @@ void notify_stat_change(stat_type which_stat, int amount, bool suppress_msg)
     if (amount == 0)
         return;
 
+    // Gnolls don't change stats, so don't notify
+    if(you.species == SP_GNOLL)
+        return;
+
     // Stop delays if a stat drops.
     if (amount < 0)
         interrupt_activity(AI_STAT_CHANGE);
@@ -371,36 +383,40 @@ static int _strength_modifier(bool innate_only)
 {
     int result = 0;
 
-    if (!innate_only)
+    // Gnolls can't modify their stats
+    if (you.species != SP_GNOLL)
     {
-        if (you.duration[DUR_MIGHT] || you.duration[DUR_BERSERK])
-            result += 5;
+        if (!innate_only)
+        {
+            if (you.duration[DUR_MIGHT] || you.duration[DUR_BERSERK])
+                result += 5;
 
-        if (you.duration[DUR_DIVINE_STAMINA])
-            result += you.attribute[ATTR_DIVINE_STAMINA];
+            if (you.duration[DUR_DIVINE_STAMINA])
+                result += you.attribute[ATTR_DIVINE_STAMINA];
 
-        result += chei_stat_boost();
+            result += chei_stat_boost();
 
-        // ego items of strength
-        result += 3 * count_worn_ego(SPARM_STRENGTH);
+            // ego items of strength
+            result += 3 * count_worn_ego(SPARM_STRENGTH);
 
-        // rings of strength
-        result += you.wearing(EQ_RINGS_PLUS, RING_STRENGTH);
+            // rings of strength
+            result += you.wearing(EQ_RINGS_PLUS, RING_STRENGTH);
 
-        // randarts of strength
-        result += you.scan_artefacts(ARTP_STRENGTH);
+            // randarts of strength
+            result += you.scan_artefacts(ARTP_STRENGTH);
 
-        // form
-        result += get_form()->str_mod;
-    }
+            // form
+            result += get_form()->str_mod;
+        }
 
-    // mutations
-    result += 2 * (_mut_level(MUT_STRONG, innate_only)
-                   - _mut_level(MUT_WEAK, innate_only));
+        // mutations
+        result += 2 * (_mut_level(MUT_STRONG, innate_only)
+                       - _mut_level(MUT_WEAK, innate_only));
 #if TAG_MAJOR_VERSION == 34
-    result += _mut_level(MUT_STRONG_STIFF, innate_only)
-              - _mut_level(MUT_FLEXIBLE_WEAK, innate_only);
+        result += _mut_level(MUT_STRONG_STIFF, innate_only)
+                  - _mut_level(MUT_FLEXIBLE_WEAK, innate_only);
 #endif
+    }
 
     return result;
 }
@@ -409,29 +425,33 @@ static int _int_modifier(bool innate_only)
 {
     int result = 0;
 
-    if (!innate_only)
+    // Gnolls can't modify their stats
+    if (you.species != SP_GNOLL)
     {
-        if (you.duration[DUR_BRILLIANCE])
-            result += 5;
+        if (!innate_only)
+        {
+            if (you.duration[DUR_BRILLIANCE])
+                result += 5;
 
-        if (you.duration[DUR_DIVINE_STAMINA])
-            result += you.attribute[ATTR_DIVINE_STAMINA];
+            if (you.duration[DUR_DIVINE_STAMINA])
+                result += you.attribute[ATTR_DIVINE_STAMINA];
 
-        result += chei_stat_boost();
+            result += chei_stat_boost();
 
-        // ego items of intelligence
-        result += 3 * count_worn_ego(SPARM_INTELLIGENCE);
+            // ego items of intelligence
+            result += 3 * count_worn_ego(SPARM_INTELLIGENCE);
 
-        // rings of intelligence
-        result += you.wearing(EQ_RINGS_PLUS, RING_INTELLIGENCE);
+            // rings of intelligence
+            result += you.wearing(EQ_RINGS_PLUS, RING_INTELLIGENCE);
 
-        // randarts of intelligence
-        result += you.scan_artefacts(ARTP_INTELLIGENCE);
-    }
+            // randarts of intelligence
+            result += you.scan_artefacts(ARTP_INTELLIGENCE);
+        }
 
-    // mutations
-    result += 2 * (_mut_level(MUT_CLEVER, innate_only)
-                   - _mut_level(MUT_DOPEY, innate_only));
+        // mutations
+        result += 2 * (_mut_level(MUT_CLEVER, innate_only)
+                       - _mut_level(MUT_DOPEY, innate_only));
+    }
 
     return result;
 }
@@ -440,38 +460,42 @@ static int _dex_modifier(bool innate_only)
 {
     int result = 0;
 
-    if (!innate_only)
+    // Gnolls can't modify their stats
+    if (you.species != SP_GNOLL)
     {
-        if (you.duration[DUR_AGILITY])
-            result += 5;
+        if (!innate_only)
+        {
+            if (you.duration[DUR_AGILITY])
+                result += 5;
 
-        if (you.duration[DUR_DIVINE_STAMINA])
-            result += you.attribute[ATTR_DIVINE_STAMINA];
+            if (you.duration[DUR_DIVINE_STAMINA])
+                result += you.attribute[ATTR_DIVINE_STAMINA];
 
-        result += chei_stat_boost();
+            result += chei_stat_boost();
 
-        // ego items of dexterity
-        result += 3 * count_worn_ego(SPARM_DEXTERITY);
+            // ego items of dexterity
+            result += 3 * count_worn_ego(SPARM_DEXTERITY);
 
-        // rings of dexterity
-        result += you.wearing(EQ_RINGS_PLUS, RING_DEXTERITY);
+            // rings of dexterity
+            result += you.wearing(EQ_RINGS_PLUS, RING_DEXTERITY);
 
-        // randarts of dexterity
-        result += you.scan_artefacts(ARTP_DEXTERITY);
+            // randarts of dexterity
+            result += you.scan_artefacts(ARTP_DEXTERITY);
 
-        // form
-        result += get_form()->dex_mod;
-    }
+            // form
+            result += get_form()->dex_mod;
+        }
 
-    // mutations
-    result += 2 * (_mut_level(MUT_AGILE, innate_only)
-                  - _mut_level(MUT_CLUMSY, innate_only));
+        // mutations
+        result += 2 * (_mut_level(MUT_AGILE, innate_only)
+                      - _mut_level(MUT_CLUMSY, innate_only));
 #if TAG_MAJOR_VERSION == 34
-    result += _mut_level(MUT_FLEXIBLE_WEAK, innate_only)
-              - _mut_level(MUT_STRONG_STIFF, innate_only);
-    result -= _mut_level(MUT_ROUGH_BLACK_SCALES, innate_only);
+        result += _mut_level(MUT_FLEXIBLE_WEAK, innate_only)
+                  - _mut_level(MUT_STRONG_STIFF, innate_only);
+        result -= _mut_level(MUT_ROUGH_BLACK_SCALES, innate_only);
 #endif
-    result += 2 * _mut_level(MUT_THIN_SKELETAL_STRUCTURE, innate_only);
+        result += 2 * _mut_level(MUT_THIN_SKELETAL_STRUCTURE, innate_only);
+    }
 
     return result;
 }
@@ -514,6 +538,10 @@ int stat_loss_roll()
 
 bool lose_stat(stat_type which_stat, int stat_loss, bool force)
 {
+    // Gnolls cannot be stat drained
+    if (you.species == SP_GNOLL)
+        return false;
+
     if (stat_loss <= 0)
         return false;
 
index 2bf6abb..ff1ec8b 100644 (file)
@@ -2397,7 +2397,8 @@ static void _gain_piety_point()
         update_player_symbol();
 
     if (have_passive(passive_t::stat_boost)
-        && chei_stat_boost(old_piety) < chei_stat_boost())
+        && chei_stat_boost(old_piety) < chei_stat_boost()
+        && you.species != SP_GNOLL)
     {
         string msg = " raises the support of your attributes";
         if (have_passive(passive_t::slowed))
@@ -3528,15 +3529,23 @@ static void _join_pakellas()
     you.attribute[ATTR_PAKELLAS_EXTRA_MP] = POT_MAGIC_MP;
 }
 
+// Setup for joining the easygoing followers of Cheibriados.
+static void _join_cheibriados()
+{
+    // Gnolls don't gain attribute bonuses under Chei
+    if (you.species != SP_GNOLL)
+    {
+        simple_god_message(" begins to support your attributes as your "
+                           "movement slows.");
+        notify_stat_change();
+    }
+}
+
 /// What special things happen when you join a god?
 static const map<god_type, function<void ()>> on_join = {
     { GOD_ASHENZARI, []() { ash_check_bondage(); }},
     { GOD_BEOGH, update_player_symbol },
-    { GOD_CHEIBRIADOS, []() {
-        simple_god_message(" begins to support your attributes as your "
-                           "movement slows.");
-        notify_stat_change();
-    }},
+    { GOD_CHEIBRIADOS, _join_cheibriados },
     { GOD_FEDHAS, []() {
         mprf(MSGCH_MONSTER_ENCHANT, "The plants of the dungeon cease their "
              "hostilities.");
index e3a587c..3ff6904 100644 (file)
@@ -172,7 +172,7 @@ static const sacrifice_def sac_data[] =
   40,
   SK_NONE,
   nullptr,
-  []() { return you.species != SP_GNOLL; },
+  nullptr,
 },
 
 { ABIL_RU_SACRIFICE_EYE, MUT_MISSING_EYE,
index 30453dc..77cb8f0 100644 (file)
@@ -348,49 +348,18 @@ void SkillMenuEntry::set_aptitude()
 
     text += "</white> ";
 
-    if (manual_bonus || you.species == SP_GNOLL)
+    if (manual_bonus)
     {
-        if (manual_bonus)
-        {
-            skm.set_flag(SKMF_MANUAL);
-            text += "<lightgreen>";
-        }
-
-        int gnoll_bonus = 0;
-
-        // Determine Gnoll aptitude bonus/malus to display if SP_GNOLL
-        if (you.species == SP_GNOLL)
-        {
-            int gnoll_skill = you.skill(m_sk, 1, true);
-
-            // Gnoll aptitude costs start at effective +5 and reduce by -2 for
-            // each level after reaching 6, floor of -9
-            gnoll_bonus = max(5 - max((gnoll_skill - 5) * 2, 0), -9);
-        }
-        manual_bonus = manual_bonus + gnoll_bonus;
+        skm.set_flag(SKMF_MANUAL);
+        text += "<lightgreen>";
 
         // Only room for two characters.
-        if (manual_bonus != 0)
-        {
-            // Add before based on positive or negative bonus (can be different
-            // from bonus being > 10)
-            if (manual_bonus > 0)
-                text += "<lightgreen>";
-            else
-                text += "<red>";
-
-            // Add '+' if bonus is positive and below 10, otherwise don't
-            if (manual_bonus < 10 && manual_bonus > 0)
-                text += make_stringf("+%d", manual_bonus);
-            else
-                text += to_string(manual_bonus);
+        if (manual_bonus < 10)
+            text += make_stringf("+%d", manual_bonus);
+        else
+            text += to_string(manual_bonus);
 
-            // Close color tags based on same rules as above
-            if (manual_bonus > 0)
-                text += "</lightgreen>";
-            else
-                text += "</red>";
-        }
+        text += "</lightgreen>";
     }
 
     m_aptitude->set_text(text);
index a28dfc8..05869f3 100644 (file)
@@ -290,19 +290,6 @@ static void _change_skill_level(skill_type exsk, int n)
              specify_base ? "base " : "",
              skill_name(exsk), (n > 0) ? "increases" : "decreases",
              you.skills[exsk]);
-
-        // Send a status message about 'aptitude' increases/decreases if Gnoll
-        if (you.species == SP_GNOLL)
-        {
-            if (n > 0 && you.skills[exsk] < 13 && you.skills[exsk] > 5)
-            {
-                mprf(MSGCH_MUTATION, "You become less interested in %s.",
-                     skill_name(exsk));
-            }
-            else if (n < 0 && you.skills[exsk] < 12 && you.skills[exsk] > 4)
-                mprf(MSGCH_MUTATION, "You become more interested in %s.",
-                     skill_name(exsk));
-        }
     }
     else if (you.num_turns)
     {
@@ -312,19 +299,6 @@ static void _change_skill_level(skill_type exsk, int n)
              skill_name(exsk),
              (n > 0) ? "gained" : "lost",
              abs(n), you.skills[exsk]);
-
-        // Send a status message about 'aptitude' increases/decreases if Gnoll
-        if (you.species == SP_GNOLL)
-        {
-            if (n > 0 && (you.skills[exsk] - n) < 13 && you.skills[exsk] > 5)
-            {
-                mprf(MSGCH_MUTATION, "You become less interested in %s.",
-                     skill_name(exsk));
-            }
-            else if (n < 0 && you.skills[exsk] < 12 && (you.skills[exsk] - n) > 4)
-                mprf(MSGCH_MUTATION, "You become more interested in %s.",
-                     skill_name(exsk));
-        }
     }
 
     if (you.skills[exsk] == n && n > 0)
@@ -1522,13 +1496,7 @@ float apt_to_factor(int apt)
 
 unsigned int skill_exp_needed(int lev, skill_type sk, species_type sp)
 {
-    ASSERT_RANGE(lev, 0, MAX_SKILL_LEVEL + 1);
-
-    // Choose between normal exp table and Gnoll exp table
-    // Regular XP table:
-    if (!(sp == SP_GNOLL))
-    {
-        const int exp[28] =
+    const int exp[28] =
           { 0, 50, 150, 300, 500, 750,          // 0-5
             1050, 1400, 1800, 2250, 2800,       // 6-10
             3450, 4200, 5050, 6000, 7050,       // 11-15
@@ -1536,35 +1504,8 @@ unsigned int skill_exp_needed(int lev, skill_type sk, species_type sp)
             15750, 17700, 19800, 22050, 24450,  // 21-25
             27000, 29750 };
 
-            return exp[lev] * species_apt_factor(sk, sp);
-    }
-    // This is a custom XP table for Gnolls, precalculated to match their
-    // dynamic aptitudes. Until SL 6, Gnolls have an effective aptitude of
-    // +5 (half as much XP needed). At 6 and after, their aptitude decreases by
-    // two for each skill level, reaching a floor of -9 at skill level 13. XP
-    // values are calculated accordingly, using the original XP table (above).
-    // At SL 6, standard +3 aptitude requires 208 XP to go from 6 to 7; thus
-    // Gnolls get their 7th level in any given skill by going from 441 total
-    // XP to 649. This pattern continues. -9 aptitude at SL 14 requires 4995 XP,
-    // so Gnolls go from 15076 total XP at SL 14 to 20071 at 15.
-    //
-    // There are ways to do this programmatically (such as to call this function
-    // inside of itself at runtime to dynamically calculate the needed XP), but
-    // they either cause strange interactions with wizard mode, or take up more
-    // resources than this implementation.
-    else
-    {
-        const int gnoll_exp[28] =
-          { 0, 21, 63, 126, 210, 315,           // 0-5
-            441, 649, 985, 1520, 2445,          // 6-10
-            3991, 6514, 10557, 15076, 20071,     // 11-15
-            25541, 31448, 37909, 45044, 52893,  // 16-20
-            61456, 70731, 80721, 91424, 102840,  // 21-25
-            114970, 128051 };
-
-        //Species_apt_factor is unnecessary for Gnoll
-        return gnoll_exp[lev];
-    }
+    ASSERT_RANGE(lev, 0, MAX_SKILL_LEVEL + 1);
+    return exp[lev] * species_apt_factor(sk, sp);
 }
 
 int species_apt(skill_type skill, species_type species)
index 28e3287..1cc6da2 100644 (file)
@@ -66,17 +66,17 @@ static const map<species_type, species_def> species_data =
     "Gn",
     "Gnoll", nullptr, nullptr,
     SPF_NONE,
-    0, 0, 0, 2,
+    0, 0, 0, 3,
     MONS_GNOLL,
     HT_LAND, US_ALIVE, SIZE_MEDIUM,
-    7, 8, 9, // 24
-    { STAT_STR, STAT_INT, STAT_DEX }, 5,
+    5, 5, 5, // 15
+    { STAT_STR }, 28,  // No natural stat gain, STR is a placeholder
     { { MUT_STRONG_NOSE, 1, 1 },  { MUT_FANGS, 1, 1 }, },
-    {"You have a short attention span."},
-    {"short attention span"},
+    { "Your physical attributes are unchanging."},
+    { "unchanging attributes" },
     { JOB_SKALD, JOB_WARPER, JOB_ARCANE_MARKSMAN, JOB_TRANSMUTER,
       JOB_WANDERER },
-    { SK_SHORT_BLADES, SK_MACES_FLAILS, SK_POLEARMS, SK_LONG_BLADES, SK_STAVES,
+    { SK_MACES_FLAILS, SK_AXES, SK_POLEARMS, SK_LONG_BLADES, SK_STAVES,
       SK_BOWS, SK_CROSSBOWS, SK_SLINGS },
 } },
 
index ad905f0..8c80291 100644 (file)
@@ -868,9 +868,11 @@ static void _jiyva_effects(int /*time_delta*/)
         }
     }
 
+    // Gnolls can't shift stats
     if (have_passive(passive_t::fluid_stats)
         && x_chance_in_y(you.piety / 4, MAX_PIETY)
-        && !player_under_penance() && one_chance_in(4))
+        && !player_under_penance() && one_chance_in(4)
+        && you.species != SP_GNOLL)
     {
         jiyva_stat_action();
     }
index ae7a9ee..68ccda1 100644 (file)
@@ -1456,6 +1456,10 @@ static bool _transformation_is_safe(transformation which_trans,
  */
 bool check_form_stat_safety(transformation new_form)
 {
+    // Gnolls don't have to worry about forms changing stats
+    if (you.species == SP_GNOLL)
+        return true;
+
     const int str_mod = get_form(new_form)->str_mod - get_form()->str_mod;
     const int dex_mod = get_form(new_form)->dex_mod - get_form()->dex_mod;