OSDN Git Service

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