Esempio n. 1
0
bool buyingstore_setup(struct map_session_data* sd, unsigned char slots)
{
	if( !battle_config.feature_buying_store || sd->state.vending || sd->state.buyingstore || sd->state.trading || slots == 0 )
	{
		return false;
	}

	if( sd->sc.data[SC_NOCHAT] && (sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) )
	{// custom: mute limitation
		return false;
	}

	if( map->list[sd->bl.m].flag.novending ) {
		// custom: no vending maps
		clif->message(sd->fd, msg_sd(sd,276)); // "You can't open a shop on this map"
		return false;
	}

	if( map->getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) {
		// custom: no vending cells
		clif->message(sd->fd, msg_sd(sd,204)); // "You can't open a shop on this cell."
		return false;
	}

	if( slots > MAX_BUYINGSTORE_SLOTS )
	{
		ShowWarning("buyingstore_setup: %d slots requisitados, mas o servidor suporta apenas %d slots.\n", (int)slots, MAX_BUYINGSTORE_SLOTS);
		slots = MAX_BUYINGSTORE_SLOTS;
	}

	sd->buyingstore.slots = slots;
	clif->buyingstore_open(sd);

	return true;
}
Esempio n. 2
0
/*==========================================
 * Initiates a trade request.
 *------------------------------------------*/
void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd)
{
    nullpo_retv(sd);

    if (map->list[sd->bl.m].flag.notrade) {
        clif->message (sd->fd, msg_sd(sd,272)); // You can't trade in this map
        return;
    }

    if (target_sd == NULL || sd == target_sd) {
        clif->tradestart(sd, 1); // character does not exist
        return;
    }

    if (target_sd->npc_id) {
        //Trade fails if you are using an NPC.
        clif->tradestart(sd, 2);
        return;
    }

    if (!battle_config.invite_request_check) {
        if (target_sd->guild_invite > 0 || target_sd->party_invite > 0 || target_sd->adopt_invite) {
            clif->tradestart(sd, 2);
            return;
        }
    }

    if ( sd->trade_partner != 0 ) { // If a character tries to trade to another one then cancel the previous one
        struct map_session_data *previous_sd = map->id2sd(sd->trade_partner);
        if( previous_sd ) {
            previous_sd->trade_partner = 0;
            clif->tradecancelled(previous_sd);
        } // Once canceled then continue to the new one.
        sd->trade_partner = 0;
        clif->tradecancelled(sd);
    }

    if (target_sd->trade_partner != 0) {
        clif->tradestart(sd, 2); // person is in another trade
        return;
    }

    if (!pc_can_give_items(sd) || !pc_can_give_items(target_sd)) //check if both GMs are allowed to trade
    {
        clif->message(sd->fd, msg_sd(sd,246)); // Your GM level doesn't authorize you to perform this action.
        clif->tradestart(sd, 2); // GM is not allowed to trade
        return;
    }

    // Players can not request trade from far away, unless they are allowed to use @trade.
    if (!pc->can_use_command(sd, "@trade") &&
            (sd->bl.m != target_sd->bl.m || !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE))) {
        clif->tradestart(sd, 0); // too far
        return ;
    }

    target_sd->trade_partner = sd->status.account_id;
    sd->trade_partner = target_sd->status.account_id;
    clif->traderequest(target_sd, sd->status.name);
}
Esempio n. 3
0
/**
 * Joins a channel.
 *
 * Ban and password checks are performed before joining the channel.
 * If the channel is an HCS_TYPE_ALLY channel, alliance channels are automatically joined.
 *
 * @param chan     The channel to join
 * @param sd       The character
 * @param password The specified join password, if any
 * @param silent   If true, suppress the "You're now in the <x> channel" greeting message
 * @retval HCS_STATUS_OK      if the operation succeeded
 * @retval HCS_STATUS_ALREADY if the character is already in the channel
 * @retval HCS_STATUS_NOPERM  if the specified password doesn't match
 * @retval HCS_STATUS_BANNED  if the character is in the channel's ban list
 * @retval HCS_STATUS_FAIL    in case of generic error
 */
enum channel_operation_status channel_join(struct channel_data *chan, struct map_session_data *sd, const char *password, bool silent)
{
	bool stealth = false;

	nullpo_retr(HCS_STATUS_FAIL, chan);
	nullpo_retr(HCS_STATUS_FAIL, sd);
	nullpo_retr(HCS_STATUS_FAIL, password);

	if (idb_exists(chan->users, sd->status.char_id)) {
		return HCS_STATUS_ALREADY;
	}

