adventureland-gui
Version:
Basic UI for headless AdventureLand clients
351 lines • 15.8 kB
JavaScript
import { AnimatedSprite, Graphics, Ticker } from "pixi.js";
import { getCosmeticFaceTextures, getCosmeticHairTextures, getCosmeticHatTextures, getCosmeticHeadTextures, getCosmeticMakeupTextures, getSkinColorTextures, getSkinTextures, getSkinType } from "./texture";
import G from "./G.json";
let focusedMon;
const monsters = new Map();
const characters = new Map();
Ticker.shared.add(() => {
const monstersToDelete = [];
for (const [id, datum] of monsters) {
if (datum.data.hp <= 0) {
datum.sprite.gotoAndStop(1);
datum.sprite.interactive = false;
datum.hpBar.visible = false;
datum.sprite.alpha = datum.sprite.alpha - Ticker.shared.elapsedMS / 500;
if (datum.sprite.alpha <= 0)
monstersToDelete.push(id);
continue;
}
const movementAngle = Math.atan2(datum.data.going_y - datum.data.y, datum.data.going_x - datum.data.x);
if (datum.data.moving) {
const distanceTraveled = datum.data.speed * Ticker.shared.elapsedMS / 1000;
const distanceToGoal = Math.hypot(datum.data.going_x - datum.data.x, datum.data.going_y - datum.data.y);
if (distanceTraveled > distanceToGoal) {
datum.data.moving = false;
datum.data.x = datum.data.going_x;
datum.data.y = datum.data.going_y;
}
else {
datum.data.x += (Math.cos(movementAngle) * distanceTraveled);
datum.data.y += (Math.sin(movementAngle) * distanceTraveled);
}
}
datum.sprite.x = datum.data.x - datum.sprite.width / 2;
datum.sprite.y = datum.data.y - datum.sprite.height;
let direction = datum.lastDirection;
if (datum.data.target && (characters.has(datum.data.target) || monsters.has(datum.data.target))) {
const target = characters.get(datum.data.target) || monsters.get(datum.data.target);
if (target) {
const targetAngle = Math.atan2(target.data.y - datum.data.y, target.data.x - datum.data.x);
direction = radsToDirection(targetAngle);
}
}
else if (datum.data.moving) {
direction = radsToDirection(movementAngle);
}
if (datum.lastDirection !== direction) {
datum.sprite.textures = datum.textures[direction];
datum.lastDirection = direction;
}
if (!datum.data.moving && !datum.data.aa && datum.sprite.playing) {
datum.sprite.gotoAndStop(1);
}
else if ((datum.data.aa || datum.data.moving) && !datum.sprite.playing) {
datum.sprite.animationSpeed = datum.data.speed / 1000;
datum.sprite.play();
}
const hpBar = datum.hpBar;
hpBar.visible = (focusedMon == datum.data.id) || (datum.focusedHover ?? false);
if (hpBar.visible) {
const spriteHalfWidth = datum.sprite.width / 2;
hpBar.clear()
.beginFill(0x888888).lineStyle(0).drawRect(datum.sprite.x - (20 - spriteHalfWidth), datum.sprite.y - 12, 40, 10)
.beginFill(0x000000).lineStyle(0).drawRect(datum.sprite.x - (18 - spriteHalfWidth), datum.sprite.y - 10, 36, 6)
.beginFill(0xFF0000).lineStyle(0).drawRect(datum.sprite.x - (16 - spriteHalfWidth), datum.sprite.y - 8, 32 * (datum.data.hp / datum.data.max_hp), 2);
}
datum.sprite.zIndex = datum.data.y;
}
for (const id of monstersToDelete) {
const datum = monsters.get(id);
if (datum) {
datum.hpBar.destroy({ baseTexture: false, children: true, texture: false });
datum.sprite.destroy({ baseTexture: false, children: true, texture: false });
monsters.delete(id);
}
}
for (const [, datum] of characters) {
const angle = Math.atan2(datum.data.going_y - datum.data.y, datum.data.going_x - datum.data.x);
if (datum.data.moving) {
const distanceTraveled = datum.data.speed * Ticker.shared.elapsedMS / 1000;
const distanceToGoal = Math.hypot(datum.data.going_x - datum.data.x, datum.data.going_y - datum.data.y);
if (distanceTraveled > distanceToGoal) {
datum.data.moving = false;
datum.data.x = datum.data.going_x;
datum.data.y = datum.data.going_y;
}
else {
datum.data.x += (Math.cos(angle) * distanceTraveled);
datum.data.y += (Math.sin(angle) * distanceTraveled);
}
}
datum.sprite.x = datum.data.x - datum.sprite.width / 2;
datum.sprite.y = datum.data.y = datum.sprite.height;
let direction = datum.lastDirection;
if (datum.data.target && (characters.has(datum.data.target) || monsters.has(datum.data.target))) {
const target = monsters.get(datum.data.target) || characters.get(datum.data.target);
if (target) {
const targetAngle = Math.atan2(target.data.y - datum.data.y, target.data.x - datum.data.x);
direction = radsToDirection(targetAngle);
}
}
else if (datum.data.moving) {
direction = radsToDirection(angle);
}
if (datum.lastDirection !== direction) {
datum.sprite.textures = datum.textures[direction];
for (let i = datum.sprite.children.length - 1; i >= 0; i--) {
const child = datum.sprite.children[i];
child.textures = datum.texturesChildren[i][direction];
}
datum.lastDirection = direction;
}
if (!datum.data.moving && datum.sprite.playing) {
datum.sprite.gotoAndStop(1);
for (let i = datum.sprite.children.length - 1; i >= 0; i--) {
const child = datum.sprite.children[i];
child.gotoAndStop(child.totalFrames > 1 ? 1 : 0);
}
}
else if (datum.data.moving && !datum.sprite.playing) {
const speed = datum.data.speed / 1000;
datum.sprite.animationSpeed = speed;
for (let i = datum.sprite.children.length - 1; i >= 0; i--) {
const child = datum.sprite.children[i];
child.animationSpeed = speed;
}
datum.sprite.play();
for (let i = datum.sprite.children.length - 1; i >= 0; i--) {
const child = datum.sprite.children[i];
child.play();
}
}
datum.sprite.zIndex = datum.data.y;
}
});
const radsToDirection = (angle) => {
if (angle > -Math.PI / 4 && angle <= Math.PI / 4) {
return 1;
}
else if (angle > Math.PI / 4 && angle <= 3 * Math.PI / 4) {
return 0;
}
else if (angle > 3 * Math.PI / 4 || angle <= -3 * Math.PI / 4) {
return 3;
}
else {
return 2;
}
};
export const removeSprite = (id) => {
const character = characters.get(id);
if (character) {
character.sprite.destroy({ baseTexture: false, children: true, texture: false });
characters.delete(id);
}
const monster = monsters.get(id);
if (monster)
monster.data.hp = -1;
};
export const removeAllSprites = () => {
for (const [, character] of characters) {
character.sprite.destroy({ baseTexture: false, children: true, texture: false });
}
characters.clear();
for (const [, monster] of monsters) {
monster.hpBar.destroy({ baseTexture: false, children: true, texture: false });
monster.sprite.destroy({ baseTexture: false, children: true, texture: false });
}
monsters.clear();
};
export const renderCharacter = (layers, character, initialDirection = 0) => {
let sprite;
if (characters.has(character.id)) {
const oldCharacter = characters.get(character.id);
for (const datum in character)
oldCharacter.data[datum] = character[datum];
sprite = oldCharacter.sprite;
}
else {
const type = getSkinType(character.skin);
if (!character.cx)
character.cx = {};
let textures;
if (type == "full") {
textures = getSkinTextures(character.skin);
}
else {
if (!character.cx.head)
character.cx.head = "makeup117";
textures = getSkinColorTextures(character.cx.head);
}
sprite = new AnimatedSprite(textures[initialDirection]);
sprite.interactive = true;
sprite.interactiveChildren = false;
sprite.sortableChildren = true;
const datum = {
data: character,
lastDirection: initialDirection,
sprite: sprite,
textures: textures,
texturesChildren: {}
};
characters.set(character.id, datum);
if (type !== "full") {
const hasCovers = G.cosmetics.prop[character.skin]?.includes("covers") ?? false;
const noHair = G.cosmetics.prop[character.skin]?.includes("no_hair") ?? false;
if (!hasCovers) {
const baseTextures = getSkinTextures(character.skin);
const baseSkin = new AnimatedSprite(baseTextures[initialDirection]);
if (sprite.width !== baseSkin.width)
baseSkin.x += (sprite.width - baseSkin.width);
baseSkin.zIndex = -1;
sprite.addChild(baseSkin);
datum.texturesChildren[sprite.children.length - 1] = baseTextures;
}
if (character.cx.head) {
const headTextures = getCosmeticHeadTextures(character.cx.head);
const head = new AnimatedSprite(headTextures[initialDirection]);
if (sprite.width !== head.width)
head.x += (sprite.width - head.width);
head.zIndex = 0;
sprite.addChild(head);
datum.texturesChildren[sprite.children.length - 1] = headTextures;
}
if (character.cx.face) {
const faceTextures = getCosmeticFaceTextures(character.cx.face);
const face = new AnimatedSprite(faceTextures[initialDirection]);
if (sprite.width !== face.width)
face.x += (sprite.width - face.width);
face.zIndex = 1;
sprite.addChild(face);
datum.texturesChildren[sprite.children.length - 1] = faceTextures;
}
if (character.cx.makeup) {
const makeupTextures = getCosmeticMakeupTextures(character.cx.makeup);
const makeup = new AnimatedSprite(makeupTextures[initialDirection]);
if (sprite.width !== makeup.width)
makeup.x += (sprite.width - makeup.width);
makeup.zIndex = 2;
sprite.addChild(makeup);
datum.texturesChildren[sprite.children.length - 1] = makeupTextures;
}
if (character.cx.hair && !noHair) {
const hairTextures = getCosmeticHairTextures(character.cx.hair);
const hair = new AnimatedSprite(hairTextures[initialDirection]);
if (sprite.width !== hair.width)
hair.x += (sprite.width - hair.width);
hair.zIndex = 3;
sprite.addChild(hair);
datum.texturesChildren[sprite.children.length - 1] = hairTextures;
}
if (character.cx.hat) {
const hatTextures = getCosmeticHatTextures(character.cx.hat);
const hat = new AnimatedSprite(hatTextures[initialDirection]);
if (sprite.width !== hat.width)
hat.x += (sprite.width - hat.width);
hat.zIndex = 4;
sprite.addChild(hat);
datum.texturesChildren[sprite.children.length - 1] = hatTextures;
}
if (hasCovers) {
const coversTextures = getSkinTextures(character.skin);
const covers = new AnimatedSprite(coversTextures[initialDirection]);
if (sprite.width !== covers.width)
covers.x += (sprite.width - covers.width);
covers.zIndex = 5;
sprite.addChild(covers);
datum.texturesChildren[sprite.children.length - 1] = coversTextures;
}
}
layers.foreground?.addChild(sprite);
if (character.moving) {
const speed = character.speed / 1000;
sprite.animationSpeed = speed;
for (let i = datum.sprite.children.length - 1; i >= 0; i--) {
const child = sprite.children[i];
child.animationSpeed = speed;
}
const randomFrame = Math.floor(Math.random() * (sprite.totalFrames + 1));
sprite.gotoAndPlay(randomFrame);
for (let i = datum.sprite.children.length - 1; i >= 0; i--) {
const child = sprite.children[i];
child.gotoAndPlay(child.totalFrames > 1 ? randomFrame : 0);
}
}
else {
sprite.gotoAndStop(1);
for (let i = datum.sprite.children.length - 1; i >= 0; i--) {
const child = sprite.children[i];
child.gotoAndStop(child.totalFrames > 1 ? 1 : 0);
}
}
}
sprite.x = character.x - sprite.width / 2;
sprite.y = character.y - sprite.height;
sprite.zIndex = character.y;
return sprite;
};
export const renderMonster = (layers, monster, initialDirection = 0) => {
let sprite;
if (monsters.has(monster.id)) {
const oldMonster = monsters.get(monster.id);
for (const datum in monster)
oldMonster.data[datum] = monster[datum];
sprite = oldMonster.sprite;
}
else {
const textures = getSkinTextures(monster.skin);
sprite = new AnimatedSprite(textures[initialDirection]);
sprite.interactive = true;
sprite.interactiveChildren = false;
layers.foreground?.addChild(sprite);
const hpBar = new Graphics();
hpBar.visible = false;
sprite.on("click", () => {
if (focusedMon != monster.id) {
focusedMon = monster.id;
}
else {
focusedMon = null;
}
});
sprite.on("mouseover", () => { datum.focusedHover = true; });
sprite.on("mouseout", () => { datum.focusedHover = false; });
layers.hpBars.addChild(hpBar);
const datum = {
data: monster,
hpBar: hpBar,
lastDirection: initialDirection,
sprite: sprite,
textures: textures
};
monsters.set(monster.id, datum);
if (monster.moving) {
sprite.animationSpeed = monster.speed / 1000;
sprite.gotoAndPlay(Math.floor(Math.random() * (sprite.totalFrames + 1)));
}
else {
sprite.gotoAndStop(1);
}
}
if (monster.size && monster.size !== 1)
sprite.scale.set(monster.size);
if (monster.x !== undefined)
sprite.x = monster.x - sprite.width / 2;
if (monster.y !== undefined) {
sprite.y = monster.y - sprite.height;
sprite.zIndex = monster.y;
}
return sprite;
};
//# sourceMappingURL=sprite.js.map