Gnolls Redux Part I: Every skill is always trained (##crawl/not mikee)
authorgammafunk <gammafunk@gmail.com>
Tue, 26 Sep 2017 19:06:01 +0000 (14:06 -0500)
committergammafunk <gammafunk@gmail.com>
Thu, 28 Sep 2017 06:29:51 +0000 (01:29 -0500)
The current "stat-locked" version of Gnolls tries harder to push the
player to not specialize in any one thing without relying on the
aptitude change breakpoints used by the initial version of the species.
But heavy armor, shields, ranged weapons, and evocations all work well
enough with low stats, and melee works well enough when paired with
these. On the magic side of things, necromancy and summoning spells work
far better with low Int compared to direct damage spell schools. So
Gnolls were still able to specialize in certain schools but additionally
had a low-stat conduct that players didn't enjoy.

The rework started in this commit will have Gnolls become a
"reverse-skillrobin" species with very high apts and high stats, but
with a drawback where they always train every type of skill at the same
time with no ability to disable or focus specific skills. All skills are
enabled for training at the same XP percentage at game start, ignoring
any training requirements of equipment or spells normally applied to
other species. Backgrounds still apply their starting XP into the
specific relevant schools, but all further XP gained after the game
begins applies to every school.

A subsequent commit will adjust Gnoll aptitudes to much higher than the
current +3, raise their stats, and remove stat-lock. Even with this
aptitude increase, skill levels will raise slowly, but all schools will
be available to use in terms of training. It's closer in spirit to the
original Gnoll design, but even more explicitly rules out skill
specialization and does this without needing aptitude breakpoints.

This commit disables all menu actions and lua functions to select or
focus skills as a Gnoll and has them always train all skills.
Experience potions apply the experience to all skills and no longer
present a special skill menu to the player.

Future commits will disable cross-training, handle god-related
limitations, and implement save compatibility.

crawl-ref/source/l-you.cc
crawl-ref/source/player.cc
crawl-ref/source/potion.cc
crawl-ref/source/skill-menu.cc
crawl-ref/source/skills.cc
crawl-ref/source/skills.h
crawl-ref/source/species-data.h

index 8f71b53..dcbd651 100644 (file)
@@ -503,7 +503,7 @@ LUAFN(you_base_skill)
 LUAFN(you_train_skill)
 {
     skill_type sk = str_to_skill(luaL_checkstring(ls, 1));
-    if (lua_gettop(ls) >= 2 && you.can_train[sk])
+    if (lua_gettop(ls) >= 2 && can_enable_skill(sk))
     {
         you.train[sk] = min(max((training_status)luaL_checkint(ls, 2),
                                                  TRAINING_DISABLED),
index 9dc9e3e..4139e73 100644 (file)
@@ -2522,15 +2522,6 @@ static void _handle_temp_mutation(int exp)
 /// update stat loss
 static void _handle_stat_loss(int exp)
 {
-#if TAG_MAJOR_VERSION == 34
-    // Gnolls that transfered from before their stats became locked may have
-    // ATTR_STAT_LOSS_XP, but we ignore it.
-    if (you.species == SP_GNOLL && you.attribute[ATTR_STAT_LOSS_XP] > 0)
-    {
-        you.attribute[ATTR_STAT_LOSS_XP] = 0;
-        return;
-    }
-#endif
     if (!(you.attribute[ATTR_STAT_LOSS_XP] > 0))
         return;
 
index 4100b56..41d4484 100644 (file)
@@ -587,8 +587,16 @@ public:
         }
         else
             mpr("A flood of memories washes over you.");
+
         // these are included in default force_more_message
-        skill_menu(SKMF_EXPERIENCE, 750 * you.experience_level);
+        const int exp = 750 * you.experience_level;
+        if (you.species == SP_GNOLL)
+        {
+            you.exp_available += exp;
+            train_skills();
+        }
+        else
+            skill_menu(SKMF_EXPERIENCE, exp);
         return true;
     }
 };
index 0c61dbe..cb8df53 100644 (file)
@@ -115,6 +115,9 @@ bool SkillMenuEntry::is_selectable(bool keep_hotkey)
     if (is_invalid_skill(m_sk))
         return false;
 
+    if (you.species == SP_GNOLL)
+        return false;
+
     if (!_show_skill(m_sk, skm.get_state(SKM_SHOW)))
         return false;
 
@@ -961,7 +964,20 @@ skill_menu_state SkillMenu::get_state(skill_menu_switch sw)
         }
     }
     else if (!m_switches[sw])
