Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new movement #2691

Open
wants to merge 10 commits into
base: feat/new-combat
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/apps/game/src/hooks/store/_three-store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StructureInfo } from "@/three/types";
import { BuildingType, HexPosition, ID, Position } from "@bibliothecadao/eternum";
import { BuildingType, HexPosition, HexTileInfo, ID, Position } from "@bibliothecadao/eternum";

export interface ThreeStore {
navigationTarget: HexPosition | null;
Expand Down Expand Up @@ -35,7 +35,7 @@ export interface ThreeStore {

interface ArmyActions {
hoveredHex: HexPosition | null;
travelPaths: Map<string, { path: HexPosition[]; isExplored: boolean }>;
travelPaths: Map<string, { path: HexTileInfo[]; isExplored: boolean }>;
selectedEntityId: ID | null;
}

Expand Down
44 changes: 34 additions & 10 deletions client/apps/game/src/three/helpers/pathfinding.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Position } from "@/types/position";
import { getNeighborOffsets, HexPosition } from "@bibliothecadao/eternum";
import { BiomeType, getNeighborOffsets, HexPosition } from "@bibliothecadao/eternum";

interface Node {
col: number;
Expand All @@ -10,17 +10,26 @@ interface Node {
}

export function findShortestPath(
startPosition: Position,
endPosition: Position,
exploredTiles: Map<number, Set<number>>,
oldPosition: Position,
newPosition: Position,
exploredTiles: Map<number, Map<number, BiomeType>>,
structureHexes: Map<number, Set<number>>,
armyHexes: Map<number, Set<number>>,
maxDistance: number,
): Position[] {
// Check if target is within maximum distance before starting pathfinding
const oldPos = oldPosition.getNormalized();
const newPos = newPosition.getNormalized();
const initialDistance = getHexDistance({ col: oldPos.x, row: oldPos.y }, { col: newPos.x, row: newPos.y });
if (initialDistance > maxDistance) {
return []; // Return empty path if target is too far
}

const openSet: Node[] = [];
const closedSet = new Set<string>();
const start = startPosition.getNormalized();
const end = endPosition.getNormalized();
const startNode: Node = {
col: start.x,
row: start.y,
col: oldPos.x,
row: oldPos.y,
f: 0,
g: 0,
};
Expand All @@ -32,7 +41,7 @@ export function findShortestPath(
let current = openSet.reduce((min, node) => (node.f < min.f ? node : min), openSet[0]);

// Reached end
if (current.col === end.x && current.row === end.y) {
if (current.col === newPos.x && current.row === newPos.y) {
return reconstructPath(current);
}

Expand All @@ -46,13 +55,28 @@ export function findShortestPath(
const neighborCol = current.col + i;
const neighborRow = current.row + j;

// Skip if path is getting too long
if (current.g + 1 > maxDistance) {
continue;
}

// Skip if not explored or already in closed set
if (!exploredTiles.get(neighborCol)?.has(neighborRow) || closedSet.has(`${neighborCol},${neighborRow}`)) {
continue;
}

// skip if the neighbor is a structure hex
if (structureHexes.get(neighborCol)?.has(neighborRow)) {
continue;
}

// skip if the neighbor is an army hex and not the end hex (where the unit is going)
if (armyHexes.get(neighborCol)?.has(neighborRow) && !(neighborCol === newPos.x && neighborRow === newPos.y)) {
continue;
}

const g = current.g + 1;
const h = getHexDistance({ col: neighborCol, row: neighborRow }, { col: end.x, row: end.y });
const h = getHexDistance({ col: neighborCol, row: neighborRow }, { col: newPos.x, row: newPos.y });
const f = g + h;

const neighborNode: Node = {
Expand Down
63 changes: 39 additions & 24 deletions client/apps/game/src/three/managers/army-manager.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { useAccountStore } from "@/hooks/store/use-account-store";
import { GUIManager } from "@/three/helpers/gui-manager";
import { findShortestPath } from "@/three/helpers/pathfinding";
import { isAddressEqualToAccount } from "@/three/helpers/utils";
import { ArmyModel } from "@/three/managers/army-model";
import { Biome } from "@/three/managers/biome";
import { LabelManager } from "@/three/managers/label-manager";
import { Position } from "@/types/position";
import { BiomeType, ContractAddress, FELT_CENTER, ID, orders } from "@bibliothecadao/eternum";
import {
Biome,
BiomeType,
configManager,
ContractAddress,
FELT_CENTER,
ID,
orders,
ResourcesIds,
} from "@bibliothecadao/eternum";
import * as THREE from "three";
import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer";
import { findShortestPath } from "../helpers/pathfinding";
import { ArmyData, ArmySystemUpdate, MovingArmyData, MovingLabelData, RenderChunkSize } from "../types";
import { calculateOffset, getHexForWorldPosition, getWorldPositionForHex } from "../utils";

Expand All @@ -28,23 +36,15 @@ export class ArmyManager {
private currentChunkKey: string | null = "190,170";
private renderChunkSize: RenderChunkSize;
private visibleArmies: ArmyData[] = [];
private biome: Biome;
private armyPaths: Map<ID, Position[]> = new Map();
private exploredTiles: Map<number, Set<number>>;
private entityIdLabels: Map<ID, CSS2DObject> = new Map();

constructor(
scene: THREE.Scene,
renderChunkSize: { width: number; height: number },
exploredTiles: Map<number, Set<number>>,
) {
constructor(scene: THREE.Scene, renderChunkSize: { width: number; height: number }) {
this.scene = scene;
this.armyModel = new ArmyModel(scene);
this.scale = new THREE.Vector3(0.3, 0.3, 0.3);
this.labelManager = new LabelManager("textures/army_label.png", 1.5);
this.renderChunkSize = renderChunkSize;
this.biome = new Biome();
this.exploredTiles = exploredTiles;
this.onMouseMove = this.onMouseMove.bind(this);
this.onRightClick = this.onRightClick.bind(this);

Expand Down Expand Up @@ -124,7 +124,12 @@ export class ArmyManager {
}
}

async onUpdate(update: ArmySystemUpdate) {
async onUpdate(
update: ArmySystemUpdate,
armyHexes: Map<number, Set<number>>,
structureHexes: Map<number, Set<number>>,
exploredTiles: Map<number, Map<number, BiomeType>>,
) {
await this.armyModel.loadPromise;
const { entityId, hexCoords, owner, battleId, currentHealth, order } = update;

Expand All @@ -146,12 +151,12 @@ export class ArmyManager {
}
}

const position = new Position({ x: hexCoords.col, y: hexCoords.row });
const newPosition = new Position({ x: hexCoords.col, y: hexCoords.row });

if (this.armies.has(entityId)) {
this.moveArmy(entityId, position);
this.moveArmy(entityId, newPosition, armyHexes, structureHexes, exploredTiles);
} else {
this.addArmy(entityId, position, owner, order);
this.addArmy(entityId, newPosition, owner, order);
}
return false;
}
Expand Down Expand Up @@ -182,7 +187,7 @@ export class ArmyManager {
// this.armyModel.dummyObject.updateMatrix();
// Determine model type based on order or other criteria
const { x, y } = army.hexCoords.getContract();
const biome = this.biome.getBiome(x, y);
const biome = Biome.getBiome(x, y);
if (biome === BiomeType.Ocean || biome === BiomeType.DeepOcean) {
this.armyModel.assignModelToEntity(army.entityId, "boat");
} else {
Expand Down Expand Up @@ -283,7 +288,7 @@ export class ArmyManager {

// Determine model type based on order or other criteria
const { x, y } = hexCoords.getContract();
const biome = this.biome.getBiome(x, y);
const biome = Biome.getBiome(x, y);
if (biome === BiomeType.Ocean || biome === BiomeType.DeepOcean) {
this.armyModel.assignModelToEntity(entityId, "boat");
} else {
Expand All @@ -303,16 +308,26 @@ export class ArmyManager {
this.renderVisibleArmies(this.currentChunkKey!);
}

public moveArmy(entityId: ID, hexCoords: Position) {
public moveArmy(
entityId: ID,
hexCoords: Position,
armyHexes: Map<number, Set<number>>,
structureHexes: Map<number, Set<number>>,
exploredTiles: Map<number, Map<number, BiomeType>>,
) {
const armyData = this.armies.get(entityId);
if (!armyData) return;

const { x: startX, y: startY } = armyData.hexCoords.getNormalized();
const { x: targetX, y: targetY } = hexCoords.getNormalized();
const startPos = armyData.hexCoords.getNormalized();
const targetPos = hexCoords.getNormalized();

if (startPos.x === targetPos.x && startPos.y === targetPos.y) return;

if (startX === targetX && startY === targetY) return;
// todo: currently taking max stamina of paladin as max stamina but need to refactor
const maxTroopStamina = configManager.getTroopStaminaConfig(ResourcesIds.Paladin);
const maxHex = Math.floor(maxTroopStamina / configManager.getMinTravelStaminaCost());

const path = findShortestPath(armyData.hexCoords, hexCoords, this.exploredTiles);
const path = findShortestPath(armyData.hexCoords, hexCoords, exploredTiles, structureHexes, armyHexes, maxHex);

if (!path || path.length === 0) return;

Expand Down Expand Up @@ -414,7 +429,7 @@ export class ArmyManager {
}

const { col, row } = getHexForWorldPosition({ x: position.x, y: position.y, z: position.z });
const biome = this.biome.getBiome(col + FELT_CENTER, row + FELT_CENTER);
const biome = Biome.getBiome(col + FELT_CENTER, row + FELT_CENTER);
if (biome === BiomeType.Ocean || biome === BiomeType.DeepOcean) {
this.armyModel.assignModelToEntity(entityId, "boat");
} else {
Expand Down
21 changes: 21 additions & 0 deletions client/apps/game/src/three/managers/biome-colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { BiomeType } from "@bibliothecadao/eternum";
import * as THREE from "three";

export const BIOME_COLORS: Record<BiomeType, THREE.Color> = {
DeepOcean: new THREE.Color("#4a6b63"),
Ocean: new THREE.Color("#657d71"),
Beach: new THREE.Color("#d7b485"),
Scorched: new THREE.Color("#393131"),
Bare: new THREE.Color("#d1ae7f"),
Tundra: new THREE.Color("#cfd4d4"),
Snow: new THREE.Color("#cfd4d4"),
TemperateDesert: new THREE.Color("#ad6c44"),
Shrubland: new THREE.Color("#c1aa7f"),
Taiga: new THREE.Color("#292d23"),
Grassland: new THREE.Color("#6f7338"),
TemperateDeciduousForest: new THREE.Color("#6f7338"),
TemperateRainForest: new THREE.Color("#6f573e"),
SubtropicalDesert: new THREE.Color("#926338"),
TropicalSeasonalForest: new THREE.Color("#897049"),
TropicalRainForest: new THREE.Color("#8a714a"),
};
Loading