drincs-productions/pixi-vn/characters

Claude Code and agent skill by drincs-productions/pixi-vn

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when defining visual novel characters for a @drincs/pixi-vn game — creating a character with a name, color, icon and other props, registering it so it can be looked up by id, and referencing it from dialogue/narration. Load this before writing any `CharacterBaseModel` or `RegisteredCharacters` code.

Install

npx vskill@latest install drincs-productions/pixi-vn/characters

SKILL.md

Pixi'VN Characters

When to use this skill

Use this skill whenever a task involves creating the characters that appear in a Pixi'VN game (name, surname, age, color, icon, and any custom fields), storing them so the engine and narration code can find them by id, or hooking a character into a narration.dialogue line. It does not cover how dialogue/text itself is displayed (see the narration skill) or how variables are generally saved (see the storage skill).

ts
import { CharacterBaseModel, RegisteredCharacters } from "@drincs/pixi-vn";

(Also available from the narrower @drincs/pixi-vn/characters subpath — see pixi-vn for when to prefer that.)

Full reference: https://pixi-vn.com/start/character

1. Defining a character

CharacterBaseModel is the built-in character class. Construct it with a unique string id and a props object (name, surname, age, icon, color — all optional):

ts
import { CharacterBaseModel } from "@drincs/pixi-vn";
export const liam = new CharacterBaseModel("liam", {
name: "Liam",
surname: "Smith",
age: 25,
icon: "https://example.com/liam.png",
color: "#9e2e12",
});
export const emma = new CharacterBaseModel("emma", {
name: "Emma",
surname: "Johnson",
age: 23,
icon: "https://example.com/emma.png",
color: "#c23b7f",
});

The id is the value used everywhere else in the game (dialogue, lookups, history) to reference this character, so it must be unique and stable across saves.

name/surname/age are actually get/set accessors backed by the game's storage (see CharacterStoredClass, which CharacterBaseModel extends, and the stored classes doc): the value passed in the constructor becomes the _default_, but setting liam.name = "..." at runtime persists an override in storage that survives save/load and takes priority over the default. icon and color are plain readonly fields, not stored/overridable at runtime.

ts
import { liam } from "@/content/characters";
console.log(liam.name); // "Liam"
liam.name = "Liam Smith";
console.log(liam.name); // "Liam Smith" — now persisted in game storage

If a character's id is ever changed between game versions, the engine does not migrate the storage data from the old id to the new one — treat ids as permanent once shipped.

Custom character classes with extra fields

If you need fields beyond name/surname/age/icon/color (e.g. a sprite id or a relationship stat), this is the recommended, docs-endorsed approach (project templates already ship it as models/Character.ts):

  1. Create a class Character extends CharacterStoredClass implements CharacterInterface.
  2. Augment the (otherwise empty) CharacterInterface in a pixi-vn.d.ts file

via declare module "@drincs/pixi-vn" so every CharacterInterface consumer in the project — including RegisteredCharacters.get() — sees the richer, project-specific type.

  1. For any field that must persist across saves, back it with a

getter/setter that calls this.getStorageProperty() / this.setStorageProperty() (inherited from CharacterStoredClass), the same pattern CharacterBaseModel uses internally for name/surname/age.

