140 lines
4.5 KiB
Go
140 lines
4.5 KiB
Go
|
|
package player
|
||
|
|
|
||
|
|
import (
|
||
|
|
"sync"
|
||
|
|
)
|
||
|
|
|
||
|
|
// hungerManager handles the changes in hunger, exhaustion and saturation of a player.
|
||
|
|
type hungerManager struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
foodLevel int
|
||
|
|
saturationLevel float64
|
||
|
|
exhaustionLevel float64
|
||
|
|
foodTick int
|
||
|
|
}
|
||
|
|
|
||
|
|
// newHungerManager returns a new hunger manager with the default values for food level, saturation level and
|
||
|
|
// exhaustion level.
|
||
|
|
func newHungerManager() *hungerManager {
|
||
|
|
return &hungerManager{foodLevel: 20, saturationLevel: 5, foodTick: 1}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Food returns the current food level of a player. The level returned is guaranteed to always be between 0
|
||
|
|
// and 20.
|
||
|
|
func (m *hungerManager) Food() int {
|
||
|
|
m.mu.RLock()
|
||
|
|
defer m.mu.RUnlock()
|
||
|
|
return m.foodLevel
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetFood sets the food level of a player. The level passed must be in a range of 0-20. If the level passed
|
||
|
|
// is negative, the food level will be set to 0. If the level exceeds 20, the food level will be set to 20.
|
||
|
|
func (m *hungerManager) SetFood(level int) {
|
||
|
|
m.mu.Lock()
|
||
|
|
defer m.mu.Unlock()
|
||
|
|
|
||
|
|
m.foodLevel = max(min(level, 20), 0)
|
||
|
|
}
|
||
|
|
|
||
|
|
// AddFood adds a number of food points to the current food level of a player.
|
||
|
|
func (m *hungerManager) AddFood(points int) {
|
||
|
|
m.mu.Lock()
|
||
|
|
defer m.mu.Unlock()
|
||
|
|
|
||
|
|
m.foodLevel = max(min(m.foodLevel+points, 20), 0)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reset resets the hunger manager to its default values, identical to those set when creating a new manager
|
||
|
|
// using newHungerManager.
|
||
|
|
func (m *hungerManager) Reset() {
|
||
|
|
m.mu.Lock()
|
||
|
|
defer m.mu.Unlock()
|
||
|
|
|
||
|
|
m.foodLevel = 20
|
||
|
|
m.saturationLevel = 5
|
||
|
|
m.exhaustionLevel = 0
|
||
|
|
m.foodTick = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// ResetExhaustion resets the player's exhaustion level to 0. It prevents the
|
||
|
|
// player's food level from decreasing immediately after cancelling food loss.
|
||
|
|
func (m *hungerManager) resetExhaustion() {
|
||
|
|
m.mu.Lock()
|
||
|
|
defer m.mu.Unlock()
|
||
|
|
m.exhaustionLevel = 0
|
||
|
|
m.saturationLevel = 0
|
||
|
|
m.foodTick = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// exhaust exhausts the player by the amount of points passed. If the total exhaustion level exceeds 4, a
|
||
|
|
// saturation point, or food point, if saturation is 0, will be subtracted.
|
||
|
|
func (m *hungerManager) exhaust(points float64) {
|
||
|
|
m.mu.Lock()
|
||
|
|
defer m.mu.Unlock()
|
||
|
|
|
||
|
|
m.exhaustionLevel += points
|
||
|
|
for m.exhaustionLevel >= 4 {
|
||
|
|
// Maximum exhaustion value is 4, so keep removing one saturation point until the exhaustion level
|
||
|
|
// is below 4.
|
||
|
|
m.exhaustionLevel -= 4
|
||
|
|
m.desaturate()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// saturate saturates the player's food and saturation by the amount of points passed. Note that the total
|
||
|
|
// saturation will never exceed the total food value.
|
||
|
|
func (m *hungerManager) saturate(food int, saturation float64) {
|
||
|
|
m.mu.Lock()
|
||
|
|
defer m.mu.Unlock()
|
||
|
|
|
||
|
|
m.foodLevel = max(min(m.foodLevel+food, 20), 0)
|
||
|
|
m.saturationLevel = max(min(m.saturationLevel+saturation, float64(m.foodLevel)), 0)
|
||
|
|
}
|
||
|
|
|
||
|
|
// desaturate removes one saturation point from the player. If the saturation level of the player is already
|
||
|
|
// 0, a point will be subtracted from the food level instead. If that level, too, is already 0, nothing will
|
||
|
|
// happen.
|
||
|
|
func (m *hungerManager) desaturate() {
|
||
|
|
if m.saturationLevel <= 0 && m.foodLevel != 0 {
|
||
|
|
m.foodLevel--
|
||
|
|
} else if m.saturationLevel > 0 {
|
||
|
|
m.saturationLevel = max(m.saturationLevel-1, 0)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// canQuicklyRegenerate checks if the player can quickly regenerate. The function returns true if Food() returns 20
|
||
|
|
// and the player still has saturation left.
|
||
|
|
// The rate of regeneration is 1/0.5 seconds.
|
||
|
|
func (m *hungerManager) canQuicklyRegenerate() bool {
|
||
|
|
m.mu.RLock()
|
||
|
|
defer m.mu.RUnlock()
|
||
|
|
|
||
|
|
return m.foodLevel == 20 && m.saturationLevel > 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// canRegenerate checks if the player with the amount of food levels in the hunger manager can regenerate.
|
||
|
|
// The function returns true if Food() returns either 18-20.
|
||
|
|
// The rate of regeneration is 1/4 seconds.
|
||
|
|
func (m *hungerManager) canRegenerate() bool {
|
||
|
|
return m.Food() >= 18
|
||
|
|
}
|
||
|
|
|
||
|
|
// canSprint returns true if the food level of the player is 7 or higher.
|
||
|
|
func (m *hungerManager) canSprint() bool {
|
||
|
|
return m.Food() > 6
|
||
|
|
}
|
||
|
|
|
||
|
|
// starving checks if the player is currently considered to be starving. True is returned if Food() returns 0.
|
||
|
|
func (m *hungerManager) starving() bool {
|
||
|
|
return m.Food() == 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// StarvationDamageSource is the world.DamageSource passed when a player is
|
||
|
|
// dealt damage from an empty food bar.
|
||
|
|
type StarvationDamageSource struct{}
|
||
|
|
|
||
|
|
func (StarvationDamageSource) ReducedByArmour() bool { return false }
|
||
|
|
func (StarvationDamageSource) ReducedByResistance() bool { return false }
|
||
|
|
func (StarvationDamageSource) Fire() bool { return false }
|
||
|
|
func (StarvationDamageSource) IgnoreTotem() bool { return false }
|