diff --git a/agot-bg-game-server/public/images/westeros-cards/DenseFog.png b/agot-bg-game-server/public/images/westeros-cards/DenseFog.png new file mode 100644 index 000000000..ce0a02aa7 Binary files /dev/null and b/agot-bg-game-server/public/images/westeros-cards/DenseFog.png differ diff --git a/agot-bg-game-server/src/client/EntireGameComponent.tsx b/agot-bg-game-server/src/client/EntireGameComponent.tsx index 2ca41f06b..a1f5ed7e8 100644 --- a/agot-bg-game-server/src/client/EntireGameComponent.tsx +++ b/agot-bg-game-server/src/client/EntireGameComponent.tsx @@ -193,7 +193,7 @@ export default class EntireGameComponent extends Component - From round 5 onwards, each house returns its alternative deck when the last house card has been played. + From round {this.settings.houseCardsEvolutionRound} onwards, each house returns its alternative deck when the last house card has been played. } popperConfig={{ modifiers: [preventOverflow] }} > diff --git a/agot-bg-game-server/src/client/GameLogListComponent.tsx b/agot-bg-game-server/src/client/GameLogListComponent.tsx index f7673e744..27b81bf5e 100644 --- a/agot-bg-game-server/src/client/GameLogListComponent.tsx +++ b/agot-bg-game-server/src/client/GameLogListComponent.tsx @@ -850,10 +850,18 @@ export default class GameLogListComponent extends Component, causing House {skippedHouse.name}'s turn to be skipped + until the next pass of the Iron Throne track + : null; return

Doran Martell: House {house.name} decided to move House - {affectedHouse.name} to the bottom of the {influenceTrack} track. + {affectedHouse.name} to the bottom of the {influenceTrack} track{skippedAddition}.

