OSDN Git Service

[Refactor] #40399 Separated object-stack.c/h from object2.c/h
[hengband/hengband.git] / src / inventory / inventory-object.c
1 #include "inventory/inventory-object.h"
2 #include "floor/floor-object.h"
3 #include "object/object-flavor.h"
4 #include "object/object-generator.h"
5 #include "object/object-hook.h"
6 #include "object/object-mark-types.h"
7 #include "object/object-stack.h"
8 #include "object/object-value.h"
9 #include "player/player-effects.h" // 暫定、相互参照している.
10 #include "util/object-sort.h"
11 #include "view/object-describer.h"
12
13 void vary_item(player_type *owner_ptr, INVENTORY_IDX item, ITEM_NUMBER num)
14 {
15     if (item >= 0) {
16         inven_item_increase(owner_ptr, item, num);
17         inven_item_describe(owner_ptr, item);
18         inven_item_optimize(owner_ptr, item);
19         return;
20     }
21
22     floor_type *floor_ptr = owner_ptr->current_floor_ptr;
23     floor_item_increase(floor_ptr, 0 - item, num);
24     floor_item_describe(owner_ptr, 0 - item);
25     floor_item_optimize(owner_ptr, 0 - item);
26 }
27
28 /*!
29  * @brief アイテムを増減させ残り所持数メッセージを表示する /
30  * Increase the "number" of an item in the inventory
31  * @param owner_ptr プレーヤーへの参照ポインタ
32  * @param item 所持数を増やしたいプレイヤーのアイテム所持スロット
33  * @param num 増やしたい量
34  * @return なし
35  */
36 void inven_item_increase(player_type *owner_ptr, INVENTORY_IDX item, ITEM_NUMBER num)
37 {
38     object_type *o_ptr = &owner_ptr->inventory_list[item];
39     num += o_ptr->number;
40     if (num > 255)
41         num = 255;
42     else if (num < 0)
43         num = 0;
44
45     num -= o_ptr->number;
46     if (num == 0)
47         return;
48
49     o_ptr->number += num;
50     owner_ptr->total_weight += (num * o_ptr->weight);
51     owner_ptr->update |= (PU_BONUS);
52     owner_ptr->update |= (PU_MANA);
53     owner_ptr->update |= (PU_COMBINE);
54     owner_ptr->window |= (PW_INVEN | PW_EQUIP);
55
56     if (o_ptr->number || !owner_ptr->ele_attack)
57         return;
58     if (!(item == INVEN_RARM) && !(item == INVEN_LARM))
59         return;
60     if (has_melee_weapon(owner_ptr, INVEN_RARM + INVEN_LARM - item))
61         return;
62
63     set_ele_attack(owner_ptr, 0, 0);
64 }
65
66 /*!
67  * @brief 所持アイテムスロットから所持数のなくなったアイテムを消去する /
68  * Erase an inventory slot if it has no more items
69  * @param owner_ptr プレーヤーへの参照ポインタ
70  * @param item 消去したいプレイヤーのアイテム所持スロット
71  * @return なし
72  */
73 void inven_item_optimize(player_type *owner_ptr, INVENTORY_IDX item)
74 {
75     object_type *o_ptr = &owner_ptr->inventory_list[item];
76     if (!o_ptr->k_idx)
77         return;
78     if (o_ptr->number)
79         return;
80
81     if (item >= INVEN_RARM) {
82         owner_ptr->equip_cnt--;
83         object_wipe(&owner_ptr->inventory_list[item]);
84         owner_ptr->update |= PU_BONUS;
85         owner_ptr->update |= PU_TORCH;
86         owner_ptr->update |= PU_MANA;
87
88         owner_ptr->window |= PW_EQUIP;
89         owner_ptr->window |= PW_SPELL;
90         return;
91     }
92
93     owner_ptr->inven_cnt--;
94     int i;
95     for (i = item; i < INVEN_PACK; i++) {
96         owner_ptr->inventory_list[i] = owner_ptr->inventory_list[i + 1];
97     }
98
99     object_wipe(&owner_ptr->inventory_list[i]);
100     owner_ptr->window |= PW_INVEN;
101     owner_ptr->window |= PW_SPELL;
102 }
103
104 /*!
105  * @brief 所持スロットから床下にオブジェクトを落とすメインルーチン /
106  * Drop (some of) a non-cursed inventory/equipment item
107  * @param owner_ptr プレーヤーへの参照ポインタ
108  * @param item 所持テーブルのID
109  * @param amt 落としたい個数
110  * @return なし
111  * @details
112  * The object will be dropped "near" the current location
113  */
114 void drop_from_inventory(player_type *owner_ptr, INVENTORY_IDX item, ITEM_NUMBER amt)
115 {
116     object_type forge;
117     object_type *q_ptr;
118     object_type *o_ptr;
119     GAME_TEXT o_name[MAX_NLEN];
120     o_ptr = &owner_ptr->inventory_list[item];
121     if (amt <= 0)
122         return;
123
124     if (amt > o_ptr->number)
125         amt = o_ptr->number;
126
127     if (item >= INVEN_RARM) {
128         item = inven_takeoff(owner_ptr, item, amt);
129         o_ptr = &owner_ptr->inventory_list[item];
130     }
131
132     q_ptr = &forge;
133     object_copy(q_ptr, o_ptr);
134     distribute_charges(o_ptr, q_ptr, amt);
135
136     q_ptr->number = amt;
137     object_desc(owner_ptr, o_name, q_ptr, 0);
138     msg_format(_("%s(%c)を落とした。", "You drop %s (%c)."), o_name, index_to_label(item));
139     (void)drop_near(owner_ptr, q_ptr, 0, owner_ptr->y, owner_ptr->x);
140     vary_item(owner_ptr, item, -amt);
141 }
142
143 /*!
144  * @brief プレイヤーの所持スロットに存在するオブジェクトをまとめなおす /
145  * Combine items in the pack
146  * @return なし
147  * @details
148  * Note special handling of the "overflow" slot
149  */
150 void combine_pack(player_type *owner_ptr)
151 {
152     bool flag = FALSE;
153     bool is_first_combination = TRUE;
154     bool combined = TRUE;
155     while (is_first_combination || combined) {
156         is_first_combination = FALSE;
157         combined = FALSE;
158
159         for (int i = INVEN_PACK; i > 0; i--) {
160             object_type *o_ptr;
161             o_ptr = &owner_ptr->inventory_list[i];
162             if (!o_ptr->k_idx)
163                 continue;
164             for (int j = 0; j < i; j++) {
165                 object_type *j_ptr;
166                 j_ptr = &owner_ptr->inventory_list[j];
167                 if (!j_ptr->k_idx)
168                     continue;
169
170                 /*
171                  * Get maximum number of the stack if these
172                  * are similar, get zero otherwise.
173                  */
174                 int max_num = object_similar_part(j_ptr, o_ptr);
175
176                 bool is_max = (max_num != 0) && (j_ptr->number < max_num);
177                 if (!is_max)
178                     continue;
179
180                 if (o_ptr->number + j_ptr->number <= max_num) {
181                     flag = TRUE;
182                     object_absorb(j_ptr, o_ptr);
183                     owner_ptr->inven_cnt--;
184                     int k;
185                     for (k = i; k < INVEN_PACK; k++) {
186                         owner_ptr->inventory_list[k] = owner_ptr->inventory_list[k + 1];
187                     }
188
189                     object_wipe(&owner_ptr->inventory_list[k]);
190                 } else {
191                     int old_num = o_ptr->number;
192                     int remain = j_ptr->number + o_ptr->number - max_num;
193                     object_absorb(j_ptr, o_ptr);
194                     o_ptr->number = remain;
195                     if (o_ptr->tval == TV_ROD) {
196                         o_ptr->pval = o_ptr->pval * remain / old_num;
197                         o_ptr->timeout = o_ptr->timeout * remain / old_num;
198                     }
199
200                     if (o_ptr->tval == TV_WAND) {
201                         o_ptr->pval = o_ptr->pval * remain / old_num;
202                     }
203                 }
204
205                 owner_ptr->window |= (PW_INVEN);
206                 combined = TRUE;
207                 break;
208             }
209         }
210     }
211
212     if (flag)
213         msg_print(_("ザックの中のアイテムをまとめ直した。", "You combine some items in your pack."));
214 }
215
216 /*!
217  * @brief プレイヤーの所持スロットに存在するオブジェクトを並び替える /
218  * Reorder items in the pack
219  * @param owner_ptr プレーヤーへの参照ポインタ
220  * @return なし
221  * @details
222  * Note special handling of the "overflow" slot
223  */
224 void reorder_pack(player_type *owner_ptr)
225 {
226     int i, j, k;
227     s32b o_value;
228     object_type forge;
229     object_type *q_ptr;
230     object_type *o_ptr;
231     bool flag = FALSE;
232
233     for (i = 0; i < INVEN_PACK; i++) {
234         if ((i == INVEN_PACK) && (owner_ptr->inven_cnt == INVEN_PACK))
235             break;
236
237         o_ptr = &owner_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(o_ptr, o_value, &owner_ptr->inventory_list[j]))
244                 break;
245         }
246
247         if (j >= i)
248             continue;
249
250         flag = TRUE;
251         q_ptr = &forge;
252         object_copy(q_ptr, &owner_ptr->inventory_list[i]);
253         for (k = i; k > j; k--) {
254             object_copy(&owner_ptr->inventory_list[k], &owner_ptr->inventory_list[k - 1]);
255         }
256
257         object_copy(&owner_ptr->inventory_list[j], q_ptr);
258         owner_ptr->window |= (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 s16b store_item_to_inventory(player_type *owner_ptr, object_type *o_ptr)
286 {
287     INVENTORY_IDX i, j, k;
288     INVENTORY_IDX n = -1;
289
290     object_type *j_ptr;
291     for (j = 0; j < INVEN_PACK; j++) {
292         j_ptr = &owner_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             owner_ptr->total_weight += (o_ptr->number * o_ptr->weight);
301             owner_ptr->update |= (PU_BONUS);
302             owner_ptr->window |= (PW_INVEN);
303             return (j);
304         }
305     }
306
307     if (owner_ptr->inven_cnt > INVEN_PACK)
308         return -1;
309
310     for (j = 0; j <= INVEN_PACK; j++) {
311         j_ptr = &owner_ptr->inventory_list[j];
312         if (!j_ptr->k_idx)
313             break;
314     }
315
316     i = j;
317     if (i < INVEN_PACK) {
318         s32b o_value = object_value(o_ptr);
319         for (j = 0; j < INVEN_PACK; j++) {
320             if (object_sort_comp(o_ptr, o_value, &owner_ptr->inventory_list[j]))
321                 break;
322         }
323
324         i = j;
325         for (k = n; k >= i; k--) {
326             object_copy(&owner_ptr->inventory_list[k + 1], &owner_ptr->inventory_list[k]);
327         }
328
329         object_wipe(&owner_ptr->inventory_list[i]);
330     }
331
332     object_copy(&owner_ptr->inventory_list[i], o_ptr);
333     j_ptr = &owner_ptr->inventory_list[i];
334     j_ptr->next_o_idx = 0;
335     j_ptr->held_m_idx = 0;
336     j_ptr->iy = j_ptr->ix = 0;
337     j_ptr->marked = OM_TOUCHED;
338
339     owner_ptr->total_weight += (j_ptr->number * j_ptr->weight);
340     owner_ptr->inven_cnt++;
341     owner_ptr->update |= (PU_BONUS | PU_COMBINE | PU_REORDER);
342     owner_ptr->window |= (PW_INVEN);
343
344     return i;
345 }
346
347 /*!
348  * todo ここのp_ptrだけは抜けない……関数ポインタの嵐でにっちもさっちもいかない
349  * @brief アイテムを拾う際にザックから溢れずに済むかを判定する /
350  * Check if we have space for an item in the pack without overflow
351  * @param owner_ptr プレーヤーへの参照ポインタ
352  * @param o_ptr 拾いたいオブジェクトの構造体参照ポインタ
353  * @return 溢れずに済むならTRUEを返す
354  */
355 bool check_store_item_to_inventory(object_type *o_ptr)
356 {
357     if (p_ptr->inven_cnt < INVEN_PACK)
358         return TRUE;
359
360     for (int j = 0; j < INVEN_PACK; j++) {
361         object_type *j_ptr = &p_ptr->inventory_list[j];
362         if (!j_ptr->k_idx)
363             continue;
364
365         if (object_similar(j_ptr, o_ptr))
366             return TRUE;
367     }
368
369     return FALSE;
370 }
371
372 /*!
373  * @brief 装備スロットからオブジェクトを外すメインルーチン /
374  * Take off (some of) a non-cursed equipment item
375  * @param owner_ptr プレーヤーへの参照ポインタ
376  * @param item オブジェクトを外したい所持テーブルのID
377  * @param amt 外したい個数
378  * @return 収められた所持スロットのID、拾うことができなかった場合-1を返す。
379  * @details
380  * Note that only one item at a time can be wielded per slot.\n
381  * Note that taking off an item when "full" may cause that item\n
382  * to fall to the ground.\n
383  * Return the inventory slot into which the item is placed.\n
384  */
385 INVENTORY_IDX inven_takeoff(player_type *owner_ptr, INVENTORY_IDX item, ITEM_NUMBER amt)
386 {
387     INVENTORY_IDX slot;
388     object_type forge;
389     object_type *q_ptr;
390     object_type *o_ptr;
391     concptr act;
392     GAME_TEXT o_name[MAX_NLEN];
393     o_ptr = &owner_ptr->inventory_list[item];
394     if (amt <= 0)
395         return -1;
396
397     if (amt > o_ptr->number)
398         amt = o_ptr->number;
399     q_ptr = &forge;
400     object_copy(q_ptr, o_ptr);
401     q_ptr->number = amt;
402     object_desc(owner_ptr, o_name, q_ptr, 0);
403     if (((item == INVEN_RARM) || (item == INVEN_LARM)) && object_is_melee_weapon(o_ptr)) {
404         act = _("を装備からはずした", "You were wielding");
405     } else if (item == INVEN_BOW) {
406         act = _("を装備からはずした", "You were holding");
407     } else if (item == INVEN_LITE) {
408         act = _("を光源からはずした", "You were holding");
409     } else {
410         act = _("を装備からはずした", "You were wearing");
411     }
412
413     inven_item_increase(owner_ptr, item, -amt);
414     inven_item_optimize(owner_ptr, item);
415
416     slot = store_item_to_inventory(owner_ptr, q_ptr);
417 #ifdef JP
418     msg_format("%s(%c)%s。", o_name, index_to_label(slot), act);
419 #else
420     msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
421 #endif
422
423     return slot;
424 }