OSDN Git Service

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