OSDN Git Service

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