SOURCE / PINNED RELEASE
Made of little things.
Browser Side Quest
- Release
- b9d42f5768e4…
- Author-recorded commit
- c267690145fa…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/browser-side-quest
Archive hash verified: 6d4b90b8a5f328f7…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Port of BrowserQuest shared/js/gametypes.js (MPL-2.0, Little Workshop / Mozilla).
// Message numbers and entity kinds are kept identical to the original protocol.
export const Messages = {
HELLO: 0,
WELCOME: 1,
SPAWN: 2,
DESPAWN: 3,
MOVE: 4,
LOOTMOVE: 5,
AGGRO: 6,
ATTACK: 7,
HIT: 8,
HURT: 9,
HEALTH: 10,
CHAT: 11,
LOOT: 12,
EQUIP: 13,
DROP: 14,
TELEPORT: 15,
DAMAGE: 16,
POPULATION: 17,
KILL: 18,
LIST: 19,
WHO: 20,
ZONE: 21,
DESTROY: 22,
HP: 23,
BLINK: 24,
OPEN: 25,
CHECK: 26,
// Port extension (world → client only): [PLAYERINFO, playerId, nostrPubkeyHex].
PLAYERINFO: 27,
} as const;
export const Entities = {
WARRIOR: 1,
// Mobs
RAT: 2,
SKELETON: 3,
GOBLIN: 4,
OGRE: 5,
SPECTRE: 6,
CRAB: 7,
BAT: 8,
WIZARD: 9,
EYE: 10,
SNAKE: 11,
SKELETON2: 12,
BOSS: 13,
DEATHKNIGHT: 14,
// Armors
FIREFOX: 20,
CLOTHARMOR: 21,
LEATHERARMOR: 22,
MAILARMOR: 23,
PLATEARMOR: 24,
REDARMOR: 25,
GOLDENARMOR: 26,
// Objects
FLASK: 35,
BURGER: 36,
CHEST: 37,
FIREPOTION: 38,
CAKE: 39,
// NPCs
GUARD: 40,
KING: 41,
OCTOCAT: 42,
VILLAGEGIRL: 43,
VILLAGER: 44,
PRIEST: 45,
SCIENTIST: 46,
AGENT: 47,
RICK: 48,
NYAN: 49,
SORCERER: 50,
BEACHNPC: 51,
FORESTNPC: 52,
DESERTNPC: 53,
LAVANPC: 54,
CODER: 55,
// Weapons
SWORD1: 60,
SWORD2: 61,
REDSWORD: 62,
GOLDENSWORD: 63,
MORNINGSTAR: 64,
AXE: 65,
BLUESWORD: 66,
} as const;
export const Orientations = {
UP: 1,
DOWN: 2,
LEFT: 3,
RIGHT: 4,
} as const;
export type Orientation = (typeof Orientations)[keyof typeof Orientations];
export type KindType = 'player' | 'mob' | 'weapon' | 'armor' | 'object' | 'npc';
const kinds: Record<string, [number, KindType]> = {
warrior: [Entities.WARRIOR, 'player'],
rat: [Entities.RAT, 'mob'],
skeleton: [Entities.SKELETON, 'mob'],
goblin: [Entities.GOBLIN, 'mob'],
ogre: [Entities.OGRE, 'mob'],
spectre: [Entities.SPECTRE, 'mob'],
deathknight: [Entities.DEATHKNIGHT, 'mob'],
crab: [Entities.CRAB, 'mob'],
snake: [Entities.SNAKE, 'mob'],
bat: [Entities.BAT, 'mob'],
wizard: [Entities.WIZARD, 'mob'],
eye: [Entities.EYE, 'mob'],
skeleton2: [Entities.SKELETON2, 'mob'],
boss: [Entities.BOSS, 'mob'],
sword1: [Entities.SWORD1, 'weapon'],
sword2: [Entities.SWORD2, 'weapon'],
axe: [Entities.AXE, 'weapon'],
redsword: [Entities.REDSWORD, 'weapon'],
bluesword: [Entities.BLUESWORD, 'weapon'],
goldensword: [Entities.GOLDENSWORD, 'weapon'],
morningstar: [Entities.MORNINGSTAR, 'weapon'],
firefox: [Entities.FIREFOX, 'armor'],
clotharmor: [Entities.CLOTHARMOR, 'armor'],
leatherarmor: [Entities.LEATHERARMOR, 'armor'],
mailarmor: [Entities.MAILARMOR, 'armor'],
platearmor: [Entities.PLATEARMOR, 'armor'],
redarmor: [Entities.REDARMOR, 'armor'],
goldenarmor: [Entities.GOLDENARMOR, 'armor'],
flask: [Entities.FLASK, 'object'],
cake: [Entities.CAKE, 'object'],
burger: [Entities.BURGER, 'object'],
chest: [Entities.CHEST, 'object'],
firepotion: [Entities.FIREPOTION, 'object'],
guard: [Entities.GUARD, 'npc'],
villagegirl: [Entities.VILLAGEGIRL, 'npc'],
villager: [Entities.VILLAGER, 'npc'],
coder: [Entities.CODER, 'npc'],
scientist: [Entities.SCIENTIST, 'npc'],
priest: [Entities.PRIEST, 'npc'],
king: [Entities.KING, 'npc'],
rick: [Entities.RICK, 'npc'],
nyan: [Entities.NYAN, 'npc'],
sorcerer: [Entities.SORCERER, 'npc'],
agent: [Entities.AGENT, 'npc'],
octocat: [Entities.OCTOCAT, 'npc'],
beachnpc: [Entities.BEACHNPC, 'npc'],
forestnpc: [Entities.FORESTNPC, 'npc'],
desertnpc: [Entities.DESERTNPC, 'npc'],
lavanpc: [Entities.LAVANPC, 'npc'],
};
const kindNames = new Map<number, string>();
for (const [name, [kind]] of Object.entries(kinds)) kindNames.set(kind, name);
export const rankedWeapons: number[] = [
Entities.SWORD1,
Entities.SWORD2,
Entities.AXE,
Entities.MORNINGSTAR,
Entities.BLUESWORD,
Entities.REDSWORD,
Entities.GOLDENSWORD,
];
export const rankedArmors: number[] = [
Entities.CLOTHARMOR,
Entities.LEATHERARMOR,
Entities.MAILARMOR,
Entities.PLATEARMOR,
Entities.REDARMOR,
Entities.GOLDENARMOR,
];
function getType(kind: number): KindType | undefined {
const name = kindNames.get(kind);
return name ? kinds[name][1] : undefined;
}
export const getWeaponRank = (kind: number): number => rankedWeapons.indexOf(kind);
export const getArmorRank = (kind: number): number => rankedArmors.indexOf(kind);
export const isPlayer = (kind: number): boolean => getType(kind) === 'player';
export const isMob = (kind: number): boolean => getType(kind) === 'mob';
export const isNpc = (kind: number): boolean => getType(kind) === 'npc';
export const isCharacter = (kind: number): boolean => isMob(kind) || isNpc(kind) || isPlayer(kind);
export const isArmor = (kind: number): boolean => getType(kind) === 'armor';
export const isWeapon = (kind: number): boolean => getType(kind) === 'weapon';
export const isObject = (kind: number): boolean => getType(kind) === 'object';
export const isChest = (kind: number): boolean => kind === Entities.CHEST;
export const isItem = (kind: number): boolean =>
isWeapon(kind) || isArmor(kind) || (isObject(kind) && !isChest(kind));
export const isHealingItem = (kind: number): boolean =>
kind === Entities.FLASK || kind === Entities.BURGER;
export const isExpendableItem = (kind: number): boolean =>
isHealingItem(kind) || kind === Entities.FIREPOTION || kind === Entities.CAKE;
export function getKindFromString(name: string): number | undefined {
return name in kinds ? kinds[name][0] : undefined;
}
export function getKindAsString(kind: number): string | undefined {
return kindNames.get(kind);
}
export function forEachKind(callback: (kind: number, name: string) => void): void {
for (const [name, [kind]] of Object.entries(kinds)) callback(kind, name);
}
export function forEachArmorKind(callback: (kind: number, name: string) => void): void {
forEachKind((kind, name) => {
if (isArmor(kind)) callback(kind, name);
});
}
export function forEachMobOrNpcKind(callback: (kind: number, name: string) => void): void {
forEachKind((kind, name) => {
if (isMob(kind) || isNpc(kind)) callback(kind, name);
});
}
export function getOrientationAsString(orientation: number): string {
switch (orientation) {
case Orientations.LEFT:
return 'left';
case Orientations.RIGHT:
return 'right';
case Orientations.UP:
return 'up';
default:
return 'down';
}
}
export function getMessageTypeAsString(type: number): string {
for (const [name, value] of Object.entries(Messages)) {
if (value === type) return name;
}
return 'UNKNOWN';
}
