initial commit
This commit is contained in:
commit
e8c3f9862a
193 files changed
+29795
No files matched your search
@@ -0,0 +1,265 @@
|
||||
import { Zombie } from './Zombie.js';
|
||||
import { GameObject } from '../engine/GameObject.js';
|
||||
import { Vector2D } from '../engine/Vector2D.js';
|
||||
import { WitchProjectile } from './WitchProjectile.js';
|
||||
|
||||
export class Witch extends Zombie {
|
||||
constructor(
|
||||
x,
|
||||
y,
|
||||
target,
|
||||
{
|
||||
container,
|
||||
viewport,
|
||||
speed = 85,
|
||||
size = 28,
|
||||
color = [0.55, 0.2, 0.8, 1],
|
||||
health = 5,
|
||||
knockback = 180,
|
||||
scoreValue = 4,
|
||||
preferredDistance = 220,
|
||||
fireInterval = 2.2,
|
||||
projectileSpeed = 220,
|
||||
projectileCount = 1,
|
||||
projectileSpread = 0.08,
|
||||
projectileColor = [0.7, 0.2, 1, 1],
|
||||
emergeTime = 0,
|
||||
spawnProjectile = (p) => {},
|
||||
onDeath = null,
|
||||
} = {},
|
||||
) {
|
||||
super(x, y, target, {
|
||||
container,
|
||||
viewport,
|
||||
speed,
|
||||
size,
|
||||
color,
|
||||
health,
|
||||
knockback,
|
||||
scoreValue,
|
||||
emergeTime,
|
||||
onDeath,
|
||||
});
|
||||
this.preferredDistance = preferredDistance;
|
||||
this.fireInterval = fireInterval;
|
||||
this.projectileSpeed = projectileSpeed;
|
||||
this.projectileCount = projectileCount;
|
||||
this.projectileSpread = projectileSpread;
|
||||
this.projectileColor = projectileColor;
|
||||
this.spawnProjectile = spawnProjectile;
|
||||
this._fireTimer = 0;
|
||||
this._time = 0;
|
||||
this._cast = 0;
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
this._time += dt;
|
||||
this._cast = Math.max(0, this._cast - dt);
|
||||
|
||||
if (this._updateEmerge(dt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fireTimer -= dt;
|
||||
|
||||
const delta = this.target.pos.subV(this.pos);
|
||||
const distance = delta.length();
|
||||
|
||||
let move = new Vector2D(0, 0);
|
||||
if (distance > 1) {
|
||||
const dir = delta.divS(distance);
|
||||
if (distance < this.preferredDistance) {
|
||||
move = dir.mulS(-this.speed);
|
||||
} else if (distance > this.preferredDistance + 40) {
|
||||
move = dir.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);
|
||||
|
||||
if (this._fireTimer <= 0 && this.target.health > 0 && distance < 480) {
|
||||
this._fireTimer = this.fireInterval * (0.8 + Math.random() * 0.4);
|
||||
this.fire();
|
||||
}
|
||||
}
|
||||
|
||||
_onEmergenceComplete() {
|
||||
this._fireTimer = this.fireInterval * (0.4 + Math.random() * 0.4);
|
||||
}
|
||||
|
||||
fire() {
|
||||
this._cast = 0.22;
|
||||
const aim = this.target.pos.subV(this.pos);
|
||||
const aimAngle = aim.lengthSqrt() > 1
|
||||
? Math.atan2(aim.y, aim.x)
|
||||
: this._time;
|
||||
|
||||
const offset = ((this.projectileCount - 1) / 2) * this.projectileSpread;
|
||||
for (let i = 0; i < this.projectileCount; i += 1) {
|
||||
const angle = aimAngle - offset + i * this.projectileSpread;
|
||||
const direction = new Vector2D(Math.cos(angle), Math.sin(angle));
|
||||
const projectile = new WitchProjectile(this.pos.x, this.pos.y, direction, {
|
||||
container: this.container,
|
||||
viewport: this.viewport,
|
||||
speed: this.projectileSpeed,
|
||||
color: this.projectileColor,
|
||||
});
|
||||
this.spawnProjectile(projectile);
|
||||
}
|
||||
}
|
||||
|
||||
draw(renderer) {
|
||||
this._drawEmerge(renderer);
|
||||
let s = this.size * this._emergeSize();
|
||||
|
||||
let bodyColor = this.baseColor;
|
||||
if (this._flash > 0) {
|
||||
bodyColor = [1, 1, 1, 1];
|
||||
} else if (this.maxHealth > 1) {
|
||||
const hpRatio = this.health / this.maxHealth;
|
||||
bodyColor = [
|
||||
this.baseColor[0] + (1 - this.baseColor[0]) * (1 - hpRatio),
|
||||
this.baseColor[1] * hpRatio,
|
||||
this.baseColor[2] * hpRatio,
|
||||
1,
|
||||
];
|
||||
}
|
||||
|
||||
const bob = Math.sin(this._time * 3.4) * s * 0.09;
|
||||
const sway = Math.sin(this._time * 1.7) * 0.09;
|
||||
const castRatio = Math.min(1, this._cast / 0.22);
|
||||
const charge = Math.max(
|
||||
0,
|
||||
Math.min(1, (this.fireInterval - this._fireTimer) / this.fireInterval),
|
||||
);
|
||||
const targetSide = this.target.pos.x < this.pos.x ? -1 : 1;
|
||||
const flightSide = Math.abs(this.velocity.x) > 20
|
||||
? (this.velocity.x < 0 ? -1 : 1)
|
||||
: targetSide;
|
||||
const facing = flightSide;
|
||||
const lean = facing * s * 0.05;
|
||||
|
||||
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 robe = s * 0.9;
|
||||
const robeW = robe * (1 + Math.sin(this._time * 3.4) * 0.05);
|
||||
const cy = this.pos.y + bob;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x + lean - robeW / 2,
|
||||
cy - robe / 2,
|
||||
robeW,
|
||||
robe,
|
||||
bodyColor,
|
||||
);
|
||||
|
||||
const armColor = [bodyColor[0] * 0.72, bodyColor[1] * 0.72, bodyColor[2] * 0.72, 1];
|
||||
const armW = s * 0.16;
|
||||
const armLen = s * (0.3 + castRatio * 0.26);
|
||||
const armSpread = s * (0.36 + castRatio * 0.14);
|
||||
const armY = cy - robe * 0.2 - castRatio * s * 0.14;
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
const sign = i === 0 ? 1 : -1;
|
||||
const raise = Math.sin(this._time * 6) * castRatio * s * 0.06;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
this.pos.x + sign * armSpread - armW / 2,
|
||||
armY - armLen / 2 + sign * raise,
|
||||
armW,
|
||||
armLen,
|
||||
armColor,
|
||||
);
|
||||
}
|
||||
|
||||
const hatW = s * 0.7;
|
||||
const hatH = s * 0.9;
|
||||
const hatCx = this.pos.x + lean + sway * s * 0.14;
|
||||
const hatCy = cy - robe / 2 - hatH * 0.55 + bob * 0.4;
|
||||
const hatAngle = sway * 0.8 + castRatio * -0.08;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
hatCx - hatW / 2,
|
||||
hatCy - hatH / 2,
|
||||
hatW,
|
||||
hatH,
|
||||
[0.15, 0.08, 0.2, 1],
|
||||
hatAngle,
|
||||
);
|
||||
|
||||
const brimW = s * 0.98;
|
||||
const brimH = s * 0.14;
|
||||
renderer.drawTexture(
|
||||
renderer.whiteTexture,
|
||||
hatCx - brimW / 2,
|
||||
hatCy + hatH * 0.1 - brimH / 2,
|
||||
brimW,
|
||||
brimH,
|
||||
[0.15, 0.08, 0.2, 1],
|
||||
hatAngle,
|
||||
);
|
||||
|
||||
const seatY = cy + robe * 0.14 + Math.sin(this._time * 2.3) * s * 0.02;
|
||||
const seatX = this.pos.x + lean;
|
||||
const handleX = seatX + facing * s * 0.5;
|
||||
const bristleX = seatX - facing * s * 0.24;
|
||||
drawLimb(handleX, seatY, bristleX, seatY, s * 0.13, [0.4, 0.3, 0.15, 1]);
|
||||
drawLimb(bristleX, seatY - s * 0.02, bristleX - facing * s * 0.14, seatY - s * 0.09, s * 0.18, [0.62, 0.48, 0.24, 1]);
|
||||
|
||||
if (charge > 0.25) {
|
||||
const eyeFade = (charge - 0.25) / 0.75;
|
||||
const eyeY = hatCy + hatH * 0.32;
|
||||
const eyeSpread = s * 0.13;
|
||||
const eyeS = s * 0.11;
|
||||
const eyeCx = this.pos.x + lean + targetSide * s * 0.06;
|
||||
renderer.setBlendMode('additive');
|
||||
renderer.drawTexture(
|
||||
renderer.glowTexture,
|
||||
eyeCx - eyeSpread - eyeS,
|
||||
eyeY - eyeS,
|
||||
eyeS * 2,
|
||||
eyeS * 2,
|
||||
[0.85, 0.35, 1, 0.75 * eyeFade],
|
||||
);
|
||||
renderer.drawTexture(
|
||||
renderer.glowTexture,
|
||||
eyeCx + eyeSpread - eyeS,
|
||||
eyeY - eyeS,
|
||||
eyeS * 2,
|
||||
eyeS * 2,
|
||||
[0.85, 0.35, 1, 0.75 * eyeFade],
|
||||
);
|
||||
renderer.setBlendMode('normal');
|
||||
}
|
||||
|
||||
if (castRatio > 0) {
|
||||
renderer.setBlendMode('additive');
|
||||
const fx = this.pos.x;
|
||||
const fy = hatCy - hatH * 0.72;
|
||||
renderer.drawTexture(
|
||||
renderer.glowTexture,
|
||||
fx - s * 0.5,
|
||||
fy - s * 0.5,
|
||||
s,
|
||||
s,
|
||||
[0.8, 0.4, 1, 0.9 * castRatio],
|
||||
);
|
||||
renderer.setBlendMode('normal');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user