OSDN Git Service

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