OSDN Git Service

First version
[st-ro/stro.git] / src / map / vending.c
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/malloc.h" // aMalloc, aFree
6 #include "../common/showmsg.h" // ShowInfo
7 #include "../common/strlib.h"
8 #include "clif.h"
9 #include "itemdb.h"
10 #include "atcommand.h"
11 #include "path.h"
12 #include "chrif.h"
13 #include "vending.h"
14 #include "pc.h"
15 #include "buyingstore.h" // struct s_autotrade_entry, struct s_autotrader
16 #include "achievement.h"
17
18 #include <stdlib.h> // atoi
19
20 static uint32 vending_nextid = 0; ///Vending_id counter
21 static DBMap *vending_db; ///DB holder the vender : charid -> map_session_data
22
23 //Autotrader
24 static DBMap *vending_autotrader_db; /// Holds autotrader info: char_id -> struct s_autotrader
25 static void vending_autotrader_remove(struct s_autotrader *at, bool remove);
26 static int vending_autotrader_free(DBKey key, DBData *data, va_list ap);
27
28 /**
29  * Lookup to get the vending_db outside module
30  * @return the vending_db
31  */
32 DBMap * vending_getdb()
33 {
34         return vending_db;
35 }
36
37 /**
38  * Create an unique vending shop id.
39  * @return the next vending_id
40  */
41 static int vending_getuid(void)
42 {
43         return ++vending_nextid;
44 }
45
46 /**
47  * Make a player close his shop
48  * @param sd : player session
49  */
50 void vending_closevending(struct map_session_data* sd)
51 {
52         nullpo_retv(sd);
53
54         if( sd->state.vending ) {
55                 if( Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE vending_id = %d;", vending_items_table, sd->vender_id ) != SQL_SUCCESS ||
56                         Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `id` = %d;", vendings_table, sd->vender_id ) != SQL_SUCCESS ) {
57                                 Sql_ShowDebug(mmysql_handle);
58                 }
59
60                 sd->state.vending = false;
61                 sd->vender_id = 0;
62                 clif_closevendingboard(&sd->bl, 0);
63                 idb_remove(vending_db, sd->status.char_id);
64         }
65 }
66
67 /**
68  * Player request a shop's item list (a player shop)
69  * @param sd : player requestion the list
70  * @param id : vender account id (gid)
71  */
72 void vending_vendinglistreq(struct map_session_data* sd, int id)
73 {
74         struct map_session_data* vsd;
75         nullpo_retv(sd);
76
77         if( (vsd = map_id2sd(id)) == NULL )
78                 return;
79         if( !vsd->state.vending )
80                 return; // not vending
81
82         if (!pc_can_give_items(sd) || !pc_can_give_items(vsd)) { //check if both GMs are allowed to trade
83                 clif_displaymessage(sd->fd, msg_txt(sd,246));
84                 return;
85         }
86
87         sd->vended_id = vsd->vender_id;  // register vending uid
88
89         clif_vendinglist(sd, id, vsd->vending);
90 }
91
92 /**
93  * Purchase item(s) from a shop
94  * @param sd : buyer player session
95  * @param aid : account id of vender
96  * @param uid : shop unique id
97  * @param data : items data who would like to purchase \n
98  *      data := {<index>.w <amount>.w }[count]
99  * @param count : number of different items he's trying to buy
100  */
101 void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count)
102 {
103         int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING];
104         double z;
105         struct s_vending vending[MAX_VENDING]; // against duplicate packets
106         struct map_session_data* vsd = map_id2sd(aid);
107
108         nullpo_retv(sd);
109         if( vsd == NULL || !vsd->state.vending || vsd->bl.id == sd->bl.id )
110                 return; // invalid shop
111
112         if( vsd->vender_id != uid ) { // shop has changed
113                 clif_buyvending(sd, 0, 0, 6);  // store information was incorrect
114                 return;
115         }
116
117         if( !searchstore_queryremote(sd, aid) && ( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) ) )
118                 return; // shop too far away
119
120         searchstore_clearremote(sd);
121
122         if( count < 1 || count > MAX_VENDING || count > vsd->vend_num )
123                 return; // invalid amount of purchased items
124
125         blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory
126
127         // duplicate item in vending to check hacker with multiple packets
128         memcpy(&vending, &vsd->vending, sizeof(vsd->vending)); // copy vending list
129
130         // some checks
131         z = 0.; // zeny counter
132         w = 0;  // weight counter
133         for( i = 0; i < count; i++ ) {
134                 short amount = *(uint16*)(data + 4*i + 0);
135                 short idx    = *(uint16*)(data + 4*i + 2);
136                 idx -= 2;
137
138                 if( amount <= 0 )
139                         return;
140
141                 // check of item index in the cart
142                 if( idx < 0 || idx >= MAX_CART )
143                         return;
144
145                 ARR_FIND( 0, vsd->vend_num, j, vsd->vending[j].index == idx );
146                 if( j == vsd->vend_num )
147                         return; //picked non-existing item
148                 else
149                         vend_list[i] = j;
150
151                 z += ((double)vsd->vending[j].value * (double)amount);
152                 if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY ) {
153                         clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny
154                         return;
155                 }
156                 if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) {
157                         clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow
158                         return;
159
160                 }
161                 w += itemdb_weight(vsd->cart.u.items_cart[idx].nameid) * amount;
162                 if( w + sd->weight > sd->max_weight ) {
163                         clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight
164                         return;
165                 }
166
167                 //Check to see if cart/vend info is in sync.
168                 if( vending[j].amount > vsd->cart.u.items_cart[idx].amount )
169                         vending[j].amount = vsd->cart.u.items_cart[idx].amount;
170
171                 // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples).
172                 // here, we check cumulative amounts
173                 if( vending[j].amount < amount ) {
174                         // send more quantity is not a hack (an other player can have buy items just before)
175                         clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity
176                         return;
177                 }
178
179                 vending[j].amount -= amount;
180
181                 switch( pc_checkadditem(sd, vsd->cart.u.items_cart[idx].nameid, amount) ) {
182                 case CHKADDITEM_EXIST:
183                         break;  //We'd add this item to the existing one (in buyers inventory)
184                 case CHKADDITEM_NEW:
185                         new_++;
186                         if (new_ > blank)
187                                 return; //Buyer has no space in his inventory
188                         break;
189                 case CHKADDITEM_OVERAMOUNT:
190                         return; //too many items
191                 }
192         }
193
194         pc_payzeny(sd, (int)z, LOG_TYPE_VENDING, vsd);
195         achievement_update_objective(sd, AG_SPEND_ZENY, 1, (int)z);
196         if( battle_config.vending_tax )
197                 z -= z * (battle_config.vending_tax/10000.);
198         pc_getzeny(vsd, (int)z, LOG_TYPE_VENDING, sd);
199
200         for( i = 0; i < count; i++ ) {
201                 short amount = *(uint16*)(data + 4*i + 0);
202                 short idx    = *(uint16*)(data + 4*i + 2);
203                 idx -= 2;
204                 z = 0.; // zeny counter
205
206                 // vending item
207                 pc_additem(sd, &vsd->cart.u.items_cart[idx], amount, LOG_TYPE_VENDING);
208                 vsd->vending[vend_list[i]].amount -= amount;
209                 z += ((double)vsd->vending[i].value * (double)amount);
210
211                 if( vsd->vending[vend_list[i]].amount ) {
212                         if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_table, vsd->vending[vend_list[i]].amount, vsd->vender_id, vsd->cart.u.items_cart[idx].id ) != SQL_SUCCESS ) {
213                                 Sql_ShowDebug( mmysql_handle );
214                         }
215                 } else {
216                         if( Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_table, vsd->vender_id, vsd->cart.u.items_cart[idx].id ) != SQL_SUCCESS ) {
217                                 Sql_ShowDebug( mmysql_handle );
218                         }
219                 }
220
221                 pc_cart_delitem(vsd, idx, amount, 0, LOG_TYPE_VENDING);
222                 if( battle_config.vending_tax )
223                         z -= z * (battle_config.vending_tax/10000.);
224                 clif_vendingreport(vsd, idx, amount, sd->status.char_id, (int)z);
225
226                 //print buyer's name
227                 if( battle_config.buyer_name ) {
228                         char temp[256];
229                         sprintf(temp, msg_txt(sd,265), sd->status.name);
230                         clif_messagecolor(&vsd->bl, color_table[COLOR_LIGHT_GREEN], temp, false, SELF);
231                 }
232         }
233
234         // compact the vending list
235         for( i = 0, cursor = 0; i < vsd->vend_num; i++ ) {
236                 if( vsd->vending[i].amount == 0 )
237                         continue;
238
239                 if( cursor != i ) { // speedup
240                         vsd->vending[cursor].index = vsd->vending[i].index;
241                         vsd->vending[cursor].amount = vsd->vending[i].amount;
242                         vsd->vending[cursor].value = vsd->vending[i].value;
243                 }
244
245                 cursor++;
246         }
247
248         vsd->vend_num = cursor;
249
250         //Always save BOTH: customer (buyer) and vender
251         if( save_settings&CHARSAVE_VENDING ) {
252                 chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
253                 chrif_save(vsd, CSAVE_INVENTORY|CSAVE_CART);
254         }
255
256         //check for @AUTOTRADE users [durf]
257         if( vsd->state.autotrade ) {
258                 //see if there is anything left in the shop
259                 ARR_FIND( 0, vsd->vend_num, i, vsd->vending[i].amount > 0 );
260                 if( i == vsd->vend_num ) {
261                         //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex]
262                         vending_closevending(vsd);
263                         map_quit(vsd);  //They have no reason to stay around anymore, do they?
264                 }
265         }
266 }
267
268 /**
269  * Player setup a new shop
270  * @param sd : player opening the shop
271  * @param message : shop title
272  * @param data : itemlist data
273  *      data := {<index>.w <amount>.w <value>.l}[count]
274  * @param count : number of different items
275  * @param at Autotrader info, or NULL if requetsed not from autotrade persistance
276  * @return 0 If success, 1 - Cannot open (die, not state.prevend, trading), 2 - No cart, 3 - Count issue, 4 - Cart data isn't saved yet, 5 - No valid item found
277  */
278 int8 vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count, struct s_autotrader *at)
279 {
280         int i, j;
281         int vending_skill_lvl;
282         char message_sql[MESSAGE_SIZE*2];
283         StringBuf buf;
284         
285         nullpo_retr(false,sd);
286
287         if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd)) {
288                 return 1; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once
289         }
290
291         vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
292         
293         // skill level and cart check
294         if( !vending_skill_lvl || !pc_iscarton(sd) ) {
295                 clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
296                 return 2;
297         }
298
299         // check number of items in shop
300         if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) { // invalid item count
301                 clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
302                 return 3;
303         }
304
305         if (save_settings&CHARSAVE_VENDING) // Avoid invalid data from saving
306                 chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
307
308         // filter out invalid items
309         i = 0;
310         for( j = 0; j < count; j++ ) {
311                 short index        = *(uint16*)(data + 8*j + 0);
312                 short amount       = *(uint16*)(data + 8*j + 2);
313                 unsigned int value = *(uint32*)(data + 8*j + 4);
314
315                 index -= 2; // offset adjustment (client says that the first cart position is 2)
316
317                 if( index < 0 || index >= MAX_CART // invalid position
318                 ||  pc_cartitem_amount(sd, index, amount) < 0 // invalid item or insufficient quantity
319                 //NOTE: official server does not do any of the following checks!
320                 ||  !sd->cart.u.items_cart[index].identify // unidentified item
321                 ||  sd->cart.u.items_cart[index].attribute == 1 // broken item
322                 ||  sd->cart.u.items_cart[index].expire_time // It should not be in the cart but just in case
323                 ||  (sd->cart.u.items_cart[index].bound && !pc_can_give_bounded_items(sd)) // can't trade account bound items and has no permission
324                 ||  !itemdb_cantrade(&sd->cart.u.items_cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item
325                         continue;
326
327                 sd->vending[i].index = index;
328                 sd->vending[i].amount = amount;
329                 sd->vending[i].value = min(value, (unsigned int)battle_config.vending_max_value);
330                 i++; // item successfully added
331         }
332
333         if( i != j )
334                 clif_displaymessage (sd->fd, msg_txt(sd,266)); //"Some of your items cannot be vended and were removed from the shop."
335
336         if( i == 0 ) { // no valid item found
337                 clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); // custom reply packet
338                 return 5;
339         }
340
341         sd->state.prevend = 0;
342         sd->state.vending = true;
343         sd->state.workinprogress = WIP_DISABLE_NONE;
344         sd->vender_id = vending_getuid();
345         sd->vend_num = i;
346         safestrncpy(sd->message, message, MESSAGE_SIZE);
347         
348         Sql_EscapeString( mmysql_handle, message_sql, sd->message );
349
350         if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`, `account_id`, `char_id`, `sex`, `map`, `x`, `y`, `title`, `autotrade`, `body_direction`, `head_direction`, `sit`) "
351                 "VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, '%d', '%d', '%d' );",
352                 vendings_table, sd->vender_id, sd->status.account_id, sd->status.char_id, sd->status.sex == SEX_FEMALE ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->state.autotrade, at ? at->dir : sd->ud.dir, at ? at->head_dir : sd->head_dir, at ? at->sit : pc_issit(sd) ) != SQL_SUCCESS ) {
353                 Sql_ShowDebug(mmysql_handle);
354         }
355
356         StringBuf_Init(&buf);
357         StringBuf_Printf(&buf, "INSERT INTO `%s`(`vending_id`,`index`,`cartinventory_id`,`amount`,`price`) VALUES", vending_items_table);
358         for (j = 0; j < i; j++) {
359                 StringBuf_Printf(&buf, "(%d,%d,%d,%d,%d)", sd->vender_id, j, sd->cart.u.items_cart[sd->vending[j].index].id, sd->vending[j].amount, sd->vending[j].value);
360                 if (j < i-1)
361                         StringBuf_AppendStr(&buf, ",");
362         }
363         if (SQL_ERROR == Sql_QueryStr(mmysql_handle, StringBuf_Value(&buf)))
364                 Sql_ShowDebug(mmysql_handle);
365         StringBuf_Destroy(&buf);
366
367         clif_openvending(sd,sd->bl.id,sd->vending);
368         clif_showvendingboard(&sd->bl,message,0);
369
370         idb_put(vending_db, sd->status.char_id, sd);
371
372         return 0;
373 }
374
375 /**
376  * Checks if an item is being sold in given player's vending.
377  * @param sd : vender session (player)
378  * @param nameid : item id
379  * @return 0:not selling it, 1: yes
380  */
381 bool vending_search(struct map_session_data* sd, unsigned short nameid)
382 {
383         int i;
384
385         if( !sd->state.vending ) { // not vending
386                 return false;
387         }
388
389         ARR_FIND( 0, sd->vend_num, i, sd->cart.u.items_cart[sd->vending[i].index].nameid == (short)nameid );
390         if( i == sd->vend_num ) { // not found
391                 return false;
392         }
393
394         return true;
395 }
396
397 /**
398  * Searches for all items in a vending, that match given ids, price and possible cards.
399  * @param sd : The vender session to search into
400  * @param s : parameter of the search (see s_search_store_search)
401  * @return Whether or not the search should be continued.
402  */
403 bool vending_searchall(struct map_session_data* sd, const struct s_search_store_search* s)
404 {
405         int i, c, slot;
406         unsigned int idx, cidx;
407         struct item* it;
408
409         if( !sd->state.vending ) // not vending
410                 return true;
411
412         for( idx = 0; idx < s->item_count; idx++ ) {
413                 ARR_FIND( 0, sd->vend_num, i, sd->cart.u.items_cart[sd->vending[i].index].nameid == (short)s->itemlist[idx] );
414                 if( i == sd->vend_num ) { // not found
415                         continue;
416                 }
417                 it = &sd->cart.u.items_cart[sd->vending[i].index];
418
419                 if( s->min_price && s->min_price > sd->vending[i].value ) { // too low price
420                         continue;
421                 }
422
423                 if( s->max_price && s->max_price < sd->vending[i].value ) { // too high price
424                         continue;
425                 }
426
427                 if( s->card_count ) { // check cards
428                         if( itemdb_isspecial(it->card[0]) ) { // something, that is not a carded
429                                 continue;
430                         }
431                         slot = itemdb_slot(it->nameid);
432
433                         for( c = 0; c < slot && it->card[c]; c ++ ) {
434                                 ARR_FIND( 0, s->card_count, cidx, s->cardlist[cidx] == it->card[c] );
435                                 if( cidx != s->card_count ) { // found
436                                         break;
437                                 }
438                         }
439
440                         if( c == slot || !it->card[c] ) { // no card match
441                                 continue;
442                         }
443                 }
444
445                 if( !searchstore_result(s->search_sd, sd->vender_id, sd->status.account_id, sd->message, it->nameid, sd->vending[i].amount, sd->vending[i].value, it->card, it->refine) ) { // result set full
446                         return false;
447                 }
448         }
449
450         return true;
451 }
452
453 /**
454 * Open vending for Autotrader
455 * @param sd Player as autotrader
456 */
457 void vending_reopen( struct map_session_data* sd )
458 {
459         struct s_autotrader *at = NULL;
460         int8 fail = -1;
461
462         nullpo_retv(sd);
463
464         // Open vending for this autotrader
465         if ((at = (struct s_autotrader *)uidb_get(vending_autotrader_db, sd->status.char_id)) && at->count && at->entries) {
466                 uint8 *data, *p;
467                 uint16 j, count;
468
469                 // Init vending data for autotrader
470                 CREATE(data, uint8, at->count * 8);
471
472                 for (j = 0, p = data, count = at->count; j < at->count; j++) {
473                         struct s_autotrade_entry *entry = at->entries[j];
474                         uint16 *index = (uint16*)(p + 0);
475                         uint16 *amount = (uint16*)(p + 2);
476                         uint32 *value = (uint32*)(p + 4);
477
478                         // Find item position in cart
479                         ARR_FIND(0, MAX_CART, entry->index, sd->cart.u.items_cart[entry->index].id == entry->cartinventory_id);
480
481                         if (entry->index == MAX_CART) {
482                                 count--;
483                                 continue;
484                         }
485
486                         *index = entry->index + 2;
487                         *amount = itemdb_isstackable(sd->cart.u.items_cart[entry->index].nameid) ? entry->amount : 1;
488                         *value = entry->price;
489
490                         p += 8;
491                 }
492
493                 sd->state.prevend = 1; // Set him into a hacked prevend state
494                 sd->state.autotrade = 1;
495
496                 // Make sure abort all NPCs
497                 npc_event_dequeue(sd);
498                 pc_cleareventtimer(sd);
499
500                 // Open the vending again
501                 if( (fail = vending_openvending(sd, at->title, data, count, at)) == 0 ) {
502                         // Make vendor look perfect
503                         pc_setdir(sd, at->dir, at->head_dir);
504                         clif_changed_dir(&sd->bl, AREA_WOS);
505                         if( at->sit ) {
506                                 pc_setsit(sd);
507                                 skill_sit(sd, 1);
508                                 clif_sitting(&sd->bl);
509                         }
510
511                         // Immediate save
512                         chrif_save(sd, CSAVE_AUTOTRADE);
513
514                         ShowInfo("Vending loaded for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
515                                 sd->status.name, count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y);
516                 }
517                 aFree(data);
518         }
519
520         if (at) {
521                 vending_autotrader_remove(at, true);
522                 if (db_size(vending_autotrader_db) == 0)
523                         vending_autotrader_db->clear(vending_autotrader_db, vending_autotrader_free);
524         }
525
526         if (fail != 0) {
527                 ShowError("vending_reopen: (Error:%d) Load failed for autotrader '"CL_WHITE"%s"CL_RESET"' (CID=%d/AID=%d)\n", fail, sd->status.name, sd->status.char_id, sd->status.account_id);
528                 map_quit(sd);
529         }
530 }
531
532 /**
533 * Initializing autotraders from table
534 */
535 void do_init_vending_autotrade(void)
536 {
537         if (battle_config.feature_autotrade) {
538                 if (Sql_Query(mmysql_handle,
539                         "SELECT `id`, `account_id`, `char_id`, `sex`, `title`, `body_direction`, `head_direction`, `sit` "
540                         "FROM `%s` "
541                         "WHERE `autotrade` = 1 AND (SELECT COUNT(`vending_id`) FROM `%s` WHERE `vending_id` = `id`) > 0 "
542                         "ORDER BY `id`;",
543                         vendings_table, vending_items_table ) != SQL_SUCCESS )
544                 {
545                         Sql_ShowDebug(mmysql_handle);
546                         return;
547                 }
548
549                 if( Sql_NumRows(mmysql_handle) > 0 ) {
550                         uint16 items = 0;
551                         DBIterator *iter = NULL;
552                         struct s_autotrader *at = NULL;
553
554                         // Init each autotrader data
555                         while (SQL_SUCCESS == Sql_NextRow(mmysql_handle)) {
556                                 size_t len;
557                                 char* data;
558
559                                 at = NULL;
560                                 CREATE(at, struct s_autotrader, 1);
561                                 Sql_GetData(mmysql_handle, 0, &data, NULL); at->id = atoi(data);
562                                 Sql_GetData(mmysql_handle, 1, &data, NULL); at->account_id = atoi(data);
563                                 Sql_GetData(mmysql_handle, 2, &data, NULL); at->char_id = atoi(data);
564                                 Sql_GetData(mmysql_handle, 3, &data, NULL); at->sex = (data[0] == 'F') ? SEX_FEMALE : SEX_MALE;
565                                 Sql_GetData(mmysql_handle, 4, &data, &len); safestrncpy(at->title, data, zmin(len + 1, MESSAGE_SIZE));
566                                 Sql_GetData(mmysql_handle, 5, &data, NULL); at->dir = atoi(data);
567                                 Sql_GetData(mmysql_handle, 6, &data, NULL); at->head_dir = atoi(data);
568                                 Sql_GetData(mmysql_handle, 7, &data, NULL); at->sit = atoi(data);
569                                 at->count = 0;
570
571                                 if (battle_config.feature_autotrade_direction >= 0)
572                                         at->dir = battle_config.feature_autotrade_direction;
573                                 if (battle_config.feature_autotrade_head_direction >= 0)
574                                         at->head_dir = battle_config.feature_autotrade_head_direction;
575                                 if (battle_config.feature_autotrade_sit >= 0)
576                                         at->sit = battle_config.feature_autotrade_sit;
577
578                                 // initialize player
579                                 CREATE(at->sd, struct map_session_data, 1);
580                                 pc_setnewpc(at->sd, at->account_id, at->char_id, 0, gettick(), at->sex, 0);
581                                 at->sd->state.autotrade = 1|2;
582                                 at->sd->state.monster_ignore = (battle_config.autotrade_monsterignore);
583                                 chrif_authreq(at->sd, true);
584                                 uidb_put(vending_autotrader_db, at->char_id, at);
585                         }
586                         Sql_FreeResult(mmysql_handle);
587
588                         // Init items for each autotraders
589                         iter = db_iterator(vending_autotrader_db);
590                         for (at = (struct s_autotrader *)dbi_first(iter); dbi_exists(iter); at = (struct s_autotrader *)dbi_next(iter)) {
591                                 uint16 j = 0;
592
593                                 if (SQL_ERROR == Sql_Query(mmysql_handle,
594                                         "SELECT `cartinventory_id`, `amount`, `price` "
595                                         "FROM `%s` "
596                                         "WHERE `vending_id` = %d "
597                                         "ORDER BY `index` ASC;",
598                                         vending_items_table, at->id ) )
599                                 {
600                                         Sql_ShowDebug(mmysql_handle);
601                                         continue;
602                                 }
603
604                                 if (!(at->count = (uint16)Sql_NumRows(mmysql_handle))) {
605                                         map_quit(at->sd);
606                                         vending_autotrader_remove(at, true);
607                                         continue;
608                                 }
609
610                                 //Init the list
611                                 CREATE(at->entries, struct s_autotrade_entry *, at->count);
612
613                                 //Add the item into list
614                                 j = 0;
615                                 while (SQL_SUCCESS == Sql_NextRow(mmysql_handle) && j < at->count) {
616                                         char *data;
617                                         CREATE(at->entries[j], struct s_autotrade_entry, 1);
618                                         Sql_GetData(mmysql_handle, 0, &data, NULL); at->entries[j]->cartinventory_id = atoi(data);
619                                         Sql_GetData(mmysql_handle, 1, &data, NULL); at->entries[j]->amount = atoi(data);
620                                         Sql_GetData(mmysql_handle, 2, &data, NULL); at->entries[j]->price = atoi(data);
621                                         j++;
622                                 }
623                                 items += j;
624                                 Sql_FreeResult(mmysql_handle);
625                         }
626                         dbi_destroy(iter);
627
628                         ShowStatus("Done loading '"CL_WHITE"%d"CL_RESET"' vending autotraders with '"CL_WHITE"%d"CL_RESET"' items.\n", db_size(vending_autotrader_db), items);
629                 }
630         }
631
632         // Everything is loaded fine, their entries will be reinserted once they are loaded
633         if (Sql_Query( mmysql_handle, "DELETE FROM `%s`;", vendings_table ) != SQL_SUCCESS ||
634                 Sql_Query( mmysql_handle, "DELETE FROM `%s`;", vending_items_table ) != SQL_SUCCESS) {
635                 Sql_ShowDebug(mmysql_handle);
636         }
637 }
638
639 /**
640  * Remove an autotrader's data
641  * @param at Autotrader
642  * @param remove If true will removes from vending_autotrader_db
643  **/
644 static void vending_autotrader_remove(struct s_autotrader *at, bool remove) {
645         nullpo_retv(at);
646         if (at->count && at->entries) {
647                 uint16 i = 0;
648                 for (i = 0; i < at->count; i++) {
649                         if (at->entries[i])
650                                 aFree(at->entries[i]);
651                 }
652                 aFree(at->entries);
653         }
654         if (remove)
655                 uidb_remove(vending_autotrader_db, at->char_id);
656         aFree(at);
657 }
658
659 /**
660 * Clear all autotraders
661 * @author [Cydh]
662 */
663 static int vending_autotrader_free(DBKey key, DBData *data, va_list ap) {
664         struct s_autotrader *at = (struct s_autotrader *)db_data2ptr(data);
665         if (at)
666                 vending_autotrader_remove(at, false);
667         return 0;
668 }
669
670 /**
671  * Initialise the vending module
672  * called in map::do_init
673  */
674 void do_final_vending(void)
675 {
676         db_destroy(vending_db);
677         vending_autotrader_db->destroy(vending_autotrader_db, vending_autotrader_free);
678 }
679
680 /**
681  * Destory the vending module
682  * called in map::do_final
683  */
684 void do_init_vending(void)
685 {
686         vending_db = idb_alloc(DB_OPT_BASE);
687         vending_autotrader_db = uidb_alloc(DB_OPT_BASE);
688         vending_nextid = 0;
689 }