OSDN Git Service

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