OSDN Git Service

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