OSDN Git Service

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