More gnoll skill code tweaks.
authorelliptic <hyperelliptical@gmail.com>
Sat, 30 Sep 2017 22:31:03 +0000 (18:31 -0400)
committerelliptic <hyperelliptical@gmail.com>
Sat, 30 Sep 2017 22:31:03 +0000 (18:31 -0400)
This commit does two things to gnoll skill training, and does not affect
non-gnolls at all.

1. Gnoll skill training is finer now - previously it used increments of
   10 skill points only, now it goes 1 skill point at a time.
2. Gnolls no longer train other skills faster if they sacrifice skills with
   Ru (e.g. Sacrifice Stealth or Sacrifice Hand).

There are two possible issues with this new approach. First, the code is not
very efficient. Using &k on a gnoll to go to XL 27 takes about 10 seconds
on my machine now. I don't think this will be noticeable outside of wizmode,
and there are probably some easy optimizations that can be made - for now,
I just went for code transparency.

Second, gnoll skill training costs are slightly randomized now (mainly in
early game). You can observe this by doing &k5 to go to XL 5 many times in
a row (on different characters). I don't think this is necessarily a problem,
and it is somewhat unavoidable when using 1 skill point precision at the
moment (since the cost of raising a skill by 1 skill point at start is 1/10
of an xp point). It is probably not too hard to fix this by scaling
you.total_experience and you.exp_available by a factor of 10 if we want.

crawl-ref/source/skills.cc

index 8322b0a..580840e 100644 (file)
@@ -1143,18 +1143,56 @@ void check_skill_cost_change()
 #endif
 }
 
-// The current cost of raising all skills by one skill point. Used to ensure
-// that gnoll skills rise evenly.
+static int _useless_skill_count()
+{
+    int count = 0;
+    for (skill_type skill = SK_FIRST_SKILL; skill < NUM_SKILLS; ++skill)
+    {
+#if TAG_MAJOR_VERSION == 34
+        if (skill == SK_STABBING || skill == SK_TRAPS)
+            continue;
+#endif
+        if (is_useless_skill(skill))
+            count++;
+    }
+    return count;
+}
+
+static int _total_skill_count()
+{
+    int count = 0;
+    for (skill_type skill = SK_FIRST_SKILL; skill < NUM_SKILLS; ++skill)
+    {
+#if TAG_MAJOR_VERSION == 34
+        if (skill == SK_STABBING || skill == SK_TRAPS)
+            continue;
+#endif
+        count++;
+    }
+    return count;
+}
+
+// The current cost of raising each skill by one skill point, taking the
+// gnoll penalty for useless skills into account and rounding up for all
+// computations. Used to ensure that gnoll skills rise evenly - we don't
+// train anything unless we have this much xp to spend.
 int _gnoll_total_skill_cost()
 {
+    int this_cost;
     int total_cost = 0;
     int cur_cost_level = you.skill_cost_level;
+    const int useless_count = _useless_skill_count();
+    const int total_count = _total_skill_count();
+    const int num = total_count;
+    const int denom = 10 * (total_count - useless_count);
     for (int i = 0; i < NUM_SKILLS; ++i)
     {
         if (!you.training[i])
             continue;
         cur_cost_level = _calc_skill_cost_level(you.total_experience + total_cost, cur_cost_level);
-        total_cost += calc_skill_cost(cur_cost_level);
+        this_cost = calc_skill_cost(cur_cost_level);
+        this_cost = (num * this_cost + denom - 1) / denom;
+        total_cost += this_cost;
     }
     return total_cost;
 }
@@ -1177,16 +1215,28 @@ static int _train(skill_type exsk, int &max_exp, bool simu)
     // This will be deducted from you.exp_available.
     int cost = calc_skill_cost(you.skill_cost_level);
 
-    // Scale cost and skill_inc to available experience.
-    const int spending_limit = min(MAX_SPENDING_LIMIT, max_exp);
-    if (cost > spending_limit)
+    if (you.species == SP_GNOLL)
     {
-        int frac = spending_limit * 10 / cost;
-        cost = spending_limit;
-        skill_inc = skill_inc * frac / 10;
+        skill_inc = 1;
+        int useless_count = _useless_skill_count();
+        int total_count = _total_skill_count();
+        int num = total_count;
+        int denom = 10 * (total_count - useless_count);
+        cost = div_rand_round(num * cost, denom);
+    }
+    else
+    {
+        // Scale cost and skill_inc to available experience.
+        const int spending_limit = min(MAX_SPENDING_LIMIT, max_exp);
+        if (cost > spending_limit)
+        {
+            int frac = spending_limit * 10 / cost;
+            cost = spending_limit;
+            skill_inc = skill_inc * frac / 10;
+        }
     }
 
-    if (skill_inc <= 0)
+    if (skill_inc <= 0 || cost > max_exp)
         return 0;
 
     // Bonus from manual