OSDN Git Service

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