File: exercise.cc

package info (click to toggle)
crawl 2%3A0.34.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 100,188 kB
  • sloc: cpp: 363,709; ansic: 27,765; javascript: 9,516; python: 8,463; perl: 3,293; java: 3,132; xml: 2,380; makefile: 1,835; sh: 611; objc: 250; cs: 15; sed: 9; lisp: 3
file content (266 lines) | stat: -rw-r--r-- 7,364 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/**
 * @file
 * @brief Collects all calls to skills.cc:exercise for
 *            easier changes to the training model.
**/

#include "AppHdr.h"

#include "exercise.h"

#include <algorithm>

#include "ability.h"
#include "fight.h"
#include "item-prop.h"
#include "skills.h"
#include "spl-util.h"
#include "transform.h" // get_form

/// Skill training for when the player casts or miscasts a spell.
void practise_casting(spell_type spell, bool success)
{
    // (!success) reduces skill increase for miscast spells
    int workout = 0;

    spschools_type disciplines = get_spell_disciplines(spell);

    int skillcount = count_bits(disciplines);
    if (!skillcount)
        return; // not a real spell (wizmode casting?)

    if (!success)
        skillcount += 4 + random2(10);

    const int diff = spell_difficulty(spell);

    // Fill all disciplines into a vector, then shuffle the vector, and
    // exercise skills in that random order. That way, first skill don't
    // stay in the queue for a shorter time.
    bool conj = false;
    vector<skill_type> disc;
    for (const auto bit : spschools_type::range())
    {
        if (!spell_typematch(spell, bit))
            continue;

        skill_type skill = spell_type2skill(bit);
        if (skill == SK_CONJURATIONS)
            conj = true;

        disc.push_back(skill);
    }

    // We slow down the training of spells with conjurations.
    if (conj && !x_chance_in_y(skillcount, 4))
        return;

    shuffle_array(disc);

    for (const skill_type skill : disc)
    {
        workout = (random2(1 + diff) / skillcount);

        if (!one_chance_in(5))
            workout++;       // most recently, this was an automatic add {dlb}

        exercise(skill, workout);
    }

    /* ******************************************************************
       Other recent formulae for the above:

       * workout = random2(spell_difficulty(spell_ex)
       * (10 + (spell_difficulty(spell_ex) * 2)) / 10 / spellsy + 1);

       * workout = spell_difficulty(spell_ex)
       * (15 + spell_difficulty(spell_ex)) / 15 / spellsy;

       spellcasting had also been generally exercised at the same time
       ****************************************************************** */

    exercise(SK_SPELLCASTING, 1 + random2(1 + diff) / skillcount);
}

/**
 * A best-fit linear approximation of the old item mass equation for armour.
 *
 * @return 42 * EVP - 13, if the player is wearing body armour; else 0.
 */
static int _armour_mass()
{
    const int evp = you.unadjusted_body_armour_penalty();
    return max(0, 42 * evp - 13);
}

static bool _check_train_armour(int amount)
{
    const int mass_base = 100; // old animal skin mass
    if (x_chance_in_y(_armour_mass() - mass_base,
                      you.skill(SK_ARMOUR, 50)))
    {
        exercise(SK_ARMOUR, amount);
        return true;
    }
    return false;
}

static bool _check_train_dodging(int amount)
{
    if (!x_chance_in_y(_armour_mass(), 800))
    {
        exercise(SK_DODGING, amount);
        return true;
    }
    return false;
}

/// Skill training while not being noticed by a monster.
void practise_sneaking()
{
    if (!x_chance_in_y(_armour_mass(), 1000))
        exercise(SK_STEALTH, 1);
}

/// Skill training while doing nothing in particular.
void practise_waiting()
{
    if (one_chance_in(6) && _check_train_armour(1))
        return; // Armour trained in check_train_armour

    if (you.berserk())
        return;

    // Exercise stealth skill:
    if (!x_chance_in_y(_armour_mass(), 1000)
        // Diminishing returns for stealth training by waiting.
        && you.skills[SK_STEALTH] <= 2 + random2(3)
        && one_chance_in(15))
    {
        exercise(SK_STEALTH, 1);
    }
}

