Spaces:
Running
Running
File size: 3,374 Bytes
1390db3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
import * as THREE from 'three';
import { G } from './globals.js';
import { getTerrainHeight } from './world.js';
// Pop the enemy's helmet off and add simple physics so it drops to ground
export function popHelmet(enemy, impulseDir = new THREE.Vector3(0, 1, 0), hitPoint = null) {
if (!enemy || !enemy.helmet || !enemy.helmetAttached) return;
const h = enemy.helmet;
// Get world transform before detaching
const worldPos = new THREE.Vector3();
const worldQuat = new THREE.Quaternion();
const worldScale = new THREE.Vector3();
h.updateMatrixWorld();
h.getWorldPosition(worldPos);
h.getWorldQuaternion(worldQuat);
h.getWorldScale(worldScale);
// Detach from enemy and add to scene root
if (h.parent) h.parent.remove(h);
h.position.copy(worldPos);
h.quaternion.copy(worldQuat);
h.scale.copy(worldScale);
G.scene.add(h);
// It should no longer count as enemy geometry for ray hits
if (h.userData) {
h.userData.enemy = null;
h.userData.hitZone = null;
h.userData.isHelmet = true;
}
// Initial velocity: away from shot direction with a fun hop up
const dir = impulseDir.clone().normalize();
const upBoost = 3 + G.random() * 1.5;
const sideJitter = new THREE.Vector3((G.random() - 0.5) * 1.5, 0, (G.random() - 0.5) * 1.5);
const vel = dir.multiplyScalar(2.2).add(new THREE.Vector3(0, upBoost, 0)).add(sideJitter);
// Angular velocity for comedic spin
const angVel = new THREE.Vector3(
(G.random() - 0.5) * 6,
(G.random() - 0.5) * 8,
(G.random() - 0.5) * 6
);
G.helmets.push({
mesh: h,
pos: h.position,
vel,
angVel,
life: 12, // fade after a while
grounded: false
});
enemy.helmetAttached = false;
}
export function updateHelmets(delta) {
const gravity = 14; // stronger than arrows for punchy drop
const bounce = 0.35;
for (let i = G.helmets.length - 1; i >= 0; i--) {
const h = G.helmets[i];
// Integrate
h.vel.y -= gravity * delta;
h.pos.addScaledVector(h.vel, delta);
// Simple rotation integration
if (h.angVel) {
h.mesh.rotateX(h.angVel.x * delta);
h.mesh.rotateY(h.angVel.y * delta);
h.mesh.rotateZ(h.angVel.z * delta);
}
// Ground collision and bounce against terrain
const groundY = getTerrainHeight(h.pos.x, h.pos.z);
if (h.pos.y <= groundY) {
if (!h.grounded) {
// First impact gets a stronger bounce
h.grounded = true;
}
h.pos.y = groundY;
if (Math.abs(h.vel.y) > 0.4) {
h.vel.y = -h.vel.y * bounce;
} else {
h.vel.y = 0;
}
// Friction on ground
h.vel.x *= 0.7;
h.vel.z *= 0.7;
// Damp spin as it settles
if (h.angVel) h.angVel.multiplyScalar(0.8);
if (Math.hypot(h.vel.x, h.vel.z) < 0.2 && Math.abs(h.vel.y) < 0.2) {
h.vel.set(0, 0, 0);
if (h.angVel) h.angVel.set(0, 0, 0);
}
}
// Lifetime fade/cleanup
h.life -= delta;
if (h.life <= 2) {
const m = h.mesh.material;
if (m && m.opacity !== undefined) {
m.transparent = true;
m.opacity = Math.max(0, h.life / 2);
}
}
if (h.life <= 0) {
// Dispose per-helmet geometries
h.mesh.traverse((obj) => { if (obj.isMesh && obj.geometry?.dispose) obj.geometry.dispose(); });
G.scene.remove(h.mesh);
G.helmets.splice(i, 1);
}
}
}
|