initial commit
This commit is contained in:
commit
e8c3f9862a
193 files changed
+29795
No files matched your search
@@ -0,0 +1,377 @@
|
||||
import { GameObject } from '../engine/GameObject.js';
|
||||
import { Vector2D } from '../engine/Vector2D.js';
|
||||
|
||||
export class Zombie extends GameObject {
|
||||
constructor(
|
||||
x,
|
||||
y,
|
||||
target,
|
||||
{
|
||||
container,
|
||||
viewport,
|
||||
speed = 110,
|
||||
size = 24,
|
||||
color = [0.15, 0.65, 0.2, 1],
|
||||
health = 1,
|
||||
knockback = 260,
|
||||
knockbackDamping = 10,
|
||||
scoreValue = 1,
|
||||
emergeTime = 0,
|
||||
onDeath = null,
|
||||
} = {},
|
||||
) {
|
||||
super(x, y);
|
||||
this.target = target;
|
||||
this.container = container;
|
||||
this.viewport = viewport;
|
||||
this.speed = speed;
|
||||
this.size = size;
|
||||
this.baseColor = [...color];
|
||||
this.health = health;
|
||||
this.maxHealth = health;
|
||||
this.knockback = knockback;
|
||||
this.knockbackDamping = knockbackDamping;
|
||||
this.scoreValue = scoreValue;
|
||||
this.onDeath = onDeath;
|
||||
this._hitKnockback = new Vector2D(0, 0);
|
||||
this._flash = 0;
|
||||
this._time = 0;
|
||||
this._broken = Math.random() * Math.PI * 2;
|
||||
this._emergeTotal = emergeTime;
|
||||
this._emerge = emergeTime;
|
||||
this.width = size;
|
||||
this.height = size;
|
||||
}
|
||||
|
||||
die() {
|
||||
if (this.onDeath) {
|
||||
this.onDeath(this);
|
||||
}
|
||||
this.remove(this.container);
|
||||
}
|
||||
|
||||
hit(impulse, damage = 1) {
|
||||
this.health -= damage;
|
||||
this._hitKnockback = impulse.mulS(this.knockback);
|
||||
this._flash = 0.08;
|
||||
return this.health <= 0;
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
this._time += dt;
|
||||
if (this._updateEmerge(dt)) return;
|
||||
const delta = this.target.pos.subV(this.pos);
|
||||
const distance = delta.length();
|
||||
let chaseVelocity = new Vector2D(0, 0);
|
||||
if (distance > 1) {
|
||||
chaseVelocity = delta.divS(distance).mulS(this.speed);
|
||||
}
|
||||
|
||||
this.velocity = chaseVelocity.addV(this._hitKnockback);
|
||||
super.update(dt);
|
||||
|
||||
this._hitKnockback = this._hitKnockback.mulS(Math.max(0, 1 - dt * this.knockbackDamping));
|
||||
this._flash = Math.max(0, this._flash - dt);
|
||||
}
|
||||
|
||||
_updateEmerge(dt) {
|
||||
if (this._emerge <= 0) return false;
|
||||
this._emerge -= dt;
|
||||
if (this._emerge <= 0) {
|
||||
this._emerge = 0;
|
||||
if (this._onEmergenceComplete) this._onEmergenceComplete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_emergeSize() {
|
||||
if (this._emerge > 0 && this._emergeTotal > 0) {
|
||||
return 0.3 + 0.7 * (1 - this._emerge / this._emergeTotal);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
_drawEmerge(renderer) {
|
||||
if (this._emerge <= 0) return;
|
||||
const gear = 1 - this._emerge / this._emergeTotal;
|
||||
renderer.setBlendMode('additive');
|
||||
const g = this.size * (3.4 - gear * 1.4);
|
||||
renderer.drawTexture(
|
||||
renderer.glowTexture,
|
||||
this.pos.x - g / 2,
|
||||
this.pos.y - g / 2,
|
||||
g,
|
||||
g,
|
||||
[this.baseColor[0], this.baseColor[1], this.baseColor[2], 0.5 * (1 - gear)],
|
||||
);
|
||||
renderer.setBlendMode('normal');
|
||||
}
|
||||
|
||||
draw(renderer) {
|
||||
this.width = this.size;
|
||||
this.height = this.size;
|
||||
|
||||
let color = this.baseColor;
|
||||
if (this._flash > 0) {
|
||||
color = [1, 1, 1, 1];
|
||||
} else if (this.maxHealth > 1) {
|
||||
const hpRatio = this.health / this.maxHealth;
|
||||
color = [
|
||||
this.baseColor[0] + (1 - this.baseColor[0]) * (1 - hpRatio),
|
||||
this.baseColor[1] * hpRatio,
|
||||
this.baseColor[2] * hpRatio,
|
||||
1,
|
||||
];
|
||||
}
|
||||
|
||||
if (this.maxHealth > 1) {
|
||||
this._drawJuggernaut(renderer, color);
|
||||
return;
|
||||
}
|
||||
|
||||
this._drawEmerge(renderer);
|
||||
const s = this.size * this._emergeSize();
|
||||
const lean = s <= 16;
|
||||
const facing = Math.atan2(
|
||||
this.target.pos.y - this.pos.y,
|
||||
this.target.pos.x - this.pos.x,
|
||||
);
|
||||
const cosF = Math.cos(facing);
|
||||
const sinF = Math.sin(facing);
|
||||
const walk = Math.sin(this._time * (lean ? 14 : 10) + this._broken);
|
||||
|
||||
const shade = [color[0] * 0.6, color[1] * 0.6, color[2] * 0.6, 1];
|
||||
const shadeDark = [color[0] * 0.38, color[1] * 0.38, color[2] * 0.38, 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),
|
||||
);
|
||||
};
|
||||
|
||||
if (!lean) {
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x - s * 0.62,
|
||||
this.pos.y - s * 0.5 + s * 0.06,
|
||||
s * 1.24,
|
||||
s * 0.92,
|
||||
[0, 0, 0, 0.35],
|
||||
);
|
||||
}
|
||||
|
||||
const legLen = s * (lean ? 0.32 : 0.4);
|
||||
const legSpread = s * 0.22;
|
||||
const hipX = this.pos.x - cosF * s * 0.18;
|
||||
const hipY = this.pos.y - sinF * s * 0.18;
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
const sign = i === 0 ? 1 : -1;
|
||||
const step = sign * walk * s * (lean ? 0.3 : 0.18);
|
||||
const footX = hipX + cosF * legLen - sinF * legSpread * sign + cosF * step;
|
||||
const footY = hipY + sinF * legLen + cosF * legSpread * sign + sinF * step;
|
||||
drawLimb(hipX, hipY, footX, footY, s * 0.15, shadeDark);
|
||||
}
|
||||
|
||||
const torsoW = s * (lean ? 0.5 : 0.62);
|
||||
const torsoH = s * (lean ? 0.46 : 0.55);
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x - torsoW / 2,
|
||||
this.pos.y - torsoH / 2,
|
||||
torsoW,
|
||||
torsoH,
|
||||
color,
|
||||
);
|
||||
|
||||
const armLen = s * 0.5;
|
||||
const armSpread = s * 0.27;
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
const sign = i === 0 ? 1 : -1;
|
||||
const shX = this.pos.x - sinF * armSpread * sign;
|
||||
const shY = this.pos.y + cosF * armSpread * sign - s * 0.02;
|
||||
const reach = 1 + walk * 0.1;
|
||||
const handX = shX + cosF * armLen * reach;
|
||||
const handY = shY + sinF * armLen * reach;
|
||||
drawLimb(shX, shY, handX, handY, s * 0.17, shade);
|
||||
}
|
||||
|
||||
const headSize = s * (lean ? 0.4 : 0.44);
|
||||
const headX = this.pos.x;
|
||||
const headY = this.pos.y - s * (lean ? 0.34 : 0.4) + walk * s * 0.03;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
headX - headSize / 2,
|
||||
headY - headSize / 2,
|
||||
headSize,
|
||||
headSize,
|
||||
light,
|
||||
);
|
||||
|
||||
const eyeSize = headSize * 0.2;
|
||||
const eyeSpread = headSize * 0.24;
|
||||
const look = lean ? 0.1 : 0.04;
|
||||
const eyeY = headY + sinF * headSize * look;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
headX - eyeSize / 2 - sinF * eyeSpread,
|
||||
eyeY - eyeSize / 2 + cosF * eyeSpread,
|
||||
eyeSize,
|
||||
eyeSize,
|
||||
[0.05, 0.05, 0.05, 1],
|
||||
);
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
headX - eyeSize / 2 + sinF * eyeSpread,
|
||||
eyeY - eyeSize / 2 - cosF * eyeSpread,
|
||||
eyeSize,
|
||||
eyeSize,
|
||||
[0.05, 0.05, 0.05, 1],
|
||||
);
|
||||
}
|
||||
|
||||
_drawJuggernaut(renderer, color) {
|
||||
const s = this.size;
|
||||
const facing = Math.atan2(
|
||||
this.target.pos.y - this.pos.y,
|
||||
this.target.pos.x - this.pos.x,
|
||||
);
|
||||
const cosF = Math.cos(facing);
|
||||
const sinF = Math.sin(facing);
|
||||
const walk = Math.sin(this._time * 5 + this._broken);
|
||||
|
||||
const shade = [color[0] * 0.6, color[1] * 0.6, color[2] * 0.6, 1];
|
||||
const shadeDark = [color[0] * 0.38, color[1] * 0.38, color[2] * 0.38, 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 mix = (a, b, t) => a + (b - a) * t;
|
||||
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x - s * 0.78,
|
||||
this.pos.y - s * 0.55 + s * 0.08,
|
||||
s * 1.56,
|
||||
s * 1.14,
|
||||
[0, 0, 0, 0.35],
|
||||
);
|
||||
|
||||
const footSize = s * 0.16;
|
||||
const footBaseY = this.pos.y + s * 0.46;
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
const sign = i === 0 ? 1 : -1;
|
||||
const stepX = -sinF * s * 0.2 * sign + cosF * walk * s * 0.06 * sign;
|
||||
const stepY = cosF * s * 0.2 * sign + sinF * walk * s * 0.06 * sign;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x + stepX - footSize / 2,
|
||||
footBaseY + stepY - footSize / 2,
|
||||
footSize,
|
||||
footSize,
|
||||
shadeDark,
|
||||
);
|
||||
}
|
||||
|
||||
const shellW = s * 1.0;
|
||||
const shellH = s * 0.95;
|
||||
const bottomY = this.pos.y + s * 0.4;
|
||||
const topY = this.pos.y - s * 0.5;
|
||||
const layers = 6;
|
||||
for (let i = 0; i < layers; i += 1) {
|
||||
const u = i / (layers - 1);
|
||||
const w = shellW * (1 - u * 0.66);
|
||||
const h = (shellH / layers) * 1.4;
|
||||
const cy = bottomY - u * (bottomY - topY) + walk * s * 0.02;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x - w / 2,
|
||||
cy - h / 2,
|
||||
w,
|
||||
h,
|
||||
[
|
||||
mix(shadeDark[0], light[0], u),
|
||||
mix(shadeDark[1], light[1], u),
|
||||
mix(shadeDark[2], light[2], u),
|
||||
1,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
const rx = this.pos.x + (i - 1) * s * 0.16;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
rx - s * 0.045,
|
||||
topY + s * 0.04,
|
||||
s * 0.09,
|
||||
shellH * 0.9,
|
||||
shadeDark,
|
||||
);
|
||||
}
|
||||
|
||||
const rivet = s * 0.055;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x - s * 0.44,
|
||||
bottomY - rivet * 0.4,
|
||||
rivet,
|
||||
rivet,
|
||||
shadeDark,
|
||||
);
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x + s * 0.44 - rivet,
|
||||
bottomY - rivet * 0.4,
|
||||
rivet,
|
||||
rivet,
|
||||
shadeDark,
|
||||
);
|
||||
|
||||
const headSize = s * 0.24;
|
||||
const headX = this.pos.x + cosF * s * 0.52;
|
||||
const headY = this.pos.y + sinF * s * 0.52;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
headX - headSize / 2,
|
||||
headY - headSize / 2,
|
||||
headSize,
|
||||
headSize,
|
||||
light,
|
||||
);
|
||||
|
||||
const eyeSize = headSize * 0.22;
|
||||
const eyeSpread = headSize * 0.3;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
headX - eyeSize / 2 - sinF * eyeSpread,
|
||||
headY - eyeSize / 2 + cosF * eyeSpread,
|
||||
eyeSize,
|
||||
eyeSize,
|
||||
[0.05, 0.05, 0.05, 1],
|
||||
);
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
headX - eyeSize / 2 + sinF * eyeSpread,
|
||||
headY - eyeSize / 2 - cosF * eyeSpread,
|
||||
eyeSize,
|
||||
eyeSize,
|
||||
[0.05, 0.05, 0.05, 1],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user