OSDN Git Service

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