374 lines
9.7 KiB
JavaScript
374 lines
9.7 KiB
JavaScript
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;
|
|
}
|