Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jango Fett, Concealing the Conspiracy #537

Merged
merged 12 commits into from
Feb 13, 2025
3 changes: 2 additions & 1 deletion server/game/cards/01_SOR/events/OverwhelmingBarrage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export default class OverwhelmingBarrage extends EventCard {
canChooseNoTargets: true,
controller: WildcardRelativePlayer.Any,
cardTypeFilter: WildcardCardType.Unit,
cardCondition: (card) => card !== thenContext.target
cardCondition: (card) => card !== thenContext.target,
source: thenContext.target
})
})
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import AbilityHelper from '../../../AbilityHelper';
import { LeaderUnitCard } from '../../../core/card/LeaderUnitCard';
import { DamageSourceType } from '../../../IDamageOrDefeatSource';

export default class JangoFettConcealingTheConspiracy extends LeaderUnitCard {
protected override getImplementationId() {
return {
id: '9155536481',
internalName: 'jango-fett#concealing-the-conspiracy',
};
}

protected override setupLeaderSideAbilities() {
// When a friendly unit deals damage to an enemy unit:
// You may exhaust this leader.
// If you do, exhaust that enemy unit.
this.addTriggeredAbility({
title: 'Exhaust this leader',
optional: true,
when: {
onDamageDealt: (event, context) => this.isEnemyUnitDamagedByFriendlyUnit(event, context)
},
immediateEffect: AbilityHelper.immediateEffects.exhaust(),
ifYouDo: (ifYouDoContext) => ({
title: 'Exhaust the damaged enemy unit',
immediateEffect: AbilityHelper.immediateEffects.exhaust(
{ target: ifYouDoContext.event.card }
)
})
});
}

protected override setupLeaderUnitSideAbilities() {
// When a friendly unit deals damage to an enemy unit:
// You may exhaust that unit.
this.addTriggeredAbility({
title: 'Exhaust the damaged enemy unit',
optional: true,
when: {
onDamageDealt: (event, context) => this.isEnemyUnitDamagedByFriendlyUnit(event, context)
},
immediateEffect: AbilityHelper.immediateEffects.exhaust((context) => {
return { target: context.event.card };
})
});
}

private isEnemyUnitDamagedByFriendlyUnit(event, context): boolean {
if (event.card.isUnit() && event.card.controller !== context.source.controller) {
console.log('----------------------');
console.log(`[Damage Recieved] ${event.card.title}`);

if (
event.damageSource.type === DamageSourceType.Ability &&
event.damageSource.card.isUnit() &&
event.damageSource.player === context.source.controller
) {
console.log(`[Ability Damage Source] ${event.damageSource.card.title}`);
return true;
}

if (
event.damageSource.type === DamageSourceType.Attack &&
event.damageSource.damageDealtBy.isUnit() &&
event.damageSource.damageDealtBy.controller === context.source.controller
) {
console.log(`[Attack Damage Source] ${event.damageSource.damageDealtBy.title}`);
return true;
}
}

return false;
}
}
5 changes: 4 additions & 1 deletion server/game/gameSystems/DistributeAmongTargetsSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export interface IDistributeAmongTargetsSystemProperties<TContext extends Abilit
maxTargets?: number;
}

export abstract class DistributeAmongTargetsSystem<TContext extends AbilityContext = AbilityContext> extends CardTargetSystem<TContext, IDistributeAmongTargetsSystemProperties> {
export abstract class DistributeAmongTargetsSystem<
TContext extends AbilityContext = AbilityContext,
TProperties extends IDistributeAmongTargetsSystemProperties<TContext> = IDistributeAmongTargetsSystemProperties<TContext>
> extends CardTargetSystem<TContext, TProperties> {
protected override readonly targetTypeFilter = [WildcardCardType.Unit, CardType.Base];
protected override defaultProperties: IDistributeAmongTargetsSystemProperties<TContext> = {
amountToDistribute: null,
Expand Down
14 changes: 11 additions & 3 deletions server/game/gameSystems/DistributeDamageSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,28 @@ import type { IDistributeAmongTargetsSystemProperties } from './DistributeAmongT
import { DistributeAmongTargetsSystem } from './DistributeAmongTargetsSystem';
import type { HealSystem } from './HealSystem';

export type IDistributeDamageSystemProperties<TContext extends AbilityContext = AbilityContext> = IDistributeAmongTargetsSystemProperties<TContext>;
export interface IDistributeDamageSystemProperties<TContext extends AbilityContext = AbilityContext> extends IDistributeAmongTargetsSystemProperties<TContext> {

/** The source of the damage, if different from the card that triggered the ability */
source?: Card;
}

/**
* System for distributing damage among target cards.
* Will prompt the user to select where to put the damage (unless auto-selecting a single target is possible).
*/
export class DistributeDamageSystem<TContext extends AbilityContext = AbilityContext> extends DistributeAmongTargetsSystem<TContext> {
export class DistributeDamageSystem<
TContext extends AbilityContext = AbilityContext,
TProperties extends IDistributeDamageSystemProperties<TContext> = IDistributeDamageSystemProperties<TContext>
> extends DistributeAmongTargetsSystem<TContext, TProperties> {
protected override readonly eventName = MetaEventName.DistributeDamage;
public override readonly name = 'distributeDamage';

public override promptType: DistributePromptType = StatefulPromptType.DistributeDamage;

protected override generateEffectSystem(target: Card = null, amount = 1): DamageSystem | HealSystem {
return new DamageSystem({ type: DamageType.Ability, target, amount });
const { source } = this.properties;
return new DamageSystem({ type: DamageType.Ability, target, amount, source });
}

// most "distribute damage" abilities require all damage to be dealt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe('Maul, Shadow Collective Visionary', function() {
expect(context.mercenaryCompany.damage).toBe(4);
});

it('redirects combat damage to another friendly Underworld unit, handling shields correctly', function () {
it('redirects combat damage to another friendly Underworld unit, handling shields and damage attribution correctly', function () {
contextRef.setupTest({
phase: 'action',
player1: {
Expand All @@ -171,7 +171,8 @@ describe('Maul, Shadow Collective Visionary', function() {
]
},
player2: {
groundArena: ['luminara-unduli#softspoken-master']
groundArena: ['luminara-unduli#softspoken-master'],
leader: 'jango-fett#concealing-the-conspiracy'
}
});

Expand Down Expand Up @@ -221,9 +222,26 @@ describe('Maul, Shadow Collective Visionary', function() {
expect(context.maul.isUpgraded()).toBeFalse();
expect(context.luminaraUnduli.damage).toBe(7);
expect(context.mercenaryCompany.damage).toBe(0);

// CASE 3: Deflect damage to Mercenary Company, opponent is able to exhaust Mercenary Company with Jango Fett's ability

context.moveToNextActionPhase();

context.player1.clickCard(context.maul);
context.player1.clickCard(context.luminaraUnduli);

expect(context.player1).toBeAbleToSelectExactly([context.mercenaryCompany]);
expect(context.player1).toHavePassAbilityButton();
context.player1.clickCard(context.mercenaryCompany);

// Resolve Jango's ability
expect(context.player2).toHavePassAbilityPrompt('Exhaust this leader');
context.player2.clickPrompt('Exhaust this leader');

expect(context.mercenaryCompany.exhausted).toBeTrue();
expect(context.mercenaryCompany.damage).toBe(4);
expect(context.maul.damage).toBe(0);
});
});
});

// TODO: test with Jango leader for attribution
});
Loading