OSDN Git Service

[Refactor] モンスター(ペット)に名付けているか判定するメソッドを追加
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-world.cpp
index cefdb3d..2750e6a 100644 (file)
@@ -1,10 +1,14 @@
-#include "spell-kind/spells-world.h"
+/*
+ * @brief 帰還やテレポート・レベル等、フロアを跨ぐ魔法効果の処理
+ * @author Hourier
+ * @date 2022/10/10
+ */
+
+#include "spell-kind/spells-world.h"
 #include "cmd-io/cmd-save.h"
 #include "core/asking-player.h"
 #include "core/player-redraw-types.h"
-#include "dungeon/dungeon.h"
 #include "dungeon/quest-completion-checker.h"
-#include "dungeon/quest.h"
 #include "floor/cave.h"
 #include "floor/floor-mode-changer.h"
 #include "floor/floor-town.h"
 #include "market/building-util.h"
 #include "monster-floor/monster-remover.h"
 #include "monster-race/monster-race.h"
-#include "monster-race/race-ability-mask.h"
-#include "monster-race/race-flags-resistance.h"
 #include "monster-race/race-flags1.h"
-#include "monster-race/race-resistance-mask.h"
 #include "monster/monster-describer.h"
 #include "monster/monster-description-types.h"
-#include "monster/monster-info.h"
-#include "player/player-status.h"
+#include "system/dungeon-info.h"
 #include "system/floor-type-definition.h"
 #include "system/grid-type-definition.h"
-#include "system/monster-race-definition.h"
-#include "system/monster-type-definition.h"
+#include "system/monster-entity.h"
+#include "system/monster-race-info.h"
 #include "system/player-type-definition.h"
 #include "target/projection-path-calculator.h"
 #include "target/target-checker.h"
 #include "target/target-setter.h"
 #include "target/target-types.h"
 #include "term/screen-processor.h"
+#include "term/z-form.h"
 #include "util/int-char-converter.h"
 #include "view/display-messages.h"
 #include "world/world.h"
@@ -53,7 +54,7 @@ bool is_teleport_level_ineffective(PlayerType *player_ptr, MONSTER_IDX idx)
     auto *floor_ptr = player_ptr->current_floor_ptr;
     bool is_special_floor = floor_ptr->inside_arena || player_ptr->phase_out || (inside_quest(floor_ptr->quest_number) && !inside_quest(random_quest_number(player_ptr, floor_ptr->dun_level)));
     bool is_invalid_floor = idx <= 0;
-    is_invalid_floor &= inside_quest(quest_number(player_ptr, floor_ptr->dun_level)) || (floor_ptr->dun_level >= d_info[player_ptr->dungeon_idx].maxdepth);
+    is_invalid_floor &= inside_quest(quest_number(player_ptr, floor_ptr->dun_level)) || (floor_ptr->dun_level >= dungeons_info[player_ptr->dungeon_idx].maxdepth);
     is_invalid_floor &= player_ptr->current_floor_ptr->dun_level >= 1;
     is_invalid_floor &= ironman_downward;
     return is_special_floor || is_invalid_floor;
@@ -68,13 +69,14 @@ bool is_teleport_level_ineffective(PlayerType *player_ptr, MONSTER_IDX idx)
  */
 void teleport_level(PlayerType *player_ptr, MONSTER_IDX m_idx)
 {
-    GAME_TEXT m_name[160];
-    bool see_m = true;
+    std::string m_name;
+    auto see_m = true;
+    auto &floor_ref = *player_ptr->current_floor_ptr;
     if (m_idx <= 0) {
-        strcpy(m_name, _("あなた", "you"));
+        m_name = _("あなた", "you");
     } else {
-        auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
-        monster_desc(player_ptr, m_name, m_ptr, 0);
+        auto *m_ptr = &floor_ref.m_list[m_idx];
+        m_name = monster_desc(player_ptr, m_ptr, 0);
         see_m = is_seen(player_ptr, m_ptr);
     }
 
@@ -105,18 +107,18 @@ void teleport_level(PlayerType *player_ptr, MONSTER_IDX m_idx)
         }
     }
 
