diff --git a/component/sprite.go b/component/sprite.go index 4d40260..1d36e1b 100644 --- a/component/sprite.go +++ b/component/sprite.go @@ -2,11 +2,29 @@ package component import ( "github.com/hajimehoshi/ebiten/v2" + "github.com/kensonjohnson/roguelike-game-go/config" "github.com/yohamta/donburi" ) type SpriteData struct { - Image *ebiten.Image + Image *ebiten.Image + OffestX int // The direction the sprite is moving FROM, -1, 0, 1 + OffestY int // The direction the sprite is moving FROM, -1, 0, 1 + AnimationFrame int + progress float64 // The progress of the current animation frame from 0 to 1 + Animating bool +} + +// Returns the X and Y offset for the current animation frame. TODO: Add +// support for animation frames. +func (sd *SpriteData) GetAnimationStep() (float64, float64) { + offsetX := (1 - sd.progress) * config.TileWidth * float64(sd.OffestX) + offsetY := (1 - sd.progress) * config.TileHeight * float64(sd.OffestY) + return offsetX, offsetY +} + +func (sd *SpriteData) SetProgress(progress float64) { + sd.progress = progress } var Sprite = donburi.NewComponentType[SpriteData]() diff --git a/system/action/monster.go b/system/action/monster.go index 22a102f..7ecf922 100644 --- a/system/action/monster.go +++ b/system/action/monster.go @@ -20,7 +20,7 @@ func TakeMonsterAction(ecs *ecs.ECS) { archetype.MonsterTag.Each(ecs.World, func(entry *donburi.Entry) { position := component.Position.Get(entry) - + sprite := component.Sprite.Get(entry) monsterVision := component.Fov.Get(entry).VisibleTiles // Check if the monster can see the player @@ -37,8 +37,12 @@ func TakeMonsterAction(ecs *ecs.ECS) { // Update the tile this monster is blocking level.GetFromXY(position.X, position.Y).Blocked = false nextTile.Blocked = true + sprite.OffestX = position.X - path[1].X + sprite.OffestY = position.Y - path[1].Y position.X = path[1].X position.Y = path[1].Y + sprite.Animating = true + sprite.SetProgress(0) // Since the monster moved, update the field of view monsterVision.Compute(level, position.X, position.Y, 8) } diff --git a/system/action/player.go b/system/action/player.go index 84d9d8a..996051d 100644 --- a/system/action/player.go +++ b/system/action/player.go @@ -14,15 +14,6 @@ import ( func TakePlayerAction(ecs *ecs.ECS) bool { turnTaken := false - // Grab current level - levelEntry := archetype.LevelTag.MustFirst(ecs.World) - level := component.Level.Get(levelEntry) - - // Grab player data - playerEntry := archetype.PlayerTag.MustFirst(ecs.World) - position := component.Position.Get(playerEntry) - vision := component.Fov.Get(playerEntry) - // Capture any keypresses we care about moveX := 0 moveY := 0 @@ -55,6 +46,16 @@ func TakePlayerAction(ecs *ecs.ECS) bool { return false } + // Grab current level + levelEntry := archetype.LevelTag.MustFirst(ecs.World) + level := component.Level.Get(levelEntry) + + // Grab player data + playerEntry := archetype.PlayerTag.MustFirst(ecs.World) + position := component.Position.Get(playerEntry) + sprite := component.Sprite.Get(playerEntry) + vision := component.Fov.Get(playerEntry) + // TODO: Update so diagonal movement consumes two turns // Attempt to move tile := level.GetFromXY(position.X+moveX, position.Y+moveY) @@ -66,6 +67,10 @@ func TakePlayerAction(ecs *ecs.ECS) bool { // Update the player's position position.X += moveX position.Y += moveY + sprite.OffestX = -moveX + sprite.OffestY = -moveY + sprite.Animating = true + sprite.SetProgress(0) // Update the player's field of view vision.VisibleTiles.Compute(level, position.X, position.Y, 8) // Update any discoverable entities diff --git a/system/render.go b/system/render.go index b67ea18..aae0c3c 100644 --- a/system/render.go +++ b/system/render.go @@ -36,7 +36,12 @@ func (r *render) Draw(ecs *ecs.ECS, screen *ebiten.Image) { if playerVision.IsVisible(position.X, position.Y) { camera.CamImageOptions.GeoM.Reset() - camera.CamImageOptions.GeoM.Translate(float64(position.X*config.TileWidth), float64(position.Y*config.TileHeight)) + if sprite.Animating { + offsetX, offsetY := sprite.GetAnimationStep() + camera.CamImageOptions.GeoM.Translate(float64(position.X*config.TileWidth)+offsetX, float64(position.Y*config.TileHeight)+offsetY) + } else { + camera.CamImageOptions.GeoM.Translate(float64(position.X*config.TileWidth), float64(position.Y*config.TileHeight)) + } camera.MainCamera.Draw(sprite.Image, camera.CamImageOptions, screen) } }) diff --git a/system/turn.go b/system/turn.go index 44b9314..0003eba 100644 --- a/system/turn.go +++ b/system/turn.go @@ -28,9 +28,9 @@ const ( ) func (td *TurnData) Update(ecs *ecs.ECS) { - td.TurnCounter++ if td.TurnState == BeforePlayerAction { + td.TurnCounter++ // Check if player survived the last cycle of monster turns entry := archetype.PlayerTag.MustFirst(ecs.World) playerHealth := component.Health.Get(entry) @@ -52,16 +52,30 @@ func (td *TurnData) Update(ecs *ecs.ECS) { } }) - level.Redraw = true + component.Sprite.Each(ecs.World, func(entry *donburi.Entry) { + sprite := component.Sprite.Get(entry) + sprite.SetProgress(float64(td.TurnCounter) / 12) + }) - td.progressTurnState() + if td.TurnCounter > 12 { + // Reset the progress of all sprites + component.Sprite.Each(ecs.World, func(entry *donburi.Entry) { + sprite := component.Sprite.Get(entry) + sprite.SetProgress(0) + sprite.Animating = false + sprite.OffestX = 0 + sprite.OffestY = 0 + }) + + level.Redraw = true + td.progressTurnState() + td.resetCounter() + } } - if td.TurnState == PlayerTurn && td.TurnCounter > 12 { - turnTaken := action.TakePlayerAction(ecs) - if turnTaken { + if td.TurnState == PlayerTurn { + if turnTaken := action.TakePlayerAction(ecs); turnTaken { td.progressTurnState() - td.resetCounter() } }