OSDN Git Service

f4df5295fe891c717897b639de6abac2167024f8
[hengbandforosx/hengbandosx.git] / src / store / store-util.cpp
1 /*!
2  * @brief 店舗処理関係のユーティリティ
3  * @date 2020/03/20
4  * @author Hourier
5  */
6
7 #include "store/store-util.h"
8 #include "object-enchant/apply-magic.h"
9 #include "object-enchant/item-apply-magic.h"
10 #include "object-enchant/item-feeling.h"
11 #include "object-enchant/special-object-flags.h"
12 #include "object-hook/hook-enchant.h"
13 #include "object/object-kind.h"
14 #include "object/object-value.h"
15 #include "perception/object-perception.h"
16 #include "sv-definition/sv-lite-types.h"
17 #include "sv-definition/sv-scroll-types.h"
18 #include "system/object-type-definition.h"
19 #include "world/world-object.h"
20
21 int cur_store_num = 0;
22 store_type *st_ptr = NULL;
23
24 /*!
25  * @brief 店舗のオブジェクト数を増やす /
26  * Add the item "o_ptr" to a real stores inventory.
27  * @param item 増やしたいアイテムのID
28  * @param num 増やしたい数
29  * @details
30  * <pre>
31  * Increase, by a given amount, the number of a certain item
32  * in a certain store.  This can result in zero items.
33  * </pre>
34  */
35 void store_item_increase(INVENTORY_IDX item, ITEM_NUMBER num)
36 {
37     object_type *o_ptr;
38     o_ptr = &st_ptr->stock[item];
39     int cnt = o_ptr->number + num;
40     if (cnt > 255)
41         cnt = 255;
42     else if (cnt < 0)
43         cnt = 0;
44
45     num = cnt - o_ptr->number;
46     o_ptr->number += num;
47 }
48
49 /*!
50  * @brief 店舗のオブジェクト数を削除する /
51  * Remove a slot if it is empty
52  * @param item 削除したいアイテムのID
53  */
54 void store_item_optimize(INVENTORY_IDX item)
55 {
56     object_type *o_ptr;
57     o_ptr = &st_ptr->stock[item];
58     if ((o_ptr->k_idx == 0) || (o_ptr->number != 0))
59         return;
60
61     st_ptr->stock_num--;
62     for (int j = item; j < st_ptr->stock_num; j++)
63         st_ptr->stock[j] = st_ptr->stock[j + 1];
64
65     (&st_ptr->stock[st_ptr->stock_num])->object_wipe();
66 }
67
68 /*!
69  * @brief 店舗の品揃え変化のためにアイテムを削除する /
70  * Attempt to delete (some of) a random item from the store
71  * @details
72  * <pre>
73  * Hack -- we attempt to "maintain" piles of items when possible.
74  * </pre>
75  */
76 void store_delete(void)
77 {
78     INVENTORY_IDX what = (INVENTORY_IDX)randint0(st_ptr->stock_num);
79     int num = st_ptr->stock[what].number;
80     if (randint0(100) < 50)
81         num = (num + 1) / 2;
82
83     if (randint0(100) < 50)
84         num = 1;
85
86     if ((st_ptr->stock[what].tval == TV_ROD) || (st_ptr->stock[what].tval == TV_WAND))
87         st_ptr->stock[what].pval -= num * st_ptr->stock[what].pval / st_ptr->stock[what].number;
88     
89     store_item_increase(what, -num);
90     store_item_optimize(what);
91 }
92
93 /*!
94  * @brief 店舗販売中の杖と魔法棒のpvalのリストを返す
95  * @param j_ptr これから売ろうとしているオブジェクト
96  * @return plavリスト(充填数)
97  * @details
98  * 回数の違う杖と魔法棒がスロットを圧迫するのでスロット数制限をかける
99  */
100 static std::vector<PARAMETER_VALUE> store_same_magic_device_pvals(object_type *j_ptr)
101 {
102     auto list = std::vector<PARAMETER_VALUE>();
103     for (INVENTORY_IDX i = 0; i < st_ptr->stock_num; i++) {
104         object_type *o_ptr = &st_ptr->stock[i];
105         if (o_ptr == j_ptr)
106             continue;
107         if (o_ptr->k_idx != j_ptr->k_idx)
108             continue;
109         if (o_ptr->tval != TV_STAFF && o_ptr->tval != TV_WAND)
110             continue;
111         list.push_back(o_ptr->pval);
112     }
113     return list;
114 }
115
116 /*!
117  * @brief 店舗の品揃え変化のためにアイテムを追加する /
118  * Creates a random item and gives it to a store
119  * @param player_ptr プレーヤーへの参照ポインタ
120  * @details
121  * <pre>
122  * This algorithm needs to be rethought.  A lot.
123  * Currently, "normal" stores use a pre-built array.
124  * Note -- the "level" given to "obj_get_num()" is a "favored"
125  * level, that is, there is a much higher chance of getting
126  * items with a level approaching that of the given level...
127  * Should we check for "permission" to have the given item?
128  * </pre>
129  */
130 void store_create(
131     player_type *player_ptr, KIND_OBJECT_IDX fix_k_idx, black_market_crap_pf black_market_crap, store_will_buy_pf store_will_buy, mass_produce_pf mass_produce)
132 {
133     if (st_ptr->stock_num >= st_ptr->stock_size)
134         return;
135
136     for (int tries = 0; tries < 4; tries++) {
137         KIND_OBJECT_IDX k_idx;
138         DEPTH level;
139         if (cur_store_num == STORE_BLACK) {
140             level = 25 + randint0(25);
141             k_idx = get_obj_num(player_ptr, level, 0x00000000);
142             if (k_idx == 0)
143                 continue;
144         } else if (fix_k_idx > 0) {
145             k_idx = fix_k_idx;
146             level = rand_range(1, STORE_OBJ_LEVEL);
147         } else {
148             k_idx = st_ptr->table[randint0(st_ptr->table.size())];
149             level = rand_range(1, STORE_OBJ_LEVEL);
150         }
151
152         object_type forge;
153         object_type *q_ptr;
154         q_ptr = &forge;
155         q_ptr->object_prep(player_ptr, k_idx);
156         apply_magic_to_object(player_ptr, q_ptr, level, AM_NO_FIXED_ART);
157         if (!(*store_will_buy)(player_ptr, q_ptr))
158             continue;
159
160         auto pvals = store_same_magic_device_pvals(q_ptr);
161         if (pvals.size() >= 2) {
162             auto pval = pvals.at(randint0(pvals.size()));
163             q_ptr->pval = pval;
164         }
165
166         if (q_ptr->tval == TV_LITE) {
167             if (q_ptr->sval == SV_LITE_TORCH)
168                 q_ptr->xtra4 = FUEL_TORCH / 2;
169
170             if (q_ptr->sval == SV_LITE_LANTERN)
171                 q_ptr->xtra4 = FUEL_LAMP / 2;
172         }
173
174         object_known(q_ptr);
175         q_ptr->ident |= IDENT_STORE;
176         if (q_ptr->tval == TV_CHEST)
177             continue;
178
179         if (cur_store_num == STORE_BLACK) {
180             if (black_market_crap(player_ptr, q_ptr) || (object_value(player_ptr, q_ptr) < 10))
181                 continue;
182         } else {
183             if (object_value(player_ptr, q_ptr) <= 0)
184                 continue;
185         }
186
187         mass_produce(player_ptr, q_ptr);
188         (void)store_carry(player_ptr, q_ptr);
189         break;
190     }
191 }
192
193 /*!
194  * @brief 店舗に並べた品を同一品であるかどうか判定する /
195  * Determine if a store item can "absorb" another item
196  * @param o_ptr 判定するオブジェクト構造体の参照ポインタ1
197  * @param j_ptr 判定するオブジェクト構造体の参照ポインタ2
198  * @return 同一扱いできるならTRUEを返す
199  * @details
200  * <pre>
201  * See "object_similar()" for the same function for the "player"
202  * </pre>
203  */
204 bool store_object_similar(object_type *o_ptr, object_type *j_ptr)
205 {
206     if (o_ptr == j_ptr)
207         return FALSE;
208
209     if (o_ptr->k_idx != j_ptr->k_idx)
210         return FALSE;
211
212     if ((o_ptr->pval != j_ptr->pval) && (o_ptr->tval != TV_WAND) && (o_ptr->tval != TV_ROD))
213         return FALSE;
214
215     if (o_ptr->to_h != j_ptr->to_h)
216         return FALSE;
217
218     if (o_ptr->to_d != j_ptr->to_d)
219         return FALSE;
220
221     if (o_ptr->to_a != j_ptr->to_a)
222         return FALSE;
223
224     if (o_ptr->name2 != j_ptr->name2)
225         return FALSE;
226
227     if (object_is_artifact(o_ptr) || object_is_artifact(j_ptr))
228         return FALSE;
229
230     for (int i = 0; i < TR_FLAG_SIZE; i++)
231         if (o_ptr->art_flags[i] != j_ptr->art_flags[i])
232             return FALSE;
233
234     if (o_ptr->xtra1 || j_ptr->xtra1)
235         return FALSE;
236
237     if (o_ptr->timeout || j_ptr->timeout)
238         return FALSE;
239
240     if (o_ptr->ac != j_ptr->ac)
241         return FALSE;
242
243     if (o_ptr->dd != j_ptr->dd)
244         return FALSE;
245
246     if (o_ptr->ds != j_ptr->ds)
247         return FALSE;
248
249     if (o_ptr->tval == TV_CHEST)
250         return FALSE;
251
252     if (o_ptr->tval == TV_STATUE)
253         return FALSE;
254
255     if (o_ptr->tval == TV_CAPTURE)
256         return FALSE;
257
258     if (o_ptr->discount != j_ptr->discount)
259         return FALSE;
260
261     return TRUE;
262 }
263
264 /*!
265  * @brief 店舗に並べた品を重ね合わせできるかどうか判定する /
266  * Allow a store item to absorb another item
267  * @param o_ptr 判定するオブジェクト構造体の参照ポインタ1
268  * @param j_ptr 判定するオブジェクト構造体の参照ポインタ2
269  * @return 重ね合わせできるならTRUEを返す
270  * @details
271  * <pre>
272  * See "object_similar()" for the same function for the "player"
273  * </pre>
274  */
275 static void store_object_absorb(object_type *o_ptr, object_type *j_ptr)
276 {
277     int max_num = (o_ptr->tval == TV_ROD) ? MIN(99, MAX_SHORT / k_info[o_ptr->k_idx].pval) : 99;
278     int total = o_ptr->number + j_ptr->number;
279     int diff = (total > max_num) ? total - max_num : 0;
280     o_ptr->number = (total > max_num) ? max_num : total;
281     if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_WAND))
282         o_ptr->pval += j_ptr->pval * (j_ptr->number - diff) / j_ptr->number;
283 }
284
285 /*!
286  * @brief 店舗にオブジェクトを加える /
287  * Add the item "o_ptr" to a real stores inventory.
288  * @param o_ptr 加えたいオブジェクトの構造体参照ポインタ
289  * @return 収めた先のID
290  * @details
291  * <pre>
292  * In all cases, return the slot (or -1) where the object was placed
293  * Note that this is a hacked up version of "store_item_to_inventory()".
294  * Also note that it may not correctly "adapt" to "knowledge" bacoming
295  * known, the player may have to pick stuff up and drop it again.
296  * </pre>
297  */
298 int store_carry(player_type *player_ptr, object_type *o_ptr)
299 {
300     PRICE value = object_value(player_ptr, o_ptr);
301     if (value <= 0)
302         return -1;
303
304     o_ptr->ident |= IDENT_FULL_KNOWN;
305     o_ptr->inscription = 0;
306     o_ptr->feeling = FEEL_NONE;
307     int slot;
308     for (slot = 0; slot < st_ptr->stock_num; slot++) {
309         object_type *j_ptr;
310         j_ptr = &st_ptr->stock[slot];
311         if (store_object_similar(j_ptr, o_ptr)) {
312             store_object_absorb(j_ptr, o_ptr);
313             return slot;
314         }
315     }
316
317     if (st_ptr->stock_num >= st_ptr->stock_size)
318         return -1;
319
320     for (slot = 0; slot < st_ptr->stock_num; slot++) {
321         object_type *j_ptr;
322         j_ptr = &st_ptr->stock[slot];
323         if (o_ptr->tval > j_ptr->tval)
324             break;
325         if (o_ptr->tval < j_ptr->tval)
326             continue;
327         if (o_ptr->sval < j_ptr->sval)
328             break;
329         if (o_ptr->sval > j_ptr->sval)
330             continue;
331         if (o_ptr->tval == TV_ROD) {
332             if (o_ptr->pval < j_ptr->pval)
333                 break;
334             if (o_ptr->pval > j_ptr->pval)
335                 continue;
336         }
337
338         PRICE j_value = object_value(player_ptr, j_ptr);
339         if (value > j_value)
340             break;
341         if (value < j_value)
342             continue;
343     }
344
345     for (int i = st_ptr->stock_num; i > slot; i--)
346         st_ptr->stock[i] = st_ptr->stock[i - 1];
347
348     st_ptr->stock_num++;
349     st_ptr->stock[slot] = *o_ptr;
350     return slot;
351 }