Noah Petherbridge
08e65c32b5
* Player character now experiences acceleration and friction when walking around the map! * Actor position and movement had to be converted from int's (render.Point) to float64's to support fine-grained acceleration steps. * Added "physics" package and physics.Vector to be a float64 counterpart for render.Point. Vector is used for uix.Actor.Position() for the sake of movement math. Vector is flattened back to a render.Point for collision purposes, since the levels and hitboxes are pixel-bound. * Refactor the uix.Actor to no longer extend the doodads.Drawing (so it can have a Position that's a Vector instead of a Point). This broke some code that expected `.Doodad` to directly reference the Drawing.Doodad: now you had to refer to it as `a.Drawing.Doodad` which was ugly. Added convenience method .Doodad() for a shortcut. * Moved functions like GetBoundingRect() from doodads package to collision, where it uses its own slimmer Actor interface for just the relevant methods it needs.
137 lines
3.3 KiB
Go
137 lines
3.3 KiB
Go
package uix
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"time"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/log"
|
|
"github.com/robertkrimen/otto"
|
|
)
|
|
|
|
// Animation holds a named animation for a doodad script.
|
|
type Animation struct {
|
|
Name string
|
|
Interval time.Duration
|
|
Layers []int
|
|
|
|
// runtime state variables
|
|
activeLayer int
|
|
nextFrameAt time.Time
|
|
}
|
|
|
|
/*
|
|
TickAnimation advances an animation forward.
|
|
|
|
This method is called by canvas.Loop() only when the actor is currently
|
|
`animating` and their current animation's nextFrameAt has been reached by the
|
|
current time.Now().
|
|
|
|
Returns true when the animation has finished and false if there is still more
|
|
frames left to animate.
|
|
*/
|
|
func (a *Actor) TickAnimation(an *Animation) bool {
|
|
an.activeLayer++
|
|
if an.activeLayer < len(an.Layers) {
|
|
a.ShowLayer(an.Layers[an.activeLayer])
|
|
} else if an.activeLayer >= len(an.Layers) {
|
|
// final layer has been shown for 2 ticks, return that the animation has
|
|
// been concluded.
|
|
return true
|
|
}
|
|
|
|
// Schedule the next frame of animation.
|
|
an.nextFrameAt = time.Now().Add(an.Interval)
|
|
|
|
return false
|
|
}
|
|
|
|
// AddAnimation installs a new animation into the scripting engine for this actor.
|
|
//
|
|
// The layers can be an array of string names or integer indexes.
|
|
func (a *Actor) AddAnimation(name string, interval int64, layers []interface{}) error {
|
|
if len(layers) == 0 {
|
|
return errors.New("no named layers given to AddAnimation()")
|
|
}
|
|
|
|
// Find all the layers by name.
|
|
var indexes []int
|
|
for _, name := range layers {
|
|
switch v := name.(type) {
|
|
case string:
|
|
var found bool
|
|
for i, layer := range a.Doodad().Layers {
|
|
if layer.Name == v {
|
|
indexes = append(indexes, i)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return fmt.Errorf("layer named '%s' not found in doodad", v)
|
|
}
|
|
case int64:
|
|
// TODO: I want to find out if this is ever not an int64 coming from
|
|
// JavaScript.
|
|
if reflect.TypeOf(v).String() != "int64" {
|
|
log.Error("AddAnimation: expected an int64 from JavaScript but got a %s", reflect.TypeOf(v))
|
|
}
|
|
|
|
iv := int(v)
|
|
if iv < len(a.Doodad().Layers) {
|
|
indexes = append(indexes, iv)
|
|
} else {
|
|
return fmt.Errorf("layer numbered '%d' is out of bounds", iv)
|
|
}
|
|
default:
|
|
return fmt.Errorf(
|
|
"invalid type for layer '%+v': should be a string (named layer) "+
|
|
"or int (indexed layer) but was a %s", v, reflect.TypeOf(name))
|
|
}
|
|
}
|
|
|
|
a.animations[name] = &Animation{
|
|
Name: name,
|
|
Interval: time.Duration(interval) * time.Millisecond,
|
|
Layers: indexes,
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PlayAnimation starts an animation and then calls a JavaScript function when
|
|
// the last frame has played out. Set a null function to ignore the callback.
|
|
func (a *Actor) PlayAnimation(name string, callback otto.Value) error {
|
|
anim, ok := a.animations[name]
|
|
if !ok {
|
|
return fmt.Errorf("animation named '%s' not found", name)
|
|
}
|
|
|
|
a.activeAnimation = anim
|
|
a.animationCallback = callback
|
|
|
|
// Show the first layer.
|
|
anim.activeLayer = 0
|
|
anim.nextFrameAt = time.Now().Add(anim.Interval)
|
|
a.ShowLayer(anim.Layers[0])
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsAnimating returns if the current actor is playing an animation.
|
|
func (a *Actor) IsAnimating() bool {
|
|
return a.activeAnimation != nil
|
|
}
|
|
|
|
// StopAnimation stops any current animations.
|
|
func (a *Actor) StopAnimation() {
|
|
if a.activeAnimation == nil {
|
|
return
|
|
}
|
|
|
|
a.activeAnimation = nil
|
|
a.animationCallback = otto.NullValue()
|
|
}
|