OSDN Git Service

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