OSDN Git Service

Merge pull request #3634 from Hourier/Prepare-Replace-ChooseRandomTrap-FloorType
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-polymorph.cpp
1 #include "spell-kind/spells-polymorph.h"
2 #include "core/stuff-handler.h"
3 #include "floor/floor-object.h"
4 #include "monster-floor/monster-generator.h"
5 #include "monster-floor/monster-remover.h"
6 #include "monster-floor/place-monster-types.h"
7 #include "monster-race/monster-race.h"
8 #include "monster-race/race-flags1.h"
9 #include "monster/monster-flag-types.h"
10 #include "monster/monster-info.h"
11 #include "monster/monster-list.h"
12 #include "monster/monster-status.h"
13 #include "monster/monster-util.h"
14 #include "system/floor-type-definition.h"
15 #include "system/grid-type-definition.h"
16 #include "system/item-entity.h"
17 #include "system/monster-entity.h"
18 #include "system/monster-race-info.h"
19 #include "system/player-type-definition.h"
20 #include "target/target-checker.h"
21 #include "util/bit-flags-calculator.h"
22
23 /*!
24  * @brief 変身処理向けにモンスターの近隣レベル帯モンスターを返す /
25  * Helper function -- return a "nearby" race for polymorphing
26  * @param floor_ptr 配置するフロアの参照ポインタ
27  * @param r_idx 基準となるモンスター種族ID
28  * @return 変更先のモンスター種族ID
29  * @details
30  * Note that this function is one of the more "dangerous" ones...
31  */
32 static MonsterRaceId poly_r_idx(PlayerType *player_ptr, MonsterRaceId r_idx)
33 {
34     auto *r_ptr = &monraces_info[r_idx];
35     if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE) || any_bits(r_ptr->flags1, RF1_QUESTOR)) {
36         return r_idx;
37     }
38
39     DEPTH lev1 = r_ptr->level - ((randint1(20) / randint1(9)) + 1);
40     DEPTH lev2 = r_ptr->level + ((randint1(20) / randint1(9)) + 1);
41     MonsterRaceId r;
42     for (int i = 0; i < 1000; i++) {
43         r = get_mon_num(player_ptr, 0, (player_ptr->current_floor_ptr->dun_level + r_ptr->level) / 2 + 5, PM_NONE);
44         if (!MonsterRace(r).is_valid()) {
45             break;
46         }
47
48         r_ptr = &monraces_info[r];
49         if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
50             continue;
51         }
52         if ((r_ptr->level < lev1) || (r_ptr->level > lev2)) {
53             continue;
54         }
55
56         r_idx = r;
57         break;
58     }
59
60     return r_idx;
61 }
62
63 /*!
64  * @brief 指定座標にいるモンスターを変身させる /
65  * Helper function -- return a "nearby" race for polymorphing
66  * @param player_ptr プレイヤーへの参照ポインタ
67  * @param y 指定のY座標
68  * @param x 指定のX座標
69  * @return 実際に変身したらTRUEを返す
70  */
71 bool polymorph_monster(PlayerType *player_ptr, POSITION y, POSITION x)
72 {
73     auto *floor_ptr = player_ptr->current_floor_ptr;
74     auto *g_ptr = &floor_ptr->grid_array[y][x];
75     auto *m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
76     MonsterRaceId new_r_idx;
77     MonsterRaceId old_r_idx = m_ptr->r_idx;
78     bool targeted = target_who == g_ptr->m_idx;
79     bool health_tracked = player_ptr->health_who == g_ptr->m_idx;
80
81     if (floor_ptr->inside_arena || player_ptr->phase_out) {
82         return false;
83     }
84     if ((player_ptr->riding == g_ptr->m_idx) || m_ptr->mflag2.has(MonsterConstantFlagType::KAGE)) {
85         return false;
86     }
87
88     MonsterEntity back_m = *m_ptr;
89     new_r_idx = poly_r_idx(player_ptr, old_r_idx);
90     if (new_r_idx == old_r_idx) {
91         return false;
92     }
93
94     bool preserve_hold_objects = !back_m.hold_o_idx_list.empty();
95
96     BIT_FLAGS mode = 0L;
97     if (m_ptr->is_friendly()) {
98         mode |= PM_FORCE_FRIENDLY;
99     }
100     if (m_ptr->is_pet()) {
101         mode |= PM_FORCE_PET;
102     }
103     if (m_ptr->mflag2.has(MonsterConstantFlagType::NOPET)) {
104         mode |= PM_NO_PET;
105     }
106
107     m_ptr->hold_o_idx_list.clear();
108     delete_monster_idx(player_ptr, g_ptr->m_idx);
109     bool polymorphed = false;
110     if (place_specific_monster(player_ptr, 0, y, x, new_r_idx, mode)) {
111         floor_ptr->m_list[hack_m_idx_ii].nickname = back_m.nickname;
112         floor_ptr->m_list[hack_m_idx_ii].parent_m_idx = back_m.parent_m_idx;
113         floor_ptr->m_list[hack_m_idx_ii].hold_o_idx_list = back_m.hold_o_idx_list;
114         polymorphed = true;
115     } else {
116         if (place_specific_monster(player_ptr, 0, y, x, old_r_idx, (mode | PM_NO_KAGE | PM_IGNORE_TERRAIN))) {
117             floor_ptr->m_list[hack_m_idx_ii] = back_m;
118             mproc_init(floor_ptr);
119         } else {
120             preserve_hold_objects = false;
121         }
122     }
123
124     if (preserve_hold_objects) {
125         for (const auto this_o_idx : back_m.hold_o_idx_list) {
126             auto *o_ptr = &floor_ptr->o_list[this_o_idx];
127             o_ptr->held_m_idx = hack_m_idx_ii;
128         }
129     } else {
130         for (auto it = back_m.hold_o_idx_list.begin(); it != back_m.hold_o_idx_list.end();) {
131             OBJECT_IDX this_o_idx = *it++;
132             delete_object_idx(player_ptr, this_o_idx);
133         }
134     }
135
136     if (targeted) {
137         target_who = hack_m_idx_ii;
138     }
139     if (health_tracked) {
140         health_track(player_ptr, hack_m_idx_ii);
141     }
142     return polymorphed;
143 }