Пример #1
0
/*==========================================
 * Divorce players
 * only used if 'partner_id' is offline
 *------------------------------------------*/
int chrif_divorceack(int char_id, int partner_id)
{
	struct map_session_data* sd;
	int i;

	if( !char_id || !partner_id )
		return 0;

	if( (sd = map_charid2sd(char_id)) != NULL && sd->status.partner_id == partner_id )
	{
		sd->status.partner_id = 0;
		for(i = 0; i < MAX_INVENTORY; i++)
			if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
				pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER);
	}

	if( (sd = map_charid2sd(partner_id)) != NULL && sd->status.partner_id == char_id )
	{
		sd->status.partner_id = 0;
		for(i = 0; i < MAX_INVENTORY; i++)
			if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
				pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER);
	}
	
	return 0;
}
Пример #2
0
int mail_removeitem(struct map_session_data *sd, short flag, int idx, int amount)
{
	int i;

	nullpo_ret(sd);

	idx -= 2;

	if( idx < 0 || idx >= MAX_INVENTORY )
			return false;
	if( amount <= 0 || amount > sd->inventory.u.items_inventory[idx].amount )
			return false;

	ARR_FIND(0, MAIL_MAX_ITEM, i, sd->mail.item[i].index == idx && sd->mail.item[i].nameid > 0);

	if( i == MAIL_MAX_ITEM ){
		return false;
	}

	if( flag ){
		if( battle_config.mail_attachment_price > 0 ){
			if( pc_payzeny( sd, battle_config.mail_attachment_price, LOG_TYPE_MAIL, NULL ) ){
				return false;
			}
		}

#if PACKETVER < 20150513
		// With client update packet
		pc_delitem(sd, idx, amount, 1, 0, LOG_TYPE_MAIL);
#else
		// RODEX refreshes the client inventory from the ACK packet
		pc_delitem(sd, idx, amount, 0, 0, LOG_TYPE_MAIL);
#endif
	}else{
		for( ; i < MAIL_MAX_ITEM-1; i++ ){
			if (sd->mail.item[i + 1].nameid == 0)
				break;
			sd->mail.item[i].index = sd->mail.item[i+1].index;
			sd->mail.item[i].nameid = sd->mail.item[i+1].nameid;
			sd->mail.item[i].amount = sd->mail.item[i+1].amount;
		}

		for( ; i < MAIL_MAX_ITEM; i++ ){
			sd->mail.item[i].index = 0;
			sd->mail.item[i].nameid = 0;
			sd->mail.item[i].amount = 0;
		}

#if PACKETVER < 20150513
		clif_additem(sd, idx, amount, 0);
#else
		clif_mail_removeitem(sd, true, idx + 2, amount);
#endif
	}

	return 1;
}
Пример #3
0
int bg_member_removeskulls(struct map_session_data *sd)
{
	int n;
	nullpo_ret(sd);
	if( (n = pc_search_inventory(sd,BLUE_SKULL)) >= 0 )
		pc_delitem(sd,n,sd->status.inventory[n].amount,0,2,LOG_TYPE_CONSUME);
	if( (n = pc_search_inventory(sd,RED_SKULL)) >= 0 )
		pc_delitem(sd,n,sd->status.inventory[n].amount,0,2,LOG_TYPE_CONSUME);
	if( (n = pc_search_inventory(sd,GREEN_SKULL)) >= 0 )
		pc_delitem(sd,n,sd->status.inventory[n].amount,0,2,LOG_TYPE_CONSUME);

	return 1;
}
Пример #4
0
static int pet_food(struct map_session_data *sd, struct pet_data *pd)
{
	int i, food_id;

	food_id = pd->petDB->FoodID;
	i = pc_search_inventory(sd, food_id);

	if( i == INDEX_NOT_FOUND ) {
		clif_pet_food(sd, food_id, 0);
		return 1;
	}

	pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_CONSUME);

	if( pd->pet.hungry > 90 )
		pet_set_intimate(pd, pd->pet.intimate - pd->petDB->r_full);
	else {
		int add_intimate = 0;

		if( battle_config.pet_friendly_rate != 100 )
			add_intimate = (pd->petDB->r_hungry * battle_config.pet_friendly_rate) / 100;
		else
			add_intimate = pd->petDB->r_hungry;
		if( pd->pet.hungry > 75 ) {
			add_intimate = add_intimate>>1;
			if( add_intimate <= 0 )
				add_intimate = 1;
		}
		pet_set_intimate(pd, pd->pet.intimate + add_intimate);
	}
