extends RefCounted const NORMAL_MAX_LEVEL := 120 const ABSORPTION_MID_LEVEL := 60 const MAX_LEVEL := 120 const ENEMY_GROWTH_PER_LEVEL := 0.10 const ENDGAME_ENEMY_GROWTH_PER_LEVEL := 0.20 const ITEM_GROWTH_PER_LEVEL := 0.07 const BOSS_HULL_MULTIPLIER := 5.0 const BOSS_SHIELD_MULTIPLIER := 3.0 const BOSS_DAMAGE_MULTIPLIER := 0.5 const FIRST_BOSS_DAMAGE_MULTIPLIER := 0.25 const FIRST_BOSS_FIRE_INTERVAL_MULTIPLIER := 2.0 const COUNT_POINTS := [Vector2(1, 11), Vector2(10, 11), Vector2(15, 11), Vector2(50, 18), Vector2(100, 23), Vector2(120, 24)] const ELITES_PER_LEVEL := 2 static func enemy_count(level: int) -> int: var clamped_level := clampi(level, 1, MAX_LEVEL) for index in range(1, COUNT_POINTS.size()): var before: Vector2 = COUNT_POINTS[index - 1] var after: Vector2 = COUNT_POINTS[index] if clamped_level <= after.x: return roundi(lerpf(before.y, after.y, inverse_lerp(before.x, after.x, clamped_level))) return 24 static func enemy_tiers(level: int) -> Array[int]: var count := enemy_count(level) # One boss and two elites on every level, included in the total count. var bosses := 1 var elites := ELITES_PER_LEVEL var tiers: Array[int] = [2] for elite in elites: tiers.append(1) for normal in count - bosses - elites: tiers.append(0) return tiers static func power_scale(level: int) -> float: return item_stat_scale(level) static func item_stat_scale(level: int) -> float: # Additive growth from level-one values, never compound interest. return 1.0 + ITEM_GROWTH_PER_LEVEL * (maxi(level, 1) - 1) static func is_hard(level: int) -> bool: return level > NORMAL_MAX_LEVEL static func enemy_stat_scale(level: int) -> float: var normal_steps := mini(maxi(level, 1), MAX_LEVEL) - 1 var endgame_steps := maxi(0, level - MAX_LEVEL) return 1.0 + ENEMY_GROWTH_PER_LEVEL * normal_steps + ENDGAME_ENEMY_GROWTH_PER_LEVEL * endgame_steps static func enemy_absorption_total(level: int) -> float: var clamped_level := clampi(level, 1, MAX_LEVEL) if clamped_level <= ABSORPTION_MID_LEVEL: return lerpf(2.0, 30.0, pow(float(clamped_level - 1) / float(ABSORPTION_MID_LEVEL - 1), 1.25)) return lerpf(30.0, 60.0, float(clamped_level - ABSORPTION_MID_LEVEL) / float(MAX_LEVEL - ABSORPTION_MID_LEVEL))