	if (chan->password[0] != '\0' && strcmp(chan->password, password) != 0) {
		if (pc_has_permission(sd, PC_PERM_HCHSYS_ADMIN)) {
			stealth = true;
		} else {
			return HCS_STATUS_NOPERM;
		}
	}

	if (chan->banned && idb_exists(chan->banned, sd->status.account_id)) {
		return HCS_STATUS_BANNED;
	}

	if (!silent && !(chan->options&HCS_OPT_ANNOUNCE_JOIN)) {
		char output[CHAT_SIZE_MAX];
		if (chan->type == HCS_TYPE_MAP) {
			sprintf(output, msg_sd(sd,1435), chan->name, map->list[chan->m].name); // You're now in the '#%s' channel for '%s'
		} else {
			sprintf(output, msg_sd(sd,1403), chan->name); // You're now in the '%s' channel
		}
		clif->messagecolor_self(sd->fd, COLOR_DEFAULT, output);
	}

	if (chan->type == HCS_TYPE_ALLY) {
		struct guild *g = sd->guild;
		int i;
		for (i = 0; i < MAX_GUILDALLIANCE; i++) {
			struct guild *sg = NULL;
			if (g->alliance[i].opposition == 0 && g->alliance[i].guild_id && (sg = guild->search(g->alliance[i].guild_id)) != NULL) {
				if (!(sg->channel->banned && idb_exists(sg->channel->banned, sd->status.account_id))) {
					channel->join_sub(sg->channel, sd, stealth);
				}
			}
		}
	}

	channel->join_sub(chan, sd, stealth);

	return HCS_STATUS_OK;
}
Esempio n. 4
0
void buyingstore_open(struct map_session_data* sd, int account_id)
{
	struct map_session_data* pl_sd;

	nullpo_retv(sd);
	if( !battle_config.feature_buying_store || pc_istrading(sd) )
	{// not allowed to sell
		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.
		return;
	}

	if( ( pl_sd = map->id2sd(account_id) ) == NULL || !pl_sd->state.buyingstore ) {
		// not online or not buying
		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
		return;
	}

	// success
	clif->buyingstore_itemlist(sd, pl_sd);
}
Esempio n. 5
0
/**
 * Sends a message to a channel.
 *
 * @param chan The destination channel.
 * @param sd   The source character.
 * @param msg  The message to send.
 *
 * If no source character is specified, it'll send an anonymous message.
 */
void channel_send(struct channel_data *chan, struct map_session_data *sd, const char *msg)
{
	char message[150];
	nullpo_retv(chan);
	nullpo_retv(msg);

	if (sd && chan->msg_delay != 0
	 && DIFF_TICK(sd->hchsysch_tick + chan->msg_delay*1000, timer->gettick()) > 0
	 && !pc_has_permission(sd, PC_PERM_HCHSYS_ADMIN)) {
		clif->messagecolor_self(sd->fd, COLOR_RED, msg_sd(sd,1455));
		return;
	} else if (sd) {
		safesnprintf(message, 150, "[ #%s ] %s : %s", chan->name, sd->status.name, msg);
		clif->channel_msg(chan,sd,message);
		if (chan->type == HCS_TYPE_IRC)
			ircbot->relay(sd->status.name,msg);
		if (chan->msg_delay != 0)
			sd->hchsysch_tick = timer->gettick();
	} else {
		safesnprintf(message, 150, "[ #%s ] %s", chan->name, msg);
		clif->channel_msg2(chan, message);
		if (chan->type == HCS_TYPE_IRC)
			ircbot->relay(NULL, msg);
	}
}
Esempio n. 6
0
/**
 * Sends a message to a channel.
 *
 * @param chan The destination channel.
 * @param sd   The source character.
 * @param msg  The message to send.
 *
 * If no source character is specified, it'll send an anonymous message.
 */
static void channel_send(struct channel_data *chan, struct map_session_data *sd, const char *msg)
{
	char message[150];
	nullpo_retv(chan);
	nullpo_retv(msg);

	if (sd && chan->msg_delay != 0
	 && DIFF_TICK(sd->hchsysch_tick + chan->msg_delay*1000, timer->gettick()) > 0
	 && !pc_has_permission(sd, PC_PERM_HCHSYS_ADMIN)) {
		clif->messagecolor_self(sd->fd, COLOR_RED, msg_sd(sd,1455));
		return;
	} else if (sd) {
		int i;

		safesnprintf(message, 150, "[ #%s ] %s : %s", chan->name, sd->status.name, msg);
		clif->channel_msg(chan,sd,message);
		if (chan->type == HCS_TYPE_IRC)
			ircbot->relay(sd->status.name,msg);
		if (chan->msg_delay != 0)
			sd->hchsysch_tick = timer->gettick();

		for (i = 0; i < MAX_EVENTQUEUE; i++) {
			if (chan->handlers[i][0] != '\0') {
				pc->setregstr(sd, script->add_str("@channelmes$"), msg);
				npc->event(sd, chan->handlers[i], 0);
			}
		}
	} else {
		safesnprintf(message, 150, "[ #%s ] %s", chan->name, msg);
		clif->channel_msg2(chan, message);
		if (chan->type == HCS_TYPE_IRC)
			ircbot->relay(NULL, msg);
	}
}
Esempio n. 7
0
/*==========================================
 * player chatroom creation
 *------------------------------------------*/