/// Skill training when the player uses the given weapon.
static void _practise_weapon_use(const item_def &weapon)
{
    const skill_type weapon_skill = item_attack_skill(weapon);
    const int mindelay_skill = weapon_min_delay_skill(weapon);
    const int your_skill = you.skills[weapon_skill];
    if (your_skill >= mindelay_skill)
        exercise(weapon_skill, coinflip()); //1/2 past mindelay
    else
    {
        // 3 at 0 skill, 2 at 1/2 mindelay skill, 1 at mindelay
        const int degree
            = 1 + div_rand_round(2 * (mindelay_skill - your_skill),
                                 mindelay_skill);
        exercise(weapon_skill, degree);
    }

    if (coinflip())
        exercise(SK_FIGHTING, 1);
}

static void _maybe_practice_shapeshifting()
{
    if (coinflip())
        return; // train shapeshifting less than UC/weapon skills
    if (you.form == transformation::none || you.form != you.default_form)
        return;
    const int to_max = get_form()->max_skill - you.skill(SK_SHAPESHIFTING);
    if (to_max <= 0)
        return;
    if (to_max <= 5 && coinflip())
        return;
    exercise(SK_SHAPESHIFTING, 1);
}

/// Skill training when the player hits a monster in melee combat.
void practise_hitting(const item_def *weapon)
{
    _maybe_practice_shapeshifting();
    if (!weapon)
    {
        exercise(SK_UNARMED_COMBAT, 1);
        return;
    }
    if (!is_melee_weapon(*weapon))
        return;

    // Slow down the practice of low-damage weapons. XXX: use speed instead?
    const int damage = property(*weapon, PWPN_DAMAGE);
    if (x_chance_in_y(damage, 20))
        _practise_weapon_use(*weapon);
}

/// Skill training when the player shoots at a monster with a ranged weapon.
void practise_launching(const item_def &weapon)
{
    if (coinflip()) // XXX: arbitrary; test and revise
        _practise_weapon_use(weapon);
}

/// Skill training when the player throws a projectile at a monster.
void practise_throwing(missile_type mi_type)
{
    if (mi_type == MI_STONE && coinflip())
        return;
    exercise(SK_THROWING, 1);
}

/// Skill training when the player stabs an vulnerable monster for extra dam.
void practise_stabbing() { exercise(SK_STEALTH, 1 + random2avg(5, 4)); }

/// Skill training when a monster attacks the player in melee.
void practise_being_attacked()
{
    if (one_chance_in(6))
        _check_train_dodging(1);
}

/// Skill training when a monster hits the player with a melee attack.
void practise_being_hit()
{
    if (coinflip())
        _check_train_armour(random_range(1, 2));
    else if (coinflip())
        exercise(SK_FIGHTING, 1);
}

/// Skill training when the player uses a special ability.
void practise_using_ability(ability_type abil)
{
    const skill_type sk = abil_skill(abil);
    if (sk != SK_NONE)
        exercise(sk, abil_skill_weight(abil));
}

/// Skill training when blocking or failing to block attacks with a shield.
void practise_shield_block(bool successful)
{
    const int odds_denom = successful ? 2 : 6;
    if (one_chance_in(odds_denom))
        exercise(SK_SHIELDS, 1);
}

/// Skill training when being hit by a spell or other ranged attack.
void practise_being_shot()
{
    if (one_chance_in(4))
        _check_train_armour(1);
}

/// Skill training when being attacked with a spell or other ranged attack.
void practise_being_shot_at()
{
    if (one_chance_in(4))
        _check_train_dodging(1);
}

/// Skill training when using an evocable item such as a wand.
void practise_evoking(int amount)
{
    // XXX: degree determination is just passed in but should be done here.
    exercise(SK_EVOCATIONS, amount);
}