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"
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;
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;
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;
39 std::vector<skill_table> s_info;
43 using GainAmountList = std::array<int, enum2i(PlayerSkillRank::MASTER)>;
45 void gain_attack_skill_exp(PlayerType *player_ptr, short &exp, const GainAmountList &gain_amount_list)
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);
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);
62 exp += static_cast<short>(gain_amount);
63 set_bits(player_ptr->update, PU_BONUS);
66 void gain_spell_skill_exp_aux(PlayerType *player_ptr, short &exp, const GainAmountList &gain_amount_list, int spell_level)
68 const auto dlev = player_ptr->current_floor_ptr->dun_level;
69 const auto plev = player_ptr->lev;
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);
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);
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);
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);
92 exp += static_cast<short>(gain_amount);
93 set_bits(player_ptr->update, PU_BONUS);
98 PlayerSkill::PlayerSkill(PlayerType *player_ptr)
99 : player_ptr(player_ptr)
103 SUB_EXP PlayerSkill::weapon_exp_at(PlayerSkillRank 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;
118 return WEAPON_EXP_UNSKILLED;
121 SUB_EXP PlayerSkill::spell_exp_at(PlayerSkillRank 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;
136 return SPELL_EXP_UNSKILLED;
139 * @brief 武器や各種スキル(騎乗以外)の抽象的表現ランクを返す。 / Return proficiency level of weapons and misc. skills (except riding)
140 * @param weapon_exp 経験値
143 PlayerSkillRank PlayerSkill::weapon_skill_rank(int weapon_exp)
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;
154 return PlayerSkillRank::MASTER;
157 bool PlayerSkill::valid_weapon_exp(int weapon_exp)
159 return (WEAPON_EXP_UNSKILLED <= weapon_exp) && (weapon_exp <= WEAPON_EXP_MASTER);
163 * @brief 騎乗スキルの抽象的ランクを返す。 / Return proficiency level of riding
164 * @param riding_exp 経験値
167 PlayerSkillRank PlayerSkill::riding_skill_rank(int riding_exp)
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;
178 return PlayerSkillRank::MASTER;
182 * @brief プレイヤーの呪文レベルの抽象的ランクを返す。 / Return proficiency level of spells
183 * @param spell_exp 経験値
186 PlayerSkillRank PlayerSkill::spell_skill_rank(int spell_exp)
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;
197 return PlayerSkillRank::MASTER;
200 concptr PlayerSkill::skill_name(PlayerSkillKindType 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:
215 return _("不明", "Unknown");
218 concptr PlayerSkill::skill_rank_str(PlayerSkillRank 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]");
233 return _("[不明]", "[Unknown]");
236 void PlayerSkill::gain_melee_weapon_exp(const ObjectType *o_ptr)
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 };
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);
250 void PlayerSkill::gain_range_weapon_exp(const ObjectType *o_ptr)
252 constexpr GainAmountList gain_amount_list{ 80, 25, 10, 2 };
253 constexpr GainAmountList others_gain_amount_list{ 8, 2, 0, 0 };
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);
264 void PlayerSkill::gain_martial_arts_skill_exp()
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);
272 void PlayerSkill::gain_two_weapon_skill_exp()
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);
280 void PlayerSkill::gain_riding_skill_exp_on_melee_attack(const monster_race *r_ptr)
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)
287 auto riding_level = r_info[this->player_ptr->current_floor_ptr->m_list[this->player_ptr->riding].r_idx].level;
290 if ((now_exp / 200 - 5) < r_ptr->level)
293 if ((now_exp / 100) < riding_level) {
294 if ((now_exp / 100 + 15) < riding_level)
295 inc += 1 + (riding_level - (now_exp / 100 + 15));
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);
304 void PlayerSkill::gain_riding_skill_exp_on_range_attack()
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)
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);
317 void PlayerSkill::gain_riding_skill_exp_on_fall_off_check(HIT_POINT dam)
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)
324 auto riding_level = r_info[this->player_ptr->current_floor_ptr->m_list[this->player_ptr->riding].r_idx].level;
326 if ((dam / 2 + riding_level) <= (now_exp / 30 + 10))
330 if ((now_exp / 100 + 15) < riding_level)
331 inc += 1 + (riding_level - (now_exp / 100 + 15));
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);
339 void PlayerSkill::gain_spell_skill_exp(int realm, int spell_idx)
341 if ((realm < 1) || ((static_cast<int>(std::size(mp_ptr->info)) < realm) && (realm != REALM_MUSIC) && (realm != REALM_HEX))) {
345 if (((spell_idx < 0) || (32 <= spell_idx)) ||
346 ((realm != this->player_ptr->realm1) && (realm != this->player_ptr->realm2))) {
350 constexpr GainAmountList gain_amount_list_first{ 60, 8, 2, 1 };
351 constexpr GainAmountList gain_amount_list_second{ 60, 8, 2, 0 };
353 const auto is_first_realm = (realm == this->player_ptr->realm1);
354 const auto *s_ptr = &mp_ptr->info[realm - 1][spell_idx];
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);
360 void PlayerSkill::gain_continuous_spell_skill_exp(int realm, int spell_idx)
362 if (((spell_idx < 0) || (32 <= spell_idx)) ||
363 ((realm != REALM_MUSIC) && (realm != REALM_HEX))) {
367 const auto *s_ptr = &technic_info[realm - MIN_TECHNIC][spell_idx];
369 const GainAmountList gain_amount_list{ 5, (one_in_(2) ? 1 : 0), (one_in_(5) ? 1 : 0), (one_in_(5) ? 1 : 0) };
371 gain_spell_skill_exp_aux(this->player_ptr, this->player_ptr->spell_exp[spell_idx], gain_amount_list, s_ptr->slevel);
374 PlayerSkillRank PlayerSkill::gain_spell_skill_exp_over_learning(int spell_idx)
376 if ((spell_idx < 0) || (static_cast<int>(std::size(this->player_ptr->spell_exp)) <= spell_idx)) {
377 return PlayerSkillRank::UNSKILLED;
380 auto &exp = this->player_ptr->spell_exp[spell_idx];
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;
388 exp += SPELL_EXP_EXPERT - SPELL_EXP_SKILLED;
390 } else if (exp >= SPELL_EXP_BEGINNER) {
391 exp = SPELL_EXP_SKILLED + (exp - SPELL_EXP_BEGINNER) * 2 / 3;
393 exp = SPELL_EXP_BEGINNER + exp / 3;
396 set_bits(this->player_ptr->update, PU_BONUS);
398 return PlayerSkill::spell_skill_rank(exp);
403 * Returns experience of a spell
404 * @param use_realm 魔法領域
405 * @param spell_idx 呪文ID
408 EXP PlayerSkill::exp_of_spell(int realm, int spell_idx) const
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];
424 * @brief 特別な武器スキル最大値の適用を行う
425 * 性格セクシーギャルの場合ムチスキルの最大値が達人になる
426 * 種族マーフォークの場合三叉槍とトライデントのスキルが達人になる
427 * (但し、いずれも職業がスペルマスターではない場合に限る)
429 void PlayerSkill::apply_special_weapon_skill_max_values()
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)) {
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);
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);
448 * @brief 武器スキル経験値を最大値で制限する
450 void PlayerSkill::limit_weapon_skills_by_max_value()
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]);