OSDN Git Service

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