2 * @brief モンスター処理 / misc code for monsters
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.
12 #include "monster/monster-list.h"
13 #include "core/player-update-types.h"
14 #include "core/speed-table.h"
15 #include "dungeon/dungeon-flag-types.h"
16 #include "floor/cave.h"
17 #include "floor/floor-object.h"
18 #include "floor/geometry.h"
19 #include "floor/wild.h"
20 #include "game-option/birth-options.h"
21 #include "game-option/cheat-options.h"
22 #include "grid/grid.h"
23 #include "monster-floor/monster-summon.h"
24 #include "monster-race/monster-kind-mask.h"
25 #include "monster-race/monster-race.h"
26 #include "monster-race/race-flags1.h"
27 #include "monster-race/race-flags2.h"
28 #include "monster-race/race-flags3.h"
29 #include "monster-race/race-flags7.h"
30 #include "monster-race/race-indice-types.h"
31 #include "monster/monster-describer.h"
32 #include "monster/monster-info.h"
33 #include "monster/monster-update.h"
34 #include "monster/monster-util.h"
35 #include "pet/pet-fall-off.h"
36 #include "player/player-status.h"
37 #include "system/alloc-entries.h"
38 #include "system/dungeon-info.h"
39 #include "system/floor-type-definition.h"
40 #include "system/grid-type-definition.h"
41 #include "system/monster-race-definition.h"
42 #include "system/monster-type-definition.h"
43 #include "system/player-type-definition.h"
44 #include "util/probability-table.h"
45 #include "view/display-messages.h"
46 #include "world/world.h"
49 #define HORDE_NOGOOD 0x01 /*!< (未実装フラグ)HORDE生成でGOODなモンスターの生成を禁止する? */
50 #define HORDE_NOEVIL 0x02 /*!< (未実装フラグ)HORDE生成でEVILなモンスターの生成を禁止する? */
53 * @brief モンスター配列の空きを探す / Acquires and returns the index of a "free" monster.
54 * @return 利用可能なモンスター配列の添字
56 * This routine should almost never fail, but it *can* happen.
58 MONSTER_IDX m_pop(FloorType *floor_ptr)
60 /* Normal allocation */
61 if (floor_ptr->m_max < w_ptr->max_m_idx) {
62 MONSTER_IDX i = floor_ptr->m_max;
68 /* Recycle dead monsters */
69 for (MONSTER_IDX i = 1; i < floor_ptr->m_max; i++) {
71 m_ptr = &floor_ptr->m_list[i];
72 if (MonsterRace(m_ptr->r_idx).is_valid()) {
79 if (w_ptr->character_dungeon) {
80 msg_print(_("モンスターが多すぎる!", "Too many monsters!"));
86 * @brief 生成モンスター種族を1種生成テーブルから選択する
87 * @param player_ptr プレイヤーへの参照ポインタ
88 * @param min_level 最小生成階
89 * @param max_level 最大生成階
90 * @return 選択されたモンスター生成種族
91 * @details nasty生成 (ゲーム内経過日数に応じて、現在フロアより深いフロアのモンスターを出現させる仕様)は
93 MonsterRaceId get_mon_num(PlayerType *player_ptr, DEPTH min_level, DEPTH max_level, BIT_FLAGS option)
95 /* town max_level : same delay as 10F, no nasty mons till day18 */
96 auto delay = mysqrt(max_level * 10000L) + (max_level * 5);
101 if (max_level > MAX_DEPTH - 1) {
102 max_level = MAX_DEPTH - 1;
105 /* +1 per day after the base date */
106 /* base dates : day5(1F), day18(10F,0F), day34(30F), day53(60F), day69(90F) */
107 const auto over_days = std::max<int>(0, w_ptr->dungeon_turn / (TURNS_PER_TICK * 10000L) - delay / 20);
109 /* Probability starts from 1/25, reaches 1/3 after 44days from a max_level dependent base date */
110 /* Boost level starts from 0, reaches +25lv after 75days from a max_level dependent base date */
111 constexpr auto chance_nasty_monster = 25;
112 constexpr auto max_num_nasty_monsters = 3;
113 constexpr auto max_depth_nasty_monster = 25;
114 auto chance_nasty = std::max(max_num_nasty_monsters, chance_nasty_monster - over_days / 2);
115 auto nasty_level = std::min(max_depth_nasty_monster, over_days / 3);
116 if (dungeons_info[player_ptr->dungeon_idx].flags.has(DungeonFeatureType::MAZE)) {
117 chance_nasty = std::min(chance_nasty / 2, chance_nasty - 10);
118 if (chance_nasty < 2) {
126 /* Boost the max_level */
127 if ((option & GMN_ARENA) || dungeons_info[player_ptr->dungeon_idx].flags.has_not(DungeonFeatureType::BEGINNER)) {
128 /* Nightmare mode allows more out-of depth monsters */
129 if (ironman_nightmare && !randint0(chance_nasty)) {
130 /* What a bizarre calculation */
131 max_level = 1 + (max_level * MAX_DEPTH / randint1(MAX_DEPTH));
133 /* Occasional "nasty" monster */
134 if (!randint0(chance_nasty)) {
135 /* Pick a max_level bonus */
136 max_level += nasty_level;
141 ProbabilityTable<int> prob_table;
143 /* Process probabilities */
144 for (auto i = 0U; i < alloc_race_table.size(); i++) {
145 const auto &entry = alloc_race_table[i];
146 if (entry.level < min_level) {
149 if (max_level < entry.level) {
151 } // sorted by depth array,
152 auto r_idx = i2enum<MonsterRaceId>(entry.index);
153 auto r_ptr = &monraces_info[r_idx];
154 if (!(option & GMN_ARENA) && !chameleon_change_m_idx) {
155 if ((r_ptr->kind_flags.has(MonsterKindType::UNIQUE) || r_ptr->population_flags.has(MonsterPopulationType::NAZGUL)) && (r_ptr->cur_num >= r_ptr->max_num)) {
159 if ((r_ptr->flags7 & (RF7_UNIQUE2)) && (r_ptr->cur_num >= 1)) {
163 if (r_idx == MonsterRaceId::BANORLUPART) {
164 if (monraces_info[MonsterRaceId::BANOR].cur_num > 0) {
167 if (monraces_info[MonsterRaceId::LUPART].cur_num > 0) {
173 prob_table.entry_item(i, entry.prob2);
177 msg_format(_("モンスター第3次候補数:%d(%d-%dF)%d ", "monster third selection:%d(%d-%dF)%d "), prob_table.item_count(), min_level, max_level,
178 prob_table.total_prob());
181 if (prob_table.empty()) {
182 return MonsterRace::empty_id();
185 // 40%で1回、50%で2回、10%で3回抽選し、その中で一番レベルが高いモンスターを選択する
188 const int p = randint0(100);
196 std::vector<int> result;
197 ProbabilityTable<int>::lottery(std::back_inserter(result), prob_table, n);
199 auto it = std::max_element(result.begin(), result.end(), [](int a, int b) { return alloc_race_table[a].level < alloc_race_table[b].level; });
201 return i2enum<MonsterRaceId>(alloc_race_table[*it].index);
205 * @param player_ptr プレイヤーへの参照ポインタ
206 * @brief カメレオンの王の変身対象となるモンスターかどうか判定する / Hack -- the index of the summoning monster
207 * @param r_idx モンスター種族ID
208 * @return 対象にできるならtrueを返す
210 static bool monster_hook_chameleon_lord(PlayerType *player_ptr, MonsterRaceId r_idx)
212 auto *floor_ptr = player_ptr->current_floor_ptr;
213 auto *r_ptr = &monraces_info[r_idx];
214 auto *m_ptr = &floor_ptr->m_list[chameleon_change_m_idx];
215 monster_race *old_r_ptr = &monraces_info[m_ptr->r_idx];
217 if (r_ptr->kind_flags.has_not(MonsterKindType::UNIQUE)) {
220 if (r_ptr->behavior_flags.has(MonsterBehaviorType::FRIENDLY) || (r_ptr->flags7 & RF7_CHAMELEON)) {
224 if (std::abs(r_ptr->level - monraces_info[MonsterRaceId::CHAMELEON_K].level) > 5) {
228 if ((r_ptr->blow[0].method == RaceBlowMethodType::EXPLODE) || (r_ptr->blow[1].method == RaceBlowMethodType::EXPLODE) || (r_ptr->blow[2].method == RaceBlowMethodType::EXPLODE) || (r_ptr->blow[3].method == RaceBlowMethodType::EXPLODE)) {
232 if (!monster_can_cross_terrain(player_ptr, floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].feat, r_ptr, 0)) {
236 if (!(old_r_ptr->flags7 & RF7_CHAMELEON)) {
237 if (monster_has_hostile_align(player_ptr, m_ptr, 0, 0, r_ptr)) {
240 } else if (summon_specific_who > 0) {
241 if (monster_has_hostile_align(player_ptr, &floor_ptr->m_list[summon_specific_who], 0, 0, r_ptr)) {
250 * @brief カメレオンの変身対象となるモンスターかどうか判定する / Hack -- the index of the summoning monster
251 * @param r_idx モンスター種族ID
252 * @return 対象にできるならtrueを返す
253 * @todo グローバル変数対策の上 monster_hook.cへ移す。
255 static bool monster_hook_chameleon(PlayerType *player_ptr, MonsterRaceId r_idx)
257 auto *floor_ptr = player_ptr->current_floor_ptr;
258 auto *r_ptr = &monraces_info[r_idx];
259 auto *m_ptr = &floor_ptr->m_list[chameleon_change_m_idx];
260 monster_race *old_r_ptr = &monraces_info[m_ptr->r_idx];
262 if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
265 if (r_ptr->flags2 & RF2_MULTIPLY) {
268 if (r_ptr->behavior_flags.has(MonsterBehaviorType::FRIENDLY) || (r_ptr->flags7 & RF7_CHAMELEON)) {
272 if ((r_ptr->blow[0].method == RaceBlowMethodType::EXPLODE) || (r_ptr->blow[1].method == RaceBlowMethodType::EXPLODE) || (r_ptr->blow[2].method == RaceBlowMethodType::EXPLODE) || (r_ptr->blow[3].method == RaceBlowMethodType::EXPLODE)) {
276 if (!monster_can_cross_terrain(player_ptr, floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].feat, r_ptr, 0)) {
280 if (!(old_r_ptr->flags7 & RF7_CHAMELEON)) {
281 if (old_r_ptr->kind_flags.has(MonsterKindType::GOOD) && r_ptr->kind_flags.has_not(MonsterKindType::GOOD)) {
284 if (old_r_ptr->kind_flags.has(MonsterKindType::EVIL) && r_ptr->kind_flags.has_not(MonsterKindType::EVIL)) {
287 if (old_r_ptr->kind_flags.has_none_of(alignment_mask)) {
290 } else if (summon_specific_who > 0) {
291 if (monster_has_hostile_align(player_ptr, &floor_ptr->m_list[summon_specific_who], 0, 0, r_ptr)) {
296 auto hook_pf = get_monster_hook(player_ptr);
297 return (*hook_pf)(player_ptr, r_idx);
302 * @param player_ptr プレイヤーへの参照ポインタ
303 * @param m_idx 変身処理を受けるモンスター情報のID
304 * @param born 生成時の初変身先指定ならばtrue
305 * @param r_idx 旧モンスター種族のID
307 void choose_new_monster(PlayerType *player_ptr, MONSTER_IDX m_idx, bool born, MonsterRaceId r_idx)
309 auto *floor_ptr = player_ptr->current_floor_ptr;
310 auto *m_ptr = &floor_ptr->m_list[m_idx];
313 bool old_unique = false;
314 if (monraces_info[m_ptr->r_idx].kind_flags.has(MonsterKindType::UNIQUE)) {
317 if (old_unique && (r_idx == MonsterRaceId::CHAMELEON)) {
318 r_idx = MonsterRaceId::CHAMELEON_K;
320 r_ptr = &monraces_info[r_idx];
322 char old_m_name[MAX_NLEN];
323 monster_desc(player_ptr, old_m_name, m_ptr, 0);
325 if (!MonsterRace(r_idx).is_valid()) {
328 chameleon_change_m_idx = m_idx;
330 get_mon_num_prep(player_ptr, monster_hook_chameleon_lord, nullptr);
332 get_mon_num_prep(player_ptr, monster_hook_chameleon, nullptr);
336 level = monraces_info[MonsterRaceId::CHAMELEON_K].level;
337 } else if (!floor_ptr->dun_level) {
338 level = wilderness[player_ptr->wilderness_y][player_ptr->wilderness_x].level;
340 level = floor_ptr->dun_level;
343 if (dungeons_info[player_ptr->dungeon_idx].flags.has(DungeonFeatureType::CHAMELEON)) {
344 level += 2 + randint1(3);
347 r_idx = get_mon_num(player_ptr, 0, level, 0);
348 r_ptr = &monraces_info[r_idx];
350 chameleon_change_m_idx = 0;
351 if (!MonsterRace(r_idx).is_valid()) {
356 m_ptr->r_idx = r_idx;
357 m_ptr->ap_r_idx = r_idx;
358 update_monster(player_ptr, m_idx, false);
359 lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
361 auto old_r_idx = m_ptr->r_idx;
362 if ((monraces_info[old_r_idx].flags7 & (RF7_LITE_MASK | RF7_DARK_MASK)) || (r_ptr->flags7 & (RF7_LITE_MASK | RF7_DARK_MASK))) {
363 player_ptr->update |= (PU_MON_LITE);
367 if (r_ptr->kind_flags.has_any_of(alignment_mask)) {
368 m_ptr->sub_align = SUB_ALIGN_NEUTRAL;
369 if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
370 m_ptr->sub_align |= SUB_ALIGN_EVIL;
372 if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
373 m_ptr->sub_align |= SUB_ALIGN_GOOD;
380 if (m_idx == player_ptr->riding) {
381 GAME_TEXT m_name[MAX_NLEN];
382 monster_desc(player_ptr, m_name, m_ptr, 0);
383 msg_format(_("突然%sが変身した。", "Suddenly, %s transforms!"), old_m_name);
384 if (!(r_ptr->flags7 & RF7_RIDING)) {
385 if (process_fall_off_horse(player_ptr, 0, true)) {
386 msg_format(_("地面に落とされた。", "You have fallen from %s."), m_name);
391 m_ptr->mspeed = get_mspeed(floor_ptr, r_ptr);
393 int oldmaxhp = m_ptr->max_maxhp;
394 if (r_ptr->flags1 & RF1_FORCE_MAXHP) {
395 m_ptr->max_maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
397 m_ptr->max_maxhp = damroll(r_ptr->hdice, r_ptr->hside);
400 if (ironman_nightmare) {
401 auto hp = m_ptr->max_maxhp * 2;
402 m_ptr->max_maxhp = std::min(MONSTER_MAXHP, hp);
405 m_ptr->maxhp = (long)(m_ptr->maxhp * m_ptr->max_maxhp) / oldmaxhp;
406 if (m_ptr->maxhp < 1) {
409 m_ptr->hp = (long)(m_ptr->hp * m_ptr->max_maxhp) / oldmaxhp;
410 m_ptr->dealt_damage = 0;
414 * @brief モンスターの個体加速を設定する / Get initial monster speed
415 * @param r_ptr モンスター種族の参照ポインタ
418 byte get_mspeed(FloorType *floor_ptr, monster_race *r_ptr)
420 auto mspeed = r_ptr->speed;
421 if (r_ptr->kind_flags.has_not(MonsterKindType::UNIQUE) && !floor_ptr->inside_arena) {
422 /* Allow some small variation per monster */
423 int i = speed_to_energy(r_ptr->speed) / (one_in_(4) ? 3 : 10);
425 mspeed += rand_spread(0, i);
437 * @brief 指定したモンスターに隣接しているモンスターの数を返す。
438 * / Count number of adjacent monsters
439 * @param player_ptr プレイヤーへの参照ポインタ
440 * @param m_idx 隣接数を調べたいモンスターのID
441 * @return 隣接しているモンスターの数
443 int get_monster_crowd_number(FloorType *floor_ptr, MONSTER_IDX m_idx)
445 auto *m_ptr = &floor_ptr->m_list[m_idx];
446 POSITION my = m_ptr->fy;
447 POSITION mx = m_ptr->fx;
449 for (int i = 0; i < 7; i++) {
450 int ay = my + ddy_ddd[i];
451 int ax = mx + ddx_ddd[i];
453 if (!in_bounds(floor_ptr, ay, ax)) {
456 if (floor_ptr->grid_array[ay][ax].m_idx > 0) {