Skip to content

Commit

Permalink
Implement shield effect
Browse files Browse the repository at this point in the history
  • Loading branch information
takashiro committed May 3, 2021
1 parent 68dc698 commit 3b20c33
Show file tree
Hide file tree
Showing 25 changed files with 686 additions and 70 deletions.
76 changes: 46 additions & 30 deletions src/api/room/player/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Artifact,
Role,
Vision,
Player as PlayerProfile,
} from '@bezier/werewolf-core';
import { Router } from 'express';

Expand All @@ -14,27 +15,33 @@ const router = Router({
mergeParams: true,
});

function showPlayerTo(player: Player, self: Player): PlayerProfile {
const profile = player.getProfile();
if (!player.isRevealed() && !player.isDisclosedTo(self)) {
profile.role = Role.Unknown;
}
const artifactNum = player.getArtifactNum();
if (artifactNum > 0) {
if (self === player) {
profile.artifacts = player.getArtifacts();
} else {
profile.artifacts = new Array(artifactNum).fill(Artifact.Unknown);
}
}
if (player.isShielded()) {
profile.shielded = true;
}
return profile;
}

function isSignificant(player: PlayerProfile): boolean {
return Boolean(player.role || player.artifacts || player.shielded);
}

function wakeUp(self: Player, driver: Driver): Vision {
const players = driver.getPlayers()
.map((player) => {
const profile = player.getProfile();
if (!player.isRevealed() && !player.isDisclosedTo(self)) {
profile.role = Role.Unknown;
}
const artifactNum = player.getArtifactNum();
if (artifactNum > 0) {
if (self === player) {
profile.artifacts = player.getArtifacts();
} else {
profile.artifacts = new Array(artifactNum).fill(Artifact.Unknown);
}
}
if (player.isShielded()) {
profile.shielded = true;
}
return profile;
})
.filter((player) => player.role || player.artifacts);
.map((player) => showPlayerTo(player, self))
.filter(isSignificant);

const cards = driver.getCenterCards()
.filter((card) => card.isRevealed())
Expand All @@ -47,14 +54,6 @@ function wakeUp(self: Player, driver: Driver): Vision {
return vision;
}

function isAccessible(self: Player, driver: Driver): boolean {
if (driver.getState() === DriverState.InvokingSkills) {
const [skill] = self.getSkills();
return skill && skill.isReady() && !skill.isFinished();
}
return driver.getState() === DriverState.Voting;
}

router.get('/', (req, res) => {
const context = $(req, res);
if (!context) {
Expand All @@ -63,12 +62,29 @@ router.get('/', (req, res) => {

const self = context.player;
const { driver } = context;
if (!isAccessible(self, driver)) {
res.status(425).send('Other players are still invoking their skills.');
} else {
switch (driver.getState()) {
case DriverState.InvokingSkills: {
const [skill] = self.getSkills();
if (skill && skill.isReady() && !skill.isFinished()) {
const vision = wakeUp(self, driver);
res.json(vision);
} else {
res.status(425).send('Other players are still invoking their skills.');
}
return;
}

case DriverState.Voting: {
const vision = wakeUp(self, driver);
res.json(vision);
return;
}

default:
break;
}

res.json({});
});

export default router;
10 changes: 10 additions & 0 deletions src/collection/ActionValidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ActionType from '../game/ActionType';
import Player from '../game/Player';

interface ActionValidation {
type: ActionType;
player: Player;
valid: boolean;
}

export default ActionValidation;
2 changes: 1 addition & 1 deletion src/collection/CompanionSkill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ export default abstract class CompanionSkill extends VisionSkill {
protected show(): Vision {
const players = this.driver.getPlayers();
const companions = players.filter((player) => this.isCompanion(player));
return this.showPlayers(companions, false);
return this.showThumbsOf(companions);
}
}
13 changes: 13 additions & 0 deletions src/collection/Skill.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Selection } from '@bezier/werewolf-core';
import ActionType from '../game/ActionType';

import Card from '../game/Card';
import Driver from '../game/Driver';
import Event from '../game/Event';
import Player from '../game/Player';
import BaseSkill from '../game/Skill';
import ActionValidation from './ActionValidation';

abstract class Skill<OutputType> extends BaseSkill<Driver, Player, Selection, OutputType> {
selectNone(sel: Selection): boolean {
Expand Down Expand Up @@ -77,6 +80,16 @@ abstract class Skill<OutputType> extends BaseSkill<Driver, Player, Selection, Ou
}
return targets;
}

protected validateAction(type: ActionType, player: Player): boolean {
const data: ActionValidation = {
type,
player,
valid: true,
};
this.driver.trigger(Event.ValidatingAction, data);
return data.valid;
}
}

export default Skill;
52 changes: 39 additions & 13 deletions src/collection/VisionSkill.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {
Role,
Selection,
Vision,
} from '@bezier/werewolf-core';

import ActionType from '../game/ActionType';
import Card from '../game/Card';
import Player from '../game/Player';

import Skill from './Skill';
import ViewAction from './ViewAction';

Expand All @@ -16,26 +19,49 @@ export default abstract class VisionSkill extends Skill<Vision | undefined> {
return this.show(data);
}

protected showPlayer(player: Player, actual: boolean): Vision {
const snapshot = actual ? player.getActualProfile() : player.getNotionalProfile();
if (actual) {
this.driver.addAction(new ViewAction(this, [player]));
protected showThumbOf(player: Player): Vision {
if (!this.validateAction(ActionType.ShowThumb, player)) {
return {};
}
return {
players: [snapshot],
players: [player.getNotionalProfile()],
};
}

protected showThumbsOf(players: Player[]): Vision {
const snapshots = players.map((player) => {
const profile = player.getNotionalProfile();
if (!this.validateAction(ActionType.ShowThumb, player)) {
profile.role = Role.Unknown;
}
return profile;
});
return {
players: snapshots,
};
}

protected showPlayers(players: Player[], actual: boolean): Vision {
if (actual) {
const snapshots = players.map((player) => player.getActualProfile());
this.driver.addAction(new ViewAction(this, players));
return {
players: snapshots,
};
protected showPlayer(player: Player): Vision {
if (!this.validateAction(ActionType.ViewRole, player)) {
return {};
}

const snapshots = players.map((player) => player.getNotionalProfile());
const snapshot = player.getActualProfile();
this.driver.addAction(new ViewAction(this, [player]));
return {
players: [snapshot],
};
}

protected showPlayers(players: Player[]): Vision {
const snapshots = players.map((player) => {
const profile = player.getActualProfile();
if (!this.validateAction(ActionType.ViewRole, player)) {
profile.role = Role.Unknown;
}
return profile;
});
this.driver.addAction(new ViewAction(this, players));
return {
players: snapshots,
};
Expand Down
2 changes: 1 addition & 1 deletion src/collection/bonus/ApprenticeTanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ export default class ApprenticeTanner extends VisionSkill {
protected show(): Vision {
const players = this.driver.getPlayers();
const tanners = players.filter((player) => player.getNotionalRole() === Role.Tanner);
return this.showPlayers(tanners, false);
return this.showThumbsOf(tanners);
}
}
2 changes: 1 addition & 1 deletion src/collection/bonus/Beholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ export default class Beholder extends VisionSkill {
protected show(): Vision {
const players = this.driver.getPlayers();
const seers = players.filter((player) => seerRoles.includes(player.getNotionalRole()));
return this.showPlayers(seers, true);
return this.showPlayers(seers);
}
}
2 changes: 1 addition & 1 deletion src/collection/bonus/Squire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export default class Squire extends VisionSkill {
protected show(): Vision {
const players = this.driver.getPlayers();
const werewolves = players.filter((player) => isWerewolf(player.getNotionalRole()));
return this.showPlayers(werewolves, true);
return this.showPlayers(werewolves);
}
}
6 changes: 5 additions & 1 deletion src/collection/daybreak/AlphaWolf/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Selection,
} from '@bezier/werewolf-core';

import ActionType from '../../../game/ActionType';
import Card from '../../../game/Card';
import MutexType from '../../../game/MutexType';

Expand All @@ -27,7 +28,10 @@ export default class AlphaWolf extends Skill<void> {

isFeasible(data: Selection): boolean {
const target = this.selectPlayer(data);
return Boolean(target) && target !== this.owner;
if (!target || target === this.owner) {
return false;
}
return this.validateAction(ActionType.MoveRole, target);
}

protected run(data: Selection): void {
Expand Down
11 changes: 9 additions & 2 deletions src/collection/daybreak/MysticWolf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import {
Selection,
Vision,
} from '@bezier/werewolf-core';

import ActionType from '../../game/ActionType';
import MutexType from '../../game/MutexType';

import VisionSkill from '../VisionSkill';

export default class MysticWolf extends VisionSkill {
Expand All @@ -11,11 +14,15 @@ export default class MysticWolf extends VisionSkill {
protected readMode = [MutexType.ActualRole];

isFeasible(data: Selection): boolean {
return Boolean(this.selectPlayer(data));
const target = this.selectPlayer(data);
if (!target) {
return false;
}
return this.validateAction(ActionType.ViewRole, target);
}

show(data: Selection): Vision | undefined {
const target = this.selectPlayer(data);
return target && this.showPlayer(target, true);
return target && this.showPlayer(target);
}
}
10 changes: 8 additions & 2 deletions src/collection/daybreak/ParanormalInvestigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {

import Player from '../../game/Player';
import MutexType from '../../game/MutexType';

import TransformAction from '../TransformAction';
import SkipAction from '../SkipAction';
import VisionSkill from '../VisionSkill';
import isWerewolf from '../isWerewolf';
import ActionType from '../../game/ActionType';

function transformTo(seen: Role): Role {
if (isWerewolf(seen)) {
Expand Down Expand Up @@ -42,7 +44,11 @@ export default class ParanormalInvestigator extends VisionSkill {
}

const target = this.selectPlayer(data);
return Boolean(target) && target !== this.owner;
if (!target || target === this.owner) {
return false;
}

return this.validateAction(ActionType.ViewRole, target);
}

protected show(data: Selection): Vision | undefined {
Expand All @@ -64,7 +70,7 @@ export default class ParanormalInvestigator extends VisionSkill {
}
}

const vision = this.showPlayers(this.selectedTargets, true);
const vision = this.showPlayers(this.selectedTargets);
if (vision.players && this.transformedTo) {
vision.players.push({
seat: this.owner.getSeat(),
Expand Down
9 changes: 7 additions & 2 deletions src/collection/daybreak/Revealer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Teamship,
Vision,
} from '@bezier/werewolf-core';
import ActionType from '../../game/ActionType';

import MutexType from '../../game/MutexType';
import RevealAction from '../RevealAction';
Expand All @@ -18,7 +19,11 @@ export default class Revealer extends VisionSkill {
protected writeMode = [MutexType.Any];

isFeasible(data: Selection): boolean {
return Boolean(this.selectPlayer(data));
const target = this.selectPlayer(data);
if (!target) {
return false;
}
return this.validateAction(ActionType.ViewRole, target);
}

protected show(data: Selection): Vision | undefined {
Expand All @@ -34,6 +39,6 @@ export default class Revealer extends VisionSkill {
this.driver.addAction(new SkipAction(this));
}

return this.showPlayer(target, true);
return this.showPlayer(target);
}
}
27 changes: 27 additions & 0 deletions src/collection/daybreak/Sentinel/ShieldEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import ActionType from '../../../game/ActionType';
import Event from '../../../game/Event';
import EventHook from '../../../game/EventHook';
import ActionValidation from '../../ActionValidation';

const forbiddenActions = [
ActionType.ViewRole,
ActionType.MoveRole,
];

export default class ShieldEffect extends EventHook<ActionValidation> {
constructor() {
super(Event.ValidatingAction);
}

// eslint-disable-next-line class-methods-use-this
process(data: ActionValidation): void {
if (!forbiddenActions.includes(data.type)) {
return;
}

const { player } = data;
if (player.isShielded()) {
data.valid = false;
}
}
}
Loading

0 comments on commit 3b20c33

Please sign in to comment.