aboutsummaryrefslogtreecommitdiff
path: root/src/view/ship.ts
blob: 5a8b86f14826ae4a8f0c3e97929551e7754f6fac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import * as Controls from 'controls'
import * as Vec2 from 'model/vec2'
import * as Size from 'model/size'
import * as Number from 'util/number'
import * as Colors from 'view/colors'
import * as Physics from 'util/physics'

export const missileWidth: number = 10
export const missileHeight: number = 5

export interface State {
  speed: Vec2.Vec2,
  pos: Vec2.Vec2,
  radius: number,
  missiles: Array<Vec2.Vec2>,
  lastFired: number,
}

export function init(windowSize: Size.Size): State {
  return {
    speed: { x: 0, y: 0 },
    pos: {
      x: windowSize.width / 6,
      y: windowSize.height / 2,
    },
    radius: Size.diagonal(windowSize) / 30,
    missiles: [],
    lastFired: 0,
  }
}

export function update(
  state: State,
  dt: number,
  windowSize: Size.Size
) {
  move(state, dt, windowSize)
  updateMissiles(state, dt, windowSize)
}

function move(state: State, dt: number, windowSize: Size.Size) {
  const dir = controlsDir(Controls.current)

  const unitDt = 0.5
  const steps = Math.round(dt / unitDt)

  const acc = Physics.acc({ dt, speed: state.speed, dir })
  Array(steps).fill(1).forEach(_ => {
    state.speed = Physics.speed({ dt: unitDt, acc, speed: state.speed })
    state.pos = Physics.pos({ dt: unitDt, acc, speed: state.speed, pos: state.pos })
  })

  state.pos = Vec2.clamp(
    state.pos,
    { x: state.radius, y: state.radius },
    { x: windowSize.width - state.radius, y: windowSize.height - state.radius }
  )
}

function controlsDir(c: Controls.Controls): Vec2.Vec2 {
  let dir = { x: 0, y: 0 }

  if (c.up && !c.down)
    dir.y = -1
  else if (c.down && !c.up)
    dir.y = 1

  if (c.right && !c.left)
    dir.x = 1
  else if (c.left && !c.right)
    dir.x = -1

  return Vec2.normalize(dir)
}

function updateMissiles(state: State, dt: number, windowSize: Size.Size) {
  if (Controls.current.spaceCount > state.lastFired) {
    state.missiles.push({x: state.pos.x + state.radius, y: state.pos.y})
    state.lastFired = Controls.current.spaceCount
  }

  state.missiles = state.missiles
    .map(missile => ({ ...missile, x: missile.x + dt}))
    .filter(missile => missile.x < windowSize.width)
}

export function project(state: State, from: Size.Size, to: Size.Size) {
  state.pos = Vec2.project(from, to, state.pos)
  state.radius = state.radius / Size.diagonal(from) * Size.diagonal(to)
}

export function view(context: CanvasRenderingContext2D, state: State) {
  context.fillStyle = Colors.colors.red
  context.beginPath()
  context.moveTo(state.pos.x - state.radius, state.pos.y - state.radius)
  context.lineTo(state.pos.x + state.radius, state.pos.y)
  context.lineTo(state.pos.x - state.radius, state.pos.y + state.radius)
  context.closePath()
  context.fill()

  state.missiles.forEach(({x, y}) => {
    context.fillStyle = Colors.colors.red
    context.beginPath()
    context.rect(
      x - missileWidth / 2,
      y - missileHeight / 2,
      missileWidth,
      missileHeight
    )
    context.fill()
  })
}