diff --git a/counters/Mania by knvi/css/style.css b/counters/Mania by knvi/css/style.css new file mode 100644 index 0000000..3e69d67 --- /dev/null +++ b/counters/Mania by knvi/css/style.css @@ -0,0 +1,79 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Arial', 'Helvetica', sans-serif; + background: transparent; + overflow: hidden; +} + +.hit-container { + display: inline-block; + background: rgba(20, 20, 30, 0.85); + padding: 9px 15px; + border-radius: 3px; + min-width: 150px; + visibility: hidden; + opacity: 0; + transition: opacity 0.3s ease, visibility 0.3s ease; +} + +.hit-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 3px 0; + gap: 60px; + line-height: 1.6; + transition: all 0.1s ease; +} + +.label { + font-weight: bold; + font-size: 18px; + letter-spacing: 0px; + text-shadow: 0px 0px 4px rgba(0, 0, 0, 1); + min-width: 33px; + text-align: left; +} + +.value { + font-weight: bold; + font-size: 18px; + text-shadow: 0px 0px 4px rgba(0, 0, 0, 1); + text-align: right; + min-width: 45px; +} + +.perfect .label, +.perfect .value { + color: #00FFFF; +} + +.great .label, +.great .value { + color: #FFFF00; +} + +.good .label, +.good .value { + color: #00FF00; +} + +.ok .label, +.ok .value { + color: #00BFFF; +} + +.meh .label, +.meh .value { + color: #FF00FF; +} + +.miss .label, +.miss .value { + color: #FF0000; +} diff --git a/counters/Mania by knvi/index.html b/counters/Mania by knvi/index.html new file mode 100644 index 0000000..fc2e55f --- /dev/null +++ b/counters/Mania by knvi/index.html @@ -0,0 +1,40 @@ + + + + + + Mania Counter + + + +
+
+ MA + 0 +
+
+ PR + 0 +
+
+ GR + 0 +
+
+ GD + 0 +
+
+ BD + 0 +
+
+ MS + 0 +
+
+ + + + + diff --git a/counters/Mania by knvi/js/script.js b/counters/Mania by knvi/js/script.js new file mode 100644 index 0000000..5ed8be6 --- /dev/null +++ b/counters/Mania by knvi/js/script.js @@ -0,0 +1,167 @@ +let socket; +let reconnectInterval; + +const GameState = { + menu: 0, + edit: 1, + play: 2, + exit: 3, + selectEdit: 4, + selectPlay: 5, + selectDrawings: 6, + resultScreen: 7, + update: 8, + busy: 9, + unknown: 10, + lobby: 11, + matchSetup: 12, + selectMulti: 13 +}; + +const elements = { + perfect: document.getElementById('perfect'), + great: document.getElementById('great'), + good: document.getElementById('good'), + ok: document.getElementById('ok'), + meh: document.getElementById('meh'), + miss: document.getElementById('miss') +}; + +const container = document.querySelector('.hit-container'); + +let previousHits = { + perfect: 0, + great: 0, + good: 0, + ok: 0, + meh: 0, + miss: 0 +}; + +let isInGameplay = false; + +function connectWebSocket() { + socket = new WebSocket('ws://127.0.0.1:24050/websocket/v2'); + + socket.onopen = () => { + console.log('connected'); + if (reconnectInterval) { + clearInterval(reconnectInterval); + reconnectInterval = null; + } + }; + + socket.onmessage = (event) => { + try { + const data = JSON.parse(event.data); + updateHitCounter(data); + } catch (error) { + console.error('error parsing data:', error); + } + }; + + socket.onerror = (error) => { + console.error('ws error:', error); + }; + + socket.onclose = () => { + console.log('disconnected'); + hideCounter(); + if (!reconnectInterval) { + reconnectInterval = setInterval(() => { + connectWebSocket(); + }, 3000); + } + }; +} + +function updateHitCounter(data) { + const statusNumber = data.state?.number; + + const isPlaying = statusNumber === GameState.play; + + if (!isPlaying || !data.play || !data.play.hits) { + if (isInGameplay) { + hideCounter(); + resetCounter(); + isInGameplay = false; + } + return; + } + + if (!isInGameplay) { + showCounter(); + isInGameplay = true; + } + + const hits = data.play.hits; + + const currentHits = { + perfect: Number(hits.geki) || 0, + great: Number(hits['300']) || 0, + good: Number(hits.katu) || 0, + ok: Number(hits['100']) || 0, + meh: Number(hits['50']) || 0, + miss: Number(hits['0']) || 0 + }; + + console.log('Parsed hits:', currentHits); + + updateValue('perfect', currentHits.perfect); + updateValue('great', currentHits.great); + updateValue('good', currentHits.good); + updateValue('ok', currentHits.ok); + updateValue('meh', currentHits.meh); + updateValue('miss', currentHits.miss); + + previousHits = currentHits; +} + +function updateValue(key, value) { + const element = elements[key]; + if (!element) { + console.warn(`Element not found for key: ${key}`); + return; + } + + const oldValue = parseInt(element.textContent) || 0; + + element.textContent = value.toString(); +} + +function showCounter() { + container.style.opacity = '1'; + container.style.visibility = 'visible'; +} + +function hideCounter() { + container.style.opacity = '0'; + container.style.visibility = 'hidden'; +} + +function resetCounter() { + Object.keys(elements).forEach(key => { + if (elements[key]) { + elements[key].textContent = '0'; + } + }); + + previousHits = { + perfect: 0, + great: 0, + good: 0, + ok: 0, + meh: 0, + miss: 0 + }; +} + +connectWebSocket(); + +hideCounter(); + +document.addEventListener('visibilitychange', () => { + if (!document.hidden && socket.readyState !== WebSocket.OPEN) { + connectWebSocket(); + } +}); diff --git a/counters/Mania by knvi/metadata.txt b/counters/Mania by knvi/metadata.txt new file mode 100644 index 0000000..174659b --- /dev/null +++ b/counters/Mania by knvi/metadata.txt @@ -0,0 +1,8 @@ +Usecase: in-game, obs-overlay +Name: Mania +Version: 1.0.0 +Author: knvi +CompatibleWith: tosu +Resolution: 169x224 +authorLinks: https://github.com/knvi +Notes: just a simple mania judgement counter (inspired by etterna)