aboutsummaryrefslogtreecommitdiff
path: root/webAO/packets/handlers
diff options
context:
space:
mode:
authorstonedDiscord <Tukz@gmx.de>2022-08-31 20:31:54 +0200
committerstonedDiscord <Tukz@gmx.de>2022-08-31 20:31:54 +0200
commitd124c7466ec13dc0353edeb5fab49f03f3035b5e (patch)
tree8f278b39e51fa5d3b738bd2d47c6f3b1d123ca9c /webAO/packets/handlers
parent1847fcf939c4a079b2f016df212e2b6c832aa2e5 (diff)
parentda992f9b3623955ef777c343a9f2c7b81d8ca942 (diff)
Merge branch 'master' of https://github.com/AttorneyOnline/webAO
Diffstat (limited to 'webAO/packets/handlers')
-rw-r--r--webAO/packets/handlers/handleARUP.ts42
-rw-r--r--webAO/packets/handlers/handleBB.ts11
-rw-r--r--webAO/packets/handlers/handleBD.ts13
-rw-r--r--webAO/packets/handlers/handleBN.ts81
-rw-r--r--webAO/packets/handlers/handleCC.ts9
-rw-r--r--webAO/packets/handlers/handleDONE.ts16
-rw-r--r--webAO/packets/handlers/handleFA.ts14
-rw-r--r--webAO/packets/handlers/handleFL.ts88
-rw-r--r--webAO/packets/handlers/handleFM.ts16
-rw-r--r--webAO/packets/handlers/handleHI.ts14
-rw-r--r--webAO/packets/handlers/handleHP.ts23
-rw-r--r--webAO/packets/handlers/handleID.ts24
-rw-r--r--webAO/packets/handlers/handleKB.ts12
-rw-r--r--webAO/packets/handlers/handleKK.ts11
-rw-r--r--webAO/packets/handlers/handleMM.ts8
-rw-r--r--webAO/packets/handlers/handlePN.ts9
-rw-r--r--webAO/packets/handlers/handleRC.ts10
-rw-r--r--webAO/packets/handlers/handleRT.ts25
-rw-r--r--webAO/packets/handlers/handleSI.ts41
-rw-r--r--webAO/packets/handlers/handleSM.ts36
-rw-r--r--webAO/packets/handlers/handleTI.ts21
-rw-r--r--webAO/packets/handlers/handleZZ.ts22
-rw-r--r--webAO/packets/handlers/handleaskchaa.ts10
23 files changed, 512 insertions, 44 deletions
diff --git a/webAO/packets/handlers/handleARUP.ts b/webAO/packets/handlers/handleARUP.ts
new file mode 100644
index 0000000..97db9cc
--- /dev/null
+++ b/webAO/packets/handlers/handleARUP.ts
@@ -0,0 +1,42 @@
+import { client } from "../../client";
+import { safeTags } from "../../encoding";
+
+/**
+ * Handle the change of players in an area.
+ * @param {Array} args packet arguments
+ */
+export const handleARUP = (args: string[]) => {
+ args = args.slice(1);
+ for (let i = 0; i < args.length - 2; i++) {
+ if (client.areas[i]) {
+ // the server sends us ARUP before we even get the area list
+ const thisarea = document.getElementById(`area${i}`)!;
+ switch (Number(args[0])) {
+ case 0: // playercount
+ client.areas[i].players = Number(args[i + 1]);
+ break;
+ case 1: // status
+ client.areas[i].status = safeTags(args[i + 1]);
+ break;
+ case 2:
+ client.areas[i].cm = safeTags(args[i + 1]);
+ break;
+ case 3:
+ client.areas[i].locked = safeTags(args[i + 1]);
+ break;
+ }
+
+ thisarea.className = `area-button area-${client.areas[
+ i
+ ].status.toLowerCase()}`;
+
+ thisarea.innerText = `${client.areas[i].name} (${client.areas[i].players}) [${client.areas[i].status}]`;
+
+ thisarea.title =
+ `Players: ${client.areas[i].players}\n` +
+ `Status: ${client.areas[i].status}\n` +
+ `CM: ${client.areas[i].cm}\n` +
+ `Area lock: ${client.areas[i].locked}`;
+ }
+ }
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleBB.ts b/webAO/packets/handlers/handleBB.ts
new file mode 100644
index 0000000..c12c4f6
--- /dev/null
+++ b/webAO/packets/handlers/handleBB.ts
@@ -0,0 +1,11 @@
+import { safeTags } from "../../encoding";
+
+
+/**
+ * Handles the warning packet
+ * on client this spawns a message box you can't close for 2 seconds
+ * @param {Array} args ban reason
+ */
+export const handleBB = (args: string[]) => {
+ alert(safeTags(args[1]));
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleBD.ts b/webAO/packets/handlers/handleBD.ts
new file mode 100644
index 0000000..4ec291e
--- /dev/null
+++ b/webAO/packets/handlers/handleBD.ts
@@ -0,0 +1,13 @@
+import { client, setBanned } from "../../client";
+import { safeTags } from "../../encoding";
+
+
+/**
+ * Handles the banned packet
+ * this one is sent when you try to reconnect but you're banned
+ * @param {Array} args ban reason
+ */
+export const handleBD = (args: string[]) => {
+ client.handleBans("Banned", safeTags(args[1]));
+ setBanned(true);
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleBN.ts b/webAO/packets/handlers/handleBN.ts
new file mode 100644
index 0000000..3cd3c5a
--- /dev/null
+++ b/webAO/packets/handlers/handleBN.ts
@@ -0,0 +1,81 @@
+import { AO_HOST, client, getIndexFromSelect, updateBackgroundPreview } from "../../client";
+import { safeTags } from "../../encoding";
+import tryUrls from "../../utils/tryUrls";
+
+
+ /**
+ * Handles a background change.
+ * @param {Array} args packet arguments
+ */
+
+export const handleBN = (args: string[]) => {
+ const bgFromArgs = safeTags(args[1]);
+ client.viewport.setBackgroundName(bgFromArgs);
+ const bgfolder = client.viewport.getBackgroundFolder();
+ const bg_index = getIndexFromSelect(
+ "bg_select",
+ client.viewport.getBackgroundName()
+ );
+ (<HTMLSelectElement>document.getElementById("bg_select")).selectedIndex =
+ bg_index;
+ updateBackgroundPreview();
+ if (bg_index === 0) {
+ (<HTMLInputElement>document.getElementById("bg_filename")).value =
+ client.viewport.getBackgroundName();
+ }
+
+ tryUrls(
+ `${AO_HOST}background/${encodeURI(args[1].toLowerCase())}/defenseempty`
+ ).then((resp) => {
+ (<HTMLImageElement>document.getElementById("bg_preview")).src = resp;
+ });
+ tryUrls(`${bgfolder}defensedesk`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_def_bench")).src =
+ resp;
+ });
+ tryUrls(`${bgfolder}stand`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_wit_bench")).src =
+ resp;
+ });
+ tryUrls(`${bgfolder}prosecutiondesk`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_pro_bench")).src =
+ resp;
+ });
+ tryUrls(`${bgfolder}full`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_court")).src = resp;
+ });
+ tryUrls(`${bgfolder}defenseempty`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_court_def")).src =
+ resp;
+ });
+ tryUrls(`${bgfolder}transition_def`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_court_deft")).src =
+ resp;
+ });
+ tryUrls(`${bgfolder}witnessempty`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_court_wit")).src =
+ resp;
+ });
+ tryUrls(`${bgfolder}transition_pro`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_court_prot")).src =
+ resp;
+ });
+ tryUrls(`${bgfolder}prosecutorempty`).then((resp) => {
+ (<HTMLImageElement>document.getElementById("client_court_pro")).src =
+ resp;
+ });
+
+ if (client.charID === -1) {
+ client.viewport.set_side({
+ position: "jud",
+ showSpeedLines: false,
+ showDesk: true,
+ });
+ } else {
+ client.viewport.set_side({
+ position: client.chars[client.charID].side,
+ showSpeedLines: false,
+ showDesk: true,
+ });
+ }
+ } \ No newline at end of file
diff --git a/webAO/packets/handlers/handleCC.ts b/webAO/packets/handlers/handleCC.ts
new file mode 100644
index 0000000..84e6ec0
--- /dev/null
+++ b/webAO/packets/handlers/handleCC.ts
@@ -0,0 +1,9 @@
+import { client } from "../../client";
+
+/**
+ * What? you want a character??
+ * @param {Array} args packet arguments
+ */
+export const handleCC = (args: string[]) => {
+ client.sendSelf(`PV#1#CID#${args[2]}#%`);
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleDONE.ts b/webAO/packets/handlers/handleDONE.ts
new file mode 100644
index 0000000..3cafd5e
--- /dev/null
+++ b/webAO/packets/handlers/handleDONE.ts
@@ -0,0 +1,16 @@
+import queryParser from "../../utils/queryParser";
+
+let { mode } = queryParser()
+ /**
+ * Handles the handshake completion packet, meaning the player
+ * is ready to select a character.
+ *
+ * @param {Array} args packet arguments
+ */
+export const handleDONE = (_args: string[]) => {
+ document.getElementById("client_loading")!.style.display = "none";
+ if (mode === "watch") {
+ // Spectators don't need to pick a character
+ document.getElementById("client_waiting")!.style.display = "none";
+ }
+ } \ No newline at end of file
diff --git a/webAO/packets/handlers/handleFA.ts b/webAO/packets/handlers/handleFA.ts
new file mode 100644
index 0000000..ccfe923
--- /dev/null
+++ b/webAO/packets/handlers/handleFA.ts
@@ -0,0 +1,14 @@
+import { client } from '../../client'
+import { safeTags } from '../../encoding';
+
+/**
+ * Handles updated area list
+ * @param {Array} args packet arguments
+ */
+export const handleFA = (args: string[]) => {
+ client.resetAreaList();
+
+ for (let i = 1; i < args.length - 1; i++) {
+ client.createArea(i - 1, safeTags(args[i]));
+ }
+}
diff --git a/webAO/packets/handlers/handleFL.ts b/webAO/packets/handlers/handleFL.ts
index d9e67e5..378d5a9 100644
--- a/webAO/packets/handlers/handleFL.ts
+++ b/webAO/packets/handlers/handleFL.ts
@@ -1,48 +1,48 @@
import { setExtraFeatures } from "../../client";
-
- /**
- * With this the server tells us which features it supports
- * @param {Array} args list of features
- */
+
+/**
+ * With this the server tells us which features it supports
+ * @param {Array} args list of features
+ */
export const handleFL = (args: string[]) => {
- console.info("Server-supported features:");
- console.info(args);
- setExtraFeatures(args);
-
- if (args.includes("yellowtext")) {
- const colorselect = <HTMLSelectElement>(
- document.getElementById("textcolor")
- );
-
- colorselect.options[colorselect.options.length] = new Option(
- "Yellow",
- "5"
- );
- colorselect.options[colorselect.options.length] = new Option("Grey", "6");
- colorselect.options[colorselect.options.length] = new Option("Pink", "7");
- colorselect.options[colorselect.options.length] = new Option("Cyan", "8");
- }
-
- if (args.includes("cccc_ic_support")) {
- document.getElementById("cccc")!.style.display = "";
- document.getElementById("pairing")!.style.display = "";
- }
-
- if (args.includes("flipping")) {
- document.getElementById("button_flip")!.style.display = "";
- }
-
- if (args.includes("looping_sfx")) {
- document.getElementById("button_shake")!.style.display = "";
- document.getElementById("2.7")!.style.display = "";
- }
-
- if (args.includes("effects")) {
- document.getElementById("2.8")!.style.display = "";
- }
-
- if (args.includes("y_offset")) {
- document.getElementById("y_offset")!.style.display = "";
- }
+ console.info("Server-supported features:");
+ console.info(args);
+ setExtraFeatures(args);
+
+ if (args.includes("yellowtext")) {
+ const colorselect = <HTMLSelectElement>(
+ document.getElementById("textcolor")
+ );
+
+ colorselect.options[colorselect.options.length] = new Option(
+ "Yellow",
+ "5"
+ );
+ colorselect.options[colorselect.options.length] = new Option("Grey", "6");
+ colorselect.options[colorselect.options.length] = new Option("Pink", "7");
+ colorselect.options[colorselect.options.length] = new Option("Cyan", "8");
+ }
+
+ if (args.includes("cccc_ic_support")) {
+ document.getElementById("cccc")!.style.display = "";
+ document.getElementById("pairing")!.style.display = "";
+ }
+
+ if (args.includes("flipping")) {
+ document.getElementById("button_flip")!.style.display = "";
+ }
+
+ if (args.includes("looping_sfx")) {
+ document.getElementById("button_shake")!.style.display = "";
+ document.getElementById("2.7")!.style.display = "";
+ }
+
+ if (args.includes("effects")) {
+ document.getElementById("2.8")!.style.display = "";
+ }
+
+ if (args.includes("y_offset")) {
+ document.getElementById("y_offset")!.style.display = "";
}
+}
diff --git a/webAO/packets/handlers/handleFM.ts b/webAO/packets/handlers/handleFM.ts
new file mode 100644
index 0000000..630477b
--- /dev/null
+++ b/webAO/packets/handlers/handleFM.ts
@@ -0,0 +1,16 @@
+import { client } from "../../client";
+import { safeTags } from "../../encoding";
+
+
+/**
+ * Handles updated music list
+ * @param {Array} args packet arguments
+ */
+export const handleFM = (args: string[]) => {
+ client.resetMusicList();
+
+ for (let i = 1; i < args.length - 1; i++) {
+ // Check when found the song for the first time
+ client.addTrack(safeTags(args[i]));
+ }
+}
diff --git a/webAO/packets/handlers/handleHI.ts b/webAO/packets/handlers/handleHI.ts
new file mode 100644
index 0000000..d15be0d
--- /dev/null
+++ b/webAO/packets/handlers/handleHI.ts
@@ -0,0 +1,14 @@
+import { client } from "../../client";
+const version = process.env.npm_package_version;
+
+
+/**
+ * Handle the player
+ * @param {Array} args packet arguments
+ */
+export const handleHI = (_args: string[]) => {
+ client.sendSelf(`ID#1#webAO#${version}#%`);
+ client.sendSelf(
+ "FL#fastloading#yellowtext#cccc_ic_support#flipping#looping_sfx#effects#%"
+ );
+}
diff --git a/webAO/packets/handlers/handleHP.ts b/webAO/packets/handlers/handleHP.ts
new file mode 100644
index 0000000..f365590
--- /dev/null
+++ b/webAO/packets/handlers/handleHP.ts
@@ -0,0 +1,23 @@
+import { client } from "../../client";
+
+
+ /**
+ * Handles a change in the health bars' states.
+ * @param {Array} args packet arguments
+ */
+export const handleHP = (args: string[]) => {
+ const percent_hp = Number(args[2]) * 10;
+ let healthbox;
+ if (args[1] === "1") {
+ // Def hp
+ client.hp[0] = Number(args[2]);
+ healthbox = document.getElementById("client_defense_hp");
+ } else {
+ // Pro hp
+ client.hp[1] = Number(args[2]);
+ healthbox = document.getElementById("client_prosecutor_hp");
+ }
+ (<HTMLElement>(
+ healthbox.getElementsByClassName("health-bar")[0]
+ )).style.width = `${percent_hp}%`;
+ } \ No newline at end of file
diff --git a/webAO/packets/handlers/handleID.ts b/webAO/packets/handlers/handleID.ts
new file mode 100644
index 0000000..7def483
--- /dev/null
+++ b/webAO/packets/handlers/handleID.ts
@@ -0,0 +1,24 @@
+import { client, setOldLoading } from "../../client";
+
+
+/**
+ * Identifies the server and issues a playerID
+ * @param {Array} args packet arguments
+ */
+export const handleID = (args: string[]) => {
+ client.playerID = Number(args[1]);
+ const serverSoftware = args[2].split("&")[0];
+ let serverVersion;
+ if (serverSoftware === "serverD") {
+ serverVersion = args[2].split("&")[1];
+ } else if (serverSoftware === "webAO") {
+ setOldLoading(false);
+ client.sendSelf("PN#0#1#%");
+ } else {
+ serverVersion = args[3];
+ }
+
+ if (serverSoftware === "serverD" && serverVersion === "1377.152") {
+ setOldLoading(true);
+ } // bugged version
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleKB.ts b/webAO/packets/handlers/handleKB.ts
new file mode 100644
index 0000000..8705b83
--- /dev/null
+++ b/webAO/packets/handlers/handleKB.ts
@@ -0,0 +1,12 @@
+import { client, setBanned } from "../../client";
+import { safeTags } from "../../encoding";
+
+/**
+ * Handles the banned packet
+ * this one is sent when you are kicked off the server
+ * @param {Array} args ban reason
+ */
+export const handleKB = (args: string[]) => {
+ client.handleBans("Banned", safeTags(args[1]));
+ setBanned(true);
+}
diff --git a/webAO/packets/handlers/handleKK.ts b/webAO/packets/handlers/handleKK.ts
new file mode 100644
index 0000000..fd9a88c
--- /dev/null
+++ b/webAO/packets/handlers/handleKK.ts
@@ -0,0 +1,11 @@
+import { client } from "../../client";
+import { safeTags } from "../../encoding";
+
+
+/**
+ * Handles the kicked packet
+ * @param {Array} args kick reason
+ */
+export const handleKK = (args: string[]) => {
+ client.handleBans("Kicked", safeTags(args[1]));
+}
diff --git a/webAO/packets/handlers/handleMM.ts b/webAO/packets/handlers/handleMM.ts
new file mode 100644
index 0000000..077140f
--- /dev/null
+++ b/webAO/packets/handlers/handleMM.ts
@@ -0,0 +1,8 @@
+
+/**
+ * Handles the "MusicMode" packet
+ * @param {Array} args packet arguments
+ */
+export const handleMM = (_args: string[]) => {
+ // It's unused nowadays, as preventing people from changing the music is now serverside
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handlePN.ts b/webAO/packets/handlers/handlePN.ts
new file mode 100644
index 0000000..b16f77d
--- /dev/null
+++ b/webAO/packets/handlers/handlePN.ts
@@ -0,0 +1,9 @@
+import { client } from "../../client";
+
+/**
+ * Indicates how many users are on this server
+ * @param {Array} args packet arguments
+ */
+export const handlePN = (_args: string[]) => {
+ client.sendServer("askchaa#%");
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleRC.ts b/webAO/packets/handlers/handleRC.ts
new file mode 100644
index 0000000..92c1e8e
--- /dev/null
+++ b/webAO/packets/handlers/handleRC.ts
@@ -0,0 +1,10 @@
+import { client } from "../../client";
+import vanilla_character_arr from "../../constants/characters.js";
+
+/**
+ * we are asking ourselves what characters there are
+ * @param {Array} args packet arguments
+ */
+export const handleRC = (_args: string[]) => {
+ client.sendSelf(`SC#${vanilla_character_arr.join("#")}#%`);
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleRT.ts b/webAO/packets/handlers/handleRT.ts
new file mode 100644
index 0000000..5bbf2b2
--- /dev/null
+++ b/webAO/packets/handlers/handleRT.ts
@@ -0,0 +1,25 @@
+import { client } from "../../client";
+
+
+/**
+ * Handles a testimony states.
+ * @param {Array} args packet arguments
+ */
+export const handleRT = (args: string[]) => {
+ const judgeid = Number(args[2]);
+ switch (args[1]) {
+ case "testimony1":
+ client.testimonyID = 1;
+ break;
+ case "testimony2":
+ // Cross Examination
+ client.testimonyID = 2;
+ break;
+ case "judgeruling":
+ client.testimonyID = 3 + judgeid;
+ break;
+ default:
+ console.warn("Invalid testimony");
+ }
+ client.viewport.initTestimonyUpdater();
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleSI.ts b/webAO/packets/handlers/handleSI.ts
new file mode 100644
index 0000000..e040c83
--- /dev/null
+++ b/webAO/packets/handlers/handleSI.ts
@@ -0,0 +1,41 @@
+import { client, extrafeatures, oldLoading } from "../../client";
+
+
+/**
+ * Received when the server announces its server info,
+ * but we use it as a cue to begin retrieving characters.
+ * @param {Array} args packet arguments
+ */
+export const handleSI = (args: string[]) => {
+ client.char_list_length = Number(args[1]);
+ client.char_list_length += 1; // some servers count starting from 0 some from 1...
+ client.evidence_list_length = Number(args[2]);
+ client.music_list_length = Number(args[3]);
+
+ (<HTMLProgressElement>document.getElementById("client_loadingbar")).max =
+ client.char_list_length +
+ client.evidence_list_length +
+ client.music_list_length;
+
+ // create the charselect grid, to be filled by the character loader
+ document.getElementById("client_chartable")!.innerHTML = "";
+
+ for (let i = 0; i < client.char_list_length; i++) {
+ const demothing = document.createElement("img");
+
+ demothing.className = "demothing";
+ demothing.id = `demo_${i}`;
+ const demoonclick = document.createAttribute("onclick");
+ demoonclick.value = `pickChar(${i})`;
+ demothing.setAttributeNode(demoonclick);
+
+ document.getElementById("client_chartable")!.appendChild(demothing);
+ }
+
+ // this is determined at the top of this file
+ if (!oldLoading && extrafeatures.includes("fastloading")) {
+ client.sendServer("RC#%");
+ } else {
+ client.sendServer("askchar2#%");
+ }
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleSM.ts b/webAO/packets/handlers/handleSM.ts
new file mode 100644
index 0000000..5dab83e
--- /dev/null
+++ b/webAO/packets/handlers/handleSM.ts
@@ -0,0 +1,36 @@
+import { client } from '../../client'
+/**
+ * Handles incoming music information, containing all music in one packet.
+ * @param {Array} args packet arguments
+ */
+export const handleSM = (args: string[]) => {
+ document.getElementById("client_loadingtext")!.innerHTML = "Loading Music ";
+ client.resetMusicList();
+ client.resetAreaList();
+
+ client.musics_time = false;
+
+ for (let i = 1; i < args.length - 1; i++) {
+ // Check when found the song for the first time
+ const trackname = args[i];
+ const trackindex = i - 1;
+ document.getElementById(
+ "client_loadingtext"
+ )!.innerHTML = `Loading Music ${i}/${client.music_list_length}`;
+ (<HTMLProgressElement>(
+ document.getElementById("client_loadingbar")
+ )).value = client.char_list_length + client.evidence_list_length + i;
+ if (client.musics_time) {
+ client.addTrack(trackname);
+ } else if (client.isAudio(trackname)) {
+ client.musics_time = true;
+ client.fix_last_area();
+ client.addTrack(trackname);
+ } else {
+ client.createArea(trackindex, trackname);
+ }
+ }
+
+ // Music done, carry on
+ client.sendServer("RD#%");
+}
diff --git a/webAO/packets/handlers/handleTI.ts b/webAO/packets/handlers/handleTI.ts
new file mode 100644
index 0000000..e418088
--- /dev/null
+++ b/webAO/packets/handlers/handleTI.ts
@@ -0,0 +1,21 @@
+/**
+ * Handles a timer update
+ * @param {Array} args packet arguments
+ */
+export const handleTI = (args: string[]) => {
+ const timerid = Number(args[1]);
+ const type = Number(args[2]);
+ const timer_value = args[3];
+ switch (type) {
+ case 0:
+ //
+ case 1:
+ document.getElementById(`client_timer${timerid}`)!.innerText =
+ timer_value;
+ case 2:
+ document.getElementById(`client_timer${timerid}`)!.style.display = "";
+ case 3:
+ document.getElementById(`client_timer${timerid}`)!.style.display =
+ "none";
+ }
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleZZ.ts b/webAO/packets/handlers/handleZZ.ts
new file mode 100644
index 0000000..1a56e49
--- /dev/null
+++ b/webAO/packets/handlers/handleZZ.ts
@@ -0,0 +1,22 @@
+import { AO_HOST, client } from "../../client";
+import { prepChat } from "../../encoding";
+
+
+/**
+ * Handles a modcall
+ * @param {Array} args packet arguments
+ */
+export const handleZZ = (args: string[]) => {
+ const oocLog = document.getElementById("client_ooclog")!;
+ oocLog.innerHTML += `$Alert: ${prepChat(args[1])}\r\n`;
+ if (oocLog.scrollTop > oocLog.scrollHeight - 60) {
+ oocLog.scrollTop = oocLog.scrollHeight;
+ }
+
+ client.viewport.getSfxAudio().pause();
+ const oldvolume = client.viewport.getSfxAudio().volume;
+ client.viewport.getSfxAudio().volume = 1;
+ client.viewport.getSfxAudio().src = `${AO_HOST}sounds/general/sfx-gallery.opus`;
+ client.viewport.getSfxAudio().play();
+ client.viewport.getSfxAudio().volume = oldvolume;
+} \ No newline at end of file
diff --git a/webAO/packets/handlers/handleaskchaa.ts b/webAO/packets/handlers/handleaskchaa.ts
new file mode 100644
index 0000000..5930bf0
--- /dev/null
+++ b/webAO/packets/handlers/handleaskchaa.ts
@@ -0,0 +1,10 @@
+import { client } from "../../client";
+import vanilla_character_arr from "../../constants/characters.js";
+
+/**
+ * What? you want a character list from me??
+ * @param {Array} args packet arguments
+ */
+export const handleaskchaa = (_args: string[]) => {
+ client.sendSelf(`SI#${vanilla_character_arr.length}#0#0#%`);
+}