root/src/map/vending.c @ 1

Revision 1, 8.7 kB (checked in by jinshiro, 17 years ago)
RevLine 
[1]1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/nullpo.h"
5#include "../common/strlib.h"
6#include "../common/utils.h"
7#include "clif.h"
8#include "itemdb.h"
9#include "atcommand.h"
10#include "map.h"
11#include "path.h"
12#include "chrif.h"
13#include "vending.h"
14#include "pc.h"
15#include "skill.h"
16#include "battle.h"
17#include "log.h"
18
19#include "irc.h"
20
21#include <stdio.h>
22#include <string.h>
23
24
25/*==========================================
26 * Close shop
27 *------------------------------------------*/
28void vending_closevending(struct map_session_data* sd)
29{
30        nullpo_retv(sd);
31
32        sd->vender_id = 0;
33        clif_closevendingboard(&sd->bl,0);
34
35        if( use_irc && irc_announce_shop_flag )
36                irc_announce_shop(sd,0);
37}
38
39/*==========================================
40 * Request a shop's item list
41 *------------------------------------------*/
42void vending_vendinglistreq(struct map_session_data* sd, int id)
43{
44        struct map_session_data* vsd;
45        nullpo_retv(sd);
46
47        if( (vsd = map_id2sd(id)) == NULL )
48                return;
49        if( vsd->vender_id == 0 )
50                return; // not vending
51
52        if ( !pc_can_give_items(pc_isGM(sd)) || !pc_can_give_items(pc_isGM(vsd)) ) //check if both GMs are allowed to trade
53        {       // GM is not allowed to trade
54                clif_displaymessage(sd->fd, msg_txt(246));
55                return;
56        } 
57
58        clif_vendinglist(sd, id, vsd->vending);
59}
60
61/*==========================================
62 * Purchase item(s) from a shop
63 *------------------------------------------*/
64void vending_purchasereq(struct map_session_data* sd, int id, const uint8* data, int count)
65{
66        int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING];
67        double z;
68        struct s_vending vending[MAX_VENDING]; // against duplicate packets
69        struct map_session_data* vsd = map_id2sd(id);
70
71        nullpo_retv(sd);
72
73        if( vsd == NULL || vsd->vender_id == 0 || vsd->vender_id == sd->bl.id )
74                return; // invalid shop
75        if( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) )
76                return; // shop too far away
77        if( count < 1 || count > MAX_VENDING || count > vsd->vend_num )
78                return; // invalid amount of purchased items
79
80        blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory
81
82        // duplicate item in vending to check hacker with multiple packets
83        memcpy(&vending, &vsd->vending, sizeof(vsd->vending)); // copy vending list
84
85        // some checks
86        z = 0.; // zeny counter
87        w = 0;  // weight counter
88        for( i = 0; i < count; i++ )
89        {
90                short amount = *(uint16*)(data + 4*i + 0);
91                short idx    = *(uint16*)(data + 4*i + 2);
92                idx -= 2;
93
94                if( amount <= 0 )
95                        return;
96
97                // check of item index in the cart
98                if( idx < 0 || idx >= MAX_CART )
99                        return;
100
101                ARR_FIND( 0, vsd->vend_num, j, vsd->vending[j].index == idx );
102                if( j == vsd->vend_num )
103                        return; //picked non-existing item
104                else
105                        vend_list[i] = j;
106
107                z += ((double)vsd->vending[j].value * (double)amount);
108                if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY )
109                {
110                        clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny
111                        return;
112                }
113                if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max )
114                {
115                        clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow
116                        return;
117
118                }
119                w += itemdb_weight(vsd->status.cart[idx].nameid) * amount;
120                if( w + sd->weight > sd->max_weight )
121                {
122                        clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight
123                        return;
124                }
125               
126                //Check to see if cart/vend info is in sync.
127                if( vending[j].amount > vsd->status.cart[idx].amount )
128                        vending[j].amount = vsd->status.cart[idx].amount;
129               
130                // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples).
131                // here, we check cumulative amounts
132                if( vending[j].amount < amount )
133                {
134                        // send more quantity is not a hack (an other player can have buy items just before)
135                        clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity
136                        return;
137                }
138               
139                vending[j].amount -= amount;
140
141                switch( pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount) ) {
142                case ADDITEM_EXIST:
143                        break;  //We'd add this item to the existing one (in buyers inventory)
144                case ADDITEM_NEW:
145                        new_++;
146                        if (new_ > blank)
147                                return; //Buyer has no space in his inventory
148                        break;
149                case ADDITEM_OVERAMOUNT:
150                        return; //too many items
151                }
152        }
153
154        //Logs (V)ending Zeny [Lupus]
155        if( log_config.zeny > 0 )
156                log_zeny(vsd, "V", sd, (int)z);
157
158        pc_payzeny(sd, (int)z);
159        if( battle_config.vending_tax )
160                z -= z * (battle_config.vending_tax/10000.);
161        pc_getzeny(vsd, (int)z);
162
163        for( i = 0; i < count; i++ )
164        {
165                short amount = *(uint16*)(data + 4*i + 0);
166                short idx    = *(uint16*)(data + 4*i + 2);
167                idx -= 2;
168
169                //Logs sold (V)ending items [Lupus]
170                if(log_config.enable_logs&0x4) {
171                        log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, &vsd->status.cart[idx]);
172                        log_pick_pc( sd, "V", vsd->status.cart[idx].nameid,  amount, &vsd->status.cart[idx]);
173                }
174
175                // vending item
176                pc_additem(sd, &vsd->status.cart[idx], amount);
177                vsd->vending[vend_list[i]].amount -= amount;
178                pc_cart_delitem(vsd, idx, amount, 0);
179                clif_vendingreport(vsd, idx, amount);
180
181                //print buyer's name
182                if( battle_config.buyer_name )
183                {
184                        char temp[256];
185                        sprintf(temp, msg_txt(265), sd->status.name);
186                        clif_disp_onlyself(vsd,temp,strlen(temp));
187                }
188        }
189
190        // compact the vending list
191        for( i = 0, cursor = 0; i < vsd->vend_num; i++ )
192        {
193                if( vsd->vending[i].amount == 0 )
194                        continue;
195               
196                if( cursor != i ) // speedup
197                {
198                        vsd->vending[cursor].index = vsd->vending[i].index;
199                        vsd->vending[cursor].amount = vsd->vending[i].amount;
200                        vsd->vending[cursor].value = vsd->vending[i].value;
201                }
202
203                cursor++;
204        }
205        vsd->vend_num = cursor;
206
207        //Always save BOTH: buyer and customer
208        if( save_settings&2 )
209        {
210                chrif_save(sd,0);
211                chrif_save(vsd,0);
212        }
213
214        //check for @AUTOTRADE users [durf]
215        if( vsd->state.autotrade )
216        {
217                //see if there is anything left in the shop
218                ARR_FIND( 0, vsd->vend_num, i, vsd->vending[i].amount > 0 );
219                if( i == vsd->vend_num )
220                {
221                        //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex]
222                        vending_closevending(vsd);
223                        map_quit(vsd);  //They have no reason to stay around anymore, do they?
224                }
225        }
226}
227
228/*==========================================
229 * Open shop
230 * data := {<index>.w <amount>.w <value>.l}[count]
231 *------------------------------------------*/
232void vending_openvending(struct map_session_data* sd, const char* message, bool flag, const uint8* data, int count)
233{
234        int i, j;
235        int vending_skill_lvl;
236        nullpo_retv(sd);
237
238        if( !flag ) // cancelled
239                return; // nothing to do
240
241        if (pc_istrading(sd))
242                return; // can't have 2 shops at once
243
244        vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
245        // skill level and cart check
246        if( !vending_skill_lvl || !pc_iscarton(sd) )
247        {
248                clif_skill_fail(sd, MC_VENDING, 0, 0);
249                return;
250        }
251
252        // check number of items in shop
253        if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl )
254        {       // invalid item count
255                clif_skill_fail(sd, MC_VENDING, 0, 0);
256                return;
257        }
258
259        // filter out invalid items
260        i = 0;
261        for( j = 0; j < count; j++ )
262        {
263                short index        = *(uint16*)(data + 8*j + 0);
264                short amount       = *(uint16*)(data + 8*j + 2);
265                unsigned int value = *(uint32*)(data + 8*j + 4);
266
267                index -= 2; // offset adjustment (client says that the first cart position is 2)
268
269                if( index < 0 || index >= MAX_CART // invalid position
270                ||  pc_cartitem_amount(sd, index, amount) < 0 // invalid item or insufficient quantity
271                //NOTE: official server does not do any of the following checks!
272                ||  !sd->status.cart[index].identify // unidentified item
273                ||  sd->status.cart[index].attribute == 1 // broken item
274                ||  !itemdb_cantrade(&sd->status.cart[index], pc_isGM(sd), pc_isGM(sd)) ) // untradeable item
275                        continue;
276
277                sd->vending[i].index = index;
278                sd->vending[i].amount = amount;
279                sd->vending[i].value = cap_value(value, 1, (unsigned int)battle_config.vending_max_value);
280
281                i++; // item successfully added
282        }
283
284        if( i != j )
285                clif_displaymessage (sd->fd, msg_txt(266)); //"Some of your items cannot be vended and were removed from the shop."
286
287        if( i == 0 )
288        {       // no valid item found
289                clif_skill_fail(sd, MC_VENDING, 0, 0); // custom reply packet
290                return;
291        }
292
293        sd->vender_id = sd->bl.id;
294        sd->vend_num = i;
295        safestrncpy(sd->message, message, MESSAGE_SIZE);
296
297        pc_stop_walking(sd,1);
298        clif_openvending(sd,sd->vender_id,sd->vending);
299        clif_showvendingboard(&sd->bl,message,0);
300
301        if( use_irc && irc_announce_shop_flag )
302                irc_announce_shop(sd,1);
303}
Note: See TracBrowser for help on using the browser.