diff --git a/assets/sfx/weapons/guns/smg-hkmp5a3/1_unload.wav b/assets/sfx/weapons/guns/smg-hkmp5a3/1_unload_var0.wav similarity index 100% rename from assets/sfx/weapons/guns/smg-hkmp5a3/1_unload.wav rename to assets/sfx/weapons/guns/smg-hkmp5a3/1_unload_var0.wav diff --git a/assets/sfx/weapons/guns/smg-hkmp5k/1_unload.wav b/assets/sfx/weapons/guns/smg-hkmp5k/1_unload_var0.wav similarity index 100% rename from assets/sfx/weapons/guns/smg-hkmp5k/1_unload.wav rename to assets/sfx/weapons/guns/smg-hkmp5k/1_unload_var0.wav diff --git a/docs/weapons.md b/docs/weapons.md index 80a772b..5850d14 100644 --- a/docs/weapons.md +++ b/docs/weapons.md @@ -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 | diff --git a/tools/audio-interval-test/index.html b/tools/audio-interval-test/index.html new file mode 100644 index 0000000..149e2ab --- /dev/null +++ b/tools/audio-interval-test/index.html @@ -0,0 +1,60 @@ + + + + + Audio Interval Test + + + +
+
+ + Repeating Sounds + +
+ + +
+
+ +
+ +
+
+ + + diff --git a/tools/audio-interval-test/index.js b/tools/audio-interval-test/index.js new file mode 100644 index 0000000..854fecf --- /dev/null +++ b/tools/audio-interval-test/index.js @@ -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')) diff --git a/tools/audio-interval-test/sfx b/tools/audio-interval-test/sfx new file mode 120000 index 0000000..43a23da --- /dev/null +++ b/tools/audio-interval-test/sfx @@ -0,0 +1 @@ +../../assets/sfx \ No newline at end of file diff --git a/tools/websocket-client/index.html b/tools/websocket-client/index.html new file mode 100644 index 0000000..5ff6c9f --- /dev/null +++ b/tools/websocket-client/index.html @@ -0,0 +1,180 @@ + + + + + Websocket Client + + + +

Websocket Client

+
+
+ Server +
+
+
+ +
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+ Message +
+
+
+ + +
+ + +
+
+
+
+
+
+ Logs +
+ +
+ +
+
+ + + diff --git a/tools/websocket-client/index.js b/tools/websocket-client/index.js new file mode 100644 index 0000000..224c7a4 --- /dev/null +++ b/tools/websocket-client/index.js @@ -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