This is exactly what the official pixi-vn-react-template (the "TS narration

  • React" project npm create pixi-vn@latest scaffolds) ships as

src/models/Character.ts — stored getters/setters for name, surname and age, and plain (non-stored) fields for icon/color:

ts
// models/Character.ts
import { type CharacterInterface, CharacterStoredClass } from "@drincs/pixi-vn";
export default class Character
extends CharacterStoredClass
implements CharacterInterface
{
constructor(
id: string | { id: string; emotion: string },
props: CharacterProps,
) {
super(
typeof id === "string" ? id : id.id,
typeof id === "string" ? "" : id.emotion,
);
this.defaultName = props.name;
this.defaultSurname = props.surname;
this.defaultAge = props.age;
this.icon = props.icon;
this.color = props.color;
}
// stored — persists across save/load, same pattern as CharacterBaseModel
private defaultName?: string;
get name(): string {
return (
this.getStorageProperty<string>("name") || this.defaultName || this.id
);
}
set name(value: string | undefined) {
this.setStorageProperty<string>("name", value);
}
private defaultSurname?: string;
get surname(): string | undefined {
return this.getStorageProperty<string>("surname") || this.defaultSurname;
}
set surname(value: string | undefined) {
this.setStorageProperty<string>("surname", value);
}
private defaultAge?: number;
get age(): number | undefined {
return this.getStorageProperty<number>("age") || this.defaultAge;
}
set age(value: number | undefined) {
this.setStorageProperty<number>("age", value);
}
// not stored
readonly icon?: string;
readonly color?: string;
}
interface CharacterProps {
name?: string;
surname?: string;
age?: number;
icon?: string;
color?: string;
}
ts
// pixi-vn.d.ts
declare module "@drincs/pixi-vn" {
interface CharacterInterface {
name: string;
surname?: string;
age?: number;
readonly icon?: string;
readonly color?: string;
}
}

2. Registering and looking up characters

Characters are looked up at runtime by id through the RegisteredCharacters registry (a namespace, not a decorator despite its file name). Calling RegisteredCharacters.add(...) is required — a CharacterBaseModel (or custom class) instance that is only exported/constructed but never passed to add will not be findable by id and will not work in dialogue lookups after a save/load. Register every character you define once, at module load time (recommended: import them all at project startup), then reference it by id elsewhere:

ts
import { RegisteredCharacters } from "@drincs/pixi-vn";
RegisteredCharacters.add(liam, emma);
// or with arrays:
RegisteredCharacters.add([liam, emma]);

Other RegisteredCharacters functions:

  • RegisteredCharacters.get(id) — returns the character (or undefined,

logging a warning if it was never registered).

  • RegisteredCharacters.has(id) — checks whether an id is registered.
  • RegisteredCharacters.values() — returns all registered characters.
  • RegisteredCharacters.keys() — returns all registered ids.
  • RegisteredCharacters.clear() — removes all registered characters (mainly

useful in tests).

ts
const liam = RegisteredCharacters.get("liam");

Re-adding an id that's already registered overwrites the previous character (a log message notes this), so registration order matters if two files define the same id.

Real-world project convention (official React template)

The official pixi-vn-react-template (what npm create pixi-vn@latest scaffolds for the "TS narration + React" setup) defines its characters as export const instances in src/content/characters.ts, then registers all of them with a single RegisteredCharacters.add([...]) call at module scope:

ts
// src/content/characters.ts
import Character from "@/models/Character";
import { RegisteredCharacters } from "@drincs/pixi-vn";
export const mc = new Character("mc", { name: "Me" });
export const james = new Character("james", {
name: "James",
color: "#0084ac",
});
export const steph = new Character("steph", {
name: "Steph",
color: "#ac5900",
});
export const sly = new Character("sly", { name: "Sly", color: "#6d00ac" });
RegisteredCharacters.add([mc, james, steph, sly]);

This works with no manual import list to maintain because src/content/index.ts uses import.meta.glob to import every file under content/ for its side effects at app startup — so the module-scope RegisteredCharacters.add(...) call above always runs. That means you never need to remember to import each new character file yourself (dropping it anywhere under content/ is enough), but conversely, a character defined (and .added) in a file _outside_ content/ that nothing else imports never gets its add call executed, and stays silently unregistered.

This is the official template's convention, not a hard requirement of the library — RegisteredCharacters.add can be called from any file, as long as it's guaranteed to run before the character is looked up. Projects without this glob-import setup must import each character file explicitly instead (e.g. from a top-level content/index.ts or the app's entry point).

Typed ids (optional)

Character ids are typed as CharacterIdType, which is string by default. If a project augments the empty PixivnCharacterIds interface (typically auto-generated by the vitePluginPixivn Vite plugin's typeFilePath option), CharacterIdType narrows to a union of known ids and typos become compile errors. Don't assume this augmentation exists unless you see a generated pixi-vn.gen.d.ts/similar file in the project — if not, treat ids as plain strings.

3. Emotions

A character id may optionally be paired with an emotion by passing an object instead of a plain string as the constructor's first argument:

ts
export const alice = new CharacterBaseModel("alice", {
name: "Alice",
icon: "https://example.com/alice.png",
color: "#9e2e12",
});
export const angryAlice = new CharacterBaseModel(
{ id: "alice", emotion: "angry" },
{ icon: "https://example.com/angryAlice.png" },
);
RegisteredCharacters.add([alice, angryAlice]);

Internally this stores the character's name/surname/age under a separate storage key ("<id>@<emotion>") while still falling back to the base id's stored/default values if an emotion-specific value isn't set — the base id and its emotion variants share the same underlying character data except where the emotion variant overrides it:

ts
console.log(alice.name); // "Alice"
alice.name = "Eleonora";
console.log(alice.name); // "Eleonora"
console.log(angryAlice.name); // "Eleonora" — falls back to the base id's value
angryAlice.name = "Angry Eleonora";
console.log(alice.name); // "Eleonora" — unaffected
console.log(angryAlice.name); // "Angry Eleonora" — its own override now set

Use this only if you need per-emotion overrides of a character's stored fields; for swapping the character's on-screen sprite/image per emotion, that's driven by the canvas/image APIs, not this module.

4. Using a character in dialogue

Once registered, pass the character instance (or its id string) as the character field of a dialogue, e.g. inside a label step:

ts
narration.dialogue = {
character: liam,
text: "Which test do you want to perform?",
};
// or by id:
narration.dialogue = { character: "liam", text: "..." };

Which form to prefer depends on whether the project's @drincs/pixi-vn/vite plugin generates typed character ids (the typeFilePath option covered in pixi-vn, which narrows CharacterIdType from plain string to a union of the actually-registered ids):

  • Without it, prefer passing the instance (character: liam) — a bare string id has no

compile-time typo protection, while the instance is a real variable reference the compiler already checks.

  • With it, prefer the string-id form (character: "liam") — it now gets the same

compile-time safety (a typo like "liem" fails to typecheck against the generated CharacterIdType union) without needing to import the character module wherever a dialogue line is written, which keeps label files decoupled from character-definition files.

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
137
Repository forks
8
7-day trend score
25

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/drincs-productions/pixi-vn/characters/badge)](https://verified-skill.com/skills/drincs-productions/pixi-vn/characters)

Something wrong with this skill or its page? Tell us.