# Routine (/nqtr/routine)



Initialize [#initialize]

To initialize a `commitment`, create a new instance of the `CommitmentBaseModel` class (or your [custom class](#custom-class)) and add it to the game commitment dictionary when the game is initialized.

<Callout type="info">
  It is recommended to import the instances at project startup, see the `src/main.ts` file.
</Callout>

To create a new instance of `CommitmentBaseModel`, you need the following parameters:

* `id`: A unique identifier (string). Used to reference the `commitment` in the game (must be unique).
* `character`: The character or characters that are in the commitment and so in the room. It can be a single character or an array of characters.
* `props`: An object with the commitment's properties:
  * `name`: The name of the commitment (Optional).
  * `image`: The image of the commitment (Optional). It can be a string with the path to the image or a function that returns a `ContainerChild` (Optional).
  * `icon`: The React icon of the commitment (Optional). It can be a React element or a function that returns a React element (Optional).
  * `onRun`: A function that is called when the commitment is run. It receives two parameters:
    * The `commitment` instance itself.
    * `event`: The event that triggered the run. It is an instance of `OnRunEvent<CommitmentInterface>`, which contains methods to navigate to other routes, jump to labels, and more.
  * `disabled`: Whether the commitment is disabled. It can be a boolean or a function that returns a boolean (optional, default is `false`). If it is disabled, this commitment will not be taken into consideration, so the characters will not be in the room, but will be busy with other commitments.
  * `hidden`: Whether the commitment is hidden. It can be a boolean or a function that returns a boolean (optional, default is `false`). If it is hidden, the commitment will not be displayed in the UI.
  * `priority`: The priority of the commitment. The higher the number, the higher the priority (optional, default is `0`).
  * `executionType`: The execution type of the commitment. It can be "automatic" or "interaction". If it is "automatic", the `onRun` function will be called automatically when the player is in the room. If it is "interaction", the player must interact with the character to run the `onRun` function (optional, default is "interaction").
  * `dateScheduling`: Used to schedule what date it will be added and removed. It is an instance of `DateSchedulingInterface` (Optional).
  * `timeSlot`: The time slot in which the commitment will be active. It is an instance of `TimeSchedulingInterface` (Optional).

<CodeBlockTabs defaultValue="values/routine.ts">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="values/routine.ts">
      values/routine.ts
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="main.ts">
      main.ts
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="values/routine.ts">
    ```ts
    import { CommitmentBaseModel, RegisteredCommitments } from "@drincs/nqtr";
    import { narration } from "@drincs/pixi-vn";
    import { NARRATION_ROUTE } from "../constans";
    import { TALK_SLEEP_LABEL_KEY } from "../labels/variousActionsLabelKeys";
    import { aliceTalkMenuLabel } from "../labels/variousActionsLabels";
    import { alice } from "./characters";

    export const aliceSleep = new CommitmentBaseModel("alice_sleep", alice, {
        priority: 1,
        timeSlot: {
            from: 20,
            to: 10,
        },
        onRun: async (_, event) => {
            await event.navigate(NARRATION_ROUTE);
            await narration.jump(TALK_SLEEP_LABEL_KEY, event);
        },
    });

    export const aliceGoSchool = new CommitmentBaseModel("alice_go_school", alice, {
        timeSlot: {
            from: 8,
            to: 14,
        },
        priority: 2,
    });

    export const aliceSmokes = new CommitmentBaseModel("alice_smokes", alice, {
        timeSlot: {
            from: 10,
            to: 20,
        },
        onRun: async (_, event) => {
            await event.navigate(NARRATION_ROUTE);
            await narration.jump(aliceTalkMenuLabel, event);
        },
    });

    RegisteredCommitments.add([aliceSleep, aliceGoSchool, aliceSmokes]);
    ```
  </CodeBlockTab>

  <CodeBlockTab value="main.ts">
    ```ts
    import "./values/routine";

    // ...
    ```
  </CodeBlockTab>
</CodeBlockTabs>

`RegisteredCommitments.add` is **required** to save the `commitments` in the game.

You can also create a function to load `commitments`. The important thing is that it is called at least once before the `commitments` are used in the game, otherwise they will not be available.

Link [#link]

For an `commitment` to be linked to a `room`, the `room` must be included in the `Commitment` instance. The relationship between a `commitment` and a `room` is one-to-many, meaning that a `rooms` can be linked to multiple `commitments`, but a `commitment` can only be linked to one `room` (a character cannot be in two places at the same time).

There are two ways to do this:

During game initialization [#during-game-initialization]

For a `commitment` to be available to the player, it must be linked to a `room`.

<CodeBlockTabs defaultValue="values/rooms.ts">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="values/rooms.ts">
      values/rooms.ts
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="values/routine.ts">
      values/routine.ts
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="values/rooms.ts">
    ```ts
    import { RegisteredRooms, RoomBaseModel } from "@drincs/nqtr";
    import { mcHome } from "./locations";
    import { aliceSleep } from "./routine";

    export const aliceRoom = new RoomBaseModel("alice_room", mcHome, {
        name: "Alice room",
        background: "location_aliceroom",
        routine: [aliceSleep], // [!code ++]
    });

    RegisteredRooms.add([aliceRoom]);
    ```
  </CodeBlockTab>

  <CodeBlockTab value="values/routine.ts">
    ```ts
    import { CommitmentBaseModel, RegisteredCommitments } from "@drincs/nqtr";
    import { narration } from "@drincs/pixi-vn";
    import { NARRATION_ROUTE } from "../constans";
    import { TALK_SLEEP_LABEL_KEY } from "../labels/variousActionsLabelKeys";
    import { alice } from "./characters";

    export const aliceSleep = new CommitmentBaseModel("alice_sleep", alice, {
        priority: 1,
        timeSlot: {
            from: 20,
            to: 10,
        },
        onRun: async (_, event) => {
            await event.navigate(NARRATION_ROUTE);
            await narration.jump(TALK_SLEEP_LABEL_KEY, event);
        },
    });

    RegisteredCommitments.add([aliceSleep]);
    ```
  </CodeBlockTab>
</CodeBlockTabs>

During the game [#during-the-game]

You can also link a commitment to a room during the game. This feature is useful if you want to temporarily add a commitment. The list of routine added this way will be included in your game saves.

To link a commitment to a room during the game, you can use the `addCommitment` method.

<CodeBlockTabs defaultValue="Typescript">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="Typescript">
      Typescript
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="ink">
      ink
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="values/routine.ts">
      values/routine.ts
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="Typescript">
    ```ts  title="content/labels/start.label.ts" groupId="narrative_language"
    import { newLabel, showImageContainer } from "@drincs/pixi-vn";
    import { aliceSmokes } from "../routine";
    import { terrace } from "../rooms";

    const startLabel = newLabel("start", [
        async () => {
            terrace.addCommitment(aliceSmokes); // [!code focus]
        },
    ]);
    export default startLabel;
    ```
  </CodeBlockTab>

  <CodeBlockTab value="ink">
    ```ink  title="ink/start.ink" groupId="narrative_language"
    == start ==
    // {alice_smokes} is the id of the commitment
    # add routine {alice_smokes} in terrace
    ```
  </CodeBlockTab>

  <CodeBlockTab value="values/routine.ts">
    ```ts
    import { CommitmentBaseModel, RegisteredCommitments } from "@drincs/nqtr";
    import { narration } from "@drincs/pixi-vn";
    import { NARRATION_ROUTE } from "../constans";
    import { aliceTalkMenuLabel } from "../labels/variousActionsLabels";
    import { alice } from "./characters";

    export const aliceSmokes = new CommitmentBaseModel("alice_smokes", alice, {
        timeSlot: {
            from: 10,
            to: 20,
        },
        onRun: async (_, event) => {
            await event.navigate(NARRATION_ROUTE);
            await narration.jump(aliceTalkMenuLabel, event);
        },
    });

    RegisteredCommitments.add([aliceSmokes]);
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Unlink [#unlink]

To detach a `commitment` from a `room`, you must first consider how it was linked.

If it was linked during the game initialization, it cannot be removed. However, you can add another `commitment` with the same character at the same time slot, so the character will be occupied with the new `commitment` and therefore will no longer be present in the previous `room`. By default, priority is given to the `commitment` added during gameplay over the one added during initialization, but this behavior can be modified using the `priority` property.

If it was linked during gameplay, it can be removed using the `removeCommitment` method.

<CodeBlockTabs defaultValue="Typescript">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="Typescript">
      Typescript
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="ink">
      ink
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="values/routine.ts">
      values/routine.ts
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="Typescript">
    ```ts  title="index.ts" groupId="narrative_language"
    import { aliceSmokes } from "../routine";
    import { routine } from "@drincs/nqtr";

    routine.remove(aliceSmokes);
    ```
  </CodeBlockTab>

  <CodeBlockTab value="ink">
    ```ink  title="index.ink" groupId="narrative_language"
    // {alice_smokes} is the id of the commitment
    # remove routine {alice_smokes}
    ```
  </CodeBlockTab>

  <CodeBlockTab value="values/routine.ts">
    ```ts
    import { CommitmentBaseModel, RegisteredCommitments } from "@drincs/nqtr";
    import { narration } from "@drincs/pixi-vn";
    import { NARRATION_ROUTE } from "../constans";
    import { aliceTalkMenuLabel } from "../labels/variousActionsLabels";
    import { alice } from "./characters";
    import { terrace } from "./rooms";

    const aliceSmokes = new CommitmentBaseModel("alice_smokes", alice, terrace, {
        timeSlot: {
            from: 10,
            to: 20,
        },
        onRun: (_, event) => {
            event.navigate(NARRATION_ROUTE);
            narration.jump(aliceTalkMenuLabel, event);
        },
    });

    RegisteredCommitments.add([aliceSmokes]);
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Get [#get]

To get a `commitment` by its `id`, use the `RegisteredCommitments.get` function.

```ts
import { RegisteredCommitments } from "@drincs/nqtr";

const aliceWorks = RegisteredCommitments.get("alice_works");
```

Get all [#get-all]

To get all `commitments`, use the `RegisteredCommitments.values` function.

```ts
import { RegisteredCommitments } from "@drincs/nqtr";

const commitments = RegisteredCommitments.values();
```

Custom class [#custom-class]

<Callout title="Templates" type="info">
  In all templates, the `Commitment` class is already defined in the file `models/nqtr/Commitment.ts`. You can use it directly or modify it to suit your needs.
</Callout>

It is recommended to create your own class `Commitment` that extends `CommitmentStoredClass` and "override" the interface `CommitmentInterface` to add, edit, or remove properties or methods.

For example, if you want to create a class `Commitment`, you must "override" the interface `CommitmentInterface` to use your properties or methods. (See the file `nqtr.d.ts`)

Now you can create a class `Commitment` that extends `CommitmentStoredClass` and implements the `CommitmentInterface`. (For more information on how to create a class in TypeScript, read [the official documentation](https://www.typescriptlang.org/docs/handbook/2/classes.html))

To create a property that stores its value in the game storage, you can create [Getters/Setters](https://www.typescriptlang.org/docs/handbook/2/classes.html#getters--setters) and use the `this.getStorageProperty()` / `this.setStorageProperty()` methods. (See the file `Commitment.ts`)

<CodeBlockTabs defaultValue="models/nqtr/Commitment.ts">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="models/nqtr/Commitment.ts">
      models/nqtr/Commitment.ts
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="nqtr.d.ts">
      nqtr.d.ts
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="models/nqtr/Commitment.ts">
    ```ts
    import {
        ActiveScheduling,
        CommitmentInterface,
        CommitmentStoredClass,
        CommitmentStoredClassProps,
        OnRunAsyncFunction,
        OnRunEvent,
        OnRunProps,
    } from "@drincs/nqtr";
    import { CharacterInterface } from "@drincs/pixi-vn";
    import {
        PixiUIParam,
        PixiUIProp,
        ReactUIParam,
        ReactUIProp,
    } from "./ui-elements";

    export default class Commitment
        extends CommitmentStoredClass
        implements CommitmentInterface
    {
        constructor(
            id: string,
            characters: CharacterInterface | CharacterInterface[],
            props: {
                name?: string;
                image?: PixiUIParam<Commitment>;
                background?: PixiUIParam<Commitment>;
                icon?: ReactUIParam<Commitment>;
                onRun?: OnRunEvent<CommitmentInterface>;
                disabled?: boolean | (() => boolean);
                hidden?: boolean | (() => boolean);
            } & CommitmentStoredClassProps,
        ) {
            characters = Array.isArray(characters) ? characters : [characters];
            super(id, characters, props.onRun, props);
            this.name = props.name || "";
            this._image = props.image;
            this._background = props.background;
            this._icon = props.icon;
            this._defaultDisabled = props.disabled || false;
            this._defaultHidden = props.hidden || false;
        }
        readonly name: string;
        private readonly _image?: PixiUIParam<Commitment>;
        get sprite(): PixiUIProp | undefined {
            const image = this._image;
            if (typeof image === "function") {
                return (runProps: OnRunProps) => image(this, runProps);
            }
            return image;
        }
        private readonly _background?: PixiUIParam<Commitment>;
        get background(): PixiUIProp | undefined {
            const background = this._background;
            if (typeof background === "function") {
                return (runProps: OnRunProps) => background(this, runProps);
            }
            return background;
        }
        private readonly _icon?: ReactUIParam<Commitment>;
        get icon(): ReactUIProp | undefined {
            const icon = this._icon;
            if (typeof icon === "function") {
                return (runProps: OnRunProps) => icon(this, runProps);
            }
            return icon;
        }
        private _defaultDisabled: boolean | (() => boolean) = false;
        get disabled(): boolean {
            let value =
                this.getStorageProperty<boolean>("disabled") ||
                this._defaultDisabled;
            if (typeof value === "function") {
                return value();
            }
            return value;
        }
        set disabled(value: boolean) {
            this.setStorageProperty("disabled", value);
        }
        private _defaultHidden: boolean | (() => boolean) = false;
        get hidden(): boolean {
            let value =
                this.getStorageProperty<boolean>("hidden") || this._defaultHidden;
            if (typeof value === "function") {
                return value();
            }
            return value;
        }
        set hidden(value: boolean) {
            this.setStorageProperty("hidden", value);
        }
        override isActive(options?: ActiveScheduling): boolean {
            if (this.hidden) {
                return false;
            }
            return super.isActive(options);
        }
        override get run(): OnRunAsyncFunction {
            return async (runProps: OnRunProps) => {
                const res = await super.run(runProps);
                await runProps.invalidateInterfaceData();
                return res;
            };
        }
    }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="nqtr.d.ts">
    ```ts
    import { ReactElement } from "react";
    import { ContainerChild } from "@drincs/pixi-vn";

    declare module "@drincs/nqtr" {
        interface CommitmentInterface {
            /**
             * The name of the commitment.
             */
            readonly name: string;
            /**
             * The sprite of the commitment.
             */
            readonly sprite?: PixiUIProp;
            /**
             * The background of the commitment.
             */
            readonly background?: PixiUIProp;
            /**
             * The React icon of the commitment.
             */
            readonly icon?: ReactUIProp;
            /**
             * Whether is disabled.
             */
            disabled: boolean;
            /**
             * Whether is hidden.
             */
            hidden: boolean;
        }
    }
    ```
  </CodeBlockTab>
</CodeBlockTabs>
