Пример #1
0
unsigned char mail_setitem(struct map_session_data *sd, int idx, int amount) {

	if( pc_istrading(sd) )
		return 1;

	if( idx == 0 ) { // Zeny Transfer
		if( amount < 0 || !pc_can_give_items(sd) )
			return 1;

		if( amount > sd->status.zeny )
			amount = sd->status.zeny;

		sd->mail.zeny = amount;
		// clif->updatestatus(sd, SP_ZENY);
		return 0;
	} else { // Item Transfer
		idx -= 2;
		mail->removeitem(sd, 0);

		if( idx < 0 || idx >= MAX_INVENTORY )
			return 1;
		if( amount < 0 || amount > sd->status.inventory[idx].amount )
			return 1;
		if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time ||
			!itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) ||
			(sd->status.inventory[idx].bound && !pc_can_give_bound_items(sd)) )
			return 1;

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

		return 0;
	}
}
Пример #2
0
/*==========================================
 * Abrir loja
 * data := {<index>.w <amount>.w <value>.l}[count]
 *------------------------------------------*/
void vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count) {
	int i, j;
	int vending_skill_lvl;
	nullpo_retv(sd);

	if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd))
		return; // nao pode abrir vendas deitado morto || não usou via habilidade (wpe/hack) || não pode ter 2 lojas ao mesmo tempo

	vending_skill_lvl = pc->checkskill(sd, MC_VENDING);
	// check de nivel de habilidade e carrinho
	if( !vending_skill_lvl || !pc_iscarton(sd) ) {
		clif->skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
		return;
	}

	// checka numero de itens na loja
	if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) {
		// contagem de item invalida
		clif->skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
		return;
	}

	// filtrar itens invalidos
	i = 0;
	for( j = 0; j < count; j++ ) {
		short index        = *(uint16*)(data + 8*j + 0);
		short amount       = *(uint16*)(data + 8*j + 2);
		unsigned int value = *(uint32*)(data + 8*j + 4);

		index -= 2; // ajuste de equilibrio (cliente diz que a posição do primeiro carrinho é 2)

		if( index < 0 || index >= MAX_CART // posição inválida
		 || pc->cartitem_amount(sd, index, amount) < 0 // item invalido ou quantidade insuficiente
		//NOT: servidores oficiais não fazem nenhum dos checks abaixo!
		 || !sd->status.cart[index].identify // item não-identficado
		 || sd->status.cart[index].attribute == 1 // item quebrado
		 || sd->status.cart[index].expire_time // Isso não deveria estar no carrinho mas apenas no caso de estar
		 || (sd->status.cart[index].bound && !pc_can_give_bound_items(sd)) // não pode trocar itens de recompensa, permissão w/o
		 || !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // Itens não-trocaveis
			continue;

		sd->vending[i].index = index;
		sd->vending[i].amount = amount;
		sd->vending[i].value = cap_value(value, 0, (unsigned int)battle_config.vending_max_value);

		i++; // item adicionado com sucesso
	}

	if( i != j )
		clif->message (sd->fd, msg_txt(266)); //"Alguns dos seus itens não pode ser vendido e foram removidos da loja."

	if( i == 0 ) { // nenhum item válido encontrado
		clif->skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); // packet de resposta personalizada
		return;
	}
	sd->state.prevend = sd->state.workinprogress = 0;
	sd->state.vending = true;
	sd->vender_id = getid();
	sd->vend_num = i;
	safestrncpy(sd->message, message, MESSAGE_SIZE);

	clif->openvending(sd,sd->bl.id,sd->vending);
	clif->showvendingboard(&sd->bl,message,0);

	idb_put(vending->db, sd->status.char_id, sd);
}
Пример #3
0
/*==========================================
 * Open shop
 * data := {<index>.w <amount>.w <value>.l}[count]
 *------------------------------------------*/
