bool Client::UseDiscipline(uint8 disc_id) { // Dont let client waste a reuse timer if they can't use the disc if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad() || IsPet()) { return(false); } //Check the disc timer uint32 remain = p_timers.GetRemainingTime(pTimerDisciplineReuseStart); if(remain > 0 && !GetGM()) { char val1[20]= {0}; char val2[20]= {0}; Message_StringID(CC_User_Disciplines, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2)); return(false); } bool active = disc_ability_timer.Enabled(); if(active) { Message(CC_User_Disciplines, "You must wait before using this discipline."); //find correct message return(false); } //can we use the disc? the client checks this for us, but we should also confirm server side. uint8 level_to_use = DisciplineUseLevel(disc_id); if(level_to_use > GetLevel() || level_to_use == 0) { Message_StringID(CC_User_Disciplines, DISC_LEVEL_USE_ERROR); return(false); } // Disciplines with no ability timer (ashenhand, silentfist, thunderkick, and unholyaura) will remain on the player until they either // use the skill the disc affects successfully, camp/zone, or attempt to use another disc. If we're here, clear that disc so they can // cast a new one. if(GetActiveDisc() != 0) { Log.Out(Logs::General, Logs::Discs, "Clearing disc %d so that disc %d can be cast.", GetActiveDisc(), disc_id); FadeDisc(); } //cast the disc if(CastDiscipline(disc_id, level_to_use)) return(true); else return(false); }
void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { _log(CLIENT__EXP, "Attempting to Set Exp for %s (XP: %u, AAXP: %u, Rez: %s)", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false"); //max_AAXP = GetEXPForLevel(52) - GetEXPForLevel(51); //GetEXPForLevel() doesn't depend on class/race, just level, so it shouldn't change between Clients max_AAXP = RuleI(AA, ExpPerPoint); //this may be redundant since we're doing this in Client::FinishConnState2() if (max_AAXP == 0 || GetEXPForLevel(GetLevel()) == 0xFFFFFFFF) { Message(13, "Error in Client::SetEXP. EXP not set."); return; // Must be invalid class/race } if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) { if (isrezzexp) this->Message_StringID(MT_Experience, REZ_REGAIN); else{ if(this->IsGrouped()) this->Message_StringID(MT_Experience, GAIN_GROUPXP); else if(IsRaidGrouped()) Message_StringID(MT_Experience, GAIN_RAIDEXP); else this->Message_StringID(MT_Experience, GAIN_XP); } } else if((set_exp + set_aaxp) < (m_pp.exp+m_pp.expAA)){ //only loss message if you lose exp, no message if you gained/lost nothing. Message(15, "You have lost experience."); } //check_level represents the level we should be when we have //this ammount of exp (once these loops complete) uint16 check_level = GetLevel()+1; //see if we gained any levels while (set_exp >= GetEXPForLevel(check_level)) { check_level++; if (check_level > 127) { //hard level cap check_level = 127; break; } if(GetMercID()) UpdateMercLevel(); } //see if we lost any levels while (set_exp < GetEXPForLevel(check_level-1)) { check_level--; if (check_level < 2) { //hard level minimum check_level = 2; break; } if(GetMercID()) UpdateMercLevel(); } check_level--; //see if we gained any AAs if (set_aaxp >= max_AAXP) { /* Note: AA exp is stored differently than normal exp. Exp points are only stored in m_pp.expAA until you gain a full AA point, once you gain it, a point is added to m_pp.aapoints and the ammount needed to gain that point is subtracted from m_pp.expAA then, once they spend an AA point, it is subtracted from m_pp.aapoints. In theory it then goes into m_pp.aapoints_spent, but im not sure if we have that in the right spot. */ //record how many points we have uint32 last_unspentAA = m_pp.aapoints; //figure out how many AA points we get from the exp were setting m_pp.aapoints = set_aaxp / max_AAXP; _log(CLIENT__EXP, "Calculating additional AA Points from AAXP for %s: %u / %u = %.1f points", this->GetCleanName(), set_aaxp, max_AAXP, (float)set_aaxp / (float)max_AAXP); //get remainder exp points, set in PP below set_aaxp = set_aaxp - (max_AAXP * m_pp.aapoints); //add in how many points we had m_pp.aapoints += last_unspentAA; //set_aaxp = m_pp.expAA % max_AAXP; //figure out how many points were actually gained /*uint32 gained = m_pp.aapoints - last_unspentAA;*/ //unused //Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA); char val1[20]={0}; Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2. //Message(15, "You now have %d skill points available to spend.", m_pp.aapoints); } uint8 maxlevel = RuleI(Character, MaxExpLevel) + 1; if(maxlevel <= 1) maxlevel = RuleI(Character, MaxLevel) + 1; if(check_level > maxlevel) { check_level = maxlevel; //DCBOOKMARK if(RuleB(Character, KeepLevelOverMax)) { set_exp = GetEXPForLevel(GetLevel()+1); } else { set_exp = GetEXPForLevel(maxlevel); } } if(RuleB(Character, PerCharacterQglobalMaxLevel)){ uint32 MaxLevel = GetCharMaxLevelFromQGlobal(); if(MaxLevel){ if(GetLevel() >= MaxLevel){ uint32 expneeded = GetEXPForLevel(MaxLevel); if(set_exp > expneeded) { set_exp = expneeded; } } } } if ((GetLevel() != check_level) && !(check_level >= maxlevel)) { char val1[20]={0}; if (GetLevel() == check_level-1){ Message_StringID(MT_Experience, GAIN_LEVEL,ConvertArray(check_level,val1)); SendLevelAppearance(); //Message(15, "You have gained a level! Welcome to level %i!", check_level); } if (GetLevel() == check_level){ Message_StringID(MT_Experience, LOSE_LEVEL,ConvertArray(check_level,val1)); //Message(15, "You lost a level! You are now level %i!", check_level); } else Message(15, "Welcome to level %i!", check_level); SetLevel(check_level); } //If were at max level then stop gaining experience if we make it to the cap if(GetLevel() == maxlevel - 1){ uint32 expneeded = GetEXPForLevel(maxlevel); if(set_exp > expneeded) { set_exp = expneeded; } } //set the client's EXP and AAEXP m_pp.exp = set_exp; m_pp.expAA = set_aaxp; if (GetLevel() < 51) { m_epp.perAA = 0; // turn off aa exp if they drop below 51 } else SendAAStats(); //otherwise, send them an AA update //send the expdata in any case so the xp bar isnt stuck after leveling uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); uint32 tmpxp2 = GetEXPForLevel(GetLevel()); // Quag: crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; float tmpxp = (float) ( (float) set_exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); eu->exp = (uint32)(330.0f * tmpxp); FastQueuePacket(&outapp); } if (admin>=100 && GetGM()) { char val1[20]={0}; char val2[20]={0}; char val3[20]={0}; Message_StringID(MT_Experience, GM_GAINXP,ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3)); //[GM] You have gained %1 AXP and %2 EXP (%3). //Message(15, "[GM] You now have %d / %d EXP and %d / %d AA exp.", set_exp, GetEXPForLevel(GetLevel()+1), set_aaxp, max_AAXP); } }
//returns true on success bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if(spec == NULL) return(false); uint16 user_skill = GetSkill(spec->tradeskill); float chance = 0.0; float skillup_modifier = 0.0; int16 thirdstat = 0; int16 stat_modifier = 15; uint16 success_modifier = 0; // Rework based on the info on eqtraders.com // http://mboards.eqtraders.com/eq/showthread.php?t=22246 // 09/10/2006 v0.1 (eq4me) // 09/11/2006 v0.2 (eq4me) // Todo: // Implementing AAs // Success modifiers based on recipes // Skillup modifiers based on the rarity of the ingredients // Some tradeskills are more eqal then others. ;-) // If you want to customize the stage1 success rate do it here. // Remember: skillup_modifier is (float). Lower is better switch(spec->tradeskill) { case FLETCHING: case ALCHEMY: case JEWELRY_MAKING: case POTTERY: skillup_modifier = 4; break; case BAKING: case BREWING: skillup_modifier = 3; break; case RESEARCH: skillup_modifier = 1; break; default: skillup_modifier = 2; break; } // Some tradeskills take the higher of one additional stat beside INT and WIS // to determine the skillup rate. Additionally these tradeskills do not have an // -15 modifier on their statbonus. if (spec->tradeskill == FLETCHING || spec->tradeskill == MAKE_POISON) { thirdstat = GetDEX(); stat_modifier = 0; } else if (spec->tradeskill == BLACKSMITHING) { thirdstat = GetSTR(); stat_modifier = 0; } int16 higher_from_int_wis = (GetINT() > GetWIS()) ? GetINT() : GetWIS(); int16 bonusstat = (higher_from_int_wis > thirdstat) ? higher_from_int_wis : thirdstat; vector< pair<uint32,uint8> >::iterator itr; //calculate the base success chance // For trivials over 68 the chance is (skill - 0.75*trivial) +51.5 // For trivial up to 68 the chance is (skill - trivial) + 66 if (spec->trivial >= 68) { chance = (user_skill - (0.75*spec->trivial)) + 51.5; } else { chance = (user_skill - spec->trivial) + 66; } int16 over_trivial = (int16)GetRawSkill(spec->tradeskill) - (int16)spec->trivial; //handle caps if(spec->nofail) { chance = 100; //cannot fail. _log(TRADESKILLS__TRACE, "...This combine cannot fail."); } else if(over_trivial >= 0) { // At reaching trivial the chance goes to 95% going up an additional // percent for every 40 skillpoints above the trivial. // The success rate is not modified through stats. // Mastery AAs are unaccounted for so far. // chance_AA = chance + ((100 - chance) * mastery_modifier) // But the 95% limit with an additional 1% for every 40 skill points // above critical still stands. // Mastery modifier is: 10%/25%/50% for rank one/two/three chance = 95.0f + (float(user_skill - spec->trivial) / 40.0f); Message_StringID(MT_Emote, TRADESKILL_TRIVIAL); } else if(chance < 5) { // Minimum chance is always 5 chance = 5; } else if(chance > 95) { //cap is 95, shouldent reach this before trivial, but just in case. chance = 95; } _log(TRADESKILLS__TRACE, "...Current skill: %d , Trivial: %d , Success chance: %f percent", user_skill , spec->trivial , chance); _log(TRADESKILLS__TRACE, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR()); float res = MakeRandomFloat(0, 99); int AAChance = 0; //AA modifiers //can we do this with nested switches? if(spec->tradeskill == ALCHEMY){ switch(GetAA(aaAlchemyMastery)){ case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if(spec->tradeskill == JEWELRY_MAKING){ switch(GetAA(aaJewelCraftMastery)){ case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } const Item_Struct* item = NULL; if (spec->tradeskill == BLACKSMITHING) { switch(GetAA(aaBlacksmithingMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == BAKING) { switch(GetAA(aaBakingMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == BREWING) { switch(GetAA(aaBrewingMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == FLETCHING) { switch(GetAA(aaFletchingMastery2)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == POTTERY) { switch(GetAA(aaPotteryMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == TAILORING) { switch(GetAA(aaTailoringMastery)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (spec->tradeskill == RESEARCH) { switch(GetAA(aaArcaneTongues)) { case 1: AAChance = 10; break; case 2: AAChance = 25; break; case 3: AAChance = 50; break; } } if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){ success_modifier = 1; if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); Message_StringID(4,TRADESKILL_SUCCEED,spec->name.c_str()); _log(TRADESKILLS__TRACE, "Tradeskill success"); itr = spec->onsuccess.begin(); while(itr != spec->onsuccess.end() && !spec->quest) { //should we check this crap? SummonItem(itr->first, itr->second); item = database.GetItem(itr->first); if (this->GetGroup()) { entity_list.MessageGroup(this,true,MT_Skills,"%s has successfully fashioned %s!",GetName(),item->Name); } if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second); itr++; } return(true); } else { success_modifier = 2; // Halves the chance if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); Message_StringID(MT_Emote,TRADESKILL_FAILED); _log(TRADESKILLS__TRACE, "Tradeskill failed"); if (this->GetGroup()) { entity_list.MessageGroup(this,true,MT_Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its"); } itr = spec->onfail.begin(); while(itr != spec->onfail.end()) { //should we check these arguments? SummonItem(itr->first, itr->second); itr++; } // Rolls on each item, is possible to return everything int SalvageChance = aabonuses.SalvageChance + itembonuses.SalvageChance + spellbonuses.SalvageChance; // Skip check if not a normal TS or if a quest recipe these should be nofail, but check amyways if(SalvageChance && spec->tradeskill != 75 && !spec->quest) { itr = spec->salvage.begin(); uint8 sc = 0; while(itr != spec->salvage.end()) { for(sc = 0; sc < itr->second; sc++) if(MakeRandomInt(0,99) < SalvageChance) SummonItem(itr->first, 1); itr++; } } } return(false); }
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { this->EVENT_ITEM_ScriptStopReturn(); if(conlevel == CON_GREEN) return; uint32 add_exp = in_add_exp; if(!resexp && (XPRate != 0)) add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f)); if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA=0; // stop exploit with sanity check uint32 add_aaxp; if(resexp) { add_aaxp = 0; } else { //figure out how much of this goes to AAs add_aaxp = add_exp * m_epp.perAA / 100; //take that ammount away from regular exp add_exp -= add_aaxp; float totalmod = 1.0; if(RuleR(Character, ExpMultiplier) >= 0){ totalmod *= RuleR(Character, ExpMultiplier); } float zemmod = 75.0; if(zone->newzone_data.zone_exp_multiplier >= 0){ zemmod = zone->newzone_data.zone_exp_multiplier * 100; } if(zone->IsHotzone()) { totalmod += RuleR(Zone, HotZoneBonus); } // AK had a permanent 20% XP increase. totalmod += 0.20; add_exp = uint32(float(add_exp) * totalmod * zemmod); } float aatotalmod = 1.0; // AK had a permanent 20% XP increase. aatotalmod += 0.20; if(zone->newzone_data.zone_exp_multiplier >= 0){ aatotalmod *= zone->newzone_data.zone_exp_multiplier * 100; } if(RuleB(Zone, LevelBasedEXPMods)){ if(zone->level_exp_mod[GetLevel()].ExpMod){ add_exp *= zone->level_exp_mod[GetLevel()].ExpMod; add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod; } } uint32 requiredxp = GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel()); float xp_cap = (float)requiredxp * 0.13f; //13% of total XP is our cap if(add_exp > xp_cap) add_exp = xp_cap; if(add_aaxp > xp_cap) add_aaxp = xp_cap; uint32 exp = GetEXP() + add_exp; uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod); uint32 had_aaexp = GetAAXP(); aaexp += had_aaexp; if(aaexp < had_aaexp) aaexp = had_aaexp; //watch for wrap uint32 neededxp = GetEXPForLevel(GetLevel()+1) - (GetEXP() + add_exp); if (admin>=100 && GetGM()) { Message(CC_Yellow, "[GM] You have gained %d (%d) AXP and %d (%d) EXP. %d more EXP is needed for Level %d", add_aaxp, GetAAXP() + add_aaxp, add_exp, GetEXP() + add_exp, neededxp, GetLevel()+1); } //Message(CC_Yellow, "AddExp: XP awarded: %i (%i) Required XP is: %i Cap: %0.2f Race: %i Class: %i Zoneid: %i", add_exp, GetEXP() + add_exp, requiredxp, xp_cap, GetBaseRace(), GetClass(), zone->GetZoneID()); SetEXP(exp, aaexp, resexp); }
//returns true on success bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if(spec == nullptr) return(false); uint16 user_skill = GetSkill(spec->tradeskill); float chance = 0.0; float skillup_modifier = 0.0; int16 thirdstat = 0; int16 stat_modifier = 15; uint16 success_modifier = 0; // Rework based on the info on eqtraders.com // http://mboards.eqtraders.com/eq/showthread.php?t=22246 // 09/10/2006 v0.1 (eq4me) // 09/11/2006 v0.2 (eq4me) // Todo: // Implementing AAs // Success modifiers based on recipes // Skillup modifiers based on the rarity of the ingredients // Some tradeskills are more eqal then others. ;-) // If you want to customize the stage1 success rate do it here. // Remember: skillup_modifier is (float). Lower is better switch(spec->tradeskill) { case SkillFletching: skillup_modifier = RuleI(Character, TradeskillUpFletching); break; case SkillAlchemy: skillup_modifier = RuleI(Character, TradeskillUpAlchemy); break; case SkillJewelryMaking: skillup_modifier = RuleI(Character, TradeskillUpJewelcrafting); break; case SkillPottery: skillup_modifier = RuleI(Character, TradeskillUpPottery); break; case SkillBaking: skillup_modifier = RuleI(Character, TradeskillUpBaking); break; case SkillBrewing: skillup_modifier = RuleI(Character, TradeskillUpBrewing); break; case SkillBlacksmithing: skillup_modifier = RuleI(Character, TradeskillUpBlacksmithing); break; case SkillResearch: skillup_modifier = RuleI(Character, TradeskillUpResearch); break; case SkillMakePoison: skillup_modifier = RuleI(Character, TradeskillUpMakePoison); break; case SkillTinkering: skillup_modifier = RuleI(Character, TradeskillUpTinkering); break; default: skillup_modifier = 2; break; } // Some tradeskills take the higher of one additional stat beside INT and WIS // to determine the skillup rate. Additionally these tradeskills do not have an // -15 modifier on their statbonus. if (spec->tradeskill == SkillFletching || spec->tradeskill == SkillMakePoison) { thirdstat = GetDEX(); stat_modifier = 0; } else if (spec->tradeskill == SkillBlacksmithing) { thirdstat = GetSTR(); stat_modifier = 0; } int16 higher_from_int_wis = (GetINT() > GetWIS()) ? GetINT() : GetWIS(); int16 bonusstat = (higher_from_int_wis > thirdstat) ? higher_from_int_wis : thirdstat; std::vector< std::pair<uint32,uint8> >::iterator itr; //calculate the base success chance // For trivials over 68 the chance is (skill - 0.75*trivial) +51.5 // For trivial up to 68 the chance is (skill - trivial) + 66 if (spec->trivial >= 68) { chance = (user_skill - (0.75*spec->trivial)) + 51.5; } else { chance = (user_skill - spec->trivial) + 66; } int16 over_trivial = (int16)GetRawSkill(spec->tradeskill) - (int16)spec->trivial; //handle caps if(spec->nofail) { chance = 100; //cannot fail. Log.Out(Logs::Detail, Logs::Tradeskills, "...This combine cannot fail."); } else if(over_trivial >= 0) { // At reaching trivial the chance goes to 95% going up an additional // percent for every 40 skillpoints above the trivial. // The success rate is not modified through stats. // Mastery AAs are unaccounted for so far. // chance_AA = chance + ((100 - chance) * mastery_modifier) // But the 95% limit with an additional 1% for every 40 skill points // above critical still stands. // Mastery modifier is: 10%/25%/50% for rank one/two/three chance = 95.0f + (float(user_skill - spec->trivial) / 40.0f); Message_StringID(MT_Emote, TRADESKILL_TRIVIAL); } else if(chance < 5) { // Minimum chance is always 5 chance = 5; } else if(chance > 95) { //cap is 95, shouldent reach this before trivial, but just in case. chance = 95; } Log.Out(Logs::Detail, Logs::Tradeskills, "...Current skill: %d , Trivial: %d , Success chance: %f percent", user_skill , spec->trivial , chance); Log.Out(Logs::Detail, Logs::Tradeskills, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR()); float res = zone->random.Real(0, 99); int aa_chance = 0; aa_chance = spellbonuses.ReduceTradeskillFail[spec->tradeskill] + itembonuses.ReduceTradeskillFail[spec->tradeskill] + aabonuses.ReduceTradeskillFail[spec->tradeskill]; const Item_Struct* item = nullptr; chance = mod_tradeskill_chance(chance, spec); if (((spec->tradeskill==75) || GetGM() || (chance > res)) || zone->random.Roll(aa_chance)) { success_modifier = 1; if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); Message_StringID(4, TRADESKILL_SUCCEED, spec->name.c_str()); Log.Out(Logs::Detail, Logs::Tradeskills, "Tradeskill success"); itr = spec->onsuccess.begin(); while(itr != spec->onsuccess.end() && !spec->quest) { //should we check this crap? SummonItem(itr->first, itr->second); item = database.GetItem(itr->first); if (this->GetGroup()) { entity_list.MessageGroup(this, true, MT_Skills, "%s has successfully fashioned %s!", GetName(), item->Name); } /* QS: Player_Log_Trade_Skill_Events */ if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){ std::string event_desc = StringFormat("Success :: fashioned recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID()); QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc); } if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second); ++itr; } return(true); } /* Tradeskill Fail */ else { success_modifier = 2; // Halves the chance if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); Message_StringID(MT_Emote,TRADESKILL_FAILED); Log.Out(Logs::Detail, Logs::Tradeskills, "Tradeskill failed"); if (this->GetGroup()) { entity_list.MessageGroup(this,true,MT_Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its"); } /* QS: Player_Log_Trade_Skill_Events */ if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){ std::string event_desc = StringFormat("Failed :: recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID()); QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc); } itr = spec->onfail.begin(); while(itr != spec->onfail.end()) { //should we check these arguments? SummonItem(itr->first, itr->second); ++itr; } /* Salvage Item rolls */ // Rolls on each item, is possible to return everything int SalvageChance = aabonuses.SalvageChance + itembonuses.SalvageChance + spellbonuses.SalvageChance; // Skip check if not a normal TS or if a quest recipe these should be nofail, but check amyways if(SalvageChance && spec->tradeskill != 75 && !spec->quest) { itr = spec->salvage.begin(); uint8 sc = 0; while(itr != spec->salvage.end()) { for(sc = 0; sc < itr->second; sc++) if(zone->random.Roll(SalvageChance)) SummonItem(itr->first, 1); ++itr; } } } return(false); }
void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { Log.Out(Logs::Detail, Logs::None, "Attempting to Set Exp for %s (XP: %u, AAXP: %u, Rez: %s)", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false"); //max_AAXP = GetEXPForLevel(52) - GetEXPForLevel(51); //GetEXPForLevel() doesn't depend on class/race, just level, so it shouldn't change between Clients max_AAXP = RuleI(AA, ExpPerPoint); //this may be redundant since we're doing this in Client::FinishConnState2() if (max_AAXP == 0 || GetEXPForLevel(GetLevel()) == 0xFFFFFFFF) { Message(13, "Error in Client::SetEXP. EXP not set."); return; // Must be invalid class/race } if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) { if (isrezzexp) this->Message_StringID(MT_Experience, REZ_REGAIN); else{ if(this->IsGrouped()) this->Message_StringID(MT_Experience, GAIN_GROUPXP); else if(IsRaidGrouped()) Message_StringID(MT_Experience, GAIN_RAIDEXP); else this->Message_StringID(MT_Experience, GAIN_XP); } } else if((set_exp + set_aaxp) < (m_pp.exp+m_pp.expAA)){ //only loss message if you lose exp, no message if you gained/lost nothing. Message(15, "You have lost experience."); } //check_level represents the level we should be when we have //this ammount of exp (once these loops complete) uint16 check_level = GetLevel()+1; //see if we gained any levels bool level_increase = true; int8 level_count = 0; while (set_exp >= GetEXPForLevel(check_level)) { check_level++; if (check_level > 127) { //hard level cap check_level = 127; break; } level_count++; if(GetMercID()) UpdateMercLevel(); } //see if we lost any levels while (set_exp < GetEXPForLevel(check_level-1)) { check_level--; if (check_level < 2) { //hard level minimum check_level = 2; break; } level_increase = false; if(GetMercID()) UpdateMercLevel(); } check_level--; //see if we gained any AAs if (set_aaxp >= max_AAXP) { /* Note: AA exp is stored differently than normal exp. Exp points are only stored in m_pp.expAA until you gain a full AA point, once you gain it, a point is added to m_pp.aapoints and the ammount needed to gain that point is subtracted from m_pp.expAA then, once they spend an AA point, it is subtracted from m_pp.aapoints. In theory it then goes into m_pp.aapoints_spent, but im not sure if we have that in the right spot. */ //record how many points we have uint32 last_unspentAA = m_pp.aapoints; //figure out how many AA points we get from the exp were setting m_pp.aapoints = set_aaxp / max_AAXP; Log.Out(Logs::Detail, Logs::None, "Calculating additional AA Points from AAXP for %s: %u / %u = %.1f points", this->GetCleanName(), set_aaxp, max_AAXP, (float)set_aaxp / (float)max_AAXP); //get remainder exp points, set in PP below set_aaxp = set_aaxp - (max_AAXP * m_pp.aapoints); //add in how many points we had m_pp.aapoints += last_unspentAA; //set_aaxp = m_pp.expAA % max_AAXP; //figure out how many points were actually gained /*uint32 gained = m_pp.aapoints - last_unspentAA;*/ //unused //Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA); char val1[20]={0}; Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2. /* QS: PlayerLogAARate */ if (RuleB(QueryServ, PlayerLogAARate)){ int add_points = (m_pp.aapoints - last_unspentAA); std::string query = StringFormat("INSERT INTO `qs_player_aa_rate_hourly` (char_id, aa_count, hour_time) VALUES (%i, %i, UNIX_TIMESTAMP() - MOD(UNIX_TIMESTAMP(), 3600)) ON DUPLICATE KEY UPDATE `aa_count` = `aa_count` + %i", this->CharacterID(), add_points, add_points); QServ->SendQuery(query.c_str()); } //Message(15, "You now have %d skill points available to spend.", m_pp.aapoints); } uint8 maxlevel = RuleI(Character, MaxExpLevel) + 1; if(maxlevel <= 1) maxlevel = RuleI(Character, MaxLevel) + 1; if(check_level > maxlevel) { check_level = maxlevel; if(RuleB(Character, KeepLevelOverMax)) { set_exp = GetEXPForLevel(GetLevel()+1); } else { set_exp = GetEXPForLevel(maxlevel); } } if(RuleB(Character, PerCharacterQglobalMaxLevel)){ uint32 MaxLevel = GetCharMaxLevelFromQGlobal(); if(MaxLevel){ if(GetLevel() >= MaxLevel){ uint32 expneeded = GetEXPForLevel(MaxLevel); if(set_exp > expneeded) { set_exp = expneeded; } } } } if ((GetLevel() != check_level) && !(check_level >= maxlevel)) { char val1[20]={0}; if (level_increase) { if (level_count == 1) Message_StringID(MT_Experience, GAIN_LEVEL, ConvertArray(check_level, val1)); else Message(15, "Welcome to level %i!", check_level); if (check_level == RuleI(Character, DeathItemLossLevel)) Message_StringID(15, CORPSE_ITEM_LOST); if (check_level == RuleI(Character, DeathExpLossLevel)) Message_StringID(15, CORPSE_EXP_LOST); } else Message_StringID(MT_Experience, LOSE_LEVEL, ConvertArray(check_level, val1)); #ifdef BOTS uint8 myoldlevel = GetLevel(); #endif SetLevel(check_level); #ifdef BOTS if(RuleB(Bots, BotLevelsWithOwner)) // hack way of doing this..but, least invasive... (same criteria as gain level for sendlvlapp) Bot::LevelBotWithClient(this, GetLevel(), (myoldlevel==check_level-1)); #endif } //If were at max level then stop gaining experience if we make it to the cap if(GetLevel() == maxlevel - 1){ uint32 expneeded = GetEXPForLevel(maxlevel); if(set_exp > expneeded) { set_exp = expneeded; } } //set the client's EXP and AAEXP m_pp.exp = set_exp; m_pp.expAA = set_aaxp; if (GetLevel() < 51) { m_epp.perAA = 0; // turn off aa exp if they drop below 51 } else SendAAStats(); //otherwise, send them an AA update //send the expdata in any case so the xp bar isnt stuck after leveling uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); uint32 tmpxp2 = GetEXPForLevel(GetLevel()); // Quag: crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; float tmpxp = (float) ( (float) set_exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); eu->exp = (uint32)(330.0f * tmpxp); FastQueuePacket(&outapp); } if (admin>=100 && GetGM()) { char val1[20]={0}; char val2[20]={0}; char val3[20]={0}; Message_StringID(MT_Experience, GM_GAINXP,ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3)); //[GM] You have gained %1 AXP and %2 EXP (%3). //Message(15, "[GM] You now have %d / %d EXP and %d / %d AA exp.", set_exp, GetEXPForLevel(GetLevel()+1), set_aaxp, max_AAXP); } }