OSDN Git Service

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