OSDN Git Service

Refactor monster_desc() to avoid sprintf(). To work with the refactored monster_desc...
[hengbandforosx/hengbandosx.git] / src / monster / monster-status-setter.cpp
1 #include "monster/monster-status-setter.h"
2 #include "avatar/avatar.h"
3 #include "cmd-visual/cmd-draw.h"
4 #include "core/player-redraw-types.h"
5 #include "core/player-update-types.h"
6 #include "core/speed-table.h"
7 #include "core/stuff-handler.h"
8 #include "core/window-redrawer.h"
9 #include "dungeon/quest-completion-checker.h"
10 #include "floor/cave.h"
11 #include "monster-floor/monster-move.h"
12 #include "monster-race/monster-kind-mask.h"
13 #include "monster-race/monster-race.h"
14 #include "monster-race/race-brightness-mask.h"
15 #include "monster-race/race-flags3.h"
16 #include "monster-race/race-flags7.h"
17 #include "monster-race/race-indice-types.h"
18 #include "monster/monster-describer.h"
19 #include "monster/monster-info.h"
20 #include "monster/monster-processor.h"
21 #include "monster/monster-status.h" //!< @todo 相互依存. 後で何とかする.
22 #include "monster/monster-util.h"
23 #include "monster/smart-learn-types.h"
24 #include "system/floor-type-definition.h"
25 #include "system/monster-entity.h"
26 #include "system/monster-race-info.h"
27 #include "system/player-type-definition.h"
28 #include "target/projection-path-calculator.h"
29 #include "view/display-messages.h"
30 #include "world/world.h"
31
32 /*!
33  * @brief モンスターをペットにする
34  * @param PlayerType プレイヤーへの参照ポインタ
35  * @param m_ptr モンスター情報構造体の参照ポインタ
36  */
37 void set_pet(PlayerType *player_ptr, MonsterEntity *m_ptr)
38 {
39     QuestCompletionChecker(player_ptr, m_ptr).complete();
40     m_ptr->mflag2.set(MonsterConstantFlagType::PET);
41     if (monraces_info[m_ptr->r_idx].kind_flags.has_none_of(alignment_mask)) {
42         m_ptr->sub_align = SUB_ALIGN_NEUTRAL;
43     }
44 }
45
46 /*!
47  * @brief モンスターを敵に回す
48  * Makes the monster hostile towards the player
49  * @param m_ptr モンスター情報構造体の参照ポインタ
50  */
51 void set_hostile(PlayerType *player_ptr, MonsterEntity *m_ptr)
52 {
53     if (player_ptr->phase_out) {
54         return;
55     }
56
57     m_ptr->mflag2.reset({ MonsterConstantFlagType::PET, MonsterConstantFlagType::FRIENDLY });
58 }
59
60 /*!
61  * @brief モンスターを怒らせる
62  * Anger the monster
63  * @param m_ptr モンスター情報構造体の参照ポインタ
64  */
65 void anger_monster(PlayerType *player_ptr, MonsterEntity *m_ptr)
66 {
67     if (player_ptr->phase_out || !m_ptr->is_friendly()) {
68         return;
69     }
70
71     const auto m_name = monster_desc(player_ptr, m_ptr, 0);
72     msg_format(_("%^sは怒った!", "%^s gets angry!"), m_name.data());
73     set_hostile(player_ptr, m_ptr);
74     chg_virtue(player_ptr, V_INDIVIDUALISM, 1);
75     chg_virtue(player_ptr, V_HONOUR, -1);
76     chg_virtue(player_ptr, V_JUSTICE, -1);
77     chg_virtue(player_ptr, V_COMPASSION, -1);
78 }
79
80 /*!
81  * @brief モンスターの時限ステータスリストを削除
82  * @param floor_ptr 現在フロアへの参照ポインタ
83  * @return m_idx モンスターの参照ID
84  * @return mproc_type 削除したいモンスターの時限ステータスID
85  */
86 static void mproc_remove(FloorType *floor_ptr, MONSTER_IDX m_idx, int mproc_type)
87 {
88     int mproc_idx = get_mproc_idx(floor_ptr, m_idx, mproc_type);
89     if (mproc_idx >= 0) {
90         floor_ptr->mproc_list[mproc_type][mproc_idx] = floor_ptr->mproc_list[mproc_type][--floor_ptr->mproc_max[mproc_type]];
91     }
92 }
93
94 /*!
95  * @brief モンスターの睡眠状態値をセットする。0で起きる。 /
96  * Set "m_ptr->mtimed[MTIMED_CSLEEP]", notice observable changes
97  * @param player_ptr プレイヤーへの参照ポインタ
98  * @param m_idx モンスター参照ID
99  * @param v セットする値
100  * @return 別途更新処理が必要な場合TRUEを返す
101  */
102 bool set_monster_csleep(PlayerType *player_ptr, MONSTER_IDX m_idx, int v)
103 {
104     auto *floor_ptr = player_ptr->current_floor_ptr;
105     auto *m_ptr = &floor_ptr->m_list[m_idx];
106     bool notice = false;
107     v = (v > 10000) ? 10000 : (v < 0) ? 0
108                                       : v;
109     if (v) {
110         if (!m_ptr->is_asleep()) {
111             mproc_add(floor_ptr, m_idx, MTIMED_CSLEEP);
112             notice = true;
113         }
114     } else {
115         if (m_ptr->is_asleep()) {
116             mproc_remove(floor_ptr, m_idx, MTIMED_CSLEEP);
117             notice = true;
118         }
119     }
120
121     m_ptr->mtimed[MTIMED_CSLEEP] = (int16_t)v;
122     if (!notice) {
123         return false;
124     }
125
126     if (m_ptr->ml) {
127         if (player_ptr->health_who == m_idx) {
128             player_ptr->redraw |= PR_HEALTH;
129         }
130
131         if (player_ptr->riding == m_idx) {
132             player_ptr->redraw |= PR_UHEALTH;
133         }
134     }
135
136     if (monraces_info[m_ptr->r_idx].brightness_flags.has_any_of(has_ld_mask)) {
137         player_ptr->update |= PU_MON_LITE;
138     }
139
140     return true;
141 }
142
143 /*!
144  * @brief モンスターの加速状態値をセット /
145  * Set "m_ptr->mtimed[MTIMED_FAST]", notice observable changes
146  * @param player_ptr プレイヤーへの参照ポインタ
147  * @param m_idx モンスター参照ID
148  * @param v セットする値
149  * @return 別途更新処理が必要な場合TRUEを返す
150  */
151 bool set_monster_fast(PlayerType *player_ptr, MONSTER_IDX m_idx, int v)
152 {
153     auto *floor_ptr = player_ptr->current_floor_ptr;
154     auto *m_ptr = &floor_ptr->m_list[m_idx];
155     bool notice = false;
156     v = (v > 200) ? 200 : (v < 0) ? 0
157                                   : v;
158     if (v) {
159         if (!m_ptr->is_accelerated()) {
160             mproc_add(floor_ptr, m_idx, MTIMED_FAST);
161             notice = true;
162         }
163     } else {
164         if (m_ptr->is_accelerated()) {
165             mproc_remove(floor_ptr, m_idx, MTIMED_FAST);
166             notice = true;
167         }
168     }
169
170     m_ptr->mtimed[MTIMED_FAST] = (int16_t)v;
171     if (!notice) {
172         return false;
173     }
174
175     if ((player_ptr->riding == m_idx) && !player_ptr->leaving) {
176         player_ptr->update |= PU_BONUS;
177     }
178
179     return true;
180 }
181
182 /*
183  * Set "m_ptr->mtimed[MTIMED_SLOW]", notice observable changes
184  */
185 bool set_monster_slow(PlayerType *player_ptr, MONSTER_IDX m_idx, int v)
186 {
187     auto *floor_ptr = player_ptr->current_floor_ptr;
188     auto *m_ptr = &floor_ptr->m_list[m_idx];
189     bool notice = false;
190     v = (v > 200) ? 200 : (v < 0) ? 0
191                                   : v;
192     if (v) {
193         if (!m_ptr->is_decelerated()) {
194             mproc_add(floor_ptr, m_idx, MTIMED_SLOW);
195             notice = true;
196         }
197     } else {
198         if (m_ptr->is_decelerated()) {
199             mproc_remove(floor_ptr, m_idx, MTIMED_SLOW);
200             notice = true;
201         }
202     }
203
204     m_ptr->mtimed[MTIMED_SLOW] = (int16_t)v;
205     if (!notice) {
206         return false;
207     }
208
209     if ((player_ptr->riding == m_idx) && !player_ptr->leaving) {
210         player_ptr->update |= PU_BONUS;
211     }
212
213     return true;
214 }
215
216 /*!
217  * @brief モンスターの朦朧状態値をセット /
218  * Set "m_ptr->mtimed[MTIMED_STUNNED]", notice observable changes
219  * @param player_ptr プレイヤーへの参照ポインタ
220  * @param m_idx モンスター参照ID
221  * @param v セットする値
222  * @return 別途更新処理が必要な場合TRUEを返す
223  */
224 bool set_monster_stunned(PlayerType *player_ptr, MONSTER_IDX m_idx, int v)
225 {
226     auto *floor_ptr = player_ptr->current_floor_ptr;
227     auto *m_ptr = &floor_ptr->m_list[m_idx];
228     bool notice = false;
229     v = (v > 200) ? 200 : (v < 0) ? 0
230                                   : v;
231     if (v) {
232         if (!m_ptr->is_stunned()) {
233             mproc_add(floor_ptr, m_idx, MTIMED_STUNNED);
234             notice = true;
235         }
236     } else {
237         if (m_ptr->is_stunned()) {
238             mproc_remove(floor_ptr, m_idx, MTIMED_STUNNED);
239             notice = true;
240         }
241     }
242
243     m_ptr->mtimed[MTIMED_STUNNED] = (int16_t)v;
244     return notice;
245 }
246
247 /*!
248  * @brief モンスターの混乱状態値をセット /
249  * Set "m_ptr->mtimed[MTIMED_CONFUSED]", notice observable changes
250  * @param player_ptr プレイヤーへの参照ポインタ
251  * @param m_idx モンスター参照ID
252  * @param v セットする値
253  * @return 別途更新処理が必要な場合TRUEを返す
254  */
255 bool set_monster_confused(PlayerType *player_ptr, MONSTER_IDX m_idx, int v)
256 {
257     auto *floor_ptr = player_ptr->current_floor_ptr;
258     auto *m_ptr = &floor_ptr->m_list[m_idx];
259     bool notice = false;
260     v = (v > 200) ? 200 : (v < 0) ? 0
261                                   : v;
262     if (v) {
263         if (!m_ptr->is_confused()) {
264             mproc_add(floor_ptr, m_idx, MTIMED_CONFUSED);
265             notice = true;
266         }
267     } else {
268         if (m_ptr->is_confused()) {
269             mproc_remove(floor_ptr, m_idx, MTIMED_CONFUSED);
270             notice = true;
271         }
272     }
273
274     m_ptr->mtimed[MTIMED_CONFUSED] = (int16_t)v;
275     return notice;
276 }
277
278 /*!
279  * @brief モンスターの恐慌状態値をセット /
280  * Set "m_ptr->mtimed[MTIMED_MONFEAR]", notice observable changes
281  * @param player_ptr プレイヤーへの参照ポインタ
282  * @param m_idx モンスター参照ID
283  * @param v セットする値
284  * @return 別途更新処理が必要な場合TRUEを返す
285  */
286 bool set_monster_monfear(PlayerType *player_ptr, MONSTER_IDX m_idx, int v)
287 {
288     auto *floor_ptr = player_ptr->current_floor_ptr;
289     auto *m_ptr = &floor_ptr->m_list[m_idx];
290     bool notice = false;
291     v = (v > 200) ? 200 : (v < 0) ? 0
292                                   : v;
293     if (v) {
294         if (!m_ptr->is_fearful()) {
295             mproc_add(floor_ptr, m_idx, MTIMED_MONFEAR);
296             notice = true;
297         }
298     } else {
299         if (m_ptr->is_fearful()) {
300             mproc_remove(floor_ptr, m_idx, MTIMED_MONFEAR);
301             notice = true;
302         }
303     }
304
305     m_ptr->mtimed[MTIMED_MONFEAR] = (int16_t)v;
306
307     if (!notice) {
308         return false;
309     }
310
311     if (m_ptr->ml) {
312         if (player_ptr->health_who == m_idx) {
313             player_ptr->redraw |= PR_HEALTH;
314         }
315
316         if (player_ptr->riding == m_idx) {
317             player_ptr->redraw |= PR_UHEALTH;
318         }
319     }
320
321     return true;
322 }
323
324 /*!
325  * @brief モンスターの無敵状態値をセット /
326  * Set "m_ptr->mtimed[MTIMED_INVULNER]", notice observable changes
327  * @param player_ptr プレイヤーへの参照ポインタ
328  * @param m_idx モンスター参照ID
329  * @param v セットする値
330  * @param energy_need TRUEならば無敵解除時に行動ターン消費を行う
331  * @return 別途更新処理が必要な場合TRUEを返す
332  */
333 bool set_monster_invulner(PlayerType *player_ptr, MONSTER_IDX m_idx, int v, bool energy_need)
334 {
335     auto *floor_ptr = player_ptr->current_floor_ptr;
336     auto *m_ptr = &floor_ptr->m_list[m_idx];
337     bool notice = false;
338     v = (v > 200) ? 200 : (v < 0) ? 0
339                                   : v;
340     if (v) {
341         if (!m_ptr->is_invulnerable()) {
342             mproc_add(floor_ptr, m_idx, MTIMED_INVULNER);
343             notice = true;
344         }
345     } else {
346         if (m_ptr->is_invulnerable()) {
347             mproc_remove(floor_ptr, m_idx, MTIMED_INVULNER);
348             if (energy_need && !player_ptr->wild_mode) {
349                 m_ptr->energy_need += ENERGY_NEED();
350             }
351             notice = true;
352         }
353     }
354
355     m_ptr->mtimed[MTIMED_INVULNER] = (int16_t)v;
356     if (!notice) {
357         return false;
358     }
359
360     if (m_ptr->ml) {
361         if (player_ptr->health_who == m_idx) {
362             player_ptr->redraw |= PR_HEALTH;
363         }
364
365         if (player_ptr->riding == m_idx) {
366             player_ptr->redraw |= PR_UHEALTH;
367         }
368     }
369
370     return true;
371 }
372
373 /*!
374  * @brief モンスターの時間停止処理
375  * @param player_ptr プレイヤーへの参照ポインタ
376  * @param num 時間停止を行った敵が行動できる回数
377  * @param who 時間停止を行う敵の種族番号
378  * @param vs_player TRUEならば時間停止開始処理を行う
379  * @return 時間停止が行われている状態ならばTRUEを返す
380  */
381 bool set_monster_timewalk(PlayerType *player_ptr, int num, MonsterRaceId who, bool vs_player)
382 {
383     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[hack_m_idx];
384     if (w_ptr->timewalk_m_idx) {
385         return false;
386     }
387
388     if (vs_player) {
389         const auto m_name = monster_desc(player_ptr, m_ptr, 0);
390
391         concptr mes;
392         switch (who) {
393         case MonsterRaceId::DIO:
394             mes = _("「『ザ・ワールド』! 時は止まった!」", "%s yells 'The World! Time has stopped!'");
395             break;
396         case MonsterRaceId::WONG:
397             mes = _("「時よ!」", "%s yells 'Time!'");
398             break;
399         case MonsterRaceId::DIAVOLO:
400             mes = _("『キング・クリムゾン』!", "%s yells 'King Crison!'");
401             break;
402         default:
403             mes = "hek!";
404             break;
405         }
406
407         msg_format(mes, m_name.data());
408         msg_print(nullptr);
409     }
410
411     w_ptr->timewalk_m_idx = hack_m_idx;
412     if (vs_player) {
413         do_cmd_redraw(player_ptr);
414     }
415
416     while (num--) {
417         if (!m_ptr->is_valid()) {
418             break;
419         }
420
421         process_monster(player_ptr, w_ptr->timewalk_m_idx);
422         reset_target(m_ptr);
423         handle_stuff(player_ptr);
424         if (vs_player) {
425             term_xtra(TERM_XTRA_DELAY, 500);
426         }
427     }
428
429     player_ptr->redraw |= PR_MAP;
430     player_ptr->update |= PU_MONSTERS;
431     player_ptr->window_flags |= PW_OVERHEAD | PW_DUNGEON;
432     w_ptr->timewalk_m_idx = 0;
433     if (vs_player || (player_has_los_bold(player_ptr, m_ptr->fy, m_ptr->fx) && projectable(player_ptr, player_ptr->y, player_ptr->x, m_ptr->fy, m_ptr->fx))) {
434         concptr mes;
435         switch (who) {
436         case MonsterRaceId::DIAVOLO:
437             mes = _("これが我が『キング・クリムゾン』の能力! 『時間を消し去って』飛び越えさせた…!!",
438                 "This is the ability of my 'King Crimson'! 'Erase the time' and let it jump over... !!");
439             break;
440         default:
441             mes = _("「時は動きだす…」", "You feel time flowing around you once more.");
442             break;
443         }
444
445         msg_print(mes);
446         msg_print(nullptr);
447     }
448
449     handle_stuff(player_ptr);
450     return true;
451 }