Neale Pickett
·
2024-12-05
tank.mjs
1const craterPoints = 7
2const craterAngle = Math.PI / craterPoints
3
4export class Tank {
5 constructor(ctx, color, sensors) {
6 this.ctx = ctx
7 this.color = color
8
9 // Do all the yucky math up front
10 this.maxlen = 0
11 this.sensors = []
12 for (let i in sensors) {
13 let s = sensors[i]
14
15 if (! s) {
16 this.sensors[i] = [0,0,0,0]
17 } else {
18 let r = s.range
19 // r, angle, width, turret
20 this.sensors[i] = {
21 range: s.range,
22 beg: s.angle - s.width/2,
23 end: s.angle + s.width/2,
24 turret: s.turret,
25 }
26 if (s.range > this.maxlen) {
27 this.maxlen = s.range
28 }
29 }
30 }
31
32 this.set_state(0, 0, 0, 0, 0)
33 }
34
35 // Set up our state, for later interleaved draw requests
36 set_state(x, y, rotation, turret, flags, sensor_state) {
37 this.x = x
38 this.y = y
39 this.rotation = rotation
40 this.turret = turret
41 if (flags & 1) {
42 this.fire = 5
43 }
44 this.led = flags & 2
45 this.dead = flags & 4
46 this.sensor_state = sensor_state
47 }
48
49 draw_crater() {
50 if (!this.dead) {
51 return
52 }
53
54 this.ctx.save()
55 this.ctx.translate(this.x, this.y)
56 this.ctx.rotate(this.rotation)
57
58 // If the cannon was fired in the same turn we were shot,
59 // render the laser for one frame.
60 if (this.fire == 5) {
61 this.ctx.save()
62 this.ctx.rotate(this.turret)
63 // one frame of cannon fire
64 this.draw_cannon()
65 this.fire = 0
66 this.ctx.restore()
67 }
68
69 this.ctx.lineWidth = 2
70 this.ctx.strokeStyle = `rgb(from ${this.color} r g b / 50%)`
71 this.ctx.fillStyle = `rgb(from ${this.color} r g b / 20%)`
72 this.ctx.beginPath()
73 this.ctx.moveTo(12, 0)
74 for (let i = 0; i < craterPoints; i += 1) {
75 this.ctx.rotate(craterAngle)
76 this.ctx.lineTo(6, 0)
77 this.ctx.rotate(craterAngle)
78 this.ctx.lineTo(12, 0)
79 }
80 this.ctx.closePath()
81 this.ctx.stroke()
82 this.ctx.fill()
83
84 this.ctx.restore()
85 }
86
87 draw_sensors() {
88 if (this.dead) {
89 return
90 }
91 this.ctx.save()
92 this.ctx.translate(this.x, this.y)
93 this.ctx.rotate(this.rotation)
94
95 this.ctx.lineWidth = 1
96 for (let i in this.sensors) {
97 var s = this.sensors[i]
98
99 this.ctx.save()
100 if (s.turret) {
101 this.ctx.rotate(this.turret)
102 }
103 if (this.sensor_state & (1 << i)) {
104 // Sensor is triggered
105 this.ctx.strokeStyle = "#000"
106 } else {
107 this.ctx.strokeStyle = `rgb(from ${this.color} r g b / 40%)`
108 }
109 this.ctx.beginPath()
110 this.ctx.moveTo(0, 0)
111 this.ctx.arc(0, 0, s.range, s.beg, s.end, false)
112 this.ctx.closePath()
113 this.ctx.stroke()
114 this.ctx.restore()
115 }
116
117 this.ctx.restore()
118 }
119
120 draw_tank() {
121 if (this.dead) {
122 return
123 }
124 this.ctx.save()
125 this.ctx.translate(this.x, this.y)
126 this.ctx.rotate(this.rotation)
127
128 this.ctx.fillStyle = this.color
129 this.ctx.fillRect(-5, -4, 10, 8)
130 this.ctx.fillStyle = "#777"
131 this.ctx.fillRect(-7, -9, 15, 5)
132 this.ctx.fillRect(-7, 4, 15, 5)
133 this.ctx.rotate(this.turret)
134 if (this.fire) {
135 this.draw_cannon()
136 this.fire -= 1
137 } else {
138 if (this.led) {
139 this.ctx.fillStyle = "#f00"
140 } else {
141 this.ctx.fillStyle = "#000"
142 }
143 this.ctx.fillRect(0, -1, 10, 2)
144 }
145
146 this.ctx.restore()
147 }
148
149 draw_cannon() {
150 this.ctx.fillStyle = ("hsl(0, 100%, 50%, " + this.fire/5 + ")")
151 this.ctx.fillRect(0, -1, 45, 2)
152 }
153
154 draw_wrap_sensors() {
155 let width = this.ctx.canvas.width
156 let height = this.ctx.canvas.height
157 let orig_x = this.x
158 let orig_y = this.y
159 for (let x = this.x - width; x < width + this.maxlen; x += width) {
160 for (let y = this.y - height; y < height + this.maxlen; y += height) {
161 if ((-this.maxlen < x) && (x < width + this.maxlen) &&
162 (-this.maxlen < y) && (y < height + this.maxlen)) {
163 this.x = x
164 this.y = y
165 this.draw_sensors()
166 }
167 }
168 }
169 this.x = orig_x
170 this.y = orig_y
171 }
172}
173