Skip to content
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
53 changes: 5 additions & 48 deletions src/components/game/BattleSimulatorPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,40 +210,23 @@ export const BattleSimulatorPanel = memo(function BattleSimulatorPanel() {
}, [selectedUnit, selectedTeam, spawnQuantity]);

const handleFight = useCallback(() => {
console.log('[BattleSimulator] handleFight called');
const bridge = getWorkerBridge();
const worldAdapter = RenderStateWorldAdapter.getInstance();

if (!worldAdapter) {
console.warn('[BattleSimulator] No world adapter available');
if (!worldAdapter || !bridge) {
return;
}

if (!bridge) {
console.warn('[BattleSimulator] No worker bridge available');
return;
}

console.log('[BattleSimulator] Registering AI for both players');
// Register both players as AI-controlled so the AI takes over and fights
const player1Faction = playerSlots[0]?.faction ?? 'dominion';
const player2Faction = playerSlots[1]?.faction ?? 'dominion';
bridge.registerAI('player1', player1Faction, 'medium');
bridge.registerAI('player2', player2Faction, 'medium');

const currentTick = bridge.currentTick;
console.log('[BattleSimulator] Current tick:', currentTick);

// Get all units from render state adapter
const entities = worldAdapter.getEntitiesWith('Unit', 'Selectable', 'Transform', 'Health');
console.log('[BattleSimulator] Found entities:', entities.length);

let skippedNoComponents = 0;
let skippedDead = 0;
let skippedWorker = 0;
let skippedWrongPlayer = 0;
let attackCommands = 0;
let moveCommands = 0;

for (const entity of entities) {
const selectable = entity.get<{ playerId: string }>('Selectable');
Expand All @@ -259,29 +242,16 @@ export const BattleSimulatorPanel = memo(function BattleSimulatorPanel() {
const transform = entity.get<{ x: number; y: number }>('Transform');
const health = entity.get<{ isDead: () => boolean }>('Health');

if (!selectable || !unit || !transform || !health) {
skippedNoComponents++;
continue;
}
if (health.isDead()) {
skippedDead++;
continue;
}
if (unit.isWorker) {
skippedWorker++;
continue;
}
if (!selectable || !unit || !transform || !health) continue;
if (health.isDead()) continue;
if (unit.isWorker) continue;

const playerId = selectable.playerId;
if (playerId !== 'player1' && playerId !== 'player2') {
skippedWrongPlayer++;
continue;
}
if (playerId !== 'player1' && playerId !== 'player2') continue;

const targetId = findValidTarget(worldAdapter, entity.id, unit, transform, playerId);

if (targetId !== null) {
attackCommands++;
const attackCommand: GameCommand = {
tick: currentTick,
playerId,
Expand All @@ -293,7 +263,6 @@ export const BattleSimulatorPanel = memo(function BattleSimulatorPanel() {
} else {
const enemyCenter = findEnemyCenter(worldAdapter, unit, playerId);
if (enemyCenter) {
moveCommands++;
const moveCommand: GameCommand = {
tick: currentTick,
playerId,
Expand All @@ -306,21 +275,9 @@ export const BattleSimulatorPanel = memo(function BattleSimulatorPanel() {
}
}

console.log('[BattleSimulator] Entity processing stats:', {
total: entities.length,
skippedNoComponents,
skippedDead,
skippedWorker,
skippedWrongPlayer,
attackCommands,
moveCommands,
});

console.log('[BattleSimulator] Calling bridge.resume()');
bridge.resume();
setIsPaused(false);
setSelectedUnit(null);
console.log('[BattleSimulator] handleFight completed');
}, [playerSlots]);

const handlePauseToggle = useCallback(() => {
Expand Down
4 changes: 4 additions & 0 deletions src/engine/workers/GameWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,10 @@ export class WorkerGame extends GameCore {
// Combat stats for range overlays
attackRange: unit.attackRange,
sightRange: unit.sightRange,
// Targeting capabilities
isNaval: unit.isNaval,
canAttackGround: unit.canAttackGround,
canAttackAir: unit.canAttackAir,
// Selection properties for hit detection
selectionRadius: selectable.selectionRadius,
selectionPriority: selectable.selectionPriority,
Expand Down
69 changes: 47 additions & 22 deletions src/engine/workers/RenderStateAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,19 @@ class UnitAdapter {
public targetY: number | null;
public speed: number;
// Command queue for shift-click visualization
public commandQueue: Array<{ type: string; targetX?: number; targetY?: number; targetEntityId?: number }>;
public commandQueue: Array<{
type: string;
targetX?: number;
targetY?: number;
targetEntityId?: number;
}>;
// Combat stats for range overlays
public attackRange: number;
public sightRange: number;
// Targeting capabilities
public isNaval: boolean;
public canAttackGround: boolean;
public canAttackAir: boolean;

constructor(data: UnitRenderState) {
this.unitId = data.unitId;
Expand All @@ -153,6 +162,9 @@ class UnitAdapter {
this.commandQueue = data.commandQueue;
this.attackRange = data.attackRange;
this.sightRange = data.sightRange;
this.isNaval = data.isNaval;
this.canAttackGround = data.canAttackGround;
this.canAttackAir = data.canAttackAir;
}

public update(data: UnitRenderState): void {
Expand All @@ -175,6 +187,9 @@ class UnitAdapter {
this.commandQueue = data.commandQueue;
this.attackRange = data.attackRange;
this.sightRange = data.sightRange;
this.isNaval = data.isNaval;
this.canAttackGround = data.canAttackGround;
this.canAttackAir = data.canAttackAir;
}

public isSelected(): boolean {
Expand Down Expand Up @@ -371,9 +386,7 @@ class BuildingComponentAdapter {
this.isFlying = data.isFlying;
this.liftProgress = data.liftProgress;
// Simulate productionQueue for renderer compatibility
this.productionQueue = data.hasProductionQueue
? [{ progress: data.productionProgress }]
: [];
this.productionQueue = data.hasProductionQueue ? [{ progress: data.productionProgress }] : [];
this.attackRange = data.attackRange;
this.sightRange = data.sightRange;
}
Expand All @@ -389,9 +402,7 @@ class BuildingComponentAdapter {
this.isFlying = data.isFlying;
this.liftProgress = data.liftProgress;
// Simulate productionQueue for renderer compatibility
this.productionQueue = data.hasProductionQueue
? [{ progress: data.productionProgress }]
: [];
this.productionQueue = data.hasProductionQueue ? [{ progress: data.productionProgress }] : [];
this.attackRange = data.attackRange;
this.sightRange = data.sightRange;
}
Expand Down Expand Up @@ -576,7 +587,7 @@ class ResourceComponentAdapter {
}

public getDepletionPercent(): number {
return this.maxAmount > 0 ? 1 - (this.amount / this.maxAmount) : 0;
return this.maxAmount > 0 ? 1 - this.amount / this.maxAmount : 0;
}

public getCurrentGatherers(): number {
Expand Down Expand Up @@ -622,7 +633,9 @@ export class RenderStateWorldAdapter implements IWorldProvider {
if (!instance) {
instance = new RenderStateWorldAdapter();
global[RENDER_STATE_ADAPTER_KEY] = instance;
debugInitialization.log('[RenderStateWorldAdapter] Created new singleton instance on globalThis');
debugInitialization.log(
'[RenderStateWorldAdapter] Created new singleton instance on globalThis'
);
}
return instance;
}
Expand Down Expand Up @@ -683,9 +696,11 @@ export class RenderStateWorldAdapter implements IWorldProvider {
try {
this._updateCount++;


// Debug: log first significant update
if (!this.hasLoggedFirstUpdate && (state.units.length > 0 || state.buildings.length > 0 || state.resources.length > 0)) {
if (
!this.hasLoggedFirstUpdate &&
(state.units.length > 0 || state.buildings.length > 0 || state.resources.length > 0)
) {
debugInitialization.log('[RenderStateWorldAdapter] First update with entities:', {
tick: state.tick,
units: state.units.length,
Expand Down Expand Up @@ -753,7 +768,12 @@ export class RenderStateWorldAdapter implements IWorldProvider {
}

// Mark as ready once we have entities
if (!this._isReady && (this.unitEntities.size > 0 || this.buildingEntities.size > 0 || this.resourceEntities.size > 0)) {
if (
!this._isReady &&
(this.unitEntities.size > 0 ||
this.buildingEntities.size > 0 ||
this.resourceEntities.size > 0)
) {
this._isReady = true;
debugInitialization.log('[RenderStateWorldAdapter] Adapter is now ready with entities:', {
units: this.unitEntities.size,
Expand All @@ -762,7 +782,10 @@ export class RenderStateWorldAdapter implements IWorldProvider {
});
}
} catch (error) {
debugInitialization.error('[RenderStateWorldAdapter] Error updating from render state:', error);
debugInitialization.error(
'[RenderStateWorldAdapter] Error updating from render state:',
error
);
}
}

Expand All @@ -786,13 +809,13 @@ export class RenderStateWorldAdapter implements IWorldProvider {

// Units: return when querying for Unit, Selectable (without Building/Resource filter),
// or Transform alone (legacy)
const includeUnits = hasUnit ||
(hasSelectable && !hasBuilding && !hasResource) ||
(hasTransform && componentTypes.length === 1);
const includeUnits =
hasUnit ||
(hasSelectable && !hasBuilding && !hasResource) ||
(hasTransform && componentTypes.length === 1);

// Buildings: return when querying for Building, or Selectable (without Unit/Resource filter)
const includeBuildings = hasBuilding ||
(hasSelectable && !hasUnit && !hasResource);
const includeBuildings = hasBuilding || (hasSelectable && !hasUnit && !hasResource);

// Resources: return when querying for Resource
const includeResources = hasResource;
Expand Down Expand Up @@ -822,10 +845,12 @@ export class RenderStateWorldAdapter implements IWorldProvider {
* Get entity by ID
*/
public getEntity(entityId: number): IEntity | null {
return this.unitEntities.get(entityId)
?? this.buildingEntities.get(entityId)
?? this.resourceEntities.get(entityId)
?? null;
return (
this.unitEntities.get(entityId) ??
this.buildingEntities.get(entityId) ??
this.resourceEntities.get(entityId) ??
null
);
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/engine/workers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ export interface UnitRenderState {
// Combat stats for range overlays
attackRange: number;
sightRange: number;
// Targeting capabilities
isNaval: boolean;
canAttackGround: boolean;
canAttackAir: boolean;
// Selection properties for hit detection
selectionRadius: number;
selectionPriority: number;
Expand Down
Loading