Пример #5
0
/**
 * Add an item to the storage from the inventory.
 * @param sd : player
 * @param stor : Storage data
 * @param index : inventory index to take the item from
 * @param amount : number of item to take
 * @return 0:fail, 1:success
 */
void storage_storageadd(struct map_session_data* sd, struct s_storage *stor, int index, int amount)
{
	enum e_storage_add result;

	nullpo_retv(sd);

	result = storage_canAddItem(stor, index, sd->inventory.u.items_inventory, amount, MAX_INVENTORY);
	if (result == STORAGE_ADD_INVALID)
		return;
	else if (result == STORAGE_ADD_OK) {
		switch( storage_additem(sd, stor, &sd->inventory.u.items_inventory[index], amount) ){
			case 0:
				pc_delitem(sd,index,amount,0,4,LOG_TYPE_STORAGE);
				return;
			case 1:
				break;
			case 2:
				result = STORAGE_ADD_NOROOM;
				break;
		}
	}

	clif_storageitemremoved(sd,index,0);
	clif_dropitem(sd,index,0);
}
Пример #6
0
/*==========================================
 * Add an item to the storage from the inventory.
 *------------------------------------------
 */
int storage_storageadd(struct map_session_data *sd,int index,int amount)
{
	struct storage *stor;

	nullpo_retr(0, sd);
	nullpo_retr(0, stor=account2storage2(sd->status.account_id));

	if((stor->storage_amount > MAX_STORAGE) || !stor->storage_status)
		return 0; // storage full / storage closed

	if(index<0 || index>=MAX_INVENTORY)
		return 0;

	if(sd->status.inventory[index].nameid <= 0)
		return 0; //No item on that spot
	
	if(amount < 1 || amount > sd->status.inventory[index].amount)
  		return 0;

//	log_tostorage(sd, index, 0);
	if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
  // remove item from inventory
		pc_delitem(sd,index,amount,0);

	return 1;
}
Пример #7
0
/// Invoked (from char-server) when a party member leaves the party.
int party_member_withdraw(int party_id, int account_id, int char_id)
{
	struct map_session_data* sd = map_id2sd(account_id);
	struct party_data* p = party_search(party_id);

	if( p ) {
		int i;
		ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id );
		if( i < MAX_PARTY ) {
			clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0);
			memset(&p->party.member[i], 0, sizeof(p->party.member[0]));
			memset(&p->data[i], 0, sizeof(p->data[0]));
			p->party.count--;
			party_check_state(p);
		}
	}

	if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) {
		int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion
		int j,i;
		j = pc_bound_chk(sd,3,idxlist);
		for(i=0;i<j;i++)
			pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER);
		sd->status.party_id = 0;
		clif_charnameupdate(sd); //Update name display [Skotlex]
		//TODO: hp bars should be cleared too
		if( p->instance_id )
			instance_check_kick(sd);
	}

	return 0;
}
Пример #8
0
static int pet_food(struct map_session_data *sd, struct pet_data *pd)
{
	int i,k;

	k=pd->petDB->FoodID;
	i=pc_search_inventory(sd,k);
	if(i < 0) {
		clif_pet_food(sd,k,0);
		return 1;
	}
	pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME);

	if( pd->pet.hungry > 90 )
		pet_set_intimate(pd, pd->pet.intimate - pd->petDB->r_full);
	else
	{
		if( battle_config.pet_friendly_rate != 100 )
			k = (pd->petDB->r_hungry * battle_config.pet_friendly_rate)/100;
		else
			k = pd->petDB->r_hungry;
		if( pd->pet.hungry > 75 )
		{
			k = k >> 1;
			if( k <= 0 )
				k = 1;
		}
		pet_set_intimate(pd, pd->pet.intimate + k);
	}
Пример #9
0
/*==========================================
* Attempt to add an item in guild storage from inventory, then refresh it
* @index : inventory idx
* return
* 	0 : fail
* 	1 : succes
 *------------------------------------------*/
