Skip to content

Commit

Permalink
Add conditional gain on piloting abilities (#781)
Browse files Browse the repository at this point in the history
  • Loading branch information
AMMayberry1 authored Mar 5, 2025
1 parent a2bf62f commit 160b92d
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 25 deletions.
7 changes: 7 additions & 0 deletions server/game/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type { SpaceArenaZone } from './core/zone/SpaceArenaZone';
import type { CaptureZone } from './core/zone/CaptureZone';
import type { IUnitCard } from './core/card/propertyMixins/UnitProperties';
import type { DelayedEffectType } from './gameSystems/DelayedEffectSystem';
import type { IUpgradeCard } from './core/card/CardInterfaces';

// allow block comments without spaces so we can have compact jsdoc descriptions in this file
/* eslint @stylistic/lines-around-comment: off */
Expand Down Expand Up @@ -234,6 +235,12 @@ export type IIfYouDoAbilityPropsWithSystems<TContext extends AbilityContext> = I
ifYouDoCondition?: (context?: TContext) => boolean;
};

export interface IGainCondition<TSource extends IUpgradeCard> {
gainCondition?: (context: AbilityContext<TSource>) => boolean;
}

export type IKeywordPropertiesWithGainCondition<TSource extends IUpgradeCard> = IKeywordProperties & IGainCondition<TSource>;

export interface IClientUIProperties {
lastPlayedCard?: ISetId;
}
Expand Down
25 changes: 4 additions & 21 deletions server/game/core/card/UpgradeCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,20 @@ import * as Contract from '../utils/Contract';
import type { MoveZoneDestination } from '../Constants';
import { AbilityType, CardType, ZoneName, WildcardRelativePlayer } from '../Constants';
import { PlayUpgradeAction } from '../../actions/PlayUpgradeAction';
import type { IActionAbilityProps, ITriggeredAbilityBaseProps, IConstantAbilityProps, IKeywordProperties, ITriggeredAbilityProps } from '../../Interfaces';
import type { IActionAbilityProps, ITriggeredAbilityBaseProps, IConstantAbilityProps, ITriggeredAbilityProps, IGainCondition, IKeywordPropertiesWithGainCondition } from '../../Interfaces';
import type { Card } from './Card';
import OngoingEffectLibrary from '../../ongoingEffects/OngoingEffectLibrary';
import { WithStandardAbilitySetup } from './propertyMixins/StandardAbilitySetup';
import type { AbilityContext } from '../ability/AbilityContext';
import type { IPlayCardActionProperties } from '../ability/PlayCardAction';
import type { IUnitCard } from './propertyMixins/UnitProperties';
import type { IPlayableCard } from './baseClasses/PlayableOrDeployableCard';
import type { ICardCanChangeControllers, IUpgradeCard } from './CardInterfaces';

interface IGainCondition<TSource extends UpgradeCard> {
gainCondition?: (context: AbilityContext<TSource>) => boolean;
}
type ITriggeredAbilityPropsWithGainCondition<TSource extends IUpgradeCard, TTarget extends Card> = ITriggeredAbilityProps<TTarget> & IGainCondition<TSource>;

type ITriggeredAbilityPropsWithGainCondition<TSource extends UpgradeCard, TTarget extends Card> = ITriggeredAbilityProps<TTarget> & IGainCondition<TSource>;
type ITriggeredAbilityBasePropsWithGainCondition<TSource extends IUpgradeCard, TTarget extends Card> = ITriggeredAbilityBaseProps<TTarget> & IGainCondition<TSource>;

type ITriggeredAbilityBasePropsWithGainCondition<TSource extends UpgradeCard, TTarget extends Card> = ITriggeredAbilityBaseProps<TTarget> & IGainCondition<TSource>;

type IActionAbilityPropsWithGainCondition<TSource extends UpgradeCard, TTarget extends Card> = IActionAbilityProps<TTarget> & IGainCondition<TSource>;

type IKeywordPropertiesWithGainCondition<TSource extends UpgradeCard> = IKeywordProperties & IGainCondition<TSource>;
type IActionAbilityPropsWithGainCondition<TSource extends IUpgradeCard, TTarget extends Card> = IActionAbilityProps<TTarget> & IGainCondition<TSource>;

const UpgradeCardParent = WithPrintedPower(WithPrintedHp(WithStandardAbilitySetup(InPlayCard)));

