OSDN Git Service

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