1 #include "hpmp/hp-mp-processor.h"
2 #include "avatar/avatar.h"
3 #include "cmd-action/cmd-pet.h"
4 #include "core/player-redraw-types.h"
5 #include "core/window-redrawer.h"
6 #include "dungeon/dungeon.h"
7 #include "flavor/flavor-describer.h"
8 #include "flavor/object-flavor-types.h"
9 #include "floor/pattern-walk.h"
10 #include "grid/feature.h"
11 #include "grid/grid.h"
12 #include "hpmp/hp-mp-regenerator.h"
13 #include "inventory/inventory-slot-types.h"
14 #include "main/sound-definitions-table.h"
15 #include "main/sound-of-music.h"
16 #include "monster-race/monster-race.h"
17 #include "monster-race/race-flags2.h"
18 #include "monster-race/race-flags3.h"
19 #include "object-enchant/object-ego.h"
20 #include "object-enchant/tr-types.h"
21 #include "object-enchant/trc-types.h"
22 #include "object/object-flags.h"
23 #include "pet/pet-util.h"
24 #include "player-base/player-class.h"
25 #include "player-base/player-race.h"
26 #include "player-info/monk-data-type.h"
27 #include "player-info/race-info.h"
28 #include "player-info/race-types.h"
29 #include "player-info/samurai-data-type.h"
30 #include "player/attack-defense-types.h"
31 #include "player/digestion-processor.h"
32 #include "player/player-damage.h"
33 #include "player/player-status-flags.h"
34 #include "player/player-status-resist.h"
35 #include "player/player-status.h"
36 #include "player/special-defense-types.h"
37 #include "status/bad-status-setter.h"
38 #include "status/element-resistance.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/object-type-definition.h"
44 #include "system/player-type-definition.h"
45 #include "timed-effect/player-cut.h"
46 #include "timed-effect/timed-effects.h"
47 #include "util/bit-flags-calculator.h"
48 #include "view/display-messages.h"
49 #include "world/world.h"
53 * @brief 地形によるダメージを与える / Deal damage from feature.
54 * @param player_ptr プレイヤー情報への参照ポインタ
55 * @param g_ptr 現在の床の情報への参照ポインタ
56 * @param msg_levitation 浮遊時にダメージを受けた場合に表示するメッセージ
57 * @param msg_normal 通常時にダメージを受けた場合に表示するメッセージの述部
58 * @param 耐性等によるダメージレートを計算する関数
59 * @param ダメージを受けた際の追加処理を行う関数
60 * @return ダメージを与えたらTRUE、なければFALSE
62 * ダメージを受けた場合、自然回復できない。
64 static bool deal_damege_by_feat(player_type *player_ptr, grid_type *g_ptr, concptr msg_levitation, concptr msg_normal,
65 std::function<PERCENTAGE(player_type *)> damage_rate, std::function<void(player_type *, int)> additional_effect)
67 feature_type *f_ptr = &f_info[g_ptr->feat];
70 if (f_ptr->flags.has(FF::DEEP)) {
71 damage = 6000 + randint0(4000);
72 } else if (!player_ptr->levitation) {
73 damage = 3000 + randint0(2000);
76 damage *= damage_rate(player_ptr);
78 if (player_ptr->levitation)
81 damage = damage / 100 + (randint0(100) < (damage % 100));
86 if (player_ptr->levitation) {
87 msg_print(msg_levitation);
89 take_hit(player_ptr, DAMAGE_NOESCAPE, damage, format(_("%sの上に浮遊したダメージ", "flying over %s"), f_info[g_ptr->get_feat_mimic()].name.c_str()));
91 if (additional_effect != nullptr)
92 additional_effect(player_ptr, damage);
94 concptr name = f_info[player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x].get_feat_mimic()].name.c_str();
95 msg_format(_("%s%s!", "The %s %s!"), name, msg_normal);
96 take_hit(player_ptr, DAMAGE_NOESCAPE, damage, name);
98 if (additional_effect != nullptr)
99 additional_effect(player_ptr, damage);
106 * @brief 10ゲームターンが進行するごとにプレイヤーのHPとMPの増減処理を行う。
107 * / Handle timed damage and regeneration every 10 game turns
109 void process_player_hp_mp(player_type *player_ptr)
111 grid_type *g_ptr = &player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x];
112 feature_type *f_ptr = &f_info[g_ptr->feat];
113 bool cave_no_regen = false;
114 int upkeep_factor = 0;
115 int regen_amount = PY_REGEN_NORMAL;
116 if (player_ptr->poisoned && !is_invuln(player_ptr)) {
117 if (take_hit(player_ptr, DAMAGE_NOESCAPE, 1, _("毒", "poison")) > 0) {
118 sound(SOUND_DAMAGE_OVER_TIME);
122 auto player_cut = player_ptr->effects()->cut();
123 if (player_cut->is_cut() && !is_invuln(player_ptr)) {
124 auto dam = player_cut->get_damage();
125 if (take_hit(player_ptr, DAMAGE_NOESCAPE, dam, _("致命傷", "a mortal wound")) > 0) {
126 sound(SOUND_DAMAGE_OVER_TIME);
130 const PlayerRace race(player_ptr);
131 if (race.life() == PlayerRaceLife::UNDEAD && race.tr_flags().has(TR_VUL_LITE)) {
132 if (!is_in_dungeon(player_ptr) && !has_resist_lite(player_ptr) && !is_invuln(player_ptr) && is_daytime()) {
133 if ((player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & (CAVE_GLOW | CAVE_MNDK)) == CAVE_GLOW) {
134 msg_print(_("日光があなたのアンデッドの肉体を焼き焦がした!", "The sun's rays scorch your undead flesh!"));
135 take_hit(player_ptr, DAMAGE_NOESCAPE, 1, _("日光", "sunlight"));
136 cave_no_regen = true;
141 o_ptr = &player_ptr->inventory_list[INVEN_LITE];
142 auto flgs = object_flags(o_ptr);
144 if ((player_ptr->inventory_list[INVEN_LITE].tval != ItemKindType::NONE) && flgs.has_not(TR_DARK_SOURCE) && !has_resist_lite(player_ptr)) {
145 GAME_TEXT o_name[MAX_NLEN];
146 char ouch[MAX_NLEN + 40];
147 describe_flavor(player_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
148 msg_format(_("%sがあなたのアンデッドの肉体を焼き焦がした!", "The %s scorches your undead flesh!"), o_name);
150 cave_no_regen = true;
151 describe_flavor(player_ptr, o_name, o_ptr, OD_NAME_ONLY);
152 sprintf(ouch, _("%sを装備したダメージ", "wielding %s"), o_name);
154 if (!is_invuln(player_ptr))
155 take_hit(player_ptr, DAMAGE_NOESCAPE, 1, ouch);
159 if (f_ptr->flags.has(FF::LAVA) && !is_invuln(player_ptr) && !has_immune_fire(player_ptr)) {
160 if (deal_damege_by_feat(
161 player_ptr, g_ptr, _("熱で火傷した!", "The heat burns you!"), _("で火傷した!", "burns you!"), calc_fire_damage_rate, nullptr)) {
162 cave_no_regen = true;
163 sound(SOUND_TERRAIN_DAMAGE);
167 if (f_ptr->flags.has(FF::COLD_PUDDLE) && !is_invuln(player_ptr) && !has_immune_cold(player_ptr)) {
168 if (deal_damege_by_feat(
169 player_ptr, g_ptr, _("冷気に覆われた!", "The cold engulfs you!"), _("に凍えた!", "frostbites you!"), calc_cold_damage_rate, nullptr)) {
170 cave_no_regen = true;
171 sound(SOUND_TERRAIN_DAMAGE);
175 if (f_ptr->flags.has(FF::ELEC_PUDDLE) && !is_invuln(player_ptr) && !has_immune_elec(player_ptr)) {
176 if (deal_damege_by_feat(
177 player_ptr, g_ptr, _("電撃を受けた!", "The electricity shocks you!"), _("に感電した!", "shocks you!"), calc_elec_damage_rate, nullptr)) {
178 cave_no_regen = true;
179 sound(SOUND_TERRAIN_DAMAGE);
183 if (f_ptr->flags.has(FF::ACID_PUDDLE) && !is_invuln(player_ptr) && !has_immune_acid(player_ptr)) {
184 if (deal_damege_by_feat(
185 player_ptr, g_ptr, _("酸が飛び散った!", "The acid melts you!"), _("に溶かされた!", "melts you!"), calc_acid_damage_rate, nullptr)) {
186 cave_no_regen = true;
187 sound(SOUND_TERRAIN_DAMAGE);
191 if (f_ptr->flags.has(FF::POISON_PUDDLE) && !is_invuln(player_ptr)) {
192 if (deal_damege_by_feat(player_ptr, g_ptr, _("毒気を吸い込んだ!", "The gas poisons you!"), _("に毒された!", "poisons you!"), calc_acid_damage_rate,
193 [](player_type *player_ptr, int damage) {
194 if (!has_resist_pois(player_ptr))
195 (void)BadStatusSetter(player_ptr).mod_poison(static_cast<TIME_EFFECT>(damage));
197 cave_no_regen = true;
198 sound(SOUND_TERRAIN_DAMAGE);
202 if (f_ptr->flags.has_all_of({ FF::WATER, FF::DEEP }) && !player_ptr->levitation && !player_ptr->can_swim && !has_resist_water(player_ptr)) {
203 if (calc_inventory_weight(player_ptr) > calc_weight_limit(player_ptr)) {
204 msg_print(_("溺れている!", "You are drowning!"));
205 take_hit(player_ptr, DAMAGE_NOESCAPE, randint1(player_ptr->lev), _("溺れ", "drowning"));
206 cave_no_regen = true;
207 sound(SOUND_TERRAIN_DAMAGE);
211 if (player_ptr->riding) {
213 if ((r_info[player_ptr->current_floor_ptr->m_list[player_ptr->riding].r_idx].flags2 & RF2_AURA_FIRE) && !has_immune_fire(player_ptr)) {
214 damage = r_info[player_ptr->current_floor_ptr->m_list[player_ptr->riding].r_idx].level / 2;
215 if (race.tr_flags().has(TR_VUL_FIRE))
216 damage += damage / 3;
217 if (has_resist_fire(player_ptr))
219 if (is_oppose_fire(player_ptr))
221 msg_print(_("熱い!", "It's hot!"));
222 take_hit(player_ptr, DAMAGE_NOESCAPE, damage, _("炎のオーラ", "Fire aura"));
224 if ((r_info[player_ptr->current_floor_ptr->m_list[player_ptr->riding].r_idx].flags2 & RF2_AURA_ELEC) && !has_immune_elec(player_ptr)) {
225 damage = r_info[player_ptr->current_floor_ptr->m_list[player_ptr->riding].r_idx].level / 2;
226 if (race.tr_flags().has(TR_VUL_ELEC))
227 damage += damage / 3;
228 if (has_resist_elec(player_ptr))
230 if (is_oppose_elec(player_ptr))
232 msg_print(_("痛い!", "It hurts!"));
233 take_hit(player_ptr, DAMAGE_NOESCAPE, damage, _("電気のオーラ", "Elec aura"));
235 if ((r_info[player_ptr->current_floor_ptr->m_list[player_ptr->riding].r_idx].flags3 & RF3_AURA_COLD) && !has_immune_cold(player_ptr)) {
236 damage = r_info[player_ptr->current_floor_ptr->m_list[player_ptr->riding].r_idx].level / 2;
237 if (race.tr_flags().has(TR_VUL_COLD))
238 damage += damage / 3;
239 if (has_resist_cold(player_ptr))
241 if (is_oppose_cold(player_ptr))
243 msg_print(_("冷たい!", "It's cold!"));
244 take_hit(player_ptr, DAMAGE_NOESCAPE, damage, _("冷気のオーラ", "Cold aura"));
248 /* Spectres -- take damage when moving through walls */
250 * Added: ANYBODY takes damage if inside through walls
251 * without wraith form -- NOTE: Spectres will never be
252 * reduced below 0 hp by being inside a stone wall; others
255 if (f_ptr->flags.has_none_of({ FF::MOVE, FF::CAN_FLY })) {
256 if (!is_invuln(player_ptr) && !player_ptr->wraith_form && !player_ptr->tim_pass_wall
257 && ((player_ptr->chp > (player_ptr->lev / 5)) || !has_pass_wall(player_ptr))) {
259 cave_no_regen = true;
261 if (has_pass_wall(player_ptr)) {
262 msg_print(_("体の分子が分解した気がする!", "Your molecules feel disrupted!"));
263 dam_desc = _("密度", "density");
265 msg_print(_("崩れた岩に押し潰された!", "You are being crushed!"));
266 dam_desc = _("硬い岩", "solid rock");
269 take_hit(player_ptr, DAMAGE_NOESCAPE, 1 + (player_ptr->lev / 5), dam_desc);
273 if (player_ptr->food < PY_FOOD_WEAK) {
274 if (player_ptr->food < PY_FOOD_STARVE) {
276 } else if (player_ptr->food < PY_FOOD_FAINT) {
277 regen_amount = PY_REGEN_FAINT;
279 regen_amount = PY_REGEN_WEAK;
283 if (pattern_effect(player_ptr)) {
284 cave_no_regen = true;
286 if (player_ptr->regenerate) {
287 regen_amount = regen_amount * 2;
289 if (!PlayerClass(player_ptr).monk_stance_is(MonkStance::NONE) || !PlayerClass(player_ptr).samurai_stance_is(SamuraiStance::NONE)) {
292 if (player_ptr->cursed.has(TRC::SLOW_REGEN)) {
297 if ((player_ptr->action == ACTION_SEARCH) || (player_ptr->action == ACTION_REST)) {
298 regen_amount = regen_amount * 2;
301 upkeep_factor = calculate_upkeep(player_ptr);
302 if ((player_ptr->action == ACTION_LEARN) || (player_ptr->action == ACTION_HAYAGAKE) || PlayerClass(player_ptr).samurai_stance_is(SamuraiStance::KOUKIJIN)) {
303 upkeep_factor += 100;
306 regenmana(player_ptr, upkeep_factor, regen_amount);
307 if (player_ptr->pclass == CLASS_MAGIC_EATER) {
308 regenmagic(player_ptr, regen_amount);
311 if ((player_ptr->csp == 0) && (player_ptr->csp_frac == 0)) {
312 while (upkeep_factor > 100) {
313 msg_print(_("こんなに多くのペットを制御できない!", "Too many pets to control at once!"));
315 do_cmd_pet_dismiss(player_ptr);
317 upkeep_factor = calculate_upkeep(player_ptr);
319 msg_format(_("維持MPは %d%%", "Upkeep: %d%% mana."), upkeep_factor);
324 if (player_ptr->poisoned)
326 if (player_cut->is_cut())
331 regen_amount = (regen_amount * player_ptr->mutant_regenerate_mod) / 100;
332 if ((player_ptr->chp < player_ptr->mhp) && !cave_no_regen) {
333 regenhp(player_ptr, regen_amount);
338 * Increase players hit points, notice effects
340 bool hp_player(player_type *player_ptr, int num)
343 vir = virtue_number(player_ptr, V_VITALITY);
349 num = num * (player_ptr->virtues[vir - 1] + 1250) / 1250;
352 if (player_ptr->chp < player_ptr->mhp) {
353 if ((num > 0) && (player_ptr->chp < (player_ptr->mhp / 3)))
354 chg_virtue(player_ptr, V_TEMPERANCE, 1);
356 player_ptr->chp += num;
357 if (player_ptr->chp >= player_ptr->mhp) {
358 player_ptr->chp = player_ptr->mhp;
359 player_ptr->chp_frac = 0;
362 player_ptr->redraw |= (PR_HP);
363 player_ptr->window_flags |= (PW_PLAYER);
365 msg_print(_("少し気分が良くなった。", "You feel a little better."));
366 } else if (num < 15) {
367 msg_print(_("気分が良くなった。", "You feel better."));
368 } else if (num < 35) {
369 msg_print(_("とても気分が良くなった。", "You feel much better."));
371 msg_print(_("ひじょうに気分が良くなった。", "You feel very good."));