diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 399f86700..e90d5aa49 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -1497,11 +1497,14 @@ namespace Framework.Constants MaxResultsLookupCommands, MaxWho, MinCharterName, + MinCreatureScaledXpRatio, + MinDiscoveredScaledXpRatio, MinDualspecLevel, MinLevelStatSave, MinPetName, MinPetitionSigns, MinPlayerName, + MinQuestScaledXpRatio, NoGrayAggroAbove, NoGrayAggroBelow, NoResetTalentCost, diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 3d9d7b665..11d1f7462 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -5007,7 +5007,7 @@ namespace Game.Entities uint k_grey = Formulas.GetGrayLevel(GetLevel()); // Victim level less gray level - if (v_level < k_grey) + if (v_level < k_grey && WorldConfig.GetIntValue(WorldCfg.MinCreatureScaledXpRatio) == 0) return false; Creature creature = victim.ToCreature(); @@ -6172,6 +6172,12 @@ namespace Game.Entities XP = (uint)(Global.ObjectMgr.GetBaseXP(areaLevel) * WorldConfig.GetFloatValue(WorldCfg.RateXpExplore)); } + if (WorldConfig.GetIntValue(WorldCfg.MinDiscoveredScaledXpRatio) != 0) + { + uint minScaledXP = (uint)(Global.ObjectMgr.GetBaseXP(areaLevel) * WorldConfig.GetFloatValue(WorldCfg.RateXpExplore)) * WorldConfig.GetUIntValue(WorldCfg.MinDiscoveredScaledXpRatio) / 100; + XP = Math.Max(minScaledXP, XP); + } + GiveXP(XP, null); SendExplorationExperience(areaId, XP); } diff --git a/Source/Game/Miscellaneous/Formulas.cs b/Source/Game/Miscellaneous/Formulas.cs index bc59d48f0..3a5c183c6 100644 --- a/Source/Game/Miscellaneous/Formulas.cs +++ b/Source/Game/Miscellaneous/Formulas.cs @@ -136,6 +136,13 @@ namespace Game baseGain = 0; } + if (WorldConfig.GetIntValue(WorldCfg.MinCreatureScaledXpRatio) != 0 && pl_level != mob_level) + { + // Use mob level instead of player level to avoid overscaling on gain in a min is enforced + uint baseGainMin = BaseGain(pl_level, pl_level) * WorldConfig.GetUIntValue(WorldCfg.MinCreatureScaledXpRatio) / 100; + baseGain = Math.Max(baseGainMin, baseGain); + } + Global.ScriptMgr.OnBaseGainCalculation(baseGain, pl_level, mob_level); return baseGain; } diff --git a/Source/Game/Quest/Quest.cs b/Source/Game/Quest/Quest.cs index c80aae0e5..021cbe867 100644 --- a/Source/Game/Quest/Quest.cs +++ b/Source/Game/Quest/Quest.cs @@ -294,14 +294,13 @@ namespace Game if (player.GetLevel() >= Global.ObjectMgr.GetMaxLevelForExpansion(PlayerConst.CurrentExpansion - 1) && player.GetSession().GetExpansion() == PlayerConst.CurrentExpansion && Expansion < (int)PlayerConst.CurrentExpansion) xp = (uint)(xp / 9.0f); - if (xp <= 100) - xp = 5 * ((xp + 2) / 5); - else if (xp <= 500) - xp = 10 * ((xp + 5) / 10); - else if (xp <= 1000) - xp = 25 * ((xp + 12) / 25); - else - xp = 50 * ((xp + 25) / 50); + xp = RoundXPValue(xp); + + if (WorldConfig.GetUIntValue(WorldCfg.MinQuestScaledXpRatio) != 0) + { + uint minScaledXP = RoundXPValue((uint)(questXp.Difficulty[RewardXPDifficulty] * RewardXPMultiplier)) * WorldConfig.GetUIntValue(WorldCfg.MinQuestScaledXpRatio) / 100; + xp = Math.Max(minScaledXP, xp); + } return xp; } @@ -558,6 +557,18 @@ namespace Game QueryData.Info.TimeAllowed = LimitTime; } + public static uint RoundXPValue(uint xp) + { + if (xp <= 100) + return 5 * ((xp + 2) / 5); + else if (xp <= 500) + return 10 * ((xp + 5) / 10); + else if (xp <= 1000) + return 25 * ((xp + 12) / 25); + else + return 50 * ((xp + 25) / 50); + } + public bool HasFlag(QuestFlags flag) { return (Flags & flag) != 0; } public bool HasFlagEx(QuestFlagsEx flag) { return (FlagsEx & flag) != 0; } public bool HasFlagEx(QuestFlagsEx2 flag) { return (FlagsEx2 & flag) != 0; } diff --git a/Source/Game/Server/WorldConfig.cs b/Source/Game/Server/WorldConfig.cs index e7c001a96..b6cad55dc 100644 --- a/Source/Game/Server/WorldConfig.cs +++ b/Source/Game/Server/WorldConfig.cs @@ -295,6 +295,28 @@ namespace Game Values[WorldCfg.GroupXpDistance] = GetDefaultValue("MaxGroupXPDistance", 74.0f); Values[WorldCfg.MaxRecruitAFriendDistance] = GetDefaultValue("MaxRecruitAFriendBonusDistance", 100.0f); + Values[WorldCfg.MinQuestScaledXpRatio] = GetDefaultValue("MinQuestScaledXPRatio", 0); + if ((int)Values[WorldCfg.MinQuestScaledXpRatio] > 100) + { + Log.outError(LogFilter.ServerLoading, $"MinQuestScaledXPRatio ({Values[WorldCfg.MinQuestScaledXpRatio]}) must be in range 0..100. Set to 0."); + Values[WorldCfg.MinQuestScaledXpRatio] = 0; + } + + Values[WorldCfg.MinCreatureScaledXpRatio] = GetDefaultValue("MinCreatureScaledXPRatio", 0); + if ((int)Values[WorldCfg.MinCreatureScaledXpRatio] > 100) + { + Log.outError(LogFilter.ServerLoading, $"MinCreatureScaledXPRatio ({Values[WorldCfg.MinCreatureScaledXpRatio]}) must be in range 0..100. Set to 0."); + Values[WorldCfg.MinCreatureScaledXpRatio] = 0; + } + + Values[WorldCfg.MinDiscoveredScaledXpRatio] = GetDefaultValue("MinDiscoveredScaledXPRatio", 0); + if ((int)Values[WorldCfg.MinDiscoveredScaledXpRatio] > 100) + { + Log.outError(LogFilter.ServerLoading, $"MinDiscoveredScaledXPRatio ({Values[WorldCfg.MinDiscoveredScaledXpRatio]}) must be in range 0..100. Set to 0."); + Values[WorldCfg.MinDiscoveredScaledXpRatio] = 0; + } + + /// @todo Add MonsterSight (with meaning) in worldserver.conf or put them as define Values[WorldCfg.SightMonster] = GetDefaultValue("MonsterSight", 50.0f); if (reload) diff --git a/Source/WorldServer/WorldServer.conf.dist b/Source/WorldServer/WorldServer.conf.dist index 53208a503..d63d47b68 100644 --- a/Source/WorldServer/WorldServer.conf.dist +++ b/Source/WorldServer/WorldServer.conf.dist @@ -1104,6 +1104,34 @@ MaxGroupXPDistance = 74 MaxRecruitAFriendBonusDistance = 100 +# +# MinQuestScaledXPRatio +# Description: Min ratio of experience that a quest can grant when player level scaling is factored. +# Example: 50 (No less than 50% experience granted from a lower leveled quests completion) +# 100 (Quests always grant full experience upon completion) +# Default: 0 - (Quests too low may grant no experience) + +MinQuestScaledXPRatio = 0 + +# +# MinCreatureScaledXPRatio +# Description: Min ratio of experience that a creature kill can grant when player level scaling is factored. This +# will also allow spell procs to trigger, such as Drain Soul, if > 0 and exp is grantable. +# Example: 50 (No less than 50% experience granted from a lower leveled creature kill) +# 100 (Creature kills always grant full experience upon kill) +# Default: 0 - (Creatures too low may grant no experience) + +MinCreatureScaledXPRatio = 0 + +# +# MinDiscoveredScaledXPRatio +# Description: Min ratio of experience that an area discovery event will grant when player level scaling is factored. +# Example: 50 (No less than 50% experience granted from discovering a new section of map) +# 100 (Map exploration always grant full experience upon discovery) +# Default: 0 - (No experience granted when discovered area is too low level) + +MinDiscoveredScaledXPRatio = 0 + # # MailDeliveryDelay # Description: Time (in seconds) mail delivery is delayed when sending items.