2 * @brief フロア生成時にアイテムを配置する
5 * @todo ちょっとギリギリ。後で分割を検討する
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"
47 #define MAX_GOLD 18 /* Number of "gold" entries */
50 * @brief オブジェクト生成テーブルに生成制約を加える /
51 * Apply a "object restriction function" to the "object allocation table"
53 * @details 生成の制約はグローバルのget_obj_num_hook関数ポインタで加える
55 static errr get_obj_num_prep(void)
57 for (auto &entry : alloc_kind_table) {
58 if (!get_obj_num_hook || (*get_obj_num_hook)(entry.index)) {
59 entry.prob2 = entry.prob1;
69 * @brief デバッグ時にアイテム生成情報をメッセージに出力する / Cheat -- describe a created object for the user
70 * @param player_ptr プレイヤーへの参照ポインタ
71 * @param o_ptr デバッグ出力するオブジェクトの構造体参照ポインタ
73 static void object_mention(player_type *player_ptr, object_type *o_ptr)
75 object_aware(player_ptr, o_ptr);
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);
84 static int get_base_floor(floor_type *floor_ptr, BIT_FLAGS mode, int rq_level)
86 if (any_bits(mode, AM_GREAT)) {
88 return rq_level + 10 + randint1(10);
90 return floor_ptr->object_level + 15;
94 if (any_bits(mode, AM_GOOD)) {
95 return floor_ptr->object_level + 10;
98 return floor_ptr->object_level;
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 アイテムの生成成功可否
110 * rq_levelは、ユニークのレベルであってランダムクエストの階層ではない
111 * ランダムクエストではない場合、-1が入る
113 bool make_object(player_type *player_ptr, object_type *j_ptr, BIT_FLAGS mode, int rq_level)
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;
123 if (get_obj_num_hook) {
127 auto k_idx = get_obj_num(player_ptr, base, mode);
128 if (get_obj_num_hook) {
129 get_obj_num_hook = nullptr;
140 apply_magic_to_object(player_ptr, j_ptr, floor_ptr->object_level, mode);
141 switch (j_ptr->tval) {
147 j_ptr->number = (byte)damroll(6, 7);
156 object_mention(player_ptr, j_ptr);
163 * @brief 生成階に応じた財宝オブジェクトの生成を行う。
164 * Make a treasure object
165 * @param floor_ptr 現在フロアへの参照ポインタ
166 * @param j_ptr 生成結果を収めたいオブジェクト構造体の参照ポインタ
167 * @return 生成に成功したらTRUEを返す。
169 * The location must be a legal, clean, floor grid.
171 bool make_gold(player_type *player_ptr, object_type *j_ptr)
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);
183 j_ptr->prep(OBJ_GOLD_LIST + i);
185 int32_t base = k_info[OBJ_GOLD_LIST + i].cost;
186 j_ptr->pval = (base + (8L * randint1(base)) + randint1(8));
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座標
198 void delete_all_items_from_floor(player_type *player_ptr, POSITION y, POSITION x)
201 floor_type *floor_ptr = player_ptr->current_floor_ptr;
202 if (!in_bounds(floor_ptr, y, x))
205 g_ptr = &floor_ptr->grid_array[y][x];
206 for (const auto this_o_idx : g_ptr->o_idx_list) {
208 o_ptr = &floor_ptr->o_list[this_o_idx];
213 g_ptr->o_idx_list.clear();
214 lite_spot(player_ptr, y, x);
218 * @brief 床上のアイテムの数を増やす /
219 * Increase the "number" of an item on the floor
220 * @param player_ptr プレイヤーへの参照ポインタ
221 * @param item 増やしたいアイテムの所持スロット
222 * @param num 増やしたいアイテムの数
224 void floor_item_increase(player_type *player_ptr, INVENTORY_IDX item, ITEM_NUMBER num)
226 floor_type *floor_ptr = player_ptr->current_floor_ptr;
228 object_type *o_ptr = &floor_ptr->o_list[item];
229 num += o_ptr->number;
235 num -= o_ptr->number;
236 o_ptr->number += num;
238 set_bits(player_ptr->window_flags, PW_FLOOR_ITEM_LIST);
242 * @brief 床上の数の無くなったアイテムスロットを消去する /
243 * Optimize an item on the floor (destroy "empty" items)
244 * @param player_ptr プレイヤーへの参照ポインタ
245 * @param item 消去したいアイテムの所持スロット
247 void floor_item_optimize(player_type *player_ptr, INVENTORY_IDX item)
249 object_type *o_ptr = &player_ptr->current_floor_ptr->o_list[item];
255 delete_object_idx(player_ptr, item);
257 set_bits(player_ptr->window_flags, PW_FLOOR_ITEM_LIST);
261 * @brief オブジェクトを削除する /
262 * Delete a dungeon object
263 * @param player_ptr プレイヤーへの参照ポインタ
264 * @param o_idx 削除対象のオブジェクト構造体ポインタ
266 * Handle "stacks" of objects correctly.
268 void delete_object_idx(player_type *player_ptr, OBJECT_IDX o_idx)
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()) {
278 lite_spot(player_ptr, y, x);
284 set_bits(player_ptr->window_flags, PW_FLOOR_ITEM_LIST);
288 * @brief 床上、モンスター所持でスタックされたアイテムを削除しスタックを補完する / Excise a dungeon object from any stacks
289 * @param floo_ptr 現在フロアへの参照ポインタ
290 * @param o_idx 削除対象のオブジェクト構造体ポインタ
292 void excise_object_idx(floor_type *floor_ptr, OBJECT_IDX o_idx)
294 auto &list = get_o_idx_list_contains(floor_ptr, o_idx);
299 * @brief 指定したOBJECT_IDXを含むリスト(モンスター所持リスト or 床上スタックリスト)への参照を得る
300 * @param floo_ptr 現在フロアへの参照ポインタ
301 * @param o_idx 参照を得るリストに含まれるOBJECT_IDX
302 * @return o_idxを含む ObjectIndexList への参照
304 ObjectIndexList &get_o_idx_list_contains(floor_type *floor_ptr, OBJECT_IDX o_idx)
306 object_type *o_ptr = &floor_ptr->o_list[o_idx];
308 if (o_ptr->is_held_by_monster()) {
309 return floor_ptr->m_list[o_ptr->held_m_idx].hold_o_idx_list;
311 return floor_ptr->grid_array[o_ptr->iy][o_ptr->ix].o_idx_list;
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を返す。
325 * The initial location is assumed to be "in_bounds(floor_ptr, )".\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
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
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
338 OBJECT_IDX drop_near(player_type *player_ptr, object_type *j_ptr, PERCENTAGE chance, POSITION y, POSITION x)
343 OBJECT_IDX o_idx = 0;
345 GAME_TEXT o_name[MAX_NLEN];
350 bool plural = (j_ptr->number != 1);
352 describe_flavor(player_ptr, o_name, j_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
355 if (!j_ptr->is_artifact() && (randint0(100) < chance)) {
357 msg_format("%sは消えた。", o_name);
359 msg_format("The %s disappear%s.", o_name, (plural ? "" : "s"));
369 floor_type *floor_ptr = player_ptr->current_floor_ptr;
370 for (dy = -3; dy <= 3; dy++) {
371 for (dx = -3; dx <= 3; dx++) {
373 d = (dy * dy) + (dx * dx);
379 if (!in_bounds(floor_ptr, ty, tx))
381 if (!projectable(player_ptr, y, x, ty, tx))
384 g_ptr = &floor_ptr->grid_array[ty][tx];
385 if (!cave_drop_bold(floor_ptr, ty, tx))
389 for (const auto this_o_idx : g_ptr->o_idx_list) {
391 o_ptr = &floor_ptr->o_list[this_o_idx];
392 if (object_similar(o_ptr, j_ptr))
403 s = 1000 - (d + k * 5);
410 if ((++bn >= 2) && !one_in_(bn))
422 if (!flag && !j_ptr->is_artifact()) {
424 msg_format("%sは消えた。", o_name);
426 msg_format("The %s disappear%s.", o_name, (plural ? "" : "s"));
431 for (i = 0; !flag && (i < 1000); i++) {
432 ty = rand_spread(by, 1);
433 tx = rand_spread(bx, 1);
435 if (!in_bounds(floor_ptr, ty, tx))
441 if (!cave_drop_bold(floor_ptr, by, bx))
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))
459 msg_format("%sは消えた。", o_name);
461 msg_format("The %s disappear%s.", o_name, (plural ? "" : "s"));
464 if (j_ptr->is_fixed_artifact() && !j_ptr->is_known()) {
465 a_info[j_ptr->name1].cur_num = 0;
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)) {
490 g_ptr = &floor_ptr->grid_array[by][bx];
491 for (const auto this_o_idx : g_ptr->o_idx_list) {
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);
502 o_idx = o_pop(floor_ptr);
505 if (!done && !o_idx) {
507 msg_format("%sは消えた。", o_name);
509 msg_format("The %s disappear%s.", o_name, (plural ? "" : "s"));
511 if (j_ptr->is_fixed_artifact()) {
512 a_info[j_ptr->name1].cur_num = 0;
519 (&floor_ptr->o_list[o_idx])->copy_from(j_ptr);
520 j_ptr = &floor_ptr->o_list[o_idx];
523 j_ptr->held_m_idx = 0;
524 g_ptr->o_idx_list.add(floor_ptr, o_idx);
528 note_spot(player_ptr, by, bx);
529 lite_spot(player_ptr, by, bx);
532 if (player_bold(player_ptr, by, bx))
533 set_bits(player_ptr->window_flags, PW_FLOOR_ITEM_LIST);
535 if (chance && player_bold(player_ptr, by, bx)) {
536 msg_print(_("何かが足下に転がってきた。", "You feel something roll beneath your feet."));
543 * @brief 床上の魔道具の残り残量メッセージを表示する /
544 * Describe the charges on an item on the floor.
545 * @param floo_ptr 現在フロアへの参照ポインタ
546 * @param item メッセージの対象にしたいアイテム所持スロット
548 void floor_item_charges(floor_type *floor_ptr, INVENTORY_IDX item)
550 object_type *o_ptr = &floor_ptr->o_list[item];
551 if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND))
553 if (!o_ptr->is_known())
557 if (o_ptr->pval <= 0) {
558 msg_print("この床上のアイテムは、もう魔力が残っていない。");
560 msg_format("この床上のアイテムは、あと %d 回分の魔力が残っている。", o_ptr->pval);
563 if (o_ptr->pval != 1) {
564 msg_format("There are %d charges remaining.", o_ptr->pval);
566 msg_format("There is %d charge remaining.", o_ptr->pval);
572 * @brief 床上のアイテムの残り数メッセージを表示する /
573 * Describe the charges on an item on the floor.
574 * @param floo_ptr 現在フロアへの参照ポインタ
575 * @param item メッセージの対象にしたいアイテム所持スロット
577 void floor_item_describe(player_type *player_ptr, INVENTORY_IDX item)
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);
583 if (o_ptr->number <= 0) {
584 msg_format("床上には、もう%sはない。", o_name);
586 msg_format("床上には、まだ %sがある。", o_name);
589 msg_format("You see %s.", o_name);
594 * Choose an item and get auto-picker entry from it.
596 object_type *choose_object(player_type *player_ptr, OBJECT_IDX *idx, concptr q, concptr s, BIT_FLAGS option, const ItemTester& item_tester)
603 FixItemTesterSetter setter(item_tester);
605 if (!get_item(player_ptr, &item, q, s, option, item_tester))
611 if (item == INVEN_FORCE)
614 return ref_item(player_ptr, item);