Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/i18n/languages/english.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export default {
RESERVE: 'Reserve!',
BUY_CARD: 'Buy {{card}}!',
},
INVALID_POINTS_CAP: 'The point cap must be between {{minCap}} and {{maxCap}}. {{cap}} is invalid.',
INVALID_CARD: '{{card}} is not a valid card.',
CARD_NOT_ACCESSIBLE: 'Cannot access {{card}} for the desired action.',
DISCARD_TOKENS_REQUIRED: 'You need to discard tokens!',
Expand Down
1 change: 1 addition & 0 deletions src/i18n/languages/french.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export default {
RESERVE: 'Réserver !',
BUY_CARD: 'Acheter {{card}} !',
},
INVALID_POINTS_CAP: 'Le plafond de points doit être entre {{minCap}} et {{maxCap}}. {{cap}} est invalide.',
INVALID_CARD: "{{card}} n'est pas une carte valide.",
CARD_NOT_ACCESSIBLE: "Impossible d'accéder à {{card}} pour l'action souhaitée.",
DISCARD_TOKENS_REQUIRED: 'Vous devez défausser des jetons !',
Expand Down
1 change: 1 addition & 0 deletions src/i18n/languages/hindi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export default {
RESERVE: 'Reserve karo!',
BUY_CARD: '{{card}} kharido!',
},
INVALID_POINTS_CAP: 'Point cap {{minCap}} aur {{maxCap}} ke beech hona chahiye. {{cap}} valid nahi hai.',
INVALID_CARD: '{{card}} ek valid card nahi hai.',
CARD_NOT_ACCESSIBLE: '{{card}} is action ke liye accessible nahi hai.',
DISCARD_TOKENS_REQUIRED: 'Aapko tokens discard karne honge!',
Expand Down
1 change: 1 addition & 0 deletions src/i18n/languages/portuguese.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export default {
RESERVE: 'Reservar!',
BUY_CARD: 'Comprar {{card}}!',
},
INVALID_POINTS_CAP: 'O limite de pontos deve estar entre {{minCap}} e {{maxCap}}. {{cap}} é inválido.',
INVALID_CARD: '{{card}} não é uma carta válida.',
CARD_NOT_ACCESSIBLE: 'Não é possível acessar {{card}} para a ação desejada.',
DISCARD_TOKENS_REQUIRED: 'Você precisa descartar fichas!',
Expand Down
4 changes: 3 additions & 1 deletion src/ps/games/splendor/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export enum VIEW_ACTION_TYPE {
GAME_END = 'end',
}

