Skip to content

Commit

Permalink
feat(Game): Implement White Werewolf Role. Closes #119
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinezanardi committed Feb 6, 2021
1 parent 7124723 commit a8a6474
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* [#116](https://github.com/antoinezanardi/werewolves-assistant-api/issues/116) - Options for game's random repartition.
* [#117](https://github.com/antoinezanardi/werewolves-assistant-api/issues/117) - Add guard protection on little girl option.
* [#118](https://github.com/antoinezanardi/werewolves-assistant-api/issues/118) - Add idiot dies on ancient death option.
* [#119](https://github.com/antoinezanardi/werewolves-assistant-api/issues/119) - Implement White Werewolf role.

### 🌟 Enhancements

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ The MongoDB database is protected under username and password authentication.

## <a name="roles-available">🃏 Roles available</a>

On this current version [![GitHub release](https://img.shields.io/github/release/antoinezanardi/werewolves-assistant-api.svg)](https://GitHub.com/antoinezanardi/werewolves-assistant-api/releases/), **20 different roles** are available to play:
On this current version [![GitHub release](https://img.shields.io/github/release/antoinezanardi/werewolves-assistant-api.svg)](https://GitHub.com/antoinezanardi/werewolves-assistant-api/releases/), **21 different roles** are available to play:

- **<img src="https://werewolves-assistant-api.antoinezanardi.fr/img/roles/werewolf.png" width="25"/> The Werewolf**
- **<img src="https://werewolves-assistant-api.antoinezanardi.fr/img/roles/big-bad-wolf.png" width="25"/> The Big Bad Wolf**
- **<img src="https://werewolves-assistant-api.antoinezanardi.fr/img/roles/vile-father-of-wolves.png" width="25"/> The Vile Father Of Wolves**
- **<img src="https://werewolves-assistant-api.antoinezanardi.fr/img/roles/white-werewolf.png" width="25"/> The White Werewolf**
- **<img src="https://werewolves-assistant-api.antoinezanardi.fr/img/roles/villager.png" width="25"/> The Villager**
- **<img src="https://werewolves-assistant-api.antoinezanardi.fr/img/roles/villager.png" width="25"/> The Villager-Villager**
- **<img src="https://werewolves-assistant-api.antoinezanardi.fr/img/roles/seer.png" width="25"/> The Seer**
Expand Down
7 changes: 5 additions & 2 deletions src/controllers/Player.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ exports.checkTargetDependingOnPlay = async(target, game, { source, action }) =>
throw generateError("CANT_LOOK_AT_HERSELF", "Seer can't see herself.");
} else if (action === "eat") {
await this.checkEatTarget(target, game, source);
} else if (action === "use-potion" && target.hasDrankLifePotion && !doesPlayerHaveAttribute(target.player, "eaten")) {
throw generateError("BAD_LIFE_POTION_USE", `Witch can only use life potion on a target eaten by werewolves.`);
} else if (action === "use-potion" && target.hasDrankLifePotion) {
const eatenAttribute = getPlayerAttribute(target.player, "eaten");
if (!eatenAttribute || eatenAttribute.source === "white-werewolf") {
throw generateError("BAD_LIFE_POTION_USE", `Witch can only use life potion on a target eaten by "werewolves" of "big-bad-wolf".`);
}
} else if (action === "protect") {
const lastProtectedTarget = await GameHistory.getLastProtectedPlayer(game._id);
if (lastProtectedTarget && lastProtectedTarget._id.toString() === target.player._id.toString()) {
Expand Down
1 change: 1 addition & 0 deletions src/helpers/constants/Player.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ exports.playerAttributes = [
{ name: "sheriff", source: "all" },
{ name: "seen", source: "seer", remainingPhases: 1 },
{ name: "eaten", source: "werewolves", remainingPhases: 1 },
{ name: "eaten", source: "white-werewolf", remainingPhases: 1 },
{ name: "eaten", source: "big-bad-wolf", remainingPhases: 1 },
{ name: "infected", source: "vile-father-of-wolves", remainingPhases: 1 },
{ name: "drank-life-potion", source: "witch", remainingPhases: 1 },
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/game/ancient-infected.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr
done();
});
});
it("🪄 Witch use her life potion on ancient (POST /games/:id/play)", done => {
it("🪄 Witch uses her life potion on ancient (POST /games/:id/play)", done => {
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
Expand Down
174 changes: 161 additions & 13 deletions tests/e2e/game/full-game.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,96 @@ describe("B - Full game of 25 players with all roles", () => {
done();
});
});
it("🎲 Game is waiting for 'white-werewolf' to 'eat'", done => {
expect(game.waiting[0]).to.deep.equals({ for: "white-werewolf", to: "eat" });
done();
});
it("🐺 White werewolf can't eat if play's source is not 'white-werewolf' (POST /games/:id/play)", done => {
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "hunter", action: "eat" })
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.type).to.equals("BAD_PLAY_SOURCE");
done();
});
});
it("🐺 White werewolf can't eat if play's action is not 'eat' (POST /games/:id/play)", done => {
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "shoot" })
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.type).to.equals("BAD_PLAY_ACTION");
done();
});
});
it("🐺 White werewolf can't eat multiple targets (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({
source: "white-werewolf", action: "eat", targets: [
{ player: players[0]._id },
{ player: players[1]._id },
],
})
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.type).to.equals("BAD_TARGETS_LENGTH");
done();
});
});
it("🐺 White werewolf can't eat an unknown target (POST /games/:id/play)", done => {
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "eat", targets: [{ player: new mongoose.Types.ObjectId() }] })
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.type).to.equals("NOT_TARGETABLE");
done();
});
});
it("🐺 White werewolf can't eat a player in the `villagers` side (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "eat", targets: [{ player: players[0]._id }] })
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.type).to.equals("MUST_EAT_WEREWOLF");
done();
});
});
it("🐺 White werewolf can't eat himself (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "eat", targets: [{ player: players[23]._id }] })
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.type).to.equals("CANT_EAT_HIMSELF");
done();
});
});
it("🐺 White werewolf skips (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "eat", targets: [] })
.end((err, res) => {
expect(res).to.have.status(200);
game = res.body;
done();
});
});
it("🎲 Game is waiting for 'big-bad-wolf' to 'eat'", done => {
expect(game.waiting[0]).to.deep.equals({ for: "big-bad-wolf", to: "eat" });
done();
Expand Down Expand Up @@ -1285,7 +1375,7 @@ describe("B - Full game of 25 players with all roles", () => {
done();
});
});
it("🪄 Witch use life potion on guard (POST /games/:id/play)", done => {
it("🪄 Witch uses life potion on guard (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
Expand Down Expand Up @@ -1885,7 +1975,7 @@ describe("B - Full game of 25 players with all roles", () => {
done();
});
});
it("🪄 Witch use death potion on seer (POST /games/:id/play)", done => {
it("🪄 Witch uses death potion on seer (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
Expand Down Expand Up @@ -2224,7 +2314,7 @@ describe("B - Full game of 25 players with all roles", () => {
done();
});
});
it("🎲 Game is waiting for 'wolves' to 'eat'", done => {
it("🎲 Game is waiting for 'werewolves' to 'eat'", done => {
expect(game.waiting[0]).to.deep.equals({ for: "werewolves", to: "eat" });
done();
});
Expand All @@ -2243,6 +2333,22 @@ describe("B - Full game of 25 players with all roles", () => {
done();
});
});
it("🎲 Game is waiting for 'white-werewolf' to 'eat'", done => {
expect(game.waiting[0]).to.deep.equals({ for: "white-werewolf", to: "eat" });
done();
});
it("🐺 White werewolf skips (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "eat" })
.end((err, res) => {
expect(res).to.have.status(200);
game = res.body;
done();
});
});
it("🐺 Big bad wolf eats the second brother (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
Expand Down Expand Up @@ -2557,7 +2663,7 @@ describe("B - Full game of 25 players with all roles", () => {
.end((err, res) => {
expect(res).to.have.status(200);
game = res.body;
expect(game.history.length).to.equals(37);
expect(game.history.length).to.equals(39);
done();
});
});
Expand All @@ -2568,7 +2674,7 @@ describe("B - Full game of 25 players with all roles", () => {
.end((err, res) => {
expect(res).to.have.status(200);
const history = res.body;
expect(history.length).to.equals(37);
expect(history.length).to.equals(39);
done();
});
});
Expand Down Expand Up @@ -2703,18 +2809,18 @@ describe("B - Full game of 25 players with all roles", () => {
done();
});
});
it("🛡 Guard protects the raven (POST /games/:id/play)", done => {
it("🛡 Guard protects a werewolf (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "guard", action: "protect", targets: [{ player: players[3]._id }] })
.send({ source: "guard", action: "protect", targets: [{ player: players[24]._id }] })
.end((err, res) => {
expect(res).to.have.status(200);
game = res.body;
expect(game.players[3].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 });
expect(game.players[24].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 });
expect(game.history[0].play.targets).to.exist;
expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id);
expect(game.history[0].play.targets[0].player._id).to.equals(players[24]._id);
done();
});
});
Expand Down Expand Up @@ -2744,6 +2850,32 @@ describe("B - Full game of 25 players with all roles", () => {
done();
});
});
it("🐺 White werewolf can't eat a dead target (POST /games/:id/play)", done => {
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "eat", targets: [{ player: players[5]._id }] })
.end((err, res) => {
expect(res).to.have.status(400);
expect(res.body.type).to.equals("NOT_TARGETABLE");
done();
});
});
it("🐺 White werewolf eats one werewolf (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "eat", targets: [{ player: players[24]._id }] })
.end((err, res) => {
expect(res).to.have.status(200);
game = res.body;
expect(game.players[24].attributes).to.deep.include({ name: "eaten", source: "white-werewolf", remainingPhases: 1 });
expect(game.history[0].play.targets).to.exist;
expect(game.history[0].play.targets[0].player._id).to.equals(players[24]._id);
done();
});
});
it("🪄 Witch skips (POST /games/:id/play)", done => {
chai.request(app)
.post(`/games/${game._id}/play`)
Expand All @@ -2759,6 +2891,9 @@ describe("B - Full game of 25 players with all roles", () => {
it("☀️ Sun is rising", done => {
expect(game.phase).to.equals("day");
expect(game.players[10].isAlive).to.be.false;
expect(game.players[24].attributes).to.not.deep.include({ name: "eaten", source: "white-werewolf", remainingPhases: 1 });
expect(game.players[24].isAlive).to.be.false;
expect(game.players[24].murdered).to.deep.equals({ by: "white-werewolf", of: "eat" });
done();
});
it("👪 All vote for vile father of wolves (POST /games/:id/play)", done => {
Expand Down Expand Up @@ -2914,6 +3049,17 @@ describe("B - Full game of 25 players with all roles", () => {
done();
});
});
it("🐺 White werewolf eats the dog-wolf (POST /games/:id/play)", done => {
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "white-werewolf", action: "eat", targets: [{ player: players[16]._id }] })
.end((err, res) => {
expect(res).to.have.status(200);
game = res.body;
done();
});
});
it("🪄 Witch skips (POST /games/:id/play)", done => {
chai.request(app)
.post(`/games/${game._id}/play`)
Expand All @@ -2929,20 +3075,22 @@ describe("B - Full game of 25 players with all roles", () => {
it("☀️ Sun is rising", done => {
expect(game.phase).to.equals("day");
expect(game.players[3].isAlive).to.be.true;
expect(game.players[16].isAlive).to.be.false;
expect(game.players[16].murdered).to.deep.equals({ by: "white-werewolf", of: "eat" });
done();
});
it("👪 All vote for the dog-wolf (POST /games/:id/play)", done => {
it("👪 All vote for the white werewolf (POST /games/:id/play)", done => {
players = game.players;
chai.request(app)
.post(`/games/${game._id}/play`)
.set({ Authorization: `Bearer ${token}` })
.send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[16]._id }] })
.send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[23]._id }] })
.end((err, res) => {
expect(res).to.have.status(200);
game = res.body;
expect(game.history[0].play.votesResult).to.equals("death");
expect(game.players[16].isAlive).to.be.false;
expect(game.players[16].murdered).to.deep.equals({ by: "all", of: "vote" });
expect(game.players[23].isAlive).to.be.false;
expect(game.players[23].murdered).to.deep.equals({ by: "all", of: "vote" });
done();
});
});
Expand Down
Loading

0 comments on commit a8a6474

Please sign in to comment.