# Activity (/nqtr/activity)



The `activity` are actions the player can take, and they can be linked to a <DynamicLink href="/nqtr/navigation">navigation element</DynamicLink>. The developer will decide how the player interacts with them.

Initialize [#initialize]

To initialize a `activity`, create a new instance of the `ActivityBaseModel` class (or your [custom class](#custom-class)) and add it to the game activity 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 `ActivityBaseModel`, you need the following parameters:

* `id`: A unique identifier (string). Used to reference the `activity` in the game (must be unique).
* `onRun`: A function that is called when the `activity` is run. It receives two parameters:
  * The `activity` instance itself.
  * `props`: the properties that will be passed to the activity. Its interface corresponds to <DynamicLink href="/nqtr#onrunprops">`OnRunProps`</DynamicLink>.
* `props`: An object with the activity's properties:
  * `name`: The name of the activity (Optional).
  * `timeSlot`: The time slot in which the activity will be active (Optional).
  * `dateScheduling`: Used to schedule what date it will be added and removed (Optional).
  * `disabled`: Whether the activity is disabled. You can also pass a Pixi’VN flag name (optional, default is `false`).
  * `hidden`: Whether the activity is hidden. You can also pass a Pixi’VN flag name (optional, default is `false`).
  * `renderIcon`: The icon of the activity (Optional).

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

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

  <CodeBlockTab value="values/activities.ts">
    ```ts
    import {
        ActivityBaseModel,
        RegisteredActivities,
        timeTracker,
    } from "@drincs/nqtr";
    import { narration } from "@drincs/pixi-vn";
    import { NARRATION_ROUTE } from "../constans";
    import { napLabel, sleepLabel } from "../labels/sleepNapLabels";
    import {
        ORDER_PRODUCT_LABEL_KEY,
        TAKE_KEY_LABEL_KEY,
    } from "../labels/variousActionsLabelKeys";

    export const bed = new ActivityBaseModel(
        "bed",
        async (_, props) => {
            await props.navigate(NARRATION_ROUTE);
            if (timeTracker.nowIsBetween(5, 22)) {
                await narration.jump(napLabel, props);
            } else {
                await narration.jump(sleepLabel, props);
            }
        },
        {
            name: "bed",
        },
    );

    export const orderProduct = new ActivityBaseModel(
        "order_product",
        async (_, props) => {
            await props.navigate(NARRATION_ROUTE);
            await narration.jump(ORDER_PRODUCT_LABEL_KEY, props);
        },
        {
            name: "order_product",
        },
    );

    export const takeProduct = new Activity(
        "take_product",
        async (_, props) => {
            await props.navigate(NARRATION_ROUTE);
            await narration.jump(TAKE_KEY_LABEL_KEY, props);
        },
        {
            name: "take_product",
        },
    );

    RegisteredActivities.add([bed, orderProduct, takeProduct]);
    ```
  </CodeBlockTab>

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

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

`RegisteredActivities.add` is **required** to save the `activities` in the game.

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

Link [#link]

For an `activity` to be available to the player, it must be linked to a <DynamicLink href="/nqtr/navigation">navigation element</DynamicLink>. The relationship between an `activity` and a navigation element is many-to-many, meaning that an `activity` can be linked to multiple navigation elements and a navigation element can have multiple `activities` associated with it.

There are two ways to do this:

During game initialization [#during-game-initialization]

When the game starts, a list of related activities will be defined. This list will not be saved when the game is saved. Therefore, the developer can modify this list from one version of the game to another without having to worry about migrations for older versions.

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

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

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

    export const mcRoom = new RoomBaseModel("mc_room", mcHome, {
        name: "MC room",
        image: "location_myroom",
        activities: [bed], // [!code ++]
    });

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

  <CodeBlockTab value="values/activities.ts">
    ```ts
    import {
        ActivityBaseModel,
        RegisteredActivities,
        timeTracker,
    } from "@drincs/nqtr";
    import { narration } from "@drincs/pixi-vn";
    import { napLabel, sleepLabel } from "../labels/sleepNapLabels";

    export const bed = new ActivityBaseModel(
        "bed",
        async (_, props) => {
            if (timeTracker.nowIsBetween(5, 22)) {
                await narration.jump(napLabel, props);
            } else {
                await narration.jump(sleepLabel, props);
            }
        },
        {
            name: "bed",
        },
    );

    RegisteredActivities.add([bed]);
    ```
  </CodeBlockTab>
</CodeBlockTabs>

During the game [#during-the-game]

You can also link an activity to a navigation element during the game. This feature is useful if you want to temporarily add an activity. The list of activities added this way will be included in your game saves.

To link an activity to a navigation element during the game, you can use the `addActivity` method.

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

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

    <CodeBlockTabsTrigger value="values/activities.ts">
      values/activities.ts
    </CodeBlockTabsTrigger>

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

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

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

  <CodeBlockTab value="ink">
    ```ink  title="ink/start.ink" groupId="narrative_language"
    == start ==
    // {order_product} is the id of the activity
    # add activity {order_product} in mc_room
    ```
  </CodeBlockTab>

  <CodeBlockTab value="values/activities.ts">
    ```ts
    import { ActivityBaseModel, RegisteredActivities } from "@drincs/nqtr";
    import { narration } from "@drincs/pixi-vn";
    import { orderProductLabel } from "../labels/variousActionsLabels";

    export const orderProduct = new ActivityBaseModel(
        "order_product",
        async (_, props) => await narration.jump(orderProductLabel, props),
        {
            name: "order_product",
        },
    );

    RegisteredActivities.add([orderProduct]);
    ```
  </CodeBlockTab>

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

    export const mcRoom = new RoomBaseModel("mc_room", mcHome, {
        name: "MC room",
        image: "location_myroom",
    });

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

Unlink [#unlink]

To unlink an activity from a navigation element, you can use the `removeActivity` method.

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

    <CodeBlockTabsTrigger value="Typescript">
      Typescript
    </CodeBlockTabsTrigger>

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

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

  <CodeBlockTab value="Example">
    ```ts
    import { orderProduct } from "../activities";
    import { mcRoom } from "../rooms";

    mcRoom.removeActivity(orderProduct);
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Typescript">
    ```ts  title="index.ts" groupId="narrative_language"
    import { ActivityBaseModel, RegisteredActivities } from "@drincs/nqtr";
    import { narration } from "@drincs/pixi-vn";
    import { orderProductLabel } from "../labels/variousActionsLabels";

    export const orderProduct = new ActivityBaseModel(
        "order_product",
        async (_, props) => await narration.jump(orderProductLabel, props),
        {
            name: "order_product",
        },
    );

    RegisteredActivities.add([orderProduct]);
    ```
  </CodeBlockTab>

  <CodeBlockTab value="ink">
    ```ink  title="index.ink" groupId="narrative_language"
    // {order_product} is the id of the activity
    # remove activity {order_product} from mc_room
    ```
  </CodeBlockTab>

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

    export const mcRoom = new RoomBaseModel("mc_room", mcHome, {
        name: "MC room",
        image: "location_myroom",
    });

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

Get [#get]

To get a `activity` by its `id`, use the `RegisteredActivities.get` function.

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

const orderProduct = RegisteredActivities.get("order_product");
```

Get all [#get-all]

To get all `activities`, use the `RegisteredActivities.values` function.

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

const activities = RegisteredActivities.values();
```

Custom class [#custom-class]

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

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

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

Now you can create a class `Activity` that extends `ActivityStoredClass` and implements the `ActivityInterface`. (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 `Activity.ts`)

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

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

  <CodeBlockTab value="models/nqtr/Activity.ts">
    ```ts
    import {
        ActiveScheduling,
        ActivityInterface,
        ActivityStoredClass,
        ActivityStoredClassProps,
        OnRunEvent,
        OnRunProps,
    } from "@drincs/nqtr";
    import {
        PixiUIParam,
        PixiUIProp,
        ReactUIParam,
        ReactUIProp,
    } from "./ui-elements";
    import { OnRunAsyncFunction } from "@drincs/nqtr";

    export default class Activity
        extends ActivityStoredClass
        implements ActivityInterface
    {
        constructor(
            id: string,
            onRun: OnRunEvent<ActivityInterface>,
            props: {
                name?: string;
                sprite?: PixiUIParam<Activity>;
                icon?: ReactUIParam<Activity>;
                disabled?: boolean | (() => boolean);
                hidden?: boolean | (() => boolean);
            } & ActivityStoredClassProps,
        ) {
            super(id, onRun, props);
            this.name = props.name || "";
            this._sprite = props.sprite;
            this._icon = props.icon;
            this._defaultDisabled = props.disabled || false;
            this._defaultHidden = props.hidden || false;
        }
        readonly name: string;
        private readonly _sprite?: PixiUIParam<Activity>;
        get sprite(): PixiUIProp | undefined {
            const sprite = this._sprite;
            if (typeof sprite === "function") {
                return (runProps: OnRunProps) => sprite(this, runProps);
            }
            return sprite;
        }
        private readonly _icon?: ReactUIParam<Activity>;
        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 { StepLabelProps } from "@drincs/pixi-vn";
    import { PixiUIProp, ReactUIProp } from "./models/nqtr/ui-elements";

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