Expand Down Expand Up @@ -162,16 +155,6 @@ export class UpgradeCard extends UpgradeCardParent implements IUpgradeCard, IPla
});
}

/**
* This is required because a gainCondition call can happen after an upgrade is discarded,
* so we need to short-circuit in that case to keep from trying to access illegal state such as parentCard
*/
private addZoneCheckToGainCondition(gainCondition?: (context: AbilityContext<this>) => boolean) {
return gainCondition == null
? null
: (context: AbilityContext<this>) => this.isInPlay() && gainCondition(context);
}

/** Adds a condition that must return true for the upgrade to be allowed to attach to the passed card. */
protected setAttachCondition(attachCondition: (card: Card) => boolean) {
Contract.assertIsNullLike(this.attachCondition, 'Attach condition is already set');
Expand Down
10 changes: 10 additions & 0 deletions server/game/core/card/baseClasses/InPlayCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,16 @@ export class InPlayCard extends InPlayCardParent implements IInPlayCard {
return true;
}

/**
* This is required because a gainCondition call can happen after an upgrade is discarded,
* so we need to short-circuit in that case to keep from trying to access illegal state such as parentCard
*/
protected addZoneCheckToGainCondition(gainCondition?: (context: AbilityContext<this>) => boolean) {
return gainCondition == null
? null
: (context: AbilityContext<this>) => this.isInPlay() && gainCondition(context);
}

public override getSummary(activePlayer: Player) {
return { ...super.getSummary(activePlayer),
parentCardId: this._parentCard ? this._parentCard.uuid : null };
Expand Down
13 changes: 9 additions & 4 deletions server/game/core/card/propertyMixins/UnitProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { ICardWithPrintedPowerProperty } from './PrintedPower';
import { WithPrintedPower } from './PrintedPower';
import * as EnumHelpers from '../../utils/EnumHelpers';
import type { Card } from '../Card';
import type { IAbilityPropsWithType, IConstantAbilityProps, IKeywordProperties, ITriggeredAbilityBaseProps, ITriggeredAbilityProps } from '../../../Interfaces';
import type { IAbilityPropsWithType, IConstantAbilityProps, IGainCondition, IKeywordProperties, ITriggeredAbilityBaseProps, ITriggeredAbilityProps } from '../../../Interfaces';
import { BountyKeywordInstance } from '../../ability/KeywordInstance';
import { KeywordWithAbilityDefinition } from '../../ability/KeywordInstance';
import TriggeredAbility from '../../ability/TriggeredAbility';
Expand All @@ -39,6 +39,8 @@ import type { AbilityContext } from '../../ability/AbilityContext';

export const UnitPropertiesCard = WithUnitProperties(InPlayCard);

type IAbilityPropsWithGainCondition<TSource extends IUpgradeCard, TTarget extends Card> = IAbilityPropsWithType<TTarget> & IGainCondition<TSource>;

export interface IUnitCard extends IInPlayCard, ICardWithDamageProperty, ICardWithPrintedPowerProperty {
get defaultArena(): Arena;
get capturedUnits(): IUnitCard[];
Expand Down Expand Up @@ -364,7 +366,7 @@ export function WithUnitProperties<TBaseClass extends InPlayCardConstructor>(Bas
title: properties.title,
matchTarget: (card, context) => card === context.source.parentCard,
targetController: WildcardRelativePlayer.Any, // this means that the effect continues to work even if the other player gains control of the upgrade
condition: properties.condition,
condition: this.addZoneCheckToGainCondition(properties.condition),
ongoingEffect: properties.ongoingEffect
});
}
Expand All @@ -376,10 +378,13 @@ export function WithUnitProperties<TBaseClass extends InPlayCardConstructor>(Bas
});
}

public addPilotingGainAbilityTargetingAttached(properties: IAbilityPropsWithType<this>) {
public addPilotingGainAbilityTargetingAttached(properties: IAbilityPropsWithGainCondition<this, IUnitCard>) {
const { gainCondition, ...gainedAbilityProperties } = properties;

this.addPilotingConstantAbilityTargetingAttached({
title: 'Give ability to the attached card',
ongoingEffect: OngoingEffectLibrary.gainAbility(properties)
condition: this.addZoneCheckToGainCondition(gainCondition),
ongoingEffect: OngoingEffectLibrary.gainAbility(gainedAbilityProperties)
});
}

Expand Down

0 comments on commit 160b92d

Please sign in to comment.