OSDN Git Service

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