The chat box and the HUD
Everything here only changes the player's own screen. Nothing is sent to Animal Jam and nobody else sees it.
A local line in the chat box
This writes into the chat history panel along the bottom of the game, the one the player scrolls back through. The line shows as the player's own, with their name, the same way the client writes what they say. Pass a name to write it from that name instead:
await phantom.aj.chat.log.write('the treasure is behind the waterfall', {
from: 'Phantom',
});Neither sends anything to Animal Jam.
The line goes when the chat box is rebuilt. On an account that can chat freely, the client empties the panel each time it rebuilds the box, which happens for an adventure, a minigame and a few of its own screens. Write your line again on phantom.aj.hud.onRebuild.
Line kinds
The panel is one HTML field, and the client writes several kinds of line into it. Each has its own call.
| Call | The line you get |
|---|---|
phantom.aj.chat.log.write(text) | The player's own line, as if they had said it. |
phantom.aj.chat.log.write(text, { from }) | from: text, with the name as plain text. |
phantom.aj.chat.log.writeAs(name, text) | A room line: the name is a link, and clicking it opens a buddy card. |
phantom.aj.chat.log.writeAvatar(userId, text) | The same, with the client finding the name from whoever has that id. |
phantom.aj.chat.log.notice(text, { ... }) | A line of your own, in any colour, with no name unless you give one. |
Use notice for text that came from somewhere else. write and writeAs pass the text to the client, which puts it into the HTML field unescaped, so a < in somebody else's message swallows the rest of the line. notice escapes it for you and takes a colour:
await phantom.aj.chat.log.notice('a buddy just came online', {
colour: 0x22c55e,
from: 'Phantom',
});Reading the panel and removing lines
const lines = await phantom.aj.chat.log.lines();
const last = lines.length - 1;
if (lines[last]?.text.includes('adopt me')) await phantom.aj.chat.log.remove(last);log.lines gives each line with the name it's under (from), the account its name links to (account), who it was addressed to (to), the text on its own (text) and its markup. It parses the field rather than pattern matching, so & comes back as an ampersand and a room line comes back with its speaker.
The panel holds a hundred lines and drops the oldest after that. The client decides this and it can't be changed. log.count says how many there are, log.remove takes one out and log.clear empties it.
Opening and closing the panel
log.open and log.close slide it with the client's own animation and sound. log.isOpen checks whether it's open the same way the panel's arrow does. log.dim dims it to half and back, as the client does while the player is busy elsewhere.
await phantom.aj.chat.log.notice('look at this', { colour: 0x8b5cf6 });
if (!(await phantom.aj.chat.log.isOpen())) await phantom.aj.chat.log.open();Letting the player copy from it
Flash text fields are selectable by default, but the client turns it off for the chat panel, twice, whatever the chat setting. That's why players can't copy a trade out of it. log.keepSelectable turns it back on and keeps it on until you call the function it returns:
The client turns it off again each time it rebuilds the chat box, and keepSelectable turns it back on each time. log.isSelectable reads the current setting.
Hearing a line arrive
const stop = phantom.aj.chat.log.onLine((line) => {
if (line.kind === 'room') console.log(`${line.from} was written into the panel`);
});log.onLine hooks the client's two write calls, so it hears room chat, the client's own lines and every line a plugin writes with write, writeAs or writeAvatar. It doesn't hear notice, which writes to the field directly.
For what people in the room say, phantom.aj.chat.onSaid is usually better. It comes off the network with the speaker looked up and phrase codes turned into text. The two lists differ: onLine is what reached the screen and onSaid is what was said.
The chat bar
The box the player types into is phantom.aj.chat.bar: text, type and onType, plus the approved-words typing behind it (predictive, addWord, reset). How the bar behaves is phantom.aj.chat.input:
limitandkeepLimit: the most characters it takes. The client sets 70 in a room and 40 in a minigame, and the field itself enforces it.show(false)hides the box, its send button and the arrow that opens the chat box.allowFreeChat(true)lifts the letter-by-letter restriction on a safe chat account's box, andresetRulesputs the account's own rules back. Animal Jam still decides what it accepts, so this changes the box, not the rules.resetRulesempties the chat box as it goes; that's the client, not Phantom.setKeys(false)removes the bar's own key listener. Do this before binding a key the bar would otherwise read.repeatsis the last five lines a member said, the ones the repeat button next to the bar offers.
Emotes and the emote menu
phantom.aj.chat.emotes.send(id) shows an emote by its id, and phantom.aj.chat.emotes.all() reads the client's whole emote catalogue in two reads:
const emotes = await phantom.aj.chat.emotes.all();
const free = emotes.filter((emote) => !emote.membersOnly);
console.log(`${free.length} emotes any account may send`);Each emote has its id, the text that shows it, every other spelling that makes it (:angry:, X( and X-( are one emote), the sprite the client draws and whether it's members only. The members-only check is the client's own, read from the sprite's name.
The HUD's menu is emotes.menu, openMenu, closeMenu and dimMenu. They're all the client's own calls on the EmoticonManager it keeps for the session, and none of them sends anything. emotes.onMenu hears the menu open and close, including when the player does it.
emotes.onSent hears every emote this client sends, with its text rather than its number. It hooks EmoticonUtility.idForEmote, which every outgoing emote goes through, so it hears the HUD's menu, the player wall's menu, :) typed into the chat bar, an emote sent in a minigame, and emotes.send:
const stop = phantom.aj.chat.emotes.onSent(({ id, text }) => {
console.log('sent emote', id, text);
});Reading needs "game": "read". Using the menu needs "game": "act".
Coloured speech balloons
phantom.aj.chat.balloons.show puts up the game's white speech balloon over an animal. Pass a colour and you get the same balloon in your colour:
const me = await phantom.aj.player.current();
if (me) await phantom.aj.chat.balloons.show(me.userId, 'hello', { colour: 0x22c55e });Nothing is drawn over the animal. The game draws its own balloon and then the artwork is recoloured. The balloon's background is white, so a per-channel multiplier turns it exactly the colour you asked for, and the shape, tail, rounding, font and fade are all still the game's. The text is set to the client's own ink or white, whichever reads on that colour, and the balloon lasts as long as the game's own countdown.
The same thing without the balloon:
const stop = await phantom.flash.recolor(
{
transform: {
redMultiplier: 0.13,
greenMultiplier: 0.77,
blueMultiplier: 0.37,
},
},
phantom.flash.use('gui.GuiManager').mainHud
);It's a multiplier, not a tint. A tint mixes towards a colour and leaves white artwork pale, but white times a colour is that colour, and the shadows and edges in the art come out as shades of it. Anything in the client drawn in white can be recoloured in place, with no replacement SWF, subclass or drawing on top. For art that isn't white, phantom.flash.recolor takes the other transforms too.
The HUD by name
phantom.aj.hud is the bar along the bottom and the strip along the top, using the names gui.GuiHud gives them.
const was = await phantom.aj.hud.setLabel('land', 'Phantom is watching');
await sleep(5000);
if (was !== null) await phantom.aj.hud.setLabel('land', was);There are four labels:
land: the land's name in the plate at the topanimal: the animal's name under its portraitmail: the unread Jam-a-Gram count on the mail buttonobjective: what an adventure is asking for
label reads one, labels reads all four, setLabel writes one and returns what was there, and onLabel hears one change:
const stop = phantom.aj.hud.onLabel('land', (text) => console.log('now in', text));The client writes its own text again the next time it has news for that label: the next room for the land, the next Jam-a-Gram for the mail count. Put your text back on onRebuild, which fires whenever the client swaps HUDs and finds every part again, and says which HUD it built.
const stop = phantom.aj.hud.onRebuild(async (mode) => {
if (mode === 'room') await phantom.aj.hud.setLabel('land', 'Phantom is watching');
});There are three HUDs, all frames of one timeline, and mode says which is up: room, adventure (with the exit button and objective bar) and minigame. Each has parts the others don't, so reading a part that isn't there returns there: false instead of throwing.
buttons reads the whole bar at once, with each button's presence, visibility and whether the client has greyed it out. A greyed button can't be pressed, and the client's handlers check for that first, so read it before pressing:
const map = await phantom.aj.hud.button('worldMapBtn');
if (map.there && !map.greyed) await phantom.aj.hud.click('worldMapBtn');phantom.aj.gui.on hears a button pressed, by the player or anything else, and keep keeps it attached through every HUD rebuild:
const stop = phantom.aj.gui.on('hud.denBtn', 'click', () => console.log('off home'), {
keep: true,
});windows says which of the HUD's own windows are open: the actions list, the emotes list, the safe chat tree, the animal's window and the chat repeat window.
There are two ways to take a part off the screen. hide keeps it hidden through every rebuild and leaves a gap where it was. showTopButton is the client's own call for the top strip, the one it uses to drop the Journey Book and player wall for accounts that can't have them. It lays the strip out again afterwards, so the buttons either side close the gap.
hideTopRow removes the whole strip. The client has no call to bring it all back at once, so use showTopButton(part, true) per button, or wait for the next HUD rebuild.
Related
phantom.aj.guifinds any element on screen by path, reads it, clicks it and watches it.phantom.aj.hudis the HUD-specific version, with a listener for each thing that changes.phantom.aj.view.hide('hud')keeps the whole HUD hidden, wherephantom.aj.hud.hidehides parts of it.phantom.aj.uidraws Phantom's own interfaces over the game, for when the client has no panel that fits.phantom.aj.chat.custom.boxandphantom.aj.chat.custom.windoware Phantom's own chat box and history window, built on all of the above.