From be5ea2b927142d737c87eac87401461e9dee9b46 Mon Sep 17 00:00:00 2001 From: Wilson Date: Tue, 1 Apr 2025 12:32:13 -0400 Subject: [PATCH] Rework level calculation (#2196) * rework level calculation * rework level calculations * generated --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com> --- .../analytics_misc/construct_list_model.dart | 39 +++++++++++++++---- .../get_analytics_controller.dart | 19 ++++----- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/lib/pangea/analytics_misc/construct_list_model.dart b/lib/pangea/analytics_misc/construct_list_model.dart index 2819af958..3da9df015 100644 --- a/lib/pangea/analytics_misc/construct_list_model.dart +++ b/lib/pangea/analytics_misc/construct_list_model.dart @@ -177,25 +177,50 @@ class ConstructListModel { if (totalXP < 0) { totalXP = 0; } + level = calculateLevelWithXp(totalXP); + } - // Don't call .floor() if NaN or Infinity - // https://pangea-chat.sentry.io/issues/6052871310 - final double levelCalculation = 1 + sqrt((1 + 8 * totalXP / 100) / 2); - if (!levelCalculation.isNaN && levelCalculation.isFinite) { - level = levelCalculation.floor(); + int calculateLevelWithXp(int totalXP) { + // [D] is the "compression factor". It determines how quickly + /// or slowly the level grows relative to XP + const double D = 2500; + final doubleScore = (1 + sqrt((1 + (8.0 * totalXP / D)) / 2.0)); + if (!doubleScore.isNaN && doubleScore.isFinite) { + return doubleScore.floor(); } else { - level = 1; ErrorHandler.logError( e: "Calculated level in Nan or Infinity", data: { "totalXP": totalXP, "prevXP": prevXP, - "level": levelCalculation, + "level": doubleScore, }, ); + return 1; } } + int calculateXpWithLevel(int level) { + // [D] is the same "compression factor" as in calculateLevelWithXp. + const double D = 2500.0; + + // If level <= 1, XP should be 0 or negative by this math. + // In practice, you might clamp it to 0: + if (level <= 1) { + return 0; + } + + // Convert level to double for the math + final double lc = level.toDouble(); + + // XP from the inverse formula: + final double xpDouble = (D / 8.0) * (2.0 * pow(lc - 1.0, 2.0) - 1.0); + + // Floor or clamp to ensure non-negative. + final int xp = xpDouble.floor(); + return (xp < 0) ? 0 : xp; + } + // TODO; make this non-nullable, returning empty if not found ConstructUses? getConstructUses(ConstructIdentifier identifier) { final partialKey = "${identifier.lemma}-${identifier.type.string}"; diff --git a/lib/pangea/analytics_misc/get_analytics_controller.dart b/lib/pangea/analytics_misc/get_analytics_controller.dart index 58988ae91..5a3caf592 100644 --- a/lib/pangea/analytics_misc/get_analytics_controller.dart +++ b/lib/pangea/analytics_misc/get_analytics_controller.dart @@ -49,22 +49,17 @@ class GetAnalyticsController extends BaseController { // the minimum XP required for a given level int get _minXPForLevel { - return _calculateMinXpForLevel(constructListModel.level); + return constructListModel.calculateXpWithLevel(constructListModel.level); } // the minimum XP required for the next level int get _minXPForNextLevel { - return _calculateMinXpForLevel(constructListModel.level + 1); + return constructListModel + .calculateXpWithLevel(constructListModel.level + 1); } int get minXPForNextLevel => _minXPForNextLevel; - /// Calculates the minimum XP required for a specific level. - int _calculateMinXpForLevel(int level) { - if (level == 1) return 0; // Ensure level 1 starts at 0 XP - return ((100 / 8) * (2 * pow(level - 1, 2))).floor(); - } - // the progress within the current level as a percentage (0.0 to 1.0) double get levelProgress { final progress = (constructListModel.totalXP - _minXPForLevel) / @@ -197,8 +192,8 @@ class GetAnalyticsController extends BaseController { } Future _onLevelDown(final int lowerLevel, final int upperLevel) async { - final offset = - _calculateMinXpForLevel(lowerLevel) - constructListModel.totalXP; + final offset = constructListModel.calculateXpWithLevel(lowerLevel) - + constructListModel.totalXP; await _pangeaController.userController.addXPOffset(offset); constructListModel.updateConstructs( [], @@ -391,8 +386,8 @@ class GetAnalyticsController extends BaseController { // generate level up analytics as a construct summary ConstructSummary summary; try { - final int maxXP = _calculateMinXpForLevel(upperLevel); - final int minXP = _calculateMinXpForLevel(lowerLevel); + final int maxXP = constructListModel.calculateXpWithLevel(upperLevel); + final int minXP = constructListModel.calculateXpWithLevel(lowerLevel); int diffXP = maxXP - minXP; if (diffXP < 0) diffXP = 0;