Update new frontend (#38)

* WIP

* fix crlf

* fix crlf

* new gesture, menu, tooltip, share

* WIP share

* clear dusties and put magic

* oops

* Remove fade

* Update js behaviour
This commit is contained in:
trichimtrich 2019-05-25 13:13:06 +08:00 committed by giongto35
parent 48d6ddbf6d
commit 4721db201e
52 changed files with 2865 additions and 7802 deletions

14
.gitattributes vendored
View file

@ -1,2 +1,16 @@
* linguist-vendored
*.go linguist-vendored=false
* text=auto eol=lf
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text
# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary

View file

@ -71,9 +71,9 @@ func main() {
rand.Seed(time.Now().UTC().UnixNano())
//if *config.IsMonitor {
go monitor()
//}
if *config.IsMonitor {
go monitor()
}
// There are two server mode
// Overlord is coordinator. If the OvelordHost Param is `overlord`, we spawn a new host as Overlord.
// else we spawn new server as normal server connecting to OverlordHost.

View file

@ -13,7 +13,7 @@
├── static: static file for front end
│ ├── js
│ │ └── ws.js: client logic
│ ├── gameboy.html: frontend with gameboy ui
│ ├── game.html: frontend with gameboy ui
│ └── index_ws.html: raw frontend without ui
├── cws
│ └── cws.go: socket multiplexer library, used for signalling

View file

@ -15,8 +15,8 @@ import (
)
const (
gameboyIndex = "./static/gameboy2.html"
debugIndex = "./static/gameboy2.html"
gameboyIndex = "./static/game.html"
debugIndex = "./static/game.html"
gamePath = "games"
)

2086
static/css/font-awesome.css vendored Normal file

File diff suppressed because it is too large Load diff

6921
static/css/gameboy.css vendored

File diff suppressed because it is too large Load diff

View file

@ -1,137 +0,0 @@
.abxy {
position: absolute;
width: 161px;
height: 160px;
top: 125px;
left: 451px
}
.abxy .button {
position: absolute;
width: 54px;
height: 54px;
}
.abxy .button.a {
width: 53px;
height: 53px;
}
.abxy .button.y {
width: 55px;
height: 54px;
}
.abxy .button.pressed {
background-position: 0 -55px;
margin-top: 6px;
opacity: 1;
}
.abxy .button.pressed.a {
background-position: 0 -54px;
}
.abxy .button.pressed.y {
background-position: 0 -56px;
}
.abxy .a {
background: url(https://gamepadviewer.com/xbox-assets-old/a.png);
top: 108px;
left: 55px;
}
.abxy .b {
background: url(https://gamepadviewer.com/xbox-assets-old/b.png);
top: 54px;
right: 0px;
}
.abxy .x {
background: url(https://gamepadviewer.com/xbox-assets-old/x.png);
top: 54px;
}
.abxy .y {
background: url(https://gamepadviewer.com/xbox-assets-old/y.png);
left: 54px;
}
.dpad {
background: url(https://gamepadviewer.com/xbox-assets-old/bg.png);
background-position: -174px -273px;
border-radius: 100%;
position: absolute;
width: 112px;
height: 112px;
top: 300px;
left: 451px;
}
.dpad .face {
position: absolute;
font-size: 30px;
line-height: 0;
color: white;
opacity: 0;
font-family: 'FontAwesome';
}
.dpad .face.pressed {
opacity: 1;
}
.dpad .face.up {
left: 42px;
top: 20px;
}
.dpad .face.up:after {
content: "\f062";
}
.dpad .face.down {
left: 42px;
bottom: 20px;
}
.dpad .face.down:after {
content: "\f063";
}
.dpad .face.left {
top: 56px;
left: 3px;
}
.dpad .face.left:after {
content: "\f060";
}
.dpad .face.right {
top: 56px;
right: 3px;
}
.dpad .face.right:after {
content: "\f061";
}
.dpad .joystick {
background-color: blue;
border-radius: 100%;
opacity: 0.5;
cursor: pointer;
height: 50%;
user-select: none;
width: 50%;
position: absolute;
top: 50%;
left: 50%;
margin-top: -28px;
margin-left: -28px;
}

201
static/css/main.css vendored
View file

@ -1,5 +1,5 @@
body {
background-image: url('https://t3.ftcdn.net/jpg/02/41/14/36/500_F_241143639_RW6VdHbqYRM0yfNpDxDlWpj0uXWglAC2.jpg');
background-image: url('/static/img/500_F_241143639_RW6VdHbqYRM0yfNpDxDlWpj0uXWglAC2.jpg');
background-repeat: repeat;
}
@ -153,7 +153,7 @@ body {
}
.btn-big {
.btn.big {
display: block;
padding-top: 5px;
text-align: center;
@ -335,126 +335,141 @@ body {
#game-screen {
overflow: hidden;
width: 100%;
height: 100%;
display: none;
background-color: #222222;
}
#menu-screen {
display:none;
width: 100%;
height: 100%;
background-image: url('https://data.whicdn.com/images/275493408/original.gif');
position: relative; /*must not static*/
display: block;
overflow: hidden;
width: 256px;
height: 240px;
/* background-color: gray; */
background-image: url('/static/img/pixel_waterfall_bg__by_isohei-d4xntof.gif');
background-size: cover;
}
#menu-screen #box-art {
position: absolute;
width:55%;
height: 85%;
top: 5px;
left: 50%;
-webkit-transform: translate(-50%, -0%);
transform: translate(-50%, -0%);
border: 1px solid #ddd;
#menu-item-choice {
display: block;
position: absolute;
width: 100%;
height: 36px;
background-color: lightgreen;
opacity: 0.75;
top: 50%;
left: 0;
transform: translateY(-50%);
}
#menu-container {
display: block;
position: absolute;
/* background-color: red; */
width: 100%;
top: 102px; /* 240px - 36 / 2 */
left: 0;
}
@font-face {
font-family: 'YourFontName'; /*a name to be used later*/
src: url('/static/fonts/8-Bit-Madness.ttf'); /*URL to font*/
src: url('/static/fonts/6809 chargen.ttf'); /*URL to font*/
}
#menu-screen #title {
position: absolute;
width:100%;
height: 10%;
bottom: 0px;
font-family: 'YourFontName';
overflow: hidden;
}
.menu-item {
display: block;
position: relative;
#menu-screen #title p {
position: absolute;
width: 100%;
height: 100%;
margin: 0;
text-align: center;
/* Starting position */
-moz-transform:translateX(50%);
-webkit-transform:translateX(50%);
transform:translateX(50%);
height: 36px; /* 35 + 1 border = 36px */
/* Apply animation to this element */
-moz-animation: horizontally 5s linear infinite alternate;
-webkit-animation: horizontally 5s linear infinite alternate;
animation: horizontally 5s linear infinite alternate;
font-family: 'YourFontName';
font-size: 19px;
/* border-top: 1px dashed blue; */
}
/* Move it (define the animation) */
.menu-item div {
overflow: hidden;
display: block;
position: absolute;
left: 15px;
top: 5px;
width: 226px;
height: 25px;
/* background-color: yellow; */
}
.menu-item div span {
position: absolute;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
text-align: center;
}
.menu-item div .pick {
overflow: unset;
-moz-animation: horizontally 4s linear infinite alternate;
-webkit-animation: horizontally 4s linear infinite alternate;
animation: horizontally 4s linear infinite alternate;
}
@-moz-keyframes horizontally {
0% { -moz-transform: translateX(50%); }
100% { -moz-transform: translateX(-50%); }
0% { transform: translateX(0%); }
25% { transform: translateX(-20%); }
50% { transform: translateX(0%); }
75% { transform: translateX(20%); }
100% { transform: translateX(0%); }
}
@-webkit-keyframes horizontally {
0% { -webkit-transform: translateX(50%); }
100% { -webkit-transform: translateX(-50%); }
0% { transform: translateX(0%); }
25% { transform: translateX(-20%); }
50% { transform: translateX(0%); }
75% { transform: translateX(20%); }
100% { transform: translateX(0%); }
}
@keyframes horizontally {
0% { transform: translateX(50%); }
100% { transform: translateX(-50%); }
0% { transform: translateX(0%); }
25% { transform: translateX(-20%); }
50% { transform: translateX(0%); }
75% { transform: translateX(20%); }
100% { transform: translateX(0%); }
}
#menu-screen .arrow {
position: absolute;
width:20px;
height: 20px;
right: 10px;
top: 50%;
-moz-transform: translate(0%, -50%);
-webkit-transform: translate(0%, -50%);
transform: translate(0%, -50%);
#noti-box {
display: none;
padding: 5px 10px 0 10px;
height: 20px;
background-color: #ddd;
opacity: 0.8;
position: absolute;
-moz-animation: breathing 1s infinite normal;
-webkit-animation: breathing 1s infinite normal;
animation: breathing 1s infinite normal;
}
border: 5px #000;
border-radius: 30px;
left: 50%;
transform: translateX(-50%);
bottom: 35px;
#menu-screen .left {
left: 10px;
-webkit-transform: scaleX(-1) translate(0%, -50%);
-moz-transform: scaleX(-1) translate(0%, -50%);
transform: scaleX(-1) translate(0%, -50%);
-webkit-animation: breathing 1s infinite normal;
-moz-animation: breathing 1s infinite normal;
animation: breathing 1s infinite normal;
}
@-webkit-keyframes breathing {
0% { opacity: 1; }
25% { opacity: 0.5; }
50% { opacity: 0; }
75% { opacity: 0.5; }
100% { opacity: 1; }
}
@-moz-keyframes breathing {
0% { opacity: 1; }
25% { opacity: 0.5; }
50% { opacity: 0; }
75% { opacity: 0.5; }
100% { opacity: 1; }
}
@keyframes breathing {
0% { opacity: 1; }
25% { opacity: 0.5; }
50% { opacity: 0; }
75% { opacity: 0.5; }
100% { opacity: 1; }
}
font-size: 12px;
}

BIN
static/fonts/6809 chargen.ttf vendored Executable file

Binary file not shown.

View file

@ -5,8 +5,16 @@
<meta name="viewport" content="user-scalable=0">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.css" rel="stylesheet">
<meta property="og:type" content="cloud-game" />
<meta property="og:title" content="Cloud Gaming - NES Emulator" />
<meta property="og:description" content="Play and share cloud gaming experience with your friends" />
<meta property="og:image" content="" />
<meta property="og:url" content="" />
<meta property="og:site_name" content="" />
<meta property="og:author" content="giongto35 trichimtrich" />
<link href="/static/css/font-awesome.css" rel="stylesheet">
<link href="/static/css/main.css" rel="stylesheet">
</head>
@ -22,23 +30,22 @@
</div>
<div id="bottom-screen">
<video id="game-screen" autoplay=true poster="https://orig00.deviantart.net/cdcd/f/2017/276/a/a/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif"></video>
<video id="game-screen" autoplay=true
poster="/static/img/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif"></video>
<div id="menu-screen">
<img id="box-art" src="/static/img/404.jpg" onerror="this.src='/static/img/404.jpg'">
<div id="title" unselectable="on" class="unselectable" >
<p>What the game ?!?</p>
</div>
<img class="arrow left" src="/static/img/arrow2_e.gif">
<img class="arrow right" src="/static/img/arrow2_e.gif">
<div id="menu-container">
</div>
<div id="menu-item-choice"></div>
</div>
</div>
<div id="btn-load" unselectable="on" class="btn-big unselectable" value="load">load</div>
<div id="btn-save" unselectable="on" class="btn-big unselectable" value="save">save</div>
<div id="btn-join" unselectable="on" class="btn-big unselectable" value="join">start</div>
<div id="btn-quit" unselectable="on" class="btn-big unselectable" value="quit">quit</div>
<div id="btn-load" unselectable="on" class="btn big unselectable" value="load">load</div>
<div id="btn-save" unselectable="on" class="btn big unselectable" value="save">save</div>
<div id="btn-join" unselectable="on" class="btn big unselectable" value="join">play</div>
<div id="btn-quit" unselectable="on" class="btn big unselectable" value="quit">quit</div>
<div id="color-button-holder">
@ -54,7 +61,10 @@
<div id="light-3"></div>
</div>
<input id="room-txt" type="text" placeholder="room id..." unselectable="on" class=" unselectable">
<!-- TODO: remove -->
<input id="room-txt" type="text" placeholder="room id..." unselectable="on" class=" unselectable" disabled>
<div id="noti-box" unselectable="on" class="unselectable">Oh my god</div>
</div>
@ -63,7 +73,7 @@
DEBUG = true;
</script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="/static/js/jquery-3.3.1.min.js"></script>
<script src="/static/js/utils.js"></script>
<!-- https://rawgit.com/Rillke/opus.js-sample/master/index.xhtml -->
@ -77,6 +87,8 @@
<script src="/static/js/gesture_joystick.js"></script>
<script src="/static/js/ws.js"></script>
<script src="/static/js/init.js"></script>
</body>
</html>

127
static/gameboy.html vendored
View file

@ -1,127 +0,0 @@
<head>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
<link type="text/css" rel="stylesheet" href="/static/css/gameboy.css">
</head>
<body scroll="no" style="overflow: hidden">
<div id="gameboy" class="green">
<div id="canvas"></div>
<div id="border"></div>
<div id="border-top"></div>
<div id="border-left"></div>
<div id="border-bottom"></div>
<div id="border-right"></div>
<div id="screw-small-right" class="screw small"></div>
<div id="screw-small-left" class="screw small"></div>
<div id="screw-large-right" class="screw large"></div>
<div id="screw-large-left" class="screw large"></div>
<div id="backboard"></div>
<div id="motherboard"></div>
<div id="motherboard-capacitors" class="capacitors"></div>
<div id="chip-short" class="chip"></div>
<div id="chip-diagonal" class="chip"></div>
<div id="chip-tall" class="chip"></div>
<div id="chip-capacitors" class="capacitors"></div>
<div id="contrast-knob"></div>
<div id="link-port"></div>
<div id="circuit-bottom" class="circuit"></div>
<div id="circuit-top" class="circuit"></div>
<div id="transistors"></div>
<div id="processor"></div>
<div id="component"></div>
<div id="controller"></div>
<div id="speaker"></div>
<div id="whitescreen"></div>
<div id="glass"></div>
<div id="glass-gameboy-text">GAME BOY</div>
<div id="glass-color-text">C</div>
<div id="screen">
<!--<video id="game-screen" autoplay=true poster="https://orig00.deviantart.net/cdcd/f/2017/276/a/a/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif">-->
<video id="game-screen" autoplay=true>
</video>
<div id="menu-screen">
<img id="box-art" src="/static/img/boxarts/Contra (USA).png">
<div id="title">
<p>Contra</p>
</div>
<img class="arrow left" src="/static/img/arrow2_e.gif">
<img class="arrow right" src="/static/img/arrow2_e.gif">
</div>
</div>
<div id="screen-gameboy-text">GAME BOY</div>
<div id="screen-nintendo-text">Nintendo</div>
<div id="joystick-pad"></div>
<div id="joystick">.</div>
<div id="control"></div>
<div id="control-b" class="control-button">B</div>
<div id="control-a" class="control-button">A</div>
<div id="start-select-box"></div>
<div id="start-select-button"></div>
<div id="cover-vertical"></div>
<div id="cover-horizontal"></div>
<div id="gloss"></div>
<div id="speaker-holes"></div>
<div id="power"></div>
</div>
<div id="colors">
<div class="color" data-color="red"></div>
<div class="color" data-color="purple"></div>
<div class="color" data-color="green"></div>
<div class="color" data-color="yellow"></div>
<div class="color" data-color="teal"></div>
<div class="color" data-color="transparent"></div><br><br>
<div>Animation powered by bchanx <a href="http://bchanx.com/animated-gameboy-in-css-blog"><i class="fas fa-blog"></i></a> <a href="https://github.com/bchanx/animated-gameboy-in-css"><i class="fab fa-github"></i></a></div><br>
<div>Rocked by <a href="https://github.com/giongto35">giongto35</a> <i class="far fa-handshake"></i> <a href="https://trich.im">trichimtrich</a></div>
</div>
<div style="position:absolute; top:17%; left: 5%;">
<h3>Instruction</h3>
<div>
Menu:
Left, Right to browse the game list <br />
V to select the game <br />
F to go fullscreen, Escape to exit <br />
<br />
Game: <br />
Use Up, Down, Left, Right to Move <br />
Z (A butotn) <br />
X (B button) <br />
C is start (or pause in some games) <br />
V is select game <br />
Q is super quit <br />
S to save <br />
L to load <br />
<br />
Your current room: <b><label id="currentRoomID" style="color:white"></b> <br />
You can join a remote game by roomID.<br />
Remote room ID: <input type="text" id="roomID"><br />
Play as player(1,2): <select id="playerIndex">
<option value="1">1</option>
<option value="2">2</option>
</select>
<button id="play" onclick="startGame()">Join</button>
<!--Fullscreen media for better gaming experience<br /-->
</div><br>
<h3>Log:</h3>
<pre id="div"></pre>
<div>
<i class="fas fa-gamepad"></i> <u><i>Refresh to retry when checking is too long</i></u>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="/static/js/const.js"></script>
<script src="/static/js/global.js"></script>
<script src="/static/js/gameboy_loader.js"></script>
<script src="/static/js/gameboy_controller.js"></script>
<script src="/static/js/ws.js"></script>
</body>

BIN
static/img/1478923556_2_(22).gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
static/img/404.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 441 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
static/img/original.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

102
static/index_ws.html vendored
View file

@ -1,102 +0,0 @@
<html>
<head>
<!-- <meta name="viewport" content="width=device-width, user-scalable=no" /> -->
<meta name="viewport" content="user-scalable=no" />
<link href="//netdna.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.css" rel="stylesheet">
<link href="/static/css/joystick.css" rel="stylesheet">
<style>
textarea {
width: 60%;
height: 50px;
}
</style>
</head>
<body>
<select id="gameOp">
</select>
<button id="play" onclick="window.startGame()">Play</button>
<button id="play" onclick="pc.close()">Stop</button>
Your current room: <b><label id="currentRoomID" style="color:blue"></b> <br />
You can join a remote game by roomID.<br />
Room ID: <input type="text" id="roomID">
Play as player(1,2): <select id="playerIndex">
<option value="1">1</option>
<option value="2">2</option>
</select>
<br /><br />
<div id="remoteVideos">
<video id="game-screen" autoplay=true width=400 height=300
poster="https://orig00.deviantart.net/cdcd/f/2017/276/a/a/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif">
</div> <br />
<h3>Instruction</h3>
<div>
C is start button. Use it to start game<br />
V is select button <br />
Player 1
Use Up, Down, Left, Right to Move <br />
Z to (A) <br />
X to (B) <br />
S to save <br />
L to load <br />
<!--Fullscreen media for better gaming experience<br /-->
</div><br>
<h3>Log:</h3>
<pre id="div"></pre>
<div>
🎮<u><i>Refresh to retry when checking is too long</i></u>
</div>
<!-- virtual controller -->
<div id="dpad" class="dpad">
<span class="face up"></span>
<span class="face down"></span>
<span class="face left"></span>
<span class="face right"></span>
</div>
<div class="abxy">
<span value="a" class="button a"></span>
<span value="b" class="button b"></span>
<span value="start" class="button x"></span>
<span value="select" class="button y"></span>
</div>
<script>
DEBUG = true;
</script>
<!-- https://rawgit.com/Rillke/opus.js-sample/master/index.xhtml -->
<script src="/static/js/libopus.js"></script>
<script src="/static/js/opus.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="/static/js/const.js"></script>
<script src="/static/js/global.js"></script>
<script src="/static/js/gameboy_controller.js"></script>
<script src="/static/js/ws.js"></script>
<script>
$("#gameOp").on("change", function () {
gameIdx = gameOp.selectedIndex;
});
gameIdx = 1;
</script>
</body>
</html>

View file

@ -1,63 +1,106 @@
// menu screen
function showMenuScreen() {
log("Clean up connection / frame");
/*
Menu Controller
*/
function reloadGameMenu() {
log("Load game menu");
// sort gameList first
gameList.sort(function (a, b) {
return a.name > b.name ? 1 : -1;
});
// generate html
var listbox = $("#menu-container");
listbox.html('');
gameList.forEach(function (game) {
listbox.append(`<div class="menu-item unselectable" unselectable="on"><div><span>${game.name}</span></div></div>`);
});
}
function showMenuScreen() {
// clear scenes
$("#game-screen").hide();
$("#menu-screen").hide();
// show
$("#game-screen").show().delay(DEBUG?0:0).fadeOut(0, () => {
$("#btn-save").hide();
$("#btn-load").hide();
$("#btn-join").html("play");
// show menu scene
$("#game-screen").show().delay(DEBUG ? 0 : 500).fadeOut(DEBUG ? 0 : 0, function () {
log("Loading menu screen");
$("#menu-screen").fadeIn(0, () => {
chooseGame(gameIdx, true);
$("#menu-screen").fadeIn(DEBUG ? 0 : 0, function () {
pickGame(gameIdx);
screenState = "menu";
});
});
}
// game menu
function chooseGame(idx, force = false) {
if (idx < 0 || (idx == gameIdx && !force) || idx >= gameList.length) return false;
function pickGame(idx) {
// check boundaries
// cycle
if (idx < 0) idx = gameList.length - 1;
if (idx >= gameList.length) idx = 0;
$("#menu-screen #box-art").fadeOut(DEBUG?0:0, function () {
$(this).attr("src", `/static/img/boxarts/${gameList[idx].name}.png`);
$(this).fadeIn(0, function () {
$("#menu-screen #title p").html(gameList[idx].name);
});
});
// transition menu box
if (idx == 0) {
$("#menu-screen .left").hide();
} else {
$("#menu-screen .left").show();
}
var listbox = $("#menu-container");
listbox.css("transition", "top 0.2s");
listbox.css("-moz-transition", "top 0.2s");
listbox.css("-webkit-transition", "top 0.2s");
if (idx == gameList.length - 1) {
$("#menu-screen .right").hide();
} else {
$("#menu-screen .right").show();
}
menuTop = MENU_TOP_POSITION - idx * 36;
listbox.css("top", `${menuTop}px`);
// overflow marquee
$(".menu-item .pick").removeClass("pick");
$(`.menu-item:eq(${idx}) span`).addClass("pick");
gameIdx = idx;
log(`> [Pick] game ${gameIdx + 1}/${gameList.length} - ${gameList[gameIdx].name}`);
}
// global func
function startGamePickerTimer(direction) {
if (gamePickerTimer === null) {
pickGame(gameIdx + (direction === "up" ? -1 : 1));
log("Start game picker timer");
// velocity?
gamePickerTimer = setInterval(function () {
pickGame(gameIdx + (direction === "up" ? -1 : 1));
}, 200);
}
}
function stopGamePickerTimer() {
if (gamePickerTimer !== null) {
log("Stop game picker timer");
clearInterval(gamePickerTimer);
gamePickerTimer = null;
}
}
function sendInputData() {
// prepare key
/*
Game controller
*/
function sendKeyState() {
// check if state is changed
if (unchangePacket > 0) {
bits = "";
// pack keystate
var bits = "";
KEY_BIT.slice().reverse().forEach(elem => {
bits += keyState[elem] ? 1 : 0;
});
data = parseInt(bits, 2);
var data = parseInt(bits, 2);
console.log(`Key state string: ${bits} ==> ${data}`);
// send
arrBuf = new Uint8Array(1);
// send packed keystate
var arrBuf = new Uint8Array(1);
arrBuf[0] = data;
inputChannel.send(arrBuf);
@ -66,21 +109,26 @@ function sendInputData() {
}
function startInputTimer() {
if (inputTimer == null) {
inputTimer = setInterval(sendInputData, 1000 / INPUT_FPS)
function startGameInputTimer() {
if (gameInputTimer === null) {
log("Start game input timer");
gameInputTimer = setInterval(sendKeyState, 1000 / INPUT_FPS)
}
}
function stopInputTimer() {
clearInterval(inputTimer);
inputTimer = null;
function stopGameInputTimer() {
if (gameInputTimer !== null) {
log("Stop game input timer");
clearInterval(gameInputTimer);
gameInputTimer = null;
}
}
function setState(name, bo) {
function setKeyState(name, state) {
if (name in keyState) {
keyState[name] = bo;
keyState[name] = state;
unchangePacket = INPUT_STATE_PACKET;
}
}
@ -88,9 +136,12 @@ function setState(name, bo) {
function doButtonDown(name) {
$(`#btn-${name}`).addClass("pressed");
if (screenState === "game") {
// game keys
setState(name, true);
if (screenState === "menu") {
if (name === "up" || name === "down") {
startGamePickerTimer(name);
}
} else if (screenState === "game") {
setKeyState(name, true);
}
}
@ -99,34 +150,29 @@ function doButtonUp(name) {
$(`#btn-${name}`).removeClass("pressed");
if (screenState === "menu") {
switch (name) {
case "left":
chooseGame(gameIdx - 1);
break;
case "right":
chooseGame(gameIdx + 1);
break;
case "join":
case "a":
case "b":
case "start":
case "select":
startGame();
// log("select game");
break;
if (name === "up" || name === "down") {
stopGamePickerTimer();
} else if (name === "join" || name === "a" || name === "b" || name === "start" || name === "select") {
startGame();
//log("select game");
}
} else if (screenState === "game") {
setState(name, false);
setKeyState(name, false);
switch (name) {
case "join":
copyToClipboard(window.location.href.split('?')[0] + `?id=${roomID}`)
popup("Copy link to clipboard!")
break;
case "save":
conn.send(JSON.stringify({ "id": "save", "data": "" }));
break;
case "load":
conn.send(JSON.stringify({ "id": "load", "data": "" }));
break;
case "full":
// Fullscreen
screen = document.getElementById("game-screen");
@ -138,18 +184,17 @@ function doButtonUp(name) {
openFullscreen(screen);
}
break;
}
}
case "quit":
stopGameInputTimer();
showMenuScreen();
// TODO: Stop game
conn.send(JSON.stringify({ "id": "quit", "data": "", "room_id": roomID }));
// global reset
if (name === "quit") {
stopInputTimer();
showMenuScreen();
// TODO: Stop game
screen = document.getElementById("game-screen");
room_id = $("#room-txt").val()
conn.send(JSON.stringify({ "id": "quit", "data": "", "room_id": room_id}));
$("#room-txt").val("");
$("#room-txt").val("");
popup("Quit!");
break;
}
}
}

View file

@ -1,5 +1,6 @@
// JOYSTICK
/*
Joystick gesture
*/
/*
cross == a <--> a
@ -13,104 +14,122 @@
dpad <--> up down left right
axis 0, 1 <--> second dpad
*/
var padState, gamepadTimer;
let joystickMap;
let joystickState;
let joystickIdx;
let joystickTimer = null;
// only capture the last plugged joystick
window.addEventListener("gamepadconnected", (e) => {
gamepad = e.gamepad;
// check state for each axis -> dpad
function checkJoystickAxisState(name, state) {
if (joystickState[name] !== state) {
joystickState[name] = state;
if (state === true) {
doButtonDown(name);
} else {
doButtonUp(button);
}
}
}
// loop timer for checking joystick state
function checkJoystickState() {
var gamepad = navigator.getGamepads()[joystickIdx];
if (gamepad) {
// axis -> dpad
var corX = gamepad.axes[0]; // -1 -> 1, left -> right
var corY = gamepad.axes[1]; // -1 -> 1, up -> down
checkJoystickAxisState("left", corX <= -0.5);
checkJoystickAxisState("right", corX >= 0.5);
checkJoystickAxisState("up", corY <= -0.5);
checkJoystickAxisState("down", corY >= 0.5);
// normal button map
Object.keys(joystickMap).forEach(function (btnIdx) {
var isPressed = false;
if (navigator.webkitGetGamepads) {
isPressed = (gamepad.buttons[btnIdx] === 1);
} else {
isPressed = (gamepad.buttons[btnIdx].value > 0 || gamepad.buttons[btnIdx].pressed === true);
}
if (joystickState[btnIdx] !== isPressed) {
joystickState[btnIdx] = isPressed;
if (isPressed === true) {
doButtonDown(joystickMap[btnIdx]);
} else {
doButtonUp(joystickMap[btnIdx]);
}
}
});
}
}
// we only capture the last plugged joystick
$(window).on("gamepadconnected", function (event) {
var gamepad = event.gamepad;
log(`Gamepad connected at index ${gamepad.index}: ${gamepad.id}. ${gamepad.buttons.length} buttons, ${gamepad.axes.length} axes.`);
padIdx = gamepad.index;
joystickIdx = gamepad.index;
// Ref: https://github.com/giongto35/cloud-game/issues/14
// get mapping first (default KeyMap2)
os = getOS();
browser = getBrowser();
var os = getOS();
var browser = getBrowser();
console.log(os);
console.log(browser);
if (os == "android") {
if (os === "android") {
// default of android is KeyMap1
padMap = { 2: "a", 0: "b", 3: "start", 4: "select", 10: "load", 11: "save", 8: "full", 9: "quit", 12: "up", 13: "down", 14: "left", 15: "right" };
joystickMap = { 2: "a", 0: "b", 3: "start", 4: "select", 10: "load", 11: "save", 8: "full", 9: "quit", 12: "up", 13: "down", 14: "left", 15: "right" };
} else {
// default of other OS is KeyMap2
padMap = { 0: "a", 1: "b", 2: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit", 12: "up", 13: "down", 14: "left", 15: "right" };
joystickMap = { 0: "a", 1: "b", 2: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit", 12: "up", 13: "down", 14: "left", 15: "right" };
}
if (os == "android" && (browser == "firefox" || browser == "uc")) { //KeyMap2
padMap = { 0: "a", 1: "b", 2: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit", 12: "up", 13: "down", 14: "left", 15: "right" };
if (os === "android" && (browser === "firefox" || browser === "uc")) { //KeyMap2
joystickMap = { 0: "a", 1: "b", 2: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit", 12: "up", 13: "down", 14: "left", 15: "right" };
}
if (os == "win" && browser == "firefox") { //KeyMap3
padMap = { 1: "a", 2: "b", 0: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit" };
if (os === "win" && browser === "firefox") { //KeyMap3
joystickMap = { 1: "a", 2: "b", 0: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit" };
}
if (os == "mac" && browser == "safari") { //KeyMap4
padMap = { 1: "a", 2: "b", 0: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit", 14: "up", 15: "down", 16: "left", 17: "right" };
if (os === "mac" && browser === "safari") { //KeyMap4
joystickMap = { 1: "a", 2: "b", 0: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit", 14: "up", 15: "down", 16: "left", 17: "right" };
}
if (os == "mac" && browser == "firefox") { //KeyMap5
padMap = { 1: "a", 2: "b", 0: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit", 14: "up", 15: "down", 16: "left", 17: "right" };
if (os === "mac" && browser === "firefox") { //KeyMap5
joystickMap = { 1: "a", 2: "b", 0: "start", 3: "select", 8: "load", 9: "save", 6: "full", 7: "quit", 14: "up", 15: "down", 16: "left", 17: "right" };
}
// reset state
padState = {
joystickState = {
left: false,
right: false,
up: false,
down: false,
};
Object.keys(padMap).forEach(k => {
padState[k] = false;
Object.keys(joystickMap).forEach(function (btnIdx) {
joystickState[btnIdx] = false;
});
// looper, too intense?
if (gamepadTimer) {
clearInterval(gamepadTimer);
if (joystickTimer !== null) {
clearInterval(joystickTimer);
}
function checkAxis(bo, axis) {
if (bo != padState[axis]) {
padState[axis] = bo;
doButton(bo, axis);
}
}
gamepadTimer = setInterval(function () {
gamepad = navigator.getGamepads()[padIdx];
if (gamepad) {
// axis pad
corX = gamepad.axes[0]; // -1 -> 1, left -> right
corY = gamepad.axes[1]; // -1 -> 1, up -> down
checkAxis(corX <= -0.5, "left");
checkAxis(corX >= 0.5, "right");
checkAxis(corY <= -0.5, "up");
checkAxis(corY >= 0.5, "down");
// normal button
Object.keys(padMap).forEach(k => {
if (navigator.webkitGetGamepads) {
curPressed = (gamepad.buttons[k] == 1);
} else {
curPressed = (gamepad.buttons[k].value > 0 || gamepad.buttons[k].pressed == true);
}
if (padState[k] != curPressed) {
padState[k] = curPressed;
doButton(curPressed, padMap[k]);
}
});
}
}, 10); // miliseconds per hit
joystickTimer = setInterval(checkJoystickState, 10); // miliseconds per hit
});
window.addEventListener("gamepaddisconnected", (event) => {
clearInterval(gamepadTimer);
// disconnected event is triggered
$(window).on("gamepaddisconnected", (event) => {
clearInterval(joystickTimer);
log(`Gamepad disconnected at index ${e.gamepad.index}`);
});

View file

@ -1,6 +1,8 @@
// KEYBOARD
/*
Keyboard gesture
*/
KEYBOARD_MAP = {
const KEYBOARD_MAP = {
37: "left",
38: "up",
39: "right",
@ -19,15 +21,15 @@ KEYBOARD_MAP = {
70: "full", // f
}
document.body.onkeyup = function (e) {
if (e.keyCode in KEYBOARD_MAP) {
doButtonUp(KEYBOARD_MAP[e.keyCode]);
$("body").on("keyup", function (event) {
if (event.keyCode in KEYBOARD_MAP) {
doButtonUp(KEYBOARD_MAP[event.keyCode]);
}
}
});
document.body.onkeydown = function (e) {
if (e.keyCode in KEYBOARD_MAP) {
doButtonDown(KEYBOARD_MAP[e.keyCode]);
$("body").on("keydown", function (event) {
if (event.keyCode in KEYBOARD_MAP) {
doButtonDown(KEYBOARD_MAP[event.keyCode]);
}
};
});

View file

@ -1,72 +1,51 @@
// Window rerender / rotate screen if needed
var isSwitch = false;
/*
Touch gesture
*/
function fixScreen() {
target = $(document);
child = $("#gamebody");
width = child.width();
height = child.height();
// Should have maximum box for desktop?
targetWidth = target.width();
targetHeight = target.height();
screenWidth = targetWidth;
screenHeight = targetHeight;
st = "translate(-50%, -50%) ";
// rotate ?
if (isPortrait()) {
st += `rotate(90deg) `;
screenWidth = targetHeight;
screenHeight = targetWidth;
isSwitch = true;
} else {
isSwitch = false;
}
// zoom in/out ?
st += `scale(${Math.min(screenWidth / width, screenHeight / height)}) `;
child.css("transform", st);
child.css("-webkit-transform", st);
child.css("-moz-transform", st);
child.css("-ms-transform", st);
}
fixScreen();
$(window).on("resize", fixScreen);
$(window).on("orientationchange", fixScreen);
// Virtual Joystick
// Virtual Gamepad / Joystick
// Ref: https://jsfiddle.net/aa0et7tr/5/
var dpadState = {};
var touchIdx = null;
const maxDiff = 20; // pixel
var dragStart = null;
var padHolder = $("#circle-pad-holder");
var padCircle = $("#circle-pad");
/*
Left panel - Dpad
*/
const MAX_DIFF = 20; // radius of circle boundary
// vpad state, use for mouse button down
let vpadState = {
up: false,
down: false,
left: false,
right: false,
};
$(".btn, .btn-big").each(function () {
vpadState[$(this).attr("value")] = false;
});
let vpadTouchIdx = null;
let vpadTouchDrag = null;
let vpadHolder = $("#circle-pad-holder");
let vpadCircle = $("#circle-pad");
function resetJoystickState() {
function resetVpadState() {
// trigger up event?
checkDPadAxis("up", false);
checkDPadAxis("down", false);
checkDPadAxis("left", false);
checkDPadAxis("right", false);
dragStart = null;
touchIdx = null;
checkVpadState("up", false);
checkVpadState("down", false);
checkVpadState("left", false);
checkVpadState("right", false);
vpadTouchDrag = null;
vpadTouchIdx = null;
$(".dpad").removeClass("pressed");
}
function checkDPadAxis(axis, bo) {
if (bo != dpadState[axis]) {
dpadState[axis] = bo;
if (dpadState[axis]) {
function checkVpadState(axis, state) {
if (state !== vpadState[axis]) {
vpadState[axis] = state;
if (state) {
doButtonDown(axis);
} else {
doButtonUp(axis);
@ -75,37 +54,141 @@ function checkDPadAxis(axis, bo) {
}
function handleJoystickDown(event) {
padCircle.css("transition", "0s");
padCircle.css("-moz-transition", "0s");
padCircle.css("-webkit-transition", "0s");
function handleVpadJoystickDown(event) {
vpadCircle.css("transition", "0s");
vpadCircle.css("-moz-transition", "0s");
vpadCircle.css("-webkit-transition", "0s");
if (event.changedTouches) {
resetJoystickState();
resetVpadState();
touchIdx = event.changedTouches[0].identifier;
dragStart = {
x: event.changedTouches[0].clientX,
y: event.changedTouches[0].clientY,
};
return;
vpadTouchIdx = event.changedTouches[0].identifier;
event.clientX = event.changedTouches[0].clientX;
event.clientY = event.changedTouches[0].clientY;
}
dragStart = {
vpadTouchDrag = {
x: event.clientX,
y: event.clientY,
};
}
function handleJoystickMove(event) {
// stop other events
event.preventDefault();
if (dragStart === null) return;
function handleVpadJoystickUp(event) {
if (vpadTouchDrag === null) return;
vpadCircle.css("transition", ".2s");
vpadCircle.css("-moz-transition", ".2s");
vpadCircle.css("-webkit-transition", ".2s");
vpadCircle.css("transform", "translate3d(0px, 0px, 0px)");
vpadCircle.css("-moz-transform", "translate3d(0px, 0px, 0px)");
vpadCircle.css("-webkit-transform", "translate3d(0px, 0px, 0px)");
resetVpadState();
}
function handleVpadJoystickMove(event) {
if (vpadTouchDrag === null) return;
if (event.changedTouches) {
// check if moving source is from other touch?
for (var i = 0; i < event.changedTouches.length; i++) {
if (event.changedTouches[i].identifier === touchIdx) {
if (event.changedTouches[i].identifier === vpadTouchIdx) {
event.clientX = event.changedTouches[i].clientX;
event.clientY = event.changedTouches[i].clientY;
}
}
if (event.clientX === undefined || event.clientY === undefined)
return;
}
var xDiff = event.clientX - vpadTouchDrag.x;
var yDiff = event.clientY - vpadTouchDrag.y;
var angle = Math.atan2(yDiff, xDiff);
var distance = Math.min(MAX_DIFF, Math.hypot(xDiff, yDiff));
var xNew = distance * Math.cos(angle);
var yNew = distance * Math.sin(angle);
// check if screen is switched or not
if (isLayoutSwitched) {
tmp = xNew;
xNew = yNew;
yNew = -tmp;
}
style = `translate(${xNew}px, ${yNew}px)`;
vpadCircle.css("transform", style);
vpadCircle.css("-webkit-transform", style);
vpadCircle.css("-moz-transform", style);
var xRatio = xNew / MAX_DIFF;
var yRatio = yNew / MAX_DIFF;
checkVpadState("left", xRatio <= -0.5);
checkVpadState("right", xRatio >= 0.5);
checkVpadState("up", yRatio <= -0.5);
checkVpadState("down", yRatio >= 0.5);
}
// touch/mouse events for dpad. mouseup events is binded to window.
vpadHolder.on('mousedown', handleVpadJoystickDown);
vpadHolder.on('touchstart', handleVpadJoystickDown);
vpadHolder.on('touchend', handleVpadJoystickUp);
/*
Right side - Control buttons
*/
function handleButtonDown(event) {
checkVpadState($(this).attr("value"), true);
// add touchIdx?
}
function handleButtonUp(event) {
checkVpadState($(this).attr("value"), false);
}
// touch/mouse events for control buttons. mouseup events is binded to window.
$(".btn").on("mousedown", handleButtonDown);
$(".btn").on("touchstart", handleButtonDown);
$(".btn").on("touchend", handleButtonUp);
/*
Touch menu
*/
let menuTouchIdx = null;
let menuTouchDrag = null;
let menuTouchTime = null;
function handleMenuDown(event) {
// Identify of touch point
if (event.changedTouches) {
menuTouchIdx = event.changedTouches[0].identifier;
event.clientX = event.changedTouches[0].clientX;
event.clientY = event.changedTouches[0].clientY;
}
menuTouchDrag = {
x: event.clientX,
y: event.clientY,
};
menuTouchTime = Date.now();
}
function handleMenuMove(event) {
if (menuTouchDrag === null) return;
if (event.changedTouches) {
// check if moving source is from other touch?
for (var i = 0; i < event.changedTouches.length; i++) {
if (event.changedTouches[i].identifier === menuTouchIdx) {
event.clientX = event.changedTouches[i].clientX;
event.clientY = event.changedTouches[i].clientY;
}
@ -114,68 +197,89 @@ function handleJoystickMove(event) {
return;
}
const xDiff = event.clientX - dragStart.x;
const yDiff = event.clientY - dragStart.y;
const angle = Math.atan2(yDiff, xDiff);
const distance = Math.min(maxDiff, Math.hypot(xDiff, yDiff));
xNew = distance * Math.cos(angle);
yNew = distance * Math.sin(angle);
var listbox = $("#menu-container");
listbox.css("transition", "");
listbox.css("-moz-transition", "");
listbox.css("-webkit-transition", "");
if (isLayoutSwitched) {
listbox.css("top", `${menuTop - (-menuTouchDrag.x + event.clientX)}px`);
} else {
listbox.css("top", `${menuTop - (menuTouchDrag.y - event.clientY)}px`);
}
}
// check if screen is switched or not
if (isSwitch) {
tmp = xNew;
xNew = yNew;
yNew = -tmp;
function handleMenuUp(event) {
if (menuTouchDrag === null) return;
if (event.changedTouches) {
if (event.changedTouches[0].identifier !== menuTouchIdx)
return;
event.clientX = event.changedTouches[0].clientX;
event.clientY = event.changedTouches[0].clientY;
}
style = `translate(${xNew}px, ${yNew}px)`;
padCircle.css("transform", style);
padCircle.css("-webkit-transform", style);
padCircle.css("-moz-transform", style);
var interval = Date.now() - menuTouchTime; // 100ms?
if (isLayoutSwitched) {
newY = -menuTouchDrag.x + event.clientX;
} else {
newY = menuTouchDrag.y - event.clientY;
}
if (interval < 200) {
// calc velo
newY = newY/ interval * 250;
}
// current item?
menuTop -= newY;
idx = Math.round((menuTop - MENU_TOP_POSITION) / -36);
pickGame(idx);
const xRatio = xNew / maxDiff;
const yRatio = yNew / maxDiff;
checkDPadAxis("left", xRatio <= -0.5);
checkDPadAxis("right", xRatio >= 0.5);
checkDPadAxis("up", yRatio <= -0.5);
checkDPadAxis("down", yRatio >= 0.5);
menuTouchDrag = null;
}
function handleJoystickUp(event) {
if (dragStart === null) return;
padCircle.css("transition", ".2s");
padCircle.css("-moz-transition", ".2s");
padCircle.css("-webkit-transition", ".2s");
padCircle.css("transform", "translate3d(0px, 0px, 0px)");
padCircle.css("-moz-transform", "translate3d(0px, 0px, 0px)");
padCircle.css("-webkit-transform", "translate3d(0px, 0px, 0px)");
// Bind events for menu
$("#menu-screen").on("mousedown", handleMenuDown);
$("#menu-screen").on("touchstart", handleMenuDown);
$("#menu-screen").on("touchend", handleMenuUp);
resetJoystickState();
/*
Common events
*/
function handleWindowMove(event) {
event.preventDefault();
handleVpadJoystickMove(event);
handleMenuMove(event);
// moving touch
if (event.changedTouches) {
for (var i = 0; i < event.changedTouches.length; i++) {
if (event.changedTouches[i].identifier !== menuTouchIdx && event.changedTouches[i].identifier !== vpadTouchIdx) {
// check class
var elem = document.elementFromPoint(event.changedTouches[i].clientX, event.changedTouches[i].clientY);
if (elem.classList.contains("btn")) {
$(elem).trigger("touchstart");
} else {
$(".btn").trigger("touchend");
}
}
}
}
}
function handleWindowUp(event) {
handleVpadJoystickUp(event);
handleMenuUp(event);
$(".btn").trigger("touchend");
}
function handleButtonDown(event) {
doButtonDown($(this).attr("value"));
}
function handleButtonUp(event) {
doButtonUp($(this).attr("value"));
}
// mouse events
$(".btn, .btn-big").on("mousedown", handleButtonDown);
$(".btn, .btn-big").on("mouseup", handleButtonUp);
padHolder.on('mousedown', handleJoystickDown);
$(window).on('mousemove', handleJoystickMove);
$(window).on('mouseup', handleJoystickUp);
// touch events
$(".btn, .btn-big").on("touchstart", handleButtonDown);
$(".btn, .btn-big").on("touchend", handleButtonUp);
padHolder.on('touchstart', handleJoystickDown);
window.addEventListener("touchmove", handleJoystickMove, { passive: false });
padHolder.on('touchend', handleJoystickUp);
// Bind events for window
$(window).on("mousemove", handleWindowMove);
window.addEventListener("touchmove", handleWindowMove, {passive: false});
$(window).on("mouseup", handleWindowUp);

60
static/js/global.js vendored
View file

@ -1,45 +1,59 @@
/*
GLOBAL CONSTS
Global Constants
*/
DEBUG = true;
const DEBUG = false;
KEY_BIT = ["a", "b", "select", "start", "up", "down", "left", "right"];
const KEY_BIT = ["a", "b", "select", "start", "up", "down", "left", "right"];
const INPUT_FPS = 100;
const INPUT_STATE_PACKET = 1;
const PINGPONGPS = 5;
INPUT_FPS = 100;
PINGPONGPS = 5;
INPUT_STATE_PACKET = 5;
const MENU_TOP_POSITION = 102;
//------------------------------------------------------------------------
/*
GLOBAL VARS
Global variables
*/
// Game state
var screenState = "loader";
var gameList = [];
var gameIdx = 5;
let screenState = "loader";
let gameList = [];
let gameIdx = 5; // contra
let gamePickerTimer = null;
let roomID = null;
// Input vars
var keyState = {
// controllers
// Game controller state
let keyState = {
// control
a: false,
b: false,
start: false,
select: false,
// navigators
// dpad
up: false,
down: false,
left: false,
right: false
}
var unchangePacket = INPUT_STATE_PACKET;
var inputTimer = null;
let unchangePacket = INPUT_STATE_PACKET;
let gameInputTimer = null;
// Network state
let pc, inputChannel;
let localSessionDescription = "";
let remoteSessionDescription = "";
let conn;
// Touch menu state
let menuDrag = null;
let menuTop = MENU_TOP_POSITION;
// Screen state
let isLayoutSwitched = false;
// Connection vars
var pc, inputChannel;
var localSessionDescription = "";
var remoteSessionDescription = "";
var conn;

45
static/js/init.js vendored Normal file
View file

@ -0,0 +1,45 @@
// Window rerender / rotate screen if needed
function fixScreenLayout() {
var targetWidth = $(document).width() * 0.8;
var targetHeight = $(document).height() * 0.8;
// Should have maximum box for desktop?
// targetWidth = 800; targetHeight = 600; // test on desktop
fixElementLayout($("#gamebody"), targetWidth, targetHeight);
}
$(window).on("resize", fixScreenLayout);
$(window).on("orientationchange", fixScreenLayout);
function parseURLForRoom() {
var queryDict = {}
location.search.substr(1).split("&").forEach(function(item) {
queryDict[item.split("=")[0]] = item.split("=")[1]
});
if (typeof queryDict["id"] === "string") {
return queryDict["id"];
}
return null;
}
$(document).ready(function () {
fixScreenLayout();
// localStorage first
//roomID = loadRoomID();
roomID = "";
// Shared URL second
var rid = parseURLForRoom();
if (rid !== null) {
roomID = rid;
}
// if from URL -> start game immediately!
$("#room-txt").val(roomID);
});

2
static/js/jquery-3.3.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

74
static/js/utils.js vendored
View file

@ -5,6 +5,11 @@ function log(msg) {
console.log(msg);
}
function popup(msg) {
$("#noti-box").html(msg);
$("#noti-box").fadeIn().delay(DEBUG ? 0 : 0).fadeOut();
}
function openFullscreen(elem) {
if (elem.requestFullscreen) {
@ -18,6 +23,7 @@ function openFullscreen(elem) {
}
}
function closeFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
@ -34,22 +40,24 @@ function closeFullscreen() {
function getOS() {
// linux? ios?
var OSName = "unknown";
if (navigator.appVersion.indexOf("Win") != -1) OSName = "win";
else if (navigator.appVersion.indexOf("Mac") != -1) OSName = "mac";
else if (navigator.userAgent.indexOf("Android") != -1) OSName = "android";
if (navigator.appVersion.indexOf("Win") !== -1) OSName = "win";
else if (navigator.appVersion.indexOf("Mac") !== -1) OSName = "mac";
else if (navigator.userAgent.indexOf("Android") !== -1) OSName = "android";
return OSName;
}
function getBrowser() {
var browserName = "unknown";
if (navigator.userAgent.indexOf("Firefox") != -1) browserName = "firefox";
if (navigator.userAgent.indexOf("Chrome") != -1) browserName = "chrome";
if (navigator.userAgent.indexOf("Edge") != -1) browserName = "edge";
if (navigator.userAgent.indexOf("Version/") != -1) browserName = "safari";
if (navigator.userAgent.indexOf("UCBrowser") != -1) browserName = "uc";
if (navigator.userAgent.indexOf("Firefox") !== -1) browserName = "firefox";
if (navigator.userAgent.indexOf("Chrome") !== -1) browserName = "chrome";
if (navigator.userAgent.indexOf("Edge") !== -1) browserName = "edge";
if (navigator.userAgent.indexOf("Version/") !== -1) browserName = "safari";
if (navigator.userAgent.indexOf("UCBrowser") !== -1) browserName = "uc";
return browserName;
}
function isPortrait() {
// ios / mobile app
switch (window.orientation) {
@ -60,10 +68,56 @@ function isPortrait() {
}
// desktop
orient = screen.msOrientation || screen.mozOrientation || (screen.orientation || {}).type;
if (orient == "portrait-primary") {
var orient = screen.msOrientation || screen.mozOrientation || (screen.orientation || {}).type;
if (orient === "portrait-primary") {
return true;
}
return false;
}
function fixElementLayout(elem, targetWidth, targetHeight) {
var width = elem.width();
var height = elem.height();
var st = "translate(-50%, -50%) ";
// rotate portrait layout
if (isPortrait()) {
st += `rotate(90deg) `;
var tmp = targetHeight;
targetHeight = targetWidth;
targetWidth = tmp;
isLayoutSwitched = true;
} else {
isLayoutSwitched = false;
}
// scale, fit to target size
st += `scale(${Math.min(targetWidth / width, targetHeight / height)}) `;
elem.css("transform", st);
elem.css("-webkit-transform", st);
elem.css("-moz-transform", st);
}
function loadRoomID() {
return localStorage.getItem("roomID");
}
function saveRoomID(roomIdx) {
localStorage.setItem("roomID", roomIdx);
}
function copyToClipboard(str) {
const el = document.createElement('textarea');
el.value = str;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
};

34
static/js/ws.js vendored
View file

@ -26,18 +26,19 @@ conn.onmessage = e => {
switch (d["id"]) {
case "gamelist":
files = JSON.parse(d["data"]);
// parse files list to gamelist
files = JSON.parse(d["data"]);
gameList = [];
files.forEach(file => {
var file = file
var name = file.substr(0, file.indexOf('.'));
gameList.push({file: file, name: name});
});
log("Received game list");
// change screen to menu
reloadGameMenu();
showMenuScreen();
break;
@ -66,14 +67,24 @@ conn.onmessage = e => {
// TODO: Calc time
break;
case "start":
log("Got start");
roomID = d["room_id"];
log(`Got start with room id: ${roomID}`);
popup("Started! You can share you game!")
saveRoomID(roomID);
$("#btn-join").html("share");
// TODO: remove
$("#room-txt").val(d["room_id"]);
break;
case "save":
log(`Got save response: ${d["data"]}`);
popup("Saved");
break;
case "load":
log(`Got load response: ${d["data"]}`);
popup("Loaded");
break;
}
}
@ -205,13 +216,18 @@ function startGame() {
screenState = "game";
// conn.send(JSON.stringify({"id": "start", "data": gameList[gameIdx].file, "room_id": $("#room-txt").val(), "player_index": parseInt(playerIndex.value, 10)}));
conn.send(JSON.stringify({"id": "start", "data": gameList[gameIdx].file, "room_id": $("#room-txt").val(), "player_index": 1}));
conn.send(JSON.stringify({"id": "start", "data": gameList[gameIdx].file, "room_id": roomID != null ? roomID : '', "player_index": 1}));
// clear menu screen
stopInputTimer();
$("#menu-screen").fadeOut(DEBUG?0:0, function() {
$("#game-screen").show();
});
stopGameInputTimer();
//$("#menu-screen").fadeOut(DEBUG ? 0 : 400, function() {
//$("#game-screen").show();
//});
$("#menu-screen").hide()
$("#game-screen").show();
$("#btn-save").show();
$("#btn-load").show();
// end clear
startInputTimer();
startGameInputTimer();
}

Binary file not shown.

78
static/test/test.html vendored
View file

@ -1,78 +0,0 @@
<html>
<body>
<button onclick="play()">hihi</button>
<button onclick="alert(1)">hoho</button>
<b>hehe</b>
<script src="/static/js/libopus.js"></script>
<script src="/static/js/opus.js"></script>
<script>
a = new ArrayBuffer(3);
v = new DataView(a);
v.setInt8(0, -28);
v.setInt8(1, -1);
v.setInt8(2, -2);
console.log(a);
b = new Uint8Array(a);
console.log(b);
d = new OpusDecoder(48000, 2);
console.log(d);
console.log(d.decode_float(a));
// Stereo
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var channels = 1;
var sampleRate = 48000;
var bitDepth = 16;
// Create an empty two second stereo buffer at the
// sample rate of the AudioContext
var req = new XMLHttpRequest();
var sound;
req.open('GET', "/static/ex3.raw", false);
// req.open('GET', "/static/sinewave.raw", false);
req.overrideMimeType('text\/plain; charset=x-user-defined');
req.onreadystatechange = function (aEvt) {
if (req.readyState == 4) {
if(req.status == 200) {
sound = req.responseText;
console.log("loaded");
} else {
alert('failed loading audio');
}
}
};
req.send(null);
function play(){
frameCount = sound.length / 2;
myAudioBuffer = audioCtx.createBuffer(channels, frameCount, sampleRate);
for (var channel = 0; channel < channels; channel++) {
var nowBuffering = myAudioBuffer.getChannelData(channel, bitDepth, sampleRate);
for (var i = 0; i < frameCount; i++) {
// audio needs to be in [-1.0; 1.0]
// for this reason I also tried to divide it by 32767
// as my pcm sample is in 16-Bit. It plays still the
// same creepy sound less noisy.
var word = (sound.charCodeAt(i * 2) & 0xff) + ((sound.charCodeAt(i * 2 + 1) & 0xff) << 8);
nowBuffering[i] = ((word + 32768) % 65536 - 32768) / 32768.0;
}
}
// Get an AudioBufferSourceNode.
// This is the AudioNode to use when we want to play an AudioBuffer
var source = audioCtx.createBufferSource();
// set the buffer in the AudioBufferSourceNode
source.buffer = myAudioBuffer;
// connect the AudioBufferSourceNode to the
// destination so we can hear the sound
source.connect(audioCtx.destination);
// start the source playing
source.start();
}
</script>
</body>
</html>

View file

@ -11,8 +11,8 @@ import (
)
const (
gameboyIndex = "./static/gameboy2.html"
debugIndex = "./static/gameboy2.html"
gameboyIndex = "./static/game.html"
debugIndex = "./static/game.html"
)
// Flag to determine if the server is overlord or not