Skip to content

Commit c9bf926

Browse files
Document and Refactor of Player Leveling logic (AscensionGameDev#2569)
* removed redundant PacketSender.SendExperience call * invert negative for readability. * refactored logic checks. * add handle for zero condition. * Combine redundant checks. One SendExperience call. * CheckLevelUp making 3 calls to GetExperienceToNextLevel per loop. Create a variable to reduce calls. * single return check * Noted two spots where AddLevels zero check is being handled. * Add handle and documentation to AddLevels * Refactor summary comment and include the zero handle, removing it from other redundant locations. * renamed levels variable to amount. * Added summary comment to SetLevel. Noted redundancy in SetLevel and AddLevels * Added a boolean to SetLevels that skips the redundant calls when inside the AddLevels while loop. Summary comment updated to reflect. * Since AddLevels has a built in zero handle now, CheckLevelUp doesn't need to return a bool and be handled outside itself. * moved the redundant EXP < 0 call in GiveExperience. It can't be negative before checking levelups. I suppose it's possible the equipment bonus is so far negative, gaining experience takes it away... but then let it be handled in CheckLevelUp instead. * added summary comments to GetExperienceToNextLevel so it's obvious it returns -1 if Maxlevel is reached. * add summary comment to CheckLevelUp * update test to match the new signature --------- Co-authored-by: lodicolo <pandacoder@pm.me>
1 parent 32efc44 commit c9bf926

File tree

2 files changed

+64
-51
lines changed

2 files changed

+64
-51
lines changed

Intersect.Server.Core/Entities/Player.cs

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ public bool ValidateLists(PlayerContext? playerContext = default)
354354
return changes;
355355
}
356356

