OSDN Git Service

[Refactor] #2628 ObjectType をItemEntity に変更した
[hengbandforosx/hengbandosx.git] / src / monster-attack / monster-eating.cpp
1 /*!
2  * @brief プレイヤーのHP/MP、アイテム、お金・明かりの残りターン、充填魔力を盗んだり減少させたりする処理
3  * @date 2020/05/31
4  * @author Hourier
5  */
6
7 #include "monster-attack/monster-eating.h"
8 #include "avatar/avatar.h"
9 #include "core/player-redraw-types.h"
10 #include "core/player-update-types.h"
11 #include "core/window-redrawer.h"
12 #include "flavor/flavor-describer.h"
13 #include "flavor/object-flavor-types.h"
14 #include "inventory/inventory-object.h"
15 #include "inventory/inventory-slot-types.h"
16 #include "mind/mind-mirror-master.h"
17 #include "monster-attack/monster-attack-player.h"
18 #include "monster/monster-status.h"
19 #include "object/object-info.h"
20 #include "object/object-mark-types.h"
21 #include "player-base/player-race.h"
22 #include "player-info/race-info.h"
23 #include "player/digestion-processor.h"
24 #include "player/player-status-flags.h"
25 #include "player/player-status-table.h"
26 #include "status/experience.h"
27 #include "system/baseitem-info-definition.h"
28 #include "system/floor-type-definition.h"
29 #include "system/monster-type-definition.h"
30 #include "system/object-type-definition.h"
31 #include "system/player-type-definition.h"
32 #include "timed-effect/player-blindness.h"
33 #include "timed-effect/player-paralysis.h"
34 #include "timed-effect/timed-effects.h"
35 #include "util/bit-flags-calculator.h"
36 #include "view/display-messages.h"
37 #include "world/world-object.h"
38
39 void process_eat_gold(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
40 {
41     auto is_paralyzed = player_ptr->effects()->paralysis()->is_paralyzed();
42     if (!is_paralyzed && (randint0(100) < (adj_dex_safe[player_ptr->stat_index[A_DEX]] + player_ptr->lev))) {
43         msg_print(_("しかし素早く財布を守った!", "You quickly protect your money pouch!"));
44         if (randint0(3)) {
45             monap_ptr->blinked = true;
46         }
47
48         return;
49     }
50
51     PRICE gold = (player_ptr->au / 10) + randint1(25);
52     if (gold < 2) {
53         gold = 2;
54     }
55
56     if (gold > 5000) {
57         gold = (player_ptr->au / 20) + randint1(3000);
58     }
59
60     if (gold > player_ptr->au) {
61         gold = player_ptr->au;
62     }
63
64     player_ptr->au -= gold;
65     if (gold <= 0) {
66         msg_print(_("しかし何も盗まれなかった。", "Nothing was stolen."));
67     } else if (player_ptr->au > 0) {
68         msg_print(_("財布が軽くなった気がする。", "Your purse feels lighter."));
69         msg_format(_("$%ld のお金が盗まれた!", "%ld coins were stolen!"), (long)gold);
70         chg_virtue(player_ptr, V_SACRIFICE, 1);
71     } else {
72         msg_print(_("財布が軽くなった気がする。", "Your purse feels lighter."));
73         msg_print(_("お金が全部盗まれた!", "All of your coins were stolen!"));
74         chg_virtue(player_ptr, V_SACRIFICE, 2);
75     }
76
77     player_ptr->redraw |= (PR_GOLD);
78     player_ptr->window_flags |= (PW_PLAYER);
79     monap_ptr->blinked = true;
80 }
81
82 /*!
83  * @brief 盗み打撃の時にアイテムが盗まれるかどうかを判定する
84  * @param player_ptr プレイヤーへの参照ポインタ
85  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
86  * @return 盗まれたらTRUE、何も盗まれなかったらFALSE
87  */
88 bool check_eat_item(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
89 {
90     if (monap_ptr->m_ptr->is_confused()) {
91         return false;
92     }
93
94     if (player_ptr->is_dead || check_multishadow(player_ptr)) {
95         return false;
96     }
97
98     auto is_paralyzed = player_ptr->effects()->paralysis()->is_paralyzed();
99     if (!is_paralyzed && (randint0(100) < (adj_dex_safe[player_ptr->stat_index[A_DEX]] + player_ptr->lev))) {
100         msg_print(_("しかしあわててザックを取り返した!", "You grab hold of your backpack!"));
101         monap_ptr->blinked = true;
102         monap_ptr->obvious = true;
103         return false;
104     }
105
106     return true;
107 }
108
109 /*!
110  * @brief プレイヤーが持っているアイテムをモンスターに移す
111  * @param player_ptr プレイヤーへの参照ポインタ
112  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
113  */
114 static void move_item_to_monster(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr, const OBJECT_IDX o_idx)
115 {
116     if (o_idx == 0) {
117         return;
118     }
119
120     ItemEntity *j_ptr;
121     j_ptr = &player_ptr->current_floor_ptr->o_list[o_idx];
122     j_ptr->copy_from(monap_ptr->o_ptr);
123     j_ptr->number = 1;
124     if ((monap_ptr->o_ptr->tval == ItemKindType::ROD) || (monap_ptr->o_ptr->tval == ItemKindType::WAND)) {
125         j_ptr->pval = monap_ptr->o_ptr->pval / monap_ptr->o_ptr->number;
126         monap_ptr->o_ptr->pval -= j_ptr->pval;
127     }
128
129     j_ptr->marked = OM_TOUCHED;
130     j_ptr->held_m_idx = monap_ptr->m_idx;
131     monap_ptr->m_ptr->hold_o_idx_list.add(player_ptr->current_floor_ptr, o_idx);
132 }
133
134 /*!
135  * @brief アイテム盗み処理
136  * @param player_ptr プレイヤーへの参照ポインタ
137  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
138  * @details eatとあるがお金や食べ物と違ってなくならない、盗んだモンスターを倒せば取り戻せる
139  */
140 void process_eat_item(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
141 {
142     for (int i = 0; i < 10; i++) {
143         OBJECT_IDX o_idx;
144         INVENTORY_IDX i_idx = (INVENTORY_IDX)randint0(INVEN_PACK);
145         monap_ptr->o_ptr = &player_ptr->inventory_list[i_idx];
146         if (!monap_ptr->o_ptr->k_idx) {
147             continue;
148         }
149
150         if (monap_ptr->o_ptr->is_artifact()) {
151             continue;
152         }
153
154         describe_flavor(player_ptr, monap_ptr->o_name, monap_ptr->o_ptr, OD_OMIT_PREFIX);
155 #ifdef JP
156         msg_format("%s(%c)を%s盗まれた!", monap_ptr->o_name, index_to_label(i_idx), ((monap_ptr->o_ptr->number > 1) ? "一つ" : ""));
157 #else
158         msg_format("%sour %s (%c) was stolen!", ((monap_ptr->o_ptr->number > 1) ? "One of y" : "Y"), monap_ptr->o_name, index_to_label(i_idx));
159 #endif
160         chg_virtue(player_ptr, V_SACRIFICE, 1);
161         o_idx = o_pop(player_ptr->current_floor_ptr);
162         move_item_to_monster(player_ptr, monap_ptr, o_idx);
163         inven_item_increase(player_ptr, i_idx, -1);
164         inven_item_optimize(player_ptr, i_idx);
165         monap_ptr->obvious = true;
166         monap_ptr->blinked = true;
167         break;
168     }
169 }
170
171 void process_eat_food(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
172 {
173     for (int i = 0; i < 10; i++) {
174         INVENTORY_IDX i_idx = (INVENTORY_IDX)randint0(INVEN_PACK);
175         monap_ptr->o_ptr = &player_ptr->inventory_list[i_idx];
176         if (!monap_ptr->o_ptr->k_idx) {
177             continue;
178         }
179
180         if ((monap_ptr->o_ptr->tval != ItemKindType::FOOD) && !((monap_ptr->o_ptr->tval == ItemKindType::CORPSE) && (monap_ptr->o_ptr->sval))) {
181             continue;
182         }
183
184         describe_flavor(player_ptr, monap_ptr->o_name, monap_ptr->o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
185 #ifdef JP
186         msg_format("%s(%c)を%s食べられてしまった!", monap_ptr->o_name, index_to_label(i_idx), ((monap_ptr->o_ptr->number > 1) ? "一つ" : ""));
187 #else
188         msg_format("%sour %s (%c) was eaten!", ((monap_ptr->o_ptr->number > 1) ? "One of y" : "Y"), monap_ptr->o_name, index_to_label(i_idx));
189 #endif
190         inven_item_increase(player_ptr, i_idx, -1);
191         inven_item_optimize(player_ptr, i_idx);
192         monap_ptr->obvious = true;
193         break;
194     }
195 }
196
197 void process_eat_lite(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
198 {
199     if ((monap_ptr->o_ptr->fuel <= 0) || monap_ptr->o_ptr->is_fixed_artifact()) {
200         return;
201     }
202
203     monap_ptr->o_ptr->fuel -= 250 + randint1(250);
204     if (monap_ptr->o_ptr->fuel < 1) {
205         monap_ptr->o_ptr->fuel = 1;
206     }
207
208     if (!player_ptr->effects()->blindness()->is_blind()) {
209         msg_print(_("明かりが暗くなってしまった。", "Your light dims."));
210         monap_ptr->obvious = true;
211     }
212
213     player_ptr->window_flags |= (PW_EQUIP);
214 }
215
216 /*!
217  * @brief モンスターからの攻撃による充填魔力吸収処理
218  * @param player_ptr プレイヤーへの参照ポインタ
219  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
220  * @return 吸収されたらTRUE、されなかったらFALSE
221  * @details 魔道具使用能力向上フラグがあれば、吸収量は全部ではない
222  * 詳細はOSDN #40911の議論を参照のこと
223  */
224 bool process_un_power(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
225 {
226     if (((monap_ptr->o_ptr->tval != ItemKindType::STAFF) && (monap_ptr->o_ptr->tval != ItemKindType::WAND)) || (monap_ptr->o_ptr->pval == 0)) {
227         return false;
228     }
229
230     bool is_magic_mastery = has_magic_mastery(player_ptr) != 0;
231     BaseitemInfo *kind_ptr = &baseitems_info[monap_ptr->o_ptr->k_idx];
232     PARAMETER_VALUE pval = kind_ptr->pval;
233     DEPTH level = monap_ptr->rlev;
234     auto drain = is_magic_mastery ? std::min<short>(pval, pval * level / 400 + pval * randint1(level) / 400) : pval;
235     drain = std::min(drain, monap_ptr->o_ptr->pval);
236     if (drain <= 0) {
237         return false;
238     }
239
240     msg_print(_("ザックからエネルギーが吸い取られた!", "Energy was drained from your pack!"));
241     if (is_magic_mastery && (drain != monap_ptr->o_ptr->pval)) {
242         msg_print(_("しかし、あなたの魔法を操る力がその一部を取り返した!", "However, your skill of magic mastery got back the part of energy!"));
243     }
244
245     monap_ptr->obvious = true;
246     int recovery = drain * kind_ptr->level;
247
248     if (monap_ptr->o_ptr->tval == ItemKindType::STAFF) {
249         recovery *= monap_ptr->o_ptr->number;
250     }
251
252     recovery = std::min(recovery, monap_ptr->m_ptr->maxhp - monap_ptr->m_ptr->hp);
253     monap_ptr->m_ptr->hp += recovery;
254
255     if (player_ptr->health_who == monap_ptr->m_idx) {
256         player_ptr->redraw |= PR_HEALTH;
257     }
258
259     if (player_ptr->riding == monap_ptr->m_idx) {
260         player_ptr->redraw |= PR_UHEALTH;
261     }
262
263     monap_ptr->o_ptr->pval = !is_magic_mastery || (monap_ptr->o_ptr->pval == 1) ? 0 : monap_ptr->o_ptr->pval - drain;
264     player_ptr->update |= PU_COMBINE | PU_REORDER;
265     player_ptr->window_flags |= PW_INVEN;
266     return true;
267 }
268
269 bool check_drain_hp(PlayerType *player_ptr, const int32_t d)
270 {
271     bool resist_drain = !drain_exp(player_ptr, d, d / 10, 50);
272     if (player_ptr->mimic_form != MimicKindType::NONE) {
273         return PlayerRace(player_ptr).is_mimic_nonliving() ? true : resist_drain;
274     }
275
276     switch (player_ptr->prace) {
277     case PlayerRaceType::ZOMBIE:
278     case PlayerRaceType::VAMPIRE:
279     case PlayerRaceType::SPECTRE:
280     case PlayerRaceType::SKELETON:
281     case PlayerRaceType::BALROG:
282     case PlayerRaceType::GOLEM:
283     case PlayerRaceType::ANDROID:
284         return true;
285     default:
286         return resist_drain;
287     }
288 }
289
290 void process_drain_life(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr, const bool resist_drain)
291 {
292     if ((monap_ptr->damage <= 5) || resist_drain) {
293         return;
294     }
295
296     bool did_heal = monap_ptr->m_ptr->hp < monap_ptr->m_ptr->maxhp;
297     monap_ptr->m_ptr->hp += damroll(4, monap_ptr->damage / 6);
298     if (monap_ptr->m_ptr->hp > monap_ptr->m_ptr->maxhp) {
299         monap_ptr->m_ptr->hp = monap_ptr->m_ptr->maxhp;
300     }
301
302     if (player_ptr->health_who == monap_ptr->m_idx) {
303         player_ptr->redraw |= (PR_HEALTH);
304     }
305
306     if (player_ptr->riding == monap_ptr->m_idx) {
307         player_ptr->redraw |= (PR_UHEALTH);
308     }
309
310     if (monap_ptr->m_ptr->ml && did_heal) {
311         msg_format(_("%sは体力を回復したようだ。", "%^s appears healthier."), monap_ptr->m_name);
312     }
313 }
314
315 void process_drain_mana(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
316 {
317     if (check_multishadow(player_ptr)) {
318         msg_print(_("攻撃は幻影に命中し、あなたには届かなかった。", "The attack hits Shadow, but you are unharmed!"));
319         return;
320     }
321
322     monap_ptr->do_cut = 0;
323     player_ptr->csp -= monap_ptr->damage;
324     if (player_ptr->csp < 0) {
325         player_ptr->csp = 0;
326         player_ptr->csp_frac = 0;
327     }
328
329     player_ptr->redraw |= (PR_MANA);
330 }
331
332 /*!
333  * @brief モンスターからの空腹進行処理
334  * @param player_ptr プレイヤーへの参照ポインタ
335  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
336  * @details 空腹、衰弱の一歩手前で止める優しさは残す。
337  */
338 void process_monster_attack_hungry(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
339 {
340     msg_format(_("あなたは腹が減った!", "You feel hungry!"));
341     auto subtracted_food = static_cast<int16_t>(player_ptr->food - monap_ptr->damage);
342     if ((player_ptr->food >= PY_FOOD_ALERT) && (PY_FOOD_ALERT > subtracted_food)) {
343         set_food(player_ptr, PY_FOOD_ALERT - 1);
344     } else if ((player_ptr->food > PY_FOOD_FAINT) && (PY_FOOD_FAINT >= subtracted_food)) {
345         set_food(player_ptr, PY_FOOD_FAINT);
346     } else {
347         set_food(player_ptr, subtracted_food);
348     }
349 }