OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / market / arena.cpp
1 #include "market/arena.h"
2 #include "cmd-building/cmd-building.h"
3 #include "core/asking-player.h"
4 #include "core/show-file.h"
5 #include "core/stuff-handler.h"
6 #include "core/window-redrawer.h"
7 #include "floor/floor-mode-changer.h"
8 #include "io/input-key-acceptor.h"
9 #include "main/sound-of-music.h"
10 #include "market/arena-info-table.h"
11 #include "market/building-actions-table.h"
12 #include "market/building-util.h"
13 #include "monster-floor/place-monster-types.h"
14 #include "monster-race/monster-race-hook.h"
15 #include "monster-race/monster-race.h"
16 #include "monster-race/race-flags-resistance.h"
17 #include "monster-race/race-flags1.h"
18 #include "monster-race/race-flags7.h"
19 #include "monster/monster-list.h"
20 #include "monster/monster-util.h"
21 #include "player-base/player-class.h"
22 #include "status/buff-setter.h"
23 #include "system/building-type-definition.h"
24 #include "system/dungeon-info.h"
25 #include "system/floor-type-definition.h"
26 #include "system/monster-race-info.h"
27 #include "system/player-type-definition.h"
28 #include "system/redrawing-flags-updater.h"
29 #include "term/screen-processor.h"
30 #include "term/z-form.h"
31 #include "util/int-char-converter.h"
32 #include "view/display-messages.h"
33 #include "world/world.h"
34 #include <algorithm>
35 #include <numeric>
36
37 /*!
38  * @brief 優勝時のメッセージを表示し、賞金を与える
39  * @param player_ptr プレイヤーへの参照ポインタ
40  * @return まだ優勝していないか、挑戦者モンスターとの戦いではFALSE
41  */
42 static bool process_ostensible_arena_victory(PlayerType *player_ptr)
43 {
44     if (player_ptr->arena_number != MAX_ARENA_MONS) {
45         return false;
46     }
47
48     clear_bldg(5, 19);
49     prt(_("アリーナの優勝者!", "               Arena Victor!"), 5, 0);
50     prt(_("おめでとう!あなたは全ての敵を倒しました。", "Congratulations!  You have defeated all before you."), 7, 0);
51     prt(_("賞金として $1,000,000 が与えられます。", "For that, receive the prize: 1,000,000 gold pieces"), 8, 0);
52
53     prt("", 10, 0);
54     prt("", 11, 0);
55     player_ptr->au += 1000000L;
56     msg_print(_("スペースキーで続行", "Press the space bar to continue"));
57     msg_print(nullptr);
58     player_ptr->arena_number++;
59     return true;
60 }
61
62 /*!
63  * @brief はぐれメタルとの対戦
64  * @param player_ptr プレイヤーへの参照ポインタ
65  * @return まだパワー・ワイアーム以下を倒していないならFALSE、倒していたらTRUE
66  */
67 static bool battle_metal_babble(PlayerType *player_ptr)
68 {
69     if (player_ptr->arena_number <= MAX_ARENA_MONS) {
70         return false;
71     }
72
73     if (player_ptr->arena_number >= MAX_ARENA_MONS + 2) {
74         msg_print(_("あなたはアリーナに入り、しばらくの間栄光にひたった。", "You enter the arena briefly and bask in your glory."));
75         msg_print(nullptr);
76         return true;
77     }
78
79     msg_print(_("君のために最強の挑戦者を用意しておいた。", "The strongest challenger is waiting for you."));
80     msg_print(nullptr);
81     if (!input_check(_("挑戦するかね?", "Do you fight? "))) {
82         msg_print(_("残念だ。", "We are disappointed."));
83         return true;
84     }
85
86     msg_print(_("死ぬがよい。", "Die, maggots."));
87     msg_print(nullptr);
88
89     player_ptr->exit_bldg = false;
90     reset_tim_flags(player_ptr);
91
92     /* Save the surface floor as saved floor */
93     prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS);
94
95     player_ptr->current_floor_ptr->inside_arena = true;
96     player_ptr->leaving = true;
97     player_ptr->leave_bldg = true;
98     return true;
99 }
100
101 static void go_to_arena(PlayerType *player_ptr)
102 {
103     if (process_ostensible_arena_victory(player_ptr)) {
104         return;
105     }
106
107     if (battle_metal_babble(player_ptr)) {
108         return;
109     }
110
111     if (player_ptr->riding && !PlayerClass(player_ptr).is_tamer()) {
112         msg_print(_("ペットに乗ったままではアリーナへ入れさせてもらえなかった。", "You don't have permission to enter with pet."));
113         msg_print(nullptr);
114         return;
115     }
116
117     player_ptr->exit_bldg = false;
118     reset_tim_flags(player_ptr);
119     prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS);
120
121     player_ptr->current_floor_ptr->inside_arena = true;
122     player_ptr->leaving = true;
123     player_ptr->leave_bldg = true;
124 }
125
126 static void see_arena_poster(PlayerType *player_ptr)
127 {
128     if (player_ptr->arena_number == MAX_ARENA_MONS) {
129         msg_print(_("あなたは勝利者だ。 アリーナでのセレモニーに参加しなさい。", "You are victorious. Enter the arena for the ceremony."));
130         return;
131     }
132
133     if (player_ptr->arena_number > MAX_ARENA_MONS) {
134         msg_print(_("あなたはすべての敵に勝利した。", "You have won against all foes."));
135         return;
136     }
137
138     auto *r_ptr = &monraces_info[arena_info[player_ptr->arena_number].r_idx];
139     msg_format(_("%s に挑戦するものはいないか?", "Do I hear any challenges against: %s"), r_ptr->name.data());
140     player_ptr->monster_race_idx = arena_info[player_ptr->arena_number].r_idx;
141     RedrawingFlagsUpdater::get_instance().set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
142     handle_stuff(player_ptr);
143 }
144
145 /*!
146  * @brief 闘技場に入るコマンドの処理 / on_defeat_arena_monster commands
147  * @param player_ptr プレイヤーへの参照ポインタ
148  * @param cmd 闘技場処理のID
149  */
150 void arena_comm(PlayerType *player_ptr, int cmd)
151 {
152     switch (cmd) {
153     case BACT_ARENA:
154         go_to_arena(player_ptr);
155         return;
156     case BACT_POSTER:
157         see_arena_poster(player_ptr);
158         return;
159     case BACT_ARENA_RULES:
160         screen_save();
161
162         /* Peruse the on_defeat_arena_monster help file */
163         (void)show_file(player_ptr, true, _("arena_j.txt", "arena.txt"), 0, 0);
164         screen_load();
165         break;
166     }
167 }
168
169 /*!
170  * @brief モンスター闘技場に参加するモンスターを更新する。
171  * @param player_ptr プレイヤーへの参照ポインタ
172  */
173 void update_gambling_monsters(PlayerType *player_ptr)
174 {
175     int total, i;
176     int max_dl = 0;
177     int mon_level;
178     int power[4];
179     bool tekitou;
180
181     for (const auto &d_ref : dungeons_info) {
182         if (max_dl < max_dlv[d_ref.idx]) {
183             max_dl = max_dlv[d_ref.idx];
184         }
185     }
186
187     mon_level = randint1(std::min(max_dl, 122)) + 5;
188     if (randint0(100) < 60) {
189         i = randint1(std::min(max_dl, 122)) + 5;
190         mon_level = std::max(i, mon_level);
191     }
192
193     if (randint0(100) < 30) {
194         i = randint1(std::min(max_dl, 122)) + 5;
195         mon_level = std::max(i, mon_level);
196     }
197
198     while (true) {
199         total = 0;
200         tekitou = false;
201         for (i = 0; i < 4; i++) {
202             MonsterRaceId r_idx;
203             int j;
204             while (true) {
205                 get_mon_num_prep(player_ptr, monster_can_entry_arena, nullptr);
206                 r_idx = get_mon_num(player_ptr, 0, mon_level, PM_ARENA);
207                 if (!MonsterRace(r_idx).is_valid()) {
208                     continue;
209                 }
210
211                 if (monraces_info[r_idx].kind_flags.has(MonsterKindType::UNIQUE) || (monraces_info[r_idx].flags7 & RF7_UNIQUE2)) {
212                     if ((monraces_info[r_idx].level + 10) > mon_level) {
213                         continue;
214                     }
215                 }
216
217                 for (j = 0; j < i; j++) {
218                     if (r_idx == battle_mon_list[j]) {
219                         break;
220                     }
221                 }
222                 if (j < i) {
223                     continue;
224                 }
225
226                 break;
227             }
228             battle_mon_list[i] = r_idx;
229             if (monraces_info[r_idx].level < 45) {
230                 tekitou = true;
231             }
232         }
233
234         std::transform(std::begin(battle_mon_list), std::end(battle_mon_list), std::begin(power),
235             [](MonsterRace r_idx) { return MonsterRace(r_idx).calc_power(); });
236         total += std::reduce(std::begin(power), std::end(power));
237
238         for (i = 0; i < 4; i++) {
239             if (power[i] <= 0) {
240                 break;
241             }
242             power[i] = total * 60 / power[i];
243             if (tekitou && ((power[i] < 160) || power[i] > 1500)) {
244                 break;
245             }
246             if ((power[i] < 160) && randint0(20)) {
247                 break;
248             }
249             if (power[i] < 101) {
250                 power[i] = 100 + randint1(5);
251             }
252             mon_odds[i] = power[i];
253         }
254
255         if (i == 4) {
256             break;
257         }
258     }
259 }
260
261 /*!
262  * @brief モンスター闘技場のメインルーチン
263  * @param player_ptr プレイヤーへの参照ポインタ
264  * @return 賭けを開始したか否か
265  */
266 bool monster_arena_comm(PlayerType *player_ptr)
267 {
268     if ((w_ptr->game_turn - w_ptr->arena_start_turn) > TURNS_PER_TICK * 250) {
269         update_gambling_monsters(player_ptr);
270         w_ptr->arena_start_turn = w_ptr->game_turn;
271     }
272
273     screen_save();
274
275     /* No money */
276     if (player_ptr->au <= 1) {
277         msg_print(_("おい!おまえ一文なしじゃないか!こっから出ていけ!", "Hey! You don't have gold - get out of here!"));
278         msg_print(nullptr);
279         screen_load();
280         return false;
281     }
282
283     clear_bldg(4, 10);
284
285     prt(_("モンスター                                                     倍率", "Monsters                                                       Odds"), 4, 4);
286     for (auto i = 0; i < 4; i++) {
287         const auto &monrace = monraces_info[battle_mon_list[i]];
288         std::string name;
289         if (monrace.kind_flags.has(MonsterKindType::UNIQUE)) {
290             name = _(monrace.name, "Fake ");
291             name.append(_("もどき", monrace.name));
292         } else {
293             name = monrace.name;
294             name.append(_("      ", ""));
295         }
296
297         constexpr auto fmt = _("%d) %-58s  %4ld.%02ld倍", "%d) %-58s  %4ld.%02ld");
298         prt(format(fmt, i + 1, name.data(), (long int)mon_odds[i] / 100, (long int)mon_odds[i] % 100), 5 + i, 1);
299     }
300
301     prt(_("どれに賭けますか:", "Which monster: "), 0, 0);
302     while (true) {
303         int i = inkey();
304
305         if (i == ESCAPE) {
306             screen_load();
307             return false;
308         }
309
310         if (i >= '1' && i <= '4') {
311             sel_monster = i - '1';
312             battle_odds = mon_odds[sel_monster];
313             break;
314         }
315
316         else {
317             bell();
318         }
319     }
320
321     clear_bldg(4, 4);
322     for (int i = 0; i < 4; i++) {
323         if (i != sel_monster) {
324             clear_bldg(i + 5, i + 5);
325         }
326     }
327
328     auto maxbet = player_ptr->lev * 200;
329     maxbet = std::min(maxbet, player_ptr->au);
330     constexpr auto prompt = _("賭け金?", "Your wager? ");
331     const auto wager_opt = input_integer(prompt, 1, maxbet, 1);
332     if (!wager_opt.has_value()) {
333         screen_load();
334         return false;
335     }
336
337     auto wager = wager_opt.value();
338     if (wager > player_ptr->au) {
339         msg_print(_("おい!金が足りないじゃないか!出ていけ!", "Hey! You don't have the gold - get out of here!"));
340         msg_print(nullptr);
341         screen_load();
342         return false;
343     }
344
345     msg_print(nullptr);
346     battle_odds = std::max(wager + 1, wager * battle_odds / 100);
347     kakekin = wager;
348     player_ptr->au -= wager;
349     reset_tim_flags(player_ptr);
350
351     prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS);
352
353     player_ptr->phase_out = true;
354     player_ptr->leaving = true;
355     player_ptr->leave_bldg = true;
356
357     screen_load();
358     return true;
359 }