Skip to content

Commit

Permalink
UPDATE: new Lucky Coin
Browse files Browse the repository at this point in the history
  • Loading branch information
lsocrate committed Jan 1, 2024
1 parent 3ae07ed commit e9dd2cc
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 67 deletions.
3 changes: 2 additions & 1 deletion server/game/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ export enum EventNames {
OnDuelResolution = 'onDuelResolution',
OnDuelFinished = 'onDuelFinished',
OnDynastyCardTurnedFaceup = 'onDynastyCardTurnedFaceup',
OnRevealFacedownDynastyCards = 'onRevealFacedownDynastyCards',
OnTransferHonor = 'onTransferHonor',
OnPassDuringDynasty = 'onPassDuringDynasty',
OnModifyHonor = 'onModifyHonor',
Expand Down Expand Up @@ -406,4 +407,4 @@ export enum FavorTypes {
export enum SkillType {
Military = 'military',
Political = 'political'
}
}
4 changes: 2 additions & 2 deletions server/game/Costs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ export function removeFromGame(properties: SelectCostProperties): Cost {
/**
* Cost that requires removing a card selected by the player from the game.
*/
export function removeSelfFromGame(): Cost {
return new GameActionCost(GameActions.removeFromGame());
export function removeSelfFromGame(properties?: { location: Array<Locations> }): Cost {
return new GameActionCost(GameActions.removeFromGame(properties));
}

/**
Expand Down
18 changes: 0 additions & 18 deletions server/game/cards/18.1-EL01/Crane/LuckyCoin.js

This file was deleted.

48 changes: 48 additions & 0 deletions server/game/cards/18.1-EL01/Crane/LuckyCoin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import AbilityDsl from '../../../abilitydsl';
import { Locations } from '../../../Constants';
import DrawCard from '../../../drawcard';
import { parseGameMode } from '../../../GameMode';

const ACTIVE_LOCATIONS = [Locations.Hand, Locations.PlayArea];

export default class LuckyCoin extends DrawCard {
static id = 'lucky-coin';

setupCardAbilities() {
this.reaction({
title: 'Replace all cards in your provinces',
when: {
onRevealFacedownDynastyCards: (_, context) => {
const totalCost = context.player
.getDynastyCardsInProvince(Locations.Provinces)
.reduce((totalCost: number, card: DrawCard) => {
const cost = !card.facedown && !isNaN(card.printedCost) ? card.printedCost : 0;
return totalCost + cost;
}, 0);
return totalCost < 6 || totalCost > 12;
}
},
cost: AbilityDsl.costs.removeSelfFromGame({ location: ACTIVE_LOCATIONS }),
location: ACTIVE_LOCATIONS,
gameAction: AbilityDsl.actions.handler({
handler: ({ player, game }) => {
const cardsToMulligan = player.getDynastyCardsInProvince(Locations.Provinces);

for (const nonStrongholdProvince of parseGameMode(game.gameMode).setupNonStrongholdProvinces) {
if (player.dynastyDeck.size() > 0) {
player.moveCard(player.dynastyDeck.first(), nonStrongholdProvince);
}
}

for (const card of cardsToMulligan) {
const originalLocation = card.location;
player.moveCard(card, 'dynasty deck bottom');
player.replaceDynastyCard(originalLocation);
}
player.shuffleDynastyDeck();
}
}),
effect: 'to replace all cards in their provinces'
});
}
}
8 changes: 6 additions & 2 deletions server/game/gamesteps/DynastyPhase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EffectNames, Phases } from '../Constants';
import { EffectNames, EventNames, Phases } from '../Constants';
import type DrawCard from '../drawcard';
import type Game from '../game';
import { Phase } from './Phase';
Expand Down Expand Up @@ -44,13 +44,15 @@ export class DynastyPhase extends Phase {
}

#flipDynastyCards() {
const allRevealedCards = new Set<DrawCard>();
for (const player of this.game.getPlayersInFirstPlayerOrder()) {
const revealedCards = new Set<DrawCard>();
for (const province of this.game.getProvinceArray()) {
for (const card of player.getDynastyCardsInProvince(province) as DrawCard[]) {
if (card.isFacedown()) {
this.game.applyGameAction(null, { flipDynasty: card });
revealedCards.add(card);
allRevealedCards.add(card);
}
}
}
Expand All @@ -60,6 +62,8 @@ export class DynastyPhase extends Phase {
);
}
}

this.game.raiseEvent(EventNames.OnRevealFacedownDynastyCards, { allRevealedCards });
}

#collectFate() {
Expand Down Expand Up @@ -88,4 +92,4 @@ export class DynastyPhase extends Phase {
}
}
}
}
}
202 changes: 158 additions & 44 deletions test/server/cards/18.1-EL01/Crane/LuckyCoin.spec.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,174 @@
describe('Lucky Coin', function() {
integration(function() {
beforeEach(function() {
describe('Lucky Coin - ATTENTION: FLAKY!!', function () {
integration(function () {
beforeEach(function () {
this.setupTest({
phase: 'conflict',
phase: 'fate',
player1: {
inPlay: ['doji-kuwanan', 'doji-challenger'],
hand: ['lucky-coin', 'a-new-name']
},
player2: {
inPlay: ['young-harrier'],
hand: ['noble-sacrifice']
inPlay: ['doji-kuwanan'],
hand: ['lucky-coin'],
dynastyDiscard: [
'iron-mine',
'miya-mystic',
'aranat',
'fushicho',
'imperial-storehouse',
'miya-library'
]
}
});

this.kuwanan = this.player1.findCardByName('doji-kuwanan');
this.challenger = this.player1.findCardByName('doji-challenger');
this.coin = this.player1.findCardByName('lucky-coin');
this.ann = this.player1.findCardByName('a-new-name');

this.harrier = this.player2.findCardByName('young-harrier');
this.sac = this.player2.findCardByName('noble-sacrifice');
this.mine = this.player1.findCardByName('iron-mine');
this.mystic = this.player1.findCardByName('miya-mystic');
this.aranat = this.player1.findCardByName('aranat');
this.fushicho = this.player1.findCardByName('fushicho');
this.storehouse = this.player1.findCardByName('imperial-storehouse');
this.library = this.player1.findCardByName('miya-library');
});

this.kuwanan.dishonor();
this.harrier.honor();
describe('for flips with ok cost', function () {
beforeEach(function () {
this.player1.placeCardInProvince(this.mine, 'province 1');
this.mine.facedown = true;
this.player1.placeCardInProvince(this.library, 'province 2');
this.library.facedown = true;
this.player1.placeCardInProvince(this.fushicho, 'province 3');
this.fushicho.facedown = true;
this.player1.placeCardInProvince(this.mystic, 'province 4');
this.mystic.facedown = true;
});

this.player1.playAttachment(this.coin, this.kuwanan);
});
describe('with attachment in play', function () {
beforeEach(function () {
this.player1.playAttachment(this.coin, this.kuwanan);

this.noMoreActions();
this.player2.clickPrompt('Done');
this.player2.clickPrompt('End Round');
this.player1.clickPrompt('End Round');
});

it('nothing happens', function () {
expect(this.player1).not.toHavePrompt('Triggered Abilities');
});
});

describe('with attachment in hand', function () {
beforeEach(function () {
this.noMoreActions();
this.player2.clickPrompt('Done');
this.player2.clickPrompt('End Round');
this.player1.clickPrompt('End Round');
});

it('should have no effect if not during a conflict', function() {
expect(this.kuwanan.getMilitarySkill()).toBe(2);
expect(this.kuwanan.getPoliticalSkill()).toBe(1);
it('nothing happens', function () {
expect(this.player1).not.toHavePrompt('Triggered Abilities');
});
});
});

it('should stop dishonor status modifying both skills', function() {
this.noMoreActions();
this.initiateConflict({
attackers: [this.challenger],
defenders: []
describe('for too expensive flips', function () {
beforeEach(function () {
this.player1.placeCardInProvince(this.mine, 'province 1');
this.mine.facedown = true;
this.player1.placeCardInProvince(this.aranat, 'province 2');
this.aranat.facedown = true;
this.player1.placeCardInProvince(this.fushicho, 'province 3');
this.fushicho.facedown = true;
this.player1.placeCardInProvince(this.mystic, 'province 4');
this.mystic.facedown = true;
});

describe('with attachment in play', function () {
beforeEach(function () {
this.player1.playAttachment(this.coin, this.kuwanan);

this.noMoreActions();
this.player2.clickPrompt('Done');
this.player2.clickPrompt('End Round');
this.player1.clickPrompt('End Round');
});

it('mulligan everything', function () {
expect(this.player1).toHavePrompt('Triggered Abilities');

this.player1.clickCard(this.coin);
expect(this.getChatLogs(3)).toContain(
'player1 uses Lucky Coin, removing Lucky Coin from the game to to replace all cards in their provinces'
);
});
});

describe('with attachment in hand', function () {
beforeEach(function () {
this.noMoreActions();
this.player2.clickPrompt('Done');
this.player2.clickPrompt('End Round');
this.player1.clickPrompt('End Round');
});

it('mulligan everything', function () {
expect(this.player1).toHavePrompt('Triggered Abilities');

this.player1.clickCard(this.coin);
expect(this.getChatLogs(3)).toContain(
'player1 uses Lucky Coin, removing Lucky Coin from the game to to replace all cards in their provinces'
);
});
});
expect(this.kuwanan.getMilitarySkill()).toBe(5);
expect(this.kuwanan.getPoliticalSkill()).toBe(4);
});

// it('should stop dishonor status losing honor on leaving play', function() {
// let honor = this.player1.honor;

// this.noMoreActions();
// this.initiateConflict({
// attackers: [this.challenger],
// defenders: []
// });
// this.player2.clickCard(this.sac);
// this.player2.clickCard(this.kuwanan);
// this.player2.clickCard(this.harrier);

// expect(this.kuwanan.location).toBe('dynasty discard pile');
// expect(this.player1.honor).toBe(honor);
// });
describe('for too cheap flips', function () {
beforeEach(function () {
this.player1.placeCardInProvince(this.mine, 'province 1');
this.mine.facedown = true;
this.player1.placeCardInProvince(this.library, 'province 2');
this.library.facedown = true;
this.player1.placeCardInProvince(this.storehouse, 'province 3');
this.storehouse.facedown = true;
this.player1.placeCardInProvince(this.mystic, 'province 4');
this.mystic.facedown = true;
});

describe('with attachment in play', function () {
beforeEach(function () {
this.player1.playAttachment(this.coin, this.kuwanan);

this.noMoreActions();
this.player2.clickPrompt('Done');
this.player2.clickPrompt('End Round');
this.player1.clickPrompt('End Round');
});

it('mulligan everything', function () {
expect(this.player1).toHavePrompt('Triggered Abilities');

this.player1.clickCard(this.coin);
expect(this.getChatLogs(3)).toContain(
'player1 uses Lucky Coin, removing Lucky Coin from the game to to replace all cards in their provinces'
);
});
});

describe('with attachment in hand', function () {
beforeEach(function () {
this.noMoreActions();
this.player2.clickPrompt('Done');
this.player2.clickPrompt('End Round');
this.player1.clickPrompt('End Round');
});

it('mulligan everything', function () {
expect(this.player1).toHavePrompt('Triggered Abilities');

this.player1.clickCard(this.coin);
expect(this.getChatLogs(3)).toContain(
'player1 uses Lucky Coin, removing Lucky Coin from the game to to replace all cards in their provinces'
);
});
});
});
});
});
});

0 comments on commit e9dd2cc

Please sign in to comment.