int storage_guild_storageadd(struct map_session_data* sd, int index, int amount)
{
	struct guild_storage *stor;

	nullpo_ret(sd);
	nullpo_ret(stor=guild2storage2(sd->status.guild_id));

	if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE )
		return 0;

	if( index<0 || index>=MAX_INVENTORY )
		return 0;

	if( sd->status.inventory[index].nameid <= 0 )
		return 0;

	if( amount < 1 || amount > sd->status.inventory[index].amount )
		return 0;

	if( stor->lock ) {
		storage_guild_storageclose(sd);
		return 0;
	}

	if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
		pc_delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE);

	return 1;
}
Пример #10
0
/**
 * Attempt to add an item in guild storage from inventory, then refresh it
 * @param sd : player
 * @param amount : number of item to delete
 */
void gstorage_storageadd(struct map_session_data *sd, int index, int amount)
{
	struct guild_storage *stor;

	nullpo_retv(sd);
	nullpo_retv(stor = gstorage_get_storage(sd->status.guild_id));

	if (!stor->opened || stor->opened != sd->status.char_id || stor->storage_amount > MAX_GUILD_STORAGE)
		return;

	if (index < 0 || index >= MAX_INVENTORY)
		return;

	if (!(sd->status.inventory[index].nameid))
		return;

	if (amount < 1 || amount > sd->status.inventory[index].amount)
		return;

	if (stor->locked) {
		gstorage_storageclose(sd); 
		return;
	}

	if (gstorage_additem(sd, stor, &sd->status.inventory[index], amount))
		pc_delitem(sd, index, amount, 0, 4, LOG_TYPE_GSTORAGE);
	else {
		clif_storageitemremoved(sd, index, 0);
		clif_dropitem(sd, index, 0);
	}
}
Пример #11
0
/**
 * Add an item to the storage from the inventory.
 * @param sd : player
 * @param index : inventory index to take the item from
 * @param amount : number of item to take
 * @return 0:fail, 1:success
 */
int storage_storageadd(struct map_session_data* sd, int index, int amount)
{
	nullpo_ret(sd);

	if( sd->status.storage.storage_amount > sd->storage_size )
		return 0; // storage full

	if( index < 0 || index >= MAX_INVENTORY )
		return 0;

	if( sd->status.inventory[index].nameid <= 0 )
		return 0; // No item on that spot

	if( amount < 1 || amount > sd->status.inventory[index].amount )
		return 0;

	if( storage_additem(sd,&sd->status.inventory[index],amount) == 0 )
		pc_delitem(sd,index,amount,0,4,LOG_TYPE_STORAGE);
	else {
		clif_dropitem(sd,index,0);
		return 0;
	}

	return 1;
}
Пример #12
0
/**
 * Attempt to add an item in guild storage from inventory, then refresh it
 * @param sd : player
 * @param amount : number of item to delete
 */
void storage_guild_storageadd(struct map_session_data* sd, int index, int amount)
{
	struct s_storage *stor;

	nullpo_retv(sd);
	nullpo_retv(stor = guild2storage2(sd->status.guild_id));

	if( !stor->status || stor->amount > MAX_GUILD_STORAGE )
		return;

	if( index < 0 || index >= MAX_INVENTORY )
		return;

	if( sd->inventory.u.items_inventory[index].nameid == 0 )
		return;

	if( amount < 1 || amount > sd->inventory.u.items_inventory[index].amount )
		return;

	if( stor->lock ) {
		storage_guild_storageclose(sd);
		return;
	}

	if(storage_guild_additem(sd,stor,&sd->inventory.u.items_inventory[index],amount))
		pc_delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE);
	else {
		clif_storageitemremoved(sd,index,0);
		clif_dropitem(sd,index,0);
	}
}
Пример #13
0
/**
* Feed homunculus
* @param sd
* @param hd
*/
int hom_food(struct map_session_data *sd, struct homun_data *hd)
{
	int i, foodID, emotion;

	nullpo_retr(1,sd);
	nullpo_retr(1,hd);

	if (hd->homunculus.vaporize)
		return 1;

	foodID = hd->homunculusDB->foodID;
	i = pc_search_inventory(sd,foodID);
	if (i < 0) {
		clif_hom_food(sd,foodID,0);
		return 1;
	}
	pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME);

	if ( hd->homunculus.hunger >= 91 ) {
		hom_decrease_intimacy(hd, 50);
		emotion = ET_KEK;
	} else if ( hd->homunculus.hunger >= 76 ) {
		hom_decrease_intimacy(hd, 5);
		emotion = ET_PROFUSELY_SWEAT;
	} else if ( hd->homunculus.hunger >= 26 ) {
		hom_increase_intimacy(hd, 75);
		emotion = ET_DELIGHT;
	} else if ( hd->homunculus.hunger >= 11 ) {
		hom_increase_intimacy(hd, 100);
		emotion = ET_DELIGHT;
	} else {
		hom_increase_intimacy(hd, 50);
		emotion = ET_DELIGHT;
	}

	hd->homunculus.hunger += 10;	//dunno increase value for each food
	if(hd->homunculus.hunger > 100)
		hd->homunculus.hunger = 100;

	log_feeding(sd, LOG_FEED_HOMUNCULUS, foodID);

	clif_emotion(&hd->bl,emotion);
	clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
	clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
	clif_hom_food(sd,foodID,1);

	// Too much food :/
	if(hd->homunculus.intimacy == 0)
		return hom_delete(sd->hd, ET_HUK);

	return 0;
}
Пример #14
0
/**
 * Finalize hatching process and load pet to client.
 * @param account_id : account ID of owner
 * @param p : pet requesting
 * @param flag : 1:stop loading of pet
 * @return 0:success, 1:failure
 */
