var SCREEN_WIDTH = 256; var SCREEN_HEIGHT = 240; var canvas_ctx, image; var framebuffer_u8, framebuffer_u32; var SAMPLE_COUNT = 4 * 1024; var SAMPLE_MASK = SAMPLE_COUNT - 1; var audio_samples_L = new Float32Array(SAMPLE_COUNT); var audio_samples_R = new Float32Array(SAMPLE_COUNT); function getParameter(t) { var e = window.location.search, i = new RegExp(t + "=([^&?]*)", "ig"); return e.match(i) ? e.match(i)[0].substr(t.length + 1) : null } var netmode = !!getParameter("roomId"); var gameId = getParameter("id"); var worker = new Worker('./js/nes-worker.js'); worker.postMessage({ f: "netmode", data: netmode }); var KEY_MAP = { ST: 13, SE: 9, A: 65, B: 83, Y: 50, X: 49, AB: [65, 83], UP: 38, DOWN: 40, LEFT: 37, RIGHT: 39, L_D: [37, 40], L_U: [37, 38], R_D: [39, 40], R_U: [39, 38] }; var player = 1; window.vm = { oldDire: null, keyEvent: function (t, e, i) { var r = KEY_MAP[t]; Array.isArray(r) ? (vm.simulateKeyPress(r[0], e), vm.simulateKeyPress(r[1], e)) : vm.simulateKeyPress(r, e) }, initCtrl: function () { var s; vm.initGameDireCtrl(function (t, e) { var i = -1 == t ? s : ["DOWN", "L_D", "LEFT", "L_U", "UP", "R_U", "RIGHT", "R_D"][t], r = -1 == t; s && (s != i || r) && vm.keyEvent(s, "keyup", e), s = i, !r && vm.keyEvent(i, "keydown", e) }), vm.initGameKeyCtrl(), $(".ctrl-wrap").on("click contextmenu touchstart touchmove touchend touchcancel tap", function (t) { return t.stopPropagation(), t.preventDefault(), !1 }) }, initGameDireCtrl: function (u) { var d = this, t = $(".dire-wrap"), p = $(".dc-centre"), e = $(".dire-ctrl"), l = $(".dc-dire"), i = e.offset(), n = i.top, a = i.left, f = p.width() / 2, m = e.width() / 2, r = function () { p.css({ transform: "translate3d(83px,83px,0)" }), p.css("opacity", "1"), l.css("opacity", "0") }, y = function (t, e) { var i = t[e]; if (!i) return { x: 160, y: 500 }; var r = i.pageX - a, s = i.pageY - n; return 600 < s ? y(t, e + 1) : { x: r, y: s } }; r(), t.on("touchstart touchmove", function (t) { var e, i, r = y(t.touches, 0), s = r.x, n = r.y, a = Math.atan2(n - m, s - m) / (Math.PI / 180); if (l.css("transform", "rotate(" + a + "deg)"), d.onCtrlDire(a, u), (e = m - s, i = m - n, Math.sqrt(e * e + i * i)) <= 86) { var o = s - f, h = n - f; p.css("transform", "translate3d(" + o + "px," + h + "px,0)") } else { var c = a * Math.PI / 180; o = m + 86 * Math.cos(c) - f, h = m + 86 * Math.sin(c) - f; p.css("transform", "translate3d(" + o + "px," + h + "px,0)") } "touchstart" === t.type && (p.css("opacity", "0.8"), l.css("opacity", "0.6")) }).on("touchend", function () { r(), u(-1, 0), d.oldDire = -1 }) }, initGameKeyCtrl: function () { var s = this; $(".key-btn").bind("touchstart touchend", function (t) { var e = $(t.target), i = e.attr("data-key"), r = "touchstart" === t.type; s.keyEvent(i, r ? "keydown" : "keyup", 0), e[r ? "addClass" : "removeClass"]("btn-on") }) }, onCtrlDire: function (t, e) { this.onCtrlDire; var i;//= Math.round((t + 180) / 45) % 8; var abst = Math.abs(t); if ((abst >= 35 && abst <= 55) || (abst >= 125 && abst <= 145)) { i = Math.round((t + 180) / 45) % 8; } else { i = Math.round((t + 180) / 90) % 4 * 2; } this.oldDire != i && (e(i, t), this.oldDire = i) }, simulateKeyPress: function (t, e) { if (!netmode) { worker.postMessage({ f: "keypad", data: {keyCode: t, action: e} }); } else { var data = { m: NetEnum.JUMP, v: { keyCode: t, keyaction: e, player: player } } NetWorkManage.sendFrameEvent(data); } } }; function startGame() { var path = "https://h5games-al.kingsome.cn/emulator-static/roms/" + gameId + ".nes" nes_load_url("screen", path) vm.initCtrl() if (netmode) { var roomId = getParameter("roomId") NetWorkManage.initengine(); NetWorkManage.cbOnLogin = function (res) { if (res.status === 0) { console.log(res) NetWorkManage.cbOnLogin = null; if (roomId === "0") { NetWorkManage.engine.createRoom('testroom', 2, ""); } else { player = 2; NetWorkManage.engine.joinRoom(roomId, ""); } } } NetWorkManage.cbOnJoinRoom = function (res) { NetWorkManage.gameStart(); }; var updateframe = function () { if (NetWorkManage.frames.length > 0) { var fr = NetWorkManage.frames.shift(); if (fr.isnet) { NetWorkHandle.processFrame(fr); } worker.postMessage({f: "updateframe"}); if (fr.frameIndex < NetWorkManage.frameIndex) { updateframe(); } } } setInterval(function () { updateframe(); }, 1000 / 60) } } function checkSession_id(res) { try { if (JSON.parse(res.target.response).errcode === 0) { startGame(); } } catch (e) { } } window.onload = function () { var noCheck = getParameter('nt'); if (noCheck) { startGame() } else { var account_id = getParameter("account_id") var session_id = getParameter("session_id") var url; if (getParameter("version") === "product") { url = "https://login.kingsome.cn/webapp/index.php?c=Login&a=sessionAuth" } else { url = "https://login-test.kingsome.cn/webapp/index.php?c=Login&a=sessionAuth" } url += "&account_id="; url += account_id; url += "&session_id="; url += session_id; var ajax = new XMLHttpRequest(); ajax.onload = checkSession_id; ajax.open("GET", url, true); ajax.send(null); } } var script_processor = null var audio_ctx = null function initAudioEvent() { script_processor && (script_processor.disconnect(audio_ctx.destination), script_processor.onaudioprocess = null, script_processor = null) audio_ctx && audio_ctx.close().catch(handleError) window.AudioContext = window.webkitAudioContext || window.AudioContext audio_ctx = new window.AudioContext(); script_processor = audio_ctx.createScriptProcessor(4096, 0, 2); script_processor.onaudioprocess = audio_callback; script_processor.connect(audio_ctx.destination); $(".key-btn").unbind("touchstart", initAudioEvent) } function audio_callback(e) { var dst = e.outputBuffer; var len = dst.length; var dst_l = dst.getChannelData(0); var dst_r = dst.getChannelData(1); for (var i = 0; i < len; i++) { var src_idx = (audio_read_cursor + i) & SAMPLE_MASK; dst_l[i] = audio_samples_L[src_idx]; dst_r[i] = audio_samples_R[src_idx]; } audio_read_cursor = (audio_read_cursor + len) & SAMPLE_MASK; } function nes_init(canvas_id) { var canvas = document.getElementById(canvas_id); canvas_ctx = canvas.getContext("2d"); image = canvas_ctx.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); canvas_ctx.fillStyle = "black"; canvas_ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); var buffer = new ArrayBuffer(image.data.length); worker.postMessage({ f: "setBuffer", data: image.data.length }); framebuffer_u8 = new Uint8ClampedArray(buffer); framebuffer_u32 = new Uint32Array(buffer); $(".key-btn").bind("touchstart", initAudioEvent) } function nes_boot(rom_data) { worker.postMessage({ f: "nesloadROM", data: rom_data }); } function nes_load_url(canvas_id, path) { nes_init(canvas_id); var req = new XMLHttpRequest(); req.open("GET", path); req.overrideMimeType("text/plain; charset=x-user-defined"); req.onerror = () => console.log(`Error loading ${path}: ${req.statusText}`); req.onload = function () { if (this.status === 200) { nes_boot(this.responseText); } else if (this.status === 0) { // Aborted, so ignore error } else { req.onerror(); } }; req.send(); } worker.onmessage = function (event) { if (event.data.f === "frame") { // framebuffer_u8 = event.data.data image.data.set(event.data.data.framebuffer_u8); canvas_ctx.putImageData(image, 0, 0); audio_samples_L = event.data.data.audio_samples_L audio_samples_R = event.data.data.audio_samples_R audio_read_cursor = event.data.data.audio_read_cursor } else if (event.data.f === 'saveData') { let data = zip(event.data.data); console.log(data); localStorage.setItem(gameId, data); } } document.addEventListener('keydown', (event) => { console.log(event.keyCode); worker.postMessage({ f: "keypad", data: {keyCode: event.keyCode, action: 'keydown'} }); }); document.addEventListener('keyup', (event) => { if (event.keyCode === 83) { //save worker.postMessage({ f: "save", }); }else if (event.keyCode === 82) { //save worker.postMessage({ f: "reload", }); } else if (event.keyCode === 76) { //load var saveData = localStorage.getItem(gameId); if( saveData == null ) { console.log("nothing to load"); alert("请先保存."); return; } let unzipData = unzip(saveData); var decodedData = JSON.parse(unzipData); worker.postMessage({ f: "load", data: decodedData }); } worker.postMessage({ f: "keypad", data: {keyCode: event.keyCode, action: 'keyup'} }); }); function unzip(binaryString) { return unescape(pako.inflate(binaryString, { to: 'string' })); } // 压缩 function zip(str) { //escape(str) --->压缩前编码,防止中午乱码 var binaryString = pako.gzip(escape(str), {to: 'string'}); return binaryString; }