|
|
|
@ -8,6 +8,8 @@ import (
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const walkThreshold = 4.00
|
|
|
|
|
|
|
|
|
|
type MovementSystem struct {
|
|
|
|
|
Position *component.Position
|
|
|
|
|
|
|
|
|
@ -31,29 +33,81 @@ func (s *MovementSystem) Update(e gohan.Entity) error {
|
|
|
|
|
|
|
|
|
|
if position.Body != nil {
|
|
|
|
|
pos := position.Body.Position()
|
|
|
|
|
vel := position.Body.Velocity()
|
|
|
|
|
position.X, position.Y = pos.X, pos.Y
|
|
|
|
|
|
|
|
|
|
if s.Sprite != nil {
|
|
|
|
|
vel := position.Body.Velocity()
|
|
|
|
|
if e == world.Player && s.Sprite != nil {
|
|
|
|
|
if world.MagnetActive {
|
|
|
|
|
if world.MagnetDirection == world.MagnetizeDown || world.MagnetDirection == world.MagnetizeLeft || world.MagnetDirection == world.MagnetizeUp || world.MagnetDirection == world.MagnetizeRight {
|
|
|
|
|
world.PlayerDirection = world.MagnetDirection
|
|
|
|
|
} else if world.MagnetDirection == world.MagnetizeDownLeft || world.MagnetDirection == world.MagnetizeUpLeft {
|
|
|
|
|
world.PlayerDirection = world.MagnetizeLeft
|
|
|
|
|
} else if world.MagnetDirection == world.MagnetizeDownRight || world.MagnetDirection == world.MagnetizeUpRight {
|
|
|
|
|
world.PlayerDirection = world.MagnetizeRight
|
|
|
|
|
} else {
|
|
|
|
|
panic(world.MagnetDirection)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*if world.MagnetDirection == world.MagnetizeDownRight && (world.PlayerNormal.Y > -0.5 || world.PlayerNormal.Y < 0.5) {
|
|
|
|
|
faceDirection = world.MagnetizeDown
|
|
|
|
|
}*/
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
|
|
// Reset to MagnetizeDown values.
|
|
|
|
|
s.Sprite.HorizontalFlip = !world.LastWalkDirL
|
|
|
|
|
s.Sprite.VerticalFlip = false
|
|
|
|
|
s.Sprite.OffsetX = 0
|
|
|
|
|
s.Sprite.OffsetY = -17
|
|
|
|
|
|
|
|
|
|
idleFrames := asset.PlayerIdleFrames
|
|
|
|
|
walkFrames := asset.PlayerWalkFrames
|
|
|
|
|
switch world.PlayerDirection {
|
|
|
|
|
case world.MagnetizeDown:
|
|
|
|
|
// Already set.
|
|
|
|
|
case world.MagnetizeLeft:
|
|
|
|
|
if vel.X <= walkThreshold {
|
|
|
|
|
s.Sprite.HorizontalFlip = false
|
|
|
|
|
s.Sprite.VerticalFlip = world.LastWalkDirU
|
|
|
|
|
idleFrames = asset.PlayerIdleFramesRot90
|
|
|
|
|
walkFrames = asset.PlayerWalkFramesRot90
|
|
|
|
|
s.Sprite.OffsetX = -1
|
|
|
|
|
s.Sprite.OffsetY = -8
|
|
|
|
|
} else {
|
|
|
|
|
// Magnetize down.
|
|
|
|
|
}
|
|
|
|
|
case world.MagnetizeUp:
|
|
|
|
|
s.Sprite.HorizontalFlip = !world.LastWalkDirL
|
|
|
|
|
s.Sprite.VerticalFlip = true
|
|
|
|
|
s.Sprite.OffsetY = 1
|
|
|
|
|
case world.MagnetizeRight:
|
|
|
|
|
if vel.X >= -walkThreshold {
|
|
|
|
|
s.Sprite.HorizontalFlip = true
|
|
|
|
|
s.Sprite.VerticalFlip = world.LastWalkDirU
|
|
|
|
|
idleFrames = asset.PlayerIdleFramesRot90
|
|
|
|
|
walkFrames = asset.PlayerWalkFramesRot90
|
|
|
|
|
s.Sprite.OffsetX = 1
|
|
|
|
|
s.Sprite.OffsetY = -8
|
|
|
|
|
} else {
|
|
|
|
|
// Magnetize down.
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
panic("DEFAULT")
|
|
|
|
|
}
|
|
|
|
|
world.PlayerIdleFrames = idleFrames
|
|
|
|
|
world.PlayerWalkFrames = walkFrames
|
|
|
|
|
|
|
|
|
|
const walkThreshold = 4.00
|
|
|
|
|
walking := vel.X < -walkThreshold || vel.X > walkThreshold
|
|
|
|
|
moveX := vel.X < -walkThreshold || vel.X > walkThreshold
|
|
|
|
|
moveY := vel.Y < -walkThreshold || vel.Y > walkThreshold
|
|
|
|
|
walking := (ebiten.IsKeyPressed(ebiten.KeyW) && moveY) || (ebiten.IsKeyPressed(ebiten.KeyS) && moveY) || (ebiten.IsKeyPressed(ebiten.KeyA) && moveX) || (ebiten.IsKeyPressed(ebiten.KeyD) && moveX)
|
|
|
|
|
if walking {
|
|
|
|
|
s.Sprite.Frames = walkFrames
|
|
|
|
|
} else {
|
|
|
|
|
s.Sprite.Frames = idleFrames
|
|
|
|
|
}
|
|
|
|
|
s.Sprite.Image = s.Sprite.Frames[s.Sprite.Frame]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil // TODO
|
|
|
|
|
s.handleLevelCollisions(e)
|
|
|
|
|
s.handleScreenCollisions(e)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -65,200 +119,6 @@ func (_ *MovementSystem) Draw(_ gohan.Entity, _ *ebiten.Image) error {
|
|
|
|
|
return gohan.ErrUnregister
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleLevelCollisions handles collisions between the player and the level.
|
|
|
|
|
func (s *MovementSystem) handleLevelCollisions(e gohan.Entity) {
|
|
|
|
|
/*
|
|
|
|
|
vx, vy := velocity.X, velocity.Y
|
|
|
|
|
if e == world.Player && world.Debug != 0 && ebiten.IsKeyPressed(ebiten.KeyShift) {
|
|
|
|
|
vx, vy = vx*2, vy*2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check hazard collisions.
|
|
|
|
|
playerRect := image.Rect(int(position.X), int(position.Y), int(position.X+world.PlayerWidth), int(position.Y+world.PlayerHeight))
|
|
|
|
|
for i := range world.HazardRects {
|
|
|
|
|
if playerRect.Overlaps(world.HazardRects[i]) {
|
|
|
|
|
world.SetGameOver(false)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check velocity collisions.
|
|
|
|
|
const gravityCollisionCheck = 0.1
|
|
|
|
|
newX, newY := position.X+vx, position.Y+vy
|
|
|
|
|
playerRectX := image.Rect(int(newX), int(position.Y), int(newX+world.PlayerWidth), int(position.Y+world.PlayerHeight))
|
|
|
|
|
playerRectY := image.Rect(int(position.X), int(newY), int(position.X+world.PlayerWidth), int(newY+world.PlayerHeight))
|
|
|
|
|
playerRectXY := image.Rect(int(newX), int(newY), int(newX+world.PlayerWidth), int(newY+world.PlayerHeight))
|
|
|
|
|
playerRectG := image.Rect(int(position.X), int(position.Y+gravityCollisionCheck), int(position.X+world.PlayerWidth), int(position.Y+gravityCollisionCheck+world.PlayerHeight))
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
collideX bool
|
|
|
|
|
collideY bool
|
|
|
|
|
collideXY bool
|
|
|
|
|
collideG bool
|
|
|
|
|
)
|
|
|
|
|
for i := range world.MetalRects {
|
|
|
|
|
if !collideX && world.MetalRects[i].Overlaps(playerRectX) {
|
|
|
|
|
collideX = true
|
|
|
|
|
}
|
|
|
|
|
if !collideY && world.MetalRects[i].Overlaps(playerRectY) {
|
|
|
|
|
collideY = true
|
|
|
|
|
}
|
|
|
|
|
if !collideXY && world.MetalRects[i].Overlaps(playerRectXY) {
|
|
|
|
|
collideXY = true
|
|
|
|
|
}
|
|
|
|
|
if !collideG && world.MetalRects[i].Overlaps(playerRectG) {
|
|
|
|
|
collideG = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if collideXY || collideX || collideY {
|
|
|
|
|
if collideY {
|
|
|
|
|
if !collideX {
|
|
|
|
|
position.X = position.X + vx
|
|
|
|
|
} else {
|
|
|
|
|
s.Velocity.X = 0
|
|
|
|
|
}
|
|
|
|
|
s.Velocity.Y = 0
|
|
|
|
|
} else if collideX {
|
|
|
|
|
if !collideY {
|
|
|
|
|
position.Y = position.Y + vy
|
|
|
|
|
} else {
|
|
|
|
|
s.Velocity.Y = 0
|
|
|
|
|
}
|
|
|
|
|
s.Velocity.X = 0
|
|
|
|
|
} else {
|
|
|
|
|
s.Velocity.X = 0
|
|
|
|
|
s.Velocity.Y = 0
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
position.X, position.Y = position.X+vx, position.Y+vy
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
world.MagnetActive = ebiten.IsKeyPressed(ebiten.KeySpace)
|
|
|
|
|
|
|
|
|
|
const gravitySize = 0.02
|
|
|
|
|
gravityX, gravityY := 0.0, gravitySize
|
|
|
|
|
|
|
|
|
|
// Magnetize against nearest wall.
|
|
|
|
|
var clinging bool
|
|
|
|
|
if world.MagnetActive && !collideG {
|
|
|
|
|
wallThreshold := 16.0 // TODO cling when added space is used
|
|
|
|
|
|
|
|
|
|
playerRectL := image.Rect(int(position.X-wallThreshold), int(position.Y), int(position.X-wallThreshold+world.PlayerWidth), int(position.Y+world.PlayerHeight))
|
|
|
|
|
playerRectR := image.Rect(int(position.X+wallThreshold), int(position.Y), int(position.X+wallThreshold+world.PlayerWidth), int(position.Y+world.PlayerHeight))
|
|
|
|
|
playerRectU := image.Rect(int(position.X), int(position.Y-wallThreshold), int(position.X+world.PlayerWidth), int(position.Y-wallThreshold+world.PlayerHeight))
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
collideL bool
|
|
|
|
|
collideR bool
|
|
|
|
|
collideU bool
|
|
|
|
|
)
|
|
|
|
|
for i := range world.MetalRects {
|
|
|
|
|
if !collideL && world.MetalRects[i].Overlaps(playerRectL) {
|
|
|
|
|
collideL = true
|
|
|
|
|
}
|
|
|
|
|
if !collideR && world.MetalRects[i].Overlaps(playerRectR) {
|
|
|
|
|
collideR = true
|
|
|
|
|
}
|
|
|
|
|
if !collideU && world.MetalRects[i].Overlaps(playerRectU) {
|
|
|
|
|
collideU = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if collideL || collideR || collideU {
|
|
|
|
|
log.Println("ground collision", collideG)
|
|
|
|
|
log.Println("cling", velocity.Y)
|
|
|
|
|
|
|
|
|
|
clinging = true
|
|
|
|
|
|
|
|
|
|
if collideL {
|
|
|
|
|
gravityX, gravityY = -gravitySize, 0.00
|
|
|
|
|
} else if collideR {
|
|
|
|
|
gravityX, gravityY = gravitySize, 0.00
|
|
|
|
|
} else if collideU {
|
|
|
|
|
gravityX, gravityY = 0.00, -gravitySize
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
world.Clinging = clinging
|
|
|
|
|
|
|
|
|
|
dampenMultiplierX, dampenMultiplierY := 3.0, 1.0
|
|
|
|
|
if gravityX != 0 {
|
|
|
|
|
dampenMultiplierX, dampenMultiplierY = 1, 3
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const dampen = 100
|
|
|
|
|
if s.Velocity.Y < 0 {
|
|
|
|
|
s.Velocity.Y -= s.Velocity.Y / (dampen / dampenMultiplierY)
|
|
|
|
|
} else if s.Velocity.Y > 0 {
|
|
|
|
|
s.Velocity.Y -= s.Velocity.Y / (dampen / dampenMultiplierY)
|
|
|
|
|
}
|
|
|
|
|
if s.Velocity.X < 0 {
|
|
|
|
|
s.Velocity.X -= s.Velocity.X / (dampen / dampenMultiplierX)
|
|
|
|
|
} else if s.Velocity.X > 0 {
|
|
|
|
|
s.Velocity.X -= s.Velocity.X / (dampen / dampenMultiplierX)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if s.Velocity.X > -0.01 && s.Velocity.X < 0.01 {
|
|
|
|
|
s.Velocity.X = 0
|
|
|
|
|
}
|
|
|
|
|
if s.Velocity.Y > -0.01 && s.Velocity.Y < 0.01 {
|
|
|
|
|
s.Velocity.Y = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply gravity.
|
|
|
|
|
const maxSpeed = 8.0
|
|
|
|
|
s.Velocity.X, s.Velocity.Y = s.Velocity.X+gravityX, s.Velocity.Y+gravityY
|
|
|
|
|
if s.Velocity.X < -maxSpeed {
|
|
|
|
|
s.Velocity.X = -maxSpeed
|
|
|
|
|
} else if s.Velocity.X > maxSpeed {
|
|
|
|
|
s.Velocity.X = maxSpeed
|
|
|
|
|
}
|
|
|
|
|
if s.Velocity.Y < -maxSpeed {
|
|
|
|
|
s.Velocity.Y = -maxSpeed
|
|
|
|
|
} else if s.Velocity.Y > maxSpeed {
|
|
|
|
|
s.Velocity.Y = maxSpeed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update player sprite.
|
|
|
|
|
s.Sprite.HorizontalFlip = world.LastWalkDirL
|
|
|
|
|
s.Sprite.VerticalFlip = false
|
|
|
|
|
|
|
|
|
|
offsetY := -12.0
|
|
|
|
|
|
|
|
|
|
idleFrames := asset.PlayerIdleFrames
|
|
|
|
|
walkFrames := asset.PlayerWalkFrames
|
|
|
|
|
if gravityX < 0 {
|
|
|
|
|
idleFrames = asset.PlayerIdleFramesRot90
|
|
|
|
|
walkFrames = asset.PlayerWalkFramesRot90
|
|
|
|
|
s.Sprite.VerticalFlip = world.LastWalkDirU
|
|
|
|
|
s.Sprite.HorizontalFlip = false
|
|
|
|
|
|
|
|
|
|
offsetY = 0
|
|
|
|
|
} else if gravityX > 0 {
|
|
|
|
|
idleFrames = asset.PlayerIdleFramesRot90
|
|
|
|
|
walkFrames = asset.PlayerWalkFramesRot90
|
|
|
|
|
s.Sprite.VerticalFlip = world.LastWalkDirU
|
|
|
|
|
s.Sprite.HorizontalFlip = true
|
|
|
|
|
|
|
|
|
|
offsetY = 0
|
|
|
|
|
} else if gravityY < 0 {
|
|
|
|
|
s.Sprite.VerticalFlip = true
|
|
|
|
|
|
|
|
|
|
offsetY = 11
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.Sprite.OffsetY = offsetY
|
|
|
|
|
|
|
|
|
|
const walkThreshold = 0.04
|
|
|
|
|
walking := (gravityY != 0 && (s.Velocity.X < -walkThreshold || s.Velocity.X > walkThreshold)) || (gravityX != 0 && (s.Velocity.Y < -walkThreshold || s.Velocity.Y > walkThreshold))
|
|
|
|
|
if walking {
|
|
|
|
|
s.Sprite.Frames = walkFrames
|
|
|
|
|
} else {
|
|
|
|
|
s.Sprite.Frames = idleFrames
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleScreenCollisions forces the player to remain within the screen bounds.
|
|
|
|
|
func (s *MovementSystem) handleScreenCollisions(e gohan.Entity) {
|
|
|
|
|
if e != world.Player {
|
|
|
|
@ -266,6 +126,8 @@ func (s *MovementSystem) handleScreenCollisions(e gohan.Entity) {
|
|
|
|
|
}
|
|
|
|
|
position := s.Position
|
|
|
|
|
|
|
|
|
|
// TODO check if in screen transition area, automatically scroll based on current screen position
|
|
|
|
|
|
|
|
|
|
screenX, screenY := s.levelCoordinatesToScreen(position.X, position.Y)
|
|
|
|
|
if screenX < 0 {
|
|
|
|
|
diff := screenX
|
|
|
|
|