From 5685e2b1714c8d85e505e9eccee549723773aaab Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 15 May 2018 21:20:00 -0500 Subject: Move files to webAO folder; GoldenLayout works! --- webAO/client.js | 648 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 648 insertions(+) create mode 100644 webAO/client.js (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js new file mode 100644 index 0000000..f2bba28 --- /dev/null +++ b/webAO/client.js @@ -0,0 +1,648 @@ +/* +glorious webao +made by sD +credits to aleks for original idea and source +*/ +var queryDict = {}; +location.search.substr(1).split("&").forEach(function(item) { + queryDict[item.split("=")[0]] = item.split("=")[1] +}) + +//document.getElementById("client_wrapper").style = "width: 800px;"; +/* Server magic */ +//serv = new WebSocket("ws://51.255.160.217:50000"); +//serv = new WebSocket("ws://85.25.196.172:5000"); +var serverIP = queryDict.ip; +serv = new WebSocket("ws://" + serverIP); +var mode = queryDict.mode; +//var AO_HOST = "http://weedlan.de/"; +if (queryDict.asset === undefined) { + var AO_HOST = "http://assets.aceattorneyonline.com/base/"; +} else { + var AO_HOST = queryDict.asset; +} +var MUSIC_HOST = AO_HOST + "sounds/music/"; +var BAR_WIDTH = 90; +var BAR_HEIGHT = 20; +var textnow = ""; +var chatmsg = { + "isnew": false +}; +var blip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); +var womboblip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); +var comboblip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); +var sfxaudio = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); +var sfxplayed = 0; +var music = new Audio(); +music.play(); +blip.volume = 0.5; +womboblip.volume = 0.5; +comboblip.volume = 0.5; +combo = false; +var charselectWidth = 8; +var musiclist = Object(); +var ex = false; +var tempchars = []; +var chars = []; +var emotes = []; +var charcheck; +var pid = 1; +var bgname = 'gs4'; +var bgfolder = AO_HOST + "background/" + bgname + "/"; +// 0 = objection shout, 1 = pre-anim, 2 = speaking, 3 = silent +var chatstate = 3 +var position; +var me = -1; +var myemotion = -1; +var myschar = -1; +var objection_state = 0; +var updateInterval = 80; +var shouttimer = 0; +var texttimer = 0; +var updater; +var CHECKupdater; +var serv; +var oldloading=false; +if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) { + oldloading=true; +} +var carea = 0; +var linifile; +var pinifile; +serv.onopen = function(evt) { + onOpen(evt) +}; +serv.onclose = function(evt) { + onClose(evt) +}; +serv.onmessage = function(evt) { + onMessage(evt) +}; +serv.onerror = function(evt) { + onError(evt) +}; + +function parseINI(data) { + var regex = { + section: /^\s*\[\s*([^\]]*)\s*\]\s*$/, + param: /^\s*([\w\.\-\_]+)\s*=\s*(.*?)\s*$/, + comment: /^\s*;.*$/ + }; + var value = {}; + var lines = data.split(/\r\n|\r|\n/); + var section = null; + lines.forEach(function(line) { + if (regex.comment.test(line)) { + return; + } else if (line.length == 0) { + return; + } else if (regex.param.test(line)) { + var match = line.match(regex.param); + if (section) { + value[section][match[1]] = match[2]; + } else { + value[match[1]] = match[2]; + } + } else if (regex.section.test(line)) { + var match = line.match(regex.section); + value[match[1]] = {}; + section = match[1]; + }; + }); + return value; +} + +function escapeChat(estring) { + estring.replace("#", ""); + estring.replace("&", ""); + estring.replace("%", ""); + estring.replace("$", ""); + return estring; +} + +function onOOCEnter(event) { + if (event.keyCode == 13) { + serv.send("CT#web" + pid + "#" + escapeChat(document.getElementById("client_oocinputbox").value) + "#%"); + document.getElementById("client_oocinputbox").value = ""; + } +} + +function onEnter(event) { + if (event.keyCode == 13) { + mychar = chars[me] + myemo = emotes[myemotion] + if(document.getElementById("sendsfx").checked){ + ssfxname=myemo.sfx; + ssfxdelay=myemo.sfxdelay; + }else{ + ssfxname="0"; + ssfxdelay="0"; + } + ICmessage = "MS#chat#" + myemo.speaking + "#" + mychar.name + "#" + myemo.silent + "#" + escapeChat(document.getElementById("client_inputbox").value) + "#" + mychar.side + "#" + ssfxname +"#" + myemo.zoom + "#" + me + "#" + ssfxdelay + "#" + objection_state + "#0#0#0#0#%"; + serv.send(ICmessage); + document.getElementById("client_inputbox").value = ''; + if (objection_state) { + document.getElementById("button_" + objection_state).className = "client_button"; + objection_state = 0; + } + } +} + +function musiclist_click(event) { + var playtrack = document.getElementById("client_musiclist").value; + serv.send("MC#" + playtrack + "#" + me + "#%"); +} + +function changeMusicVolume() { + music.volume = document.getElementById("client_mvolume").value / 100; +} + +function changeSFXVolume() { + sfxaudio.volume = document.getElementById("client_svolume").value / 100; +} + +function changeBlipVolume() { + blip.volume = document.getElementById("client_bvolume").value / 100; + womboblip.volume = document.getElementById("client_bvolume").value / 100; + comboblip.volume = document.getElementById("client_bvolume").value / 100; +} + +function changeCharacter(event) { + serv.send("FC#%"); + document.getElementById("client_charselect").style.display = "block"; + document.getElementById("client_emo").innerHTML = ""; +} + +function imgError(image) { + image.onerror = ""; + image.src = "/misc/placeholder.gif"; + return true; +} + +function demoError(image) { + image.onerror = ""; + image.src = "/misc/placeholder.png"; + return true; +} + +function ImageExist(url) { + var img = new Image(); + img.src = url; + return img.height != 0; +} + +function changebg(position) { + var standname + bgfolder = AO_HOST + "background/" + escape(bgname) + "/"; + document.getElementById("client_fg").style.display = "none"; + document.getElementById("client_bench").style.display = "none"; + switch (position) { + case "def": + document.getElementById("client_court").src = bgfolder + "defenseempty.png" + document.getElementById("client_bench").style.display = "block"; + document.getElementById("client_bench").src = bgfolder + "defensedesk.png" + standname="defense"; + break; + case "pro": + document.getElementById("client_court").src = bgfolder + "prosecutorempty.png" + document.getElementById("client_bench").style.display = "block" + document.getElementById("client_bench").src = bgfolder + "prosecutiondesk.png" + standname="prosecution"; + break; + case "hld": + document.getElementById("client_court").src = bgfolder + "helperstand.png" + standname="defense"; + break; + case "hlp": + document.getElementById("client_court").src = bgfolder + "prohelperstand.png" + standname="prosecution"; + break; + case "wit": + document.getElementById("client_court").src = bgfolder + "witnessempty.png" + document.getElementById("client_bench").style.display = "block" + document.getElementById("client_bench").src = bgfolder + "estrado.png" + standname="prosecution"; + break; + case "jud": + document.getElementById("client_court").src = bgfolder + "judgestand.png" + standname="prosecution"; + break; + } + if(chatmsg.type==5){ + document.getElementById("client_bench").style.display = "none"; + document.getElementById("client_court").src = AO_HOST + "themes/default/"+standname+"_speedlines.gif"; + } +} + +function updateText() { + if (chatmsg.content.trim() == "") { + document.getElementById("client_name").style.display = "none"; + document.getElementById("client_chat").style.display = "none"; + } else { + document.getElementById("client_name").style.display = "block"; + document.getElementById("client_chat").style.display = "block"; + } + if (chatmsg.isnew){ + switch (chatmsg.objection) { + case "0": + shouttimer = 0; + break; + case "1": + document.getElementById("client_char").src = AO_HOST + "misc/holdit.gif"; + shouttimer = 800; + chatmsg.sound = "sfx-objection" + break; + case "2": + document.getElementById("client_char").src = AO_HOST + "misc/takethat.gif"; + shouttimer = 800; + chatmsg.sound = "sfx-objection" + break; + case "3": + document.getElementById("client_char").src = AO_HOST + "misc/objection.gif"; + shouttimer = 800; + chatmsg.sound = "sfx-objection" + break; + } + chatmsg.isnew=false; + chatmsg.startspeaking=true; +} + if (texttimer >= shouttimer) { + if (chatmsg.startspeaking) { + changebg(chatmsg.side); + document.getElementById("client_char").src = AO_HOST + "characters/" + escape(chatmsg.name) + "/" + chatmsg.speaking + ".gif"; + document.getElementById("client_name").style.fontSize = (document.getElementById("client_name").offsetHeight * 0.7) + "px"; + document.getElementById("client_chat").style.fontSize = (document.getElementById("client_chat").offsetHeight * 0.25) + "px"; + document.getElementById("client_name").innerHTML = "

" + escapeHtml(chatmsg.nameplate) + "

"; + switch(chatmsg.color){ + case "0": + stylecolor="color: #ffffff;"; + break; + case "1": + stylecolor="color: #00ff00;"; + break; + case "2": + stylecolor="color: #ff0000;"; + break; + case "3": + stylecolor="color: #ffaa00;"; + break; + case "4": + stylecolor="color: #0000ff;"; + break; + case "5": + stylecolor="color: #ffff00;"; + break; + case "6": + stylecolor="color: #aa00aa;"; + break; + } + document.getElementById("client_inner_chat").style = stylecolor; + chatmsg.startspeaking = false; + } else { + if (textnow != chatmsg.content) { + if(chatmsg.content.substring(textnow.length, textnow.length + 1)!=" "){ + combo = (combo + 1) % 2; + switch (combo) { + case 0: + blip.play() + break; + case 1: + //womboblip.play() + break; + } + } + textnow = chatmsg.content.substring(0, textnow.length + 1); + document.getElementById("client_inner_chat").innerHTML = escapeHtml(textnow); + if (textnow == chatmsg.content) { + chatstate = 3; + texttimer=0; + clearInterval(updater); + document.getElementById("client_char").src = AO_HOST + "characters/" + escape(chatmsg.name) + "/" + chatmsg.silent + ".gif"; + } + } + } + } + if (!sfxplayed && chatmsg.snddelay + shouttimer >= texttimer) { + sfxaudio.pause(); + sfxplayed = 1 + if (chatmsg.sound != "0" && chatmsg.sound != "1") { + sfxaudio.src = AO_HOST + "sounds/general/" + escape(chatmsg.sound) + ".wav"; + sfxaudio.play(); + } + } + texttimer = texttimer + updateInterval; +} + +function onOpen(e) { + if (mode == "join") { + serv.send("HI#" + navigator.userAgent + "#%"); + serv.send("ID#webAO#2.4.5#%"); + } else { + document.getElementById("client_loading").style.display = "none"; + } + CHECKupdater = setInterval(sendCheck, 5000); +}; + +function onClose(e) { + document.getElementById("client_error").style.display = "block"; +}; + +function ReconnectButton() { + serv = new WebSocket("ws://" + serverIP); + if (serv) { + serv.send("HI#" + navigator.userAgent + "#%"); + document.getElementById("client_error").style.display = "none"; + } +} + +function RetryButton() { +serv.send("HI#" + navigator.userAgent + "#%"); +} + +function onError(e) { + document.getElementById("client_error").style.display = "block"; +}; + +function onMessage(e) { + msg = e.data; + console.log(msg) + lines = msg.split('%'); + arguments = lines[0].split('#'); + header = arguments[0]; + switch (header) { + case "MS": + if (arguments[4] != chatmsg.content) { + document.getElementById("client_inner_chat").innerHTML = ''; + chatmsg.pre = escape(arguments[2]); + chatmsg.character = -1; + for (var i = 0; i < chars.length; i++) { + if (chars[i].name == arguments[3]) { + chatmsg.character = i; + break; + } + } + chatmsg.preanim = escape(arguments[2]); + chatmsg.nameplate = arguments[3]; + chatmsg.name = arguments[3]; + chatmsg.speaking = "(b)" + escape(arguments[4]); + chatmsg.silent = "(a)" + escape(arguments[4]); + chatmsg.content = escapeHtml(arguments[5]); + chatmsg.side = arguments[6]; + chatmsg.sound = escape(arguments[7]); + chatmsg.type = arguments[8]; + //chatmsg.charid = arguments[9]; + chatmsg.snddelay = arguments[10]; + chatmsg.objection = arguments[11]; + chatmsg.evidence = arguments[12]; + //chatmsg.flip = arguments[13]; + chatmsg.flash = arguments[14]; + chatmsg.color = arguments[15]; + chatmsg.isnew = true; + addlog(chatmsg.nameplate + ": " + escapeHtml(arguments[5])) + changebg(chatmsg.side); + textnow = ''; + sfxplayed = 0 + texttimer = 0 + updater = setInterval(updateText, updateInterval); + } + break; + case "CT": + document.getElementById("client_ooclog").innerHTML = document.getElementById("client_ooclog").innerHTML + arguments[1] + ": " + arguments[2] + "\r\n" + break; + case "MC": + music.pause(); + music.src = MUSIC_HOST + arguments[1]; + music.play(); + if (arguments[2] >= 0) { + musicname = chars[arguments[2]].name; + } else { + musicname = "$SERVER" + } + addlog(musicname + " changed music to " + arguments[1]); + break; + case "RMC": + music.pause(); + music = new Audio(musiclist[arguments[0]]); + music.totime = arguments[1] + music.offset = new Date().getTime() / 1000 + music.addEventListener('loadedmetadata', function() { + music.currentTime += parseFloat(music.totime + (new Date().getTime() / 1000 - music.offset)).toFixed(3); + music.play(); + }, false) + break; + case "CI": + document.getElementById("client_loadingtext").innerHTML = "Loading Character " + arguments[1]; + serv.send("AN#" + ((arguments[1] / 10) + 1) + "#%"); + for (var i = 2; i < arguments.length - 1; i++) { + if (i % 2 == 0) { + charguments = arguments[i].split("&"); + chars[arguments[i - 1]] = { + "name": charguments[0], + "desc": charguments[1], + "evidence": charguments[3], + "icon": AO_HOST + "characters/" + escape(charguments[0]) + "/char_icon.png" + }; + } + } + break; + case "SC": + document.getElementById("client_loadingtext").innerHTML = "Loading Characters"; + for (var i = 1; i < arguments.length - 1; i++) { + charguments = arguments[i].split("&"); + chars[i - 1] = { + "name": charguments[0], + "desc": charguments[1], + "evidence": charguments[3], + "icon": AO_HOST + "characters/" + escape(charguments[0]) + "/char_icon.png" + } + } + serv.send("RM#%"); + break; + case "EI": + document.getElementById("client_loadingtext").innerHTML = "Loading Evidence " + arguments[1]; + //serv.send("AE#" + (arguments[1] + 1) + "#%"); + serv.send("RM#%"); + break; + case "EM": + document.getElementById("client_loadingtext").innerHTML = "Loading Music " + arguments[1]; + serv.send("AM#" + ((arguments[1] / 10) + 1) + "#%"); + var hmusiclist = document.getElementById("client_musiclist"); + for (var i = 2; i < arguments.length - 1; i++) { + if (i % 2 == 0) { + var newentry = document.createElement("OPTION"); + newentry.text = arguments[i]; + hmusiclist.options.add(newentry); + } + } + break; + case "SM": + document.getElementById("client_loadingtext").innerHTML = "Loading Music "; + var hmusiclist = document.getElementById("client_musiclist"); + for (var i = 1; i < arguments.length - 1; i++) { + var newentry = document.createElement("OPTION"); + newentry.text = arguments[i]; + hmusiclist.options.add(newentry); + } + serv.send("RD#%"); + break; + case "music": + for (var i = 0; i < arguments.length / 2; i++) { + musiclist[arguments[2 * i]] = arguments[2 * i + 1]; + } + break; + case "DONE": + document.getElementById("client_loading").style.display = "none"; + document.getElementById("client_chatlog").style.display = "grid"; + document.getElementById("client_wrapper").style.display = "block"; + document.getElementById("client_charselect").style.display = "block"; + break; + case "BN": + bgname = arguments[1]; + break; + case "NBG": + /* TODO */ + break; + case "HP": + /* TODO */ + if (arguments[1] == 1) { + document.getElementById("client_defense_hp").style.clip = "rect(0px," + BAR_WIDTH * arguments[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; + } else { + document.getElementById("client_prosecutor_hp").style.clip = "rect(0px," + BAR_WIDTH * arguments[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; + } + break; + case "ID": + pid = arguments[1]; + case "PN": + serv.send("askchaa#%"); + break; + case "SI": + if(oldloading){ + serv.send("askchar2#%"); + }else{ + serv.send("RC#%"); + } + break; + case "CharsCheck": + document.getElementById("client_chartable").innerHTML = ""; + for (var i = 0; i < chars.length; i++) { + if (i % charselectWidth == 0) { + var tr = document.createElement('TR'); + } + var td = document.createElement('TD'); + var icon_chosen; + var thispick = chars[i].icon; + if (arguments[1 + i] == "-1") { + icon_chosen = " dark"; + } else { + icon_chosen = ""; + } + td.innerHTML = "" + chars[i].desc + ""; + tr.appendChild(td); + if (i % charselectWidth == 0) { + document.getElementById("client_chartable").appendChild(tr); + } + } + changebg("def"); + break; + case "PV": + me = arguments[3] + document.getElementById("client_charselect").style.display = "none"; + var xhr = new XMLHttpRequest(); + xhr.open('GET', AO_HOST + 'characters/' + escape(chars[me].name) + '/char.ini', true); + xhr.responseType = 'text'; + xhr.onload = function(e) { + if (this.status == 200) { + linifile = this.responseText; + pinifile = parseINI(linifile); + chars[me].side = pinifile.Options.side; + for (var i = 1; i < pinifile.Emotions.number; i++) { + var emoteinfo = pinifile.Emotions[i].split('#'); + esfx="0"; + esfxd="0"; + if (typeof pinifile.SoundN !== 'undefined') { + esfx=pinifile.SoundN[i]; + } + if (typeof pinifile.SoundT !== 'undefined') { + esfxd=pinifile.SoundT[i]; + } + emotes[i] = { + desc: emoteinfo[0], + speaking: emoteinfo[1], + silent: emoteinfo[2], + zoom: emoteinfo[3], + sfx: esfx, + sfxdelay: esfxd, + button_off: AO_HOST + 'characters/' + escape(chars[me].name) + '/emotions/button' + i + '_off.png', + button_on: AO_HOST + 'characters/' + escape(chars[me].name) + '/emotions/button' + i + '_on.png' + }; + document.getElementById("client_emo").innerHTML += "" + emotes[i].desc + ""; + } + pickemotion(1); + } + }; + xhr.send(); + break; + } +}; + +function addlog(toadd) { + document.getElementById("client_log").innerHTML = toadd + "
" + document.getElementById("client_log").innerHTML +} + +function pickchar(ccharacter) { + if (ccharacter < 1000) { + serv.send("CC#" + pid + "#" + ccharacter + "#web#%"); + } else { + //spectator + document.getElementById("client_charselect").style.display = "none"; + document.getElementById("client_inputbox").style.display = "none"; + document.getElementById("client_emo").style.display = "none"; + } +} + +function pickemotion(emo) { + if (myemotion != -1) { + document.getElementById("emo_" + myemotion).src = emotes[myemotion].button_off; + } + document.getElementById("emo_" + emo).src = emotes[emo].button_on; + myemotion = emo +} + +function toggleshout(shout) { + if (shout == objection_state) { + document.getElementById("button_" + shout).className = "client_button"; + objection_state = 0; + } else { + document.getElementById("button_" + shout).className = "client_button dark"; + if (objection_state) { + document.getElementById("button_" + objection_state).className = "client_button"; + } + objection_state = shout; + } +} + +function sendMusic(song) { + serv.send("MC#" + song); +} + +function sendCheck() { + serv.send("CH#" + me + "#%"); +} + +function escapeHtml(unsafe) { + var transfer = unsafe; + transfer.replace(/&/g, "&"); + transfer.replace(//g, ">"); + transfer.replace(/"/g, """); + transfer.replace(/'/g, "'"); + return transfer; +} + +if(typeof(String.prototype.trim) === "undefined") +{ + String.prototype.trim = function() + { + return String(this).replace(/^\s+|\s+$/g, ''); + }; +} \ No newline at end of file -- cgit From dca5bf29f3ad15f51d47696ad35525c516ca3db6 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Fri, 18 May 2018 23:07:57 -0500 Subject: Refactor and clean up client code --- webAO/client.js | 1251 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 740 insertions(+), 511 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index f2bba28..46ae84d 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -1,199 +1,734 @@ /* -glorious webao -made by sD -credits to aleks for original idea and source + * Glorious webAO + * made by sD, refactored by oldmud0 + * credits to aleks for original idea and source */ -var queryDict = {}; + +let queryDict = {}; location.search.substr(1).split("&").forEach(function(item) { queryDict[item.split("=")[0]] = item.split("=")[1] -}) +}); -//document.getElementById("client_wrapper").style = "width: 800px;"; /* Server magic */ -//serv = new WebSocket("ws://51.255.160.217:50000"); -//serv = new WebSocket("ws://85.25.196.172:5000"); -var serverIP = queryDict.ip; -serv = new WebSocket("ws://" + serverIP); -var mode = queryDict.mode; -//var AO_HOST = "http://weedlan.de/"; -if (queryDict.asset === undefined) { - var AO_HOST = "http://assets.aceattorneyonline.com/base/"; -} else { - var AO_HOST = queryDict.asset; -} -var MUSIC_HOST = AO_HOST + "sounds/music/"; -var BAR_WIDTH = 90; -var BAR_HEIGHT = 20; -var textnow = ""; -var chatmsg = { - "isnew": false -}; -var blip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); -var womboblip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); -var comboblip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); -var sfxaudio = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); -var sfxplayed = 0; -var music = new Audio(); + +const serverIP = queryDict.ip; +let mode = queryDict.mode; + +const AO_HOST = queryDict.asset || "http://assets.aceattorneyonline.com/base/"; +const MUSIC_HOST = AO_HOST + "sounds/music/"; +const BAR_WIDTH = 90; +const BAR_HEIGHT = 20; +const CHAR_SELECT_WIDTH = 8; +const UPDATE_INTERVAL = 80; + +let client = new Client(serverIP); +let viewport = new Viewport(); + +let music = new Audio(); music.play(); -blip.volume = 0.5; -womboblip.volume = 0.5; -comboblip.volume = 0.5; -combo = false; -var charselectWidth = 8; -var musiclist = Object(); -var ex = false; -var tempchars = []; -var chars = []; -var emotes = []; -var charcheck; -var pid = 1; -var bgname = 'gs4'; -var bgfolder = AO_HOST + "background/" + bgname + "/"; -// 0 = objection shout, 1 = pre-anim, 2 = speaking, 3 = silent -var chatstate = 3 -var position; -var me = -1; -var myemotion = -1; -var myschar = -1; -var objection_state = 0; -var updateInterval = 80; -var shouttimer = 0; -var texttimer = 0; -var updater; -var CHECKupdater; -var serv; -var oldloading=false; + +let oldLoading = false; if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) { - oldloading=true; + oldLoading = true; } -var carea = 0; -var linifile; -var pinifile; -serv.onopen = function(evt) { - onOpen(evt) -}; -serv.onclose = function(evt) { - onClose(evt) -}; -serv.onmessage = function(evt) { - onMessage(evt) -}; -serv.onerror = function(evt) { - onError(evt) -}; - -function parseINI(data) { - var regex = { - section: /^\s*\[\s*([^\]]*)\s*\]\s*$/, - param: /^\s*([\w\.\-\_]+)\s*=\s*(.*?)\s*$/, - comment: /^\s*;.*$/ - }; - var value = {}; - var lines = data.split(/\r\n|\r|\n/); - var section = null; - lines.forEach(function(line) { - if (regex.comment.test(line)) { - return; - } else if (line.length == 0) { - return; - } else if (regex.param.test(line)) { - var match = line.match(regex.param); - if (section) { - value[section][match[1]] = match[2]; + +let selectedShout = 0; + +export default class Client { + constructor(address) { + this.serv = new WebSocket("ws://" + serverIP); + + this.serv.onopen = (evt) => this.onOpen(evt); + this.serv.onclose = (evt) => this.onClose(evt); + this.serv.onmessage = (evt) => this.onMessage(evt); + this.serv.onerror = (evt) => this.onError(evt); + + this.playerID = 1; + this.charID = -1; + + this.chars = []; + this.emotes = []; + + this.selectedEmote = -1; + + this.checkUpdater = null; + + // Only used for RMC/`music` packets, not EM/SM/MC packets. + this.musicList = Object(); + } + + /** + * Gets the current player's character. + */ + me() { + return this.chars[this.charID]; + } + + /** + * Gets the player's currently selected emote. + */ + myEmote() { + return this.emotes[this.selectedEmote]; + } + + /** + * Sends an out-of-character chat message. + * @param {string} message the message to send + */ + sendOOC(message) { + this.serv.send(`CT#web${this.playerID}#${escapeChat(message)}#%`); + } + + /** + * Sends an in-character chat message. + * @param {string} speaking who is speaking + * @param {string} name the name of the current character + * @param {string} silent whether or not it's silent + * @param {string} message the message to be sent + * @param {string} side the name of the side in the background + * @param {string} ssfxname the name of the sound effect + * @param {string} zoom whether or not to zoom + * @param {string} ssfxdelay the delay (in milliseconds) to play the sound effect + * @param {string} objection the number of the shout to play + */ + sendIC(speaking, name, silent, message, side, ssfxname, zoom, ssfxdelay, objection) { + this.serv.send( + `MS#chat#${speaking}#${name}#${silent}` + `#${escapeChat(message)}#${side}#${ssfxname}#${zoom}` + `#${this.charID}#${ssfxdelay}#${selectedShout}#0#0#0#0#%` + ); + } + + /** + * Requests to change the music to the specified track. + * @param {string} track the track ID + */ + sendMusicChange(track) { + this.serv.send(`MC#${track}#${this.charID}#%`); + } + + /** + * Requests to leave the room and free the character slot. + * + * Note: This packet is undocumented. It is not implemented by + * either the AO2 client or tsuserver. + */ + sendLeaveRoom() { + this.serv.send("FC#%"); + } + + /** + * Begins the handshake process by sending an identifier + * to the server. + */ + joinServer() { + this.serv.send(`HI#${navigator.userAgent}#%`); + this.serv.send("ID#webAO#2.4.5#%"); + this.CHECKupdater = setInterval(() => this.sendCheck, 5000); + } + + /** + * Requests to play as a specified character. + * @param {number} character the character ID + */ + sendCharacter(character) { + this.serv.send(`CC#${this.playerID}#${character}#web#%`); + } + + /** + * Requests to select a music track. + * @param {number?} song the song to be played + */ + sendMusic(song) { + this.serv.send(`MC#${song}`); + } + + /** + * Sends a keepalive packet. + */ + sendCheck() { + this.serv.send(`CH#${this.charID}#%`); + } + + /** + * Triggered when a connection is established to the server. + */ + onOpen(e) { + // XXX: Why does watching mean just SITTING there and doing nothing? + if (mode == "join") { + client.joinServer(); + } else { + document.getElementById("client_loading").style.display = "none"; + } + } + + /** + * Triggered when the connection to the server closes. + * @param {CloseEvent} e + */ + onClose(e) { + document.getElementById("client_error").style.display = "block"; + this.cleanup(); + } + + /** + * Triggered when a packet is received from the server. + * @param {MessageEvent} e + */ + onMessage(e) { + let msg = e.data; + console.debug(msg); + let lines = msg.split('%'); + let arguments = lines[0].split('#'); + let header = arguments[0]; + this[`handle${header}`](arguments); + } + + /** + * Triggered when an network error occurs. + * @param {ErrorEvent} e + */ + onError(e) { + document.getElementById("client_error").style.display = "block"; + this.cleanup(); + } + + cleanup() { + clearInterval(this.checkUpdater); + } + + handleMS(arguments) { + // TODO: this if-statement might be a bug. + if (arguments[4] != viewport.chatmsg.content) { + document.getElementById("client_inner_chat").innerHTML = ""; + let chatmsg = { + pre: escape(arguments[2]), + character: -1, // Will do a linear search + preanim: escape(arguments[2]), // XXX: why again? + nameplate: arguments[3], // TODO: parse INI to get this info + name: arguments[3], + speaking: "(b)" + escape(arguments[4]), + silent: "(a)" + escape(arguments[4]), + content: escapeHtml(arguments[5]), + side: arguments[6], + sound: escape(arguments[7]), + type: arguments[8], + // charid: arguments[9], + snddelay: arguments[10], + objection: arguments[11], + evidence: arguments[12], + // flip: arguments[13], + flash: arguments[14], + color: arguments[15], + isnew: true, + }; + + // The dreaded linear search... + for (let i = 0; i < this.chars.length; i++) { + if (this.chars[i].name == arguments[3]) { + chatmsg.character = i; + break; + } + } + + viewport.say(chatmsg); + } + } + + handleCT(arguments) { + document.getElementById("client_ooclog").innerHTML = document.getElementById("client_ooclog").innerHTML + arguments[1] + ": " + arguments[2] + "\r\n"; + } + + handleMC(arguments) { + music.pause(); + music.src = MUSIC_HOST + arguments[1]; + music.play(); + if (arguments[2] >= 0) { + musicname = this.chars[arguments[2]].name; + appendICLog(`${musicname} changed music to ${arguments[1]}`); + } else { + appendICLog(`The music was changed to ${arguments[1]}`); + } + } + + handleRMC(arguments) { + music.pause(); + music = new Audio(this.musicList[arguments[1]]); + // Music offset + drift from song loading + music.totime = arguments[1]; + music.offset = new Date().getTime() / 1000; + music.addEventListener('loadedmetadata', function() { + music.currentTime += parseFloat(music.totime + (new Date().getTime() / 1000 - music.offset)).toFixed(3); + music.play(); + }, false); + } + + handleCI(arguments) { + document.getElementById("client_loadingtext").innerHTML = "Loading Character " + arguments[1]; + this.serv.send("AN#" + ((arguments[1] / 10) + 1) + "#%"); + for (let i = 2; i < arguments.length - 1; i++) { + if (i % 2 == 0) { + charguments = arguments[i].split("&"); + this.chars[arguments[i - 1]] = { + "name": charguments[0], + "desc": charguments[1], + "evidence": charguments[3], + "icon": AO_HOST + "characters/" + escape(charguments[0]) + "/char_icon.png" + }; + } + } + } + + handleSC(arguments) { + document.getElementById("client_loadingtext").innerHTML = "Loading Characters"; + for (let i = 1; i < arguments.length - 1; i++) { + charguments = arguments[i].split("&"); + this.chars[i - 1] = { + "name": charguments[0], + "desc": charguments[1], + "evidence": charguments[3], + "icon": AO_HOST + "characters/" + escape(charguments[0]) + "/char_icon.png" + } + } + this.serv.send("RM#%"); + } + + handleEI(arguments) { + document.getElementById("client_loadingtext").innerHTML = "Loading Evidence " + arguments[1]; + //serv.send("AE#" + (arguments[1] + 1) + "#%"); + this.serv.send("RM#%"); + } + + handleEM(arguments) { + document.getElementById("client_loadingtext").innerHTML = "Loading Music " + arguments[1]; + this.serv.send("AM#" + ((arguments[1] / 10) + 1) + "#%"); + let hmusiclist = document.getElementById("client_musiclist"); + for (let i = 2; i < arguments.length - 1; i++) { + if (i % 2 == 0) { + let newentry = document.createElement("OPTION"); + newentry.text = arguments[i]; + hmusiclist.options.add(newentry); + } + } + } + + handleSM(arguments) { + document.getElementById("client_loadingtext").innerHTML = "Loading Music "; + let hmusiclist = document.getElementById("client_musiclist"); + for (let i = 1; i < arguments.length - 1; i++) { + let newentry = document.createElement("OPTION"); + newentry.text = arguments[i]; + hmusiclist.options.add(newentry); + } + this.serv.send("RD#%"); + } + + handlemusic(arguments) { + for (let i = 0; i < arguments.length / 2; i++) { + this.musicList[arguments[2 * i]] = arguments[2 * i + 1]; + } + } + + handleDONE(arguments) { + document.getElementById("client_loading").style.display = "none"; + document.getElementById("client_chatlog").style.display = "grid"; + document.getElementById("client_wrapper").style.display = "block"; + document.getElementById("client_charselect").style.display = "block"; + } + + handleBN(arguments) { + viewport.bgname = escape(arguments[1]); + } + + handleNBG(arguments) { + // TODO (set by sD) + } + + handleHP(arguments) { + // TODO (set by sD) + // Also, this is broken. + if (arguments[1] == 1) { + document.getElementById("client_defense_hp").style.clip = "rect(0px," + BAR_WIDTH * arguments[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; + } else { + document.getElementById("client_prosecutor_hp").style.clip = "rect(0px," + BAR_WIDTH * arguments[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; + } + } + + handleID(arguments) { + this.playerID = arguments[1]; + } + + handlePN(arguments) { + this.serv.send("askchaa#%"); + } + + handleSI(arguments) { + if (oldLoading) { + this.serv.send("askchar2#%"); + } else { + this.serv.send("RC#%"); + } + } + + handleCharsCheck(arguments) { + document.getElementById("client_chartable").innerHTML = ""; + for (let i = 0; i < this.chars.length; i++) { + if (i % CHAR_SELECT_WIDTH == 0) { + var tr = document.createElement('TR'); + } + let td = document.createElement('TD'); + let icon_chosen; + let thispick = this.chars[i].icon; + if (arguments[1 + i] == "-1") { + icon_chosen = " dark"; } else { - value[match[1]] = match[2]; + icon_chosen = ""; + } + td.innerHTML = "" + chars[i].desc + ""; + tr.appendChild(td); + if (i % CHAR_SELECT_WIDTH == 0) { + document.getElementById("client_chartable").appendChild(tr); + } + } + changeBackground("def"); + } + + handlePV(arguments) { + this.charID = arguments[3]; + document.getElementById("client_charselect").style.display = "none"; + let xhr = new XMLHttpRequest(); + xhr.open('GET', AO_HOST + 'characters/' + escape(me().name) + '/char.ini', true); + xhr.responseType = 'text'; + xhr.onload = function(e) { + if (this.status == 200) { + let linifile = this.responseText; + let pinifile = INI.parse(linifile); + me().side = pinifile.Options.side; + for (let i = 1; i < pinifile.Emotions.number; i++) { + let emoteinfo = pinifile.Emotions[i].split('#'); + esfx = "0"; + esfxd = "0"; + if (typeof pinifile.SoundN !== 'undefined') { + esfx = pinifile.SoundN[i]; + } + if (typeof pinifile.SoundT !== 'undefined') { + esfxd = pinifile.SoundT[i]; + } + this.emotes[i] = { + desc: emoteinfo[0], + speaking: emoteinfo[1], + silent: emoteinfo[2], + zoom: emoteinfo[3], + sfx: esfx, + sfxdelay: esfxd, + button_off: AO_HOST + 'characters/' + escape(me().name) + '/emotions/button' + i + '_off.png', + button_on: AO_HOST + 'characters/' + escape(me().name) + '/emotions/button' + i + '_on.png' + }; + document.getElementById("client_emo").innerHTML += "" + emotes[i].desc + ""; + } + pickemotion(1); } - } else if (regex.section.test(line)) { - var match = line.match(regex.section); - value[match[1]] = {}; - section = match[1]; }; - }); - return value; + xhr.send(); + } } -function escapeChat(estring) { - estring.replace("#", ""); - estring.replace("&", ""); - estring.replace("%", ""); - estring.replace("$", ""); - return estring; +class Viewport { + constructor() { + this.textnow = ""; + this.chatmsg = { + "isnew": false, + "content": "", + "objection": "0", + "sound": "", + "startspeaking": false, + "side": null, + "color": "0", + "snddelay": 0 + }; + this.blip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); + this.blip.volume = 0.5; + + // Wombo + combo: two audio channels allocated to make blips less jittery + + // TODO: read blip type ("gender") from ini + this.womboblip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); + this.womboblip.volume = 0.5; + + this.comboblip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); + this.comboblip.volume = 0.5; + + this.combo = false; + + this.sfxaudio = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); + this.sfxplayed = 0; + + this.updater = null; + + this.bgname = "gs4"; + + this.shoutTimer = 0; + this.textTimer = 0; + } + + /** + * Returns the path which the background is located in. + */ + bgFolder() { + return `${AO_HOST}background/${bgname}/`; + } + + /** + * Sets a new emote. + * @param {object} chatmsg the new chat message + */ + say(chatmsg) { + this.chatmsg = chatmsg; + appendICLog(chatmsg.nameplate + ": " + escapeHtml(arguments[5])); + changeBackground(chatmsg.side); + this.textnow = ''; + this.sfxplayed = 0; + this.textTimer = 0; + this.updater = setInterval(() => this.updateText(), UPDATE_INTERVAL); + } + + /** + * Updates the chatbox based on the given text. + * + * XXX: This relies on a global variable `this.chatmsg`! + */ + updateText() { + if (this.chatmsg.content.trim() == "") { + document.getElementById("client_name").style.display = "none"; + document.getElementById("client_chat").style.display = "none"; + } else { + document.getElementById("client_name").style.display = "block"; + document.getElementById("client_chat").style.display = "block"; + } + + if (this.chatmsg.isnew) { + const shouts = { + "1": "holdit", + "2": "takethat", + "3": "objection" + }; + + let shout = shouts[this.chatmsg.objection]; + if (typeof shout !== "undefined") { + document.getElementById("client_char").src = AO_HOST + "misc/" + shout + ".gif"; + this.chatmsg.sound = "sfx-" + shout; + this.shoutTimer = 800; + } else { + this.shoutTimer = 0; + } + + this.chatmsg.isnew = false; + this.chatmsg.startspeaking = true; + } + + if (this.textTimer >= this.shoutTimer) { + if (this.chatmsg.startspeaking) { + changeBackground(this.chatmsg.side); + document.getElementById("client_char").src = AO_HOST + "characters/" + escape(this.chatmsg.name) + "/" + this.chatmsg.speaking + ".gif"; + document.getElementById("client_name").style.fontSize = (document.getElementById("client_name").offsetHeight * 0.7) + "px"; + document.getElementById("client_chat").style.fontSize = (document.getElementById("client_chat").offsetHeight * 0.25) + "px"; + document.getElementById("client_name").innerHTML = "

" + escapeHtml(this.chatmsg.nameplate) + "

"; + + const colors = { + "0": "#ffffff", + "1": "#00ff00", + "2": "#ff0000", + "3": "#ffaa00", + "4": "#0000ff", + "5": "#ffff00", + "6": "#aa00aa" + } + stylecolor = "color: " + (colors[this.chatmsg.color] || "#ffffff"); + document.getElementById("client_inner_chat").style = stylecolor; + this.chatmsg.startspeaking = false; + } else { + if (this.textnow != this.chatmsg.content) { + if (this.chatmsg.content.charAt(this.textnow.length) != " ") { + this.combo = (this.combo + 1) % 2; + switch (this.combo) { + case 0: + this.blip.play() + break; + case 1: + //this.womboblip.play() + break; + } + } + this.textnow = this.chatmsg.content.substring(0, this.textnow.length + 1); + document.getElementById("client_inner_chat").innerHTML = escapeHtml(this.textnow); + if (this.textnow == this.chatmsg.content) { + this.textTimer = 0; + clearInterval(this.updater); + document.getElementById("client_char").src = AO_HOST + "characters/" + escape(this.chatmsg.name) + "/" + this.chatmsg.silent + ".gif"; + } + } + } + } + if (!this.sfxplayed && this.chatmsg.snddelay + this.shoutTimer >= this.textTimer) { + this.sfxaudio.pause(); + this.sfxplayed = 1; + if (this.chatmsg.sound != "0" && this.chatmsg.sound != "1") { + this.sfxaudio.src = AO_HOST + "sounds/general/" + escape(this.chatmsg.sound) + ".wav"; + this.sfxaudio.play(); + } + } + this.textTimer = this.textTimer + UPDATE_INTERVAL; + } +} + +class INI { + static parse(data) { + let regex = { + section: /^\s*\[\s*([^\]]*)\s*\]\s*$/, + param: /^\s*([\w\.\-\_]+)\s*=\s*(.*?)\s*$/, + comment: /^\s*;.*$/ + }; + let value = {}; + let lines = data.split(/\r\n|\r|\n/); + let section = null; + lines.forEach(function(line) { + if (regex.comment.test(line)) { + return; + } else if (line.length == 0) { + return; + } else if (regex.param.test(line)) { + let match = line.match(regex.param); + if (section) { + value[section][match[1]] = match[2]; + } else { + value[match[1]] = match[2]; + } + } else if (regex.section.test(line)) { + let match = line.match(regex.section); + value[match[1]] = {}; + section = match[1]; + }; + }); + return value; + } } +/** + * Triggered when the Return key is pressed on the out-of-character chat input box. + * @param {KeyboardEvent} event + */ function onOOCEnter(event) { if (event.keyCode == 13) { - serv.send("CT#web" + pid + "#" + escapeChat(document.getElementById("client_oocinputbox").value) + "#%"); + client.sendOOC(document.getElementById("client_oocinputbox").value); document.getElementById("client_oocinputbox").value = ""; } } +/** + * Triggered when the Return key is pressed on the in-character chat input box. + * @param {KeyboardEvent} event + */ function onEnter(event) { if (event.keyCode == 13) { - mychar = chars[me] - myemo = emotes[myemotion] - if(document.getElementById("sendsfx").checked){ - ssfxname=myemo.sfx; - ssfxdelay=myemo.sfxdelay; - }else{ - ssfxname="0"; - ssfxdelay="0"; + let mychar = client.me(); + let myemo = client.myEmote(); + let ssfxname = "0"; + let ssfxdelay = "0"; + if (document.getElementById("sendsfx").checked) { + ssfxname = myemo.sfx; + ssfxdelay = myemo.sfxdelay; } - ICmessage = "MS#chat#" + myemo.speaking + "#" + mychar.name + "#" + myemo.silent + "#" + escapeChat(document.getElementById("client_inputbox").value) + "#" + mychar.side + "#" + ssfxname +"#" + myemo.zoom + "#" + me + "#" + ssfxdelay + "#" + objection_state + "#0#0#0#0#%"; - serv.send(ICmessage); - document.getElementById("client_inputbox").value = ''; - if (objection_state) { - document.getElementById("button_" + objection_state).className = "client_button"; - objection_state = 0; + // TODO URGENT: Do NOT send if we know that our message is going to get thrown away! + client.sendIC(myemo.speaking, mychar.name, myemo.silent, document.getElementById("client_inputbox").value, mychar.side, ssfxname, myemo.zoom, ssfxdelay, selectedShout); + document.getElementById("client_inputbox").value = ""; + if (selectedShout) { + document.getElementById("button_" + selectedShout).className = "client_button"; + selectedShout = 0; } } } +/** + * Triggered when an item on the music list is clicked. + * @param {MouseEvent} event + */ function musiclist_click(event) { - var playtrack = document.getElementById("client_musiclist").value; - serv.send("MC#" + playtrack + "#" + me + "#%"); + let playtrack = document.getElementById("client_musiclist").value; + client.sendMusicChange(playtrack); } +/** + * Triggered by the music volume slider. + */ function changeMusicVolume() { - music.volume = document.getElementById("client_mvolume").value / 100; + viewport.music.volume = document.getElementById("client_mvolume").value / 100; } +/** + * Triggered by the sound effect volume slider. + */ function changeSFXVolume() { - sfxaudio.volume = document.getElementById("client_svolume").value / 100; + viewport.sfxaudio.volume = document.getElementById("client_svolume").value / 100; } +/** + * Triggered by the blip volume slider. + */ function changeBlipVolume() { - blip.volume = document.getElementById("client_bvolume").value / 100; - womboblip.volume = document.getElementById("client_bvolume").value / 100; - comboblip.volume = document.getElementById("client_bvolume").value / 100; + viewport.blip.volume = document.getElementById("client_bvolume").value / 100; + viewport.womboblip.volume = document.getElementById("client_bvolume").value / 100; + viewport.comboblip.volume = document.getElementById("client_bvolume").value / 100; } +/** + * Triggered when a character icon is clicked in the character selection menu. + * @param {MouseEvent} event + */ function changeCharacter(event) { - serv.send("FC#%"); + client.sendLeaveRoom(); document.getElementById("client_charselect").style.display = "block"; document.getElementById("client_emo").innerHTML = ""; } +/** + * Triggered when there was an error loading a character sprite. + * @param {HTMLImageElement} image the element containing the missing image + */ function imgError(image) { image.onerror = ""; image.src = "/misc/placeholder.gif"; return true; } +/** + * Triggered when there was an error loading a character icon. + * @param {HTMLImageElement} image the element containing the missing image + */ function demoError(image) { image.onerror = ""; image.src = "/misc/placeholder.png"; return true; } +/** + * Checks if an image exists at the specified URI. + * @param {string} url the URI to be checked + */ function ImageExist(url) { var img = new Image(); img.src = url; return img.height != 0; } -function changebg(position) { - var standname - bgfolder = AO_HOST + "background/" + escape(bgname) + "/"; +/** + * Changes the viewport background based on a given position. + * + * Valid positions: `def, pro, hld, hlp, wit, jud` + * @param {string} position the position to change into + */ +function changeBackground(position) { + var standname; + let bgfolder = viewport.bgFolder(); document.getElementById("client_fg").style.display = "none"; document.getElementById("client_bench").style.display = "none"; switch (position) { @@ -201,445 +736,139 @@ function changebg(position) { document.getElementById("client_court").src = bgfolder + "defenseempty.png" document.getElementById("client_bench").style.display = "block"; document.getElementById("client_bench").src = bgfolder + "defensedesk.png" - standname="defense"; + standname = "defense"; break; case "pro": document.getElementById("client_court").src = bgfolder + "prosecutorempty.png" document.getElementById("client_bench").style.display = "block" document.getElementById("client_bench").src = bgfolder + "prosecutiondesk.png" - standname="prosecution"; + standname = "prosecution"; break; case "hld": document.getElementById("client_court").src = bgfolder + "helperstand.png" - standname="defense"; + standname = "defense"; break; case "hlp": document.getElementById("client_court").src = bgfolder + "prohelperstand.png" - standname="prosecution"; + standname = "prosecution"; break; case "wit": document.getElementById("client_court").src = bgfolder + "witnessempty.png" document.getElementById("client_bench").style.display = "block" document.getElementById("client_bench").src = bgfolder + "estrado.png" - standname="prosecution"; + standname = "prosecution"; break; case "jud": document.getElementById("client_court").src = bgfolder + "judgestand.png" - standname="prosecution"; + standname = "prosecution"; break; } - if(chatmsg.type==5){ + if (viewport.chatmsg.type == 5) { document.getElementById("client_bench").style.display = "none"; - document.getElementById("client_court").src = AO_HOST + "themes/default/"+standname+"_speedlines.gif"; - } -} - -function updateText() { - if (chatmsg.content.trim() == "") { - document.getElementById("client_name").style.display = "none"; - document.getElementById("client_chat").style.display = "none"; - } else { - document.getElementById("client_name").style.display = "block"; - document.getElementById("client_chat").style.display = "block"; - } - if (chatmsg.isnew){ - switch (chatmsg.objection) { - case "0": - shouttimer = 0; - break; - case "1": - document.getElementById("client_char").src = AO_HOST + "misc/holdit.gif"; - shouttimer = 800; - chatmsg.sound = "sfx-objection" - break; - case "2": - document.getElementById("client_char").src = AO_HOST + "misc/takethat.gif"; - shouttimer = 800; - chatmsg.sound = "sfx-objection" - break; - case "3": - document.getElementById("client_char").src = AO_HOST + "misc/objection.gif"; - shouttimer = 800; - chatmsg.sound = "sfx-objection" - break; - } - chatmsg.isnew=false; - chatmsg.startspeaking=true; -} - if (texttimer >= shouttimer) { - if (chatmsg.startspeaking) { - changebg(chatmsg.side); - document.getElementById("client_char").src = AO_HOST + "characters/" + escape(chatmsg.name) + "/" + chatmsg.speaking + ".gif"; - document.getElementById("client_name").style.fontSize = (document.getElementById("client_name").offsetHeight * 0.7) + "px"; - document.getElementById("client_chat").style.fontSize = (document.getElementById("client_chat").offsetHeight * 0.25) + "px"; - document.getElementById("client_name").innerHTML = "

" + escapeHtml(chatmsg.nameplate) + "

"; - switch(chatmsg.color){ - case "0": - stylecolor="color: #ffffff;"; - break; - case "1": - stylecolor="color: #00ff00;"; - break; - case "2": - stylecolor="color: #ff0000;"; - break; - case "3": - stylecolor="color: #ffaa00;"; - break; - case "4": - stylecolor="color: #0000ff;"; - break; - case "5": - stylecolor="color: #ffff00;"; - break; - case "6": - stylecolor="color: #aa00aa;"; - break; - } - document.getElementById("client_inner_chat").style = stylecolor; - chatmsg.startspeaking = false; - } else { - if (textnow != chatmsg.content) { - if(chatmsg.content.substring(textnow.length, textnow.length + 1)!=" "){ - combo = (combo + 1) % 2; - switch (combo) { - case 0: - blip.play() - break; - case 1: - //womboblip.play() - break; - } - } - textnow = chatmsg.content.substring(0, textnow.length + 1); - document.getElementById("client_inner_chat").innerHTML = escapeHtml(textnow); - if (textnow == chatmsg.content) { - chatstate = 3; - texttimer=0; - clearInterval(updater); - document.getElementById("client_char").src = AO_HOST + "characters/" + escape(chatmsg.name) + "/" + chatmsg.silent + ".gif"; - } - } - } - } - if (!sfxplayed && chatmsg.snddelay + shouttimer >= texttimer) { - sfxaudio.pause(); - sfxplayed = 1 - if (chatmsg.sound != "0" && chatmsg.sound != "1") { - sfxaudio.src = AO_HOST + "sounds/general/" + escape(chatmsg.sound) + ".wav"; - sfxaudio.play(); - } + document.getElementById("client_court").src = AO_HOST + "themes/default/" + standname + "_speedlines.gif"; } - texttimer = texttimer + updateInterval; } -function onOpen(e) { - if (mode == "join") { - serv.send("HI#" + navigator.userAgent + "#%"); - serv.send("ID#webAO#2.4.5#%"); - } else { - document.getElementById("client_loading").style.display = "none"; - } - CHECKupdater = setInterval(sendCheck, 5000); -}; - -function onClose(e) { - document.getElementById("client_error").style.display = "block"; -}; - +/** + * Triggered when the reconnect button is pushed. + */ function ReconnectButton() { - serv = new WebSocket("ws://" + serverIP); - if (serv) { - serv.send("HI#" + navigator.userAgent + "#%"); + client = new Client(serverIP); + if (client) { + mode = "join"; // HACK: see client.onOpen document.getElementById("client_error").style.display = "none"; } } +/** + * Triggered when the retry button is pushed (during the loading process). + */ function RetryButton() { -serv.send("HI#" + navigator.userAgent + "#%"); + client.joinServer(); } -function onError(e) { - document.getElementById("client_error").style.display = "block"; -}; - -function onMessage(e) { - msg = e.data; - console.log(msg) - lines = msg.split('%'); - arguments = lines[0].split('#'); - header = arguments[0]; - switch (header) { - case "MS": - if (arguments[4] != chatmsg.content) { - document.getElementById("client_inner_chat").innerHTML = ''; - chatmsg.pre = escape(arguments[2]); - chatmsg.character = -1; - for (var i = 0; i < chars.length; i++) { - if (chars[i].name == arguments[3]) { - chatmsg.character = i; - break; - } - } - chatmsg.preanim = escape(arguments[2]); - chatmsg.nameplate = arguments[3]; - chatmsg.name = arguments[3]; - chatmsg.speaking = "(b)" + escape(arguments[4]); - chatmsg.silent = "(a)" + escape(arguments[4]); - chatmsg.content = escapeHtml(arguments[5]); - chatmsg.side = arguments[6]; - chatmsg.sound = escape(arguments[7]); - chatmsg.type = arguments[8]; - //chatmsg.charid = arguments[9]; - chatmsg.snddelay = arguments[10]; - chatmsg.objection = arguments[11]; - chatmsg.evidence = arguments[12]; - //chatmsg.flip = arguments[13]; - chatmsg.flash = arguments[14]; - chatmsg.color = arguments[15]; - chatmsg.isnew = true; - addlog(chatmsg.nameplate + ": " + escapeHtml(arguments[5])) - changebg(chatmsg.side); - textnow = ''; - sfxplayed = 0 - texttimer = 0 - updater = setInterval(updateText, updateInterval); - } - break; - case "CT": - document.getElementById("client_ooclog").innerHTML = document.getElementById("client_ooclog").innerHTML + arguments[1] + ": " + arguments[2] + "\r\n" - break; - case "MC": - music.pause(); - music.src = MUSIC_HOST + arguments[1]; - music.play(); - if (arguments[2] >= 0) { - musicname = chars[arguments[2]].name; - } else { - musicname = "$SERVER" - } - addlog(musicname + " changed music to " + arguments[1]); - break; - case "RMC": - music.pause(); - music = new Audio(musiclist[arguments[0]]); - music.totime = arguments[1] - music.offset = new Date().getTime() / 1000 - music.addEventListener('loadedmetadata', function() { - music.currentTime += parseFloat(music.totime + (new Date().getTime() / 1000 - music.offset)).toFixed(3); - music.play(); - }, false) - break; - case "CI": - document.getElementById("client_loadingtext").innerHTML = "Loading Character " + arguments[1]; - serv.send("AN#" + ((arguments[1] / 10) + 1) + "#%"); - for (var i = 2; i < arguments.length - 1; i++) { - if (i % 2 == 0) { - charguments = arguments[i].split("&"); - chars[arguments[i - 1]] = { - "name": charguments[0], - "desc": charguments[1], - "evidence": charguments[3], - "icon": AO_HOST + "characters/" + escape(charguments[0]) + "/char_icon.png" - }; - } - } - break; - case "SC": - document.getElementById("client_loadingtext").innerHTML = "Loading Characters"; - for (var i = 1; i < arguments.length - 1; i++) { - charguments = arguments[i].split("&"); - chars[i - 1] = { - "name": charguments[0], - "desc": charguments[1], - "evidence": charguments[3], - "icon": AO_HOST + "characters/" + escape(charguments[0]) + "/char_icon.png" - } - } - serv.send("RM#%"); - break; - case "EI": - document.getElementById("client_loadingtext").innerHTML = "Loading Evidence " + arguments[1]; - //serv.send("AE#" + (arguments[1] + 1) + "#%"); - serv.send("RM#%"); - break; - case "EM": - document.getElementById("client_loadingtext").innerHTML = "Loading Music " + arguments[1]; - serv.send("AM#" + ((arguments[1] / 10) + 1) + "#%"); - var hmusiclist = document.getElementById("client_musiclist"); - for (var i = 2; i < arguments.length - 1; i++) { - if (i % 2 == 0) { - var newentry = document.createElement("OPTION"); - newentry.text = arguments[i]; - hmusiclist.options.add(newentry); - } - } - break; - case "SM": - document.getElementById("client_loadingtext").innerHTML = "Loading Music "; - var hmusiclist = document.getElementById("client_musiclist"); - for (var i = 1; i < arguments.length - 1; i++) { - var newentry = document.createElement("OPTION"); - newentry.text = arguments[i]; - hmusiclist.options.add(newentry); - } - serv.send("RD#%"); - break; - case "music": - for (var i = 0; i < arguments.length / 2; i++) { - musiclist[arguments[2 * i]] = arguments[2 * i + 1]; - } - break; - case "DONE": - document.getElementById("client_loading").style.display = "none"; - document.getElementById("client_chatlog").style.display = "grid"; - document.getElementById("client_wrapper").style.display = "block"; - document.getElementById("client_charselect").style.display = "block"; - break; - case "BN": - bgname = arguments[1]; - break; - case "NBG": - /* TODO */ - break; - case "HP": - /* TODO */ - if (arguments[1] == 1) { - document.getElementById("client_defense_hp").style.clip = "rect(0px," + BAR_WIDTH * arguments[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; - } else { - document.getElementById("client_prosecutor_hp").style.clip = "rect(0px," + BAR_WIDTH * arguments[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; - } - break; - case "ID": - pid = arguments[1]; - case "PN": - serv.send("askchaa#%"); - break; - case "SI": - if(oldloading){ - serv.send("askchar2#%"); - }else{ - serv.send("RC#%"); - } - break; - case "CharsCheck": - document.getElementById("client_chartable").innerHTML = ""; - for (var i = 0; i < chars.length; i++) { - if (i % charselectWidth == 0) { - var tr = document.createElement('TR'); - } - var td = document.createElement('TD'); - var icon_chosen; - var thispick = chars[i].icon; - if (arguments[1 + i] == "-1") { - icon_chosen = " dark"; - } else { - icon_chosen = ""; - } - td.innerHTML = "" + chars[i].desc + ""; - tr.appendChild(td); - if (i % charselectWidth == 0) { - document.getElementById("client_chartable").appendChild(tr); - } - } - changebg("def"); - break; - case "PV": - me = arguments[3] - document.getElementById("client_charselect").style.display = "none"; - var xhr = new XMLHttpRequest(); - xhr.open('GET', AO_HOST + 'characters/' + escape(chars[me].name) + '/char.ini', true); - xhr.responseType = 'text'; - xhr.onload = function(e) { - if (this.status == 200) { - linifile = this.responseText; - pinifile = parseINI(linifile); - chars[me].side = pinifile.Options.side; - for (var i = 1; i < pinifile.Emotions.number; i++) { - var emoteinfo = pinifile.Emotions[i].split('#'); - esfx="0"; - esfxd="0"; - if (typeof pinifile.SoundN !== 'undefined') { - esfx=pinifile.SoundN[i]; - } - if (typeof pinifile.SoundT !== 'undefined') { - esfxd=pinifile.SoundT[i]; - } - emotes[i] = { - desc: emoteinfo[0], - speaking: emoteinfo[1], - silent: emoteinfo[2], - zoom: emoteinfo[3], - sfx: esfx, - sfxdelay: esfxd, - button_off: AO_HOST + 'characters/' + escape(chars[me].name) + '/emotions/button' + i + '_off.png', - button_on: AO_HOST + 'characters/' + escape(chars[me].name) + '/emotions/button' + i + '_on.png' - }; - document.getElementById("client_emo").innerHTML += "" + emotes[i].desc + ""; - } - pickemotion(1); - } - }; - xhr.send(); - break; - } -}; - -function addlog(toadd) { - document.getElementById("client_log").innerHTML = toadd + "
" + document.getElementById("client_log").innerHTML +/** + * Appends a message to the in-character chat log. + * @param {string} toadd the string to be added + */ +function appendICLog(toadd) { + document.getElementById("client_log").innerHTML = toadd + "
" + document.getElementById("client_log").innerHTML; } +/** + * Requests to play as a character. + * @param {number} ccharacter the character ID; if this is a large number, then spectator is chosen instead. + */ function pickchar(ccharacter) { if (ccharacter < 1000) { - serv.send("CC#" + pid + "#" + ccharacter + "#web#%"); + sendCharacter(ccharacter); } else { - //spectator + // Spectator document.getElementById("client_charselect").style.display = "none"; document.getElementById("client_inputbox").style.display = "none"; document.getElementById("client_emo").style.display = "none"; } } +/** + * Highlights and selects an emotion for in-character chat. + * @param {string} emo the new emotion to be selected + */ function pickemotion(emo) { - if (myemotion != -1) { - document.getElementById("emo_" + myemotion).src = emotes[myemotion].button_off; + if (client.selectedEmote != -1) { + document.getElementById("emo_" + client.selectedEmote).src = client.myEmote().button_off; } - document.getElementById("emo_" + emo).src = emotes[emo].button_on; - myemotion = emo + client.selectedEmote = emo + document.getElementById("emo_" + emo).src = client.myEmote().button_on; } +/** + * Highlights and selects a shout for in-character chat. + * If the same shout button is selected, then the shout is canceled. + * @param {string} shout the new shout to be selected + */ function toggleshout(shout) { - if (shout == objection_state) { + if (shout == selectedShout) { document.getElementById("button_" + shout).className = "client_button"; - objection_state = 0; + selectedShout = 0; } else { document.getElementById("button_" + shout).className = "client_button dark"; - if (objection_state) { - document.getElementById("button_" + objection_state).className = "client_button"; + if (selectedShout) { + document.getElementById("button_" + selectedShout).className = "client_button"; } - objection_state = shout; + selectedShout = shout; } } -function sendMusic(song) { - serv.send("MC#" + song); -} - -function sendCheck() { - serv.send("CH#" + me + "#%"); +/** + * Escapes a string to be HTML-safe. + * + * XXX: This is unnecessary if we use `createTextNode` instead! + * @param {string} unsafe an unsanitized string + */ +function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); } -function escapeHtml(unsafe) { - var transfer = unsafe; - transfer.replace(/&/g, "&"); - transfer.replace(//g, ">"); - transfer.replace(/"/g, """); - transfer.replace(/'/g, "'"); - return transfer; +/** + * Escapes a string to AO1 escape codes. + * @param {string} estring the string to be escaped + */ +function escapeChat(estring) { + return estring + .replace(/#/g, "") + .replace(/&/g, "") + .replace(/%/g, "") + .replace(/\$/g, ""); } -if(typeof(String.prototype.trim) === "undefined") +// TODO: Possibly safe to remove, since we are using a transpiler. +if (typeof(String.prototype.trim) === "undefined") { String.prototype.trim = function() { -- cgit From 86d03dea6d34f570e482381da29df79585756654 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Sat, 19 May 2018 01:36:33 -0500 Subject: Fix code broken from refactoring --- webAO/client.js | 285 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 165 insertions(+), 120 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index 46ae84d..4871080 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -21,12 +21,6 @@ const BAR_HEIGHT = 20; const CHAR_SELECT_WIDTH = 8; const UPDATE_INTERVAL = 80; -let client = new Client(serverIP); -let viewport = new Viewport(); - -let music = new Audio(); -music.play(); - let oldLoading = false; if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) { oldLoading = true; @@ -34,9 +28,9 @@ if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phon let selectedShout = 0; -export default class Client { +class Client { constructor(address) { - this.serv = new WebSocket("ws://" + serverIP); + this.serv = new WebSocket("ws://" + address); this.serv.onopen = (evt) => this.onOpen(evt); this.serv.onclose = (evt) => this.onClose(evt); @@ -55,6 +49,28 @@ export default class Client { // Only used for RMC/`music` packets, not EM/SM/MC packets. this.musicList = Object(); + + this.handlers = { + "MS": (args) => this.handleMS(args), + "CT": (args) => this.handleCT(args), + "MC": (args) => this.handleMC(args), + "RMC": (args) => this.handleRMC(args), + "CI": (args) => this.handleCI(args), + "SC": (args) => this.handleSC(args), + "EI": (args) => this.handleEI(args), + "EM": (args) => this.handleEM(args), + "SM": (args) => this.handleSM(args), + "music": (args) => this.handlemusic(args), + "DONE": (args) => this.handleDONE(args), + "BN": (args) => this.handleBN(args), + "NBG": (args) => this.handleNBG(args), + "HP": (args) => this.handleHP(args), + "ID": (args) => this.handleID(args), + "PN": (args) => this.handlePN(args), + "SI": (args) => this.handleSI(args), + "CharsCheck": (args) => this.handleCharsCheck(args), + "PV": (args) => this.handlePV(args) + } } /** @@ -93,8 +109,8 @@ export default class Client { */ sendIC(speaking, name, silent, message, side, ssfxname, zoom, ssfxdelay, objection) { this.serv.send( - `MS#chat#${speaking}#${name}#${silent}` - `#${escapeChat(message)}#${side}#${ssfxname}#${zoom}` + `MS#chat#${speaking}#${name}#${silent}` + + `#${escapeChat(message)}#${side}#${ssfxname}#${zoom}` + `#${this.charID}#${ssfxdelay}#${selectedShout}#0#0#0#0#%` ); } @@ -179,9 +195,14 @@ export default class Client { let msg = e.data; console.debug(msg); let lines = msg.split('%'); - let arguments = lines[0].split('#'); - let header = arguments[0]; - this[`handle${header}`](arguments); + let args = lines[0].split('#'); + let header = args[0]; + let handler = this.handlers[header]; + if (typeof handler !== "undefined") { + handler(args); + } else { + console.warn(`Invalid packet header ${header}`); + } } /** @@ -197,35 +218,35 @@ export default class Client { clearInterval(this.checkUpdater); } - handleMS(arguments) { + handleMS(args) { // TODO: this if-statement might be a bug. - if (arguments[4] != viewport.chatmsg.content) { + if (args[4] != viewport.chatmsg.content) { document.getElementById("client_inner_chat").innerHTML = ""; let chatmsg = { - pre: escape(arguments[2]), + pre: escape(args[2]), character: -1, // Will do a linear search - preanim: escape(arguments[2]), // XXX: why again? - nameplate: arguments[3], // TODO: parse INI to get this info - name: arguments[3], - speaking: "(b)" + escape(arguments[4]), - silent: "(a)" + escape(arguments[4]), - content: escapeHtml(arguments[5]), - side: arguments[6], - sound: escape(arguments[7]), - type: arguments[8], - // charid: arguments[9], - snddelay: arguments[10], - objection: arguments[11], - evidence: arguments[12], - // flip: arguments[13], - flash: arguments[14], - color: arguments[15], + preanim: escape(args[2]), // XXX: why again? + nameplate: args[3], // TODO: parse INI to get this info + name: args[3], + speaking: "(b)" + escape(args[4]), + silent: "(a)" + escape(args[4]), + content: args[5], + side: args[6], + sound: escape(args[7]), + type: args[8], + // charid: args[9], + snddelay: args[10], + objection: args[11], + evidence: args[12], + // flip: args[13], + flash: args[14], + color: args[15], isnew: true, }; // The dreaded linear search... for (let i = 0; i < this.chars.length; i++) { - if (this.chars[i].name == arguments[3]) { + if (this.chars[i].name == args[3]) { chatmsg.character = i; break; } @@ -235,27 +256,27 @@ export default class Client { } } - handleCT(arguments) { - document.getElementById("client_ooclog").innerHTML = document.getElementById("client_ooclog").innerHTML + arguments[1] + ": " + arguments[2] + "\r\n"; + handleCT(args) { + document.getElementById("client_ooclog").innerHTML = document.getElementById("client_ooclog").innerHTML + args[1] + ": " + args[2] + "\r\n"; } - handleMC(arguments) { + handleMC(args) { music.pause(); - music.src = MUSIC_HOST + arguments[1]; + music.src = MUSIC_HOST + args[1]; music.play(); - if (arguments[2] >= 0) { - musicname = this.chars[arguments[2]].name; - appendICLog(`${musicname} changed music to ${arguments[1]}`); + if (args[2] >= 0) { + let musicname = this.chars[args[2]].name; + appendICLog(`${musicname} changed music to ${args[1]}`); } else { - appendICLog(`The music was changed to ${arguments[1]}`); + appendICLog(`The music was changed to ${args[1]}`); } } - handleRMC(arguments) { + handleRMC(args) { music.pause(); - music = new Audio(this.musicList[arguments[1]]); + music = new Audio(this.musicList[args[1]]); // Music offset + drift from song loading - music.totime = arguments[1]; + music.totime = args[1]; music.offset = new Date().getTime() / 1000; music.addEventListener('loadedmetadata', function() { music.currentTime += parseFloat(music.totime + (new Date().getTime() / 1000 - music.offset)).toFixed(3); @@ -263,106 +284,104 @@ export default class Client { }, false); } - handleCI(arguments) { - document.getElementById("client_loadingtext").innerHTML = "Loading Character " + arguments[1]; - this.serv.send("AN#" + ((arguments[1] / 10) + 1) + "#%"); - for (let i = 2; i < arguments.length - 1; i++) { + handleCI(args) { + document.getElementById("client_loadingtext").innerHTML = "Loading Character " + args[1]; + this.serv.send("AN#" + ((args[1] / 10) + 1) + "#%"); + for (let i = 2; i < args.length - 1; i++) { if (i % 2 == 0) { - charguments = arguments[i].split("&"); - this.chars[arguments[i - 1]] = { - "name": charguments[0], - "desc": charguments[1], - "evidence": charguments[3], - "icon": AO_HOST + "characters/" + escape(charguments[0]) + "/char_icon.png" + let chargs = args[i].split("&"); + this.chars[args[i - 1]] = { + "name": chargs[0], + "desc": chargs[1], + "evidence": chargs[3], + "icon": AO_HOST + "characters/" + escape(chargs[0]) + "/char_icon.png" }; } } } - handleSC(arguments) { + handleSC(args) { document.getElementById("client_loadingtext").innerHTML = "Loading Characters"; - for (let i = 1; i < arguments.length - 1; i++) { - charguments = arguments[i].split("&"); + for (let i = 1; i < args.length - 1; i++) { + let chargs = args[i].split("&"); this.chars[i - 1] = { - "name": charguments[0], - "desc": charguments[1], - "evidence": charguments[3], - "icon": AO_HOST + "characters/" + escape(charguments[0]) + "/char_icon.png" + "name": chargs[0], + "desc": chargs[1], + "evidence": chargs[3], + "icon": AO_HOST + "characters/" + escape(chargs[0]) + "/char_icon.png" } } this.serv.send("RM#%"); } - handleEI(arguments) { - document.getElementById("client_loadingtext").innerHTML = "Loading Evidence " + arguments[1]; - //serv.send("AE#" + (arguments[1] + 1) + "#%"); + handleEI(args) { + document.getElementById("client_loadingtext").innerHTML = "Loading Evidence " + args[1]; + //serv.send("AE#" + (args[1] + 1) + "#%"); this.serv.send("RM#%"); } - handleEM(arguments) { - document.getElementById("client_loadingtext").innerHTML = "Loading Music " + arguments[1]; - this.serv.send("AM#" + ((arguments[1] / 10) + 1) + "#%"); + handleEM(args) { + document.getElementById("client_loadingtext").innerHTML = "Loading Music " + args[1]; + this.serv.send("AM#" + ((args[1] / 10) + 1) + "#%"); let hmusiclist = document.getElementById("client_musiclist"); - for (let i = 2; i < arguments.length - 1; i++) { + for (let i = 2; i < args.length - 1; i++) { if (i % 2 == 0) { let newentry = document.createElement("OPTION"); - newentry.text = arguments[i]; + newentry.text = args[i]; hmusiclist.options.add(newentry); } } } - handleSM(arguments) { + handleSM(args) { document.getElementById("client_loadingtext").innerHTML = "Loading Music "; let hmusiclist = document.getElementById("client_musiclist"); - for (let i = 1; i < arguments.length - 1; i++) { + for (let i = 1; i < args.length - 1; i++) { let newentry = document.createElement("OPTION"); - newentry.text = arguments[i]; + newentry.text = args[i]; hmusiclist.options.add(newentry); } this.serv.send("RD#%"); } - handlemusic(arguments) { - for (let i = 0; i < arguments.length / 2; i++) { - this.musicList[arguments[2 * i]] = arguments[2 * i + 1]; + handlemusic(args) { + for (let i = 0; i < args.length / 2; i++) { + this.musicList[args[2 * i]] = args[2 * i + 1]; } } - handleDONE(arguments) { + handleDONE(args) { document.getElementById("client_loading").style.display = "none"; - document.getElementById("client_chatlog").style.display = "grid"; - document.getElementById("client_wrapper").style.display = "block"; document.getElementById("client_charselect").style.display = "block"; } - handleBN(arguments) { - viewport.bgname = escape(arguments[1]); + handleBN(args) { + viewport.bgname = escape(args[1]); } - handleNBG(arguments) { + handleNBG(args) { // TODO (set by sD) } - handleHP(arguments) { + handleHP(args) { // TODO (set by sD) // Also, this is broken. - if (arguments[1] == 1) { - document.getElementById("client_defense_hp").style.clip = "rect(0px," + BAR_WIDTH * arguments[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; + if (args[1] == 1) { + document.getElementById("client_defense_hp").style.clip = "rect(0px," + BAR_WIDTH * args[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; } else { - document.getElementById("client_prosecutor_hp").style.clip = "rect(0px," + BAR_WIDTH * arguments[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; + document.getElementById("client_prosecutor_hp").style.clip = "rect(0px," + BAR_WIDTH * args[2] / 10 + "px," + BAR_HEIGHT + "px,0px)"; } } - handleID(arguments) { - this.playerID = arguments[1]; + handleID(args) { + this.playerID = args[1]; } - handlePN(arguments) { + handlePN(args) { this.serv.send("askchaa#%"); } - handleSI(arguments) { + handleSI(args) { if (oldLoading) { this.serv.send("askchar2#%"); } else { @@ -370,7 +389,7 @@ export default class Client { } } - handleCharsCheck(arguments) { + handleCharsCheck(args) { document.getElementById("client_chartable").innerHTML = ""; for (let i = 0; i < this.chars.length; i++) { if (i % CHAR_SELECT_WIDTH == 0) { @@ -379,12 +398,14 @@ export default class Client { let td = document.createElement('TD'); let icon_chosen; let thispick = this.chars[i].icon; - if (arguments[1 + i] == "-1") { + if (args[i + 1] == "-1") { icon_chosen = " dark"; } else { icon_chosen = ""; } - td.innerHTML = "" + chars[i].desc + ""; + td.innerHTML = `${this.chars[i].desc}`; tr.appendChild(td); if (i % CHAR_SELECT_WIDTH == 0) { document.getElementById("client_chartable").appendChild(tr); @@ -393,36 +414,38 @@ export default class Client { changeBackground("def"); } - handlePV(arguments) { - this.charID = arguments[3]; + handlePV(args) { + this.charID = args[3]; document.getElementById("client_charselect").style.display = "none"; + let me = this.me(); + let emotes = this.emotes; let xhr = new XMLHttpRequest(); - xhr.open('GET', AO_HOST + 'characters/' + escape(me().name) + '/char.ini', true); + xhr.open('GET', AO_HOST + 'characters/' + escape(this.me().name) + '/char.ini', true); xhr.responseType = 'text'; - xhr.onload = function(e) { + xhr.onload = function (e) { if (this.status == 200) { let linifile = this.responseText; let pinifile = INI.parse(linifile); - me().side = pinifile.Options.side; + me.side = pinifile.Options.side; for (let i = 1; i < pinifile.Emotions.number; i++) { let emoteinfo = pinifile.Emotions[i].split('#'); - esfx = "0"; - esfxd = "0"; + let esfx = "0"; + let esfxd = "0"; if (typeof pinifile.SoundN !== 'undefined') { esfx = pinifile.SoundN[i]; } if (typeof pinifile.SoundT !== 'undefined') { esfxd = pinifile.SoundT[i]; } - this.emotes[i] = { + emotes[i] = { desc: emoteinfo[0], speaking: emoteinfo[1], silent: emoteinfo[2], zoom: emoteinfo[3], sfx: esfx, sfxdelay: esfxd, - button_off: AO_HOST + 'characters/' + escape(me().name) + '/emotions/button' + i + '_off.png', - button_on: AO_HOST + 'characters/' + escape(me().name) + '/emotions/button' + i + '_on.png' + button_off: AO_HOST + 'characters/' + escape(me.name) + '/emotions/button' + i + '_off.png', + button_on: AO_HOST + 'characters/' + escape(me.name) + '/emotions/button' + i + '_on.png' }; document.getElementById("client_emo").innerHTML += "" + emotes[i].desc + ""; } @@ -475,7 +498,7 @@ class Viewport { * Returns the path which the background is located in. */ bgFolder() { - return `${AO_HOST}background/${bgname}/`; + return `${AO_HOST}background/${this.bgname}/`; } /** @@ -484,7 +507,7 @@ class Viewport { */ say(chatmsg) { this.chatmsg = chatmsg; - appendICLog(chatmsg.nameplate + ": " + escapeHtml(arguments[5])); + appendICLog(`${chatmsg.nameplate}: ${chatmsg.content}`); changeBackground(chatmsg.side); this.textnow = ''; this.sfxplayed = 0; @@ -543,7 +566,7 @@ class Viewport { "5": "#ffff00", "6": "#aa00aa" } - stylecolor = "color: " + (colors[this.chatmsg.color] || "#ffffff"); + let stylecolor = "color: " + (colors[this.chatmsg.color] || "#ffffff"); document.getElementById("client_inner_chat").style = stylecolor; this.chatmsg.startspeaking = false; } else { @@ -560,7 +583,7 @@ class Viewport { } } this.textnow = this.chatmsg.content.substring(0, this.textnow.length + 1); - document.getElementById("client_inner_chat").innerHTML = escapeHtml(this.textnow); + document.getElementById("client_inner_chat").innerHTML = this.textnow; if (this.textnow == this.chatmsg.content) { this.textTimer = 0; clearInterval(this.updater); @@ -617,18 +640,19 @@ class INI { * Triggered when the Return key is pressed on the out-of-character chat input box. * @param {KeyboardEvent} event */ -function onOOCEnter(event) { +export function onOOCEnter(event) { if (event.keyCode == 13) { client.sendOOC(document.getElementById("client_oocinputbox").value); document.getElementById("client_oocinputbox").value = ""; } } +window.onOOCEnter = onOOCEnter; /** * Triggered when the Return key is pressed on the in-character chat input box. * @param {KeyboardEvent} event */ -function onEnter(event) { +export function onEnter(event) { if (event.keyCode == 13) { let mychar = client.me(); let myemo = client.myEmote(); @@ -647,15 +671,17 @@ function onEnter(event) { } } } +window.onEnter = onEnter; /** * Triggered when an item on the music list is clicked. * @param {MouseEvent} event */ -function musiclist_click(event) { +export function musiclist_click(event) { let playtrack = document.getElementById("client_musiclist").value; client.sendMusicChange(playtrack); } +window.musiclist_click = musiclist_click; /** * Triggered by the music volume slider. @@ -684,31 +710,34 @@ function changeBlipVolume() { * Triggered when a character icon is clicked in the character selection menu. * @param {MouseEvent} event */ -function changeCharacter(event) { +export function changeCharacter(event) { client.sendLeaveRoom(); document.getElementById("client_charselect").style.display = "block"; document.getElementById("client_emo").innerHTML = ""; } +window.changeCharacter = changeCharacter; /** * Triggered when there was an error loading a character sprite. * @param {HTMLImageElement} image the element containing the missing image */ -function imgError(image) { +export function imgError(image) { image.onerror = ""; image.src = "/misc/placeholder.gif"; return true; } +window.imgError = imgError; /** * Triggered when there was an error loading a character icon. * @param {HTMLImageElement} image the element containing the missing image */ -function demoError(image) { +export function demoError(image) { image.onerror = ""; image.src = "/misc/placeholder.png"; return true; } +window.demoError = demoError; /** * Checks if an image exists at the specified URI. @@ -772,36 +801,38 @@ function changeBackground(position) { /** * Triggered when the reconnect button is pushed. */ -function ReconnectButton() { +export function ReconnectButton() { client = new Client(serverIP); if (client) { mode = "join"; // HACK: see client.onOpen document.getElementById("client_error").style.display = "none"; } } +window.ReconnectButton = ReconnectButton; /** * Triggered when the retry button is pushed (during the loading process). */ -function RetryButton() { +export function RetryButton() { client.joinServer(); } +window.RetryButton = RetryButton; /** * Appends a message to the in-character chat log. * @param {string} toadd the string to be added */ function appendICLog(toadd) { - document.getElementById("client_log").innerHTML = toadd + "
" + document.getElementById("client_log").innerHTML; + document.getElementById("client_log").appendChild(document.createTextNode(toadd)); } /** * Requests to play as a character. * @param {number} ccharacter the character ID; if this is a large number, then spectator is chosen instead. */ -function pickchar(ccharacter) { +export function pickchar(ccharacter) { if (ccharacter < 1000) { - sendCharacter(ccharacter); + client.sendCharacter(ccharacter); } else { // Spectator document.getElementById("client_charselect").style.display = "none"; @@ -809,25 +840,27 @@ function pickchar(ccharacter) { document.getElementById("client_emo").style.display = "none"; } } +window.pickchar = pickchar; /** * Highlights and selects an emotion for in-character chat. * @param {string} emo the new emotion to be selected */ -function pickemotion(emo) { +export function pickemotion(emo) { if (client.selectedEmote != -1) { document.getElementById("emo_" + client.selectedEmote).src = client.myEmote().button_off; } client.selectedEmote = emo document.getElementById("emo_" + emo).src = client.myEmote().button_on; } +window.pickemotion = pickemotion; /** * Highlights and selects a shout for in-character chat. * If the same shout button is selected, then the shout is canceled. * @param {string} shout the new shout to be selected */ -function toggleshout(shout) { +export function toggleshout(shout) { if (shout == selectedShout) { document.getElementById("button_" + shout).className = "client_button"; selectedShout = 0; @@ -839,6 +872,7 @@ function toggleshout(shout) { selectedShout = shout; } } +window.toggleshout = toggleshout; /** * Escapes a string to be HTML-safe. @@ -874,4 +908,15 @@ if (typeof(String.prototype.trim) === "undefined") { return String(this).replace(/^\s+|\s+$/g, ''); }; -} \ No newline at end of file +} + + +// +// Client code +// + +let client = new Client(serverIP); +let viewport = new Viewport(); + +let music = new Audio(); +music.play(); \ No newline at end of file -- cgit From 91f9a9e5b22ab6c3f3cb1e63dfe8d6724e8bd84d Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Sat, 19 May 2018 10:55:46 -0500 Subject: Fix volume sliders not working --- webAO/client.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index 4871080..e177bd8 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -686,25 +686,28 @@ window.musiclist_click = musiclist_click; /** * Triggered by the music volume slider. */ -function changeMusicVolume() { +export function changeMusicVolume() { viewport.music.volume = document.getElementById("client_mvolume").value / 100; } +window.changeMusicVolume = changeMusicVolume; /** * Triggered by the sound effect volume slider. */ -function changeSFXVolume() { +export function changeSFXVolume() { viewport.sfxaudio.volume = document.getElementById("client_svolume").value / 100; } +window.changeSFXVolume = changeSFXVolume; /** * Triggered by the blip volume slider. */ -function changeBlipVolume() { +export function changeBlipVolume() { viewport.blip.volume = document.getElementById("client_bvolume").value / 100; viewport.womboblip.volume = document.getElementById("client_bvolume").value / 100; viewport.comboblip.volume = document.getElementById("client_bvolume").value / 100; } +window.changeBlipVolume = changeBlipVolume; /** * Triggered when a character icon is clicked in the character selection menu. -- cgit From 4ff26c72e9ec8e702c8305e0b7a9acff5b2f5040 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 21:52:58 -0500 Subject: Move music to viewport --- webAO/client.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index e177bd8..f9b122e 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -261,6 +261,7 @@ class Client { } handleMC(args) { + const music = viewport.music; music.pause(); music.src = MUSIC_HOST + args[1]; music.play(); @@ -273,8 +274,9 @@ class Client { } handleRMC(args) { - music.pause(); - music = new Audio(this.musicList[args[1]]); + viewport.music.pause(); + viewport.music = new Audio(this.musicList[args[1]]); + const music = viewport.music; // Music offset + drift from song loading music.totime = args[1]; music.offset = new Date().getTime() / 1000; @@ -486,6 +488,9 @@ class Viewport { this.sfxaudio = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); this.sfxplayed = 0; + this.music = new Audio(); + this.music.play(); + this.updater = null; this.bgname = "gs4"; @@ -920,6 +925,3 @@ if (typeof(String.prototype.trim) === "undefined") let client = new Client(serverIP); let viewport = new Viewport(); - -let music = new Audio(); -music.play(); \ No newline at end of file -- cgit From 385b42a84ad52280ae9e209e543562f52c34d9e5 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 21:54:09 -0500 Subject: Document network handler methods --- webAO/client.js | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index f9b122e..a62d307 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -218,6 +218,10 @@ class Client { clearInterval(this.checkUpdater); } + /** + * Handles an in-character chat message. + * @param {*} args packet arguments + */ handleMS(args) { // TODO: this if-statement might be a bug. if (args[4] != viewport.chatmsg.content) { @@ -256,10 +260,18 @@ class Client { } } + /** + * Handles an out-of-character chat message. + * @param {Array} args packet arguments + */ handleCT(args) { document.getElementById("client_ooclog").innerHTML = document.getElementById("client_ooclog").innerHTML + args[1] + ": " + args[2] + "\r\n"; } + /** + * Handles a music change to an arbitrary resource. + * @param {Array} args packet arguments + */ handleMC(args) { const music = viewport.music; music.pause(); @@ -273,6 +285,10 @@ class Client { } } + /** + * Handles a music change to an arbitrary resource, with an offset in seconds. + * @param {Array} args packet arguments + */ handleRMC(args) { viewport.music.pause(); viewport.music = new Audio(this.musicList[args[1]]); @@ -286,6 +302,11 @@ class Client { }, false); } + /** + * Handles incoming character information, bundling multiple characters + * per packet. + * @param {Array} args packet arguments + */ handleCI(args) { document.getElementById("client_loadingtext").innerHTML = "Loading Character " + args[1]; this.serv.send("AN#" + ((args[1] / 10) + 1) + "#%"); @@ -302,6 +323,11 @@ class Client { } } + /** + * Handles incoming character information, containing only one character + * per packet. + * @param {Array} args packet arguments + */ handleSC(args) { document.getElementById("client_loadingtext").innerHTML = "Loading Characters"; for (let i = 1; i < args.length - 1; i++) { @@ -316,12 +342,24 @@ class Client { this.serv.send("RM#%"); } + /** + * Handles incoming evidence information, containing only one evidence + * item per packet. + * + * Mostly unimplemented in webAO. + * @param {Array} args packet arguments + */ handleEI(args) { document.getElementById("client_loadingtext").innerHTML = "Loading Evidence " + args[1]; //serv.send("AE#" + (args[1] + 1) + "#%"); this.serv.send("RM#%"); } + /** + * Handles incoming music information, containing multiple entries + * per packet. + * @param {Array} args packet arguments + */ handleEM(args) { document.getElementById("client_loadingtext").innerHTML = "Loading Music " + args[1]; this.serv.send("AM#" + ((args[1] / 10) + 1) + "#%"); @@ -335,6 +373,11 @@ class Client { } } + /** + * Handles incoming music information, containing only one entry + * per packet. + * @param {Array} args packet arguments + */ handleSM(args) { document.getElementById("client_loadingtext").innerHTML = "Loading Music "; let hmusiclist = document.getElementById("client_musiclist"); @@ -346,17 +389,32 @@ class Client { this.serv.send("RD#%"); } + /** + * Handles incoming music information, containing all entries + * in the same packet. + * @param {Array} args packet arguments + */ handlemusic(args) { for (let i = 0; i < args.length / 2; i++) { this.musicList[args[2 * i]] = args[2 * i + 1]; } } + /** + * Handles the handshake completion packet, meaning the player + * is ready to select a character. + * + * @param {Array} args packet arguments + */ handleDONE(args) { document.getElementById("client_loading").style.display = "none"; document.getElementById("client_charselect").style.display = "block"; } + /** + * Handles a background change. + * @param {Array} args packet arguments + */ handleBN(args) { viewport.bgname = escape(args[1]); } @@ -365,6 +423,10 @@ class Client { // TODO (set by sD) } + /** + * Handles a change in the health bars' states. + * @param {Array} args packet arguments + */ handleHP(args) { // TODO (set by sD) // Also, this is broken. @@ -375,6 +437,10 @@ class Client { } } + /** + * Handles the issuance of a player ID by the server. + * @param {Array} args packet arguments + */ handleID(args) { this.playerID = args[1]; } @@ -383,6 +449,11 @@ class Client { this.serv.send("askchaa#%"); } + /** + * Received when the server announces its server info, + * but we use it as a cue to begin retrieving characters. + * @param {Array} args packet arguments + */ handleSI(args) { if (oldLoading) { this.serv.send("askchar2#%"); @@ -391,6 +462,10 @@ class Client { } } + /** + * Handles the list of all used and vacant characters. + * @param {Array} args packet arguments + */ handleCharsCheck(args) { document.getElementById("client_chartable").innerHTML = ""; for (let i = 0; i < this.chars.length; i++) { @@ -416,6 +491,10 @@ class Client { changeBackground("def"); } + /** + * Handles the server's assignment of a character for the player to use. + * @param {Array} args packet arguments + */ handlePV(args) { this.charID = args[3]; document.getElementById("client_charselect").style.display = "none"; -- cgit From 9f7f070be3d0ded8e6a9e3eae8aa55e5c6958df8 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 21:57:02 -0500 Subject: Enhance IC log and add timestamps --- webAO/client.js | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index a62d307..41a5916 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -27,6 +27,7 @@ if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phon } let selectedShout = 0; +let lastICMessageTime = new Date(0); class Client { constructor(address) { @@ -71,6 +72,8 @@ class Client { "CharsCheck": (args) => this.handleCharsCheck(args), "PV": (args) => this.handlePV(args) } + + this._lastTimeICReceived = new Date(0); } /** @@ -591,7 +594,7 @@ class Viewport { */ say(chatmsg) { this.chatmsg = chatmsg; - appendICLog(`${chatmsg.nameplate}: ${chatmsg.content}`); + appendICLog(chatmsg.content, chatmsg.nameplate); changeBackground(chatmsg.side); this.textnow = ''; this.sfxplayed = 0; @@ -908,9 +911,35 @@ window.RetryButton = RetryButton; /** * Appends a message to the in-character chat log. * @param {string} toadd the string to be added + * @param {string} name the name of the sender */ -function appendICLog(toadd) { - document.getElementById("client_log").appendChild(document.createTextNode(toadd)); +function appendICLog(toadd, name = "", time = new Date()) { + const entry = document.createElement("p"); + const nameField = document.createElement("span"); + nameField.id = "iclog_name"; + nameField.appendChild(document.createTextNode(name)); + entry.appendChild(nameField); + entry.appendChild(document.createTextNode(toadd)); + + // Only put a timestamp if the minute has changed. + if (lastICMessageTime.getMinutes() !== time.getMinutes()) { + const timeStamp = document.createElement("span"); + timeStamp.id = "iclog_time"; + timeStamp.innerText = time.toLocaleTimeString(undefined, { + hour: "numeric", + minute: "2-digit" + }); + entry.appendChild(timeStamp); + } + + const clientLog = document.getElementById("client_log"); + clientLog.appendChild(entry); + + if (clientLog.scrollTop > clientLog.scrollHeight - 600) { + clientLog.scrollTop = clientLog.scrollHeight; + } + + lastICMessageTime = new Date(); } /** -- cgit From 5973dd424852bd9db5c3612bb98070bc4ab00688 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 21:58:24 -0500 Subject: Use an array of blip channels instead of two hardcoded ones --- webAO/client.js | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index 41a5916..eca41da 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -556,16 +556,15 @@ class Viewport { this.blip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); this.blip.volume = 0.5; - // Wombo + combo: two audio channels allocated to make blips less jittery + // Allocate multiple blip audio channels to make blips less jittery // TODO: read blip type ("gender") from ini - this.womboblip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); - this.womboblip.volume = 0.5; - - this.comboblip = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); - this.comboblip.volume = 0.5; - - this.combo = false; + this.blipChannels = new Array(6); + for (let i = 0; i < this.blipChannels.length; i++) { + this.blipChannels[i] = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); + this.blipChannels[i].volume = 0.5; + } + this.currentBlipChannel = 0; this.sfxaudio = new Audio(AO_HOST + 'sounds/general/sfx-blipmale.wav'); this.sfxplayed = 0; @@ -581,6 +580,16 @@ class Viewport { this.textTimer = 0; } + /** + * Sets the volume of the blip sound. + * @param {number} volume + */ + setBlipVolume(volume) { + for (let i = 0; i < this.blipChannels.length; i++) { + this.blipChannels[i].volume = 0.5; + } + } + /** * Returns the path which the background is located in. */ @@ -659,15 +668,9 @@ class Viewport { } else { if (this.textnow != this.chatmsg.content) { if (this.chatmsg.content.charAt(this.textnow.length) != " ") { - this.combo = (this.combo + 1) % 2; - switch (this.combo) { - case 0: - this.blip.play() - break; - case 1: - //this.womboblip.play() - break; - } + this.blipChannels[this.currentBlipChannel].play(); + this.currentBlipChannel++; + this.currentBlipChannel %= this.blipChannels.length; } this.textnow = this.chatmsg.content.substring(0, this.textnow.length + 1); document.getElementById("client_inner_chat").innerHTML = this.textnow; @@ -790,9 +793,7 @@ window.changeSFXVolume = changeSFXVolume; * Triggered by the blip volume slider. */ export function changeBlipVolume() { - viewport.blip.volume = document.getElementById("client_bvolume").value / 100; - viewport.womboblip.volume = document.getElementById("client_bvolume").value / 100; - viewport.comboblip.volume = document.getElementById("client_bvolume").value / 100; + viewport.setBlipVolume(document.getElementById("client_bvolume").value / 100); } window.changeBlipVolume = changeBlipVolume; -- cgit From 78f8f96b97dffae06bdde1c52bd2d2942a622aa9 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 21:59:37 -0500 Subject: Log console on network close events --- webAO/client.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index eca41da..44a130c 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -186,6 +186,7 @@ class Client { * @param {CloseEvent} e */ onClose(e) { + console.error(`The connection was closed: ${e.reason} (${e.code})`); document.getElementById("client_error").style.display = "block"; this.cleanup(); } @@ -213,6 +214,7 @@ class Client { * @param {ErrorEvent} e */ onError(e) { + console.error(`A network error occurred: ${e.reason} (${e.code})`); document.getElementById("client_error").style.display = "block"; this.cleanup(); } -- cgit From e600f7a355a6b26032a3a693276c466477f72924 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 22:30:11 -0500 Subject: Use Java hash of user agent as HDID (sD's idea, not mine!) --- webAO/client.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index 44a130c..8dcb929 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -141,7 +141,7 @@ class Client { * to the server. */ joinServer() { - this.serv.send(`HI#${navigator.userAgent}#%`); + this.serv.send(`HI#${navigator.userAgent.hashCode()}#%`); this.serv.send("ID#webAO#2.4.5#%"); this.CHECKupdater = setInterval(() => this.sendCheck, 5000); } @@ -1029,6 +1029,18 @@ if (typeof(String.prototype.trim) === "undefined") }; } +// Used for HDID calculation. +String.prototype.hashCode = function() { + var hash = 0, i, chr; + if (this.length === 0) return hash; + for (i = 0; i < this.length; i++) { + chr = this.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +}; + // // Client code -- cgit From 2b9e81b2a9e52149f23adcae509afe6904db372b Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 22:46:57 -0500 Subject: Stop throwing away players' messages Only clear the chat input once our message has been actually sent. --- webAO/client.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index 8dcb929..f90c61a 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -261,6 +261,10 @@ class Client { } } + if (chatmsg.character == this.charID) { + resetICParams(); + } + viewport.say(chatmsg); } } @@ -754,17 +758,24 @@ export function onEnter(event) { ssfxname = myemo.sfx; ssfxdelay = myemo.sfxdelay; } - // TODO URGENT: Do NOT send if we know that our message is going to get thrown away! client.sendIC(myemo.speaking, mychar.name, myemo.silent, document.getElementById("client_inputbox").value, mychar.side, ssfxname, myemo.zoom, ssfxdelay, selectedShout); - document.getElementById("client_inputbox").value = ""; - if (selectedShout) { - document.getElementById("button_" + selectedShout).className = "client_button"; - selectedShout = 0; - } } } window.onEnter = onEnter; +/** + * Resets the IC parameters for the player to enter a new chat message. + * This should only be called when the player's previous chat message + * was successfully sent/presented. + */ +function resetICParams() { + document.getElementById("client_inputbox").value = ""; + if (selectedShout) { + document.getElementById("button_" + selectedShout).className = "client_button"; + selectedShout = 0; + } +} + /** * Triggered when an item on the music list is clicked. * @param {MouseEvent} event -- cgit From fc7555116a8152b2967c4ccd8f8afc49facbc16f Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 22:47:16 -0500 Subject: Fix blip volume slider (again) --- webAO/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index f90c61a..36817c5 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -592,7 +592,7 @@ class Viewport { */ setBlipVolume(volume) { for (let i = 0; i < this.blipChannels.length; i++) { - this.blipChannels[i].volume = 0.5; + this.blipChannels[i].volume = volume; } } -- cgit From 3408fc4af20b8c27b14dbc955a2253f72f6fd216 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 22:48:08 -0500 Subject: Play the correct objection sound (with unknown consequences) --- webAO/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index 36817c5..5c2332f 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -641,7 +641,7 @@ class Viewport { let shout = shouts[this.chatmsg.objection]; if (typeof shout !== "undefined") { document.getElementById("client_char").src = AO_HOST + "misc/" + shout + ".gif"; - this.chatmsg.sound = "sfx-" + shout; + (new Audio(`${AO_HOST}/characters/${this.chatmsg.name}/${shout}.wav`)).play(); this.shoutTimer = 800; } else { this.shoutTimer = 0; -- cgit From a62edeaf53278e19fc865e39e428317e3a49198d Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 22:48:53 -0500 Subject: Increase update interval even though it's probably wrong --- webAO/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index 5c2332f..f75970d 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -19,7 +19,7 @@ const MUSIC_HOST = AO_HOST + "sounds/music/"; const BAR_WIDTH = 90; const BAR_HEIGHT = 20; const CHAR_SELECT_WIDTH = 8; -const UPDATE_INTERVAL = 80; +const UPDATE_INTERVAL = 100; let oldLoading = false; if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) { -- cgit From 41d166c7912048fe1bdbf186fb68b9fc617ae2f1 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Tue, 22 May 2018 23:29:03 -0500 Subject: Improve connection error dialog --- webAO/client.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index f75970d..89baeff 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -187,8 +187,11 @@ class Client { */ onClose(e) { console.error(`The connection was closed: ${e.reason} (${e.code})`); - document.getElementById("client_error").style.display = "block"; - this.cleanup(); + if (e.code !== 1001) { + document.getElementById("client_error").style.display = "block"; + document.getElementById("error_id").textContent = e.code; + this.cleanup(); + } } /** @@ -216,10 +219,12 @@ class Client { onError(e) { console.error(`A network error occurred: ${e.reason} (${e.code})`); document.getElementById("client_error").style.display = "block"; + document.getElementById("error_id").textContent = e.code; this.cleanup(); } cleanup() { + this.serv.close(1001); clearInterval(this.checkUpdater); } @@ -906,6 +911,7 @@ function changeBackground(position) { * Triggered when the reconnect button is pushed. */ export function ReconnectButton() { + client.cleanup(); client = new Client(serverIP); if (client) { mode = "join"; // HACK: see client.onOpen -- cgit From 334ee0e9feebe15926e5ad9a967c24cdf5264cdb Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Wed, 23 May 2018 00:09:13 -0500 Subject: New client build, with GoldenLayout included --- webAO/client.js | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'webAO/client.js') diff --git a/webAO/client.js b/webAO/client.js index 89baeff..6d301e5 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -19,7 +19,7 @@ const MUSIC_HOST = AO_HOST + "sounds/music/"; const BAR_WIDTH = 90; const BAR_HEIGHT = 20; const CHAR_SELECT_WIDTH = 8; -const UPDATE_INTERVAL = 100; +const UPDATE_INTERVAL = 65; let oldLoading = false; if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) { @@ -224,7 +224,11 @@ class Client { } cleanup() { - this.serv.close(1001); + try { + this.serv.close(1001); + } catch (e) { + // I don't care if this errors + } clearInterval(this.checkUpdater); } @@ -279,7 +283,11 @@ class Client { * @param {Array} args packet arguments */ handleCT(args) { - document.getElementById("client_ooclog").innerHTML = document.getElementById("client_ooclog").innerHTML + args[1] + ": " + args[2] + "\r\n"; + const oocLog = document.getElementById("client_ooclog"); + oocLog.innerHTML += `${args[1]}: ${args[2]}\r\n`; + if (oocLog.scrollTop > oocLog.scrollHeight - 60) { + oocLog.scrollTop = oocLog.scrollHeight; + } } /** @@ -589,6 +597,16 @@ class Viewport { this.shoutTimer = 0; this.textTimer = 0; + + this._animating = false; + } + + /** + * Returns whether or not the viewport is busy + * performing a task (animating). + */ + isAnimating() { + return this._animating; } /** @@ -619,6 +637,7 @@ class Viewport { this.textnow = ''; this.sfxplayed = 0; this.textTimer = 0; + this._animating = true; this.updater = setInterval(() => this.updateText(), UPDATE_INTERVAL); } @@ -687,6 +706,7 @@ class Viewport { document.getElementById("client_inner_chat").innerHTML = this.textnow; if (this.textnow == this.chatmsg.content) { this.textTimer = 0; + this._animating = false; clearInterval(this.updater); document.getElementById("client_char").src = AO_HOST + "characters/" + escape(this.chatmsg.name) + "/" + this.chatmsg.silent + ".gif"; } -- cgit