OSDN Git Service

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