; } case "ser-gerris-drinkwater-used": { diff --git a/agot-bg-game-server/src/client/GameSettingsComponent.tsx b/agot-bg-game-server/src/client/GameSettingsComponent.tsx index d8cd0eeb8..d9c4dfcc0 100644 --- a/agot-bg-game-server/src/client/GameSettingsComponent.tsx +++ b/agot-bg-game-server/src/client/GameSettingsComponent.tsx @@ -349,7 +349,7 @@ export default class GameSettingsComponent extends Component this.changeGameSettings(() => this.gameSettings.asosHouseCards = !this.gameSettings.asosHouseCards)} /> - + this.changeGameSettings(() => this.gameSettings.houseCardsEvolution = !this.gameSettings.houseCardsEvolution)} /> + { + this.gameSettings.houseCardsEvolution && ( +
+ - Round: + +
+ ) + } {this.props.entireGame.isCustomBalancingOptionAvailable(this.gameSettings) && - {this.props.gameState.revealeadWesterosCards.length > 0 && ( + {this.props.gameState.revealedWesterosCards.length > 0 && ( - {this.props.gameState.revealeadWesterosCards.map((wc, i) => ( + {this.props.gameState.revealedWesterosCards.map((wc, i) => ( = new BetterMap(); @observable visibleRegionsPerPlayer: BetterMap = new BetterMap(); @observable publicVisibleRegions: Region[] = []; + unitVisibilityRange = 1; votes: BetterMap = new BetterMap(); @observable paused: Date | null = null; @@ -336,6 +337,7 @@ export default class IngameGameState extends GameState< }); if (this.fogOfWar) { + this.unitVisibilityRange = 1; this.publicVisibleRegions = []; this.entireGame.users.values.filter(u => u.connected).forEach(u => { this.entireGame.sendMessageToClients([u], { @@ -1658,7 +1660,7 @@ export default class IngameGameState extends GameState< return this.visibleRegionsPerPlayer.get(player); } - calculateVisibleRegionsForPlayer(player: Player | null, unitVisibilityRange = 1): Region[] { + calculateVisibleRegionsForPlayer(player: Player | null): Region[] { if (!this.fogOfWar || !player) { return []; } @@ -1676,7 +1678,7 @@ export default class IngameGameState extends GameState< const checkedRegions: Region[] = []; // Additionally we see regions adjacents to our regions with units - for(let i=0; i 1) { + if (this.unitVisibilityRange > 1) { regionsWithUnits.push(...additionalRegionsToCheck); regionsWithUnits = _.uniq(regionsWithUnits); } @@ -2306,6 +2308,7 @@ export default class IngameGameState extends GameState< ? this.visibleRegionsPerPlayer.entries.map(([p, regions]) => [p.user.id, regions.map(r => r.id)]) : this.visibleRegionsPerPlayer.entries.filter(([p, _regions]) => p.user == user).map(([p, regions]) => [p.user.id, regions.map(r => r.id)]), publicVisibleRegions: this.publicVisibleRegions.map(r => r.id), + unitVisibilityRange: this.unitVisibilityRange, oldPlayerIds: this.oldPlayerIds, replacerIds: this.replacerIds, timeoutPlayerIds: this.timeoutPlayerIds, @@ -2332,6 +2335,7 @@ export default class IngameGameState extends GameState< data.visibleRegionsPerPlayer.map(([uid, rids]) => [ingameGameState.players.get(entireGame.users.get(uid)), rids.map(rid => ingameGameState.world.regions.get(rid))]) ); ingameGameState.publicVisibleRegions = data.publicVisibleRegions.map(rid => ingameGameState.world.regions.get(rid)); + ingameGameState.unitVisibilityRange = data.unitVisibilityRange; ingameGameState.oldPlayerIds = data.oldPlayerIds; ingameGameState.replacerIds = data.replacerIds; ingameGameState.timeoutPlayerIds = data.timeoutPlayerIds; @@ -2378,6 +2382,7 @@ export interface SerializedIngameGameState { players: SerializedPlayer[]; visibleRegionsPerPlayer: [string, string[]][]; publicVisibleRegions: string[]; + unitVisibilityRange: number; oldPlayerIds: string[]; replacerIds: string[]; timeoutPlayerIds: string[]; diff --git a/agot-bg-game-server/src/common/ingame-game-state/action-game-state/resolve-march-order-game-state/combat-game-state/immediately-house-card-abilities-resolution-game-state/doran-martell-ability-game-state/DoranMartellAbilityGameState.ts b/agot-bg-game-server/src/common/ingame-game-state/action-game-state/resolve-march-order-game-state/combat-game-state/immediately-house-card-abilities-resolution-game-state/doran-martell-ability-game-state/DoranMartellAbilityGameState.ts index 4824a22d4..5a5082712 100644 --- a/agot-bg-game-server/src/common/ingame-game-state/action-game-state/resolve-march-order-game-state/combat-game-state/immediately-house-card-abilities-resolution-game-state/doran-martell-ability-game-state/DoranMartellAbilityGameState.ts +++ b/agot-bg-game-server/src/common/ingame-game-state/action-game-state/resolve-march-order-game-state/combat-game-state/immediately-house-card-abilities-resolution-game-state/doran-martell-ability-game-state/DoranMartellAbilityGameState.ts @@ -29,11 +29,31 @@ export default class DoranMartellAbilityGameState extends GameState< onSimpleChoiceGameStateEnd(choice: number, resolvedAutomatically: boolean): void { const enemy = this.combatGameState.getEnemy(this.childGameState.house); + // Remember the house that currently would resolve next march order + const nextHouse = this.getHouseThatWouldResolveNextMarchOrder(); + // Put the enemy at the end of the influence track const influenceTrack = this.game.getInfluenceTrackByI(choice); const newInfluenceTrack = _.concat(_.without(influenceTrack, enemy), enemy); this.ingame.setInfluenceTrack(choice, newInfluenceTrack); + if (choice == 0) { + // Add the skippedTurnForHouse property if next player in order loses their turn. + const nextHouseAfterDoran = this.getHouseThatWouldResolveNextMarchOrder(); + + if (nextHouse && nextHouse != nextHouseAfterDoran) { + this.ingame.log({ + type: "doran-used", + house: this.childGameState.house.id, + affectedHouse: enemy.id, + influenceTrack: choice, + skippedHouse: nextHouse.id, + }, resolvedAutomatically); + this.parentGameState.onHouseCardResolutionFinish(this.childGameState.house); + return; + } + } + this.ingame.log({ type: "doran-used", house: this.childGameState.house.id, @@ -62,6 +82,28 @@ export default class DoranMartellAbilityGameState extends GameState< ); } + getHouseThatWouldResolveNextMarchOrder(): House | null { + const turnOrder = this.game.getTurnOrder(); + const numberOfHouses = turnOrder.length; + + let currentIndex = this.combatGameState.parentGameState.currentTurnOrderIndex; + + // Check each house in order to find one that has an available March order. + // Check at most once for each house + for (let i = 0;i < numberOfHouses;i++) { + currentIndex = (currentIndex + 1) % numberOfHouses; + const currentHouseToCheck = turnOrder[currentIndex]; + + const regions = this.combatGameState.actionGameState.getRegionsWithMarchOrderOfHouse(currentHouseToCheck); + if (regions.length > 0) { + return currentHouseToCheck; + } + } + + // If no house has any march order available, return null + return null; + } + onPlayerMessage(player: Player, message: ClientMessage): void { this.childGameState.onPlayerMessage(player, message); } diff --git a/agot-bg-game-server/src/common/ingame-game-state/action-game-state/resolve-march-order-game-state/combat-game-state/post-combat-game-state/PostCombatGameState.ts b/agot-bg-game-server/src/common/ingame-game-state/action-game-state/resolve-march-order-game-state/combat-game-state/post-combat-game-state/PostCombatGameState.ts index b012f8d94..1930b50ff 100644 --- a/agot-bg-game-server/src/common/ingame-game-state/action-game-state/resolve-march-order-game-state/combat-game-state/post-combat-game-state/PostCombatGameState.ts +++ b/agot-bg-game-server/src/common/ingame-game-state/action-game-state/resolve-march-order-game-state/combat-game-state/post-combat-game-state/PostCombatGameState.ts @@ -471,7 +471,7 @@ export default class PostCombatGameState extends GameState< if (house.houseCards.values.every(hc => hc.state == HouseCardState.USED)) { if (this.entireGame.gameSettings.houseCardsEvolution && house.laterHouseCards != null - && this.combat.ingameGameState.game.turn >= 5) { + && this.combat.ingameGameState.game.turn >= this.entireGame.gameSettings.houseCardsEvolutionRound) { // We need to swap to the new deck now this.game.previousPlayerHouseCards.set(house, new BetterMap()); diff --git a/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/GameLog.ts b/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/GameLog.ts index 530513097..66e117e62 100644 --- a/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/GameLog.ts +++ b/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/GameLog.ts @@ -322,6 +322,7 @@ interface DoranUsed { house: string; affectedHouse: string; influenceTrack: number; + skippedHouse?: string; } interface SerGerrisDrinkwaterUsed { diff --git a/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/createGame.ts b/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/createGame.ts index 6bcd8df8b..eb03e0610 100644 --- a/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/createGame.ts +++ b/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/createGame.ts @@ -410,14 +410,13 @@ export default function createGame(ingame: IngameGameState, housesToCreate: stri game.westerosDecks = baseGameData.westerosCards.map(westerosDeckData => { const cards: WesterosCard[] = []; westerosDeckData.forEach((westerosCardData: WesterosCardData) => { - const westerosCardType = westerosCardTypes.get(westerosCardData.type); - const quantity = westerosCardData.quantity ? westerosCardData.quantity : 1; - for (let i = 0;i < quantity;i++) { - const id = ++lastWesterosCardId; - - cards.push(new WesterosCard(id, westerosCardType)); - } - }); + const westerosCardType = westerosCardTypes.get(westerosCardData.type); + const quantity = westerosCardData.quantity ? westerosCardData.quantity : 1; + for (let i = 0;i < quantity;i++) { + const id = ++lastWesterosCardId; + cards.push(new WesterosCard(id, westerosCardType)); + } + }); return shuffleInPlace(cards); }); @@ -467,6 +466,12 @@ export default function createGame(ingame: IngameGameState, housesToCreate: stri game.westerosDecks[0] = cards; } + if (gameSettings.fogOfWar) { + const id = ++lastWesterosCardId; + game.westerosDecks[2].push(new WesterosCard(id, westerosCardTypes.get("dense-fog"))); + shuffleInPlace(game.westerosDecks[2]); + } + game.winterIsComingHappened = []; game.westerosDecks.forEach(_wd => game.winterIsComingHappened.push(false)); diff --git a/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/westeros-card/DenseFogWesterosCardType.ts b/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/westeros-card/DenseFogWesterosCardType.ts new file mode 100644 index 000000000..79d8e2dbc --- /dev/null +++ b/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/westeros-card/DenseFogWesterosCardType.ts @@ -0,0 +1,10 @@ +import WesterosCardType from "./WesterosCardType"; +import WesterosGameState from "../../westeros-game-state/WesterosGameState"; + +export default class DenseFogWesterosCardType extends WesterosCardType { + execute(westeros: WesterosGameState): void { + westeros.ingame.unitVisibilityRange = 0; + westeros.ingame.updateVisibleRegions(true); + westeros.onWesterosCardEnd(); + } +} \ No newline at end of file diff --git a/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/westeros-card/westerosCardTypes.ts b/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/westeros-card/westerosCardTypes.ts index 78a869965..64f4557c1 100644 --- a/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/westeros-card/westerosCardTypes.ts +++ b/agot-bg-game-server/src/common/ingame-game-state/game-data-structure/westeros-card/westerosCardTypes.ts @@ -32,6 +32,7 @@ import FamineWesterosCardType from "./FamineWesterosCardType"; import IronbornRaidWesterosCardType from "./IronbornRaidWesterosCardType"; import ShiftingAmbitionsWesterosCardType from "./ShiftingAmbitionsWesterosCardType"; import NewInformationWesterosCardType from "./NewInformationWesterosCardType"; +import DenseFogWesterosCardType from "./DenseFogWesterosCardType"; export const lastDaysOfSummer = new LastDaysOfSummerWesterosCardType("last-days-of-summer", "Last\xa0Days\xa0of\xa0Summer", "Nothing happens.", "Nothing\xa0happens", 2); export const winterIsComing = new WinterIsComingWesterosCardType("winter-is-coming", "Winter\xa0is\xa0Coming", "Immediately shuffle this deck. Then draw and resolve a new card.", "Reshuffle\xa0deck"); @@ -59,6 +60,7 @@ export const stormOfSwords = new StormOfSwordsWesterosCardType("storm-of-swords" export const rainsOfAutumn = new RainsOfAutumnWesterosCardType("rains-of-autumn", "Rains\xa0of\xa0Autumn", "March +1 Orders cannot be played this Planning Phase.", "No\xa0M+1", 2); export const putToTheSword = new PutToTheSwordWesterosCardType("put-to-the-sword", "Put\xa0to\xa0the\xa0Sword", "The holder of the Valyrian Steel Blade chooses one of the following conditions for this Planning Phase: a) Defense Orders cannot be played b) March +1 Orders cannot be played, or c) no restrictions.", "VSB\xa0decides"); export const wildlingsAttack = new WildlingsAttackWesterosCardType("wildlings-attack", "Wildlings\xa0Attack", "The wildlings attack Westeros.", ""); +export const denseFog = new DenseFogWesterosCardType("dense-fog", "Dense\xa0Fog", "Visibility distance is reduced by one this round.", "", 2); export const domesticDisputes = new DomesticDisputesWesterosCardType("domestic-disputes", "Domestic\xa0Disputes", "The Targaryen player may discard 1 Power\xa0token to choose up to 4 other houses. Place 1 loyalty\xa0token on the home areas of those houses.", "Loyalty\xa0tokens in home areas", 0, ["storms-end"]); export const emptyPromises = new EmptyPromisesWesterosCardType("empty-promises", "Empty\xa0Promises", "The Targaryen player may discard 1 Power\xa0token to place a loyalty\xa0token on either: The Mountains of the Moon or Moat Cailin.", "Place loyalty\xa0tokens", 0, ["the-stony-shore"], ["the-mountains-of-the-moon", "moat-cailin"]); @@ -103,5 +105,6 @@ export const westerosCardTypes = new BetterMap([ [strongholdsOfResistance.id, strongholdsOfResistance], [theLongPlan.id, theLongPlan], [wateringTheSeed.id, wateringTheSeed], - [wordSpreadsQuickly.id, wordSpreadsQuickly] + [wordSpreadsQuickly.id, wordSpreadsQuickly], + [denseFog.id, denseFog] ]); diff --git a/agot-bg-game-server/src/common/ingame-game-state/planning-game-state/PlanningGameState.ts b/agot-bg-game-server/src/common/ingame-game-state/planning-game-state/PlanningGameState.ts index ae789a9ab..cbe49b7f1 100644 --- a/agot-bg-game-server/src/common/ingame-game-state/planning-game-state/PlanningGameState.ts +++ b/agot-bg-game-server/src/common/ingame-game-state/planning-game-state/PlanningGameState.ts @@ -26,7 +26,7 @@ export default class PlanningGameState extends GameState = new BetterMap(); planningRestrictions: PlanningRestriction[]; - revealeadWesterosCards: WesterosCard[]; + revealedWesterosCards: WesterosCard[]; get ingame(): IngameGameState { return this.parentGameState; @@ -44,9 +44,9 @@ export default class PlanningGameState extends GameState pr.id), - revealedWesterosCardIds: this.revealeadWesterosCards.map(wc => wc.id), + revealedWesterosCardIds: this.revealedWesterosCards.map(wc => wc.id), childGameState: this.childGameState.serializeToClient(admin, player) }; } @@ -112,7 +112,7 @@ export default class PlanningGameState extends GameState planningRestrictions.get(prid)); - planningGameState.revealeadWesterosCards = data.revealedWesterosCardIds.map((cid, i) => getById(ingameGameState.game.westerosDecks[i], cid)); + planningGameState.revealedWesterosCards = data.revealedWesterosCardIds.map((cid, i) => getById(ingameGameState.game.westerosDecks[i], cid)); planningGameState.placedOrders = new BetterMap( data.placedOrders.map( ([regionId, orderId]) => [ diff --git a/agot-bg-game-server/src/common/ingame-game-state/westeros-game-state/WesterosGameState.ts b/agot-bg-game-server/src/common/ingame-game-state/westeros-game-state/WesterosGameState.ts index 44597eb75..e3a4cbc82 100644 --- a/agot-bg-game-server/src/common/ingame-game-state/westeros-game-state/WesterosGameState.ts +++ b/agot-bg-game-server/src/common/ingame-game-state/westeros-game-state/WesterosGameState.ts @@ -93,7 +93,7 @@ export default class WesterosGameState extends GameState { settings.loyaltyTokenCountNeededToWin = 7; } + if (settings.houseCardsEvolutionRound < 2 || settings.houseCardsEvolutionRound > 10) { + settings.houseCardsEvolutionRound = 5; + } + if (settings.houseCardsEvolution) { settings.adwdHouseCards = false; settings.asosHouseCards = false; diff --git a/agot-bg-game-server/src/server/serializedGameMigrations.ts b/agot-bg-game-server/src/server/serializedGameMigrations.ts index 28ceba1d9..912c97491 100644 --- a/agot-bg-game-server/src/server/serializedGameMigrations.ts +++ b/agot-bg-game-server/src/server/serializedGameMigrations.ts @@ -2384,6 +2384,13 @@ const serializedGameMigrations: {version: string; migrate: (serializeGamed: any) } return serializedGame; } + }, + { + version: "121", + migrate: (serializedGame: any) => { + serializedGame.gameSettings.houseCardsEvolutionRound = 5; + return serializedGame; + } } ];