OSDN Git Service

[Refactor] #1496 Defined has_additional_speed() method in PlayerClass
[hengbandforosx/hengbandosx.git] / src / player-base / player-class.cpp
1 /*!
2  * @brief プレイヤーの職業クラスに基づく耐性・能力の判定処理等を行うクラス
3  * @date 2021/09/08
4  * @author Hourier
5  */
6 #include "player-base/player-class.h"
7 #include "core/player-redraw-types.h"
8 #include "core/player-update-types.h"
9 #include "inventory/inventory-slot-types.h"
10 #include "mind/mind-elementalist.h"
11 #include "player-info/bard-data-type.h"
12 #include "player-info/bluemage-data-type.h"
13 #include "player-info/equipment-info.h"
14 #include "player-info/force-trainer-data-type.h"
15 #include "player-info/magic-eater-data-type.h"
16 #include "player-info/mane-data-type.h"
17 #include "player-info/monk-data-type.h"
18 #include "player-info/ninja-data-type.h"
19 #include "player-info/samurai-data-type.h"
20 #include "player-info/smith-data-type.h"
21 #include "player-info/sniper-data-type.h"
22 #include "player-info/spell-hex-data-type.h"
23 #include "player/attack-defense-types.h"
24 #include "player/player-status-flags.h"
25 #include "player/special-defense-types.h"
26 #include "realm/realm-types.h"
27 #include "status/action-setter.h"
28 #include "system/object-type-definition.h"
29 #include "system/player-type-definition.h"
30 #include "util/bit-flags-calculator.h"
31
32 PlayerClass::PlayerClass(PlayerType *player_ptr)
33     : player_ptr(player_ptr)
34 {
35 }
36
37 bool PlayerClass::equals(PlayerClassType type) const
38 {
39     return this->player_ptr->pclass == type;
40 }
41
42 /*!
43  * @brief プレイヤーの職業により得られる特性フラグを返す
44  */
45 TrFlags PlayerClass::tr_flags() const
46 {
47     TrFlags flags;
48     const auto plev = this->player_ptr->lev;
49
50     switch (this->player_ptr->pclass) {
51     case PlayerClassType::WARRIOR: {
52         if (plev > 29)
53             flags.set(TR_RES_FEAR);
54         if (plev > 44)
55             flags.set(TR_REGEN);
56
57         break;
58     }
59     case PlayerClassType::SAMURAI: {
60         if (plev > 29)
61             flags.set(TR_RES_FEAR);
62
63         break;
64     }
65     case PlayerClassType::PALADIN: {
66         if (plev > 39)
67             flags.set(TR_RES_FEAR);
68
69         break;
70     }
71     case PlayerClassType::CHAOS_WARRIOR: {
72         if (plev > 29)
73             flags.set(TR_RES_CHAOS);
74         if (plev > 39)
75             flags.set(TR_RES_FEAR);
76
77         break;
78     }
79     case PlayerClassType::MONK:
80     case PlayerClassType::FORCETRAINER: {
81         if ((plev > 9) && !heavy_armor(this->player_ptr))
82             flags.set(TR_SPEED);
83         if ((plev > 24) && !heavy_armor(this->player_ptr))
84             flags.set(TR_FREE_ACT);
85
86         break;
87     }
88     case PlayerClassType::NINJA: {
89         if (heavy_armor(this->player_ptr)) {
90             flags.set(TR_SPEED);
91         } else {
92             if ((!this->player_ptr->inventory_list[INVEN_MAIN_HAND].k_idx || can_attack_with_main_hand(this->player_ptr)) && (!this->player_ptr->inventory_list[INVEN_SUB_HAND].k_idx || can_attack_with_sub_hand(this->player_ptr)))
93                 flags.set(TR_SPEED);
94             if (plev > 24 && !this->player_ptr->is_icky_wield[0] && !this->player_ptr->is_icky_wield[1])
95                 flags.set(TR_FREE_ACT);
96         }
97
98         flags.set(TR_SLOW_DIGEST);
99         flags.set(TR_RES_FEAR);
100         if (plev > 19)
101             flags.set(TR_RES_POIS);
102         if (plev > 24)
103             flags.set(TR_SUST_DEX);
104         if (plev > 29)
105             flags.set(TR_SEE_INVIS);
106
107         break;
108     }
109     case PlayerClassType::MINDCRAFTER: {
110         if (plev > 9)
111             flags.set(TR_RES_FEAR);
112         if (plev > 19)
113             flags.set(TR_SUST_WIS);
114         if (plev > 29)
115             flags.set(TR_RES_CONF);
116         if (plev > 39)
117             flags.set(TR_TELEPATHY);
118
119         break;
120     }
121     case PlayerClassType::BARD: {
122         flags.set(TR_RES_SOUND);
123         break;
124     }
125     case PlayerClassType::BERSERKER: {
126         flags.set(TR_SUST_STR);
127         flags.set(TR_SUST_DEX);
128         flags.set(TR_SUST_CON);
129         flags.set(TR_REGEN);
130         flags.set(TR_FREE_ACT);
131         flags.set(TR_SPEED);
132         if (plev > 39)
133             flags.set(TR_REFLECT);
134
135         break;
136     }
137     case PlayerClassType::MIRROR_MASTER: {
138         if (plev > 39)
139             flags.set(TR_REFLECT);
140
141         break;
142     }
143     case PlayerClassType::ELEMENTALIST:
144         if (has_element_resist(this->player_ptr, ElementRealmType::FIRE, 1))
145             flags.set(TR_RES_FIRE);
146         if (has_element_resist(this->player_ptr, ElementRealmType::FIRE, 30))
147             flags.set(TR_IM_FIRE);
148         if (has_element_resist(this->player_ptr, ElementRealmType::ICE, 1))
149             flags.set(TR_RES_COLD);
150         if (has_element_resist(this->player_ptr, ElementRealmType::ICE, 30))
151             flags.set(TR_IM_COLD);
152         if (has_element_resist(this->player_ptr, ElementRealmType::SKY, 1))
153             flags.set(TR_RES_ELEC);
154         if (has_element_resist(this->player_ptr, ElementRealmType::SKY, 30))
155             flags.set(TR_IM_ELEC);
156         if (has_element_resist(this->player_ptr, ElementRealmType::SEA, 1))
157             flags.set(TR_RES_ACID);
158         if (has_element_resist(this->player_ptr, ElementRealmType::SEA, 30))
159             flags.set(TR_IM_ACID);
160         if (has_element_resist(this->player_ptr, ElementRealmType::DARKNESS, 1))
161             flags.set(TR_RES_DARK);
162         if (has_element_resist(this->player_ptr, ElementRealmType::DARKNESS, 30))
163             flags.set(TR_RES_NETHER);
164         if (has_element_resist(this->player_ptr, ElementRealmType::CHAOS, 1))
165             flags.set(TR_RES_CONF);
166         if (has_element_resist(this->player_ptr, ElementRealmType::CHAOS, 30))
167             flags.set(TR_RES_CHAOS);
168         if (has_element_resist(this->player_ptr, ElementRealmType::EARTH, 1))
169             flags.set(TR_RES_SHARDS);
170         if (has_element_resist(this->player_ptr, ElementRealmType::EARTH, 30))
171             flags.set(TR_REFLECT);
172         if (has_element_resist(this->player_ptr, ElementRealmType::DEATH, 1))
173             flags.set(TR_RES_POIS);
174         if (has_element_resist(this->player_ptr, ElementRealmType::DEATH, 30))
175             flags.set(TR_RES_DISEN);
176         break;
177     default:
178         break;
179     }
180
181     return flags;
182 }
183
184 TrFlags PlayerClass::stance_tr_flags() const
185 {
186     TrFlags flags;
187
188     switch (this->get_samurai_stance()) {
189     case SamuraiStanceType::FUUJIN:
190         if (!this->player_ptr->blind) {
191             flags.set(TR_REFLECT);
192         }
193         break;
194     case SamuraiStanceType::MUSOU:
195         flags.set({ TR_RES_ACID, TR_RES_ELEC, TR_RES_FIRE, TR_RES_COLD, TR_RES_POIS });
196         flags.set({ TR_REFLECT, TR_RES_FEAR, TR_RES_LITE, TR_RES_DARK, TR_RES_BLIND, TR_RES_CONF,
197             TR_RES_SOUND, TR_RES_SHARDS, TR_RES_NETHER, TR_RES_NEXUS, TR_RES_CHAOS, TR_RES_DISEN,
198             TR_RES_WATER, TR_RES_TIME, TR_RES_CURSE });
199         flags.set({ TR_HOLD_EXP, TR_FREE_ACT, TR_LEVITATION, TR_LITE_1, TR_SEE_INVIS, TR_TELEPATHY, TR_SLOW_DIGEST, TR_REGEN });
200         flags.set({ TR_SH_FIRE, TR_SH_ELEC, TR_SH_COLD });
201         flags.set({ TR_SUST_STR, TR_SUST_INT, TR_SUST_WIS, TR_SUST_DEX, TR_SUST_CON, TR_SUST_CHR });
202         break;
203     case SamuraiStanceType::KOUKIJIN:
204         flags.set({ TR_VUL_ACID, TR_VUL_ELEC, TR_VUL_FIRE, TR_VUL_COLD });
205         break;
206     default:
207         break;
208     }
209
210     switch (this->get_monk_stance()) {
211     case MonkStanceType::GENBU:
212         flags.set(TR_REFLECT);
213         break;
214     case MonkStanceType::SUZAKU:
215         flags.set(TR_LEVITATION);
216         break;
217     case MonkStanceType::SEIRYU:
218         flags.set({ TR_RES_ACID, TR_RES_ELEC, TR_RES_FIRE, TR_RES_COLD, TR_RES_POIS });
219         flags.set({ TR_SH_FIRE, TR_SH_ELEC, TR_SH_COLD });
220         flags.set(TR_LEVITATION);
221         break;
222     default:
223         break;
224     }
225
226     return flags;
227 }
228
229 bool PlayerClass::has_stun_immunity() const
230 {
231     return (this->player_ptr->pclass == PlayerClassType::BERSERKER) && (this->player_ptr->lev > 34);
232 }
233
234 /*!
235  * @brief 加速ボーナスのある種族かを返す
236  * @return 加速ボーナスのある種族か否か
237  * @details 
238  * 種族と職業の両方で特性による加速が得られる場合、重複して加速することはない.
239  * 代りに経験値補正が軽くなる.
240  */
241 bool PlayerClass::has_additional_speed() const
242 {
243     auto has_additional_speed = this->equals(PlayerClassType::MONK);
244     has_additional_speed |= this->equals(PlayerClassType::FORCETRAINER);
245     has_additional_speed |= this->equals(PlayerClassType::NINJA);
246     return has_additional_speed;
247 }
248
249 bool PlayerClass::is_soldier() const
250 {
251     auto is_soldier = this->equals(PlayerClassType::WARRIOR);
252     is_soldier |= this->equals(PlayerClassType::ARCHER);
253     is_soldier |= this->equals(PlayerClassType::CAVALRY);
254     is_soldier |= this->equals(PlayerClassType::BERSERKER);
255     is_soldier |= this->equals(PlayerClassType::SMITH);
256     return is_soldier;
257 }
258
259 bool PlayerClass::is_wizard() const
260 {
261     auto is_wizard = this->equals(PlayerClassType::MAGE);
262     is_wizard |= this->equals(PlayerClassType::HIGH_MAGE);
263     is_wizard |= this->equals(PlayerClassType::SORCERER);
264     is_wizard |= this->equals(PlayerClassType::MAGIC_EATER);
265     is_wizard |= this->equals(PlayerClassType::BLUE_MAGE);
266     is_wizard |= this->equals(PlayerClassType::ELEMENTALIST);
267     return is_wizard;
268 }
269
270 bool PlayerClass::lose_balance()
271 {
272     if (this->player_ptr->pclass != PlayerClassType::SAMURAI) {
273         return false;
274     }
275
276     if (this->samurai_stance_is(SamuraiStanceType::NONE)) {
277         return false;
278     }
279
280     this->set_samurai_stance(SamuraiStanceType::NONE);
281     this->player_ptr->update |= PU_BONUS;
282     this->player_ptr->update |= PU_MONSTERS;
283     this->player_ptr->redraw |= PR_STATE;
284     this->player_ptr->redraw |= PR_STATUS;
285     this->player_ptr->action = ACTION_NONE;
286     return true;
287 }
288
289 SamuraiStanceType PlayerClass::get_samurai_stance() const
290 {
291     auto samurai_data = this->get_specific_data<samurai_data_type>();
292     if (!samurai_data) {
293         return SamuraiStanceType::NONE;
294     }
295
296     return samurai_data->stance;
297 }
298
299 bool PlayerClass::samurai_stance_is(SamuraiStanceType stance) const
300 {
301     return this->get_samurai_stance() == stance;
302 }
303
304 /**
305  * @brief 剣術家の型を崩す
306  *
307  * @param stance_list 崩す型を指定する。取っている型が指定された型に含まれない場合は崩さない。
308  */
309 void PlayerClass::break_samurai_stance(std::initializer_list<SamuraiStanceType> stance_list)
310 {
311     auto samurai_data = this->get_specific_data<samurai_data_type>();
312     if (!samurai_data) {
313         return;
314     }
315
316     for (auto stance : stance_list) {
317         if (samurai_data->stance == stance) {
318             set_action(player_ptr, ACTION_NONE);
319             samurai_data->stance = SamuraiStanceType::NONE;
320             break;
321         }
322     }
323 }
324
325 void PlayerClass::set_samurai_stance(SamuraiStanceType stance) const
326 {
327     auto samurai_data = this->get_specific_data<samurai_data_type>();
328     if (!samurai_data) {
329         return;
330     }
331
332     samurai_data->stance = stance;
333 }
334
335 MonkStanceType PlayerClass::get_monk_stance() const
336 {
337     auto monk_data = this->get_specific_data<monk_data_type>();
338     if (!monk_data) {
339         return MonkStanceType::NONE;
340     }
341
342     return monk_data->stance;
343 }
344
345 bool PlayerClass::monk_stance_is(MonkStanceType stance) const
346 {
347     return this->get_monk_stance() == stance;
348 }
349
350 void PlayerClass::set_monk_stance(MonkStanceType stance) const
351 {
352     auto monk_data = this->get_specific_data<monk_data_type>();
353     if (!monk_data) {
354         return;
355     }
356
357     monk_data->stance = stance;
358 }
359
360 /**
361  * @brief プレイヤーの職業にで使用する職業固有データ領域を初期化する
362  * @details 事前条件: PlayerType の職業や領域が決定済みであること
363  */
364 void PlayerClass::init_specific_data()
365 {
366     switch (this->player_ptr->pclass) {
367     case PlayerClassType::SMITH:
368         this->player_ptr->class_specific_data = std::make_shared<smith_data_type>();
369         break;
370     case PlayerClassType::FORCETRAINER:
371         this->player_ptr->class_specific_data = std::make_shared<force_trainer_data_type>();
372         break;
373     case PlayerClassType::BLUE_MAGE:
374         this->player_ptr->class_specific_data = std::make_shared<bluemage_data_type>();
375         break;
376     case PlayerClassType::MAGIC_EATER:
377         this->player_ptr->class_specific_data = std::make_shared<magic_eater_data_type>();
378         break;
379     case PlayerClassType::BARD:
380         this->player_ptr->class_specific_data = std::make_shared<bard_data_type>();
381         break;
382     case PlayerClassType::IMITATOR:
383         this->player_ptr->class_specific_data = std::make_shared<mane_data_type>();
384         break;
385     case PlayerClassType::SNIPER:
386         this->player_ptr->class_specific_data = std::make_shared<sniper_data_type>();
387         break;
388     case PlayerClassType::SAMURAI:
389         this->player_ptr->class_specific_data = std::make_shared<samurai_data_type>();
390         break;
391     case PlayerClassType::MONK:
392         this->player_ptr->class_specific_data = std::make_shared<monk_data_type>();
393         break;
394     case PlayerClassType::NINJA:
395         this->player_ptr->class_specific_data = std::make_shared<ninja_data_type>();
396         break;
397     case PlayerClassType::HIGH_MAGE:
398         if (this->player_ptr->realm1 == REALM_HEX) {
399             this->player_ptr->class_specific_data = std::make_shared<spell_hex_data_type>();
400         } else {
401             this->player_ptr->class_specific_data = no_class_specific_data();
402         }
403         break;
404     default:
405         this->player_ptr->class_specific_data = no_class_specific_data();
406         break;
407     }
408 }