OSDN Git Service

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