OSDN Git Service

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