OSDN Git Service

[Refactor] #3133 Reshaped object_similar_part()
[hengbandforosx/hengbandosx.git] / src / object / object-stack.cpp
1 /*!
2  * @brief 同種のアイテムをインベントリや床に重ね合わせたり、その判断を行う処理
3  * @date 2020/06/03
4  * @author Hourier
5  */
6
7 #include "object/object-stack.h"
8 #include "game-option/game-play-options.h"
9 #include "object-enchant/object-ego.h"
10 #include "object-enchant/special-object-flags.h"
11 #include "object-enchant/trc-types.h"
12 #include "object/object-value.h"
13 #include "object/tval-types.h"
14 #include "perception/object-perception.h"
15 #include "smith/object-smith.h"
16 #include "sv-definition/sv-other-types.h"
17 #include "system/baseitem-info.h"
18 #include "system/item-entity.h"
19 #include "util/bit-flags-calculator.h"
20
21 /*!
22  * @brief 魔法棒やロッドのスロット分割時に使用回数を分配する /
23  * Distribute charges of rods or wands.
24  * @param o_ptr 分割元オブジェクトの構造体参照ポインタ source item
25  * @param q_ptr 分割先オブジェクトの構造体参照ポインタ target item, must be of the same type as o_ptr
26  * @param amt 分割したい回数量 number of items that are transfered
27  * @details
28  * Hack -- If rods or wands are dropped, the total maximum timeout or\n
29  * charges need to be allocated between the two stacks.  If all the items\n
30  * are being dropped, it makes for a neater message to leave the original\n
31  * stack's pval alone. -LM-\n
32  */
33 void distribute_charges(ItemEntity *o_ptr, ItemEntity *q_ptr, int amt)
34 {
35     if (!o_ptr->is_wand_rod()) {
36         return;
37     }
38
39     q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
40     if (amt < o_ptr->number) {
41         o_ptr->pval -= q_ptr->pval;
42     }
43
44     if ((o_ptr->bi_key.tval() != ItemKindType::ROD) || !o_ptr->timeout) {
45         return;
46     }
47
48     if (q_ptr->pval > o_ptr->timeout) {
49         q_ptr->timeout = o_ptr->timeout;
50     } else {
51         q_ptr->timeout = q_ptr->pval;
52     }
53
54     if (amt < o_ptr->number) {
55         o_ptr->timeout -= q_ptr->timeout;
56     }
57 }
58
59 /*!
60  * @brief 魔法棒やロッドの使用回数を減らす /
61  * @param o_ptr オブジェクトの構造体参照ポインタ source item
62  * @param amt 減らしたい回数量 number of items that are transfered
63  * @details
64  * Hack -- If rods or wand are destroyed, the total maximum timeout or\n
65  * charges of the stack needs to be reduced, unless all the items are\n
66  * being destroyed. -LM-\n
67  */
68 void reduce_charges(ItemEntity *o_ptr, int amt)
69 {
70     if (o_ptr->is_wand_rod() && (amt < o_ptr->number)) {
71         o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
72     }
73 }
74
75 /*!
76  * @brief 両オブジェクトをスロットに重ね合わせ可能な最大数を返す。
77  * Determine if an item can partly absorb a second item. Return maximum number of stack.
78  * @param o_ptr 検証したいオブジェクトの構造体参照ポインタ1
79  * @param j_ptr 検証したいオブジェクトの構造体参照ポインタ2
80  * @return 重ね合わせ可能なアイテム数
81  */
82 int object_similar_part(const ItemEntity *o_ptr, const ItemEntity *j_ptr)
83 {
84     if (o_ptr->bi_id != j_ptr->bi_id) {
85         return 0;
86     }
87
88     constexpr auto max_stack_size = 99;
89     auto max_num = max_stack_size;
90     switch (o_ptr->bi_key.tval()) {
91     case ItemKindType::CHEST:
92     case ItemKindType::CARD:
93     case ItemKindType::CAPTURE:
94         return 0;
95     case ItemKindType::STATUE: {
96         const auto o_sval = o_ptr->bi_key.sval();
97         const auto j_sval = j_ptr->bi_key.sval();
98         if ((o_sval != SV_PHOTO) || (j_sval != SV_PHOTO) || (o_ptr->pval != j_ptr->pval)) {
99             return 0;
100         }
101
102         break;
103     }
104     case ItemKindType::FIGURINE:
105     case ItemKindType::CORPSE:
106         if (o_ptr->pval != j_ptr->pval) {
107             return 0;
108         }
109
110         break;
111     case ItemKindType::FOOD:
112     case ItemKindType::POTION:
113     case ItemKindType::SCROLL:
114         break;
115     case ItemKindType::STAFF:
116         if ((none_bits(o_ptr->ident, IDENT_EMPTY) && !o_ptr->is_known()) || (none_bits(j_ptr->ident, IDENT_EMPTY) && !j_ptr->is_known())) {
117             return 0;
118         }
119
120         if (o_ptr->pval != j_ptr->pval) {
121             return 0;
122         }
123
124         break;
125     case ItemKindType::WAND:
126         if ((!(o_ptr->ident & (IDENT_EMPTY)) && !o_ptr->is_known()) || (!(j_ptr->ident & (IDENT_EMPTY)) && !j_ptr->is_known())) {
127             return 0;
128         }
129
130         break;
131     case ItemKindType::ROD:
132         max_num = std::min(max_num, MAX_SHORT / baseitems_info[o_ptr->bi_id].pval);
133         break;
134     case ItemKindType::GLOVES:
135         if (o_ptr->is_glove_same_temper(j_ptr)) {
136             return 0;
137         }
138
139         if (!o_ptr->is_known() || !j_ptr->is_known()) {
140             return 0;
141         }
142
143         if (!o_ptr->can_pile(j_ptr)) {
144             return 0;
145         }
146
147         break;
148     case ItemKindType::LITE:
149         if (o_ptr->fuel != j_ptr->fuel) {
150             return 0;
151         }
152
153         if (!o_ptr->can_pile(j_ptr)) {
154             return 0;
155         }
156
157         break;
158     case ItemKindType::BOW:
159     case ItemKindType::DIGGING:
160     case ItemKindType::HAFTED:
161     case ItemKindType::POLEARM:
162     case ItemKindType::SWORD:
163     case ItemKindType::BOOTS:
164     case ItemKindType::HELM:
165     case ItemKindType::CROWN:
166     case ItemKindType::SHIELD:
167     case ItemKindType::CLOAK:
168     case ItemKindType::SOFT_ARMOR:
169     case ItemKindType::HARD_ARMOR:
170     case ItemKindType::DRAG_ARMOR:
171     case ItemKindType::RING:
172     case ItemKindType::AMULET:
173     case ItemKindType::WHISTLE:
174         if (!o_ptr->is_known() || !j_ptr->is_known()) {
175             return 0;
176         }
177
178         if (!o_ptr->can_pile(j_ptr)) {
179             return 0;
180         }
181
182         break;
183     case ItemKindType::BOLT:
184     case ItemKindType::ARROW:
185     case ItemKindType::SHOT:
186         if (!o_ptr->can_pile(j_ptr)) {
187             return 0;
188         }
189
190         break;
191     default:
192         if (!o_ptr->is_known() || !j_ptr->is_known()) {
193             return 0;
194         }
195
196         break;
197     }
198
199     if (o_ptr->art_flags != j_ptr->art_flags) {
200         return 0;
201     }
202
203     if (o_ptr->curse_flags != j_ptr->curse_flags) {
204         return 0;
205     }
206
207     if (any_bits(o_ptr->ident, IDENT_BROKEN) != any_bits(j_ptr->ident, IDENT_BROKEN)) {
208         return 0;
209     }
210
211     if (o_ptr->is_inscribed() && j_ptr->is_inscribed() && (o_ptr->inscription != j_ptr->inscription)) {
212         return 0;
213     }
214
215     if (!stack_force_notes && (o_ptr->inscription != j_ptr->inscription)) {
216         return 0;
217     }
218
219     if (!stack_force_costs && (o_ptr->discount != j_ptr->discount)) {
220         return 0;
221     }
222
223     return max_num;
224 }
225
226 /*!
227  * @brief 両オブジェクトをスロットに重ねることができるかどうかを返す。
228  * Determine if an item can absorb a second item.
229  * @param o_ptr 検証したいオブジェクトの構造体参照ポインタ1
230  * @param j_ptr 検証したいオブジェクトの構造体参照ポインタ2
231  * @return 重ね合わせ可能ならばTRUEを返す。
232  */
233 bool object_similar(const ItemEntity *o_ptr, const ItemEntity *j_ptr)
234 {
235     int total = o_ptr->number + j_ptr->number;
236     int max_num = object_similar_part(o_ptr, j_ptr);
237     if (!max_num) {
238         return false;
239     }
240     if (total > max_num) {
241         return 0;
242     }
243
244     return true;
245 }
246
247 /*!
248  * @brief 両オブジェクトをスロットに重ね合わせる。
249  * Allow one item to "absorb" another, assuming they are similar
250  * @param o_ptr 重ね合わせ先のオブジェクトの構造体参照ポインタ
251  * @param j_ptr 重ね合わせ元のオブジェクトの構造体参照ポインタ
252  */
253 void object_absorb(ItemEntity *o_ptr, ItemEntity *j_ptr)
254 {
255     int max_num = object_similar_part(o_ptr, j_ptr);
256     int total = o_ptr->number + j_ptr->number;
257     int diff = (total > max_num) ? total - max_num : 0;
258
259     o_ptr->number = (total > max_num) ? max_num : total;
260     if (j_ptr->is_known()) {
261         object_known(o_ptr);
262     }
263
264     if (((o_ptr->ident & IDENT_STORE) || (j_ptr->ident & IDENT_STORE)) && (!((o_ptr->ident & IDENT_STORE) && (j_ptr->ident & IDENT_STORE)))) {
265         if (j_ptr->ident & IDENT_STORE) {
266             j_ptr->ident &= 0xEF;
267         }
268         if (o_ptr->ident & IDENT_STORE) {
269             o_ptr->ident &= 0xEF;
270         }
271     }
272
273     if (j_ptr->is_fully_known()) {
274         o_ptr->ident |= (IDENT_FULL_KNOWN);
275     }
276     if (j_ptr->is_inscribed()) {
277         o_ptr->inscription = j_ptr->inscription;
278     }
279     if (j_ptr->feeling) {
280         o_ptr->feeling = j_ptr->feeling;
281     }
282     if (o_ptr->discount < j_ptr->discount) {
283         o_ptr->discount = j_ptr->discount;
284     }
285
286     const auto tval = o_ptr->bi_key.tval();
287     if (tval == ItemKindType::ROD) {
288         o_ptr->pval += j_ptr->pval * (j_ptr->number - diff) / j_ptr->number;
289         o_ptr->timeout += j_ptr->timeout * (j_ptr->number - diff) / j_ptr->number;
290     }
291
292     if (tval == ItemKindType::WAND) {
293         o_ptr->pval += j_ptr->pval * (j_ptr->number - diff) / j_ptr->number;
294     }
295 }