Hearing the player
Each thing the player does in the game, like pressing an item or pointing at a buddy, is an event. You pass it a function and get back a function that stops listening.
const stop = phantom.aj.guiItems.onPress(({ screen, index, item }) => {
console.log(screen, 'slot', index, item?.name);
});You don't need to find the window or wait for it to open. The listener works before the window exists and keeps working after the client destroys it and builds a new one.
Items in any window
Every inventory, list and picker in the client is built the same way, so one listener covers all of them.
| Event | When |
|---|---|
phantom.aj.guiItems.onPress | The player presses an item |
phantom.aj.guiItems.onHover | The mouse arrives on one (over) or leaves it (out) |
phantom.aj.guiItems.onMembersOnly | A player who isn't a member presses something members only use |
They all pass the same fields.
| Field | What it is |
|---|---|
action | press, over, out or membersOnly |
screen | Which window: avatarEditor, denEditor, petInventory, buddyList, ... |
index | The slot in the grid, from 0 |
item | What the slot was showing, or null for an empty one |
target | The name of the thing pressed, when the window has buttons of its own in it |
windowClass | The client's class for the slot, if you're reading the source alongside |
handler | The client's own method that dealt with it |
stageX, stageY | Where the mouse was |
item is read from the client's own item.
| Field | What it is |
|---|---|
className | item.Item for clothing, den.DenItem, pet.PetItem, buddy.Buddy |
defId | What kind of thing it is |
invIdx | Which one of the player's it is |
name | Its name, in the player's language |
colour | Its colour, for clothing |
membersOnly | Whether only members may use it |
userName | Whose row it is, for a buddy or a Jam-a-gram |
messageId | A Jam-a-gram's id |
A field the item doesn't have is null: a buddy has no defId and a chair has no userName.
Pass screens to listen to only some windows. The others aren't hooked.
phantom.aj.guiItems.onHover(
({ action, item }) => {
if (action === 'over' && item?.defId != null)
console.log('pointing at', item.name, item.defId);
},
{ screens: ['avatarEditor', 'denEditor', 'trade'] }
);phantom.aj.guiItems.screens() lists every window that has a grid.
Tooltips
phantom.aj.popups.tooltip.onShow hears every tooltip the game shows, but a tooltip is text in the player's language. onHover gives you the definition id instead, which is the same for everyone and is what price lists and wikis use.
Any button
phantom.aj.gui.onPress fires when the player presses the mouse on anything the client draws.
phantom.aj.gui.onPress(({ path, name, className, text }) => {
console.log('pressed', path ?? name, className, text ?? '');
});| Field | What it is |
|---|---|
path | Where it is, as the rest of phantom.aj.gui takes it: hud.denBtn |
name | Its own name in the client |
className | Its class: flash.display.SimpleButton, flash.text.TextField |
text | Its text, when it's a text field |
names | The name of everything it's inside, outermost first |
It's also the quickest way to find out what something is called: turn it on in a script, click the thing and read the path.
phantom.aj.gui.onClick is the same for a click (button down and up on the same thing). Nearly all of the client's buttons act on the press, so onPress matches what the game did. Neither one stops the game handling the press.
A specific button
If you know which button you want, listen to that one. keep keeps the listener on it when the client rebuilds the HUD for each room.
phantom.aj.gui.on('hud.denBtn', 'click', () => console.log('off home'), { keep: true });Windows opening and closing
| Event | When |
|---|---|
phantom.aj.screens.onOpen, onClose | One of the client's own windows, by name |
phantom.aj.screens.onReady | One of them finished loading its art |
phantom.aj.popups.onOpen, onClose | Any popup the client puts up or takes down |
phantom.aj.gui.onBuilt, onGone | The client building or removing one element |
phantom.aj.room.onLoaded | The room has finished building |
phantom.aj.hud.onRebuild | The HUD has been built again |
phantom.aj.screens.loading.onChange | The loading screen between rooms |
phantom.aj.screens.loading.onProgress | Each stage of the client's own startup |
const stop = phantom.aj.screens.onOpen(({ screen }) => console.log('opened', screen), {
screens: ['avatarSwitcher', 'journeyBook'],
});phantom.aj.screens.all() returns every window that's open now, in one message.
The book
The client uses one book window for everything with pages: the Jamaa Journal, the Journey Book, the e-books, the comic and the membership book. phantom.aj.journeyBook.book listens to it.
| Event | When |
|---|---|
phantom.aj.journeyBook.book.onChange | The book turning, its spread coming in, its zoom, its quiz, its popups |
phantom.aj.journeyBook.book.onPress | Anything on the page pressed, with the clip's own name |
Start with onPress. The client works out what a clip on the page does from its name (joinRoom_crystal$sands, gameBtn_45, badgeCont_Otters_3), and that name is what the listener gets.
const stop = phantom.aj.journeyBook.book.onPress(({ kind, name }) => {
if (kind === 'link') console.log('the page offered the website:', name);
});Listening doesn't press anything. Stamps, stickers and gifts only ask Animal Jam to mark the account when the player presses them.
Windows inside the editors
The editors open other windows inside themselves, and these have their own events:
| Event | When |
|---|---|
phantom.aj.editor.avatar.onPanel | The shop, the recycler, the pets or the trade picker, in the editor |
phantom.aj.editor.avatar.onItemPress | Any of the editor's item windows pressed |
phantom.aj.editor.avatar.onStep | An item taken off to make room for another, and the like |
phantom.aj.screens.startup.onPopup | Each popup the client builds at login |
The room and the pointer
The events above are for the client's windows. For presses on the room itself, phantom.aj.input listens to the room's stage handlers:
| Event | When |
|---|---|
phantom.aj.input.onClick | A press over the room, with the point, the modifiers and the target |
phantom.aj.input.onRelease | The button coming up, which is where a drag ends |
phantom.aj.input.onPointer | The pointer moving, read in the game a few frames apart |
phantom.aj.input.onAnyInput | Any press or key press anywhere in the game, windows and games alike |
onClick comes from the room's own handler (RoomManagerView.onMouseDownEvt), so it only fires for presses the game acted on, not while a modal window is up or the screen is darkened.
onPointer isn't a hook. Flash fires a move event for every pixel, which would be hundreds of messages a second, so onPointer watches the room's fields inside the game instead and sends nothing while the pointer is still. See Shortcuts, moving and the view.
Walking, jumping and sliding
phantom.aj.room.players.onMove hears the server's move message, which is one message per walk and only says where the animal ended up. For the walk itself, phantom.aj.avatars.motion reads it from the client.
const me = await phantom.aj.player.current();
if (me) {
const stop = phantom.aj.avatars.motion.onMovement(me.userId, (walk) => {
if (!walk) return;
if (walk.jump.jumping) console.log('jumped');
if (walk.slide.at >= 0) console.log('being carried along a slide');
if (walk.platform.on) console.log('standing on a platform');
});
}It's a watch inside the game, so nothing is sent while the animal stands still. phantom.aj.avatars.motion.movement(userId) reads the same thing once. phantom.aj.avatars.motion.effects(userId) reads the liquid an animal is standing in, the particles a recycled kind gives off, its glow and the balloon over it.
The player's pet has its own:
const stop = phantom.aj.player.pets.world.onWalk((walk) => {
if (walk) console.log(`the pet is crossing to x ${walk.targetX}`);
});phantom.aj.player.pets.world.onChange fires when the pet is brought out, put away, shown or hidden. onWalk fires when the client picks a new side for the pet to cross to or changes its animation, so you hear where it's going before it gets there.
Area events
The events above work everywhere. Each area also has typed events for its own windows, with the data that window is about, like a shop row with its price or an avatar editor item with whether it's worn. They're the on… calls on each area's page in the reference.
Use the area's events first. If they don't cover what you need, fall back to phantom.aj.guiItems.onPress and phantom.aj.gui.onPress, and let us know about the gap.
Hooking a handler yourself
The client's handlers take a mouse event, and the thing pressed is its currentTarget. By the time your listener runs, the client has already handled the press, and the avatar editor, for one, destroys its grid and builds a new one. If you read the item afterwards you'll sometimes get null.
Every event on this page reads what it needs inside the call, before the client's handler runs. Hooks shows how to do the same for a handler that isn't covered here.