OSDN Git Service

[Refactor] ボール系魔法のコピペを低減
[hengbandforosx/hengbandosx.git] / src / inventory / inventory-object.cpp
1 #include "inventory/inventory-object.h"
2 #include "core/player-update-types.h"
3 #include "core/window-redrawer.h"
4 #include "flavor/flavor-describer.h"
5 #include "floor/floor-object.h"
6 #include "inventory/inventory-slot-types.h"
7 #include "object-hook/hook-weapon.h"
8 #include "object/object-info.h"
9 #include "object/object-mark-types.h"
10 #include "object/object-stack.h"
11 #include "object/object-value.h"
12 #include "player-info/equipment-info.h"
13 #include "spell-realm/spells-craft.h"
14 #include "system/object-type-definition.h"
15 #include "system/player-type-definition.h"
16 #include "util/object-sort.h"
17 #include "view/display-messages.h"
18 #include "view/object-describer.h"
19
20 void vary_item(PlayerType *player_ptr, INVENTORY_IDX item, ITEM_NUMBER num)
21 {
22     if (item >= 0) {
23         inven_item_increase(player_ptr, item, num);
24         inven_item_describe(player_ptr, item);
25         inven_item_optimize(player_ptr, item);
26         return;
27     }
28
29     floor_item_increase(player_ptr, 0 - item, num);
30     floor_item_describe(player_ptr, 0 - item);
31     floor_item_optimize(player_ptr, 0 - item);
32 }
33
34 /*!
35  * @brief アイテムを増減させ残り所持数メッセージを表示する /
36  * Increase the "number" of an item in the inventory
37  * @param player_ptr プレイヤーへの参照ポインタ
38  * @param item 所持数を増やしたいプレイヤーのアイテム所持スロット
39  * @param num 増やしたい量
40  */
41 void inven_item_increase(PlayerType *player_ptr, INVENTORY_IDX item, ITEM_NUMBER num)
42 {
43     auto *o_ptr = &player_ptr->inventory_list[item];
44     num += o_ptr->number;
45     if (num > 255) {
46         num = 255;
47     } else if (num < 0) {
48         num = 0;
49     }
50
51     num -= o_ptr->number;
52     if (num == 0) {
53         return;
54     }
55
56     o_ptr->number += num;
57     player_ptr->update |= (PU_BONUS);
58     player_ptr->update |= (PU_MANA);
59     player_ptr->update |= (PU_COMBINE);
60     player_ptr->window_flags |= (PW_INVEN | PW_EQUIP);
61
62     if (o_ptr->number || !player_ptr->ele_attack) {
63         return;
64     }
65     if (!(item == INVEN_MAIN_HAND) && !(item == INVEN_SUB_HAND)) {
66         return;
67     }
68     if (has_melee_weapon(player_ptr, INVEN_MAIN_HAND + INVEN_SUB_HAND - item)) {
69         return;
70     }
71
72     set_ele_attack(player_ptr, 0, 0);
73 }
74
75 /*!
76  * @brief 所持アイテムスロットから所持数のなくなったアイテムを消去する /
77  * Erase an inventory slot if it has no more items
78  * @param player_ptr プレイヤーへの参照ポインタ
79  * @param item 消去したいプレイヤーのアイテム所持スロット
80  */
81 void inven_item_optimize(PlayerType *player_ptr, INVENTORY_IDX item)
82 {
83     auto *o_ptr = &player_ptr->inventory_list[item];
84     if (!o_ptr->k_idx) {
85         return;
86     }
87     if (o_ptr->number) {
88         return;
89     }
90
91     if (item >= INVEN_MAIN_HAND) {
92         player_ptr->equip_cnt--;
93         (&player_ptr->inventory_list[item])->wipe();
94         player_ptr->update |= PU_BONUS;
95         player_ptr->update |= PU_TORCH;
96         player_ptr->update |= PU_MANA;
97
98         player_ptr->window_flags |= PW_EQUIP;
99         player_ptr->window_flags |= PW_SPELL;
100         return;
101     }
102
103     player_ptr->inven_cnt--;
104     int i;
105     for (i = item; i < INVEN_PACK; i++) {
106         player_ptr->inventory_list[i] = player_ptr->inventory_list[i + 1];
107     }
108
109     (&player_ptr->inventory_list[i])->wipe();
110     player_ptr->window_flags |= PW_INVEN;
111     player_ptr->window_flags |= PW_SPELL;
112 }
113
114 /*!
115  * @brief 所持スロットから床下にオブジェクトを落とすメインルーチン /
116  * Drop (some of) a non-cursed inventory/equipment item
117  * @param player_ptr プレイヤーへの参照ポインタ
118  * @param item 所持テーブルのID
119  * @param amt 落としたい個数
120  * @details
121  * The object will be dropped "near" the current location
122  */
123 void drop_from_inventory(PlayerType *player_ptr, INVENTORY_IDX item, ITEM_NUMBER amt)
124 {
125     ObjectType forge;
126     ObjectType *q_ptr;
127     ObjectType *o_ptr;
128     GAME_TEXT o_name[MAX_NLEN];
129     o_ptr = &player_ptr->inventory_list[item];
130     if (amt <= 0) {
131         return;
132     }
133
134     if (amt > o_ptr->number) {
135         amt = o_ptr->number;
136     }
137
138     if (item >= INVEN_MAIN_HAND) {
139         item = inven_takeoff(player_ptr, item, amt);
140         o_ptr = &player_ptr->inventory_list[item];
141     }
142
143     q_ptr = &forge;
144     q_ptr->copy_from(o_ptr);
145     distribute_charges(o_ptr, q_ptr, amt);
146
147     q_ptr->number = amt;
148     describe_flavor(player_ptr, o_name, q_ptr, 0);
149     msg_format(_("%s(%c)を落とした。", "You drop %s (%c)."), o_name, index_to_label(item));
150     (void)drop_near(player_ptr, q_ptr, 0, player_ptr->y, player_ptr->x);
151     vary_item(player_ptr, item, -amt);
152 }
153
154 /*!
155  * @brief プレイヤーの所持スロットに存在するオブジェクトをまとめなおす /
156  * Combine items in the pack
157  * @details
158  * Note special handling of the "overflow" slot
159  */
160 void combine_pack(PlayerType *player_ptr)
161 {
162     bool flag = false;
163     bool is_first_combination = true;
164     bool combined = true;
165     while (is_first_combination || combined) {
166         is_first_combination = false;
167         combined = false;
168
169         for (int i = INVEN_PACK; i > 0; i--) {
170             ObjectType *o_ptr;
171             o_ptr = &player_ptr->inventory_list[i];
172             if (!o_ptr->k_idx) {
173                 continue;
174             }
175             for (int j = 0; j < i; j++) {
176                 ObjectType *j_ptr;
177                 j_ptr = &player_ptr->inventory_list[j];
178                 if (!j_ptr->k_idx) {
179                     continue;
180                 }
181
182                 /*
183                  * Get maximum number of the stack if these
184                  * are similar, get zero otherwise.
185                  */
186                 int max_num = object_similar_part(j_ptr, o_ptr);
187
188                 bool is_max = (max_num != 0) && (j_ptr->number < max_num);
189                 if (!is_max) {
190                     continue;
191                 }
192
193                 if (o_ptr->number + j_ptr->number <= max_num) {
194                     flag = true;
195                     object_absorb(j_ptr, o_ptr);
196                     player_ptr->inven_cnt--;
197                     int k;
198                     for (k = i; k < INVEN_PACK; k++) {
199                         player_ptr->inventory_list[k] = player_ptr->inventory_list[k + 1];
200                     }
201
202                     (&player_ptr->inventory_list[k])->wipe();
203                 } else {
204                     int old_num = o_ptr->number;
205                     int remain = j_ptr->number + o_ptr->number - max_num;
206                     object_absorb(j_ptr, o_ptr);
207                     o_ptr->number = remain;
208                     if (o_ptr->tval == ItemKindType::ROD) {
209                         o_ptr->pval = o_ptr->pval * remain / old_num;
210                         o_ptr->timeout = o_ptr->timeout * remain / old_num;
211                     }
212
213                     if (o_ptr->tval == ItemKindType::WAND) {
214                         o_ptr->pval = o_ptr->pval * remain / old_num;
215                     }
216                 }
217
218                 player_ptr->window_flags |= (PW_INVEN);
219                 combined = true;
220                 break;
221             }
222         }
223     }
224
225     if (flag) {
226         msg_print(_("ザックの中のアイテムをまとめ直した。", "You combine some items in your pack."));
227     }
228 }
229
230 /*!
231  * @brief プレイヤーの所持スロットに存在するオブジェクトを並び替える /
232  * Reorder items in the pack
233  * @param player_ptr プレイヤーへの参照ポインタ
234  * @details
235  * Note special handling of the "overflow" slot
236  */
237 void reorder_pack(PlayerType *player_ptr)
238 {
239     int i, j, k;
240     int32_t o_value;
241     ObjectType forge;
242     ObjectType *q_ptr;
243     ObjectType *o_ptr;
244     bool flag = false;
245
246     for (i = 0; i < INVEN_PACK; i++) {
247         if ((i == INVEN_PACK) && (player_ptr->inven_cnt == INVEN_PACK)) {
248             break;
249         }
250
251         o_ptr = &player_ptr->inventory_list[i];
252         if (!o_ptr->k_idx) {
253             continue;
254         }
255
256         o_value = object_value(o_ptr);
257         for (j = 0; j < INVEN_PACK; j++) {
258             if (object_sort_comp(player_ptr, o_ptr, o_value, &player_ptr->inventory_list[j])) {
259                 break;
260             }
261         }
262
263         if (j >= i) {
264             continue;
265         }
266
267         flag = true;
268         q_ptr = &forge;
269         q_ptr->copy_from(&player_ptr->inventory_list[i]);
270         for (k = i; k > j; k--) {
271             (&player_ptr->inventory_list[k])->copy_from(&player_ptr->inventory_list[k - 1]);
272         }
273
274         (&player_ptr->inventory_list[j])->copy_from(q_ptr);
275         player_ptr->window_flags |= (PW_INVEN);
276     }
277
278     if (flag) {
279         msg_print(_("ザックの中のアイテムを並べ直した。", "You reorder some items in your pack."));
280     }
281 }
282
283 /*!
284  * @brief オブジェクトをプレイヤーが拾って所持スロットに納めるメインルーチン /
285  * Add an item to the players inventory, and return the slot used.
286  * @param o_ptr 拾うオブジェクトの構造体参照ポインタ
287  * @return 収められた所持スロットのID、拾うことができなかった場合-1を返す。
288  * @details
289  * If the new item can combine with an existing item in the inventory,\n
290  * it will do so, using "object_similar()" and "object_absorb()", else,\n
291  * the item will be placed into the "proper" location in the inventory.\n
292  *\n
293  * This function can be used to "over-fill" the player's pack, but only\n
294  * once, and such an action must trigger the "overflow" code immediately.\n
295  * Note that when the pack is being "over-filled", the new item must be\n
296  * placed into the "overflow" slot, and the "overflow" must take place\n
297  * before the pack is reordered, but (optionally) after the pack is\n
298  * combined.  This may be tricky.  See "dungeon.c" for info.\n
299  *\n
300  * Note that this code must remove any location/stack information\n
301  * from the object once it is placed into the inventory.\n
302  */
303 int16_t store_item_to_inventory(PlayerType *player_ptr, ObjectType *o_ptr)
304 {
305     INVENTORY_IDX i, j, k;
306     INVENTORY_IDX n = -1;
307
308     ObjectType *j_ptr;
309     for (j = 0; j < INVEN_PACK; j++) {
310         j_ptr = &player_ptr->inventory_list[j];
311         if (!j_ptr->k_idx) {
312             continue;
313         }
314
315         n = j;
316         if (object_similar(j_ptr, o_ptr)) {
317             object_absorb(j_ptr, o_ptr);
318
319             player_ptr->update |= (PU_BONUS);
320             player_ptr->window_flags |= (PW_INVEN | PW_PLAYER);
321             return j;
322         }
323     }
324
325     if (player_ptr->inven_cnt > INVEN_PACK) {
326         return -1;
327     }
328
329     for (j = 0; j <= INVEN_PACK; j++) {
330         j_ptr = &player_ptr->inventory_list[j];
331         if (!j_ptr->k_idx) {
332             break;
333         }
334     }
335
336     i = j;
337     if (i < INVEN_PACK) {
338         int32_t o_value = object_value(o_ptr);
339         for (j = 0; j < INVEN_PACK; j++) {
340             if (object_sort_comp(player_ptr, o_ptr, o_value, &player_ptr->inventory_list[j])) {
341                 break;
342             }
343         }
344
345         i = j;
346         for (k = n; k >= i; k--) {
347             (&player_ptr->inventory_list[k + 1])->copy_from(&player_ptr->inventory_list[k]);
348         }
349
350         (&player_ptr->inventory_list[i])->wipe();
351     }
352
353     (&player_ptr->inventory_list[i])->copy_from(o_ptr);
354     j_ptr = &player_ptr->inventory_list[i];
355     j_ptr->held_m_idx = 0;
356     j_ptr->iy = j_ptr->ix = 0;
357     j_ptr->marked = OM_TOUCHED;
358
359     player_ptr->inven_cnt++;
360     player_ptr->update |= (PU_BONUS | PU_COMBINE | PU_REORDER);
361     player_ptr->window_flags |= (PW_INVEN | PW_PLAYER);
362
363     return i;
364 }
365
366 /*!
367  * @brief アイテムを拾う際にザックから溢れずに済むかを判定する /
368  * Check if we have space for an item in the pack without overflow
369  * @param player_ptr プレイヤーへの参照ポインタ
370  * @param o_ptr 拾いたいオブジェクトの構造体参照ポインタ
371  * @return 溢れずに済むならTRUEを返す
372  */
373 bool check_store_item_to_inventory(PlayerType *player_ptr, const ObjectType *o_ptr)
374 {
375     if (player_ptr->inven_cnt < INVEN_PACK) {
376         return true;
377     }
378
379     for (int j = 0; j < INVEN_PACK; j++) {
380         auto *j_ptr = &player_ptr->inventory_list[j];
381         if (!j_ptr->k_idx) {
382             continue;
383         }
384
385         if (object_similar(j_ptr, o_ptr)) {
386             return true;
387         }
388     }
389
390     return false;
391 }
392
393 /*!
394  * @brief 装備スロットからオブジェクトを外すメインルーチン /
395  * Take off (some of) a non-cursed equipment item
396  * @param player_ptr プレイヤーへの参照ポインタ
397  * @param item オブジェクトを外したい所持テーブルのID
398  * @param amt 外したい個数
399  * @return 収められた所持スロットのID、拾うことができなかった場合-1を返す。
400  * @details
401  * Note that only one item at a time can be wielded per slot.\n
402  * Note that taking off an item when "full" may cause that item\n
403  * to fall to the ground.\n
404  * Return the inventory slot into which the item is placed.\n
405  */
406 INVENTORY_IDX inven_takeoff(PlayerType *player_ptr, INVENTORY_IDX item, ITEM_NUMBER amt)
407 {
408     INVENTORY_IDX slot;
409     ObjectType forge;
410     ObjectType *q_ptr;
411     ObjectType *o_ptr;
412     concptr act;
413     GAME_TEXT o_name[MAX_NLEN];
414     o_ptr = &player_ptr->inventory_list[item];
415     if (amt <= 0) {
416         return -1;
417     }
418
419     if (amt > o_ptr->number) {
420         amt = o_ptr->number;
421     }
422     q_ptr = &forge;
423     q_ptr->copy_from(o_ptr);
424     q_ptr->number = amt;
425     describe_flavor(player_ptr, o_name, q_ptr, 0);
426     if (((item == INVEN_MAIN_HAND) || (item == INVEN_SUB_HAND)) && o_ptr->is_melee_weapon()) {
427         act = _("を装備からはずした", "You were wielding");
428     } else if (item == INVEN_BOW) {
429         act = _("を装備からはずした", "You were holding");
430     } else if (item == INVEN_LITE) {
431         act = _("を光源からはずした", "You were holding");
432     } else {
433         act = _("を装備からはずした", "You were wearing");
434     }
435
436     inven_item_increase(player_ptr, item, -amt);
437     inven_item_optimize(player_ptr, item);
438
439     slot = store_item_to_inventory(player_ptr, q_ptr);
440 #ifdef JP
441     msg_format("%s(%c)%s。", o_name, index_to_label(slot), act);
442 #else
443     msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
444 #endif
445
446     return slot;
447 }