moomooio-client
Version:
MooMooIO-Client is a client-side wrapper API designed for the game MooMoo.io. This package aims to provide a simple yet powerful API for creating clones, mods, or plugins for MooMoo.io.
283 lines (253 loc) • 9.09 kB
text/typescript
import InitializePlayer from "../ServerPackets/InitializePlayer";
import { PlayerData } from "../ServerPackets/UpdatePlayers";
import PropertyTracker from "../utils/PropertyTracker";
import UnifyEmitter, { EventsObject } from "unify-emitter";
import Team from "./Team";
import GameObject from "./GameObject";
export interface PlayerEvents {
move: {
/**
* The direction where the player is moving (in radians)
*/
direction: number
/**
* The player instance
*/
player: ThisType<Player<PlayerEvents>>;
/**
* player position on y-axis
*/
y: number;
/**
* player position on x-axis
*/
x: number;
};
rotate: {
/**
* The angle where the player is facing (in radians)
*/
angle: number;
/**
* The player instance
*/
player: ThisType<Player<PlayerEvents>>;
};
punch: {
/**
* the punch direction (in radians)
*/
direction: number;
/**
* tells if the player successfully hit something
*/
didHit: boolean;
/**
* The player instance
*/
player: ThisType<Player<PlayerEvents>>;
// TODO: Add weapons field
};
chat: {
/**
* the content of message the player sent
*/
message: string;
/**
* The player instance of who sent the message
*/
player: ThisType<Player<PlayerEvents>>;
};
healthChange: {
/**
* a boolean value indicating whether the player is healing or taking damage.
*/
isHealing: boolean;
/**
* a positive number representing the magnitude of the health change.
*/
amount: number;
/**
* the player instance who initiated the health change.
*/
player: ThisType<Player<PlayerEvents>>;
};
hatChange: {
/**
* The previous hat Id that the player was wearing.
*/
previous: number;
/**
* The current hat Id that the player is wearing.
*/
current: number;
/**
* The player instance who initiated the hat change.
*/
player: ThisType<Player<PlayerEvents>>;
};
tailChange: {
/**
* The previous accessory Id that the player was wearing.
*/
previous: number;
/**
* The current accessory Id that the player is wearing.
*/
current: number;
/**
* the player instance who initiated the health change.
*/
player: ThisType<Player<PlayerEvents>>;
};
place: GameObject;
destroyed: null;
update: ThisType<Player<PlayerEvents>>;
}
/**
* @member `buildType` The type of object the player is currently holding. If it's -1, it means the player is not holding anything.
* @member `hatType` Specifies the type of hat the player is wearing, such as a boost hat or soldier hat.
* @member `weaponType` The type of weapon the player has, which can be used to obtain weapon data.
* @member `isBestKiller` Indicates whether the player is the best killer, shown by a skull icon.
* @member `isLeader` Indicates whether the player is the owner of a clan, shown by a crown icon.
* @member `weaponVariant` Shows the level of the player's weapon (such as stone, gold, etc.).
* @member `playerID` is a unique identifier for the player (similar to initID).
* @member `tailType` Describes the type of tail the player has, like wings.
* @member `maxHealth` the maximum health that the player can have.
* @member `angle` indicates the direction the player is facing.
* @member `isMyPlayer` indicates whether the player is yours.
* @member `initID` is a unique identifier for the player.
* @member `skinID` the ID or index of the player's skin.
* @member `layer` Represents the layer of the player.
* @member `health` the current health of the player.
* @member `scale` indicates the size of the player.
* @member `x` the player's position on the x-axis.
* @member `y` the player's position on the y-axis.
* @member `teamID` The ID of the player's team.
* @member `playerName` the name of the player.
* @member `isInitialized` tells where or not a player have basic information like `id`, `name`, `skinID`, ...etc
*/
export type PlayerType = Player<PlayerEvents>;
export default class Player<T extends PlayerEvents> extends UnifyEmitter<
EventsObject<PlayerEvents, T>
> {
#teamID: null | string = null;
#isBestKiller = false;
#weaponVariant = 0;
#isLeader = false;
#isMyPlayer = false;
#playerName!: string;
#weaponType = 0;
#buildType = -1;
#maxHealth = 100;
#playerID!: number;
#tailType = new PropertyTracker(0);
#hatType = new PropertyTracker(0);
#health = new PropertyTracker(100);
#initID!: string;
#skinID = 0;
#scale = 35;
#layer = 0;
#angle = new PropertyTracker(0);
#x = new PropertyTracker(0);
#y = new PropertyTracker(0);
#isInitialized = false;
initialize(initializePlayer: InitializePlayer) {
this.#isMyPlayer = initializePlayer.isMyPlayer;
this.#playerName = initializePlayer.playerName;
this.#maxHealth = initializePlayer.maxHealth;
this.#playerID = initializePlayer.playerID;
this.#initID = initializePlayer.initID;
this.#skinID = initializePlayer.skinID;
this.#scale = initializePlayer.scale;
this.#health = new PropertyTracker(initializePlayer.health);
this.#angle = new PropertyTracker(initializePlayer.angle);
this.#x = new PropertyTracker(initializePlayer.x);
this.#y = new PropertyTracker(initializePlayer.y);
this.#isInitialized = true;
}
update(playerData: PlayerData) {
this.#weaponVariant = playerData.weaponVariant;
this.#isBestKiller = playerData.isBestKiller;
this.#weaponType = playerData.weaponType;
this.#tailType.set(playerData.tailType);
this.#buildType = playerData.buildType;
this.#hatType.set(playerData.hatType);
this.#isLeader = playerData.isLeader;
this.#angle.set(playerData.angle);
this.#teamID = playerData.teamID;
this.#layer = playerData.layer;
this.#x.set(playerData.x);
this.#y.set(playerData.y);
if (this.#x.isDiff() || this.#y.isDiff()) {
const deltaX = this.#x.current - (this.#x.previous || 0);
const deltaY = this.#y.current - (this.#y.previous || 0);
this.emit("move", {
direction: Math.atan2(deltaY, deltaX),
x: this.x,
y: this.y,
player: this,
});
}
if (this.#hatType.isDiff()) {
this.emit("hatChange", {
previous: this.#hatType.previous ?? 0,
current: this.#hatType.current,
player: this,
});
}
if (this.#tailType.isDiff()) {
this.emit("tailChange", {
previous: this.#tailType.previous ?? 0,
current: this.#tailType.current,
player: this,
});
}
if (this.#angle.isDiff()) {
this.emit("rotate", {
angle: this.angle,
player: this,
});
}
this.emit("update", this);
}
clear() {
this.emit("destroyed", null);
this.#isInitialized = false;
this.removeListeners();
}
/* prettier-ignore */ get weaponVariant() { return this.#weaponVariant }
/* prettier-ignore */ get isInitialized() { return this.#isInitialized }
/* prettier-ignore */ get isBestKiller() { return this.#isBestKiller }
/* prettier-ignore */ get tailType() { return this.#tailType.current }
/* prettier-ignore */ get hatType() { return this.#hatType.current }
/* prettier-ignore */ get weaponType() { return this.#weaponType }
/* prettier-ignore */ get isMyPlayer() { return this.#isMyPlayer }
/* prettier-ignore */ get playerName() { return this.#playerName }
/* prettier-ignore */ get health() { return this.#health.current}
/* prettier-ignore */ get buildType() { return this.#buildType }
/* prettier-ignore */ get maxHealth() { return this.#maxHealth }
/* prettier-ignore */ get angle() { return this.#angle.current }
/* prettier-ignore */ get isLeader() { return this.#isLeader }
/* prettier-ignore */ get playerID() { return this.#playerID }
/* prettier-ignore */ get teamID() { return this.#teamID }
/* prettier-ignore */ get initID() { return this.#initID }
/* prettier-ignore */ get skinID() { return this.#skinID }
/* prettier-ignore */ get layer() { return this.#layer }
/* prettier-ignore */ get scale() { return this.#scale }
/* prettier-ignore */ get x() { return this.#x.current }
/* prettier-ignore */ get y() { return this.#y.current }
setHealth(health: number): void {
this.#health.set(Math.min(this.maxHealth, Math.max(health, 0)));
const prev = this.#health.previous ?? 100;
const curr = this.#health.current;
this.emit("healthChange", {
isHealing: prev - curr < 0,
amount: Math.abs(prev - curr),
player: this,
});
}
getTeam(teams: Map<string, Team>): Team | null {
return (this.teamID && teams.get(this.teamID)) || null;
}
}