您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
e-typingに対戦機能を追加したい
当前为
// ==UserScript== // @name e-対戦 [e-typing] // @namespace http://tampermonkey.net/ // @version 3.0 // @description e-typingに対戦機能を追加したい // @author Toshi // @match https://www.e-typing.ne.jp/app/jsa_std* // @exclude https://www.e-typing.ne.jp/app/ad* // @icon https://www.google.com/s2/favicons?sz=64&domain=e-typing.ne.jp // @license MIT // @require https://www.gstatic.com/firebasejs/7.2.1/firebase-app.js // @require https://www.gstatic.com/firebasejs/7.2.1/firebase-auth.js // @require https://www.gstatic.com/firebasejs/7.2.1/firebase-database.js // @grant unsafeWindow // ==/UserScript== const firebaseConfig = { //テスト用データベース /* apiKey: "AIzaSyARzYljiRuZCoABA32_wnuyMkjScpcZUN8", databaseURL: "https://e-typing-battle-fb330-default-rtdb.firebaseio.com/" */ //本番用データベース apiKey: "AIzaSyDsHiPII5dgN_AEGwOtMehyveucoF4Twvs", databaseURL: "https://e-typing-battle-default-rtdb.firebaseio.com" }; class MyResult{ constructor(){ this.battlePlayerResultData = {} this.etypingPlus = ['score','level','time','typeCount','missCount','wpm','correct','latency','rkpm'] this.normal = ['score','level','time','typeCount','missCount','wpm','correct','weakKey'] } sendResult(){ playerStateChange.update('result') const RESULT_DATA = document.getElementsByClassName("result_data")[0].firstElementChild.children const RESULT_ITEM = RESULT_DATA.length == 9 ? this.etypingPlus : this.normal let updates = {} let sendData = {} for(let i=0; i<RESULT_ITEM.length; i++){ sendData[RESULT_ITEM[i]] = RESULT_DATA[i].lastElementChild.textContent } updates['/users/' + myID + '/result/'] = sendData firebase.database().ref().update(updates) this.createBattlePlayerResultArea() //相手のリザルトを取得していた場合、相手のリザルトを表示 const BATTLE_PLAYPER_RESULT = Object.keys(this.battlePlayerResultData) if(BATTLE_PLAYPER_RESULT.length){ const RESULT_DATA = document.getElementsByClassName("result_data")[1].firstElementChild.children const RESULT_ITEM = RESULT_DATA.length == 9 ? myResult.etypingPlus : myResult.normal for(let i=0;i<BATTLE_PLAYPER_RESULT.length;i++){ const INDEX = RESULT_ITEM.indexOf(BATTLE_PLAYPER_RESULT[i]) if(INDEX >= 0){ RESULT_DATA[INDEX].lastElementChild.textContent = this.battlePlayerResultData[BATTLE_PLAYPER_RESULT[i]] } } document.getElementById("prev").firstElementChild.textContent = battleUserData.data.name } } createBattlePlayerResultArea(){ const RESULT_DATA = document.getElementsByClassName("result_data")[1].firstElementChild.children document.getElementById("prev").firstElementChild.textContent = '待機中' for(let i=0; i<RESULT_DATA.length; i++){ RESULT_DATA[i].lastElementChild.textContent = '' RESULT_DATA[i].lastElementChild.style.color = '#23c21f' } if(document.getElementsByClassName('RTCLine')[0].textContent == 'タイムアウトしました。'){ battleUserData.resultTimeoutPlayer() } } onBattleResultDisplay(snapshot){ const uid = snapshot.ref_.path.pieces_[1]; const Info = snapshot.ref_.path.pieces_[3] const SnapShotValue = snapshot.val() myResult.battlePlayerResultData[Info] = SnapShotValue if(playerStateChange.prevState == 'result'){ const RESULT_DATA = document.getElementsByClassName("result_data")[1].firstElementChild.children const RESULT_ITEM = RESULT_DATA.length == 9 ? myResult.etypingPlus : myResult.normal const INDEX = RESULT_ITEM.indexOf(Info) if(INDEX >= 0){ RESULT_DATA[INDEX].lastElementChild.textContent = SnapShotValue } document.getElementById("prev").firstElementChild.textContent = battleUserData.data.name } firebase.database().ref('usersState/').off('child_changed'); } reBuildResultAria(){ //e-typing plus用 document.getElementById("RTCGamePlayScene").style.marginTop = '' document.getElementById("RTCGamePlayScene").style.display = 'none' document.getElementById("battle-display-option").style.display = 'none' document.getElementById("comment").style.display = 'none' document.getElementById("result").querySelector('article').style.height = '441px' document.getElementById("current").style.height = '414px' document.getElementById("exampleList").style.height = '345px' document.getElementsByClassName("result_data")[0].style.height = '365px' document.getElementById("prev").style.height = '414px' document.getElementsByClassName("result_data")[1].style.height = '365px' for(let i=0;i<2;i++){ document.getElementsByClassName("result_data")[i].firstElementChild.insertAdjacentHTML('beforeend', `<li class='battle-result'><div class="data"></div></li><li class='input-mode'><div class="data"></div></li>`) } } checkResultDisplay(){ myResult.sendResult() myResult.reBuildResultAria() } } let myResult class DeleteUser { constructor(){ } userTimeoutCheck(DeleteTimeStamp){ firebase.database().ref('users').once('value').then(users => { const PLAYERS_KEY = Object.keys(users.val()) let updates = {} for(let i=0;i<PLAYERS_KEY.length;i++){ const checkID = PLAYERS_KEY[i] const TIMEOUT_TIME = DeleteTimeStamp - users.val()[checkID].deleteTimeStamp if(TIMEOUT_TIME >= 100000){ //50秒TimeStampの更新が無ければユーザーを削除する updates['/users/' + checkID] = null; updates['/usersState/' + checkID] = null; }else if(TIMEOUT_TIME >= 20000){ //20秒TimeStampの更新が無ければタイムアウト状態にする updates['/usersState/' + checkID + '/state'] = "timeOut" } } firebase.database().ref().update(updates); }); } } let deleteUser = new DeleteUser() class PlayerStateChange { constructor(){ this.prevState = 'idle' } update(state){ if(this.prevState == 'oldVersion'){return;} let updates = {} if(state != 'afk' && state != 'timeOut'){ this.prevState = state } updates['/usersState/' + myID + '/state'] = state; switch(state){ case "oldVersion": break; case "move": break; case "idle": break; case "preStart": firebase.database().ref('usersState/').off('child_changed'); firebase.database().ref('usersState/').on('child_changed', battleUserData.onChangeUserState); break; case "matching": battleArea.displayReadyButton() break; case "ready": updates['/users/' + myID + '/status/' + '/lineInput'] = '準備完了'; break; case "play": break; case "soloPlay": break; case "result": break; case "afk": break; case "timeOut": break; } firebase.database().ref().update(updates) } } let playerStateChange class MyData { constructor(){ this.locationDateTimeStamp this.localDateTimeStamp this.myName this.UserCheckTimeCount = 0 this.lineInput = '' this.clearCount = 0 this.updateTimeInterval this.timeStampForAFK this.isWrittenAFKState this.AFK_TIMEOUT = 60000; myResult = new MyResult() deleteUser = new DeleteUser() playerStateChange = new PlayerStateChange() this.update() this.loggedIn() this.getLocationDate().then( () => myData.startingClockTime()) } update(){ var updates = {}; this.myName = localStorage.getItem("battleName") //ユーザーネーム更新 updates['/usersState/' + myID + '/name'] = this.myName; updates['/users/' + myID + '/version'] = GM_info.script.version updates['/users/' + myID + '/status/' + '/lineInput'] = ''; updates['/users/' + myID + '/status/' + '/clearCount'] = 0; firebase.database().ref().update(updates) playerStateChange.update(playerStateChange.prevState) } loggedIn(){ document.getElementById("start_btn").style.display = 'block' document.getElementsByClassName("loading")[0].style.display = 'none' window.addEventListener('beforeunload', e => { playerStateChange.update('move') myData.resetResult() }); if(window.parent.document.getElementsByClassName("pp_close").length){ window.parent.document.getElementsByClassName("pp_close")[0].addEventListener('click',e => { playerStateChange.update('move') myData.resetResult() }); } window.addEventListener('focus', e => { myData.update() myData.startingClockTime() myData.resetResult() }); } resetResult(){ let updates = {}; updates['/users/' + myID + '/result/'] = '' updates['/usersState/' + myID + '/matchPlayerKey'] = null firebase.database().ref().update(updates) } updateTimeStamp(){ const newDate = new Date().getTime() var updates = {}; const deleteTimeStamp = myData.locationDateTimeStamp + (newDate - myData.localDateTimeStamp) updates['/users/' + myID + '/deleteTimeStamp'] = deleteTimeStamp myData.afkStateChange(newDate) //30秒に一度、ルーム内のユーザーの存在をチェックする if(newDate - myData.UserCheckTimeCount >= 30000){ myData.UserCheckTimeCount = newDate deleteUser.userTimeoutCheck(deleteTimeStamp) } firebase.database().ref().update(updates); } startingClockTime(){ //ユーザー確認用タイムスタンプを更新 this.updateTimeStamp() clearInterval(this.updateTimeInterval) this.updateTimeInterval = setInterval(this.updateTimeStamp,5000) } async getLocationDate(){ const resp = await fetch(window.location.href) //サーバー時刻のタイムスタンプ this.locationDateTimeStamp = await new Date(resp.headers.get("date")).getTime() //ローカル時刻タイムスタンプ this.localDateTimeStamp = new Date().getTime() this.timeStampForAFK = new Date().getTime(); this.afkEvent = this.afkReturnStateChange.bind(this) document.body.addEventListener("keydown",this.afkEvent); document.body.addEventListener("mousedown",this.afkEvent); document.body.addEventListener("mousemove",this.afkEvent); return true } afkReturnStateChange(){ this.timeStampForAFK = new Date().getTime(); if(this.isWrittenAFKState){ this.isWrittenAFKState = false; playerStateChange.update(playerStateChange.prevState) this.updateTimeStamp() } } afkStateChange(newDate){ if(newDate > this.AFK_TIMEOUT + this.timeStampForAFK && (playerStateChange.prevState == "matching" || playerStateChange.prevState == "preStart")){ if(!this.isWrittenAFKState){ this.isWrittenAFKState = true; playerStateChange.update('afk') } } } } let myData let myID class LoginFirebase { constructor(){ firebase.initializeApp(firebaseConfig) this.roginAnon() } roginAnon(){ firebase.auth().signInAnonymously().catch(function(error) { // Handle Errors here. var errorCode = error.code; var errorMessage = error.message; console.log(errorCode); console.log( errorMessage); alert("RealTimeCombatting:Firebaseのサインインに失敗しました。"); return false; // ... }); firebase.auth().onAuthStateChanged(function(user) { if (user) { // User is signed in. myID = "U"+user.uid console.log('!!!') var path = firebase.database().ref('users/' + myID); path.transaction(function(currentData) { //ユーザー情報を更新 myData = new MyData() }).then( () => loginFirebase.versionCheck()); } }); } versionCheck(){ firebase.database().ref('newVersion').once('value').then(version => { const newVersion = version.val() if(+GM_info.script.version < +newVersion){ if(confirm(`e-対戦:新しいバージョンがあります。スクリプトの更新をお願いします。`)){ open('https://greasyfork.org/ja/scripts/471999-e-%E5%AF%BE%E6%88%A6-e-typing/versions') } playerStateChange.update('oldVersion') } }).catch(error => console.log(error)); } } let loginFirebase const typingAppMod = () => { //タイピング画面に移動した。 if(setUp.battleSwitch){ battleArea = new BattleArea() keyJudge = new KeyJudge() } } class SetMutationObserver { constructor(){ this.observer this.elem = document.getElementById("app") this.config = { childList: true//「子ノード(テキストノードも含む)」の変化 }; this.set() this.startObserve() } startObserve(){ this.observer.observe(this.elem, this.config); } stopObserve(){ this.observer.disconnect(); } set(){ this.observer = new MutationObserver(function(event){ const add = event[0].addedNodes[0] const remove = event[0].removedNodes[0] console.log(add) console.log(remove) if(remove && remove.id == 'hands' && playerStateChange.prevState != "soloPlay"){ console.log('resultDisplay') setMutationObserver.setResultObserver() return; } if(add && add.id == 'example_container'){ console.log('battleAreaSetUp') if(!setUp.battleSwitch){ setMutationObserver.stopObserve() return; } if(battleUserData && battleUserData.data){ battleUserData.deleteBattlePlayerEvents() } typingAppMod() return; } }); } setResultObserver(){ this.result = document.getElementById("result") this.resultObserver = new MutationObserver(function(){ console.log('result!') myResult.checkResultDisplay() setMutationObserver.resultObserver.disconnect(); }); this.resultObserver.observe(this.result, this.config); } } let setMutationObserver class KeyJudge { constructor(){ this.wordReload = false; this.clearLine = 0 if(keyJudge){ keyJudge.removeEvent() } this.addEvent() } addEvent(){ this.Event = this.wait.bind(this) this.playEvent = this.startSpaceKey.bind(this) window.addEventListener("keydown",this.Event) window.addEventListener("keydown",this.playEvent) } removeSpaceKeyEvent(){ window.removeEventListener("keydown",this.playEvent) } removeEvent(){ window.removeEventListener("keydown",this.Event) } wait(event){ setTimeout(() => this.keyDown(event)) } startSpaceKey(event){ if(event.code == 'Space' || event.code == 'KeyL'){ playerStateChange.update('soloPlay') this.removeSpaceKeyEvent() battleArea.displayKeyboard() } } judge(event , sentenceText){ let result if(setUp.typingMode == "roma"){ result = sentenceText.textContent.slice(-1).toLowerCase() == event.key ? true:false }else if(setUp.typingMode == "eng"){ result = sentenceText.textContent.slice(-1).replace("␣", " ") == event.key ? true:false }else if(setUp.typingMode == "kana"){ result = this.createKanaChar(event).includes(sentenceText.textContent.slice(-1)) } return result; } keyDown(event){ if(playerStateChange.prevState == 'soloPlay'){return} const sentenceText = document.getElementsByClassName("entered")[setUp.enteredClass] let key if(sentenceText){ key = this.judge(event , sentenceText) } if(event.key == "Escape"){ this.wordReload = false } if(!sentenceText && this.wordReload){ this.sendWordData('') this.wordReload = true if(!sentenceText){ this.wordReload = false } }else if(sentenceText && key){ this.sendWordData(sentenceText.textContent) this.wordReload = true if(!sentenceText){ this.wordReload = false } } } createKanaChar(event){ let char = windows_keymap[event.code] ? windows_keymap[event.code] : kana_keymap[event.key]; if(event.shiftKey){ if(event.code == "KeyE"){char[0] = "ぃ";} if(event.code == "KeyZ"){char[0] = "っ";} } if(event.shiftKey && event.key === "0"){char = ["を"];} return char; } sendWordData(text) { var updates = {} updates['/users/' + myID + '/status/' + '/lineInput'] = text.substr( -25, 25 ); if(!text){ this.clearLine++ updates['/users/' + myID + '/status/' + '/clearCount'] = this.clearLine } firebase.database().ref().update(updates) } } let keyJudge class BattleArea { constructor(){ battleUserData = new BattleUserData() playerStateChange.update('preStart') const RTC = document.getElementById("RTCGamePlayScene") if(RTC){ RTC.remove() document.getElementById("battle-display-option").remove() document.getElementById("user-state-area").remove() } this.createArea() myData.resetResult() this.Lstart = false this.stateName = { "oldVersion":'マッチ不可', "move":'離席中', "idle":'離席中', "preStart":'対戦募集', "matching":'マッチ済み', "ready":'マッチ済み', "play":'対戦中', "soloPlay":'ソロ', "result":'リザルト', "afk":'離席中', "timeOut":'離席中' } } displayReadyButton(){ document.getElementById("l-ready-button").style.display = 'block' document.getElementById("ready-button").style.display = 'block' } hideReadyButton(){ document.getElementById("l-ready-button").style.display = 'none' document.getElementById("ready-button").style.display = 'none' } displayKeyboard(){ document.getElementById('virtual_keyboard').style.display = 'block'; document.getElementById('hands').style.display = 'block'; document.getElementById('RTCGamePlayScene').style.display = 'none'; document.getElementById("battle-display-option").style.display = 'none' document.getElementById("user-state-area").style.display = 'none' } hideKeyboard(){ document.getElementById('virtual_keyboard').style.display = 'none'; document.getElementById('hands').style.display = 'none'; document.getElementById('RTCGamePlayScene').style.display = 'block'; document.getElementById("battle-display-option").style.visibility = 'hidden' document.getElementById("user-state-area").style.display = 'none' } addTable(){ document.getElementById('user-state-area').insertAdjacentHTML('beforeend', `<table class='user-state-table'><tbody><tr><td>参加者</td><td>状態</td></tr></tbody></table>`) } createActiveUserTable(){ document.getElementById('RTCGamePlayScene').insertAdjacentHTML('afterend', `<div id='user-state-area'><table class='user-state-table'><tbody><tr><td>参加者</td><td>状態</td></tr></tbody></table></div>`) firebase.database().ref('usersState').once('value').then(usersState => { const USERS_STATE = usersState.val() const USERS_KEY = Object.keys(USERS_STATE) for(let i=0;i<USERS_KEY.length;i++){ const tableClass = document.getElementsByClassName("user-state-table") const table = tableClass[tableClass.length-1].firstElementChild if(table.children.length < 4){ table.insertAdjacentHTML('beforeend',`<tr id='${USERS_KEY[i]}' class='${USERS_KEY[i] == myID ? 'mine': ''}'><td class='name'>${USERS_STATE[USERS_KEY[i]].name}</td><td class='state'>${battleArea.stateName[USERS_STATE[USERS_KEY[i]].state]}</td></tr>`) }else{ battleArea.addTable() i-- } } }); } createArea(){ document.getElementById('virtual_keyboard').style.display = 'none'; document.getElementById('hands').style.display = 'none'; document.getElementById('start_msg').insertAdjacentHTML("afterbegin" , `<div style="cursor: pointer; display: none;position: absolute;bottom: -23px;left: 28px;width: 130px;font-size: 1.1rem;" class="loading" id='ready-button'>準備完了</div> <div style="cursor: pointer;display: none;position: absolute;bottom: -23px;width: 130px;right: 26px;font-size: 0.8rem;" class="loading" id='l-ready-button'>Lスタートで準備完了</div>`) document.getElementById("ready-button").addEventListener('click', event => { event.target.style.display = 'none' document.getElementById("l-ready-button").style.display = 'none' battleArea.Lstart = false playerStateChange.update('ready') }) document.getElementById("l-ready-button").addEventListener('click', event => { event.target.style.display = 'none' document.getElementById("ready-button").style.display = 'none' battleArea.Lstart = true playerStateChange.update('ready') }) const SEARCH_PLAYER_GUIDE = playerStateChange.prevState != "oldVersion" ? '対戦相手を探しています' : `<a href="https://greasyfork.org/ja/scripts/471999-e-%E5%AF%BE%E6%88%A6-e-typing/versions">こちら</a>から最新のスクリプトに更新をお願いします` document.getElementById('example_container').insertAdjacentHTML('afterend', `<div id='battle-display-option'> <div class="display-head"><strong>表示方法</strong></div> <div><label><input type="radio" name="layout" id="keyboard-display" ${localStorage.getItem('battle-display-option') == 'keyboard-display' || !localStorage.getItem('battle-display-option')? 'checked' : ''}>キーボード</label> <label><input type="radio" name="layout" id="player-display" ${localStorage.getItem('battle-display-option') == 'player-display' ? 'checked' : ''}>対戦相手</label></div> </div> <div id="RTCGamePlayScene"><table class='user-table' rules="all" border="1"><tbody> <tr> <td class='user-name'></td> <td><span class="RTCLine" style='color:#7b7a7a;'>${SEARCH_PLAYER_GUIDE}</span></td> <td class="InputMode"></td></tr></tbody></table></div>`) document.getElementById("battle-display-option").addEventListener('change', event => { localStorage.setItem('battle-display-option',event.target.id) }) this.createActiveUserTable() } addBattleStatusTable(userName,key,inputType){ const INPUT_TYPE = inputType != 'kana' ? 'ローマ字' : 'かな' document.getElementsByClassName("user-name")[0].textContent = userName document.getElementsByClassName("InputMode")[0].textContent = INPUT_TYPE document.getElementsByClassName("RTCLine")[0].textContent = '対戦相手の準備完了を待ってます' document.getElementsByClassName("RTCLine")[0].insertAdjacentHTML('afterend',`<div id="battle-progress-bar"></div>`) document.getElementById("battle-progress-bar").style.width = (document.getElementsByClassName("RTCLine")[0].parentElement.clientWidth-3) + 'px' } removeBattleStatusTable(){ setTimeout( () => { if(playerStateChange.prevState == "preStart"){ document.getElementsByClassName("RTCLine")[0].textContent = '対戦相手を探しています' } },2000) document.getElementsByClassName("user-name")[0].textContent = '' document.getElementsByClassName("InputMode")[0].textContent = '' document.getElementsByClassName("RTCLine")[0].style.color = '#7b7a7a' document.getElementsByClassName("RTCLine")[0].textContent = '離脱しました' if(document.getElementById("battle-progress-bar") != null){ document.getElementById("battle-progress-bar").remove() } } } let battleArea class BattleUserData{ constructor(){ this.data } onUpdateUserStatus(snapshot){ const uid = snapshot.ref_.path.pieces_[1]; const Update_Info = snapshot.ref_.path.pieces_[3] const SnapShotValue = snapshot.val() switch(Update_Info){ case "clearCount": document.getElementById("battle-progress-bar").style.transform = `scaleX(${(100 - (+SnapShotValue / 15 * 100))/100})` break; case "lineInput": if(SnapShotValue){ document.getElementsByClassName('RTCLine')[0].textContent = SnapShotValue; }else{ document.getElementsByClassName('RTCLine')[0].textContent = ""; } break; } } findBattlePlayer(name,key,state){ battleUserData.data = { name:name, key:key, mode:'roma', state:state } battleArea.addBattleStatusTable(battleUserData.data.name , battleUserData.data.key , battleUserData.data.mode) this.addBattlePlayerEvents() playerStateChange.update('matching') let updates = {} updates['/usersState/' + myID + '/matchPlayerKey'] = battleUserData.data.key firebase.database().ref().update(updates) document.getElementById("start_msg").getElementsByTagName('em')[0].textContent = 'スペースキーを押すと一人プレイで開始します' const userTable = document.getElementById(battleUserData.data.key) if(userTable){ userTable.style.fontWeight = 'bold' } } addBattlePlayerEvents(){ firebase.database().ref('users/' + battleUserData.data.key + '/status').on('child_changed', battleUserData.onUpdateUserStatus); firebase.database().ref('users/' + battleUserData.data.key + '/result').on('child_added', myResult.onBattleResultDisplay); } deleteBattlePlayerEvents(){ firebase.database().ref('users/' + battleUserData.data.key + '/status').off('child_changed'); firebase.database().ref('users/' + battleUserData.data.key + '/result').off('child_changed'); } lostBattlePlayer(){ this.deleteBattlePlayerEvents() battleUserData.data = null battleArea.hideReadyButton() playerStateChange.update('preStart') document.getElementById("start_msg").getElementsByTagName('em')[0].textContent = 'スペースキーで開始' battleArea.removeBattleStatusTable() } resultTimeoutPlayer(){ this.deleteBattlePlayerEvents() const RESULT_DATA = document.getElementsByClassName("result_data")[1].firstElementChild.children document.getElementById("prev").firstElementChild.textContent = 'タイムアウト' for(let i=0; i<RESULT_DATA.length; i++){ RESULT_DATA[i].lastElementChild.textContent = '-' RESULT_DATA[i].lastElementChild.style.color = '#23c21f' } } updateUserTable(uid, name, state){ const userTable = document.getElementById(uid) if(userTable){ userTable.getElementsByClassName("name")[0].textContent = name; userTable.getElementsByClassName("state")[0].textContent = battleArea.stateName[state]; }else{ const tableClass = document.getElementsByClassName("user-state-table") const table = tableClass[tableClass.length-1].firstElementChild if(table.children.length < 4){ table.insertAdjacentHTML('beforeend',`<tr id='${uid}'><td class='name'>${name}</td><td class='state'>${battleArea.stateName[state]}</td></tr>`) }else{ battleArea.addTable() this.updateUserTable(uid, name, state) } } } onChangeUserState(snapshot){ const uid = snapshot.ref_.path.pieces_[1]; const Update_Info = snapshot.ref_.path.pieces_[3] const name = snapshot.val().name const state = snapshot.val().state const matchPlayerKey = snapshot.val().matchPlayerKey //対戦相手のstateを更新する if(battleUserData.data && uid == battleUserData.data.key){ battleUserData.data.state = state; } if(!battleUserData.data){ battleUserData.updateUserTable(uid, name, state) //お互いに状態がpreStartのプレイヤーをマッチさせる if( playerStateChange.prevState == "preStart"){ //先にマッチ画面で待機していたプレイヤー if(uid != myID && state == 'preStart'){ battleUserData.findBattlePlayer(name , uid , state) } //後から入室したプレイヤー if(matchPlayerKey == myID && state == 'matching'){ battleUserData.findBattlePlayer(name , uid , state) } } } if(battleUserData.data){ if(battleUserData.data.state == 'ready'){ document.getElementsByClassName("RTCLine")[0].style.color = '' } //自分と相手が準備完了したら同時に開始。 if( (battleUserData.data.key == uid || myID == uid) && (battleUserData.data.state == 'ready' && playerStateChange.prevState == "ready") ){ document.dispatchEvent( new KeyboardEvent("keydown",{ // keyCode:32 スペースキー keyCode:76 Lキー // keyプロパティ & codeプロパティでは開始できませんでした。 keyCode: (battleArea.Lstart ? 76 : 32) })) const layoutRadioButton = document.querySelector("input[name='layout']:checked") if(layoutRadioButton.id == 'keyboard-display'){ battleArea.displayKeyboard() }else{ battleArea.hideKeyboard() } document.getElementsByClassName('RTCLine')[0].textContent = ''; playerStateChange.update('play') keyJudge.removeSpaceKeyEvent() } //相手が離脱した。 if(uid == battleUserData.data.key && (state == 'soloPlay' || state == 'idle' || state == 'timeOut' || state == 'move' || state == 'afk')){ if(playerStateChange.prevState != "play" && playerStateChange.prevState != "result"){ battleUserData.lostBattlePlayer() }else if(playerStateChange.prevState == "play"){ document.getElementsByClassName('RTCLine')[0].textContent = 'タイムアウトしました。'; battleUserData.deleteBattlePlayerEvents() }else if(playerStateChange.prevState == "result"){ battleUserData.resultTimeoutPlayer() } myData.resetResult() } } } } let battleUserData //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const kana_keymap = { 0: ["わ"], 1: ["ぬ"], "!": ["ぬ"], 2: ["ふ"], 3: ["あ"], 4: ["う"], 5: ["え"], 6: ["お"], 7: ["や"], 8: ["ゆ"], 9: ["よ"], "-": ["ほ","-"], "q": ["た"], "Q": ["た"], "w": ["て"], "W": ["て"], "e": ["い"], "E": ["い"], "r": ["す"], "R": ["す"], "t": ["か"], "T": ["か"], "y": ["ん"], "Y": ["ん"], "u": ["な"], "U": ["な"], "i": ["に"], "I": ["に"], "o": ["ら"], "O": ["ら"], "p": ["せ"], "P": ["せ"], "a": ["ち"], "A": ["ち"], "s": ["と"], "S": ["と"], "d": ["し"], "D": ["し"], "f": ["は"], "F": ["は"], "g": ["き"], "G": ["き"], "h": ["く"], "H": ["く"], "j": ["ま"], "J": ["ま"], "k": ["の"], "K": ["の"], "l": ["り"], "L": ["り"], "z": ["つ"], "Z": ["つ"], "x": ["さ"], "X": ["さ"], "c": ["そ"], "C": ["そ"], "v": ["ひ"], "V": ["ひ"], "b": ["こ"], "B": ["こ"], "n": ["み"], "N": ["み"], "m": ["も"], "M": ["も"], ",": ["ね",","], "<": ["、"], ".": ["る","."], ">": ["。"], "/": ["め","/"], "?": ["・"], "#": ["ぁ"], "$": ["ぅ"], "%": ["ぇ"], "'": ["ゃ","’","'"], "^": ["へ"], "~": ["へ"], "&": ["ぉ"], "(": ["ゅ"], ")": ["ょ"], '|': ["ー"], "_": ["ろ"], "=": ["ほ"], "+": ["れ"], ";": ["れ"], '"': ["ふ","”","“","\""], "@": ["゛"], '`': ["゛"], "[": ["゜"], ']': ["む"], "{": ["「"], '}': ["」"], ":": ["け"], "*": ["け"] } const windows_keymap = { 'IntlYen': ["ー","¥","\\"], "IntlRo": ["ろ","¥","\\"], "Space": [" "], "Numpad1": [], "Numpad2": [], "Numpad3": [], "Numpad4": [], "Numpad5": [], "Numpad6": [], "Numpad7": [], "Numpad8": [], "Numpad9": [], "Numpad0": [], "NumpadDivide": [], "NumpadMultiply": [], "NumpadSubtract": [], "NumpadAdd": [], "NumpadDecimal": [] } class SetUp { constructor(){ this.typingMode = 'roma' this.enteredClass = 2 this.battleSwitch = true this.checkDisplayMenu() loginFirebase = localStorage.getItem("battle-option") != "false" ? new LoginFirebase() : null } checkDisplayMenu(){ const config = { childList: true//「子ノード(テキストノードも含む)」の変化 }; const Observe = new MutationObserver(function(event){ setUp.createMenu() setMutationObserver = new SetMutationObserver() Observe.disconnect(); }); Observe.observe(document.getElementById("app"), config); } checkTypingMode(){ if(location.href.match(/kana\.1/)){ this.typingMode = "kana" this.enteredClass = 1 }else if(location.href.match(/std\.2/) || location.href.match(/lstn\.4/)){ this.typingMode = "eng" this.enteredClass = 1 }else{ this.typingMode = "roma" this.enteredClass = 2 } } createMenu(){ const NAME = localStorage.getItem("battleName") setUp.battleSwitch = localStorage.getItem("battle-option") == "false" ? false : true; if(loginFirebase){ document.getElementById("start_btn").style.display = setUp.battleSwitch == false ? '' : 'none' document.getElementById("start_btn").insertAdjacentHTML('afterend',`<div class='loading'>対戦データベースに接続中</div>`) } this.addCss() const FUNC_VIEW = document.getElementById("func_view") FUNC_VIEW.style.height = document.getElementById("func_view").clientHeight + 30 + "px" FUNC_VIEW.insertAdjacentHTML('beforeend' , `<div><div> <label><small>対戦機能</small> <input id="battle-option" type="checkbox" style="display:none;" ${setUp.battleSwitch == false ? "" : "checked"}> <div id="sound-effect-btn" style="margin-left:4px;" class="switch_btn"><a class="on_btn btn show">ON</a> <a class="off_btn btn" style="display:${setUp.battleSwitch == false ? "block" : ""};">OFF</a></div> </label> <input type="text" id="battle-name" placeholder="Name" value="${NAME ? NAME : 'Guest'}" maxlength="10" style="display:${setUp.battleSwitch == false ? "none" : "inline"}; position: absolute;width: 7rem;margin: 2px;right: 85px;"> </div></div>`) if(!NAME){ localStorage.setItem("battleName" , 'Guest') } document.getElementById("battle-name").addEventListener("change", event => { localStorage.setItem("battleName" , event.target.value) myData.update() }) document.getElementById("battle-option").addEventListener("change" , event => { localStorage.setItem("battle-option" , event.target.checked); if(event.target.checked){ document.querySelector("#sound-effect-btn .off_btn").style.display = "" document.getElementById("battle-name").style.display = "inline" setUp.battleSwitch = true; if(!loginFirebase){ loginFirebase = new LoginFirebase() document.getElementById("start_btn").style.display = setUp.battleSwitch == false ? '' : 'none' document.getElementById("start_btn").insertAdjacentHTML('afterend',`<div class='loading'>対戦データベースに接続中</div>`) } }else{ document.querySelector("#sound-effect-btn .off_btn").style.display = "block" document.getElementById("battle-name").style.display = "none" setUp.battleSwitch = false; } }) } addCss(){ document.getElementById("app").insertAdjacentHTML('afterend',`<style> .loading{ color: #fff; font-size: 12px; font-weight: bold; background-color: #057fff; width: 160px; height: 45px; margin: 0 auto; text-align: center; line-height: 45px; overflow: hidden; border-radius: 3px; } .user-table{ width: 100%; position: relative; height: 68px; left: 0; right: 0; margin: auto; } .user-table tr{ font-weight:bold; height: 3rem; } .user-name{ font-size: 1rem; width: 90px; text-align: center; } .RTCLine{ max-width: 350px; white-space: nowrap; overflow:hidden; width: 68%; color:#ffd0a6; font-size: 26px; font-weight: normal; } .InputMode{ font-size: 1rem; width: 95px; text-align: center; } .clear-line{ font-size: 1rem; text-align: center; } #RTCGamePlayScene{ margin: 8px; margin-top:1rem; } .display-head{ margin-left: 0.2rem; font-size: 1rem; margin-bottom: 0.3rem; } #battle-display-option{ margin-left: 4rem; display: flex; flex-direction: column; } #battle-progress-bar{ position: absolute; bottom: 1px; background-color: #bcbcbc; height: 4px; transform-origin: left top; } #user-state-area{ display: flex; justify-content: space-evenly; align-items: flex-start; margin-top: 1.3rem; } .user-state-table, .user-state-table td, .user-state-table th{ border: 1px solid #595959; border-collapse: collapse; text-align: center; } .user-state-table td, .user-state-table th { padding: 3px; width: 100px; height: 25px; } .mine{ font-weight:bold; } </style>`) } } const setUp = new SetUp()