OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / player-base / player-race.cpp
1 /*!
2  * @brief プレイヤーの種族に基づく耐性・能力の判定処理等を行うクラス
3  * @date 2021/09/08
4  * @author Hourier
5  * @details PlayerRaceからPlayerClassへの依存はあるが、逆は依存させないこと.
6  */
7 #include "player-base/player-race.h"
8 #include "grid/feature-flag-types.h"
9 #include "player-base/player-class.h"
10 #include "player-info/mimic-info-table.h"
11 #include "player/race-info-table.h"
12 #include "system/angband-exceptions.h"
13 #include "system/floor-type-definition.h"
14 #include "system/grid-type-definition.h"
15 #include "system/player-type-definition.h"
16 #include "system/terrain-type-definition.h"
17 #include "util/bit-flags-calculator.h"
18
19 /*!
20  * @brief Construct a new Player Race:: Player Race object
21  *
22  * @param base_race true の場合、仮に変身している場合でも元の種族について扱う。 false の場合変身している種族について扱う。
23  * 引数を省略した場合は false
24  */
25 PlayerRace::PlayerRace(PlayerType *player_ptr, bool base_race)
26     : player_ptr(player_ptr)
27     , base_race(base_race)
28 {
29 }
30
31 /*!
32  * @brief 種族固有の特性フラグを取得する
33  * @return
34  */
35 TrFlags PlayerRace::tr_flags() const
36 {
37     TrFlags flags;
38
39     auto race_ptr = this->get_info();
40     if (race_ptr->infra > 0) {
41         flags.set(TR_INFRA);
42     }
43
44     for (auto &cond : race_ptr->extra_flags) {
45         if (this->player_ptr->lev < cond.level) {
46             continue;
47         }
48         if (cond.pclass.has_value()) {
49             auto is_class_equal = PlayerClass(this->player_ptr).equals(cond.pclass.value());
50             if (cond.not_class && is_class_equal) {
51                 continue;
52             }
53             if (!cond.not_class && !is_class_equal) {
54                 continue;
55             }
56         }
57
58         flags.set(cond.type);
59     }
60
61     return flags;
62 }
63
64 /*!
65  * @brief プレイヤーの種族情報テーブルへのポインタを取得する
66  *
67  * @return プレイヤーの種族情報テーブルへのポインタ
68  */
69 const player_race_info *PlayerRace::get_info() const
70 {
71     if (this->base_race) {
72         return &race_info[enum2i(this->player_ptr->prace)];
73     }
74
75     switch (this->player_ptr->mimic_form) {
76     case MimicKindType::NONE:
77         return &race_info[enum2i(this->player_ptr->prace)];
78     case MimicKindType::DEMON:
79     case MimicKindType::DEMON_LORD:
80     case MimicKindType::VAMPIRE:
81         return &mimic_info.at(this->player_ptr->mimic_form);
82     default:
83         THROW_EXCEPTION(std::logic_error, "Invalid MimicKindType was specified!");
84     }
85 }
86
87 /*!
88  * @brief 種族の生命形態を返す
89  * @param player_ptr プレイヤー情報への参照ポインタ
90  * @return 生命形態
91  */
92 PlayerRaceLifeType PlayerRace::life() const
93 {
94     return this->get_info()->life;
95 }
96
97 /*!
98  * @brief 種族の食料形態を返す
99  * @param player_ptr プレイヤー情報への参照ポインタ
100  * @param base_race ミミック中も元種族の情報を返すならtrue
101  * @return 食料形態
102  */
103 PlayerRaceFoodType PlayerRace::food() const
104 {
105     return this->get_info()->food;
106 }
107
108 bool PlayerRace::is_mimic_nonliving() const
109 {
110     constexpr int nonliving_flag = 1;
111     return any_bits(mimic_info.at(this->player_ptr->mimic_form).choice, nonliving_flag);
112 }
113
114 bool PlayerRace::has_cut_immunity() const
115 {
116     auto cut_immunity = this->equals(PlayerRaceType::GOLEM);
117     cut_immunity |= this->equals(PlayerRaceType::SKELETON);
118     cut_immunity |= this->equals(PlayerRaceType::SPECTRE);
119     cut_immunity |= this->equals(PlayerRaceType::ZOMBIE) && (this->player_ptr->lev > 11);
120     return cut_immunity;
121 }
122
123 bool PlayerRace::has_stun_immunity() const
124 {
125     return this->equals(PlayerRaceType::GOLEM);
126 }
127
128 bool PlayerRace::equals(PlayerRaceType prace) const
129 {
130     return (this->player_ptr->mimic_form == MimicKindType::NONE) && (this->player_ptr->prace == prace);
131 }
132
133 /*!
134  * @brief 速度計算 - 種族
135  * @return 速度値の増減分
136  * @details
137  * ** クラッコンと妖精に加算(+レベル/10)
138  * ** 悪魔変化/吸血鬼変化で加算(+3)
139  * ** 魔王変化で加算(+5)
140  * ** マーフォークがFF_WATER地形にいれば加算(+2+レベル/10)
141  * ** そうでなく浮遊を持っていないなら減算(-2)
142  */
143 int16_t PlayerRace::speed() const
144 {
145     int16_t result = 0;
146
147     if (this->equals(PlayerRaceType::KLACKON) || this->equals(PlayerRaceType::SPRITE)) {
148         result += (this->player_ptr->lev) / 10;
149     }
150
151     if (this->equals(PlayerRaceType::MERFOLK)) {
152         auto *floor_ptr = this->player_ptr->current_floor_ptr;
153         auto *f_ptr = &terrains_info[floor_ptr->grid_array[this->player_ptr->y][this->player_ptr->x].feat];
154         if (f_ptr->flags.has(TerrainCharacteristics::WATER)) {
155             result += (2 + this->player_ptr->lev / 10);
156         } else if (!this->player_ptr->levitation) {
157             result -= 2;
158         }
159     }
160
161     switch (this->player_ptr->mimic_form) {
162     case MimicKindType::NONE:
163         return result;
164     case MimicKindType::DEMON:
165         return result + 3;
166     case MimicKindType::DEMON_LORD:
167         return result + 5;
168     case MimicKindType::VAMPIRE:
169         return result + 3;
170     default:
171         THROW_EXCEPTION(std::logic_error, "Invalid MimicKindType was specified!");
172     }
173 }
174
175 /*!
176  * @brief 腕力補正計算 - 種族
177  * @return 腕力補正値
178  * @details
179  * * 種族による腕力修正値。
180  * * エントはレベル26,41,46到達ごとに加算(+1)
181  */
182 int16_t PlayerRace::additional_strength() const
183 {
184     int16_t result = 0;
185
186     if (this->equals(PlayerRaceType::ENT)) {
187         if (this->player_ptr->lev > 25) {
188             result++;
189         }
190         if (this->player_ptr->lev > 40) {
191             result++;
192         }
193         if (this->player_ptr->lev > 45) {
194             result++;
195         }
196     }
197
198     return result;
199 }
200
201 /*!
202  * @brief 器用さ補正計算 - 種族
203  * @return 器用さ補正値
204  * @details
205  * * 種族による器用さ修正値。
206  * * エントはレベル26,41,46到達ごとに減算(-1)
207  */
208 int16_t PlayerRace::additional_dexterity() const
209 {
210     int16_t result = 0;
211
212     if (this->equals(PlayerRaceType::ENT)) {
213         if (this->player_ptr->lev > 25) {
214             result--;
215         }
216         if (this->player_ptr->lev > 40) {
217             result--;
218         }
219         if (this->player_ptr->lev > 45) {
220             result--;
221         }
222     }
223
224     return result;
225 }
226
227 /*!
228  * @brief 耐久力補正計算 - 種族
229  * @return 耐久力補正値
230  * @details
231  * * 種族による耐久力修正値。
232  * * エントはレベル26,41,46到達ごとに加算(+1)
233  */
234 int16_t PlayerRace::additional_constitution() const
235 {
236     int16_t result = 0;
237
238     if (PlayerRace(this->player_ptr).equals(PlayerRaceType::ENT)) {
239         if (this->player_ptr->lev > 25) {
240             result++;
241         }
242         if (this->player_ptr->lev > 40) {
243             result++;
244         }
245         if (this->player_ptr->lev > 45) {
246             result++;
247         }
248     }
249
250     return result;
251 }
252
253 /*!
254  * @brief 救援召喚時のモンスターシンボルを返す
255  * @param player_ptr プレイヤー情報への参照ポインタ
256  * @return シンボル文字
257  */
258 char PlayerRace::get_summon_symbol() const
259 {
260     auto symbol = 'N';
261     auto mmc_ptr = this->get_info();
262
263     auto l = strlen(mmc_ptr->symbol);
264     auto mul = 1;
265     for (size_t i = 0; i < l; i++) {
266         if (one_in_(mul)) {
267             symbol = mmc_ptr->symbol[i];
268         }
269         mul *= 13;
270     }
271     return symbol;
272 }