From 45539a851a3da8e49f80ccbf34b01926ca7624e1 Mon Sep 17 00:00:00 2001 From: Constantin Wastchenko <80072466+cwastche@users.noreply.github.com> Date: Sun, 11 Aug 2024 21:12:15 +0200 Subject: [PATCH] Fix/models (#1191) * Load structure models * Fix explore if no capacity * Remove biome models if struct on tile * rebase * fix knip --- .../dojo/modelManager/ArmyMovementManager.ts | 26 ++++ .../src/three/components/StructureManager.ts | 138 ++++++++++++------ client/src/three/scenes/Worldmap.ts | 25 +++- 3 files changed, 142 insertions(+), 47 deletions(-) diff --git a/client/src/dojo/modelManager/ArmyMovementManager.ts b/client/src/dojo/modelManager/ArmyMovementManager.ts index df7bbcb10..d32795528 100644 --- a/client/src/dojo/modelManager/ArmyMovementManager.ts +++ b/client/src/dojo/modelManager/ArmyMovementManager.ts @@ -73,6 +73,8 @@ export class ArmyMovementManager { private positionModel: OverridableComponent; private armyModel: Component; private ownerModel: Component; + private capacityModel: Component; + private weightModel: Component; private entityOwnerModel: Component; private staminaConfigModel: Component; private entity: Entity; @@ -91,6 +93,8 @@ export class ArmyMovementManager { Position, Army, Owner, + Capacity, + Weight, EntityOwner, StaminaConfig, Production, @@ -102,6 +106,8 @@ export class ArmyMovementManager { this.positionModel = Position; this.armyModel = Army; this.ownerModel = Owner; + this.capacityModel = Capacity; + this.weightModel = Weight; this.entityOwnerModel = EntityOwner; this.staminaConfigModel = StaminaConfig; this.entity = getEntityIdFromKeys([BigInt(entityId)]); @@ -189,6 +195,10 @@ export class ArmyMovementManager { return false; } + if (this._getArmyRemainingCapacity() < EternumGlobalConfig.exploration.reward) { + return false; + } + return true; } @@ -395,4 +405,20 @@ export class ArmyMovementManager { this._travelToHex(path); } }; + + private _getArmyRemainingCapacity = () => { + const armyCapacity = getComponentValue(this.capacityModel, this.entity); + const armyWeight = getComponentValue(this.weightModel, this.entity); + const armyEntity = getComponentValue(this.armyModel, this.entity); + + const knights = armyEntity?.troops.knight_count || 0n; + const crossbowmen = armyEntity?.troops.crossbowman_count || 0n; + const paladins = armyEntity?.troops.paladin_count || 0n; + const troopQty = (knights + crossbowmen + paladins) / BigInt(EternumGlobalConfig.resources.resourcePrecision); + + const capacity = armyCapacity?.weight_gram || 0n; + const weight = (armyWeight?.value || 0n) / BigInt(EternumGlobalConfig.resources.resourcePrecision); + + return capacity * troopQty - weight; + }; } diff --git a/client/src/three/components/StructureManager.ts b/client/src/three/components/StructureManager.ts index 7613b2104..61a9f275f 100644 --- a/client/src/three/components/StructureManager.ts +++ b/client/src/three/components/StructureManager.ts @@ -5,85 +5,137 @@ import { LabelManager } from "./LabelManager"; import { getWorldPositionForHex } from "@/ui/utils/utils"; import { StructureSystemUpdate } from "../systems/types"; import { FELT_CENTER } from "@/ui/config"; -import { ID } from "@bibliothecadao/eternum"; +import { ID, StructureType } from "@bibliothecadao/eternum"; const neutralColor = new THREE.Color(0xffffff); const myColor = new THREE.Color("lime"); -const MODEL_PATH = "models/buildings/castle2.glb"; -const LABEL_PATH = "textures/realm_label.png"; +const StructureModelPaths: Record = { + [StructureType.Realm]: "models/buildings/castle2.glb", + [StructureType.Hyperstructure]: "models/buildings/farm.glb", // USING PLACEHOLDER MODEL + // [StructureType.Hyperstructure]: "models/buildings/hyperstructure-half-transformed.glb", + // [StructureType.Hyperstructure]: "models/buildings/hyperstructure.glb", + [StructureType.Bank]: "models/buildings/bank.glb", + [StructureType.FragmentMine]: "models/buildings/mine.glb", + [StructureType.Settlement]: "", +}; + +const StructureLabelPaths: Record = { + [StructureType.Realm]: "textures/realm_label.png", + [StructureType.Hyperstructure]: "textures/hyper_label.png", + [StructureType.FragmentMine]: "textures/shard_label.png", + [StructureType.Bank]: "", + [StructureType.Settlement]: "", +}; + const MAX_INSTANCES = 1000; export class StructureManager { private scene: THREE.Scene; - private instancedModel: InstancedModel | undefined; + private structureModels: Map = new Map(); + private labelManagers: Map = new Map(); private dummy: THREE.Object3D = new THREE.Object3D(); - private isLoaded: boolean = false; - loadPromise: Promise; + modelLoadPromises: Promise[] = []; structures: Structures = new Structures(); - private labelManager: LabelManager; + structuresMap: Map> = new Map(); + totalStructures: number = 0; constructor(scene: THREE.Scene) { this.scene = scene; - this.labelManager = new LabelManager(LABEL_PATH); - - this.loadPromise = new Promise((resolve, reject) => { - const loader = new GLTFLoader(); - loader.load( - MODEL_PATH, - (gltf) => { - const model = gltf.scene as THREE.Group; - this.instancedModel = new InstancedModel(model, MAX_INSTANCES); - this.instancedModel.setCount(0); - this.scene.add(this.instancedModel.group); - this.isLoaded = true; - resolve(); - }, - undefined, - (error) => { - console.error("An error occurred while loading the model:", error); - reject(error); - }, - ); - }); + this.loadModels(); + } + + public async loadModels() { + const loader = new GLTFLoader(); + + for (const [key, modelPath] of Object.entries(StructureModelPaths)) { + const structureType = StructureType[key as keyof typeof StructureType]; + + if (structureType === undefined) continue; + if (!modelPath) continue; + + const loadPromise = new Promise((resolve, reject) => { + loader.load( + modelPath, + (gltf) => { + const model = gltf.scene as THREE.Group; + + const instancedModel = new InstancedModel(model, MAX_INSTANCES); + instancedModel.setCount(0); + this.structureModels.set(structureType, instancedModel); + this.scene.add(instancedModel.group); + + const labelManager = new LabelManager( + StructureLabelPaths[StructureType[structureType] as unknown as StructureType], + ); + this.labelManagers.set(structureType, labelManager); + resolve(); + }, + undefined, + (error) => { + console.error(`An error occurred while loading the ${StructureType[structureType as any]} model:`, error); + reject(error); + }, + ); + }); + this.modelLoadPromises.push(loadPromise); + } } async onUpdate(update: StructureSystemUpdate) { - await this.loadPromise; - const { entityId, hexCoords, isMine } = update; + await Promise.all(this.modelLoadPromises); + + const { entityId, hexCoords, isMine, structureType } = update; const normalizedCoord = { col: hexCoords.col - FELT_CENTER, row: hexCoords.row - FELT_CENTER }; const position = getWorldPositionForHex(normalizedCoord); this.dummy.position.copy(position); this.dummy.updateMatrix(); + if (!this.structuresMap.has(normalizedCoord.col)) { + this.structuresMap.set(normalizedCoord.col, new Set()); + } + if (!this.structuresMap.get(normalizedCoord.col)!.has(normalizedCoord.row)) { + this.structuresMap.get(normalizedCoord.col)!.add(normalizedCoord.row); + this.totalStructures++; + } + + const key = StructureType[structureType] as unknown as StructureType; + // Ensure the structure is added and get its index - const index = this.structures.addStructure(entityId); + const index = this.structures.addStructure(entityId, key); - if (this.instancedModel) { - this.instancedModel.setMatrixAt(index, this.dummy.matrix); - this.instancedModel.setCount(this.structures.counter); // Set the count to the current number of structures + if (this.structureModels) { + const modelType = this.structureModels.get(key); + modelType?.setMatrixAt(index, this.dummy.matrix); + modelType?.setCount(this.structures.getCountForType(key)); // Set the count to the current number of structures // Add label on top of the structure with appropriate color const labelColor = isMine ? myColor : neutralColor; - const label = this.labelManager.createLabel(position as any, labelColor); - this.scene.add(label); + const label = this.labelManagers.get(key)?.createLabel(position as any, labelColor); + this.scene.add(label!); } } } class Structures { - private structures: Map = new Map(); - counter: number = 0; + private structures: Map = new Map(); + private counters: Map = new Map(); + + addStructure(entityId: ID, structureType: StructureType): number { + const index = this.counters.get(structureType) || 0; - addStructure(entityId: ID): number { if (!this.structures.has(entityId)) { - this.structures.set(entityId, this.counter); - this.counter++; + this.structures.set(entityId, { index, structureType }); + this.counters.set(structureType, index + 1); } - return this.structures.get(entityId)!; + return this.structures.get(entityId)!.index; } getStructureIndex(entityId: ID) { - return this.structures.get(entityId); + return this.structures.get(entityId)?.index; + } + + getCountForType(structureType: StructureType): number { + return this.counters.get(structureType) || 0; } } diff --git a/client/src/three/scenes/Worldmap.ts b/client/src/three/scenes/Worldmap.ts index 4faa05cd3..e69748204 100644 --- a/client/src/three/scenes/Worldmap.ts +++ b/client/src/three/scenes/Worldmap.ts @@ -31,6 +31,8 @@ export default class WorldmapScene extends HexagonScene { height: 30, }; + private totalStructures: number = 0; + private currentChunk: string = "null"; // Store @@ -41,7 +43,6 @@ export default class WorldmapScene extends HexagonScene { private structureManager: StructureManager; private battleManager: BattleManager; private exploredTiles: Map> = new Map(); - private structures: Map> = new Map(); private battles: Map> = new Map(); private cachedMatrices: Map> = @@ -75,9 +76,25 @@ export default class WorldmapScene extends HexagonScene { this.battleManager = new BattleManager(this.scene); this.systemManager.Army.onUpdate((value) => this.armyManager.onUpdate(value)); - this.systemManager.Structure.onUpdate((value) => this.structureManager.onUpdate(value)); this.systemManager.Battle.onUpdate((value) => this.battleManager.onUpdate(value)); this.systemManager.Tile.onUpdate((value) => this.updateExploredHex(value)); + this.systemManager.Structure.onUpdate((value) => { + this.structureManager.onUpdate(value); + if (this.totalStructures !== this.structureManager.totalStructures) { + const { col, row } = value.hexCoords; + const { chunkX, chunkZ } = this.worldToChunkCoordinates( + getWorldPositionForHex({ col: col - FELT_CENTER, row: row - FELT_CENTER }).x, + getWorldPositionForHex({ col: col - FELT_CENTER, row: row - FELT_CENTER }).z, + ); + const startRow = chunkZ * this.chunkSize; + const startCol = chunkX * this.chunkSize; + + this.totalStructures = this.structureManager.totalStructures; + + this.removeCachedMatricesForChunk(startRow, startCol); + this.updateHexagonGrid(startRow, startCol, this.renderChunkSize.height, this.renderChunkSize.width); + } + }); this.inputManager.addListener( "mousemove", @@ -176,7 +193,7 @@ export default class WorldmapScene extends HexagonScene { const pos = getWorldPositionForHex({ row, col }); dummy.position.copy(pos); - const isStructure = this.structures.get(col)?.has(row) || false; + const isStructure = this.structureManager.structuresMap.get(col)?.has(row) || false; if (isStructure) { dummy.scale.set(0, 0, 0); @@ -291,7 +308,7 @@ export default class WorldmapScene extends HexagonScene { const pos = getWorldPositionForHex({ row: globalRow, col: globalCol }); dummy.position.copy(pos); - const isStructure = this.structures.get(col)?.has(row) || false; + const isStructure = this.structureManager.structuresMap.get(globalCol)?.has(globalRow) || false; const isBattle = this.battles.get(globalCol)?.has(globalRow) || false; const isExplored = this.exploredTiles.get(globalCol)?.has(globalRow) || false; if (isStructure || !isExplored || !isBattle) {