github.com/df-mc/dragonfly@v0.9.13/server/entity/experience.go (about) 1 package entity 2 3 import ( 4 "fmt" 5 "math" 6 "sync" 7 ) 8 9 // ExperienceManager manages experience and levels for entities, and provides functions to add, remove, and calculate 10 // experience needed for upcoming levels. 11 type ExperienceManager struct { 12 mu sync.RWMutex 13 experience int 14 d float64 15 } 16 17 // NewExperienceManager returns a new ExperienceManager with no experience. 18 func NewExperienceManager() *ExperienceManager { 19 return &ExperienceManager{} 20 } 21 22 // Experience returns the amount of experience the manager currently has. 23 func (e *ExperienceManager) Experience() int { 24 e.mu.RLock() 25 defer e.mu.RUnlock() 26 return e.experience 27 } 28 29 // Add adds experience to the total experience and recalculates the level and progress if necessary. Passing a negative 30 // value is valid. If the new experience would otherwise drop below 0, it is set to 0. 31 func (e *ExperienceManager) Add(amount int) (level int, progress float64) { 32 e.mu.Lock() 33 defer e.mu.Unlock() 34 e.experience += amount 35 if e.experience < 0 { 36 e.experience = 0 37 e.d = 0 38 } 39 return progressFromExperience(e.total()) 40 } 41 42 // total returns the total amount of experience including the extra decimals provided for more accuracy. 43 func (e *ExperienceManager) total() float64 { 44 return float64(e.experience) + e.d 45 } 46 47 // Level returns the current experience level. 48 func (e *ExperienceManager) Level() int { 49 e.mu.RLock() 50 defer e.mu.RUnlock() 51 level, _ := progressFromExperience(e.total()) 52 return level 53 } 54 55 // SetLevel sets the level of the manager. 56 func (e *ExperienceManager) SetLevel(level int) { 57 if level < 0 || level > math.MaxInt32 { 58 panic(fmt.Sprintf("level must be between 0 and 2,147,483,647, got %d", level)) 59 } 60 e.mu.Lock() 61 defer e.mu.Unlock() 62 _, progress := progressFromExperience(e.total()) 63 e.experience = experienceForLevels(level) + int(float64(experienceForLevel(level))*progress) 64 } 65 66 // Progress returns the progress towards the next level. 67 func (e *ExperienceManager) Progress() float64 { 68 e.mu.RLock() 69 defer e.mu.RUnlock() 70 _, progress := progressFromExperience(e.total()) 71 return progress 72 } 73 74 // SetProgress sets the progress of the manager. 75 func (e *ExperienceManager) SetProgress(progress float64) { 76 if progress < 0 || progress > 1 { 77 panic(fmt.Sprintf("progress must be between 0 and 1, got %f", progress)) 78 } 79 e.mu.Lock() 80 defer e.mu.Unlock() 81 currentLevel, _ := progressFromExperience(e.total()) 82 progressExp := float64(experienceForLevel(currentLevel)) * progress 83 e.experience = experienceForLevels(currentLevel) + int(progressExp) 84 e.d = progressExp - math.Trunc(progressExp) 85 } 86 87 // Reset resets the total experience, level, and progress of the manager to zero. 88 func (e *ExperienceManager) Reset() { 89 e.mu.Lock() 90 defer e.mu.Unlock() 91 e.experience, e.d = 0, 0 92 } 93 94 // progressFromExperience returns the level and progress from the total experience given. 95 func progressFromExperience(experience float64) (level int, progress float64) { 96 var a, b, c float64 97 if experience <= float64(experienceForLevels(16)) { 98 a, b = 1.0, 6.0 99 } else if experience <= float64(experienceForLevels(31)) { 100 a, b, c = 2.5, -40.5, 360.0 101 } else { 102 a, b, c = 4.5, -162.5, 2220.0 103 } 104 105 var sol float64 106 if d := b*b - 4*a*(c-experience); d > 0 { 107 s := math.Sqrt(d) 108 sol = math.Max((-b+s)/(2*a), (-b-s)/(2*a)) 109 } else if d == 0 { 110 sol = -b / (2 * a) 111 } 112 return int(sol), sol - math.Trunc(sol) 113 } 114 115 // experienceForLevels calculates the amount of experience needed in total to reach a certain level. 116 func experienceForLevels(level int) int { 117 if level <= 16 { 118 return level*level + level*6 119 } else if level <= 31 { 120 return int(float64(level*level)*2.5 - 40.5*float64(level) + 360) 121 } 122 return int(float64(level*level)*4.5 - 162.5*float64(level) + 2220) 123 } 124 125 // experienceForLevel returns the amount experience needed to reach level + 1. 126 func experienceForLevel(level int) int { 127 if level <= 15 { 128 return 2*level + 7 129 } else if level <= 30 { 130 return 5*level - 38 131 } 132 return 9*level - 158 133 }