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 combat client mockup #2693

Merged
merged 22 commits into from
Feb 14, 2025
Merged
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
12 changes: 6 additions & 6 deletions client/apps/game/src/hooks/store/_three-store.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { StructureInfo } from "@/three/types";
import { BuildingType, HexPosition, HexTileInfo, ID, Position } from "@bibliothecadao/eternum";
import { ActionPath, BuildingType, HexPosition, ID, Position } from "@bibliothecadao/eternum";

export interface ThreeStore {
navigationTarget: HexPosition | null;
setNavigationTarget: (hex: HexPosition | null) => void;
armyActions: ArmyActions;
setArmyActions: (armyActions: ArmyActions) => void;
updateHoveredHex: (hoveredHex: HexPosition | null) => void;
updateTravelPaths: (travelPaths: Map<string, { path: HexPosition[]; isExplored: boolean }>) => void;
updateActionPaths: (actionPaths: Map<string, ActionPath[]>) => void;
updateSelectedEntityId: (selectedEntityId: ID | null) => void;
selectedHex: HexPosition | null;
setSelectedHex: (hex: HexPosition | null) => void;
Expand Down Expand Up @@ -35,7 +35,7 @@ export interface ThreeStore {

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

Expand All @@ -44,14 +44,14 @@ export const createThreeStoreSlice = (set: any, _get: any) => ({
setNavigationTarget: (hex: HexPosition | null) => set({ navigationTarget: hex }),
armyActions: {
hoveredHex: null,
travelPaths: new Map(),
actionPaths: new Map(),
selectedEntityId: null,
},
setArmyActions: (armyActions: ArmyActions) => set({ armyActions }),
updateHoveredHex: (hoveredHex: HexPosition | null) =>
set((state: any) => ({ armyActions: { ...state.armyActions, hoveredHex } })),
updateTravelPaths: (travelPaths: Map<string, { path: HexPosition[]; isExplored: boolean }>) =>
set((state: any) => ({ armyActions: { ...state.armyActions, travelPaths } })),
updateActionPaths: (actionPaths: Map<string, ActionPath[]>) =>
set((state: any) => ({ armyActions: { ...state.armyActions, actionPaths } })),
updateSelectedEntityId: (selectedEntityId: ID | null) =>
set((state: any) => ({ armyActions: { ...state.armyActions, selectedEntityId } })),
selectedHex: { col: 0, row: 0 },
Expand Down
26 changes: 22 additions & 4 deletions client/apps/game/src/three/managers/highlight-hex-manager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import { createHexagonShape } from "@/three/geometry/hexagon-geometry";
import { HEX_SIZE } from "@/three/scenes/constants";
import { highlightHexMaterial } from "@/three/shaders/highlight-hex-material";
import { HexPosition } from "@bibliothecadao/eternum";
import { ActionPath, ActionType } from "@bibliothecadao/eternum";
import * as THREE from "three";
import { getWorldPositionForHex } from "../utils";

export const getHighlightColorForAction = (actionType: ActionType): THREE.Vector3 => {
switch (actionType) {
case ActionType.Explore:
return new THREE.Vector3(0.0, 1.5, 1.5); // More intense cyan
case ActionType.Move:
return new THREE.Vector3(0.0, 1.5, 0.0); // More intense green
case ActionType.Attack:
return new THREE.Vector3(2.0, 0.0, 0.0); // More intense red
case ActionType.Merge:
return new THREE.Vector3(1.5, 0.0, 1.5); // More intense purple
case ActionType.Build:
return new THREE.Vector3(0.0, 0.0, 1.5); // More intense blue
default:
return new THREE.Vector3(1.5, 1.5, 1.5); // More intense white
}
};

export class HighlightHexManager {
private highlightedHexes: THREE.Mesh[] = [];
private material: THREE.ShaderMaterial;
Expand All @@ -13,7 +30,7 @@ export class HighlightHexManager {
this.material = highlightHexMaterial;
}

highlightHexes(hexes: HexPosition[]) {
highlightHexes(actionPaths: ActionPath[]) {
// Remove existing highlights
this.highlightedHexes.forEach((mesh) => this.scene.remove(mesh));
this.highlightedHexes = [];
Expand All @@ -22,9 +39,10 @@ export class HighlightHexManager {
const bigHexagonShape = createHexagonShape(HEX_SIZE);
const hexagonGeometry = new THREE.ShapeGeometry(bigHexagonShape);

hexes.forEach((hex) => {
const position = getWorldPositionForHex(hex);
actionPaths.forEach((hex) => {
const position = getWorldPositionForHex(hex.hex);
const highlightMesh = new THREE.Mesh(hexagonGeometry, this.material.clone());
highlightMesh.material.uniforms.color.value = getHighlightColorForAction(hex.actionType);
highlightMesh.position.set(position.x, 0.3, position.z);
highlightMesh.rotation.x = -Math.PI / 2;
highlightMesh.renderOrder = 5;
Expand Down
8 changes: 7 additions & 1 deletion client/apps/game/src/three/scenes/hexception.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Position } from "@/types/position";
import { IS_FLAT_MODE } from "@/ui/config";
import { ResourceIcon } from "@/ui/elements/resource-icon";
import {
ActionType,
BUILDINGS_CENTER,
Biome,
BiomeType,
Expand Down Expand Up @@ -171,7 +172,12 @@ export default class HexceptionScene extends HexagonScene {
(building) => {
if (building) {
this.buildingPreview?.setPreviewBuilding(building as any);
this.highlightHexManager.highlightHexes(this.highlights);
this.highlightHexManager.highlightHexes(
this.highlights.map((hex) => ({
hex: { col: hex.col, row: hex.row },
actionType: ActionType.Build,
})),
);
} else {
this.clearBuildingMode();
}
Expand Down
86 changes: 59 additions & 27 deletions client/apps/game/src/three/scenes/worldmap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,25 @@ import { LeftView } from "@/types";
import { Position } from "@/types/position";
import { FELT_CENTER, IS_FLAT_MODE, IS_MOBILE } from "@/ui/config";
import { UNDEFINED_STRUCTURE_ENTITY_ID } from "@/ui/constants";
import { CombatModal } from "@/ui/modules/military/combat-modal";
import { getBlockTimestamp } from "@/utils/timestamp";
import {
ActionPath,
ActionPaths,
ActionType,
ArmyMovementManager,
Biome,
BiomeType,
DUMMY_HYPERSTRUCTURE_ENTITY_ID,
getNeighborOffsets,
HexPosition,
ID,
SetupResult,
TileManager,
TravelPaths,
getNeighborOffsets,
} from "@bibliothecadao/eternum";
import { getEntities } from "@dojoengine/state";
import throttle from "lodash/throttle";
import { Account, AccountInterface } from "starknet";
import * as THREE from "three";
import { Raycaster } from "three";
import { MapControls } from "three/examples/jsm/controls/MapControls";
Expand Down Expand Up @@ -231,8 +235,8 @@ export default class WorldmapScene extends HexagonScene {
return;
}
const { hexCoords } = hex;
const { selectedEntityId, travelPaths } = this.state.armyActions;
if (selectedEntityId && travelPaths.size > 0) {
const { selectedEntityId, actionPaths } = this.state.armyActions;
if (selectedEntityId && actionPaths.size > 0) {
if (this.previouslyHoveredHex?.col !== hexCoords.col || this.previouslyHoveredHex?.row !== hexCoords.row) {
this.previouslyHoveredHex = hexCoords;
}
Expand Down Expand Up @@ -342,28 +346,53 @@ export default class WorldmapScene extends HexagonScene {
// Check if account exists before allowing actions
const account = useAccountStore.getState().account;

const { currentBlockTimestamp, currentArmiesTick } = getBlockTimestamp();

const { selectedEntityId, travelPaths } = this.state.armyActions;
if (selectedEntityId && travelPaths.size > 0 && hexCoords) {
const travelPath = travelPaths.get(TravelPaths.posKey(hexCoords, true));
if (travelPath) {
const selectedPath = travelPath.path;
const isExplored = travelPath.isExplored ?? false;
if (selectedPath.length > 0) {
const armyMovementManager = new ArmyMovementManager(
this.dojo.components,
this.dojo.network.provider,
selectedEntityId,
);
playSound(soundSelector.unitMarching1, this.state.isSoundOn, this.state.effectsLevel);
armyMovementManager.moveArmy(account!, selectedPath, isExplored, currentBlockTimestamp, currentArmiesTick);
this.state.updateHoveredHex(null);
const { selectedEntityId, actionPaths } = this.state.armyActions;
if (selectedEntityId && actionPaths.size > 0 && hexCoords) {
const actionPath = actionPaths.get(ActionPaths.posKey(hexCoords, true));
if (actionPath && account) {
const actionType = ActionPaths.getActionType(actionPath);
if (actionType === ActionType.Explore || actionType === ActionType.Move) {
this.onArmyMovement(account, actionPath, selectedEntityId);
} else if (actionType === ActionType.Attack) {
this.onArmyAttack(account, actionPath, selectedEntityId);
}
}
}
}

private onArmyMovement(account: Account | AccountInterface, actionPath: ActionPath[], selectedEntityId: ID) {
const { currentBlockTimestamp, currentArmiesTick } = getBlockTimestamp();
const selectedPath = actionPath.map((path) => path.hex);
// can only move on explored hexes
const isExplored = ActionPaths.getActionType(actionPath) === ActionType.Move;
if (selectedPath.length > 0) {
const armyMovementManager = new ArmyMovementManager(
this.dojo.components,
this.dojo.network.provider,
selectedEntityId,
);
playSound(soundSelector.unitMarching1, this.state.isSoundOn, this.state.effectsLevel);
armyMovementManager.moveArmy(account!, selectedPath, isExplored, currentBlockTimestamp, currentArmiesTick);
this.state.updateHoveredHex(null);
}
}

private onArmyAttack(account: Account | AccountInterface, actionPath: ActionPath[], selectedEntityId: ID) {
const selectedPath = actionPath.map((path) => path.hex);

// Get the target hex (last hex in the path)
const targetHex = selectedPath[selectedPath.length - 1];

// Find the army at the target position

this.state.toggleModal(
<CombatModal
attackerEntityId={selectedEntityId}
targetHex={new Position({ x: targetHex.col, y: targetHex.row }).getContract()}
/>,
);
}

private onArmySelection(selectedEntityId: ID | null) {
if (!selectedEntityId) {
this.clearSelection();
Expand All @@ -378,20 +407,20 @@ export default class WorldmapScene extends HexagonScene {

const { currentDefaultTick, currentArmiesTick } = getBlockTimestamp();

const travelPaths = armyMovementManager.findPaths(
const actionPaths = armyMovementManager.findActionPaths(
this.structureHexes,
this.armyHexes,
this.exploredTiles,
currentDefaultTick,
currentArmiesTick,
);
this.state.updateTravelPaths(travelPaths.getPaths());
this.highlightHexManager.highlightHexes(travelPaths.getHighlightedHexes());
this.state.updateActionPaths(actionPaths.getPaths());
this.highlightHexManager.highlightHexes(actionPaths.getHighlightedHexes());
}

private clearSelection() {
this.highlightHexManager.highlightHexes([]);
this.state.updateTravelPaths(new Map());
this.state.updateActionPaths(new Map());
this.structurePreview?.clearPreviewStructure();
this.state.updateSelectedEntityId(null);
this.state.setSelectedHex(null);
Expand Down Expand Up @@ -921,12 +950,15 @@ export default class WorldmapScene extends HexagonScene {
return exploredHexes;
}

private getBuildableHexesForCurrentChunk() {
private getBuildableHexesForCurrentChunk(): ActionPath[] {
const exploredHexes = this.getExploredHexesForCurrentChunk();
const buildableHexes = exploredHexes.filter(
(hex) => !this.structureManager.structureHexCoords.get(hex.col)?.has(hex.row),
);
return buildableHexes;
return buildableHexes.map((hex) => ({
hex: { col: hex.col, row: hex.row },
actionType: ActionType.Build,
}));
}

private cacheMatricesForChunk(startRow: number, startCol: number) {
Expand Down
Loading
Loading