2021-11-16 18:38:24 +00:00
|
|
|
package system
|
|
|
|
|
|
|
|
import (
|
2021-12-15 06:22:57 +00:00
|
|
|
"fmt"
|
2021-12-10 03:43:15 +00:00
|
|
|
"log"
|
2021-12-14 04:50:25 +00:00
|
|
|
"os"
|
2021-11-16 18:38:24 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"code.rocketnine.space/tslocum/gohan"
|
2021-12-08 05:11:47 +00:00
|
|
|
"code.rocketnine.space/tslocum/monovania/asset"
|
2021-11-16 18:38:24 +00:00
|
|
|
"code.rocketnine.space/tslocum/monovania/component"
|
2021-12-10 03:43:15 +00:00
|
|
|
"code.rocketnine.space/tslocum/monovania/engine"
|
2021-12-08 05:11:47 +00:00
|
|
|
"code.rocketnine.space/tslocum/monovania/world"
|
|
|
|
"github.com/fogleman/ease"
|
2021-11-16 18:38:24 +00:00
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
2021-12-08 05:11:47 +00:00
|
|
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
2021-11-16 18:38:24 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type playerMoveSystem struct {
|
2021-12-01 16:57:09 +00:00
|
|
|
player gohan.Entity
|
|
|
|
movement *MovementSystem
|
|
|
|
lastWalkDirL bool
|
2021-12-14 04:50:25 +00:00
|
|
|
|
|
|
|
rewindTicks int
|
|
|
|
nextRewindTick int
|
2021-11-16 18:38:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewPlayerMoveSystem(player gohan.Entity, m *MovementSystem) *playerMoveSystem {
|
|
|
|
return &playerMoveSystem{
|
|
|
|
player: player,
|
|
|
|
movement: m,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-08 05:11:47 +00:00
|
|
|
func (_ *playerMoveSystem) Needs() []gohan.ComponentID {
|
|
|
|
return []gohan.ComponentID{
|
2021-12-14 04:50:25 +00:00
|
|
|
component.PositionComponentID,
|
2021-12-08 05:11:47 +00:00
|
|
|
component.VelocityComponentID,
|
|
|
|
component.WeaponComponentID,
|
|
|
|
component.SpriteComponentID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (_ *playerMoveSystem) Uses() []gohan.ComponentID {
|
|
|
|
return nil
|
2021-11-16 18:38:24 +00:00
|
|
|
}
|
|
|
|
|
2021-12-08 05:11:47 +00:00
|
|
|
func (s *playerMoveSystem) Update(ctx *gohan.Context) error {
|
2021-12-14 04:50:25 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyEscape) && !world.World.DisableEsc {
|
|
|
|
os.Exit(0)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if !world.World.GameStarted {
|
|
|
|
world.World.GameStartedTicks++
|
|
|
|
if world.World.GameStartedTicks == logoTime {
|
|
|
|
world.World.GameStarted = true
|
|
|
|
world.World.FadingIn = true
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if world.World.FadingIn {
|
|
|
|
world.World.FadeInTicks++
|
|
|
|
if world.World.FadeInTicks == fadeInTime {
|
|
|
|
world.World.FadingIn = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-16 18:38:24 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyV) {
|
2021-12-14 04:50:25 +00:00
|
|
|
v := 1
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyShift) {
|
|
|
|
v = 2
|
|
|
|
}
|
|
|
|
if world.World.Debug == v {
|
2021-11-16 18:38:24 +00:00
|
|
|
world.World.Debug = 0
|
2021-12-14 04:50:25 +00:00
|
|
|
} else {
|
|
|
|
world.World.Debug = v
|
2021-11-16 18:38:24 +00:00
|
|
|
}
|
2021-12-01 16:57:09 +00:00
|
|
|
s.movement.UpdateDebugCollisionRects()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyN) {
|
|
|
|
world.World.NoClip = !world.World.NoClip
|
2021-11-16 18:38:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-12-10 03:43:15 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyX) {
|
|
|
|
position := engine.Engine.Component(s.player, component.PositionComponentID).(*component.PositionComponent)
|
|
|
|
world.World.SpawnX, world.World.SpawnY = position.X, position.Y-12
|
|
|
|
log.Printf("Spawn point set to %.0f,%.0f", world.World.SpawnX, world.World.SpawnY)
|
|
|
|
return nil
|
|
|
|
}
|
2021-12-15 06:22:57 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyK) {
|
|
|
|
if world.World.Keys < 3 {
|
|
|
|
world.World.Keys++
|
|
|
|
}
|
|
|
|
world.World.SetMessage(fmt.Sprintf("YOU NOW HAVE %d KEYS.", world.World.Keys))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if world.World.MessageVisible {
|
|
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyEnter) {
|
|
|
|
world.World.MessageVisible = false
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2021-11-16 18:38:24 +00:00
|
|
|
|
2021-12-14 04:50:25 +00:00
|
|
|
setWalkFrames := func() {
|
|
|
|
if world.World.NoClip {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sprite := component.Sprite(ctx)
|
2021-12-15 06:22:57 +00:00
|
|
|
sprite.Frames = []*ebiten.Image{
|
|
|
|
asset.PlayerSS.WalkR1,
|
|
|
|
asset.PlayerSS.IdleR,
|
|
|
|
asset.PlayerSS.WalkR2,
|
|
|
|
asset.PlayerSS.IdleR,
|
2021-12-14 04:50:25 +00:00
|
|
|
}
|
2021-12-15 06:22:57 +00:00
|
|
|
sprite.HorizontalFlip = s.lastWalkDirL
|
2021-12-14 04:50:25 +00:00
|
|
|
sprite.NumFrames = 4
|
|
|
|
sprite.FrameTime = 150 * time.Millisecond
|
|
|
|
}
|
|
|
|
|
|
|
|
setJumpAndIdleFrames := func() {
|
|
|
|
sprite := component.Sprite(ctx)
|
|
|
|
sprite.NumFrames = 0
|
2021-12-15 06:22:57 +00:00
|
|
|
if (s.movement.OnGround == -1 && s.movement.OnLadder == -1) || s.movement.Jumping || world.World.NoClip {
|
|
|
|
sprite.Image = asset.PlayerSS.WalkR2
|
2021-12-14 04:50:25 +00:00
|
|
|
} else {
|
2021-12-15 06:22:57 +00:00
|
|
|
sprite.Image = asset.PlayerSS.IdleR
|
2021-12-14 04:50:25 +00:00
|
|
|
}
|
2021-12-15 06:22:57 +00:00
|
|
|
sprite.HorizontalFlip = s.lastWalkDirL
|
2021-12-14 04:50:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Rewind time.
|
|
|
|
const minRewindTicks = 144 / 3
|
|
|
|
const maxRewindTicks = 144 * 1.5
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyR) {
|
|
|
|
if len(s.movement.playerPositions) > 1 {
|
|
|
|
position := component.Position(ctx)
|
|
|
|
if !world.World.Rewinding {
|
|
|
|
world.World.Rewinding = true
|
|
|
|
world.World.GameOver = false
|
|
|
|
|
|
|
|
velocity := component.Velocity(ctx)
|
|
|
|
velocity.X, velocity.Y = 0, 0
|
|
|
|
|
|
|
|
s.movement.RecordPosition(position)
|
|
|
|
|
|
|
|
setWalkFrames()
|
|
|
|
}
|
|
|
|
|
|
|
|
lastPos := s.movement.playerPositions[len(s.movement.playerPositions)-1]
|
|
|
|
nextPos := s.movement.playerPositions[len(s.movement.playerPositions)-2]
|
|
|
|
rx, ry := nextPos[0]-lastPos[0], nextPos[1]-lastPos[1]
|
|
|
|
|
|
|
|
if s.rewindTicks == 0 {
|
|
|
|
dx, dy := deltaXY(lastPos[0], lastPos[1], nextPos[0], nextPos[1])
|
|
|
|
|
|
|
|
s.nextRewindTick = 144 * int((dx+dy)/150)
|
|
|
|
if s.nextRewindTick < minRewindTicks {
|
|
|
|
s.nextRewindTick = minRewindTicks
|
|
|
|
} else if s.nextRewindTick > maxRewindTicks {
|
|
|
|
s.nextRewindTick = maxRewindTicks
|
|
|
|
}
|
|
|
|
s.rewindTicks++
|
|
|
|
|
|
|
|
// Update player direction.
|
|
|
|
rewindDirL := rx >= 0
|
|
|
|
if s.lastWalkDirL != rewindDirL {
|
|
|
|
s.lastWalkDirL = rewindDirL
|
|
|
|
setWalkFrames()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
pct := 1.0
|
|
|
|
if s.nextRewindTick > 0 {
|
|
|
|
pct = float64(s.rewindTicks) / float64(s.nextRewindTick)
|
|
|
|
if pct > 1 {
|
|
|
|
pct = 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
position.X, position.Y = lastPos[0]+(rx*pct), lastPos[1]+(ry*pct)
|
|
|
|
|
|
|
|
if s.rewindTicks == s.nextRewindTick {
|
|
|
|
s.movement.RemoveLastPosition()
|
|
|
|
s.rewindTicks = 0
|
|
|
|
} else {
|
|
|
|
s.rewindTicks++
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setJumpAndIdleFrames()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
} else if s.nextRewindTick != 0 {
|
|
|
|
s.rewindTicks = 0
|
|
|
|
s.nextRewindTick = 0
|
|
|
|
world.World.Rewinding = false
|
2021-12-14 16:03:30 +00:00
|
|
|
s.movement.RemoveLastPosition()
|
2021-12-14 04:50:25 +00:00
|
|
|
}
|
|
|
|
|
2021-11-16 18:38:24 +00:00
|
|
|
moveSpeed := 0.1
|
|
|
|
maxSpeed := 0.5
|
2021-12-08 05:11:47 +00:00
|
|
|
maxLevitateSpeed := 1.0
|
2021-11-16 18:38:24 +00:00
|
|
|
maxYSpeed := 0.5
|
2021-12-13 01:56:04 +00:00
|
|
|
const jumpVelocity = -1.02
|
|
|
|
const dashVelocity = 5
|
2021-11-16 18:38:24 +00:00
|
|
|
|
2021-12-08 05:11:47 +00:00
|
|
|
velocity := component.Velocity(ctx)
|
2021-12-01 16:57:09 +00:00
|
|
|
|
|
|
|
var walkKeyPressed bool
|
|
|
|
|
2021-12-13 01:56:04 +00:00
|
|
|
// Jump.
|
|
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyJ) {
|
|
|
|
if ((s.movement.OnGround != -1 || s.movement.OnLadder != -1) && world.World.Jumps == 0) || (world.World.CanDoubleJump && world.World.Jumps < 2) {
|
|
|
|
velocity.Y = jumpVelocity
|
|
|
|
s.movement.Jumping = true
|
|
|
|
s.movement.LastJump = time.Now()
|
|
|
|
world.World.Jumps++
|
|
|
|
} else if world.World.CanLevitate && world.World.Jumps == 2 {
|
|
|
|
world.World.Levitating = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if s.movement.Jumping && (!ebiten.IsKeyPressed(ebiten.KeyJ) || time.Since(s.movement.LastJump) >= 200*time.Millisecond) {
|
|
|
|
s.movement.Jumping = false
|
|
|
|
}
|
|
|
|
|
2021-12-04 05:50:36 +00:00
|
|
|
if s.movement.OnGround != -1 && ebiten.IsKeyPressed(ebiten.KeyS) && !world.World.NoClip {
|
2021-12-14 04:50:25 +00:00
|
|
|
// Update player direction.
|
2021-12-08 05:11:47 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyA) {
|
|
|
|
s.lastWalkDirL = true
|
|
|
|
} else if ebiten.IsKeyPressed(ebiten.KeyD) {
|
|
|
|
s.lastWalkDirL = false
|
|
|
|
}
|
2021-12-14 04:50:25 +00:00
|
|
|
|
2021-12-04 05:50:36 +00:00
|
|
|
// Duck and look down.
|
2021-12-08 05:11:47 +00:00
|
|
|
sprite := component.Sprite(ctx)
|
2021-12-04 05:50:36 +00:00
|
|
|
sprite.NumFrames = 0
|
2021-12-15 06:22:57 +00:00
|
|
|
sprite.Image = asset.PlayerSS.DuckR
|
|
|
|
sprite.HorizontalFlip = s.lastWalkDirL
|
2021-12-04 05:50:36 +00:00
|
|
|
walkKeyPressed = true
|
|
|
|
|
|
|
|
if world.World.DuckStart == -1 {
|
|
|
|
if world.World.DuckEnd == -1 {
|
|
|
|
world.World.DuckStart = 0
|
2021-11-16 18:38:24 +00:00
|
|
|
} else {
|
2021-12-04 05:50:36 +00:00
|
|
|
world.World.DuckStart = 1 - world.World.DuckEnd
|
2021-11-16 18:38:24 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-04 05:50:36 +00:00
|
|
|
offset := ((float64(world.World.ScreenH) / 4) / 3) * -1
|
|
|
|
if world.World.OffsetY > offset {
|
|
|
|
pct := world.World.DuckStart
|
|
|
|
if pct < 0.5 {
|
|
|
|
pct = ease.InOutQuint(pct)
|
|
|
|
} else {
|
|
|
|
pct = ease.InOutQuint(pct)
|
|
|
|
}
|
|
|
|
world.World.OffsetY = offset * pct
|
2021-12-01 16:57:09 +00:00
|
|
|
|
2021-12-04 05:50:36 +00:00
|
|
|
if world.World.DuckStart < 1 {
|
|
|
|
world.World.DuckStart += 0.01
|
|
|
|
}
|
2021-12-01 16:57:09 +00:00
|
|
|
}
|
2021-12-04 05:50:36 +00:00
|
|
|
} else {
|
|
|
|
if world.World.DuckStart != -1 {
|
|
|
|
world.World.DuckEnd = 1 - world.World.DuckStart
|
|
|
|
world.World.DuckStart = -1
|
|
|
|
}
|
|
|
|
if world.World.DuckEnd != -1 {
|
|
|
|
offset := ((float64(world.World.ScreenH) / 4) / 3) * -1
|
|
|
|
pct := world.World.DuckEnd
|
|
|
|
if pct < 0.5 {
|
|
|
|
pct = ease.InOutQuint(pct)
|
2021-11-16 18:38:24 +00:00
|
|
|
} else {
|
2021-12-04 05:50:36 +00:00
|
|
|
pct = ease.InOutQuint(pct)
|
|
|
|
}
|
|
|
|
pct = 1 - pct
|
|
|
|
world.World.OffsetY = offset * pct
|
|
|
|
|
|
|
|
if world.World.DuckEnd < 1 {
|
|
|
|
world.World.DuckEnd += 0.01
|
2021-11-16 18:38:24 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-01 16:57:09 +00:00
|
|
|
|
2021-12-04 05:50:36 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyA) {
|
2021-12-08 05:11:47 +00:00
|
|
|
if velocity.X > -maxSpeed || world.World.NoClip {
|
2021-12-04 05:50:36 +00:00
|
|
|
if s.movement.OnLadder != -1 {
|
|
|
|
velocity.X -= moveSpeed / 2
|
|
|
|
} else {
|
|
|
|
velocity.X -= moveSpeed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-08 05:11:47 +00:00
|
|
|
if !world.World.NoClip {
|
|
|
|
sprite := component.Sprite(ctx)
|
|
|
|
sprite.Frames = []*ebiten.Image{
|
2021-12-15 06:22:57 +00:00
|
|
|
asset.PlayerSS.WalkR1,
|
|
|
|
asset.PlayerSS.IdleR,
|
|
|
|
asset.PlayerSS.WalkR2,
|
|
|
|
asset.PlayerSS.IdleR,
|
2021-12-08 05:11:47 +00:00
|
|
|
}
|
2021-12-15 06:22:57 +00:00
|
|
|
sprite.HorizontalFlip = true
|
2021-12-08 05:11:47 +00:00
|
|
|
sprite.NumFrames = 4
|
|
|
|
sprite.FrameTime = 150 * time.Millisecond
|
2021-12-04 05:50:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
walkKeyPressed = true
|
|
|
|
s.lastWalkDirL = true
|
2021-12-01 16:57:09 +00:00
|
|
|
}
|
2021-12-04 05:50:36 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyD) {
|
2021-12-08 05:11:47 +00:00
|
|
|
if velocity.X < maxSpeed || world.World.NoClip {
|
2021-12-04 05:50:36 +00:00
|
|
|
if s.movement.OnLadder != -1 {
|
|
|
|
velocity.X += moveSpeed / 2
|
|
|
|
} else {
|
|
|
|
velocity.X += moveSpeed
|
|
|
|
}
|
|
|
|
}
|
2021-12-01 16:57:09 +00:00
|
|
|
|
2021-12-08 05:11:47 +00:00
|
|
|
if !world.World.NoClip {
|
|
|
|
sprite := component.Sprite(ctx)
|
|
|
|
sprite.Frames = []*ebiten.Image{
|
|
|
|
asset.PlayerSS.WalkR1,
|
|
|
|
asset.PlayerSS.IdleR,
|
|
|
|
asset.PlayerSS.WalkR2,
|
|
|
|
asset.PlayerSS.IdleR,
|
|
|
|
}
|
2021-12-15 06:22:57 +00:00
|
|
|
sprite.HorizontalFlip = false
|
2021-12-08 05:11:47 +00:00
|
|
|
sprite.NumFrames = 4
|
|
|
|
sprite.FrameTime = 150 * time.Millisecond
|
2021-12-04 05:50:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
walkKeyPressed = true
|
|
|
|
s.lastWalkDirL = false
|
|
|
|
}
|
2021-11-16 18:38:24 +00:00
|
|
|
}
|
2021-12-13 01:56:04 +00:00
|
|
|
|
|
|
|
// Dash.
|
|
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyK) && world.World.CanDash && world.World.Dashes == 0 && s.movement.OnGround == -1 && s.movement.OnLadder == -1 {
|
|
|
|
if s.lastWalkDirL {
|
|
|
|
velocity.X = -dashVelocity
|
|
|
|
} else {
|
|
|
|
velocity.X = dashVelocity
|
|
|
|
}
|
|
|
|
velocity.Y = 0
|
|
|
|
s.movement.Dashing = true
|
|
|
|
s.movement.LastDash = time.Now()
|
|
|
|
world.World.Dashes = 1
|
|
|
|
}
|
|
|
|
if s.movement.Dashing && (!ebiten.IsKeyPressed(ebiten.KeyK) || time.Since(s.movement.LastDash) >= 250*time.Millisecond) {
|
|
|
|
velocity.X = 0
|
|
|
|
s.movement.Dashing = false
|
|
|
|
}
|
|
|
|
|
2021-12-01 16:57:09 +00:00
|
|
|
if s.movement.OnLadder != -1 || world.World.NoClip {
|
2021-11-16 18:38:24 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyW) {
|
2021-12-08 05:11:47 +00:00
|
|
|
if velocity.Y > -maxYSpeed || world.World.NoClip {
|
2021-11-16 18:38:24 +00:00
|
|
|
velocity.Y -= moveSpeed
|
|
|
|
}
|
2021-12-01 16:57:09 +00:00
|
|
|
|
2021-12-14 04:50:25 +00:00
|
|
|
setWalkFrames()
|
2021-12-01 16:57:09 +00:00
|
|
|
walkKeyPressed = true
|
2021-11-16 18:38:24 +00:00
|
|
|
}
|
2021-12-01 16:57:09 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyS) && s.movement.OnGround == -1 {
|
2021-12-08 05:11:47 +00:00
|
|
|
if velocity.Y < maxYSpeed || world.World.NoClip {
|
2021-11-16 18:38:24 +00:00
|
|
|
velocity.Y += moveSpeed
|
|
|
|
}
|
2021-12-01 16:57:09 +00:00
|
|
|
|
2021-12-14 04:50:25 +00:00
|
|
|
setWalkFrames()
|
2021-12-01 16:57:09 +00:00
|
|
|
walkKeyPressed = true
|
2021-11-16 18:38:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-08 05:11:47 +00:00
|
|
|
if world.World.Levitating {
|
2021-12-15 06:22:57 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyJ) {
|
2021-12-08 05:11:47 +00:00
|
|
|
if velocity.Y > -maxLevitateSpeed {
|
|
|
|
velocity.Y -= moveSpeed
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
world.World.Levitating = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 01:56:04 +00:00
|
|
|
if !walkKeyPressed || s.movement.Jumping || (s.movement.OnGround == -1 && s.movement.OnLadder == -1) || world.World.NoClip {
|
2021-12-14 04:50:25 +00:00
|
|
|
setJumpAndIdleFrames()
|
2021-12-01 16:57:09 +00:00
|
|
|
}
|
|
|
|
|
2021-11-16 18:38:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-08 05:11:47 +00:00
|
|
|
func (s *playerMoveSystem) Draw(_ *gohan.Context, _ *ebiten.Image) error {
|
2021-11-16 18:38:24 +00:00
|
|
|
return gohan.ErrSystemWithoutDraw
|
|
|
|
}
|
2021-12-14 04:50:25 +00:00
|
|
|
|
|
|
|
func deltaXY(x1, y1, x2, y2 float64) (dx float64, dy float64) {
|
|
|
|
dx, dy = x1-x2, y1-y2
|
|
|
|
if dx < 0 {
|
|
|
|
dx *= -1
|
|
|
|
}
|
|
|
|
if dy < 0 {
|
|
|
|
dy *= -1
|
|
|
|
}
|
|
|
|
return dx, dy
|
|
|
|
}
|