OSDN Git Service

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