1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-12 02:30:43 +00:00

Pause npcs at each target location for a random time (#245)

Loop through neutral animation a random number of repetitions before moving on.  Only run the skill animation once, it is not designed for looping.
This commit is contained in:
nicholas-eden 2019-12-06 20:58:36 -08:00 committed by Tim Sarbin
parent 87e016617a
commit 9a8e16c411
3 changed files with 76 additions and 35 deletions

View File

@ -1,24 +1,20 @@
package d2scene
import (
"image/color"
"github.com/OpenDiablo2/D2Shared/d2data/d2dc6"
"github.com/OpenDiablo2/D2Shared/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine"
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
"github.com/OpenDiablo2/D2Shared/d2common/d2resource"
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
"github.com/OpenDiablo2/D2Shared/d2data/d2dc6"
"github.com/OpenDiablo2/D2Shared/d2helper"
"github.com/OpenDiablo2/OpenDiablo2/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core"
"github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface"
"github.com/OpenDiablo2/OpenDiablo2/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine"
"github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui"
"github.com/hajimehoshi/ebiten"
"image/color"
)
type Game struct {
@ -113,13 +109,14 @@ func (v *Game) Update(tickTime float64) {
if npc.HasPaths &&
npc.AnimatedEntity.LocationX == npc.AnimatedEntity.TargetX &&
npc.AnimatedEntity.LocationY == npc.AnimatedEntity.TargetY {
npc.AnimatedEntity.LocationY == npc.AnimatedEntity.TargetY &&
npc.AnimatedEntity.Wait() {
// If at the target, set target to the next path.
// TODO: pause at target, figure out how to use Path.Action
path := npc.NextPath()
npc.AnimatedEntity.SetTarget(
float64(path.X),
float64(path.Y),
path.Action,
)
}
@ -134,7 +131,7 @@ func (v *Game) Update(tickTime float64) {
mx, my := ebiten.CursorPosition()
px, py := d2helper.ScreenToIso(float64(mx)-v.mapEngine.OffsetX, float64(my)-v.mapEngine.OffsetY)
v.mapEngine.Hero.AnimatedEntity.SetTarget(px*5, py*5)
v.mapEngine.Hero.AnimatedEntity.SetTarget(px*5, py*5, 1)
}
rx, ry := d2helper.IsoToScreen(v.mapEngine.Hero.AnimatedEntity.LocationX/5, v.mapEngine.Hero.AnimatedEntity.LocationY/5, 0, 0)

View File

@ -2,25 +2,18 @@ package d2render
import (
"fmt"
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
"github.com/OpenDiablo2/D2Shared/d2data"
"github.com/OpenDiablo2/D2Shared/d2data/d2cof"
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
"github.com/OpenDiablo2/D2Shared/d2data/d2dcc"
"github.com/OpenDiablo2/D2Shared/d2helper"
"github.com/hajimehoshi/ebiten"
"log"
"math"
"math/rand"
"strings"
"github.com/OpenDiablo2/D2Shared/d2data"
"github.com/OpenDiablo2/D2Shared/d2data/d2cof"
"github.com/OpenDiablo2/D2Shared/d2data/d2dcc"
"github.com/OpenDiablo2/D2Shared/d2helper"
"github.com/OpenDiablo2/D2Shared/d2common/d2interface"
"github.com/OpenDiablo2/D2Shared/d2common/d2enum"
"github.com/OpenDiablo2/D2Shared/d2data/d2datadict"
"github.com/hajimehoshi/ebiten"
)
var DccLayerNames = []string{"HD", "TR", "LG", "RA", "LA", "RH", "LH", "SH", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"}
@ -52,11 +45,13 @@ type AnimatedEntity struct {
currentFrame int
offsetX, offsetY int32
//frameLocations []d2common.Rectangle
object *d2datadict.ObjectLookupRecord
layerCache []LayerCacheEntry
drawOrder [][]d2enum.CompositeType
TargetX float64
TargetY float64
object *d2datadict.ObjectLookupRecord
layerCache []LayerCacheEntry
drawOrder [][]d2enum.CompositeType
TargetX float64
TargetY float64
action int32
repetitions int32
}
// CreateAnimatedEntity creates an instance of AnimatedEntity
@ -157,6 +152,14 @@ func (v *AnimatedEntity) LoadLayer(layer string, fileProvider d2interface.FilePr
return result
}
// If an npc has a path to pause at each location.
// Waits for animation to end and all repetitions to be exhausted.
func (v AnimatedEntity) Wait() bool {
// currentFrame might skip the final frame if framesToAdd doesn't match up,
// bail immediately after the last repetition if that happens.
return v.repetitions < 0 || (v.repetitions == 0 && v.currentFrame >= v.framesToAnimate-1)
}
// Render draws this animated entity onto the target
func (v *AnimatedEntity) Render(target *ebiten.Image, offsetX, offsetY int) {
if v.animationSpeed > 0 {
@ -167,6 +170,7 @@ func (v *AnimatedEntity) Render(target *ebiten.Image, offsetX, offsetY int) {
v.currentFrame += int(math.Floor(framesToAdd))
for v.currentFrame >= v.framesToAnimate {
v.currentFrame -= v.framesToAnimate
v.repetitions = d2helper.MinInt32(-1, v.repetitions-1)
}
}
}
@ -347,20 +351,39 @@ func (v *AnimatedEntity) Step(tickTime float64) {
v.TileY = int(v.LocationY / 5)
if v.LocationX == v.TargetX && v.LocationY == v.TargetY {
if v.animationMode != d2enum.AnimationModeObjectNeutral.String() {
v.SetMode(d2enum.AnimationModeObjectNeutral.String(), v.weaponClass, v.direction)
v.repetitions = 3 + rand.Int31n(5)
newAnimationMode := d2enum.AnimationModeObjectNeutral
// TODO: Figure out what 1-3 are for, 4 is correct.
switch v.action {
case 1:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 2:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 3:
newAnimationMode = d2enum.AnimationModeMonsterNeutral
case 4:
newAnimationMode = d2enum.AnimationModeMonsterSkill1
v.repetitions = 0
}
if v.animationMode != newAnimationMode.String() {
v.SetMode(newAnimationMode.String(), v.weaponClass, v.direction)
}
}
}
// SetTarget sets target coordinates and changes animation based on proximity and direction
func (v *AnimatedEntity) SetTarget(tx, ty float64) {
func (v *AnimatedEntity) SetTarget(tx, ty float64, action int32) {
angle := 359 - d2helper.GetAngleBetween(
v.LocationX,
v.LocationY,
tx,
ty,
)
v.action = action
// TODO: Check if is in town and if is player.
newAnimationMode := d2enum.AnimationModeMonsterWalk.String()
if tx != v.LocationX || ty != v.LocationY {
@ -375,6 +398,10 @@ func (v *AnimatedEntity) SetTarget(tx, ty float64) {
}
func angleToDirection(angle float64, numberOfDirections int) int {
if numberOfDirections == 0 {
return 0
}
degreesPerDirection := 360.0 / float64(numberOfDirections)
offset := 45.0 - (degreesPerDirection / 2)
newDirection := int((angle - offset) / degreesPerDirection)

View File

@ -52,3 +52,20 @@ func TestAngleToDirection_8Directions(t *testing.T) {
}
}
func TestAngleToDirection_1Direction(t *testing.T) {
angle := 0.0
for i := 0; i < 120; i++ {
assert.Equal(t, 0, angleToDirection(angle, 1))
angle += 3
}
}
func TestAngleToDirection_0Directions(t *testing.T) {
angle := 0.0
for i := 0; i < 120; i++ {
assert.Equal(t, 0, angleToDirection(angle, 0))
angle += 3
}
}