Skip to content

Commit

Permalink
checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
AMMayberry1 committed Jul 30, 2024
1 parent 3f81f2c commit 7213df7
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 371 deletions.
314 changes: 293 additions & 21 deletions server/swu/game/card/basecard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
isArena,
Aspects,
cardLocationMatches,
WildcardLocations
WildcardLocations,
StatType
} from '../Constants.js';
import {
ActionProps,
Expand All @@ -31,10 +32,12 @@ import {
// import { PlayAttachmentAction } from './PlayAttachmentAction.js';
// import { StatusToken } from './StatusToken';
import Player from '../player.js';
import type DeckCard = require('./deckcard');
import DeckCard from './deckcard.js';
import StatModifier = require('../StatModifier');
import type { CardEffect } from '../effects/types';
// import type { GainAllAbilities } from './Effects/Library/gainAllAbilities';
import { PlayUnitAction } from '../gameActions/PlayUnitAction.js';
import { checkConvertToEnum } from '../utils/helpers';

// TODO: convert enums to unions
type PrintedKeyword =
Expand Down Expand Up @@ -114,17 +117,18 @@ class BaseCard extends EffectSource {
this.printedTitle = cardData.title;
this.printedSubtitle = cardData.subtitle;
this.internalName = cardData.internalname;
this.printedType = this.#checkConvertToEnum([cardData.type], CardTypes)[0]; // TODO: does this work for leader consistently, since it has two types?
this.printedType = checkConvertToEnum([cardData.type], CardTypes)[0]; // TODO: does this work for leader consistently, since it has two types?
this.traits = cardData.traits; // TODO: enum for these
this.aspects = this.#checkConvertToEnum(cardData.aspects, Aspects);
this.aspects = checkConvertToEnum(cardData.aspects, Aspects);
this.printedKeywords = cardData.keywords; // TODO: enum for these
this.printedHp = this.getPrintedStat(StatType.Hp);

this.setupCardAbilities(AbilityDsl);
this.parseKeywords(cardData.text ? cardData.text.replace(/<[^>]*>/g, '').toLowerCase() : '');
// this.parseKeywords(cardData.text ? cardData.text.replace(/<[^>]*>/g, '').toLowerCase() : '');
// this.applyAttachmentBonus();

if (this.type === CardTypes.Unit) {
actions.push(AbilityDsl.attack());
this.abilities.actions.push(AbilityDsl.attack());
}
}

Expand All @@ -137,21 +141,6 @@ class BaseCard extends EffectSource {
this.printedTitle = name;
}

// convert a set of strings to map to an enum type, throw if any of them is not a legal value
#checkConvertToEnum<T>(values: string[], enumObj: T): Array<T[keyof T]> {
let result: Array<T[keyof T]> = [];

for (const value of values) {
if (Object.values(enumObj).indexOf(value.toLowerCase()) >= 0) {
result.push(value as T[keyof T]);
} else {
throw new Error(`Invalid value for enum: ${value}`);
}
}

return result;
}

#mostRecentEffect(predicate: (effect: CardEffect) => boolean): CardEffect | undefined {
const effects = this.getRawEffects().filter(predicate);
return effects[effects.length - 1];
Expand Down Expand Up @@ -757,6 +746,7 @@ class BaseCard extends EffectSource {
return {};
}

// TODO: would something like this be helpful for swu?
parseKeywords(text: string) {
const potentialKeywords = [];
for (const line of text.split('\n')) {
Expand Down Expand Up @@ -1146,6 +1136,288 @@ class BaseCard extends EffectSource {

// return Object.assign(state, selectionState);
// }

// --------------------------- TODO: type annotations for all of the below --------------------------

// this will be helpful if we ever get a card where a stat that is "X, where X is ..."
getPrintedStat(type: StatType) {
if (type === StatType.Power) {
return this.cardData.damage === null || this.cardData.damage === undefined
? NaN
: isNaN(parseInt(this.cardData.power))
? 0
: parseInt(this.cardData.power);
} else if (type === StatType.Hp) {
return this.cardData.hp === null || this.cardData.hp === undefined
? NaN
: isNaN(parseInt(this.cardData.hp))
? 0
: parseInt(this.cardData.hp);
}
}

addDamage(amount: number) {
if (isNaN(this.hp)) {

}
}

// TODO: type annotations for all of the hp stuff
get hp(): number | null {
return this.getHp();
}

getHp(floor = true, excludeModifiers = []): number | null {
if (this.printedHp === null) {
return null;
}

let modifiers = this.getHpModifiers(excludeModifiers);
let skill = modifiers.reduce((total, modifier) => total + modifier.amount, 0);
if (isNaN(skill)) {
return 0;
}
return floor ? Math.max(0, skill) : skill;
}

get baseHp(): number {
return this.getBaseHp();
}

getBaseHp(): number {
let skill = this.getBaseStatModifiers().baseHp;
if (isNaN(skill)) {
return 0;
}
return Math.max(0, skill);
}

getHpModifiers(exclusions) {
let baseStatModifiers = this.getBaseStatModifiers();
if (isNaN(baseStatModifiers.baseHp)) {
return baseStatModifiers.baseHpModifiers;
}

if (!exclusions) {
exclusions = [];
}

let rawEffects;
if (typeof exclusions === 'function') {
rawEffects = this.getRawEffects().filter((effect) => !exclusions(effect));
} else {
rawEffects = this.getRawEffects().filter((effect) => !exclusions.includes(effect.type));
}

let modifiers = baseStatModifiers.baseHpModifiers;

// hp modifiers
// TODO: remove status tokens completely, upgrades completely cover that category
let modifierEffects = rawEffects.filter(
(effect) =>
effect.type === EffectNames.UpgradeHpModifier ||
effect.type === EffectNames.ModifyHp
);
modifierEffects.forEach((modifierEffect) => {
const value = modifierEffect.getValue(this);
modifiers.push(StatModifier.fromEffect(value, modifierEffect));
});

return modifiers;
}

get power() {
return this.getPower();
}

getPower(floor = true, excludeModifiers = []) {
let modifiers = this.getPowerModifiers(excludeModifiers);
let skill = modifiers.reduce((total, modifier) => total + modifier.amount, 0);
if (isNaN(skill)) {
return 0;
}
return floor ? Math.max(0, skill) : skill;
}

get basePower() {
return this.getBasePower();
}

getBasePower() {
let skill = this.getBaseStatModifiers().basePower;
if (isNaN(skill)) {
return 0;
}
return Math.max(0, skill);
}

getPowerModifiers(exclusions) {
let baseStatModifiers = this.getBaseStatModifiers();
if (isNaN(baseStatModifiers.basePower)) {
return baseStatModifiers.basePowerModifiers;
}

if (!exclusions) {
exclusions = [];
}

let rawEffects;
if (typeof exclusions === 'function') {
rawEffects = this.getRawEffects().filter((effect) => !exclusions(effect));
} else {
rawEffects = this.getRawEffects().filter((effect) => !exclusions.includes(effect.type));
}

// set effects (i.e., "set power to X")
let setEffects = rawEffects.filter(
(effect) => effect.type === EffectNames.SetPower
);
if (setEffects.length > 0) {
let latestSetEffect = _.last(setEffects);
let setAmount = latestSetEffect.getValue(this);
return [
StatModifier.fromEffect(
setAmount,
latestSetEffect,
true,
`Set by ${StatModifier.getEffectName(latestSetEffect)}`
)
];
}

let modifiers = baseStatModifiers.basePowerModifiers;

// power modifiers
// TODO: remove status tokens completely, upgrades completely cover that category
// TODO: does this work for resolving effects like Raid that depend on whether we're the attacker or not?
let modifierEffects = rawEffects.filter(
(effect) =>
effect.type === EffectNames.UpgradePowerModifier ||
effect.type === EffectNames.ModifyPower ||
effect.type === EffectNames.ModifyStats
);
modifierEffects.forEach((modifierEffect) => {
const value = modifierEffect.getValue(this);
modifiers.push(StatModifier.fromEffect(value, modifierEffect));
});

return modifiers;
}

/**
* Direct the stat query to the correct sub function.
* @param {string} type - The type of the stat; power or hp
* @return {number} The chosen stat value
*/
getStat(type) {
switch (type) {
case StatType.Power:
return this.getPower();
case StatType.Hp:
return this.getHp();
}
}

// TODO: rename this to something clearer
/**
* Apply any modifiers that explicitly say they change the base skill value
*/
getBaseStatModifiers() {
const baseModifierEffects = [
EffectNames.CopyCharacter,
EffectNames.CalculatePrintedPower,
EffectNames.SetBasePower,
];

let baseEffects = this.getRawEffects().filter((effect) => baseModifierEffects.includes(effect.type));
let basePowerModifiers = [StatModifier.fromCard(this.printedPower, this, 'Printed power', false)];
let baseHpModifiers = [StatModifier.fromCard(this.printedHp, this, 'Printed hp', false)];
let basePower = this.printedPower;
let baseHp = this.printedHp;

baseEffects.forEach((effect) => {
switch (effect.type) {
// this case is for cards that don't have a default printed power but it is instead calculated
case EffectNames.CalculatePrintedPower: {
let damageFunction = effect.getValue(this);
let calculatedDamageValue = damageFunction(this);
basePower = calculatedDamageValue;
basePowerModifiers = basePowerModifiers.filter(
(mod) => !mod.name.startsWith('Printed power')
);
basePowerModifiers.push(
StatModifier.fromEffect(
basePower,
effect,
false,
`Printed power due to ${StatModifier.getEffectName(effect)}`
)
);
break;
}
case EffectNames.CopyCharacter: {
let copiedCard = effect.getValue(this);
basePower = copiedCard.getPrintedStat(StatType.Power);
baseHp = copiedCard.getPrintedStat(StatType.Hp);
// replace existing base or copied modifier
basePowerModifiers = basePowerModifiers.filter(
(mod) => !mod.name.startsWith('Printed stat')
);
baseHpModifiers = baseHpModifiers.filter(
(mod) => !mod.name.startsWith('Printed stat')
);
basePowerModifiers.push(
StatModifier.fromEffect(
basePower,
effect,
false,
`Printed skill from ${copiedCard.name} due to ${StatModifier.getEffectName(effect)}`
)
);
baseHpModifiers.push(
StatModifier.fromEffect(
baseHp,
effect,
false,
`Printed skill from ${copiedCard.name} due to ${StatModifier.getEffectName(effect)}`
)
);
break;
}
case EffectNames.SetBasePower:
basePower = effect.getValue(this);
basePowerModifiers.push(
StatModifier.fromEffect(
basePower,
effect,
true,
`Base power set by ${StatModifier.getEffectName(effect)}`
)
);
break;
}
});

let overridingPowerModifiers = basePowerModifiers.filter((mod) => mod.overrides);
if (overridingPowerModifiers.length > 0) {
let lastModifier = _.last(overridingPowerModifiers);
basePowerModifiers = [lastModifier];
basePower = lastModifier.amount;
}
let overridingHpModifiers = baseHpModifiers.filter((mod) => mod.overrides);
if (overridingHpModifiers.length > 0) {
let lastModifier = _.last(overridingHpModifiers);
baseHpModifiers = [lastModifier];
baseHp = lastModifier.amount;
}

return {
basePowerModifiers: basePowerModifiers,
basePower: basePower,
baseHpModifiers: baseHpModifiers,
baseHp: baseHp
};
}
}

export = BaseCard;
Loading

0 comments on commit 7213df7

Please sign in to comment.