int pet_recv_petdata(int account_id,struct s_pet *p,int flag)
{
	struct map_session_data *sd;

	sd = map_id2sd(account_id);

	if(sd == NULL)
		return 1;

	if(flag == 1) {
		sd->status.pet_id = 0;

		return 1;
	}

	if(p->incubate == 1) {
		int i;

		//Delete egg from inventory. [Skotlex]
		for (i = 0; i < MAX_INVENTORY; i++) {
			if(sd->status.inventory[i].card[0] == CARD0_PET &&
				p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]))
				break;
		}

		if(i >= MAX_INVENTORY) {
			ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name);
			sd->status.pet_id = 0;

			return 1;
		}

		if (!pet_birth_process(sd,p)) //Pet hatched. Delete egg.
			pc_delitem(sd,i,1,0,0,LOG_TYPE_OTHER);
	} else {
		pet_data_init(sd,p);

		if(sd->pd && sd->bl.prev != NULL) {
			if(map_addblock(&sd->pd->bl))
				return 1;

			clif_spawn(&sd->pd->bl);
			clif_send_petdata(sd,sd->pd,0,0);
			clif_send_petdata(sd,sd->pd,5,battle_config.pet_hair_style);
			clif_pet_equip_area(sd->pd);
			clif_send_petstatus(sd);
		}
	}

	return 0;
}
Пример #15
0
int merc_hom_food(struct map_session_data *sd, struct homun_data *hd)
{
	int i, foodID, emotion;
	char mes[255];

	if(hd->homunculus.vaporize)
		return 1 ;

	foodID = hd->homunculusDB->foodID;
	i = pc_search_inventory(sd,foodID);
	if(i < 0) {
		clif_hom_food(sd,foodID,0);
		return 1;
	}
	pc_delitem(sd,i,1,0,0);

	if ( hd->homunculus.hunger >= 91 ) {
		merc_hom_decrease_intimacy(hd, 50);
		emotion = E_WAH;
	} else if ( hd->homunculus.hunger >= 76 ) {
		merc_hom_decrease_intimacy(hd, 5);
		emotion = E_SWT2;
	} else if ( hd->homunculus.hunger >= 26 ) {
		merc_hom_increase_intimacy(hd, 75);
		emotion = E_HO;
	} else if ( hd->homunculus.hunger >= 11 ) {
		merc_hom_increase_intimacy(hd, 100);
		emotion = E_HO;
	} else {
		merc_hom_increase_intimacy(hd, 50);
		emotion = E_HO;
	}

	hd->homunculus.hunger += 10;	//dunno increase value for each food
	if(hd->homunculus.hunger > 100)
		hd->homunculus.hunger = 100;

	clif_emotion(&hd->bl,emotion);
	snprintf(mes, sizeof mes,msg_txt(908),hd->homunculus.name);
	clif_message(&hd->bl,mes);

	clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
	clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
	clif_hom_food(sd,foodID,1);
       	
	// Too much food :/
	if(hd->homunculus.intimacy == 0)
		return merc_hom_delete(sd->hd, E_OMG);

	return 0;
}
Пример #16
0
int merc_hom_food(struct map_session_data *sd, struct homun_data *hd)
{
	int i, foodID, emotion;

	if(hd->homunculus.vaporize)
		return 1 ;

	foodID = hd->homunculusDB->foodID;
	i = pc_search_inventory(sd,foodID);
	if(i < 0) {
		clif_hom_food(sd,foodID,0);
		return 1;
	}
	pc_delitem(sd,i,1,0);

	if ( hd->homunculus.hunger >= 91 ) {
		merc_hom_decrease_intimacy(hd, 50);
		emotion = 16;
	} else if ( hd->homunculus.hunger >= 76 ) {
		merc_hom_decrease_intimacy(hd, 5);
		emotion = 19;
	} else if ( hd->homunculus.hunger >= 26 ) {
		merc_hom_increase_intimacy(hd, 75);
		emotion = 2;
	} else if ( hd->homunculus.hunger >= 11 ) {
		merc_hom_increase_intimacy(hd, 100);
		emotion = 2;
	} else {
		merc_hom_increase_intimacy(hd, 50);
		emotion = 2;
	}

	hd->homunculus.hunger += 10;	//dunno increase value for each food
	if(hd->homunculus.hunger > 100)
		hd->homunculus.hunger = 100;

	clif_emotion(&hd->bl,emotion) ;
	clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
	clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
	clif_hom_food(sd,foodID,1);
       	
	// Too much food :/
	if(hd->homunculus.intimacy == 0)
		return merc_hom_delete(sd->hd, 23); //omg  

	return 0;
}
Пример #17
0
/// Invoked (from char-server) when a party member leaves the party.
int party_member_withdraw(int party_id, uint32 account_id, uint32 char_id)
{
	struct map_session_data* sd = map_id2sd(account_id);
	struct party_data* p = party_search(party_id);

	if( p ) {
		int i;
		ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id );
		if( i < MAX_PARTY ) {
			clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0);
			memset(&p->party.member[i], 0, sizeof(p->party.member[0]));
			memset(&p->data[i], 0, sizeof(p->data[0]));
			p->party.count--;
			party_check_state(p);
		}
	}

	if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) {
#ifdef BOUND_ITEMS
		int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion
		int j,i;

		party_trade_bound_cancel(sd);
		j = pc_bound_chk(sd,BOUND_PARTY,idxlist);

		for(i = 0; i < j; i++)
			pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_BOUND_REMOVAL);