-    if ((ironman_downward && (m_idx <= 0)) || (player_ptr->current_floor_ptr->dun_level <= d_info[player_ptr->dungeon_idx].mindepth)) {
+    if ((ironman_downward && (m_idx <= 0)) || (floor_ref.dun_level <= dungeons_info[player_ptr->dungeon_idx].mindepth)) {
 #ifdef JP
         if (see_m) {
-            msg_format("%^sは床を突き破って沈んでいく。", m_name);
+            msg_format("%^sは床を突き破って沈んでいく。", m_name.data());
         }
 #else
         if (see_m) {
-            msg_format("%^s sink%s through the floor.", m_name, (m_idx <= 0) ? "" : "s");
+            msg_format("%^s sink%s through the floor.", m_name.data(), (m_idx <= 0) ? "" : "s");
         }
 #endif
         if (m_idx <= 0) {
-            if (!is_in_dungeon(player_ptr)) {
+            if (!floor_ref.is_in_dungeon()) {
                 player_ptr->dungeon_idx = ironman_downward ? DUNGEON_ANGBAND : player_ptr->recall_dungeon;
                 player_ptr->oldpy = player_ptr->y;
                 player_ptr->oldpx = player_ptr->x;
@@ -130,8 +132,8 @@ void teleport_level(PlayerType *player_ptr, MONSTER_IDX m_idx)
                 do_cmd_save_game(player_ptr, true);
             }
 
-            if (!is_in_dungeon(player_ptr)) {
-                player_ptr->current_floor_ptr->dun_level = d_info[player_ptr->dungeon_idx].mindepth;
+            if (!floor_ref.is_in_dungeon()) {
+                floor_ref.dun_level = dungeons_info[player_ptr->dungeon_idx].mindepth;
                 prepare_change_floor_mode(player_ptr, CFM_RAND_PLACE);
             } else {
                 prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_DOWN | CFM_RAND_PLACE | CFM_RAND_CONNECT);
@@ -139,14 +141,14 @@ void teleport_level(PlayerType *player_ptr, MONSTER_IDX m_idx)
 
             player_ptr->leaving = true;
         }
-    } else if (inside_quest(quest_number(player_ptr, player_ptr->current_floor_ptr->dun_level)) || (player_ptr->current_floor_ptr->dun_level >= d_info[player_ptr->dungeon_idx].maxdepth)) {
+    } else if (inside_quest(quest_number(player_ptr, floor_ref.dun_level)) || (floor_ref.dun_level >= dungeons_info[player_ptr->dungeon_idx].maxdepth)) {
 #ifdef JP
         if (see_m) {
-            msg_format("%^sは天井を突き破って宙へ浮いていく。", m_name);
+            msg_format("%^sは天井を突き破って宙へ浮いていく。", m_name.data());
         }
 #else
         if (see_m) {
-            msg_format("%^s rise%s up through the ceiling.", m_name, (m_idx <= 0) ? "" : "s");
+            msg_format("%^s rise%s up through the ceiling.", m_name.data(), (m_idx <= 0) ? "" : "s");
         }
 #endif
 
@@ -162,17 +164,17 @@ void teleport_level(PlayerType *player_ptr, MONSTER_IDX m_idx)
             prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_UP | CFM_RAND_PLACE | CFM_RAND_CONNECT);
 
             leave_quest_check(player_ptr);
-            player_ptr->current_floor_ptr->quest_number = QuestId::NONE;
+            floor_ref.quest_number = QuestId::NONE;
             player_ptr->leaving = true;
         }
     } else if (go_up) {
 #ifdef JP
         if (see_m) {
-            msg_format("%^sは天井を突き破って宙へ浮いていく。", m_name);
+            msg_format("%^sは天井を突き破って宙へ浮いていく。", m_name.data());
         }
 #else
         if (see_m) {
-            msg_format("%^s rise%s up through the ceiling.", m_name, (m_idx <= 0) ? "" : "s");
+            msg_format("%^s rise%s up through the ceiling.", m_name.data(), (m_idx <= 0) ? "" : "s");
         }
 #endif
 
@@ -191,11 +193,11 @@ void teleport_level(PlayerType *player_ptr, MONSTER_IDX m_idx)
     } else {
 #ifdef JP
         if (see_m) {
-            msg_format("%^sは床を突き破って沈んでいく。", m_name);
+            msg_format("%^sは床を突き破って沈んでいく。", m_name.data());
         }
 #else
         if (see_m) {
-            msg_format("%^s sink%s through the floor.", m_name, (m_idx <= 0) ? "" : "s");
+            msg_format("%^s sink%s through the floor.", m_name.data(), (m_idx <= 0) ? "" : "s");
         }
 #endif
 
@@ -217,13 +219,11 @@ void teleport_level(PlayerType *player_ptr, MONSTER_IDX m_idx)
         return;
     }
 
-    auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
+    auto *m_ptr = &floor_ref.m_list[m_idx];
     QuestCompletionChecker(player_ptr, m_ptr).complete();
-    if (record_named_pet && is_pet(m_ptr) && m_ptr->nickname) {
-        char m2_name[MAX_NLEN];
-
-        monster_desc(player_ptr, m2_name, m_ptr, MD_INDEF_VISIBLE);
-        exe_write_diary(player_ptr, DIARY_NAMED_PET, RECORD_NAMED_PET_TELE_LEVEL, m2_name);
+    if (record_named_pet && m_ptr->is_named_pet()) {
+        const auto m2_name = monster_desc(player_ptr, m_ptr, MD_INDEF_VISIBLE);
+        exe_write_diary(player_ptr, DIARY_NAMED_PET, RECORD_NAMED_PET_TELE_LEVEL, m2_name.data());
     }
 
     delete_monster_idx(player_ptr, m_idx);
@@ -248,18 +248,17 @@ bool teleport_level_other(PlayerType *player_ptr)
         return true;
     }
 
-    monster_type *m_ptr;
-    monster_race *r_ptr;
+    MonsterEntity *m_ptr;
+    MonsterRaceInfo *r_ptr;
     m_ptr = &player_ptr->current_floor_ptr->m_list[target_m_idx];
-    r_ptr = &r_info[m_ptr->r_idx];
-    GAME_TEXT m_name[MAX_NLEN];
-    monster_desc(player_ptr, m_name, m_ptr, 0);
-    msg_format(_("%^sの足を指さした。", "You gesture at %^s's feet."), m_name);
+    r_ptr = &monraces_info[m_ptr->r_idx];
+    const auto m_name = monster_desc(player_ptr, m_ptr, 0);
+    msg_format(_("%^sの足を指さした。", "You gesture at %^s's feet."), m_name.data());
 
     auto has_immune = r_ptr->resistance_flags.has_any_of(RFR_EFF_RESIST_NEXUS_MASK) || r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_TELEPORT);
 
     if (has_immune || (r_ptr->flags1 & RF1_QUESTOR) || (r_ptr->level + randint1(50) > player_ptr->lev + randint1(60))) {
-        msg_format(_("しかし効果がなかった!", "%^s is unaffected!"), m_name);
+        msg_format(_("しかし効果がなかった!", "%^s is unaffected!"), m_name.data());
     } else {
         teleport_level(player_ptr, target_m_idx);
     }
