From 9045d89e1c0ea9c9a92acd86c833919f0e0e0d52 Mon Sep 17 00:00:00 2001 From: cwastche Date: Thu, 8 Aug 2024 16:10:11 -0400 Subject: [PATCH 1/5] Load structure models --- .../src/three/components/StructureManager.ts | 128 ++++++++++++------ 1 file changed, 85 insertions(+), 43 deletions(-) diff --git a/client/src/three/components/StructureManager.ts b/client/src/three/components/StructureManager.ts index 7613b2104..c1e7cc63c 100644 --- a/client/src/three/components/StructureManager.ts +++ b/client/src/three/components/StructureManager.ts @@ -5,85 +5,127 @@ 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"; +export 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]: "", +}; + +export 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; 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(); + 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; } } From b4055fec2f296a90458072e598e92ced038487fa Mon Sep 17 00:00:00 2001 From: cwastche Date: Thu, 8 Aug 2024 16:40:54 -0400 Subject: [PATCH 2/5] Fix explore if no capacity --- .../dojo/modelManager/ArmyMovementManager.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) 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; + }; } From c43f795d209ac4987232fc600fa4eed1de662e12 Mon Sep 17 00:00:00 2001 From: cwastche Date: Fri, 9 Aug 2024 14:16:18 -0400 Subject: [PATCH 3/5] Remove biome models if struct on tile --- .../src/three/components/StructureManager.ts | 10 ++++++++ client/src/three/scenes/Worldmap.ts | 25 ++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/client/src/three/components/StructureManager.ts b/client/src/three/components/StructureManager.ts index c1e7cc63c..f671edb99 100644 --- a/client/src/three/components/StructureManager.ts +++ b/client/src/three/components/StructureManager.ts @@ -37,6 +37,8 @@ export class StructureManager { private dummy: THREE.Object3D = new THREE.Object3D(); modelLoadPromises: Promise[] = []; structures: Structures = new Structures(); + structuresMap: Map> = new Map(); + totalStructures: number = 0; constructor(scene: THREE.Scene) { this.scene = scene; @@ -89,6 +91,14 @@ export class StructureManager { 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 diff --git a/client/src/three/scenes/Worldmap.ts b/client/src/three/scenes/Worldmap.ts index 809f8c1c2..5fe35dcd8 100644 --- a/client/src/three/scenes/Worldmap.ts +++ b/client/src/three/scenes/Worldmap.ts @@ -30,6 +30,8 @@ export default class WorldmapScene extends HexagonScene { height: 30, }; + private totalStructures: number = 0; + private currentChunk: string = "null"; // Store @@ -40,7 +42,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> = @@ -74,9 +75,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: StructureSystemUpdate) => { + 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", @@ -175,7 +192,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); @@ -289,7 +306,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) { From 7fd0a4129e1701b839199fe992d859a249dc3395 Mon Sep 17 00:00:00 2001 From: cwastche Date: Fri, 9 Aug 2024 14:25:46 -0400 Subject: [PATCH 4/5] rebase --- client/src/three/scenes/Worldmap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/three/scenes/Worldmap.ts b/client/src/three/scenes/Worldmap.ts index 5fe35dcd8..5260ad7b1 100644 --- a/client/src/three/scenes/Worldmap.ts +++ b/client/src/three/scenes/Worldmap.ts @@ -77,7 +77,7 @@ export default class WorldmapScene extends HexagonScene { this.systemManager.Army.onUpdate((value) => this.armyManager.onUpdate(value)); this.systemManager.Battle.onUpdate((value) => this.battleManager.onUpdate(value)); this.systemManager.Tile.onUpdate((value) => this.updateExploredHex(value)); - this.systemManager.Structure.onUpdate((value: StructureSystemUpdate) => { + this.systemManager.Structure.onUpdate((value) => { this.structureManager.onUpdate(value); if (this.totalStructures !== this.structureManager.totalStructures) { const { col, row } = value.hexCoords; From c765e79aaf73a74a63ff5196b3868fc41075b277 Mon Sep 17 00:00:00 2001 From: cwastche Date: Fri, 9 Aug 2024 15:05:40 -0400 Subject: [PATCH 5/5] fix knip --- client/src/three/components/StructureManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/three/components/StructureManager.ts b/client/src/three/components/StructureManager.ts index f671edb99..61a9f275f 100644 --- a/client/src/three/components/StructureManager.ts +++ b/client/src/three/components/StructureManager.ts @@ -10,7 +10,7 @@ import { ID, StructureType } from "@bibliothecadao/eternum"; const neutralColor = new THREE.Color(0xffffff); const myColor = new THREE.Color("lime"); -export const StructureModelPaths: Record = { +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", @@ -20,7 +20,7 @@ export const StructureModelPaths: Record = { [StructureType.Settlement]: "", }; -export const StructureLabelPaths: Record = { +const StructureLabelPaths: Record = { [StructureType.Realm]: "textures/realm_label.png", [StructureType.Hyperstructure]: "textures/hyper_label.png", [StructureType.FragmentMine]: "textures/shard_label.png",