#endif

		sd->status.party_id = 0;
		clif_charnameupdate(sd); //Update name display [Skotlex]
		//TODO: hp bars should be cleared too

		if( p->instance_id ) {
			int16 m = sd->bl.m;

			if( map[m].instance_id ) { // User was on the instance map
				if( map[m].save.map )
					pc_setpos(sd, map[m].save.map, map[m].save.x, map[m].save.y, CLR_TELEPORT);
				else
					pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT);
			}
		}
	}

	return 0;
}
Пример #18
0
int mail_removeitem(struct map_session_data *sd, short flag)
{
	nullpo_ret(sd);

	if( sd->mail.amount )
	{
		if (flag) // Item send
			pc_delitem(sd, sd->mail.index, sd->mail.amount, 1, 0, LOG_TYPE_MAIL);
		else
			clif_additem(sd, sd->mail.index, sd->mail.amount, 0);
	}

	sd->mail.nameid = 0;
	sd->mail.index = 0;
	sd->mail.amount = 0;
	return 1;
}
Пример #19
0
// 作成可否
int guild_created(int account_id,int guild_id)
{
	struct map_session_data *sd=map_id2sd(account_id);

	if(sd==NULL)
		return 0;
	if(!guild_id) {
		clif_guild_created(sd,2);	// 作成失敗(同名ギルド存在)
		return 0;
	}
	//struct guild *g;
	sd->status.guild_id=guild_id;
	clif_guild_created(sd,0);
	if(battle_config.guild_emperium_check)
		pc_delitem(sd,pc_search_inventory(sd,714),1,0,0);	// エンペリウム消耗
	return 0;
}
Пример #20
0
/*==========================================
 * 離婚情報同期要求
 *------------------------------------------*/
int chrif_divorce(int char_id, int partner_id)
{
	struct map_session_data* sd;
	int i;

	if (!char_id || !partner_id || (sd = map_charid2sd(partner_id)) == NULL || sd->status.partner_id != char_id)
		return 0;

	//離婚(相方は既にキャラが消えている筈なので)
	sd->status.partner_id = 0;

	//相方の結婚指輪を剥奪
	for(i = 0; i < MAX_INVENTORY; i++)
		if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
			pc_delitem(sd, i, 1, 0);

	return 0;
}
Пример #21
0
/*==========================================
 * カプラ倉庫へ入れる
 *------------------------------------------
 */
