diff --git a/README.md b/README.md index a51e81f..161773f 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,68 @@ The [`Modifier`](/lib/modifier/modifier.ts) class allows `GameObject`s to modify The [`Event`](/lib/event/event.ts) class represents the base class for all sorts of events that may happen in the game. Events are stored in a global [`EventLog`](/lib/event/event-log.ts). +## Custom Game Objects + +When creating your own game, you will most likely want to create custom `GameObject`s. All you need to do is create a new class that extends `GameObject` and register it: + +```ts +import { GameObject } from '@satellite-games/orbit'; + +export class Item extends GameObject { + declare name: string; + declare cost: number; + // ... +} + +// This registers your Game Object with Orbit +declare module '@satellite-games/orbit' { + interface Registry { + item: RegistryEntry; + } +} +``` + +Registering your `GameObject` enables type-safety while working with your `GameObject`. It is even possible to achieve full type-safety for all of your `Blueprint` names, which will be the possible values of the `GameObject.name` property. + +```ts +import { GameObject } from '@satellite-games/orbit'; + +// Create a literal type with all possible item names +export type ItemName = 'item.potion' | 'item.pouch'; + +export class Item extends GameObject { + declare name: ItemName; + declare cost: number; + // ... +} + +// This registers your GameObject with Orbit +declare module '@satellite-games/orbit' { + interface Registry { + item: RegistryEntry; + } +} + +// This will further increase type-safety, e.g. creating and maintaining Blueprints +// All of this is type-safe, even the names! +const itemBlueprints: Blueprint = [ + { + name: 'item.pouch', + cost: 200, + }, + { + name: 'item.potion', + cost: 50, + dependencies: { + name: 'item.pouch', + } as Dependency, + }, +]; +``` + +While it can be quite tedious to manually maintain a literal type that contains all possible names, +it's easy to write a script that'll generate those literal types for you based on your master data. + ## Building a package with Orbit If you're building a package that is based on Orbit, you might want consumers to be able to access Orbit's features or types without having to install it separately. To achieve that, simply re-export everything from Orbit in your package. For example, in your main entrypoint file, do: diff --git a/lib/game-object/game-object.ts b/lib/game-object/game-object.ts index 954b3fc..a57492d 100644 --- a/lib/game-object/game-object.ts +++ b/lib/game-object/game-object.ts @@ -4,7 +4,7 @@ import type { ElementType, NumericProperty } from '@/types/private-types'; import { Modifier, applyModifiers } from '@/modifier'; import { Dependency } from '@/dependency/dependency'; import { getGameObjectKey } from './game-object.utils'; -import type { GameObjectKey, GameObjectName, GameObjectRegistry } from './types'; +import type { GameObjectKey, GameObjectName, Registry } from './types'; /** * A game object is an entity in the game world. It is a container for data and functions. @@ -35,7 +35,7 @@ export class GameObject implements GameObject { /** * Any child game objects that are stored on this game object. */ - children: Partial>>; + children: Partial>>; constructor(init: { name: GameObjectName; @@ -43,7 +43,7 @@ export class GameObject implements GameObject { owner?: GameObject | null; modifiers?: Modifier[]; dependencies?: Dependency[]; - children?: Partial>>; + children?: Partial>>; [key: string]: any; }) { // Perform a shallow copy of the initialization object. This also covers diff --git a/lib/game-object/types.ts b/lib/game-object/types.ts index 2682de9..fd52e19 100644 --- a/lib/game-object/types.ts +++ b/lib/game-object/types.ts @@ -31,16 +31,16 @@ export type Saved & Required>; /** - * An entry of `GameObjectRegistry`. Use this type together with `GameObjectRegistry` + * An entry of `Registry`. Use this type together with `Registry` * to register new `GameObject`s. */ -export type GameObjectRegistryEntry = { +export type RegistryEntry = { type: T; names: U[]; }; /** - * The registry of `GameObject`s. Use this interface together with `GameObjectRegistryEntry` + * The registry of `GameObject`s. Use this interface together with `RegistryEntry` * to register new `GameObject`s. * @example * class MyGameObject extends GameObject { @@ -48,19 +48,19 @@ export type GameObjectRegistryEntry = { * } * type MyGameObjectName = 'my-game-object.foo' | 'my-game-object.bar'; * - * interface GameObjectRegistry { - * 'my-game-object': GameObjectRegistryEntry; + * interface Registry { + * 'my-game-object': RegistryEntry; * } */ -export interface GameObjectRegistry {} +export interface Registry {} /** - * The key of `GameObjectRegistry`. Represents the unique key of a `GameObject` class. + * The key of `Registry`. Represents the unique key of a `GameObject` class. */ -export type GameObjectKey = keyof GameObjectRegistry; +export type GameObjectKey = keyof Registry; /** * Represents all possible values for a `GameObject`s `name` property. Usually, this is a union * of all blueprint names of a `GameObject`. */ -export type GameObjectName = GameObjectRegistry[GameObjectKey]['names'][number]; +export type GameObjectName = Registry[GameObjectKey]['names'][number]; diff --git a/tests/internal-mocks/stat.go.ts b/tests/internal-mocks/stat.go.ts index 534ccad..a5de264 100644 --- a/tests/internal-mocks/stat.go.ts +++ b/tests/internal-mocks/stat.go.ts @@ -24,7 +24,7 @@ export const stats: Record> = { }; declare module '@/registry' { - interface GameObjectRegistry { - stat: GameObjectRegistryEntry; + interface Registry { + stat: RegistryEntry; } } diff --git a/tests/internal-mocks/train.go.ts b/tests/internal-mocks/train.go.ts index 6fcc06e..7ac634c 100644 --- a/tests/internal-mocks/train.go.ts +++ b/tests/internal-mocks/train.go.ts @@ -39,7 +39,7 @@ export const trains: Record> = { }; declare module '@/registry' { - interface GameObjectRegistry { - train: GameObjectRegistryEntry; + interface Registry { + train: RegistryEntry; } } diff --git a/tests/internal-mocks/wagon.go.ts b/tests/internal-mocks/wagon.go.ts index 042f961..6999ffd 100644 --- a/tests/internal-mocks/wagon.go.ts +++ b/tests/internal-mocks/wagon.go.ts @@ -43,7 +43,7 @@ export const wagons: Record> = { }; declare module '@/registry' { - interface GameObjectRegistry { - wagon: GameObjectRegistryEntry; + interface Registry { + wagon: RegistryEntry; } }