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
14
.gitattributes
vendored
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
2
document/implementation/README.md
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
6921
static/css/gameboy.css
vendored
137
static/css/joystick.css
vendored
|
|
@ -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
|
|
@ -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
44
static/gameboy2.html → static/game.html
vendored
|
|
@ -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
|
|
@ -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
|
After Width: | Height: | Size: 42 KiB |
BIN
static/img/404.jpg
vendored
|
Before Width: | Height: | Size: 210 KiB |
BIN
static/img/500_F_241143639_RW6VdHbqYRM0yfNpDxDlWpj0uXWglAC2.jpg
vendored
Normal file
|
After Width: | Height: | Size: 235 KiB |
BIN
static/img/arrow2_e.gif
vendored
|
Before Width: | Height: | Size: 151 B |
BIN
static/img/boxarts/1943.png
vendored
|
Before Width: | Height: | Size: 39 KiB |
BIN
static/img/boxarts/Battle City.png
vendored
|
Before Width: | Height: | Size: 354 KiB |
BIN
static/img/boxarts/Bubble Bobble.png
vendored
|
Before Width: | Height: | Size: 441 KiB |
|
Before Width: | Height: | Size: 168 KiB |
BIN
static/img/boxarts/Chip 'n Dale Rescue Rangers 2.png
vendored
|
Before Width: | Height: | Size: 165 KiB |
BIN
static/img/boxarts/Contra.png
vendored
|
Before Width: | Height: | Size: 146 KiB |
BIN
static/img/boxarts/Gradius.png
vendored
|
Before Width: | Height: | Size: 137 KiB |
BIN
static/img/boxarts/Ice Climber.png
vendored
|
Before Width: | Height: | Size: 55 KiB |
BIN
static/img/boxarts/Kirby's Adventure.png
vendored
|
Before Width: | Height: | Size: 154 KiB |
BIN
static/img/boxarts/Mega Man 2.png
vendored
|
Before Width: | Height: | Size: 156 KiB |
BIN
static/img/boxarts/Mega Man 6.png
vendored
|
Before Width: | Height: | Size: 188 KiB |
BIN
static/img/boxarts/Metal Gear.png
vendored
|
Before Width: | Height: | Size: 188 KiB |
BIN
static/img/boxarts/Mike Tyson's Punch-Out!!.png
vendored
|
Before Width: | Height: | Size: 173 KiB |
BIN
static/img/boxarts/Mortal Kombat 4.png
vendored
|
Before Width: | Height: | Size: 338 KiB |
BIN
static/img/boxarts/Ninja Gaiden.png
vendored
|
Before Width: | Height: | Size: 185 KiB |
BIN
static/img/boxarts/Nintendo World Cup.png
vendored
|
Before Width: | Height: | Size: 166 KiB |
BIN
static/img/boxarts/Super Mario Bros 2.png
vendored
|
Before Width: | Height: | Size: 152 KiB |
BIN
static/img/boxarts/Super Mario Bros 3.png
vendored
|
Before Width: | Height: | Size: 108 KiB |
BIN
static/img/boxarts/Super Mario Bros.png
vendored
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 207 KiB |
BIN
static/img/boxarts/Zelda II.png
vendored
|
Before Width: | Height: | Size: 83 KiB |
BIN
static/img/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif
vendored
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
static/img/original.gif
vendored
Normal file
|
After Width: | Height: | Size: 886 KiB |
BIN
static/img/pixel_waterfall_bg__by_isohei-d4xntof.gif
vendored
Normal file
|
After Width: | Height: | Size: 48 KiB |
102
static/index_ws.html
vendored
|
|
@ -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>
|
||||
191
static/js/controller.js
vendored
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
147
static/js/gesture_joystick.js
vendored
|
|
@ -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}`);
|
||||
});
|
||||
|
||||
|
|
|
|||
22
static/js/gesture_keyboard.js
vendored
|
|
@ -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]);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
366
static/js/gesture_touch.js
vendored
|
|
@ -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
|
|
@ -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
|
|
@ -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
74
static/js/utils.js
vendored
|
|
@ -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
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
|||
BIN
static/test/sinewave.raw
vendored
78
static/test/test.html
vendored
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||