105 lines
3.0 KiB
Go
105 lines
3.0 KiB
Go
|
|
package entity
|
||
|
|
|
||
|
|
import (
|
||
|
|
"github.com/df-mc/dragonfly/server/block"
|
||
|
|
"github.com/df-mc/dragonfly/server/world"
|
||
|
|
"github.com/go-gl/mathgl/mgl64"
|
||
|
|
"math"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// PassiveBehaviourConfig holds optional parameters for a PassiveBehaviour.
|
||
|
|
type PassiveBehaviourConfig struct {
|
||
|
|
// Gravity is the amount of Y velocity subtracted every tick.
|
||
|
|
Gravity float64
|
||
|
|
// Drag is used to reduce all axes of the velocity every tick. Velocity is
|
||
|
|
// multiplied with (1-Drag) every tick.
|
||
|
|
Drag float64
|
||
|
|
// ExistenceDuration is the duration that an entity with this behaviour
|
||
|
|
// should last. Once this time expires, the entity is closed. If
|
||
|
|
// ExistenceDuration is 0, the entity will never expire automatically.
|
||
|
|
ExistenceDuration time.Duration
|
||
|
|
// Expire is called when the entity expires due to its age reaching the
|
||
|
|
// ExistenceDuration.
|
||
|
|
Expire func(e *Ent, tx *world.Tx)
|
||
|
|
// Tick is called for every tick that the entity is alive. Tick is called
|
||
|
|
// after the entity moves on a tick.
|
||
|
|
Tick func(e *Ent, tx *world.Tx)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (conf PassiveBehaviourConfig) Apply(data *world.EntityData) {
|
||
|
|
data.Data = conf.New()
|
||
|
|
}
|
||
|
|
|
||
|
|
// New creates a PassiveBehaviour using the parameters in conf.
|
||
|
|
func (conf PassiveBehaviourConfig) New() *PassiveBehaviour {
|
||
|
|
if conf.ExistenceDuration == 0 {
|
||
|
|
conf.ExistenceDuration = math.MaxInt64
|
||
|
|
}
|
||
|
|
return &PassiveBehaviour{conf: conf, fuse: conf.ExistenceDuration, mc: &MovementComputer{
|
||
|
|
Gravity: conf.Gravity,
|
||
|
|
Drag: conf.Drag,
|
||
|
|
DragBeforeGravity: true,
|
||
|
|
}}
|
||
|
|
}
|
||
|
|
|
||
|
|
// PassiveBehaviour implements Behaviour for entities that act passively. This
|
||
|
|
// means that they can move, but only under influence of the environment, which
|
||
|
|
// includes, for example, falling, and flowing water.
|
||
|
|
type PassiveBehaviour struct {
|
||
|
|
conf PassiveBehaviourConfig
|
||
|
|
mc *MovementComputer
|
||
|
|
|
||
|
|
close bool
|
||
|
|
fallDistance float64
|
||
|
|
fuse time.Duration
|
||
|
|
}
|
||
|
|
|
||
|
|
// Explode adds velocity to a passive entity to blast it away from the
|
||
|
|
// explosion's source.
|
||
|
|
func (p *PassiveBehaviour) Explode(e *Ent, src mgl64.Vec3, impact float64, _ block.ExplosionConfig) {
|
||
|
|
e.data.Vel = e.data.Vel.Add(e.data.Pos.Sub(src).Normalize().Mul(impact))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fuse returns the leftover time until PassiveBehaviourConfig.Expire is called,
|
||
|
|
// or -1 if this function is not set.
|
||
|
|
func (p *PassiveBehaviour) Fuse() time.Duration {
|
||
|
|
if p.conf.Expire != nil {
|
||
|
|
return p.fuse
|
||
|
|
}
|
||
|
|
return -1
|
||
|
|
}
|
||
|
|
|
||
|
|
// Tick implements the behaviour for a passive entity. It performs movement and
|
||
|
|
// updates its state.
|
||
|
|
func (p *PassiveBehaviour) Tick(e *Ent, tx *world.Tx) *Movement {
|
||
|
|
if p.close {
|
||
|
|
_ = e.Close()
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
m := p.mc.TickMovement(e, e.data.Pos, e.data.Vel, e.data.Rot, tx)
|
||
|
|
e.data.Pos, e.data.Vel = m.pos, m.vel
|
||
|
|
p.fallDistance = math.Max(p.fallDistance-m.dvel[1], 0)
|
||
|
|
|
||
|
|
p.fuse = p.conf.ExistenceDuration - e.Age()
|
||
|
|
|
||
|
|
if p.conf.Tick != nil {
|
||
|
|
p.conf.Tick(e, tx)
|
||
|
|
}
|
||
|
|
|
||
|
|
if p.Fuse()%(time.Second/4) == 0 {
|
||
|
|
for _, v := range tx.Viewers(m.pos) {
|
||
|
|
v.ViewEntityState(e)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if e.Age() > p.conf.ExistenceDuration {
|
||
|
|
p.close = true
|
||
|
|
if p.conf.Expire != nil {
|
||
|
|
p.conf.Expire(e, tx)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return m
|
||
|
|
}
|