int32 Client::CalcBaseMana() { int WisInt = 0; int MindLesserFactor, MindFactor; int32 max_m = 0; int wisint_mana = 0; int base_mana = 0; int ConvertedWisInt = 0; switch(GetCasterClass()) { case 'I': WisInt = GetINT(); if((( WisInt - 199 ) / 2) > 0) MindLesserFactor = ( WisInt - 199 ) / 2; else MindLesserFactor = 0; MindFactor = WisInt - MindLesserFactor; if(WisInt > 100) max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); else max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); break; case 'W': WisInt = GetWIS(); if((( WisInt - 199 ) / 2) > 0) MindLesserFactor = ( WisInt - 199 ) / 2; else MindLesserFactor = 0; MindFactor = WisInt - MindLesserFactor; if(WisInt > 100) max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); else max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); break; case 'N': { max_m = 0; break; } default: { Log.Out(Logs::General, Logs::None, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); max_m = 0; break; } } #if EQDEBUG >= 11 Log.Out(Logs::General, Logs::None, "Client::CalcBaseMana() called for %s - returning %d", GetName(), max_m); #endif return max_m; }
/* If you change this function, you should update the above function to keep the #aggro command accurate. */ bool Mob::CheckWillAggro(Mob *mob) { if(!mob) return false; //sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM //they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting if (mob->IsClient()) { if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn()) return false; } Mob *ownr = mob->GetOwner(); if(ownr && ownr->IsClient() && !ownr->CastToClient()->ClientFinishedLoading()) return false; float iAggroRange = GetAggroRange(); // Check If it's invisible and if we can see invis // Check if it's a client, and that the client is connected and not linkdead, // and that the client isn't Playing an NPC, with thier gm flag on // Check if it's not a Interactive NPC // Trumpcard: The 1st 3 checks are low cost calcs to filter out unnessecary distance checks. Leave them at the beginning, they are the most likely occurence. // Image: I moved this up by itself above faction and distance checks because if one of these return true, theres no reason to go through the other information float t1, t2, t3; t1 = mob->GetX() - GetX(); t2 = mob->GetY() - GetY(); t3 = mob->GetZ() - GetZ(); //Cheap ABS() if(t1 < 0) t1 = 0 - t1; if(t2 < 0) t2 = 0 - t2; if(t3 < 0) t3 = 0 - t3; if(( t1 > iAggroRange) || ( t2 > iAggroRange) || ( t3 > iAggroRange) ||(mob->IsInvisible(this)) || (mob->IsClient() && (!mob->CastToClient()->Connected() || mob->CastToClient()->IsLD() || mob->CastToClient()->IsBecomeNPC() || mob->CastToClient()->GetGM() ) )) { return(false); } // Don't aggro new clients if we are already engaged unless PROX_AGGRO is set if (IsEngaged() && (!GetSpecialAbility(PROX_AGGRO) || (GetSpecialAbility(PROX_AGGRO) && !CombatRange(mob)))) { Log.Out(Logs::Moderate, Logs::Aggro, "%s is in combat, and does not have prox_aggro, or does and is out of combat range with %s", GetName(), mob->GetName()); return false; } //im not sure I understand this.. //if I have an owner and it is not this mob, then I cannot //aggro this mob...??? //changed to be 'if I have an owner and this is it' if(mob == GetOwner()) { return(false); } float dist2 = DistanceSquared(mob->GetPosition(), m_Position); float iAggroRange2 = iAggroRange*iAggroRange; if( dist2 > iAggroRange2 ) { // Skip it, out of range return(false); } //Image: Get their current target and faction value now that its required //this function call should seem backwards FACTION_VALUE fv = mob->GetReverseFactionCon(this); // Make sure they're still in the zone // Are they in range? // Are they kos? // Are we stupid or are they green // and they don't have their gm flag on int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE) heroicCHA_mod = THREATENLY_ARRGO_CHANCE; if ( //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetINT() <= RuleI(Aggro, IntAggroThreshold) ) ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GREEN ) ) && ( ( fv == FACTION_SCOWLS || (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr) || ( fv == FACTION_THREATENLY && zone->random.Roll(THREATENLY_ARRGO_CHANCE - heroicCHA_mod) ) ) ) ) { //FatherNiwtit: make sure we can see them. last since it is very expensive if(CheckLosFN(mob)) { Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName()); return( mod_will_aggro(mob, this) ); } } Log.Out(Logs::Detail, Logs::Aggro, "Is In zone?:%d\n", mob->InZone()); Log.Out(Logs::Detail, Logs::Aggro, "Dist^2: %f\n", dist2); Log.Out(Logs::Detail, Logs::Aggro, "Range^2: %f\n", iAggroRange2); Log.Out(Logs::Detail, Logs::Aggro, "Faction: %d\n", fv); Log.Out(Logs::Detail, Logs::Aggro, "Int: %d\n", GetINT()); Log.Out(Logs::Detail, Logs::Aggro, "Con: %d\n", GetLevelCon(mob->GetLevel())); return(false); }
void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { //this logic is duplicated from below, try to keep it up to date. float iAggroRange = GetAggroRange(); float t1, t2, t3; t1 = mob->GetX() - GetX(); t2 = mob->GetY() - GetY(); t3 = mob->GetZ() - GetZ(); //Cheap ABS() if(t1 < 0) t1 = 0 - t1; if(t2 < 0) t2 = 0 - t2; if(t3 < 0) t3 = 0 - t3; if(( t1 > iAggroRange) || ( t2 > iAggroRange) || ( t3 > iAggroRange) ) { towho->Message(0, "...%s is out of range (fast). distances (%.3f,%.3f,%.3f), range %.3f", mob->GetName(), t1, t2, t3, iAggroRange); return; } if(mob->IsInvisible(this)) { towho->Message(0, "...%s is invisible to me. ", mob->GetName()); return; } if((mob->IsClient() && (!mob->CastToClient()->Connected() || mob->CastToClient()->IsLD() || mob->CastToClient()->IsBecomeNPC() || mob->CastToClient()->GetGM() ) )) { towho->Message(0, "...%s is my owner. ", mob->GetName()); return; } if(mob == GetOwner()) { towho->Message(0, "...%s a GM or is not connected. ", mob->GetName()); return; } float dist2 = DistanceSquared(mob->GetPosition(), m_Position); float iAggroRange2 = iAggroRange*iAggroRange; if( dist2 > iAggroRange2 ) { towho->Message(0, "...%s is out of range. %.3f > %.3f ", mob->GetName(), dist2, iAggroRange2); return; } if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GREEN ) { towho->Message(0, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); return; } if(verbose) { int my_primary = GetPrimaryFaction(); int mob_primary = mob->GetPrimaryFaction(); Mob *own = GetOwner(); if(own != nullptr) my_primary = own->GetPrimaryFaction(); own = mob->GetOwner(); if(mob_primary > 0 && own != nullptr) mob_primary = own->GetPrimaryFaction(); if(mob_primary == 0) { towho->Message(0, "...%s has no primary faction", mob->GetName()); } else if(mob_primary < 0) { towho->Message(0, "...%s is on special faction %d", mob->GetName(), mob_primary); } else { char namebuf[256]; if(!database.GetFactionName(mob_primary, namebuf, sizeof(namebuf))) strcpy(namebuf, "(Unknown)"); std::list<struct NPCFaction*>::iterator cur,end; cur = faction_list.begin(); end = faction_list.end(); bool res = false; for(; cur != end; ++cur) { struct NPCFaction* fac = *cur; if ((int32)fac->factionID == mob_primary) { if (fac->npc_value > 0) { towho->Message(0, "...%s is on ALLY faction %s (%d) with %d", mob->GetName(), namebuf, mob_primary, fac->npc_value); res = true; break; } else if (fac->npc_value < 0) { towho->Message(0, "...%s is on ENEMY faction %s (%d) with %d", mob->GetName(), namebuf, mob_primary, fac->npc_value); res = true; break; } else { towho->Message(0, "...%s is on NEUTRAL faction %s (%d) with 0", mob->GetName(), namebuf, mob_primary); res = true; break; } } } if(!res) { towho->Message(0, "...%s is on faction %s (%d), which I have no entry for.", mob->GetName(), namebuf, mob_primary); } } } FACTION_VALUE fv = mob->GetReverseFactionCon(this); if(!( fv == FACTION_SCOWLS || (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr) || fv == FACTION_THREATENLY )) { towho->Message(0, "...%s faction not low enough. value='%s'", mob->GetName(), FactionValueToString(fv)); return; } if(fv == FACTION_THREATENLY) { towho->Message(0, "...%s threatening to me, so they only have a %d chance per check of attacking.", mob->GetName()); } if(!CheckLosFN(mob)) { towho->Message(0, "...%s is out of sight.", mob->GetName()); } towho->Message(0, "...%s meets all conditions, I should be attacking them.", mob->GetName()); }
void CPremiumPlayerCtrl:: LoadIniFile(const char* szpIniFileName,struct i3client_t::tagPremiumInfo* pPremium) { char AppName[_MAX_PATH]; char inStr[_MAX_PATH]; char Session[_MAX_PATH]; for(int idx=0; idx < i3client_t::tagPremiumInfo::maxmap_number; idx++){ wsprintf(AppName,"Map%02d",idx); ::GetPrivateProfileString(AppName,"Allow","YES",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.bAllow = GetBOOL(inStr); ::GetPrivateProfileString(AppName,"iRemainSecond","2147483647",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.iRemainSecond=GetINT(inStr); ::GetPrivateProfileString(AppName,"bDecreseRemainSecond","NO",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.bDecreseRemainSecond=GetBOOL(inStr); ::GetPrivateProfileString(AppName,"bDecreseAllRemainSecond","NO",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.bDecreseAllRemainSecond = GetBOOL(inStr); ::GetPrivateProfileString(AppName,"fAddExpRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.fAddExpRatio = GetFLOAT(inStr); ::GetPrivateProfileString(AppName,"fAddGenExpRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.fAddGenExpRatio = GetFLOAT(inStr); ::GetPrivateProfileString(AppName,"fAddGenCapabilityRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.fAddGenCapabilityRatio=GetFLOAT(inStr); ::GetPrivateProfileString(AppName,"fAddItemDropRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.fAddItemDropRatio =GetFLOAT(inStr); ::GetPrivateProfileString(AppName,"fAddNarkRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->Map[idx].Default.fAddNarkRatio = GetFLOAT(inStr); pPremium->Map[idx].Cur = pPremium->Map[idx].Default; } wsprintf(AppName,"Gameble"); ::GetPrivateProfileString(AppName,"fAddPriceRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->Gamble.fAddPriceRatio = GetFLOAT(inStr); for(idx=0; idx < i3client_t::tagPremiumInfo::tagGamble::maxnum_apperance_item_group; idx++){ wsprintf(Session,"Group%02d"); ::GetPrivateProfileString(AppName,Session,"YES",inStr,_MAX_PATH,szpIniFileName); pPremium->Gamble.ItemGroupArray[idx].bApperance=GetBOOL(inStr); } wsprintf(AppName,"ItemOptionUpgrade"); ::GetPrivateProfileString(AppName,"iMaxlevel","10",inStr,_MAX_PATH,szpIniFileName); pPremium->ItemOptionUpgrade.iMaxlevel = GetINT(inStr); ::GetPrivateProfileString(AppName,"fAddPriceRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->ItemOptionUpgrade.fAddPriceRatio = GetFLOAT(inStr); wsprintf(AppName,"Die"); ::GetPrivateProfileString(AppName,"fExpRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->Die.fExpRatio = GetFLOAT(inStr); ::GetPrivateProfileString(AppName,"fNakRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->Die.fNakRatio = GetFLOAT(inStr); ::GetPrivateProfileString("PremiumBooth","Use","FALSE",inStr,_MAX_PATH,szpIniFileName); pPremium->bCreatePremiumBooth = GetBOOL(inStr); ::GetPrivateProfileString("ItemCraft","iMaxLevel","10",inStr,_MAX_PATH,szpIniFileName); pPremium->iMaxLevelItemCraft = GetINT(inStr); ::GetPrivateProfileString("ItemPrecocity","TimeRatio","1.0f",inStr,_MAX_PATH,szpIniFileName); pPremium->fItemPrecocityTimeRatio = GetFLOAT(inStr); ::GetPrivateProfileString("ItemRecycle","Use","YES",inStr,_MAX_PATH,szpIniFileName); pPremium->bItemRecycle = GetBOOL(inStr); pPremium->GonyounPracticeBattle.lDate = time(NULL); ::GetPrivateProfileString("GonyounPracticeBattle","iMaxCount","5",inStr,_MAX_PATH,szpIniFileName); pPremium->GonyounPracticeBattle.iMaxCount = GetINT(inStr); pPremium->GonyounPracticeBattle.iDecreseCount = pPremium->GonyounPracticeBattle.iMaxCount; pPremium->WorldChatting.lDate = time(NULL); ::GetPrivateProfileString("WorldChatting","iMaxCount","10",inStr,_MAX_PATH,szpIniFileName); pPremium->WorldChatting.iMaxCount = GetINT(inStr); pPremium->WorldChatting.iDecreaseCount = pPremium->WorldChatting.iMaxCount; ::GetPrivateProfileString("Status","iDefaultInitCount","1",inStr,_MAX_PATH,szpIniFileName); pPremium->Status.iDefaultInitCount= GetINT(inStr); ::GetPrivateProfileString("Status","iInitCount","1000",inStr,_MAX_PATH,szpIniFileName); pPremium->Status.iInitCount=GetINT(inStr); }
int32 Client::CalcBaseMana() { int WisInt = 0; int MindLesserFactor, MindFactor; int32 max_m = 0; int wisint_mana = 0; int base_mana = 0; int ConvertedWisInt = 0; switch(GetCasterClass()) { case 'I': WisInt = GetINT(); if (GetClientVersion() >= EQClientSoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if (WisInt > 100) { ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); if (WisInt > 201) { ConvertedWisInt -= ((WisInt - 201) * 5 / 4); } } else { ConvertedWisInt = WisInt; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if(base_data) { max_m = base_data->base_mana + (ConvertedWisInt * base_data->mana_factor) + (GetHeroicINT() * 10); } } else { if((( WisInt - 199 ) / 2) > 0) MindLesserFactor = ( WisInt - 199 ) / 2; else MindLesserFactor = 0; MindFactor = WisInt - MindLesserFactor; if(WisInt > 100) max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); else max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); } break; case 'W': WisInt = GetWIS(); if (GetClientVersion() >= EQClientSoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if (WisInt > 100) { ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); if (WisInt > 201) { ConvertedWisInt -= ((WisInt - 201) * 5 / 4); } } else { ConvertedWisInt = WisInt; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if(base_data) { max_m = base_data->base_mana + (ConvertedWisInt * base_data->mana_factor) + (GetHeroicWIS() * 10); } } else { if((( WisInt - 199 ) / 2) > 0) MindLesserFactor = ( WisInt - 199 ) / 2; else MindLesserFactor = 0; MindFactor = WisInt - MindLesserFactor; if(WisInt > 100) max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); else max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); } break; case 'N': { max_m = 0; break; } default: { LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); max_m = 0; break; } } #if EQDEBUG >= 11 LogFile->write(EQEMuLog::Debug, "Client::CalcBaseMana() called for %s - returning %d", GetName(), max_m); #endif return max_m; }
//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); }
//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); }