OSDN Git Service

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