357+
/// <summary>
358+
/// Returns the required experience for the next level based on <see cref="ClassBase"/>. Returns -1 if MaxLevel.
359+
/// </summary>
360+
/// <param name="level">The current player level. Before leveling up.</param>
357361
private long GetExperienceToNextLevel(int level)
358362
{
359363
if (level >= Options.Instance.Player.MaxLevel)
@@ -1234,36 +1238,57 @@ public void FixVitals()
12341238

12351239
#region Leveling
12361240

1237-
public void SetLevel(int level, bool resetExperience = false)
1241+
/// <summary>
1242+
/// Sets the player's level to a sepecific value. Then sends all relevent data through <see cref="PacketSender"/>
1243+
/// </summary>
1244+
/// <param name="newLevel">Does nothing if less than 1. Clamped to <see cref="Options"/> MaxLevel</param>
1245+
/// <param name="resetExperience">Unless <paramref name="newLevel"/> is zero, will reset Exp to 0 if true.</param>
1246+
/// <param name="sendPackets">If set to false, will not send packets or <see cref="RecalculateStatsAndPoints"/> or <see cref="UnequipInvalidItems"/>/></param>
1247+
public void SetLevel(int newLevel, bool resetExperience = false, bool sendPackets = true)
12381248
{
1239-
if (level < 1)
1249+
if (newLevel < 1)
12401250
{
12411251
return;
12421252
}
12431253

1244-
Level = Math.Min(Options.Instance.Player.MaxLevel, level);
1254+
Level = Math.Min(Options.Instance.Player.MaxLevel, newLevel);
12451255
if (resetExperience)
12461256
{
12471257
Exp = 0;
12481258
}
12491259

1250-
RecalculateStatsAndPoints();
1251-
UnequipInvalidItems();
1252-
PacketSender.SendEntityDataToProximity(this);
1253-
PacketSender.SendExperience(this);
1260+
if (sendPackets)
1261+
{
1262+
RecalculateStatsAndPoints();
1263+
UnequipInvalidItems();
1264+
PacketSender.SendEntityDataToProximity(this);
1265+
PacketSender.SendExperience(this);
1266+
}
12541267
}
12551268

1256-
public void AddLevels(int levels = 1, bool resetExperience = true)
1269+
/// <summary>
1270+
/// Adds levels to the player based on what level they're currently at. Then sends all relevent data through <see cref="PacketSender"/>
1271+
/// <para>If <paramref name="amount"/> is zero, will still call <see cref="PacketSender.SendExperience"/> but do nothing else.</para>
1272+
/// </summary>
1273+
/// <param name="amount">Adds levels if positive, removes if negative and does nothing if zero.</param>
1274+
/// <param name="resetExperience">If levels is not zero, and this is true, resets the Exp to zero after adjusting levels.</param>
1275+
public void AddLevels(int amount = 1, bool resetExperience = true)
12571276
{
1277+
if (amount == 0)
1278+
{
1279+
PacketSender.SendExperience(this);
1280+
return;
1281+
}
1282+
12581283
ClassBase? classDescriptor = null;
12591284
List<(string, Color)> messageList = [];
12601285

1261-
var targetLevel = Math.Clamp(Level + levels, 1, Options.Instance.Player.MaxLevel);
1262-
if (levels > 0)
1286+
var targetLevel = Math.Clamp(Level + amount, 1, Options.Instance.Player.MaxLevel);
1287+
if (amount > 0)
12631288
{
12641289
while (Level < targetLevel)
12651290
{
1266-
SetLevel(Level + 1, resetExperience);
1291+
SetLevel(Level + 1, resetExperience, sendPackets: false);
12671292
messageList.Add((Strings.Player.LevelUp.ToString(Level), CustomColors.Combat.LevelUp));
12681293

12691294
if ((classDescriptor?.Id == ClassId || ClassBase.TryGet(ClassId, out classDescriptor)) && classDescriptor?.Spells != default)
@@ -1287,11 +1312,11 @@ public void AddLevels(int levels = 1, bool resetExperience = true)
12871312
PacketSender.SendActionMsg(this, Strings.Combat.LevelUp, CustomColors.Combat.LevelUp);
12881313
}
12891314
}
1290-
else if (levels < 0)
1315+
else if (amount < 0)
12911316
{
12921317
while (targetLevel < Level)
12931318
{
1294-
SetLevel(Level - 1);
1319+
SetLevel(Level - 1, sendPackets:false);
12951320
messageList.Add((Strings.Player.LevelLost.ToString(Level), CustomColors.Combat.LevelLost));
12961321

12971322
if ((classDescriptor?.Id == ClassId || ClassBase.TryGet(ClassId, out classDescriptor)) && classDescriptor?.Spells != default)
@@ -1317,10 +1342,11 @@ public void AddLevels(int levels = 1, bool resetExperience = true)
13171342

13181343
RecalculateStatsAndPoints();
13191344
UnequipInvalidItems();
1345+
PacketSender.SendEntityDataToProximity(this);
13201346
PacketSender.SendExperience(this);
1347+
13211348
PacketSender.SendPointsTo(this);
13221349
PacketSender.SendPlayerSpells(this);
1323-
PacketSender.SendEntityDataToProximity(this);
13241350

13251351
if (StatPoints > 0)
13261352
{
@@ -1352,17 +1378,10 @@ public void GiveExperience(long amount)
13521378
TakeExperience(-amount);
13531379
return;
13541380
}
1381+
var equipmentBonus = (int)Math.Round(amount * GetEquipmentBonusEffect(ItemEffect.EXP) / 100f);
1382+
Exp += amount + equipmentBonus;
13551383

1356-
Exp += (int)Math.Round(amount + (amount * (GetEquipmentBonusEffect(ItemEffect.EXP) / 100f)));
1357-
if (Exp < 0)
1358-
{
1359-
Exp = 0;
1360-
}
1361-
1362-
if (!CheckLevelUp())
1363-
{
1364-
PacketSender.SendExperience(this);
1365-
}
1384+
CheckLevelUp();
13661385
}
13671386

13681387
public void TakeExperience(long amount, bool enableLosingLevels = false, bool force = false)
@@ -1385,50 +1404,44 @@ public void TakeExperience(long amount, bool enableLosingLevels = false, bool fo
13851404
}
13861405

13871406
Exp -= amount;
1388-
if (Exp < 0)
1407+
1408+
var levelsToRemove = 0;
1409+
while (Exp < 0)
13891410
{
1390-
if (!enableLosingLevels || Level == 1)
1411+
if (enableLosingLevels && Level - levelsToRemove > 1)
13911412
{
1392-
Exp = 0;
1393-
PacketSender.SendExperience(this);
1413+
++levelsToRemove;
1414+
Exp += GetExperienceToNextLevel(Level - levelsToRemove);
13941415
}
13951416
else
13961417
{
1397-
var levelCount = 0;
1398-
while (Exp < 0 && Level + levelCount > 1)
1399-
{
1400-
--levelCount;
1401-
Exp += GetExperienceToNextLevel(Level + levelCount);
1402-
}
1403-
1404-
AddLevels(levelCount);
1405-
1406-
if (Exp < 0)
1407-
{
1408-
Exp = 0;
1409-
PacketSender.SendExperience(this);
1410-
}
1418+
Exp = 0;
14111419
}
14121420
}
1421+
1422+
AddLevels(-levelsToRemove);
14131423
}
14141424

1415-
private bool CheckLevelUp()
1425+
/// <summary>
1426+
/// Checks if the player has enough Exp to level. Removes EXP and calls <see cref="AddLevels"/> with the number of levels gained.
1427+
/// </summary>
1428+
private void CheckLevelUp()
14161429
{
14171430
var levelCount = 0;
1418-
while (Exp >= GetExperienceToNextLevel(Level + levelCount) &&
1419-
GetExperienceToNextLevel(Level + levelCount) > 0)
1431+
var experienceToNextLevel = GetExperienceToNextLevel(Level + levelCount);
1432+
while (Exp >= experienceToNextLevel && experienceToNextLevel > 0)
14201433
{
1421-
Exp -= GetExperienceToNextLevel(Level + levelCount);
1434+
Exp -= experienceToNextLevel;
14221435
levelCount++;
1436+
experienceToNextLevel = GetExperienceToNextLevel(Level + levelCount);
14231437
}
14241438

1425-
if (levelCount <= 0)
1439+
if (Exp < 0)
14261440
{
1427-
return false;
1441+
Exp = 0;
14281442
}
14291443

1430-
AddLevels(levelCount, false);
1431-
return true;
1444+
AddLevels(levelCount, false); //If zero, still calls PacketSender.SendExperience
14321445
}
14331446

14341447
#endregion

Intersect.Tests.Server/Entities/PlayerTests.SetLevel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void TestSetLevel(int initialLevel, int initialExperience, int newLevel,
4040
}
4141
);
4242

43-
player.SetLevel(level: newLevel, resetExperience: resetExperience);
43+
player.SetLevel(newLevel: newLevel, resetExperience: resetExperience);
4444

4545
Assert.Multiple(
4646
() =>

0 commit comments

Comments
 (0)