OSDN Git Service

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