138 lines
4.1 KiB
TypeScript
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;
|
|
}
|
|
}
|