Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge 655a12a into 174f4c6
Browse files Browse the repository at this point in the history
  • Loading branch information
marcsoiferman-cm committed Dec 6, 2019
2 parents 174f4c6 + 655a12a commit ccb1473
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 7 deletions.
2 changes: 1 addition & 1 deletion server/game/Events/InitiateAbilityEventWindow.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class InitiateAbilityInterruptWindow extends TriggeredAbilityWindow {
getMinCostReduction() {
if(this.playEvent) {
const context = this.playEvent.context;
const alternatePools = context.player.getAlternateFatePools(this.playEvent.playType, context.source);
const alternatePools = context.player.getAlternateFatePools(this.playEvent.playType, context.source, context);
const alternatePoolTotal = alternatePools.reduce((total, pool) => total + pool.fate, 0);
const maxPlayerFate = context.player.checkRestrictions('spendFate', context) ? context.player.fate : 0;
return Math.max(context.ability.getReducedCost(context) - maxPlayerFate - alternatePoolTotal, 0);
Expand Down
19 changes: 19 additions & 0 deletions server/game/cards/09.6-SD/StudentOfEsoterica.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const DrawCard = require('../../drawcard.js');
const AbilityDsl = require('../../abilitydsl');

class StudentOfEsoterica extends DrawCard {
setupCardAbilities() {
this.persistentEffect({
effect: AbilityDsl.effects.alternateFatePool(card => {
if(card.hasTrait('spell')) {
return this;
}
return false;
})
});
}
}

StudentOfEsoterica.id = 'student-of-esoterica';

module.exports = StudentOfEsoterica;
17 changes: 13 additions & 4 deletions server/game/costs/ReduceableFateCost.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const Event = require('../Events/Event');
const { EventNames } = require('../Constants');
const GameActions = require('../GameActions/GameActions');

class ReduceableFateCost {
constructor(ignoreType) {
Expand All @@ -17,12 +18,12 @@ class ReduceableFateCost {
}

resolve(context, result) {
let alternatePools = context.player.getAlternateFatePools(context.playType, context.source);
let alternatePools = context.player.getAlternateFatePools(context.playType, context.source, context);
let alternatePoolTotal = alternatePools.reduce((total, pool) => total + pool.fate, 0);
let maxPlayerFate = context.player.checkRestrictions('spendFate', context) ? context.player.fate : 0;
if(this.getReducedCost(context) > maxPlayerFate + alternatePoolTotal) {
result.cancelled = true;
} else if(!result.cancelled && alternatePools.length > 0 && context.player.checkRestrictions('takeFateFromRings', context)) {
} else if(!result.cancelled && alternatePools.length > 0) {
let properties = {
reducedCost: this.getReducedCost(context),
remainingPoolTotal: alternatePoolTotal
Expand Down Expand Up @@ -54,10 +55,18 @@ class ReduceableFateCost {
if(result.canCancel) {
choices.push('Cancel');
}
if(properties.maxFate === 0) {
context.costs.alternateFate.set(properties.pool, 0);
return;
}

context.player.setSelectableCards([properties.pool]);
context.game.promptWithHandlerMenu(context.player, {
activePromptTitle: 'Choose amount of fate to spend from the ' + properties.pool.name,
choices: choices,
choiceHandler: choice => {
context.player.clearSelectableCards();

if(choice === 'Cancel') {
result.cancelled = true;
return;
Expand All @@ -84,8 +93,8 @@ class ReduceableFateCost {
return reducedCost;
}
let totalAlternateFate = 0;
for(let alternatePool of context.player.getAlternateFatePools(context.playType, context.source)) {
alternatePool.modifyFate(-context.costs.alternateFate.get(alternatePool));
for(let alternatePool of context.player.getAlternateFatePools(context.playType, context.source, context)) {
GameActions.removeFate({ amount: context.costs.alternateFate.get(alternatePool)}).resolve(alternatePool, context);
totalAlternateFate += context.costs.alternateFate.get(alternatePool);
}
return Math.max(reducedCost - totalAlternateFate, 0);
Expand Down
18 changes: 16 additions & 2 deletions server/game/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const PlayableLocation = require('./playablelocation.js');
const PlayerPromptState = require('./playerpromptstate.js');
const RoleCard = require('./rolecard.js');
const StrongholdCard = require('./strongholdcard.js');
const Ring = require('./ring.js');

const { Locations, Decks, EffectNames, CardTypes, PlayTypes, EventNames, AbilityTypes, ConflictTypes } = require('./Constants');
const provinceLocations = [Locations.StrongholdProvince, Locations.ProvinceOne, Locations.ProvinceTwo, Locations.ProvinceThree, Locations.ProvinceFour];
Expand Down Expand Up @@ -579,16 +580,29 @@ class Player extends GameObject {
this.playableLocations = _.reject(this.playableLocations, l => l === location);
}

getAlternateFatePools(playingType, card) {
getAlternateFatePools(playingType, card, context) {
let effects = this.getEffects(EffectNames.AlternateFatePool);
let alternateFatePools = effects.filter(match => match(card) && match(card).fate > 0).map(match => match(card));
let rings = alternateFatePools.filter(a => a instanceof Ring);
let cards = alternateFatePools.filter(a => !(a instanceof Ring));
if(!this.checkRestrictions('takeFateFromRings', context)) {
rings.forEach(ring => {
alternateFatePools.splice(ring, 1);
});
}
cards.forEach(card => {
if(!card.allowGameAction('removeFate')) {
alternateFatePools.splice(card, 1);
}
});

return _.uniq(alternateFatePools);
}

getMinimumCost(playingType, context, target, ignoreType = false) {
const card = context.source;
let reducedCost = this.getReducedCost(playingType, card, target, ignoreType);
let alternateFatePools = this.getAlternateFatePools(playingType, card);
let alternateFatePools = this.getAlternateFatePools(playingType, card, context);
let alternateFate = alternateFatePools.reduce((total, pool) => total + pool.fate, 0);
let triggeredCostReducers = 0;
let fakeWindow = { addChoice: () => triggeredCostReducers++ };
Expand Down
43 changes: 43 additions & 0 deletions test/server/cards/04.6-EU/AwakenedTsukumogami.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,48 @@ describe('Awakened Tsukumogami', function() {
expect(this.game.rings.air.fate).toBe(1);
});
});

describe('Awakened Tsukumogami\'s ability (Stone of Sorrows)', function() {
beforeEach(function() {
this.setupTest({
phase: 'conflict',
player1: {
inPlay: ['agasha-swordsmith'],
hand: ['the-stone-of-sorrows']
},
player2: {
fate: 1,
inPlay: ['awakened-tsukumogami', 'asako-azunami', 'awakened-tsukumogami'],
hand: ['against-the-waves', 'consumed-by-five-fires', 'the-mirror-s-gaze', 'daimyo-s-favor']
}
});
this.agashaSwordsmith = this.player1.findCardByName('agasha-swordsmith');
this.agashaSwordsmith.fate = 4;
this.game.rings.air.fate = 2;
this.game.rings.water.fate = 2;
this.game.rings.fire.fate = 5;
this.player1.playAttachment('the-stone-of-sorrows', this.agashaSwordsmith);
});

it('should not allow the player to choose whether to take fate from the ring or their fate pool', function() {
let waterFate = this.game.rings.water.fate;
let playerFate = this.player2.fate;

this.player2.clickCard('against-the-waves');
expect(this.player2).toHavePrompt('Against the Waves');
this.player2.clickCard(this.agashaSwordsmith);
expect(this.player2).not.toHavePrompt('Choose amount of fate to spend from the Water ring');
expect(this.player2.fate).toBe(playerFate - 1);
expect(this.game.rings.water.fate).toBe(waterFate);
expect(this.agashaSwordsmith.bowed).toBe(true);
expect(this.player1).toHavePrompt('Action Window');
});

it('should not let playing a card you can\'t afford if you can\'t take fate from the rings', function() {
this.player2.fate = 3;
this.player2.clickCard('consumed-by-five-fires');
expect(this.player2).toHavePrompt('Action Window');
});
});
});
});
Loading

0 comments on commit ccb1473

Please sign in to comment.