OSDN Git Service

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