OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / floor / floor-object.cpp
1 /*!
2  * @brief フロア生成時にアイテムを配置する
3  * @date 2020/06/01
4  * @author Hourier
5  * @todo ちょっとギリギリ。後で分割を検討する
6  */
7
8 #include "floor/floor-object.h"
9 #include "artifact/fixed-art-generator.h"
10 #include "core/window-redrawer.h"
11 #include "flavor/flavor-describer.h"
12 #include "flavor/object-flavor-types.h"
13 #include "floor/cave.h"
14 #include "game-option/birth-options.h"
15 #include "game-option/cheat-options.h"
16 #include "game-option/cheat-types.h"
17 #include "grid/feature.h"
18 #include "grid/grid.h"
19 #include "inventory/inventory-slot-types.h"
20 #include "inventory/item-getter.h"
21 #include "main/sound-definitions-table.h"
22 #include "main/sound-of-music.h"
23 #include "object-enchant/item-apply-magic.h"
24 #include "object-enchant/item-magic-applier.h"
25 #include "object-enchant/special-object-flags.h"
26 #include "object/object-info.h"
27 #include "object/object-kind-hook.h"
28 #include "object/object-stack.h"
29 #include "perception/object-perception.h"
30 #include "system/alloc-entries.h"
31 #include "system/artifact-type-definition.h"
32 #include "system/baseitem-info.h"
33 #include "system/floor-type-definition.h"
34 #include "system/grid-type-definition.h"
35 #include "system/item-entity.h"
36 #include "system/monster-entity.h"
37 #include "system/player-type-definition.h"
38 #include "system/redrawing-flags-updater.h"
39 #include "system/system-variables.h"
40 #include "target/projection-path-calculator.h"
41 #include "util/bit-flags-calculator.h"
42 #include "view/display-messages.h"
43 #include "window/display-sub-windows.h"
44 #include "wizard/wizard-messages.h"
45 #include "world/world-object.h"
46 #include "world/world.h"
47
48 #define MAX_GOLD 18 /* Number of "gold" entries */
49
50 /*!
51  * @brief オブジェクト生成テーブルに生成制約を加える /
52  * Apply a "object restriction function" to the "object allocation table"
53  * @return 常に0を返す。
54  * @details 生成の制約はグローバルのget_obj_index_hook関数ポインタで加える
55  */
56 static errr get_obj_index_prep(void)
57 {
58     for (auto &entry : alloc_kind_table) {
59         if (!get_obj_index_hook || (*get_obj_index_hook)(entry.index)) {
60             entry.prob2 = entry.prob1;
61         } else {
62             entry.prob2 = 0;
63         }
64     }
65
66     return 0;
67 }
68
69 /*!
70  * @brief デバッグ時にアイテム生成情報をメッセージに出力する / Cheat -- describe a created object for the user
71  * @param player_ptr プレイヤーへの参照ポインタ
72  * @param o_ptr デバッグ出力するオブジェクトの構造体参照ポインタ
73  */
74 static void object_mention(PlayerType *player_ptr, ItemEntity *o_ptr)
75 {
76     object_aware(player_ptr, o_ptr);
77     object_known(o_ptr);
78
79     o_ptr->ident |= (IDENT_FULL_KNOWN);
80     const auto item_name = describe_flavor(player_ptr, o_ptr, 0);
81     msg_format_wizard(player_ptr, CHEAT_OBJECT, _("%sを生成しました。", "%s was generated."), item_name.data());
82 }
83
84 static int get_base_floor(FloorType *floor_ptr, BIT_FLAGS mode, std::optional<int> rq_mon_level)
85 {
86     if (any_bits(mode, AM_GREAT)) {
87         if (rq_mon_level.has_value()) {
88             return rq_mon_level.value() + 10 + randint1(10);
89         } else {
90             return floor_ptr->object_level + 15;
91         }
92     }
93
94     if (any_bits(mode, AM_GOOD)) {
95         return floor_ptr->object_level + 10;
96     }
97
98     return floor_ptr->object_level;
99 }
100
101 static void set_ammo_quantity(ItemEntity *j_ptr)
102 {
103     auto is_ammo = j_ptr->is_ammo();
104     is_ammo |= j_ptr->bi_key.tval() == ItemKindType::SPIKE;
105     if (is_ammo && !j_ptr->is_fixed_artifact()) {
106         j_ptr->number = damroll(6, 7);
107     }
108 }
109
110 /*!
111  * @brief 生成階に応じたベースアイテムの生成を行う。
112  * Attempt to make an object (normal or good/great)
113  * @param player_ptr プレイヤーへの参照ポインタ
114  * @param j_ptr 生成結果を収めたいオブジェクト構造体の参照ポインタ
115  * @param mode オプションフラグ
116  * @param rq_mon_level ランダムクエスト討伐対象のレベル。ランダムクエスト以外の生成であれば無効値
117  * @return アイテムの生成成功可否
118  */
119 bool make_object(PlayerType *player_ptr, ItemEntity *j_ptr, BIT_FLAGS mode, std::optional<int> rq_mon_level)
120 {
121     auto *floor_ptr = player_ptr->current_floor_ptr;
122     auto prob = any_bits(mode, AM_GOOD) ? 10 : 1000;
123     auto base = get_base_floor(floor_ptr, mode, rq_mon_level);
124     if (!one_in_(prob) || !make_artifact_special(player_ptr, j_ptr)) {
125         if (any_bits(mode, AM_GOOD) && !get_obj_index_hook) {
126             get_obj_index_hook = kind_is_good;
127         }
128
129         if (get_obj_index_hook) {
130             get_obj_index_prep();
131         }
132
133         auto bi_id = get_obj_index(floor_ptr, base, mode);
134         if (get_obj_index_hook) {
135             get_obj_index_hook = nullptr;
136             get_obj_index_prep();
137         }
138
139         if (bi_id == 0) {
140             return false;
141         }
142
143         j_ptr->prep(bi_id);
144     }
145
146     ItemMagicApplier(player_ptr, j_ptr, floor_ptr->object_level, mode).execute();
147     set_ammo_quantity(j_ptr);
148     if (cheat_peek) {
149         object_mention(player_ptr, j_ptr);
150     }
151
152     return true;
153 }
154
155 /*!
156  * @brief 生成階に応じた財宝オブジェクトの生成を行う。
157  * Make a treasure object
158  * @param floor_ptr 現在フロアへの参照ポインタ
159  * @param j_ptr 生成結果を収めたいオブジェクト構造体の参照ポインタ
160  * @return 生成に成功したらTRUEを返す。
161  * @details
162  * The location must be a legal, clean, floor grid.
163  */
164 bool make_gold(PlayerType *player_ptr, ItemEntity *j_ptr)
165 {
166     auto *floor_ptr = player_ptr->current_floor_ptr;
167     int i = ((randint1(floor_ptr->object_level + 2) + 2) / 2) - 1;
168     if (one_in_(CHANCE_BASEITEM_LEVEL_BOOST)) {
169         i += randint1(floor_ptr->object_level + 1);
170     }
171
172     if (coin_type) {
173         i = coin_type;
174     }
175     if (i >= MAX_GOLD) {
176         i = MAX_GOLD - 1;
177     }
178     j_ptr->prep(OBJ_GOLD_LIST + i);
179
180     int32_t base = baseitems_info[OBJ_GOLD_LIST + i].cost;
181     j_ptr->pval = (base + (8L * randint1(base)) + randint1(8));
182
183     return true;
184 }
185
186 /*!
187  * @brief フロア中のアイテムを全て削除する / Deletes all objects at given location
188  * Delete a dungeon object
189  * @param player_ptr プレイヤーへの参照ポインタ
190  * @param y 削除したフロアマスのY座標
191  * @param x 削除したフロアマスのX座標
192  */
193 void delete_all_items_from_floor(PlayerType *player_ptr, POSITION y, POSITION x)
194 {
195     grid_type *g_ptr;
196     auto *floor_ptr = player_ptr->current_floor_ptr;
197     if (!in_bounds(floor_ptr, y, x)) {
198         return;
199     }
200
201     g_ptr = &floor_ptr->grid_array[y][x];
202     for (const auto this_o_idx : g_ptr->o_idx_list) {
203         ItemEntity *o_ptr;
204         o_ptr = &floor_ptr->o_list[this_o_idx];
205         o_ptr->wipe();
206         floor_ptr->o_cnt--;
207     }
208
209     g_ptr->o_idx_list.clear();
210     lite_spot(player_ptr, y, x);
211 }
212
213 /*!
214  * @brief 床上のアイテムの数を増やす /
215  * Increase the "number" of an item on the floor
216  * @param player_ptr プレイヤーへの参照ポインタ
217  * @param item 増やしたいアイテムの所持スロット
218  * @param num 増やしたいアイテムの数
219  */
220 void floor_item_increase(PlayerType *player_ptr, INVENTORY_IDX item, ITEM_NUMBER num)
221 {
222     auto *floor_ptr = player_ptr->current_floor_ptr;
223
224     auto *o_ptr = &floor_ptr->o_list[item];
225     num += o_ptr->number;
226     if (num > 255) {
227         num = 255;
228     } else if (num < 0) {
229         num = 0;
230     }
231
232     num -= o_ptr->number;
233     o_ptr->number += num;
234     static constexpr auto flags = {
235         SubWindowRedrawingFlag::FLOOR_ITEMS,
236         SubWindowRedrawingFlag::FOUND_ITEMS,
237     };
238     RedrawingFlagsUpdater::get_instance().set_flags(flags);
239 }
240
241 /*!
242  * @brief 床上の数の無くなったアイテムスロットを消去する /
243  * Optimize an item on the floor (destroy "empty" items)
244  * @param player_ptr プレイヤーへの参照ポインタ
245  * @param item 消去したいアイテムの所持スロット
246  */
247 void floor_item_optimize(PlayerType *player_ptr, INVENTORY_IDX item)
248 {
249     auto *o_ptr = &player_ptr->current_floor_ptr->o_list[item];
250     if (!o_ptr->is_valid()) {
251         return;
252     }
253     if (o_ptr->number) {
254         return;
255     }
256
257     delete_object_idx(player_ptr, item);
258     static constexpr auto flags = {
259         SubWindowRedrawingFlag::FLOOR_ITEMS,
260         SubWindowRedrawingFlag::FOUND_ITEMS,
261     };
262     RedrawingFlagsUpdater::get_instance().set_flags(flags);
263 }
264
265 /*!
266  * @brief オブジェクトを削除する /
267  * Delete a dungeon object
268  * @param player_ptr プレイヤーへの参照ポインタ
269  * @param o_idx 削除対象のオブジェクト構造体ポインタ
270  * @details
271  * Handle "stacks" of objects correctly.
272  */
273 void delete_object_idx(PlayerType *player_ptr, OBJECT_IDX o_idx)
274 {
275     ItemEntity *j_ptr;
276     auto *floor_ptr = player_ptr->current_floor_ptr;
277     excise_object_idx(floor_ptr, o_idx);
278     j_ptr = &floor_ptr->o_list[o_idx];
279     if (!j_ptr->is_held_by_monster()) {
280         POSITION y, x;
281         y = j_ptr->iy;
282         x = j_ptr->ix;
283         lite_spot(player_ptr, y, x);
284     }
285
286     j_ptr->wipe();
287     floor_ptr->o_cnt--;
288     static constexpr auto flags = {
289         SubWindowRedrawingFlag::FLOOR_ITEMS,
290         SubWindowRedrawingFlag::FOUND_ITEMS,
291     };
292     RedrawingFlagsUpdater::get_instance().set_flags(flags);
293 }
294
295 /*!
296  * @brief 床上、モンスター所持でスタックされたアイテムを削除しスタックを補完する / Excise a dungeon object from any stacks
297  * @param floo_ptr 現在フロアへの参照ポインタ
298  * @param o_idx 削除対象のオブジェクト構造体ポインタ
299  */
300 void excise_object_idx(FloorType *floor_ptr, OBJECT_IDX o_idx)
301 {
302     auto &list = get_o_idx_list_contains(floor_ptr, o_idx);
303     list.remove(o_idx);
304 }
305
306 /*!
307  * @brief 指定したOBJECT_IDXを含むリスト(モンスター所持リスト or 床上スタックリスト)への参照を得る
308  * @param floo_ptr 現在フロアへの参照ポインタ
309  * @param o_idx 参照を得るリストに含まれるOBJECT_IDX
310  * @return o_idxを含む ObjectIndexList への参照
311  */
312 ObjectIndexList &get_o_idx_list_contains(FloorType *floor_ptr, OBJECT_IDX o_idx)
313 {
314     auto *o_ptr = &floor_ptr->o_list[o_idx];
315
316     if (o_ptr->is_held_by_monster()) {
317         return floor_ptr->m_list[o_ptr->held_m_idx].hold_o_idx_list;
318     } else {
319         return floor_ptr->grid_array[o_ptr->iy][o_ptr->ix].o_idx_list;
320     }
321 }
322
323 /*!
324  * @brief 生成済のオブジェクトをフロアの所定の位置に落とす。
325  * Let an object fall to the ground at or near a location.
326  * @param player_ptr プレイヤーへの参照ポインタ
327  * @param j_ptr 落としたいオブジェクト構造体の参照ポインタ
328  * @param chance ドロップの消滅率(%)
329  * @param y 配置したいフロアのY座標
330  * @param x 配置したいフロアのX座標
331  * @return 生成に成功したらオブジェクトのIDを返す。
332  * @details
333  * The initial location is assumed to be "in_bounds(floor_ptr, )".\n
334  *\n
335  * This function takes a parameter "chance".  This is the percentage\n
336  * chance that the item will "disappear" instead of drop.  If the object\n
337  * has been thrown, then this is the chance of disappearance on contact.\n
338  *\n
339  * Hack -- this function uses "chance" to determine if it should produce\n
340  * some form of "description" of the drop event (under the player).\n
341  *\n
342  * We check several locations to see if we can find a location at which\n
343  * the object can combine, stack, or be placed.  Artifacts will try very\n
344  * hard to be placed, including "teleporting" to a useful grid if needed.\n
345  */
346 OBJECT_IDX drop_near(PlayerType *player_ptr, ItemEntity *j_ptr, PERCENTAGE chance, POSITION y, POSITION x)
347 {
348     int i, k, d, s;
349     POSITION dy, dx;
350     POSITION ty, tx = 0;
351     OBJECT_IDX o_idx = 0;
352     grid_type *g_ptr;
353     bool flag = false;
354     bool done = false;
355 #ifdef JP
356 #else
357     bool plural = (j_ptr->number != 1);
358 #endif
359     const auto item_name = describe_flavor(player_ptr, j_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
360     if (!j_ptr->is_fixed_or_random_artifact() && (randint0(100) < chance)) {
361 #ifdef JP
362         msg_format("%sは消えた。", item_name.data());
363 #else
364         msg_format("The %s disappear%s.", item_name.data(), (plural ? "" : "s"));
365 #endif
366         if (w_ptr->wizard) {
367             msg_print(_("(破損)", "(breakage)"));
368         }
369
370         return 0;
371     }
372
373     int bs = -1;
374     int bn = 0;
375
376     POSITION by = y;
377     POSITION bx = x;
378     auto *floor_ptr = player_ptr->current_floor_ptr;
379     for (dy = -3; dy <= 3; dy++) {
380         for (dx = -3; dx <= 3; dx++) {
381             bool comb = false;
382             d = (dy * dy) + (dx * dx);
383             if (d > 10) {
384                 continue;
385             }
386
387             ty = y + dy;
388             tx = x + dx;
389             if (!in_bounds(floor_ptr, ty, tx)) {
390                 continue;
391             }
392             if (!projectable(player_ptr, y, x, ty, tx)) {
393                 continue;
394             }
395
396             g_ptr = &floor_ptr->grid_array[ty][tx];
397             if (!cave_drop_bold(floor_ptr, ty, tx)) {
398                 continue;
399             }
400
401             k = 0;
402             for (const auto this_o_idx : g_ptr->o_idx_list) {
403                 ItemEntity *o_ptr;
404                 o_ptr = &floor_ptr->o_list[this_o_idx];
405                 if (object_similar(o_ptr, j_ptr)) {
406                     comb = true;
407                 }
408
409                 k++;
410             }
411
412             if (!comb) {
413                 k++;
414             }
415             if (k > 99) {
416                 continue;
417             }
418
419             s = 1000 - (d + k * 5);
420             if (s < bs) {
421                 continue;
422             }
423
424             if (s > bs) {
425                 bn = 0;
426             }
427
428             if ((++bn >= 2) && !one_in_(bn)) {
429                 continue;
430             }
431
432             bs = s;
433             by = ty;
434             bx = tx;
435
436             flag = true;
437         }
438     }
439
440     if (!flag && !j_ptr->is_fixed_or_random_artifact()) {
441 #ifdef JP
442         msg_format("%sは消えた。", item_name.data());
443 #else
444         msg_format("The %s disappear%s.", item_name.data(), (plural ? "" : "s"));
445 #endif
446         if (w_ptr->wizard) {
447             msg_print(_("(床スペースがない)", "(no floor space)"));
448         }
449
450         return 0;
451     }
452
453     for (i = 0; !flag && (i < 1000); i++) {
454         ty = rand_spread(by, 1);
455         tx = rand_spread(bx, 1);
456
457         if (!in_bounds(floor_ptr, ty, tx)) {
458             continue;
459         }
460
461         by = ty;
462         bx = tx;
463
464         if (!cave_drop_bold(floor_ptr, by, bx)) {
465             continue;
466         }
467
468         flag = true;
469     }
470
471     auto &artifact = j_ptr->get_fixed_artifact();
472     if (!flag) {
473         int candidates = 0, pick;
474         for (ty = 1; ty < floor_ptr->height - 1; ty++) {
475             for (tx = 1; tx < floor_ptr->width - 1; tx++) {
476                 if (cave_drop_bold(floor_ptr, ty, tx)) {
477                     candidates++;
478                 }
479             }
480         }
481
482         if (!candidates) {
483 #ifdef JP
484             msg_format("%sは消えた。", item_name.data());
485 #else
486             msg_format("The %s disappear%s.", item_name.data(), (plural ? "" : "s"));
487 #endif
488
489             if (w_ptr->wizard) {
490                 msg_print(_("(床スペースがない)", "(no floor space)"));
491             }
492
493             if (preserve_mode) {
494                 if (j_ptr->is_fixed_artifact() && !j_ptr->is_known()) {
495                     artifact.is_generated = false;
496                 }
497             }
498
499             return 0;
500         }
501
502         pick = randint1(candidates);
503         for (ty = 1; ty < floor_ptr->height - 1; ty++) {
504             for (tx = 1; tx < floor_ptr->width - 1; tx++) {
505                 if (cave_drop_bold(floor_ptr, ty, tx)) {
506                     pick--;
507                     if (!pick) {
508                         break;
509                     }
510                 }
511             }
512
513             if (!pick) {
514                 break;
515             }
516         }
517
518         by = ty;
519         bx = tx;
520     }
521
522     g_ptr = &floor_ptr->grid_array[by][bx];
523     for (const auto this_o_idx : g_ptr->o_idx_list) {
524         ItemEntity *o_ptr;
525         o_ptr = &floor_ptr->o_list[this_o_idx];
526         if (object_similar(o_ptr, j_ptr)) {
527             object_absorb(o_ptr, j_ptr);
528             done = true;
529             break;
530         }
531     }
532
533     if (!done) {
534         o_idx = o_pop(floor_ptr);
535     }
536
537     if (!done && !o_idx) {
538 #ifdef JP
539         msg_format("%sは消えた。", item_name.data());
540 #else
541         msg_format("The %s disappear%s.", item_name.data(), (plural ? "" : "s"));
542 #endif
543         if (w_ptr->wizard) {
544             msg_print(_("(アイテムが多過ぎる)", "(too many objects)"));
545         }
546
547         if (j_ptr->is_fixed_artifact()) {
548             artifact.is_generated = false;
549         }
550
551         return 0;
552     }
553
554     if (!done) {
555         (&floor_ptr->o_list[o_idx])->copy_from(j_ptr);
556         j_ptr = &floor_ptr->o_list[o_idx];
557         j_ptr->iy = by;
558         j_ptr->ix = bx;
559         j_ptr->held_m_idx = 0;
560         g_ptr->o_idx_list.add(floor_ptr, o_idx);
561         done = true;
562     }
563
564     if (j_ptr->is_fixed_artifact() && w_ptr->character_dungeon) {
565         artifact.floor_id = player_ptr->floor_id;
566     }
567
568     note_spot(player_ptr, by, bx);
569     lite_spot(player_ptr, by, bx);
570     sound(SOUND_DROP);
571
572     if (player_bold(player_ptr, by, bx)) {
573         static constexpr auto flags = {
574             SubWindowRedrawingFlag::FLOOR_ITEMS,
575             SubWindowRedrawingFlag::FOUND_ITEMS,
576         };
577         RedrawingFlagsUpdater::get_instance().set_flags(flags);
578     }
579
580     if (chance && player_bold(player_ptr, by, bx)) {
581         msg_print(_("何かが足下に転がってきた。", "You feel something roll beneath your feet."));
582     }
583
584     return o_idx;
585 }
586
587 /*!
588  * @brief 床上の魔道具の残り残量メッセージを表示する /
589  * Describe the charges on an item on the floor.
590  * @param floo_ptr 現在フロアへの参照ポインタ
591  * @param item メッセージの対象にしたいアイテム所持スロット
592  */
593 void floor_item_charges(FloorType *floor_ptr, INVENTORY_IDX inventory)
594 {
595     const auto &item = floor_ptr->o_list[inventory];
596     if (!item.is_wand_staff() || !item.is_known()) {
597         return;
598     }
599
600 #ifdef JP
601     if (item.pval <= 0) {
602         msg_print("この床上のアイテムは、もう魔力が残っていない。");
603     } else {
604         msg_format("この床上のアイテムは、あと %d 回分の魔力が残っている。", item.pval);
605     }
606 #else
607     if (item.pval != 1) {
608         msg_format("There are %d charges remaining.", item.pval);
609     } else {
610         msg_format("There is %d charge remaining.", item.pval);
611     }
612 #endif
613 }
614
615 /*!
616  * @brief 床上のアイテムの残り数メッセージを表示する /
617  * Describe the charges on an item on the floor.
618  * @param floo_ptr 現在フロアへの参照ポインタ
619  * @param item メッセージの対象にしたいアイテム所持スロット
620  */
621 void floor_item_describe(PlayerType *player_ptr, INVENTORY_IDX item)
622 {
623     auto *o_ptr = &player_ptr->current_floor_ptr->o_list[item];
624     const auto item_name = describe_flavor(player_ptr, o_ptr, 0);
625 #ifdef JP
626     if (o_ptr->number <= 0) {
627         msg_format("床上には、もう%sはない。", item_name.data());
628     } else {
629         msg_format("床上には、まだ %sがある。", item_name.data());
630     }
631 #else
632     msg_format("You see %s.", item_name.data());
633 #endif
634 }
635
636 /*
637  * Choose an item and get auto-picker entry from it.
638  */
639 ItemEntity *choose_object(PlayerType *player_ptr, OBJECT_IDX *idx, concptr q, concptr s, BIT_FLAGS option, const ItemTester &item_tester)
640 {
641     OBJECT_IDX item;
642
643     if (idx) {
644         *idx = INVEN_NONE;
645     }
646
647     FixItemTesterSetter setter(item_tester);
648
649     if (!get_item(player_ptr, &item, q, s, option, item_tester)) {
650         return nullptr;
651     }
652
653     if (idx) {
654         *idx = item;
655     }
656
657     if (item == INVEN_FORCE) {
658         return nullptr;
659     }
660
661     return ref_item(player_ptr, item);
662 }