bool chat_createpcchat(struct map_session_data* sd, const char* title, const char* pass, int limit, bool pub) {
	struct chat_data* cd;
	nullpo_ret(sd);
	nullpo_ret(title);
	nullpo_ret(pass);

	if( sd->chatID )
		return false; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex]

	if( sd->state.vending || sd->state.buyingstore )
	{// not chat, when you already have a store open
		return false;
	}

	if( map->list[sd->bl.m].flag.nochat ) {
		clif->message(sd->fd, msg_sd(sd,281));
		return false; //Can't create chatrooms on this map.
	}

	if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNOCHAT) ) {
		clif->message (sd->fd, msg_sd(sd,865)); // "Can't create chat rooms in this area."
		return false;
	}

	pc_stop_walking(sd, STOPWALKING_FLAG_FIXPOS);

	cd = chat->create(&sd->bl, title, pass, limit, pub, 0, "", 0, 1, MAX_LEVEL);
	if( cd ) {
		cd->users = 1;
		cd->usersd[0] = sd;
		pc_setchatid(sd,cd->bl.id);
		pc_stop_attack(sd);
		clif->createchat(sd,0); // 0 = success
		clif->dispchat(cd,0);
		return true;
	}
	clif->createchat(sd,1); // 1 = Room limit exceeded

	return false;
}
Esempio n. 8
0
bool buyingstore_setup(struct map_session_data* sd, unsigned char slots)
{
	nullpo_retr(false, sd);
	if( !battle_config.feature_buying_store || sd->state.vending || sd->state.buyingstore || sd->state.trading || slots == 0 )
	{
		return false;
	}

	if(pc_ismuted(&sd->sc, MANNER_NOROOM))
	{// custom: mute limitation
		return false;
	}

	if( map->list[sd->bl.m].flag.novending ) {
		// custom: no vending maps
		clif->message(sd->fd, msg_sd(sd,276)); // "You can't open a shop on this map"
		return false;
	}

	if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING)) {
		// custom: no vending cells
		clif->message(sd->fd, msg_sd(sd,204)); // "You can't open a shop on this cell."
		return false;
	}

	if( slots > MAX_BUYINGSTORE_SLOTS )
	{
		ShowWarning("buyingstore_setup: Requested %d slots, but server supports only %d slots.\n", (int)slots, MAX_BUYINGSTORE_SLOTS);
		slots = MAX_BUYINGSTORE_SLOTS;
	}

	sd->buyingstore.slots = slots;
	clif->buyingstore_open(sd);

	return true;
}
Esempio n. 9
0
/*==========================================
 * Request a shop's item list
 *------------------------------------------*/
void vending_vendinglistreq(struct map_session_data* sd, unsigned int id) {
	struct map_session_data* vsd;
	nullpo_retv(sd);

	if( (vsd = map->id2sd(id)) == NULL )
		return;
	if( !vsd->state.vending )
		return; // not vending

	if (!pc_can_give_items(sd) || !pc_can_give_items(vsd)) { //check if both GMs are allowed to trade
		// GM is not allowed to trade
		clif->message(sd->fd, msg_sd(sd,246));
		return;
	}

	sd->vended_id = vsd->vender_id;  // register vending uid

	clif->vendinglist(sd, id, vsd->vending);
}
Esempio n. 10
0
/*==========================================
 * Purchase item(s) from a shop
 *------------------------------------------*/
