OSDN Git Service

[Refactor] #40569 Separated floor-type-definition.h from floor.h
[hengband/hengband.git] / src / monster / monster-list.c
1 /*!
2  * @brief モンスター処理 / misc code for monsters
3  * @date 2014/07/08
4  * @author
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.  Other copyrights may also apply.
9  * 2014 Deskull rearranged comment for Doxygen.
10  */
11
12 #include "monster/monster-list.h"
13 #include "core/player-update-types.h"
14 #include "core/speed-table.h"
15 #include "dungeon/dungeon.h"
16 #include "floor/floor-object.h"
17 #include "floor/wild.h"
18 #include "game-option/birth-options.h"
19 #include "grid/grid.h"
20 #include "monster-floor/monster-summon.h"
21 #include "monster-race/monster-race.h"
22 #include "monster-race/race-flags1.h"
23 #include "monster-race/race-flags2.h"
24 #include "monster-race/race-flags3.h"
25 #include "monster-race/race-flags7.h"
26 #include "monster-race/race-indice-types.h"
27 #include "monster/monster-describer.h"
28 #include "monster/monster-info.h"
29 #include "monster/monster-update.h"
30 #include "monster/monster-util.h"
31 #include "object/object-generator.h"
32 #include "pet/pet-fall-off.h"
33 #include "system/alloc-entries.h"
34 #include "system/floor-type-definition.h"
35 #include "view/display-messages.h"
36 #include "world/world.h"
37
38 #define HORDE_NOGOOD 0x01 /*!< (未実装フラグ)HORDE生成でGOODなモンスターの生成を禁止する? */
39 #define HORDE_NOEVIL 0x02 /*!< (未実装フラグ)HORDE生成でEVILなモンスターの生成を禁止する? */
40
41 /*!
42  * @brief モンスター配列の空きを探す / Acquires and returns the index of a "free" monster.
43  * @return 利用可能なモンスター配列の添字
44  * @details
45  * This routine should almost never fail, but it *can* happen.
46  */
47 MONSTER_IDX m_pop(floor_type *floor_ptr)
48 {
49     /* Normal allocation */
50     if (floor_ptr->m_max < current_world_ptr->max_m_idx) {
51         MONSTER_IDX i = floor_ptr->m_max;
52         floor_ptr->m_max++;
53         floor_ptr->m_cnt++;
54         return i;
55     }
56
57     /* Recycle dead monsters */
58     for (MONSTER_IDX i = 1; i < floor_ptr->m_max; i++) {
59         monster_type *m_ptr;
60         m_ptr = &floor_ptr->m_list[i];
61         if (m_ptr->r_idx)
62             continue;
63         floor_ptr->m_cnt++;
64         return i;
65     }
66
67     if (current_world_ptr->character_dungeon)
68         msg_print(_("モンスターが多すぎる!", "Too many monsters!"));
69     return 0;
70 }
71
72 /*!
73  * @brief 生成モンスター種族を1種生成テーブルから選択する
74  * @param player_ptr プレーヤーへの参照ポインタ
75  * @param level 生成階
76  * @return 選択されたモンスター生成種族
77  */
78 MONRACE_IDX get_mon_num(player_type *player_ptr, DEPTH level, BIT_FLAGS option)
79 {
80     int i, j, p;
81     int r_idx;
82     long value, total;
83     monster_race *r_ptr;
84     alloc_entry *table = alloc_race_table;
85
86     int pls_kakuritu, pls_level, over_days;
87     int delay = mysqrt(level * 10000L) + (level * 5);
88
89     /* town level : same delay as 10F, no nasty mons till day18 */
90     if (!level)
91         delay = 360;
92
93     if (level > MAX_DEPTH - 1)
94         level = MAX_DEPTH - 1;
95
96     /* +1 per day after the base date */
97     /* base dates : day5(1F), day18(10F,0F), day34(30F), day53(60F), day69(90F) */
98     over_days = MAX(0, current_world_ptr->dungeon_turn / (TURNS_PER_TICK * 10000L) - delay / 20);
99
100     /* starts from 1/25, reaches 1/3 after 44days from a level dependent base date */
101     pls_kakuritu = MAX(NASTY_MON_MAX, NASTY_MON_BASE - over_days / 2);
102     /* starts from 0, reaches +25lv after 75days from a level dependent base date */
103     pls_level = MIN(NASTY_MON_PLUS_MAX, over_days / 3);
104
105     if (d_info[player_ptr->dungeon_idx].flags1 & DF1_MAZE) {
106         pls_kakuritu = MIN(pls_kakuritu / 2, pls_kakuritu - 10);
107         if (pls_kakuritu < 2)
108             pls_kakuritu = 2;
109         pls_level += 2;
110         level += 3;
111     }
112
113     /* Boost the level */
114     if (!player_ptr->phase_out && !(d_info[player_ptr->dungeon_idx].flags1 & DF1_BEGINNER)) {
115         /* Nightmare mode allows more out-of depth monsters */
116         if (ironman_nightmare && !randint0(pls_kakuritu)) {
117             /* What a bizarre calculation */
118             level = 1 + (level * MAX_DEPTH / randint1(MAX_DEPTH));
119         } else {
120             /* Occasional "nasty" monster */
121             if (!randint0(pls_kakuritu)) {
122                 /* Pick a level bonus */
123                 level += pls_level;
124             }
125         }
126     }
127
128     total = 0L;
129
130     /* Process probabilities */
131     for (i = 0; i < alloc_race_size; i++) {
132         if (table[i].level > level)
133             break;
134         table[i].prob3 = 0;
135         r_idx = table[i].index;
136         r_ptr = &r_info[r_idx];
137         if (!(option & GMN_ARENA) && !chameleon_change_m_idx) {
138             if (((r_ptr->flags1 & (RF1_UNIQUE)) || (r_ptr->flags7 & (RF7_NAZGUL))) && (r_ptr->cur_num >= r_ptr->max_num)) {
139                 continue;
140             }
141
142             if ((r_ptr->flags7 & (RF7_UNIQUE2)) && (r_ptr->cur_num >= 1)) {
143                 continue;
144             }
145
146             if (r_idx == MON_BANORLUPART) {
147                 if (r_info[MON_BANOR].cur_num > 0)
148                     continue;
149                 if (r_info[MON_LUPART].cur_num > 0)
150                     continue;
151             }
152         }
153
154         table[i].prob3 = table[i].prob2;
155         total += table[i].prob3;
156     }
157
158     if (total <= 0)
159         return 0;
160
161     value = randint0(total);
162     int found_count = 0;
163     for (i = 0; i < alloc_race_size; i++) {
164         if (value < table[i].prob3)
165             break;
166         value = value - table[i].prob3;
167         found_count++;
168     }
169
170     p = randint0(100);
171
172     /* Try for a "harder" monster once (50%) or twice (10%) */
173     if (p < 60) {
174         j = found_count;
175         value = randint0(total);
176         for (found_count = 0; found_count < alloc_race_size; found_count++) {
177             if (value < table[found_count].prob3)
178                 break;
179
180             value = value - table[found_count].prob3;
181         }
182
183         if (table[found_count].level < table[j].level)
184             found_count = j;
185     }
186
187     /* Try for a "harder" monster twice (10%) */
188     if (p < 10) {
189         j = found_count;
190         value = randint0(total);
191         for (found_count = 0; found_count < alloc_race_size; found_count++) {
192             if (value < table[found_count].prob3)
193                 break;
194
195             value = value - table[found_count].prob3;
196         }
197
198         if (table[found_count].level < table[j].level)
199             found_count = j;
200     }
201
202     return (table[found_count].index);
203 }
204
205 /*!
206  * @param player_ptr プレーヤーへの参照ポインタ
207  * @brief カメレオンの王の変身対象となるモンスターかどうか判定する / Hack -- the index of the summoning monster
208  * @param r_idx モンスター種族ID
209  * @return 対象にできるならtrueを返す
210  */
211 static bool monster_hook_chameleon_lord(player_type *player_ptr, MONRACE_IDX r_idx)
212 {
213     floor_type *floor_ptr = player_ptr->current_floor_ptr;
214     monster_race *r_ptr = &r_info[r_idx];
215     monster_type *m_ptr = &floor_ptr->m_list[chameleon_change_m_idx];
216     monster_race *old_r_ptr = &r_info[m_ptr->r_idx];
217
218     if (!(r_ptr->flags1 & (RF1_UNIQUE)))
219         return FALSE;
220     if (r_ptr->flags7 & (RF7_FRIENDLY | RF7_CHAMELEON))
221         return FALSE;
222
223     if (ABS(r_ptr->level - r_info[MON_CHAMELEON_K].level) > 5)
224         return FALSE;
225
226     if ((r_ptr->blow[0].method == RBM_EXPLODE) || (r_ptr->blow[1].method == RBM_EXPLODE) || (r_ptr->blow[2].method == RBM_EXPLODE)
227         || (r_ptr->blow[3].method == RBM_EXPLODE))
228         return FALSE;
229
230     if (!monster_can_cross_terrain(player_ptr, floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].feat, r_ptr, 0))
231         return FALSE;
232
233     if (!(old_r_ptr->flags7 & RF7_CHAMELEON)) {
234         if (monster_has_hostile_align(player_ptr, m_ptr, 0, 0, r_ptr))
235             return FALSE;
236     } else if (summon_specific_who > 0) {
237         if (monster_has_hostile_align(player_ptr, &floor_ptr->m_list[summon_specific_who], 0, 0, r_ptr))
238             return FALSE;
239     }
240
241     return TRUE;
242 }
243
244 /*!
245  * @brief カメレオンの変身対象となるモンスターかどうか判定する / Hack -- the index of the summoning monster
246  * @param r_idx モンスター種族ID
247  * @return 対象にできるならtrueを返す
248  * @todo グローバル変数対策の上 monster_hook.cへ移す。
249  */
250 static bool monster_hook_chameleon(player_type *player_ptr, MONRACE_IDX r_idx)
251 {
252     floor_type *floor_ptr = player_ptr->current_floor_ptr;
253     monster_race *r_ptr = &r_info[r_idx];
254     monster_type *m_ptr = &floor_ptr->m_list[chameleon_change_m_idx];
255     monster_race *old_r_ptr = &r_info[m_ptr->r_idx];
256
257     if (r_ptr->flags1 & (RF1_UNIQUE))
258         return FALSE;
259     if (r_ptr->flags2 & RF2_MULTIPLY)
260         return FALSE;
261     if (r_ptr->flags7 & (RF7_FRIENDLY | RF7_CHAMELEON))
262         return FALSE;
263
264     if ((r_ptr->blow[0].method == RBM_EXPLODE) || (r_ptr->blow[1].method == RBM_EXPLODE) || (r_ptr->blow[2].method == RBM_EXPLODE)
265         || (r_ptr->blow[3].method == RBM_EXPLODE))
266         return FALSE;
267
268     if (!monster_can_cross_terrain(player_ptr, floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].feat, r_ptr, 0))
269         return FALSE;
270
271     if (!(old_r_ptr->flags7 & RF7_CHAMELEON)) {
272         if ((old_r_ptr->flags3 & RF3_GOOD) && !(r_ptr->flags3 & RF3_GOOD))
273             return FALSE;
274         if ((old_r_ptr->flags3 & RF3_EVIL) && !(r_ptr->flags3 & RF3_EVIL))
275             return FALSE;
276         if (!(old_r_ptr->flags3 & (RF3_GOOD | RF3_EVIL)) && (r_ptr->flags3 & (RF3_GOOD | RF3_EVIL)))
277             return FALSE;
278     } else if (summon_specific_who > 0) {
279         if (monster_has_hostile_align(player_ptr, &floor_ptr->m_list[summon_specific_who], 0, 0, r_ptr))
280             return FALSE;
281     }
282
283     return (*(get_monster_hook(player_ptr)))(player_ptr, r_idx);
284 }
285
286 /*!
287  * @brief モンスターの変身処理
288  * @param player_ptr プレーヤーへの参照ポインタ
289  * @param m_idx 変身処理を受けるモンスター情報のID
290  * @param born 生成時の初変身先指定ならばtrue
291  * @param r_idx 旧モンスター種族のID
292  * @return なし
293  */
294 void choose_new_monster(player_type *player_ptr, MONSTER_IDX m_idx, bool born, MONRACE_IDX r_idx)
295 {
296     floor_type *floor_ptr = player_ptr->current_floor_ptr;
297     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
298     monster_race *r_ptr;
299
300     bool old_unique = FALSE;
301     if (r_info[m_ptr->r_idx].flags1 & RF1_UNIQUE)
302         old_unique = TRUE;
303     if (old_unique && (r_idx == MON_CHAMELEON))
304         r_idx = MON_CHAMELEON_K;
305     r_ptr = &r_info[r_idx];
306
307     char old_m_name[MAX_NLEN];
308     monster_desc(player_ptr, old_m_name, m_ptr, 0);
309
310     if (!r_idx) {
311         DEPTH level;
312
313         chameleon_change_m_idx = m_idx;
314         if (old_unique)
315             get_mon_num_prep(player_ptr, monster_hook_chameleon_lord, NULL);
316         else
317             get_mon_num_prep(player_ptr, monster_hook_chameleon, NULL);
318
319         if (old_unique)
320             level = r_info[MON_CHAMELEON_K].level;
321         else if (!floor_ptr->dun_level)
322             level = wilderness[player_ptr->wilderness_y][player_ptr->wilderness_x].level;
323         else
324             level = floor_ptr->dun_level;
325
326         if (d_info[player_ptr->dungeon_idx].flags1 & DF1_CHAMELEON)
327             level += 2 + randint1(3);
328
329         r_idx = get_mon_num(player_ptr, level, 0);
330         r_ptr = &r_info[r_idx];
331
332         chameleon_change_m_idx = 0;
333         if (!r_idx)
334             return;
335     }
336
337     m_ptr->r_idx = r_idx;
338     m_ptr->ap_r_idx = r_idx;
339     update_monster(player_ptr, m_idx, FALSE);
340     lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
341
342     int old_r_idx = m_ptr->r_idx;
343     if ((r_info[old_r_idx].flags7 & (RF7_LITE_MASK | RF7_DARK_MASK)) || (r_ptr->flags7 & (RF7_LITE_MASK | RF7_DARK_MASK)))
344         player_ptr->update |= (PU_MON_LITE);
345
346     if (born) {
347         if (r_ptr->flags3 & (RF3_EVIL | RF3_GOOD)) {
348             m_ptr->sub_align = SUB_ALIGN_NEUTRAL;
349             if (r_ptr->flags3 & RF3_EVIL)
350                 m_ptr->sub_align |= SUB_ALIGN_EVIL;
351             if (r_ptr->flags3 & RF3_GOOD)
352                 m_ptr->sub_align |= SUB_ALIGN_GOOD;
353         }
354
355         return;
356     }
357
358     if (m_idx == player_ptr->riding) {
359         GAME_TEXT m_name[MAX_NLEN];
360         monster_desc(player_ptr, m_name, m_ptr, 0);
361         msg_format(_("突然%sが変身した。", "Suddenly, %s transforms!"), old_m_name);
362         if (!(r_ptr->flags7 & RF7_RIDING))
363             if (process_fall_off_horse(player_ptr, 0, TRUE))
364                 msg_format(_("地面に落とされた。", "You have fallen from %s."), m_name);
365     }
366
367     m_ptr->mspeed = get_mspeed(player_ptr, r_ptr);
368
369     int oldmaxhp = m_ptr->max_maxhp;
370     if (r_ptr->flags1 & RF1_FORCE_MAXHP) {
371         m_ptr->max_maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
372     } else {
373         m_ptr->max_maxhp = damroll(r_ptr->hdice, r_ptr->hside);
374     }
375
376     if (ironman_nightmare) {
377         u32b hp = m_ptr->max_maxhp * 2L;
378         m_ptr->max_maxhp = (HIT_POINT)MIN(30000, hp);
379     }
380
381     m_ptr->maxhp = (long)(m_ptr->maxhp * m_ptr->max_maxhp) / oldmaxhp;
382     if (m_ptr->maxhp < 1)
383         m_ptr->maxhp = 1;
384     m_ptr->hp = (long)(m_ptr->hp * m_ptr->max_maxhp) / oldmaxhp;
385     m_ptr->dealt_damage = 0;
386 }
387
388 /*!
389  * todo ここには本来floor_type*を追加したいが、monster.hにfloor.hの参照を追加するとコンパイルエラーが出るので保留
390  * @brief モンスターの個体加速を設定する / Get initial monster speed
391  * @param r_ptr モンスター種族の参照ポインタ
392  * @return 加速値
393  */
394 SPEED get_mspeed(player_type *player_ptr, monster_race *r_ptr)
395 {
396     SPEED mspeed = r_ptr->speed;
397     if (!(r_ptr->flags1 & RF1_UNIQUE) && !player_ptr->current_floor_ptr->inside_arena) {
398         /* Allow some small variation per monster */
399         int i = SPEED_TO_ENERGY(r_ptr->speed) / (one_in_(4) ? 3 : 10);
400         if (i)
401             mspeed += rand_spread(0, i);
402     }
403
404     if (mspeed > 199)
405         mspeed = 199;
406
407     return mspeed;
408 }
409
410 /*!
411  * @brief 指定したモンスターに隣接しているモンスターの数を返す。
412  * / Count number of adjacent monsters
413  * @param player_ptr プレーヤーへの参照ポインタ
414  * @param m_idx 隣接数を調べたいモンスターのID
415  * @return 隣接しているモンスターの数
416  */
417 int get_monster_crowd_number(floor_type *floor_ptr, MONSTER_IDX m_idx)
418 {
419     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
420     POSITION my = m_ptr->fy;
421     POSITION mx = m_ptr->fx;
422     int count = 0;
423     for (int i = 0; i < 7; i++) {
424         int ay = my + ddy_ddd[i];
425         int ax = mx + ddx_ddd[i];
426
427         if (!in_bounds(floor_ptr, ay, ax))
428             continue;
429         if (floor_ptr->grid_array[ay][ax].m_idx > 0)
430             count++;
431     }
432
433     return count;
434 }