From f60cdfa576490d9888a88d73911c0388ffa159c6 Mon Sep 17 00:00:00 2001 From: berg44 Date: Fri, 15 Sep 2023 14:44:14 -0600 Subject: [PATCH] Hashed Option for towns&roads to indicate Premove is Planned! --- soos-client/src/components/hex/Hex.tsx | 2 +- soos-client/src/components/road/Road.tsx | 27 ++++++---- soos-client/src/components/town/Town.scss | 2 +- soos-client/src/components/town/Town.tsx | 41 +++++++++------ .../src/features/gameView/GameView.tsx | 10 ++-- .../gameView/components/board/Board.tsx | 40 +++++++++------ soos-gamelogic/src/build-actions.ts | 14 +++--- soos-gamelogic/src/game.ts | 7 +++ .../src/routes/socket-io/game-socket.ts | 50 ++++++++++++------- 9 files changed, 119 insertions(+), 74 deletions(-) diff --git a/soos-client/src/components/hex/Hex.tsx b/soos-client/src/components/hex/Hex.tsx index ea5ba76..2cbc3ea 100644 --- a/soos-client/src/components/hex/Hex.tsx +++ b/soos-client/src/components/hex/Hex.tsx @@ -3,7 +3,7 @@ import { hexCoordsToPixels } from '../../utils'; import './Hex.scss'; // Debug thing to show the HexCoords of every hex on the board. -const showAllCoords = false; +const showAllCoords =true; function getTerrainClass(terrainType: TerrainType, resourceType?: ResourceType): string { let terrainClass = ''; diff --git a/soos-client/src/components/road/Road.tsx b/soos-client/src/components/road/Road.tsx index 1efd20a..025f0df 100644 --- a/soos-client/src/components/road/Road.tsx +++ b/soos-client/src/components/road/Road.tsx @@ -6,16 +6,17 @@ import './Road.scss'; export type RoadProps = { gameRoad: GameRoad; highlighted: boolean; - premove: boolean; + makingPremoves: boolean; + premoveQueued: boolean; onClick: () => void; }; export const Road = (props: RoadProps) => { - const { gameRoad, onClick, premove, highlighted } = props; + const { gameRoad, onClick, makingPremoves, premoveQueued, highlighted } = props; const coords = gameRoad.coords; const { x, y } = edgeCoordsToPixels(coords); const playerIdx = gameRoad.playerIdx ?? -1; - const shouldDisplay = premove || highlighted || playerIdx !== -1; + const shouldDisplay = makingPremoves || highlighted || playerIdx !== -1|| premoveQueued; // don't display anything at all if (!shouldDisplay) { @@ -31,20 +32,20 @@ export const Road = (props: RoadProps) => { top: y + 'px', }} onClick={() => { - console.log("road Clicked"); - onClick() + console.log('road Clicked'); + onClick(); }} > {makeSVG( coords.direction, Variables.PlayerColors[playerIdx], - props.premove || (playerIdx === -1 && props.highlighted), + props.makingPremoves || (playerIdx === -1 && props.highlighted), premoveQueued, )} ); }; -function makeSVG(direction: HexDirection, color: string, dotted: boolean) { +function makeSVG(direction: HexDirection, color: string, dotted: boolean, hashed:boolean) { let rotation = 0, translateX = 0, translateY = 0; if (direction === HexDirection.NW || direction === HexDirection.SE) { rotation = -33; @@ -61,13 +62,13 @@ function makeSVG(direction: HexDirection, color: string, dotted: boolean) { } let fillOpacity, strokeDasharray, strokeWidth = 0; - if (dotted) { + if (dotted && !hashed) { // dotted line fillOpacity = '0'; strokeDasharray = '3 2'; strokeWidth = 3; } - + const checkerSize = 3; return ( + + + + + + diff --git a/soos-client/src/components/town/Town.scss b/soos-client/src/components/town/Town.scss index edb343c..5b151c2 100644 --- a/soos-client/src/components/town/Town.scss +++ b/soos-client/src/components/town/Town.scss @@ -3,7 +3,7 @@ height: 20px; position: absolute; - z-index: 10; + z-index: 11; font-size: 15px; text-align: center; } diff --git a/soos-client/src/components/town/Town.tsx b/soos-client/src/components/town/Town.tsx index 91cd562..4315f7c 100644 --- a/soos-client/src/components/town/Town.tsx +++ b/soos-client/src/components/town/Town.tsx @@ -8,6 +8,7 @@ export type TownProps = { gameTown: GameTown; highlighted: boolean; makingPremove: boolean; + premoveQueued:boolean; onClick: (vertexCoords: VertexCoords) => void; }; @@ -35,11 +36,11 @@ const suburbSize = 28; const suburbPolygonPoints = townPoints(suburbSize, [[ 0, .25 ], [ 0, 1 ], [ 1, 1 ], [ 1, .5 ], [ .5, .5 ], [ .5, .25 ], [ .25, 0 ]]); export const Town = (props: TownProps) => { - const { boardPlayerIdx, gameTown, onClick, makingPremove, highlighted } = props; + const { boardPlayerIdx, gameTown, onClick, makingPremove, highlighted, premoveQueued } = props; const playerIdx = gameTown.playerIdx ?? -1; const playerColor = Variables.PlayerColors[playerIdx]; - const playerClass = gameTown.isUnclaimed() ? 'unclaimed' : ''; + const playerClass = gameTown.isUnclaimed() && !premoveQueued ? 'unclaimed' : ''; let highlightedClass = ''; if (highlighted) { highlightedClass = 'highlight'; @@ -73,16 +74,24 @@ export const Town = (props: TownProps) => { break; } - svg = makeTownSVG(townSize, points, playerColor, - highlighted || (makingPremove && activePlayersTown), highlighted); + let townColor = playerColor; + let townColorId = playerIdx.toString(); + if(boardPlayerIdx!==undefined && premoveQueued){ + townColor=Variables.PlayerColors[boardPlayerIdx]; + townColorId = boardPlayerIdx.toString(); + console.log('prepping premove on town for ' + townColor); + } + + svg = makeTownSVG(townSize, points, townColor, + highlighted || (makingPremove && activePlayersTown), premoveQueued, townColorId); const { x, y } = vertexCoordsToPixels(gameTown.coords!); - const shouldDisplay = highlighted || playerIdx !== -1; + const shouldDisplay = highlighted || playerIdx !== -1 || premoveQueued; if (!shouldDisplay) { return null; } - + highlightedClass= ''; return (
{ ); }; -function makeTownSVG(townSize:number, points:string, playerColor:string, dotted:boolean, - hashed:boolean ){ +function makeTownSVG(townSize:number, points:string, townColor:string, dotted:boolean, + hashed:boolean, playerId:string ){ let fillOpacity, strokeDasharray, strokeWidth = 0; - if (dotted) { + if (dotted && !hashed) { // dotted line fillOpacity = '0'; strokeDasharray = '3 2'; strokeWidth = 3; } - + const checkersName = 'checkers'+playerId; + const checkersURL = 'url(#'+checkersName+')'; const checkerSize = 3; return ( - - - - + + + diff --git a/soos-client/src/features/gameView/GameView.tsx b/soos-client/src/features/gameView/GameView.tsx index 7837730..ebe1f56 100644 --- a/soos-client/src/features/gameView/GameView.tsx +++ b/soos-client/src/features/gameView/GameView.tsx @@ -14,8 +14,6 @@ import { import { Player } from '~/src/components'; import { Board, ResourceBar, TradeWindow } from './components'; -let premoves: BuildAction[] = []; - type GameViewProps = { socket: Socket; game: Game; @@ -30,9 +28,8 @@ export const GameView = (props: GameViewProps) => { const [ playerId, setPlayerId ] = React.useState(undefined); const [ isTradeWindowShowing, setIsTradeWindowShowing ] = React.useState(false); const [ makingPremoves, setMakingPremoves ] = React.useState(false); - const [ possibleBuildActions, setPossibleBuildActions ] = React.useState([]); - + const [ queuedPremoves, setQueuedPremoves ] = React.useState([]); // Set up force update function const [ count, setCount ] = React.useState(0); game.forceUpdate = () => { @@ -59,15 +56,17 @@ export const GameView = (props: GameViewProps) => { setPossibleBuildActions([]); console.log('pba: ' + possibleBuildActions.length); console.log('got new game state'); + socket.emit('getPremoves'); } function setPremoves(serverPremoves: BuildAction[]) { console.log('got premoves: '); console.log(serverPremoves); - premoves = []; + const premoves:BuildAction[] = []; for (const serverMove of serverPremoves) { premoves.push(hydrateBuildAction(serverMove)); } + setQueuedPremoves(premoves); game.forceUpdate(); } @@ -132,6 +131,7 @@ export const GameView = (props: GameViewProps) => { makingPremoves={makingPremoves} playerId={playerId} possibleBuildActions={possibleBuildActions} + queuedPremoves = {queuedPremoves} /> {/* List of players' resource count & victory points */} diff --git a/soos-client/src/features/gameView/components/board/Board.tsx b/soos-client/src/features/gameView/components/board/Board.tsx index 55b8461..4ac6609 100644 --- a/soos-client/src/features/gameView/components/board/Board.tsx +++ b/soos-client/src/features/gameView/components/board/Board.tsx @@ -16,12 +16,13 @@ type BoardProps = { makingPremoves: boolean; playerId?: number; possibleBuildActions: BuildAction[]; + queuedPremoves: BuildAction[]; }; const premoves: BuildAction[] = []; export const Board = (props: BoardProps) => { - const { game, socket, makingPremoves, playerId, possibleBuildActions } = props; + const { game, socket, makingPremoves, playerId, possibleBuildActions, queuedPremoves } = props; function sendGameStateToServer() { socket.emit('newGameState', game.toString()); @@ -53,31 +54,36 @@ export const Board = (props: BoardProps) => { const townBuildActions = possibleBuildActions.filter(pba => pba.type === BuildActionType.Settlement || pba.type === BuildActionType.City); console.log('town build actions: ' + townBuildActions.length); const isSettlementSetup = game.setupPhase() && game.currPlayerIdx === playerId && !game.claimedSettlement; - + const townQdActions = queuedPremoves.filter(pba => pba.type === BuildActionType.Settlement || pba.type === BuildActionType.City); const towns = []; + console.log('Building towns for player '+ playerId); for (const town of game.map.towns) { if (!town || !town.coords) { continue; } const townCoords = town.coords; - + let queued = false; let buildAction: BuildAction | undefined; if (isSettlementSetup) { buildAction = new BuildSettlementAction(playerId, townCoords); } else { // TODO optimize buildAction = townBuildActions.find(pba => townCoords.equals(pba.location)); + queued = townQdActions.find(pba => townCoords.equals(pba.location))!==undefined; if(buildAction!==undefined) { console.log('Found build action for location: ' + townCoords.toString()); } + if(queued){ + console.log('Premove found for town! ' + townCoords.toString()); + } } - towns.push( { if (buildAction) { if(makingPremoves){ @@ -93,31 +99,29 @@ export const Board = (props: BoardProps) => { } let roadBuildActions = possibleBuildActions.filter(pba => pba.type === BuildActionType.Road); - + const roadQdActions = queuedPremoves.filter(pba=>pba.type===BuildActionType.Road); if (playerId!==undefined && ((game.gamePhase === GamePhase.PlaceSettlement1 || game.gamePhase === GamePhase.PlaceSettlement2) && game.currPlayerIdx === playerId && game.claimedSettlement) || makingPremoves) { // custom roadBuildActions for setup phase roadBuildActions = game.getValidBuildActions(playerId!, BuildActionType.Road); } - for (const premoveAction of premoves){ - if(premoveAction.type===BuildActionType.Road) { - roadBuildActions.push(premoveAction); - } - } const roads = []; + let queued = false; for (const road of game.map.roads) { const roadCoords = road.coords; // TODO optimize const buildAction = roadBuildActions.find(pba => road.coords.equals(pba.location)); - + queued = false; + queued = roadQdActions.find(pba => roadCoords.equals(pba.location))!==undefined; roads.push( { if (buildAction) { if(makingPremoves){ @@ -144,9 +148,15 @@ export const Board = (props: BoardProps) => { return (
- {hexes} - {towns} - {roads} +
+ {hexes} +
+
+ {towns} +
+
+ {roads} +
{robber}
); diff --git a/soos-gamelogic/src/build-actions.ts b/soos-gamelogic/src/build-actions.ts index c5255bc..0990c7c 100644 --- a/soos-gamelogic/src/build-actions.ts +++ b/soos-gamelogic/src/build-actions.ts @@ -97,19 +97,19 @@ export class BuildRoadAction implements BuildAction { isPossible(gameState: Game): boolean { if (this.shouldDisqualify(gameState)) { - console.log("road is claimed"); + console.log('road is claimed'); return false; } // if it's setup phase, it just needs to be their turn if (gameState.setupPhase()) { - console.log("road being built during set up phase"); + console.log('road being built during set up phase'); return gameState.currPlayerIdx === this.playerId && gameState.claimedSettlement; } const player = gameState.players[this.playerId]; if (player===undefined || !player.hasResources(AllBuildCosts[this.type])) { - console.log("player does not have resources or player does not exist"); + console.log('player does not have resources or player does not exist'); return false; } @@ -247,21 +247,21 @@ export class BuildCityAction implements BuildAction { isPossible(gameState: Game): boolean { if (this.shouldDisqualify(gameState)) {//city action: town has to be claimed, and player ID has to be the same. - console.log("city not possible: not claimed or wrong player"); + console.log('city not possible: not claimed or wrong player'); return false; } const player = gameState.players[this.playerId]; if (player) { - console.log("city possible? checking resources"); + console.log('city possible? checking resources'); return player.hasResources(AllBuildCosts[this.type]); } - console.log("city is possible default fail"); + console.log('city is possible default fail'); return false; } shouldDisqualify(gameState: Game): boolean { const validSettlePlan = gameState.settlePremovePresent(this.location, this.playerId); - console.log('checking valid city move: city owned by ' + gameState.map.townAt(this.location)?.playerIdx + " vs " + this.playerId + " and validSP: " + validSettlePlan); + console.log('checking valid city move: city owned by ' + gameState.map.townAt(this.location)?.playerIdx + ' vs ' + this.playerId + ' and validSP: ' + validSettlePlan); return !validSettlePlan || gameState.map.townAt(this.location)?.playerIdx !== this.playerId; } diff --git a/soos-gamelogic/src/game.ts b/soos-gamelogic/src/game.ts index b2038f4..34085c7 100644 --- a/soos-gamelogic/src/game.ts +++ b/soos-gamelogic/src/game.ts @@ -441,6 +441,13 @@ export default class Game { } } while (playerIndex !== this.currPlayerIdx); + //clean-up: + for (let i = this.premoveActions.length-1; i>=0;i--){ + if(this.premoveActions[i].shouldDisqualify(this)){ + this.premoveActions.splice(i); + } + } + return; } diff --git a/soos-server/src/routes/socket-io/game-socket.ts b/soos-server/src/routes/socket-io/game-socket.ts index 6b5f403..2f5b6e2 100644 --- a/soos-server/src/routes/socket-io/game-socket.ts +++ b/soos-server/src/routes/socket-io/game-socket.ts @@ -77,6 +77,7 @@ const gameEvents: Set = new Set([ 'premove', 'logPremoves', 'retrieveGameState', + 'getPremoves', ]); export const registerGameSocketListeners = ( @@ -117,25 +118,25 @@ export const registerGameSocketListeners = ( callback(context?.game.toString()); }); - socket.on('newGameState', (newGameState) => { - if (!context) { - return new Error(); - } - - //console.log(newGameState); - console.log('got New Game State'); - let premoves: BuildAction[] = []; - if (context.game) { - premoves = context.game.premoveActions; - } - context.game = gameFromString(newGameState); - for (const moves of premoves) { - context.game.addPremove(moves); - } - - saveGame(context); - io.to(context.activeGamecode).emit('updateGameState', newGameState); - }); + // socket.on('newGameState', (newGameState) => { + // if (!context) { + // return new Error(); + // } + + // //console.log(newGameState); + // console.log('got New Game State'); + // let premoves: BuildAction[] = []; + // if (context.game) { + // premoves = context.game.premoveActions; + // } + // context.game = gameFromString(newGameState); + // for (const moves of premoves) { + // context.game.addPremove(moves); + // } + + // saveGame(context); + // io.to(context.activeGamecode).emit('updateGameState', newGameState); + // }); socket.on('autoPickSettlements', () => { if (!context) { @@ -193,6 +194,17 @@ export const registerGameSocketListeners = ( socket.emit('setPremoves', gameMoves); }); + socket.on('getPremoves', () => { + if (!context) { + return new Error(); + } + const gameMoves = context.game.getPremoves(context.playerIndex); + // for (let gameMove of gameMoves) { + // gameMove = hydrateBuildAction(gameMove); + // } + socket.emit('setPremoves', gameMoves); + }); + socket.on('nextTurn', ()=>{ if (!context) { return new Error();