Skip to content

Commit

Permalink
Update display prompt for You're My Only Hope (#576)
Browse files Browse the repository at this point in the history
  • Loading branch information
moyerr authored Feb 17, 2025
1 parent caee255 commit be4bd4d
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 58 deletions.
56 changes: 34 additions & 22 deletions server/game/cards/01_SOR/events/YoureMyOnlyHope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { EventCard } from '../../../core/card/EventCard';
import { CostAdjustType } from '../../../core/cost/CostAdjuster';

export default class YoureMyOnlyHope extends EventCard {
protected override readonly overrideNotImplemented: boolean = true;

protected override getImplementationId() {
return {
id: '3509161777',
Expand All @@ -15,26 +13,40 @@ export default class YoureMyOnlyHope extends EventCard {
public override setupCardAbilities() {
this.setEventAbility({
title: 'Look at the top card of your deck',
immediateEffect: AbilityHelper.immediateEffects.lookAt((context) => ({
target: context.source.controller.getTopCardOfDeck()
})),
ifYouDo: (ifYouDoContext) => ({
title: ifYouDoContext.source.controller.base.remainingHp <= 5
? `Play ${ifYouDoContext.source.controller.getTopCardOfDeck().title} for free`
: `Play ${ifYouDoContext.source.controller.getTopCardOfDeck().title}, it costs 5 less`,
optional: true,
immediateEffect: AbilityHelper.immediateEffects.conditional({
condition: (context) => context.source.controller.base.remainingHp <= 5,
onTrue: AbilityHelper.immediateEffects.playCardFromOutOfPlay((context) => ({
target: context.source.controller.getTopCardOfDeck(),
adjustCost: { costAdjustType: CostAdjustType.Free }
})),
onFalse: AbilityHelper.immediateEffects.playCardFromOutOfPlay((context) => ({
target: context.source.controller.getTopCardOfDeck(),
adjustCost: { costAdjustType: CostAdjustType.Decrease, amount: 5 }
}))
})
})
immediateEffect: AbilityHelper.immediateEffects.lookAtAndChooseOption(
(context) => {
const topCardOfDeck = context.source.controller.getTopCardOfDeck();
const canPlayForFree = context.source.controller.base.remainingHp <= 5;
const leaveOnTopButton = {
text: 'Leave on top',
arg: 'leave',
immediateEffect: AbilityHelper.immediateEffects.noAction({ hasLegalTarget: true })
};
const playForFreeButton = {
text: 'Play for free',
arg: 'play-free',
immediateEffect: AbilityHelper.immediateEffects.playCardFromOutOfPlay({
target: topCardOfDeck,
adjustCost: { costAdjustType: CostAdjustType.Free }
})
};
const playForDiscountButton = {
text: 'Play for 5 less',
arg: 'play-discount',
immediateEffect: AbilityHelper.immediateEffects.playCardFromOutOfPlay({
target: topCardOfDeck,
adjustCost: { costAdjustType: CostAdjustType.Decrease, amount: 5 }
})
};

return {
target: topCardOfDeck,
perCardButtons: canPlayForFree
? [playForFreeButton, leaveOnTopButton]
: [playForDiscountButton, leaveOnTopButton]
};
}
)
});
}
}
8 changes: 4 additions & 4 deletions test/helpers/CustomMatchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -873,9 +873,9 @@ var customMatchers = {
result.pass = stringArraysEqual(actualButtonsInPrompt, expectedButtonsInPrompt);

if (result.pass) {
result.message = `Expected ${player.name} not to have this exact set of "per card" buttons but it did: ${expectedButtonsInPrompt.join(', ')}`;
result.message = `Expected ${player.name} not to have this exact set of enabled "per card" buttons but it did: ${expectedButtonsInPrompt.join(', ')}`;
} else {
result.message = `Expected ${player.name} to have this exact set of "per card" buttons: '${expectedButtonsInPrompt.join(', ')}' but it has: '${actualButtonsInPrompt.join(', ')}'`;
result.message = `Expected ${player.name} to have this exact set of enabled "per card" buttons: '${expectedButtonsInPrompt.join(', ')}' but it has: '${actualButtonsInPrompt.join(', ')}'`;
}

result.message += `\n\n${generatePromptHelpMessage(player.testContext)}`;
Expand All @@ -898,9 +898,9 @@ var customMatchers = {
result.pass = stringArraysEqual(actualButtonsInPrompt, expectedButtonsInPrompt);

if (result.pass) {
result.message = `Expected ${player.name} not to have this exact set of "per card" buttons but it did: ${expectedButtonsInPrompt.join(', ')}`;
result.message = `Expected ${player.name} not to have this exact set of disabled "per card" buttons but it did: ${expectedButtonsInPrompt.join(', ')}`;
} else {
result.message = `Expected ${player.name} to have this exact set of "per card" buttons: '${expectedButtonsInPrompt.join(', ')}' but it has: '${actualButtonsInPrompt.join(', ')}'`;
result.message = `Expected ${player.name} to have this exact set of disabled "per card" buttons: '${expectedButtonsInPrompt.join(', ')}' but it has: '${actualButtonsInPrompt.join(', ')}'`;
}

result.message += `\n\n${generatePromptHelpMessage(player.testContext)}`;
Expand Down
87 changes: 56 additions & 31 deletions test/server/cards/01_SOR/events/YoureMyOnlyHope.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,89 @@ describe('You\'re My Only Hope', function() {
leader: 'hera-syndulla#spectre-two',
base: { card: 'administrators-tower', damage: 21 },
hand: ['youre-my-only-hope'],
deck: ['atst', 'wampa'],
deck: ['atst'],
groundArena: ['isb-agent']
},
player2: {
hand: ['regional-governor'],
spaceArena: ['black-one#scourge-of-starkiller-base']
},

// IMPORTANT: this is here for backwards compatibility of older tests, don't use in new code
autoSingleTarget: true
}
});
});

it('shows the top card of the deck and allow to play it', function () {
it('shows the top card of the deck and allows the player to leave it', function () {
const { context } = contextRef;
const reset = () => {
context.player1.moveCard(context.youreMyOnlyHope, 'hand');
context.player2.passAction();
};

// Scenario 1: Do nothing
context.player1.clickCard(context.youreMyOnlyHope);
// TODO: we need a 'look at' prompt for secretly revealing, currently chat logs go to all players
expect(context.getChatLogs(1)).toContain('You\'re My Only Hope sees AT-ST');
expect(context.player1).toHavePassAbilityPrompt('Play AT-ST, it costs 5 less');

context.player1.passAction();
expect(context.player1).toHaveExactSelectableDisplayPromptCards([context.atst]);
expect(context.player1).toHaveExactDisplayPromptPerCardButtons(['Play for 5 less', 'Leave on top']);
expect(context.getChatLogs(1)[0]).not.toContain(context.atst.title);

context.player1.clickDisplayCardPromptButton(context.atst.uuid, 'leave');

expect(context.atst).toBeInZone('deck', context.player1);
});

reset();
it('shows the top card of the deck and allows the player to play it for 5 less', function () {
const { context } = contextRef;

// Scenario 2: Play the card for 5 less
context.player1.clickCard(context.youreMyOnlyHope);
// TODO: we need a 'look at' prompt for secretly revealing, currently chat logs go to all players
expect(context.getChatLogs(1)).toContain('You\'re My Only Hope sees AT-ST');
expect(context.player1).toHavePassAbilityPrompt('Play AT-ST, it costs 5 less');

let exhaustedResourcesBeforeAction = context.player1.exhaustedResourceCount;
context.player1.clickPrompt('Play AT-ST, it costs 5 less');
expect(context.player1).toHaveExactSelectableDisplayPromptCards([context.atst]);
expect(context.player1).toHaveExactDisplayPromptPerCardButtons(['Play for 5 less', 'Leave on top']);
expect(context.getChatLogs(1)[0]).not.toContain(context.atst.title);

const exhaustedResourcesBeforeAction = context.player1.exhaustedResourceCount;
context.player1.clickDisplayCardPromptButton(context.atst.uuid, 'play-discount');

expect(context.player1.exhaustedResourceCount).toBe(exhaustedResourcesBeforeAction + 3);
expect(context.atst).toBeInZone('groundArena', context.player1);
});

reset();
it('shows the top card of the deck and allows the player to play it for free if their base has 5 or less remaining HP', function () {
const { context } = contextRef;

// Scenario 3: Play the card for free
context.player1.clickCard(context.isbAgent);
context.player1.clickCard(context.p2Base);
context.player2.clickCard(context.blackOne);
context.player2.clickCard(context.p1Base);

context.player1.clickCard(context.youreMyOnlyHope);
expect(context.getChatLogs(1)).toContain('You\'re My Only Hope sees Wampa');
expect(context.player1).toHavePassAbilityPrompt('Play Wampa for free');

exhaustedResourcesBeforeAction = context.player1.exhaustedResourceCount;
context.player1.clickPrompt('Play Wampa for free');
expect(context.player1).toHaveExactSelectableDisplayPromptCards([context.atst]);
expect(context.player1).toHaveExactDisplayPromptPerCardButtons(['Play for free', 'Leave on top']);
expect(context.getChatLogs(1)[0]).not.toContain(context.atst.title);

const exhaustedResourcesBeforeAction = context.player1.exhaustedResourceCount;
context.player1.clickDisplayCardPromptButton(context.atst.uuid, 'play-free');

expect(context.player1.exhaustedResourceCount).toBe(exhaustedResourcesBeforeAction);
expect(context.wampa).toBeInZone('groundArena', context.player1);
expect(context.atst).toBeInZone('groundArena', context.player1);
});

it('shows the top card of the deck and disallows the player from playing it if it has been named by the Regional Governor', function () {
const { context } = contextRef;

reset();
context.player1.passAction();
context.player2.clickCard(context.regionalGovernor);
context.player2.chooseListOption('AT-ST');

context.player1.clickCard(context.youreMyOnlyHope);

expect(context.player1).toHaveExactSelectableDisplayPromptCards([context.atst]);
expect(context.player1).toHaveExactDisabledDisplayPromptPerCardButtons(['Play for 5 less']);
expect(context.player1).toHaveExactEnabledDisplayPromptPerCardButtons(['Leave on top']);
expect(context.getChatLogs(1)[0]).not.toContain(context.atst.title);

context.player1.clickDisplayCardPromptButton(context.atst.uuid, 'leave');
expect(context.atst).toBeInZone('deck', context.player1);
});

it('does nothing if the deck is empty', function () {
const { context } = contextRef;
context.player1.moveCard(context.atst, 'discard');

// Scenario 4: Does not trigger if the deck is empty
expect(context.player1.deck.length).toEqual(0);
context.player1.clickCard(context.youreMyOnlyHope);
expect(context.youreMyOnlyHope).toBeInZone('discard', context.player1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('Lux Bonteri, Renegade Separatist', function () {
context.player1.moveCard(context.scoutBikePursuer, 'hand');

context.player2.clickCard(context.youreMyOnlyHope);
context.player2.clickPrompt('Play Ki-Adi-Mundi, it costs 5 less');
context.player2.clickDisplayCardPromptButton(context.kiadimundi.uuid, 'play-discount');

// player 2 play a unit with decreased cost, nothing happens

Expand Down

0 comments on commit be4bd4d

Please sign in to comment.