/*========================================== * change a chatroom's owner *------------------------------------------*/ int chat_changechatowner(struct map_session_data* sd, const char* nextownername) { struct chat_data* cd; struct map_session_data* tmpsd; int i; nullpo_retr(1, sd); cd = (struct chat_data*)map_id2bl(sd->chatID); if( cd == NULL || (struct block_list*) sd != cd->owner ) return 1; ARR_FIND( 1, cd->users, i, strncmp(cd->usersd[i]->status.name, nextownername, NAME_LENGTH) == 0 ); if( i == cd->users ) return -1; // name not found // erase temporarily clif_clearchat(cd,0); // set new owner cd->owner = (struct block_list*) cd->usersd[i]; clif_changechatowner(cd,cd->usersd[i]); // swap the old and new owners' positions tmpsd = cd->usersd[i]; cd->usersd[i] = cd->usersd[0]; cd->usersd[0] = tmpsd; // set the new chatroom position map_delblock( &cd->bl ); cd->bl.x = cd->owner->x; cd->bl.y = cd->owner->y; map_addblock( &cd->bl ); // and display again clif_dispchat(cd,0); return 0; }
/*========================================== * Change a chatroom's owner * Return * 0: User not found/Missing data * 1: Success *------------------------------------------*/ bool chat_changechatowner(struct map_session_data* sd, const char* nextownername) { struct chat_data* cd; struct map_session_data* tmpsd; int i; nullpo_ret(sd); nullpo_ret(nextownername); cd = map->id2cd(sd->chat_id); if (cd == NULL || &sd->bl != cd->owner) return false; ARR_FIND( 1, cd->users, i, strncmp(cd->usersd[i]->status.name, nextownername, NAME_LENGTH) == 0 ); if( i == cd->users ) return false; // name not found // erase temporarily clif->clearchat(cd,0); // set new owner cd->owner = &cd->usersd[i]->bl; clif->changechatowner(cd,cd->usersd[i]); // swap the old and new owners' positions tmpsd = cd->usersd[i]; cd->usersd[i] = cd->usersd[0]; cd->usersd[0] = tmpsd; // set the new chatroom position map->delblock( &cd->bl ); cd->bl.x = cd->owner->x; cd->bl.y = cd->owner->y; map->addblock( &cd->bl ); // and display again clif->dispchat(cd,0); return true; }
/*=============================================================== * Action that elemental perform after changing mode. * Activates one of the skills of the new mode. *-------------------------------------------------------------*/ int elemental_change_mode_ack(struct elemental_data *ed, int mode) { struct block_list *bl = &ed->master->bl; uint16 skill_id, skill_lv; int i; nullpo_ret(ed); if( !bl ) return 0; // Select a skill. ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&mode)); if( i == MAX_ELESKILLTREE ) return 0; skill_id = ed->db->skill[i].id; skill_lv = ed->db->skill[i].lv; if( elemental_skillnotok(skill_id, ed) ) return 0; if( ed->ud.skilltimer != INVALID_TIMER ) return 0; else if( DIFF_TICK(gettick(), ed->ud.canact_tick) < 0 ) return 0; ed->target_id = bl->id; // Set new target ed->last_thinktime = gettick(); if( skill_get_inf(skill_id) & INF_GROUND_SKILL ) unit_skilluse_pos(&ed->bl, bl->x, bl->y, skill_id, skill_lv); else unit_skilluse_id(&ed->bl,bl->id,skill_id,skill_lv); ed->target_id = 0; // Reset target after casting the skill to avoid continious attack. return 1; }
/** * Adds a configuration listener for a plugin. * * @param pluginID The plugin identifier. * @param type The configuration type to listen for. * @param name The configuration entry name. * @param func The callback function. * @retval true if the listener was added successfully. * @retval false in case of error. */ bool hplugins_addconf(unsigned int pluginID, enum HPluginConfType type, char *name, void (*parse_func) (const char *key, const char *val), int (*return_func) (const char *key)) { struct HPConfListenStorage *conf; int i; if (parse_func == NULL) { ShowError("HPM->addConf:%s: missing setter function for config '%s'\n",HPM->pid2name(pluginID),name); return false; } if (type == HPCT_BATTLE && return_func == NULL) { ShowError("HPM->addConf:%s: missing getter function for config '%s'\n",HPM->pid2name(pluginID),name); return false; } if (type >= HPCT_MAX) { ShowError("HPM->addConf:%s: unknown point '%u' specified for config '%s'\n",HPM->pid2name(pluginID),type,name); return false; } ARR_FIND(0, VECTOR_LENGTH(HPM->config_listeners[type]), i, strcmpi(name, VECTOR_INDEX(HPM->config_listeners[type], i).key) == 0); if (i != VECTOR_LENGTH(HPM->config_listeners[type])) { ShowError("HPM->addConf:%s: duplicate '%s', already in use by '%s'!", HPM->pid2name(pluginID), name, HPM->pid2name(VECTOR_INDEX(HPM->config_listeners[type], i).pluginID)); return false; } VECTOR_ENSURE(HPM->config_listeners[type], 1, 1); VECTOR_PUSHZEROED(HPM->config_listeners[type]); conf = &VECTOR_LAST(HPM->config_listeners[type]); conf->pluginID = pluginID; safestrncpy(conf->key, name, HPM_ADDCONF_LENGTH); conf->parse_func = parse_func; conf->return_func = return_func; return true; }
/** * Make player leave the channel and cleanup association * - If no one remains in the chat, delete it * @param channel: Channel data * @param sd: Player data * @param flag: Called from deletion process, do not recall delete * @return * 0: Success * -1: Invalid player or channel */ int channel_clean(struct Channel *channel, struct map_session_data *sd, int flag) { unsigned char i; if(!channel || !sd) return -1; if( channel == sd->gcbind ) sd->gcbind = NULL; ARR_FIND(0, sd->channel_count, i, sd->channels[i] == channel); if( i < sd->channel_count ) { unsigned char cursor = i; sd->channels[i] = NULL; sd->channel_tick[i] = 0; for(; i < sd->channel_count; i++ ) { //slice move list down if( sd->channels[i] == NULL ) continue; if(i != cursor) { sd->channels[cursor] = sd->channels[i]; sd->channel_tick[cursor] = sd->channel_tick[i]; } cursor++; } if ( !(sd->channel_count = cursor) ) { //if in no more chan delete db aFree(sd->channels); aFree(sd->channel_tick); sd->channels = NULL; sd->channel_tick = NULL; } } idb_remove(channel->users,sd->status.char_id); //remove user for channel user list //auto delete when no more user in if( !db_size(channel->users) && !(flag&1) ) channel_delete(channel,false); return 0; }
int quest_update_status(TBL_PC * sd, int quest_id, quest_state status) { int i; //Only status of active and inactive quests can be updated. Completed quests can't (for now). [Inkfish] ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == quest_id); if(i == sd->avail_quests) { ShowError("quest_update_status: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id); return -1; } sd->quest_log[i].state = status; sd->save_quest = true; if( status < Q_COMPLETE ) { clif_quest_update_status(sd, quest_id, (bool)status); return 0; } achievement_validate_quest(sd,quest_id); if( i != (--sd->avail_quests) ) { struct quest tmp_quest; memcpy(&tmp_quest, &sd->quest_log[i],sizeof(struct quest)); memcpy(&sd->quest_log[i], &sd->quest_log[sd->avail_quests],sizeof(struct quest)); memcpy(&sd->quest_log[sd->avail_quests], &tmp_quest,sizeof(struct quest)); } clif_quest_delete(sd, quest_id); if( save_settings&64 ) chrif_save(sd,0); return 0; }
int party_removemember2(struct map_session_data *sd,uint32 char_id,int party_id) { if( sd ) { if( !sd->status.party_id ) return -3; party_trade_bound_cancel(sd); intif_party_leave(sd->status.party_id,sd->status.account_id,sd->status.char_id); return 1; } else { int i; struct party_data *p; if( !(p = party_search(party_id)) ) return -2; ARR_FIND(0,MAX_PARTY,i,p->party.member[i].char_id == char_id ); if( i >= MAX_PARTY ) return -1; intif_party_leave(party_id,p->party.member[i].account_id,char_id); return 1; } }
/*========================================== * Kick an user from a chatroom * Return: * 0: User cannot be kicked (is gm)/Missing data * 1: Success *------------------------------------------*/ bool chat_kickchat(struct map_session_data* sd, const char* kickusername) { struct chat_data* cd; int i; nullpo_ret(sd); cd = (struct chat_data *)map->id2bl(sd->chatID); if( cd==NULL || (struct block_list *)sd != cd->owner ) return false; ARR_FIND( 0, cd->users, i, strncmp(cd->usersd[i]->status.name, kickusername, NAME_LENGTH) == 0 ); if( i == cd->users ) // User not found return false; if (pc_has_permission(cd->usersd[i], PC_PERM_NO_CHAT_KICK)) return false; //gm kick protection [Valaris] idb_iput(cd->kick_list,cd->usersd[i]->status.char_id,1); chat->leave(cd->usersd[i], true); return true; }
// Add a player to party request int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member) { struct party_data *p; int i; nullpo_ret(member); p = inter_party->fromsql(party_id); if( p == NULL || p->size == MAX_PARTY ) { mapif->party_memberadded(fd, party_id, member->account_id, member->char_id, 1); return 0; } ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == 0 ); if( i == MAX_PARTY ) {// Party full mapif->party_memberadded(fd, party_id, member->account_id, member->char_id, 1); return 0; } memcpy(&p->party.member[i], member, sizeof(struct party_member)); p->party.member[i].leader = 0; if (p->party.member[i].online) p->party.count++; p->size++; if (p->size == 2 || p->size == 3) // Check family state. And also accept either of their Parents. [RoM] inter_party->calc_state(p); else //Check even share range. if (member->lv < p->min_lv || member->lv > p->max_lv || p->family) { if (p->family) p->family = 0; //Family state broken. inter_party->check_lv(p); } mapif->party_info(-1, &p->party, 0); mapif->party_memberadded(fd, party_id, member->account_id, member->char_id, 0); inter_party->tosql(&p->party, PS_ADDMEMBER, i); return 0; }
/** * Updates a quest's state. * Only status of active and inactive quests can be updated. Completed quests can't (for now). * @param sd : Character's data * @param quest_id : Quest ID to update * @param qs : New quest state * @return 0 in case of success, nonzero otherwise * @author [Inkfish] */ int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state status) { int i; ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == quest_id); if( i == sd->avail_quests ) { ShowError("quest_update_status: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id); return -1; } sd->quest_log[i].state = status; sd->save_quest = true; if( status < Q_COMPLETE ) { clif_quest_update_status(sd, quest_id, status == Q_ACTIVE ? true : false); return 0; } achievement_validate_quest(sd,quest_id); // The quest is complete, so it needs to be moved to the completed quests block at the end of the array. if( i < (--sd->avail_quests) ) { struct quest tmp_quest; memcpy(&tmp_quest, &sd->quest_log[i], sizeof(struct quest)); memcpy(&sd->quest_log[i], &sd->quest_log[sd->avail_quests], sizeof(struct quest)); memcpy(&sd->quest_log[sd->avail_quests], &tmp_quest, sizeof(struct quest)); } clif_quest_delete(sd, quest_id); if( save_settings&64 ) chrif_save(sd,0); return 0; }
// ギルドメンバ追加要求 int mapif_parse_GuildAddMember(int fd, int guild_id, struct guild_member *m) { struct guild *g; int i; g = (struct guild*)idb_get(guild_db, guild_id); if (g == NULL) { mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 1); return 0; } ARR_FIND( 0, g->max_member, i, g->member[i].account_id == 0 ); if( i < g->max_member ) { memcpy(&g->member[i], m, sizeof(struct guild_member)); mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 0); guild_calcinfo(g); mapif_guild_info(-1, g); } else mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 1); return 0; }
/*========================================== * kick an user from a chatroom *------------------------------------------*/ int chat_kickchat(struct map_session_data *sd, const char *kickusername) { struct chat_data *cd; int i; nullpo_retr(1, sd); cd = (struct chat_data *)map_id2bl(sd->chatID); if(cd==NULL || (struct block_list *)sd != cd->owner) return -1; ARR_FIND(0, cd->users, i, strncmp(cd->usersd[i]->status.name, kickusername, NAME_LENGTH) == 0); if(i == cd->users) return -1; if(pc_has_permission(cd->usersd[i], PC_PERM_NO_CHAT_KICK)) return 0; //gm kick protection [Valaris] idb_put(cd->kick_list,cd->usersd[i]->status.char_id,(void *)1); chat_leavechat(cd->usersd[i],1); return 0; }
/** * Read homunculus skill db */ static bool read_homunculus_skilldb_sub(char* split[], int columns, int current) {// <hom class>,<skill id>,<max level>[,<job level>],<req id1>,<req lv1>,<req id2>,<req lv2>,<req id3>,<req lv3>,<req id4>,<req lv4>,<req id5>,<req lv5>,<intimacy lv req> int skill_id, class_idx; int i, j; int minJobLevelPresent = 0; if (columns == 14) minJobLevelPresent = 1; // MinJobLvl has been added // check for bounds [celest] if ((class_idx = hom_class2index(atoi(split[0]))) == -1) { ShowWarning("read_homunculus_skilldb: Invalud homunculus class %d.\n", atoi(split[0])); return false; } skill_id = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex] // Search an empty line or a line with the same skill_id (stored in j) ARR_FIND( 0, MAX_SKILL_TREE, j, !hskill_tree[class_idx][j].id || hskill_tree[class_idx][j].id == skill_id ); if (j == MAX_SKILL_TREE) { ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", skill_id, atoi(split[0])); return false; } hskill_tree[class_idx][j].id = skill_id; hskill_tree[class_idx][j].max = atoi(split[2]); if (minJobLevelPresent) hskill_tree[class_idx][j].joblv = atoi(split[3]); for (i = 0; i < MAX_HOM_SKILL_REQUIRE; i++) { hskill_tree[class_idx][j].need[i].id = atoi(split[3+i*2+minJobLevelPresent]); hskill_tree[class_idx][j].need[i].lv = atoi(split[3+i*2+minJobLevelPresent+1]); } hskill_tree[class_idx][j].intimacylv = atoi(split[13+minJobLevelPresent]); return true; }
/// Player joins team bool bg_team_join(int bg_id, struct map_session_data *sd) { int i; struct battleground_data *bgd = bg->team_search(bg_id); if( bgd == NULL || sd == NULL || sd->bg_id ) return false; ARR_FIND(0, MAX_BG_MEMBERS, i, bgd->members[i].sd == NULL); if( i == MAX_BG_MEMBERS ) return false; // No free slots sd->bg_id = bg_id; bgd->members[i].sd = sd; bgd->members[i].x = sd->bl.x; bgd->members[i].y = sd->bl.y; /* populate 'where i came from' */ if(map->list[sd->bl.m].flag.nosave || map->list[sd->bl.m].instance_id >= 0) { struct map_data *m=&map->list[sd->bl.m]; if(m->save.map) memcpy(&bgd->members[i].source,&m->save,sizeof(struct point)); else memcpy(&bgd->members[i].source,&sd->status.save_point,sizeof(struct point)); } else memcpy(&bgd->members[i].source,&sd->status.last_point,sizeof(struct point)); bgd->count++; guild->send_dot_remove(sd); for( i = 0; i < MAX_BG_MEMBERS; i++ ) { struct map_session_data *pl_sd = bgd->members[i].sd; if (pl_sd != NULL && pl_sd != sd) clif->hpmeter_single(sd->fd, pl_sd->bl.id, pl_sd->battle_status.hp, pl_sd->battle_status.max_hp); } clif->bg_hp(sd); clif->bg_xy(sd); return true; }
/* * Reads one line from database and assigns it to RAM. * return * 0 = failure * 1 = success */ static int cashshop_parse_dbrow( char** str, const char* source, int line ){ unsigned short nameid = atoi( str[1] ); if( itemdb_exists( nameid ) ){ uint16 tab = atoi( str[0] ); uint32 price = atoi( str[2] ); struct cash_item_data* cid; int j; if( tab > CASHSHOP_TAB_SEARCH ){ ShowWarning( "cashshop_parse_dbrow: Invalid tab %d in line %d of \"%s\", skipping...\n", tab, line, source ); return 0; }else if( price < 1 ){ ShowWarning( "cashshop_parse_dbrow: Invalid price %d in line %d of \"%s\", skipping...\n", price, line, source ); return 0; } ARR_FIND( 0, cash_shop_items[tab].count, j, nameid == cash_shop_items[tab].item[j]->nameid ); if( j == cash_shop_items[tab].count ){ RECREATE( cash_shop_items[tab].item, struct cash_item_data *, ++cash_shop_items[tab].count ); CREATE( cash_shop_items[tab].item[ cash_shop_items[tab].count - 1], struct cash_item_data, 1 ); cid = cash_shop_items[tab].item[ cash_shop_items[tab].count - 1]; }else{
/** * Updates a quest's state. * * Only status of active and inactive quests can be updated. Completed quests can't (for now). [Inkfish] * * @param sd Character's data * @param quest_id Quest ID to update * @param qs New quest state * @return 0 in case of success, nonzero otherwise */ static int quest_update_status(struct map_session_data *sd, int quest_id, enum quest_state qs) { int i; nullpo_retr(-1, sd); ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == quest_id); if( i == sd->avail_quests ) { ShowError("quest_update_status: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id); return -1; } sd->quest_log[i].state = qs; sd->save_quest = true; if( qs < Q_COMPLETE ) { clif->quest_update_status(sd, quest_id, qs == Q_ACTIVE ? true : false); return 0; } // The quest is complete, so it needs to be moved to the completed quests block at the end of the array. if( i < (--sd->avail_quests) ) { struct quest tmp_quest; memcpy(&tmp_quest, &sd->quest_log[i],sizeof(struct quest)); memcpy(&sd->quest_log[i], &sd->quest_log[sd->avail_quests],sizeof(struct quest)); memcpy(&sd->quest_log[sd->avail_quests], &tmp_quest,sizeof(struct quest)); } clif->quest_delete(sd, quest_id); quest->questinfo_refresh(sd); if( map->save_settings&64 ) chrif->save(sd,0); return 0; }
/** * Removes a quest from a player's list * * @param sd Player's data * @param quest_id ID of the quest to remove * @return 0 in case of success, nonzero otherwise */ static int quest_delete(struct map_session_data *sd, int quest_id) { int i; nullpo_retr(-1, sd); //Search for quest ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id); if(i == sd->num_quests) { ShowError("quest_delete: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id); return -1; } if( sd->quest_log[i].state != Q_COMPLETE ) sd->avail_quests--; if( i < --sd->num_quests ) { // Compact the array memmove(&sd->quest_log[i], &sd->quest_log[i+1], sizeof(struct quest)*(sd->num_quests-i)); } if( sd->num_quests == 0 ) { aFree(sd->quest_log); sd->quest_log = NULL; } else { RECREATE(sd->quest_log, struct quest, sd->num_quests); } sd->save_quest = true; clif->quest_delete(sd, quest_id); quest->questinfo_refresh(sd); if( map->save_settings&64 ) chrif->save(sd,0); 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); }
/*========================================== * 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? } } }
int merc_search_index(int class_) { int i; ARR_FIND(0, MAX_MERCENARY_CLASS, i, mercenary_db[i].class_ == class_); return (i == MAX_MERCENARY_CLASS)?-1:i; }
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; }
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(sd) ) {// custom: GM is not allowed to sell clif->message(sd->fd, msg_txt(246)); clif->buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( ( pl_sd = iMap->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->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, 0, 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( iMap->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 ) { iMap->quit(pl_sd); } }
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(sd) ) {// custom: GM is not allowed to buy (give zeny) sd->buyingstore.slots = 0; clif->message(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 ) {// custom: no vending maps clif->message(sd->fd, msg_txt(276)); // "You can't open a shop on this map" return; } if( iMap->getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) {// custom: no vending cells clif->message(sd->fd, msg_txt(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; 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; } 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); }
/** * Loads group configuration from config file into memory. * @private */ static void read_config(void) { config_setting_t *groups = NULL; const char *config_filename = "conf/groups.conf"; // FIXME hardcoded name int group_count = 0; if (conf_read_file(&pc_group_config, config_filename)) return; groups = config_lookup(&pc_group_config, "groups"); if (groups != NULL) { GroupSettings *group_settings = NULL; DBIterator *iter = NULL; int i, loop = 0; group_count = config_setting_length(groups); for (i = 0; i < group_count; ++i) { int id = 0, level = 0; const char *groupname = NULL; int log_commands = 0; config_setting_t *group = config_setting_get_elem(groups, i); if (!config_setting_lookup_int(group, "id", &id)) { ShowConfigWarning(group, "pc_groups:read_config: \"groups\" list member #%d has undefined id, removing...", i); config_setting_remove_elem(groups, i); --i; --group_count; continue; } if (id2group(id) != NULL) { ShowConfigWarning(group, "pc_groups:read_config: duplicate group id %d, removing...", i); config_setting_remove_elem(groups, i); --i; --group_count; continue; } config_setting_lookup_int(group, "level", &level); config_setting_lookup_bool(group, "log_commands", &log_commands); if (!config_setting_lookup_string(group, "name", &groupname)) { char temp[20]; config_setting_t *name = NULL; snprintf(temp, sizeof(temp), "Group %d", id); if ((name = config_setting_add(group, "name", CONFIG_TYPE_STRING)) == NULL || !config_setting_set_string(name, temp)) { ShowError("pc_groups:read_config: failed to set missing group name, id=%d, skipping... (%s:%d)\n", id, config_setting_source_file(group), config_setting_source_line(group)); continue; } config_setting_lookup_string(group, "name", &groupname); // Retrieve the pointer } if (name2group(groupname) != NULL) { ShowConfigWarning(group, "pc_groups:read_config: duplicate group name %s, removing...", groupname); config_setting_remove_elem(groups, i); --i; --group_count; continue; } CREATE(group_settings, GroupSettings, 1); group_settings->id = id; group_settings->level = level; group_settings->name = groupname; group_settings->log_commands = (bool)log_commands; group_settings->inherit = config_setting_get_member(group, "inherit"); group_settings->commands = config_setting_get_member(group, "commands"); group_settings->permissions = config_setting_get_member(group, "permissions"); group_settings->inheritance_done = false; group_settings->root = group; group_settings->group_pos = i; strdb_put(pc_groupname_db, groupname, group_settings); idb_put(pc_group_db, id, group_settings); } group_count = config_setting_length(groups); // Save number of groups // Check if all commands and permissions exist iter = db_iterator(pc_group_db); for (group_settings = dbi_first(iter); dbi_exists(iter); group_settings = dbi_next(iter)) { config_setting_t *commands = group_settings->commands, *permissions = group_settings->permissions; int count = 0, j; // Make sure there is "commands" group if (commands == NULL) commands = group_settings->commands = config_setting_add(group_settings->root, "commands", CONFIG_TYPE_GROUP); count = config_setting_length(commands); for (j = 0; j < count; ++j) { config_setting_t *command = config_setting_get_elem(commands, j); const char *name = config_setting_name(command); if (!atcommand_exists(name)) { ShowConfigWarning(command, "pc_groups:read_config: non-existent command name '%s', removing...", name); config_setting_remove(commands, name); --j; --count; } } // Make sure there is "permissions" group if (permissions == NULL) permissions = group_settings->permissions = config_setting_add(group_settings->root, "permissions", CONFIG_TYPE_GROUP); count = config_setting_length(permissions); for(j = 0; j < count; ++j) { config_setting_t *permission = config_setting_get_elem(permissions, j); const char *name = config_setting_name(permission); int p; ARR_FIND(0, ARRAYLENGTH(pc_g_permission_name), p, strcmp(pc_g_permission_name[p].name, name) == 0); if (p == ARRAYLENGTH(pc_g_permission_name)) { ShowConfigWarning(permission, "pc_groups:read_config: non-existent permission name '%s', removing...", name); config_setting_remove(permissions, name); --p; --count; } } } dbi_destroy(iter); // Apply inheritance i = 0; // counter for processed groups while (i < group_count) { iter = db_iterator(pc_group_db); for (group_settings = dbi_first(iter); dbi_exists(iter); group_settings = dbi_next(iter)) { config_setting_t *inherit = NULL, *commands = group_settings->commands, *permissions = group_settings->permissions; int j, inherit_count = 0, done = 0; if (group_settings->inheritance_done) // group already processed continue; if ((inherit = group_settings->inherit) == NULL || (inherit_count = config_setting_length(inherit)) <= 0) { // this group does not inherit from others ++i; group_settings->inheritance_done = true; continue; } for (j = 0; j < inherit_count; ++j) { GroupSettings *inherited_group = NULL; const char *groupname = config_setting_get_string_elem(inherit, j); if (groupname == NULL) { ShowConfigWarning(inherit, "pc_groups:read_config: \"inherit\" array member #%d is not a name, removing...", j); config_setting_remove_elem(inherit,j); continue; } if ((inherited_group = name2group(groupname)) == NULL) { ShowConfigWarning(inherit, "pc_groups:read_config: non-existent group name \"%s\", removing...", groupname); config_setting_remove_elem(inherit,j); continue; } if (!inherited_group->inheritance_done) continue; // we need to do that group first // Copy settings (commands/permissions) that are not defined yet if (inherited_group->commands != NULL) { int l = 0, commands_count = config_setting_length(inherited_group->commands); for (l = 0; l < commands_count; ++l) config_setting_copy(commands, config_setting_get_elem(inherited_group->commands, l)); } if (inherited_group->permissions != NULL) { int l = 0, permissions_count = config_setting_length(inherited_group->permissions); for (l = 0; l < permissions_count; ++l) config_setting_copy(permissions, config_setting_get_elem(inherited_group->permissions, l)); } ++done; // copied commands and permissions from one of inherited groups } if (done == inherit_count) { // copied commands from all of inherited groups ++i; group_settings->inheritance_done = true; // we're done with this group } } dbi_destroy(iter); if (++loop > group_count) { ShowWarning("pc_groups:read_config: Could not process inheritance rules, check your config '%s' for cycles...\n", config_filename); break; } } // while(i < group_count) // Pack permissions into GroupSettings.e_permissions for faster checking iter = db_iterator(pc_group_db); for (group_settings = dbi_first(iter); dbi_exists(iter); group_settings = dbi_next(iter)) { config_setting_t *permissions = group_settings->permissions; int c, count = config_setting_length(permissions); for (c = 0; c < count; ++c) { config_setting_t *perm = config_setting_get_elem(permissions, c); const char *name = config_setting_name(perm); int val = config_setting_get_bool(perm); int j; if (val == 0) // does not have this permission continue; ARR_FIND(0, ARRAYLENGTH(pc_g_permission_name), j, strcmp(pc_g_permission_name[j].name, name) == 0); group_settings->e_permissions |= pc_g_permission_name[j].permission; } } dbi_destroy(iter); } ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' groups in '"CL_WHITE"%s"CL_RESET"'.\n", group_count, config_filename); if( ( pc_group_max = group_count ) ) { DBIterator *iter = db_iterator(pc_group_db); GroupSettings *group_settings = NULL; int* group_ids = aMalloc( pc_group_max * sizeof(int) ); int i = 0; for (group_settings = dbi_first(iter); dbi_exists(iter); group_settings = dbi_next(iter)) { group_ids[i++] = group_settings->id; } atcommand_db_load_groups(group_ids); aFree(group_ids); dbi_destroy(iter); } }
void searchstore_click(struct map_session_data* sd, int account_id, int store_id, unsigned short nameid) { unsigned int i; struct map_session_data* pl_sd; searchstore_search_t store_search; if( !battle_config.feature_search_stores || !sd->searchstore.open || !sd->searchstore.count ) { return; } searchstore_clearremote(sd); ARR_FIND( 0, sd->searchstore.count, i, sd->searchstore.items[i].store_id == store_id && sd->searchstore.items[i].account_id == account_id && sd->searchstore.items[i].nameid == nameid ); if( i == sd->searchstore.count ) {// no such result, crafted ShowWarning("searchstore_click: Received request with item %hu of account %d, which is not part of current result set (account_id=%d, char_id=%d).\n", nameid, account_id, sd->bl.id, sd->status.char_id); clif_search_store_info_failed(sd, SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE); return; } if( ( pl_sd = map_id2sd(account_id) ) == NULL ) {// no longer online clif_search_store_info_failed(sd, SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE); return; } if( !searchstore_hasstore(pl_sd, sd->searchstore.type) || searchstore_getstoreid(pl_sd, sd->searchstore.type) != store_id ) {// no longer vending/buying or not same shop clif_search_store_info_failed(sd, SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE); return; } store_search = searchstore_getsearchfunc(sd->searchstore.type); if( !store_search(pl_sd, nameid) ) {// item no longer being sold/bought clif_search_store_info_failed(sd, SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE); return; } switch( sd->searchstore.effect ) { case EFFECTTYPE_NORMAL: // display coords if( sd->bl.m != pl_sd->bl.m ) {// not on same map, wipe previous marker clif_search_store_info_click_ack(sd, -1, -1); } else { clif_search_store_info_click_ack(sd, pl_sd->bl.x, pl_sd->bl.y); } break; case EFFECTTYPE_CASH: // open remotely // to bypass range checks sd->searchstore.remote_id = account_id; switch( sd->searchstore.type ) { case SEARCHTYPE_VENDING: vending_vendinglistreq(sd, account_id); break; case SEARCHTYPE_BUYING_STORE: buyingstore_open(sd, account_id); break; } break; default: // unknown ShowError("searchstore_click: Unknown search store effect %u (account_id=%d).\n", (unsigned int)sd->searchstore.effect, sd->bl.id); } }
/** * Open vending for Autotrader * @param sd Player as autotrader */ void vending_reopen( struct map_session_data* sd ){ nullpo_retv(sd); // Ready to open vending for this char if ( autotrader_count > 0 && autotraders){ uint16 i; uint8 *data, *p, fail = 0; uint16 j, count; ARR_FIND(0,autotrader_count,i,autotraders[i] && autotraders[i]->char_id == sd->status.char_id); if (i >= autotrader_count) { return; } // Init vending data for autotrader CREATE(data, uint8, autotraders[i]->count * 8); for (j = 0, p = data, count = autotraders[i]->count; j < autotraders[i]->count; j++) { struct s_autotrade_entry *entry = autotraders[i]->entries[j]; uint16 *index = (uint16*)(p + 0); uint16 *amount = (uint16*)(p + 2); uint32 *value = (uint32*)(p + 4); // Find item position in cart ARR_FIND(0, MAX_CART, entry->index, sd->status.cart[entry->index].id == entry->cartinventory_id); if (entry->index == MAX_CART) { count--; continue; } *index = entry->index + 2; *amount = itemdb_isstackable(sd->status.cart[entry->index].nameid) ? entry->amount : 1; *value = entry->price; p += 8; } // Set him into a hacked prevend state sd->state.prevend = 1; // Make sure abort all NPCs npc_event_dequeue(sd); pc_cleareventtimer(sd); // Open the vending again if( (fail = vending_openvending(sd, autotraders[i]->title, data, count)) == 0 ){ // Set him to autotrade if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1, `body_direction` = '%d', `head_direction` = '%d', `sit` = '%d' " "WHERE `id` = %d;", vendings_db, autotraders[i]->dir, autotraders[i]->head_dir, autotraders[i]->sit, sd->vender_id ) != SQL_SUCCESS ) { Sql_ShowDebug( mmysql_handle ); } // Make vendor look perfect pc_setdir(sd, autotraders[i]->dir, autotraders[i]->head_dir); clif_changed_dir(&sd->bl, AREA_WOS); if( autotraders[i]->sit ) { pc_setsit(sd); skill_sit(sd, 1); clif_sitting(&sd->bl); } // Immediate save chrif_save(sd, 3); ShowInfo("Loaded vending for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n", sd->status.name, count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y); }else{ // Failed to open the vending, set him offline ShowError("Failed (%d) to load autotrade vending data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", fail, sd->status.name, count ); map_quit( sd ); } aFree(data); //If the last autotrade is loaded, clear autotraders [Cydh] if (++autotrader_loaded_count >= autotrader_count) do_final_vending_autotrade(); } }
/** * Purchase item(s) from a shop * @param sd : buyer player session * @param aid : account id of vender * @param uid : shop unique id * @param data : items data who would like to purchase \n * data := {<index>.w <amount>.w }[count] * @param count : number of different items he's trying to buy */ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count) { int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING]; double z; struct s_vending vending[MAX_VENDING]; // against duplicate packets struct map_session_data* vsd = map_id2sd(aid); nullpo_retv(sd); if( vsd == NULL || !vsd->state.vending || vsd->bl.id == sd->bl.id ) return; // invalid shop if( vsd->vender_id != uid ) { // shop has changed clif_buyvending(sd, 0, 0, 6); // store information was incorrect return; } if( !searchstore_queryremote(sd, aid) && ( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) ) ) return; // shop too far away searchstore_clearremote(sd); if( count < 1 || count > MAX_VENDING || count > vsd->vend_num ) return; // invalid amount of purchased items blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory // duplicate item in vending to check hacker with multiple packets memcpy(&vending, &vsd->vending, sizeof(vsd->vending)); // copy vending list // some checks z = 0.; // zeny counter w = 0; // weight counter for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); short idx = *(uint16*)(data + 4*i + 2); idx -= 2; if( amount <= 0 ) return; // check of item index in the cart if( idx < 0 || idx >= MAX_CART ) return; ARR_FIND( 0, vsd->vend_num, j, vsd->vending[j].index == idx ); if( j == vsd->vend_num ) return; //picked non-existing item else vend_list[i] = j; z += ((double)vsd->vending[j].value * (double)amount); if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY ) { clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny return; } if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) { clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow return; } w += itemdb_weight(vsd->status.cart[idx].nameid) * amount; if( w + sd->weight > sd->max_weight ) { clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight return; } //Check to see if cart/vend info is in sync. if( vending[j].amount > vsd->status.cart[idx].amount ) vending[j].amount = vsd->status.cart[idx].amount; // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples). // here, we check cumulative amounts if( vending[j].amount < amount ) { // send more quantity is not a hack (an other player can have buy items just before) clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity return; } vending[j].amount -= amount; switch( pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount) ) { case CHKADDITEM_EXIST: break; //We'd add this item to the existing one (in buyers inventory) case CHKADDITEM_NEW: new_++; if (new_ > blank) return; //Buyer has no space in his inventory break; case CHKADDITEM_OVERAMOUNT: return; //too many items } } pc_payzeny(sd, (int)z, LOG_TYPE_VENDING, vsd); if( battle_config.vending_tax ) z -= z * (battle_config.vending_tax/10000.); pc_getzeny(vsd, (int)z, LOG_TYPE_VENDING, sd); for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); short idx = *(uint16*)(data + 4*i + 2); idx -= 2; // vending item pc_additem(sd, &vsd->status.cart[idx], amount, LOG_TYPE_VENDING); vsd->vending[vend_list[i]].amount -= amount; if( vsd->vending[vend_list[i]].amount ){ if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_db, vsd->vending[vend_list[i]].amount, vsd->vender_id, vsd->status.cart[idx].id ) != SQL_SUCCESS ){ Sql_ShowDebug( mmysql_handle ); } }else{ if( Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_db, vsd->vender_id, vsd->status.cart[idx].id ) != SQL_SUCCESS ){ Sql_ShowDebug( mmysql_handle ); } } pc_cart_delitem(vsd, idx, amount, 0, LOG_TYPE_VENDING); clif_vendingreport(vsd, idx, amount); //print buyer's name if( battle_config.buyer_name ) { char temp[256]; sprintf(temp, msg_txt(sd,265), sd->status.name); clif_disp_onlyself(vsd,temp,strlen(temp)); } } // compact the vending list for( i = 0, cursor = 0; i < vsd->vend_num; i++ ) { if( vsd->vending[i].amount == 0 ) continue; if( cursor != i ) { // speedup vsd->vending[cursor].index = vsd->vending[i].index; vsd->vending[cursor].amount = vsd->vending[i].amount; vsd->vending[cursor].value = vsd->vending[i].value; } cursor++; } vsd->vend_num = cursor; //Always save BOTH: customer (buyer) and vender if( save_settings&2 ) { chrif_save(sd,0); chrif_save(vsd,0); } //check for @AUTOTRADE users [durf] if( vsd->state.autotrade ) { //see if there is anything left in the shop ARR_FIND( 0, vsd->vend_num, i, vsd->vending[i].amount > 0 ); if( i == vsd->vend_num ) { //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex] vending_closevending(vsd); map_quit(vsd); //They have no reason to stay around anymore, do they? } } }
/*========================================== * Compra de item(s) de uma venda *------------------------------------------*/ 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]; // contra duplicagem de packtes struct map_session_data* vsd = map->id2sd(aid); nullpo_retv(sd); if( vsd == NULL || !vsd->state.vending || vsd->bl.id == sd->bl.id ) return; // venda invalida if( vsd->vender_id != uid ) { // venda mudou clif->buyvending(sd, 0, 0, 6); // informacao de loja estava incorreta return; } if( !searchstore->queryremote(sd, aid) && ( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) ) ) return; // venda muito distante searchstore->clearremote(sd); if( count < 1 || count > MAX_VENDING || count > vsd->vend_num ) return; // quantidade de itens comprados invalida blank = pc->inventoryblank(sd); //numero de celulas livres no invetorio do comprador // Duplica item na venda para checar hacker com packets multiplos memcpy(&vend, &vsd->vending, sizeof(vsd->vending)); // copia lista de venda // alguns checks z = 0.; // contador de zeny w = 0; // contador de weight 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 de index de item no carrinho 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; //colhido um item inexistente 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); // voce nao tem zeny suficiente return; } if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) { clif->buyvending(sd, idx, vsd->vending[j].amount, 4); // muito 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); // voce nao pode comprar, por causa do peso-limite return; } //Check para ver se a info do(a) carrinho/venda esta sincronizada. if( vend[j].amount > vsd->status.cart[idx].amount ) vend[j].amount = vsd->status.cart[idx].amount; // se tentarem adicionar packets (exemplo: pega 2x ou mais 2 maçãs se o mercador tem apenas 3 maçãs) // aqui, checamos quantidade acumulativa if( vend[j].amount < amount ) { // enviar mais quantidade não é um hack (um outro jogador pode ter comprado os itens um pouco antes) clif->buyvending(sd, idx, vsd->vending[j].amount, 4); // quantidade insuficiente return; } vend[j].amount -= amount; switch( pc->checkadditem(sd, vsd->status.cart[idx].nameid, amount) ) { case ADDITEM_EXIST: break; //Adicionariamos esse item para o Item existente (no inventorio do comprador) case ADDITEM_NEW: new_++; if (new_ > blank) return; //Comprador não tem espaço no seu inventorio break; case ADDITEM_OVERAMOUNT: return; //muitos itens } } 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; // vendendo 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); //imprimindo o nome do comprador if( battle_config.buyer_name ) { char temp[256]; sprintf(temp, msg_txt(265), sd->status.name); clif_disp_onlyself(vsd,temp,strlen(temp)); } } // compacta a lista de venda for( i = 0, cursor = 0; i < vsd->vend_num; i++ ) { if( vsd->vending[i].amount == 0 ) continue; if( cursor != i ) { // acelera 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; //Sempre salva AMBOS: comprador e freguês if( map->save_settings&2 ) { chrif->save(sd,0); chrif->save(vsd,0); } //check para usuários do @AUTOTRADE [durf] if( vsd->state.autotrade ) { //olha se tem alguma coisa mais na venda ARR_FIND( 0, vsd->vend_num, i, vsd->vending[i].amount > 0 ); if( i == vsd->vend_num ) { //Fecha a Venda (isso é automaticamente feito pelo client, temos que fazer isso manualmente para vededores de @autotrade) [Skotlex] vending->close(vsd); map->quit(vsd); //Eles nao tem motivo para ficarem por perto mais, tem? } else pc->autotrade_update(vsd,PAUC_REFRESH); } }
// ギルド同盟/敵対通知 int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,int flag,const char *name1,const char *name2) { struct guild *g[2]; int guild_id[2]; const char *guild_name[2]; struct map_session_data *sd[2]; int j,i; guild_id[0] = guild_id1; guild_id[1] = guild_id2; guild_name[0] = name1; guild_name[1] = name2; sd[0] = map_id2sd(account_id1); sd[1] = map_id2sd(account_id2); g[0]=guild_search(guild_id1); g[1]=guild_search(guild_id2); if(sd[0]!=NULL && (flag&0x0f)==0){ sd[0]->guild_alliance=0; sd[0]->guild_alliance_account=0; } if(flag&0x70){ // 失敗 for(i=0;i<2-(flag&1);i++) if( sd[i]!=NULL ) clif_guild_allianceack(sd[i],((flag>>4)==i+1)?3:4); return 0; } if(!(flag&0x08)){ // 関係追加 for(i=0;i<2-(flag&1);i++) { if(g[i]!=NULL) { ARR_FIND( 0, MAX_GUILDALLIANCE, j, g[i]->alliance[j].guild_id == 0 ); if( j < MAX_GUILDALLIANCE ) { g[i]->alliance[j].guild_id=guild_id[1-i]; memcpy(g[i]->alliance[j].name,guild_name[1-i],NAME_LENGTH); g[i]->alliance[j].opposition=flag&1; } } } }else{ // 関係解消 for(i=0;i<2-(flag&1);i++) { if(g[i]!=NULL) { ARR_FIND( 0, MAX_GUILDALLIANCE, j, g[i]->alliance[j].guild_id == guild_id[1-i] && g[i]->alliance[j].opposition == (flag&1) ); if( j < MAX_GUILDALLIANCE ) g[i]->alliance[j].guild_id = 0; } if( sd[i]!=NULL ) // 解消通知 clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1)); } } if((flag&0x0f)==0){ // 同盟通知 if( sd[1]!=NULL ) clif_guild_allianceack(sd[1],2); }else if((flag&0x0f)==1){ // 敵対通知 if( sd[0]!=NULL ) clif_guild_oppositionack(sd[0],0); } for(i=0;i<2-(flag&1);i++){ // 同盟/敵対リストの再送信 struct map_session_data *sd; if(g[i]!=NULL) for(j=0;j<g[i]->max_member;j++) if((sd=g[i]->member[j].sd)!=NULL) clif_guild_allianceinfo(sd); } return 0; }
/// Searches for all items in a vending, that match given ids, price and possible cards. /// @return Whether or not the search should be continued. bool vending_searchall(struct map_session_data* sd, const struct s_search_store_search* s) { int i, c, slot; unsigned int idx, cidx; struct item* it; if( !sd->state.vending ) {// not vending return true; } for( idx = 0; idx < s->item_count; idx++ ) { ARR_FIND( 0, sd->vend_num, i, sd->status.cart[sd->vending[i].index].nameid == (short)s->itemlist[idx] ); if( i == sd->vend_num ) {// not found continue; } it = &sd->status.cart[sd->vending[i].index]; if( s->min_price && s->min_price > sd->vending[i].value ) {// too low price continue; } if( s->max_price && s->max_price < sd->vending[i].value ) {// too high price continue; } if( s->card_count ) {// check cards if( itemdb_isspecial(it->card[0]) ) {// something, that is not a carded continue; } slot = itemdb_slot(it->nameid); for( c = 0; c < slot && it->card[c]; c ++ ) { ARR_FIND( 0, s->card_count, cidx, s->cardlist[cidx] == it->card[c] ); if( cidx != s->card_count ) {// found break; } } if( c == slot || !it->card[c] ) {// no card match continue; } } if( !searchstore_result(s->search_sd, sd->vender_id, sd->status.account_id, sd->message, it->nameid, sd->vending[i].amount, sd->vending[i].value, it->card, it->refine) ) {// result set full return false; } } return true; }