Skip to content

Commit

Permalink
Simpler card model (#34)
Browse files Browse the repository at this point in the history
* newlines for doc comments

* drop primitives

* revise attraction lights

* drop EnumLike fields

* reduce constants

* revise card model

* fix legalities entry

* revise doc

* revise doc

* cleanup: rm primitives.ts

* finish merging in root types for single/double sided split cards

* restore AnyMultiFaced

* update doc

* revise group definitions

* further revise groups

* revise doc

* use a link here too

* improve card doc

* trim whitespace
  • Loading branch information
scarletcs authored Mar 13, 2024
1 parent 11284c7 commit 9d653a4
Show file tree
Hide file tree
Showing 33 changed files with 722 additions and 471 deletions.
5 changes: 0 additions & 5 deletions src/internal/Primitives.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/internal/index.ts

This file was deleted.

191 changes: 84 additions & 107 deletions src/objects/Card/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,27 @@ import { ScryfallLayout, ScryfallLayoutGroup } from "./values";
import { ScryfallCardFace } from "./CardFace";
import { ScryfallCardFields } from "./CardFields";

type Layout<T extends ScryfallLayout> = Pick<ScryfallCardFields.Core.All, "layout"> & {
layout: T | `${T}`;
type Layout<T extends `${ScryfallLayout}`> = Pick<ScryfallCardFields.Core.All, "layout"> & {
layout: `${T}`;
};

/**
* A collection of types representing Scryfall cards of each possible layout.
*
*
* An individual type exists for each possible layout: {@link ScryfallCard.Normal}, {@link ScryfallCard.Transform}, etc.
*
* Then various groups exist to help describe cards of indeterminate layout:
* - {@link ScryfallCard.Any} describes any card at all. Think of it as like `any` but for cards.
* This collection is focused around four core varieties of cards:
* - {@link ScryfallCard.AnySingleFaced} describes any card with one face and no `card_faces` property, e.g. {@link ScryfallCard.Normal Normal} or {@link ScryfallCard.Saga Saga}.
* - {@link ScryfallCard.AnyMultiFaced} describes any card with multiple faces. It may be a split card with both "faces" on the front, or a double-sided card with faces on the front and back of the card. It also collects the next two groups on this list.
* - {@link ScryfallCard.AnySingleSidedSplit} describes any card with multiple faces where both faces are on the front, e.g. {@link ScryfallCard.Adventure Adventure}, {@link ScryfallCard.Flip Flip}, or {@link ScryfallCard.Split Split}.
* - {@link ScryfallCard.AnyDoubleSidedSplit} describes any card with multiple faces where the faces are on the front and back of the card, e.g. {@link ScryfallCard.Transform Transform}, {@link ScryfallCard.ModalDfc ModalDfc}, or {@link ScryfallCard.ReversibleCard ReversibleCard}.
* - {@link ScryfallCard.ReversibleCard} describes solely reversible cards.
*
* It also provides broader groupings:
* - {@link ScryfallCard.Any} describes any card at all. Think of it as like `any` but for cards.
* - {@link ScryfallCard.AnySplit} is an alias for either AnySingleSidedSplit or AnyDoubleSidedSplit.
* - {@link ScryfallCard.AnyMultiFaced} is an alias for AnySingleSidedSplit, AnyDoubleSidedSplit, or Reversible. This describes all layouts that can have a `card_faces` field.
*
* We recommend starting from `ScryfallCard.Any` to describe generic API responses, and you will need to do type narrowing to access more specific fields.
* There is also an alias for each possible layout: {@link ScryfallCard.Normal}, {@link ScryfallCard.Transform}, etc.
*
* We recommend starting from {@link ScryfallCard.Any} to describe generic API responses, and you will need to do type narrowing to access more specific fields.
*
* @example // Type narrowing by layout
* const mysteryCard: ScryfallCard.Any = getCard();
Expand All @@ -45,9 +48,16 @@ type Layout<T extends ScryfallLayout> = Pick<ScryfallCardFields.Core.All, "layou
export namespace ScryfallCard {
/** The abstract root implementation of cards. */
export type AbstractCard = ScryfallObject.Object<ScryfallObject.ObjectType.Card> & ScryfallCardFields.Core.All;
}

type SingleFace = AbstractCard &
Layout<ScryfallLayoutGroup.SingleFaceType> &
export namespace ScryfallCard {
/**
* Any card with a single-faced layout.
*
* Examples: {@link ScryfallLayout.Normal}, {@link ScryfallLayout.Mutate}, {@link ScryfallLayout.Token}.
*/
export type AnySingleFaced = AbstractCard &
Layout<ScryfallLayoutGroup.SingleFacedType> &
ScryfallCardFields.Gameplay.RootProperties &
ScryfallCardFields.Gameplay.CardSpecific &
ScryfallCardFields.Gameplay.CardFaceSpecific &
Expand All @@ -58,95 +68,108 @@ export namespace ScryfallCard {
ScryfallCardFields.Print.CardSideSpecific &
ScryfallCardFields.Print.CardFaceSpecific;

type MultiFace<T extends ScryfallCardFace.AbstractCardFace> = AbstractCard &
Layout<ScryfallLayoutGroup.MultiFaceType> &
ScryfallCardFields.Gameplay.RootProperties &
ScryfallCardFields.Gameplay.CardSpecific &
ScryfallCardFields.Gameplay.CardFaces<T> &
ScryfallCardFields.Print.RootProperties &
ScryfallCardFields.Print.CardSpecific;

type SingleSidedSplit = MultiFace<ScryfallCardFace.Split> &
Layout<ScryfallLayoutGroup.SingleSidedSplitType> &
ScryfallCardFields.Gameplay.CardSideSpecific &
ScryfallCardFields.Print.CardSideSpecific &
ScryfallCardFields.Print.SingleSideOnly;

type DoubleSidedSplit = MultiFace<ScryfallCardFace.DoubleSided> & Layout<ScryfallLayoutGroup.DoubleSidedSplitType>;

type AlwaysOversized = {
oversized: true;
};

/** A card with the Normal layout. */
export type Normal = Layout<ScryfallLayout.Normal> & SingleFace;
export type Normal = AnySingleFaced & Layout<ScryfallLayout.Normal>;

/** A card with the Meld layout. */
export type Meld = Layout<ScryfallLayout.Meld> & SingleFace;
export type Meld = AnySingleFaced & Layout<ScryfallLayout.Meld>;

/** A card with the Leveler layout. */
export type Leveler = Layout<ScryfallLayout.Leveler> & SingleFace;
export type Leveler = AnySingleFaced & Layout<ScryfallLayout.Leveler>;

/** A card with the Class layout. */
export type Class = Layout<ScryfallLayout.Class> & SingleFace;
export type Class = AnySingleFaced & Layout<ScryfallLayout.Class>;

/** A card with the Saga layout. */
export type Saga = Layout<ScryfallLayout.Saga> & SingleFace;
export type Saga = AnySingleFaced & Layout<ScryfallLayout.Saga>;

/** A card with the Mutate layout. */
export type Mutate = Layout<ScryfallLayout.Mutate> & SingleFace;
export type Mutate = AnySingleFaced & Layout<ScryfallLayout.Mutate>;

/** A card with the Prototype layout. */
export type Prototype = Layout<ScryfallLayout.Prototype> & SingleFace;
export type Prototype = AnySingleFaced & Layout<ScryfallLayout.Prototype>;

/** A card with the Battle layout. */
export type Battle = Layout<ScryfallLayout.Battle> & SingleFace;
export type Battle = AnySingleFaced & Layout<ScryfallLayout.Battle>;

/** A card with the Planar layout. */
export type Planar = Layout<ScryfallLayout.Planar> & SingleFace & AlwaysOversized;
export type Planar = AnySingleFaced & Layout<ScryfallLayout.Planar>;

/** A card with the Scheme layout. */
export type Scheme = Layout<ScryfallLayout.Scheme> & SingleFace & AlwaysOversized;
export type Scheme = AnySingleFaced & Layout<ScryfallLayout.Scheme>;

/** A card with the Vanguard layout. */
export type Vanguard = Layout<ScryfallLayout.Vanguard> &
SingleFace &
ScryfallCardFields.Gameplay.VanguardStats &
ScryfallCardFields.Gameplay.NoCombatStats;
export type Vanguard = AnySingleFaced & Layout<ScryfallLayout.Vanguard>;

/** A card with the Token layout. */
export type Token = Layout<ScryfallLayout.Token> & SingleFace;
export type Token = AnySingleFaced & Layout<ScryfallLayout.Token>;

/** A card with the Emblem layout. */
export type Emblem = Layout<ScryfallLayout.Emblem> & SingleFace;
export type Emblem = AnySingleFaced & Layout<ScryfallLayout.Emblem>;

/** A card with the Augment layout. */
export type Augment = Layout<ScryfallLayout.Augment> & SingleFace;
export type Augment = AnySingleFaced & Layout<ScryfallLayout.Augment>;

/** A card with the Host layout. */
export type Host = Layout<ScryfallLayout.Host> & SingleFace;
export type Host = AnySingleFaced & Layout<ScryfallLayout.Host>;
}

export namespace ScryfallCard {
type MultiFace<Face extends ScryfallCardFace.AbstractCardFace> = AbstractCard &
ScryfallCardFields.Gameplay.RootProperties &
ScryfallCardFields.Gameplay.CardSpecific &
ScryfallCardFields.Gameplay.CardFaces<Face> &
ScryfallCardFields.Print.RootProperties &
ScryfallCardFields.Print.CardSpecific;

/**
* Any split layout, either single sided or double sided. These will both have `card_faces`.
*/
export type AnySplit = AnySingleSidedSplit | AnyDoubleSidedSplit;

/**
* Any single-sided split card. These all have `card_faces`, and the faces are both on the front.
*
* Examples: {@link ScryfallLayout.Split}, {@link ScryfallLayout.Flip}, {@link ScryfallLayout.Adventure}.
*/
export type AnySingleSidedSplit = MultiFace<ScryfallCardFace.Split> &
Layout<ScryfallLayoutGroup.SingleSidedSplitType> &
ScryfallCardFields.Gameplay.CardSideSpecific &
ScryfallCardFields.Gameplay.CombatStats &
ScryfallCardFields.Print.CardSideSpecific &
ScryfallCardFields.Print.SingleSideOnly;

/** A card with the Split layout. */
export type Split = Layout<ScryfallLayout.Split> & SingleSidedSplit;
export type Split = AnySingleSidedSplit & Layout<ScryfallLayout.Split>;

/** A card with the Flip layout. */
export type Flip = Layout<ScryfallLayout.Flip> & SingleSidedSplit & ScryfallCardFields.Gameplay.CombatStats;
export type Flip = AnySingleSidedSplit & Layout<ScryfallLayout.Flip>;

/** A card with the Adventure layout. */
export type Adventure = Layout<ScryfallLayout.Adventure> & SingleSidedSplit & ScryfallCardFields.Gameplay.CombatStats;
export type Adventure = AnySingleSidedSplit & Layout<ScryfallLayout.Adventure>;

/**
* Any double-sided split card. These all have `card_faces`, and the faces are on the obverse and reverse of the card.
*
* Examples: {@link ScryfallLayout.Transform}, {@link ScryfallLayout.ModalDfc}, {@link ScryfallLayout.DoubleFacedToken}.
*/
export type AnyDoubleSidedSplit = MultiFace<ScryfallCardFace.DoubleSided> &
Layout<ScryfallLayoutGroup.DoubleSidedSplitType>;

/** A card with the Transform layout. */
export type Transform = Layout<ScryfallLayout.Transform> & DoubleSidedSplit;
export type Transform = AnyDoubleSidedSplit & Layout<ScryfallLayout.Transform>;

/** A card with the ModalDfc layout. */
export type ModalDfc = Layout<ScryfallLayout.ModalDfc> & DoubleSidedSplit;
export type ModalDfc = AnyDoubleSidedSplit & Layout<ScryfallLayout.ModalDfc>;

/** A card with the DoubleFacedToken layout. */
export type DoubleFacedToken = Layout<ScryfallLayout.DoubleFacedToken> & DoubleSidedSplit;
export type DoubleFacedToken = AnyDoubleSidedSplit & Layout<ScryfallLayout.DoubleFacedToken>;

/** A card with the ArtSeries layout. */
export type ArtSeries = Layout<ScryfallLayout.ArtSeries> & DoubleSidedSplit;
export type ArtSeries = AnyDoubleSidedSplit & Layout<ScryfallLayout.ArtSeries>;
}

export namespace ScryfallCard {
/** A card with the ReversibleCard layout. */
export type ReversibleCard = Layout<ScryfallLayout.ReversibleCard> &
Omit<AbstractCard, "oracle_id"> &
Expand All @@ -155,66 +178,20 @@ export namespace ScryfallCard {
ScryfallCardFields.Gameplay.CardFaces<ScryfallCardFace.Reversible> &
ScryfallCardFields.Print.RootProperties &
ScryfallCardFields.Print.CardSpecific;
}

export namespace ScryfallCard {
/**
* A card with an indeterminate layout.
*
* An object of this value may be any card at all.
*
* Since this may be of any layout, common fields are available, but layout-specific fields (e.g. card_faces) will be unavailable until you perform type narrowing on
*/
export type Any =
| Normal
| Meld
| Leveler
| Class
| Saga
| Mutate
| Prototype
| Battle
| Planar
| Scheme
| Vanguard
| Token
| Emblem
| Augment
| Host
| Split
| Flip
| Adventure
| Transform
| ModalDfc
| DoubleFacedToken
| ArtSeries
| ReversibleCard;

/**
* Any card with a single-faced layout. These all have a .
*
* Examples: {@link Normal}, {@link Mutate}, {@link Token}.
*/
export type AnySingleFaced = Any & Layout<ScryfallLayoutGroup.SingleFaceType>;

/**
* Any multi-faced layout, which is any that would have a `card_faces` field.
*
* @see {@link AnySingleSidedSplit} is in this group.
* @see {@link AnyDoubleSidedSplit} is in this group.
* @see {@link ReversibleCard} is in this group.
*/
export type AnyMultiFaced = Any & Layout<ScryfallLayoutGroup.MultiFaceType>;

/**
* Any single-sided split card. These all have `card_faces`, and the faces are both on the front.
*
* Examples: {@link Split}, {@link Flip}, {@link Adventure}.
*/
export type AnySingleSidedSplit = Any & Layout<ScryfallLayoutGroup.SingleSidedSplitType>;
export type Any = AnySingleFaced | AnySingleSidedSplit | AnyDoubleSidedSplit | ReversibleCard;

/**
* Any double-sided split card. These all have `card_faces`, and the faces are on the obverse and reverse of the card.
*
* Examples: {@link Transform}, {@link ModalDfc}, {@link DoubleFacedToken}.
* Any card that is multifaced: either a single or double sided split layout, or a reversible card.
*/
export type AnyDoubleSidedSplit = Any & Layout<ScryfallLayoutGroup.DoubleSidedSplitType>;
export type AnyMultiFaced = AnySingleSidedSplit | AnyDoubleSidedSplit | ReversibleCard;
}
Loading

0 comments on commit 9d653a4

Please sign in to comment.