/* * Display opponents name of sd */ static int duel_showinfo_sub(struct map_session_data* sd, va_list va) { struct map_session_data *ssd = va_arg(va, struct map_session_data*); int *p = va_arg(va, int*); char output[256]; if (sd->duel_group != ssd->duel_group) return 0; sprintf(output, " %d. %s", ++(*p), sd->status.name); clif_messagecolor(&ssd->bl, color_table[COLOR_LIGHT_GREEN], output, false, SELF); return 1; }
/** * Make a player join the map channel * - Create the map_channel if it does not exist * @param sd: Player data * @return * -1: Invalid player * -2: Player already in channel (channel_join) * -3: Player banned (channel_join) */ int channel_mjoin(struct map_session_data *sd) { char mout[60]; if(!sd) return -1; if( !map[sd->bl.m].channel ) { map[sd->bl.m].channel = channel_create_simple(NULL,NULL,CHAN_TYPE_MAP,sd->bl.m); } if( map[sd->bl.m].channel->opt & CHAN_OPT_ANNOUNCE_SELF ) { sprintf(mout, msg_txt(sd,1435),map[sd->bl.m].channel->name,map[sd->bl.m].name); // You're now in the '#%s' channel for '%s'. clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], mout, false, SELF); } return channel_join(map[sd->bl.m].channel,sd); }
/* * Display duel infos, * Number of duely... */ void duel_showinfo(const unsigned int did, struct map_session_data* sd) { int p=0; char output[256]; if(duel_list[did].max_players_limit > 0) sprintf(output, msg_txt(sd,370), //" -- Duels: %d/%d, Members: %d/%d, Max players: %d --" did, duel_count, duel_list[did].members_count, duel_list[did].members_count + duel_list[did].invites_count, duel_list[did].max_players_limit); else sprintf(output, msg_txt(sd,371), //" -- Duels: %d/%d, Members: %d/%d --" did, duel_count, duel_list[did].members_count, duel_list[did].members_count + duel_list[did].invites_count); clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], output, false, SELF); map_foreachpc(duel_showinfo_sub, sd, &p); }
/** * Format message from player to send to the channel * - Also truncate extra characters if message is too long * @param channel: Channel data * @param sd: Player data * @param msg: Message to send * @return * 0: Success * -1: Invalid player, channel, or message * -2: Delay message from last message */ int channel_send(struct Channel *channel, struct map_session_data *sd, const char *msg) { int idx = 0; if(!channel || !sd || !msg || (idx = channel_pc_haschan(sd, channel)) < 0) return -1; if(!pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) && channel->msg_delay != 0 && DIFF_TICK(sd->channel_tick[idx] + channel->msg_delay, gettick()) > 0) { clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,1455),false,SELF); //You're talking too fast! return -2; } else { char output[CHAT_SIZE_MAX]; unsigned long color = channel->color; if((channel->opt&CHAN_OPT_COLOR_OVERRIDE) && sd->fontcolor && sd->fontcolor < channel_config.colors_count && channel_config.colors[sd->fontcolor]) color = channel_config.colors[sd->fontcolor]; safesnprintf(output, CHAT_SIZE_MAX, "%s %s : %s", channel->alias, sd->status.name, msg); clif_channel_msg(channel,output,color); sd->channel_tick[idx] = gettick(); } return 0; }
/* * Create a new duel for sd */ int duel_create(struct map_session_data* sd, const unsigned int maxpl) { int i=1; char output[256]; while(duel_list[i].members_count > 0 && i < MAX_DUEL) i++; if(i == MAX_DUEL) return 0; duel_count++; duel_set(i, sd); duel_list[i].members_count++; duel_list[i].invites_count = 0; duel_list[i].max_players_limit = maxpl; strcpy(output, msg_txt(sd,372)); // " -- Duel has been created (@invite/@leave) --" clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], output, false, SELF); clif_map_property(&sd->bl, MAPPROPERTY_FREEPVPZONE, SELF); //clif_misceffect2(&sd->bl, 159); return i; }
/** * Display some information to users in channel * @param sd: Player data * @param options: * colors: Display available colors for channel system * mine: List of players in channel and number of users * void: List of public channel and map and guild and number of users * @return 0 on success or -1 on failure */ int channel_display_list(struct map_session_data *sd, const char *options){ if(!sd || !options) return -1; //display availaible colors if( options[0] != '\0' && strcmpi(options,"colors") == 0 ) { char msg[40]; unsigned char k; clif_displaymessage(sd->fd, msg_txt(sd,1444)); // ---- Available Colors ---- for( k = 0; k < channel_config.colors_count; k++ ) { if (channel_config.colors[k]) { sprintf(msg, msg_txt(sd,1445),channel_config.colors_name[k]);// - '%s' clif_messagecolor(&sd->bl,channel_config.colors[k],msg,false,SELF); } } } else if( options[0] != '\0' && strcmpi(options,"mine") == 0 ) { //display chan I'm into clif_displaymessage(sd->fd, msg_txt(sd,1475)); // ---- My Channels ---- if(!sd->channel_count) clif_displaymessage(sd->fd, msg_txt(sd,1476)); // You have not joined any channels. else { unsigned char k; for(k = 0; k < sd->channel_count; k++) { char output[CHAT_SIZE_MAX]; struct Channel *channel; if (!(channel = sd->channels[k])) continue; sprintf(output, msg_txt(sd,1409), channel->name, db_size(channel->users));// - #%s (%d users) clif_displaymessage(sd->fd, output); } } } else { //display public chanels DBIterator *iter; bool has_perm = pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ? true : false; struct Channel *channel; char output[CHAT_SIZE_MAX]; struct map_data *mapdata = map_getmapdata(sd->bl.m); clif_displaymessage(sd->fd, msg_txt(sd,1410)); // ---- Public Channels ---- if( channel_config.map_tmpl.name[0] && mapdata->channel ) { sprintf(output, msg_txt(sd,1409), mapdata->channel->name, db_size(mapdata->channel->users));// - #%s (%d users) clif_displaymessage(sd->fd, output); } if( channel_config.ally_tmpl.name[0] && sd->status.guild_id ) { struct guild *g = sd->guild; if (g && g->channel) { sprintf(output, msg_txt(sd,1409), g->channel->name, db_size(((struct Channel *)g->channel)->users));// - #%s (%d users) clif_displaymessage(sd->fd, output); } } iter = db_iterator(channel_db); for(channel = (struct Channel *)dbi_first(iter); dbi_exists(iter); channel = (struct Channel *)dbi_next(iter)) { if (!has_perm && !channel_pccheckgroup(channel, sd->group_id)) continue; if( has_perm || channel->type == CHAN_TYPE_PUBLIC ) { sprintf(output, msg_txt(sd,1409), channel->name, db_size(channel->users));// - #%s (%d users) clif_displaymessage(sd->fd, output); } } dbi_destroy(iter); } return 0; }
/** * 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->cart.u.items_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->cart.u.items_cart[idx].amount ) vending[j].amount = vsd->cart.u.items_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->cart.u.items_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); achievement_update_objective(sd, AG_SPEND_ZENY, 1, (int)z); 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; z = 0.; // zeny counter // vending item pc_additem(sd, &vsd->cart.u.items_cart[idx], amount, LOG_TYPE_VENDING); vsd->vending[vend_list[i]].amount -= amount; z += ((double)vsd->vending[i].value * (double)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_table, vsd->vending[vend_list[i]].amount, vsd->vender_id, vsd->cart.u.items_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_table, vsd->vender_id, vsd->cart.u.items_cart[idx].id ) != SQL_SUCCESS ) { Sql_ShowDebug( mmysql_handle ); } } pc_cart_delitem(vsd, idx, amount, 0, LOG_TYPE_VENDING); if( battle_config.vending_tax ) z -= z * (battle_config.vending_tax/10000.); clif_vendingreport(vsd, idx, amount, sd->status.char_id, (int)z); //print buyer's name if( battle_config.buyer_name ) { char temp[256]; sprintf(temp, msg_txt(sd,265), sd->status.name); clif_messagecolor(&vsd->bl, color_table[COLOR_LIGHT_GREEN], temp, false, SELF); } } // 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&CHARSAVE_VENDING ) { chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART); chrif_save(vsd, CSAVE_INVENTORY|CSAVE_CART); } //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? } } }
/** * Homunculus leveled up * @param hd */ int hom_levelup(struct homun_data *hd) { struct s_homunculus *hom; struct h_stats *min = NULL, *max = NULL; int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ; int growth_max_hp, growth_max_sp ; int m_class; if ((m_class = hom_class2mapid(hd->homunculus.class_)) == -1) { ShowError("hom_levelup: Invalid class %d. \n", hd->homunculus.class_); return 0; } /// When homunculus is homunculus S, we check to see if we need to apply previous class stats if(m_class&HOM_S && hd->homunculus.level < battle_config.hom_S_growth_level) { int i; if (!hd->homunculus.prev_class) { /// We also need to be sure that the previous class exists, otherwise give it something to work with hd->homunculus.prev_class = 6001; } // Give the homunculus the level up stats database it needs i = hom_search(hd->homunculus.prev_class,HOMUNCULUS_CLASS); if (i < 0) // Nothing should go wrong here, but check anyways return 0; max = &homunculus_db[i].gmax; min = &homunculus_db[i].gmin; } if (((m_class&HOM_REG) && hd->homunculus.level >= battle_config.hom_max_level) || ((m_class&HOM_S) && hd->homunculus.level >= battle_config.hom_S_max_level) || !hd->exp_next || hd->homunculus.exp < hd->exp_next) return 0; hom = &hd->homunculus; hom->level++ ; if (!(hom->level % 3)) hom->skillpts++ ; //1 skillpoint each 3 base level hom->exp -= hd->exp_next ; hd->exp_next = hexptbl[hom->level - 1] ; if (!max) { max = &hd->homunculusDB->gmax; min = &hd->homunculusDB->gmin; } growth_max_hp = rnd_value(min->HP, max->HP); growth_max_sp = rnd_value(min->SP, max->SP); growth_str = rnd_value(min->str, max->str); growth_agi = rnd_value(min->agi, max->agi); growth_vit = rnd_value(min->vit, max->vit); growth_dex = rnd_value(min->dex, max->dex); growth_int = rnd_value(min->int_,max->int_); growth_luk = rnd_value(min->luk, max->luk); //Aegis discards the decimals in the stat growth values! growth_str-=growth_str%10; growth_agi-=growth_agi%10; growth_vit-=growth_vit%10; growth_dex-=growth_dex%10; growth_int-=growth_int%10; growth_luk-=growth_luk%10; hom->max_hp += growth_max_hp; hom->max_sp += growth_max_sp; hom->str += growth_str; hom->agi += growth_agi; hom->vit += growth_vit; hom->dex += growth_dex; hom->int_+= growth_int; hom->luk += growth_luk; APPLY_HOMUN_LEVEL_STATWEIGHT(); // Needed to update skill list for mutated homunculus so unlocked skills will appear when the needed level is reached. status_calc_homunculus(hd,SCO_NONE); clif_hominfo(hd->master,hd,0); clif_homskillinfoblock(hd->master); if ( hd->master && battle_config.homunculus_show_growth ) { char output[256] ; sprintf(output, "Growth: hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ", growth_max_hp, growth_max_sp, growth_str/10.0, growth_agi/10.0, growth_vit/10.0, growth_int/10.0, growth_dex/10.0, growth_luk/10.0); clif_messagecolor(&hd->master->bl, color_table[COLOR_LIGHT_GREEN], output, false, SELF); } return 1; }