import { Zombie } from './Zombie.js'; import { GameObject } from '../engine/GameObject.js'; import { Vector2D } from '../engine/Vector2D.js'; export class Leaper extends Zombie { constructor( x, y, target, { container, viewport, speed = 95, size = 28, color = [0.15, 0.25, 0.75, 1], health = 3, knockback = 240, scoreValue = 6, dashRange = 300, windupTime = 0.35, dashTime = 0.28, dashSpeed = 680, dashCooldown = 1.8, emergeTime = 0, } = {}, ) { super(x, y, target, { container, viewport, speed, size, color, health, knockback, scoreValue, emergeTime, }); this.dashRange = dashRange; this.windupTime = windupTime; this.dashTime = dashTime; this.dashSpeed = dashSpeed; this.dashCooldown = dashCooldown; this._dashTimer = 0; this._windupTimer = 0; this._dashCooldown = 0; this._dashDirection = new Vector2D(1, 0); this._time = 0; } update(dt) { this._time += dt; if (this._updateEmerge(dt)) return; this._dashCooldown = Math.max(0, this._dashCooldown - dt); const delta = this.target.pos.subV(this.pos); const distance = delta.length(); let move = new Vector2D(0, 0); if (this._windupTimer > 0) { this._windupTimer -= dt; if (this._windupTimer <= 0) { this._dashTimer = this.dashTime; this._dashDirection = distance > 1 ? delta.divS(distance) : new Vector2D(1, 0); } } else if (this._dashTimer > 0) { this._dashTimer -= dt; move = this._dashDirection.mulS(this.dashSpeed); } else if (distance > 1) { if (distance < this.dashRange && this._dashCooldown <= 0) { this._windupTimer = this.windupTime; } else { move = delta.divS(distance).mulS(this.speed); } } this.velocity = move.addV(this._hitKnockback); GameObject.prototype.update.call(this, dt); this._hitKnockback = this._hitKnockback.mulS(Math.max(0, 1 - dt * this.knockbackDamping)); this._flash = Math.max(0, this._flash - dt); } draw(renderer) { this._drawEmerge(renderer); const s = this.size * this._emergeSize(); const dashing = this._dashTimer > 0; const winding = this._windupTimer > 0; let color = this.baseColor; if (this._flash > 0) { color = [1, 1, 1, 1]; } else if (winding || dashing) { color = [ this.baseColor[0] + (1 - this.baseColor[0]) * 0.8, this.baseColor[1] + (1 - this.baseColor[1]) * 0.8, this.baseColor[2], 1, ]; } const towards = dashing ? this._dashDirection : this.target.pos.subV(this.pos); const facing = Math.atan2(towards.y, towards.x); const dirX = Math.cos(facing); const dirY = Math.sin(facing); const perpX = -dirY; const perpY = dirX; const shade = [color[0] * 0.65, color[1] * 0.65, color[2] * 0.65, 1]; const shadeDark = [color[0] * 0.4, color[1] * 0.4, color[2] * 0.4, 1]; const light = [ Math.min(1, color[0] + 0.25), Math.min(1, color[1] + 0.25), Math.min(1, color[2] + 0.25), 1, ]; const drawLimb = (ax, ay, bx, by, thickness, limbColor) => { const len = Math.hypot(bx - ax, by - ay); if (len < 1) return; renderer.drawTexture( renderer.whiteTexture, (ax + bx) / 2 - len / 2, (ay + by) / 2 - thickness / 2, len, thickness, limbColor, Math.atan2(by - ay, bx - ax), ); }; const bodyLen = s * (dashing ? 1.45 : winding ? 0.9 : 1.15); const bodyH = s * (dashing ? 0.42 : winding ? 0.7 : 0.55); renderer.drawTexture( renderer.whiteTexture, this.pos.x - s * 0.72, this.pos.y - s * 0.26 + s * 0.08, s * 1.44, s * 0.52, [0, 0, 0, 0.35], ); if (winding) { renderer.drawTexture( renderer.whiteTexture, this.pos.x - s * 0.9, this.pos.y - s * 0.6, s * 1.8, s * 1.2, [1, 1, 1, 0.3], ); } const tailBaseX = this.pos.x - dirX * bodyLen * 0.45; const tailBaseY = this.pos.y - dirY * bodyLen * 0.45; const tailSway = dashing ? 0 : Math.sin(this._time * 16) * s * 0.14; const tailTipX = tailBaseX - dirX * (dashing ? s * 0.6 : s * 0.42) + perpX * tailSway; const tailTipY = tailBaseY - dirY * (dashing ? s * 0.6 : s * 0.42) + perpY * tailSway; drawLimb(tailBaseX, tailBaseY, tailTipX, tailTipY, s * 0.11, shadeDark); drawLimb( tailTipX, tailTipY, tailTipX + perpX * tailSway * 0.5 - dirX * s * 0.08, tailTipY + perpY * tailSway * 0.5 - dirY * s * 0.08, s * 0.15, shade, ); const legL = s * (dashing ? 0.3 : winding ? 0.16 : 0.4); const legW = s * 0.11; for (let pair = 0; pair < 2; pair += 1) { const front = pair === 0; const baseFx = this.pos.x + dirX * bodyLen * (front ? 0.4 : -0.4); const baseFy = this.pos.y + dirY * bodyLen * (front ? 0.4 : -0.4); for (let i = 0; i < 2; i += 1) { const sign = i === 0 ? 1 : -1; const bx = baseFx + perpX * bodyH * 0.5 * sign; const by = baseFy + perpY * bodyH * 0.5 * sign; let fx = bx; let fy = by; if (dashing) { fx = bx - dirX * legL; fy = by - dirY * legL; } else if (winding) { fx = bx + perpX * sign * s * 0.1; fy = by + s * 0.13; } else { const run = Math.sin(this._time * 18 + (front ? 0 : Math.PI)) * s * 0.16; fx = bx + dirX * run * 0.7 + perpX * sign * s * 0.04; fy = by + legL; } drawLimb(bx, by, fx, fy, legW, shadeDark); } } renderer.drawTexture( renderer.whiteTexture, this.pos.x - bodyLen / 2, this.pos.y - bodyH / 2, bodyLen, bodyH, color, facing, ); const headLen = s * (dashing ? 0.38 : 0.44); const headH = s * 0.3; const headCx = this.pos.x + dirX * (bodyLen * 0.5 - s * 0.03); const headCy = this.pos.y + dirY * (bodyLen * 0.5 - s * 0.03); renderer.drawTexture( renderer.whiteTexture, headCx - headLen / 2, headCy - headH / 2, headLen, headH, light, facing, ); const snoutLen = s * 0.28; const snoutCx = headCx + dirX * (headLen * 0.5 + snoutLen * 0.4); const snoutCy = headCy + dirY * (headLen * 0.5 + snoutLen * 0.4); drawLimb(headCx, headCy, snoutCx, snoutCy, s * 0.18, shade); const noseS = s * 0.08; renderer.drawTexture( renderer.whiteTexture, snoutCx + dirX * snoutLen * 0.5 - noseS / 2, snoutCy + dirY * snoutLen * 0.5 - noseS / 2, noseS, noseS, [0.03, 0.05, 0.12, 1], ); const eyeS = s * 0.08; const eyeCx = headCx + dirX * headLen * 0.28; const eyeCy = headCy + dirY * headLen * 0.28; for (let i = 0; i < 2; i += 1) { const sign = i === 0 ? 1 : -1; renderer.drawTexture( renderer.whiteTexture, eyeCx + perpX * eyeS * 1.5 * sign - eyeS / 2, eyeCy + perpY * eyeS * 1.5 * sign - eyeS / 2, eyeS, eyeS, [0.03, 0.05, 0.12, 1], ); } const earS = s * 0.13; const earBack = dashing || winding ? 0.6 : 1; for (let i = 0; i < 2; i += 1) { const sign = i === 0 ? 1 : -1; const eax = headCx + perpX * earS * 1.55 * sign; const eay = headCy + perpY * earS * 1.55 * sign; drawLimb( eax, eay, eax - dirX * earS * earBack, eay - dirY * earS * earBack, earS * 0.5, shadeDark, ); } } }