OSDN Git Service

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