mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-24 17:05:04 +00:00
Updating phase state control to allow for reinit of units at any phase.
I wanted to be able to cause the turns to either progress, or to set the units in their specific spots. This way if I skip ahead a bunch of turns for one reason or another, I don't have to worry about the units on the board being all messed up.
This commit is contained in:
parent
9a43be9b9c
commit
3642e391bc
7 changed files with 101 additions and 56 deletions
2
ai_animation/.gitignore
vendored
2
ai_animation/.gitignore
vendored
|
|
@ -26,4 +26,4 @@ dist-ssr
|
||||||
# AI things
|
# AI things
|
||||||
.claude/
|
.claude/
|
||||||
|
|
||||||
./public/games/
|
public/games/
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { Tween, Group as TweenGroup } from "@tweenjs/tween.js";
|
||||||
import { hideStandingsBoard, } from "./domElements/standingsBoard";
|
import { hideStandingsBoard, } from "./domElements/standingsBoard";
|
||||||
import { MomentsDataSchema, MomentsDataSchemaType } from "./types/moments";
|
import { MomentsDataSchema, MomentsDataSchemaType } from "./types/moments";
|
||||||
|
|
||||||
//FIXME: This whole file is a mess. Need to organkze and format
|
//FIXME: This whole file is a mess. Need to organize and format
|
||||||
|
|
||||||
enum AvailableMaps {
|
enum AvailableMaps {
|
||||||
STANDARD = "standard"
|
STANDARD = "standard"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { loadBtn, prevBtn, nextBtn, speedSelector, fileInput, playBtn, mapView,
|
||||||
import { updateChatWindows } from "./domElements/chatWindows";
|
import { updateChatWindows } from "./domElements/chatWindows";
|
||||||
import { initStandingsBoard, hideStandingsBoard, showStandingsBoard } from "./domElements/standingsBoard";
|
import { initStandingsBoard, hideStandingsBoard, showStandingsBoard } from "./domElements/standingsBoard";
|
||||||
import { initRelationshipPopup, hideRelationshipPopup, updateRelationshipPopup } from "./domElements/relationshipPopup";
|
import { initRelationshipPopup, hideRelationshipPopup, updateRelationshipPopup } from "./domElements/relationshipPopup";
|
||||||
import { displayPhaseWithAnimation, advanceToNextPhase, resetToPhase } from "./phase";
|
import { displayPhaseWithAnimation, advanceToNextPhase, resetToPhase, nextPhase, previousPhase } from "./phase";
|
||||||
import { config } from "./config";
|
import { config } from "./config";
|
||||||
import { Tween, Group, Easing } from "@tweenjs/tween.js";
|
import { Tween, Group, Easing } from "@tweenjs/tween.js";
|
||||||
import { initRotatingDisplay, updateRotatingDisplay } from "./components/rotatingDisplay";
|
import { initRotatingDisplay, updateRotatingDisplay } from "./components/rotatingDisplay";
|
||||||
|
|
@ -23,15 +23,8 @@ let prevPos
|
||||||
|
|
||||||
// --- INITIALIZE SCENE ---
|
// --- INITIALIZE SCENE ---
|
||||||
function initScene() {
|
function initScene() {
|
||||||
gameState.initScene()
|
gameState.createThreeScene()
|
||||||
|
|
||||||
// Lighting (keep it simple)
|
|
||||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
|
||||||
gameState.scene.add(ambientLight);
|
|
||||||
|
|
||||||
const dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
|
|
||||||
dirLight.position.set(300, 400, 300);
|
|
||||||
gameState.scene.add(dirLight);
|
|
||||||
|
|
||||||
// Initialize standings board
|
// Initialize standings board
|
||||||
initStandingsBoard();
|
initStandingsBoard();
|
||||||
|
|
@ -267,12 +260,10 @@ fileInput.addEventListener('change', e => {
|
||||||
});
|
});
|
||||||
|
|
||||||
prevBtn.addEventListener('click', () => {
|
prevBtn.addEventListener('click', () => {
|
||||||
if (gameState.phaseIndex > 0) {
|
previousPhase()
|
||||||
resetToPhase(gameState.phaseIndex - 1)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
nextBtn.addEventListener('click', () => {
|
nextBtn.addEventListener('click', () => {
|
||||||
advanceToNextPhase()
|
nextPhase()
|
||||||
});
|
});
|
||||||
|
|
||||||
playBtn.addEventListener('click', togglePlayback);
|
playBtn.addEventListener('click', togglePlayback);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,42 @@ import { config } from "./config";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function _setPhase(phaseIndex: number) {
|
||||||
|
const gameLength = gameState.gameData.phases.length
|
||||||
|
// Validate that the phaseIndex is within the bounds of the game length.
|
||||||
|
if (phaseIndex >= gameLength || phaseIndex < 0) {
|
||||||
|
throw new Error(`Provided invalid phaseIndex, cannot setPhase to ${phaseIndex} - game has ${gameState.gameData.phases.length} phases`)
|
||||||
|
}
|
||||||
|
if (Math.abs(phaseIndex - gameState.phaseIndex) > 1) {
|
||||||
|
// We're moving more than one Phase, to do so clear the board and reInit the units on the correct phase
|
||||||
|
gameState.unitAnimations = [];
|
||||||
|
initUnits(phaseIndex)
|
||||||
|
updateMapOwnership()
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Finally, update the gameState with the current phaseIndex
|
||||||
|
gameState.phaseIndex = phaseIndex
|
||||||
|
// If we're at the end of the game, don't attempt to animate.
|
||||||
|
if (phaseIndex === gameLength - 1) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
displayPhase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nextPhase() {
|
||||||
|
_setPhase(gameState.phaseIndex + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function previousPhase() {
|
||||||
|
_setPhase(gameState.phaseIndex - 1)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unified function to display a phase with proper transitions
|
* Unified function to display a phase with proper transitions
|
||||||
* Handles both initial display and animated transitions between phases
|
* Handles both initial display and animated transitions between phases
|
||||||
|
|
@ -86,8 +122,8 @@ export function displayPhase(skipMessages = false) {
|
||||||
* Used when first loading a game
|
* Used when first loading a game
|
||||||
*/
|
*/
|
||||||
export function displayInitialPhase() {
|
export function displayInitialPhase() {
|
||||||
initUnits();
|
|
||||||
gameState.phaseIndex = 0;
|
gameState.phaseIndex = 0;
|
||||||
|
initUnits(0);
|
||||||
displayPhase(true);
|
displayPhase(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,17 +135,6 @@ export function displayPhaseWithAnimation() {
|
||||||
displayPhase(false);
|
displayPhase(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicityly sets the phase to a given index,
|
|
||||||
// Removes and recreates all units.
|
|
||||||
export function resetToPhase(index: number) {
|
|
||||||
gameState.phaseIndex = index
|
|
||||||
gameState.unitAnimations = [];
|
|
||||||
gameState.unitMeshes.map(unitMesh => gameState.scene.remove(unitMesh))
|
|
||||||
|
|
||||||
updateMapOwnership()
|
|
||||||
initUnits()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advances to the next phase in the game sequence
|
* Advances to the next phase in the game sequence
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,27 @@ const PhaseSchema = z.object({
|
||||||
messages: z.array(z.any()),
|
messages: z.array(z.any()),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
orders: z.record(PowerENUMSchema, z.array(OrderFromString).nullable()),
|
orders: z.record(PowerENUMSchema, z.array(OrderFromString).nullable()),
|
||||||
results: z.record(z.string(), z.array(z.any())),
|
results: z.record(z.string(), z.array(z.any())).transform((originalResults) => {
|
||||||
|
// Transform results from {"A BUD": [results]} to {A: {"BUD": [results]}, F: {"BUD": [results]}}
|
||||||
|
const transformed: { A: Record<string, any[]>, F: Record<string, any[]> } = { A: {}, F: {} };
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(originalResults)) {
|
||||||
|
const tokens = key.split(' ');
|
||||||
|
if (tokens.length >= 2) {
|
||||||
|
const unitType = tokens[0]; // "A" or "F"
|
||||||
|
const province = tokens[1].split('/')[0]; // Remove coast specification if present
|
||||||
|
|
||||||
|
if (unitType === 'A' || unitType === 'F') {
|
||||||
|
if (!transformed[unitType as 'A' | 'F']) {
|
||||||
|
transformed[unitType as 'A' | 'F'] = {};
|
||||||
|
}
|
||||||
|
transformed[unitType as 'A' | 'F'][province] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformed;
|
||||||
|
}),
|
||||||
state: z.object({
|
state: z.object({
|
||||||
units: z.record(PowerENUMSchema, z.array(z.string())),
|
units: z.record(PowerENUMSchema, z.array(z.string())),
|
||||||
centers: z.record(PowerENUMSchema, z.array(ProvinceENUMSchema)),
|
centers: z.record(PowerENUMSchema, z.array(ProvinceENUMSchema)),
|
||||||
|
|
|
||||||
|
|
@ -94,25 +94,26 @@ export function createAnimationsForNextPhase() {
|
||||||
}
|
}
|
||||||
for (const order of orders) {
|
for (const order of orders) {
|
||||||
// Check if unit bounced
|
// Check if unit bounced
|
||||||
let lastPhaseResultMatches = Object.entries(previousPhase.results).filter(([key, _]) => {
|
// With new format: {A: {"BUD": [results]}, F: {"BUD": [results]}}
|
||||||
return key.split(" ")[1] == order.unit.origin
|
const unitType = order.unit.type;
|
||||||
}).map(val => {
|
const unitOrigin = order.unit.origin;
|
||||||
// in the form "A BER" (unitType origin)
|
|
||||||
let orderSplit = val[0].split(" ")
|
let result = undefined;
|
||||||
return { origin: orderSplit[1], unitType: orderSplit[0], result: val[1][0] }
|
if (previousPhase.results && previousPhase.results[unitType] && previousPhase.results[unitType][unitOrigin]) {
|
||||||
})
|
const resultArray = previousPhase.results[unitType][unitOrigin];
|
||||||
// This should always exist. If we don't have a match here, that means something went wrong with our order parsing
|
result = resultArray.length > 0 ? resultArray[0] : null;
|
||||||
if (!lastPhaseResultMatches) {
|
|
||||||
throw new Error("No result present in current phase for previous phase order. Cannot continue")
|
|
||||||
}
|
}
|
||||||
if (lastPhaseResultMatches.length > 1) {
|
|
||||||
throw new Error("Multiple matching results from last phase. Should only ever be 1.")
|
if (result === undefined) {
|
||||||
|
throw new Error(`No result present in current phase for previous phase order: ${unitType} ${unitOrigin}. Cannot continue`);
|
||||||
}
|
}
|
||||||
if (lastPhaseResultMatches[0].result === "bounce") {
|
|
||||||
|
if (result === "bounce") {
|
||||||
order.type = "bounce"
|
order.type = "bounce"
|
||||||
}
|
}
|
||||||
// If the result is void, that means the move was not valid?
|
// If the result is void, that means the move was not valid?
|
||||||
if (lastPhaseResultMatches[0].result === "void") continue;
|
if (result === "void") continue;
|
||||||
let unitIndex = -1
|
let unitIndex = -1
|
||||||
|
|
||||||
switch (order.type) {
|
switch (order.type) {
|
||||||
|
|
@ -163,10 +164,6 @@ export function createAnimationsForNextPhase() {
|
||||||
|
|
||||||
case "support":
|
case "support":
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,11 +144,17 @@ export function createUnitMesh(unitData: UnitData): THREE.Group {
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _removeUnitsFromBoard() {
|
||||||
|
|
||||||
// Creates the units for the current gameState.phaseIndex.
|
gameState.unitMeshes.map((mesh) => gameState.scene.remove(mesh))
|
||||||
export function initUnits() {
|
}
|
||||||
createSupplyCenters()
|
|
||||||
for (const [power, unitArr] of Object.entries(gameState.gameData.phases[gameState.phaseIndex].state.units)) {
|
/*
|
||||||
|
* Given a phaseIndex, Add the units for that phase to the board, in the province specified in the game.json file.
|
||||||
|
*/
|
||||||
|
function _addUnitsToBoard(phaseIndex: number) {
|
||||||
|
_removeUnitsFromBoard()
|
||||||
|
for (const [power, unitArr] of Object.entries(gameState.gameData.phases[phaseIndex].state.units)) {
|
||||||
unitArr.forEach(unitStr => {
|
unitArr.forEach(unitStr => {
|
||||||
const match = unitStr.match(/^([AF])\s+(.+)$/);
|
const match = unitStr.match(/^([AF])\s+(.+)$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
|
|
@ -163,3 +169,9 @@ export function initUnits() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Creates the units for the current gameState.phaseIndex.
|
||||||
|
export function initUnits(phaseIndex: number) {
|
||||||
|
if (phaseIndex === undefined) throw new Error("Cannot pass undefined phaseIndex");
|
||||||
|
createSupplyCenters()
|
||||||
|
_addUnitsToBoard(phaseIndex)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue