tanks

Blow up enemy tanks using code
git clone https://git.woozle.org/neale/tanks.git

tanks / docs / assets / js
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