int chrif_changesex(struct map_session_data *sd) { chrif_check(-1); WFIFOHEAD(char_fd,44); WFIFOW(char_fd,0) = 0x2b0e; WFIFOL(char_fd,2) = sd->status.account_id; safestrncpy((char*)WFIFOP(char_fd,6), sd->status.name, NAME_LENGTH); WFIFOW(char_fd,30) = 5; WFIFOSET(char_fd,44); clif_displaymessage(sd->fd, msg_txt(410)); //"Need disconnection to perform change-sex request..." if (sd->fd) clif_authfail_fd(sd->fd, 15); else map_quit(sd); return 0; }
void channel_list(struct map_session_data *sd) { // Display a list of all channels char output[256]; struct channel_data *cd; DBIterator* iter; DBKey key; iter = channel_db->iterator(channel_db); for( cd = (struct channel_data *)iter->first(iter,&key); iter->exists(iter); cd = (struct channel_data *)iter->next(iter,&key) ) { if( cd->users < 1 && cd->type == CHN_USER ) continue; sprintf(output, msg_txt(812), cd->name, cd->users, !cd->pass[0] ? msg_txt(813) : msg_txt(814)); clif_displaymessage(sd->fd, output); } iter->destroy(iter); return; }
/*========================================== * Request a shop's item list *------------------------------------------*/ void vending_vendinglistreq(struct map_session_data* sd, int id) { struct map_session_data* vsd; nullpo_retv(sd); if( (vsd = map_id2sd(id)) == NULL ) return; if( vsd->vender_id == 0 ) return; // not vending if ( !pc_can_give_items(pc_isGM(sd)) || !pc_can_give_items(pc_isGM(vsd)) ) //check if both GMs are allowed to trade { // GM is not allowed to trade clif_displaymessage(sd->fd, msg_txt(246)); return; } clif_vendinglist(sd, id, vsd->vending); }
/*========================================== * 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_displaymessage(sd->fd, msg_txt(246)); return; } sd->vended_id = vsd->vender_id; // register vending uid clif_vendinglist(sd, id, vsd->vending); }
int pet_change_name_ack(struct map_session_data *sd, char* name, int flag) { struct pet_data *pd = sd->pd; if (!pd) return 0; normalize_name(name," ");//bugreport:3032 if ( !flag || !strlen(name) ) { clif_displaymessage(sd->fd, msg_txt(sd,280)); // You cannot use this name for your pet. clif_send_petstatus(sd); //Send status so client knows oet name change got rejected. return 0; } memcpy(pd->pet.name, name, NAME_LENGTH); clif_charnameack (0,&pd->bl); pd->pet.rename_flag = 1; clif_pet_equip_area(pd); clif_send_petstatus(sd); return 1; }
/** * Player attempt tp open his storage. * @param sd : player * @return 0:success, 1:fail */ int storage_storageopen(struct map_session_data *sd) { nullpo_ret(sd); if(sd->state.storage_flag) return 1; //Already open? if( !pc_can_give_items(sd) ) { // check is this GM level is allowed to put items to storage clif_displaymessage(sd->fd, msg_txt(sd,246)); return 1; } sd->state.storage_flag = 1; storage_sortitem(sd->storage.u.items_storage, sd->storage.max_amount); clif_storagelist(sd, sd->storage.u.items_storage, sd->storage.max_amount, storage_getName(0)); clif_updatestorageamount(sd, sd->storage.amount, sd->storage.max_amount); return 0; }
int bg_send_xy_timer_sub(DBKey key, void *data, va_list ap) { struct battleground_data *bg = (struct battleground_data *)data; struct map_session_data *sd; char output[128]; int i, m; nullpo_ret(bg); m = map_mapindex2mapid(bg->mapindex); bg->reveal_flag = !bg->reveal_flag; // Switch for( i = 0; i < MAX_BG_MEMBERS; i++ ) { if( (sd = bg->members[i].sd) == NULL ) continue; if( battle_config.bg_idle_autokick && DIFF_TICK(last_tick, sd->idletime) >= battle_config.bg_idle_autokick && bg->g ) { sprintf(output, "- AFK [%s] Kicked -", sd->status.name); clif_broadcast2(&sd->bl, output, (int)strlen(output)+1, bg->color, 0x190, 20, 0, 0, BG); bg_team_leave(sd,3); clif_displaymessage(sd->fd, "You have been kicked from Battleground because of your AFK status."); pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); continue; } if( sd->bl.x != bg->members[i].x || sd->bl.y != bg->members[i].y ) { // xy update bg->members[i].x = sd->bl.x; bg->members[i].y = sd->bl.y; clif_bg_xy(sd); } if( bg->reveal_pos && bg->reveal_flag && sd->bl.m == m ) // Reveal each 4 seconds map_foreachinmap(bg_reveal_pos,m,BL_PC,sd,1,bg->color); if( battle_config.bg_idle_announce && !sd->state.bg_afk && DIFF_TICK(last_tick, sd->idletime) >= battle_config.bg_idle_announce && bg->g ) { // Idle announces sd->state.bg_afk = 1; sprintf(output, "%s : %s seens to be away. AFK Warning - Can be kicked out with @reportafk", bg->g->name, sd->status.name); clif_bg_message(bg, bg->bg_id, bg->g->name, output, strlen(output) + 1); } } return 0; }
/*========================================== * Opens a storage. Returns: * 0 - success * 1 - fail *------------------------------------------*/ int storage_storageopen(struct map_session_data *sd) { nullpo_ret(sd); if(sd->state.storage_flag) return 1; //Already open? if( !pc_can_give_items(sd) ) { //check is this GM level is allowed to put items to storage clif_displaymessage(sd->fd, msg_txt(sd,246)); return 1; } sd->state.storage_flag = 1; storage_sortitem(sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); clif_storagelist(sd, sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); clif_updatestorageamount(sd, sd->status.storage.storage_amount, MAX_STORAGE); return 0; }
int ext_storage_open(struct map_session_data *sd) { nullpo_ret(sd); if(sd->state.storage_flag) return 1; if( !pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level is allowed to put items to storage clif_displaymessage(sd->fd, msg_txt(246)); return 1; } sd->state.storage_flag = 3; storage_sortitem(sd->status.ext_storage.items, ARRAYLENGTH(sd->status.ext_storage.items)); clif_storagelist(sd, sd->status.ext_storage.items, ARRAYLENGTH(sd->status.ext_storage.items)); clif_updateextrastorageamount(sd,sd->status.ext_storage.storage_amount); return 0; }
// ギルド敵対 int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd) { struct guild *g; int i; nullpo_retr(0, sd); g=guild_search(sd->status.guild_id); if(g==NULL || tsd==NULL) return 0; // Prevent creation opposition with same guilds [LuzZza] if(sd->status.guild_id == tsd->status.guild_id) return 0; if( guild_get_alliance_count(g,1)>=3 ) { clif_guild_oppositionack(sd,1); return 0; } if(agit_flag) { clif_displaymessage(sd->fd,"You cannot make oppositions during Guild Wars!"); return 0; } for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに関係を持っているか確認 if(g->alliance[i].guild_id==tsd->status.guild_id){ if(g->alliance[i].opposition==1){ // すでに敵対 clif_guild_oppositionack(sd,2); return 0; } //Change alliance to opposition. intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, sd->status.account_id,tsd->status.account_id,8 ); } } // inter鯖に敵対要請 intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, sd->status.account_id,tsd->status.account_id,1 ); return 0; }
int pet_birth_process(struct map_session_data *sd, struct s_pet *pet) { char pet_output[1024]; // Declaracion de char para Invocacion Pet's [Tab] nullpo_retr(1, sd); Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); if(sd->status.pet_id && pet->incuvate == 1) { sd->status.pet_id = 0; return 1; } pet->incuvate = 0; pet->account_id = sd->status.account_id; pet->char_id = sd->status.char_id; sd->status.pet_id = pet->pet_id; if(pet_data_init(sd, pet)) { sd->status.pet_id = 0; return 1; } intif_save_petdata(sd->status.account_id,pet); if (save_settings&8) chrif_save(sd,0); //is it REALLY Needed to save the char for hatching a pet? [Skotlex] if(sd->bl.prev != NULL) { map_addblock(&sd->pd->bl); 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); } Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); clif_misceffect(&sd->pd->bl, 0); // Efecto 1 de nacimiento [Tab] clif_misceffect(&sd->pd->bl, 344); // Efecto 2 de nacimiento [Tab] sprintf(pet_output,"Get Out %s... NOW!",pet->name); // Cuidado aca con el nombre del pet clif_displaymessage(sd->fd, pet_output); // Frase nacimiento [Tab] return 0; }
/*========================================== * 性別変化終了 (modified by Yor) *------------------------------------------ */ static void chrif_changedsex(Session *, const Packet_Fixed<0x2b0d>& fixed) { dumb_ptr<map_session_data> sd; AccountId acc = fixed.account_id; SEX sex = fixed.sex; if (battle_config.etc_log) PRINTF("chrif_changedsex %d.\n"_fmt, acc); sd = map_id2sd(account_to_block(acc)); if (acc) { if (sd != nullptr && sd->status.sex != sex) { if (sd->status.sex == SEX::MALE) sd->sex = sd->status.sex = SEX::FEMALE; else if (sd->status.sex == SEX::FEMALE) sd->sex = sd->status.sex = SEX::MALE; // to avoid any problem with equipment and invalid sex, equipment is unequiped. for (IOff0 i : IOff0::iter()) { if (sd->status.inventory[i].nameid && bool(sd->status.inventory[i].equip)) pc_unequipitem(sd, i, CalcStatus::NOW); } // save character chrif_save(sd); sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it) clif_displaymessage(sd->sess, "Your sex has been changed (need disconexion by the server)..."_s); clif_setwaitclose(sd->sess); // forced to disconnect for the change } } else { if (sd != nullptr) { PRINTF("chrif_changedsex failed.\n"_fmt); } } }
/*========================================== * R 2b0f <accid>.l <name>.24B <type>.w <answer>.w * Reply to chrif_char_ask_name() (request to do some character operation) * type of operation: * 1: block, 2: ban, 3: unblock, 4: unban, 5: changesex * type of answer: * 0: login-server request done * 1: player not found * 2: gm level too low * 3: login-server offline *------------------------------------------*/ int chrif_char_ask_name_answer(int fd) { struct map_session_data* sd; char* action; char output[256]; int acc = RFIFOL(fd,2); // account_id of who has asked (-1 if nobody) char* player_name = (char*)RFIFOP(fd,6); uint16 type; uint16 answer; type = RFIFOW(fd,30); answer = RFIFOW(fd,32); sd = map_id2sd(acc); if (acc < 0 || sd == NULL) { ShowError("chrif_char_ask_name_answer failed - player not online.\n"); return 0; } switch(type) { case 1: action = "block"; break; case 2: action = "ban"; break; case 3: action = "unblock"; break; case 4: action = "unban"; break; case 5: action = "change the sex of"; break; default: action = "???"; break; } switch(answer) { case 0: sprintf(output, "Login-server has been asked to %s the player '%20s'.", action, player_name); break; case 1: sprintf(output, "The player '%20s' doesn't exist.", player_name); break; case 2: sprintf(output, "Your GM level don't authorise you to %s the player '%20s'.", action, player_name); break; case 3: sprintf(output, "Login-server is offline. Impossible to %s the player '%20s'.", action, player_name); break; default: output[0] = '\0'; break; } clif_displaymessage(sd->fd, output); return 0; }
/** * Pet menu options. * @param sd : player requesting * @param menunum : menu option chosen * @return 0:success, 1:failure */ int pet_menu(struct map_session_data *sd,int menunum) { struct item_data *egg_id; nullpo_ret(sd); if (sd->pd == NULL) return 1; //You lost the pet already. if(!sd->status.pet_id || sd->pd->pet.intimate <= 0 || sd->pd->pet.incubate) return 1; egg_id = itemdb_exists(sd->pd->petDB->EggID); if (egg_id) { if ((egg_id->flag.trade_restriction&0x01) && !pc_inventoryblank(sd)) { clif_displaymessage(sd->fd, msg_txt(sd, 451)); // You can't return your pet because your inventory is full. return 1; } } switch(menunum) { case 0: clif_send_petstatus(sd); break; case 1: pet_food(sd, sd->pd); break; case 2: pet_performance(sd, sd->pd); break; case 3: pet_return_egg(sd, sd->pd); break; case 4: pet_unequipitem(sd, sd->pd); break; } return 0; }
/** * Attempt to set item or zeny * @param sd * @param idx 0 - Zeny; >= 2 - Inventory item * @param amount * @return True if item/zeny can be set, False if failed */ bool mail_setitem(struct map_session_data *sd, short idx, int amount) { if( sd->state.account_protection ) { clif_displaymessage(sd->fd, "Your Account is Locked."); return 1; } if( pc_istrading(sd) ) return false; if( idx == 0 ) { // Zeny Transfer if( !pc_can_give_items(sd) ) return false; if( amount > sd->status.zeny ) amount = sd->status.zeny; sd->mail.zeny = amount; // clif_updatestatus(sd, SP_ZENY); return true; } else { // Item Transfer idx -= 2; mail_removeitem(sd, 0); if( idx < 0 || idx >= MAX_INVENTORY ) return false; if( amount > sd->status.inventory[idx].amount ) return false; if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time || !itemdb_available(sd->status.inventory[idx].nameid) || !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) || (sd->status.inventory[idx].bound && !pc_can_give_bounded_items(sd)) ) return false; sd->mail.index = idx; sd->mail.nameid = sd->status.inventory[idx].nameid; sd->mail.amount = amount; return true; } }
/*========================================== * Disconnection of a player (account has been deleted in login-server) by [Yor] *------------------------------------------*/ int chrif_accountdeletion(int fd) { int acc; struct map_session_data *sd; acc = RFIFOL(fd,2); if (battle_config.etc_log) ShowNotice("chrif_accountdeletion %d.\n", acc); sd = map_id2sd(acc); if (acc > 0) { if (sd != NULL) { sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters clif_displaymessage(sd->fd, "Your account has been deleted (disconnection)..."); clif_setwaitclose(sd->fd); // forced to disconnect for the change } } else { if (sd != NULL) ShowError("chrif_accountdeletion failed - player not online.\n"); } return 0; }
void buyingstore_open(struct map_session_data* sd, int account_id) { struct map_session_data* pl_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_displaymessage(sd->fd, msg_txt(246)); return; } if( (pl_sd = map_id2sd(account_id)) == NULL || !pl_sd->state.buyingstore ) // Not online or not buying return; // Out of view range if( !searchstore_queryremote(sd, account_id) && (sd->bl.m != pl_sd->bl.m || !check_distance_bl(&sd->bl, &pl_sd->bl, AREA_SIZE)) ) return; // Success clif_buyingstore_itemlist(sd, pl_sd); }
/*========================================== * Reloads the instance in runtime (reloadscript) *------------------------------------------*/ void do_reload_instance(void) { struct instance_data *im; struct instance_db *db; struct s_mapiterator* iter; struct map_session_data *sd; int i; for( i = 1; i < MAX_INSTANCE_DATA; i++ ) { im = &instance_data[i]; if(!im->cnt_map) continue; else { // First we load the NPCs again instance_addnpc(im); // Create new keep timer if((db = instance_searchtype_db(im->type)) != NULL) im->keep_limit = (unsigned int)time(NULL) + db->limit; } } // Reset player to instance beginning iter = mapit_getallusers(); for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) if(sd && map[sd->bl.m].instance_id) { struct party_data *p; if(!(p = party_search(sd->status.party_id)) || p->instance_id != map[sd->bl.m].instance_id) // Someone not in party is on instance map continue; im = &instance_data[p->instance_id]; if((db = instance_searchtype_db(im->type)) != NULL && !instance_enter(sd,db->name)) { // All good clif_displaymessage(sd->fd, msg_txt(sd,515)); // Instance has been reloaded instance_reqinfo(sd,p->instance_id); } else // Something went wrong ShowError("do_reload_instance: Error setting character at instance start: character_id=%d instance=%s.\n",sd->status.char_id,db->name); } mapit_free(iter); }
/** * A player is attempting to change the channel color * @param sd: Player data * @param chname: Channel name * @param color: New color * @return 0 on success or -1 on failure */ int channel_pccolor(struct map_session_data *sd, char *chname, char *color){ struct Channel *channel; char output[CHAT_SIZE_MAX]; int k; if(!sd) return 0; if( channel_chk(chname,NULL,1) ) { clif_displaymessage(sd->fd, msg_txt(sd,1405));// Channel name must start with '#'. return -1; } channel = channel_name2channel(chname,sd,0); if( !channel ) { sprintf(output, msg_txt(sd,1407), chname);// Channel '%s' is not available. clif_displaymessage(sd->fd, output); return -1; } if( !pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) { if (channel->char_id != sd->status.char_id) { sprintf(output, msg_txt(sd,1412), chname);// You're not the owner of channel '%s'. clif_displaymessage(sd->fd, output); return -1; } else if (!(channel->opt&CHAN_OPT_COLOR_OVERRIDE)) { sprintf(output, msg_txt(sd,764), chname); // You cannot change the color for channel '%s'. clif_displaymessage(sd->fd, output); return -1; } } ARR_FIND(0,channel_config.colors_count,k,( strcmpi(color,channel_config.colors_name[k]) == 0 ) ); if( k >= channel_config.colors_count ) { sprintf(output, msg_txt(sd,1411), color);// Unknown color '%s'. clif_displaymessage(sd->fd, output); return -1; } channel->color = channel_config.colors[k]; sprintf(output, msg_txt(sd,1413),chname,channel_config.colors_name[k]);// '%s' channel color updated to '%s'. clif_displaymessage(sd->fd, output); return 0; }
/** * Attempt to open guild storage for player * @param sd : player * @return 0 : success, 1 : fail, 2 : no guild found */ char gstorage_storageopen(struct map_session_data* sd) { struct guild_storage *gstor; nullpo_ret(sd); if(sd->status.guild_id <= 0) return 2; if(sd->state.storage_flag) return 1; //Can't open both storages at a time. if( !pc_can_give_items(sd) ) { //check is this GM level can open guild storage and store items [Lupus] clif_displaymessage(sd->fd, msg_txt(sd,246)); return 1; } if((gstor = gstorage_get_storage(sd->status.guild_id)) == NULL) { intif_request_guild_storage(sd->status.account_id,sd->status.guild_id); return 0; } if(gstor->opened) return 1; if( gstor->locked ) return 1; gstor->opened = sd->status.char_id; sd->state.storage_flag = 2; storage_sortitem(gstor->items, ARRAYLENGTH(gstor->items)); clif_storagelist(sd, gstor->items, ARRAYLENGTH(gstor->items)); clif_updatestorageamount(sd, gstor->storage_amount, MAX_GUILD_STORAGE); return 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; struct item_data* id; 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(pc_isGM(sd)) ) {// custom: GM is not allowed to buy (give zeny) sd->buyingstore.slots = 0; clif_displaymessage(sd->fd, msg_txt(246)); clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0); return; } if( sd->sc.data[SC_NOCHAT] && (sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) ) {// custom: mute limitation return; } if( map[sd->bl.m].flag.novending || map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) {// custom: no vending maps/cells clif_displaymessage(sd->fd, msg_txt(276)); // "You can't open a shop on this map" 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; 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_isGM(sd), pc_isGM(sd)) || ( idx = pc_search_inventory(sd, nameid) ) == -1 ) {// 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); }
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); } }
/** * Player setup a new shop * @param sd : player opening the shop * @param message : shop title * @param data : itemlist data \n * data := {<index>.w <amount>.w <value>.l}[count] * @param count : number of different items * @return 0 If success, 1 - Cannot open (die, not state.prevend, trading), 2 - No cart, 3 - Count issue, 4 - Cart data isn't saved yet, 5 - No valid item found */ char vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count) { int i, j; int vending_skill_lvl; char message_sql[MESSAGE_SIZE*2]; nullpo_retr(false,sd); if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd)) { return 1; // 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 2; } // 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 3; } if (save_settings&2) // Avoid invalid data from saving chrif_save(sd, 0); // 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_bounded_items(sd)) // can't trade account bound items and has no 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 = min(value, (unsigned int)battle_config.vending_max_value); // Player just moved item to cart and we don't have the correct cart ID yet. if (sd->status.cart[sd->vending[i].index].id == 0) { struct item_data *idb = itemdb_search(sd->status.cart[index].nameid); char msg[256]; sprintf(msg, msg_txt(sd, 733), idb->jname); clif_displaymessage(sd->fd, msg); clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); return 4; } i++; // item successfully added } if( i != j ) clif_displaymessage (sd->fd, msg_txt(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 5; } sd->state.prevend = 0; sd->state.vending = true; sd->vender_id = vending_getuid(); sd->vend_num = i; safestrncpy(sd->message, message, MESSAGE_SIZE); Sql_EscapeString( mmysql_handle, message_sql, sd->message ); if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`autotrade`, `body_direction`, `head_direction`, `sit`) " "VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, '%d', '%d', '%d' );", vendings_db, sd->vender_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->state.autotrade, sd->ud.dir, sd->head_dir, pc_issit(sd) ) != SQL_SUCCESS ){ Sql_ShowDebug(mmysql_handle); } for( i = 0; i < count; i++ ) { if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`vending_id`,`index`,`cartinventory_id`,`amount`,`price`) VALUES( %d, %d, %d, %d, %d );", vending_items_db, sd->vender_id, i, sd->status.cart[sd->vending[i].index].id, sd->vending[i].amount, sd->vending[i].value ) != SQL_SUCCESS ){ Sql_ShowDebug(mmysql_handle); } } clif_openvending(sd,sd->bl.id,sd->vending); clif_showvendingboard(&sd->bl,message,0); idb_put(vending_db, sd->status.char_id, sd); return 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? } } }
/*========================================== * Open shop * data := {<index>.w <amount>.w <value>.l}[count] *------------------------------------------*/ void vending_openvending(struct map_session_data* sd, const char* message, bool flag, const uint8* data, int count) { int i, j, char_id; int vending_skill_lvl; nullpo_retv(sd); if( !flag ) // cancelled return; // nothing to do if (pc_istrading(sd)) return; // 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, 0, 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, 0, 0); return; } if((sd->bl.m == map_mapname2mapid("mercadores") && ( ((sd->bl.x != 65) && (sd->bl.x != 74) && (sd->bl.x != 85) && (sd->bl.x != 94) && (sd->bl.x != 34) && (sd->bl.x != 25) && (sd->bl.x != 14) && (sd->bl.x != 5) && (sd->bl.x != 44) && (sd->bl.x != 55)) ))) { clif_displaymessage(sd->fd,"Você não pode abrir lojas no meio da Sala."); 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 // Can't Trade Account bound items || ( sd->status.cart[index].card[0] == CARD0_CREATE && (char_id = MakeDWord(sd->status.cart[index].card[2],sd->status.cart[index].card[3])) > 0 && ((battle_config.bg_reserved_char_id && char_id == battle_config.bg_reserved_char_id) || (battle_config.ancient_reserved_char_id && char_id == battle_config.ancient_reserved_char_id)) ) || !itemdb_cantrade(&sd->status.cart[index], pc_isGM(sd), pc_isGM(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_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, 0, 0); // custom reply packet return; } sd->state.vending = true; sd->vender_id = vending_getuid(); sd->vend_num = i; safestrncpy(sd->message, message, MESSAGE_SIZE); pc_stop_walking(sd,1); clif_openvending(sd,sd->bl.id,sd->vending); clif_showvendingboard(&sd->bl,message,0); if( battle_config.channel_announces&0x10 ) { char chat_message[256]; sprintf(chat_message, msg_txt(820), vending_chat_nick, sd->status.name, sd->message, map[sd->bl.m].name, sd->bl.x, sd->bl.y); clif_channel_message(server_channel[CHN_VENDING], chat_message, 27); } if( map[sd->bl.m].flag.vending_cell ) map_setcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_NOBOARDS, false); }
/*========================================== * Initiates a trade request. *------------------------------------------*/ void trade_traderequest (struct map_session_data *sd, struct map_session_data *target_sd) { nullpo_retv (sd); if (map[sd->bl.m].flag.notrade) { clif_displaymessage (sd->fd, msg_txt (272)); return; //Can't trade in notrade mapflag maps. } 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 cancelled 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_displaymessage (sd->fd, msg_txt (246)); 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", COMMAND_ATCOMMAND) && (sd->bl.m != target_sd->bl.m || !check_distance_bl (&sd->bl, &target_sd->bl, TRADE_DISTANCE))) { clif_tradestart (sd, 0); // too far return ; } if (target_sd->weight > target_sd->max_weight || sd->weight > sd->max_weight) { clif_tradestart (sd, 2); // trade failed (overweight) return; } if (sd->trade_partner != 0) trade_tradecancel (sd); if (sd->state.vending || target_sd->state.vending) return; // ignore request at the last minute (official) target_sd->state.can_tradeack = 1; target_sd->trade_partner = sd->status.account_id; sd->trade_partner = target_sd->status.account_id; clif_traderequest (target_sd, sd->status.name); }
int party_invite(struct map_session_data *sd,struct map_session_data *tsd) { struct party_data *p=party_search(sd->status.party_id); int i,flag=0; nullpo_retr(0, sd); if (p==NULL) return 0; if(tsd==NULL) { //TODO: Find the correct reply packet. clif_displaymessage(sd->fd, msg_txt(3)); return 0; } //Only leader can invite. ARR_FIND(0, MAX_PARTY, i, p->data[i].sd == sd); if (i == MAX_PARTY || !p->party.member[i].leader) { //TODO: Find the correct reply packet. clif_displaymessage(sd->fd, msg_txt(282)); return 0; } if(!battle_config.invite_request_check) { if (tsd->guild_invite>0 || tsd->trade_partner || tsd->adopt_invite) { clif_party_inviteack(sd,tsd->status.name,0); return 0; } } if (!tsd->fd) { //You can't invite someone who has already disconnected. clif_party_inviteack(sd,tsd->status.name,1); return 0; } if( tsd->status.party_id > 0 || tsd->party_invite > 0 ) {// already associated with a party clif_party_inviteack(sd,tsd->status.name,0); return 0; } for(i=0;i<MAX_PARTY;i++){ if(p->party.member[i].account_id == 0) //Room for a new member. flag = 1; /* By default Aegis BLOCKS more than one char from the same account on a party. * But eA does support it... so this check is left commented. if(p->party.member[i].account_id==tsd->status.account_id) { clif_party_inviteack(sd,tsd->status.name,4); return 0; } */ } if (!flag) { //Full party. clif_party_inviteack(sd,tsd->status.name,3); return 0; } tsd->party_invite=sd->status.party_id; tsd->party_invite_account=sd->status.account_id; clif_party_invite(sd,tsd); return 1; }
/** * Attempt to create new buying store * @param sd * @param zenylimit * @param result * @param storename * @param *itemlist { <nameid>.W, <amount>.W, <price>.L }* * @param count Number of item on the itemlist * @return 0 If success, 1 - Cannot open, 2 - Manner penalty, 3 - Mapflag restiction, 4 - Cell restriction, 5 - Invalid count/result, 6 - Cannot give item, 7 - Will be overweight */ char 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; char message_sql[MESSAGE_SIZE*2]; nullpo_retr(1, sd); if( !result || count == 0 ) {// canceled, or no items return 5; } 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 1; } if( !pc_can_give_items(sd) ) {// custom: GM is not allowed to buy (give zeny) sd->buyingstore.slots = 0; clif_displaymessage(sd->fd, msg_txt(sd,246)); clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0); return 6; } if( sd->sc.data[SC_NOCHAT] && (sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) ) {// custom: mute limitation return 2; } if( map[sd->bl.m].flag.novending ) {// custom: no vending maps clif_displaymessage(sd->fd, msg_txt(sd,276)); // "You can't open a shop on this map" return 3; } if( map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) {// custom: no vending cells clif_displaymessage(sd->fd, msg_txt(sd,204)); // "You can't open a shop on this cell." return 4; } 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) ) == -1 ) {// 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 5; } 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 7; } // 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)); Sql_EscapeString( mmysql_handle, message_sql, sd->message ); if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`limit`,`autotrade`, `body_direction`, `head_direction`, `sit`) " "VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, %d, '%d', '%d', '%d' );", buyingstores_db, sd->buyer_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->buyingstore.zenylimit, sd->state.autotrade, sd->ud.dir, sd->head_dir, pc_issit(sd) ) != SQL_SUCCESS ){ Sql_ShowDebug(mmysql_handle); } for( i = 0; i < sd->buyingstore.slots; i++ ){ if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`buyingstore_id`,`index`,`item_id`,`amount`,`price`) VALUES( %d, %d, %hu, %d, %d );", buyingstore_items_db, sd->buyer_id, i, sd->buyingstore.items[i].nameid, sd->buyingstore.items[i].amount, sd->buyingstore.items[i].price ) != SQL_SUCCESS ){ Sql_ShowDebug(mmysql_handle); } } clif_buyingstore_myitemlist(sd); clif_buyingstore_entry(sd); idb_put(buyingstore_db, sd->status.char_id, sd); return 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_tradecancel (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, 0); 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_displaymessage (sd->fd, msg_txt (260)); clif_tradeitemok (sd, index + 2, 1); return; } if (item->expire_time) { // Rental System clif_displaymessage (sd->fd, msg_txt (260)); clif_tradeitemok (sd, index + 2, 1); 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, 1); 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, 1); 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, 0); // Return the index as it was received clif_tradeadditem (sd, target_sd, index + 2, amount); }
///! TODO: Party invitation cross map-server through inter-server, so does with the reply. int party_invite(struct map_session_data *sd,struct map_session_data *tsd) { struct party_data *p; int i; nullpo_ret(sd); if( ( p = party_search(sd->status.party_id) ) == NULL ) return 0; // confirm if this player is a party leader ARR_FIND(0, MAX_PARTY, i, p->data[i].sd == sd); if( i == MAX_PARTY || !p->party.member[i].leader ) { clif_displaymessage(sd->fd, msg_txt(sd,282)); return 0; } if (tsd && battle_config.block_account_in_same_party) { ARR_FIND(0, MAX_PARTY, i, p->party.member[i].account_id == tsd->status.account_id); if (i < MAX_PARTY) { clif_party_invite_reply(sd, tsd->status.name, PARTY_REPLY_DUAL); return 0; } } // confirm if there is an open slot in the party ARR_FIND(0, MAX_PARTY, i, p->party.member[i].account_id == 0); if( i == MAX_PARTY ) { clif_party_invite_reply(sd, (tsd?tsd->status.name:""), PARTY_REPLY_FULL); return 0; } // confirm whether the account has the ability to invite before checking the player if( !pc_has_permission(sd, PC_PERM_PARTY) || (tsd && !pc_has_permission(tsd, PC_PERM_PARTY)) ) { clif_displaymessage(sd->fd, msg_txt(sd,81)); // "Your GM level doesn't authorize you to preform this action on the specified player." return 0; } if( tsd == NULL) { clif_party_invite_reply(sd, "", PARTY_REPLY_OFFLINE); return 0; } if(!battle_config.invite_request_check) { if (tsd->guild_invite>0 || tsd->trade_partner || tsd->adopt_invite) { clif_party_invite_reply(sd,tsd->status.name,PARTY_REPLY_JOIN_OTHER_PARTY); return 0; } } if (!tsd->fd) { //You can't invite someone who has already disconnected. clif_party_invite_reply(sd,tsd->status.name,PARTY_REPLY_REJECTED); return 0; } if( tsd->status.party_id > 0 || tsd->party_invite > 0 ) {// already associated with a party clif_party_invite_reply(sd,tsd->status.name,PARTY_REPLY_JOIN_OTHER_PARTY); return 0; } tsd->party_invite=sd->status.party_id; tsd->party_invite_account=sd->status.account_id; clif_party_invite(sd,tsd); return 1; }