tanks

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

tanks / www
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