int merc_hom_dead(struct homun_data *hd, int flag) { struct map_session_data *sd = hd->master; struct s_homunculus *hom; struct item tmp_item; int rate; sd = hd->master; hd = sd->hd; hom = &hd->homunculus; rate =(hd->homunculus.class_) + 5530; merc_save(hd); sd = hd->master; pc_delitem(sd, pc_search_inventory(sd,690), 1, 0, 0); //sd->status.hom_id = 0; sd->status.lock = 0; merc_hom_hungry_timer_delete(hd); hd->homunculus.hp = 0; hom->char_id = sd->status.char_id; memset(&tmp_item,0,sizeof(tmp_item)); tmp_item.nameid = rate; tmp_item.identify = 1; tmp_item.card[0] = CARD0_HUN; tmp_item.card[1] = GetWord(hd->homunculus.hom_id,0); tmp_item.card[2] = GetWord(hd->homunculus.hom_id,1); tmp_item.card[3] = 1; if((flag = pc_additem(sd,&tmp_item,1))) { clif_additem(sd,0,0,flag); map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); } hd->homunculus.hp = 0; hd->homunculus.intimacy = 0; return unit_remove_map(&hd->bl,CLR_OUTSIGHT); }
void mail_deliveryfail(struct map_session_data *sd, struct mail_message *msg) { nullpo_retv(sd); nullpo_retv(msg); if( msg->item.amount > 0 ) { // Item recieve (due to failure) log_pick_pc(sd, LOG_TYPE_MAIL, msg->item.nameid, msg->item.amount, &msg->item); pc_additem(sd, &msg->item, msg->item.amount); } if( msg->zeny > 0 ) { //Zeny recieve (due to failure) log_zeny(sd, LOG_TYPE_MAIL, sd, msg->zeny); sd->status.zeny += msg->zeny; clif_updatestatus(sd, SP_ZENY); } clif_Mail_send(sd->fd, true); }
void mail_getattachment(struct map_session_data* sd, int zeny, struct item* item) { if( item->nameid > 0 && item->amount > 0 ) { pc_additem(sd, item, item->amount); if(log_config.enable_logs&0x2000) log_pick_pc(sd, "E", item->nameid, item->amount, item, item->serial); if( battle_config.lootevent & 8 ) { pc_setglobalreg( sd, "LastLootID", item->nameid ); //Last lootet Item ID pc_setglobalreg( sd, "LastLootAmount", item->amount ); //Last looted Item Amount npc_event_doall_id( "OnLoot", sd->bl.id ); } clif_Mail_getattachment(sd->fd, 0); } if( zeny > 0 ) { //Zeny recieve if(log_config.zeny) log_zeny(sd, "E", sd, zeny); pc_getzeny(sd, zeny); } }
void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count) { int zeny = 0; unsigned int i, weight, listidx, k; struct map_session_data* pl_sd; if( count == 0 ) {// nothing to do return; } if( !battle_config.feature_buying_store || pc_istrading(sd) ) {// not allowed to sell clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( !pc_can_give_items(pc_isGM(sd)) ) {// custom: GM is not allowed to sell clif_displaymessage(sd->fd, msg_txt(246)); clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( ( pl_sd = map_id2sd(account_id) ) == NULL || !pl_sd->state.buyingstore || pl_sd->buyer_id != buyer_id ) {// not online, not buying or not same store clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( !searchstore_queryremote(sd, account_id) && ( sd->bl.m != pl_sd->bl.m || !check_distance_bl(&sd->bl, &pl_sd->bl, AREA_SIZE) ) ) {// out of view range clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } searchstore_clearremote(sd); if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit ) {// buyer lost zeny in the mean time? fix the limit pl_sd->buyingstore.zenylimit = pl_sd->status.zeny; } weight = pl_sd->weight; // check item list for( i = 0; i < count; i++ ) {// itemlist: <index>.W <name id>.W <amount>.W unsigned short nameid, amount; int index; index = RBUFW(itemlist,i*6+0)-2; nameid = RBUFW(itemlist,i*6+2); amount = RBUFW(itemlist,i*6+4); if( i ) {// duplicate check. as the client does this too, only malicious intent should be caught here ARR_FIND( 0, i, k, RBUFW(itemlist,k*6+0)-2 == index ); if( k != i ) {// duplicate ShowWarning("buyingstore_trade: Found duplicate item on selling list (prevnameid=%hu, prevamount=%hu, nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", RBUFW(itemlist,k*6+2), RBUFW(itemlist,k*6+4), nameid, amount, sd->status.account_id, sd->status.char_id); clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } } if( index < 0 || index >= ARRAYLENGTH(sd->status.inventory) || sd->inventory_data[index] == NULL || sd->status.inventory[index].nameid != nameid || sd->status.inventory[index].amount < amount ) {// invalid input clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_isGM(sd), pc_isGM(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) {// non-tradable item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid ); if( listidx == pl_sd->buyingstore.slots || pl_sd->buyingstore.items[listidx].amount == 0 ) {// there is no such item or the buyer has already bought all of them clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } if( pl_sd->buyingstore.items[listidx].amount < amount ) {// buyer does not need that much of the item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_COUNT, nameid); return; } if( pc_checkadditem(pl_sd, nameid, amount) == ADDITEM_OVERAMOUNT ) {// buyer does not have enough space for this item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } if( amount*(unsigned int)sd->inventory_data[index]->weight > pl_sd->max_weight-weight ) {// normally this is not supposed to happen, as the total weight is // checked upon creation, but the buyer could have gained items clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } weight+= amount*sd->inventory_data[index]->weight; if( amount*pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit-zeny ) {// buyer does not have enough zeny clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_ZENY, nameid); return; } zeny+= amount*pl_sd->buyingstore.items[listidx].price; } // process item list for( i = 0; i < count; i++ ) {// itemlist: <index>.W <name id>.W <amount>.W unsigned short nameid, amount; int index; index = RBUFW(itemlist,i*6+0)-2; nameid = RBUFW(itemlist,i*6+2); amount = RBUFW(itemlist,i*6+4); ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid ); zeny = amount*pl_sd->buyingstore.items[listidx].price; // log log_pick_pc(sd, LOG_TYPE_BUYING_STORE, nameid, -((int)amount), &sd->status.inventory[index]); log_pick_pc(pl_sd, LOG_TYPE_BUYING_STORE, nameid, amount, &sd->status.inventory[index]); log_zeny(sd, LOG_TYPE_BUYING_STORE, pl_sd, zeny); // move item pc_additem(pl_sd, &sd->status.inventory[index], amount); pc_delitem(sd, index, amount, 1, 0); pl_sd->buyingstore.items[listidx].amount-= amount; // pay up pc_payzeny(pl_sd, zeny); pc_getzeny(sd, zeny); pl_sd->buyingstore.zenylimit-= zeny; // notify clients clif_buyingstore_delete_item(sd, index, amount, pl_sd->buyingstore.items[listidx].price); clif_buyingstore_update_item(pl_sd, nameid, amount); } // check whether or not there is still something to buy ARR_FIND( 0, pl_sd->buyingstore.slots, i, pl_sd->buyingstore.items[i].amount != 0 ); if( i == pl_sd->buyingstore.slots ) {// everything was bought clif_buyingstore_trade_failed_buyer(pl_sd, BUYINGSTORE_TRADE_BUYER_NO_ITEMS); } else if( pl_sd->buyingstore.zenylimit == 0 ) {// zeny limit reached clif_buyingstore_trade_failed_buyer(pl_sd, BUYINGSTORE_TRADE_BUYER_ZENY); } else {// continue buying return; } // cannot continue buying buyingstore_close(pl_sd); // remove auto-trader if( pl_sd->state.autotrade ) { map_quit(pl_sd); } }
/** * Purchase item(s) from a shop * @param sd : buyer player session * @param aid : account id of vender * @param uid : shop unique id * @param data : items data who would like to purchase \n * data := {<index>.w <amount>.w }[count] * @param count : number of different items he's trying to buy */ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count) { int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING]; double z; struct s_vending vending[MAX_VENDING]; // against duplicate packets struct map_session_data* vsd = map_id2sd(aid); nullpo_retv(sd); if( vsd == NULL || !vsd->state.vending || vsd->bl.id == sd->bl.id ) return; // invalid shop if( vsd->vender_id != uid ) { // shop has changed clif_buyvending(sd, 0, 0, 6); // store information was incorrect return; } if( !searchstore_queryremote(sd, aid) && ( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) ) ) return; // shop too far away searchstore_clearremote(sd); if( count < 1 || count > MAX_VENDING || count > vsd->vend_num ) return; // invalid amount of purchased items blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory // duplicate item in vending to check hacker with multiple packets memcpy(&vending, &vsd->vending, sizeof(vsd->vending)); // copy vending list // some checks z = 0.; // zeny counter w = 0; // weight counter for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); short idx = *(uint16*)(data + 4*i + 2); idx -= 2; if( amount <= 0 ) return; // check of item index in the cart if( idx < 0 || idx >= MAX_CART ) return; ARR_FIND( 0, vsd->vend_num, j, vsd->vending[j].index == idx ); if( j == vsd->vend_num ) return; //picked non-existing item else vend_list[i] = j; z += ((double)vsd->vending[j].value * (double)amount); if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY ) { clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny return; } if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) { clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow return; } w += itemdb_weight(vsd->status.cart[idx].nameid) * amount; if( w + sd->weight > sd->max_weight ) { clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight return; } //Check to see if cart/vend info is in sync. if( vending[j].amount > vsd->status.cart[idx].amount ) vending[j].amount = vsd->status.cart[idx].amount; // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples). // here, we check cumulative amounts if( vending[j].amount < amount ) { // send more quantity is not a hack (an other player can have buy items just before) clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity return; } vending[j].amount -= amount; switch( pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount) ) { case CHKADDITEM_EXIST: break; //We'd add this item to the existing one (in buyers inventory) case CHKADDITEM_NEW: new_++; if (new_ > blank) return; //Buyer has no space in his inventory break; case CHKADDITEM_OVERAMOUNT: return; //too many items } } pc_payzeny(sd, (int)z, LOG_TYPE_VENDING, vsd); if( battle_config.vending_tax ) z -= z * (battle_config.vending_tax/10000.); pc_getzeny(vsd, (int)z, LOG_TYPE_VENDING, sd); for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); short idx = *(uint16*)(data + 4*i + 2); idx -= 2; // vending item pc_additem(sd, &vsd->status.cart[idx], amount, LOG_TYPE_VENDING); vsd->vending[vend_list[i]].amount -= amount; if( vsd->vending[vend_list[i]].amount ){ if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_db, vsd->vending[vend_list[i]].amount, vsd->vender_id, vsd->status.cart[idx].id ) != SQL_SUCCESS ){ Sql_ShowDebug( mmysql_handle ); } }else{ if( Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_db, vsd->vender_id, vsd->status.cart[idx].id ) != SQL_SUCCESS ){ Sql_ShowDebug( mmysql_handle ); } } pc_cart_delitem(vsd, idx, amount, 0, LOG_TYPE_VENDING); clif_vendingreport(vsd, idx, amount); //print buyer's name if( battle_config.buyer_name ) { char temp[256]; sprintf(temp, msg_txt(sd,265), sd->status.name); clif_disp_onlyself(vsd,temp,strlen(temp)); } } // compact the vending list for( i = 0, cursor = 0; i < vsd->vend_num; i++ ) { if( vsd->vending[i].amount == 0 ) continue; if( cursor != i ) { // speedup vsd->vending[cursor].index = vsd->vending[i].index; vsd->vending[cursor].amount = vsd->vending[i].amount; vsd->vending[cursor].value = vsd->vending[i].value; } cursor++; } vsd->vend_num = cursor; //Always save BOTH: customer (buyer) and vender if( save_settings&2 ) { chrif_save(sd,0); chrif_save(vsd,0); } //check for @AUTOTRADE users [durf] if( vsd->state.autotrade ) { //see if there is anything left in the shop ARR_FIND( 0, vsd->vend_num, i, vsd->vending[i].amount > 0 ); if( i == vsd->vend_num ) { //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex] vending_closevending(vsd); map_quit(vsd); //They have no reason to stay around anymore, do they? } } }
/*========================================== * Purchase item(s) from a shop *------------------------------------------*/ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count) { int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING]; double z; struct s_vending vending[MAX_VENDING]; // against duplicate packets struct map_session_data* vsd = map_id2sd(aid); char output[256]; nullpo_retv(sd); if( vsd == NULL || !vsd->state.vending || vsd->bl.id == sd->bl.id ) return; // invalid shop if( vsd->vender_id != uid ) {// shop has changed clif_buyvending(sd, 0, 0, 6); // store information was incorrect return; } if( !searchstore_queryremote(sd, aid) && ( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) ) ) return; // shop too far away searchstore_clearremote(sd); if( count < 1 || count > MAX_VENDING || count > vsd->vend_num ) return; // invalid amount of purchased items blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory // duplicate item in vending to check hacker with multiple packets memcpy(&vending, &vsd->vending, sizeof(vsd->vending)); // copy vending list // some checks z = 0.; // zeny counter w = 0; // weight counter for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); short idx = *(uint16*)(data + 4*i + 2); idx -= 2; if( amount <= 0 ) return; // check of item index in the cart if( idx < 0 || idx >= MAX_CART ) return; ARR_FIND( 0, vsd->vend_num, j, vsd->vending[j].index == idx ); if( j == vsd->vend_num ) return; //picked non-existing item else vend_list[i] = j; z += ((double)vsd->vending[j].value * (double)amount); if( !vsd->vend_coin || vsd->vend_coin == battle_config.vending_zeny_id ) { // Normal Vending - Zeny Option if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY ) { clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny return; } if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) { clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow return; } } else if( battle_config.vending_cash_id && vsd->vend_coin == battle_config.vending_cash_id ) { // Cash Shop if( z > (double)sd->cashPoints || z < 0. || z > (double)MAX_ZENY ) { sprintf(output,msg_txt(915),itemdb_jname(vsd->vend_coin)); clif_displaymessage(sd->fd,output); return; } if( z + (double)vsd->cashPoints > (double)MAX_ZENY && !battle_config.vending_over_max ) { sprintf(output,msg_txt(916),itemdb_jname(vsd->vend_coin)); clif_displaymessage(sd->fd,output); return; } } w += itemdb_weight(vsd->status.cart[idx].nameid) * amount; if( w + sd->weight > sd->max_weight ) { clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight return; } //Check to see if cart/vend info is in sync. if( vending[j].amount > vsd->status.cart[idx].amount ) vending[j].amount = vsd->status.cart[idx].amount; // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples). // here, we check cumulative amounts if( vending[j].amount < amount ) { // send more quantity is not a hack (an other player can have buy items just before) clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity return; } vending[j].amount -= amount; switch( pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount) ) { case ADDITEM_EXIST: break; //We'd add this item to the existing one (in buyers inventory) case ADDITEM_NEW: new_++; if (new_ > blank) return; //Buyer has no space in his inventory break; case ADDITEM_OVERAMOUNT: return; //too many items } } // Payments if( !vsd->vend_coin || vsd->vend_coin == battle_config.vending_zeny_id ) { if( log_config.zeny > 0 ) log_zeny(vsd, "V", sd, (int)z); //Logs (V)ending Zeny [Lupus] pc_payzeny(sd, (int)z); if( battle_config.vending_tax || (vsd->state.autotrade && battle_config.at_tax && !pc_isPremium(vsd)) ) z -= z * ((battle_config.vending_tax + ((vsd->state.autotrade && !pc_isPremium(vsd)) ? battle_config.at_tax : 0)) / 10000.); pc_getzeny(vsd, (int)z); } else if( battle_config.vending_cash_id && vsd->vend_coin == battle_config.vending_cash_id ) { pc_paycash(sd,(int)z,0); pc_getcash(vsd,(int)z,0); } else { if( z < 0. || (i = pc_search_inventory(sd,vsd->vend_coin)) < 0 || z > (double)sd->status.inventory[i].amount ) { sprintf(output,msg_txt(915),itemdb_jname(vsd->vend_coin)); clif_displaymessage(sd->fd,output); return; } switch( pc_checkadditem(vsd,vsd->vend_coin,(int)z) ) { case ADDITEM_NEW: if( pc_inventoryblank(vsd) > 0 ) break; case ADDITEM_OVERAMOUNT: sprintf(output,msg_txt(916),itemdb_jname(vsd->vend_coin)); clif_displaymessage(sd->fd,output); return; } pc_additem(vsd,&sd->status.inventory[i],(int)z); pc_delitem(sd,i,(int)z,0,6); } for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); short idx = *(uint16*)(data + 4*i + 2); idx -= 2; //Logs sold (V)ending items [Lupus] if(log_config.enable_logs&0x4) { log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, &vsd->status.cart[idx], vsd->status.cart[idx].serial ); log_pick_pc( sd, "V", vsd->status.cart[idx].nameid, amount, &vsd->status.cart[idx], vsd->status.cart[idx].serial ); } // vending item pc_additem(sd, &vsd->status.cart[idx], amount); vsd->vending[vend_list[i]].amount -= amount; pc_cart_delitem(vsd, idx, amount, 0); clif_vendingreport(vsd, idx, amount); //print buyer's name if( battle_config.buyer_name ) { char temp[256]; sprintf(temp, msg_txt(265), sd->status.name); clif_disp_onlyself(vsd,temp,strlen(temp)); } } // compact the vending list for( i = 0, cursor = 0; i < vsd->vend_num; i++ ) { if( vsd->vending[i].amount == 0 ) continue; if( cursor != i ) // speedup { vsd->vending[cursor].index = vsd->vending[i].index; vsd->vending[cursor].amount = vsd->vending[i].amount; vsd->vending[cursor].value = vsd->vending[i].value; } cursor++; } vsd->vend_num = cursor; //Always save BOTH: buyer and customer if( save_settings&2 ) { chrif_save(sd,0); chrif_save(vsd,0); } //check for @AUTOTRADE users [durf] if( vsd->state.autotrade ) { //see if there is anything left in the shop ARR_FIND( 0, vsd->vend_num, i, vsd->vending[i].amount > 0 ); if( i == vsd->vend_num ) { //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex] vending_closevending(vsd); map_quit(vsd); //They have no reason to stay around anymore, do they? } } }
//Does party loot. first_charid holds the charid of the player who has time priority to take the item. int party_share_loot(struct party_data* p, struct map_session_data* sd, struct item* item, int first_charid) { TBL_PC* target = NULL; int i; if (p && p->party.item&2 && (first_charid || !(battle_config.party_share_type&1))) { //item distribution to party members. if (battle_config.party_share_type&2) { // Round Robin TBL_PC* psd; i = p->itemc; do { i++; if (i >= MAX_PARTY) i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc" if( (psd = p->data[i].sd) == NULL || sd->bl.m != psd->bl.m || pc_isdead(psd) || (battle_config.idle_no_share && pc_isidle(psd)) ) continue; if (pc_additem(psd,item,item->amount,LOG_TYPE_PICKDROP_PLAYER)) continue; //Chosen char can't pick up loot. //Successful pick. p->itemc = i; target = psd; break; } while (i != p->itemc); } else { // Random pick TBL_PC* psd[MAX_PARTY]; int count = 0; //Collect pick candidates for (i = 0; i < MAX_PARTY; i++) { if( (psd[count] = p->data[i].sd) == NULL || psd[count]->bl.m != sd->bl.m || pc_isdead(psd[count]) || (battle_config.idle_no_share && pc_isidle(psd[count])) ) continue; count++; } while (count > 0) { //Pick a random member. i = rnd()%count; if (pc_additem(psd[i],item,item->amount,LOG_TYPE_PICKDROP_PLAYER)) { // Discard this receiver. psd[i] = psd[count-1]; count--; } else { // Successful pick. target = psd[i]; break; } } } } if (!target) { target = sd; //Give it to the char that picked it up if ((i = pc_additem(sd,item,item->amount,LOG_TYPE_PICKDROP_PLAYER))) return i; } if( p && battle_config.party_show_share_picker && battle_config.show_picker_item_type&(1<<itemdb_type(item->nameid)) ) clif_party_show_picker(target, item); return 0; }
/*========================================== * 取引許諾(trade押し) *------------------------------------------ */ void trade_tradecommit(struct map_session_data *sd) { struct map_session_data *target_sd; nullpo_retv(sd); target_sd = map_id2sd(sd->trade.partner); if(target_sd && target_sd->bl.prev) { if (sd->state.deal_locked >= 1 && target_sd->state.deal_locked >= 1) { // both have pressed 'ok' if (sd->state.deal_locked < 2) // set locked to 2 sd->state.deal_locked = 2; if(target_sd->state.deal_locked == 2) { // the other one pressed 'trade' too int trade_i, idx, flag; // checks quantity of items if (!trade_check(sd, target_sd)) { // this function do like the real trade, but with virtual inventories trade_tradecancel(sd); return; } // check zenys if (sd->trade.zeny > sd->status.zeny || target_sd->trade.zeny > target_sd->status.zeny || sd->status.zeny - sd->trade.zeny + target_sd->trade.zeny > MAX_ZENY || target_sd->status.zeny - target_sd->trade.zeny + sd->trade.zeny > MAX_ZENY) { trade_tradecancel(sd); return; } // do trade // ---- of items for(trade_i = 0; trade_i < MAX_DEAL_ITEMS; trade_i++) { if(sd->trade.item_amount[trade_i] != 0) { idx = sd->trade.item_index[trade_i] - 2; if (itemdb_isdropable(sd->status.inventory[idx].nameid)) { flag = pc_additem(target_sd, &sd->status.inventory[idx], sd->trade.item_amount[trade_i]); if(flag==0) pc_delitem(sd, idx, sd->trade.item_amount[trade_i], 1, 0); else clif_additem(sd, idx, sd->trade.item_amount[trade_i], 0); } sd->trade.item_amount[trade_i] = 0; } sd->trade.item_index[trade_i] = 0; if(target_sd->trade.item_amount[trade_i] != 0) { idx = target_sd->trade.item_index[trade_i] - 2; if (itemdb_isdropable(target_sd->status.inventory[idx].nameid)) { flag = pc_additem(sd, &target_sd->status.inventory[idx], target_sd->trade.item_amount[trade_i]); if(flag==0) pc_delitem(target_sd, idx, target_sd->trade.item_amount[trade_i], 1, 0); else clif_additem(target_sd, idx, target_sd->trade.item_amount[trade_i], 0); } target_sd->trade.item_amount[trade_i] = 0; } target_sd->trade.item_index[trade_i] = 0; } // ---- of zenys if(sd->trade.zeny || target_sd->trade.zeny) { sd->status.zeny = sd->status.zeny - sd->trade.zeny + target_sd->trade.zeny; target_sd->status.zeny = target_sd->status.zeny - target_sd->trade.zeny + sd->trade.zeny; clif_updatestatus(sd, SP_ZENY); clif_updatestatus(target_sd, SP_ZENY); } sd->trade.zeny = 0; target_sd->trade.zeny = 0; // clean up variables sd->state.deal_locked = 0; sd->state.deal_mode = 0; sd->trade.partner = 0; target_sd->state.deal_locked = 0; target_sd->state.deal_mode = 0; target_sd->trade.partner = 0; clif_tradecompleted(sd,0); clif_tradecompleted(target_sd,0); // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players chrif_save(sd,0); chrif_save(target_sd,0); } } } else { trade_tradecancel(sd); } return; }
/*========================================== * 取引許諾(trade押し) *------------------------------------------ */ void trade_tradecommit(dumb_ptr<map_session_data> sd) { dumb_ptr<map_session_data> target_sd; int trade_i; nullpo_retv(sd); if ((target_sd = map_id2sd(account_to_block(sd->trade_partner))) != nullptr) { MAP_LOG_PC(sd, " TRADECOMMIT WITH %d GIVE %d GET %d"_fmt, target_sd->status_key.char_id, sd->deal_zeny, target_sd->deal_zeny); if ((sd->deal_locked >= 1) && (target_sd->deal_locked >= 1)) { // both have pressed 'ok' if (sd->deal_locked < 2) { sd->deal_locked = 2; } // set locked to 2 if (target_sd->deal_locked == 2) { // the other one pressed 'trade' too if (sd->deal_zeny > sd->status.zeny) { sd->deal_zeny = 0; trade_tradecancel(sd); MAP_LOG_PC(sd, " TRADECANCEL"_fmt); return; } if (target_sd->deal_zeny > target_sd->status.zeny) { target_sd->deal_zeny = 0; trade_tradecancel(sd); MAP_LOG_PC(sd, " TRADECANCEL"_fmt); return; } sd->trade_partner = AccountId(); target_sd->trade_partner = AccountId(); for (trade_i = 0; trade_i < TRADE_MAX; trade_i++) { if (sd->deal_item_amount[trade_i] != 0) { assert (sd->deal_item_index[trade_i].ok()); IOff0 n = sd->deal_item_index[trade_i].unshift(); PickupFail flag = pc_additem(target_sd, &sd->status.inventory[n], sd->deal_item_amount[trade_i]); if (flag == PickupFail::OKAY) pc_delitem(sd, n, sd->deal_item_amount[trade_i], 1); else clif_additem(sd, n, sd->deal_item_amount[trade_i], PickupFail::OKAY); sd->deal_item_index[trade_i] = IOff2::from(0); sd->deal_item_amount[trade_i] = 0; } if (target_sd->deal_item_amount[trade_i] != 0) { assert (target_sd->deal_item_index[trade_i].ok()); IOff0 n = target_sd->deal_item_index[trade_i].unshift(); PickupFail flag = pc_additem(sd, &target_sd->status.inventory[n], target_sd->deal_item_amount[trade_i]); if (flag == PickupFail::OKAY) pc_delitem(target_sd, n, target_sd->deal_item_amount[trade_i], 1); else clif_additem(target_sd, n, target_sd->deal_item_amount[trade_i], PickupFail::OKAY); target_sd->deal_item_index[trade_i] = IOff2::from(0); target_sd->deal_item_amount[trade_i] = 0; } } if (sd->deal_zeny) { int deal = sd->deal_zeny; sd->deal_zeny = 0; sd->status.zeny -= deal; clif_updatestatus(sd, SP::ZENY); target_sd->status.zeny += deal; clif_updatestatus(target_sd, SP::ZENY); } if (target_sd->deal_zeny) { int deal = target_sd->deal_zeny; target_sd->deal_zeny = 0; target_sd->status.zeny -= deal; clif_updatestatus(target_sd, SP::ZENY); sd->status.zeny += deal; clif_updatestatus(sd, SP::ZENY); } sd->deal_locked = 0; target_sd->deal_locked = 0; clif_tradecompleted(sd, 0); clif_tradecompleted(target_sd, 0); MAP_LOG_PC(sd, " TRADEOK"_fmt); } } } }
int party_share_loot(struct party *p, TBL_PC *sd, struct item *item_data) { TBL_PC *target=NULL; int i; if (p && p->item&2) { //item distribution to party members. if (battle_config.party_share_type) { //Round Robin TBL_PC *psd; i = p->itemc; do { i++; if (i >= MAX_PARTY) i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc" if ((psd=p->member[i].sd)==NULL || sd->bl.m != psd->bl.m) continue; if (pc_additem(psd,item_data,item_data->amount)) continue; //Chosen char can't pick up loot. //Successful pick. p->itemc = i; target = psd; break; } while (i != p->itemc); } else { //Random pick TBL_PC *psd[MAX_PARTY]; int count=0; //Collect pick candidates for (i = 0; i < MAX_PARTY; i++) { if ((psd[count]=p->member[i].sd) && psd[count]->bl.m == sd->bl.m) count++; } if (count > 0) { //Pick a random member. do { i = rand()%count; if (pc_additem(psd[i],item_data,item_data->amount)) { //Discard this receiver. psd[i] = psd[count-1]; count--; } else { //Successful pick. target = psd[i]; break; } } while (count > 0); } } } if (!target) { //Give it to the owner. target = sd; if ((i=pc_additem(sd,item_data,item_data->amount))) return i; } if(log_config.pick) //Logs items, taken by (P)layers [Lupus] log_pick(target, "P", 0, item_data->nameid, item_data->amount, item_data); //Logs if(battle_config.party_show_share_picker && target != sd){ char output[80]; sprintf(output, "%s acquired the item.",target->status.name); clif_disp_onlyself(sd,output,strlen(output)); } return 0; }
int createdb_start(USER *sd) { int item_c=RFIFOB(sd->fd,5); int item[10],item_amount[10]; int item_s[10]; int len=6; int x; int curitem; int rate; struct item *fl; for(x=0;x<item_c;x++) { curitem=RFIFOB(sd->fd,len)-1; item_s[x]=curitem; item[x]=sd->status.inventory[curitem].id; if(itemdb_type(item[x])<3 || itemdb_type(item[x])>17) { item_amount[x]=RFIFOB(sd->fd,len+1); len+=2; } else { item_amount[x]=1; len+=1; } } sd->creation_works=0; sd->creation_item=0; sd->creation_itemamount=0; sd->creation_rate=0; sd->creation_faileditem=0; sd->creation_failedamount=0; sd->creation_failrate=0; if(item_c) { create_db->foreach(create_db,create_check_sub,sd,item_c,item,item_amount); } else { return 0; } if(sd->creation_works) { for(x=0;x<item_c;x++) { // printf("%d, %d\n",item_s[x],item_amount[x]); pc_delitem(sd,item_s[x],item_amount[x],25); } //rate checking :) rate = rnd(100000); if (sd->creation_rate >= rate) { CALLOC(fl,struct item,1); fl->id=sd->creation_item; fl->dura=itemdb_dura(fl->id); fl->amount=sd->creation_itemamount; pc_additem(sd,fl); FREE(fl); clif_playsound(&sd->bl, 123);//play sound on success clif_sendminitext(sd, "You were successful!",0); } else { //if failed but can recover an item if (sd->creation_failrate >= rate)
/*========================================== * 取引許諾(trade押し) *------------------------------------------ */ void trade_tradecommit(struct map_session_data *sd) { struct map_session_data *target_sd; int trade_i; int flag; nullpo_retv(sd); if (sd->state.trading && (target_sd = map_id2sd(sd->trade_partner)) != NULL) { if ((sd->state.deal_locked >= 1) && (target_sd->state.deal_locked >= 1)) { // both have pressed 'ok' if (sd->state.deal_locked < 2) { // set locked to 2 sd->state.deal_locked = 2; } if (target_sd->state.deal_locked == 2) { // the other one pressed 'trade' too // check exploit (trade more items that you have) if (impossible_trade_check(sd)) { trade_tradecancel(sd); return; } // check exploit (trade more items that you have) if (impossible_trade_check(target_sd)) { trade_tradecancel(target_sd); return; } // check zenys value against hackers if (sd->deal.zeny >= 0 && sd->deal.zeny <= MAX_ZENY && sd->deal.zeny <= sd->status.zeny && // check amount (target_sd->status.zeny + sd->deal.zeny) <= MAX_ZENY && // fix positiv overflow target_sd->deal.zeny >= 0 && target_sd->deal.zeny <= MAX_ZENY && target_sd->deal.zeny <= target_sd->status.zeny && // check amount (sd->status.zeny + target_sd->deal.zeny) <= MAX_ZENY) { // fix positiv overflow // check for full inventory (can not add traded items) if (!trade_check(sd)) { // check the both players trade_tradecancel(sd); return; } // trade is accepted for(trade_i = 0; trade_i < 10; trade_i++) { if (sd->deal.item[trade_i].amount != 0) { int n = sd->deal.item[trade_i].index; if (sd->status.inventory[n].amount < sd->deal.item[trade_i].amount) sd->deal.item[trade_i].amount = sd->status.inventory[n].amount; log_trade(sd, target_sd, n, sd->deal.item[trade_i].amount); flag = pc_additem(target_sd, &sd->status.inventory[n], sd->deal.item[trade_i].amount); if (flag == 0) { //Logs (T)rade [Lupus] if(log_config.pick > 0 ) log_pick(sd, "T", 0, sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]); log_pick(target_sd, "T", 0, sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]); //Logs pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1); } else { clif_additem(sd, n, sd->deal.item[trade_i].amount, 0); } sd->deal.item[trade_i].index = 0; sd->deal.item[trade_i].amount = 0; } if (target_sd->deal.item[trade_i].amount != 0) { int n = target_sd->deal.item[trade_i].index; if (target_sd->status.inventory[n].amount < target_sd->deal.item[trade_i].amount) target_sd->deal.item[trade_i].amount = target_sd->status.inventory[n].amount; log_trade(target_sd, sd, n, target_sd->deal.item[trade_i].amount); flag = pc_additem(sd, &target_sd->status.inventory[n], target_sd->deal.item[trade_i].amount); if (flag == 0) { //Logs (T)rade [Lupus] if(log_config.pick > 0 ) log_pick(target_sd, "T", 0, target_sd->status.inventory[n].nameid, -(target_sd->deal.item[trade_i].amount), &target_sd->status.inventory[n]); log_pick(sd, "T", 0, target_sd->status.inventory[n].nameid, target_sd->deal.item[trade_i].amount, &target_sd->status.inventory[n]); //Logs pc_delitem(target_sd, n, target_sd->deal.item[trade_i].amount, 1); } else { clif_additem(target_sd, n, target_sd->deal.item[trade_i].amount, 0); } target_sd->deal.item[trade_i].index = 0; target_sd->deal.item[trade_i].amount = 0; } } if (sd->deal.zeny) { //Logs Zeny (T)rade [Lupus] if(log_config.zeny > 0 ) log_zeny(target_sd, "T", sd, sd->deal.zeny); //Logs sd->status.zeny -= sd->deal.zeny; target_sd->status.zeny += sd->deal.zeny; } if (target_sd->deal.zeny) { //Logs Zeny (T)rade [Lupus] if(log_config.zeny > 0 ) log_zeny(sd, "T", target_sd, target_sd->deal.zeny); //Logs target_sd->status.zeny -= target_sd->deal.zeny; sd->status.zeny += target_sd->deal.zeny; } if (sd->deal.zeny || target_sd->deal.zeny) { clif_updatestatus(sd, SP_ZENY); sd->deal.zeny = 0; clif_updatestatus(target_sd, SP_ZENY); target_sd->deal.zeny = 0; } sd->state.deal_locked = 0; sd->trade_partner = 0; sd->state.trading = 0; target_sd->state.deal_locked = 0; target_sd->trade_partner = 0; target_sd->state.trading = 0; clif_tradecompleted(sd, 0); clif_tradecompleted(target_sd, 0); // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players chrif_save(sd,0); // do pc_makesavestatus and save storage too chrif_save(target_sd,0); // do pc_makesavestatus and save storage too // zeny value was modified!!!! hacker with packet modified } else { trade_tradecancel(sd); } } } } }
/* ============================================================== bg_arena (0 EoS | 1 Boss | 2 TI | 3 CTF | 4 TD | 5 SC | 6 CON | 7 RUSH | 8 DOM) bg_result (0 Won | 1 Tie | 2 Lost) ============================================================== */ void bg_team_rewards(int bg_id, int nameid, int amount, int kafrapoints, int quest_id, const char *var, int add_value, int bg_arena, int bg_result) { struct battleground_data *bg; struct map_session_data *sd; struct item_data *id; struct item it; int i, j, flag, fame, get_amount, rank = 0, type; if( amount < 1 || (bg = bg_team_search(bg_id)) == NULL || (id = itemdb_exists(nameid)) == NULL ) return; if( battle_config.bg_reward_rates != 100 ) { // BG Reward Rates amount = amount * battle_config.bg_reward_rates / 100; kafrapoints = kafrapoints * battle_config.bg_reward_rates / 100; } bg_result = cap_value(bg_result,0,2); memset(&it,0,sizeof(it)); if( nameid == 7828 || nameid == 7829 || nameid == 7773 ) { it.nameid = nameid; it.identify = 1; } else nameid = 0; for( j = 0; j < MAX_BG_MEMBERS; j++ ) { if( (sd = bg->members[j].sd) == NULL ) continue; if( battle_config.bg_ranking_bonus ) { rank = 0; ARR_FIND(0,MAX_FAME_LIST,i,bgrank_fame_list[i].id == sd->status.char_id); if( i < MAX_FAME_LIST ) rank = 1; else { ARR_FIND(0,MAX_FAME_LIST,i,bg_fame_list[i].id == sd->status.char_id); if( i < MAX_FAME_LIST ) rank = 1; } } if( quest_id ) quest_add(sd,quest_id); pc_setglobalreg(sd,var,pc_readglobalreg(sd,var) + add_value); if( kafrapoints > 0 ) { get_amount = kafrapoints; if( rank ) get_amount += battle_config.bg_ranking_bonus * get_amount / 100; pc_getcash(sd,0,get_amount); } if( nameid && amount > 0 ) { get_amount = amount; if( rank ) get_amount += battle_config.bg_ranking_bonus * get_amount / 100; if( (flag = pc_additem(sd,&it,get_amount,LOG_TYPE_SCRIPT)) ) clif_additem(sd,0,0,flag); } type = bg->members[j].ranked ? 2 : 3; // Where to Add Fame switch( bg_result ) { case 0: // Won add2limit(sd->status.bgstats.win,1,USHRT_MAX); achievement_validate_bg(sd,ATB_VICTORY,1); fame = 100; if( sd->bmaster_flag ) { add2limit(sd->status.bgstats.leader_win,1,USHRT_MAX); achievement_validate_bg(sd,ATB_LEADER_VICTORY,1); fame += 25; } pc_addfame(sd,fame,type); switch( bg_arena ) { case 0: add2limit(sd->status.bgstats.eos_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_EOS_VICTORY,1); break; case 1: add2limit(sd->status.bgstats.boss_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_BOSS_VICTORY,1); break; case 2: add2limit(sd->status.bgstats.ti_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_TI_VICTORY,1); break; case 3: add2limit(sd->status.bgstats.ctf_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_CTF_VICTORY,1); break; case 4: add2limit(sd->status.bgstats.td_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_TDM_VICTORY,1); break; case 5: add2limit(sd->status.bgstats.sc_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_SC_VICTORY,1); break; case 6: add2limit(sd->status.bgstats.cq_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_CON_VICTORY,1); break; case 7: add2limit(sd->status.bgstats.ru_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_RU_VICTORY,1); break; case 8: add2limit(sd->status.bgstats.dom_wins,1,USHRT_MAX); achievement_validate_bg(sd,ATB_DOM_VICTORY,1); break; } break; case 1: // Tie add2limit(sd->status.bgstats.tie,1,USHRT_MAX); fame = 75; if( sd->bmaster_flag ) { add2limit(sd->status.bgstats.leader_tie,1,USHRT_MAX); fame += 10; } pc_addfame(sd,fame,type); switch( bg_arena ) { case 0: add2limit(sd->status.bgstats.eos_tie,1,USHRT_MAX); break; case 1: add2limit(sd->status.bgstats.boss_tie,1,USHRT_MAX); break; case 2: add2limit(sd->status.bgstats.ti_tie,1,USHRT_MAX); break; case 3: add2limit(sd->status.bgstats.ctf_tie,1,USHRT_MAX); break; case 4: add2limit(sd->status.bgstats.td_tie,1,USHRT_MAX); break; case 5: add2limit(sd->status.bgstats.sc_tie,1,USHRT_MAX); break; // No Tie for Conquest or Rush case 8: add2limit(sd->status.bgstats.dom_tie,1,USHRT_MAX); break; } break; case 2: // Lost add2limit(sd->status.bgstats.lost,1,USHRT_MAX); achievement_validate_bg(sd,ATB_DEFEAT,1); fame = 50; if( sd->bmaster_flag ) { add2limit(sd->status.bgstats.leader_lost,1,USHRT_MAX); achievement_validate_bg(sd,ATB_LEADER_DEFEAT,1); } pc_addfame(sd,fame,type); switch( bg_arena ) { case 0: add2limit(sd->status.bgstats.eos_lost,1,USHRT_MAX); break; case 1: add2limit(sd->status.bgstats.boss_lost,1,USHRT_MAX); break; case 2: add2limit(sd->status.bgstats.ti_lost,1,USHRT_MAX); break; case 3: add2limit(sd->status.bgstats.ctf_lost,1,USHRT_MAX); break; case 4: add2limit(sd->status.bgstats.td_lost,1,USHRT_MAX); break; case 5: add2limit(sd->status.bgstats.sc_lost,1,USHRT_MAX); break; case 6: add2limit(sd->status.bgstats.cq_lost,1,USHRT_MAX); break; case 7: add2limit(sd->status.bgstats.ru_lost,1,USHRT_MAX); break; case 8: add2limit(sd->status.bgstats.dom_lost,1,USHRT_MAX); break; } break; } } }
//Does party loot. first_charid holds the charid of the player who has time priority to take the item. int party_share_loot(struct party_data* p, struct map_session_data* sd, struct item* item_data, int first_charid) { TBL_PC* target = NULL; int i; if (p && p->party.item&2 && (first_charid || !(battle_config.party_share_type&1))) { //item distribution to party members. if (battle_config.party_share_type&2) { //Round Robin TBL_PC* psd; i = p->itemc; do { i++; if (i >= MAX_PARTY) i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc" if( (psd = p->data[i].sd) == NULL || sd->bl.m != psd->bl.m || pc_isdead(psd) || (battle_config.idle_no_share && pc_isidle(psd)) ) continue; if (pc_additem(psd,item_data,item_data->amount)) continue; //Chosen char can't pick up loot. //Successful pick. p->itemc = i; target = psd; break; } while (i != p->itemc); } else { //Random pick TBL_PC* psd[MAX_PARTY]; int count = 0; //Collect pick candidates for (i = 0; i < MAX_PARTY; i++) { if( (psd[count] = p->data[i].sd) == NULL || psd[count]->bl.m != sd->bl.m || pc_isdead(psd[count]) || (battle_config.idle_no_share && pc_isidle(psd[count])) ) continue; count++; } while (count > 0) { //Pick a random member. i = rand()%count; if (pc_additem(psd[i],item_data,item_data->amount)) { //Discard this receiver. psd[i] = psd[count-1]; count--; } else { //Successful pick. target = psd[i]; break; } } } } if (!target) { target = sd; //Give it to the char that picked it up if ((i=pc_additem(sd,item_data,item_data->amount))) return i; } if(log_config.enable_logs&0x8) //Logs items, taken by (P)layers [Lupus] log_pick_pc(target, "P", item_data->nameid, item_data->amount, item_data); if(battle_config.party_show_share_picker && target != sd) { char output[80]; sprintf(output, "%s acquired %s.",target->status.name, itemdb_jname(item_data->nameid)); clif_disp_onlyself(sd,output,strlen(output)); } return 0; }
/*========================================== * Žæˆø‹–‘ø(trade‰Ÿ‚µ) *------------------------------------------*/ void trade_tradecommit(struct map_session_data *sd) { struct map_session_data *tsd; int trade_i; int flag; if (!sd->state.trading || !sd->state.deal_locked) //Locked should be 1 (pressed ok) before you can press trade. return; if ((tsd = map_id2sd(sd->trade_partner)) == NULL) { trade_tradecancel(sd); return; } sd->state.deal_locked = 2; if (tsd->state.deal_locked < 2) return; //Not yet time for trading. //Now is a good time (to save on resources) to check that the trade can indeed be made and it's not exploitable. // check exploit (trade more items that you have) if (impossible_trade_check(sd)) { trade_tradecancel(sd); return; } // check exploit (trade more items that you have) if (impossible_trade_check(tsd)) { trade_tradecancel(tsd); return; } // check for full inventory (can not add traded items) if (!trade_check(sd,tsd)) { // check the both players trade_tradecancel(sd); return; } // trade is accepted and correct. for( trade_i = 0; trade_i < 10; trade_i++ ) { int n; if (sd->deal.item[trade_i].amount) { n = sd->deal.item[trade_i].index; flag = pc_additem(tsd, &sd->status.inventory[n], sd->deal.item[trade_i].amount); if (flag == 0) { //Logs (T)rade [Lupus] if(log_config.enable_logs&0x2) { log_pick_pc(sd, "T", sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]); log_pick_pc(tsd, "T", sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]); } pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1); } else clif_additem(sd, n, sd->deal.item[trade_i].amount, 0); sd->deal.item[trade_i].index = 0; sd->deal.item[trade_i].amount = 0; } if (tsd->deal.item[trade_i].amount) { n = tsd->deal.item[trade_i].index; flag = pc_additem(sd, &tsd->status.inventory[n], tsd->deal.item[trade_i].amount); if (flag == 0) { //Logs (T)rade [Lupus] if(log_config.enable_logs&0x2) { log_pick_pc(tsd, "T", tsd->status.inventory[n].nameid, -(tsd->deal.item[trade_i].amount), &tsd->status.inventory[n]); log_pick_pc(sd, "T", tsd->status.inventory[n].nameid, tsd->deal.item[trade_i].amount, &tsd->status.inventory[n]); } pc_delitem(tsd, n, tsd->deal.item[trade_i].amount, 1); } else clif_additem(tsd, n, tsd->deal.item[trade_i].amount, 0); tsd->deal.item[trade_i].index = 0; tsd->deal.item[trade_i].amount = 0; } } if( sd->deal.zeny || tsd->deal.zeny ) { sd->status.zeny += tsd->deal.zeny - sd->deal.zeny; tsd->status.zeny += sd->deal.zeny - tsd->deal.zeny; //Logs Zeny (T)rade [Lupus] if( sd->deal.zeny && log_config.zeny ) log_zeny(tsd, "T", sd, sd->deal.zeny); if( tsd->deal.zeny && log_config.zeny ) log_zeny(sd, "T", tsd, tsd->deal.zeny); sd->deal.zeny = 0; tsd->deal.zeny = 0; clif_updatestatus(sd, SP_ZENY); clif_updatestatus(tsd, SP_ZENY); } sd->state.deal_locked = 0; sd->trade_partner = 0; sd->state.trading = 0; tsd->state.deal_locked = 0; tsd->trade_partner = 0; tsd->state.trading = 0; clif_tradecompleted(sd, 0); clif_tradecompleted(tsd, 0); // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players if (save_settings&1) { chrif_save(sd,0); chrif_save(tsd,0); } }
/*========================================== * Žæˆø‹–‘ø(trade‰Ÿ‚µ) *------------------------------------------*/ void trade_tradecommit (struct map_session_data *sd) { struct map_session_data *tsd; int trade_i; int flag; if (!sd->state.trading || !sd->state.deal_locked) //Locked should be 1 (pressed ok) before you can press trade. return; if ( (tsd = map_id2sd (sd->trade_partner)) == NULL) { trade_tradecancel (sd); return; } sd->state.deal_locked = 2; if (tsd->state.deal_locked < 2) return; //Not yet time for trading. //Now is a good time (to save on resources) to check that the trade can indeed be made and it's not exploitable. // check exploit (trade more items that you have) if (impossible_trade_check (sd)) { trade_tradecancel (sd); return; } // check exploit (trade more items that you have) if (impossible_trade_check (tsd)) { trade_tradecancel (tsd); return; } // check for full inventory (can not add traded items) if (!trade_check (sd, tsd)) { // check the both players trade_tradecancel (sd); return; } // trade is accepted and correct. for (trade_i = 0; trade_i < 10; trade_i++) { int n; if (sd->deal.item[trade_i].amount) { n = sd->deal.item[trade_i].index; flag = pc_additem (tsd, &sd->status.inventory[n], sd->deal.item[trade_i].amount, LOG_TYPE_TRADE); if (flag == 0) pc_delitem (sd, n, sd->deal.item[trade_i].amount, 1, 6, LOG_TYPE_TRADE); else clif_additem (sd, n, sd->deal.item[trade_i].amount, 0); sd->deal.item[trade_i].index = 0; sd->deal.item[trade_i].amount = 0; } if (tsd->deal.item[trade_i].amount) { n = tsd->deal.item[trade_i].index; flag = pc_additem (sd, &tsd->status.inventory[n], tsd->deal.item[trade_i].amount, LOG_TYPE_TRADE); if (flag == 0) pc_delitem (tsd, n, tsd->deal.item[trade_i].amount, 1, 6, LOG_TYPE_TRADE); else clif_additem (tsd, n, tsd->deal.item[trade_i].amount, 0); tsd->deal.item[trade_i].index = 0; tsd->deal.item[trade_i].amount = 0; } } if (sd->deal.zeny) { pc_payzeny (sd , sd->deal.zeny, LOG_TYPE_TRADE, tsd); pc_getzeny (tsd, sd->deal.zeny, LOG_TYPE_TRADE, sd); sd->deal.zeny = 0; sd->deal.zeny = 0; } if (tsd->deal.zeny) { pc_payzeny (tsd, tsd->deal.zeny, LOG_TYPE_TRADE, sd); pc_getzeny (sd , tsd->deal.zeny, LOG_TYPE_TRADE, tsd); tsd->deal.zeny = 0; } sd->state.deal_locked = 0; sd->trade_partner = 0; sd->state.trading = 0; tsd->state.deal_locked = 0; tsd->trade_partner = 0; tsd->state.trading = 0; clif_tradecompleted (sd, 0); clif_tradecompleted (tsd, 0); // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players if (save_settings & 1) { chrif_save (sd, 0); chrif_save (tsd, 0); } }
/** * Start transaction * @param sd Player/Seller * @param account_id Buyer account ID * @param *itemlist List of sold items { <index>.W, <nameid>.W, <amount>.W }* * @param count Number of item on the itemlist */ void buyingstore_trade(struct map_session_data *sd, int account_id, unsigned int buyer_id, const uint8 *itemlist, unsigned int count) { int zeny = 0; unsigned int i, weight, listidx, k; struct map_session_data *pl_sd; nullpo_retv(sd); if( count == 0 ) // Nothing to do return; if( !battle_config.feature_buying_store || pc_istrading(sd) ) { // Not allowed to sell clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( !pc_can_give_items(sd) ) { // Custom: GM is not allowed to sell clif_displaymessage(sd->fd, msg_txt(246)); clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } // Not online, not buying or not same store if( (pl_sd = map_id2sd(account_id)) == NULL || !pl_sd->state.buyingstore || pl_sd->buyer_id != buyer_id ) { clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( !searchstore_queryremote(sd, account_id) && (sd->bl.m != pl_sd->bl.m || !check_distance_bl(&sd->bl, &pl_sd->bl, AREA_SIZE)) ) { // Out of view range clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } searchstore_clearremote(sd); if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit ) // Buyer lost zeny in the mean time? Fix the limit pl_sd->buyingstore.zenylimit = pl_sd->status.zeny; weight = pl_sd->weight; // Check item list for( i = 0; i < count; i++ ) { // itemlist: <index>.W <name id>.W <amount>.W unsigned short nameid, amount; int index; index = RBUFW(itemlist,i * 6 + 0) - 2; nameid = RBUFW(itemlist,i * 6 + 2); amount = RBUFW(itemlist,i * 6 + 4); if( i ) { // Duplicate check. as the client does this too, only malicious intent should be caught here ARR_FIND(0, i, k, RBUFW(itemlist,k * 6 + 0) - 2 == index); if( k != i ) { // Duplicate ShowWarning("buyingstore_trade: Found duplicate item on selling list (prevnameid=%hu, prevamount=%hu, nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", RBUFW(itemlist,k * 6 + 2), RBUFW(itemlist,k * 6 + 4), nameid, amount, sd->status.account_id, sd->status.char_id); clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } } if( index < 0 || index >= ARRAYLENGTH(sd->status.inventory) || sd->inventory_data[index] == NULL || sd->status.inventory[index].nameid != nameid || sd->status.inventory[index].amount < amount ) { // Invalid input clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } if( sd->status.inventory[index].expire_time || (sd->status.inventory[index].bound && !pc_can_give_bounded_items(sd)) || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) { // Non-tradable item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } ARR_FIND(0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid); // There is no such item or the buyer has already bought all of them if( listidx == pl_sd->buyingstore.slots || pl_sd->buyingstore.items[listidx].amount == 0 ) { clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } // Buyer does not need that much of the item if( pl_sd->buyingstore.items[listidx].amount < amount ) { clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_COUNT, nameid); return; } // Buyer does not have enough space for this item if( pc_checkadditem(pl_sd, nameid, amount) == CHKADDITEM_OVERAMOUNT ) { clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } // Normally this is not supposed to happen, as the total weight is // checked upon creation, but the buyer could have gained items if( amount * (unsigned int)sd->inventory_data[index]->weight > pl_sd->max_weight - weight ) { clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } weight += amount * sd->inventory_data[index]->weight; // Buyer does not have enough zeny if( amount * pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit - zeny ) { clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_ZENY, nameid); return; } zeny += amount * pl_sd->buyingstore.items[listidx].price; } // Process item list for( i = 0; i < count; i++ ) { // itemlist: <index>.W <name id>.W <amount>.W unsigned short nameid, amount; int index; index = RBUFW(itemlist,i * 6 + 0) - 2; nameid = RBUFW(itemlist,i * 6 + 2); amount = RBUFW(itemlist,i * 6 + 4); ARR_FIND(0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid); zeny = amount * pl_sd->buyingstore.items[listidx].price; // Move item pc_additem(pl_sd, &sd->status.inventory[index], amount, LOG_TYPE_BUYING_STORE); pc_delitem(sd, index, amount, 1, 0, LOG_TYPE_BUYING_STORE); pl_sd->buyingstore.items[listidx].amount -= amount; if( pl_sd->buyingstore.items[listidx].amount > 0 ) { if( Sql_Query(mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `buyingstore_id` = %d AND `index` = %d;", buyingstore_items_db, pl_sd->buyingstore.items[listidx].amount, pl_sd->buyer_id, listidx) != SQL_SUCCESS ) Sql_ShowDebug(mmysql_handle); } else { if( Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `buyingstore_id` = %d AND `index` = %d;", buyingstore_items_db, pl_sd->buyer_id, listidx) != SQL_SUCCESS ) Sql_ShowDebug(mmysql_handle); } // Pay up pc_payzeny(pl_sd, zeny, LOG_TYPE_BUYING_STORE, sd); pc_getzeny(sd, zeny, LOG_TYPE_BUYING_STORE, pl_sd); pl_sd->buyingstore.zenylimit -= zeny; // Notify clients clif_buyingstore_delete_item(sd, index, amount, pl_sd->buyingstore.items[listidx].price); clif_buyingstore_update_item(pl_sd, nameid, amount); } if( save_settings&128 ) { chrif_save(sd, 0); chrif_save(pl_sd, 0); } // Check whether or not there is still something to buy ARR_FIND(0, pl_sd->buyingstore.slots, i, pl_sd->buyingstore.items[i].amount != 0); if( i == pl_sd->buyingstore.slots ) // Everything was bought clif_buyingstore_trade_failed_buyer(pl_sd, BUYINGSTORE_TRADE_BUYER_NO_ITEMS); else if( pl_sd->buyingstore.zenylimit == 0 ) // Zeny limit reached clif_buyingstore_trade_failed_buyer(pl_sd, BUYINGSTORE_TRADE_BUYER_ZENY); else { // Continue buying if( Sql_Query(mmysql_handle, "UPDATE `%s` SET `limit` = %d WHERE `id` = %d;", buyingstores_db, pl_sd->buyingstore.zenylimit, pl_sd->buyer_id) != SQL_SUCCESS ) Sql_ShowDebug(mmysql_handle); return; } // Cannot continue buying buyingstore_close(pl_sd); // Remove auto-trader if( pl_sd->state.autotrade ) map_quit(pl_sd); }