131 lines
3.3 KiB
GDScript3
131 lines
3.3 KiB
GDScript3
|
|
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,
|
||
|
|
)
|