OSDN Git Service

Merge pull request #3776 from Hourier/Reshape-Rooms-Normal
[hengbandforosx/hengbandosx.git] / src / cmd-building / cmd-building.cpp
1 /*!
2  * @brief 町の施設処理 / Building commands
3  * @date 2013/12/23
4  * @author
5  * Created by Ken Wigle for Kangband - a variant of Angband 2.8.3
6  * -KMW-
7  *
8  * Rewritten for Kangband 2.8.3i using Kamband's version of
9  * building.c as written by Ivan Tkatchev
10  *
11  * Changed for ZAngband by Robert Ruehlmann
12  */
13
14 #include "cmd-building/cmd-building.h"
15 #include "avatar/avatar.h"
16 #include "cmd-action/cmd-spell.h"
17 #include "cmd-building/cmd-inn.h"
18 #include "cmd-io/cmd-dump.h"
19 #include "core/asking-player.h"
20 #include "core/scores.h"
21 #include "core/show-file.h"
22 #include "core/special-internal-keys.h"
23 #include "core/stuff-handler.h"
24 #include "core/window-redrawer.h"
25 #include "floor/cave.h"
26 #include "floor/floor-events.h"
27 #include "floor/floor-mode-changer.h"
28 #include "floor/wild.h"
29 #include "io/input-key-acceptor.h"
30 #include "io/input-key-requester.h"
31 #include "main/music-definitions-table.h"
32 #include "main/sound-of-music.h"
33 #include "market/arena.h"
34 #include "market/bounty.h"
35 #include "market/building-actions-table.h"
36 #include "market/building-craft-armor.h"
37 #include "market/building-craft-fix.h"
38 #include "market/building-craft-weapon.h"
39 #include "market/building-enchanter.h"
40 #include "market/building-monster.h"
41 #include "market/building-quest.h"
42 #include "market/building-recharger.h"
43 #include "market/building-service.h"
44 #include "market/building-util.h"
45 #include "market/play-gamble.h"
46 #include "market/poker.h"
47 #include "monster-race/monster-race.h"
48 #include "mutation/mutation-flag-types.h"
49 #include "mutation/mutation-investor-remover.h"
50 #include "object-hook/hook-armor.h"
51 #include "object-hook/hook-weapon.h"
52 #include "object/item-tester-hooker.h"
53 #include "player-status/player-energy.h"
54 #include "player/player-personality-types.h"
55 #include "spell-kind/spells-perception.h"
56 #include "spell-kind/spells-world.h"
57 #include "spell/spells-status.h"
58 #include "system/angband-exceptions.h"
59 #include "system/angband-system.h"
60 #include "system/building-type-definition.h"
61 #include "system/floor-type-definition.h"
62 #include "system/grid-type-definition.h"
63 #include "system/item-entity.h"
64 #include "system/player-type-definition.h"
65 #include "system/redrawing-flags-updater.h"
66 #include "system/terrain-type-definition.h"
67 #include "term/gameterm.h"
68 #include "term/screen-processor.h"
69 #include "util/bit-flags-calculator.h"
70 #include "util/int-char-converter.h"
71 #include "view/display-messages.h"
72 #include "world/world.h"
73
74 uint32_t mon_odds[4];
75 int battle_odds;
76 PRICE kakekin;
77 int sel_monster;
78
79 bool reinit_wilderness = false;
80
81 /*!
82  * @brief 町に関するヘルプを表示する / Display town history
83  * @param player_ptr プレイヤーへの参照ポインタ
84  */
85 static void town_history(PlayerType *player_ptr)
86 {
87     screen_save();
88     (void)show_file(player_ptr, true, _("jbldg.txt", "bldg.txt"), 0, 0);
89     screen_load();
90 }
91
92 /*!
93  * @brief 施設の処理実行メインルーチン
94  * @param player_ptr プレイヤーへの参照ポインタ
95  * @param bldg 施設構造体の参照ポインタ
96  * @param i 実行したい施設のサービステーブルの添字
97  * @return 施設から別フロアへ移動するか否か (アリーナ/モンスター闘技場のみtrue)
98  */
99 static bool bldg_process_command(PlayerType *player_ptr, building_type *bldg, int i)
100 {
101     msg_flag = false;
102     msg_erase();
103     const auto can_be_owner = is_owner(player_ptr, bldg);
104     const auto building_cost = can_be_owner ? bldg->member_costs[i] : bldg->other_costs[i];
105     if (((bldg->action_restr[i] == 1) && !is_member(player_ptr, bldg)) || ((bldg->action_restr[i] == 2) && !can_be_owner)) {
106         msg_print(_("それを選択する権利はありません!", "You have no right to choose that!"));
107         return false;
108     }
109
110     const auto building_action = bldg->actions[i];
111     if ((building_action != BACT_RECHARGE) && (((bldg->member_costs[i] > player_ptr->au) && can_be_owner) || ((bldg->other_costs[i] > player_ptr->au) && !can_be_owner))) {
112         msg_print(_("お金が足りません!", "You do not have the gold!"));
113         return false;
114     }
115
116     switch (building_action) {
117     case BACT_NOTHING:
118         /* Do nothing */
119         return false;
120     case BACT_RESEARCH_ITEM:
121         if (identify_fully(player_ptr, false)) {
122             player_ptr->au -= building_cost;
123         }
124
125         return false;
126     case BACT_TOWN_HISTORY:
127         town_history(player_ptr);
128         return false;
129     case BACT_RACE_LEGENDS:
130         race_legends(player_ptr);
131         return false;
132     case BACT_QUEST:
133         castle_quest(player_ptr);
134         return false;
135     case BACT_KING_LEGENDS:
136     case BACT_ARENA_LEGENDS:
137     case BACT_LEGENDS:
138         show_highclass(player_ptr);
139         return false;
140     case BACT_POSTER:
141     case BACT_ARENA_RULES:
142     case BACT_ARENA:
143         return arena_comm(player_ptr, building_action);
144     case BACT_IN_BETWEEN:
145     case BACT_CRAPS:
146     case BACT_SPIN_WHEEL:
147     case BACT_DICE_SLOTS:
148     case BACT_GAMBLE_RULES:
149     case BACT_POKER:
150         gamble_comm(player_ptr, building_action);
151         return false;
152     case BACT_REST:
153     case BACT_RUMORS:
154     case BACT_FOOD:
155         if (inn_comm(player_ptr, building_action)) {
156             player_ptr->au -= building_cost;
157         }
158
159         return false;
160     case BACT_RESEARCH_MONSTER:
161         if (research_mon(player_ptr)) {
162             player_ptr->au -= building_cost;
163         }
164
165         return false;
166     case BACT_COMPARE_WEAPONS:
167         player_ptr->au -= compare_weapons(player_ptr, building_cost);
168         return false;
169     case BACT_ENCHANT_WEAPON:
170         enchant_item(player_ptr, building_cost, 1, 1, 0, FuncItemTester(&ItemEntity::allow_enchant_melee_weapon));
171         return false;
172     case BACT_ENCHANT_ARMOR:
173         enchant_item(player_ptr, building_cost, 0, 0, 1, FuncItemTester(&ItemEntity::is_protector));
174         return false;
175     case BACT_RECHARGE:
176         building_recharge(player_ptr);
177         return false;
178     case BACT_RECHARGE_ALL:
179         building_recharge_all(player_ptr);
180         return false;
181     case BACT_IDENTS:
182         if (!input_check(_("持ち物を全て鑑定してよろしいですか?", "Do you pay to identify all your possession? "))) {
183             return false;
184         }
185
186         identify_pack(player_ptr);
187         msg_print(_(" 持ち物全てが鑑定されました。", "Your possessions have been identified."));
188         player_ptr->au -= building_cost;
189         return false;
190     case BACT_IDENT_ONE:
191         if (ident_spell(player_ptr, false)) {
192             player_ptr->au -= building_cost;
193         }
194
195         return false;
196     case BACT_LEARN:
197         do_cmd_study(player_ptr);
198         return false;
199     case BACT_HEALING:
200         if (cure_critical_wounds(player_ptr, 200)) {
201             player_ptr->au -= building_cost;
202         }
203
204         return false;
205     case BACT_RESTORE:
206         if (restore_all_status(player_ptr)) {
207             player_ptr->au -= building_cost;
208         }
209
210         return false;
211     case BACT_ENCHANT_ARROWS:
212         enchant_item(player_ptr, building_cost, 1, 1, 0, FuncItemTester(&ItemEntity::is_ammo));
213         return false;
214     case BACT_ENCHANT_BOW:
215         enchant_item(player_ptr, building_cost, 1, 1, 0, TvalItemTester(ItemKindType::BOW));
216         return false;
217     case BACT_RECALL:
218         if (recall_player(player_ptr, 1)) {
219             player_ptr->au -= building_cost;
220         }
221
222         return false;
223     case BACT_TELEPORT_LEVEL:
224         screen_save();
225         clear_bldg(4, 20);
226         if (free_level_recall(player_ptr)) {
227             player_ptr->au -= building_cost;
228         }
229
230         screen_load();
231         return false;
232     case BACT_LOSE_MUTATION: {
233         auto muta = player_ptr->muta;
234         if (player_ptr->ppersonality == PERSONALITY_LUCKY) {
235             // ラッキーマンの白オーラは突然変異治療の対象外
236             muta.reset(PlayerMutationType::GOOD_LUCK);
237         }
238
239         if (muta.any()) {
240             while (!lose_mutation(player_ptr, 0)) {
241                 ;
242             }
243
244             player_ptr->au -= building_cost;
245             return false;
246         }
247
248         msg_print(_("治すべき突然変異が無い。", "You have no mutations."));
249         msg_print(nullptr);
250         return false;
251     }
252     case BACT_BATTLE:
253         return monster_arena_comm(player_ptr);
254     case BACT_TSUCHINOKO:
255         tsuchinoko();
256         return false;
257     case BACT_BOUNTY:
258         show_bounty();
259         return false;
260     case BACT_TARGET:
261         today_target(player_ptr);
262         return false;
263     case BACT_KANKIN:
264         exchange_cash(player_ptr);
265         return false;
266     case BACT_HEIKOUKA:
267         msg_print(_("平衡化の儀式を行なった。", "You received an equalization ritual."));
268         set_virtue(player_ptr, Virtue::COMPASSION, 0);
269         set_virtue(player_ptr, Virtue::HONOUR, 0);
270         set_virtue(player_ptr, Virtue::JUSTICE, 0);
271         set_virtue(player_ptr, Virtue::SACRIFICE, 0);
272         set_virtue(player_ptr, Virtue::KNOWLEDGE, 0);
273         set_virtue(player_ptr, Virtue::FAITH, 0);
274         set_virtue(player_ptr, Virtue::ENLIGHTEN, 0);
275         set_virtue(player_ptr, Virtue::ENCHANT, 0);
276         set_virtue(player_ptr, Virtue::CHANCE, 0);
277         set_virtue(player_ptr, Virtue::NATURE, 0);
278         set_virtue(player_ptr, Virtue::HARMONY, 0);
279         set_virtue(player_ptr, Virtue::VITALITY, 0);
280         set_virtue(player_ptr, Virtue::UNLIFE, 0);
281         set_virtue(player_ptr, Virtue::PATIENCE, 0);
282         set_virtue(player_ptr, Virtue::TEMPERANCE, 0);
283         set_virtue(player_ptr, Virtue::DILIGENCE, 0);
284         set_virtue(player_ptr, Virtue::VALOUR, 0);
285         set_virtue(player_ptr, Virtue::INDIVIDUALISM, 0);
286         initialize_virtues(player_ptr);
287         player_ptr->au -= building_cost;
288         return false;
289     case BACT_TELE_TOWN:
290         if (!tele_town(player_ptr)) {
291             return false;
292         }
293
294         player_ptr->au -= building_cost;
295         return true;
296     case BACT_EVAL_AC:
297         if (eval_ac(player_ptr->dis_ac + player_ptr->dis_to_a)) {
298             player_ptr->au -= building_cost;
299         }
300
301         return false;
302     case BACT_BROKEN_WEAPON:
303         player_ptr->au -= repair_broken_weapon(player_ptr, building_cost);
304         return false;
305     default:
306         THROW_EXCEPTION(std::logic_error, "Invalid building action is specified!");
307     }
308 }
309
310 /*!
311  * @brief 施設入り口にプレイヤーが乗った際の処理 / Do building commands
312  * @param プレイヤーへの参照ポインタ
313  */
314 void do_cmd_building(PlayerType *player_ptr)
315 {
316     if (player_ptr->wild_mode) {
317         return;
318     }
319
320     PlayerEnergy energy(player_ptr);
321     energy.set_player_turn_energy(100);
322     const auto p_pos = player_ptr->get_position();
323     if (!cave_has_flag_bold(player_ptr->current_floor_ptr, p_pos.y, p_pos.x, TerrainCharacteristics::BLDG)) {
324         msg_print(_("ここには建物はない。", "You see no building here."));
325         return;
326     }
327
328     int which = player_ptr->current_floor_ptr->get_grid(p_pos).get_terrain().subtype;
329
330     building_type *bldg;
331     bldg = &buildings[which];
332
333     reinit_wilderness = false;
334
335     if ((which == 2) && (player_ptr->arena_number < 0)) {
336         msg_print(_("「敗者に用はない。」", "'There's no place here for a LOSER like you!'"));
337         return;
338     }
339
340     if ((which == 2) && player_ptr->current_floor_ptr->inside_arena) {
341         if (!player_ptr->exit_bldg && player_ptr->current_floor_ptr->m_cnt > 0) {
342             prt(_("ゲートは閉まっている。モンスターがあなたを待っている!", "The gates are closed.  The monster awaits!"), 0, 0);
343         } else {
344             prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_NO_RETURN);
345             player_ptr->current_floor_ptr->inside_arena = false;
346             player_ptr->leaving = true;
347             command_new = SPECIAL_KEY_BUILDING;
348             energy.reset_player_turn();
349         }
350
351         return;
352     }
353
354     TermCenteredOffsetSetter tcos(MAIN_TERM_MIN_COLS, MAIN_TERM_MIN_ROWS);
355
356     auto &system = AngbandSystem::get_instance();
357     if (system.is_phase_out()) {
358         prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_NO_RETURN);
359         player_ptr->leaving = true;
360         system.set_phase_out(false);
361         command_new = SPECIAL_KEY_BUILDING;
362         energy.reset_player_turn();
363         return;
364     }
365
366     player_ptr->oldpy = player_ptr->y;
367     player_ptr->oldpx = player_ptr->x;
368     forget_lite(player_ptr->current_floor_ptr);
369     forget_view(player_ptr->current_floor_ptr);
370     w_ptr->character_icky_depth++;
371
372     command_arg = 0;
373     command_rep = 0;
374     command_new = 0;
375
376     display_buikding_service(player_ptr, bldg);
377     play_music(TERM_XTRA_MUSIC_BASIC, MUSIC_BASIC_BUILD);
378
379     while (true) {
380         prt("", 1, 0);
381         building_prt_gold(player_ptr);
382         const auto command = inkey();
383         if (command == ESCAPE) {
384             player_ptr->current_floor_ptr->inside_arena = false;
385             system.set_phase_out(false);
386             break;
387         }
388
389         auto is_valid_command = false;
390         int i;
391         for (i = 0; i < 8; i++) {
392             if (bldg->letters[i] && (bldg->letters[i] == command)) {
393                 is_valid_command = true;
394                 break;
395             }
396         }
397
398         const auto should_leave = is_valid_command ? bldg_process_command(player_ptr, bldg, i) : false;
399         handle_stuff(player_ptr);
400         if (should_leave) {
401             break;
402         }
403     }
404
405     select_floor_music(player_ptr);
406
407     msg_flag = false;
408     msg_erase();
409
410     if (reinit_wilderness) {
411         player_ptr->leaving = true;
412     }
413
414     w_ptr->character_icky_depth--;
415     term_clear();
416
417     auto &rfu = RedrawingFlagsUpdater::get_instance();
418     static constexpr auto flags_srf = {
419         StatusRecalculatingFlag::VIEW,
420         StatusRecalculatingFlag::MONSTER_STATUSES,
421         StatusRecalculatingFlag::BONUS,
422         StatusRecalculatingFlag::LITE,
423         StatusRecalculatingFlag::MONSTER_LITE,
424     };
425     rfu.set_flags(flags_srf);
426     static constexpr auto flags_mwrf = {
427         MainWindowRedrawingFlag::BASIC,
428         MainWindowRedrawingFlag::EXTRA,
429         MainWindowRedrawingFlag::EQUIPPY,
430         MainWindowRedrawingFlag::MAP,
431     };
432     rfu.set_flags(flags_mwrf);
433     static constexpr auto flags_swrf = {
434         SubWindowRedrawingFlag::OVERHEAD,
435         SubWindowRedrawingFlag::DUNGEON,
436     };
437     rfu.set_flags(flags_swrf);
438 }