From 841f56812c1fe8545411b2ef1f68882b7e4e12f6 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Tue, 12 Aug 2025 15:24:15 -0500 Subject: [PATCH] Add ribbons for each challenge --- src/data/challenge.ts | 27 ++++++++++++++++++++++++--- src/system/ribbons/ribbon-data.ts | 23 ++++++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 1eb16b1c48a..89435149d2f 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -44,7 +44,7 @@ export abstract class Challenge { public conditions: ChallengeCondition[]; /** - * The Ribbon awarded on challenge completion, or 0 if the challenge has no ribbon. + * The Ribbon awarded on challenge completion, or 0 if the challenge has no ribbon or is not enabled * * @defaultValue 0 */ @@ -434,8 +434,11 @@ type ChallengeCondition = (data: GameData) => boolean; */ export class SingleGenerationChallenge extends Challenge { public override get ribbonAwarded(): RibbonFlag { - return RibbonData.MONO_GEN; + // NOTE: This logic will not work for the eventual mono gen 10 ribbon, as + // as its flag will not be in sequence with the other mono gen ribbons. + return this.value ? ((RibbonData.MONO_GEN_1 << (this.value - 1)) as RibbonFlag) : 0; } + constructor() { super(Challenges.SINGLE_GENERATION, 9); } @@ -703,7 +706,7 @@ export class SingleTypeChallenge extends Challenge { // `this.value` represents the 1-based index of pokemon type // `RibbonData.MONO_NORMAL` starts the flag position for the types, // and we shift it by 1 for the specific type. - return (RibbonData.MONO_NORMAL << (this.value - 1)) as RibbonFlag; + return this.value ? ((RibbonData.MONO_NORMAL << (this.value - 1)) as RibbonFlag) : 0; } private static TYPE_OVERRIDES: monotypeOverride[] = [ { species: SpeciesId.CASTFORM, type: PokemonType.NORMAL, fusion: false }, @@ -774,6 +777,9 @@ export class SingleTypeChallenge extends Challenge { * Implements a fresh start challenge. */ export class FreshStartChallenge extends Challenge { + public override get ribbonAwarded(): RibbonFlag { + return this.value ? RibbonData.FRESH_START : 0; + } constructor() { super(Challenges.FRESH_START, 2); } @@ -847,6 +853,9 @@ export class FreshStartChallenge extends Challenge { * Implements an inverse battle challenge. */ export class InverseBattleChallenge extends Challenge { + public override get ribbonAwarded(): RibbonFlag { + return this.value ? RibbonData.INVERSE : 0; + } constructor() { super(Challenges.INVERSE_BATTLE, 1); } @@ -880,6 +889,9 @@ export class InverseBattleChallenge extends Challenge { * Implements a flip stat challenge. */ export class FlipStatChallenge extends Challenge { + public override get ribbonAwarded(): RibbonFlag { + return this.value ? RibbonData.FLIP_STATS : 0; + } constructor() { super(Challenges.FLIP_STAT, 1); } @@ -960,6 +972,9 @@ export class LowerStarterPointsChallenge extends Challenge { * Implements a No Support challenge */ export class LimitedSupportChallenge extends Challenge { + public override get ribbonAwarded(): RibbonFlag { + return this.value ? ((RibbonData.NO_HEAL << (this.value - 1)) as RibbonFlag) : 0; + } constructor() { super(Challenges.LIMITED_SUPPORT, 3); } @@ -992,6 +1007,9 @@ export class LimitedSupportChallenge extends Challenge { * Implements a Limited Catch challenge */ export class LimitedCatchChallenge extends Challenge { + public override get ribbonAwarded(): RibbonFlag { + return this.value ? RibbonData.LIMITED_CATCH : 0; + } constructor() { super(Challenges.LIMITED_CATCH, 1); } @@ -1016,6 +1034,9 @@ export class LimitedCatchChallenge extends Challenge { * Implements a Permanent Faint challenge */ export class HardcoreChallenge extends Challenge { + public override get ribbonAwarded(): RibbonFlag { + return this.value ? RibbonData.HARDCORE : 0; + } constructor() { super(Challenges.HARDCORE, 1); } diff --git a/src/system/ribbons/ribbon-data.ts b/src/system/ribbons/ribbon-data.ts index 692f61a4beb..080b118e7f5 100644 --- a/src/system/ribbons/ribbon-data.ts +++ b/src/system/ribbons/ribbon-data.ts @@ -1,5 +1,5 @@ import type { Brander } from "#types/type-helpers"; -export type RibbonFlag = number & Brander<"RibbonFlag">; +export type RibbonFlag = (number & Brander<"RibbonFlag">) | 0; /** * Class for ribbon data management. Usually constructed via the {@linkcode fromJSON} method. @@ -78,6 +78,27 @@ export class RibbonData { public static readonly NUZLOCKE = 0x10000000 as RibbonFlag; /** Ribbon for reaching max friendship */ public static readonly FRIENDSHIP = 0x20000000 as RibbonFlag; + /** Ribbon for winning the flip stats challenge */ + public static readonly FLIP_STATS = 0x40000000 as RibbonFlag; + /** Ribbon for winning the inverse challenge */ + public static readonly INVERSE = 0x80000000 as RibbonFlag; + /** Ribbon for winning the fresh start challenge */ + public static readonly FRESH_START = 0x100000000 as RibbonFlag; + /** Ribbon for winning the hardcore challenge */ + public static readonly HARDCORE = 0x200000000 as RibbonFlag; + /** Ribbon for winning the limited catch challenge */ + public static readonly LIMITED_CATCH = 0x400000000 as RibbonFlag; + /** Ribbon for winning the limited support challenge set to no heal */ + public static readonly NO_HEAL = 0x800000000 as RibbonFlag; + /** Ribbon for winning the limited uspport challenge set to no shop */ + public static readonly NO_SHOP = 0x1000000000 as RibbonFlag; + /** Ribbon for winning the limited support challenge set to both*/ + public static readonly NO_SUPPORT = 0x2000000000 as RibbonFlag; + + // NOTE: max possible ribbon flag is 0x20000000000000 (53 total ribbons) + // Once this is exceeded, bitfield needs to be changed to a bigint or even a uint array + // Note that this has no impact on serialization as it is stored in hex. + //#endregion Ribbons /** Create a new instance of RibbonData. Generally, {@linkcode fromJSON} is used instead. */