Include tools for testing various parts of the appfeature/data-structs
@@ -3,7 +3,7 @@ | |||
| Combat knife | Combat knife | - | | - | - | | |||
| Oriental sword | Katana | - | | - | - | | |||
| Twin pistols | Beretta M9 | 9x19mm Parabellum | 15 | - | 381 | | |||
| Shotgun | Ithaca Model 37 | 12 gauge 2.75" shells | 8 | - | - | | |||
| Shotgun | Ithaca Model 37 | 12 gauge 2.75" shells | 8 | - | 410 | | |||
| Sub-machine gun | H&K MP5A3 | 9x19mm Parabellum | 30 | 800 (cyclic) | 400 | | |||
| Sub-machine gun | H&K MP5K | 9x19mm Parabellum | 30 | 900 (cyclic) | 375 | | |||
| Sub-machine gun | H&K MP7 | HK 4.6x30mm | 30 | 950 (cyclic) | 735 | | |||
@@ -0,0 +1,60 @@ | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Audio Interval Test</title> | |||
<style> | |||
label { | |||
position: relative; | |||
} | |||
label > span { | |||
position: absolute; | |||
left: -999999px; | |||
} | |||
label + input[type="checkbox"] { | |||
position: absolute; | |||
left: -999999px; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<form id="formAudio"> | |||
<fieldset> | |||
<legend> | |||
Repeating Sounds | |||
</legend> | |||
<div> | |||
<label> | |||
<span>Interval (bpm)</span> | |||
<input | |||
type="number" | |||
name="interval" | |||
value="750" | |||
min="100" | |||
max="1000" | |||
/> | |||
</label> | |||
<input | |||
type="checkbox" | |||
name="play" | |||
readonly | |||
tabindex="-1" | |||
checked | |||
/> | |||
</div> | |||
<div> | |||
<label> | |||
<span>Interval (ms)</span> | |||
<output>-</output> | |||
</label> | |||
</div> | |||
<button type="submit"> | |||
Toggle Sound | |||
</button> | |||
</fieldset> | |||
</form> | |||
<script src="index.js"></script> | |||
</body> | |||
</html> |
@@ -0,0 +1,57 @@ | |||
function AudioForm(el) { | |||
const interval = el.elements['interval'] | |||
const play = el.elements['play'] | |||
const output = el.querySelector('output') | |||
const audioPool = [] | |||
let intervalHandle = null | |||
let audioIndex = 0 | |||
const items = 50 | |||
for (let i = 0; i < items; i += 1) { | |||
const newAudio = new Audio('./sfx/weapons/guns/smg-hkmp7/1_unload_var0.wav') | |||
newAudio.volume = 0.2; | |||
newAudio.addEventListener('canplaythrough', () => { | |||
audioPool[i].ready = true | |||
}) | |||
newAudio.addEventListener('ended', () => { | |||
newAudio.pause() | |||
newAudio.currentTime = 0 | |||
}) | |||
audioPool[i] = { | |||
audio: newAudio, | |||
ended: true, | |||
ready: false, | |||
} | |||
newAudio.load() | |||
} | |||
const playSound = () => { | |||
for (let i = 0; i < items; i += 1) { | |||
if (!audioPool[i].ready) { | |||
return; | |||
} | |||
} | |||
audioPool[audioIndex].audio.play(); | |||
audioIndex = (audioIndex + 1) % items; | |||
} | |||
el.addEventListener('submit', (e) => { | |||
e.preventDefault() | |||
const ms = 60000 / Number(interval.value); | |||
output.innerText = `${ms} ms` | |||
if (play.checked) { | |||
intervalHandle = window.setInterval(() => { | |||
playSound() | |||
}, ms) | |||
} else { | |||
window.clearInterval(intervalHandle); | |||
} | |||
play.checked = !play.checked | |||
}) | |||
} | |||
const audioForm = new AudioForm(window.document.getElementById('formAudio')) |
@@ -0,0 +1 @@ | |||
../../assets/sfx |
@@ -0,0 +1,180 @@ | |||
<!DOCTYPE HTML> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<title>Websocket Client</title> | |||
<style> | |||
form { | |||
margin: 2em 0; | |||
} | |||
fieldset { | |||
display: contents; | |||
} | |||
legend { | |||
position: absolute; | |||
left: -999999px; | |||
} | |||
legend ~ div { | |||
margin: 0.5em 0; | |||
} | |||
legend ~ div > div { | |||
display: flex; | |||
gap: 0.5em; | |||
} | |||
legend ~ div > div :first-child { | |||
flex: auto; | |||
} | |||
legend ~ div > div :first-child > label { | |||
display: block; | |||
width: 100%; | |||
} | |||
legend ~ div > div :first-child > label > input { | |||
display: block; | |||
width: 100%; | |||
box-sizing: border-box; | |||
} | |||
label { | |||
position: relative; | |||
} | |||
label > span { | |||
position: absolute; | |||
left: -999999px; | |||
} | |||
label + input[type="checkbox"] { | |||
position: absolute; | |||
left: -999999px; | |||
} | |||
textarea { | |||
width: 100%; | |||
box-sizing: border-box; | |||
display: block; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<h1>Websocket Client</h1> | |||
<form | |||
id="formConnection" | |||
> | |||
<fieldset> | |||
<legend>Server</legend> | |||
<div> | |||
<div> | |||
<div> | |||
<label> | |||
<span>URL</span> | |||
<input | |||
type="url" | |||
name="serverUrl" | |||
autocomplete="off" | |||
value="ws://localhost:42069" | |||
required | |||
> | |||
</label> | |||
</div> | |||
</div> | |||
</div> | |||
<div> | |||
<div> | |||
<div> | |||
<label> | |||
<span>Username</span> | |||
<input | |||
type="text" | |||
name="username" | |||
autocomplete="off" | |||
value="Web" | |||
required | |||
> | |||
</label> | |||
</div> | |||
</div> | |||
</div> | |||
<button | |||
type="submit" | |||
name="connect" | |||
> | |||
Connect | |||
</button> | |||
</fieldset> | |||
</form> | |||
<form | |||
id="formMessage" | |||
> | |||
<fieldset | |||
name="fieldset" | |||
disabled | |||
> | |||
<legend>Message</legend> | |||
<div> | |||
<div> | |||
<div> | |||
<label> | |||
<span>Message</span> | |||
<input | |||
type="text" | |||
value="" | |||
autocomplete="off" | |||
name="message" | |||
placeholder="Type your message" | |||
> | |||
</label> | |||
<input | |||
type="checkbox" | |||
name="binary" | |||
readonly | |||
tabindex="-1" | |||
/> | |||
</div> | |||
<button | |||
type="submit" | |||
> | |||
Send as Text (Enter) | |||
</button> | |||
<button | |||
name="submitBinary" | |||
type="button" | |||
> | |||
Send as Binary (Shift + Enter) | |||
</button> | |||
</div> | |||
</div> | |||
</fieldset> | |||
</form> | |||
<form | |||
id="formLogs" | |||
> | |||
<fieldset> | |||
<legend>Logs</legend> | |||
<div> | |||
<label> | |||
<span>Logs</span> | |||
<textarea | |||
rows="10" | |||
cols="50" | |||
name="logs" | |||
readonly | |||
></textarea> | |||
</label> | |||
</div> | |||
<button | |||
type="submit" | |||
> | |||
Clear Logs | |||
</button> | |||
</fieldset> | |||
</form> | |||
<script src="index.js"></script> | |||
</body> | |||
</html> |
@@ -0,0 +1,149 @@ | |||
const stringifyData = async value => { | |||
const isString = typeof value === 'string' | |||
const resolve = () => ( | |||
isString | |||
? Promise.resolve(value) | |||
: value.arrayBuffer() | |||
) | |||
const a = await resolve() | |||
if (isString) { | |||
return `String:${a.length}(${a})` | |||
} | |||
const bytes = Array.from(new Uint8Array(a)).map(c => Number(c).toString(16).padStart(2, '0')).join(' ') | |||
return `Binary:${a.byteLength}(${bytes})` | |||
} | |||
function ConnectionForm(el) { | |||
let ws = null | |||
el.addEventListener('submit', e => { | |||
e.preventDefault() | |||
if (!ws) { | |||
const username = el.elements['username'].value | |||
const baseUrl = el.elements['serverUrl'].value | |||
ws = new WebSocket(`${baseUrl}?username=${username}`) | |||
ws.addEventListener('open', () => { | |||
this.messageForm.enable() | |||
this.logsForm.append('Connection opened') | |||
el.elements['serverUrl'].setAttribute('disabled', 'disabled') | |||
el.elements['username'].setAttribute('disabled', 'disabled') | |||
el.elements['connect'].innerText = 'Disconnect' | |||
}) | |||
ws.addEventListener('message', evt => { | |||
stringifyData(evt.data).then(value => { | |||
this.logsForm.append(`Recv: ${value}`) | |||
}) | |||
}) | |||
ws.addEventListener('error', evt => { | |||
console.error(evt) | |||
}) | |||
ws.addEventListener('close', event => { | |||
this.messageForm.reset() | |||
this.messageForm.disable() | |||
this.logsForm.append(`Connection closed: wasClean: ${event.wasClean}, evCode: ${event.code}`) | |||
el.elements['serverUrl'].removeAttribute('disabled') | |||
el.elements['username'].removeAttribute('disabled') | |||
el.elements['connect'].innerText = 'Connect' | |||
ws = null | |||
}) | |||
return | |||
} | |||
ws.close() | |||
}) | |||
this.send = value => { | |||
if (!ws) { | |||
return | |||
} | |||
stringifyData(value).then(v => { | |||
this.logsForm.append(`Send: ${v}`) | |||
ws.send(value) | |||
}) | |||
} | |||
} | |||
function MessageForm(el) { | |||
const inputMessage = el.elements['message'] | |||
const binary = el.elements['binary'] | |||
inputMessage.addEventListener('keydown', e => { | |||
if (e.key !== 'Shift') { | |||
return; | |||
} | |||
binary.checked = true; | |||
}); | |||
inputMessage.addEventListener('keyup', e => { | |||
if (e.key !== 'Shift') { | |||
return; | |||
} | |||
binary.checked = false; | |||
}); | |||
el.addEventListener('submit', e => { | |||
e.preventDefault() | |||
if (inputMessage.value.length < 1) { | |||
return | |||
} | |||
this.connectionForm.send( | |||
binary.checked | |||
? new Blob(inputMessage.value.split('')) | |||
: inputMessage.value, | |||
); | |||
inputMessage.value = '' | |||
inputMessage.focus() | |||
}) | |||
el.elements['submitBinary'].addEventListener('click', e => { | |||
e.preventDefault() | |||
if (inputMessage.value.length < 1) { | |||
return | |||
} | |||
this.connectionForm.send(new Blob(inputMessage.value.split(''))) | |||
inputMessage.value = '' | |||
inputMessage.focus() | |||
}) | |||
this.reset = () => { | |||
inputMessage.value = '' | |||
} | |||
this.enable = () => { | |||
el.elements['fieldset'].removeAttribute('disabled') | |||
} | |||
this.disable = () => { | |||
el.elements['fieldset'].setAttribute('disabled', 'disabled') | |||
} | |||
} | |||
function LogsForm(el) { | |||
let logs = el.elements['logs'] | |||
el.addEventListener('submit', e => { | |||
e.preventDefault() | |||
logs.value = '' | |||
}) | |||
this.append = data => { | |||
logs.value += data + '\n' | |||
logs.scrollTop = logs.scrollHeight | |||
} | |||
} | |||
const logsForm = new LogsForm(window.document.getElementById('formLogs')) | |||
const messageForm = new MessageForm(window.document.getElementById('formMessage')) | |||
const connectionForm = new ConnectionForm(window.document.getElementById('formConnection')) | |||
connectionForm.logsForm = logsForm | |||
connectionForm.messageForm = messageForm | |||
messageForm.connectionForm = connectionForm |