OSDN Git Service

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