You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
8.9 KiB
Go
280 lines
8.9 KiB
Go
package system
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
|
|
"code.rocketnine.space/tslocum/doctorlectro/asset"
|
|
"code.rocketnine.space/tslocum/doctorlectro/component"
|
|
"code.rocketnine.space/tslocum/doctorlectro/world"
|
|
"code.rocketnine.space/tslocum/gohan"
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
)
|
|
|
|
const walkThreshold = 4.00
|
|
|
|
type MovementSystem struct {
|
|
Position *component.Position
|
|
Velocity *component.Velocity
|
|
Sprite *component.Sprite
|
|
}
|
|
|
|
func NewMovementSystem() *MovementSystem {
|
|
return &MovementSystem{}
|
|
}
|
|
|
|
func (s *MovementSystem) Update(e gohan.Entity) error {
|
|
if world.GameOver && e == world.Player {
|
|
return nil
|
|
}
|
|
|
|
if world.GameOverTicks != 0 {
|
|
return nil
|
|
}
|
|
|
|
position := s.Position
|
|
|
|
if position.Body != nil {
|
|
pos := position.Body.Position()
|
|
vel := position.Body.Velocity()
|
|
position.X, position.Y = pos.X, pos.Y
|
|
|
|
if e == world.Player {
|
|
const threshold = 1
|
|
if world.PlayerNormal.Y <= -threshold {
|
|
world.PlayerDirection = world.MagnetizeDown
|
|
} else if world.MagnetActive && world.PlayerNormal.Y >= threshold {
|
|
world.PlayerDirection = world.MagnetizeUp
|
|
} else if world.MagnetActive && world.PlayerNormal.X <= -threshold {
|
|
world.PlayerDirection = world.MagnetizeRight
|
|
} else if world.MagnetActive && world.PlayerNormal.X >= threshold {
|
|
world.PlayerDirection = world.MagnetizeLeft
|
|
} else if world.MagnetActive && false {
|
|
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 {
|
|
if world.PlayerNormal.X != 0 {
|
|
world.PlayerDirection = world.MagnetizeLeft
|
|
} else {
|
|
world.PlayerDirection = world.MagnetizeDown
|
|
}
|
|
} else if world.MagnetDirection == world.MagnetizeDownRight {
|
|
if world.PlayerNormal.X != 0 {
|
|
world.PlayerDirection = world.MagnetizeRight
|
|
} else {
|
|
world.PlayerDirection = world.MagnetizeDown
|
|
}
|
|
} else if world.MagnetDirection == world.MagnetizeUpLeft {
|
|
if world.PlayerNormal.X != 0 {
|
|
world.PlayerDirection = world.MagnetizeLeft
|
|
} else {
|
|
world.PlayerDirection = world.MagnetizeUp
|
|
}
|
|
} else if world.MagnetDirection == world.MagnetizeUpRight {
|
|
if world.PlayerNormal.X != 0 {
|
|
world.PlayerDirection = world.MagnetizeRight
|
|
} else {
|
|
world.PlayerDirection = world.MagnetizeUp
|
|
}
|
|
} else {
|
|
panic(world.MagnetDirection)
|
|
}
|
|
}
|
|
|
|
// Reset to MagnetizeDown values.
|
|
s.Sprite.HorizontalFlip = !world.LastWalkDirL
|
|
s.Sprite.VerticalFlip = false
|
|
s.Sprite.OffsetX = -8
|
|
s.Sprite.OffsetY = -18
|
|
|
|
magnetizeUp := func() {
|
|
s.Sprite.HorizontalFlip = !world.LastWalkDirL
|
|
s.Sprite.VerticalFlip = true
|
|
s.Sprite.OffsetY = 2
|
|
}
|
|
|
|
idleFrames := asset.PlayerIdleFrames
|
|
walkFrames := asset.PlayerWalkFrames
|
|
switch world.PlayerDirection {
|
|
case world.MagnetizeDown:
|
|
// Already set.
|
|
case world.MagnetizeLeft:
|
|
if vel.X <= walkThreshold || true {
|
|
s.Sprite.HorizontalFlip = false
|
|
s.Sprite.VerticalFlip = world.LastWalkDirU
|
|
idleFrames = asset.PlayerIdleFramesRot90
|
|
walkFrames = asset.PlayerWalkFramesRot90
|
|
s.Sprite.OffsetX = -8
|
|
s.Sprite.OffsetY = -8
|
|
} else if world.MagnetActive && (world.MagnetDirection == world.MagnetizeUpLeft || world.MagnetDirection == world.MagnetizeUpRight) {
|
|
magnetizeUp()
|
|
} else {
|
|
// Magnetize down.
|
|
}
|
|
case world.MagnetizeUp:
|
|
magnetizeUp()
|
|
case world.MagnetizeRight:
|
|
if vel.X >= -walkThreshold || true {
|
|
s.Sprite.HorizontalFlip = true
|
|
s.Sprite.VerticalFlip = world.LastWalkDirU
|
|
idleFrames = asset.PlayerIdleFramesRot90
|
|
walkFrames = asset.PlayerWalkFramesRot90
|
|
s.Sprite.OffsetX = -8
|
|
s.Sprite.OffsetY = -8
|
|
} else if world.MagnetActive && (world.MagnetDirection == world.MagnetizeUpLeft || world.MagnetDirection == world.MagnetizeUpRight) {
|
|
magnetizeUp()
|
|
} else {
|
|
// Magnetize down.
|
|
}
|
|
default:
|
|
panic("DEFAULT")
|
|
}
|
|
world.PlayerIdleFrames = idleFrames
|
|
world.PlayerWalkFrames = walkFrames
|
|
|
|
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]
|
|
|
|
normalize := func(f float64) float64 {
|
|
if f > 0 && f < 0.1 {
|
|
f = 0.1
|
|
} else if f < 0 && f > -0.1 {
|
|
f = -0.1
|
|
}
|
|
return f
|
|
}
|
|
|
|
mapScreenWidth := 60 * TileWidth
|
|
mapScreenHeight := 34 * TileWidth
|
|
targetX, targetY := 16+(int(position.X-world.PlayerWidth/2)/mapScreenWidth)*mapScreenWidth, 16+(int(position.Y-world.PlayerWidth/2)/mapScreenHeight)*mapScreenHeight
|
|
|
|
const targetThreshold = 2.0
|
|
const targetDiv = 42.0
|
|
deltaX, deltaY := world.CamX-world.CamTargetX, world.CamY-world.CamTargetY
|
|
world.CamTargetX, world.CamTargetY = float64(targetX), float64(targetY)
|
|
if (world.CamX == 0 && world.CamY == 0) || (deltaX > targetThreshold && deltaX < targetThreshold) || (deltaY > targetThreshold && deltaY < targetThreshold) {
|
|
world.CamX, world.CamY = world.CamTargetX, world.CamTargetY
|
|
} else {
|
|
world.CamX += normalize((world.CamTargetX - world.CamX) / targetDiv)
|
|
world.CamY += normalize((world.CamTargetY - world.CamY) / targetDiv)
|
|
}
|
|
|
|
// Remove activated trigger.
|
|
if world.RemoveTrigger != -1 {
|
|
i := world.RemoveTrigger
|
|
if world.TriggerActions[i] != world.TriggerData {
|
|
world.TriggerEntities[i].With(func(s *component.Sprite) {
|
|
s.Image = world.TileImages[world.TileScreenOff]
|
|
})
|
|
}
|
|
world.TriggerRects = append(world.TriggerRects[:i], world.TriggerRects[i+1:]...)
|
|
world.TriggerActions = append(world.TriggerActions[:i], world.TriggerActions[i+1:]...)
|
|
world.TriggerEntities = append(world.TriggerEntities[:i], world.TriggerEntities[i+1:]...)
|
|
world.RemoveTrigger = -1
|
|
}
|
|
|
|
// Check trigger rects.
|
|
playerRect := world.BBToLevelRect(world.PlayerShape.BB())
|
|
for i := range world.TriggerRects {
|
|
if playerRect.Overlaps(world.TriggerRects[i]) {
|
|
action := world.TriggerActions[i]
|
|
var message string
|
|
switch action {
|
|
case world.TriggerJump:
|
|
world.CanJump = true
|
|
message = "<J> TO JUMP."
|
|
case world.TriggerMagnetize:
|
|
world.CanMagnetize = true
|
|
message = "<SPACE> TO MAGNETIZE."
|
|
case world.TriggerFire:
|
|
world.CanFire = true
|
|
message = "<K> TO FIRE."
|
|
case world.TriggerSwitch:
|
|
world.OpenSwitch()
|
|
message = "SECURITY BEAM DEACTIVATED."
|
|
case world.TriggerData:
|
|
message = "ALL SYSTEMS ARE OPERATIONAL.\n\nKEEP AWAY FROM MAGNETIC FIELDS."
|
|
default:
|
|
panic(fmt.Sprintf("unknown trigger action %d", action))
|
|
}
|
|
// Notify player of new ability.
|
|
if action == world.TriggerSwitch {
|
|
world.SetMessage(fmt.Sprintf("ACCESS GRANTED!\n\n%s", message))
|
|
} else if action == world.TriggerData {
|
|
world.SetMessage(fmt.Sprintf("SUPERWEAPON CONSOLE\n\n%s", message))
|
|
} else {
|
|
world.SetMessage(fmt.Sprintf("SYSTEM UPGRADED!\n\n%s", message))
|
|
}
|
|
// Remove trigger during next tick.
|
|
world.RemoveTrigger = i
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if world.MagnetActive && playerRect.Overlaps(world.DataRect) {
|
|
asset.SoundAlarm.Rewind()
|
|
asset.SoundAlarm.Play()
|
|
|
|
world.GameOver = true
|
|
world.GameOverTicks = 1
|
|
}
|
|
}
|
|
} else if s.Velocity != nil {
|
|
position.X, position.Y = position.X+s.Velocity.X, position.Y+s.Velocity.Y
|
|
|
|
bulletRect := image.Rect(int(position.X), int(position.Y), int(position.X+4), int(position.Y+4))
|
|
// Hit destructible.
|
|
for i := range world.DestructibleRects {
|
|
if world.DestructibleRects[i].Overlaps(bulletRect) {
|
|
asset.SoundExplode.Rewind()
|
|
asset.SoundExplode.Play()
|
|
|
|
world.DestructibleEntities[i].Remove()
|
|
|
|
for _, shape := range world.DestructibleShapes[i] {
|
|
world.Space.RemoveShape(shape)
|
|
}
|
|
|
|
world.DestructibleRects = append(world.DestructibleRects[:i], world.DestructibleRects[i+1:]...)
|
|
world.DestructibleEntities = append(world.DestructibleEntities[:i], world.DestructibleEntities[i+1:]...)
|
|
world.DestructibleShapes = append(world.DestructibleShapes[:i], world.DestructibleShapes[i+1:]...)
|
|
|
|
e.Remove()
|
|
return nil
|
|
}
|
|
}
|
|
// Hit beam.
|
|
for i := range world.BeamRects {
|
|
if world.BeamRects[i].Overlaps(bulletRect) {
|
|
asset.SoundHit.Rewind()
|
|
asset.SoundHit.Play()
|
|
|
|
e.Remove()
|
|
return nil
|
|
}
|
|
}
|
|
// Hit wall.
|
|
for i := range world.MetalRects {
|
|
if world.MetalRects[i].Overlaps(bulletRect) {
|
|
asset.SoundHit.Rewind()
|
|
asset.SoundHit.Play()
|
|
|
|
e.Remove()
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (_ *MovementSystem) Draw(_ gohan.Entity, _ *ebiten.Image) error {
|
|
return gohan.ErrUnregister
|
|
}
|