OSDN Git Service

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