OSDN Git Service

6f5d2720e51f51251edb25fa3479c5cd2ddfefec
[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/window-redrawer.h"
11 #include "flavor/flavor-describer.h"
12 #include "flavor/object-flavor-types.h"
13 #include "inventory/inventory-object.h"
14 #include "inventory/inventory-slot-types.h"
15 #include "mind/mind-mirror-master.h"
16 #include "monster-attack/monster-attack-player.h"
17 #include "monster/monster-status.h"
18 #include "object/object-info.h"
19 #include "object/object-mark-types.h"
20 #include "player-base/player-race.h"
21 #include "player-info/race-info.h"
22 #include "player/digestion-processor.h"
23 #include "player/player-status-flags.h"
24 #include "player/player-status-table.h"
25 #include "status/experience.h"
26 #include "system/baseitem-info.h"
27 #include "system/floor-type-definition.h"
28 #include "system/item-entity.h"
29 #include "system/monster-entity.h"
30 #include "system/player-type-definition.h"
31 #include "system/redrawing-flags-updater.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, Virtue::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, Virtue::SACRIFICE, 2);
75     }
76
77     auto &rfu = RedrawingFlagsUpdater::get_instance();
78     rfu.set_flag(MainWindowRedrawingFlag::GOLD);
79     player_ptr->window_flags |= (PW_PLAYER);
80     monap_ptr->blinked = true;
81 }
82
83 /*!
84  * @brief 盗み打撃の時にアイテムが盗まれるかどうかを判定する
85  * @param player_ptr プレイヤーへの参照ポインタ
86  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
87  * @return 盗まれたらTRUE、何も盗まれなかったらFALSE
88  */
89 bool check_eat_item(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
90 {
91     if (monap_ptr->m_ptr->is_confused()) {
92         return false;
93     }
94
95     if (player_ptr->is_dead || check_multishadow(player_ptr)) {
96         return false;
97     }
98
99     auto is_paralyzed = player_ptr->effects()->paralysis()->is_paralyzed();
100     if (!is_paralyzed && (randint0(100) < (adj_dex_safe[player_ptr->stat_index[A_DEX]] + player_ptr->lev))) {
101         msg_print(_("しかしあわててザックを取り返した!", "You grab hold of your backpack!"));
102         monap_ptr->blinked = true;
103         monap_ptr->obvious = true;
104         return false;
105     }
106
107     return true;
108 }
109
110 /*!
111  * @brief プレイヤーが持っているアイテムをモンスターに移す
112  * @param player_ptr プレイヤーへの参照ポインタ
113  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
114  */
115 static void move_item_to_monster(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr, const OBJECT_IDX o_idx)
116 {
117     if (o_idx == 0) {
118         return;
119     }
120
121     auto *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->is_wand_rod()) {
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.clear().set(OmType::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->is_valid()) {
147             continue;
148         }
149
150         if (monap_ptr->o_ptr->is_fixed_or_random_artifact()) {
151             continue;
152         }
153
154         const auto item_name = describe_flavor(player_ptr, monap_ptr->o_ptr, OD_OMIT_PREFIX);
155 #ifdef JP
156         msg_format("%s(%c)を%s盗まれた!", item_name.data(), 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"), item_name.data(), index_to_label(i_idx));
159 #endif
160         chg_virtue(player_ptr, Virtue::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->is_valid()) {
177             continue;
178         }
179
180         const auto tval = monap_ptr->o_ptr->bi_key.tval();
181         if ((tval != ItemKindType::FOOD) && !((tval == ItemKindType::CORPSE) && (monap_ptr->o_ptr->bi_key.sval() != 0))) {
182             continue;
183         }
184
185         const auto item_name = describe_flavor(player_ptr, monap_ptr->o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
186 #ifdef JP
187         msg_format("%s(%c)を%s食べられてしまった!", item_name.data(), index_to_label(i_idx), ((monap_ptr->o_ptr->number > 1) ? "一つ" : ""));
188 #else
189         msg_format("%sour %s (%c) was eaten!", ((monap_ptr->o_ptr->number > 1) ? "One of y" : "Y"), item_name.data(), index_to_label(i_idx));
190 #endif
191         inven_item_increase(player_ptr, i_idx, -1);
192         inven_item_optimize(player_ptr, i_idx);
193         monap_ptr->obvious = true;
194         break;
195     }
196 }
197
198 void process_eat_lite(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
199 {
200     if ((monap_ptr->o_ptr->fuel <= 0) || monap_ptr->o_ptr->is_fixed_artifact()) {
201         return;
202     }
203
204     monap_ptr->o_ptr->fuel -= 250 + randint1(250);
205     if (monap_ptr->o_ptr->fuel < 1) {
206         monap_ptr->o_ptr->fuel = 1;
207     }
208
209     if (!player_ptr->effects()->blindness()->is_blind()) {
210         msg_print(_("明かりが暗くなってしまった。", "Your light dims."));
211         monap_ptr->obvious = true;
212     }
213
214     player_ptr->window_flags |= (PW_EQUIPMENT);
215 }
216
217 /*!
218  * @brief モンスターからの攻撃による充填魔力吸収処理
219  * @param player_ptr プレイヤーへの参照ポインタ
220  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
221  * @return 吸収されたらTRUE、されなかったらFALSE
222  * @details 魔道具使用能力向上フラグがあれば、吸収量は全部ではない
223  * 詳細はOSDN #40911の議論を参照のこと
224  */
225 bool process_un_power(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
226 {
227     if (!monap_ptr->o_ptr->is_wand_staff() || (monap_ptr->o_ptr->pval == 0)) {
228         return false;
229     }
230
231     const auto is_magic_mastery = has_magic_mastery(player_ptr) != 0;
232     const auto &baseitem = monap_ptr->o_ptr->get_baseitem();
233     const auto pval = baseitem.pval;
234     const auto level = monap_ptr->rlev;
235     auto drain = is_magic_mastery ? std::min<short>(pval, pval * level / 400 + pval * randint1(level) / 400) : pval;
236     drain = std::min(drain, monap_ptr->o_ptr->pval);
237     if (drain <= 0) {
238         return false;
239     }
240
241     msg_print(_("ザックからエネルギーが吸い取られた!", "Energy was drained from your pack!"));
242     if (is_magic_mastery && (drain != monap_ptr->o_ptr->pval)) {
243         msg_print(_("しかし、あなたの魔法を操る力がその一部を取り返した!", "However, your skill of magic mastery got back the part of energy!"));
244     }
245
246     monap_ptr->obvious = true;
247     auto recovery = drain * baseitem.level;
248     const auto tval = monap_ptr->o_ptr->bi_key.tval();
249     if (tval == ItemKindType::STAFF) {
250         recovery *= monap_ptr->o_ptr->number;
251     }
252
253     recovery = std::min(recovery, monap_ptr->m_ptr->maxhp - monap_ptr->m_ptr->hp);
254     monap_ptr->m_ptr->hp += recovery;
255
256     auto &rfu = RedrawingFlagsUpdater::get_instance();
257     if (player_ptr->health_who == monap_ptr->m_idx) {
258         rfu.set_flag(MainWindowRedrawingFlag::HEALTH);
259     }
260
261     if (player_ptr->riding == monap_ptr->m_idx) {
262         rfu.set_flag(MainWindowRedrawingFlag::UHEALTH);
263     }
264
265     monap_ptr->o_ptr->pval = !is_magic_mastery || (monap_ptr->o_ptr->pval == 1) ? 0 : monap_ptr->o_ptr->pval - drain;
266     const auto flags = {
267         StatusRedrawingFlag::COMBINATION,
268         StatusRedrawingFlag::REORDER,
269     };
270     rfu.set_flags(flags);
271     player_ptr->window_flags |= PW_INVENTORY;
272     return true;
273 }
274
275 bool check_drain_hp(PlayerType *player_ptr, const int32_t d)
276 {
277     bool resist_drain = !drain_exp(player_ptr, d, d / 10, 50);
278     if (player_ptr->mimic_form != MimicKindType::NONE) {
279         return PlayerRace(player_ptr).is_mimic_nonliving() ? true : resist_drain;
280     }
281
282     switch (player_ptr->prace) {
283     case PlayerRaceType::ZOMBIE:
284     case PlayerRaceType::VAMPIRE:
285     case PlayerRaceType::SPECTRE:
286     case PlayerRaceType::SKELETON:
287     case PlayerRaceType::BALROG:
288     case PlayerRaceType::GOLEM:
289     case PlayerRaceType::ANDROID:
290         return true;
291     default:
292         return resist_drain;
293     }
294 }
295
296 void process_drain_life(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr, const bool resist_drain)
297 {
298     if ((monap_ptr->damage <= 5) || resist_drain) {
299         return;
300     }
301
302     bool did_heal = monap_ptr->m_ptr->hp < monap_ptr->m_ptr->maxhp;
303     monap_ptr->m_ptr->hp += damroll(4, monap_ptr->damage / 6);
304     if (monap_ptr->m_ptr->hp > monap_ptr->m_ptr->maxhp) {
305         monap_ptr->m_ptr->hp = monap_ptr->m_ptr->maxhp;
306     }
307
308     auto &rfu = RedrawingFlagsUpdater::get_instance();
309     if (player_ptr->health_who == monap_ptr->m_idx) {
310         rfu.set_flag(MainWindowRedrawingFlag::HEALTH);
311     }
312
313     if (player_ptr->riding == monap_ptr->m_idx) {
314         rfu.set_flag(MainWindowRedrawingFlag::UHEALTH);
315     }
316
317     if (monap_ptr->m_ptr->ml && did_heal) {
318         msg_format(_("%sは体力を回復したようだ。", "%s^ appears healthier."), monap_ptr->m_name);
319     }
320 }
321
322 void process_drain_mana(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
323 {
324     if (check_multishadow(player_ptr)) {
325         msg_print(_("攻撃は幻影に命中し、あなたには届かなかった。", "The attack hits Shadow, but you are unharmed!"));
326         return;
327     }
328
329     monap_ptr->do_cut = 0;
330     player_ptr->csp -= monap_ptr->damage;
331     if (player_ptr->csp < 0) {
332         player_ptr->csp = 0;
333         player_ptr->csp_frac = 0;
334     }
335
336     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::MP);
337 }
338
339 /*!
340  * @brief モンスターからの空腹進行処理
341  * @param player_ptr プレイヤーへの参照ポインタ
342  * @monap_ptr モンスターからモンスターへの直接攻撃構造体への参照ポインタ
343  * @details 空腹、衰弱の一歩手前で止める優しさは残す。
344  */
345 void process_monster_attack_hungry(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
346 {
347     msg_format(_("あなたは腹が減った!", "You feel hungry!"));
348     auto subtracted_food = static_cast<int16_t>(player_ptr->food - monap_ptr->damage);
349     if ((player_ptr->food >= PY_FOOD_ALERT) && (PY_FOOD_ALERT > subtracted_food)) {
350         set_food(player_ptr, PY_FOOD_ALERT - 1);
351     } else if ((player_ptr->food > PY_FOOD_FAINT) && (PY_FOOD_FAINT >= subtracted_food)) {
352         set_food(player_ptr, PY_FOOD_FAINT);
353     } else {
354         set_food(player_ptr, subtracted_food);
355     }
356 }