Fix enemy facing direction and add player damage feedback
This commit is contained in:
+79
-1
@@ -96,8 +96,17 @@ export class Game {
|
||||
private uiOfferIcons: HTMLElement[] = [];
|
||||
private uiOfferTitles: HTMLElement[] = [];
|
||||
private uiOfferDescs: HTMLElement[] = [];
|
||||
private uiDamageVignette!: HTMLElement;
|
||||
private uiLowHpVignette!: HTMLElement;
|
||||
private mouseOnCanvas = false;
|
||||
|
||||
private bloodParticles: {
|
||||
mesh: THREE.Mesh;
|
||||
vel: THREE.Vector3;
|
||||
life: number;
|
||||
mat: THREE.MeshBasicMaterial;
|
||||
}[] = [];
|
||||
|
||||
constructor() {
|
||||
this.scene = new THREE.Scene();
|
||||
this.scene.background = new THREE.Color(0x0d1117);
|
||||
@@ -178,6 +187,8 @@ export class Game {
|
||||
this.uiLevelUp = document.getElementById('level-up')!;
|
||||
this.uiConfirmBtn = document.getElementById('btn-confirm') as HTMLButtonElement;
|
||||
this.uiConfirmBtn.addEventListener('click', () => this.confirmOffer());
|
||||
this.uiDamageVignette = document.getElementById('damage-vignette')!;
|
||||
this.uiLowHpVignette = document.getElementById('low-hp-vignette')!;
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const card = document.getElementById(`offer-${i}`)!;
|
||||
@@ -869,6 +880,71 @@ export class Game {
|
||||
if (this.player.health <= 0 && this.state === 'playing') {
|
||||
this.gameOver();
|
||||
}
|
||||
|
||||
this.uiLowHpVignette.classList.toggle(
|
||||
'active', healthPct < 0.3 && healthPct > 0
|
||||
);
|
||||
}
|
||||
|
||||
onPlayerHurt(sourcePos: THREE.Vector3) {
|
||||
const el = this.uiDamageVignette;
|
||||
el.classList.remove('hurt');
|
||||
void el.offsetWidth;
|
||||
el.classList.add('hurt');
|
||||
|
||||
const away = new THREE.Vector3()
|
||||
.subVectors(this.player.body.position, sourcePos);
|
||||
away.y = 0;
|
||||
if (away.lengthSq() < 0.01) {
|
||||
away.set(Math.random() - 0.5, 0, Math.random() - 0.5);
|
||||
}
|
||||
away.normalize();
|
||||
|
||||
const geom = new THREE.SphereGeometry(0.07, 6, 6);
|
||||
const count = 12;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const mat = new THREE.MeshBasicMaterial({
|
||||
color: 0xcc2222,
|
||||
transparent: true,
|
||||
});
|
||||
const mesh = new THREE.Mesh(geom, mat);
|
||||
mesh.position.copy(this.player.body.position);
|
||||
mesh.position.y += 0.5;
|
||||
this.scene.add(mesh);
|
||||
this.bloodParticles.push({
|
||||
mesh,
|
||||
mat,
|
||||
vel: away.clone()
|
||||
.multiplyScalar(2 + Math.random() * 3)
|
||||
.add(new THREE.Vector3(
|
||||
(Math.random() - 0.5) * 1.5,
|
||||
3 + Math.random() * 3,
|
||||
(Math.random() - 0.5) * 1.5
|
||||
)),
|
||||
life: 0.5 + Math.random() * 0.3,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateBloodParticles(dt: number) {
|
||||
for (let i = this.bloodParticles.length - 1; i >= 0; i--) {
|
||||
const p = this.bloodParticles[i];
|
||||
p.life -= dt;
|
||||
if (p.life <= 0) {
|
||||
this.scene.remove(p.mesh);
|
||||
p.mesh.geometry.dispose();
|
||||
p.mat.dispose();
|
||||
this.bloodParticles.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
p.vel.y -= 14 * dt;
|
||||
p.mesh.position.addScaledVector(p.vel, dt);
|
||||
if (p.mesh.position.y < 0.06) {
|
||||
p.mesh.position.y = 0.06;
|
||||
p.vel.set(0, 0, 0);
|
||||
}
|
||||
p.mat.opacity = Math.max(0, p.life / 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
private animate() {
|
||||
@@ -889,6 +965,8 @@ export class Game {
|
||||
}
|
||||
}
|
||||
|
||||
this.updateBloodParticles(dt);
|
||||
|
||||
// Update animations
|
||||
this.player.updateAnimations(dt);
|
||||
this.player.rightHandWeapon.updateAnimation(dt);
|
||||
@@ -919,7 +997,7 @@ export class Game {
|
||||
if (enemy.dead) continue;
|
||||
const dist = enemy.body.position.distanceTo(this.player.body.position);
|
||||
if (dist < enemy.contactRadius) {
|
||||
this.player.damage(enemy.contactDps * dt);
|
||||
this.player.damage(enemy.contactDps * dt, enemy.body.position);
|
||||
if (thorns > 0) {
|
||||
enemy.takeDamage(thorns * dt, this, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user