export const POINTS_TO_WIN = 15;
export const MIN_POINTS_TO_WIN = 8;
export const MAX_POINTS_TO_WIN = 21;
export const DEFAULT_POINTS_TO_WIN = 15;
export const MAX_TOKEN_COUNT = 10;
export const MAX_RESERVE_COUNT = 3;
40 changes: 25 additions & 15 deletions src/ps/games/splendor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { BaseGame } from '@/ps/games/game';
import {
ACTIONS,
AllTokenTypes,
DEFAULT_POINTS_TO_WIN,
MAX_POINTS_TO_WIN,
MAX_RESERVE_COUNT,
MAX_TOKEN_COUNT,
POINTS_TO_WIN,
MIN_POINTS_TO_WIN,
TOKEN_TYPE,
TokenTypes,
VIEW_ACTION_TYPE,
Expand All @@ -29,6 +31,13 @@ export class Splendor extends BaseGame<State> {

constructor(ctx: BaseContext) {
super(ctx);

const givenCap = ctx.args.join('').trim();
this.state.pointsToWin = givenCap ? parseInt(givenCap) : DEFAULT_POINTS_TO_WIN;
if (this.state.pointsToWin < MIN_POINTS_TO_WIN || this.state.pointsToWin > MAX_POINTS_TO_WIN || isNaN(this.state.pointsToWin)) {
this.throw('GAME.SPLENDOR.INVALID_POINTS_CAP', { cap: givenCap, minCap: MIN_POINTS_TO_WIN, maxCap: MAX_POINTS_TO_WIN });
}

super.persist(ctx);

if (ctx.backup) return;
Expand Down Expand Up @@ -67,7 +76,7 @@ export class Splendor extends BaseGame<State> {
this.turn === lastPlayerInRound &&
Object.values(this.players)
.filter(player => !player.out)
.some(player => this.state.playerData[player.turn].points >= POINTS_TO_WIN)
.some(player => this.state.playerData[player.turn].points >= this.state.pointsToWin)
);
}

Expand Down Expand Up @@ -216,8 +225,7 @@ export class Splendor extends BaseGame<State> {
case VIEW_ACTION_TYPE.CLICK_DECK: {
if (!['1', '2', '3'].includes(actionCtx)) throw new ChatError(this.$T('GAME.SPLENDOR.WHICH_TIER'));
const tier = +actionCtx as 1 | 2 | 3;
if (this.state.board.cards[tier].deck.length === 0)
throw new ChatError(this.$T('GAME.SPLENDOR.DECK_EMPTY', { tier }));
if (this.state.board.cards[tier].deck.length === 0) throw new ChatError(this.$T('GAME.SPLENDOR.DECK_EMPTY', { tier }));

const canReserve = this.canReserve(player);
if (!canReserve) throw new ChatError(this.$T('GAME.SPLENDOR.RESERVE_LIMIT'));
Expand All @@ -234,10 +242,8 @@ export class Splendor extends BaseGame<State> {
const tokens = this.parseTokens(actionCtx, true);
const discarding = Object.values(tokens).sum();

if (discarding < toDiscard)
throw new ChatError(this.$T('GAME.SPLENDOR.DISCARD_MORE', { required: toDiscard, discarding }));
if (!this.canAfford(tokens, playerData.tokens, null, false))
throw new ChatError(this.$T('GAME.SPLENDOR.CANNOT_DISCARD'));
if (discarding < toDiscard) throw new ChatError(this.$T('GAME.SPLENDOR.DISCARD_MORE', { required: toDiscard, discarding }));
if (!this.canAfford(tokens, playerData.tokens, null, false)) throw new ChatError(this.$T('GAME.SPLENDOR.CANNOT_DISCARD'));

this.spendTokens(tokens, playerData);
logEntry = { turn: player.turn, time: new Date(), action: VIEW_ACTION_TYPE.TOO_MANY_TOKENS, ctx: { discard: tokens } };
Expand Down Expand Up @@ -278,8 +284,7 @@ export class Splendor extends BaseGame<State> {
let reservedId: string;
if (deckReserve) {
const tier = deckReserve as 1 | 2 | 3;
if (this.state.board.cards[tier].deck.length === 0)
throw new ChatError(this.$T('GAME.SPLENDOR.DECK_EMPTY', { tier }));
if (this.state.board.cards[tier].deck.length === 0) throw new ChatError(this.$T('GAME.SPLENDOR.DECK_EMPTY', { tier }));

const [card] = this.state.board.cards[tier].deck.splice(0, 1);
playerData.reserved.push(card);
Expand Down Expand Up @@ -421,8 +426,7 @@ export class Splendor extends BaseGame<State> {
const amt = +(entry.match(/\d/) ?? '0');
if (!(amt >= 0 && amt < 10)) throw new ChatError(this.$T('GAME.SPLENDOR.INVALID_COUNT', { value: entry.substring(1) }));
if (!AllTokenTypes.includes(type)) throw new ChatError(this.$T('GAME.SPLENDOR.UNRECOGNIZED_TYPE', { type }));
if (type === TOKEN_TYPE.DRAGON && !allowDragon)
throw new ChatError(this.$T('GAME.SPLENDOR.DRAGON_NOT_ALLOWED'));
if (type === TOKEN_TYPE.DRAGON && !allowDragon) throw new ChatError(this.$T('GAME.SPLENDOR.DRAGON_NOT_ALLOWED'));
tokens[type] += amt;
});
return tokens;
Expand All @@ -433,8 +437,7 @@ export class Splendor extends BaseGame<State> {
if (count > 0) return { type, count, available: this.state.board.tokens[type], name: metadata.types[type].name };
});

if (tokens[TOKEN_TYPE.DRAGON])
return { success: false, error: this.$T('GAME.SPLENDOR.DRAGON_ONLY_BY_RESERVE') };
if (tokens[TOKEN_TYPE.DRAGON]) return { success: false, error: this.$T('GAME.SPLENDOR.DRAGON_ONLY_BY_RESERVE') };

const tooMany = input.filter(({ count, available }) => count > available);
if (tooMany.length > 0) {
Expand Down Expand Up @@ -513,7 +516,14 @@ export class Splendor extends BaseGame<State> {
else view = { type: 'player', active: false, self: side };
} else view = { type: 'spectator', active: false, action: this.winCtx ? VIEW_ACTION_TYPE.GAME_END : null };

const ctx: RenderCtx = { id: this.id, board: this.state.board, players: this.state.playerData, turns: this.turns, view, $T: this.$T };
const ctx: RenderCtx = {
id: this.id,
board: this.state.board,
players: this.state.playerData,
turns: this.turns,
view,
$T: this.$T,
};

if (this.winCtx) {
ctx.header = this.$T('GAME.GAME_ENDED');
Expand Down
1 change: 1 addition & 0 deletions src/ps/games/splendor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export type ViewType =
| (ActivePlayer & ActionState);

export type State = {
pointsToWin: number;
turn: Turn;
board: Board;
playerData: Record<Turn, PlayerData>;
Expand Down