@@ -292,11 +291,11 @@ bool tele_town(PlayerType *player_ptr)
     for (i = 1; i < max_towns; i++) {
         char buf[80];
 
-        if ((i == NO_TOWN) || (i == SECRET_TOWN) || (i == player_ptr->town_num) || !(player_ptr->visit & (1UL << (i - 1)))) {
+        if ((i == VALID_TOWNS) || (i == SECRET_TOWN) || (i == player_ptr->town_num) || !(player_ptr->visit & (1UL << (i - 1)))) {
             continue;
         }
 
-        sprintf(buf, "%c) %-20s", I2A(i - 1), town_info[i].name);
+        strnfmt(buf, sizeof(buf), "%c) %-20s", I2A(i - 1), town_info[i].name);
         prt(buf, 5 + i, 5);
         num++;
     }
@@ -319,7 +318,7 @@ bool tele_town(PlayerType *player_ptr)
 
         else if ((i < 'a') || (i > ('a' + max_towns - 2))) {
             continue;
-        } else if (((i - 'a' + 1) == player_ptr->town_num) || ((i - 'a' + 1) == NO_TOWN) || ((i - 'a' + 1) == SECRET_TOWN) || !(player_ptr->visit & (1UL << (i - 'a')))) {
+        } else if (((i - 'a' + 1) == player_ptr->town_num) || ((i - 'a' + 1) == VALID_TOWNS) || ((i - 'a' + 1) == SECRET_TOWN) || !(player_ptr->visit & (1UL << (i - 'a')))) {
             continue;
         }
         break;
@@ -365,30 +364,97 @@ void reserve_alter_reality(PlayerType *player_ptr, TIME_EFFECT turns)
 }
 
 /*!
+ * @brief これまでに入ったダンジョンの一覧を表示し、選択させる。
+ * @param note ダンジョンに施す処理記述
+ * @param y コンソールY座標
+ * @param x コンソールX座標
+ * @return 選択されたダンジョンID
+ */
+static DUNGEON_IDX choose_dungeon(concptr note, POSITION y, POSITION x)
+{
+    DUNGEON_IDX select_dungeon;
+    if (lite_town || vanilla_town || ironman_downward) {
+        if (max_dlv[DUNGEON_ANGBAND]) {
+            return DUNGEON_ANGBAND;
+        } else {
+            msg_format(_("まだ%sに入ったことはない。", "You haven't entered %s yet."), dungeons_info[DUNGEON_ANGBAND].name.data());
+            msg_print(nullptr);
+            return 0;
+        }
+    }
+
+    std::vector<DUNGEON_IDX> dun;
+
+    screen_save();
+    for (const auto &d_ref : dungeons_info) {
+        char buf[80];
+        bool seiha = false;
+
+        if (d_ref.idx == 0 || !d_ref.maxdepth) {
+            continue;
+        }
+        if (!max_dlv[d_ref.idx]) {
+            continue;
+        }
+        if (MonsterRace(d_ref.final_guardian).is_valid()) {
+            if (!monraces_info[d_ref.final_guardian].max_num) {
+                seiha = true;
+            }
+        } else if (max_dlv[d_ref.idx] == d_ref.maxdepth) {
+            seiha = true;
+        }
+
+        strnfmt(buf, sizeof(buf), _("      %c) %c%-12s : 最大 %d 階", "      %c) %c%-16s : Max level %d"), static_cast<char>('a' + dun.size()), seiha ? '!' : ' ', d_ref.name.data(), (int)max_dlv[d_ref.idx]);
+        prt(buf, y + dun.size(), x);
+        dun.push_back(d_ref.idx);
+    }
+
+    if (dun.empty()) {
+        prt(_("      選べるダンジョンがない。", "      No dungeon is available."), y, x);
+    }
+
+    prt(format(_("どのダンジョン%sしますか:", "Which dungeon do you %s?: "), note), 0, 0);
+    while (true) {
+        auto i = inkey();
+        if ((i == ESCAPE) || dun.empty()) {
+            screen_load();
+            return 0;
+        }
+        if (i >= 'a' && i < static_cast<char>('a' + dun.size())) {
+            select_dungeon = dun[i - 'a'];
+            break;
+        } else {
+            bell();
+        }
+    }
+    screen_load();
+
+    return select_dungeon;
+}
+
+/*!
  * @brief プレイヤーの帰還発動及び中止処理 /
  * Recall the player to town or dungeon
  * @param player_ptr プレイヤーへの参照ポインタ
  * @param turns 発動までのターン数
  * @return 常にTRUEを返す
+ * @todo Recall the player to the last visited town when in the wilderness
  */
 bool recall_player(PlayerType *player_ptr, TIME_EFFECT turns)
 {
-    /*
-     * TODO: Recall the player to the last
-     * visited town when in the wilderness
-     */
-    if (player_ptr->current_floor_ptr->inside_arena || ironman_downward) {
+    const auto &floor_ref = *player_ptr->current_floor_ptr;
+    if (floor_ref.inside_arena || ironman_downward) {
         msg_print(_("何も起こらなかった。", "Nothing happens."));
         return true;
     }
 
-    bool is_special_floor = is_in_dungeon(player_ptr);
-    is_special_floor &= max_dlv[player_ptr->dungeon_idx] > player_ptr->current_floor_ptr->dun_level;
-    is_special_floor &= !inside_quest(player_ptr->current_floor_ptr->quest_number);
+    bool is_special_floor = floor_ref.is_in_dungeon();
+    is_special_floor &= max_dlv[player_ptr->dungeon_idx] > floor_ref.dun_level;
+    is_special_floor &= !inside_quest(floor_ref.quest_number);
     is_special_floor &= !player_ptr->word_recall;
     if (is_special_floor) {
         if (get_check(_("ここは最深到達階より浅い階です。この階に戻って来ますか? ", "Reset recall depth? "))) {
-            max_dlv[player_ptr->dungeon_idx] = player_ptr->current_floor_ptr->dun_level;
+            max_dlv[player_ptr->dungeon_idx] = floor_ref.dun_level;
             if (record_maxdepth) {
                 exe_write_diary(player_ptr, DIARY_TRUMP, player_ptr->dungeon_idx, _("帰還のときに", "when recalled from dungeon"));
             }
@@ -402,7 +468,7 @@ bool recall_player(PlayerType *player_ptr, TIME_EFFECT turns)
         return true;
     }
 
-    if (!is_in_dungeon(player_ptr)) {
+    if (!floor_ref.is_in_dungeon()) {
         DUNGEON_IDX select_dungeon;
         select_dungeon = choose_dungeon(_("に帰還", "recall"), 2, 14);
         if (!select_dungeon) {
@@ -424,25 +490,27 @@ bool free_level_recall(PlayerType *player_ptr)
         return false;
     }
 
-    DEPTH max_depth = d_info[select_dungeon].maxdepth;
+    const auto &dungeon = dungeons_info[select_dungeon];
+    auto max_depth = dungeon.maxdepth;
     if (select_dungeon == DUNGEON_ANGBAND) {
-        if (quest[enum2i(QuestId::OBERON)].status != QuestStatusType::FINISHED) {
+        const auto &quest_list = QuestList::get_instance();
+        if (quest_list[QuestId::OBERON].status != QuestStatusType::FINISHED) {
             max_depth = 98;
-        } else if (quest[enum2i(QuestId::SERPENT)].status != QuestStatusType::FINISHED) {
+        } else if (quest_list[QuestId::SERPENT].status != QuestStatusType::FINISHED) {
             max_depth = 99;
         }
     }
 
-    QUANTITY amt = get_quantity(
-        format(_("%sの何階にテレポートしますか?", "Teleport to which level of %s? "), d_info[select_dungeon].name.c_str()), (QUANTITY)max_depth);
+    const auto mes = _("%sの何階にテレポートしますか?", "Teleport to which level of %s? ");
+    QUANTITY amt = get_quantity(format(mes, dungeon.name.data()), (QUANTITY)max_depth);
     if (amt <= 0) {
         return false;
     }
 
     player_ptr->word_recall = 1;
     player_ptr->recall_dungeon = select_dungeon;
-    max_dlv[player_ptr->recall_dungeon] = ((amt > d_info[select_dungeon].maxdepth) ? d_info[select_dungeon].maxdepth
-                                                                                   : ((amt < d_info[select_dungeon].mindepth) ? d_info[select_dungeon].mindepth : amt));
+    max_dlv[player_ptr->recall_dungeon] = ((amt > dungeon.maxdepth) ? dungeon.maxdepth
+                                                                    : ((amt < dungeon.mindepth) ? dungeon.mindepth : amt));
     if (record_maxdepth) {
         exe_write_diary(player_ptr, DIARY_TRUMP, select_dungeon, _("トランプタワーで", "at Trump Tower"));
     }
@@ -461,8 +529,6 @@ bool free_level_recall(PlayerType *player_ptr)
 bool reset_recall(PlayerType *player_ptr)
 {
     int select_dungeon, dummy = 0;
-    char ppp[80];
-    char tmp_val[160];
 
     select_dungeon = choose_dungeon(_("をセット", "reset"), 2, 14);
     if (ironman_downward) {
@@ -473,8 +539,10 @@ bool reset_recall(PlayerType *player_ptr)
     if (!select_dungeon) {
         return false;
     }
-    sprintf(ppp, _("何階にセットしますか (%d-%d):", "Reset to which level (%d-%d): "), (int)d_info[select_dungeon].mindepth, (int)max_dlv[select_dungeon]);
-    sprintf(tmp_val, "%d", (int)std::max(player_ptr->current_floor_ptr->dun_level, 1));
+    char ppp[80];
+    strnfmt(ppp, sizeof(ppp), _("何階にセットしますか (%d-%d):", "Reset to which level (%d-%d): "), (int)dungeons_info[select_dungeon].mindepth, (int)max_dlv[select_dungeon]);
+    char tmp_val[160];
+    strnfmt(tmp_val, sizeof(tmp_val), "%d", (int)std::max(player_ptr->current_floor_ptr->dun_level, 1));
 
     if (!get_string(ppp, tmp_val, 10)) {
         return false;
@@ -487,8 +555,8 @@ bool reset_recall(PlayerType *player_ptr)
     if (dummy > max_dlv[select_dungeon]) {
         dummy = max_dlv[select_dungeon];
     }
-    if (dummy < d_info[select_dungeon].mindepth) {
-        dummy = d_info[select_dungeon].mindepth;
+    if (dummy < dungeons_info[select_dungeon].mindepth) {
+        dummy = dungeons_info[select_dungeon].mindepth;
     }
 
     max_dlv[select_dungeon] = dummy;
@@ -497,7 +565,7 @@ bool reset_recall(PlayerType *player_ptr)
         exe_write_diary(player_ptr, DIARY_TRUMP, select_dungeon, _("フロア・リセットで", "using a scroll of reset recall"));
     }
 #ifdef JP
-    msg_format("%sの帰還レベルを %d 階にセット。", d_info[select_dungeon].name.c_str(), dummy, dummy * 50);
+    msg_format("%sの帰還レベルを %d 階にセット。", dungeons_info[select_dungeon].name.data(), dummy, dummy * 50);
 #else
     msg_format("Recall depth set to level %d (%d').", dummy, dummy * 50);
 #endif