diff options
author | ai4rei <ai4rei@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2010-12-23 16:05:49 +0000 |
---|---|---|
committer | ai4rei <ai4rei@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2010-12-23 16:05:49 +0000 |
commit | 631630d348375d8e64b57aceb09ee5da13f76fca (patch) | |
tree | aa7926da2b598b8c05b9b3dd572d2559b38aaaed /src/map | |
parent | 4b60855ad391b0b827d76773dcacb69f8baa8729 (diff) | |
download | hercules-631630d348375d8e64b57aceb09ee5da13f76fca.tar.gz hercules-631630d348375d8e64b57aceb09ee5da13f76fca.tar.bz2 hercules-631630d348375d8e64b57aceb09ee5da13f76fca.tar.xz hercules-631630d348375d8e64b57aceb09ee5da13f76fca.zip |
* 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
Diffstat (limited to 'src/map')
-rw-r--r-- | src/map/npc.c | 166 |
1 files changed, 104 insertions, 62 deletions
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 <index,amount> +/// @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<n;i++) { - int nameid, idx; - short qty; - idx = item_list[i*2]-2; - qty = (short)item_list[i*2+1]; - - if (idx <0 || idx >=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 (i<n) { - //Error/Exploit... of some sort. If we return 1, the client will not mark - //any item as deleted even though a few were sold. In such a case, we - //have no recourse but to kick them out so their inventory will refresh - //correctly on relog. [Skotlex] - if (i) set_eof(sd->fd); - return 1; - } + return 0; } |