void vending_openvending(struct map_session_data *sd, const char *message, const uint8 *data, int count)
{
	int i, j;
	int vending_skill_lvl;
	nullpo_retv(sd);

	if(pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd))
		return; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once

	vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
	// skill level and cart check
	if(!vending_skill_lvl || !pc_iscarton(sd)) {
		clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
		return;
	}

	// check number of items in shop
	if(count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl) {
		// invalid item count
		clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
		return;
	}

	// filter out invalid items
	i = 0;
	for(j = 0; j < count; j++) {
		short index        = *(uint16 *)(data + 8*j + 0);
		short amount       = *(uint16 *)(data + 8*j + 2);
		unsigned int value = *(uint32 *)(data + 8*j + 4);

		index -= 2; // offset adjustment (client says that the first cart position is 2)

		if(index < 0 || index >= MAX_CART  // invalid position
		   ||  pc_cartitem_amount(sd, index, amount) < 0 // invalid item or insufficient quantity
		   //NOTE: official server does not do any of the following checks!
		   ||  !sd->status.cart[index].identify // unidentified item
		   ||  sd->status.cart[index].attribute == 1 // broken item
		   ||  sd->status.cart[index].expire_time // It should not be in the cart but just in case
		   ||  (sd->status.cart[index].bound && !pc_can_give_bound_items(sd)) // can't trade bound items w/o permission
		   ||  !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)))  // untradeable item
			continue;

		sd->vending[i].index = index;
		sd->vending[i].amount = amount;
#if VERSION == -1
		sd->vending[i].value = cap_value(value, 0, (unsigned int)battle_config.vending_max_value_ot);
#else
		sd->vending[i].value = cap_value(value, 0, (unsigned int)battle_config.vending_max_value);
#endif

		i++; // item successfully added
	}

	if(i != j)
		clif_displaymessage(sd->fd, msg_txt(266));  //"Some of your items cannot be vended and were removed from the shop."

	if(i == 0) {
		// no valid item found
		clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); // custom reply packet
		return;
	}
	sd->state.prevend = sd->state.workinprogress = 0;
	sd->state.vending = true;
	sd->vender_id = getid();
	sd->vend_num = i;
	safestrncpy(sd->message, message, MESSAGE_SIZE);

	clif_openvending(sd,sd->bl.id,sd->vending);
	clif_showvendingboard(&sd->bl,message,0);

	idb_put(vending->db, sd->status.char_id, sd);
}
Пример #4
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;

	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->message(sd->fd, msg_sd(sd,246)); // Your GM level doesn't authorize you to perform this action.
		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 || (sd->status.inventory[index].bound && !pc_can_give_bound_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 );
		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;

		// move item
		pc->additem(pl_sd, &sd->status.inventory[index], amount, LOG_TYPE_BUYING_STORE);
		pc->delitem(sd, index, amount, 1, DELITEM_NORMAL, LOG_TYPE_BUYING_STORE);
		pl_sd->buyingstore.items[listidx].amount-= amount;

		// 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( map->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
		return;
	}

	// cannot continue buying
	buyingstore->close(pl_sd);

	// remove auto-trader
	if( pl_sd->state.autotrade ) {
		map->quit(pl_sd);
	}
}
Пример #5
0
/*==========================================
 * Adds an item/qty to the trade window
 *------------------------------------------*/
