Skip to content

Commit

Permalink
feat(game-options): sheriff must settle tie between votes game option (
Browse files Browse the repository at this point in the history
…#719)

Closes #704
  • Loading branch information
antoinezanardi authored Dec 4, 2023
1 parent 48bc69c commit da119d3
Show file tree
Hide file tree
Showing 12 changed files with 27,158 additions and 27,000 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const DEFAULT_GAME_OPTIONS: ReadonlyDeep<GameOptions> = {
phase: GamePhases.NIGHT,
},
hasDoubledVote: true,
mustSettleTieInVotes: true,
},
bigBadWolf: { isPowerlessIfWerewolfDies: true },
whiteWerewolf: { wakingUpInterval: 2 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ class CreateSheriffGameOptionsDto {
@IsOptional()
@IsBoolean()
public hasDoubledVote: boolean = SHERIFF_GAME_OPTIONS_FIELDS_SPECS.hasDoubledVote.default;

@ApiProperty({
...SHERIFF_GAME_OPTIONS_API_PROPERTIES.mustSettleTieInVotes,
required: false,
} as ApiPropertyOptions)
@IsOptional()
@IsBoolean()
public mustSettleTieInVotes: boolean = SHERIFF_GAME_OPTIONS_FIELDS_SPECS.mustSettleTieInVotes.default;
}

export { CreateSheriffGameOptionsDto };
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { MakeGamePlayWithRelationsDto } from "@/modules/game/dto/make-game-
import { GamePlayActions, GamePlayCauses, WitchPotions } from "@/modules/game/enums/game-play.enum";
import { PlayerAttributeNames, PlayerGroups } from "@/modules/game/enums/player.enum";
import { createGamePlaySurvivorsVote, createGamePlaySheriffSettlesVotes, createGamePlaySurvivorsElectSheriff } from "@/modules/game/helpers/game-play/game-play.factory";
import { createGame } from "@/modules/game/helpers/game.factory";
import { createGame, createGameWithCurrentGamePlay } from "@/modules/game/helpers/game.factory";
import { getFoxSniffedPlayers, getPlayerWithActiveAttributeName, getPlayerWithCurrentRole } from "@/modules/game/helpers/game.helper";
import { addPlayerAttributeInGame, addPlayersAttributeInGame, appendUpcomingPlayInGame, prependUpcomingPlayInGame, removePlayerAttributeByNameInGame, updatePlayerInGame } from "@/modules/game/helpers/game.mutator";
import { createCantVoteByScapegoatPlayerAttribute, createCharmedByPiedPiperPlayerAttribute, createDrankDeathPotionByWitchPlayerAttribute, createDrankLifePotionByWitchPlayerAttribute, createEatenByBigBadWolfPlayerAttribute, createEatenByWerewolvesPlayerAttribute, createEatenByWhiteWerewolfPlayerAttribute, createInLoveByCupidPlayerAttribute, createPowerlessByFoxPlayerAttribute, createProtectedByDefenderPlayerAttribute, createScandalmongerMarkByScandalmongerPlayerAttribute, createSeenBySeerPlayerAttribute, createSheriffBySurvivorsPlayerAttribute, createSheriffBySheriffPlayerAttribute, createWorshipedByWildChildPlayerAttribute, createPowerlessByAccursedWolfFatherPlayerAttribute } from "@/modules/game/helpers/player/player-attribute/player-attribute.factory";
Expand Down Expand Up @@ -100,14 +100,15 @@ export class GamePlayMakerService {
}

private async handleTieInVotes(game: GameWithCurrentPlay): Promise<Game> {
const clonedGame = createGame(game) as GameWithCurrentPlay;
const clonedGame = createGameWithCurrentGamePlay(game);
const { mustSettleTieInVotes: mustSheriffSettleTieInVotes } = clonedGame.options.roles.sheriff;
const scapegoatPlayer = getPlayerWithCurrentRole(clonedGame, RoleNames.SCAPEGOAT);
if (scapegoatPlayer && isPlayerAliveAndPowerful(scapegoatPlayer, game)) {
const playerVoteScapegoatedBySurvivorsDeath = createPlayerVoteScapegoatedBySurvivorsDeath();
return this.playerKillerService.killOrRevealPlayer(scapegoatPlayer._id, clonedGame, playerVoteScapegoatedBySurvivorsDeath);
}
const sheriffPlayer = getPlayerWithActiveAttributeName(clonedGame, PlayerAttributeNames.SHERIFF);
if (sheriffPlayer?.isAlive === true) {
if (sheriffPlayer?.isAlive === true && mustSheriffSettleTieInVotes) {
const gamePlaySheriffSettlesVotes = createGamePlaySheriffSettlesVotes();
return prependUpcomingPlayInGame(gamePlaySheriffSettlesVotes, clonedGame);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const SHERIFF_GAME_OPTIONS_FIELDS_SPECS = {
required: true,
default: DEFAULT_GAME_OPTIONS.roles.sheriff.hasDoubledVote,
},
mustSettleTieInVotes: {
required: true,
default: DEFAULT_GAME_OPTIONS.roles.sheriff.mustSettleTieInVotes,
},
} as const satisfies Record<keyof SheriffGameOptions, MongoosePropOptions>;

const SHERIFF_GAME_OPTIONS_API_PROPERTIES: ReadonlyDeep<Record<keyof SheriffGameOptions, ApiPropertyOptions>> = {
Expand All @@ -37,6 +41,10 @@ const SHERIFF_GAME_OPTIONS_API_PROPERTIES: ReadonlyDeep<Record<keyof SheriffGame
description: "If set to `true`, `sheriff` vote during the village's vote is doubled, otherwise, it's a regular vote",
...convertMongoosePropOptionsToApiPropertyOptions(SHERIFF_GAME_OPTIONS_FIELDS_SPECS.hasDoubledVote),
},
mustSettleTieInVotes: {
description: "If set to `true`, in case of a tie in votes, the sheriff will have to settle the tie by choosing the player to kill between the tied players. Otherwise, there will be another vote between the tied players",
...convertMongoosePropOptionsToApiPropertyOptions(SHERIFF_GAME_OPTIONS_FIELDS_SPECS.mustSettleTieInVotes),
},
};

export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class SheriffGameOptions {
@Prop(SHERIFF_GAME_OPTIONS_FIELDS_SPECS.hasDoubledVote)
@Expose()
public hasDoubledVote: boolean;

@ApiProperty(SHERIFF_GAME_OPTIONS_API_PROPERTIES.mustSettleTieInVotes as ApiPropertyOptions)
@Prop(SHERIFF_GAME_OPTIONS_FIELDS_SPECS.mustSettleTieInVotes)
@Expose()
public mustSettleTieInVotes: boolean;
}

const SHERIFF_GAME_OPTIONS_SCHEMA = SchemaFactory.createForClass(SheriffGameOptions);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"roles": {
"sheriff": {
"mustSettleTieInVotes": false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ Feature: 🎖️ Sheriff player attribute
| source | interaction |
| survivors | choose-as-sheriff |


When the survivors elect sheriff with the following votes
| voter | target |
| Antoine | Olivia |
Expand Down Expand Up @@ -88,6 +87,53 @@ Feature: 🎖️ Sheriff player attribute
And the player named JB should be alive
And the player named Thomas should be murdered by sheriff from vote

Scenario: 🎖️ Sheriff doesn't break tie between votes with good game options

Given a created game with options described in file sheriff-does-not-settle-tie-in-votes.json and with the following players
| name | role |
| Antoine | villager |
| Olivia | werewolf |
| Thomas | villager |
| JB | villager |
| Babou | villager |
Then the request should have succeeded with status code 201
And the game's current play should be survivors to elect-sheriff

When the survivors elect sheriff with the following votes
| voter | target |
| Antoine | Olivia |
| JB | Olivia |
| Thomas | Olivia |
Then the request should have succeeded with status code 200
And the player named Olivia should have the active sheriff from survivors attribute
And the game's current play should be werewolves to eat

When the werewolves eat the player named Babou
Then the player named Babou should be murdered by werewolves from eaten
And the game's current play should be survivors to bury-dead-bodies

When the survivors bury dead bodies
Then the game's current play should be survivors to vote

When the survivors vote with the following votes
| voter | target |
| JB | Thomas |
| Thomas | JB |
Then the player named JB should be alive
And the player named Thomas should be alive
And the game's current play should be survivors to vote
And the game's current play should be played by the following players
| name |
| Antoine |
| Olivia |
| Thomas |
| JB |
And the game's current play should have eligible targets boundaries from 0 to 4
And the game's current play should have the following eligible targets interactable players
| name |
| Thomas |
| JB |

Scenario: 🎖️ Sheriff can't break ties with a player which is not in the previous tie

Given a created game with the following players
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,7 @@ describe("Game Controller", () => {
phase: GamePhases.DAY,
},
hasDoubledVote: false,
mustSettleTieInVotes: false,
},
bigBadWolf: { isPowerlessIfWerewolfDies: false },
whiteWerewolf: { wakingUpInterval: 5 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ function createFakeCreateSheriffGameOptionsDto(sheriffGameOptions: Partial<Creat
isEnabled: sheriffGameOptions.isEnabled ?? faker.datatype.boolean(),
electedAt: createFakeCreateSheriffElectionGameOptionsDto(sheriffGameOptions.electedAt),
hasDoubledVote: sheriffGameOptions.hasDoubledVote ?? faker.datatype.boolean(),
mustSettleTieInVotes: sheriffGameOptions.mustSettleTieInVotes ?? faker.datatype.boolean(),
...override,
}, DEFAULT_PLAIN_TO_INSTANCE_OPTIONS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ function createFakeSheriffGameOptions(sheriffGameOptions: Partial<SheriffGameOpt
isEnabled: sheriffGameOptions.isEnabled ?? faker.datatype.boolean(),
electedAt: createFakeSheriffElectionGameOptions(sheriffGameOptions.electedAt),
hasDoubledVote: sheriffGameOptions.hasDoubledVote ?? faker.datatype.boolean(),
mustSettleTieInVotes: sheriffGameOptions.mustSettleTieInVotes ?? faker.datatype.boolean(),
...override,
}, DEFAULT_PLAIN_TO_INSTANCE_OPTIONS);
}
Expand Down
Loading

0 comments on commit da119d3

Please sign in to comment.