OSDN Git Service

[Refactor] #37285 Separated monster-attach-lose.c/h from monster-attack-switcher.c
[hengband/hengband.git] / src / inventory / inventory-util.c
1 /*!
2  * @brief インベントリ関係のユーティリティ
3  * @date 2020/07/02
4  * @author Hourier
5  * @details 少々雑多なので後で整理を検討する
6  */
7
8 #include "inventory/inventory-util.h"
9 #include "core/asking-player.h"
10 #include "flavor/flavor-describer.h"
11 #include "inventory/inventory-slot-types.h"
12 #include "io/input-key-requester.h"
13 #include "object/item-tester-hooker.h"
14 #include "object/item-use-flags.h"
15 #include "system/floor-type-definition.h"
16 #include "system/object-type-definition.h"
17 #include "util/int-char-converter.h"
18 #include "util/quarks.h"
19 #include "util/string-processor.h"
20
21 bool select_ring_slot;
22
23 /*!
24  * @brief プレイヤーの所持/装備オブジェクトIDが指輪枠かを返す /
25  * @param i プレイヤーの所持/装備オブジェクトID
26  * @return 指輪枠ならばTRUEを返す。
27  */
28 bool is_ring_slot(int i) { return (i == INVEN_RIGHT) || (i == INVEN_LEFT); }
29
30 /*!
31  * @brief 床オブジェクトに選択タグを与える/タグに該当するオブジェクトがあるかを返す /
32  * Find the "first" inventory object with the given "tag".
33  * @param cp 対応するタグIDを与える参照ポインタ
34  * @param tag 該当するオブジェクトがあるかを調べたいタグ
35  * @param floor_list 床上アイテムの配列
36  * @param floor_num  床上アイテムの配列ID
37  * @return タグに該当するオブジェクトがあるならTRUEを返す
38  * @details
39  * A "tag" is a numeral "n" appearing as "@n" anywhere in the\n
40  * inscription of an object.  Alphabetical characters don't work as a\n
41  * tag in this form.\n
42  *\n
43  * Also, the tag "@xn" will work as well, where "n" is a any tag-char,\n
44  * and "x" is the "current" command_cmd code.\n
45  */
46 bool get_tag_floor(floor_type *floor_ptr, COMMAND_CODE *cp, char tag, FLOOR_IDX floor_list[], ITEM_NUMBER floor_num)
47 {
48     for (COMMAND_CODE i = 0; i < floor_num && i < 23; i++) {
49         object_type *o_ptr = &floor_ptr->o_list[floor_list[i]];
50         if (!o_ptr->inscription)
51             continue;
52
53         concptr s = angband_strchr(quark_str(o_ptr->inscription), '@');
54         while (s) {
55             if ((s[1] == command_cmd) && (s[2] == tag)) {
56                 *cp = i;
57                 return TRUE;
58             }
59
60             s = angband_strchr(s + 1, '@');
61         }
62     }
63
64     if (tag < '0' || '9' < tag) {
65         return FALSE;
66     }
67
68     for (COMMAND_CODE i = 0; i < floor_num && i < 23; i++) {
69         object_type *o_ptr = &floor_ptr->o_list[floor_list[i]];
70         if (!o_ptr->inscription)
71             continue;
72
73         concptr s = angband_strchr(quark_str(o_ptr->inscription), '@');
74         while (s) {
75             if (s[1] == tag) {
76                 *cp = i;
77                 return TRUE;
78             }
79
80             s = angband_strchr(s + 1, '@');
81         }
82     }
83
84     return FALSE;
85 }
86
87 /*!
88  * @brief 所持/装備オブジェクトに選択タグを与える/タグに該当するオブジェクトがあるかを返す /
89  * Find the "first" inventory object with the given "tag".
90  * @param owner_ptr プレーヤーへの参照ポインタ
91  * @param cp 対応するタグIDを与える参照ポインタ
92  * @param tag 該当するオブジェクトがあるかを調べたいタグ
93  * @param mode 所持、装備の切り替え
94  * @return タグに該当するオブジェクトがあるならTRUEを返す
95  * @details
96  * A "tag" is a numeral "n" appearing as "@n" anywhere in the\n
97  * inscription of an object.  Alphabetical characters don't work as a\n
98  * tag in this form.\n
99  *\n
100  * Also, the tag "@xn" will work as well, where "n" is a any tag-char,\n
101  * and "x" is the "current" command_cmd code.\n
102  */
103 bool get_tag(player_type *owner_ptr, COMMAND_CODE *cp, char tag, BIT_FLAGS mode, tval_type tval)
104 {
105     COMMAND_CODE start, end;
106     switch (mode) {
107     case USE_EQUIP:
108         start = INVEN_RARM;
109         end = INVEN_TOTAL - 1;
110         break;
111
112     case USE_INVEN:
113         start = 0;
114         end = INVEN_PACK - 1;
115         break;
116
117     default:
118         return FALSE;
119     }
120
121     for (COMMAND_CODE i = start; i <= end; i++) {
122         object_type *o_ptr = &owner_ptr->inventory_list[i];
123         if ((o_ptr->k_idx == 0) || (o_ptr->inscription == 0))
124             continue;
125
126         if (!item_tester_okay(owner_ptr, o_ptr, tval) && !(mode & USE_FULL))
127             continue;
128
129         concptr s = angband_strchr(quark_str(o_ptr->inscription), '@');
130         while (s) {
131             if ((s[1] == command_cmd) && (s[2] == tag)) {
132                 *cp = i;
133                 return TRUE;
134             }
135
136             s = angband_strchr(s + 1, '@');
137         }
138     }
139
140     if (tag < '0' || '9' < tag)
141         return FALSE;
142
143     for (COMMAND_CODE i = start; i <= end; i++) {
144         object_type *o_ptr = &owner_ptr->inventory_list[i];
145         if ((o_ptr->k_idx == 0) || (o_ptr->inscription == 0))
146             continue;
147
148         if (!item_tester_okay(owner_ptr, o_ptr, tval) && !(mode & USE_FULL))
149             continue;
150
151         concptr s = angband_strchr(quark_str(o_ptr->inscription), '@');
152         while (s) {
153             if (s[1] == tag) {
154                 *cp = i;
155                 return TRUE;
156             }
157
158             s = angband_strchr(s + 1, '@');
159         }
160     }
161
162     return FALSE;
163 }
164
165 /*!
166  * @brief プレイヤーの所持/装備オブジェクトが正規のものかを返す /
167  * Auxiliary function for "get_item()" -- test an index
168  * @param i 選択アイテムID
169  * @return 正規のIDならばTRUEを返す。
170  */
171 bool get_item_okay(player_type *owner_ptr, OBJECT_IDX i, tval_type item_tester_tval)
172 {
173     if ((i < 0) || (i >= INVEN_TOTAL))
174         return FALSE;
175
176     if (select_ring_slot)
177         return is_ring_slot(i);
178
179     return item_tester_okay(owner_ptr, &owner_ptr->inventory_list[i], item_tester_tval);
180 }
181
182 /*!
183  * @brief 選択したアイテムの確認処理のメインルーチン /
184  * @param owner_ptr プレーヤーへの参照ポインタ
185  * @param item 選択アイテムID
186  * @return 確認がYesならTRUEを返す。
187  * @details The item can be negative to mean "item on floor".
188  * Hack -- allow user to "prevent" certain choices
189  */
190 bool get_item_allow(player_type *owner_ptr, INVENTORY_IDX item)
191 {
192     if (!command_cmd)
193         return TRUE;
194
195     object_type *o_ptr;
196     if (item >= 0)
197         o_ptr = &owner_ptr->inventory_list[item];
198     else
199         o_ptr = &owner_ptr->current_floor_ptr->o_list[0 - item];
200
201     if (!o_ptr->inscription)
202         return TRUE;
203
204     concptr s = angband_strchr(quark_str(o_ptr->inscription), '!');
205     while (s) {
206         if ((s[1] == command_cmd) || (s[1] == '*'))
207             if (!verify(owner_ptr, _("本当に", "Really try"), item))
208                 return FALSE;
209
210         s = angband_strchr(s + 1, '!');
211     }
212
213     return TRUE;
214 }
215
216 /*!
217  * @brief 選択アルファベットラベルからプレイヤーの装備オブジェクトIDを返す /
218  * @param owner_ptr プレーヤーへの参照ポインタ
219  * Convert a label into the index of a item in the "equip"
220  * @return 対応するID。該当スロットにオブジェクトが存在しなかった場合-1を返す / Return "-1" if the label does not indicate a real item
221  */
222 INVENTORY_IDX label_to_equipment(player_type *owner_ptr, int c)
223 {
224     INVENTORY_IDX i = (INVENTORY_IDX)(islower(c) ? A2I(c) : -1) + INVEN_RARM;
225
226     if ((i < INVEN_RARM) || (i >= INVEN_TOTAL))
227         return -1;
228
229     if (select_ring_slot)
230         return is_ring_slot(i) ? i : -1;
231
232     if (!owner_ptr->inventory_list[i].k_idx)
233         return -1;
234
235     return i;
236 }
237
238 /*!
239  * @brief 選択アルファベットラベルからプレイヤーの所持オブジェクトIDを返す /
240  * Convert a label into the index of an item in the "inven"
241  * @param owner_ptr プレーヤーへの参照ポインタ
242  * @param c 選択されたアルファベット
243  * @return 対応するID。該当スロットにオブジェクトが存在しなかった場合-1を返す / Return "-1" if the label does not indicate a real item
244  * @details Note that the label does NOT distinguish inven/equip.
245  */
246 INVENTORY_IDX label_to_inventory(player_type *owner_ptr, int c)
247 {
248     INVENTORY_IDX i = (INVENTORY_IDX)(islower(c) ? A2I(c) : -1);
249
250     if ((i < 0) || (i > INVEN_PACK) || (owner_ptr->inventory_list[i].k_idx == 0))
251         return -1;
252
253     return i;
254 }
255
256 /*!
257  * @brief 選択したアイテムの確認処理の補助 /
258  * Verify the choice of an item.
259  * @param owner_ptr プレーヤーへの参照ポインタ
260  * @param prompt メッセージ表示の一部
261  * @param item 選択アイテムID
262  * @return 確認がYesならTRUEを返す。
263  * @details The item can be negative to mean "item on floor".
264  */
265 bool verify(player_type *owner_ptr, concptr prompt, INVENTORY_IDX item)
266 {
267     GAME_TEXT o_name[MAX_NLEN];
268     char out_val[MAX_NLEN + 20];
269     object_type *o_ptr;
270     if (item >= 0)
271         o_ptr = &owner_ptr->inventory_list[item];
272     else
273         o_ptr = &owner_ptr->current_floor_ptr->o_list[0 - item];
274
275     describe_flavor(owner_ptr, o_name, o_ptr, 0);
276     (void)sprintf(out_val, _("%s%sですか? ", "%s %s? "), prompt, o_name);
277     return get_check(out_val);
278 }
279
280 /*!
281  * @brief タグIDにあわせてタグアルファベットのリストを返す /
282  * Move around label characters with correspond tags
283  * @param owner_ptr プレーヤーへの参照ポインタ
284  * @param label ラベルリストを取得する文字列参照ポインタ
285  * @param mode 所持品リストか装備品リストかの切り替え
286  * @return なし
287  */
288 void prepare_label_string(player_type *owner_ptr, char *label, BIT_FLAGS mode, tval_type tval)
289 {
290     concptr alphabet_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
291     int offset = (mode == USE_EQUIP) ? INVEN_RARM : 0;
292     strcpy(label, alphabet_chars);
293     for (int i = 0; i < 52; i++) {
294         COMMAND_CODE index;
295         SYMBOL_CODE c = alphabet_chars[i];
296         if (!get_tag(owner_ptr, &index, c, mode, tval))
297             continue;
298
299         if (label[i] == c)
300             label[i] = ' ';
301
302         label[index - offset] = c;
303     }
304 }