Skip to content

Commit

Permalink
feat(big-bad-wolf): powerless attribute when one werewolf dies (#708)
Browse files Browse the repository at this point in the history
Closes #700
  • Loading branch information
antoinezanardi committed Dec 2, 2023
1 parent f5c8f3c commit 6e48a61
Show file tree
Hide file tree
Showing 11 changed files with 31,845 additions and 30,740 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ function createCantVoteByScapegoatPlayerAttribute(game: Game, playerAttribute: P
});
}

function createPowerlessByWerewolvesPlayerAttribute(playerAttribute: Partial<PlayerAttribute> = {}): PlayerAttribute {
return createPlayerAttribute({
name: PlayerAttributeNames.POWERLESS,
source: PlayerGroups.WEREWOLVES,
doesRemainAfterDeath: true,
...playerAttribute,
});
}

function createPowerlessByAccursedWolfFatherPlayerAttribute(playerAttribute: Partial<PlayerAttribute> = {}): PlayerAttribute {
return createPlayerAttribute({
name: PlayerAttributeNames.POWERLESS,
Expand Down Expand Up @@ -199,6 +208,7 @@ export {
createCharmedByPiedPiperPlayerAttribute,
createCantVoteBySurvivorsPlayerAttribute,
createCantVoteByScapegoatPlayerAttribute,
createPowerlessByWerewolvesPlayerAttribute,
createPowerlessByAccursedWolfFatherPlayerAttribute,
createPowerlessByFoxPlayerAttribute,
createPowerlessByElderPlayerAttribute,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ function doesPlayerHaveAttributeWithNameAndSource(player: Player, attributeName:
return !!getPlayerAttributeWithNameAndSource(player, attributeName, attributeSource);
}

function doesPlayerHaveActiveAttributeWithNameAndSource(player: Player, attributeName: PlayerAttributeNames, attributeSource: GameSource, game: Game): boolean {
const attribute = getPlayerAttributeWithNameAndSource(player, attributeName, attributeSource);
return !!attribute && isPlayerAttributeActive(attribute, game);
}

export {
isPlayerAttributeActive,
getPlayerAttributeWithName,
Expand All @@ -42,4 +47,5 @@ export {
doesPlayerHaveActiveAttributeWithName,
getPlayerAttributeWithNameAndSource,
doesPlayerHaveAttributeWithNameAndSource,
doesPlayerHaveActiveAttributeWithNameAndSource,
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PlayerAttributeNames, PlayerGroups } from "@/modules/game/enums/player.
import { createGamePlay, createGamePlaySurvivorsElectSheriff, createGamePlaySurvivorsVote } from "@/modules/game/helpers/game-play/game-play.factory";
import { areGamePlaysEqual, canSurvivorsVote, findPlayPriorityIndex } from "@/modules/game/helpers/game-play/game-play.helper";
import { createGame, createGameWithCurrentGamePlay } from "@/modules/game/helpers/game.factory";
import { areAllWerewolvesAlive, getGroupOfPlayers, getLeftToEatByWerewolvesPlayers, getLeftToEatByWhiteWerewolfPlayers, getPlayerDtoWithRole, getPlayersWithActiveAttributeName, getPlayersWithCurrentRole, getPlayerWithActiveAttributeName, getPlayerWithCurrentRole, isGameSourceGroup, isGameSourceRole } from "@/modules/game/helpers/game.helper";
import { getGroupOfPlayers, getLeftToEatByWerewolvesPlayers, getLeftToEatByWhiteWerewolfPlayers, getPlayerDtoWithRole, getPlayersWithActiveAttributeName, getPlayersWithCurrentRole, getPlayerWithActiveAttributeName, getPlayerWithCurrentRole, isGameSourceGroup, isGameSourceRole } from "@/modules/game/helpers/game.helper";
import { canPiedPiperCharm, isPlayerAliveAndPowerful, isPlayerPowerful } from "@/modules/game/helpers/player/player.helper";
import { GameHistoryRecordService } from "@/modules/game/providers/services/game-history/game-history-record.service";
import type { GameHistoryRecord } from "@/modules/game/schemas/game-history-record/game-history-record.schema";
Expand Down Expand Up @@ -165,7 +165,7 @@ export class GamePlayService {
[PlayerGroups.SURVIVORS]: this.isSurvivorsGamePlaySuitableForCurrentPhase,
[PlayerGroups.LOVERS]: this.isLoversGamePlaySuitableForCurrentPhase,
[PlayerGroups.CHARMED]: this.isPiedPiperGamePlaySuitableForCurrentPhase,
[PlayerGroups.WEREWOLVES]: () => game instanceof CreateGameDto || getGroupOfPlayers(game, source).some(werewolf => isPlayerAliveAndPowerful(werewolf, game)),
[PlayerGroups.WEREWOLVES]: () => game instanceof CreateGameDto || getGroupOfPlayers(game, source).some(werewolf => werewolf.isAlive),
[PlayerGroups.VILLAGERS]: () => false,
};
return specificGroupMethods[source](game, gamePlay);
Expand Down Expand Up @@ -213,10 +213,8 @@ export class GamePlayService {
}
const { doSkipCallIfNoTarget } = game.options.roles;
const availableTargets = getLeftToEatByWerewolvesPlayers(game);
const { isPowerlessIfWerewolfDies } = game.options.roles.bigBadWolf;
const bigBadWolfPlayer = getPlayerWithCurrentRole(game, RoleNames.BIG_BAD_WOLF);
return !!bigBadWolfPlayer && isPlayerAliveAndPowerful(bigBadWolfPlayer, game) &&
(!isPowerlessIfWerewolfDies || areAllWerewolvesAlive(game) && (!doSkipCallIfNoTarget || !!availableTargets.length));
return !!bigBadWolfPlayer && isPlayerAliveAndPowerful(bigBadWolfPlayer, game) && (!doSkipCallIfNoTarget || !!availableTargets.length);
}

private isThreeBrothersGamePlaySuitableForCurrentPhase(game: CreateGameDto | Game): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { createGamePlayHunterShoots, createGamePlayScapegoatBansVoting, createGa
import { createGame } from "@/modules/game/helpers/game.factory";
import { doesGameHaveCurrentOrUpcomingPlaySourceAndAction, getAliveVillagerSidedPlayers, getNearestAliveNeighbor, getPlayerWithCurrentRole, getPlayerWithIdOrThrow } from "@/modules/game/helpers/game.helper";
import { addPlayerAttributeInGame, addPlayersAttributeInGame, prependUpcomingPlayInGame, updatePlayerInGame } from "@/modules/game/helpers/game.mutator";
import { createCantVoteBySurvivorsPlayerAttribute, createContaminatedByRustySwordKnightPlayerAttribute, createPowerlessByElderPlayerAttribute } from "@/modules/game/helpers/player/player-attribute/player-attribute.factory";
import { doesPlayerHaveActiveAttributeWithName } from "@/modules/game/helpers/player/player-attribute/player-attribute.helper";
import { createCantVoteBySurvivorsPlayerAttribute, createContaminatedByRustySwordKnightPlayerAttribute, createPowerlessByElderPlayerAttribute, createPowerlessByWerewolvesPlayerAttribute } from "@/modules/game/helpers/player/player-attribute/player-attribute.factory";
import { doesPlayerHaveActiveAttributeWithName, doesPlayerHaveActiveAttributeWithNameAndSource } from "@/modules/game/helpers/player/player-attribute/player-attribute.helper";
import { createPlayerBrokenHeartByCupidDeath, createPlayerDeath, createPlayerReconsiderPardonBySurvivorsDeath } from "@/modules/game/helpers/player/player-death/player-death.factory";
import { createPlayer } from "@/modules/game/helpers/player/player.factory";
import { isPlayerPowerful } from "@/modules/game/helpers/player/player.helper";
Expand Down Expand Up @@ -165,6 +165,18 @@ export class PlayerKillerService {
return clonedGame;
}

private applyPlayerSideDeathOutcomes(killedPlayer: Player, game: Game): Game {
const clonedGame = createGame(game);
const bigBadWolfPlayer = getPlayerWithCurrentRole(clonedGame, RoleNames.BIG_BAD_WOLF);
const { isPowerlessIfWerewolfDies } = game.options.roles.bigBadWolf;
if (killedPlayer.side.current !== RoleSides.WEREWOLVES || !bigBadWolfPlayer ||
!isPowerlessIfWerewolfDies || killedPlayer.role.current === RoleNames.BIG_BAD_WOLF ||
doesPlayerHaveActiveAttributeWithNameAndSource(bigBadWolfPlayer, PlayerAttributeNames.POWERLESS, PlayerGroups.WEREWOLVES, clonedGame)) {
return clonedGame;
}
return addPlayerAttributeInGame(bigBadWolfPlayer._id, clonedGame, createPowerlessByWerewolvesPlayerAttribute());
}

private applyRustySwordKnightDeathOutcomes(killedPlayer: Player, game: Game, death: PlayerDeath): Game {
const clonedGame = createGame(game);
const leftAliveWerewolfNeighbor = getNearestAliveNeighbor(killedPlayer._id, clonedGame, { direction: "left", playerSide: RoleSides.WEREWOLVES });
Expand Down Expand Up @@ -231,6 +243,7 @@ export class PlayerKillerService {
let clonedPlayerToKill = createPlayer(killedPlayer);
const cantFindPlayerException = createCantFindPlayerUnexpectedException("applyPlayerDeathOutcomes", { gameId: game._id, playerId: killedPlayer._id });
clonedGame = this.applyPlayerRoleDeathOutcomes(clonedPlayerToKill, clonedGame, death);
clonedGame = this.applyPlayerSideDeathOutcomes(clonedPlayerToKill, clonedGame);
clonedPlayerToKill = getPlayerWithIdOrThrow(clonedPlayerToKill._id, clonedGame, cantFindPlayerException);
clonedGame = this.applyPlayerAttributesDeathOutcomes(clonedPlayerToKill, clonedGame);
if (!doesGameHaveCurrentOrUpcomingPlaySourceAndAction(clonedGame, PlayerGroups.SURVIVORS, GamePlayActions.BURY_DEAD_BODIES)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Feature: 馃惡馃懝 Big Bad Wolf role
| Olivia | JB |
| Thomas | JB |
Then the player named JB should be murdered by survivors from vote
And the player named Olivia should have the active powerless from werewolves attribute
And the game's current play should be survivors to bury-dead-bodies

When the survivors bury dead bodies
Expand Down Expand Up @@ -258,6 +259,7 @@ Feature: 馃惡馃懝 Big Bad Wolf role
| Olivia | JB |
| Thomas | JB |
Then the player named JB should be murdered by survivors from vote
And the player named Olivia should not have the active powerless from werewolves attribute
And the game's current play should be survivors to bury-dead-bodies

When the survivors bury dead bodies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ function createFakeWorshipedByWildChildPlayerAttribute(attribute: Partial<Player
}, override);
}

function createFakePowerlessByWerewolvesPlayerAttribute(attribute: Partial<PlayerAttribute> = {}, override: object = {}): PlayerAttribute {
return createFakePlayerAttribute({
name: PlayerAttributeNames.POWERLESS,
source: PlayerGroups.WEREWOLVES,
doesRemainAfterDeath: true,
...attribute,
}, override);
}

function createFakePowerlessByAccursedWolfFatherPlayerAttribute(attribute: Partial<PlayerAttribute> = {}, override: object = {}): PlayerAttribute {
return createFakePlayerAttribute({
name: PlayerAttributeNames.POWERLESS,
Expand Down Expand Up @@ -223,6 +232,7 @@ export {
createFakeScandalmongerMarkedByScandalmongerPlayerAttribute,
createFakeInLoveByCupidPlayerAttribute,
createFakeWorshipedByWildChildPlayerAttribute,
createFakePowerlessByWerewolvesPlayerAttribute,
createFakePowerlessByAccursedWolfFatherPlayerAttribute,
createFakePowerlessByFoxPlayerAttribute,
createFakePowerlessByElderPlayerAttribute,
Expand Down

0 comments on commit 6e48a61

Please sign in to comment.