victor's picture
victor HF Staff
Initial commit
b29710c
import * as THREE from "three";
import { PATH_POINTS, GRID_CELL_SIZE } from "../config/gameConfig.js";
// Snap value to grid (to nearest grid line)
export function snapToGrid(value, size = GRID_CELL_SIZE) {
return Math.round(value / size) * size;
}
// Grid helpers to work with cell centers
export function worldToCell(x, z, size = GRID_CELL_SIZE) {
// Map world coords to integer col/row indices
const col = Math.floor(x / size);
const row = Math.floor(z / size);
return { col, row };
}
export function cellToWorldCenter(col, row, size = GRID_CELL_SIZE) {
// Center of the tile: (col + 0.5, row + 0.5) * size
return new THREE.Vector3((col + 0.5) * size, 0, (row + 0.5) * size);
}
// Check if point is on road
export function isOnRoad(p, pathPoints = PATH_POINTS) {
const halfWidth = 1.6;
for (let i = 0; i < pathPoints.length - 1; i++) {
const a = pathPoints[i];
const b = pathPoints[i + 1];
if (pointSegmentDistance2D(p, a, b) <= halfWidth) return true;
}
return false;
}
// Calculate distance from point to line segment in 2D
export function pointSegmentDistance2D(p, a, b) {
const apx = p.x - a.x,
apz = p.z - a.z;
const abx = b.x - a.x,
abz = b.z - a.z;
const abLenSq = abx * abx + abz * abz;
let t = 0;
if (abLenSq > 0) t = (apx * abx + apz * abz) / abLenSq;
t = Math.max(0, Math.min(1, t));
const cx = a.x + t * abx,
cz = a.z + t * abz;
const dx = p.x - cx,
dz = p.z - cz;
return Math.hypot(dx, dz);
}
// Simple particle/hit effect system
export class EffectSystem {
constructor(scene) {
this.scene = scene;
this.effects = [];
}
spawnHitEffect(pos) {
const geo = new THREE.SphereGeometry(0.2, 6, 6);
const mat = new THREE.MeshBasicMaterial({
color: 0xfff176,
transparent: true,
opacity: 0.9,
});
const m = new THREE.Mesh(geo, mat);
m.position.copy(pos);
this.scene.add(m);
this.effects.push({ mesh: m, life: 0.25 });
}
update(dt) {
for (let i = this.effects.length - 1; i >= 0; i--) {
const e = this.effects[i];
e.life -= dt;
e.mesh.scale.addScalar(6 * dt);
e.mesh.material.opacity = Math.max(0, e.life / 0.25);
if (e.life <= 0) {
this.scene.remove(e.mesh);
this.effects.splice(i, 1);
}
}
}
}