OSDN Git Service

[Refactor] #2628 ObjectType をItemEntity に変更した
[hengbandforosx/hengbandosx.git] / src / player / player-skill.cpp
1 #include "player/player-skill.h"
2 #include "core/player-update-types.h"
3 #include "monster-race/monster-race.h"
4 #include "player-base/player-class.h"
5 #include "player-base/player-race.h"
6 #include "player-info/class-info.h"
7 #include "player/player-realm.h"
8 #include "sv-definition/sv-weapon-types.h"
9 #include "system/floor-type-definition.h"
10 #include "system/monster-race-definition.h"
11 #include "system/monster-type-definition.h"
12 #include "system/object-type-definition.h"
13 #include "system/player-type-definition.h"
14 #include "util/bit-flags-calculator.h"
15
16 /* Proficiency of weapons and misc. skills (except riding) */
17 constexpr SUB_EXP WEAPON_EXP_UNSKILLED = 0;
18 constexpr SUB_EXP WEAPON_EXP_BEGINNER = 4000;
19 constexpr SUB_EXP WEAPON_EXP_SKILLED = 6000;
20 constexpr SUB_EXP WEAPON_EXP_EXPERT = 7000;
21 constexpr SUB_EXP WEAPON_EXP_MASTER = 8000;
22
23 /* Proficiency of riding */
24 constexpr SUB_EXP RIDING_EXP_UNSKILLED = 0;
25 constexpr SUB_EXP RIDING_EXP_BEGINNER = 500;
26 constexpr SUB_EXP RIDING_EXP_SKILLED = 2000;
27 constexpr SUB_EXP RIDING_EXP_EXPERT = 5000;
28 constexpr SUB_EXP RIDING_EXP_MASTER = 8000;
29
30 /* Proficiency of spells */
31 constexpr SUB_EXP SPELL_EXP_UNSKILLED = 0;
32 constexpr SUB_EXP SPELL_EXP_BEGINNER = 900;
33 constexpr SUB_EXP SPELL_EXP_SKILLED = 1200;
34 constexpr SUB_EXP SPELL_EXP_EXPERT = 1400;
35 constexpr SUB_EXP SPELL_EXP_MASTER = 1600;
36
37 /*
38  * The skill table
39  */
40 std::vector<skill_table> class_skills_info;
41
42 namespace {
43
44 using GainAmountList = std::array<int, enum2i(PlayerSkillRank::MASTER)>;
45
46 void gain_attack_skill_exp(PlayerType *player_ptr, short &exp, const GainAmountList &gain_amount_list)
47 {
48     auto gain_amount = 0;
49     auto calc_gain_amount = [&gain_amount_list, exp](PlayerSkillRank rank, int next_rank_exp) {
50         return std::min(gain_amount_list[enum2i(rank)], next_rank_exp - exp);
51     };
52
53     if (exp < WEAPON_EXP_BEGINNER) {
54         gain_amount = calc_gain_amount(PlayerSkillRank::UNSKILLED, WEAPON_EXP_BEGINNER);
55     } else if (exp < WEAPON_EXP_SKILLED) {
56         gain_amount = calc_gain_amount(PlayerSkillRank::BEGINNER, WEAPON_EXP_SKILLED);
57     } else if ((exp < WEAPON_EXP_EXPERT) && (player_ptr->lev > 19)) {
58         gain_amount = calc_gain_amount(PlayerSkillRank::SKILLED, WEAPON_EXP_EXPERT);
59     } else if ((exp < WEAPON_EXP_MASTER) && (player_ptr->lev > 34)) {
60         gain_amount = calc_gain_amount(PlayerSkillRank::EXPERT, WEAPON_EXP_MASTER);
61     }
62
63     exp += static_cast<short>(gain_amount);
64     set_bits(player_ptr->update, PU_BONUS);
65 }
66
67 void gain_spell_skill_exp_aux(PlayerType *player_ptr, short &exp, const GainAmountList &gain_amount_list, int spell_level)
68 {
69     const auto dlev = player_ptr->current_floor_ptr->dun_level;
70     const auto plev = player_ptr->lev;
71
72     auto gain_amount = 0;
73     auto calc_gain_amount = [&gain_amount_list, exp](PlayerSkillRank rank, int next_rank_exp) {
74         return std::min(gain_amount_list[enum2i(rank)], next_rank_exp - exp);
75     };
76
77     if (exp < SPELL_EXP_BEGINNER) {
78         gain_amount = calc_gain_amount(PlayerSkillRank::UNSKILLED, SPELL_EXP_BEGINNER);
79     } else if (exp < SPELL_EXP_SKILLED) {
80         if ((dlev > 4) && ((dlev + 10) > plev)) {
81             gain_amount = calc_gain_amount(PlayerSkillRank::BEGINNER, SPELL_EXP_SKILLED);
82         }
83     } else if (exp < SPELL_EXP_EXPERT) {
84         if (((dlev + 5) > plev) && ((dlev + 5) > spell_level)) {
85             gain_amount = calc_gain_amount(PlayerSkillRank::SKILLED, SPELL_EXP_EXPERT);
86         }
87     } else if (exp < SPELL_EXP_MASTER) {
88         if (((dlev + 5) > plev) && (dlev > spell_level)) {
89             gain_amount = calc_gain_amount(PlayerSkillRank::EXPERT, SPELL_EXP_MASTER);
90         }
91     }
92
93     exp += static_cast<short>(gain_amount);
94     set_bits(player_ptr->update, PU_BONUS);
95 }
96
97 }
98
99 PlayerSkill::PlayerSkill(PlayerType *player_ptr)
100     : player_ptr(player_ptr)
101 {
102 }
103
104 SUB_EXP PlayerSkill::weapon_exp_at(PlayerSkillRank rank)
105 {
106     switch (rank) {
107     case PlayerSkillRank::UNSKILLED:
108         return WEAPON_EXP_UNSKILLED;
109     case PlayerSkillRank::BEGINNER:
110         return WEAPON_EXP_BEGINNER;
111     case PlayerSkillRank::SKILLED:
112         return WEAPON_EXP_SKILLED;
113     case PlayerSkillRank::EXPERT:
114         return WEAPON_EXP_EXPERT;
115     case PlayerSkillRank::MASTER:
116         return WEAPON_EXP_MASTER;
117     }
118
119     return WEAPON_EXP_UNSKILLED;
120 }
121
122 SUB_EXP PlayerSkill::spell_exp_at(PlayerSkillRank rank)
123 {
124     switch (rank) {
125     case PlayerSkillRank::UNSKILLED:
126         return SPELL_EXP_UNSKILLED;
127     case PlayerSkillRank::BEGINNER:
128         return SPELL_EXP_BEGINNER;
129     case PlayerSkillRank::SKILLED:
130         return SPELL_EXP_SKILLED;
131     case PlayerSkillRank::EXPERT:
132         return SPELL_EXP_EXPERT;
133     case PlayerSkillRank::MASTER:
134         return SPELL_EXP_MASTER;
135     }
136
137     return SPELL_EXP_UNSKILLED;
138 }
139 /*!
140  * @brief 武器や各種スキル(騎乗以外)の抽象的表現ランクを返す。 /  Return proficiency level of weapons and misc. skills (except riding)
141  * @param weapon_exp 経験値
142  * @return ランク値
143  */
144 PlayerSkillRank PlayerSkill::weapon_skill_rank(int weapon_exp)
145 {
146     if (weapon_exp < WEAPON_EXP_BEGINNER) {
147         return PlayerSkillRank::UNSKILLED;
148     } else if (weapon_exp < WEAPON_EXP_SKILLED) {
149         return PlayerSkillRank::BEGINNER;
150     } else if (weapon_exp < WEAPON_EXP_EXPERT) {
151         return PlayerSkillRank::SKILLED;
152     } else if (weapon_exp < WEAPON_EXP_MASTER) {
153         return PlayerSkillRank::EXPERT;
154     } else {
155         return PlayerSkillRank::MASTER;
156     }
157 }
158
159 bool PlayerSkill::valid_weapon_exp(int weapon_exp)
160 {
161     return (WEAPON_EXP_UNSKILLED <= weapon_exp) && (weapon_exp <= WEAPON_EXP_MASTER);
162 }
163
164 /*!
165  * @brief 騎乗スキルの抽象的ランクを返す。 / Return proficiency level of riding
166  * @param riding_exp 経験値
167  * @return ランク値
168  */
169 PlayerSkillRank PlayerSkill::riding_skill_rank(int riding_exp)
170 {
171     if (riding_exp < RIDING_EXP_BEGINNER) {
172         return PlayerSkillRank::UNSKILLED;
173     } else if (riding_exp < RIDING_EXP_SKILLED) {
174         return PlayerSkillRank::BEGINNER;
175     } else if (riding_exp < RIDING_EXP_EXPERT) {
176         return PlayerSkillRank::SKILLED;
177     } else if (riding_exp < RIDING_EXP_MASTER) {
178         return PlayerSkillRank::EXPERT;
179     } else {
180         return PlayerSkillRank::MASTER;
181     }
182 }
183
184 /*!
185  * @brief プレイヤーの呪文レベルの抽象的ランクを返す。 / Return proficiency level of spells
186  * @param spell_exp 経験値
187  * @return ランク値
188  */
189 PlayerSkillRank PlayerSkill::spell_skill_rank(int spell_exp)
190 {
191     if (spell_exp < SPELL_EXP_BEGINNER) {
192         return PlayerSkillRank::UNSKILLED;
193     } else if (spell_exp < SPELL_EXP_SKILLED) {
194         return PlayerSkillRank::BEGINNER;
195     } else if (spell_exp < SPELL_EXP_EXPERT) {
196         return PlayerSkillRank::SKILLED;
197     } else if (spell_exp < SPELL_EXP_MASTER) {
198         return PlayerSkillRank::EXPERT;
199     } else {
200         return PlayerSkillRank::MASTER;
201     }
202 }
203
204 concptr PlayerSkill::skill_name(PlayerSkillKindType skill)
205 {
206     switch (skill) {
207     case PlayerSkillKindType::MARTIAL_ARTS:
208         return _("マーシャルアーツ", "Martial Arts");
209     case PlayerSkillKindType::TWO_WEAPON:
210         return _("二刀流", "Dual Wielding");
211     case PlayerSkillKindType::RIDING:
212         return _("乗馬", "Riding");
213     case PlayerSkillKindType::SHIELD:
214         return _("盾", "Shield");
215     case PlayerSkillKindType::MAX:
216         break;
217     }
218
219     return _("不明", "Unknown");
220 }
221
222 concptr PlayerSkill::skill_rank_str(PlayerSkillRank rank)
223 {
224     switch (rank) {
225     case PlayerSkillRank::UNSKILLED:
226         return _("[初心者]", "[Unskilled]");
227     case PlayerSkillRank::BEGINNER:
228         return _("[入門者]", "[Beginner]");
229     case PlayerSkillRank::SKILLED:
230         return _("[熟練者]", "[Skilled]");
231     case PlayerSkillRank::EXPERT:
232         return _("[エキスパート]", "[Expert]");
233     case PlayerSkillRank::MASTER:
234         return _("[達人]", "[Master]");
235     }
236
237     return _("[不明]", "[Unknown]");
238 }
239
240 void PlayerSkill::gain_melee_weapon_exp(const ItemEntity *o_ptr)
241 {
242     const GainAmountList gain_amount_list{ { 80, 10, 1, (one_in_(2) ? 1 : 0) } };
243     constexpr GainAmountList others_gain_amount_list{ { 8, 1, 0, 0 } };
244
245     for (auto sval = 0U; sval < this->player_ptr->weapon_exp[o_ptr->tval].size(); ++sval) {
246         auto &now_exp = this->player_ptr->weapon_exp[o_ptr->tval][sval];
247         if (now_exp < this->player_ptr->weapon_exp_max[o_ptr->tval][sval]) {
248             gain_attack_skill_exp(this->player_ptr, now_exp,
249                 (static_cast<int>(sval) == o_ptr->sval) ? gain_amount_list : others_gain_amount_list);
250         }
251     }
252 }
253
254 void PlayerSkill::gain_range_weapon_exp(const ItemEntity *o_ptr)
255 {
256     constexpr GainAmountList gain_amount_list{ { 80, 25, 10, 2 } };
257     constexpr GainAmountList others_gain_amount_list{ { 8, 2, 0, 0 } };
258
259     for (auto sval = 0U; sval < this->player_ptr->weapon_exp[o_ptr->tval].size(); ++sval) {
260         auto &now_exp = this->player_ptr->weapon_exp[o_ptr->tval][sval];
261         if (now_exp < this->player_ptr->weapon_exp_max[o_ptr->tval][sval]) {
262             gain_attack_skill_exp(this->player_ptr, now_exp,
263                 (static_cast<int>(sval) == o_ptr->sval) ? gain_amount_list : others_gain_amount_list);
264         }
265     }
266 }
267
268 void PlayerSkill::gain_martial_arts_skill_exp()
269 {
270     if (this->player_ptr->skill_exp[PlayerSkillKindType::MARTIAL_ARTS] < class_skills_info[enum2i(this->player_ptr->pclass)].s_max[PlayerSkillKindType::MARTIAL_ARTS]) {
271         const GainAmountList gain_amount_list{ 40, 5, 1, (one_in_(3) ? 1 : 0) };
272         gain_attack_skill_exp(this->player_ptr, this->player_ptr->skill_exp[PlayerSkillKindType::MARTIAL_ARTS], gain_amount_list);
273     }
274 }
275
276 void PlayerSkill::gain_two_weapon_skill_exp()
277 {
278     if (this->player_ptr->skill_exp[PlayerSkillKindType::TWO_WEAPON] < class_skills_info[enum2i(this->player_ptr->pclass)].s_max[PlayerSkillKindType::TWO_WEAPON]) {
279         const GainAmountList gain_amount_list{ 80, 4, 1, (one_in_(3) ? 1 : 0) };
280         gain_attack_skill_exp(this->player_ptr, this->player_ptr->skill_exp[PlayerSkillKindType::TWO_WEAPON], gain_amount_list);
281     }
282 }
283
284 void PlayerSkill::gain_riding_skill_exp_on_melee_attack(const monster_race *r_ptr)
285 {
286     auto now_exp = this->player_ptr->skill_exp[PlayerSkillKindType::RIDING];
287     auto max_exp = class_skills_info[enum2i(this->player_ptr->pclass)].s_max[PlayerSkillKindType::RIDING];
288     if (now_exp >= max_exp) {
289         return;
290     }
291
292     auto riding_level = monraces_info[this->player_ptr->current_floor_ptr->m_list[this->player_ptr->riding].r_idx].level;
293     int inc = 0;
294
295     if ((now_exp / 200 - 5) < r_ptr->level) {
296         inc += 1;
297     }
298
299     if ((now_exp / 100) < riding_level) {
300         if ((now_exp / 100 + 15) < riding_level) {
301             inc += 1 + (riding_level - (now_exp / 100 + 15));
302         } else {
303             inc += 1;
304         }
305     }
306
307     this->player_ptr->skill_exp[PlayerSkillKindType::RIDING] = std::min<SUB_EXP>(max_exp, now_exp + inc);
308     set_bits(this->player_ptr->update, PU_BONUS);
309 }
310
311 void PlayerSkill::gain_riding_skill_exp_on_range_attack()
312 {
313     auto now_exp = this->player_ptr->skill_exp[PlayerSkillKindType::RIDING];
314     auto max_exp = class_skills_info[enum2i(this->player_ptr->pclass)].s_max[PlayerSkillKindType::RIDING];
315     if (now_exp >= max_exp) {
316         return;
317     }
318
319     if (((this->player_ptr->skill_exp[PlayerSkillKindType::RIDING] - (RIDING_EXP_BEGINNER * 2)) / 200 < monraces_info[this->player_ptr->current_floor_ptr->m_list[this->player_ptr->riding].r_idx].level) && one_in_(2)) {
320         this->player_ptr->skill_exp[PlayerSkillKindType::RIDING] += 1;
321         set_bits(this->player_ptr->update, PU_BONUS);
322     }
323 }
324
325 void PlayerSkill::gain_riding_skill_exp_on_fall_off_check(int dam)
326 {
327     auto now_exp = this->player_ptr->skill_exp[PlayerSkillKindType::RIDING];
328     auto max_exp = class_skills_info[enum2i(this->player_ptr->pclass)].s_max[PlayerSkillKindType::RIDING];
329     if (now_exp >= max_exp || max_exp <= 1000) {
330         return;
331     }
332
333     auto riding_level = monraces_info[this->player_ptr->current_floor_ptr->m_list[this->player_ptr->riding].r_idx].level;
334
335     if ((dam / 2 + riding_level) <= (now_exp / 30 + 10)) {
336         return;
337     }
338
339     int inc = 0;
340     if ((now_exp / 100 + 15) < riding_level) {
341         inc += 1 + (riding_level - (now_exp / 100 + 15));
342     } else {
343         inc += 1;
344     }
345
346     this->player_ptr->skill_exp[PlayerSkillKindType::RIDING] = std::min<SUB_EXP>(max_exp, now_exp + inc);
347     set_bits(this->player_ptr->update, PU_BONUS);
348 }
349
350 void PlayerSkill::gain_spell_skill_exp(int realm, int spell_idx)
351 {
352     if ((realm < 1) || ((static_cast<int>(std::size(mp_ptr->info)) < realm) && (realm != REALM_MUSIC) && (realm != REALM_HEX))) {
353         return;
354     }
355
356     if (((spell_idx < 0) || (32 <= spell_idx)) ||
357         ((realm != this->player_ptr->realm1) && (realm != this->player_ptr->realm2))) {
358         return;
359     }
360
361     constexpr GainAmountList gain_amount_list_first{ { 60, 8, 2, 1 } };
362     constexpr GainAmountList gain_amount_list_second{ { 60, 8, 2, 0 } };
363
364     const auto is_first_realm = (realm == this->player_ptr->realm1);
365     const auto *s_ptr = &mp_ptr->info[realm - 1][spell_idx];
366
367     gain_spell_skill_exp_aux(this->player_ptr, this->player_ptr->spell_exp[spell_idx + (is_first_realm ? 0 : 32)],
368         (is_first_realm ? gain_amount_list_first : gain_amount_list_second), s_ptr->slevel);
369 }
370
371 void PlayerSkill::gain_continuous_spell_skill_exp(int realm, int spell_idx)
372 {
373     if (((spell_idx < 0) || (32 <= spell_idx)) ||
374         ((realm != REALM_MUSIC) && (realm != REALM_HEX))) {
375         return;
376     }
377
378     const auto *s_ptr = &technic_info[realm - MIN_TECHNIC][spell_idx];
379
380     const GainAmountList gain_amount_list{ 5, (one_in_(2) ? 1 : 0), (one_in_(5) ? 1 : 0), (one_in_(5) ? 1 : 0) };
381
382     gain_spell_skill_exp_aux(this->player_ptr, this->player_ptr->spell_exp[spell_idx], gain_amount_list, s_ptr->slevel);
383 }
384
385 PlayerSkillRank PlayerSkill::gain_spell_skill_exp_over_learning(int spell_idx)
386 {
387     if ((spell_idx < 0) || (static_cast<int>(std::size(this->player_ptr->spell_exp)) <= spell_idx)) {
388         return PlayerSkillRank::UNSKILLED;
389     }
390
391     auto &exp = this->player_ptr->spell_exp[spell_idx];
392
393     if (exp >= SPELL_EXP_EXPERT) {
394         exp = SPELL_EXP_MASTER;
395     } else if (exp >= SPELL_EXP_SKILLED) {
396         if (spell_idx >= 32) {
397             exp = SPELL_EXP_EXPERT;
398         } else {
399             exp += SPELL_EXP_EXPERT - SPELL_EXP_SKILLED;
400         }
401     } else if (exp >= SPELL_EXP_BEGINNER) {
402         exp = SPELL_EXP_SKILLED + (exp - SPELL_EXP_BEGINNER) * 2 / 3;
403     } else {
404         exp = SPELL_EXP_BEGINNER + exp / 3;
405     }
406
407     set_bits(this->player_ptr->update, PU_BONUS);
408
409     return PlayerSkill::spell_skill_rank(exp);
410 }
411
412 /*!
413  * @brief 呪文の経験値を返す /
414  * Returns experience of a spell
415  * @param use_realm 魔法領域
416  * @param spell_idx 呪文ID
417  * @return 経験値
418  */
419 EXP PlayerSkill::exp_of_spell(int realm, int spell_idx) const
420 {
421     PlayerClass pc(this->player_ptr);
422     if (pc.equals(PlayerClassType::SORCERER)) {
423         return SPELL_EXP_MASTER;
424     } else if (pc.equals(PlayerClassType::RED_MAGE)) {
425         return SPELL_EXP_SKILLED;
426     } else if (realm == this->player_ptr->realm1) {
427         return this->player_ptr->spell_exp[spell_idx];
428     } else if (realm == this->player_ptr->realm2) {
429         return this->player_ptr->spell_exp[spell_idx + 32];
430     } else {
431         return 0;
432     }
433 }
434
435 /*!
436  * @brief 特別な武器スキル最大値の適用を行う
437  * 性格セクシーギャルの場合ムチスキルの最大値が達人になる
438  * 種族マーフォークの場合三叉槍とトライデントのスキルが達人になる
439  * (但し、いずれも職業がスペルマスターではない場合に限る)
440  */
441 void PlayerSkill::apply_special_weapon_skill_max_values()
442 {
443     this->player_ptr->weapon_exp_max = class_skills_info[enum2i(this->player_ptr->pclass)].w_max;
444     if (PlayerClass(this->player_ptr).equals(PlayerClassType::SORCERER)) {
445         return;
446     }
447
448     auto &w_exp_max = this->player_ptr->weapon_exp_max;
449     if (this->player_ptr->ppersonality == PERSONALITY_SEXY) {
450         w_exp_max[ItemKindType::HAFTED][SV_WHIP] = PlayerSkill::weapon_exp_at(PlayerSkillRank::MASTER);
451     }
452
453     if (PlayerRace(player_ptr).equals(PlayerRaceType::MERFOLK)) {
454         w_exp_max[ItemKindType::POLEARM][SV_TRIDENT] = PlayerSkill::weapon_exp_at(PlayerSkillRank::MASTER);
455         w_exp_max[ItemKindType::POLEARM][SV_TRIFURCATE_SPEAR] = PlayerSkill::weapon_exp_at(PlayerSkillRank::MASTER);
456     }
457 }
458
459 /*!
460  * @brief 武器スキル経験値を最大値で制限する
461  */
462 void PlayerSkill::limit_weapon_skills_by_max_value()
463 {
464     for (auto tval : TV_WEAPON_RANGE) {
465         auto &exp_table = this->player_ptr->weapon_exp[tval];
466         const auto &max_exp_table = this->player_ptr->weapon_exp_max[tval];
467         for (auto i = 0U; i < exp_table.size(); ++i) {
468             exp_table[i] = std::min(exp_table[i], max_exp_table[i]);
469         }
470     }
471 }