void trade_tradeadditem(struct map_session_data *sd, short index, short amount) {
	struct map_session_data *target_sd;
	struct item *item;
	int trade_i, trade_weight;
	int src_lv, dst_lv;

	nullpo_retv(sd);
	if( !sd->state.trading || sd->state.deal_locked > 0 )
		return; //Can't add stuff.

	if( (target_sd = map->id2sd(sd->trade_partner)) == NULL )
	{
		trade->cancel(sd);
		return;
	}

	if (amount == 0) {
		//Why do this.. ~.~ just send an ack, the item won't display on the trade window.
		clif->tradeitemok(sd, index, TIO_SUCCESS);
		return;
	}

	index -= 2; // 0 is for zeny, 1 is unknown. Gravity, go figure...

	//Item checks...
	if( index < 0 || index >= MAX_INVENTORY )
		return;
	if( amount < 0 || amount > sd->status.inventory[index].amount )
		return;

	item = &sd->status.inventory[index];
	src_lv = pc_get_group_level(sd);
	dst_lv = pc_get_group_level(target_sd);
	if( !itemdb_cantrade(item, src_lv, dst_lv) && //Can't trade
		(pc->get_partner(sd) != target_sd || !itemdb_canpartnertrade(item, src_lv, dst_lv)) ) //Can't partner-trade
	{
		clif->message (sd->fd, msg_txt(260));
		clif->tradeitemok(sd, index+2, TIO_INDROCKS);
		return;
	}

	if( item->expire_time )
	{ // Rental System
		clif->message (sd->fd, msg_txt(260));
		clif->tradeitemok(sd, index+2, TIO_INDROCKS);
		return;
	}

	if( item->bound &&
			!( item->bound == IBT_GUILD && sd->status.guild_id == target_sd->status.guild_id ) &&
			!( item->bound == IBT_PARTY && sd->status.party_id == target_sd->status.party_id )
					&& !pc_can_give_bound_items(sd) ) {
		clif->message(sd->fd, msg_txt(293));
		clif->tradeitemok(sd, index+2, TIO_INDROCKS);
		return;
	}

	//Locate a trade position
	ARR_FIND( 0, 10, trade_i, sd->deal.item[trade_i].index == index || sd->deal.item[trade_i].amount == 0 );
	if( trade_i == 10 ) //No space left
	{
		clif->tradeitemok(sd, index+2, TIO_OVERWEIGHT);
		return;
	}

	trade_weight = sd->inventory_data[index]->weight * amount;
	if (target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight) {
		//fail to add item -- the player was over weighted.
		clif->tradeitemok(sd, index+2, TIO_OVERWEIGHT);
		return;
	}

	if (sd->deal.item[trade_i].index == index) {
		//The same item as before is being readjusted.
		if (sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount) {
			//packet deal exploit check
			amount = sd->status.inventory[index].amount - sd->deal.item[trade_i].amount;
			trade_weight = sd->inventory_data[index]->weight * amount;
		}
		sd->deal.item[trade_i].amount += amount;
	} else {
		//New deal item
		sd->deal.item[trade_i].index = index;
		sd->deal.item[trade_i].amount = amount;
	}
	sd->deal.weight += trade_weight;

	clif->tradeitemok(sd, index+2, TIO_SUCCESS); // Return the index as it was received
	clif->tradeadditem(sd, target_sd, index+2, amount);
}
Пример #6
0
/// Attaches an item to a message being written
/// @param sd : The player who's writting
/// @param idx : the inventory idx of the item
/// @param amount : Amount of the item to be attached
void rodex_add_item(struct map_session_data *sd, int16 idx, int16 amount)
{
	int i;
	bool is_stack = false;

	nullpo_retv(sd);

	if (idx < 0 || idx >= MAX_INVENTORY) {
		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR);
		return;
	}

	if (amount < 0 || amount > sd->status.inventory[idx].amount) {
		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR);
		return;
	}

	if (!pc_can_give_items(sd) || sd->status.inventory[idx].expire_time ||
		!itemdb_canmail(&sd->status.inventory[idx], pc_get_group_level(sd)) ||
		(sd->status.inventory[idx].bound && !pc_can_give_bound_items(sd))) {
		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_NOT_TRADEABLE);
		return;
	}

	if (itemdb->isstackable(sd->status.inventory[idx].nameid) == 1) {
		for (i = 0; i < RODEX_MAX_ITEM; ++i) {
			if (sd->rodex.tmp.items[i].idx == idx) {
				if (sd->status.inventory[idx].nameid == sd->rodex.tmp.items[i].item.nameid &&
					sd->status.inventory[idx].unique_id == sd->rodex.tmp.items[i].item.unique_id) {
					is_stack = true;
					break;
				}
			}
		}

		if (i == RODEX_MAX_ITEM && sd->rodex.tmp.items_count < RODEX_MAX_ITEM) {
			ARR_FIND(0, RODEX_MAX_ITEM, i, sd->rodex.tmp.items[i].idx == 0);
		}
	} else if (sd->rodex.tmp.items_count < RODEX_MAX_ITEM) {
		ARR_FIND(0, RODEX_MAX_ITEM, i, sd->rodex.tmp.items[i].idx == 0);
	} else {
		i = RODEX_MAX_ITEM;
	}

	if (i == RODEX_MAX_ITEM) {
		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_NO_SPACE);
		return;
	}

	if (sd->rodex.tmp.items[i].item.amount + amount > sd->status.inventory[idx].amount) {
		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR);
		return;
	}

	if (sd->rodex.tmp.weight + sd->inventory_data[idx]->weight * amount > RODEX_WEIGHT_LIMIT) {
		clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR);
		return;
	}

	sd->rodex.tmp.items[i].idx = idx;
	sd->rodex.tmp.weight += sd->inventory_data[idx]->weight * amount;
	if (is_stack == false) {
		sd->rodex.tmp.items[i].item = sd->status.inventory[idx];
		sd->rodex.tmp.items[i].item.amount = amount;
		sd->rodex.tmp.items_count++;
	} else {
		sd->rodex.tmp.items[i].item.amount += amount;
	}
	sd->rodex.tmp.type |= MAIL_TYPE_ITEM;

	clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_SUCCESS);
}