Files

140 lines
4.5 KiB
Go
Raw Permalink Normal View History

2026-07-09 08:33:57 +08:00
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 }