118 lines
3.6 KiB
Go
118 lines
3.6 KiB
Go
|
|
package entity
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"math"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ExperienceManager manages experience and levels for entities, and provides functions to add, remove, and calculate
|
||
|
|
// experience needed for upcoming levels.
|
||
|
|
type ExperienceManager struct {
|
||
|
|
experience int
|
||
|
|
dec float64
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewExperienceManager returns a new ExperienceManager with no experience.
|
||
|
|
func NewExperienceManager() *ExperienceManager {
|
||
|
|
return &ExperienceManager{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Experience returns the amount of experience the manager currently has.
|
||
|
|
func (e *ExperienceManager) Experience() int {
|
||
|
|
return e.experience
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add adds experience to the total experience and recalculates the level and progress if necessary. Passing a negative
|
||
|
|
// value is valid. If the new experience would otherwise drop below 0, it is set to 0.
|
||
|
|
func (e *ExperienceManager) Add(amount int) (level int, progress float64) {
|
||
|
|
if e.experience += amount; e.experience < 0 {
|
||
|
|
e.Reset()
|
||
|
|
}
|
||
|
|
return progressFromExperience(e.total())
|
||
|
|
}
|
||
|
|
|
||
|
|
// total returns the total amount of experience including the extra decimals provided for more accuracy.
|
||
|
|
func (e *ExperienceManager) total() float64 {
|
||
|
|
return float64(e.experience) + e.dec
|
||
|
|
}
|
||
|
|
|
||
|
|
// Level returns the current experience level.
|
||
|
|
func (e *ExperienceManager) Level() int {
|
||
|
|
level, _ := progressFromExperience(e.total())
|
||
|
|
return level
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetLevel sets the level of the manager.
|
||
|
|
func (e *ExperienceManager) SetLevel(level int) {
|
||
|
|
if level < 0 || level > math.MaxInt32 {
|
||
|
|
panic(fmt.Sprintf("level must be between 0 and 2,147,483,647, got %v", level))
|
||
|
|
}
|
||
|
|
_, progress := progressFromExperience(e.total())
|
||
|
|
e.experience = experienceForLevels(level) + int(float64(experienceForLevel(level))*progress)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Progress returns the progress towards the next level.
|
||
|
|
func (e *ExperienceManager) Progress() float64 {
|
||
|
|
_, progress := progressFromExperience(e.total())
|
||
|
|
return progress
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetProgress sets the progress of the manager.
|
||
|
|
func (e *ExperienceManager) SetProgress(progress float64) {
|
||
|
|
if progress < 0 || progress > 1 {
|
||
|
|
panic(fmt.Sprintf("progress must be between 0 and 1, got %f", progress))
|
||
|
|
}
|
||
|
|
currentLevel, _ := progressFromExperience(e.total())
|
||
|
|
progressExp := float64(experienceForLevel(currentLevel)) * progress
|
||
|
|
e.experience = experienceForLevels(currentLevel) + int(progressExp)
|
||
|
|
e.dec = progressExp - math.Trunc(progressExp)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reset resets the total experience, level, and progress of the manager to zero.
|
||
|
|
func (e *ExperienceManager) Reset() {
|
||
|
|
e.experience, e.dec = 0, 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// progressFromExperience returns the level and progress from the total experience given.
|
||
|
|
func progressFromExperience(experience float64) (level int, progress float64) {
|
||
|
|
var a, b, c float64
|
||
|
|
|
||
|
|
switch {
|
||
|
|
case experience <= float64(experienceForLevels(16)):
|
||
|
|
a, b = 1.0, 6.0
|
||
|
|
case experience <= float64(experienceForLevels(31)):
|
||
|
|
a, b, c = 2.5, -40.5, 360.0
|
||
|
|
default:
|
||
|
|
a, b, c = 4.5, -162.5, 2220.0
|
||
|
|
}
|
||
|
|
|
||
|
|
var sol float64
|
||
|
|
if d := b*b - 4*a*(c-experience); d > 0 {
|
||
|
|
s := math.Sqrt(d)
|
||
|
|
sol = math.Max((-b+s)/(2*a), (-b-s)/(2*a))
|
||
|
|
} else if d == 0 {
|
||
|
|
sol = -b / (2 * a)
|
||
|
|
}
|
||
|
|
return int(sol), sol - math.Trunc(sol)
|
||
|
|
}
|
||
|
|
|
||
|
|
// experienceForLevels calculates the amount of experience needed in total to reach a certain level.
|
||
|
|
func experienceForLevels(level int) int {
|
||
|
|
if level <= 16 {
|
||
|
|
return level*level + level*6
|
||
|
|
} else if level <= 31 {
|
||
|
|
return int(float64(level*level)*2.5 - 40.5*float64(level) + 360)
|
||
|
|
}
|
||
|
|
return int(float64(level*level)*4.5 - 162.5*float64(level) + 2220)
|
||
|
|
}
|
||
|
|
|
||
|
|
// experienceForLevel returns the amount experience needed to reach level + 1.
|
||
|
|
func experienceForLevel(level int) int {
|
||
|
|
if level <= 15 {
|
||
|
|
return 2*level + 7
|
||
|
|
} else if level <= 30 {
|
||
|
|
return 5*level - 38
|
||
|
|
}
|
||
|
|
return 9*level - 158
|
||
|
|
}
|