-        return SKM_NONE;
+    {
+        if (you.species == SP_GNOLL)
+        {
+            switch (sw)
+            {
+            case SKM_MODE:  return SKM_MODE_MANUAL;
+            case SKM_DO:    return SKM_DO_FOCUS;
+            case SKM_SHOW:  return SKM_SHOW_ALL;
+            default:        return SKM_NONE;
+            }
+        }
+        else
+            return SKM_NONE;
+    }
     else
         return m_switches[sw]->get_state();
 }
@@ -1133,47 +1149,50 @@ void SkillMenu::init_help()
 void SkillMenu::init_switches()
 {
     SkillMenuSwitch* sw;
-    sw = new SkillMenuSwitch("mode", '/');
-    m_switches[SKM_MODE] = sw;
-    sw->add(SKM_MODE_AUTO);
-    if (!is_set(SKMF_SPECIAL) && !is_set(SKMF_SIMPLE))
-        sw->add(SKM_MODE_MANUAL);
-    if (!you.auto_training)
-        sw->set_state(SKM_MODE_MANUAL);
-    sw->update();
-    sw->set_id(SKM_MODE);
-    add_item(sw, sw->size(), m_pos);
-
-    sw = new SkillMenuSwitch("skill", '|');
-    m_switches[SKM_DO] = sw;
-    if (!is_set(SKMF_EXPERIENCE)
-        && (is_set(SKMF_SIMPLE) || Options.skill_focus != SKM_FOCUS_ON))
-    {
-        sw->add(SKM_DO_PRACTISE);
-    }
-    if (!is_set(SKMF_RESKILLING) && !is_set(SKMF_SIMPLE)
-        && Options.skill_focus != SKM_FOCUS_OFF)
+    if (you.species != SP_GNOLL)
     {
-        sw->add(SKM_DO_FOCUS);
-    }
-    sw->set_state(you.skill_menu_do);
-    sw->add_hotkey('\t');
-    sw->update();
-    sw->set_id(SKM_DO);
-    add_item(sw, sw->size(), m_pos);
+        sw = new SkillMenuSwitch("mode", '/');
+        m_switches[SKM_MODE] = sw;
+        sw->add(SKM_MODE_AUTO);
+        if (!is_set(SKMF_SPECIAL) && !is_set(SKMF_SIMPLE))
+            sw->add(SKM_MODE_MANUAL);
+        if (!you.auto_training)
+            sw->set_state(SKM_MODE_MANUAL);
+        sw->update();
+        sw->set_id(SKM_MODE);
+        add_item(sw, sw->size(), m_pos);
 
-    sw = new SkillMenuSwitch("skills", '*');
-    m_switches[SKM_SHOW] = sw;
-    sw->add(SKM_SHOW_DEFAULT);
-    if (!is_set(SKMF_SIMPLE) && !is_set(SKMF_EXPERIENCE))
-    {
-        sw->add(SKM_SHOW_ALL);
-        if (Options.default_show_all_skills)
-            sw->set_state(SKM_SHOW_ALL);
+        sw = new SkillMenuSwitch("skill", '|');
+        m_switches[SKM_DO] = sw;
+        if (!is_set(SKMF_EXPERIENCE)
+            && (is_set(SKMF_SIMPLE) || Options.skill_focus != SKM_FOCUS_ON))
+        {
+            sw->add(SKM_DO_PRACTISE);
+        }
+        if (!is_set(SKMF_RESKILLING) && !is_set(SKMF_SIMPLE)
+            && Options.skill_focus != SKM_FOCUS_OFF)
+        {
+            sw->add(SKM_DO_FOCUS);
+        }
+        sw->set_state(you.skill_menu_do);
+        sw->add_hotkey('\t');
+        sw->update();
+        sw->set_id(SKM_DO);
+        add_item(sw, sw->size(), m_pos);
+
+        sw = new SkillMenuSwitch("skills", '*');
+        m_switches[SKM_SHOW] = sw;
+        sw->add(SKM_SHOW_DEFAULT);
+        if (!is_set(SKMF_SIMPLE) && !is_set(SKMF_EXPERIENCE))
+        {
+            sw->add(SKM_SHOW_ALL);
+            if (Options.default_show_all_skills)
+                sw->set_state(SKM_SHOW_ALL);
+        }
+        sw->update();
+        sw->set_id(SKM_SHOW);
+        add_item(sw, sw->size(), m_pos);
     }
-    sw->update();
-    sw->set_id(SKM_SHOW);
-    add_item(sw, sw->size(), m_pos);
 
     if (is_set(SKMF_CHANGED))
     {
index 6ed67c0..1d93200 100644 (file)
@@ -540,6 +540,9 @@ void update_can_train()
 
 bool training_restricted(skill_type sk)
 {
+    if (you.species == SP_GNOLL)
+        return false;
+
     switch (sk)
     {
     case SK_FIGHTING:
@@ -584,14 +587,17 @@ void init_can_train()
 
 void init_train()
 {
+    const bool is_gnoll = you.species == SP_GNOLL;
+
     for (int i = 0; i < NUM_SKILLS; ++i)
         if (you.can_train[i] && you.skill_points[i])
             you.train[i] = you.train_alt[i] = TRAINING_ENABLED;
         else
         {
             // Skills are on by default in auto mode and off in manual.
-            you.train[i] = (training_status)you.auto_training;
-            you.train_alt[i] = (training_status)!you.auto_training;
+            you.train[i] = (training_status) (is_gnoll || you.auto_training);
+            you.train_alt[i] =
+                (training_status) (is_gnoll || !you.auto_training);
         }
 }
 
@@ -734,11 +740,19 @@ bool check_selected_skills()
  */
 void reset_training()
 {
+    // Disable this here since we don't want any autotraining related skilling
+    // changes for Gnolls.
+    if (you.species == SP_GNOLL)
+        you.auto_training = false;
+
     // We clear the values in the training array. In auto mode they are set
     // to 0 (and filled later with the content of the queue), in manual mode,
     // the trainable ones are set to 1 (or 2 for focus).
     for (int i = 0; i < NUM_SKILLS; ++i)
-        if (you.auto_training || !skill_trained(i))
+        // Gnolls always train all skills
+        if (you.species == SP_GNOLL)
+            you.training[i] = 1;
+        else if (you.auto_training || !skill_trained(i))
             you.training[i] = 0;
         else
             you.training[i] = you.train[i];
@@ -1816,3 +1830,13 @@ void fixup_skills()
     if (you.exp_available >= calc_skill_cost(you.skill_cost_level))
         skill_menu(SKMF_EXPERIENCE);
 }
+
+/** Can the player enable training for this skill?
+ *
+ * @param sk The skill to check.
+ * @returns True if the skill can be enabled for training, false otherwise.
+ */
+bool can_enable_skill(skill_type sk)
+{
+    return you.species != SP_GNOLL && you.can_train[sk];
+}
index 607a39d..e919f61 100644 (file)
@@ -106,6 +106,7 @@ int transfer_skill_points(skill_type fsk, skill_type tsk, int skp_max,
                           bool simu, bool boost = false);
 int skill_bump(skill_type skill, int scale = 1);
 void fixup_skills();
+bool can_enable_skill(skill_type sk);
 
 static const skill_type skill_display_order[] =
 {
index 74455d9..26a8906 100644 (file)
@@ -72,8 +72,8 @@ static const map<species_type, species_def> species_data =
     7, 7, 7, // 21
     { STAT_STR }, 28,  // No natural stat gain, STR is a placeholder
     { { MUT_STRONG_NOSE, 1, 1 },  { MUT_FANGS, 1, 1 }, },
-    { "Your physical attributes are unchanging."},
-    { "unchanging attributes" },
+    { "Your experience applies equally to all skills."},
+    { "distributed training", },
     { JOB_SKALD, JOB_WARPER, JOB_ARCANE_MARKSMAN, JOB_TRANSMUTER,
       JOB_WANDERER },
     { SK_MACES_FLAILS, SK_AXES, SK_POLEARMS, SK_LONG_BLADES, SK_STAVES,