diff options
| author | stonedDiscord <Tukz@gmx.de> | 2026-02-11 12:28:49 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-11 12:28:49 +0100 |
| commit | 8e7af5b93009c987a4c0c3fef014cf7b64463e03 (patch) | |
| tree | 04b0c383ca35962dc3054ec7ace34bb64f019622 | |
| parent | 726d89ce8605d27be1c14bba59f81cfd6254ccb3 (diff) | |
| parent | 5bb35a981e2f35df7fbf126faa8655ee3b3ca142 (diff) | |
Merge pull request #293 from OmniTroid/better-charloading
Better charloading
| -rw-r--r-- | webAO/client.ts | 3 | ||||
| -rw-r--r-- | webAO/client/aoHost.ts | 3 | ||||
| -rw-r--r-- | webAO/client/checkCallword.ts | 2 | ||||
| -rw-r--r-- | webAO/client/handleCharacterInfo.ts | 176 | ||||
| -rw-r--r-- | webAO/dom/areaClick.ts | 4 | ||||
| -rw-r--r-- | webAO/dom/renderPlayerList.ts | 58 | ||||
| -rw-r--r-- | webAO/dom/updatePlayerAreas.ts | 24 | ||||
| -rw-r--r-- | webAO/packets/handlers/handleASS.ts | 2 | ||||
| -rw-r--r-- | webAO/packets/handlers/handleMC.ts | 2 | ||||
| -rw-r--r-- | webAO/packets/handlers/handleMS.ts | 5 | ||||
| -rw-r--r-- | webAO/packets/handlers/handlePR.ts | 56 | ||||
| -rw-r--r-- | webAO/packets/handlers/handlePU.ts | 36 | ||||
| -rw-r--r-- | webAO/packets/handlers/handlePV.ts | 3 | ||||
| -rw-r--r-- | webAO/packets/handlers/handleRMC.ts | 2 | ||||
| -rw-r--r-- | webAO/packets/handlers/handleSC.ts | 7 | ||||
| -rw-r--r-- | webAO/packets/handlers/handleSI.ts | 1 | ||||
| -rw-r--r-- | webAO/packets/handlers/handleZZ.ts | 2 | ||||
| -rw-r--r-- | webAO/styles/client.css | 17 | ||||
| -rw-r--r-- | webAO/viewport/utils/handleICSpeaking.ts | 2 | ||||
| -rw-r--r-- | webAO/viewport/utils/initTestimonyUpdater.ts | 2 | ||||
| -rw-r--r-- | webAO/viewport/viewport.ts | 6 |
21 files changed, 235 insertions, 178 deletions
diff --git a/webAO/client.ts b/webAO/client.ts index b7a15a4..05a40c9 100644 --- a/webAO/client.ts +++ b/webAO/client.ts @@ -150,6 +150,8 @@ class Client extends EventEmitter { connect: () => void; loadResources: () => void; isLowMemory: () => void; + /** Maps player ID to player data */ + playerlist: Map<number, { charId: number; charName: string; showName: string; name: string; area: number }>; charicon_extensions: string[]; emote_extensions: string[]; emotions_extensions: string[]; @@ -211,6 +213,7 @@ class Client extends EventEmitter { this.temp_packet = ""; loadResources; isLowMemory; + this.playerlist = new Map(); this.charicon_extensions = [".png", ".webp"]; this.emote_extensions = [".gif", ".png", ".apng", ".webp", ".webp.static"]; this.emotions_extensions = [".png", ".webp"]; diff --git a/webAO/client/aoHost.ts b/webAO/client/aoHost.ts index 003e237..0d7cb0f 100644 --- a/webAO/client/aoHost.ts +++ b/webAO/client/aoHost.ts @@ -2,7 +2,7 @@ import queryParser from "../utils/queryParser"; const { asset } = queryParser(); export let AO_HOST = asset; -export const setAOhost = (val: string) => { +export const setAOhost = (val: string): string => { const currentProtocol = window.location.protocol; const assetProtocol = val.split(":")[0] + ":"; @@ -19,4 +19,5 @@ export const setAOhost = (val: string) => { AO_HOST = val; } console.log("Asset URL ist now " + AO_HOST); + return AO_HOST; }; diff --git a/webAO/client/checkCallword.ts b/webAO/client/checkCallword.ts index b7413f7..795eac9 100644 --- a/webAO/client/checkCallword.ts +++ b/webAO/client/checkCallword.ts @@ -11,7 +11,7 @@ export function checkCallword(message: string, sfxAudio: HTMLAudioElement) { if (item !== "" && message.toLowerCase().includes(item.toLowerCase())) { sfxAudio.pause(); sfxAudio.src = `${AO_HOST}sounds/general/sfx-gallery.opus`; - sfxAudio.play(); + sfxAudio.play().catch(() => {}); } } } diff --git a/webAO/client/handleCharacterInfo.ts b/webAO/client/handleCharacterInfo.ts index 3764c84..28992e9 100644 --- a/webAO/client/handleCharacterInfo.ts +++ b/webAO/client/handleCharacterInfo.ts @@ -2,50 +2,22 @@ import { client } from "../client"; import { safeTags } from "../encoding"; import iniParse from "../iniParse"; import request from "../services/request"; -import fileExists from "../utils/fileExists"; import { AO_HOST } from "./aoHost"; -export const getCharIcon = async (img: HTMLImageElement, charname: string) => { - img.alt = charname; - const charIconBaseUrl = `${AO_HOST}characters/${encodeURI( - charname.toLowerCase(), - )}/char_icon`; - for (let i = 0; i < client.charicon_extensions.length; i++) { - const fileUrl = charIconBaseUrl + client.charicon_extensions[i]; - const exists = await fileExists(fileUrl); - if (exists) { - img.alt = charname; - img.title = charname; - img.src = fileUrl; - return; - } - } -}; - /** - * Handles the incoming character information, and downloads the sprite + ini for it - * @param {Array} chargs packet arguments - * @param {Number} charid character ID + * Lightweight character setup that runs on join. Sets the icon src directly + * (letting the browser handle loading) and stores default character data. + * Does NOT fetch char.ini — that is deferred until needed via ensureCharIni. */ -export const handleCharacterInfo = async (chargs: string[], charid: number) => { +export const setupCharacterBasic = (chargs: string[], charid: number) => { const img = <HTMLImageElement>document.getElementById(`demo_${charid}`); if (chargs[0]) { - let cini: any = {}; - - getCharIcon(img, chargs[0]); - - // If the ini doesn't exist on the server this will throw an error - try { - const cinidata = await request( - `${AO_HOST}characters/${encodeURI(chargs[0].toLowerCase())}/char.ini`, - ); - cini = iniParse(cinidata); - } catch (err) { - cini = {}; - img.classList.add("noini"); - console.warn(`character ${chargs[0]} is missing from webAO`); - // If it does, give the user a visual indication that the character is unusable - } + img.alt = chargs[0]; + img.title = chargs[0]; + const iconExt = client.charicon_extensions[0] || ".png"; + img.src = `${AO_HOST}characters/${encodeURI( + chargs[0].toLowerCase(), + )}/char_icon${iconExt}`; const mute_select = <HTMLSelectElement>( document.getElementById("mute_select") @@ -56,47 +28,109 @@ export const handleCharacterInfo = async (chargs: string[], charid: number) => { ); pair_select.add(new Option(safeTags(chargs[0]), String(charid))); - // sometimes ini files lack important settings - const default_options = { - name: chargs[0], - showname: chargs[0], - side: "def", - blips: "male", - chat: "", - category: "", - }; - cini.options = Object.assign(default_options, cini.options); - - // sometimes ini files lack important settings - const default_emotions = { - number: 0, - }; - cini.emotions = Object.assign(default_emotions, cini.emotions); - + // Store defaults — these get replaced with actual ini values by ensureCharIni client.chars[charid] = { name: safeTags(chargs[0]), - showname: safeTags(cini.options.showname), + showname: safeTags(chargs[0]), desc: safeTags(chargs[1]), - blips: safeTags(cini.options.blips).toLowerCase(), - gender: safeTags(cini.options.gender).toLowerCase(), - side: safeTags(cini.options.side).toLowerCase(), - chat: - cini.options.chat === "" - ? safeTags(cini.options.category).toLowerCase() - : safeTags(cini.options.chat).toLowerCase(), + blips: "male", + gender: "", + side: "def", + chat: "", evidence: chargs[3], - icon: img.src, - inifile: cini, + icon: "", muted: false, }; + } else { + console.warn(`missing charid ${charid}`); + img.style.display = "none"; + } +}; - if ( - client.chars[charid].blips === "male" && - client.chars[charid].gender !== "male" && - client.chars[charid].gender !== "" - ) { - client.chars[charid].blips = client.chars[charid].gender; +/** + * Fetches and parses char.ini for a character if not already loaded. + * Replaces default values in client.chars[charid] with actual ini values. + */ +export const ensureCharIni = async (charid: number): Promise<any> => { + const char = client.chars[charid]; + if (!char) return {}; + if (char.inifile) return char.inifile; + + const img = <HTMLImageElement>document.getElementById(`demo_${charid}`); + let cini: any = {}; + + try { + const cinidata = await request( + `${AO_HOST}characters/${encodeURI(char.name.toLowerCase())}/char.ini`, + ); + cini = iniParse(cinidata); + } catch (err) { + cini = {}; + if (img) img.classList.add("noini"); + console.warn(`character ${char.name} is missing from webAO`); + } + + const default_options = { + name: char.name, + showname: char.name, + side: "def", + blips: "male", + chat: "", + category: "", + }; + cini.options = Object.assign(default_options, cini.options); + + const default_emotions = { + number: 0, + }; + cini.emotions = Object.assign(default_emotions, cini.emotions); + + // Replace defaults with actual ini values + char.showname = safeTags(cini.options.showname); + char.blips = safeTags(cini.options.blips).toLowerCase(); + char.gender = safeTags(cini.options.gender).toLowerCase(); + char.side = safeTags(cini.options.side).toLowerCase(); + char.chat = + cini.options.chat === "" + ? safeTags(cini.options.category).toLowerCase() + : safeTags(cini.options.chat).toLowerCase(); + char.icon = img ? img.src : ""; + char.inifile = cini; + + if ( + char.blips === "male" && + char.gender !== "male" && + char.gender !== "" + ) { + char.blips = char.gender; + } + + return cini; +}; + +/** + * Full character info load (used by iniEdit and handleMS ini-edit path). + * Fetches icon + ini for a single character, replacing any existing data. + */ +export const handleCharacterInfo = async (chargs: string[], charid: number) => { + const img = <HTMLImageElement>document.getElementById(`demo_${charid}`); + if (chargs[0]) { + img.alt = chargs[0]; + img.title = chargs[0]; + const iconExt = client.charicon_extensions[0] || ".png"; + img.src = `${AO_HOST}characters/${encodeURI( + chargs[0].toLowerCase(), + )}/char_icon${iconExt}`; + + // Reset inifile so ensureCharIni will re-fetch + if (client.chars[charid]) { + client.chars[charid].name = safeTags(chargs[0]); + client.chars[charid].inifile = null; + } else { + setupCharacterBasic(chargs, charid); } + + await ensureCharIni(charid); } else { console.warn(`missing charid ${charid}`); img.style.display = "none"; diff --git a/webAO/dom/areaClick.ts b/webAO/dom/areaClick.ts index 1e41f4b..27682c7 100644 --- a/webAO/dom/areaClick.ts +++ b/webAO/dom/areaClick.ts @@ -1,5 +1,5 @@ import { client } from "../client"; -import { updatePlayerAreas } from "./updatePlayerAreas"; +import { renderPlayerList } from "./renderPlayerList"; /** * Triggered when an item on the area list is clicked. * @param {HTMLElement} el @@ -13,6 +13,6 @@ export function area_click(el: HTMLElement) { areaHr.textContent = `switched to ${el.textContent}`; document.getElementById("client_log")!.appendChild(areaHr); client.area = Number(el.id.substring(4)); - updatePlayerAreas(client.area); + renderPlayerList(); } window.area_click = area_click; diff --git a/webAO/dom/renderPlayerList.ts b/webAO/dom/renderPlayerList.ts new file mode 100644 index 0000000..48354ae --- /dev/null +++ b/webAO/dom/renderPlayerList.ts @@ -0,0 +1,58 @@ +import { client } from "../client"; +import { AO_HOST } from "../client/aoHost"; + +export function renderPlayerList() { + const list = document.getElementById("client_playerlist") as HTMLTableElement; + list.innerHTML = ""; + + const header = list.createTHead().insertRow(); + for (const label of ["Icon", "Character", "Showname", "OOC Name"]) { + const th = document.createElement("th"); + th.textContent = label; + header.appendChild(th); + } + + const body = list.createTBody(); + for (const [playerID, player] of client.playerlist) { + const playerRow = body.insertRow(); + playerRow.id = `client_playerlist_entry${playerID}`; + playerRow.style.display = player.area === client.area ? "" : "none"; + + const imgCell = playerRow.insertCell(0); + imgCell.style.width = "64px"; + const img = document.createElement("img"); + img.style.maxWidth = "60px"; + img.style.maxHeight = "60px"; + if (player.charName) { + const iconExt = client.charicon_extensions[0] || ".png"; + img.src = `${AO_HOST}characters/${encodeURI(player.charName.toLowerCase())}/char_icon${iconExt}`; + img.alt = player.charName; + img.title = player.charName; + } + imgCell.appendChild(img); + + const charNameCell = playerRow.insertCell(1); + charNameCell.textContent = + player.charName ? `[${playerID}] ${player.charName}` : ""; + + const showNameCell = playerRow.insertCell(2); + showNameCell.textContent = player.showName; + + const oocNameCell = playerRow.insertCell(3); + oocNameCell.textContent = player.name; + + const kickCell = playerRow.insertCell(4); + kickCell.style.width = "64px"; + const kick = document.createElement("button"); + kick.innerText = "Kick"; + kick.onclick = () => window.kickPlayer(playerID); + kickCell.appendChild(kick); + + const banCell = playerRow.insertCell(5); + banCell.style.width = "64px"; + const ban = document.createElement("button"); + ban.innerText = "Ban"; + ban.onclick = () => window.banPlayer(playerID); + banCell.appendChild(ban); + } +} diff --git a/webAO/dom/updatePlayerAreas.ts b/webAO/dom/updatePlayerAreas.ts deleted file mode 100644 index 99eccf1..0000000 --- a/webAO/dom/updatePlayerAreas.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { client } from "../client"; -import { area_click } from "./areaClick"; -/** - * Triggered when someone switches areas - * @param {Number} ownarea - */ -export function updatePlayerAreas(ownarea: number) { - for (let i = 0; i < client.areas.length; i++) { - if (i === ownarea) - for (let classelement of Array.from( - document.getElementsByClassName( - `area${i}`, - ) as HTMLCollectionOf<HTMLElement>, - )) - classelement.style.display = ""; - else - for (let classelement of Array.from( - document.getElementsByClassName( - `area${i}`, - ) as HTMLCollectionOf<HTMLElement>, - )) - classelement.style.display = "none"; - } -} diff --git a/webAO/packets/handlers/handleASS.ts b/webAO/packets/handlers/handleASS.ts index a46d68e..c50443a 100644 --- a/webAO/packets/handlers/handleASS.ts +++ b/webAO/packets/handlers/handleASS.ts @@ -1,4 +1,5 @@ import { setAOhost } from "../../client/aoHost"; +import { renderPlayerList } from "../../dom/renderPlayerList"; /** * new asset url!! @@ -6,4 +7,5 @@ import { setAOhost } from "../../client/aoHost"; */ export const handleASS = (args: string[]) => { if (args[1] !== "None") setAOhost(args[1]); + renderPlayerList(); }; diff --git a/webAO/packets/handlers/handleMC.ts b/webAO/packets/handlers/handleMC.ts index 8a1c0a2..44237ea 100644 --- a/webAO/packets/handlers/handleMC.ts +++ b/webAO/packets/handlers/handleMC.ts @@ -24,7 +24,7 @@ export const handleMC = (args: string[]) => { music.src = `${AO_HOST}sounds/music/${encodeURI(track.toLowerCase())}`; } music.loop = looping; - music.play(); + music.play().catch(() => {}); try { musicname = client.chars[charID].name; diff --git a/webAO/packets/handlers/handleMS.ts b/webAO/packets/handlers/handleMS.ts index 2622fe6..9b46bc0 100644 --- a/webAO/packets/handlers/handleMS.ts +++ b/webAO/packets/handlers/handleMS.ts @@ -1,7 +1,7 @@ /* eslint indent: ["error", 2, { "SwitchCase": 1 }] */ import { client, extrafeatures, UPDATE_INTERVAL } from "../../client"; -import { handleCharacterInfo } from "../../client/handleCharacterInfo"; +import { handleCharacterInfo, ensureCharIni } from "../../client/handleCharacterInfo"; import { resetICParams } from "../../client/resetICParams"; import { prepChat, safeTags } from "../../encoding"; import { handle_ic_speaking } from "../../viewport/utils/handleICSpeaking"; @@ -27,6 +27,9 @@ export const handleMS = (args: string[]) => { ); const chargs = (`${char_name}&` + "iniediter").split("&"); handleCharacterInfo(chargs, char_id); + } else if (!client.chars[char_id].inifile) { + // Lazily load char.ini in background so future messages have proper data + ensureCharIni(char_id); } } diff --git a/webAO/packets/handlers/handlePR.ts b/webAO/packets/handlers/handlePR.ts index aeb3969..f81d8dc 100644 --- a/webAO/packets/handlers/handlePR.ts +++ b/webAO/packets/handlers/handlePR.ts @@ -1,51 +1,5 @@ import { client } from "../../client"; -import { kickPlayer, banPlayer } from "../../dom/banPlayer"; - -function addPlayer(playerID: number) { - const list = <HTMLTableElement>document.getElementById("client_playerlist"); - const playerRow = list.insertRow(); - playerRow.id = `client_playerlist_entry${playerID}`; - playerRow.className = `area0`; - - const imgCell = playerRow.insertCell(0); - imgCell.style.width = "64px"; - const img = document.createElement("img"); - imgCell.appendChild(img); - - const name = document.createTextNode("No Data"); - - const charNameCell = playerRow.insertCell(1); - charNameCell.appendChild(name); - const showNameCell = playerRow.insertCell(2); - showNameCell.appendChild(name); - const oocNameCell = playerRow.insertCell(3); - oocNameCell.appendChild(name); - - const kickCell = playerRow.insertCell(4); - kickCell.style.width = "64px"; - const kick = <HTMLButtonElement>document.createElement("button"); - kick.innerText = "Kick"; - kick.onclick = () => { - window.kickPlayer(playerID); - }; - kickCell.appendChild(kick); - - const banCell = playerRow.insertCell(5); - banCell.style.width = "64px"; - const ban = <HTMLButtonElement>document.createElement("button"); - ban.innerText = "Ban"; - ban.onclick = () => { - window.banPlayer(playerID); - }; - banCell.appendChild(ban); -} - -function removePlayer(playerID: number) { - const playerRow = <HTMLTableElement>( - document.getElementById(`client_playerlist_entry${playerID}`) - ); - playerRow.remove(); -} +import { renderPlayerList } from "../../dom/renderPlayerList"; /** * Handles a player joining or leaving @@ -53,6 +7,10 @@ function removePlayer(playerID: number) { */ export const handlePR = (args: string[]) => { const playerID = Number(args[1]); - if (Number(args[2]) === 0) addPlayer(playerID); - else if (Number(args[2]) === 1) removePlayer(playerID); + if (Number(args[2]) === 0) { + client.playerlist.set(playerID, { charId: -1, charName: "", showName: "", name: "", area: 0 }); + } else if (Number(args[2]) === 1) { + client.playerlist.delete(playerID); + } + renderPlayerList(); }; diff --git a/webAO/packets/handlers/handlePU.ts b/webAO/packets/handlers/handlePU.ts index 508bb51..3b70ad3 100644 --- a/webAO/packets/handlers/handlePU.ts +++ b/webAO/packets/handlers/handlePU.ts @@ -1,36 +1,42 @@ import { client } from "../../client"; -import { getCharIcon } from "../../client/handleCharacterInfo"; -import { updatePlayerAreas } from "../../dom/updatePlayerAreas"; +import { ensureCharIni } from "../../client/handleCharacterInfo"; +import { renderPlayerList } from "../../dom/renderPlayerList"; /** * Handles a playerlist update * @param {Array} args packet arguments */ export const handlePU = (args: string[]) => { - const playerRow = <HTMLTableElement>( - document.getElementById(`client_playerlist_entry${Number(args[1])}`) - ); + const playerID = Number(args[1]); + const player = client.playerlist.get(playerID); + if (!player) return; + const type = Number(args[2]); const data = args[3]; + switch (type) { case 0: - const oocName = <HTMLElement>playerRow.childNodes[3]; - oocName.innerText = data; + player.name = data; break; case 1: - const playerImg = <HTMLImageElement>playerRow.childNodes[0].firstChild; - getCharIcon(playerImg, data); - const charName = <HTMLElement>playerRow.childNodes[1]; - charName.innerText = `[${args[1]}] ${data}`; + player.charName = data; + const charId = client.chars.findIndex( + (c: any) => c && c.name.toLowerCase() === data.toLowerCase() + ); + if (charId >= 0) { + player.charId = charId; + ensureCharIni(charId); + } break; case 2: - const showName = <HTMLElement>playerRow.childNodes[2]; - showName.innerText = data; + player.showName = data; break; case 3: - playerRow.className = `area${data}`; - updatePlayerAreas(client.area); + player.area = Number(data); + break; default: break; } + + renderPlayerList(); }; diff --git a/webAO/packets/handlers/handlePV.ts b/webAO/packets/handlers/handlePV.ts index a39a4dd..1ee13c8 100644 --- a/webAO/packets/handlers/handlePV.ts +++ b/webAO/packets/handlers/handlePV.ts @@ -3,6 +3,7 @@ import fileExists from "../../utils/fileExists"; import { updateActionCommands } from "../../dom/updateActionCommands"; import { pickEmotion } from "../../dom/pickEmotion"; import { AO_HOST } from "../../client/aoHost"; +import { ensureCharIni } from "../../client/handleCharacterInfo"; function addEmoteButton(i: number, imgurl: string, desc: string) { const emotesList = document.getElementById("client_emo"); @@ -34,7 +35,7 @@ export const handlePV = async (args: string[]) => { const emotesList = document.getElementById("client_emo"); emotesList.style.display = ""; emotesList.innerHTML = ""; // Clear emote box - const ini = me.inifile; + const ini = await ensureCharIni(client.charID); me.side = ini.options.side; updateActionCommands(me.side); if (ini.emotions.number === 0) { diff --git a/webAO/packets/handlers/handleRMC.ts b/webAO/packets/handlers/handleRMC.ts index 7758844..0cc6aa7 100644 --- a/webAO/packets/handlers/handleRMC.ts +++ b/webAO/packets/handlers/handleRMC.ts @@ -17,7 +17,7 @@ export const handleRMC = (args: string[]) => { music.currentTime += parseFloat( music.totime + (new Date().getTime() / 1000 - music.offset), ).toFixed(3); - music.play(); + music.play().catch(() => {}); }, false, ); diff --git a/webAO/packets/handlers/handleSC.ts b/webAO/packets/handlers/handleSC.ts index 271a164..f7b789f 100644 --- a/webAO/packets/handlers/handleSC.ts +++ b/webAO/packets/handlers/handleSC.ts @@ -1,7 +1,7 @@ import queryParser from "../../utils/queryParser"; import { client } from "../../client"; -import { handleCharacterInfo } from "../../client/handleCharacterInfo"; +import { setupCharacterBasic } from "../../client/handleCharacterInfo"; const { mode } = queryParser(); /** @@ -17,13 +17,10 @@ export const handleSC = async (args: string[]) => { document.getElementById("client_charselect")!.style.display = "block"; } - document.getElementById("client_loadingtext")!.innerHTML = - "Loading Characters"; for (let i = 1; i < args.length; i++) { const chargs = args[i].split("&"); const charid = i - 1; - - setTimeout(() => handleCharacterInfo(chargs, charid), charid * 6); + setupCharacterBasic(chargs, charid); } // We're done with the characters, request the music client.sender.sendServer("RM#%"); diff --git a/webAO/packets/handlers/handleSI.ts b/webAO/packets/handlers/handleSI.ts index f20f4b2..eac84e0 100644 --- a/webAO/packets/handlers/handleSI.ts +++ b/webAO/packets/handlers/handleSI.ts @@ -20,6 +20,7 @@ export const handleSI = (args: string[]) => { const demothing = document.createElement("img"); demothing.className = "demothing"; + demothing.loading = "lazy"; demothing.id = `demo_${i}`; const demoonclick = document.createAttribute("onclick"); demoonclick.value = `pickChar(${i})`; diff --git a/webAO/packets/handlers/handleZZ.ts b/webAO/packets/handlers/handleZZ.ts index 31ceef5..0496d42 100644 --- a/webAO/packets/handlers/handleZZ.ts +++ b/webAO/packets/handlers/handleZZ.ts @@ -18,6 +18,6 @@ export const handleZZ = (args: string[]) => { 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().play().catch(() => {}); client.viewport.getSfxAudio().volume = oldvolume; }; diff --git a/webAO/styles/client.css b/webAO/styles/client.css index 88eca59..e3ee820 100644 --- a/webAO/styles/client.css +++ b/webAO/styles/client.css @@ -714,6 +714,23 @@ opacity: 0.5; } +#client_playerlist { + width: 100%; + border-collapse: collapse; +} + +#client_playerlist th, +#client_playerlist td { + border-bottom: 1px solid #555; + padding: 4px 6px; + text-align: left; +} + +#client_playerlist th { + font-weight: bold; + border-bottom: 2px solid #888; +} + .hrtext { overflow: hidden; text-align: center; diff --git a/webAO/viewport/utils/handleICSpeaking.ts b/webAO/viewport/utils/handleICSpeaking.ts index 6cf665c..ec791c5 100644 --- a/webAO/viewport/utils/handleICSpeaking.ts +++ b/webAO/viewport/utils/handleICSpeaking.ts @@ -169,7 +169,7 @@ export const handle_ic_speaking = async (playerChatMsg: ChatMsg) => { )}/${shout}.opus`; const exists = await fileExists(perCharPath); client.viewport.shoutaudio.src = exists ? perCharPath : client.resources[shout].sfx; - client.viewport.shoutaudio.play(); + client.viewport.shoutaudio.play().catch(() => {}); client.viewport.setShoutTimer(client.resources[shout].duration); } else { client.viewport.setShoutTimer(0); diff --git a/webAO/viewport/utils/initTestimonyUpdater.ts b/webAO/viewport/utils/initTestimonyUpdater.ts index c1e4d93..a545c97 100644 --- a/webAO/viewport/utils/initTestimonyUpdater.ts +++ b/webAO/viewport/utils/initTestimonyUpdater.ts @@ -18,7 +18,7 @@ export const initTestimonyUpdater = () => { } client.viewport.testimonyAudio.src = client.resources[testimony].sfx; - client.viewport.testimonyAudio.play(); + client.viewport.testimonyAudio.play().catch(() => {}); const testimonyOverlay = <HTMLImageElement>( document.getElementById("client_testimony") diff --git a/webAO/viewport/viewport.ts b/webAO/viewport/viewport.ts index c03d750..a7363a9 100644 --- a/webAO/viewport/viewport.ts +++ b/webAO/viewport/viewport.ts @@ -104,7 +104,7 @@ const viewport = (): Viewport => { sfxAudio.pause(); sfxAudio.loop = looping; sfxAudio.src = sfxname; - sfxAudio.play(); + sfxAudio.play().catch(() => {}); }; /** * Updates the testimony overaly @@ -150,7 +150,7 @@ const viewport = (): Viewport => { const charEmote = chatmsg.sprite.toLowerCase(); if (chatmsg.content.charAt(textnow.length) !== " ") { - blipChannels[currentBlipChannel].play(); + blipChannels[currentBlipChannel].play().catch(() => {}); currentBlipChannel++; currentBlipChannel %= blipChannels.length; } @@ -356,7 +356,7 @@ const viewport = (): Viewport => { eviBox.style.opacity = "1"; testimonyAudio.src = `${AO_HOST}sounds/general/sfx-evidenceshoop.opus`; - testimonyAudio.play(); + testimonyAudio.play().catch(() => {}); if (chatmsg.side === "def") { // Only def show evidence on right |
