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  }