e-対戦 [e-typing]

e-typingに対戦機能を追加したい

  1. // ==UserScript==
  2. // @name e-対戦 [e-typing]
  3. // @namespace http://tampermonkey.net/
  4. // @version 6.2
  5. // @description e-typingに対戦機能を追加したい
  6. // @author Toshi
  7. // @match https://www.e-typing.ne.jp/app/jsa_std*trysc*
  8. // @match https://www.e-typing.ne.jp/app/jsa_kana*trysc*
  9.  
  10. // @exclude https://www.e-typing.ne.jp/app/ad*
  11. // @exclude https://www.e-typing.ne.jp/app/*std.2*
  12. // @icon https://www.google.com/s2/favicons?sz=64&domain=e-typing.ne.jp
  13. // @license MIT
  14. // @require https://www.gstatic.com/firebasejs/10.1.0/firebase-app-compat.js
  15. // @require https://www.gstatic.com/firebasejs/10.1.0/firebase-auth-compat.js
  16. // @require https://www.gstatic.com/firebasejs/10.1.0/firebase-database-compat.js
  17.  
  18. // @grant unsafeWindow
  19.  
  20.  
  21. // ==/UserScript==
  22.  
  23. //localStorage内に'firebase:previous_websocket_failure'keyが存在しているとデータ取得できなくなるため、ロード時に削除。
  24. localStorage.removeItem('firebase:previous_websocket_failure')
  25.  
  26. const firebaseConfig = {
  27. //テスト用データベース
  28. /* apiKey: "AIzaSyARzYljiRuZCoABA32_wnuyMkjScpcZUN8",
  29. databaseURL: "https://e-typing-battle-fb330-default-rtdb.firebaseio.com/" */
  30.  
  31. //本番用データベース
  32. apiKey: "AIzaSyDsHiPII5dgN_AEGwOtMehyveucoF4Twvs",
  33. databaseURL: "https://e-typing-battle-default-rtdb.firebaseio.com"
  34. };
  35.  
  36.  
  37. class MyResult{
  38.  
  39. constructor(){
  40. this.battlePlayerResultData = {}
  41.  
  42. this.etypingPlus = ['score','level','time','typeCount','missCount','wpm','correct','latency','rkpm']
  43. this.normal = ['score','level','time','typeCount','missCount','wpm','correct','weakKey']
  44. }
  45.  
  46.  
  47.  
  48. sendResult(){
  49. playerStateChange.update('result')
  50.  
  51. const RESULT_DATA = document.getElementsByClassName("result_data")[0].firstElementChild.children
  52. const RESULT_ITEM = RESULT_DATA.length == 9 ? this.etypingPlus : this.normal
  53. let updates = {}
  54. let sendData = {}
  55.  
  56.  
  57. for(let i=0; i<RESULT_ITEM.length; i++){
  58. sendData[RESULT_ITEM[i]] = RESULT_DATA[i].lastElementChild.textContent
  59. }
  60.  
  61.  
  62.  
  63.  
  64. updates['/users/' + myID + '/result/'] = sendData
  65. firebaseDB.ref().update(updates)
  66.  
  67.  
  68. this.createBattlePlayerResultArea()
  69. //相手のリザルトを取得していた場合、相手のリザルトを表示
  70. const BATTLE_PLAYPER_RESULT = Object.keys(this.battlePlayerResultData)
  71.  
  72. if(BATTLE_PLAYPER_RESULT.length){
  73. const RESULT_DATA = document.getElementsByClassName("result_data")[1].firstElementChild.children
  74. const RESULT_ITEM = RESULT_DATA.length == 9 ? myResult.etypingPlus : myResult.normal
  75.  
  76. for(let i=0;i<BATTLE_PLAYPER_RESULT.length;i++){
  77. const INDEX = RESULT_ITEM.indexOf(BATTLE_PLAYPER_RESULT[i])
  78.  
  79. if(INDEX >= 0){
  80. RESULT_DATA[INDEX].lastElementChild.textContent = this.battlePlayerResultData[BATTLE_PLAYPER_RESULT[i]]
  81. }
  82. }
  83.  
  84. document.getElementById("prev").firstElementChild.textContent = battleUserData.data.name
  85. myResult.displayResultRivalInputMode()
  86.  
  87. }
  88.  
  89. }
  90.  
  91.  
  92. createBattlePlayerResultArea(){
  93. const RESULT_DATA = document.getElementsByClassName("result_data")[1].firstElementChild.children
  94. document.getElementById("prev").firstElementChild.textContent = '待機中'
  95.  
  96. for(let i=0; i<RESULT_DATA.length; i++){
  97. RESULT_DATA[i].lastElementChild.textContent = ''
  98. RESULT_DATA[i].lastElementChild.style.color = '#23c21f'
  99. }
  100.  
  101. if(battleArea.RTCLine.textContent == 'タイムアウトしました。'){
  102. battleUserData.resultTimeoutPlayer()
  103. }
  104.  
  105. }
  106.  
  107.  
  108. onBattleResultDisplay(snapshot){
  109. const uid = snapshot._delegate.ref._path.pieces_[1];
  110. const Info = snapshot._delegate.ref._path.pieces_[3]
  111. const SnapShotValue = snapshot.val()
  112.  
  113.  
  114. myResult.battlePlayerResultData[Info] = SnapShotValue
  115.  
  116. if(playerStateChange.prevState == 'result'){
  117. const RESULT_DATA = document.getElementsByClassName("result_data")[1].firstElementChild.children
  118. const RESULT_ITEM = RESULT_DATA.length == 9 ? myResult.etypingPlus : myResult.normal
  119.  
  120. const INDEX = RESULT_ITEM.indexOf(Info)
  121.  
  122. if(INDEX >= 0){
  123. RESULT_DATA[INDEX].lastElementChild.textContent = SnapShotValue
  124. }
  125.  
  126. document.getElementById("prev").firstElementChild.textContent = battleUserData.data.name
  127. myResult.displayResultRivalInputMode()
  128. }
  129.  
  130. battleUserData.removePlayerStateChangeEvent()
  131. }
  132.  
  133. displayResultRivalInputMode(){
  134. const INPUT_MODE = document.getElementsByClassName("result_data")[1].getElementsByClassName("input-mode")[0]
  135. const MODE = battleUserData.data.mode == 'roma' ? 'ローマ字' : 'かな'
  136.  
  137. if(INPUT_MODE){
  138. INPUT_MODE.lastElementChild.textContent = MODE
  139. }
  140. }
  141.  
  142. reBuildResultAria(){
  143.  
  144. //e-typing plus用
  145. document.getElementById("RTCGamePlayScene").style.marginTop = ''
  146. document.getElementById("RTCGamePlayScene").style.display = 'none'
  147. document.getElementById("battle-display-option").style.display = 'none'
  148. document.getElementById("comment").style.display = 'none'
  149. document.getElementById("result").querySelector('article').style.height = '441px'
  150. document.getElementById("current").style.height = '414px'
  151. document.getElementById("exampleList").style.height = '345px'
  152. document.getElementsByClassName("result_data")[0].style.height = '365px'
  153. document.getElementById("prev").style.height = '414px'
  154. document.getElementsByClassName("result_data")[1].style.height = '365px'
  155.  
  156. for(let i=0;i<2;i++){
  157. document.getElementsByClassName("result_data")[i].firstElementChild.insertAdjacentHTML('beforeend',
  158. `<li class='input-mode'><div class="data"></div></li><li class='battle-result'><div class="data"></div></li>`)
  159. }
  160.  
  161. const INPUT_MODE = document.getElementsByClassName("result_data")[0].getElementsByClassName("input-mode")[0]
  162. const MODE = setUp.typingMode == 'roma' ? 'ローマ字' : 'かな'
  163.  
  164. if(INPUT_MODE){
  165. INPUT_MODE.lastElementChild.textContent = MODE
  166. }
  167. }
  168.  
  169.  
  170. checkResultDisplay(){
  171. myResult.reBuildResultAria()
  172. myResult.sendResult()
  173. }
  174.  
  175.  
  176.  
  177. }
  178. let myResult
  179.  
  180.  
  181.  
  182. class DeleteUser {
  183.  
  184. constructor(){
  185.  
  186. }
  187.  
  188. userTimeoutCheck(DeleteTimeStamp){
  189.  
  190. firebaseDB.ref('users').once('value').then(users => {
  191.  
  192. const PLAYERS_KEY = Object.keys(users.val())
  193. let updates = {}
  194.  
  195. for(let i=0;i<PLAYERS_KEY.length;i++){
  196.  
  197. const checkID = PLAYERS_KEY[i]
  198. const TIMEOUT_TIME = DeleteTimeStamp - users.val()[checkID].deleteTimeStamp
  199.  
  200. if(TIMEOUT_TIME >= 100000 || !users.val()[checkID].deleteTimeStamp){
  201. //50秒TimeStampの更新が無ければユーザーを削除する
  202. updates['/users/' + checkID] = null;
  203. updates['/usersState/' + checkID] = null;
  204.  
  205. }else if(TIMEOUT_TIME >= 20000){
  206. //20秒TimeStampの更新が無ければタイムアウト状態にする
  207. updates['/usersState/' + checkID + '/state'] = "timeOut"
  208. }
  209.  
  210. }
  211.  
  212. firebaseDB.ref().update(updates);
  213. });
  214. }
  215.  
  216. }
  217. let deleteUser = new DeleteUser()
  218.  
  219.  
  220. class PlayerStateChange {
  221.  
  222. constructor(){
  223. this.prevState = 'idle'
  224. this.scriptUpdateNotify = `<a href="https://greasyfork.org/ja/scripts/471999-e-%E5%AF%BE%E6%88%A6-e-typing/versions" style='font-weight:bold;' target="_blank">こちら</a>から最新のスクリプトに更新をお願いします`
  225.  
  226. }
  227.  
  228.  
  229. update(state){
  230.  
  231. if(this.prevState == 'oldVersion'){return;}
  232.  
  233. let updates = {}
  234.  
  235. if(state != 'afk' && state != 'timeOut'){
  236. this.prevState = state
  237. }
  238.  
  239. updates['/usersState/' + myID + '/state'] = state;
  240.  
  241.  
  242. switch(state){
  243. case "oldVersion":
  244.  
  245. const RTCLine = document.getElementsByClassName("RTCLine")[0]
  246.  
  247. if(RTCLine){
  248. RTCLine.innerHTML = playerStateChange.scriptUpdateNotify
  249. }
  250. break;
  251.  
  252. case "move":
  253.  
  254. break;
  255.  
  256. case "idle":
  257.  
  258. break;
  259.  
  260. case "preStart":
  261. battleUserData.removePlayerStateChangeEvent()
  262. battleUserData.addPlayerStateChangeEvent()
  263.  
  264.  
  265. break;
  266.  
  267. case "matching":
  268. battleArea.displayReadyButton()
  269.  
  270. break;
  271.  
  272.  
  273. case "ready":
  274.  
  275. break;
  276.  
  277. case "play":
  278.  
  279. break;
  280.  
  281. case "soloPlay":
  282. battleUserData.removePlayerStateChangeEvent()
  283.  
  284.  
  285. break;
  286.  
  287. case "result":
  288.  
  289. break;
  290.  
  291. case "afk":
  292.  
  293. break;
  294.  
  295. case "timeOut":
  296.  
  297. break;
  298. }
  299.  
  300. firebaseDB.ref().update(updates)
  301.  
  302. }
  303.  
  304. }
  305. let playerStateChange
  306.  
  307.  
  308.  
  309. class MyData {
  310.  
  311. constructor(){
  312. this.locationDateTimeStamp
  313. this.localDateTimeStamp
  314. this.myName
  315. this.UserCheckTimeCount = 0
  316. this.lineInput = ''
  317. this.clearCount = 0
  318. this.updateTimeInterval
  319. this.AFK_TIMEOUT = 60000;
  320.  
  321.  
  322. myResult = new MyResult()
  323. deleteUser = new DeleteUser()
  324. playerStateChange = new PlayerStateChange()
  325.  
  326. this.update()
  327. this.loggedIn()
  328. this.getLocationDate().then( () => myData.startingClockTime())
  329. }
  330.  
  331.  
  332.  
  333. update(){
  334. var updates = {};
  335. const NAME = localStorage.getItem("battleName")
  336. this.myName = NAME ? NAME : 'Guest'
  337.  
  338. //ユーザーネーム更新
  339. updates['/usersState/' + myID + '/name'] = this.myName;
  340. updates['/usersState/' + myID + '/mode'] = setUp.typingMode;
  341. updates['/users/' + myID + '/version'] = GM_info.script.version
  342. updates['/users/' + myID + '/status/' + '/lineInput'] = '';
  343. updates['/users/' + myID + '/status/' + '/clearCount'] = 0;
  344.  
  345. firebaseDB.ref().update(updates)
  346. playerStateChange.update(playerStateChange.prevState)
  347. }
  348.  
  349. loggedIn(){
  350.  
  351. document.getElementById("start_btn").style.display = 'block'
  352. document.getElementsByClassName("loading")[0].style.display = 'none'
  353.  
  354. window.addEventListener('beforeunload', e => {
  355. playerStateChange.update('move')
  356. myData.resetResult()
  357. });
  358.  
  359. if(window.parent.document.getElementsByClassName("pp_close").length){
  360.  
  361. window.parent.document.getElementsByClassName("pp_close")[0].addEventListener('click',e => {
  362. playerStateChange.update('move')
  363. myData.resetResult()
  364. });
  365.  
  366. }
  367.  
  368. window.addEventListener('focus', e => {
  369. myData.update()
  370. myData.startingClockTime()
  371. myData.resetResult()
  372. });
  373.  
  374. }
  375.  
  376. resetResult(){
  377.  
  378. let updates = {};
  379. updates['/users/' + myID + '/result/'] = ''
  380. updates['/usersState/' + myID + '/matchPlayerKey'] = null
  381. firebaseDB.ref().update(updates)
  382.  
  383. }
  384.  
  385. updateTimeStamp(){
  386. const newDate = new Date().getTime()
  387. var updates = {};
  388. const deleteTimeStamp = myData.locationDateTimeStamp + (newDate - myData.localDateTimeStamp)
  389. updates['/users/' + myID + '/deleteTimeStamp'] = deleteTimeStamp
  390.  
  391. //30秒に一度、ルーム内のユーザーの存在をチェックする
  392. if(newDate - myData.UserCheckTimeCount >= 30000){
  393. myData.UserCheckTimeCount = newDate
  394. deleteUser.userTimeoutCheck(deleteTimeStamp)
  395. }
  396.  
  397. firebaseDB.ref().update(updates);
  398. }
  399.  
  400.  
  401. startingClockTime(){
  402. //ユーザー確認用タイムスタンプを更新
  403. this.updateTimeStamp()
  404.  
  405. clearInterval(this.updateTimeInterval)
  406. this.updateTimeInterval = setInterval(this.updateTimeStamp,5000)
  407. }
  408.  
  409. async getLocationDate(){
  410. const resp = await fetch(window.location.href)
  411.  
  412. //サーバー時刻のタイムスタンプ
  413. this.locationDateTimeStamp = await new Date(resp.headers.get("date")).getTime()
  414. //ローカル時刻タイムスタンプ
  415. this.localDateTimeStamp = new Date().getTime()
  416.  
  417.  
  418. return true
  419. }
  420.  
  421.  
  422. }
  423. let myData
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430. let myID
  431. const firebaseApp = firebase.initializeApp(firebaseConfig,"firebaseApp");
  432. const firebaseDB = firebaseApp.database();
  433.  
  434. class LoginFirebase {
  435.  
  436. constructor(){
  437. firebase.initializeApp(firebaseConfig)
  438. this.roginAnon()
  439. }
  440.  
  441.  
  442. roginAnon(){
  443.  
  444. firebase.auth().signInAnonymously().catch(function(error) {
  445. // Handle Errors here.
  446. var errorCode = error.code;
  447. var errorMessage = error.message;
  448.  
  449. console.log(errorCode);
  450. console.log( errorMessage);
  451. alert("RealTimeCombatting:Firebaseのサインインに失敗しました。");
  452. return false;
  453. // ...
  454. });
  455.  
  456. firebase.auth().onAuthStateChanged(function(user) {
  457.  
  458. if (user) {
  459. // User is signed in.
  460. myID = "U"+user.uid
  461. console.log('!!!')
  462.  
  463. var path = firebaseDB.ref('users/' + myID);
  464.  
  465. path.transaction(function(currentData) {
  466. //ユーザー情報を更新
  467. myData = new MyData()
  468. }).then( () => loginFirebase.versionCheck());
  469. }
  470. });
  471.  
  472. }
  473.  
  474.  
  475. versionCheck(){
  476. firebaseDB.ref('newVersion').once('value').then(version => {
  477. const newVersion = version.val()
  478.  
  479. if(+GM_info.script.version < +newVersion){
  480. playerStateChange.update('oldVersion')
  481.  
  482. }
  483.  
  484. }).catch(error => console.log(error));
  485. }
  486. }
  487.  
  488. let loginFirebase
  489.  
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496.  
  497.  
  498.  
  499.  
  500.  
  501.  
  502. const typingAppMod = () => {
  503.  
  504.  
  505. //タイピング画面に移動した。
  506.  
  507. if(setUp.battleSwitch){
  508.  
  509. battleArea = new BattleArea()
  510. keyJudge = new KeyJudge()
  511.  
  512.  
  513. }
  514.  
  515. }
  516.  
  517.  
  518. class SetMutationObserver {
  519.  
  520. constructor(){
  521. this.observer
  522. this.elem = document.getElementById("app")
  523. this.config = {
  524. childList: true//「子ノード(テキストノードも含む)」の変化
  525. };
  526.  
  527. this.set()
  528. this.startObserve()
  529. }
  530.  
  531. startObserve(){
  532. this.observer.observe(this.elem, this.config);
  533. }
  534.  
  535. stopObserve(){
  536. this.observer.disconnect();
  537. }
  538.  
  539. set(){
  540. this.observer = new MutationObserver(function(event){
  541.  
  542. const add = event[0].addedNodes[0]
  543. const remove = event[0].removedNodes[0]
  544.  
  545. if(remove && remove.id == 'hands'){
  546.  
  547. if(playerStateChange.prevState == "play"){
  548. setMutationObserver.setResultObserver()
  549. }else{
  550. playerStateChange.update('result')
  551. }
  552.  
  553. return;
  554. }
  555.  
  556. if(add && add.id == 'example_container'){
  557. //console.log('battleAreaSetUp')
  558.  
  559. if(!setUp.battleSwitch){
  560. setMutationObserver.stopObserve()
  561. return;
  562. }
  563.  
  564. if(battleUserData && battleUserData.data){
  565. battleUserData.deleteBattlePlayerEvents()
  566. }
  567.  
  568. myResult.battlePlayerResultData = {}
  569. typingAppMod()
  570. return;
  571. }
  572. });
  573. }
  574.  
  575.  
  576. setResultObserver(){
  577. this.result = document.getElementById("result")
  578.  
  579. this.resultObserver = new MutationObserver(function(){
  580. console.log('resultDisplay')
  581. myResult.checkResultDisplay()
  582. setMutationObserver.resultObserver.disconnect();
  583.  
  584. });
  585.  
  586. this.resultObserver.observe(this.result, this.config);
  587. }
  588.  
  589. }
  590. let setMutationObserver
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597. class KeyJudge {
  598.  
  599. constructor(){
  600. this.wordReload = false;
  601. this.clearLine = 0
  602. this.wordSendSwitch = 1
  603. if(keyJudge){
  604. keyJudge.removeEvent()
  605. }
  606.  
  607. this.addEvent()
  608. }
  609.  
  610. addEvent(){
  611. this.Event = this.wait.bind(this)
  612. this.playEvent = this.startSpaceKey.bind(this)
  613. window.addEventListener("keydown",this.Event)
  614. window.addEventListener("keydown",this.playEvent)
  615. }
  616.  
  617. removeSpaceKeyEvent(){
  618. window.removeEventListener("keydown",this.playEvent)
  619. }
  620.  
  621. removeEvent(){
  622. window.removeEventListener("keydown",this.Event)
  623. }
  624.  
  625.  
  626. wait(event){
  627. setTimeout(() => this.checkType(event))
  628. }
  629.  
  630. startSpaceKey(event){
  631. if(event.code == 'Space' || event.code == 'Digit1' || event.code == 'Digit2' || event.code == 'Digit3' || event.code == 'Digit0' || (event.code == 'KeyL' && setUp.typingMode == "roma")){
  632. playerStateChange.update('soloPlay')
  633. this.removeSpaceKeyEvent()
  634. this.removeEvent()
  635. battleArea.displayKeyboard()
  636.  
  637.  
  638. }
  639. }
  640.  
  641.  
  642. judge(event , sentenceText){
  643. let result
  644. if(setUp.typingMode == "roma"){
  645. result = sentenceText.textContent.slice(-1).toLowerCase() == event.key ? true:false
  646. }else if(setUp.typingMode == "eng"){
  647. result = sentenceText.textContent.slice(-1).replace("␣", " ") == event.key ? true:false
  648. }else if(setUp.typingMode == "kana"){
  649. result = this.createKanaChar(event).includes(sentenceText.textContent.slice(-1))
  650. }
  651. return result;
  652. }
  653.  
  654. checkType(event){
  655. const sentenceText = document.getElementsByClassName("entered")[setUp.enteredClass]
  656. let key
  657.  
  658. if(sentenceText){
  659. key = this.judge(event , sentenceText)
  660. }
  661.  
  662. if(!sentenceText && this.wordReload){
  663. this.sendWordData('')
  664. this.wordReload = true
  665.  
  666. if(!sentenceText){
  667. this.wordReload = false
  668. }
  669.  
  670. }else if(sentenceText && key){
  671.  
  672. this.sendWordData(sentenceText.textContent)
  673. this.wordReload = true
  674.  
  675. if(!sentenceText){
  676. this.wordReload = false
  677. }
  678.  
  679. }
  680. }
  681.  
  682. createKanaChar(event){
  683. let char = windows_keymap[event.code] ? windows_keymap[event.code] : kana_keymap[event.key];
  684.  
  685. if(event.shiftKey){
  686. if(event.code == "KeyE"){char[0] = "ぃ";}
  687. if(event.code == "KeyZ"){char[0] = "っ";}
  688. }
  689.  
  690. if(event.shiftKey && event.key === "0"){char = ["を"];}
  691.  
  692. return char;
  693. }
  694.  
  695. sendWordData(text) {
  696.  
  697. var updates = {}
  698. if(this.wordSendSwitch == 1 || !text){
  699. this.wordSendSwitch = ''
  700. }else{
  701. this.wordSendSwitch = 1
  702. }
  703.  
  704. updates['/users/' + myID + '/status/' + '/lineInput'] = text.slice(-1) + this.wordSendSwitch;
  705.  
  706. if(!text){
  707. this.clearLine++
  708. updates['/users/' + myID + '/status/' + '/clearCount'] = this.clearLine
  709.  
  710. }
  711.  
  712. firebaseDB.ref().update(updates)
  713. }
  714.  
  715. }
  716. let keyJudge
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.  
  724.  
  725. class BattleArea {
  726.  
  727. constructor(){
  728. battleUserData = new BattleUserData()
  729. if(setUp.soundEffectSwitch){
  730. soundEffect = new SoundEffect()
  731. }
  732. playerStateChange.update('preStart')
  733.  
  734. const RTC = document.getElementById("RTCGamePlayScene")
  735. if(RTC){
  736. RTC.remove()
  737. document.getElementById("battle-display-option").remove()
  738. document.getElementById("user-state-area").remove()
  739. }
  740.  
  741.  
  742. this.createArea()
  743. myData.resetResult()
  744. this.Lstart = false
  745.  
  746. this.joinLength = 0
  747. this.RTCLine
  748. this.myDisplayOption
  749.  
  750. this.stateName = {
  751. "preStart":'対戦募集',
  752. "matching":'マッチ済み',
  753. "ready":'マッチ済み',
  754. "play":'対戦中',
  755. "result":'リザルト',
  756. "oldVersion":'マッチ不可',
  757. "soloPlay":'ソロプレイ',
  758. "move":'離席中',
  759. "idle":'離席中',
  760. "afk":'離席中',
  761. "timeOut":'タイムアウト'
  762. }
  763. }
  764.  
  765.  
  766. displayReadyButton(){
  767.  
  768. const LstartButton = document.getElementById("l-ready-button")
  769.  
  770. if(LstartButton){
  771. document.getElementById("l-ready-button").style.display = 'block'
  772. }
  773.  
  774. document.getElementById("ready-button").style.display = 'block'
  775. }
  776.  
  777. hideReadyButton(){
  778. const LstartButton = document.getElementById("l-ready-button")
  779.  
  780. if(LstartButton){
  781. document.getElementById("l-ready-button").style.display = 'none'
  782. }
  783.  
  784. document.getElementById("ready-button").style.display = 'none'
  785. }
  786.  
  787.  
  788. displayKeyboard(){
  789. document.getElementById('virtual_keyboard').style.display = 'block';
  790. document.getElementById('hands').style.display = 'block';
  791. document.getElementById('RTCGamePlayScene').style.display = 'none';
  792. document.getElementById("battle-display-option").style.display = 'none'
  793. document.getElementById("user-state-area").style.display = 'none'
  794. }
  795.  
  796. hideKeyboard(){
  797. document.getElementById('virtual_keyboard').style.display = 'none';
  798. document.getElementById('hands').style.display = 'none';
  799. document.getElementById('RTCGamePlayScene').style.display = 'block';
  800. document.getElementById("battle-display-option").style.visibility = 'hidden'
  801. document.getElementById("user-state-area").style.display = 'none'
  802. }
  803.  
  804.  
  805. addTable(){
  806. document.getElementById('user-state-area').insertAdjacentHTML('beforeend',`<table class='user-state-table'><tbody><tr><td>現在の参加者</td></tr></tbody></table>`)
  807. }
  808.  
  809. updateJoinLength(){
  810. const JOIN_LENGTH_ELEMENT = document.getElementById("join-length")
  811. if(JOIN_LENGTH_ELEMENT){
  812. JOIN_LENGTH_ELEMENT.textContent = battleArea.joinLength
  813. }
  814. }
  815.  
  816. createActiveUserTable(){
  817. document.getElementById('RTCGamePlayScene').insertAdjacentHTML('afterend',`<div id='user-state-area'></div>`)
  818.  
  819. this.addTable()
  820.  
  821. firebaseDB.ref('usersState').once('value').then(usersState => {
  822.  
  823. const USERS_STATE = usersState.val()
  824. const USERS_KEY = Object.keys(USERS_STATE)
  825.  
  826. for(let i=0;i<USERS_KEY.length;i++){
  827. const tableClass = document.getElementsByClassName("user-state-table")
  828. const table = tableClass[tableClass.length-1].firstElementChild
  829. if(USERS_STATE[USERS_KEY[i]].name){
  830. if(table.children.length < 4){
  831. battleArea.joinLength ++
  832. table.insertAdjacentHTML('beforeend',`<tr id='${USERS_KEY[i]}' class='${USERS_KEY[i] == myID ? 'mine': ''}'><td class='state'>${battleArea.stateName[USERS_STATE[USERS_KEY[i]].state]}</td></tr>`)
  833. }else{
  834. battleArea.addTable()
  835. i--
  836. }
  837. }
  838. }
  839.  
  840. battleArea.updateJoinLength()
  841.  
  842. })
  843.  
  844. setTimeout(() => {
  845. const tableClass = document.getElementsByClassName("user-state-table")
  846. if(tableClass[0].firstChild.children.length == 1){
  847. battleArea.RTCLine.innerHTML = 'データを取得できませんでした。<br>シークレットウィンドウや別のブラウザでお試しください。'
  848. battleArea.RTCLine.style.fontSize = 'larger'
  849. }
  850. },2000)
  851.  
  852.  
  853. }
  854.  
  855.  
  856. createArea(){
  857. document.getElementById('virtual_keyboard').style.display = 'none';
  858. document.getElementById('hands').style.display = 'none';
  859.  
  860. document.getElementById('start_msg').insertAdjacentHTML("afterbegin" ,
  861. `<div class="loading ready-btn" id='ready-button'>準備完了</div>
  862. ${setUp.typingMode == 'roma' ? `<div class="loading ready-btn" id='l-ready-button'>Lスタートで準備完了</div>` : ''}`)
  863.  
  864.  
  865. document.getElementById("ready-button").addEventListener('click', event => {
  866. event.target.style.display = 'none'
  867. const LstartButton = document.getElementById("l-ready-button")
  868.  
  869. if(LstartButton){
  870. LstartButton.style.display = 'none'
  871. }
  872.  
  873. battleArea.Lstart = false
  874. playerStateChange.update('ready')
  875. if(setUp.soundEffectSwitch){
  876. soundEffect.play('ready')
  877. }
  878. })
  879.  
  880. const LstartButton = document.getElementById("l-ready-button")
  881.  
  882. if(LstartButton){
  883.  
  884. LstartButton.addEventListener('click', event => {
  885. event.target.style.display = 'none'
  886. document.getElementById("ready-button").style.display = 'none'
  887. battleArea.Lstart = true
  888. playerStateChange.update('ready')
  889. if(setUp.soundEffectSwitch){
  890. soundEffect.play('ready')
  891. }
  892. })
  893.  
  894. }
  895.  
  896. const SEARCH_PLAYER_GUIDE = playerStateChange.prevState != "oldVersion" ? `対戦相手を探しています (<span id='join-length'>0</span>人参加中)` : playerStateChange.scriptUpdateNotify;
  897.  
  898.  
  899. document.getElementById('example_container').insertAdjacentHTML('afterend',
  900. `<div id='battle-display-option'>
  901. <div class="display-head"><strong>表示方法</strong></div>
  902. <div><label><input type="radio" name="layout" id="keyboard-display" ${localStorage.getItem('battle-display-option') == 'keyboard-display' || !localStorage.getItem('battle-display-option')? 'checked' : ''}>キーボード</label>
  903. <label><input type="radio" name="layout" id="player-display" ${localStorage.getItem('battle-display-option') == 'player-display' ? 'checked' : ''}>対戦相手</label></div>
  904. </div>
  905. <div id="RTCGamePlayScene"><table class='user-table' rules="all" border="1"><tbody>
  906. <tr>
  907. <td class='user-name'></td>
  908. <td><span class="RTCLine" style='color:#7b7a7a;'>${SEARCH_PLAYER_GUIDE}</span></td>
  909. <td class="InputMode"></td></tr></tbody></table></div>`)
  910.  
  911. this.RTCLine = document.getElementsByClassName("RTCLine")[0]
  912.  
  913. document.getElementById("battle-display-option").addEventListener('change', event => {
  914. localStorage.setItem('battle-display-option',event.target.id)
  915. battleArea.sendBattleDisplayOption(event.target.id)
  916. })
  917. this.myDisplayOption = document.querySelector("input[name='layout']:checked").id
  918. this.sendBattleDisplayOption(this.myDisplayOption)
  919. this.createActiveUserTable()
  920. }
  921.  
  922. sendBattleDisplayOption(displayOption){
  923. let updates = {}
  924. updates['/usersState/' + myID + '/displayOption'] = displayOption
  925. this.myDisplayOption = displayOption
  926. firebaseDB.ref().update(updates)
  927. }
  928.  
  929.  
  930.  
  931. addBattleStatusTable(userName,key,inputType){
  932. const INPUT_TYPE = inputType != 'kana' ? 'ローマ字' : 'かな'
  933. document.getElementsByClassName("user-name")[0].textContent = userName
  934. document.getElementsByClassName("InputMode")[0].textContent = INPUT_TYPE
  935. battleArea.RTCLine.textContent = '対戦相手の準備完了を待ってます'
  936. battleArea.RTCLine.insertAdjacentHTML('afterend',`<div id="battle-progress-bar"></div>`)
  937.  
  938. document.getElementById("battle-progress-bar").style.width = (battleArea.RTCLine.parentElement.clientWidth-3) + 'px'
  939.  
  940. }
  941.  
  942. removeBattleStatusTable(){
  943. setTimeout( () => {
  944. if(playerStateChange.prevState == "preStart"){
  945. battleArea.RTCLine.textContent = '対戦相手を探しています'
  946. }
  947. },2000)
  948. document.getElementsByClassName("user-name")[0].textContent = ''
  949. document.getElementsByClassName("InputMode")[0].textContent = ''
  950. battleArea.RTCLine.style.color = '#7b7a7a'
  951. battleArea.RTCLine.textContent = '離脱しました'
  952.  
  953.  
  954. if(document.getElementById("battle-progress-bar") != null){
  955. document.getElementById("battle-progress-bar").remove()
  956. }
  957.  
  958. }
  959.  
  960. }
  961. let battleArea
  962.  
  963.  
  964.  
  965. class BattleUserData{
  966.  
  967. constructor(){
  968. this.data
  969. }
  970.  
  971.  
  972. onUpdateUserStatus(snapshot){
  973. const uid = snapshot._delegate.ref._path.pieces_[1];
  974. const Update_Info = snapshot._delegate.ref._path.pieces_[3]
  975. const SnapShotValue = snapshot.val()
  976.  
  977. switch(Update_Info){
  978. case "clearCount":
  979. document.getElementById("battle-progress-bar").style.transform = `scaleX(${(100 - (+SnapShotValue / 15 * 100))/100})`
  980.  
  981. break;
  982.  
  983. case "lineInput":
  984. if(SnapShotValue){
  985. battleArea.RTCLine.textContent = (battleArea.RTCLine.textContent + SnapShotValue.slice(0,1)).substr( -28, 28 );
  986. } else{
  987. battleArea.RTCLine.textContent = "";
  988. }
  989. break;
  990. }
  991. }
  992.  
  993. findBattlePlayer(name,key,mode,state,displayOption){
  994.  
  995. battleUserData.data = {
  996. name:name,
  997. key:key,
  998. mode:mode,
  999. display:displayOption,
  1000. state:state
  1001. }
  1002.  
  1003. battleArea.addBattleStatusTable(battleUserData.data.name , battleUserData.data.key , battleUserData.data.mode)
  1004. playerStateChange.update('matching')
  1005.  
  1006. if(setUp.soundEffectSwitch){
  1007. soundEffect.play('match')
  1008. }
  1009.  
  1010. let updates = {}
  1011. updates['/usersState/' + myID + '/matchPlayerKey'] = battleUserData.data.key
  1012.  
  1013. firebaseDB.ref().update(updates)
  1014.  
  1015. document.getElementById("start_msg").getElementsByTagName('em')[0].textContent = 'スペースキーを押すと一人プレイで開始します'
  1016.  
  1017. const userTable = document.getElementById(battleUserData.data.key)
  1018.  
  1019. if(userTable){
  1020. userTable.style.fontWeight = 'bold'
  1021. }
  1022.  
  1023. }
  1024.  
  1025. addBattlePlayerEvents(){
  1026.  
  1027. if(battleArea.myDisplayOption != 'keyboard-display'){
  1028. firebaseDB.ref('users/' + battleUserData.data.key + '/status').on('child_changed', battleUserData.onUpdateUserStatus);
  1029. }
  1030.  
  1031. firebaseDB.ref('users/' + battleUserData.data.key + '/result').on('child_added', myResult.onBattleResultDisplay);
  1032. }
  1033.  
  1034.  
  1035. deleteBattlePlayerEvents(){
  1036. firebaseDB.ref('users/' + battleUserData.data.key + '/status').off('child_changed');
  1037. firebaseDB.ref('users/' + battleUserData.data.key + '/result').off('child_added');
  1038. }
  1039.  
  1040. addPlayerStateChangeEvent(){
  1041. firebaseDB.ref('usersState/').on('child_changed', battleUserData.onChangeUserState);
  1042. firebaseDB.ref('usersState/').on('child_removed', battleUserData.onRemoveUserState);
  1043. }
  1044.  
  1045.  
  1046. removePlayerStateChangeEvent(){
  1047. firebaseDB.ref('usersState/').off('child_changed');
  1048. firebaseDB.ref('usersState/').off('child_removed');
  1049. }
  1050.  
  1051. lostBattlePlayer(){
  1052. this.deleteBattlePlayerEvents()
  1053. const userTable = document.getElementById(battleUserData.data.key)
  1054.  
  1055. if(userTable){
  1056. userTable.style.fontWeight = ''
  1057. }
  1058.  
  1059. battleUserData.data = null
  1060. battleArea.hideReadyButton()
  1061. playerStateChange.update('preStart')
  1062. document.getElementById("start_msg").getElementsByTagName('em')[0].textContent = 'スペースキーで開始'
  1063. battleArea.removeBattleStatusTable()
  1064.  
  1065.  
  1066. }
  1067.  
  1068.  
  1069. resultTimeoutPlayer(){
  1070. this.deleteBattlePlayerEvents()
  1071. const RESULT_DATA = document.getElementsByClassName("result_data")[1].firstElementChild.children
  1072. document.getElementById("prev").firstElementChild.textContent = 'タイムアウト'
  1073.  
  1074. for(let i=0; i<RESULT_DATA.length; i++){
  1075. RESULT_DATA[i].lastElementChild.textContent = '-'
  1076. RESULT_DATA[i].lastElementChild.style.color = '#23c21f'
  1077. }
  1078.  
  1079. }
  1080.  
  1081. updateUserTable(uid, name, state){
  1082. const userTable = document.getElementById(uid)
  1083. if(!name){return;}
  1084.  
  1085. if(userTable){
  1086. userTable.getElementsByClassName("state")[0].textContent = battleArea.stateName[state];
  1087. }else{
  1088. const tableClass = document.getElementsByClassName("user-state-table")
  1089. const table = tableClass[tableClass.length-1].firstElementChild
  1090.  
  1091. if(table.children.length < 4){
  1092. battleArea.joinLength ++
  1093. table.insertAdjacentHTML('beforeend',`<tr id='${uid}'><td class='state'>${battleArea.stateName[state]}</td></tr>`)
  1094. battleArea.updateJoinLength()
  1095. }else{
  1096. battleArea.addTable()
  1097. this.updateUserTable(uid, name, state)
  1098. }
  1099.  
  1100. }
  1101.  
  1102. }
  1103.  
  1104. removeUserTable(uid){
  1105. const userTable = document.getElementById(uid)
  1106.  
  1107. if(userTable){
  1108. battleArea.joinLength --
  1109. userTable.remove()
  1110. battleArea.updateJoinLength()
  1111. }
  1112. }
  1113.  
  1114.  
  1115. battleStart(){
  1116.  
  1117. document.dispatchEvent( new KeyboardEvent("keydown",{
  1118. // keyCode:32 スペースキー keyCode:76 Lキー
  1119. // keyプロパティ & codeプロパティでは開始できませんでした。
  1120. keyCode: (battleArea.Lstart ? 76 : 32)
  1121.  
  1122. }))
  1123.  
  1124. battleUserData.addBattlePlayerEvents()
  1125.  
  1126. if(battleUserData.data.displayOption == 'keyboard-display'){
  1127. keyJudge.removeEvent()
  1128. }
  1129.  
  1130.  
  1131. if(battleArea.myDisplayOption == 'keyboard-display'){
  1132. battleArea.displayKeyboard()
  1133. }else{
  1134. battleArea.hideKeyboard()
  1135. }
  1136.  
  1137. battleArea.RTCLine.textContent = '';
  1138. playerStateChange.update('play')
  1139. keyJudge.removeSpaceKeyEvent()
  1140.  
  1141. }
  1142.  
  1143. rivalReady(){
  1144. battleArea.RTCLine.style.color = ''
  1145. battleArea.RTCLine.textContent = '準備完了';
  1146. if(setUp.soundEffectSwitch){
  1147. soundEffect.play('ready')
  1148. }
  1149. }
  1150.  
  1151. onChangeUserState(snapshot){
  1152. const uid = snapshot._delegate.ref._path.pieces_[1];
  1153. const name = snapshot.val().name
  1154. const state = snapshot.val().state
  1155. const mode = snapshot.val().mode
  1156. const displayOption = snapshot.val().displayOption
  1157. const matchPlayerKey = snapshot.val().matchPlayerKey
  1158.  
  1159. //対戦相手のstateを更新する
  1160. if(battleUserData.data && uid == battleUserData.data.key){
  1161. battleUserData.data.state = state;
  1162. }
  1163.  
  1164. //対戦相手のdisplayOptionを更新する
  1165. if(battleUserData.data && uid == battleUserData.data.key){
  1166. battleUserData.data.displayOption = displayOption;
  1167. }
  1168.  
  1169. //参加者のstateを更新する
  1170. if(playerStateChange.prevState != "play"){
  1171. battleUserData.updateUserTable(uid, name, state)
  1172. }
  1173.  
  1174. if(!battleUserData.data){
  1175.  
  1176. //お互いに状態がpreStartのプレイヤーをマッチさせる
  1177. if( playerStateChange.prevState == "preStart"){
  1178.  
  1179. //先にマッチ画面で待機していたプレイヤー
  1180. if(uid != myID && state == 'preStart'){
  1181. battleUserData.findBattlePlayer(name , uid , mode, state , displayOption)
  1182. }
  1183.  
  1184. //後から入室したプレイヤー
  1185. if(matchPlayerKey == myID && state == 'matching'){
  1186. battleUserData.findBattlePlayer(name , uid , mode, state , displayOption)
  1187. }
  1188.  
  1189. }
  1190. }
  1191.  
  1192.  
  1193. if(battleUserData.data){
  1194.  
  1195. if(battleUserData.data.state == 'ready' && battleUserData.data.key == uid){
  1196. battleUserData.rivalReady()
  1197. }
  1198.  
  1199. //自分と相手が準備完了したら同時に開始。
  1200. if( (battleUserData.data.key == uid || myID == uid) && (battleUserData.data.state == 'ready' && playerStateChange.prevState == "ready") ){
  1201. battleUserData.battleStart()
  1202. }
  1203.  
  1204. //相手が離脱した。
  1205. if(uid == battleUserData.data.key && (state == 'soloPlay' || state == 'idle' || state == 'timeOut' || state == 'move' || state == 'afk')){
  1206.  
  1207. if(playerStateChange.prevState != "play" && playerStateChange.prevState != "result"){
  1208. battleUserData.lostBattlePlayer()
  1209. }else if(playerStateChange.prevState == "play"){
  1210. battleArea.RTCLine.textContent = 'タイムアウトしました。';
  1211. battleUserData.deleteBattlePlayerEvents()
  1212. }else if(playerStateChange.prevState == "result"){
  1213. battleUserData.resultTimeoutPlayer()
  1214. }
  1215. myData.resetResult()
  1216. }
  1217.  
  1218.  
  1219. }
  1220.  
  1221. }
  1222.  
  1223. onRemoveUserState(snapshot){
  1224. const uid = snapshot._delegate.ref._path.pieces_[1];
  1225.  
  1226. if( playerStateChange.prevState != "play"){
  1227. battleUserData.removeUserTable(uid)
  1228. }
  1229. }
  1230.  
  1231.  
  1232. }
  1233. let battleUserData
  1234.  
  1235.  
  1236.  
  1237.  
  1238. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1239. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1240. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1241. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1242. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1243. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1244. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1245. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1246.  
  1247.  
  1248.  
  1249.  
  1250.  
  1251.  
  1252.  
  1253. const kana_keymap = {
  1254. 0: ["わ"],
  1255. 1: ["ぬ"],
  1256. "!": ["ぬ"],
  1257. 2: ["ふ"],
  1258. 3: ["あ"],
  1259. 4: ["う"],
  1260. 5: ["え"],
  1261. 6: ["お"],
  1262. 7: ["や"],
  1263. 8: ["ゆ"],
  1264. 9: ["よ"],
  1265. "-": ["ほ","-"],
  1266. "q": ["た"],
  1267. "Q": ["た"],
  1268. "w": ["て"],
  1269. "W": ["て"],
  1270. "e": ["い"],
  1271. "E": ["い"],
  1272. "r": ["す"],
  1273. "R": ["す"],
  1274. "t": ["か"],
  1275. "T": ["か"],
  1276. "y": ["ん"],
  1277. "Y": ["ん"],
  1278. "u": ["な"],
  1279. "U": ["な"],
  1280. "i": ["に"],
  1281. "I": ["に"],
  1282. "o": ["ら"],
  1283. "O": ["ら"],
  1284. "p": ["せ"],
  1285. "P": ["せ"],
  1286. "a": ["ち"],
  1287. "A": ["ち"],
  1288. "s": ["と"],
  1289. "S": ["と"],
  1290. "d": ["し"],
  1291. "D": ["し"],
  1292. "f": ["は"],
  1293. "F": ["は"],
  1294. "g": ["き"],
  1295. "G": ["き"],
  1296. "h": ["く"],
  1297. "H": ["く"],
  1298. "j": ["ま"],
  1299. "J": ["ま"],
  1300. "k": ["の"],
  1301. "K": ["の"],
  1302. "l": ["り"],
  1303. "L": ["り"],
  1304. "z": ["つ"],
  1305. "Z": ["つ"],
  1306. "x": ["さ"],
  1307. "X": ["さ"],
  1308. "c": ["そ"],
  1309. "C": ["そ"],
  1310. "v": ["ひ"],
  1311. "V": ["ひ"],
  1312. "b": ["こ"],
  1313. "B": ["こ"],
  1314. "n": ["み"],
  1315. "N": ["み"],
  1316. "m": ["も"],
  1317. "M": ["も"],
  1318. ",": ["ね",","],
  1319. "<": ["、"],
  1320. ".": ["る","."],
  1321. ">": ["。"],
  1322. "/": ["め","/"],
  1323. "?": ["・"],
  1324. "#": ["ぁ"],
  1325. "$": ["ぅ"],
  1326. "%": ["ぇ"],
  1327. "'": ["ゃ","’","'"],
  1328. "^": ["へ"],
  1329. "~": ["へ"],
  1330. "&": ["ぉ"],
  1331. "(": ["ゅ"],
  1332. ")": ["ょ"],
  1333. '|': ["ー"],
  1334. "_": ["ろ"],
  1335. "=": ["ほ"],
  1336. "+": ["れ"],
  1337. ";": ["れ"],
  1338. '"': ["ふ","”","“","\""],
  1339. "@": ["゛"],
  1340. '`': ["゛"],
  1341. "[": ["゜"],
  1342. ']': ["む"],
  1343. "{": ["「"],
  1344. '}': ["」"],
  1345. ":": ["け"],
  1346. "*": ["け"]
  1347. }
  1348.  
  1349. const windows_keymap = {
  1350. 'IntlYen': ["ー","¥","\\"],
  1351. "IntlRo": ["ろ","¥","\\"],
  1352. "Space": [" "],
  1353. "Numpad1": [],
  1354. "Numpad2": [],
  1355. "Numpad3": [],
  1356. "Numpad4": [],
  1357. "Numpad5": [],
  1358. "Numpad6": [],
  1359. "Numpad7": [],
  1360. "Numpad8": [],
  1361. "Numpad9": [],
  1362. "Numpad0": [],
  1363. "NumpadDivide": [],
  1364. "NumpadMultiply": [],
  1365. "NumpadSubtract": [],
  1366. "NumpadAdd": [],
  1367. "NumpadDecimal": []
  1368. }
  1369.  
  1370.  
  1371.  
  1372. class SetUp {
  1373.  
  1374. constructor(){
  1375.  
  1376. this.typingMode = 'roma'
  1377. this.enteredClass = 2
  1378. this.battleSwitch = true
  1379. this.soundEffectSwitch = true
  1380.  
  1381. this.checkTypingMode()
  1382. loginFirebase = localStorage.getItem("battle-option") != "false" ? new LoginFirebase() : null
  1383. }
  1384.  
  1385.  
  1386. checkDisplayMenu(){
  1387. const config = {
  1388. childList: true//「子ノード(テキストノードも含む)」の変化
  1389. };
  1390.  
  1391. const Observe = new MutationObserver(function(event){
  1392.  
  1393. setTimeout( () => {
  1394. setUp.createMenu()
  1395. setMutationObserver = new SetMutationObserver()
  1396. })
  1397.  
  1398. Observe.disconnect();
  1399. });
  1400.  
  1401. Observe.observe(document.getElementById("app"), config);
  1402.  
  1403. }
  1404.  
  1405.  
  1406. checkTypingMode(){
  1407.  
  1408. if(location.href.match(/kana\.1/)){
  1409. this.typingMode = "kana"
  1410. this.enteredClass = 1
  1411. }else if(location.href.match(/std\.2/) || location.href.match(/lstn\.4/)){
  1412. this.typingMode = "eng"
  1413. this.enteredClass = 1
  1414. }else{
  1415. this.typingMode = "roma"
  1416. this.enteredClass = 2
  1417. }
  1418.  
  1419. this.checkDisplayMenu()
  1420. }
  1421.  
  1422. createMenu(){
  1423.  
  1424. const NAME = localStorage.getItem("battleName")
  1425. this.battleSwitch = localStorage.getItem("battle-option") == "false" ? false : true;
  1426. this.soundEffectSwitch = localStorage.getItem("sound-option") == "false" ? false : true;
  1427.  
  1428.  
  1429. if(!localStorage.getItem("battle-option")){
  1430. localStorage.setItem("battle-option",'true')
  1431. }
  1432.  
  1433. if(loginFirebase){
  1434. document.getElementById("start_btn").style.display = this.battleSwitch == false ? '' : 'none'
  1435. document.getElementById("start_btn").insertAdjacentHTML('afterend',`<div class='loading'>対戦データベースに接続中</div>`)
  1436. }
  1437.  
  1438. this.addCss()
  1439.  
  1440. const FUNC_VIEW = document.getElementById("func_view")
  1441. setTimeout( () => FUNC_VIEW.style.height = FUNC_VIEW.clientHeight + 30 + "px", 100)
  1442. FUNC_VIEW.insertAdjacentHTML('beforeend' ,
  1443. `<div id='battle-option-container'>
  1444. <label><small>対戦機能</small>
  1445. <input id="battle-option" type="checkbox" style="display:none;" ${this.battleSwitch == false ? "" : "checked"}>
  1446. <div id="battle-effect-btn" style="margin-left:4px;margin-right: 18px;" class="switch_btn"><a class="on_btn btn show">ON</a>
  1447. <a class="off_btn btn" style="display:${this.battleSwitch == false ? "block" : ""};">OFF</a></div>
  1448. </label>
  1449.  
  1450. <label><small>マッチ音</small>
  1451. <input id="sound-option" type="checkbox" style="display:none;" ${this.soundEffectSwitch == false ? "" : "checked"}>
  1452. <div id="sound-effect-btn" style="margin-left:4px;margin-right: 18px;" class="switch_btn"><a class="on_btn btn show">ON</a>
  1453. <a class="off_btn btn" style="display:${this.soundEffectSwitch == false ? "block" : ""};">OFF</a></div>
  1454. </label>
  1455.  
  1456. <input type="button" id="battle-name" value="対戦ネーム変更">
  1457. </div>`)
  1458.  
  1459. if(!NAME){
  1460. localStorage.setItem("battleName" , 'Guest')
  1461. }
  1462.  
  1463.  
  1464. document.getElementById("battle-name").addEventListener("click", event => {
  1465. const NAME = window.prompt("対戦時のユーザー名を入力してください", myData.myName)
  1466.  
  1467. if(NAME){
  1468. localStorage.setItem("battleName" , NAME)
  1469. myData.update()
  1470. }
  1471. })
  1472.  
  1473. document.getElementById("battle-option").addEventListener("change" , event => {
  1474. localStorage.setItem("battle-option" , event.target.checked);
  1475.  
  1476. if(event.target.checked){
  1477. document.querySelector("#battle-effect-btn .off_btn").style.display = ""
  1478. setUp.battleSwitch = true;
  1479.  
  1480. if(!loginFirebase){
  1481. loginFirebase = new LoginFirebase()
  1482. document.getElementById("start_btn").style.display = setUp.battleSwitch == false ? '' : 'none'
  1483. document.getElementById("start_btn").insertAdjacentHTML('afterend',`<div class='loading'>対戦データベースに接続中</div>`)
  1484. }
  1485.  
  1486. }else{
  1487. document.querySelector("#battle-effect-btn .off_btn").style.display = "block"
  1488. setUp.battleSwitch = false;
  1489. }
  1490. })
  1491.  
  1492. document.getElementById("sound-option").addEventListener("change" , event => {
  1493. localStorage.setItem("sound-option" , event.target.checked);
  1494.  
  1495. if(event.target.checked){
  1496. document.querySelector("#sound-effect-btn .off_btn").style.display = ""
  1497. setUp.soundEffectSwitch = true;
  1498.  
  1499. }else{
  1500. document.querySelector("#sound-effect-btn .off_btn").style.display = "block"
  1501. setUp.soundEffectSwitch = false;
  1502. }
  1503. })
  1504.  
  1505. }
  1506.  
  1507. addCss(){
  1508.  
  1509. document.getElementById("app").insertAdjacentHTML('afterend',`<style>
  1510.  
  1511.  
  1512. .loading{
  1513. color: #fff;
  1514. font-size: 12px;
  1515. font-weight: bold;
  1516. background-color: #057fff;
  1517. width: 160px;
  1518. height: 45px;
  1519. margin: 0 auto;
  1520. text-align: center;
  1521. line-height: 45px;
  1522. overflow: hidden;
  1523. border-radius: 3px;
  1524. }
  1525.  
  1526.  
  1527. #battle-option-container{
  1528. display: flex;
  1529. align-items: center;
  1530. justify-content: center;
  1531. }
  1532.  
  1533. #battle-option-container label{
  1534. cursor: pointer;
  1535.  
  1536. }
  1537.  
  1538. #battle-name{
  1539. width: 7rem;
  1540. margin: 2px;
  1541. cursor: pointer;
  1542. }
  1543.  
  1544.  
  1545. .user-table{
  1546. width: 100%;
  1547. position: relative;
  1548. height: 68px;
  1549. left: 0;
  1550. right: 0;
  1551. margin: auto;
  1552. }
  1553.  
  1554. .user-table tr{
  1555. font-weight:bold;
  1556. height: 3rem;
  1557. }
  1558.  
  1559. .user-name{
  1560. font-size: 1rem;
  1561. width: 90px;
  1562. text-align: center;
  1563. }
  1564.  
  1565. .RTCLine{
  1566. max-width: 350px;
  1567. white-space: nowrap;
  1568. overflow:hidden;
  1569. width: 68%;
  1570. color:#ffd0a6;
  1571. font-size: 26px;
  1572. font-weight: normal;
  1573. }
  1574.  
  1575. .InputMode{
  1576. font-size: 1rem;
  1577. width: 95px;
  1578. text-align: center;
  1579. }
  1580.  
  1581. .clear-line{
  1582. font-size: 1rem;
  1583. text-align: center;
  1584. }
  1585.  
  1586. #RTCGamePlayScene{
  1587. margin: 8px;
  1588. margin-top:1rem;
  1589. }
  1590.  
  1591. .display-head{
  1592. margin-left: 0.2rem;
  1593. font-size: 1rem;
  1594. margin-bottom: 0.3rem;
  1595. }
  1596.  
  1597. #battle-display-option{
  1598. margin-left: 4rem;
  1599. display: flex;
  1600. flex-direction: column;
  1601. }
  1602.  
  1603.  
  1604. #battle-progress-bar{
  1605. position: absolute;
  1606. bottom: 1px;
  1607. background-color: #bcbcbc;
  1608. height: 4px;
  1609. transform-origin: left top;
  1610. }
  1611.  
  1612. #user-state-area{
  1613. display: flex;
  1614. justify-content: space-evenly;
  1615. align-items: flex-start;
  1616. margin-top: 1.3rem;
  1617. }
  1618.  
  1619.  
  1620. .user-state-table, .user-state-table td, .user-state-table th{
  1621. border: 1px solid #595959;
  1622. border-collapse: collapse;
  1623. text-align: center;
  1624. }
  1625.  
  1626.  
  1627. .user-state-table td, .user-state-table th {
  1628. padding: 3px;
  1629. width: 100px;
  1630. height: 25px;
  1631.  
  1632. }
  1633.  
  1634. .mine{
  1635. font-weight:bold;
  1636. }
  1637.  
  1638. .ready-btn{
  1639. cursor: pointer;
  1640. display: none;
  1641. position: absolute;
  1642. bottom: -23px;
  1643. width: 130px;
  1644. }
  1645.  
  1646. #ready-button{
  1647. left: 28px;
  1648. font-size: 1.1rem;
  1649. }
  1650.  
  1651. #l-ready-button{
  1652. right: 26px;
  1653. font-size: 0.8rem;
  1654. }
  1655. </style>`)
  1656.  
  1657. }
  1658.  
  1659.  
  1660.  
  1661. }
  1662.  
  1663. const setUp = new SetUp()
  1664.  
  1665. const SoundURL = {
  1666. 'match':'https://dl.dropboxusercontent.com/scl/fi/14ovgqnxribt5yw0bcpzv/match.mp3?rlkey=6lsizg3oda06psbn9zoip1lyd&dl=0',
  1667. 'ready':'https://dl.dropboxusercontent.com/scl/fi/p5u2luqb36st0qt18qkfi/ready.mp3?rlkey=i1ibbnb2mh6q7fmrtjrqw2wqy&dl=0'
  1668.  
  1669.  
  1670. }
  1671. window.AudioContext = window.AudioContext || window.webkitAudioContext;
  1672. class SoundEffect {
  1673.  
  1674. constructor(){
  1675. this.match = new AudioContext();
  1676. this.ready = new AudioContext();
  1677. this.audioBuffer = {}
  1678.  
  1679. this.loadSoundEffect('match')
  1680. this.loadSoundEffect('ready')
  1681.  
  1682. }
  1683.  
  1684. loadSoundEffect(soundName){
  1685. fetch(SoundURL[soundName]).then(function(response) {
  1686. return response.arrayBuffer();
  1687. }).then(function(arrayBuffer) {
  1688. soundEffect[soundName].decodeAudioData(arrayBuffer, function(buffer) {
  1689. soundEffect.audioBuffer[soundName] = buffer;
  1690. });
  1691. })
  1692. }
  1693.  
  1694. play(soundName){
  1695. let playGain = this[soundName].createGain();
  1696. let playSrc = this[soundName].createBufferSource();
  1697.  
  1698. playSrc.buffer = this.audioBuffer[soundName];
  1699. playSrc.connect(playGain);
  1700. playGain.connect(this[soundName].destination);
  1701. playGain.gain.value = 0.2
  1702. playSrc.start(0);
  1703. }
  1704.  
  1705.  
  1706. }
  1707. let soundEffect
  1708.  
  1709.  
  1710.  
  1711.