emulator/fc2/js/nes-embed.js
2019-06-11 15:27:11 +08:00

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;
}