What the room holds
phantom.aj.entitiesThe screen and sound
The room's players, NPCs and objects as things to tag, highlight and click.
Scripts and plugins
A script has all of it, as phantom.aj.entities, and so does a plugin, from ctx.phantom(): the SDK runs in the plugin's own frame. Each call is marked with what it needs: game:read for what the area knows and hears, which "game": "read" in the manifest grants, and game:act for doing things in it as the player, which needs "game": "act" (see Permissions).
Overview
| Member | What it does | Plugins |
|---|---|---|
each() | Does something to every player or NPC in the room, now and as each arrives, room after room, until the handle is called: that undoes every highlight, hide, tag and click listener made through the entity it was given, on every one. | game:act |
npcs() | The NPCs in the room (adventure and quest characters), held by handle: release each when done. | game:read |
objects() | The objects the player can use (a shop's door, a game's sign), held by handle: release each when done. | game:read |
player() | One player by user id, or null when they are not in the room. | game:read |
players() | The players in the room, the player too, as they are now; empty with no room. | game:read |
Methods
each
each(kind: "player", apply: (entity: PlayerEntity) => unknown, options?: EachOptions<AvatarWorldView> | undefined) => Promise<UiRule>
each(kind: "npc", apply: (entity: NpcEntity) => unknown, options?: EachOptions<QuestActor> | undefined) => Promise<UiRule>Does something to every player or NPC in the room, now and as each arrives, room after room, until the handle is called: that undoes every highlight, hide, tag and click listener made through the entity it was given, on every one. Built on phantom.flash.ui.rule, so the game itself looks for new ones. rule.refresh() asks unless again for each.
Parameters
| Name | Type | |
|---|---|---|
kind | "player" | |
apply | (entity: PlayerEntity) => unknown | |
options optional | EachOptions<AvatarWorldView> | undefined |
Returns
Promise<UiRule>
Types
EachOptions · UiRule · PlayerEntity · NpcEntity
Example
const rule = await phantom.aj.entities.each('npc', (npc) => npc.highlight({ pulse: true }));npcs
npcs() => Promise<NpcEntity[]>The NPCs in the room (adventure and quest characters), held by handle: release each when done. Empty in rooms without any.
Returns
Promise<NpcEntity[]>
Types
objects
objects() => Promise<ObjectEntity[]>The objects the player can use (a shop's door, a game's sign), held by handle: release each when done. Their clicks are tested on the stage, since room art takes no mouse events. REFUSED when the client does not reach its private volume list.
Returns
Promise<ObjectEntity[]>
Types
player
player(userId: string | number) => Promise<PlayerEntity | null>One player by user id, or null when they are not in the room.
Parameters
| Name | Type | |
|---|---|---|
userId | string | number |
Returns
Promise<PlayerEntity \| null>
Types
players
players() => Promise<PlayerEntity[]>The players in the room, the player too, as they are now; empty with no room. Holds nothing: each place is a path. Players who arrive later are not in it: each follows them.
Returns
Promise<PlayerEntity[]>
Types
PlayerEntity · EntityKind · TypedFlash · FlashRead · FlashWrite · HoldOptions · FlashArgumentLike · FlashPlace · FlashValue · FlashArgument · FlashData · AnyFlash · FlashMethod · FlashConstructor · EntityKeeper · HighlightOptions · ActOptions · TagOptions · OverlayNode · OverlayAlign · OverlayDrag · TextSpan · OverlayOptions
Types
The types the calls above take and give, with every field. Import any of them in a plugin from @phantomclient/plugin/sdk, or use them as they are in a script.
PlayerEntity
| Field | Type | |
|---|---|---|
game | GameContext | |
kind | EntityKind | |
id | string | The player's user id, the NPC's actor id, or the volume's name and object index (door#0). |
name | string | null | What the client calls it, when it says. |
place | TypedFlash<AvatarWorldView> | Its display object, typed as its class. |
tagY | number | Where a tag goes by default, in the object's own coordinates. |
handle | FlashHandle | null | The handle it is held by, if any, which release lets go of. |
keeper | EntityKeeper | null | |
highlight | (options?: HighlightOptions, act?: ActOptions) => Promise<() => Promise<void>> | Glows (by default), pulses or recolours the entity until the handle is called. |
hide | (act?: ActOptions) => Promise<() => Promise<void>> | Hides the entity until the handle is called, whatever the client does meanwhile. |
tag | (text: string, options?: TagOptions) => Promise<OverlayNode> | A label that follows the entity, above it by default. Calling the node removes it. |
onClick | (listener: (entity: PlayerEntity) => void) => () => void | Hears clicks on the entity. Players and NPCs are listened to themselves; room objects take no mouse events of their own in the client, so a press on the stage is tested against them (hitTestPoint). |
release | () => Promise<void> | Lets go of the object in Flash, for entities held by handle. |
kept | (stop: () => Promise<void>) => () => Promise<void> | |
describe | () => string |
EntityKind
type EntityKind = 'player' | 'npc' | 'object'TypedFlash
A place in Flash whose declared type is T.
type TypedFlash<T> = FlashPlace<T> &
FlashConstructor<T> &
(unknown extends T
? unknown
: {
readonly [Key in keyof T as Key extends `$${string}` ? never : Key]: T[Key] extends (
...args: infer Args
) => infer Result
? FlashMethod<Args, Result>
: TypedFlash<T[Key]>;
})FlashRead
What reading a place of type T resolves with.
type FlashRead<T> = unknown extends T
? FlashData
: T extends Primitive
? T
: T extends readonly (infer Item)[]
? FlashRead<Item>[]
: T extends (...args: never[]) => unknown
? TypedFlash<unknown>
: TypedFlash<T>FlashWrite
What may be given where Flash expects T.
type FlashWrite<T> = unknown extends T
? FlashArgumentLike
: T extends Primitive
? T
: T extends readonly (infer Item)[]
? FlashWrite<Item>[]
: T extends (...args: never[]) => unknown
? ((...args: FlashData[]) => void) | FlashCallback
: TypedFlash<T> | FlashHandle | FlashArgumentLikeHoldOptions
How a hold behaves.
| Field | Type | |
|---|---|---|
everyFrames optional | number | undefined | Write on every nth frame. Every frame by default, which is what a value the client resets needs. |
when optional | "enter" | "exit" | undefined | Which half of the frame to write in. 'enter' by default, before the frame is read. Use 'exit' for a member the game writes every frame of its own, such as a thing it pins to the pointer. Two objects' enter-frame listeners have no order between them, so an ordinary hold on one of those stands only on the frames the loader happens to run second, and the value flickers or never takes at all. Exit-frame work runs after every enter-frame listener, so what is held is what is drawn and what the next frame reads. |
while optional | HookWhen | undefined | Only while this holds, in the same rules as a hook's when, read between calls. |
once optional | boolean | undefined | Write once, on the first frame the condition holds, then stop. |
restore optional | boolean | undefined | Put back what was there before the first write when the hold ends. On by default. |
onFailed optional | ((error: string) => void) | undefined | Called if the game turns the hold off because a write failed. |
FlashArgumentLike
Anything the bridge carries, typed places included.
type FlashArgumentLike = FlashArgument | TypedFlash<unknown> | FlashArgumentLike[] | { [key: string]: FlashArgumentLike }FlashPlace
The operations every place has, whatever its type.
| Field | Type | |
|---|---|---|
$get | () => Promise<FlashRead<T>> | Reads the value here: plain data as it is, an object as a typed place held by handle. |
$set | (value: FlashWrite<T>) => Promise<void> | Writes the value here, once. The client may put its own back a frame later: a member it sets every frame, or on every redraw, or an object it builds again for the next room. For a change that has to stay, use $keep. |
$keep | (value: FlashWrite<T>, options?: HoldOptions | undefined) => Promise<() => Promise<void>> | Writes the value here and keeps it here, until the function it gives back is called, which puts back what was there. It is $set for a change that has to last. The game writes it itself, every frame, so a value the client puts back is overwritten before Flash draws it. The place is followed rather than pinned: nothing is lost when the object is not there yet (it is written as soon as it is) or when the client builds it again (the new one is written too). It is phantom.flash.hold on this place, and takes the same options. ts const hud = phantom.flash.use('gui.GuiManager').mainHud; const faded = await hud.alpha.$keep(0.5); |
$call | (...args: FlashArgumentLike[]) => Promise<FlashData> | Calls what is here, whatever the declarations say is at this place. A place is callable in its own right (avatars.createAvatar(id, x, y, name)), which is how to write a call the declarations describe: the arguments and the answer are both checked. This is the same call for the places they cannot describe, and it saves casting one to reach it: - a class whose instance side declares nothing, because every member of it is static (gui.EmoticonUtility), so $as and a hook's self give a place with no members; - an object the client typed as Object or *, or one reached with $at; - a member holding a function, such as a callback the client keeps in a field. It answers FlashData, since there is no declared return to read it as. ts const emotes = phantom.flash.use('gui.EmoticonUtility'); console.log(await emotes.stringForId(3)); console.log(await emotes.$at('stringForId').$call(3)); |
$listen | (event: string, listener: (event: FlashData, keep: <Kept>(value: Kept) => Kept) => void, options?: { read?: readonly (readonly string[])[] | undefined; capture?: boolean | undefined; } | undefined) => Promise<() => Promise<void>> | Listens for an event on the object here. Resolves with what stops listening. Without read, the event is its readable members, objects among them as typed places held by handle, and every one of them is released once the listener returns, except those passed to keep before then (release those with $release when done). read names the paths to read as the event is dispatched instead: the event is then { className, type, read } with the values in that order, and nothing is held. A listener that ignores the event passes read: []. capture listens on the way down. Both are FlashBridge.listen's options. ts await stage.$listen('mouseMove', (event) => console.log((event as { read: number[] }).read), { read: [['stageX'], ['stageY']], }); |
$describe | () => Promise<FlashValue> | The public members of what is here, as Flash's describeType lists them. |
$at | (...path: string[]) => AnyFlash | Further along, by names the types do not know: untyped from here on. |
$as | { <Name extends keyof ClientInstances & string>(name: Name): TypedFlash<ClientInstances[Name]>; (name: string): AnyFlash; } | The same place, read as the client's name from here on. Nothing is sent and nothing is checked in the game: it says what the object here is, so the editor completes and checks its members again after a step the declarations could not follow ($at, a hook's capture, anything the client typed as Object or *). Naming the wrong class is not caught here; the read or call that follows is what fails. ts const grid = phantom.flash .use('gui.AvatarEditor') .$at('_colorTableColor1') .$as('gui.ColorTable'); console.log(await grid._numCols.$get(), 'by', await grid._numRows.$get()); |
$release | () => Promise<void> | Releases the object this place is held by, when it is one; nothing otherwise. |
$reference | FlashReference | The untyped reference underneath. |
FlashValue
A value on the TypeScript side: plain data, undefined, or a handle to an object in Flash.
type FlashValue = | null
| undefined
| boolean
| number
| string
| FlashValue[]
| { [key: string]: FlashValue }
| FlashHandleFlashArgument
What may be passed to Flash: a value, a function Flash may call, or an object to make there.
type FlashArgument = | FlashValue
| FlashArgument[]
| { [key: string]: FlashArgument }
| ((...args: FlashValue[]) => void)
| FlashNewFlashData
Plain data read from Flash, with objects that live there as typed places.
type FlashData = | null
| undefined
| boolean
| number
| string
| FlashData[]
| { [key: string]: FlashData }
| TypedFlash<unknown>AnyFlash
A place whose class the declarations do not describe: phantom.flash.use('a.Class') for a class the client has no declaration for, $at, $as with a name, or anything read back from one.
Every member is another place of the same kind, and every place can be called, which is what reflection allows and what a place does at run time. Nothing is checked, because there is nothing to check it against: a name the game does not have fails when it is used, not here. then is left out, so awaiting a place is still the mistake it always was.
type AnyFlash = FlashPlace<unknown> &
FlashMethod<unknown[], unknown> & {
readonly [member: string]: AnyFlash;
} & { then?: undefined }FlashMethod
A method of the client, called with checked arguments.
type FlashMethod<Args extends readonly unknown[], Result> = (
...args: { [Index in keyof Args]: FlashWrite<Args[Index]> }
) => Promise<FlashRead<Result>>FlashConstructor
A class's constructor, from its declared $new.
type FlashConstructor<T> = T extends { $new(...args: infer Args): infer Instance }
? { $new: FlashMethod<Args, Instance> }
: unknownEntityKeeper
Where an entity's own changes are kept, so they are undone with whatever made it: a rule undoes them when the rule stops or the object goes.
type EntityKeeper = (undo: () => Promise<unknown>) => voidHighlightOptions
Highlights and colour changes, as scripts describe them, made into the loader's HighlightEffect.
const off = await phantom.flash.highlight(view, { color: 0x8b5cf6, strength: 2, pulse: true });
const normal = await phantom.flash.recolor({ saturation: 0 });| Field | Type | |
|---|---|---|
color optional | number | undefined | The glow's colour. A glow is drawn unless glow is false or another effect is asked for alone. |
strength optional | number | undefined | How strong the glow is: 2 by default. |
blur optional | number | undefined | How far the glow spreads, in pixels: 8 by default. |
alpha optional | number | undefined | |
inner optional | boolean | undefined | Glow inside the object's edges rather than around them. |
knockout optional | boolean | undefined | Only the glow, without the object. |
glow optional | boolean | undefined | |
pulse optional | boolean | { frames?: number | undefined; from?: number | undefined; to?: number | undefined; } | undefined | Fade in and out: true for a gentle pulse (from 60% to full strength and back every two and a half seconds), or how: frames for one cycle at the game's 24 a second, from and to as fractions of the glow. |
recolor optional | RecolorOptions | undefined | A colour change on the object, as recolor takes it. |
hide optional | boolean | undefined | Hide the object while highlighted. |
ActOptions
How long something that acts may take, and what may end it first.
| Field | Type | |
|---|---|---|
timeoutMs optional | number | undefined | 10 seconds by default. |
signal optional | AbortSignal | undefined |
TagOptions
type TagOptions = OverlayOptions & {
/** Where on the entity the tag sits, in its own coordinates: above the head by default. */
anchorX?: number;
anchorY?: number;
}OverlayNode
A drawn node. Calling it removes it, with everything drawn in it.
type OverlayNode = (() => Promise<void>) & {
readonly node: number;
readonly type: OverlaySpec['type'];
/** Changes some of its fields; `null` resets one. */
update(changes: OverlayOptions): Promise<void>;
show(): Promise<void>;
hide(): Promise<void>;
remove(): Promise<void>;
/**
* Where it is now and how big it is, children and all, in the coordinates its own `x` and `y` are
* in. For placing the next thing beside it without knowing how wide its text came out.
*/
bounds(): Promise<OverlayBounds>;
/**
* Hears a mouse event on it. While it listens, the mouse does not reach the game through it, apart
* from `hover`, which watches the mouse go by rather than taking it.
*/
on(
event: 'click' | 'down' | 'up' | 'over' | 'out' | 'hover',
listener: (position: Position) => void
): () => Promise<void>;
/** Hears the wheel turned over it, with how far, in lines: up is positive. */
on(
event: 'wheel',
listener: (turn: { x: number; y: number; delta: number }) => void
): () => Promise<void>;
/** Hears where a dragged node (`drag`) was let go, in its parent's coordinates. */
on(event: 'move', listener: (position: Position) => void): () => Promise<void>;
/** Hears a click on a span of its rich text with a link, with the link's value. */
on(event: 'link', listener: (value: string) => void): () => Promise<void>;
/** Hears that its image did not load. */
onFailed(listener: (message: string) => void): () => void;
/**
…OverlayAlign
Where a node sits in the box it is drawn in: the stage for a node of the overlay's own, the content of the group or panel it is in, or the client object it replaces. The node's x and y are then an offset from that point, and the loader puts it there again whenever the box changes size, so a node in the middle stays in the middle when the window is resized.
| Field | Type | |
|---|---|---|
x optional | "left" | "right" | "center" | undefined | left by default: the node's left edge, its middle, or its right edge. |
y optional | "top" | "middle" | "bottom" | undefined | top by default. |
OverlayDrag
A node the player moves with the mouse. Flash follows the mouse itself, so nothing crosses the bridge while the drag runs: the node posts move with where it ended up, in its parent's coordinates, and live posts one for each step of the drag as well.
| Field | Type | |
|---|---|---|
axis optional | OverlayDragAxis | undefined | both by default. |
left optional | number | undefined | Kept inside these, in its parent's coordinates; each may be left out. |
top optional | number | undefined | |
right optional | number | undefined | |
bottom optional | number | undefined | |
step optional | number | undefined | Moved in steps of this many pixels, so a slider can hold whole values. |
live optional | boolean | undefined | Post move for every step of the drag, not only where it ended. |
TextSpan
One run of rich text. Its text is set as text, never read as HTML, and its look is applied as a text format over its range; link makes the run clickable, posting a link event with the value.
| Field | Type | |
|---|---|---|
text | string | |
color optional | number | undefined | |
bold optional | boolean | undefined | |
italic optional | boolean | undefined | |
underline optional | boolean | undefined | |
size optional | number | undefined | |
font optional | string | undefined | |
link optional | string | undefined |
OverlayOptions
A node, as scripts describe it. Colours may be numbers; the anchor may be any place.
| Field | Type | |
|---|---|---|
parent optional | OverlayNode | null | undefined | The group or panel it is drawn in. |
host optional | OverlayPlaceOptions | null | undefined | A container of the client's it is drawn in (see FlashOverlay.attach). |
replaces optional | OverlayPlaceOptions | null | undefined | A display object of the client's it stands in for (see FlashOverlay.replace). |
layer optional | OverlayLayer | null | undefined | Where a node drawn in the overlay itself (no parent, host or replaces) goes: above everything, top, by default; or room, inside the game's room just under its chat balloons. A mark over an animal belongs in the room: it stays over the animals and the scenery in front of them, but never covers what anybody says, the HUD, a popup or the World Map. It is placed and sized just as it would be on top, so an anchor is followed the same way. A node in a group goes where its group does, so give the group the layer. Where the game has no room to draw in, and with a loader from before roomLayer (overlay.can('roomLayer')), it is drawn on top. ts const view = phantom.flash.use('avatar.AvatarManager').playerAvatarWorldView; const star = await phantom.flash.overlay.text('★', { layer: 'room', anchor: { place: view, y: -90 }, centered: true, }); |
x optional | number | undefined | |
y optional | number | undefined | |
anchor optional | OverlayAnchorOptions | null | undefined | |
align optional | OverlayAlign | null | undefined | Where it sits in the box it is drawn in: the stage, the group or panel it is in, or the client object it replaces. x and y are then an offset from that point. Not with anchor. |
drag optional | boolean | OverlayDrag | null | undefined | Moved by the player with the mouse: true, or the rules it moves by. on('move') hears it. |
visible optional | boolean | undefined | |
alpha optional | number | undefined | |
rotation optional | number | undefined | |
scale optional | number | undefined | |
centered optional | boolean | undefined | |
fill optional | OverlayColor | null | undefined | |
stroke optional | number | { color: number; alpha?: number | undefined; width?: number | undefined; } | null | undefined | |
width optional | number | undefined | |
height optional | number | undefined | |
cornerRadius optional | number | undefined | |
radius optional | number | undefined | |
toX optional | number | undefined | |
toY optional | number | undefined | |
text optional | string | undefined | |
rich optional | string | TextSpan[] | null | undefined | Rich text: spans, or markup (parseMarkup), in place of text. |
font optional | Partial<OverlayFont> | undefined | |
background optional | OverlayColor | null | undefined | |
padding optional | number | undefined | |
outline optional | number | null | undefined | |
wrapWidth optional | number | undefined | text: wrap at this width, over as many lines as it takes; one line, however long, when left out. |
url optional | string | undefined | |
hand optional | boolean | undefined |
NpcEntity
| Field | Type | |
|---|---|---|
game | GameContext | |
kind | EntityKind | |
id | string | The player's user id, the NPC's actor id, or the volume's name and object index (door#0). |
name | string | null | What the client calls it, when it says. |
place | TypedFlash<QuestActor> | Its display object, typed as its class. |
tagY | number | Where a tag goes by default, in the object's own coordinates. |
handle | FlashHandle | null | The handle it is held by, if any, which release lets go of. |
keeper | EntityKeeper | null | |
highlight | (options?: HighlightOptions, act?: ActOptions) => Promise<() => Promise<void>> | Glows (by default), pulses or recolours the entity until the handle is called. |
hide | (act?: ActOptions) => Promise<() => Promise<void>> | Hides the entity until the handle is called, whatever the client does meanwhile. |
tag | (text: string, options?: TagOptions) => Promise<OverlayNode> | A label that follows the entity, above it by default. Calling the node removes it. |
onClick | (listener: (entity: NpcEntity) => void) => () => void | Hears clicks on the entity. Players and NPCs are listened to themselves; room objects take no mouse events of their own in the client, so a press on the stage is tested against them (hitTestPoint). |
release | () => Promise<void> | Lets go of the object in Flash, for entities held by handle. |
kept | (stop: () => Promise<void>) => () => Promise<void> | |
describe | () => string |
ObjectEntity
| Field | Type | |
|---|---|---|
game | GameContext | |
kind | EntityKind | |
id | string | The player's user id, the NPC's actor id, or the volume's name and object index (door#0). |
name | string | null | What the client calls it, when it says. |
place | TypedFlash<MovieClip> | Its display object, typed as its class. |
tagY | number | Where a tag goes by default, in the object's own coordinates. |
handle | FlashHandle | null | The handle it is held by, if any, which release lets go of. |
keeper | EntityKeeper | null | |
highlight | (options?: HighlightOptions, act?: ActOptions) => Promise<() => Promise<void>> | Glows (by default), pulses or recolours the entity until the handle is called. |
hide | (act?: ActOptions) => Promise<() => Promise<void>> | Hides the entity until the handle is called, whatever the client does meanwhile. |
tag | (text: string, options?: TagOptions) => Promise<OverlayNode> | A label that follows the entity, above it by default. Calling the node removes it. |
onClick | (listener: (entity: ObjectEntity) => void) => () => void | Hears clicks on the entity. Players and NPCs are listened to themselves; room objects take no mouse events of their own in the client, so a press on the stage is tested against them (hitTestPoint). |
release | () => Promise<void> | Lets go of the object in Flash, for entities held by handle. |
kept | (stop: () => Promise<void>) => () => Promise<void> | |
describe | () => string |
EachOptions
What each follows, and when it leaves one alone.
| Field | Type | |
|---|---|---|
unless optional | ((entity: Entity<T>) => boolean | Promise<boolean>) | undefined | Leaves an entity alone while this holds (a buddy, the player themselves). |
everyFrames optional | number | undefined | How often the game looks for new ones, in frames: 6 by default. |
UiRule
A running rule: call it to stop, which undoes it everywhere.
type UiRule = (() => Promise<void>) & {
/** Asks `unless` again for every object, applying or undoing the rule where the answer changed. */
refresh(): Promise<void>;
/** How many objects the rule is applied to now. */
readonly count: number;
}