OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / action / activation-execution.cpp
1 /*!
2  * @file activation-execution.cpp
3  * @brief アイテムの発動実行定義
4  */
5
6 #include "action/activation-execution.h"
7 #include "action/action-limited.h"
8 #include "artifact/artifact-info.h"
9 #include "artifact/random-art-effects.h"
10 #include "core/window-redrawer.h"
11 #include "effect/attribute-types.h"
12 #include "effect/spells-effect-util.h"
13 #include "flavor/flavor-describer.h"
14 #include "flavor/object-flavor-types.h"
15 #include "floor/geometry.h"
16 #include "game-option/disturbance-options.h"
17 #include "game-option/input-options.h"
18 #include "main/sound-definitions-table.h"
19 #include "main/sound-of-music.h"
20 #include "monster-floor/monster-generator.h"
21 #include "monster-floor/place-monster-types.h"
22 #include "monster-race/monster-race.h"
23 #include "monster/monster-info.h"
24 #include "monster/monster-util.h"
25 #include "object-activation/activation-switcher.h"
26 #include "object-activation/activation-util.h"
27 #include "object-enchant/activation-info-table.h"
28 #include "object-enchant/object-ego.h"
29 #include "object/object-info.h"
30 #include "player-base/player-class.h"
31 #include "player-status/player-energy.h"
32 #include "racial/racial-android.h"
33 #include "specific-object/monster-ball.h"
34 #include "spell-kind/spells-launcher.h"
35 #include "spell-kind/spells-teleport.h"
36 #include "spell-realm/spells-hex.h"
37 #include "spell-realm/spells-song.h"
38 #include "sv-definition/sv-lite-types.h"
39 #include "sv-definition/sv-ring-types.h"
40 #include "system/artifact-type-definition.h"
41 #include "system/baseitem-info.h"
42 #include "system/floor-type-definition.h"
43 #include "system/item-entity.h"
44 #include "system/monster-entity.h"
45 #include "system/player-type-definition.h"
46 #include "system/redrawing-flags-updater.h"
47 #include "target/target-getter.h"
48 #include "term/screen-processor.h"
49 #include "timed-effect/player-confusion.h"
50 #include "timed-effect/timed-effects.h"
51 #include "util/sort.h"
52 #include "view/display-messages.h"
53 #include "world/world.h"
54
55 static void decide_activation_level(ae_type *ae_ptr)
56 {
57     if (ae_ptr->o_ptr->is_fixed_artifact()) {
58         ae_ptr->lev = ae_ptr->o_ptr->get_fixed_artifact().level;
59         return;
60     }
61
62     if (ae_ptr->o_ptr->is_random_artifact()) {
63         auto act_ptr = find_activation_info(ae_ptr->o_ptr);
64         if (act_ptr.has_value()) {
65             ae_ptr->lev = act_ptr.value()->level;
66         }
67
68         return;
69     }
70
71     const auto tval = ae_ptr->o_ptr->bi_key.tval();
72     if (((tval == ItemKindType::RING) || (tval == ItemKindType::AMULET)) && ae_ptr->o_ptr->is_ego()) {
73         ae_ptr->lev = ae_ptr->o_ptr->get_ego().level;
74     }
75 }
76
77 static void decide_chance_fail(PlayerType *player_ptr, ae_type *ae_ptr)
78 {
79     ae_ptr->chance = player_ptr->skill_dev;
80     if (player_ptr->effects()->confusion()->is_confused()) {
81         ae_ptr->chance = ae_ptr->chance / 2;
82     }
83
84     ae_ptr->fail = ae_ptr->lev + 5;
85     if (ae_ptr->chance > ae_ptr->fail) {
86         ae_ptr->fail -= (ae_ptr->chance - ae_ptr->fail) * 2;
87     } else {
88         ae_ptr->chance -= (ae_ptr->fail - ae_ptr->chance) * 2;
89     }
90
91     if (ae_ptr->fail < USE_DEVICE) {
92         ae_ptr->fail = USE_DEVICE;
93     }
94
95     if (ae_ptr->chance < USE_DEVICE) {
96         ae_ptr->chance = USE_DEVICE;
97     }
98 }
99
100 static void decide_activation_success(PlayerType *player_ptr, ae_type *ae_ptr)
101 {
102     if (PlayerClass(player_ptr).equals(PlayerClassType::BERSERKER)) {
103         ae_ptr->success = false;
104         return;
105     }
106
107     if (ae_ptr->chance > ae_ptr->fail) {
108         ae_ptr->success = randint0(ae_ptr->chance * 2) >= ae_ptr->fail;
109         return;
110     }
111
112     ae_ptr->success = randint0(ae_ptr->fail * 2) < ae_ptr->chance;
113 }
114
115 static bool check_activation_success(ae_type *ae_ptr)
116 {
117     if (ae_ptr->success) {
118         return true;
119     }
120
121     if (flush_failure) {
122         flush();
123     }
124
125     msg_print(_("うまく始動させることができなかった。", "You failed to activate it properly."));
126     sound(SOUND_FAIL);
127     return false;
128 }
129
130 static bool check_activation_conditions(PlayerType *player_ptr, ae_type *ae_ptr)
131 {
132     if (!check_activation_success(ae_ptr)) {
133         return false;
134     }
135
136     if (ae_ptr->o_ptr->timeout) {
137         msg_print(_("それは微かに音を立て、輝き、消えた...", "It whines, glows and fades..."));
138         return false;
139     }
140
141     if (ae_ptr->o_ptr->is_fuel() && (ae_ptr->o_ptr->fuel == 0)) {
142         msg_print(_("燃料がない。", "It has no fuel."));
143         PlayerEnergy(player_ptr).reset_player_turn();
144         return false;
145     }
146
147     return true;
148 }
149
150 /*!
151  * @brief アイテムの発動効果を処理する。
152  * @param player_ptr プレイヤーへの参照ポインタ
153  * @param o_ptr 対象のオブジェクト構造体ポインタ
154  * @return 発動実行の是非を返す。
155  */
156 static bool activate_artifact(PlayerType *player_ptr, ItemEntity *o_ptr)
157 {
158     auto tmp_act_ptr = find_activation_info(o_ptr);
159     if (!tmp_act_ptr.has_value()) {
160         msg_print("Activation information is not found.");
161         return false;
162     }
163
164     auto *act_ptr = tmp_act_ptr.value();
165     const auto item_name = describe_flavor(player_ptr, o_ptr, OD_NAME_ONLY | OD_OMIT_PREFIX | OD_BASE_NAME);
166     if (!switch_activation(player_ptr, &o_ptr, act_ptr, item_name.data())) {
167         return false;
168     }
169
170     if (act_ptr->timeout.constant >= 0) {
171         o_ptr->timeout = (int16_t)act_ptr->timeout.constant;
172         if (act_ptr->timeout.dice > 0) {
173             o_ptr->timeout += randint1(act_ptr->timeout.dice);
174         }
175
176         return true;
177     }
178
179     switch (act_ptr->index) {
180     case RandomArtActType::BR_FIRE:
181         o_ptr->timeout = o_ptr->bi_key == BaseitemKey(ItemKindType::RING, SV_RING_FLAMES) ? 200 : 250;
182         return true;
183     case RandomArtActType::BR_COLD:
184         o_ptr->timeout = o_ptr->bi_key == BaseitemKey(ItemKindType::RING, SV_RING_ICE) ? 200 : 250;
185         return true;
186     case RandomArtActType::TERROR:
187         o_ptr->timeout = 3 * (player_ptr->lev + 10);
188         return true;
189     case RandomArtActType::MURAMASA:
190         return true;
191     default:
192         msg_format("Special timeout is not implemented: %d.", enum2i(act_ptr->index));
193         return false;
194     }
195 }
196
197 static bool activate_whistle(PlayerType *player_ptr, ae_type *ae_ptr)
198 {
199     if (ae_ptr->o_ptr->bi_key.tval() != ItemKindType::WHISTLE) {
200         return false;
201     }
202
203     if (music_singing_any(player_ptr)) {
204         stop_singing(player_ptr);
205     }
206
207     if (SpellHex(player_ptr).is_spelling_any()) {
208         (void)SpellHex(player_ptr).stop_all_spells();
209     }
210
211     std::vector<MONSTER_IDX> who;
212     for (MONSTER_IDX pet_ctr = player_ptr->current_floor_ptr->m_max - 1; pet_ctr >= 1; pet_ctr--) {
213         const auto &m_ref = player_ptr->current_floor_ptr->m_list[pet_ctr];
214         if (m_ref.is_pet() && (player_ptr->riding != pet_ctr)) {
215             who.push_back(pet_ctr);
216         }
217     }
218
219     uint16_t dummy_why;
220     ang_sort(player_ptr, who.data(), &dummy_why, who.size(), ang_sort_comp_pet, ang_sort_swap_hook);
221     for (auto pet_ctr : who) {
222         teleport_monster_to(player_ptr, pet_ctr, player_ptr->y, player_ptr->x, 100, TELEPORT_PASSIVE);
223     }
224
225     ae_ptr->o_ptr->timeout = 100 + randint1(100);
226     return true;
227 }
228
229 /*!
230  * @brief 装備を発動するコマンドのサブルーチン /
231  * Activate a wielded object.  Wielded objects never stack.
232  * And even if they did, activatable objects never stack.
233  * @param item 発動するオブジェクトの所持品ID
234  * @details
235  * <pre>
236  * Currently, only (some) artifacts, and Dragon Scale Mail, can be activated.
237  * But one could, for example, easily make an activatable "Ring of Plasma".
238  * Note that it always takes a turn to activate an artifact, even if
239  * the user hits "escape" at the "direction" prompt.
240  * </pre>
241  */
242 void exe_activate(PlayerType *player_ptr, INVENTORY_IDX item)
243 {
244     PlayerEnergy(player_ptr).set_player_turn_energy(100);
245     ae_type tmp_ae;
246     ae_type *ae_ptr = initialize_ae_type(player_ptr, &tmp_ae, item);
247     decide_activation_level(ae_ptr);
248     decide_chance_fail(player_ptr, ae_ptr);
249     if (cmd_limit_time_walk(player_ptr)) {
250         return;
251     }
252
253     decide_activation_success(player_ptr, ae_ptr);
254     if (!check_activation_conditions(player_ptr, ae_ptr)) {
255         return;
256     }
257
258     msg_print(_("始動させた...", "You activate it..."));
259     sound(SOUND_ZAP);
260     if (activation_index(ae_ptr->o_ptr) > RandomArtActType::NONE) {
261         (void)activate_artifact(player_ptr, ae_ptr->o_ptr);
262         static constexpr auto flags = {
263             SubWindowRedrawingFlag::INVENTORY,
264             SubWindowRedrawingFlag::EQUIPMENT,
265         };
266         RedrawingFlagsUpdater::get_instance().set_flags(flags);
267         return;
268     }
269
270     if (activate_whistle(player_ptr, ae_ptr)) {
271         return;
272     }
273
274     if (exe_monster_capture(player_ptr, *ae_ptr->o_ptr)) {
275         return;
276     }
277
278     msg_print(_("おっと、このアイテムは始動できない。", "Oops.  That object cannot be activated."));
279 }