OSDN Git Service

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