Files
firstProj/src/controllers/Map/Map.ts
T
2026-06-19 11:56:35 +03:00

138 lines
4.1 KiB
TypeScript

import { Box3, BoxGeometry, Mesh, Object3D, Vector3 } from "three";
import { ThreeC } from "../core/ThreeC";
import { PhysicsBody, PhysicsLayer } from "../core/PhysicsC";
import { PhysicsTriggerC } from "./PhysicsTriggerC";
import { PropC } from "../Props/PropC";
import { findDamageStateLayers } from "../Props/PropDamageLayers";
import { resolvePropType } from "../Props/PropDropTable";
import { InteractiveZoneC } from "./InteractiveZoneC";
import { ToolZoneC } from "./ToolZoneC";
const MAP_PHYSICS_LAYERS = ["Colliders", "Lootable"];
const GATHER_TRIGGER_PADDING = 0.15;
export class Map {
static init() {
const mapObject = ThreeC.getObject("map");
if (!mapObject) {
console.warn("Map model resource not found: map");
return;
}
mapObject.position.set(0, 0, 0);
ThreeC.setShadowsStateForChildren(mapObject, true, true);
ThreeC.addToScene(mapObject);
this.hideReferenceHpUi(mapObject);
PhysicsTriggerC.init();
this.buildPhysics(mapObject);
InteractiveZoneC.init(mapObject);
ToolZoneC.init(mapObject);
}
private static hideReferenceHpUi(mapObject: Object3D) {
const ui = mapObject.getObjectByName("UI");
if (ui) ui.visible = false;
const woodUi = mapObject.getObjectByName("UI_Wood001");
if (woodUi) woodUi.visible = false;
}
private static buildPhysics(mapObject: Object3D) {
for (const layerName of MAP_PHYSICS_LAYERS) {
const layer = mapObject.getObjectByName(layerName);
if (!layer) {
console.warn(`Map physics layer not found: ${layerName}`);
continue;
}
if (layerName === "Colliders") {
// Floor-only layer (BoxCollider): hide in scene, no physics needed.
layer.visible = false;
continue;
}
layer.children.forEach((lootableObject) => {
this.setupLootableObject(lootableObject);
});
}
}
private static setupLootableObject(lootableObject: Object3D) {
const propType = resolvePropType(lootableObject.name);
const damageLayers = findDamageStateLayers(lootableObject);
const maxHealth = damageLayers.length > 0 ? damageLayers.length : 1;
const prop = new PropC(lootableObject, propType, maxHealth);
prop.initDamageLayers(damageLayers);
const colliderMesh = this.findMesh(lootableObject, (name) => name.includes("collider"));
const visualMeshes = this.getVisualMeshes(lootableObject);
if (colliderMesh) {
colliderMesh.visible = false;
prop.addWallBody(
new PhysicsBody(colliderMesh, false, 0, PhysicsLayer.Wall, PhysicsLayer.Player),
);
} else {
visualMeshes.forEach((mesh) => {
prop.addWallBody(
new PhysicsBody(mesh, false, 0, PhysicsLayer.Wall, PhysicsLayer.Player),
);
});
}
PhysicsTriggerC.register(this.createGatherTrigger(lootableObject), lootableObject);
}
private static findMesh(
root: Object3D,
matcher: (name: string) => boolean,
): Mesh | null {
let found: Mesh | null = null;
root.traverse((child) => {
if (found) return;
if ((child as Mesh).isMesh && matcher(child.name.toLowerCase())) {
found = child as Mesh;
}
});
return found;
}
private static getVisualMeshes(root: Object3D): Mesh[] {
const visualMeshes: Mesh[] = [];
root.traverse((child) => {
if (!(child as Mesh).isMesh) return;
const name = child.name.toLowerCase();
if (name.includes("collider") || name.includes("trigger") || name.includes("gathertrigger")) return;
visualMeshes.push(child as Mesh);
});
return visualMeshes;
}
private static createGatherTrigger(lootableObject: Object3D): Mesh {
lootableObject.updateWorldMatrix(true, true);
const size = new Vector3();
new Box3().setFromObject(lootableObject).getSize(size);
const triggerMesh = new Mesh(
new BoxGeometry(
size.x + GATHER_TRIGGER_PADDING * 2,
size.y + GATHER_TRIGGER_PADDING * 2,
size.z + GATHER_TRIGGER_PADDING * 2,
),
);
triggerMesh.visible = false;
triggerMesh.name = "GatherTrigger";
lootableObject.add(triggerMesh);
return triggerMesh;
}
}