void storage_storageadd(struct map_session_data *sd, int idx, int amount)
{
	struct storage *stor;

	nullpo_retv(sd);
	nullpo_retv(stor = (struct storage *)numdb_search(storage_db,sd->status.account_id));

	if(!stor->storage_status)
		return;
	if(idx < 0 || idx >= MAX_INVENTORY)
		return;
	if(itemdb_isstorageable(sd->status.inventory[idx].nameid) == 0)
		return;
	if(amount < 1 || amount > sd->status.inventory[idx].amount)
		return;

	if(storage_additem(sd, stor, &sd->status.inventory[idx], amount) == 0)
		pc_delitem(sd, idx, amount, 0, 4);

	return;
}
Пример #22
0
int mail_removeitem(struct map_session_data *sd, short flag)
{
	nullpo_ret(sd);	

	if( sd->mail.amount )
	{
		if (flag)
		{ // Item send
			log_pick_pc(sd, LOG_TYPE_MAIL, sd->mail.nameid, -sd->mail.amount, &sd->status.inventory[sd->mail.index]);

			pc_delitem(sd, sd->mail.index, sd->mail.amount, 1, 0);
		}
		else
			clif_additem(sd, sd->mail.index, sd->mail.amount, 0);
	}

	sd->mail.nameid = 0;
	sd->mail.index = 0;
	sd->mail.amount = 0;
	return 1;
}
Пример #23
0
int ext_storage_add(struct map_session_data *sd,int index,int amount)
{
	nullpo_ret(sd);

	if( sd->status.ext_storage.storage_amount > MAX_EXTRA_STORAGE )
		return 0; // storage full

	if( index < 0 || index >= MAX_INVENTORY )
		return 0;

	if( sd->status.inventory[index].nameid <= 0 )
		return 0; //No item on that spot

	if( amount < 1 || amount > sd->status.inventory[index].amount )
		return 0;

	if( ext_storage_additem(sd,&sd->status.inventory[index],amount) == 0 )
		pc_delitem(sd,index,amount,0,4);

	return 1;
}
Пример #24
0
/**
 * Apply a pet's equipment.
 * @param sd : player requesting
 * @param index : index value of item
 * @return 0:success, 1:failure
 */
int pet_equipitem(struct map_session_data *sd,int index)
{
	struct pet_data *pd;
	unsigned short nameid;

	nullpo_retr(1, sd);

	pd = sd->pd;

	if (!pd)
		return 1;

	nameid = sd->status.inventory[index].nameid;

	if(pd->petDB->AcceID == 0 || nameid != pd->petDB->AcceID || pd->pet.equip != 0) {
		clif_equipitemack(sd,0,0,0);
		return 1;
	}

	pc_delitem(sd,index,1,0,0,LOG_TYPE_OTHER);
	pd->pet.equip = nameid;
	status_set_viewdata(&pd->bl, pd->pet.class_); //Updates view_data.
	clif_pet_equip_area(pd);

	if (battle_config.pet_equip_required) { // Skotlex: start support timers if need
		unsigned int tick = gettick();

		if (pd->s_skill && pd->s_skill->timer == INVALID_TIMER) {
			if (pd->s_skill->id)
				pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0);
			else
				pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0);
		}

		if (pd->bonus && pd->bonus->timer == INVALID_TIMER)
			pd->bonus->timer=add_timer(tick+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
	}

	return 0;
}
Пример #25
0
int mail_removeitem(struct map_session_data *sd, short flag)
{
	nullpo_retr(0,sd);

	if( sd->mail.amount )
	{
		if (flag)
		{ // Item send
			if(log_config.enable_logs&0x2000)
				log_pick_pc(sd, "E", sd->mail.nameid, -sd->mail.amount, &sd->status.inventory[sd->mail.index]);

			pc_delitem(sd, sd->mail.index, sd->mail.amount, 1);
		}
		else
			clif_additem(sd, sd->mail.index, sd->mail.amount, 0);
	}

	sd->mail.nameid = 0;
	sd->mail.index = 0;
	sd->mail.amount = 0;
	return 1;
}
Пример #26
0
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);
}
Пример #27
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);
	}
}
Пример #28
0
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);
	}
}
Пример #29
0
/*==========================================
 * 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?
		}
	}
}
Пример #30
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;
}