OSDN Git Service

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