From 631630d348375d8e64b57aceb09ee5da13f76fca Mon Sep 17 00:00:00 2001 From: ai4rei Date: Thu, 23 Dec 2010 16:05:49 +0000 Subject: * Fixed server processing the sell list (deleting items and giving zeny) for script-controlled shops (OnSellItem), causing the controller script to fail (bugreport:4656, since r6557). - This also makes the server first check the sell list and only continue, if all items can be processed, thus no longer causing incomplete deals and the need for client disconnection in such case (since r6557). git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@14617 54d463be-8e91-2dee-dedb-b68131a5f0ec --- Changelog-Trunk.txt | 3 + src/map/npc.c | 166 ++++++++++++++++++++++++++++++++-------------------- 2 files changed, 107 insertions(+), 62 deletions(-) diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index 21f369862..7212f628f 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -1,5 +1,8 @@ Date Added +2010/12/23 + * Fixed server processing the sell list (deleting items and giving zeny) for script-controlled shops (OnSellItem), causing the controller script to fail (bugreport:4656, since r6557). [Ai4rei] + - This also makes the server first check the sell list and only continue, if all items can be processed, thus no longer causing incomplete deals and the need for client disconnection in such case (since r6557). 2010/12/22 * NPC shop buy list received from the client is now validated for scripted shops (OnBuyItem), before the control is transfered to the script, as well (bugreport:3680, since r5841, related r8525). [Ai4rei] * Resolved random compiler warnings. [Ai4rei] diff --git a/src/map/npc.c b/src/map/npc.c index ca49c084f..2c514eb72 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -1371,97 +1371,139 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) return 0; } -/*========================================== - * - *------------------------------------------*/ + +/// npc_selllist for script-controlled shops +static int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd) +{ + char npc_ev[NAME_LENGTH*2+3]; + int i, idx; + int key_nameid = 0; + int key_amount = 0; + + // discard old contents + script_cleararray_pc(sd, "@sold_nameid", (void*)0); + script_cleararray_pc(sd, "@sold_quantity", (void*)0); + + // save list of to be sold items + for( i = 0; i < n; i++ ) + { + idx = item_list[i*2]-2; + + script_setarray_pc(sd, "@sold_nameid", i, (void*)(intptr)sd->status.inventory[idx].nameid, &key_nameid); + script_setarray_pc(sd, "@sold_quantity", i, (void*)(intptr)item_list[i*2+1], &key_amount); + } + + // invoke event + snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnSellItem", nd->exname); + npc_event(sd, npc_ev, 0); + return 0; +} + + +/// Player item selling to npc shop. +/// +/// @param item_list 'n' pairs +/// @return result code for clif_parse_NpcSellListSend int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) { double z; int i,skill; - int key_nameid = 0; - int key_amount = 0; struct npc_data *nd; - + nullpo_retr(1, sd); nullpo_retr(1, item_list); - if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL) + if( ( nd = npc_checknear(sd, map_id2bl(sd->npc_shopid)) ) == NULL || nd->subtype != SHOP ) + { return 1; - nd = nd->master_nd; //For OnSell triggers. + } - if( nd ) + z = 0; + + // verify the sell list + for( i = 0; i < n; i++ ) { - // discard old contents - script_cleararray_pc(sd, "@sold_nameid", (void*)0); - script_cleararray_pc(sd, "@sold_quantity", (void*)0); - } + int nameid, amount, idx, value; - for(i=0,z=0;i=MAX_INVENTORY || qty < 0) - break; - - nameid=sd->status.inventory[idx].nameid; - if (nameid == 0 || !sd->inventory_data[idx] || - sd->status.inventory[idx].amount < qty) - break; - - z+=(double)qty*pc_modifysellvalue(sd,sd->inventory_data[idx]->value_sell); + idx = item_list[i*2]-2; + amount = item_list[i*2+1]; - if(sd->inventory_data[idx]->type == IT_PETEGG && - sd->status.inventory[idx].card[0] == CARD0_PET) + if( idx >= MAX_INVENTORY || idx < 0 || amount < 0 ) { - if(search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0) - intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1],sd->status.inventory[idx].card[2])); + return 1; + } + + nameid = sd->status.inventory[idx].nameid; + + if( !nameid || !sd->inventory_data[idx] || sd->status.inventory[idx].amount < amount ) + { + return 1; + } + + if( nd->master_nd ) + {// Script-controlled shops decide by themselves, what can be sold and at what price. + continue; } - if(log_config.enable_logs&0x20) //Logs items, Sold to NPC (S)hop [Lupus] - log_pick_pc(sd, "S", nameid, -qty, &sd->status.inventory[idx]); + value = pc_modifysellvalue(sd, sd->inventory_data[idx]->value_sell); + + z+= (double)value*amount; + } + + if( nd->master_nd ) + {// Script-controlled shops + return npc_selllist_sub(sd, n, item_list, nd->master_nd); + } + + // delete items + for( i = 0; i < n; i++ ) + { + int nameid, amount, idx; + + idx = item_list[i*2]-2; + amount = item_list[i*2+1]; + nameid = sd->status.inventory[idx].nameid; + + //Logs items, Sold to NPC (S)hop [Lupus] + if( log_config.enable_logs&0x20 ) + log_pick_pc(sd, "S", nameid, -amount, &sd->status.inventory[idx]); + //Logs - if( nd ) + if( sd->inventory_data[idx]->type == IT_PETEGG && sd->status.inventory[idx].card[0] == CARD0_PET ) { - script_setarray_pc(sd, "@sold_nameid", i, (void*)(intptr)sd->status.inventory[idx].nameid, &key_nameid); - script_setarray_pc(sd, "@sold_quantity", i, (void*)(intptr)qty, &key_amount); + if( search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0 ) + { + intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1], sd->status.inventory[idx].card[2])); + } } - pc_delitem(sd,idx,qty,0,6); + + pc_delitem(sd, idx, amount, 0, 6); } - if (z > MAX_ZENY) z = MAX_ZENY; + if( z > MAX_ZENY ) + z = MAX_ZENY; - if(log_config.zeny) //Logs (S)hopping Zeny [Lupus] + //Logs (S)hopping Zeny [Lupus] + if( log_config.zeny ) log_zeny(sd, "S", sd, (int)z); + //Logs - pc_getzeny(sd,(int)z); - - if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) { - if (sd->status.skill[MC_OVERCHARGE].flag != 0) + pc_getzeny(sd, (int)z); + + // custom merchant shop exp bonus + if( battle_config.shop_exp > 0 && z > 0 && ( skill = pc_checkskill(sd,MC_OVERCHARGE) ) > 0) + { + if( sd->status.skill[MC_OVERCHARGE].flag != 0 ) skill = sd->status.skill[MC_OVERCHARGE].flag - 2; - if (skill > 0) { + if( skill > 0 ) + { z = z * (double)skill * (double)battle_config.shop_exp/10000.; - if (z < 1) + if( z < 1 ) z = 1; - pc_gainexp(sd,NULL,0,(int)z, false); + pc_gainexp(sd, NULL, 0, (int)z, false); } } - - if(nd) { - char npc_ev[NAME_LENGTH*2+3]; - snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnSellItem", nd->exname); - npc_event(sd, npc_ev, 0); - } - - if (ifd); - return 1; - } + return 0; } -- cgit v1.2.3-70-g09d2