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"
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;
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;
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;
40 std::vector<skill_table> s_info;
44 using GainAmountList = std::array<int, enum2i(PlayerSkillRank::MASTER)>;
46 void gain_attack_skill_exp(PlayerType *player_ptr, short &exp, const GainAmountList &gain_amount_list)
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);
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);
63 exp += static_cast<short>(gain_amount);
64 set_bits(player_ptr->update, PU_BONUS);
67 void gain_spell_skill_exp_aux(PlayerType *player_ptr, short &exp, const GainAmountList &gain_amount_list, int spell_level)
69 const auto dlev = player_ptr->current_floor_ptr->dun_level;
70 const auto plev = player_ptr->lev;
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);
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);
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);
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);
93 exp += static_cast<short>(gain_amount);
94 set_bits(player_ptr->update, PU_BONUS);
99 PlayerSkill::PlayerSkill(PlayerType *player_ptr)
100 : player_ptr(player_ptr)
104 SUB_EXP PlayerSkill::weapon_exp_at(PlayerSkillRank 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;
119 return WEAPON_EXP_UNSKILLED;
122 SUB_EXP PlayerSkill::spell_exp_at(PlayerSkillRank 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;
137 return SPELL_EXP_UNSKILLED;
140 * @brief 武器や各種スキル(騎乗以外)の抽象的表現ランクを返す。 / Return proficiency level of weapons and misc. skills (except riding)
141 * @param weapon_exp 経験値
144 PlayerSkillRank PlayerSkill::weapon_skill_rank(int weapon_exp)
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;
155 return PlayerSkillRank::MASTER;
159 bool PlayerSkill::valid_weapon_exp(int weapon_exp)
161 return (WEAPON_EXP_UNSKILLED <= weapon_exp) && (weapon_exp <= WEAPON_EXP_MASTER);
165 * @brief 騎乗スキルの抽象的ランクを返す。 / Return proficiency level of riding
166 * @param riding_exp 経験値
169 PlayerSkillRank PlayerSkill::riding_skill_rank(int riding_exp)
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;
180 return PlayerSkillRank::MASTER;
185 * @brief プレイヤーの呪文レベルの抽象的ランクを返す。 / Return proficiency level of spells
186 * @param spell_exp 経験値
189 PlayerSkillRank PlayerSkill::spell_skill_rank(int spell_exp)
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;
200 return PlayerSkillRank::MASTER;
204 concptr PlayerSkill::skill_name(PlayerSkillKindType 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:
219 return _("不明", "Unknown");
222 concptr PlayerSkill::skill_rank_str(PlayerSkillRank 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]");
237 return _("[不明]", "[Unknown]");
240 void PlayerSkill::gain_melee_weapon_exp(const ObjectType *o_ptr)
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 };
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);
254 void PlayerSkill::gain_range_weapon_exp(const ObjectType *o_ptr)
256 constexpr GainAmountList gain_amount_list{ 80, 25, 10, 2 };
257 constexpr GainAmountList others_gain_amount_list{ 8, 2, 0, 0 };
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);
268 void PlayerSkill::gain_martial_arts_skill_exp()
270 if (this->player_ptr->skill_exp[PlayerSkillKindType::MARTIAL_ARTS] < s_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);
276 void PlayerSkill::gain_two_weapon_skill_exp()
278 if (this->player_ptr->skill_exp[PlayerSkillKindType::TWO_WEAPON] < s_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);
284 void PlayerSkill::gain_riding_skill_exp_on_melee_attack(const monster_race *r_ptr)
286 auto now_exp = this->player_ptr->skill_exp[PlayerSkillKindType::RIDING];
287 auto max_exp = s_info[enum2i(this->player_ptr->pclass)].s_max[PlayerSkillKindType::RIDING];
288 if (now_exp >= max_exp) {
292 auto riding_level = r_info[this->player_ptr->current_floor_ptr->m_list[this->player_ptr->riding].r_idx].level;
295 if ((now_exp / 200 - 5) < r_ptr->level) {
299 if ((now_exp / 100) < riding_level) {
300 if ((now_exp / 100 + 15) < riding_level) {
301 inc += 1 + (riding_level - (now_exp / 100 + 15));
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);
311 void PlayerSkill::gain_riding_skill_exp_on_range_attack()
313 auto now_exp = this->player_ptr->skill_exp[PlayerSkillKindType::RIDING];
314 auto max_exp = s_info[enum2i(this->player_ptr->pclass)].s_max[PlayerSkillKindType::RIDING];
315 if (now_exp >= max_exp) {
319 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)) {
320 this->player_ptr->skill_exp[PlayerSkillKindType::RIDING] += 1;
321 set_bits(this->player_ptr->update, PU_BONUS);
325 void PlayerSkill::gain_riding_skill_exp_on_fall_off_check(int dam)
327 auto now_exp = this->player_ptr->skill_exp[PlayerSkillKindType::RIDING];
328 auto max_exp = s_info[enum2i(this->player_ptr->pclass)].s_max[PlayerSkillKindType::RIDING];
329 if (now_exp >= max_exp || max_exp <= 1000) {
333 auto riding_level = r_info[this->player_ptr->current_floor_ptr->m_list[this->player_ptr->riding].r_idx].level;
335 if ((dam / 2 + riding_level) <= (now_exp / 30 + 10)) {
340 if ((now_exp / 100 + 15) < riding_level) {
341 inc += 1 + (riding_level - (now_exp / 100 + 15));
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);
350 void PlayerSkill::gain_spell_skill_exp(int realm, int spell_idx)
352 if ((realm < 1) || ((static_cast<int>(std::size(mp_ptr->info)) < realm) && (realm != REALM_MUSIC) && (realm != REALM_HEX))) {
356 if (((spell_idx < 0) || (32 <= spell_idx)) ||
357 ((realm != this->player_ptr->realm1) && (realm != this->player_ptr->realm2))) {
361 constexpr GainAmountList gain_amount_list_first{ 60, 8, 2, 1 };
362 constexpr GainAmountList gain_amount_list_second{ 60, 8, 2, 0 };
364 const auto is_first_realm = (realm == this->player_ptr->realm1);
365 const auto *s_ptr = &mp_ptr->info[realm - 1][spell_idx];
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);
371 void PlayerSkill::gain_continuous_spell_skill_exp(int realm, int spell_idx)
373 if (((spell_idx < 0) || (32 <= spell_idx)) ||
374 ((realm != REALM_MUSIC) && (realm != REALM_HEX))) {
378 const auto *s_ptr = &technic_info[realm - MIN_TECHNIC][spell_idx];
380 const GainAmountList gain_amount_list{ 5, (one_in_(2) ? 1 : 0), (one_in_(5) ? 1 : 0), (one_in_(5) ? 1 : 0) };
382 gain_spell_skill_exp_aux(this->player_ptr, this->player_ptr->spell_exp[spell_idx], gain_amount_list, s_ptr->slevel);
385 PlayerSkillRank PlayerSkill::gain_spell_skill_exp_over_learning(int spell_idx)
387 if ((spell_idx < 0) || (static_cast<int>(std::size(this->player_ptr->spell_exp)) <= spell_idx)) {
388 return PlayerSkillRank::UNSKILLED;
391 auto &exp = this->player_ptr->spell_exp[spell_idx];
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;
399 exp += SPELL_EXP_EXPERT - SPELL_EXP_SKILLED;
401 } else if (exp >= SPELL_EXP_BEGINNER) {
402 exp = SPELL_EXP_SKILLED + (exp - SPELL_EXP_BEGINNER) * 2 / 3;
404 exp = SPELL_EXP_BEGINNER + exp / 3;
407 set_bits(this->player_ptr->update, PU_BONUS);
409 return PlayerSkill::spell_skill_rank(exp);
414 * Returns experience of a spell
415 * @param use_realm 魔法領域
416 * @param spell_idx 呪文ID
419 EXP PlayerSkill::exp_of_spell(int realm, int spell_idx) const
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];
436 * @brief 特別な武器スキル最大値の適用を行う
437 * 性格セクシーギャルの場合ムチスキルの最大値が達人になる
438 * 種族マーフォークの場合三叉槍とトライデントのスキルが達人になる
439 * (但し、いずれも職業がスペルマスターではない場合に限る)
441 void PlayerSkill::apply_special_weapon_skill_max_values()
443 this->player_ptr->weapon_exp_max = s_info[enum2i(this->player_ptr->pclass)].w_max;
444 if (PlayerClass(this->player_ptr).equals(PlayerClassType::SORCERER)) {
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);
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);
460 * @brief 武器スキル経験値を最大値で制限する
462 void PlayerSkill::limit_weapon_skills_by_max_value()
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]);