OSDN Git Service

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