diff --git a/server/entity/animation/animation.go b/server/entity/animation/animation.go new file mode 100644 index 000000000..56710ee68 --- /dev/null +++ b/server/entity/animation/animation.go @@ -0,0 +1,61 @@ +package animation + +// Animation represents an animation & controller that may be attached to an entity. +// Animations and controllers must be defined in a resource pack +type Animation struct { + name, state, controller string + stopCondition string +} + +// New returns a new animation that can be attached to an entity. By default no controller or state is sent to the viewer. +// To add a state and controller use WithController and WithState respectively. +func New(animationName string) Animation { + return Animation{ + name: animationName, + state: "", + controller: "", + stopCondition: "", + } +} + +// Name returns the name of the animation to be played +func (a Animation) Name() string { + return a.name +} + +// WithController sets the controller with the specified state. +// The controller must be added in a resource pack +func (a Animation) WithController(controller string) Animation { + a.controller = controller + return a +} + +// Controller returns the name of the controller being used. Controller returns an empty string if +// no controller was previously set +func (a Animation) Controller() string { + return a.controller +} + +// WithState sets the state to transition to as defined in the controller. +func (a Animation) WithState(state string) Animation { + a.state = state + return a +} + +// State returns the current state being played. State returns an empty string if +// no controller was previously set +func (a Animation) State() string { + return a.state +} + +// WithStopCondition takes the molang expression and stops the animation if the query passes. +func (a Animation) WithStopCondition(condition string) Animation { + a.stopCondition = condition + return a +} + +// StopCondition returns the stop condition. StopCondition returns an empty string if +// no molang expression was set +func (a Animation) StopCondition() string { + return a.stopCondition +} diff --git a/server/session/world.go b/server/session/world.go index 4624fb85d..97249f5e3 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -1,12 +1,14 @@ package session import ( - "github.com/df-mc/dragonfly/server/entity/effect" "image/color" "math/rand" "strings" "time" + "github.com/df-mc/dragonfly/server/entity/animation" + "github.com/df-mc/dragonfly/server/entity/effect" + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/entity" @@ -915,9 +917,12 @@ func (s *Session) ViewEntityState(e world.Entity) { } // ViewEntityAnimation ... -func (s *Session) ViewEntityAnimation(e world.Entity, animationName string) { +func (s *Session) ViewEntityAnimation(e world.Entity, animation animation.Animation) { s.writePacket(&packet.AnimateEntity{ - Animation: animationName, + Animation: animation.Name(), + NextState: animation.State(), + StopCondition: animation.StopCondition(), + Controller: animation.Controller(), EntityRuntimeIDs: []uint64{ s.entityRuntimeID(e), }, diff --git a/server/world/viewer.go b/server/world/viewer.go index eae4c8562..f112577cb 100644 --- a/server/world/viewer.go +++ b/server/world/viewer.go @@ -1,11 +1,13 @@ package world import ( + "time" + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/entity/animation" "github.com/df-mc/dragonfly/server/world/chunk" "github.com/go-gl/mathgl/mgl64" "github.com/google/uuid" - "time" ) // Viewer is a viewer in the world. It can view changes that are made in the world, such as the addition of @@ -48,7 +50,7 @@ type Viewer interface { // physical appearance, for example when sprinting. ViewEntityState(e Entity) // ViewEntityAnimation starts viewing an animation performed by an entity. The animation has to be from a resource pack. - ViewEntityAnimation(e Entity, animationName string) + ViewEntityAnimation(e Entity, animation animation.Animation) // ViewParticle views a particle spawned at a given position in the world. It is called when a particle, // for example a block breaking particle, is spawned near the player. ViewParticle(pos mgl64.Vec3, p Particle) @@ -89,7 +91,7 @@ func (NopViewer) ViewEntityItems(Entity) {} func (NopViewer) ViewEntityArmour(Entity) {} func (NopViewer) ViewEntityAction(Entity, EntityAction) {} func (NopViewer) ViewEntityState(Entity) {} -func (NopViewer) ViewEntityAnimation(Entity, string) {} +func (NopViewer) ViewEntityAnimation(Entity, animation.Animation) {} func (NopViewer) ViewParticle(mgl64.Vec3, Particle) {} func (NopViewer) ViewSound(mgl64.Vec3, Sound) {} func (NopViewer) ViewBlockUpdate(cube.Pos, Block, int) {} diff --git a/server/world/world.go b/server/world/world.go index f54151122..b4b4eee65 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -2,13 +2,15 @@ package world import ( "errors" - "github.com/df-mc/goleveldb/leveldb" "math/rand" "sync" "time" + "github.com/df-mc/goleveldb/leveldb" + "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/entity/animation" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/internal/sliceutil" "github.com/df-mc/dragonfly/server/world/chunk" @@ -643,6 +645,19 @@ func (w *World) AddParticle(pos mgl64.Vec3, p Particle) { } } +// AnimateEntity will start an animation for the specified entity. Viewers that are viewing the entity will be +// played the animation. +func (w *World) AnimateEntity(e Entity, animation animation.Animation) { + // Ignore if no animation name has been given + if animation.Name() == "" { + return + } + + for _, v := range w.Viewers(e.Position()) { + v.ViewEntityAnimation(e, animation) + } +} + // PlaySound plays a sound at a specific position in the world. Viewers of that position will be able to hear // the sound if they're close enough. func (w *World) PlaySound(pos mgl64.Vec3, s Sound) {