OSDN Git Service

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