netfishing/progression/player_experience.gd

131 lines
3.3 KiB
GDScript3
Raw Normal View History

class_name PlayerExperience
extends Node
signal experience_changed(total_experience: int, level: int)
signal experience_awarded(
amount: int,
previous_total: int,
new_total: int,
previous_level: int,
new_level: int,
)
const MAX_TOTAL_EXPERIENCE: int = 1000000000000
const MAX_LEVEL: int = 100000
var _total_experience: int = 0
func get_total_experience() -> int:
return _total_experience
func get_level() -> int:
return level_for_total_experience(_total_experience)
func get_experience_in_level() -> int:
return _total_experience - total_experience_for_level(get_level())
func get_experience_for_next_level() -> int:
return experience_required_for_next_level(get_level())
func get_level_progress() -> float:
return progress_for_total_experience(_total_experience)
func award_experience(amount: int) -> bool:
if amount <= 0 or _total_experience >= MAX_TOTAL_EXPERIENCE:
return false
var previous_total: int = _total_experience
var previous_level: int = level_for_total_experience(previous_total)
_total_experience = mini(
_total_experience + amount,
MAX_TOTAL_EXPERIENCE,
)
var awarded_amount: int = _total_experience - previous_total
if awarded_amount <= 0:
return false
var new_level: int = level_for_total_experience(_total_experience)
experience_changed.emit(_total_experience, new_level)
experience_awarded.emit(
awarded_amount,
previous_total,
_total_experience,
previous_level,
new_level,
)
return true
func restore_total_experience(total_experience: int) -> bool:
if total_experience < 0 or total_experience > MAX_TOTAL_EXPERIENCE:
return false
var changed: bool = _total_experience != total_experience
_total_experience = total_experience
if changed:
experience_changed.emit(_total_experience, get_level())
return true
func reset_to_defaults() -> void:
restore_total_experience(0)
func to_save_data() -> Dictionary:
return {"total_experience": _total_experience}
static func experience_required_for_next_level(level: int) -> int:
var safe_level: int = maxi(level, 1)
var level_index: int = safe_level - 1
return 100 + level_index * 25 + level_index * level_index * 5
static func total_experience_for_level(level: int) -> int:
var completed_levels: int = clampi(level - 1, 0, MAX_LEVEL - 1)
if completed_levels == 0:
return 0
var linear_sum: int = floori(
float(completed_levels) * float(completed_levels - 1) / 2.0
)
var square_sum: int = floori(
float(completed_levels - 1)
* float(completed_levels)
* float(2 * completed_levels - 1)
/ 6.0
)
return completed_levels * 100 + linear_sum * 25 + square_sum * 5
static func level_for_total_experience(total_experience: int) -> int:
var safe_total: int = clampi(
total_experience,
0,
MAX_TOTAL_EXPERIENCE,
)
var low: int = 1
var high: int = MAX_LEVEL
while low < high:
var middle: int = low + floori(float(high - low + 1) / 2.0)
if total_experience_for_level(middle) <= safe_total:
low = middle
else:
high = middle - 1
return low
static func progress_for_total_experience(total_experience: int) -> float:
var level: int = level_for_total_experience(total_experience)
var level_start: int = total_experience_for_level(level)
var required: int = experience_required_for_next_level(level)
if required <= 0:
return 0.0
return clampf(
float(total_experience - level_start) / float(required),
0.0,
1.0,
)