OSDN Git Service

Merge pull request #2335 from sikabane-works/release/3.0.0Alpha54
[hengbandforosx/hengbandosx.git] / src / market / bounty.cpp
1 #include "market/bounty.h"
2 #include "autopick/autopick.h"
3 #include "avatar/avatar.h"
4 #include "cmd-building/cmd-building.h"
5 #include "core/asking-player.h"
6 #include "core/player-redraw-types.h"
7 #include "core/stuff-handler.h"
8 #include "dungeon/dungeon.h"
9 #include "flavor/flavor-describer.h"
10 #include "game-option/cheat-options.h"
11 #include "inventory/inventory-object.h"
12 #include "inventory/inventory-slot-types.h"
13 #include "io/input-key-acceptor.h"
14 #include "market/bounty-prize-table.h"
15 #include "market/building-util.h"
16 #include "monster-race/monster-race-hook.h"
17 #include "monster-race/monster-race.h"
18 #include "monster-race/race-flags1.h"
19 #include "monster-race/race-flags2.h"
20 #include "monster-race/race-flags7.h"
21 #include "monster-race/race-flags9.h"
22 #include "monster-race/race-indice-types.h"
23 #include "monster/monster-list.h"
24 #include "monster/monster-util.h"
25 #include "object-enchant/apply-magic.h"
26 #include "object-enchant/item-apply-magic.h"
27 #include "object/object-info.h"
28 #include "object/object-kind-hook.h"
29 #include "perception/object-perception.h"
30 #include "sv-definition/sv-other-types.h"
31 #include "system/floor-type-definition.h"
32 #include "system/monster-race-definition.h"
33 #include "system/object-type-definition.h"
34 #include "system/player-type-definition.h"
35 #include "term/screen-processor.h"
36 #include "term/term-color-types.h"
37 #include "view/display-messages.h"
38 #include "world/world.h"
39
40 /*!
41  * @brief 賞金首の引き換え処理 / Get prize
42  * @param player_ptr プレイヤーへの参照ポインタ
43  * @return 各種賞金首のいずれかでも換金が行われたか否か。
44  */
45 bool exchange_cash(PlayerType *player_ptr)
46 {
47     bool change = false;
48     GAME_TEXT o_name[MAX_NLEN];
49     ObjectType *o_ptr;
50
51     for (INVENTORY_IDX i = 0; i <= INVEN_SUB_HAND; i++) {
52         o_ptr = &player_ptr->inventory_list[i];
53         if ((o_ptr->tval == ItemKindType::CAPTURE) && (o_ptr->pval == MON_TSUCHINOKO)) {
54             char buf[MAX_NLEN + 32];
55             describe_flavor(player_ptr, o_name, o_ptr, 0);
56             sprintf(buf, _("%s を換金しますか?", "Convert %s into money? "), o_name);
57             if (get_check(buf)) {
58                 msg_format(_("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)(1000000L * o_ptr->number));
59                 player_ptr->au += 1000000L * o_ptr->number;
60                 player_ptr->redraw |= (PR_GOLD);
61                 vary_item(player_ptr, i, -o_ptr->number);
62             }
63
64             change = true;
65         }
66     }
67
68     for (INVENTORY_IDX i = 0; i < INVEN_PACK; i++) {
69         o_ptr = &player_ptr->inventory_list[i];
70         if ((o_ptr->tval == ItemKindType::CORPSE) && (o_ptr->sval == SV_CORPSE) && (o_ptr->pval == MON_TSUCHINOKO)) {
71             char buf[MAX_NLEN + 32];
72             describe_flavor(player_ptr, o_name, o_ptr, 0);
73             sprintf(buf, _("%s を換金しますか?", "Convert %s into money? "), o_name);
74             if (get_check(buf)) {
75                 msg_format(_("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)(200000L * o_ptr->number));
76                 player_ptr->au += 200000L * o_ptr->number;
77                 player_ptr->redraw |= (PR_GOLD);
78                 vary_item(player_ptr, i, -o_ptr->number);
79             }
80
81             change = true;
82         }
83     }
84
85     for (INVENTORY_IDX i = 0; i < INVEN_PACK; i++) {
86         o_ptr = &player_ptr->inventory_list[i];
87         if ((o_ptr->tval == ItemKindType::CORPSE) && (o_ptr->sval == SV_SKELETON) && (o_ptr->pval == MON_TSUCHINOKO)) {
88             char buf[MAX_NLEN + 32];
89             describe_flavor(player_ptr, o_name, o_ptr, 0);
90             sprintf(buf, _("%s を換金しますか?", "Convert %s into money? "), o_name);
91             if (get_check(buf)) {
92                 msg_format(_("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)(100000L * o_ptr->number));
93                 player_ptr->au += 100000L * o_ptr->number;
94                 player_ptr->redraw |= (PR_GOLD);
95                 vary_item(player_ptr, i, -o_ptr->number);
96             }
97
98             change = true;
99         }
100     }
101
102     for (INVENTORY_IDX i = 0; i < INVEN_PACK; i++) {
103         o_ptr = &player_ptr->inventory_list[i];
104         if ((o_ptr->tval == ItemKindType::CORPSE) && (o_ptr->sval == SV_CORPSE) && (streq(r_info[o_ptr->pval].name.c_str(), r_info[w_ptr->today_mon].name.c_str()))) {
105             char buf[MAX_NLEN + 32];
106             describe_flavor(player_ptr, o_name, o_ptr, 0);
107             sprintf(buf, _("%s を換金しますか?", "Convert %s into money? "), o_name);
108             if (get_check(buf)) {
109                 msg_format(
110                     _("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)((r_info[w_ptr->today_mon].level * 50 + 100) * o_ptr->number));
111                 player_ptr->au += (r_info[w_ptr->today_mon].level * 50 + 100) * o_ptr->number;
112                 player_ptr->redraw |= (PR_GOLD);
113                 vary_item(player_ptr, i, -o_ptr->number);
114             }
115
116             change = true;
117         }
118     }
119
120     for (INVENTORY_IDX i = 0; i < INVEN_PACK; i++) {
121         o_ptr = &player_ptr->inventory_list[i];
122
123         if ((o_ptr->tval == ItemKindType::CORPSE) && (o_ptr->sval == SV_SKELETON) && (streq(r_info[o_ptr->pval].name.c_str(), r_info[w_ptr->today_mon].name.c_str()))) {
124             char buf[MAX_NLEN + 32];
125             describe_flavor(player_ptr, o_name, o_ptr, 0);
126             sprintf(buf, _("%s を換金しますか?", "Convert %s into money? "), o_name);
127             if (get_check(buf)) {
128                 msg_format(_("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)((r_info[w_ptr->today_mon].level * 30 + 60) * o_ptr->number));
129                 player_ptr->au += (r_info[w_ptr->today_mon].level * 30 + 60) * o_ptr->number;
130                 player_ptr->redraw |= (PR_GOLD);
131                 vary_item(player_ptr, i, -o_ptr->number);
132             }
133
134             change = true;
135         }
136     }
137
138     for (int j = 0; j < MAX_BOUNTY; j++) {
139         for (INVENTORY_IDX i = INVEN_PACK - 1; i >= 0; i--) {
140             o_ptr = &player_ptr->inventory_list[i];
141             if ((o_ptr->tval != ItemKindType::CORPSE) || (o_ptr->pval != w_ptr->bounty_r_idx[j])) {
142                 continue;
143             }
144
145             char buf[MAX_NLEN + 20];
146             int num, k;
147             INVENTORY_IDX item_new;
148             ObjectType forge;
149
150             describe_flavor(player_ptr, o_name, o_ptr, 0);
151             sprintf(buf, _("%sを渡しますか?", "Hand %s over? "), o_name);
152             if (!get_check(buf)) {
153                 continue;
154             }
155
156             vary_item(player_ptr, i, -o_ptr->number);
157             chg_virtue(player_ptr, V_JUSTICE, 5);
158             w_ptr->bounty_r_idx[j] += 10000;
159
160             for (num = 0, k = 0; k < MAX_BOUNTY; k++) {
161                 if (w_ptr->bounty_r_idx[k] >= 10000) {
162                     num++;
163                 }
164             }
165
166             msg_format(_("これで合計 %d ポイント獲得しました。", "You earned %d point%s total."), num, (num > 1 ? "s" : ""));
167
168             (&forge)->prep(lookup_kind(prize_list[num - 1].tval, prize_list[num - 1].sval));
169             apply_magic_to_object(player_ptr, &forge, player_ptr->current_floor_ptr->object_level, AM_NO_FIXED_ART);
170
171             object_aware(player_ptr, &forge);
172             object_known(&forge);
173
174             /*
175              * Hand it --- Assume there is an empty slot.
176              * Since a corpse is handed at first,
177              * there is at least one empty slot.
178              */
179             item_new = store_item_to_inventory(player_ptr, &forge);
180             describe_flavor(player_ptr, o_name, &forge, 0);
181             msg_format(_("%s(%c)を貰った。", "You get %s (%c). "), o_name, index_to_label(item_new));
182
183             autopick_alter_item(player_ptr, item_new, false);
184             handle_stuff(player_ptr);
185             change = true;
186         }
187     }
188
189     if (change) {
190         return true;
191     }
192
193     msg_print(_("賞金を得られそうなものは持っていなかった。", "You have nothing."));
194     msg_print(nullptr);
195     return false;
196 }
197
198 /*!
199  * @brief 本日の賞金首情報を表示する。
200  * @param player_ptr プレイヤーへの参照ポインタ
201  */
202 void today_target(PlayerType *player_ptr)
203 {
204     char buf[160];
205     auto *r_ptr = &r_info[w_ptr->today_mon];
206
207     clear_bldg(4, 18);
208     c_put_str(TERM_YELLOW, _("本日の賞金首", "Wanted monster that changes from day to day"), 5, 10);
209     sprintf(buf, _("ターゲット: %s", "target: %s"), r_ptr->name.c_str());
210     c_put_str(TERM_YELLOW, buf, 6, 10);
211     sprintf(buf, _("死体 ---- $%d", "corpse   ---- $%d"), (int)r_ptr->level * 50 + 100);
212     prt(buf, 8, 10);
213     sprintf(buf, _("骨   ---- $%d", "skeleton ---- $%d"), (int)r_ptr->level * 30 + 60);
214     prt(buf, 9, 10);
215     player_ptr->today_mon = w_ptr->today_mon;
216 }
217
218 /*!
219  * @brief ツチノコの賞金首情報を表示する。
220  */
221 void tsuchinoko(void)
222 {
223     clear_bldg(4, 18);
224     c_put_str(TERM_YELLOW, _("一獲千金の大チャンス!!!", "Big chance for quick money!!!"), 5, 10);
225     c_put_str(TERM_YELLOW, _("ターゲット:幻の珍獣「ツチノコ」", "target: the rarest animal 'Tsuchinoko'"), 6, 10);
226     c_put_str(TERM_WHITE, _("生け捕り ---- $1,000,000", "catch alive ---- $1,000,000"), 8, 10);
227     c_put_str(TERM_WHITE, _("死体     ----   $200,000", "corpse      ----   $200,000"), 9, 10);
228     c_put_str(TERM_WHITE, _("骨       ----   $100,000", "bones       ----   $100,000"), 10, 10);
229 }
230
231 /*!
232  * @brief 通常の賞金首情報を表示する。
233  */
234 void show_bounty(void)
235 {
236     TERM_LEN y = 0;
237
238     clear_bldg(4, 18);
239     prt(_("死体を持ち帰れば報酬を差し上げます。", "Offer a prize when you bring a wanted monster's corpse"), 4, 10);
240     c_put_str(TERM_YELLOW, _("現在の賞金首", "Wanted monsters"), 6, 10);
241
242     for (int i = 0; i < MAX_BOUNTY; i++) {
243         byte color;
244         concptr done_mark;
245         monster_race *r_ptr = &r_info[(w_ptr->bounty_r_idx[i] > 10000 ? w_ptr->bounty_r_idx[i] - 10000 : w_ptr->bounty_r_idx[i])];
246
247         if (w_ptr->bounty_r_idx[i] > 10000) {
248             color = TERM_RED;
249             done_mark = _("(済)", "(done)");
250         } else {
251             color = TERM_WHITE;
252             done_mark = "";
253         }
254
255         c_prt(color, format("%s %s", r_ptr->name.c_str(), done_mark), y + 7, 10);
256
257         y = (y + 1) % 10;
258         if (!y && (i < MAX_BOUNTY - 1)) {
259             prt(_("何かキーを押してください", "Hit any key."), 0, 0);
260             (void)inkey();
261             prt("", 0, 0);
262             clear_bldg(7, 18);
263         }
264     }
265 }
266
267 /*!
268  * @brief 今日の賞金首を確定する / Determine today's bounty monster
269  * @param PlayerType プレイヤーへの参照ポインタ
270  * @note conv_old is used if loaded 0.0.3 or older save file
271  */
272 void determine_daily_bounty(PlayerType *player_ptr, bool conv_old)
273 {
274     int max_dl = 3;
275     if (!conv_old) {
276         for (const auto &d_ref : d_info) {
277             if (max_dlv[d_ref.idx] < d_ref.mindepth) {
278                 continue;
279             }
280             if (max_dl < max_dlv[d_ref.idx]) {
281                 max_dl = max_dlv[d_ref.idx];
282             }
283         }
284     } else {
285         max_dl = std::max(max_dlv[DUNGEON_ANGBAND], 3);
286     }
287
288     get_mon_num_prep_bounty(player_ptr);
289
290     while (true) {
291         w_ptr->today_mon = get_mon_num(player_ptr, std::min(max_dl / 2, 40), max_dl, GMN_ARENA);
292         monster_race *r_ptr;
293         r_ptr = &r_info[w_ptr->today_mon];
294
295         if (cheat_hear) {
296             msg_format("日替わり候補: %s ", r_ptr->name.c_str());
297         }
298
299         if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
300             continue;
301         }
302         if (r_ptr->flags7 & (RF7_NAZGUL | RF7_UNIQUE2)) {
303             continue;
304         }
305         if (r_ptr->flags2 & RF2_MULTIPLY) {
306             continue;
307         }
308         if ((r_ptr->flags9 & (RF9_DROP_CORPSE | RF9_DROP_SKELETON)) != (RF9_DROP_CORPSE | RF9_DROP_SKELETON)) {
309             continue;
310         }
311         if (r_ptr->rarity > 10) {
312             continue;
313         }
314         break;
315     }
316
317     // プレイヤーは日替わり賞金首に関する知識を失う。
318     player_ptr->today_mon = 0;
319 }
320
321 /*!
322  * @brief 賞金首となるユニークを確定する / Determine bounty uniques
323  * @param player_ptr プレイヤーへの参照ポインタ
324  */
325 void determine_bounty_uniques(PlayerType *player_ptr)
326 {
327     get_mon_num_prep_bounty(player_ptr);
328
329     for (int i = 0; i < MAX_BOUNTY; i++) {
330         while (true) {
331             w_ptr->bounty_r_idx[i] = get_mon_num(player_ptr, 0, MAX_DEPTH - 1, GMN_ARENA);
332             monster_race *r_ptr;
333             r_ptr = &r_info[w_ptr->bounty_r_idx[i]];
334
335             if (r_ptr->kind_flags.has_not(MonsterKindType::UNIQUE)) {
336                 continue;
337             }
338
339             if (!(r_ptr->flags9 & (RF9_DROP_CORPSE | RF9_DROP_SKELETON))) {
340                 continue;
341             }
342
343             if (r_ptr->rarity > 100) {
344                 continue;
345             }
346
347             if (no_questor_or_bounty_uniques(w_ptr->bounty_r_idx[i])) {
348                 continue;
349             }
350
351             int j;
352             for (j = 0; j < i; j++) {
353                 if (w_ptr->bounty_r_idx[i] == w_ptr->bounty_r_idx[j]) {
354                     break;
355                 }
356             }
357
358             if (j == i) {
359                 break;
360             }
361         }
362     }
363
364     for (int i = 0; i < MAX_BOUNTY - 1; i++) {
365         for (int j = i; j < MAX_BOUNTY; j++) {
366             MONRACE_IDX tmp;
367             if (r_info[w_ptr->bounty_r_idx[i]].level > r_info[w_ptr->bounty_r_idx[j]].level) {
368                 tmp = w_ptr->bounty_r_idx[i];
369                 w_ptr->bounty_r_idx[i] = w_ptr->bounty_r_idx[j];
370                 w_ptr->bounty_r_idx[j] = tmp;
371             }
372         }
373     }
374 }