Neale Pickett
·
2024-12-05
tanks.js
1function dbg(o) {
2 e = document.getElementById("debug");
3 e.innerHTML = o;
4}
5
6function torgba(color, alpha) {
7 var r = parseInt(color.substring(1,3), 16);
8 var g = parseInt(color.substring(3,5), 16);
9 var b = parseInt(color.substring(5,7), 16);
10
11 return "rgba(" + r + "," + g + "," + b + "," + alpha + ")";
12}
13
14function Tank(ctx, width, height, color, sensors) {
15 var craterStroke = torgba(color, 0.5);
16 var craterFill = torgba(color, 0.2);
17 var sensorStroke = torgba(color, 0.4);
18 var maxlen = 0;
19
20 this.x = 0;
21 this.y = 0;
22 this.rotation = 0;
23 this.turret = 0;
24
25 this.dead = 0;
26
27 // Do all the yucky math up front
28 this.sensors = new Array();
29 for (i in sensors) {
30 var s = sensors[i];
31
32 if (! s) {
33 this.sensors[i] = [0,0,0,0];
34 } else {
35 // r, angle, width, turret
36 this.sensors[i] = new Array();
37 this.sensors[i][0] = s[0];
38 this.sensors[i][1] = s[1] - s[2]/2;
39 this.sensors[i][2] = s[1] + s[2]/2;
40 this.sensors[i][3] = s[3]?1:0;
41 if (s[0] > maxlen) {
42 maxlen = s[0];
43 }
44 }
45 }
46
47 // Set up our state, for later interleaved draw requests
48 this.set_state = function(x, y, rotation, turret, flags, sensor_state) {
49 this.x = x;
50 this.y = y;
51 this.rotation = rotation;
52 this.turret = turret;
53 if (flags & 1) {
54 this.fire = 5;
55 }
56 this.led = flags & 2;
57 this.dead = flags & 4;
58 this.sensor_state = sensor_state;
59 }
60
61 this.draw_crater = function() {
62 if (!this.dead) {
63 return;
64 }
65
66 var points = 7;
67 var angle = Math.PI / points;
68
69 ctx.save();
70 ctx.translate(this.x, this.y);
71 ctx.rotate(this.rotation);
72
73 if (this.fire == 5) {
74 ctx.save();
75 ctx.rotate(this.turret);
76 // one frame of cannon fire
77 this.draw_cannon();
78 this.fire = 0;
79 ctx.restore();
80 }
81
82 ctx.lineWidth = 2;
83 ctx.strokeStyle = craterStroke;
84 ctx.fillStyle = craterFill;
85 ctx.beginPath();
86 ctx.moveTo(12, 0);
87 for (i = 0; i < points; i += 1) {
88 ctx.rotate(angle);
89 ctx.lineTo(6, 0);
90 ctx.rotate(angle);
91 ctx.lineTo(12, 0);
92 }
93 ctx.closePath()
94 ctx.stroke();
95 ctx.fill();
96
97 ctx.restore();
98 }
99
100 this.draw_sensors = function() {
101 if (this.dead) {
102 return;
103 }
104 ctx.save();
105 ctx.translate(this.x, this.y);
106 ctx.rotate(this.rotation);
107
108 ctx.lineWidth = 1;
109 for (i in this.sensors) {
110 var s = this.sensors[i];
111 var adj = this.turret * s[3];
112
113 if (this.sensor_state & (1 << i)) {
114 // Sensor is triggered
115 ctx.strokeStyle = "#000";
116 } else {
117 ctx.strokeStyle = sensorStroke;
118 }
119 ctx.beginPath();
120 ctx.moveTo(0, 0);
121 ctx.arc(0, 0, s[0], s[1] + adj, s[2] + adj, false);
122 ctx.closePath();
123 ctx.stroke();
124 }
125
126 ctx.restore();
127 }
128
129 this.draw_tank = function() {
130 if (this.dead) {
131 return;
132 }
133 ctx.save();
134 ctx.translate(this.x, this.y);
135 ctx.rotate(this.rotation);
136
137 ctx.fillStyle = color;
138 ctx.fillRect(-5, -4, 10, 8);
139 ctx.fillStyle = "#777";
140 ctx.fillRect(-7, -9, 15, 5);
141 ctx.fillRect(-7, 4, 15, 5);
142 ctx.rotate(this.turret);
143 if (this.fire) {
144 this.draw_cannon();
145 this.fire -= 1;
146 } else {
147 if (this.led) {
148 ctx.fillStyle = "#f00";
149 } else {
150 ctx.fillStyle = "#000";
151 }
152 ctx.fillRect(0, -1, 10, 2);
153 }
154
155 ctx.restore();
156 }
157
158 this.draw_cannon = function() {
159 ctx.fillStyle = ("rgba(255,255,64," + this.fire/5 + ")");
160 ctx.fillRect(0, -1, 45, 2);
161 }
162
163 this.draw_wrap_sensors = function() {
164 var orig_x = this.x;
165 var orig_y = this.y;
166 for (x = this.x - width; x < width + maxlen; x += width) {
167 for (y = this.y - height; y < height + maxlen; y += height) {
168 if ((-maxlen < x) && (x < width + maxlen) &&
169 (-maxlen < y) && (y < height + maxlen)) {
170 this.x = x;
171 this.y = y;
172 this.draw_sensors();
173 }
174 }
175 }
176 this.x = orig_x;
177 this.y = orig_y;
178 }
179}
180
181var loop_id;
182
183function start(id, game) {
184 var canvas = document.getElementById(id);
185 var ctx = canvas.getContext('2d');
186
187 canvas.width = game[0][0];
188 canvas.height = game[0][1];
189 // game[2] is tank descriptions
190 var turns = game[2];
191
192 // Set up tanks
193 var tanks = new Array();
194 for (i in game[1]) {
195 var desc = game[1][i];
196 tanks[i] = new Tank(ctx, game[0][0], game[0][1], desc[0], desc[1]);
197 }
198
199 var frame = 0;
200 var lastframe = 0;
201 var fps = document.getElementById('fps');
202
203 function update_fps() {
204 fps.innerHTML = (frame - lastframe);
205 lastframe = frame;
206 }
207
208 function drawFrame(idx) {
209 canvas.width = canvas.width;
210 turn = turns[idx];
211
212 // Update and draw craters first
213 for (i in turn) {
214 t = turn[i];
215 if (!t) {
216 // old data, force-kill it
217 tanks[i].fire = 0;
218 tanks[i].dead = 5;
219 } else {
220 tanks[i].set_state(t[0], t[1], t[2], t[3], t[4], t[5]);
221 }
222 tanks[i].draw_crater();
223 }
224 // Then sensors
225 for (i in turn) {
226 tanks[i].draw_wrap_sensors();
227 }
228 // Then tanks
229 for (i in turn) {
230 tanks[i].draw_tank()
231 }
232 }
233
234 function update() {
235 var idx = frame % (turns.length + 20);
236 var turn;
237
238 frame += 1;
239 if (idx >= turns.length) {
240 return;
241 }
242
243 drawFrame(idx);
244 }
245
246 loop_id = setInterval(update, 66);
247 if (fps) {
248 setInterval(update_fps, 1000);
249 }
250}
251