feat: Implement 2D tactical view and refine LOS with corner detection
This commit is contained in:
@@ -47,6 +47,10 @@ export class GameRenderer {
|
||||
this.rangedGroup = new THREE.Group();
|
||||
this.scene.add(this.rangedGroup);
|
||||
|
||||
this.tokensGroup = new THREE.Group();
|
||||
this.scene.add(this.tokensGroup);
|
||||
this.tokens = new Map();
|
||||
|
||||
this.entities = new Map();
|
||||
}
|
||||
|
||||
@@ -333,6 +337,14 @@ export class GameRenderer {
|
||||
// Prevent snapping if animation is active
|
||||
if (mesh.userData.isMoving || mesh.userData.pathQueue.length > 0) return;
|
||||
mesh.position.set(entity.x, 1.56 / 2, -entity.y);
|
||||
|
||||
// Sync Token
|
||||
if (this.tokens) {
|
||||
const token = this.tokens.get(entity.id);
|
||||
if (token) {
|
||||
token.position.set(entity.x, 0.05, -entity.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,6 +371,15 @@ export class GameRenderer {
|
||||
mesh.position.x = THREE.MathUtils.lerp(data.startPos.x, data.targetPos.x, progress);
|
||||
mesh.position.z = THREE.MathUtils.lerp(data.startPos.z, data.targetPos.z, progress);
|
||||
|
||||
// Sync Token
|
||||
if (this.tokens) {
|
||||
const token = this.tokens.get(id);
|
||||
if (token) {
|
||||
token.position.x = mesh.position.x;
|
||||
token.position.z = mesh.position.z;
|
||||
}
|
||||
}
|
||||
|
||||
// Hop (Botecito)
|
||||
const jumpHeight = 0.5;
|
||||
const baseHeight = 1.56 / 2;
|
||||
@@ -1130,4 +1151,69 @@ export class GameRenderer {
|
||||
// Walls are implicit (Line just turns red and stops/passes through)
|
||||
}
|
||||
}
|
||||
showTokens(heroes, monsters) {
|
||||
this.hideTokens(); // Clear existing (makes invisible)
|
||||
if (this.tokensGroup) this.tokensGroup.visible = true; // Now force visible
|
||||
|
||||
const createToken = (entity, type, subType) => {
|
||||
const geometry = new THREE.CircleGeometry(0.35, 32);
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
color: (type === 'hero') ? 0x00BFFF : 0xDC143C, // Fallback color
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 1.0
|
||||
});
|
||||
const token = new THREE.Mesh(geometry, material);
|
||||
token.rotation.x = -Math.PI / 2;
|
||||
|
||||
const mesh3D = this.entities.get(entity.id);
|
||||
if (mesh3D) {
|
||||
token.position.set(mesh3D.position.x, 0.05, mesh3D.position.z);
|
||||
} else {
|
||||
token.position.set(entity.x, 0.05, -entity.y);
|
||||
}
|
||||
|
||||
this.tokensGroup.add(token);
|
||||
this.tokens.set(entity.id, token);
|
||||
|
||||
// White Border Ring
|
||||
const borderGeo = new THREE.RingGeometry(0.35, 0.38, 32);
|
||||
const borderMat = new THREE.MeshBasicMaterial({ color: 0xFFFFFF, side: THREE.DoubleSide });
|
||||
const border = new THREE.Mesh(borderGeo, borderMat);
|
||||
border.position.z = 0.001;
|
||||
token.add(border);
|
||||
|
||||
// Load Image
|
||||
let path = '';
|
||||
// Ensure filename is safe (though keys usually are)
|
||||
const filename = subType;
|
||||
|
||||
if (type === 'hero') {
|
||||
path = `/assets/images/dungeon1/tokens/heroes/${filename}.png`;
|
||||
} else {
|
||||
path = `/assets/images/dungeon1/tokens/enemies/${filename}.png`;
|
||||
}
|
||||
|
||||
this.getTexture(path, (texture) => {
|
||||
token.material.map = texture;
|
||||
token.material.color.setHex(0xFFFFFF); // Reset to white to show texture
|
||||
token.material.needsUpdate = true;
|
||||
}, undefined, (err) => {
|
||||
console.warn(`[GameRenderer] Token texture missing: ${path}`);
|
||||
});
|
||||
};
|
||||
|
||||
if (heroes) heroes.forEach(h => createToken(h, 'hero', h.key));
|
||||
if (monsters) monsters.forEach(m => {
|
||||
if (!m.isDead) createToken(m, 'monster', m.key);
|
||||
});
|
||||
}
|
||||
|
||||
hideTokens() {
|
||||
if (this.tokensGroup) {
|
||||
this.tokensGroup.clear();
|
||||
this.tokensGroup.visible = false;
|
||||
}
|
||||
if (this.tokens) this.tokens.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user