aboutsummaryrefslogtreecommitdiff
path: root/webAO
diff options
context:
space:
mode:
authorcaleb.mabry.15@cnu.edu <caleb.mabry.15@cnu.edu>2022-03-10 23:21:38 -0500
committercaleb.mabry.15@cnu.edu <caleb.mabry.15@cnu.edu>2022-03-10 23:21:38 -0500
commitdc208478fd18a4e28c0083641bf405170859fc58 (patch)
tree1fad0ecbaf5639d02908b5f9c9f31f1878ee446c /webAO
parent6dd1b296c2dd1d7462dd8514dff43db59ac8dd19 (diff)
Magnum Opus pt1
Diffstat (limited to 'webAO')
-rw-r--r--webAO/client.html613
-rw-r--r--webAO/client.js342
-rw-r--r--webAO/client/setEmote.js43
-rw-r--r--webAO/dom/changeSFXVolume.js10
-rw-r--r--webAO/dom/changeShoutVolume.js10
-rw-r--r--webAO/dom/toggleEffect.js13
-rw-r--r--webAO/favicon.icobin5694 -> 0 bytes
-rw-r--r--webAO/images/desc.pngbin50785 -> 0 bytes
-rw-r--r--webAO/index.html126
-rw-r--r--webAO/logo-new-512.pngbin238578 -> 0 bytes
-rw-r--r--webAO/logo-new.pngbin29302 -> 0 bytes
-rw-r--r--webAO/manifest.json23
-rw-r--r--webAO/master.js2
-rw-r--r--webAO/packets/ms.js28
-rw-r--r--webAO/styles/master.css2
-rw-r--r--webAO/sw.js26
-rw-r--r--webAO/utils/fileExists.js17
-rw-r--r--webAO/utils/fileExistsSync.js11
-rw-r--r--webAO/utils/getAnimLength.js22
-rw-r--r--webAO/utils/getResources.js39
-rw-r--r--webAO/utils/queryParser.js9
21 files changed, 261 insertions, 1075 deletions
diff --git a/webAO/client.html b/webAO/client.html
deleted file mode 100644
index 5dd0264..0000000
--- a/webAO/client.html
+++ /dev/null
@@ -1,613 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <title>Attorney Online session</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta name="description" content="An off-the-cuff courtroom drama simulator">
- <meta name="author" content="stonedDiscord">
-
- <meta property="og:title" content="Attorney Online">
- <meta property="og:description" content="Attorney Online is an online version of the world-renowned courtroom drama simulator that allows you to create and play out cases in an off-the-cuff format.">
- <meta property="og:type" content="website">
- <meta property="og:url" content="http://web.aceattorneyonline.com/">
- <meta property="og:image" content="https://repository-images.githubusercontent.com/78860508/89fcba80-aafd-11e9-80db-c5b10c01aba9">
-
- <meta property="twitter:card" content="summary_large_image">
- <meta property="twitter:title" content="Attorney Online">
- <meta property="twitter:description" content="Attorney Online is an online version of the world-renowned courtroom drama simulator that allows you to create and play out cases in an off-the-cuff format.">
- <meta property="twitter:url" content="http://web.aceattorneyonline.com/">
- <meta property="twitter:image" content="https://repository-images.githubusercontent.com/78860508/89fcba80-aafd-11e9-80db-c5b10c01aba9">
-
- <meta http-equiv="Content-Security-Policy" content="script-src-elem 'self' 'unsafe-inline' https://ajax.googleapis.com;
- script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ajax.googleapis.com;
- style-src-elem 'self' 'unsafe-inline' https://golden-layout.com;
- style-src 'self' 'unsafe-inline' https://golden-layout.com;
- img-src 'self' data: file: *;
- connect-src 'self' ws: file: *;
- media-src 'self' file: *;">
-
- <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
- <link rel="apple-touch-icon" href="/images/logo-new.png"/>
- <link rel="stylesheet" type="text/css" href="styles/client.css?v=1.0.3" id="client_stylesheet">
- <link rel="stylesheet" type="text/css" href="styles/default.css?v=1.0.1" id="client_theme">
- <link rel="stylesheet" type="text/css" href="styles/chatbox/aa.css?v=1.0.1" id="chatbox_theme">
- <link rel="stylesheet" type="text/css" href="styles/nameplates.css" id="nameplate_setting">
- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
- <link type="text/css" rel="stylesheet" href="golden/css/goldenlayout.css" />
- <link type="text/css" rel="stylesheet"
- href="https://golden-layout.com/files/latest/css/goldenlayout-dark-theme.css" />
- <script src="lib/jdataview.min.js"></script>
- <script src="lib/gify.min.js"></script>
- <script src="ui.b.js"></script>
- <script src="client.b.js?v=1.0.2"></script>
-</head>
-
-<body>
- <div id="client_loading">
- <h1 id="client_loadingtext">Loading</h1>
- <noscript><p style="color: red;">webAO requires JavaScript to work</p></noscript>
- <p>Having trouble? <a href="https://discord.gg/9rYQVVQ">Join us on Discord</a></p>
- <button class="client_reconnect" onclick="ReconnectButton()">Reconnect</button>
- </div>
- <div id="client_charselect">
- <p>Choose your character</p>
- <button onclick="pickChar(-1)">Or spectate</button>
- <br><br>
- <input id="client_charactersearch" placeholder="Search" oninput="chartable_filter(event)"></input>
- <br><br>
- <div id="client_chartable"></div>
- </div>
- <div id="client_error" class="error" style="display: none">
- <h1 id="client_errortext">You were disconnected from the server.</h1>
- <p style="color: #a00">Code:
- <span id="error_id">(none)</span>
- </p>
- <button class="client_reconnect" onclick="ReconnectButton()">Reconnect</button>
- </div>
-</body>
-
-<template id="client_wrapper">
- <meta name="frame-title" lang="en" content="Game">
- <div id="client_icwrapper">
- <div id="client_background">
- <div id="client_gamewindow">
- <div id="client_fullview">
- <img id="client_court" onerror="switchPanTilt(2);" onload="switchPanTilt(1);">
- <div id="client_stitch_court">
- <img id="client_court_def" onerror="imgError(this);">
- <img id="client_court_deft" onerror="imgError(this);">
- <img id="client_court_wit" onerror="imgError(this);">
- <img id="client_court_prot" onerror="imgError(this);">
- <img id="client_court_pro" onerror="imgError(this);">
- </div>
- <div id="client_def_pair_char" class="client_char" alt="Paired character">
- <img id="client_def_pair_gif" onerror="charError(this);">
- <img id="client_def_pair_png" onerror="charError(this);">
- <img id="client_def_pair_apng" onerror="charError(this);">
- <img id="client_def_pair_webp" onerror="charError(this);">
- </div>
- <div id="client_def_char" class="client_char" alt="Character">
- <img id="client_def_char_gif" onerror="charError(this);">
- <img id="client_def_char_png" onerror="charError(this);">
- <img id="client_def_char_apng" onerror="charError(this);">
- <img id="client_def_char_webp" onerror="charError(this);">
- </div>
- <div id="client_wit_pair_char" class="client_char" alt="Paired character">
- <img id="client_wit_pair_gif" onerror="charError(this);">
- <img id="client_wit_pair_png" onerror="charError(this);">
- <img id="client_wit_pair_apng" onerror="charError(this);">
- <img id="client_wit_pair_webp" onerror="charError(this);">
- </div>
- <div id="client_wit_char" class="client_char" alt="Character">
- <img id="client_wit_char_gif" onerror="charError(this);">
- <img id="client_wit_char_png" onerror="charError(this);">
- <img id="client_wit_char_apng" onerror="charError(this);">
- <img id="client_wit_char_webp" onerror="charError(this);">
- </div>
- <div id="client_pro_pair_char" class="client_char" alt="Paired character">
- <img id="client_pro_pair_gif" onerror="charError(this);">
- <img id="client_pro_pair_png" onerror="charError(this);">
- <img id="client_pro_pair_apng" onerror="charError(this);">
- <img id="client_pro_pair_webp" onerror="charError(this);">
- </div>
- <div id="client_pro_char" class="client_char" alt="Character">
- <img id="client_pro_char_gif" onerror="charError(this);">
- <img id="client_pro_char_png" onerror="charError(this);">
- <img id="client_pro_char_apng" onerror="charError(this);">
- <img id="client_pro_char_webp" onerror="charError(this);">
- </div>
- <img id="client_def_bench" class="client_bench">
- <img id="client_wit_bench" class="client_bench">
- <img id="client_pro_bench" class="client_bench">
- </div>
- <div id="client_classicview">
- <img id="client_court_classic" onerror="imgError(this);">
- <div id="client_pair_char" class="client_char" alt="Paired character">
- <img id="client_pair_gif" onerror="charError(this);">
- <img id="client_pair_png" onerror="charError(this);">
- <img id="client_pair_apng" onerror="charError(this);">
- <img id="client_pair_webp" onerror="charError(this);">
- </div>
- <div id="client_char" class="client_char" alt="Character">
- <img id="client_char_gif" onerror="charError(this);">
- <img id="client_char_png" onerror="charError(this);">
- <img id="client_char_apng" onerror="charError(this);">
- <img id="client_char_webp" onerror="charError(this);">
- </div>
- <img id="client_bench_classic" class="client_bench">
- </div>
- <img id="client_fg" alt="Various overlay" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
- <img id="client_evi" src="" alt="Character Evidence" onerror="imgError(this);">
- <img id="client_testimony" alt="Testimony overlay" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" onerror="imgError(this);">
- <img id="client_shout" alt="Shout overlay" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
- <div id="client_chatcontainer" style="font-size: 12px; opacity: 0;">
- <div id="client_chatdecoration"></div>
- <div id="client_name">
- <p id="client_inner_name"></p>
- </div>
- <div id="client_chat">
- <p id="client_inner_chat"></p>
- <div id="client_chatwaiting">&#9658;</div>
- </div>
- </div>
- <div id="client_trackstatus" style="display: none;"><p id="client_trackstatustext">None</p></div>
- </div>
- </div>
- <input id="client_inputbox" class="long" type="text" onkeypress="onEnter(event)"
- placeholder="Say something&hellip;" autocomplete="off">
- <div id="client_bars">
- <span id="client_defense_hp" class="health-box">
- <div class="health-bar"></div>
- </span>
- <span id="client_prosecutor_hp" class="health-box">
- <div class="health-bar"></div>
- </span>
- </div>
- <div id="client_iccontrols">
- <meta name="frame-title" lang="en" content="miscellaneous">
- <div id="client_emo">
- </div>
- <br>
- <div id="character_options">
- <div id="emote_options">
- <button id="button_1" alt="Hold it!" class="objection_button" onclick="toggleShout(1)">Hold it!</button>
- <button id="button_2" alt="Objection!" class="objection_button"
- onclick="toggleShout(2)">Objection!</button>
- <button id="button_3" alt="Take That!" class="objection_button" onclick="toggleShout(3)">Take
- That!</button>
- <button id="button_4" alt="Custom" class="objection_button" onclick="toggleShout(4)" style="display: none">
- Custom</button>
- <br>
- <br>
- <label for="textcolor">Text Color:</label>
- <select id="textcolor" name="textcolor">
- <option value="0">White</option>
- <option value="1">Green</option>
- <option value="2">Red</option>
- <option value="3">Orange</option>
- <option value="4">Blue</option>
- </select>
- <label for="sendsfx"> SFX: </label>
- <input type="checkbox" id="sendsfx" name="sendsfx" value="sendsfx" checked>
- <label for="sendpreanim"> Preanimation: </label>
- <input type="checkbox" id="sendpreanim" name="sendpreanim" value="sendpreanim">
- <br>
- <br>
- <button id="button_flip" alt="Flip" class="client_button" onclick="toggleEffect(this)" style="display: none">Flip</button>
- <button id="button_flash" alt="Flash" class="client_button" onclick="toggleEffect(this)">Flash</button>
- <button id="button_shake" alt="Shake" class="client_button" onclick="toggleEffect(this)" style="display: none">Shake</button>
- </div>
- <br>
- <br>
- <div id="character_options">
- <label for="role_select">Role:</label>
- <select id="role_select" name="role_select" onchange="changeRoleOOC()">
- <option value="">Default</option>
- <option value="def">Defense</option>
- <option value="pro">Prosecution</option>
- <option value="jud">Judge</option>
- <option value="wit">Witness</option>
- <option value="hld">Helper Defense</option>
- <option value="hlp">Helper Prosecution</option>
- <option value="jur">Jury</option>
- <option value="sea">Seance</option>
- </select>
- <br>
- <br>
- <button id="char_change" alt="Change" class="client_button hover_button" onclick="changeCharacter()">Change
- Character</button>
- <button id="char_random" alt="Random" class="client_button hover_button"
- onclick="randomCharacterOOC()">Random Character</button>
- <br>
- <br>
- <span id="pairing" style="display: none">
- <label for="pair_select">Pairing partner:</label>
- <select name="pair_select" id="pair_select">
- <option value="-1">None</option>
- </select>
- <table style="border: none;margin-left: auto;margin-right: auto;">
- <tr ><td><p>Horizontal offset:</p></td><td><input type="range" name="pair_offset" id="pair_offset" min="-100" max="100" value="0"></td></tr>
- <tr id="y_offset" style="display: none"><td><p >Vertical offset:</p></td><td><input type="range" name="pair_y_offset" id="pair_y_offset" min="-100" max="100" value="0"></td></tr>
- </table>
- <input id="pair_reset" type="button" value="Reset" onclick="resetOffset()">
- <br>
- <br>
- <br>
- <span id="cccc" style="display: none">
- <label for="ic_chat_name">Custom Showname:</label>
- <input id="ic_chat_name" name="ic_chat_name" type="text">
- <br>
- <label for="showname">Show others:</label>
- <input id="showname" name="showname" type="checkbox" onclick="showname_click()">
- <br>
- <br>
- <label for="check_nonint">Noninterrupting Preanimation:</label>
- <input type="checkbox" name="check_nonint" id="check_nonint">
- </span>
- <br>
- <br>
- <span id="2.7" style="display: none">
- <label for="check_loopsfx">Looping SFX:</label>
- <input type="checkbox" name="check_loopsfx" id="check_loopsfx">
- </span>
- <br>
- <br>
- <span id="2.8" style="display: none">
- <label for="check_additive">Additive:</label>
- <input type="checkbox" name="check_additive" id="check_additive">
- <br>
- <br>
- <label for="effect_select">Effect:</label>
- <select id="effect_select" name="effect_select">
- <option value="||">None</option>
- <option value="realization||sfx-realization">Realization</option>
- <option value="hearts||sfx-squee">Hearts</option>
- <option value="reaction||sfx-reactionding">Reaction</option>
- <option value="impact||sfx-fan">Impact</option>
- </select>
- </span>
- </div>
- </div>
- <fieldset style="margin:10px;">
- <legend>Actions</legend>
- <!-- Judge Commands -->
- <span id="judge_action" style="display:none">
- <span id="menu_wt" onclick="initWT()" class="judge_button"
- style="color: blue"><i>Witness<br>Testimony</i></span>
-
- <span id="menu_ce" onclick="initCE()" class="judge_button"
- style="color: red"><i>Cross<br>Examination</i></span>
-
- <span id="menu_nguilty" onclick="notguilty()" class="judge_button"
- style="color: white; font-family: serif; text-shadow: -1px 0 #000, 0 1px #000, 1px 0 #000, 0 -1px #000; font-size: 1.5em; line-height: 0.75;">Not<br>Guilty</span>
-
- <span id="menu_guilty" onclick="guilty()" class="judge_button"
- style="color: black; font-family: serif; font-size: 1.5em;">Guilty</span>
-
- <br>
- <span style="display:inline-block; vertical-align: middle;">
- <span id="menu_rhpd" onclick="redHPD()" class="healthchange_button">
- &#8863;
- </span>
- <span style="font-size: 1.25em">Defense</span>
- <span id="menu_ahpd" onclick="addHPD()" class="healthchange_button">
- &#8862;
- </span>
- </span>
- <span style="display:inline-block; vertical-align: middle;">
- <span id="menu_ahpp" onclick="addHPP()" class="healthchange_button">
- &#8862;
- </span>
- <span style="font-size: 1.25em">Prosecution</span>
- <span id="menu_rhpp" onclick="redHPP()" class="healthchange_button">
- &#8863;
- </span>
- </span>
- </span>
- <!-- No Commands -->
- <span id="no_action">
- No judge actions available for this role.
- </span>
- <br>
- <br>
- <span id="muting">
- <label for="mute_select" style="float: left">Mute a character: </label>
- <select name="mute_select" id="mute_select" size="15" style="float: left"
- onchange="mutelist_click(event)"></select>
- </span>
- </fieldset>
- </div>
- </div>
-</template>
-
-<template id="mainmenu">
- <meta name="frame-title" lang="en" content="Log">
- <div id="client_menu">
- <div id="client_menu_buttons">
- <div class="hrtext">Main Menu</div>
- <span id="menu_1" onclick="toggleMenu(1)" class="menu_button active">
- <b class="menu_icon">&#128205;</b>
- <div class="menu_text">Areas</div>
- </span>
- <span id="menu_2" onclick="toggleMenu(2)" class="menu_button">
- <b class="menu_icon">&#128188;</b>
- <div class="menu_text">Evidence</div>
- </span>
- <span id="menu_3" onclick="toggleMenu(3)" class="menu_button">
- <b class="menu_icon">&#128295;</b>
- <div class="menu_text">Settings</div>
- </span>
- <span id="menu_4" onclick="toggleMenu(4)" class="menu_button">
- <b class="menu_icon">&#10067;</b>
- <div class="menu_text">About</div>
- </span>
- <span id="menu_cm" onclick="callMod()" class="menu_button">
- <b class="menu_icon">&#128680;</b>
- <div class="menu_text" style="color: #ce2727;">Call Mod</div>
- </span>
- </div>
- <div class="hrtext">
- <span id="content_name">Content</span>
- </div>
- <!-- Areas section -->
- <span class="menu_content active" id="content_1">
- <meta name="frame-title" lang="en" content="Areas">
- <div id="areas"></div>
- <br>
- <fieldset style="text-align: left">
- <legend>Current Area Background</legend>
- <span>
- <img id="bg_preview" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" alt="Background Preview" onerror="imgError(this);">
- </span>
- <span style="display:inline-block">
- <label for="bg_filename">Background:</label>
- <br>
- <select id="bg_select" name="bg_select" onchange="updateBackgroundPreview()"
- style="margin-top:10px"></select>
- <br>
- <input id="bg_filename" name="bg_filename" type="text" style="margin-top:10px">
- <br>
- <br>
- <button id="bg_change" alt="Change" class="client_button hover_button"
- onclick="changeBackgroundOOC()">Change</button>
- </span>
- </fieldset>
- <fieldset style="text-align: left; display: none">
- <legend>Timers</legend>
- <span id="client_timer0">00:00:00</span>
- <span id="client_timer1">00:00:00</span>
- <span id="client_timer2">00:00:00</span>
- <span id="client_timer3">00:00:00</span>
- <span id="client_timer4">00:00:00</span>
- <span id="client_timer5">00:00:00</span>
- <span id="client_timer6">00:00:00</span>
- <span id="client_timer7">00:00:00</span>
- <span id="client_timer8">00:00:00</span>
- <span id="client_timer9">00:00:00</span>
- <span id="client_timer10">00:00:00</span>
- <span id="client_timer11">00:00:00</span>
- <span id="client_timer12">00:00:00</span>
- <span id="client_timer13">00:00:00</span>
- <span id="client_timer14">00:00:00</span>
- <span id="client_timer15">00:00:00</span>
- <span id="client_timer16">00:00:00</span>
- <span id="client_timer17">00:00:00</span>
- <span id="client_timer18">00:00:00</span>
- <span id="client_timer19">00:00:00</span>
- <span id="client_timer20">00:00:00</span>
- </fieldset>
- </span>
-
- <!-- Evidence section -->
- <span class="menu_content" id="content_2">
- <meta name="frame-title" lang="en" content="Evidence">
- <fieldset style="text-align: left; display: flex; flex-direction: column;">
- <legend>Information</legend>
- <hr>
- <div style="display: flex;">
- <img id="evi_preview" class="evi_icon" src="" alt="Evidence Icon" onerror="imgError(this);">
- <div id="evi_options">
- <input id="evi_name" name="evi_name" type="text" placeholder="Evidence name">
- <br>
- <label for="evi_filename">Icon:</label>
- <select id="evi_select" name="evi_select" onchange="updateEvidenceIcon()"></select>
- <input id="evi_filename" name="evi_filename" type="text"
- placeholder="Custom filename">
- </div>
- </div>
- <hr>
- <div style="display: flex; padding-top: 5px;">
- <textarea id="evi_desc" name="evi_desc" rows="2" cols="20"
- placeholder="Evidence description"></textarea>
- </div>
- <div style="display: block; text-align: center; padding-top: 20px;">
- <button id="evi_add" alt="Add Evidence" class="client_button hover_button"
- onclick="addEvidence()">Add</button>
- <button id="evi_edit" alt="Edit Evidence" class="client_button hover_button inactive"
- onclick="editEvidence()">Edit</button>
- <button id="evi_cancel" alt="Cancel Evidence" class="client_button hover_button inactive"
- onclick="cancelEvidence()">Cancel</button>
- <button id="evi_del" alt="Remove Evidence" class="client_button hover_button inactive"
- onclick="deleteEvidence()">Remove</button>
- </div>
- </fieldset>
- <br>
- <div id="evidences"></div>
- <br>
- <button id="button_present" alt="Present" class="client_button" onclick="toggleEffect(this)">Present</button>
- </span>
-
- <!-- Settings section -->
- <span class="menu_content" id="content_3">
- <meta name="frame-title" lang="en" content="Settings">
- <h2>Volume</h2>
- <p>Music</p>
- <input id="client_mvolume" class="long" type="range" min="0" max="1" value="1" step="0.01"
- onchange="changeMusicVolume()">
-
- <p>SFX</p>
- <audio id="client_sfxaudio" onvolumechange="changeSFXVolume()" onerror="opusCheck()" controls></audio>
-
- <p>Shouts</p>
- <audio id="client_shoutaudio" onvolumechange="changeShoutVolume()" onerror="opusCheck()" controls></audio>
-
- <p>Testimony/Guilty</p>
- <audio id="client_testimonyaudio" onvolumechange="changeTestimonyVolume()" onerror="opusCheck()" controls></audio>
-
- <p>Blip</p>
- <input id="client_bvolume" class="long" type="range" min="0" max="1" value="1" step="0.01"
- onchange="changeBlipVolume()">
- <br>
- <br>
- <label for="client_themeselect">Menu theme:</label>
- <select id="client_themeselect" name="client_themeselect" onchange="reloadTheme()">
- <option value="default" selected>Default</option>
- <option value="classic">Classic</option>
- <option value="soj">DD / SoJ</option>
- <option value="cyber">Cyber</option>
- <option value="trilogy">Trilogy</option>
- </select>
- <br>
- <br>
- <label for="client_chatboxselect">Chatbox theme:</label>
- <select id="client_chatboxselect" name="client_chatboxselect" onchange="setChatbox(this.value)">
- <option value="dynamic" selected>Use characters</option>
- <option value="aa">AA</option>
- <option value="chatdd">DD / SoJ</option>
- <option value="dgs">DGS</option>
- <option value="chatplvsaa">PL vs AA</option>
- <option value="trilogy">Trilogy</option>
- <option value="chatfuture">Future theme</option>
- <option value="legacy">old webAO</option>
- <option value="chat999">999</option>
- <option value="acww">Animal Crossing</option>
- <option value="dr1">DR 1</option>
- <option value="chatdr2">DR 2</option>
- <option value="drv3">DR 3</option>
- <option value="drae">DR: AE</option>
- <option value="ddlc">DDLC</option>
- <option value="homestuck">Homestuck</option>
- <option value="key">Key</option>
- <option value="n64zelda">N64 Zelda</option>
- <option value="papermario">Paper Mario</option>
- <option value="chatp3">Persona 3</option>
- <option value="p4">Persona 4</option>
- <option value="p5">Persona 5</option>
- <option value="halla">VA-11 HALL-A</option>
- <option value="whentheycry">When They Cry Series</option>
- <option value="yakuza">Yakuza</option>
- <option value="yttd">YTTD</option>
- </select>
- <!--
- <br>
- <br>
- <button id="client_modcall" onclick="modcall_test()">Test modcall</button>
- -->
- <br>
- <br>
- <p>Ini editing (experimental)</p>
- <label for="client_ininame">Iniedit Character:</label>
- <select type="text" id="client_ininame" name="client_ininame"></select>
- <button id="client_inichange" onclick="iniedit()">Change</button>
- <br>
- <br>
- <p>Pan-tilt (experimental)</p>
- <label for="client_pantilt">Enable:</label>
- <input type="checkbox" id="client_pantilt" name="client_pantilt" onclick="switchPanTilt()">
- <br>
- <br>
- <p>16:9 viewport (experimental)</p>
- <label for="client_hdviewport">Enable:</label>
- <input type="checkbox" id="client_hdviewport" name="client_hdviewport" onclick="switchAspectRatio()">
- <br>
- <label for="client_hdviewport_offset">Offset chatbox:</label>
- <input type="checkbox" id="client_hdviewport_offset" id="client_hdviewport_offset" onclick="switchChatOffset()">
- <br>
- <br>
- <label for="client_callwords">Callwords:</label>
- <br>
- <textarea id="client_callwords" name="client_callwords" rows="4" cols="10"
- placeholder="Put 1 callword per line here" onchange="changeCallwords()"></textarea>
- <br>
- <br>
- <span style="color:red">&#8595; Only touch these settings if you know what you are doing. &#8595;</span>
- <br>
- <br>
- <label for="client_encoding">Client side chat encoding:</label>
- <select id="client_encoding" name="client_encoding">
- <option value="none" selected>None</option>
- <option value="unicode">Unicode</option>
- <option value="utf16">UTF-16</option>
- </select>
- <br>
- <br>
- <label for="client_decoding">Client side chat decoding:</label>
- <select id="client_decoding" name="client_decoding">
- <option value="none">None</option>
- <option value="unicode" selected>Unicode</option>
- <option value="utf16">UTF-16</option>
- </select>
- <br>
- <br>
- <label for="bg_command">Change background command:</label>
- <input id="bg_command" name="bg_command" type="text" value="bg $1">
- <br>
- <br>
- <label for="randomchar_command">Random character command:</label>
- <input id="randomchar_command" name="randomchar_command" type="text" value="randomchar">
- <br>
- <br>
- <span style="color:blue">Changing these settings will save them as a cookie.<br>
- By doing so, you agree to it being saved.<br>
- If you don't agree, disable cookies for this site in your browser.</span>
- </span>
-
- <!-- About section -->
- <span class="menu_content" id="content_4">
- <meta name="frame-title" lang="en" content="About">
- <img id="about-logo" src="images/logo-new.png" alt="Attorney Online logo">
- <h1 style="line-height: .3em;">webAO</h1>
- <h3 id="client_version">version</h3>
- <p>Client created by
- <a href="https://github.com/stonedDiscord">@stonedDiscord</a> and fixed up by
- <a href="https://github.com/oldmud0">@oldmud0</a> and
- <a href="https://github.com/qubrick">Qubrick</a>.</p>
- <p><a href="https://github.com/AttorneyOnline/webAO">Follow development on our GitHub</a></p>
- <p>Special thanks to Aleks for the first webAO PoC.</p>
- <p><a href="styles/igiari/readme.txt">Igiari font by Caveras</a></p>
- </span>
- </div>
-</template>
-
-<template id="log">
- <meta name="frame-title" lang="en" content="Log">
- <div id="client_log">
- <div class="hrtext">&#8595; log starts here &#8595;</div>
- </div>
-</template>
-
-<template id="ooc">
- <meta name="frame-title" lang="en" content="Server">
- <div style="height: 100%; display: flex; flex-direction: column;">
- <textarea id="client_ooclog" style="flex: 1 auto" readonly></textarea>
- <span id="client_oocinput">
- <input id="OOC_name" name="OOC_name" type="text">
- <input id="client_oocinputbox" type="text" onkeypress="onOOCEnter(event)">
- </span>
- <span id="client_replaycontrols" style="display: none; white-space: nowrap;">
- <input id="client_replaygo" style="width: 25%;" type="button" onclick="onReplayGo(event)" value="GO">
- <input id="client_replaytimer" style="width: 25%;" type="number" value="2000">
- </span>
- </div>
-</template>
-
-<template id="music">
- <meta name="frame-title" lang="en" content="Music">
- <input id="client_musicsearch" placeholder="Search" oninput="musiclist_filter(event)"></input>
- <select id="client_musiclist" size="5" onchange="musiclist_click(event)">
- </select>
-</template>
-
-</html>
diff --git a/webAO/client.js b/webAO/client.js
index c10d25b..adab9dd 100644
--- a/webAO/client.js
+++ b/webAO/client.js
@@ -19,28 +19,29 @@ import vanilla_evidence_arr from './constants/evidence.js';
import chatbox_arr from './styles/chatbox/chatboxes.js';
import iniParse from './iniParse';
-import calculatorHandler from './utils/calculatorHandler.js';
import getCookie from './utils/getCookie.js';
import setCookie from './utils/setCookie.js';
import request from './services/request.js';
+import { changeShoutVolume } from './dom/changeShoutVolume.js';
+import { changeSFXVolume } from './dom/changeSFXVolume.js';
+import setEmote from './client/setEmote.js';
+import fileExists from './utils/fileExists.js';
+import queryParser from './utils/queryParser.js';
+import getAnimLength from './utils/getAnimLength.js';
+import getResources from './utils/getResources.js';
const version = process.env.npm_package_version;
let client;
let viewport;
-
// Get the arguments from the URL bar
-const queryDict = {};
-location.search.substr(1).split('&').forEach((item) => {
- queryDict[item.split('=')[0]] = item.split('=')[1];
-});
-
-let { ip: serverIP, mode } = queryDict;
-
+let {
+ ip: serverIP, mode, asset, theme,
+} = queryParser();
// Unless there is an asset URL specified, use the wasabi one
const DEFAULT_HOST = 'http://attorneyoffline.de/base/';
-let AO_HOST = queryDict.asset || DEFAULT_HOST;
-const THEME = queryDict.theme || 'default';
+let AO_HOST = asset || DEFAULT_HOST;
+const THEME = theme || 'default';
const UPDATE_INTERVAL = 60;
@@ -121,44 +122,7 @@ class Client extends EventEmitter {
this.banned = false;
- this.resources = {
- holdit: {
- src: `${AO_HOST}misc/default/holdit_bubble.png`,
- duration: 720,
- },
- objection: {
- src: `${AO_HOST}misc/default/objection_bubble.png`,
- duration: 720,
- },
- takethat: {
- src: `${AO_HOST}misc/default/takethat_bubble.png`,
- duration: 840,
- },
- custom: {
- src: '',
- duration: 840,
- },
- witnesstestimony: {
- src: `${AO_HOST}themes/${THEME}/witnesstestimony.gif`,
- duration: 1560,
- sfx: `${AO_HOST}sounds/general/sfx-testimony.opus`,
- },
- crossexamination: {
- src: `${AO_HOST}themes/${THEME}/crossexamination.gif`,
- duration: 1600,
- sfx: `${AO_HOST}sounds/general/sfx-testimony2.opus`,
- },
- guilty: {
- src: `${AO_HOST}themes/${THEME}/guilty.gif`,
- duration: 2870,
- sfx: `${AO_HOST}sounds/general/sfx-guilty.opus`,
- },
- notguilty: {
- src: `${AO_HOST}themes/${THEME}/notguilty.gif`,
- duration: 2440,
- sfx: `${AO_HOST}sounds/general/sfx-notguilty.opus`,
- },
- };
+ this.resources = getResources(AO_HOST, THEME);
this.selectedEmote = -1;
this.selectedEvidence = 0;
@@ -220,15 +184,15 @@ class Client extends EventEmitter {
}
/**
- * Gets the player's currently selected emote.
- */
+ * Gets the player's currently selected emote.
+ */
get emote() {
return this.emotes[this.selectedEmote];
}
/**
- * Gets the current evidence ID unless the player doesn't want to present any evidence
- */
+ * Gets the current evidence ID unless the player doesn't want to present any evidence
+ */
get evidence() {
return (document.getElementById('button_present').classList.contains('dark')) ? this.selectedEvidence : 0;
}
@@ -238,12 +202,7 @@ class Client extends EventEmitter {
* @param {string} message the message to send
*/
sendServer(message) {
- console.debug(`C: ${message}`);
- if (mode === 'replay') {
- this.sendSelf(message);
- } else {
- this.serv.send(message);
- }
+ mode === 'replay' ? this.sendSelf(message) : this.serv.send(message);
}
/**
@@ -270,7 +229,9 @@ class Client extends EventEmitter {
*/
sendOOC(message) {
setCookie('OOC_name', document.getElementById('OOC_name').value);
- this.sendServer(`CT#${escapeChat(encodeChat(document.getElementById('OOC_name').value))}#${escapeChat(encodeChat(message))}#%`);
+ const oocName = `${escapeChat(encodeChat(document.getElementById('OOC_name').value))}`;
+ const oocMessage = `${escapeChat(encodeChat(message))}`;
+ this.sendServer(`CT#${oocName}#${oocMessage}#%`);
}
/**
@@ -427,8 +388,6 @@ class Client extends EventEmitter {
* to the server.
*/
joinServer() {
- console.log(`Your emulated HDID is ${hdid}`);
-
this.sendServer(`HI#${hdid}#%`);
this.sendServer('ID#webAO#webAO#%');
if (mode !== 'replay') { this.checkUpdater = setInterval(() => this.sendCheck(), 5000); }
@@ -1537,7 +1496,7 @@ class Client extends EventEmitter {
this.charID = Number(args[3]);
document.getElementById('client_charselect').style.display = 'none';
- const me = this.character;
+ const me = this.chars[this.charID];
this.selectedEmote = -1;
const { emotes } = this;
const emotesList = document.getElementById('client_emo');
@@ -1742,22 +1701,6 @@ class Viewport {
}
/**
- * Returns whether or not the viewport is busy
- * performing a task (animating).
- */
- get isAnimating() {
- return this._animating;
- }
-
- /**
- * Sets the volume of the blip sounds.
- * @param {number} volume
- */
- set blipVolume(volume) {
- this.blipChannels.forEach((channel) => channel.volume = volume);
- }
-
- /**
* Sets the volume of the music.
* @param {number} volume
*/
@@ -1794,21 +1737,21 @@ class Viewport {
const bgfolder = viewport.bgFolder;
const view = document.getElementById('client_fullview');
-
+
let bench;
if ('def,pro,wit'.includes(position)) {
- bench = document.getElementById('client_'+position+'_bench');
+ bench = document.getElementById(`client_${position}_bench`);
} else {
bench = document.getElementById('client_bench_classic');
}
-
+
let court;
if ('def,pro,wit'.includes(position)) {
- court = document.getElementById('client_court_'+position);
+ court = document.getElementById(`client_court_${position}`);
} else {
court = document.getElementById('client_court_classic');
}
-
+
const positions = {
def: {
bg: 'defenseempty.png',
@@ -1867,7 +1810,7 @@ class Viewport {
}
if (viewport.chatmsg.type === 5) {
- console.warn("this is a zoom");
+ console.warn('this is a zoom');
court.src = `${AO_HOST}themes/default/${encodeURI(speedLines)}`;
bench.style.opacity = 0;
} else {
@@ -1930,76 +1873,6 @@ class Viewport {
}
/**
- * Gets animation length. If the animation cannot be found, it will
- * silently fail and return 0 instead.
- * @param {string} filename the animation file name
- */
-
- async getAnimLength(url) {
- const extensions = ['.gif', '.webp'];
- for (const extension of extensions) {
- const urlWithExtension = url + extension;
- const exists = await fileExists(urlWithExtension);
- if (exists) {
- const fileBuffer = await requestBuffer(urlWithExtension);
- const length = calculatorHandler[extension](fileBuffer);
- return length;
- }
- }
- return 0;
- }
-
- oneSuccess(promises) {
- return Promise.all(promises.map((p) =>
- // If a request fails, count that as a resolution so it will keep
- // waiting for other possible successes. If a request succeeds,
- // treat it as a rejection so Promise.all immediately bails out.
- p.then(
- (val) => Promise.reject(val),
- (err) => Promise.resolve(err),
- ))).then(
- // If '.all' resolved, we've just got an array of errors.
- (errors) => Promise.reject(errors),
- // If '.all' rejected, we've got the result we wanted.
- (val) => Promise.resolve(val),
- );
- }
-
- rejectOnError(f) {
- return new Promise((resolve, reject) => f.then((res) => {
- if (res.ok) resolve(f);
- else reject(f);
- }));
- }
-
- /**
- * Adds up the frame delays to find out how long a GIF is
- * I totally didn't steal this
- * @param {data} gifFile the GIF data
- */
- calculateGifLength(gifFile) {
- const d = new Uint8Array(gifFile);
- // Thanks to http://justinsomnia.org/2006/10/gif-animation-duration-calculation/
- // And http://www.w3.org/Graphics/GIF/spec-gif89a.txt
- let duration = 0;
- for (let i = 0; i < d.length; i++) {
- // Find a Graphic Control Extension hex(21F904__ ____ __00)
- if (d[i] === 0x21
- && d[i + 1] === 0xF9
- && d[i + 2] === 0x04
- && d[i + 7] === 0x00) {
- // Swap 5th and 6th bytes to get the delay per frame
- const delay = (d[i + 5] << 8) | (d[i + 4] & 0xFF);
-
- // Should be aware browsers have a minimum frame delay
- // e.g. 6ms for IE, 2ms modern browsers (50fps)
- duration += delay < 2 ? 10 : delay;
- }
- }
- return duration * 10;
- }
-
- /**
* Updates the testimony overaly
*/
updateTestimony() {
@@ -2038,45 +1911,6 @@ class Viewport {
}
/**
- * Sets all the img tags to the right sources
- * @param {*} chatmsg
- */
- setEmote(charactername, emotename, prefix, pair, side) {
- const pairID = pair ? 'pair' : 'char';
- const characterFolder = `${AO_HOST}characters/`;
- const position = 'def,pro,wit'.includes(side) ? `${side}_` : '';
-
- const gif_s = document.getElementById(`client_${position}${pairID}_gif`);
- const png_s = document.getElementById(`client_${position}${pairID}_png`);
- const apng_s = document.getElementById(`client_${position}${pairID}_apng`);
- const webp_s = document.getElementById(`client_${position}${pairID}_webp`);
- const extensionsMap = {
- '.gif': gif_s,
- '.png': png_s,
- '.apng': apng_s,
- '.webp': webp_s,
- };
-
- for (const [extension, htmlElement] of Object.entries(extensionsMap)) {
- // Hides all sprites before creating a new sprite
- if (this.lastChar !== this.chatmsg.name) {
- htmlElement.src = transparentPNG;
- }
- let url;
- if (extension === '.png') {
- url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(emotename)}${extension}`;
- } else {
- url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(prefix)}${encodeURI(emotename)}${extension}`;
- }
- const exists = fileExistsSync(url);
- if (exists) {
- htmlElement.src = url;
- return;
- }
- }
- }
-
- /**
* Sets a new emote.
* This sets up everything before the tick() loops starts
* a lot of things can probably be moved here, like starting the shout animation if there is one
@@ -2114,7 +1948,8 @@ class Viewport {
}
this.lastEvi = this.chatmsg.evidence;
- if ('def,pro,wit'.includes(this.chatmsg.side)) {
+ const validSides = ['def', 'pro', 'wit'];
+ if (validSides.includes(this.chatmsg.side)) {
charLayers = document.getElementById(`client_${this.chatmsg.side}_char`);
pairLayers = document.getElementById(`client_${this.chatmsg.side}_pair_char`);
}
@@ -2139,10 +1974,10 @@ class Viewport {
checkCallword(this.chatmsg.content);
- this.setEmote(this.chatmsg.name.toLowerCase(), this.chatmsg.sprite, '(a)', false, this.chatmsg.side);
+ setEmote(AO_HOST, this, this.chatmsg.name.toLowerCase(), this.chatmsg.sprite, '(a)', false, this.chatmsg.side);
if (this.chatmsg.other_name) {
- this.setEmote(this.chatmsg.other_name.toLowerCase(), this.chatmsg.other_emote, '(a)', false, this.chatmsg.side);
+ setEmote(AO_HOST, this, this.chatmsg.other_name.toLowerCase(), this.chatmsg.other_emote, '(a)', false, this.chatmsg.side);
}
// gets which shout shall played
@@ -2168,23 +2003,15 @@ class Viewport {
this.chatmsg.startpreanim = true;
let gifLength = 0;
- switch (this.chatmsg.type) {
- // case 0:
- // normal emote, no preanim
- case 1:
- // play preanim
- // Hide message box
- chatContainerBox.style.opacity = 0;
- // If preanim existed then determine the length
- gifLength = await this.getAnimLength(`${AO_HOST}characters/${encodeURI(this.chatmsg.name.toLowerCase())}/${encodeURI(this.chatmsg.preanim)}`);
- this.chatmsg.startspeaking = false;
- break;
- // case 5:
- // zoom
- default:
- this.chatmsg.startspeaking = true;
- break;
+
+ if (this.chatmsg.type === 1) {
+ chatContainerBox.style.opacity = 0;
+ gifLength = await getAnimLength(`${AO_HOST}characters/${encodeURI(this.chatmsg.name.toLowerCase())}/${encodeURI(this.chatmsg.preanim)}`);
+ this.chatmsg.startspeaking = false;
+ } else {
+ this.chatmsg.startspeaking = true;
}
+
this.chatmsg.preanimdelay = parseInt(gifLength);
this.changeBackground(chatmsg.side);
@@ -2193,11 +2020,7 @@ class Viewport {
resizeChatbox();
// Flip the character
- if (this.chatmsg.flip === 1) {
- charLayers.style.transform = 'scaleX(-1)';
- } else {
- charLayers.style.transform = 'scaleX(1)';
- }
+ charLayers.style.transform = this.chatmsg.flip === 1 ? 'scaleX(-1)' : 'scaleX(1)';
// Shift by the horizontal offset
switch (this.chatmsg.side) {
@@ -2220,11 +2043,7 @@ class Viewport {
charLayers.style.top = `${Number(this.chatmsg.self_offset[1])}%`;
// flip the paired character
- if (this.chatmsg.other_flip === 1) {
- pairLayers.style.transform = 'scaleX(-1)';
- } else {
- pairLayers.style.transform = 'scaleX(1)';
- }
+ pairLayers.style.transform = this.chatmsg.other_flip === 1 ? 'scaleX(-1)' : 'scaleX(1)';
this.blipChannels.forEach((channel) => channel.src = `${AO_HOST}sounds/general/sfx-blip${encodeURI(this.chatmsg.blips.toLowerCase())}.opus`);
@@ -2238,10 +2057,18 @@ class Viewport {
// apply effects
fg.style.animation = '';
+ const badEffects = ['-', 'none'];
+ if (this.chatmsg.effects[0] && !badEffects.includes(this.chatmsg.effects[i].toLowerCase())) {
+ const baseEffectUrl = `${AO_HOST}themes/default/effects/`;
+ fg.src = `${baseEffectUrl}${encodeURI(this.chatmsg.effects[0].toLowerCase())}.webp`;
+ } else {
+ fg.src = transparentPNG;
+ }
- if (this.chatmsg.effects[0] && this.chatmsg.effects[0] !== '-' && this.chatmsg.effects[0].toLowerCase() !== 'none' ) { fg.src = `${AO_HOST}themes/default/effects/${encodeURI(this.chatmsg.effects[0].toLowerCase())}.webp`; } else { fg.src = transparentPNG; }
-
- if (this.chatmsg.sound === '0' || this.chatmsg.sound === '1' || this.chatmsg.sound === '' || this.chatmsg.sound === undefined) { this.chatmsg.sound = this.chatmsg.effects[2]; }
+ const soundChecks = ['0', '1', '', undefined];
+ if (soundChecks.some((check) => this.chatmsg.sound === check)) {
+ this.chatmsg.sound = this.chatmsg.effects[2];
+ }
this.tick();
}
@@ -2320,7 +2147,7 @@ class Viewport {
shoutSprite.style.opacity = 0;
shoutSprite.style.animation = '';
const preanim = this.chatmsg.preanim.toLowerCase();
- this.setEmote(charName, preanim, '', false, this.chatmsg.side);
+ setEmote(AO_HOST, this, charName, preanim, '', false, this.chatmsg.side);
charLayers.style.opacity = 1;
}
@@ -2371,17 +2198,17 @@ class Viewport {
}
if (this.chatmsg.other_name) {
- this.setEmote(pairName, pairEmote, '(a)', true, this.chatmsg.side);
+ setEmote(AO_HOST, this, pairName, pairEmote, '(a)', true, this.chatmsg.side);
pairLayers.style.opacity = 1;
} else {
pairLayers.style.opacity = 0;
}
- this.setEmote(charName, charEmote, '(b)', false, this.chatmsg.side);
+ setEmote(AO_HOST, this, charName, charEmote, '(b)', false, this.chatmsg.side);
charLayers.style.opacity = 1;
if (this.textnow === this.chatmsg.content) {
- this.setEmote(charName, charEmote, '(a)', false, this.chatmsg.side);
+ setEmote(AO_HOST, this, charName, charEmote, '(a)', false, this.chatmsg.side);
charLayers.style.opacity = 1;
waitingBox.style.opacity = 1;
this._animating = false;
@@ -2402,7 +2229,7 @@ class Viewport {
if (this.textnow === this.chatmsg.content) {
this._animating = false;
- this.setEmote(charName, charEmote, '(a)', false, this.chatmsg.side);
+ setEmote(AO_HOST, this, charName, charEmote, '(a)', false, this.chatmsg.side);
charLayers.style.opacity = 1;
waitingBox.style.opacity = 1;
clearTimeout(this.updater);
@@ -2629,22 +2456,6 @@ export function changeMusicVolume() {
window.changeMusicVolume = changeMusicVolume;
/**
- * Triggered by the sound effect volume slider.
- */
-export function changeSFXVolume() {
- setCookie('sfxVolume', document.getElementById('client_sfxaudio').volume);
-}
-window.changeSFXVolume = changeSFXVolume;
-
-/**
- * Triggered by the shout volume slider.
- */
-export function changeShoutVolume() {
- setCookie('shoutVolume', document.getElementById('client_shoutaudio').volume);
-}
-window.changeShoutVolume = changeShoutVolume;
-
-/**
* Triggered by the testimony volume slider.
*/
export function changeTestimonyVolume() {
@@ -2833,29 +2644,6 @@ async function requestBuffer(url) {
}
/**
- * Checks if a file exists at the specified URI.
- * @param {string} url the URI to be checked
- */
-async function fileExists(url) {
- try {
- await request(url);
- return true;
- } catch (err) {
- return false;
- }
-}
-const fileExistsSync = (url) => {
- try {
- const http = new XMLHttpRequest();
- http.open('HEAD', url, false);
- http.send();
- return http.status != 404;
- } catch (e) {
- return false;
- }
-};
-
-/**
* Triggered when the reconnect button is pushed.
*/
export function ReconnectButton() {
@@ -3316,20 +3104,6 @@ export function updateBackgroundPreview() {
window.updateBackgroundPreview = updateBackgroundPreview;
/**
- * Highlights and selects an effect for in-character chat.
- * If the same effect button is selected, then the effect is canceled.
- * @param {string} effect the new effect to be selected
- */
-export function toggleEffect(button) {
- if (button.classList.contains('dark')) {
- button.className = 'client_button';
- } else {
- button.className = 'client_button dark';
- }
-}
-window.toggleEffect = toggleEffect;
-
-/**
* Highlights and selects a menu.
* @param {string} menu the menu to be selected
*/
diff --git a/webAO/client/setEmote.js b/webAO/client/setEmote.js
new file mode 100644
index 0000000..16c95be
--- /dev/null
+++ b/webAO/client/setEmote.js
@@ -0,0 +1,43 @@
+import fileExistsSync from '../utils/fileExistsSync';
+
+/**
+ * Sets all the img tags to the right sources
+ * @param {*} chatmsg
+ */
+
+const setEmote = (AO_HOST, client, charactername, emotename, prefix, pair, side) => {
+ const pairID = pair ? 'pair' : 'char';
+ const characterFolder = `${AO_HOST}characters/`;
+ const acceptedPositions = ['def', 'pro', 'wit'];
+ const position = acceptedPositions.includes(side) ? `${side}_` : '';
+
+ const gif_s = document.getElementById(`client_${position}${pairID}_gif`);
+ const png_s = document.getElementById(`client_${position}${pairID}_png`);
+ const apng_s = document.getElementById(`client_${position}${pairID}_apng`);
+ const webp_s = document.getElementById(`client_${position}${pairID}_webp`);
+ const extensionsMap = {
+ '.gif': gif_s,
+ '.png': png_s,
+ '.apng': apng_s,
+ '.webp': webp_s,
+ };
+
+ for (const [extension, htmlElement] of Object.entries(extensionsMap)) {
+ // Hides all sprites before creating a new sprite
+ if (client.lastChar !== client.chatmsg.name) {
+ htmlElement.src = transparentPNG;
+ }
+ let url;
+ if (extension === '.png') {
+ url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(emotename)}${extension}`;
+ } else {
+ url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(prefix)}${encodeURI(emotename)}${extension}`;
+ }
+ const exists = fileExistsSync(url);
+ if (exists) {
+ htmlElement.src = url;
+ return;
+ }
+ }
+};
+export default setEmote;
diff --git a/webAO/dom/changeSFXVolume.js b/webAO/dom/changeSFXVolume.js
new file mode 100644
index 0000000..0c3009f
--- /dev/null
+++ b/webAO/dom/changeSFXVolume.js
@@ -0,0 +1,10 @@
+import setCookie from '../utils/setCookie';
+
+/**
+ * Triggered by the sound effect volume slider.
+ */
+
+export function changeSFXVolume() {
+ setCookie('sfxVolume', document.getElementById('client_sfxaudio').volume);
+}
+window.changeSFXVolume = changeSFXVolume;
diff --git a/webAO/dom/changeShoutVolume.js b/webAO/dom/changeShoutVolume.js
new file mode 100644
index 0000000..db67e2d
--- /dev/null
+++ b/webAO/dom/changeShoutVolume.js
@@ -0,0 +1,10 @@
+import setCookie from '../utils/setCookie';
+
+/**
+ * Triggered by the shout volume slider.
+ */
+
+export function changeShoutVolume() {
+ setCookie('shoutVolume', document.getElementById('client_shoutaudio').volume);
+}
+window.changeShoutVolume = changeShoutVolume;
diff --git a/webAO/dom/toggleEffect.js b/webAO/dom/toggleEffect.js
new file mode 100644
index 0000000..3d19c9c
--- /dev/null
+++ b/webAO/dom/toggleEffect.js
@@ -0,0 +1,13 @@
+/**
+ * Highlights and selects an effect for in-character chat.
+ * If the same effect button is selected, then the effect is canceled.
+ * @param {string} effect the new effect to be selected
+ */
+export function toggleEffect(button) {
+ if (button.classList.contains('dark')) {
+ button.className = 'client_button';
+ } else {
+ button.className = 'client_button dark';
+ }
+}
+window.toggleEffect = toggleEffect;
diff --git a/webAO/favicon.ico b/webAO/favicon.ico
deleted file mode 100644
index 83b380d..0000000
--- a/webAO/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/webAO/images/desc.png b/webAO/images/desc.png
deleted file mode 100644
index 4d8ec37..0000000
--- a/webAO/images/desc.png
+++ /dev/null
Binary files differ
diff --git a/webAO/index.html b/webAO/index.html
deleted file mode 100644
index c82cacf..0000000
--- a/webAO/index.html
+++ /dev/null
@@ -1,126 +0,0 @@
-<html>
-<head>
- <title>Attorney Online ONLINE</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
- <meta name="description" content="An off-the-cuff courtroom drama simulator">
- <meta name="author" content="stonedDiscord">
-
- <meta property="og:title" content="Attorney Online">
- <meta property="og:description" content="Attorney Online is an online version of the world-renowned courtroom drama simulator that allows you to create and play out cases in an off-the-cuff format.">
- <meta property="og:type" content="website">
- <meta property="og:url" content="http://web.aceattorneyonline.com/">
- <meta property="og:image" content="https://repository-images.githubusercontent.com/78860508/89fcba80-aafd-11e9-80db-c5b10c01aba9">
-
- <meta property="twitter:card" content="summary_large_image">
- <meta property="twitter:title" content="Attorney Online">
- <meta property="twitter:description" content="Attorney Online is an online version of the world-renowned courtroom drama simulator that allows you to create and play out cases in an off-the-cuff format.">
- <meta property="twitter:url" content="http://web.aceattorneyonline.com/">
- <meta property="twitter:image" content="https://repository-images.githubusercontent.com/78860508/89fcba80-aafd-11e9-80db-c5b10c01aba9">
-
- <meta http-equiv="Content-Security-Policy"
- content="default-src 'self' 'unsafe-inline' 'unsafe-eval' *.aceattorneyonline.com data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ws:;">
- <meta http-equiv="X-Content-Security-Policy"
- content="default-src 'self' 'unsafe-inline' 'unsafe-eval' *.aceattorneyonline.com data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ws:;">
- <meta http-equiv="X-WebKit-CSP"
- content="default-src 'self' 'unsafe-inline' 'unsafe-eval' *.aceattorneyonline.com data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ws:;">
-
- <meta name="viewport" content="width=700, initial-scale=1">
- <meta name="theme-color" content="#2c3d51">
-
- <link href="https://fonts.googleapis.com/css?family=Poiret+One" rel="stylesheet">
- <link href="https://fonts.googleapis.com/css?family=Oswald%7CRoboto+Condensed" rel="stylesheet">
- <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
- <link rel="apple-touch-icon" href="/images/logo-new.png"/>
- <link rel="stylesheet" type="text/css" href="styles/master.css">
- <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
- <link rel="icon" href="images/favicon.ico">
- <script src="master.b.js"></script>
- <link rel="manifest" href="manifest.json">
- <script>
- if ('serviceWorker' in navigator) {
- window.addEventListener('load', () => {
- navigator.serviceWorker.register('../sw.js').then( () => {
- console.log('Service Worker Registered')
- })
- })
-}
- </script>
-
-
-</head>
-
-<body>
- <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark fixed-top">
- <div class="container">
- <div class="collapse navbar-collapse" id="navbarResponsive">
- <ul class="navbar-nav ml-auto">
- <li class="nav-item">
- <a class="nav-link" href="https://aceattorneyonline.com/#">Home</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="https://aceattorneyonline.com/#about">About</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="https://aceattorneyonline.com/#download">Download</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="https://aceattorneyonline.com/#faq">FAQ</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="https://aceattorneyonline.com/help.html">Help</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" target="_blank" href="https://discord.gg/wWvQ3pw">Discord</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="https://aceattorneyonline.com/blog">Blog</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="http://web.aceattorneyonline.com/">Play Online</a>
- </li>
- </ul>
- </div>
- </div>
- </nav>
- <div id="serverlist_container">
- <h2>Server List</h2>
- <div id="https_error" class="error" style="display: none;">
- <p>https is not supported</p>
- <p>Please <a href="http://web.aceattorneyonline.com/">click here</a> to be redirected to the http version</p>
- </div>
- <div id="ms_error" class="error" style="display: none;">
- <p>Could not connect to the master server.</p>
- <p>Please check your firewall.</p>
- <p id="ms_error_code">A network error occurred</p>
- </div>
- <noscript>
- <div id="js_error" class="error">
- <p>webAO requires JavaScript to work</p>
- </div>
- </noscript>
- <ul class="serverlist" id="masterlist">
- <li id="server-2" class="available" onmouseover="setServ(-2)">
- <p>Singleplayer (beta)</p>
- <a class="button" href="client.html?mode=replay">Try</a>
- </li>
- <li id="server-1" class="unavailable" onmouseover="setServ(-1)">
- <p>Localhost</p>
- <a class="button" href="client.html?mode=watch&ip=127.0.0.1:50001">Watch</a>
- <a class="button" href="client.html?mode=join&ip=127.0.0.1:50001">Join</a>
- </li>
- </ul>
- <div id="info_container">
- <p id="serverinfo">Masterserver version - ...</p>
- <p id="clientinfo">Client version - ...</p>
- <textarea id="masterchat" readonly></textarea>
- <p>Having trouble? <a href="https://discord.gg/9rYQVVQ">Join us on Discord</a></p>
- </div>
- </div>
- <div id="serverdescription_container">
- <p id="serverdescription_content">
- </p>
- </div>
-</body>
-
-</html> \ No newline at end of file
diff --git a/webAO/logo-new-512.png b/webAO/logo-new-512.png
deleted file mode 100644
index 33fd586..0000000
--- a/webAO/logo-new-512.png
+++ /dev/null
Binary files differ
diff --git a/webAO/logo-new.png b/webAO/logo-new.png
deleted file mode 100644
index f53fe30..0000000
--- a/webAO/logo-new.png
+++ /dev/null
Binary files differ
diff --git a/webAO/manifest.json b/webAO/manifest.json
deleted file mode 100644
index acda5d5..0000000
--- a/webAO/manifest.json
+++ /dev/null
@@ -1,23 +0,0 @@
- {
- "name": "Ace Attorney Online",
- "short_name": "WebAO",
- "start_url": "index.html",
- "scope": "./",
- "icons": [
- {
- "src": "/logo-new.png",
- "sizes": "128x128",
- "type": "image/png"
- },
- {
- "src": "/logo-new-512.png",
- "sizes": "512x512",
- "type": "image/png",
- "purpose": "any maskable"
-
- }
- ],
- "theme_color": "#2c3d51",
- "background_color": "#2c3d51",
- "display": "standalone"
- } \ No newline at end of file
diff --git a/webAO/master.js b/webAO/master.js
index 7e4906e..df79d85 100644
--- a/webAO/master.js
+++ b/webAO/master.js
@@ -58,7 +58,6 @@ export function setServ(ID) {
window.setServ = setServ;
function onOpen(_e) {
- console.log(`Your emulated HDID is ${hdid}`);
masterserver.send('ID#webAO#webAO#%');
masterserver.send('ALL#%');
@@ -120,7 +119,6 @@ function checkOnline(serverID, coIP) {
};
oserv.onerror = function (_evt) {
- console.warn(`${coIP} threw an error.`);
document.getElementById(`server${serverID}`).className = 'unavailable';
};
}
diff --git a/webAO/packets/ms.js b/webAO/packets/ms.js
new file mode 100644
index 0000000..26851bd
--- /dev/null
+++ b/webAO/packets/ms.js
@@ -0,0 +1,28 @@
+export default {
+ deskmod,
+ preanim,
+ name,
+ emote,
+ message,
+ side,
+ sfx_name,
+ emote_modifier,
+ sfx_delay,
+ objection_modifier,
+ evidence,
+ flip,
+ realization,
+ text_color,
+ showname,
+ other_charid,
+ self_hoffset,
+ self_yoffset,
+ noninterrupting_preanim,
+ looping_sfx,
+ screenshake,
+ frame_screenshake,
+ frame_realization,
+ frame_sfx,
+ additive,
+ effect,
+};
diff --git a/webAO/styles/master.css b/webAO/styles/master.css
index 5d957a0..1ea4796 100644
--- a/webAO/styles/master.css
+++ b/webAO/styles/master.css
@@ -53,7 +53,7 @@
width: 185px;
height: 276px;
padding: 0px;
- background: url("../images/desc.png") no-repeat;
+ background: url("../desc.png") no-repeat;
border: 2px solid #888;
border-radius: 5px;
}
diff --git a/webAO/sw.js b/webAO/sw.js
deleted file mode 100644
index 09a2251..0000000
--- a/webAO/sw.js
+++ /dev/null
@@ -1,26 +0,0 @@
-const cacheName = 'webAO';
-
-// Cache all the files to make a PWA
-self.addEventListener('install', (e) => {
- e.waitUntil(
- caches.open(cacheName).then((cache) =>
- // Our application only has two files here index.html and manifest.json
- // but you can add more such as style.css as your app grows
- cache.addAll([
- './',
- './index.html',
- '../manifest.json',
- ])),
- );
-});
-
-// Our service worker will intercept all fetch requests
-// and check if we have cached the file
-// if so it will serve the cached file
-self.addEventListener('fetch', (event) => {
- event.respondWith(
- caches.open(cacheName)
- .then((cache) => cache.match(event.request, { ignoreSearch: true }))
- .then((response) => response || fetch(event.request)),
- );
-});
diff --git a/webAO/utils/fileExists.js b/webAO/utils/fileExists.js
new file mode 100644
index 0000000..261acda
--- /dev/null
+++ b/webAO/utils/fileExists.js
@@ -0,0 +1,17 @@
+const fileExists = async (url) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ xhr.onload = function (e) {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200) {
+ return true;
+ }
+ return false;
+ }
+ };
+ xhr.onerror = function (e) {
+ return false;
+ };
+ xhr.send(null);
+};
+export default fileExists;
diff --git a/webAO/utils/fileExistsSync.js b/webAO/utils/fileExistsSync.js
new file mode 100644
index 0000000..1d7fde2
--- /dev/null
+++ b/webAO/utils/fileExistsSync.js
@@ -0,0 +1,11 @@
+const fileExistsSync = (url) => {
+ try {
+ const http = new XMLHttpRequest();
+ http.open('HEAD', url, false);
+ http.send();
+ return http.status != 404;
+ } catch (e) {
+ return false;
+ }
+};
+export default fileExistsSync;
diff --git a/webAO/utils/getAnimLength.js b/webAO/utils/getAnimLength.js
new file mode 100644
index 0000000..e64703f
--- /dev/null
+++ b/webAO/utils/getAnimLength.js
@@ -0,0 +1,22 @@
+import calculatorHandler from './calculatorHandler';
+import fileExists from './fileExists.js';
+/**
+ * Gets animation length. If the animation cannot be found, it will
+ * silently fail and return 0 instead.
+ * @param {string} filename the animation file name
+ */
+
+const getAnimLength = async (url) => {
+ const extensions = ['.gif', '.webp'];
+ for (const extension of extensions) {
+ const urlWithExtension = url + extension;
+ const exists = await fileExists(urlWithExtension);
+ if (exists) {
+ const fileBuffer = await requestBuffer(urlWithExtension);
+ const length = calculatorHandler[extension](fileBuffer);
+ return length;
+ }
+ }
+ return 0;
+};
+export default getAnimLength;
diff --git a/webAO/utils/getResources.js b/webAO/utils/getResources.js
new file mode 100644
index 0000000..a0c513e
--- /dev/null
+++ b/webAO/utils/getResources.js
@@ -0,0 +1,39 @@
+const getResources = (AO_HOST, THEME) => ({
+ holdit: {
+ src: `${AO_HOST}misc/default/holdit_bubble.png`,
+ duration: 720,
+ },
+ objection: {
+ src: `${AO_HOST}misc/default/objection_bubble.png`,
+ duration: 720,
+ },
+ takethat: {
+ src: `${AO_HOST}misc/default/takethat_bubble.png`,
+ duration: 840,
+ },
+ custom: {
+ src: '',
+ duration: 840,
+ },
+ witnesstestimony: {
+ src: `${AO_HOST}themes/${THEME}/witnesstestimony.gif`,
+ duration: 1560,
+ sfx: `${AO_HOST}sounds/general/sfx-testimony.opus`,
+ },
+ crossexamination: {
+ src: `${AO_HOST}themes/${THEME}/crossexamination.gif`,
+ duration: 1600,
+ sfx: `${AO_HOST}sounds/general/sfx-testimony2.opus`,
+ },
+ guilty: {
+ src: `${AO_HOST}themes/${THEME}/guilty.gif`,
+ duration: 2870,
+ sfx: `${AO_HOST}sounds/general/sfx-guilty.opus`,
+ },
+ notguilty: {
+ src: `${AO_HOST}themes/${THEME}/notguilty.gif`,
+ duration: 2440,
+ sfx: `${AO_HOST}sounds/general/sfx-notguilty.opus`,
+ },
+});
+export default getResources;
diff --git a/webAO/utils/queryParser.js b/webAO/utils/queryParser.js
new file mode 100644
index 0000000..1c2b83a
--- /dev/null
+++ b/webAO/utils/queryParser.js
@@ -0,0 +1,9 @@
+// Get the arguments from the URL bar
+const queryParser = () => {
+ const queryDict = {};
+ location.search.substr(1).split('&').forEach((item) => {
+ queryDict[item.split('=')[0]] = item.split('=')[1];
+ });
+ return queryDict;
+};
+export default queryParser;