void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid, const uint8* data, int count) {
	int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING];
	double z;
	struct s_vending vend[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(&vend, &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( vend[j].amount > vsd->status.cart[idx].amount )
			vend[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( vend[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;
		}

		vend[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
		}
	}

	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;
		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_sd(vsd,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( map->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->close(vsd);
			map->quit(vsd); //They have no reason to stay around anymore, do they?
		} else
			pc->autotrade_update(vsd,PAUC_REFRESH);
	}
}
Esempio n. 11
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;
		sd->vending[i].value = cap_value(value, 0, (unsigned int)battle_config.vending_max_value);

		i++; // item successfully added
	}

	if( i != j )
		clif->message (sd->fd, msg_sd(sd,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);
}
Esempio n. 12
0
void buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count)
{
	unsigned int i, weight, listidx;

	nullpo_retv(sd);
	if (!result || count == 0) {
		// canceled, or no items
		return;
	}

	if( !battle_config.feature_buying_store || pc_istrading(sd) || sd->buyingstore.slots == 0 || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || !storename[0] )
	{// disabled or invalid input
		sd->buyingstore.slots = 0;
		clif->buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
		return;
	}

	if( !pc_can_give_items(sd) )
	{// custom: GM is not allowed to buy (give zeny)
		sd->buyingstore.slots = 0;
		clif->message(sd->fd, msg_sd(sd,246)); // Your GM level doesn't authorize you to perform this action.
		clif->buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
		return;
	}

	if(pc_ismuted(&sd->sc, MANNER_NOROOM))
	{// custom: mute limitation
		return;
	}

	if( map->list[sd->bl.m].flag.novending ) {
		// custom: no vending maps
		clif->message(sd->fd, msg_sd(sd,276)); // "You can't open a shop on this map"
		return;
	}

	if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING)) {
		// custom: no vending cells
		clif->message(sd->fd, msg_sd(sd,204)); // "You can't open a shop on this cell."
		return;
	}

	weight = sd->weight;

	// check item list
	for (i = 0; i < count; i++) {
		// itemlist: <name id>.W <amount>.W <price>.L
		unsigned short nameid, amount;
		int price, idx;
		struct item_data* id;

		nameid = RBUFW(itemlist,i*8+0);
		amount = RBUFW(itemlist,i*8+2);
		price  = RBUFL(itemlist,i*8+4);

		if( ( id = itemdb->exists(nameid) ) == NULL || amount == 0 )
		{// invalid input
			break;
		}

		if( price <= 0 || price > BUYINGSTORE_MAX_PRICE )
		{// invalid price: unlike vending, items cannot be bought at 0 Zeny
			break;
		}

		if (!id->flag.buyingstore || !itemdb->cantrade_sub(id, pc_get_group_level(sd), pc_get_group_level(sd))
		 || (idx = pc->search_inventory(sd, nameid)) == INDEX_NOT_FOUND
		 ) { // restrictions: allowed, no character-bound items and at least one must be owned
			break;
		}

		if( sd->status.inventory[idx].amount+amount > BUYINGSTORE_MAX_AMOUNT )
		{// too many items of same kind
			break;
		}

		if( i )
		{// duplicate check. as the client does this too, only malicious intent should be caught here
			ARR_FIND( 0, i, listidx, sd->buyingstore.items[listidx].nameid == nameid );
			if( listidx != i )
			{// duplicate
				ShowWarning("buyingstore_create: Found duplicate item on buying list (nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", nameid, amount, sd->status.account_id, sd->status.char_id);
				break;
			}
		}

		weight+= id->weight*amount;
		sd->buyingstore.items[i].nameid = nameid;
		sd->buyingstore.items[i].amount = amount;
		sd->buyingstore.items[i].price  = price;
	}

	if( i != count )
	{// invalid item/amount/price
		sd->buyingstore.slots = 0;
		clif->buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
		return;
	}

	if( (sd->max_weight*90)/100 < weight )
	{// not able to carry all wanted items without getting overweight (90%)
		sd->buyingstore.slots = 0;
		clif->buyingstore_open_failed(sd, BUYINGSTORE_CREATE_OVERWEIGHT, weight);
		return;
	}

	// success
	sd->state.buyingstore = true;
	sd->buyer_id = buyingstore->getuid();
	sd->buyingstore.zenylimit = zenylimit;
	sd->buyingstore.slots = i;  // store actual amount of items
	safestrncpy(sd->message, storename, sizeof(sd->message));
	clif->buyingstore_myitemlist(sd);
	clif->buyingstore_entry(sd);
}
Esempio n. 13
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);
	}
}
Esempio n. 14
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_sd(sd,260)); // This item cannot be traded.
        clif->tradeitemok(sd, index+2, TIO_INDROCKS);
        return;
    }

    if( item->expire_time )
    {   // Rental System
        clif->message (sd->fd, msg_sd(sd,260)); // This item cannot be traded.
        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_sd(sd,293)); // This bound item cannot be traded to that character.
        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);
}