Merge branch 'master' of github.com:bui/taiko-web

This commit is contained in:
purerosefallen 2019-06-01 23:00:02 +08:00
commit f51574c8fb
No known key found for this signature in database
GPG Key ID: E6D78C90943A3185
49 changed files with 3023 additions and 1254 deletions

90
app.py
View File

@ -40,10 +40,10 @@ def get_config():
try: try:
config = json.load(open('config.json', 'r')) config = json.load(open('config.json', 'r'))
except ValueError: except ValueError:
print 'WARNING: Invalid config.json, using default values' print('WARNING: Invalid config.json, using default values')
config = {} config = {}
else: else:
print 'WARNING: No config.json found, using default values' print('WARNING: No config.json found, using default values')
config = {} config = {}
if not config.get('songs_baseurl'): if not config.get('songs_baseurl'):
@ -55,77 +55,6 @@ def get_config():
return config return config
def parse_osu(osu):
osu_lines = open(osu, 'r').read().replace('\x00', '').split('\n')
sections = {}
current_section = (None, [])
for line in osu_lines:
line = line.strip()
secm = re.match('^\[(\w+)\]$', line)
if secm:
if current_section:
sections[current_section[0]] = current_section[1]
current_section = (secm.group(1), [])
else:
if current_section:
current_section[1].append(line)
else:
current_section = ('Default', [line])
if current_section:
sections[current_section[0]] = current_section[1]
return sections
def get_osu_key(osu, section, key, default=None):
sec = osu[section]
for line in sec:
ok = line.split(':', 1)[0].strip()
ov = line.split(':', 1)[1].strip()
if ok.lower() == key.lower():
return ov
return default
def get_preview(song_id, song_type):
preview = 0
if song_type == "tja":
if os.path.isfile('public/songs/%s/main.tja' % song_id):
preview = get_tja_preview('public/songs/%s/main.tja' % song_id)
else:
osus = [osu for osu in os.listdir('public/songs/%s' % song_id) if osu in ['easy.osu', 'normal.osu', 'hard.osu', 'oni.osu']]
if osus:
osud = parse_osu('public/songs/%s/%s' % (song_id, osus[0]))
preview = int(get_osu_key(osud, 'General', 'PreviewTime', 0))
return preview
def get_tja_preview(tja):
tja_lines = open(tja, 'r').read().replace('\x00', '').split('\n')
for line in tja_lines:
line = line.strip()
if ':' in line:
name, value = line.split(':', 1)
if name.lower() == 'demostart':
value = value.strip()
try:
value = float(value)
except ValueError:
pass
else:
return int(value * 1000)
elif line.lower() == '#start':
break
return 0
def get_version(): def get_version():
version = {'commit': None, 'commit_short': '', 'version': None, 'url': DEFAULT_URL} version = {'commit': None, 'commit_short': '', 'version': None, 'url': DEFAULT_URL}
if os.path.isfile('version.json'): if os.path.isfile('version.json'):
@ -168,7 +97,7 @@ def route_api_preview():
abort(400) abort(400)
song_type = song_row[0][12] song_type = song_row[0][12]
prev_path = make_preview(song_id, song_type) prev_path = make_preview(song_id, song_type, song_row[0][15])
if not prev_path: if not prev_path:
return redirect(get_config()['songs_baseurl'] + '%s/main.ogg' % song_id) return redirect(get_config()['songs_baseurl'] + '%s/main.ogg' % song_id)
@ -182,7 +111,6 @@ def route_api_songs():
raw_categories = query_db('select * from categories') raw_categories = query_db('select * from categories')
categories = {} categories = {}
def_category = {'title': None, 'title_en': None}
for cat in raw_categories: for cat in raw_categories:
categories[cat[0]] = cat[1] categories[cat[0]] = cat[1]
@ -195,9 +123,9 @@ def route_api_songs():
for song in songs: for song in songs:
song_id = song[0] song_id = song[0]
song_type = song[12] song_type = song[12]
preview = get_preview(song_id, song_type) preview = song[15]
category_out = categories[song[11]] if song[11] in categories else def_category category_out = categories[song[11]] if song[11] in categories else ""
song_skin_out = song_skins[song[14]] if song[14] in song_skins else None song_skin_out = song_skins[song[14]] if song[14] in song_skins else None
songs_out.append({ songs_out.append({
@ -213,7 +141,8 @@ def route_api_songs():
'category': category_out, 'category': category_out,
'type': song_type, 'type': song_type,
'offset': song[13], 'offset': song[13],
'song_skin': song_skin_out 'song_skin': song_skin_out,
'volume': song[16]
}) })
return jsonify(songs_out) return jsonify(songs_out)
@ -226,13 +155,12 @@ def route_api_config():
return jsonify(config) return jsonify(config)
def make_preview(song_id, song_type): def make_preview(song_id, song_type, preview):
song_path = 'public/songs/%s/main.ogg' % song_id song_path = 'public/songs/%s/main.ogg' % song_id
prev_path = 'public/songs/%s/preview.mp3' % song_id prev_path = 'public/songs/%s/preview.mp3' % song_id
if os.path.isfile(song_path) and not os.path.isfile(prev_path): if os.path.isfile(song_path) and not os.path.isfile(prev_path):
preview = get_preview(song_id, song_type) / 1000 if not preview or preview <= 0:
if not preview or preview <= 0.1:
print('Skipping #%s due to no preview' % song_id) print('Skipping #%s due to no preview' % song_id)
return False return False

Binary file not shown.

View File

@ -10,24 +10,16 @@
#loading-don{ #loading-don{
background-image: url("dancing-don.gif"); background-image: url("dancing-don.gif");
} }
#touch-drum-img{
background-image: url("touch_drum.png");
}
#touch-full-btn{ #touch-full-btn{
background-image: url("touch_fullscreen.png"); background-image: url("touch_fullscreen.png");
} }
#touch-pause-btn{ #touch-pause-btn{
background-image: url("touch_pause.png"); background-image: url("touch_pause.png");
} }
.song-stage-1{ .settings-outer{
background-image: url("bg_stage_1.png"); background-image: url("bg_settings.png");
background-size: calc(100vh / 720 * 66);
} }
.song-stage-2{ #gamepad-bg,
background-image: url("bg_stage_2.png"); #gamepad-buttons{
background-size: calc(100vh / 720 * 254); background-image: url("settings_gamepad.png");
}
.song-stage-3{
background-image: url("bg_stage_3.png");
background-size: calc(100vh / 720 * 458);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

View File

@ -66,8 +66,5 @@
"", "",
"logo5": "logo5":
"",
"globe":
"" ""
} }

View File

@ -44,7 +44,8 @@
box-sizing: border-box; box-sizing: border-box;
} }
#debug .input-slider{ #debug .input-slider,
#debug .select{
display: flex; display: flex;
width: 100%; width: 100%;
height: 30px; height: 30px;
@ -59,7 +60,8 @@
padding: 2px 4px; padding: 2px 4px;
text-align: center; text-align: center;
} }
#debug .input-slider>span{ #debug .input-slider>span,
#debug .select>span{
display: block; display: block;
width: 10%; width: 10%;
height: 100%; height: 100%;
@ -70,10 +72,19 @@
line-height: 2em; line-height: 2em;
cursor: pointer; cursor: pointer;
} }
#debug .input-slider>span:hover{ #debug .input-slider>span:hover,
#debug .select>span:hover{
opacity: 1; opacity: 1;
background: #333; background: #333;
} }
#debug .select select{
width: 90%;
height: 100%;
box-sizing: border-box;
font-size: 18px;
font-family: sans-serif;
padding: 2px 4px;
}
#debug label{ #debug label{
display: block; display: block;
@ -111,6 +122,7 @@
margin-left: 3px; margin-left: 3px;
} }
#debug .autoplay-label{ #debug .autoplay-label,
#debug .branch-hide{
display: none; display: none;
} }

View File

@ -94,3 +94,6 @@
z-index: 2; z-index: 2;
transition: 1s background-color linear; transition: 1s background-color linear;
} }
.fix-animations *{
animation: none !important;
}

View File

@ -15,7 +15,7 @@ body{
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #000; background-color: #000;
background-position: top center; background-position: center;
background-size: 30vh; background-size: 30vh;
} }
#screen.pattern-bg{ #screen.pattern-bg{
@ -29,8 +29,8 @@ body{
width:90%; width:90%;
height:10%; height:10%;
border:1px solid black; border:1px solid black;
position: fixed; position: absolute;
top:50%; top:45%;
left:5%; left:5%;
background: rgba(0,0,0,0.65); background: rgba(0,0,0,0.65);
} }

View File

@ -23,115 +23,6 @@
left: 0; left: 0;
z-index: -1; z-index: -1;
} }
#tutorial-outer{
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: absolute;
width: 100%;
height: 100%;
}
#tutorial{
background: rgb(246, 234, 212);
color: black;
border: 0.25em black solid;
border-radius: 0.5em;
width: 800px;
padding: 1em;
margin: 1em;
font-size: 21px;
position: relative;
}
.touch-enabled #tutorial{
font-size: 3vmin;
}
#tutorial-title{
z-index: 1;
position: absolute;
color: white;
top: -0.7em;
font-size: 1.65em;
}
#tutorial-content{
margin: 0.7em 0;
overflow-y: auto;
max-height: calc(100vh - 14em);
}
kbd{
font-family: inherit;
padding: 0.1em 0.6em;
border: 1px solid #ccc;
font-size: 0.6em;
background-color: #f7f7f7;
color: #333;
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
border-radius: 3px;
display: inline-block;
text-shadow: 0 1px 0 #fff;
line-height: 1.4;
white-space: nowrap;
}
.taibtn{
display: inline-block;
background: #f6ead4;
padding: 0.4em 0.4em;
border-radius: 0.5em;
border: 0.1em rgba(218, 205, 178, 1) solid;
cursor: pointer;
font-size: 1.4em;
box-sizing: border-box;
color: #555;
text-align: center;
}
#tutorial-end-button{
float: right;
padding: 0.4em 1.5em;
font-weight: bold;
border-color: #000;
color: #000;
}
.taibtn:hover,
#tutorial-end-button:hover{
position: relative;
z-index: 1;
color: #fff;
background: #ffb547;
border-color: #fff;
}
.taibtn::before{
padding-left: inherit;
}
#about-link-btns{
float: left;
display: flex;
}
#about-link-btns .taibtn{
margin-right: 0.4em;
}
#diag-txt textarea,
#diag-txt iframe{
width: 100%;
height: 5em;
font-size: inherit;
resize: none;
word-break: break-all;
margin-bottom: 1em;
background: #fff;
border: 1px solid #a9a9a9;
user-select: all;
}
.text-warn{
color: #d00;
}
.link-btn a{
color: inherit;
text-decoration: none;
pointer-events: none;
}
.nowrap{
white-space: nowrap;
}
#session-invite{ #session-invite{
width: 100%; width: 100%;
height: 1.9em; height: 1.9em;
@ -149,10 +40,10 @@ kbd{
} }
@keyframes bgscroll{ @keyframes bgscroll{
from{ from{
background-position: 0 top; background-position: 50% top;
} }
to{ to{
background-position: calc(-100vh / 720 * 512) top; background-position: calc(50% - 100vh / 720 * 512) top;
} }
} }
#song-select{ #song-select{

View File

@ -26,6 +26,7 @@
height: calc(44 / 720 * 100vh); height: calc(44 / 720 * 100vh);
background-position: center bottom; background-position: center bottom;
background-repeat-y: no-repeat; background-repeat-y: no-repeat;
background-size: auto 100%;
bottom: 0; bottom: 0;
} }
.portrait #songbg{ .portrait #songbg{

View File

@ -38,53 +38,6 @@
-webkit-text-stroke: 0.25em #f00; -webkit-text-stroke: 0.25em #f00;
filter: blur(0.3vmin); filter: blur(0.3vmin);
} }
#lang{
font-size: 3vmin;
position: absolute;
bottom: 0;
left: 0;
width: 7em;
height: 4em;
color: #000;
z-index: 5;
}
#lang:focus-within{
outline: #4d90fe auto 5px;
}
#lang-dropdown{
font-size: 1.7em;
font-family: Microsoft YaHei, sans-serif;
opacity: 0;
width: 100%;
height: 100%;
color: #000;
cursor: pointer;
}
#lang-id{
position: absolute;
top: 0;
bottom: 0;
left: 2.6em;
font-family: TnT, Meiryo, sans-serif;
font-size: 1.5em;
font-weight: normal;
color: #fff;
line-height: 2.75em;
z-index: 0;
}
#lang-icon{
position: absolute;
width: 2.8em;
height: 2.8em;
padding: 0.6em;
fill: currentColor;
}
#lang:hover #lang-icon{
color: #f00;
}
#lang:hover #lang-id::before {
-webkit-text-stroke: 0.25em #f00;
}
#title-disclaimer { #title-disclaimer {
text-align: center; text-align: center;
position: absolute; position: absolute;

261
public/src/css/view.css Normal file
View File

@ -0,0 +1,261 @@
.view-outer{
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: absolute;
width: 100%;
height: 100%;
background-position: center;
}
.view{
background: rgb(246, 234, 212);
color: black;
border: 0.25em black solid;
border-radius: 0.5em;
width: 800px;
max-width: 40em;
padding: 1em;
margin: 1em;
font-size: 21px;
position: relative;
}
@media (max-width: 950px){
.view-outer:not(.touch-enabled) .view{
font-size: 3vmin;
}
}
@media (max-height: 650px){
.view-outer:not(.touch-enabled) .view{
font-size: 3vmin;
}
}
.touch-enabled .view{
font-size: 3vmin;
}
.view-title{
z-index: 1;
position: absolute;
color: white;
top: -0.7em;
font-size: 1.65em;
}
.view-content{
margin: 0.7em 0;
overflow-y: auto;
max-height: calc(100vh - 14em);
}
kbd{
font-family: inherit;
padding: 0.1em 0.6em;
border: 1px solid #ccc;
font-size: 0.6em;
background-color: #f7f7f7;
color: #333;
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
border-radius: 3px;
display: inline-block;
text-shadow: 0 1px 0 #fff;
line-height: 1.4;
white-space: nowrap;
}
.taibtn{
display: inline-block;
background: #f6ead4;
padding: 0.4em 0.4em;
border-radius: 0.5em;
border: 0.1em rgba(218, 205, 178, 1) solid;
cursor: pointer;
font-size: 1.4em;
box-sizing: border-box;
color: #555;
text-align: center;
}
.view-end-button{
float: right;
padding: 0.4em 1.5em;
font-weight: bold;
border-color: #000;
color: #000;
z-index: 1;
}
.taibtn:hover,
.taibtn.selected,
.view-end-button:hover,
.view-end-button.selected{
position: relative;
color: #fff;
background: #ffb547;
border-color: #fff;
}
.taibtn::before,
.view-end-button::before{
display: none;
}
.taibtn:hover::before,
.taibtn.selected::before,
.view-end-button:hover::before,
.view-end-button.selected::before{
display: block
}
.taibtn::before{
padding-left: inherit;
}
.left-buttons{
float: left;
display: flex;
}
.left-buttons .taibtn{
margin-right: 0.4em;
}
#diag-txt textarea,
#diag-txt iframe{
width: 100%;
height: 5em;
font-size: inherit;
resize: none;
word-break: break-all;
margin-bottom: 1em;
background: #fff;
border: 1px solid #a9a9a9;
user-select: all;
}
.text-warn{
color: #d00;
}
.link-btn a{
color: inherit;
text-decoration: none;
pointer-events: none;
}
.nowrap{
white-space: nowrap;
}
@keyframes border-pulse{
0%{border-color: #ff0}
50%{border-color: rgba(255, 255, 0, 0)}
100%{border-color: #ff0}
}
@keyframes border-pulse2{
0%{border-color: #e29e06}
50%{border-color: rgba(226, 158, 6, 0)}
100%{border-color: #e29e06}
}
.settings-outer{
background-size: 50vh;
}
.setting-box{
display: flex;
height: 2em;
margin-top: 1.2em;
border: 0.25em solid #000;
border-radius: 0.5em;
padding: 0.3em;
outline: none;
color: #000;
cursor: pointer;
}
.setting-box:first-child{
margin-top: 0;
}
.settings-outer .view-content:not(:hover) .setting-box.selected,
.view-outer:not(.settings-outer) .setting-box.selected,
.setting-box:hover{
background: #ffb547;
animation: 2s linear border-pulse infinite;
}
.bold-fonts .setting-box{
line-height: 1em;
}
.setting-name{
position: relative;
width: 50%;
padding: 0.3em;
font-size: 1.3em;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
}
.view-content:not(:hover) .setting-box.selected .setting-name,
.view-outer:not(.settings-outer) .setting-box.selected .setting-name,
.setting-box:hover .setting-name,
.setting-box:hover #gamepad-value{
color: #fff;
z-index: 0;
}
.setting-name::before{
padding-left: 0.3em;
}
.setting-value{
display: flex;
background: #fff;
width: 50%;
border-radius: 0.2em;
padding: 0.5em;
box-sizing: border-box;
}
.setting-value.selected{
width: calc(50% + 0.2em);
margin: -0.1em;
border: 0.2em solid #e29e06;
padding: 0.4em;
animation: 2s linear border-pulse2 infinite;
}
.setting-value>div{
padding: 0 0.4em;
overflow: hidden;
text-overflow: ellipsis;
}
.shadow-outer{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
}
#settings-gamepad{
display: none;
}
#settings-gamepad .view{
position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 574px;
height: 428px;
max-height: calc(100vh - 14em + 88px);
}
#settings-gamepad .setting-box{
height: auto;
}
#gamepad-bg{
position: relative;
width: 550px;
height: 317px;
max-height: none;
background-repeat: no-repeat;
text-align: center;
font-size: 1.4em;
cursor: pointer;
}
#gamepad-buttons{
position: absolute;
left: 141px;
top: 120px;
width: 282px;
height: 131px;
background-position: 0 -318px;
background-repeat: no-repeat;
pointer-events: none;
}
#gamepad-value{
position: relative;
margin-top: 1em;
}
#gamepad-value::before{
left: auto;
}

View File

@ -4,20 +4,20 @@
loader.changePage("about", true) loader.changePage("about", true)
cancelTouch = false cancelTouch = false
this.endButton = document.getElementById("tutorial-end-button") this.endButton = this.getElement("view-end-button")
this.diagTxt = document.getElementById("diag-txt") this.diagTxt = document.getElementById("diag-txt")
this.version = document.getElementById("version-link").href this.version = document.getElementById("version-link").href
this.tutorialOuter = document.getElementById("tutorial-outer") this.tutorialOuter = this.getElement("view-outer")
if(touchEnabled){ if(touchEnabled){
this.tutorialOuter.classList.add("touch-enabled") this.tutorialOuter.classList.add("touch-enabled")
} }
this.linkIssues = document.getElementById("link-issues") this.linkIssues = document.getElementById("link-issues")
this.linkEmail = document.getElementById("link-email") this.linkEmail = document.getElementById("link-email")
var tutorialTitle = document.getElementById("tutorial-title") var tutorialTitle = this.getElement("view-title")
tutorialTitle.innerText = strings.aboutSimulator tutorialTitle.innerText = strings.aboutSimulator
tutorialTitle.setAttribute("alt", strings.aboutSimulator) tutorialTitle.setAttribute("alt", strings.aboutSimulator)
var tutorialContent = document.getElementById("tutorial-content") var tutorialContent = this.getElement("view-content")
strings.about.bugReporting.forEach(string => { strings.about.bugReporting.forEach(string => {
tutorialContent.appendChild(document.createTextNode(string)) tutorialContent.appendChild(document.createTextNode(string))
tutorialContent.appendChild(document.createElement("br")) tutorialContent.appendChild(document.createElement("br"))
@ -34,20 +34,62 @@
pageEvents.add(this.linkIssues, ["click", "touchend"], this.linkButton.bind(this)) pageEvents.add(this.linkIssues, ["click", "touchend"], this.linkButton.bind(this))
pageEvents.add(this.linkEmail, ["click", "touchend"], this.linkButton.bind(this)) pageEvents.add(this.linkEmail, ["click", "touchend"], this.linkButton.bind(this))
pageEvents.once(this.endButton, ["mousedown", "touchstart"]).then(this.onEnd.bind(this)) pageEvents.add(this.endButton, ["mousedown", "touchstart"], this.onEnd.bind(this))
pageEvents.keyOnce(this, 13, "down").then(this.onEnd.bind(this)) this.items = [this.linkIssues, this.linkEmail, this.endButton]
this.selected = 2
this.keyboard = new Keyboard({
confirm: ["enter", "space", "don_l", "don_r"],
previous: ["left", "up", "ka_l"],
next: ["right", "down", "ka_r"],
back: ["escape"]
}, this.keyPressed.bind(this))
this.gamepad = new Gamepad({ this.gamepad = new Gamepad({
"confirm": ["start", "b", "ls", "rs"] "confirm": ["b", "ls", "rs"],
}, this.onEnd.bind(this)) "previous": ["u", "l", "lb", "lt", "lsu", "lsl"],
"next": ["d", "r", "rb", "rt", "lsd", "lsr"],
"back": ["start", "a"]
}, this.keyPressed.bind(this))
this.addDiag() pageEvents.send("about", this.addDiag())
}
getElement(name){
return loader.screen.getElementsByClassName(name)[0]
}
keyPressed(pressed, name){
if(!pressed){
return
}
var selected = this.items[this.selected]
if(name === "confirm"){
if(selected === this.endButton){
this.onEnd()
}else{
this.getLink(selected).click()
pageEvents.send("about-link", selected)
assets.sounds["se_don"].play()
}
}else if(name === "previous" || name === "next"){
selected.classList.remove("selected")
this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1))
this.items[this.selected].classList.add("selected")
assets.sounds["se_ka"].play()
}else if(name === "back"){
this.onEnd()
}
}
mod(length, index){
return ((index % length) + length) % length
} }
onEnd(event){ onEnd(event){
var touched = false var touched = false
if(event && event.type === "touchstart"){ if(event){
event.preventDefault() if(event.type === "touchstart"){
touched = true event.preventDefault()
touched = true
}else if(event.which !== 1){
return
}
} }
this.clean() this.clean()
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
@ -143,17 +185,24 @@
} }
} }
var issueBody = strings.issueTemplate + "\n\n\n\n" + diag var issueBody = strings.about.issueTemplate + "\n\n\n\n" + diag
this.getLink(this.linkEmail).href += "?body=" + encodeURIComponent(issueBody.replace(/\n/g, "<br>\r\n")) this.getLink(this.linkEmail).href += "?body=" + encodeURIComponent(issueBody.replace(/\n/g, "<br>\r\n"))
return diag
} }
getLink(target){ getLink(target){
return target.getElementsByTagName("a")[0] return target.getElementsByTagName("a")[0]
} }
linkButton(event){ linkButton(event){
this.getLink(event.currentTarget).click() if(event.target === event.currentTarget){
this.getLink(event.currentTarget).click()
pageEvents.send("about-link", event.currentTarget)
assets.sounds["se_don"].play()
}
} }
clean(){ clean(){
cancelTouch = true cancelTouch = true
this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
pageEvents.remove(this.linkIssues, ["click", "touchend"]) pageEvents.remove(this.linkIssues, ["click", "touchend"])
pageEvents.remove(this.linkEmail, ["click", "touchend"]) pageEvents.remove(this.linkEmail, ["click", "touchend"])
@ -161,7 +210,7 @@
if(this.textarea){ if(this.textarea){
pageEvents.remove(this.textarea, ["focus", "blur"]) pageEvents.remove(this.textarea, ["focus", "blur"])
} }
pageEvents.keyRemove(this, 13) pageEvents.keyRemove(this, "all")
delete this.endButton delete this.endButton
delete this.diagTxt delete this.diagTxt
delete this.version delete this.version

View File

@ -7,6 +7,7 @@ var assets = {
"scoresheet.js", "scoresheet.js",
"songselect.js", "songselect.js",
"keyboard.js", "keyboard.js",
"gameinput.js",
"game.js", "game.js",
"controller.js", "controller.js",
"circle.js", "circle.js",
@ -27,7 +28,8 @@ var assets = {
"debug.js", "debug.js",
"session.js", "session.js",
"importsongs.js", "importsongs.js",
"logo.js" "logo.js",
"settings.js"
], ],
"css": [ "css": [
"main.css", "main.css",
@ -35,7 +37,8 @@ var assets = {
"loadsong.css", "loadsong.css",
"game.css", "game.css",
"debug.css", "debug.css",
"songbg.css" "songbg.css",
"view.css"
], ],
"assetsCss": [ "assetsCss": [
"fonts/fonts.css", "fonts/fonts.css",
@ -71,18 +74,15 @@ var assets = {
"bg_score_p2.png", "bg_score_p2.png",
"bg_settings.png", "bg_settings.png",
"bg_pause.png", "bg_pause.png",
"bg_stage_1.png",
"bg_stage_2.png",
"bg_stage_3.png",
"badge_auto.png", "badge_auto.png",
"touch_drum.png",
"touch_pause.png", "touch_pause.png",
"touch_fullscreen.png", "touch_fullscreen.png",
"mimizu.png", "mimizu.png",
"results_flowers.png", "results_flowers.png",
"results_mikoshi.png", "results_mikoshi.png",
"results_tetsuohana.png", "results_tetsuohana.png",
"results_tetsuohana2.png" "results_tetsuohana2.png",
"settings_gamepad.png"
], ],
"audioSfx": [ "audioSfx": [
"se_cancel.wav", "se_cancel.wav",
@ -154,7 +154,8 @@ var assets = {
"audioMusic": [ "audioMusic": [
"bgm_songsel.mp3", "bgm_songsel.mp3",
"bgm_result.mp3", "bgm_result.mp3",
"bgm_setsume.mp3" "bgm_setsume.mp3",
"bgm_settings.mp3"
], ],
"fonts": [ "fonts": [
"Kozuka", "Kozuka",
@ -168,7 +169,8 @@ var assets = {
"tutorial.html", "tutorial.html",
"about.html", "about.html",
"debug.html", "debug.html",
"session.html" "session.html",
"settings.html"
], ],
"songs": [], "songs": [],

View File

@ -29,33 +29,58 @@ class CanvasCache{
return return
} }
var saved = false var saved = false
var time = Date.now()
if(!img){ if(!img){
var w = config.w var w = config.w
var h = config.h var h = config.h
this.x += this.lastW + 1 this.x += this.lastW + (this.lastW ? 1 : 0)
if(this.x + w > this.w){ if(this.x + w > this.w){
this.x = 0 this.x = 0
this.y += this.lastH + 1 this.y += this.lastH + 1
} }
this.lastW = w if(this.y + h > this.h){
this.lastH = Math.max(this.lastH, h) var clear = true
img = { var oldest = {time: time}
x: this.x, this.map.forEach((oldImg, id) => {
y: this.y, if(oldImg.time < oldest.time){
w: w, oldest.id = id
h: h oldest.time = oldImg.time
}
})
var oldImg = this.map.get(oldest.id)
this.map.delete(oldest.id)
img = {
x: oldImg.x,
y: oldImg.y,
w: w,
h: h
}
}else{
var clear = false
this.lastW = w
this.lastH = Math.max(this.lastH, h)
img = {
x: this.x,
y: this.y,
w: w,
h: h
}
} }
this.map.set(config.id, img)
saved = true saved = true
this.ctx.save() this.ctx.save()
this.ctx.translate(img.x |0, img.y |0) this.ctx.translate(img.x |0, img.y |0)
if(clear){
this.ctx.clearRect(0, 0, (img.w |0) + 1, (img.h |0) + 1)
}
this.ctx.beginPath() this.ctx.beginPath()
this.ctx.rect(0, 0, img.w |0, img.h |0) this.ctx.rect(0, 0, img.w |0, img.h |0)
this.ctx.clip() this.ctx.clip()
this.map.set(config.id, img)
callback(this.ctx) callback(this.ctx)
} }
img.time = time
if(setOnly){ if(setOnly){
this.ctx.restore() this.ctx.restore()
return return
@ -81,6 +106,10 @@ class CanvasCache{
this.ctx.clearRect(0, 0, this.w, this.h) this.ctx.clearRect(0, 0, this.w, this.h)
} }
clean(){ clean(){
if(!this.canvas){
return
}
this.resize(1, 1, 1)
delete this.map delete this.map
delete this.ctx delete this.ctx
delete this.canvas delete this.canvas

View File

@ -48,9 +48,9 @@
this.regex = { this.regex = {
comma: /[,.]/, comma: /[,.]/,
ideographicComma: /[、。]/, ideographicComma: /[、。]/,
apostrophe: /[']/, apostrophe: /[']/,
degree: /[゚°]/, degree: /[゚°]/,
brackets: /[\(\)\[\]「」『』【】]/, brackets: /[\(\)\[\]「」『』【】:;]/,
tilde: /[\-~〜_]/, tilde: /[\-~〜_]/,
tall: /[bdfghj-l-t♪]/, tall: /[bdfghj-l-t♪]/,
i: /[i]/, i: /[i]/,
@ -268,13 +268,16 @@
easeOut(pos){ easeOut(pos){
return Math.sin(Math.PI / 2 * pos) return Math.sin(Math.PI / 2 * pos)
} }
easeOutBack(pos){
return Math.sin(Math.PI / 1.74 * pos) * 1.03
}
easeInOut(pos){ easeInOut(pos){
return (Math.cos(Math.PI * pos) - 1) / -2 return (Math.cos(Math.PI * pos) - 1) / -2
} }
verticalText(config){ verticalText(config){
var ctx = config.ctx var ctx = config.ctx
var inputText = config.text var inputText = config.text.toString()
var mul = config.fontSize / 40 var mul = config.fontSize / 40
var ura = false var ura = false
var r = this.regex var r = this.regex
@ -572,7 +575,7 @@
} }
if(symbol.ura){ if(symbol.ura){
ctx.font = (30 * mul) + "px Meiryo, sans-serif" ctx.font = (30 * mul) + "px Meiryo, sans-serif"
ctx.textBaseline = "center" ctx.textBaseline = "middle"
ctx.beginPath() ctx.beginPath()
ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2) ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2)
if(action === "stroke"){ if(action === "stroke"){
@ -581,7 +584,7 @@
}else if(action === "fill"){ }else if(action === "fill"){
ctx.strokeStyle = config.fill ctx.strokeStyle = config.fill
ctx.lineWidth = 2.5 * mul ctx.lineWidth = 2.5 * mul
ctx.fillText(symbol.text, currentX, currentY) ctx.fillText(symbol.text, currentX, currentY + (17 * mul))
} }
ctx.stroke() ctx.stroke()
}else{ }else{
@ -598,7 +601,7 @@
layeredText(config, layers){ layeredText(config, layers){
var ctx = config.ctx var ctx = config.ctx
var inputText = config.text var inputText = config.text.toString()
var mul = config.fontSize / 40 var mul = config.fontSize / 40
var ura = false var ura = false
var r = this.regex var r = this.regex
@ -622,8 +625,6 @@
drawn.push({text: symbol, x: -2, y: 0, w: 20, scale: [0.6, 0.5]}) drawn.push({text: symbol, x: -2, y: 0, w: 20, scale: [0.6, 0.5]})
}else if(symbol === " "){ }else if(symbol === " "){
drawn.push({text: symbol, x: 0, y: 0, w: 10}) drawn.push({text: symbol, x: 0, y: 0, w: 10})
}else if(symbol === "'"){
drawn.push({text: ",", x: 0, y: -15, w: 7, scale: [1, 0.7]})
}else if(symbol === '"'){ }else if(symbol === '"'){
drawn.push({text: symbol, x: 2, y: 0, w: 10}) drawn.push({text: symbol, x: 2, y: 0, w: 10})
}else if(symbol === "∀"){ }else if(symbol === "∀"){
@ -634,6 +635,8 @@
} }
}else if(symbol === ""){ }else if(symbol === ""){
drawn.push({text: symbol, x: -9, y: 0, w: 37}) drawn.push({text: symbol, x: -9, y: 0, w: 37})
}else if(r.apostrophe.test(symbol)){
drawn.push({text: ",", x: 0, y: -15, w: 7, scale: [1, 0.7]})
}else if(r.comma.test(symbol)){ }else if(r.comma.test(symbol)){
// Comma, full stop // Comma, full stop
if(bold){ if(bold){
@ -788,7 +791,7 @@
} }
if(symbol.ura){ if(symbol.ura){
ctx.font = (30 * mul) + "px Meiryo, sans-serif" ctx.font = (30 * mul) + "px Meiryo, sans-serif"
ctx.textBaseline = "center" ctx.textBaseline = "middle"
ctx.beginPath() ctx.beginPath()
ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2) ctx.arc(currentX, currentY + (17 * mul), (18 * mul), 0, Math.PI * 2)
if(action === "strokeText"){ if(action === "strokeText"){
@ -797,7 +800,7 @@
}else if(action === "fillText"){ }else if(action === "fillText"){
ctx.strokeStyle = layer.fill ctx.strokeStyle = layer.fill
ctx.lineWidth = 2.5 * mul ctx.lineWidth = 2.5 * mul
ctx.fillText(symbol.text, currentX, currentY) ctx.fillText(symbol.text, currentX, currentY + (17 * mul))
} }
ctx.stroke() ctx.stroke()
}else{ }else{
@ -1167,6 +1170,7 @@
var firstTop = config.multiplayer ? 0 : 30 var firstTop = config.multiplayer ? 0 : 30
var secondTop = config.multiplayer ? 0 : 8 var secondTop = config.multiplayer ? 0 : 8
config.percentage = Math.max(0, Math.min(1, config.percentage))
var cleared = config.percentage - 1 / 50 >= config.clear var cleared = config.percentage - 1 / 50 >= config.clear
var gaugeW = 14 * 50 var gaugeW = 14 * 50

View File

@ -1,6 +1,5 @@
class Circle{ class Circle{
constructor(config){ constructor(config){
// id, ms, type, text, speed, endTime, requiredHits
this.id = config.id this.id = config.id
this.ms = config.start this.ms = config.start
this.originalMS = this.ms this.originalMS = this.ms
@ -23,38 +22,13 @@ class Circle{
this.gogoChecked = false this.gogoChecked = false
this.beatMS = config.beatMS this.beatMS = config.beatMS
this.fixedPos = config.fixedPos this.fixedPos = config.fixedPos
} this.branch = config.branch
getMS(){ this.section = config.section
return this.ms
}
getEndTime(){
return this.endTime
}
getType(){
return this.type
}
getLastFrame(){
return this.lastFrame
} }
animate(ms){ animate(ms){
this.animating = true this.animating = true
this.animT = ms this.animT = ms
} }
isAnimated(){
return this.animating
}
getAnimT(){
return this.animT
}
getPlayed(){
return this.isPlayed
}
isAnimationFinished(){
return this.animationEnded
}
endAnimation(){
this.animationEnded = true
}
played(score, big){ played(score, big){
this.score = score this.score = score
this.isPlayed = score <= 0 ? score - 1 : (big ? 2 : 1) this.isPlayed = score <= 0 ? score - 1 : (big ? 2 : 1)
@ -65,16 +39,4 @@ class Circle{
this.timesKa++ this.timesKa++
} }
} }
getScore(){
return this.score
}
getID(){
return this.id
}
getText(){
return this.text
}
getSpeed(){
return this.speed
}
} }

View File

@ -21,13 +21,14 @@ class Controller{
assets.songs.forEach(song => { assets.songs.forEach(song => {
if(song.id == this.selectedSong.folder){ if(song.id == this.selectedSong.folder){
this.mainAsset = song.sound this.mainAsset = song.sound
this.volume = song.volume || 1
} }
}) })
this.game = new Game(this, this.selectedSong, this.parsedSongData) this.game = new Game(this, this.selectedSong, this.parsedSongData)
this.view = new View(this) this.view = new View(this)
this.mekadon = new Mekadon(this, this.game) this.mekadon = new Mekadon(this, this.game)
this.keyboard = new Keyboard(this) this.keyboard = new GameInput(this)
this.playedSounds = {} this.playedSounds = {}
} }
@ -35,6 +36,9 @@ class Controller{
if(syncWith){ if(syncWith){
this.syncWith = syncWith this.syncWith = syncWith
} }
if(this.multiplayer !== 2){
snd.musicGain.setVolumeMul(this.volume)
}
this.game.run() this.game.run()
this.view.run() this.view.run()
if(this.multiplayer === 1){ if(this.multiplayer === 1){
@ -58,11 +62,19 @@ class Controller{
this.viewLoop() this.viewLoop()
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
this.gameInterval = setInterval(this.gameLoop.bind(this), 1000 / 60) this.gameInterval = setInterval(this.gameLoop.bind(this), 1000 / 60)
pageEvents.send("game-start", {
selectedSong: this.selectedSong,
autoPlayEnabled: this.autoPlayEnabled,
multiplayer: this.multiplayer,
touchEnabled: this.touchEnabled
})
} }
} }
stopMainLoop(){ stopMainLoop(){
this.mainLoopRunning = false this.mainLoopRunning = false
this.mainAsset.stop() if(this.mainAsset){
this.mainAsset.stop()
}
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
clearInterval(this.gameInterval) clearInterval(this.gameInterval)
} }
@ -153,8 +165,26 @@ class Controller{
if(this.multiplayer){ if(this.multiplayer){
new LoadSong(this.selectedSong, false, true, this.touchEnabled) new LoadSong(this.selectedSong, false, true, this.touchEnabled)
}else{ }else{
var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled) new Promise(resolve => {
taikoGame.run() var songObj = assets.songs.find(song => song.id === this.selectedSong.folder)
if(songObj.chart){
var reader = new FileReader()
var promise = pageEvents.load(reader).then(event => {
this.songData = event.target.result.replace(/\0/g, "").split("\n")
resolve()
})
if(this.selectedSong.type === "tja"){
reader.readAsText(songObj.chart, "sjis")
}else{
reader.readAsText(songObj.chart)
}
}else{
resolve()
}
}).then(() => {
var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled)
taikoGame.run()
})
} }
} }
playSound(id, time){ playSound(id, time){
@ -180,11 +210,8 @@ class Controller{
getKeys(){ getKeys(){
return this.keyboard.getKeys() return this.keyboard.getKeys()
} }
setKey(keyCode, down, ms){ setKey(pressed, name, ms){
return this.keyboard.setKey(keyCode, down, ms) return this.keyboard.setKey(pressed, name, ms)
}
getBindings(){
return this.keyboard.getBindings()
} }
getElapsedTime(){ getElapsedTime(){
return this.game.elapsedTime return this.game.elapsedTime
@ -214,7 +241,7 @@ class Controller{
if(this.multiplayer){ if(this.multiplayer){
p2.play(circle, this.mekadon) p2.play(circle, this.mekadon)
}else{ }else{
this.mekadon.play(circle) return this.mekadon.play(circle)
} }
} }
clean(){ clean(){
@ -224,6 +251,7 @@ class Controller{
this.stopMainLoop() this.stopMainLoop()
this.keyboard.clean() this.keyboard.clean()
this.view.clean() this.view.clean()
snd.buffer.loadSettings()
if(!this.multiplayer){ if(!this.multiplayer){
debugObj.controller = null debugObj.controller = null

View File

@ -8,15 +8,20 @@ class Debug{
this.debugDiv.innerHTML = assets.pages["debug"] this.debugDiv.innerHTML = assets.pages["debug"]
document.body.appendChild(this.debugDiv) document.body.appendChild(this.debugDiv)
this.titleDiv = this.debugDiv.getElementsByClassName("title")[0] this.titleDiv = this.byClass("title")
this.minimiseDiv = this.debugDiv.getElementsByClassName("minimise")[0] this.minimiseDiv = this.byClass("minimise")
this.offsetDiv = this.debugDiv.getElementsByClassName("offset")[0] this.offsetDiv = this.byClass("offset")
this.measureNumDiv = this.debugDiv.getElementsByClassName("measure-num")[0] this.measureNumDiv = this.byClass("measure-num")
this.restartCheckbox = this.debugDiv.getElementsByClassName("change-restart")[0] this.branchHideDiv = this.byClass("branch-hide")
this.autoplayLabel = this.debugDiv.getElementsByClassName("autoplay-label")[0] this.branchSelectDiv = this.byClass("branch-select")
this.autoplayCheckbox = this.debugDiv.getElementsByClassName("autoplay")[0] this.branchSelect = this.branchSelectDiv.getElementsByTagName("select")[0]
this.restartBtn = this.debugDiv.getElementsByClassName("restart-btn")[0] this.branchResetBtn = this.branchSelectDiv.getElementsByClassName("reset")[0]
this.exitBtn = this.debugDiv.getElementsByClassName("exit-btn")[0] this.volumeDiv = this.byClass("music-volume")
this.restartCheckbox = this.byClass("change-restart")
this.autoplayLabel = this.byClass("autoplay-label")
this.autoplayCheckbox = this.byClass("autoplay")
this.restartBtn = this.byClass("restart-btn")
this.exitBtn = this.byClass("exit-btn")
this.moving = false this.moving = false
pageEvents.add(window, ["mousedown", "mouseup", "blur"], this.stopMove.bind(this)) pageEvents.add(window, ["mousedown", "mouseup", "blur"], this.stopMove.bind(this))
@ -26,6 +31,8 @@ class Debug{
pageEvents.add(this.restartBtn, "click", this.restartSong.bind(this)) pageEvents.add(this.restartBtn, "click", this.restartSong.bind(this))
pageEvents.add(this.exitBtn, "click", this.clean.bind(this)) pageEvents.add(this.exitBtn, "click", this.clean.bind(this))
pageEvents.add(this.autoplayCheckbox, "change", this.toggleAutoplay.bind(this)) pageEvents.add(this.autoplayCheckbox, "change", this.toggleAutoplay.bind(this))
pageEvents.add(this.branchSelect, "change", this.branchChange.bind(this))
pageEvents.add(this.branchResetBtn, "click", this.branchReset.bind(this))
this.offsetSlider = new InputSlider(this.offsetDiv, -60, 60, 3) this.offsetSlider = new InputSlider(this.offsetDiv, -60, 60, 3)
this.offsetSlider.onchange(this.offsetChange.bind(this)) this.offsetSlider.onchange(this.offsetChange.bind(this))
@ -34,9 +41,17 @@ class Debug{
this.measureNumSlider.onchange(this.measureNumChange.bind(this)) this.measureNumSlider.onchange(this.measureNumChange.bind(this))
this.measureNumSlider.set(0) this.measureNumSlider.set(0)
this.volumeSlider = new InputSlider(this.volumeDiv, 0, 3, 2)
this.volumeSlider.onchange(this.volumeChange.bind(this))
this.volumeSlider.set(1)
this.moveTo(100, 100) this.moveTo(100, 100)
this.restore() this.restore()
this.updateStatus() this.updateStatus()
pageEvents.send("debug")
}
byClass(name){
return this.debugDiv.getElementsByClassName(name)[0]
} }
startMove(event){ startMove(event){
if(event.which === 1){ if(event.which === 1){
@ -87,20 +102,30 @@ class Debug{
} }
updateStatus(){ updateStatus(){
if(debugObj.controller && !this.controller){ if(debugObj.controller && !this.controller){
this.controller = debugObj.controller
this.restartBtn.style.display = "block" this.restartBtn.style.display = "block"
this.autoplayLabel.style.display = "block" this.autoplayLabel.style.display = "block"
if(this.controller.parsedSongData.branches){
this.branchHideDiv.style.display = "block"
}
this.controller = debugObj.controller
var selectedSong = this.controller.selectedSong var selectedSong = this.controller.selectedSong
this.defaultOffset = selectedSong.offset || 0 this.defaultOffset = selectedSong.offset || 0
if(this.songFolder === selectedSong.folder){ if(this.songFolder === selectedSong.folder){
this.offsetChange(this.offsetSlider.get(), true) this.offsetChange(this.offsetSlider.get(), true)
this.branchChange(null, true)
this.volumeChange(this.volumeSlider.get(), true)
}else{ }else{
this.songFolder = selectedSong.folder this.songFolder = selectedSong.folder
this.offsetSlider.set(this.defaultOffset) this.offsetSlider.set(this.defaultOffset)
this.branchReset(null, true)
this.volumeSlider.set(this.controller.volume)
} }
var measures = this.controller.parsedSongData.measures var measures = this.controller.parsedSongData.measures.filter((measure, i, array) => {
return i === 0 || Math.abs(measure.ms - array[i - 1].ms) > 0.01
})
this.measureNumSlider.setMinMax(0, measures.length - 1) this.measureNumSlider.setMinMax(0, measures.length - 1)
if(this.measureNum && measures.length > this.measureNum){ if(this.measureNum && measures.length > this.measureNum){
var measureMS = measures[this.measureNum].ms var measureMS = measures[this.measureNum].ms
@ -116,6 +141,7 @@ class Debug{
if(circles[i].endTime >= measureMS){ if(circles[i].endTime >= measureMS){
break break
} }
game.skipNote(circles[i])
} }
if(game.mainMusicPlaying){ if(game.mainMusicPlaying){
game.mainMusicPlaying = false game.mainMusicPlaying = false
@ -127,6 +153,7 @@ class Debug{
if(this.controller && !debugObj.controller){ if(this.controller && !debugObj.controller){
this.restartBtn.style.display = "" this.restartBtn.style.display = ""
this.autoplayLabel.style.display = "" this.autoplayLabel.style.display = ""
this.branchHideDiv.style.display = ""
this.controller = null this.controller = null
} }
} }
@ -141,6 +168,11 @@ class Debug{
songData.measures.forEach(measure => { songData.measures.forEach(measure => {
measure.ms = measure.originalMS + offset measure.ms = measure.originalMS + offset
}) })
if(songData.branches){
songData.branches.forEach(branch => {
branch.ms = branch.originalMS + offset
})
}
if(this.restartCheckbox.checked && !noRestart){ if(this.restartCheckbox.checked && !noRestart){
this.restartSong() this.restartSong()
} }
@ -152,6 +184,14 @@ class Debug{
this.restartSong() this.restartSong()
} }
} }
volumeChange(value, noRestart){
if(this.controller){
snd.musicGain.setVolumeMul(value)
}
if(this.restartCheckbox.checked && !noRestart){
this.restartSong()
}
}
restartSong(){ restartSong(){
if(this.controller){ if(this.controller){
this.controller.restartSong() this.controller.restartSong()
@ -162,29 +202,57 @@ class Debug{
this.controller.autoPlayEnabled = this.autoplayCheckbox.checked this.controller.autoPlayEnabled = this.autoplayCheckbox.checked
if(!this.controller.autoPlayEnabled){ if(!this.controller.autoPlayEnabled){
var keyboard = debugObj.controller.keyboard var keyboard = debugObj.controller.keyboard
var kbd = keyboard.getBindings() keyboard.setKey(false, "don_l")
keyboard.setKey(kbd.don_l, false) keyboard.setKey(false, "don_r")
keyboard.setKey(kbd.don_r, false) keyboard.setKey(false, "ka_l")
keyboard.setKey(kbd.ka_l, false) keyboard.setKey(false, "ka_r")
keyboard.setKey(kbd.ka_r, false)
} }
} }
} }
branchChange(event, noRestart){
if(this.controller){
var game = this.controller.game
var name = this.branchSelect.value
game.branch = name === "auto" ? false : name
game.branchSet = name === "auto"
if(noRestart){
game.branchStatic = true
}
var selectedOption = this.branchSelect.selectedOptions[0]
this.branchSelect.style.background = selectedOption.style.background
if(this.restartCheckbox.checked && !noRestart){
this.restartSong()
}
}
}
branchReset(event, noRestart){
this.branchSelect.value = "auto"
this.branchChange(null, noRestart)
}
clean(){ clean(){
this.offsetSlider.clean() this.offsetSlider.clean()
this.measureNumSlider.clean()
pageEvents.remove(window, ["mousedown", "mouseup", "blur"]) pageEvents.remove(window, ["mousedown", "mouseup", "blur"])
pageEvents.mouseRemove(this) pageEvents.mouseRemove(this)
pageEvents.remove(this.titleDiv, "mousedown")
pageEvents.remove(this.title, "mousedown") pageEvents.remove(this.title, "mousedown")
pageEvents.remove(this.minimiseDiv, "click") pageEvents.remove(this.minimiseDiv, "click")
pageEvents.remove(this.restartBtn, "click") pageEvents.remove(this.restartBtn, "click")
pageEvents.remove(this.exitBtn, "click") pageEvents.remove(this.exitBtn, "click")
pageEvents.remove(this.autoplayCheckbox, "change") pageEvents.remove(this.autoplayCheckbox, "change")
pageEvents.remove(this.branchSelect, "change")
pageEvents.remove(this.branchResetBtn, "click")
delete this.titleDiv delete this.titleDiv
delete this.minimiseDiv delete this.minimiseDiv
delete this.offsetDiv delete this.offsetDiv
delete this.measureNumDiv delete this.measureNumDiv
delete this.branchHideDiv
delete this.branchSelectDiv
delete this.branchSelect
delete this.branchResetBtn
delete this.volumeDiv
delete this.restartCheckbox delete this.restartCheckbox
delete this.autoplayLabel delete this.autoplayLabel
delete this.autoplayCheckbox delete this.autoplayCheckbox

View File

@ -19,8 +19,8 @@ class Game{
difficulty: this.rules.difficulty difficulty: this.rules.difficulty
} }
this.HPGain = 100 / this.songData.circles.filter(circle => { this.HPGain = 100 / this.songData.circles.filter(circle => {
var type = circle.getType() var type = circle.type
return type === "don" || type === "ka" || type === "daiDon" || type === "daiKa" return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active)
}).length }).length
this.paused = false this.paused = false
this.started = false this.started = false
@ -28,6 +28,9 @@ class Game{
this.musicFadeOut = 0 this.musicFadeOut = 0
this.fadeOutStarted = false this.fadeOutStarted = false
this.currentTimingPoint = 0 this.currentTimingPoint = 0
this.branchNames = ["normal", "advanced", "master"]
this.resetSection()
this.gameLagSync = !this.controller.touchEnabled && !(/Firefox/.test(navigator.userAgent))
assets.songs.forEach(song => { assets.songs.forEach(song => {
if(song.id == selectedSong.folder){ if(song.id == selectedSong.folder){
@ -69,17 +72,19 @@ class Game{
} }
updateCirclesStatus(){ updateCirclesStatus(){
var nextSet = false var nextSet = false
var ms = this.elapsedTime
var circles = this.songData.circles var circles = this.songData.circles
var startIndex = this.currentCircle === 0 ? 0 : this.currentCircle - 1 var startIndex = this.currentCircle === 0 ? 0 : this.currentCircle - 1
for(var i = startIndex; i < circles.length && i < this.currentCircle + 2; i++){ var index = 0
var circle = circles[i]
if(!circle.getPlayed()){
var ms = this.elapsedTime
var type = circle.getType()
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
var endTime = circle.getEndTime() + (drumrollNotes ? 0 : this.rules.bad)
if(ms >= circle.getMS()){ for(var i = startIndex; i < circles.length; i++){
var circle = circles[i]
if(circle && (!circle.branch || circle.branch.active) && !circle.isPlayed){
var type = circle.type
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
var endTime = circle.endTime + (drumrollNotes ? 0 : this.rules.bad)
if(ms >= circle.ms){
if(drumrollNotes && !circle.rendaPlayed && ms < endTime){ if(drumrollNotes && !circle.rendaPlayed && ms < endTime){
circle.rendaPlayed = true circle.rendaPlayed = true
if(this.rules.difficulty === "easy"){ if(this.rules.difficulty === "easy"){
@ -97,11 +102,14 @@ class Game{
if(ms > endTime){ if(ms > endTime){
if(!this.controller.autoPlayEnabled){ if(!this.controller.autoPlayEnabled){
if(drumrollNotes){ if(drumrollNotes){
if(circle.section && circle.timesHit === 0){
this.resetSection()
}
circle.played(-1, false) circle.played(-1, false)
this.updateCurrentCircle() this.updateCurrentCircle()
if(this.controller.multiplayer === 1){ if(this.controller.multiplayer === 1){
var value = { var value = {
pace: (ms - circle.getMS()) / circle.timesHit pace: (ms - circle.ms) / circle.timesHit
} }
if(type === "drumroll" || type === "daiDrumroll"){ if(type === "drumroll" || type === "daiDrumroll"){
value.kaAmount = circle.timesKa / circle.timesHit value.kaAmount = circle.timesKa / circle.timesHit
@ -109,58 +117,178 @@ class Game{
p2.send("drumroll", value) p2.send("drumroll", value)
} }
}else{ }else{
var currentScore = 0 this.skipNote(circle)
circle.played(-1, type === "daiDon" || type === "daiKa")
this.controller.displayScore(currentScore, true)
this.updateCurrentCircle() this.updateCurrentCircle()
this.updateCombo(currentScore)
this.updateGlobalScore(currentScore, 1)
if(this.controller.multiplayer === 1){
p2.send("note", {
score: -1
})
}
} }
} }
}else if(!this.controller.autoPlayEnabled && !nextSet){ }else if(!this.controller.autoPlayEnabled && !nextSet){
nextSet = true nextSet = true
this.currentCircle = i this.currentCircle = i
} }
if(index++ > 1){
break
}
} }
} }
var branches = this.songData.branches
if(branches){
var force = this.controller.multiplayer === 2 ? p2 : this
var measures = this.songData.measures
if(this.controller.multiplayer === 2 || force.branch){
if(!force.branchSet){
force.branchSet = true
if(branches.length){
this.setBranch(branches[0], force.branch)
}
var view = this.controller.view
var currentMeasure = view.branch
for(var i = measures.length; i--;){
var measure = measures[i]
if(measure.nextBranch && measure.ms <= ms){
currentMeasure = measure.nextBranch.active
}
}
if(view.branch !== currentMeasure){
if(!this.branchStatic){
view.branchAnimate = {
ms: ms,
fromBranch: view.branch
}
}
this.branchStatic = false
view.branch = currentMeasure
}
}
}
for(var i = 0; i < measures.length; i++){
var measure = measures[i]
if(measure.ms > ms){
break
}else if(measure.nextBranch && !measure.gameChecked){
measure.gameChecked = true
var branch = measure.nextBranch
if(branch.type){
var accuracy = 0
if(branch.type === "drumroll"){
if(force.branch){
var accuracy = Math.max(0, branch.requirement[force.branch])
}else{
var accuracy = this.sectionDrumroll
}
}else if(this.sectionNotes.length !== 0){
if(force.branch){
var accuracy = Math.max(0, Math.min(100, branch.requirement[force.branch]))
}else{
var accuracy = this.sectionNotes.reduce((a, b) => a + b) / this.sectionNotes.length * 100
}
}
if(accuracy >= branch.requirement.master){
this.setBranch(branch, "master")
}else if(accuracy >= branch.requirement.advanced){
this.setBranch(branch, "advanced")
}else{
this.setBranch(branch, "normal")
}
}else if(this.controller.multiplayer === 1){
p2.send("branch", "normal")
}
}
}
}
}
fixNoteStream(keysDon){
var circleIsNote = circle => {
var type = circle.type
return type === "don" || type === "ka" || type === "daiDon" || type === "daiKa"
}
var correctNote = circle => {
var type = circle.type
return keysDon ? (type === "don" || type === "daiDon") : (type === "ka" || type === "daiKa")
}
var ms = this.elapsedTime
var circles = this.songData.circles
for(var i = this.currentCircle + 1; i < circles.length; i++){
var circle = circles[i]
var relative = ms - circle.ms
if(!circle.branch || circle.branch.active){
if((!circleIsNote(circle) || relative < -this.rules.bad)){
break
}else if(Math.abs(relative) < this.rules.ok && correctNote(circle)){
for(var j = this.currentCircle; j < i; j++){
var circle = circles[j]
if(circle && !circle.branch || circle.branch.active){
this.skipNote(circles[j])
}
}
this.currentCircle = i
return circles[i]
}
}
}
}
skipNote(circle){
if(circle.section){
this.resetSection()
}
circle.played(-1, circle.type === "daiDon" || circle.type === "daiKa")
this.sectionNotes.push(0)
this.controller.displayScore(0, true)
this.updateCombo(0)
this.updateGlobalScore(0, 1)
if(this.controller.multiplayer === 1){
p2.send("note", {
score: -1
})
}
} }
checkPlays(){ checkPlays(){
var circles = this.songData.circles var circles = this.songData.circles
var circle = circles[this.currentCircle] var circle = circles[this.currentCircle]
if(circle && this.controller.autoPlayEnabled){ if(this.controller.autoPlayEnabled){
return this.controller.autoPlay(circle) while(circle && this.controller.autoPlay(circle)){
circle = circles[this.currentCircle]
}
return
} }
var keys = this.controller.getKeys() var keys = this.controller.getKeys()
var kbd = this.controller.getBindings()
var don_l = keys[kbd["don_l"]] && !this.controller.isWaiting(kbd["don_l"], "score") var don_l = keys["don_l"] && !this.controller.isWaiting("don_l", "score")
var don_r = keys[kbd["don_r"]] && !this.controller.isWaiting(kbd["don_r"], "score") var don_r = keys["don_r"] && !this.controller.isWaiting("don_r", "score")
var ka_l = keys[kbd["ka_l"]] && !this.controller.isWaiting(kbd["ka_l"], "score") var ka_l = keys["ka_l"] && !this.controller.isWaiting("ka_l", "score")
var ka_r = keys[kbd["ka_r"]] && !this.controller.isWaiting(kbd["ka_r"], "score") var ka_r = keys["ka_r"] && !this.controller.isWaiting("ka_r", "score")
if(don_l && don_r){ var checkDon = () => {
this.checkKey([kbd["don_l"], kbd["don_r"]], circle, "daiDon") if(don_l && don_r){
}else if(don_l){ this.checkKey(["don_l", "don_r"], circle, "daiDon")
this.checkKey([kbd["don_l"]], circle, "don") }else if(don_l){
}else if(don_r){ this.checkKey(["don_l"], circle, "don")
this.checkKey([kbd["don_r"]], circle, "don") }else if(don_r){
this.checkKey(["don_r"], circle, "don")
}
} }
if(ka_l && ka_r){ var checkKa = () => {
this.checkKey([kbd["ka_l"], kbd["ka_r"]], circle, "daiKa") if(ka_l && ka_r){
}else if(ka_l){ this.checkKey(["ka_l", "ka_r"], circle, "daiKa")
this.checkKey([kbd["ka_l"]], circle, "ka") }else if(ka_l){
}else if(ka_r){ this.checkKey(["ka_l"], circle, "ka")
this.checkKey([kbd["ka_r"]], circle, "ka") }else if(ka_r){
this.checkKey(["ka_r"], circle, "ka")
}
}
var keyTime = this.controller.getKeyTime()
if(keyTime["don"] >= keyTime["ka"]){
checkDon()
checkKa()
}else{
checkKa()
checkDon()
} }
} }
checkKey(keyCodes, circle, check){ checkKey(keyCodes, circle, check){
if(circle && !circle.getPlayed()){ if(circle && !circle.isPlayed){
if(!this.checkScore(circle, check)){ if(!this.checkScore(circle, check)){
return return
} }
@ -171,7 +299,7 @@ class Game{
} }
checkScore(circle, check){ checkScore(circle, check){
var ms = this.elapsedTime var ms = this.elapsedTime
var type = circle.getType() var type = circle.type
var keysDon = check === "don" || check === "daiDon" var keysDon = check === "don" || check === "daiDon"
var keysKa = check === "ka" || check === "daiKa" var keysKa = check === "ka" || check === "daiKa"
@ -182,7 +310,14 @@ class Game{
var keyTime = this.controller.getKeyTime() var keyTime = this.controller.getKeyTime()
var currentTime = keysDon ? keyTime["don"] : keyTime["ka"] var currentTime = keysDon ? keyTime["don"] : keyTime["ka"]
var relative = currentTime - circle.getMS() var relative = currentTime - circle.ms
if(relative >= this.rules.ok){
var fixedNote = this.fixNoteStream(keysDon)
if(fixedNote){
return this.checkScore(fixedNote, check)
}
}
if(typeDon || typeKa){ if(typeDon || typeKa){
if(-this.rules.bad >= relative || relative >= this.rules.bad){ if(-this.rules.bad >= relative || relative >= this.rules.bad){
@ -213,17 +348,26 @@ class Game{
circle.played(score, score === 0 ? typeDai : keyDai) circle.played(score, score === 0 ? typeDai : keyDai)
this.controller.displayScore(score, false, typeDai && keyDai) this.controller.displayScore(score, false, typeDai && keyDai)
}else{ }else{
var keyTime = this.controller.getKeyTime()
var keyTimeRelative = Math.abs(keyTime.don - keyTime.ka)
if(Math.abs(relative) >= (keyTimeRelative <= 25 ? this.rules.bad : this.rules.good)){
return true
}
circle.played(-1, typeDai) circle.played(-1, typeDai)
this.controller.displayScore(score, true, false) this.controller.displayScore(score, true, false)
} }
this.updateCombo(score) this.updateCombo(score)
this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime) this.updateGlobalScore(score, typeDai && keyDai ? 2 : 1, circle.gogoTime)
this.updateCurrentCircle() this.updateCurrentCircle()
if(this.controller.multiplayer == 1){ if(circle.section){
this.resetSection()
}
this.sectionNotes.push(score === 450 ? 1 : (score === 230 ? 0.5 : 0))
if(this.controller.multiplayer === 1){
var value = { var value = {
score: score, score: score,
ms: circle.getMS() - currentTime, ms: circle.ms - currentTime,
dai: typeDai ? keyDai ? 2 : 1 : 0 dai: typeDai ? (keyDai ? 2 : 1) : 0
} }
if((!keysDon || !typeDon) && (!keysKa || !typeKa)){ if((!keysDon || !typeDon) && (!keysKa || !typeKa)){
value.reverse = true value.reverse = true
@ -231,12 +375,12 @@ class Game{
p2.send("note", value) p2.send("note", value)
} }
}else{ }else{
if(circle.getMS() > currentTime || currentTime > circle.getEndTime()){ if(circle.ms > currentTime || currentTime > circle.endTime){
return true return true
} }
if(keysDon && type === "balloon"){ if(keysDon && type === "balloon"){
this.checkBalloon(circle) this.checkBalloon(circle)
if(check === "daiDon" && !circle.getPlayed()){ if(check === "daiDon" && !circle.isPlayed){
this.checkBalloon(circle) this.checkBalloon(circle)
} }
}else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){ }else if((keysDon || keysKa) && (type === "drumroll" || type === "daiDrumroll")){
@ -256,24 +400,28 @@ class Game{
circle.played(score) circle.played(score)
if(this.controller.multiplayer == 1){ if(this.controller.multiplayer == 1){
p2.send("drumroll", { p2.send("drumroll", {
pace: (this.elapsedTime - circle.getMS()) / circle.timesHit pace: (this.elapsedTime - circle.ms) / circle.timesHit
}) })
} }
}else{ }else{
var score = 300 var score = 300
circle.hit() circle.hit()
} }
this.globalScore.drumroll ++ this.globalScore.drumroll++
this.sectionDrumroll++
this.globalScore.points += score this.globalScore.points += score
this.view.setDarkBg(false) this.view.setDarkBg(false)
} }
checkDrumroll(circle, keysKa){ checkDrumroll(circle, keysKa){
var ms = this.elapsedTime var ms = this.elapsedTime
var dai = circle.getType() === "daiDrumroll" var dai = circle.type === "daiDrumroll"
var score = 100 var score = 100
if(circle.section && circle.timesHit === 0){
this.resetSection()
}
circle.hit(keysKa) circle.hit(keysKa)
var keyTime = this.controller.getKeyTime() var keyTime = this.controller.getKeyTime()
if(circle.getType() === "drumroll"){ if(circle.type === "drumroll"){
var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka" var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka"
}else{ }else{
var sound = keyTime["don"] > keyTime["ka"] ? "daiDon" : "daiKa" var sound = keyTime["don"] > keyTime["ka"] ? "daiDon" : "daiKa"
@ -291,6 +439,7 @@ class Game{
circleAnim.animate(ms) circleAnim.animate(ms)
this.view.drumroll.push(circleAnim) this.view.drumroll.push(circleAnim)
this.globalScore.drumroll++ this.globalScore.drumroll++
this.sectionDrumroll++
this.globalScore.points += score * (dai ? 2 : 1) this.globalScore.points += score * (dai ? 2 : 1)
this.view.setDarkBg(false) this.view.setDarkBg(false)
} }
@ -298,11 +447,11 @@ class Game{
var ms = this.elapsedTime var ms = this.elapsedTime
if(!this.lastCircle){ if(!this.lastCircle){
var circles = this.songData.circles var circles = this.songData.circles
this.lastCircle = circles[circles.length - 1].getEndTime() this.lastCircle = circles[circles.length - 1].endTime
if(this.controller.multiplayer){ if(this.controller.multiplayer){
var syncWith = this.controller.syncWith var syncWith = this.controller.syncWith
var syncCircles = syncWith.game.songData.circles var syncCircles = syncWith.game.songData.circles
var syncLastCircle = syncCircles[syncCircles.length - 1].getEndTime() var syncLastCircle = syncCircles[syncCircles.length - 1].endTime
if(syncLastCircle > this.lastCircle){ if(syncLastCircle > this.lastCircle){
this.lastCircle = syncLastCircle this.lastCircle = syncLastCircle
} }
@ -319,7 +468,8 @@ class Game{
var started = this.fadeOutStarted var started = this.fadeOutStarted
if(started){ if(started){
var ms = this.elapsedTime var ms = this.elapsedTime
var musicDuration = this.controller.mainAsset.duration * 1000 - this.controller.offset var duration = this.mainAsset ? this.mainAsset.duration : 0
var musicDuration = duration * 1000 - this.controller.offset
if(this.musicFadeOut === 0){ if(this.musicFadeOut === 0){
if(this.controller.multiplayer === 1){ if(this.controller.multiplayer === 1){
p2.send("gameresults", this.getGlobalScore()) p2.send("gameresults", this.getGlobalScore())
@ -345,7 +495,7 @@ class Game{
playMainMusic(){ playMainMusic(){
var ms = this.elapsedTime + this.controller.offset var ms = this.elapsedTime + this.controller.offset
if(!this.mainMusicPlaying && (!this.fadeOutStarted || ms < this.fadeOutStarted + 1600)){ if(!this.mainMusicPlaying && (!this.fadeOutStarted || ms < this.fadeOutStarted + 1600)){
if(this.controller.multiplayer !== 2){ if(this.controller.multiplayer !== 2 && this.mainAsset){
this.mainAsset.play((ms < 0 ? -ms : 0) / 1000, false, Math.max(0, ms / 1000)) this.mainAsset.play((ms < 0 ? -ms : 0) / 1000, false, Math.max(0, ms / 1000))
} }
this.mainMusicPlaying = true this.mainMusicPlaying = true
@ -356,12 +506,15 @@ class Game{
assets.sounds["se_pause"].play() assets.sounds["se_pause"].play()
this.paused = true this.paused = true
this.latestDate = Date.now() this.latestDate = Date.now()
this.mainAsset.stop() if(this.mainAsset){
this.mainAsset.stop()
}
this.mainMusicPlaying = false this.mainMusicPlaying = false
this.view.pauseMove(0, true) this.view.pauseMove(0, true)
this.view.gameDiv.classList.add("game-paused") this.view.gameDiv.classList.add("game-paused")
this.view.lastMousemove = this.view.getMS() this.view.lastMousemove = this.view.getMS()
this.view.cursorHidden = false this.view.cursorHidden = false
pageEvents.send("pause")
}else{ }else{
assets.sounds["se_cancel"].play() assets.sounds["se_cancel"].play()
this.paused = false this.paused = false
@ -370,6 +523,7 @@ class Game{
this.sndTime = currentDate - snd.buffer.getTime() * 1000 this.sndTime = currentDate - snd.buffer.getTime() * 1000
this.view.gameDiv.classList.remove("game-paused") this.view.gameDiv.classList.remove("game-paused")
this.view.pointer() this.view.pointer()
pageEvents.send("unpause", currentDate - this.latestDate)
} }
} }
isPaused(){ isPaused(){
@ -385,12 +539,13 @@ class Game{
this.sndTime = this.startDate - snd.buffer.getTime() * 1000 this.sndTime = this.startDate - snd.buffer.getTime() * 1000
}else if(ms < 0 || ms >= 0 && this.started){ }else if(ms < 0 || ms >= 0 && this.started){
var currentDate = Date.now() var currentDate = Date.now()
if(!this.controller.touchEnabled){ if(this.gameLagSync){
var sndTime = currentDate - snd.buffer.getTime() * 1000 var sndTime = currentDate - snd.buffer.getTime() * 1000
var lag = sndTime - this.sndTime var lag = sndTime - this.sndTime
if(Math.abs(lag) >= 50){ if(Math.abs(lag) >= 50){
this.startDate += lag this.startDate += lag
this.sndTime = sndTime this.sndTime = sndTime
pageEvents.send("game-lag", lag)
} }
} }
this.elapsedTime = currentDate - this.startDate this.elapsedTime = currentDate - this.startDate
@ -407,7 +562,10 @@ class Game{
return this.songData.circles return this.songData.circles
} }
updateCurrentCircle(){ updateCurrentCircle(){
this.currentCircle++ var circles = this.songData.circles
do{
var circle = circles[++this.currentCircle]
}while(circle && circle.branch && !circle.branch.active)
} }
getCurrentCircle(){ getCurrentCircle(){
return this.currentCircle return this.currentCircle
@ -461,4 +619,59 @@ class Game{
} }
this.globalScore.points += Math.floor(score * multiplier / 10) * 10 this.globalScore.points += Math.floor(score * multiplier / 10) * 10
} }
setBranch(currentBranch, activeName){
var pastActive = currentBranch.active
var ms = currentBranch.ms
for(var i = 0; i < this.songData.branches.length; i++){
var branch = this.songData.branches[i]
if(branch.ms >= ms){
var relevantName = activeName
var req = branch.requirement
var noNormal = req.advanced <= 0
var noAdvanced = req.master <= 0 || req.advanced >= req.master || branch.type === "accuracy" && req.advanced > 100
var noMaster = branch.type === "accuracy" && req.master > 100
if(relevantName === "normal" && noNormal){
relevantName = noAdvanced ? "master" : "advanced"
}
if(relevantName === "advanced" && noAdvanced){
relevantName = noMaster ? "normal" : "master"
}
if(relevantName === "master" && noMaster){
relevantName = noAdvanced ? "normal" : "advanced"
}
for(var j in this.branchNames){
var name = this.branchNames[j]
if(name in branch){
branch[name].active = name === relevantName
}
}
if(branch === currentBranch){
activeName = relevantName
}
branch.active = relevantName
}
}
var circles = this.songData.circles
var circle = circles[this.currentCircle]
if(!circle || circle.branch === currentBranch[pastActive]){
var ms = this.elapsedTime
var closestCircle = circles.findIndex(circle => {
return (!circle.branch || circle.branch.active) && circle.endTime >= ms
})
if(closestCircle !== -1){
this.currentCircle = closestCircle
}
}
this.HPGain = 100 / this.songData.circles.filter(circle => {
var type = circle.type
return (type === "don" || type === "ka" || type === "daiDon" || type === "daiKa") && (!circle.branch || circle.branch.active)
}).length
if(this.controller.multiplayer === 1){
p2.send("branch", activeName)
}
}
resetSection(){
this.sectionNotes = []
this.sectionDrumroll = 0
}
} }

247
public/src/js/gameinput.js Normal file
View File

@ -0,0 +1,247 @@
class GameInput{
constructor(controller){
this.controller = controller
this.game = this.controller.game
this.keyboard = new Keyboard({
ka_l: ["ka_l"],
don_l: ["don_l"],
don_r: ["don_r"],
ka_r: ["ka_r"],
pause: ["q", "esc"],
back: ["backspace"],
previous: ["left", "up"],
next: ["right", "down"],
confirm: ["enter", "space"]
}, this.keyPress.bind(this))
this.keys = {}
this.waitKeyupScore = {}
this.waitKeyupSound = {}
this.waitKeyupMenu = {}
this.keyTime = {
"don": -Infinity,
"ka": -Infinity
}
this.keyboardEvents = 0
var layout = settings.getItem("gamepadLayout")
if(layout === "b"){
var gameBtn = {
don_l: ["d", "r", "ls"],
don_r: ["a", "x", "rs"],
ka_l: ["u", "l", "lb", "lt"],
ka_r: ["b", "y", "rb", "rt"]
}
}else if(layout === "c"){
var gameBtn = {
don_l: ["d", "l", "ls"],
don_r: ["a", "b", "rs"],
ka_l: ["u", "r", "lb", "lt"],
ka_r: ["x", "y", "rb", "rt"]
}
}else{
var gameBtn = {
don_l: ["u", "d", "l", "r", "ls"],
don_r: ["a", "b", "x", "y", "rs"],
ka_l: ["lb", "lt"],
ka_r: ["rb", "rt"]
}
}
this.gamepad = new Gamepad(gameBtn)
this.gamepadInterval = setInterval(this.gamepadKeys.bind(this), 1000 / 60 / 2)
this.gamepadMenu = new Gamepad({
cancel: ["a"],
confirm: ["b", "ls", "rs"],
previous: ["u", "l", "lb", "lt", "lsu", "lsl"],
next: ["d", "r", "rb", "rt", "lsd", "lsr"],
pause: ["start"]
})
if(controller.multiplayer === 1){
pageEvents.add(window, "beforeunload", event => {
if(p2.otherConnected){
pageEvents.send("p2-abandoned", event)
}
})
}
}
keyPress(pressed, name){
if(!this.controller.autoPlayEnabled || this.game.isPaused() || name !== "don_l" && name !== "don_r" && name !== "ka_l" && name !== "ka_r"){
this.setKey(pressed, name, this.game.getAccurateTime())
}
}
checkGameKeys(){
if(this.controller.autoPlayEnabled){
this.checkKeySound("don_l", "don")
this.checkKeySound("don_r", "don")
this.checkKeySound("ka_l", "ka")
this.checkKeySound("ka_r", "ka")
}
}
gamepadKeys(){
if(!this.game.isPaused() && !this.controller.autoPlayEnabled){
this.gamepad.play((pressed, name) => {
if(pressed){
if(this.keys[name]){
this.setKey(false, name)
}
this.setKey(true, name, this.game.getAccurateTime())
}else{
this.setKey(false, name)
}
})
}
}
checkMenuKeys(){
if(!this.controller.multiplayer && !this.locked){
var moveMenu = 0
var ms = this.game.getAccurateTime()
this.gamepadMenu.play((pressed, name) => {
if(pressed){
if(this.game.isPaused()){
if(name === "cancel"){
this.locked = true
return setTimeout(() => {
this.controller.togglePause()
this.locked = false
}, 200)
}
}
if(this.keys[name]){
this.setKey(false, name)
}
this.setKey(true, name, ms)
}else{
this.setKey(false, name)
}
})
this.checkKey("pause", "menu", () => {
this.controller.togglePause()
for(var key in this.keyTime){
this.keys[key] = null
this.keyTime[key] = -Infinity
}
})
var moveMenuMinus = () => {
moveMenu = -1
}
var moveMenuPlus = () => {
moveMenu = 1
}
var moveMenuConfirm = () => {
if(this.game.isPaused()){
this.locked = true
setTimeout(() => {
this.controller.view.pauseConfirm()
this.locked = false
}, 200)
}
}
this.checkKey("previous", "menu", moveMenuMinus)
this.checkKey("ka_l", "menu", moveMenuMinus)
this.checkKey("next", "menu", moveMenuPlus)
this.checkKey("ka_r", "menu", moveMenuPlus)
this.checkKey("confirm", "menu", moveMenuConfirm)
this.checkKey("don_l", "menu", moveMenuConfirm)
this.checkKey("don_r", "menu", moveMenuConfirm)
if(moveMenu && this.game.isPaused()){
assets.sounds["se_ka"].play()
this.controller.view.pauseMove(moveMenu)
}
}
if(this.controller.multiplayer !== 2){
this.checkKey("back", "menu", () => {
if(this.controller.multiplayer === 1 && p2.otherConnected){
p2.send("gameend")
pageEvents.send("p2-abandoned")
}
this.controller.togglePause()
this.controller.songSelection()
})
}
}
checkKey(name, type, callback){
if(this.keys[name] && !this.isWaiting(name, type)){
this.waitForKeyup(name, type)
callback()
}
}
checkKeySound(name, sound){
this.checkKey(name, "sound", () => {
var circles = this.controller.getCircles()
var circle = circles[this.controller.getCurrentCircle()]
var currentTime = this.keyTime[name]
this.keyTime[sound] = currentTime
if(circle && !circle.isPlayed){
if(circle.type === "balloon"){
if(sound === "don" && circle.requiredHits - circle.timesHit <= 1){
this.controller.playSound("se_balloon")
return
}
}
}
this.controller.playSound("neiro_1_" + sound)
})
}
getKeys(){
return this.keys
}
setKey(pressed, name, ms){
if(pressed){
this.keys[name] = true
this.waitKeyupScore[name] = false
this.waitKeyupSound[name] = false
this.waitKeyupMenu[name] = false
if(this.game.isPaused()){
return
}
this.keyTime[name] = ms
if(name == "don_l" || name == "don_r"){
this.checkKeySound(name, "don")
this.keyboardEvents++
}else if(name == "ka_l" || name == "ka_r"){
this.checkKeySound(name, "ka")
this.keyboardEvents++
}
}else{
this.keys[name] = false
this.waitKeyupScore[name] = false
this.waitKeyupSound[name] = false
this.waitKeyupMenu[name] = false
}
}
isWaiting(name, type){
if(type === "score"){
return this.waitKeyupScore[name]
}else if(type === "sound"){
return this.waitKeyupSound[name]
}else if(type === "menu"){
return this.waitKeyupMenu[name]
}
}
waitForKeyup(name, type){
if(!this.keys[name]){
return
}
if(type === "score"){
this.waitKeyupScore[name] = true
}else if(type === "sound"){
this.waitKeyupSound[name] = true
}else if(type === "menu"){
this.waitKeyupMenu[name] = true
}
}
getKeyTime(){
return this.keyTime
}
clean(){
this.keyboard.clean()
this.gamepad.clean()
this.gamepadMenu.clean()
clearInterval(this.gamepadInterval)
if(this.controller.multiplayer === 1){
pageEvents.remove(window, "beforeunload")
}
}
}

View File

@ -1,6 +1,7 @@
class Gamepad{ class Gamepad{
constructor(bindings, callback){ constructor(bindings, callback){
this.bindings = bindings this.bindings = bindings
this.callback = !!callback
this.b = { this.b = {
"a": 0, "a": 0,
"b": 1, "b": 1,
@ -25,6 +26,7 @@ class Gamepad{
"lsl": "lsl" "lsl": "lsl"
} }
this.btn = {} this.btn = {}
this.gamepadEvents = 0
if(callback){ if(callback){
this.interval = setInterval(() => { this.interval = setInterval(() => {
this.play(callback) this.play(callback)
@ -86,6 +88,9 @@ class Gamepad{
for(var name in bindings[bind]){ for(var name in bindings[bind]){
var bindName = bindings[bind][name] var bindName = bindings[bind][name]
this.checkButton(gamepads, this.b[bindName], bind, callback, force[bindName]) this.checkButton(gamepads, this.b[bindName], bind, callback, force[bindName])
if(!this.b){
return
}
} }
} }
break break
@ -123,6 +128,7 @@ class Gamepad{
if(pressed){ if(pressed){
callback(true, keyCode) callback(true, keyCode)
this.gamepadEvents++
}else if(!button){ }else if(!button){
if(released){ if(released){
this.toRelease[keyCode + "released"] = true this.toRelease[keyCode + "released"] = true
@ -134,6 +140,11 @@ class Gamepad{
} }
} }
clean(){ clean(){
clearInterval(this.interval) if(this.callback){
clearInterval(this.interval)
}
delete this.bindings
delete this.b
delete this.btn
} }
} }

View File

@ -29,6 +29,8 @@
this.otherFiles = {} this.otherFiles = {}
this.songs = [] this.songs = []
this.stylesheet = [] this.stylesheet = []
this.songTitle = {}
this.uraRegex = /\s*[\(]裏[\)]$/
this.courseTypes = { this.courseTypes = {
"easy": 0, "easy": 0,
"normal": 1, "normal": 1,
@ -82,7 +84,7 @@
file: file, file: file,
index: i index: i
}) })
}else if(name === "genre.ini" || name === "box.def"){ }else if(name === "genre.ini" || name === "box.def" || name === "songtitle.txt"){
var level = (file.webkitRelativePath.match(/\//g) || []).length var level = (file.webkitRelativePath.match(/\//g) || []).length
metaFiles.push({ metaFiles.push({
file: file, file: file,
@ -154,6 +156,20 @@
break break
} }
} }
}else if(name === "songtitle.txt"){
var lastTitle
for(var i = 0; i < data.length; i++){
var line = data[i].trim()
if(line){
var lang = line.slice(0, 2)
if(line.charAt(2) !== " " || !(lang in allStrings)){
this.songTitle[line] = {}
lastTitle = line
}else if(lastTitle){
this.songTitle[lastTitle][lang] = line.slice(3).trim()
}
}
}
} }
if(category){ if(category){
var metaPath = file.webkitRelativePath.toLowerCase().slice(0, file.name.length * -1) var metaPath = file.webkitRelativePath.toLowerCase().slice(0, file.name.length * -1)
@ -168,7 +184,11 @@
this.osuFiles.forEach(filesLoop) this.osuFiles.forEach(filesLoop)
} }
}).catch(() => {}) }).catch(() => {})
reader.readAsText(file, "sjis") if(name === "songtitle.txt"){
reader.readAsText(file)
}else{
reader.readAsText(file, "sjis")
}
return promise return promise
} }
@ -183,26 +203,27 @@
var songObj = { var songObj = {
id: index + 1, id: index + 1,
type: "tja", type: "tja",
chart: data, chart: file,
stars: [] stars: [],
music: "muted"
} }
var titleLang = {}
var subtitleLang = {}
var dir = file.webkitRelativePath.toLowerCase() var dir = file.webkitRelativePath.toLowerCase()
dir = dir.slice(0, dir.lastIndexOf("/") + 1) dir = dir.slice(0, dir.lastIndexOf("/") + 1)
var hasCategory = false var hasCategory = false
for(var diff in tja.metadata){ for(var diff in tja.metadata){
var meta = tja.metadata[diff] var meta = tja.metadata[diff]
songObj.title = songObj.title_en = meta.title || file.name.slice(0, file.name.lastIndexOf(".")) songObj.title = meta.title || file.name.slice(0, file.name.lastIndexOf("."))
var subtitle = meta.subtitle || "" var subtitle = meta.subtitle || ""
if(subtitle.startsWith("--")){ if(subtitle.startsWith("--") || subtitle.startsWith("++")){
subtitle = subtitle.slice(2) subtitle = subtitle.slice(2).trim()
}
songObj.subtitle = songObj.subtitle_en = subtitle
songObj.preview = meta.demostart ? Math.floor(meta.demostart * 1000) : 0
if(meta.level){
songObj.stars[this.courseTypes[diff]] = meta.level
} }
songObj.subtitle = subtitle
songObj.preview = meta.demostart || 0
songObj.stars[this.courseTypes[diff]] = (meta.level || "0") + (meta.branch ? " B" : "")
if(meta.wave){ if(meta.wave){
songObj.music = this.otherFiles[dir + meta.wave.toLowerCase()] songObj.music = this.otherFiles[dir + meta.wave.toLowerCase()] || songObj.music
} }
if(meta.genre){ if(meta.genre){
songObj.category = this.categories[meta.genre.toLowerCase()] || meta.genre songObj.category = this.categories[meta.genre.toLowerCase()] || meta.genre
@ -210,11 +231,44 @@
if(meta.taikowebskin){ if(meta.taikowebskin){
songObj.song_skin = this.getSkin(dir, meta.taikowebskin) songObj.song_skin = this.getSkin(dir, meta.taikowebskin)
} }
for(var id in allStrings){
var songTitle = songObj.title
var ura = ""
if(songTitle){
var uraPos = songTitle.search(this.uraRegex)
if(uraPos !== -1){
ura = songTitle.slice(uraPos)
songTitle = songTitle.slice(0, uraPos)
}
}
if(meta["title" + id]){
titleLang[id] = meta["title" + id]
}else if(songTitle in this.songTitle && this.songTitle[songTitle][id]){
titleLang[id] = this.songTitle[songTitle][id] + ura
}
if(meta["subtitle" + id]){
subtitleLang[id] = meta["subtitle" + id]
}
}
}
var titleLangArray = []
for(var id in titleLang){
titleLangArray.push(id + " " + titleLang[id])
}
if(titleLangArray.length !== 0){
songObj.title_lang = titleLangArray.join("\n")
}
var subtitleLangArray = []
for(var id in subtitleLang){
subtitleLangArray.push(id + " " + subtitleLang[id])
}
if(subtitleLangArray.length !== 0){
songObj.subtitle_lang = subtitleLangArray.join("\n")
} }
if(!songObj.category){ if(!songObj.category){
songObj.category = category || this.getCategory(file) songObj.category = category || this.getCategory(file)
} }
if(songObj.music && songObj.stars.filter(star => star).length !== 0){ if(songObj.stars.length !== 0){
this.songs[index] = songObj this.songs[index] = songObj
} }
}).catch(() => {}) }).catch(() => {})
@ -235,12 +289,12 @@
var songObj = { var songObj = {
id: index + 1, id: index + 1,
type: "osu", type: "osu",
chart: data, chart: file,
subtitle: osu.metadata.ArtistUnicode || osu.metadata.Artist, subtitle: osu.metadata.ArtistUnicode || osu.metadata.Artist,
subtitle_en: osu.metadata.Artist || osu.metadata.ArtistUnicode, subtitle_lang: osu.metadata.Artist || osu.metadata.ArtistUnicode,
preview: osu.generalInfo.PreviewTime, preview: osu.generalInfo.PreviewTime / 1000,
stars: [null, null, null, parseInt(osu.difficulty.overallDifficulty) || 1], stars: [null, null, null, parseInt(osu.difficulty.overallDifficulty) || 1],
music: this.otherFiles[dir + osu.generalInfo.AudioFilename.toLowerCase()] music: this.otherFiles[dir + osu.generalInfo.AudioFilename.toLowerCase()] || "muted"
} }
var filename = file.name.slice(0, file.name.lastIndexOf(".")) var filename = file.name.slice(0, file.name.lastIndexOf("."))
var title = osu.metadata.TitleUnicode || osu.metadata.Title var title = osu.metadata.TitleUnicode || osu.metadata.Title
@ -251,13 +305,11 @@
suffix = " " + matches[0] suffix = " " + matches[0]
} }
songObj.title = title + suffix songObj.title = title + suffix
songObj.title_en = (osu.metadata.Title || osu.metadata.TitleUnicode) + suffix songObj.title_lang = (osu.metadata.Title || osu.metadata.TitleUnicode) + suffix
}else{ }else{
songObj.title = filename songObj.title = filename
} }
if(songObj.music){ this.songs[index] = songObj
this.songs[index] = songObj
}
songObj.category = category || this.getCategory(file) songObj.category = category || this.getCategory(file)
}).catch(() => {}) }).catch(() => {})
reader.readAsText(file) reader.readAsText(file)
@ -386,6 +438,7 @@
document.head.appendChild(style) document.head.appendChild(style)
} }
if(this.songs.length){ if(this.songs.length){
var length = this.songs.length
assets.songs = this.songs assets.songs = this.songs
assets.customSongs = true assets.customSongs = true
assets.customSelected = 0 assets.customSelected = 0
@ -395,6 +448,7 @@
loader.screen.removeChild(this.loaderDiv) loader.screen.removeChild(this.loaderDiv)
this.clean() this.clean()
new SongSelect("browse", false, this.songSelect.touchEnabled) new SongSelect("browse", false, this.songSelect.touchEnabled)
pageEvents.send("import-songs", length)
}, 500) }, 500)
}else{ }else{
loader.screen.removeChild(this.loaderDiv) loader.screen.removeChild(this.loaderDiv)

View File

@ -1,247 +1,105 @@
class Keyboard{ class Keyboard{
constructor(controller){ constructor(bindings, callback){
this.controller = controller this.bindings = bindings
this.game = this.controller.game this.callback = callback
this.wildcard = false
this.kbd = { this.substitute = {
"don_l": 70, // F "up": "arrowup",
"don_r": 74, // J "right": "arrowright",
"ka_l": 68, // D "down": "arrowdown",
"ka_r": 75, // K "left": "arrowleft",
"pause": 81, // Q "space": " ",
"back": 8, // Backspace "esc": "escape",
"previous": 37, // Left "ctrl": "control",
"next": 39, // Right "altgr": "altgraph"
"confirm": 13 // Enter
} }
this.kbdAlias = { this.btn = {}
"pause": [27], // Esc this.update()
"previous": [38], // Up pageEvents.keyAdd(this, "all", "both", this.keyEvent.bind(this))
"next": [40], // Down pageEvents.blurAdd(this, this.blurEvent.bind(this))
"confirm": [32] // Space }
} update(){
this.keys = {} var kbdSettings = settings.getItem("keyboardSettings")
this.waitKeyupScore = {} var drumKeys = {}
this.waitKeyupSound = {} for(var name in kbdSettings){
this.waitKeyupMenu = {} var keys = kbdSettings[name]
this.keyTime = { for(var i in keys){
"don": -Infinity, drumKeys[keys[i]] = name
"ka": -Infinity
}
var gameBtn = {}
gameBtn[this.kbd["don_l"]] = ["u", "d", "l", "r", "ls"]
gameBtn[this.kbd["don_r"]] = ["a", "b", "x", "y", "rs"]
gameBtn[this.kbd["ka_l"]] = ["lb", "lt"]
gameBtn[this.kbd["ka_r"]] = ["rb", "rt"]
this.gamepad = new Gamepad(gameBtn)
this.gamepadInterval = setInterval(this.gamepadKeys.bind(this), 1000 / 60 / 2)
var menuBtn = {
"cancel": ["a"],
}
menuBtn[this.kbd["confirm"]] = ["b", "ls", "rs"]
menuBtn[this.kbd["previous"]] = ["u", "l", "lb", "lt", "lsu", "lsl"],
menuBtn[this.kbd["next"]] = ["d", "r", "rb", "rt", "lsd", "lsr"]
menuBtn[this.kbd["pause"]] = ["start"]
this.gamepadMenu = new Gamepad(menuBtn)
this.kbdSearch = {}
for(var name in this.kbdAlias){
var list = this.kbdAlias[name]
for(var i in list){
this.kbdSearch[list[i]] = this.kbd[name]
} }
} }
for(var name in this.kbd){ this.kbd = {}
this.kbdSearch[this.kbd[name]] = this.kbd[name] for(var name in this.bindings){
} var keys = this.bindings[name]
for(var i in keys){
pageEvents.keyAdd(this, "all", "both", event => { var key = keys[i]
if(event.keyCode === 8){ if(key in drumKeys){
// Disable back navigation when pressing backspace continue
event.preventDefault()
}
var key = this.kbdSearch[event.keyCode]
if(key && !event.repeat && this.buttonEnabled(key)){
var ms = this.game.getAccurateTime()
this.setKey(key, event.type === "keydown", ms)
}
})
}
getBindings(){
return this.kbd
}
buttonEnabled(keyCode){
if(this.controller.autoPlayEnabled){
switch(keyCode){
case this.kbd["don_l"]:
case this.kbd["don_r"]:
case this.kbd["ka_l"]:
case this.kbd["ka_r"]:
return false
}
}
return true
}
checkGameKeys(){
if(this.controller.autoPlayEnabled){
this.checkKeySound(this.kbd["don_l"], "don")
this.checkKeySound(this.kbd["don_r"], "don")
this.checkKeySound(this.kbd["ka_l"], "ka")
this.checkKeySound(this.kbd["ka_r"], "ka")
}
}
gamepadKeys(){
if(!this.game.isPaused() && !this.controller.autoPlayEnabled){
this.gamepad.play((pressed, keyCode) => {
if(pressed){
if(this.keys[keyCode]){
this.setKey(keyCode, false)
}
this.setKey(keyCode, true, this.game.getAccurateTime())
}else{
this.setKey(keyCode, false)
} }
}) if(key in kbdSettings){
} var keyArray = kbdSettings[key]
} for(var j in keyArray){
checkMenuKeys(){ key = keyArray[j]
if(!this.controller.multiplayer && !this.locked){ if(!(key in this.kbd)){
var moveMenu = 0 this.kbd[key] = name
var ms = this.game.getAccurateTime()
this.gamepadMenu.play((pressed, keyCode) => {
if(pressed){
if(this.game.isPaused()){
if(keyCode === "cancel"){
this.locked = true
return setTimeout(() => {
this.controller.togglePause()
this.locked = false
}, 200)
} }
} }
if(this.keys[keyCode]){
this.setKey(keyCode, false)
}
this.setKey(keyCode, true, ms)
}else{ }else{
this.setKey(keyCode, false) if(key in this.substitute){
} key = this.substitute[key]
}) }
this.checkKey(this.kbd["pause"], "menu", () => { if(!(key in this.kbd)){
this.controller.togglePause() if(key === "wildcard"){
for(var key in this.keyTime){ this.wildcard = true
this.keys[key] = null }
this.keyTime[key] = -Infinity this.kbd[key] = name
} }
})
var moveMenuMinus = () => {
moveMenu = -1
}
var moveMenuPlus = () => {
moveMenu = 1
}
var moveMenuConfirm = () => {
if(this.game.isPaused()){
this.locked = true
setTimeout(() => {
this.controller.view.pauseConfirm()
this.locked = false
}, 200)
} }
} }
this.checkKey(this.kbd["previous"], "menu", moveMenuMinus)
this.checkKey(this.kbd["ka_l"], "menu", moveMenuMinus)
this.checkKey(this.kbd["next"], "menu", moveMenuPlus)
this.checkKey(this.kbd["ka_r"], "menu", moveMenuPlus)
this.checkKey(this.kbd["confirm"], "menu", moveMenuConfirm)
this.checkKey(this.kbd["don_l"], "menu", moveMenuConfirm)
this.checkKey(this.kbd["don_r"], "menu", moveMenuConfirm)
if(moveMenu && this.game.isPaused()){
assets.sounds["se_ka"].play()
this.controller.view.pauseMove(moveMenu)
}
}
if(this.controller.multiplayer !== 2){
this.checkKey(this.kbd["back"], "menu", () => {
if(this.controller.multiplayer === 1){
p2.send("gameend")
}
this.controller.togglePause()
this.controller.songSelection()
})
} }
} }
checkKey(keyCode, type, callback){ keyEvent(event){
if(this.keys[keyCode] && !this.isWaiting(keyCode, type)){ var key = event.key.toLowerCase()
this.waitForKeyup(keyCode, type) if(key === "escape" || key === "backspace" || key === "tab"){
callback() event.preventDefault()
} }
} if(!event.repeat){
checkKeySound(keyCode, sound){ var pressed = event.type === "keydown"
this.checkKey(keyCode, "sound", () => { if(pressed){
var circles = this.controller.getCircles() this.btn[key] = true
var circle = circles[this.controller.getCurrentCircle()]
if(
sound === "don"
&& circle
&& !circle.getPlayed()
&& circle.getType() === "balloon"
&& circle.requiredHits - circle.timesHit <= 1
){
this.controller.playSound("se_balloon")
}else{ }else{
this.controller.playSound("neiro_1_" + sound) delete this.btn[key]
if(key in this.kbd){
for(var i in this.btn){
if(this.kbd[i] === this.kbd[key]){
return
}
}
}
} }
this.keyTime[sound] = this.keyTime[keyCode] if(key in this.kbd){
}) this.callback(pressed, this.kbd[key], event)
} }else if(this.wildcard){
getKeys(){ this.callback(pressed, this.kbd["wildcard"], event)
return this.keys
}
setKey(keyCode, down, ms){
if(down){
this.keys[keyCode] = true
if(this.game.isPaused()){
return
} }
this.keyTime[keyCode] = ms
if(keyCode == this.kbd.don_l || keyCode == this.kbd.don_r){
this.checkKeySound(keyCode, "don")
}else if(keyCode == this.kbd.ka_l || keyCode == this.kbd.ka_r){
this.checkKeySound(keyCode, "ka")
}
}else{
this.keys[keyCode] = false
this.waitKeyupScore[keyCode] = false
this.waitKeyupSound[keyCode] = false
this.waitKeyupMenu[keyCode] = false
} }
} }
isWaiting(keyCode, type){ blurEvent(){
if(type === "score"){ for(var key in this.btn){
return this.waitKeyupScore[keyCode] if(this.btn[key]){
}else if(type === "sound"){ delete this.btn[key]
return this.waitKeyupSound[keyCode] var name = this.kbd[key] || (this.wildcard ? "wildcard" : false)
}else if(type === "menu"){ if(name){
return this.waitKeyupMenu[keyCode] this.callback(false, name)
}
}
} }
} }
waitForKeyup(keyCode, type){
if(type === "score"){
this.waitKeyupScore[keyCode] = true
}else if(type === "sound"){
this.waitKeyupSound[keyCode] = true
}else if(type === "menu"){
this.waitKeyupMenu[keyCode] = true
}
}
getKeyTime(){
return this.keyTime
}
clean(){ clean(){
pageEvents.keyRemove(this, "all") pageEvents.keyRemove(this, "all")
clearInterval(this.gamepadInterval) pageEvents.blurRemove(this)
delete this.bindings
delete this.callback
delete this.kbd
delete this.btn
} }
} }

View File

@ -25,6 +25,12 @@ class Loader{
var queryString = gameConfig._version.commit_short ? "?" + gameConfig._version.commit_short : "" var queryString = gameConfig._version.commit_short ? "?" + gameConfig._version.commit_short : ""
if(gameConfig.custom_js){
var script = document.createElement("script")
this.addPromise(pageEvents.load(script))
script.src = gameConfig.custom_js + queryString
document.head.appendChild(script)
}
assets.js.forEach(name => { assets.js.forEach(name => {
var script = document.createElement("script") var script = document.createElement("script")
this.addPromise(pageEvents.load(script)) this.addPromise(pageEvents.load(script))
@ -32,7 +38,15 @@ class Loader{
document.head.appendChild(script) document.head.appendChild(script)
}) })
this.addPromise(new Promise(resolve => { this.addPromise(new Promise((resolve, reject) => {
if(
versionLink.href !== gameConfig._version.url &&
gameConfig._version.commit &&
versionLink.href.indexOf(gameConfig._version.commit) === -1
){
// Version in the config does not match version on the page
reject()
}
var cssCount = document.styleSheets.length + assets.css.length var cssCount = document.styleSheets.length + assets.css.length
assets.css.forEach(name => { assets.css.forEach(name => {
var stylesheet = document.createElement("link") var stylesheet = document.createElement("link")
@ -112,6 +126,7 @@ class Loader{
0.5 0.5
) )
snd.sfxLoudGain.setVolume(1.2) snd.sfxLoudGain.setVolume(1.2)
snd.buffer.saveSettings()
this.afterJSCount = 0 this.afterJSCount = 0
@ -147,17 +162,29 @@ class Loader{
} }
})) }))
var readyEvent = "normal"
var songId
var hashLower = location.hash.toLowerCase()
p2 = new P2Connection() p2 = new P2Connection()
if(location.hash.length === 6){ if(hashLower.startsWith("#song=")){
var number = parseInt(location.hash.slice(6))
if(number > 0){
songId = number
readyEvent = "song-id"
}
}else if(location.hash.length === 6){
p2.hashLock = true p2.hashLock = true
this.addPromise(new Promise(resolve => { this.addPromise(new Promise(resolve => {
p2.open() p2.open()
pageEvents.add(p2, "message", response => { pageEvents.add(p2, "message", response => {
if(response.type === "session"){ if(response.type === "session"){
pageEvents.send("session-start", "invited")
readyEvent = "session-start"
resolve() resolve()
}else if(response.type === "gameend"){ }else if(response.type === "gameend"){
p2.hash("") p2.hash("")
p2.hashLock = false p2.hashLock = false
readyEvent = "session-expired"
resolve() resolve()
} }
}) })
@ -176,13 +203,17 @@ class Loader{
p2.hash("") p2.hash("")
} }
settings = new Settings()
pageEvents.setKbd()
Promise.all(this.promises).then(() => { Promise.all(this.promises).then(() => {
this.canvasTest.drawAllImages().then(result => { this.canvasTest.drawAllImages().then(result => {
perf.allImg = result perf.allImg = result
perf.load = Date.now() - this.startTime perf.load = Date.now() - this.startTime
this.canvasTest.clean() this.canvasTest.clean()
this.clean() this.clean()
this.callback() this.callback(songId)
pageEvents.send("ready", readyEvent)
}) })
}, this.errorMsg.bind(this)) }, this.errorMsg.bind(this))
@ -192,7 +223,7 @@ class Loader{
} }
addPromise(promise){ addPromise(promise){
this.promises.push(promise) this.promises.push(promise)
promise.then(this.assetLoaded.bind(this)) promise.then(this.assetLoaded.bind(this), this.errorMsg.bind(this))
} }
loadSound(name, gain){ loadSound(name, gain){
var id = this.getFilename(name) var id = this.getFilename(name)
@ -205,6 +236,7 @@ class Loader{
} }
errorMsg(error){ errorMsg(error){
console.error(error) console.error(error)
pageEvents.send("loader-error", error)
this.error = true this.error = true
this.loaderPercentage.appendChild(document.createElement("br")) this.loaderPercentage.appendChild(document.createElement("br"))
this.loaderPercentage.appendChild(document.createTextNode("An error occurred, please refresh")) this.loaderPercentage.appendChild(document.createTextNode("An error occurred, please refresh"))
@ -237,7 +269,9 @@ class Loader{
} }
clean(){ clean(){
var fontDetectDiv = document.getElementById("fontdetectHelper") var fontDetectDiv = document.getElementById("fontdetectHelper")
fontDetectDiv.parentNode.removeChild(fontDetectDiv) if(fontDetectDiv){
fontDetectDiv.parentNode.removeChild(fontDetectDiv)
}
delete this.loaderPercentage delete this.loaderPercentage
delete this.loaderProgress delete this.loaderProgress
delete this.promises delete this.promises

View File

@ -4,6 +4,15 @@ class LoadSong{
this.autoPlayEnabled = autoPlayEnabled this.autoPlayEnabled = autoPlayEnabled
this.multiplayer = multiplayer this.multiplayer = multiplayer
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
var resolution = settings.getItem("resolution")
this.imgScale = 1
if(resolution === "medium"){
this.imgScale = 0.75
}else if(resolution === "low"){
this.imgScale = 0.5
}else if(resolution === "lowest"){
this.imgScale = 0.25
}
loader.changePage("loadsong", true) loader.changePage("loadsong", true)
var loadingText = document.getElementById("loading-text") var loadingText = document.getElementById("loading-text")
@ -15,6 +24,12 @@ class LoadSong{
cancel.setAttribute("alt", strings.cancel) cancel.setAttribute("alt", strings.cancel)
} }
this.run() this.run()
pageEvents.send("load-song", {
selectedSong: selectedSong,
autoPlayEnabled: autoPlayEnabled,
multiplayer: multiplayer,
touchEnabled: touchEnabled
})
} }
run(){ run(){
var song = this.selectedSong var song = this.selectedSong
@ -51,9 +66,10 @@ class LoadSong{
} }
if(type === "don"){ if(type === "don"){
song.donBg = null song.donBg = null
} }else if(type === "song"){
if(type === "song"){
song.songBg = null song.songBg = null
}else if(type === "stage"){
song.songStage = null
} }
} }
} }
@ -65,19 +81,14 @@ class LoadSong{
continue continue
} }
let img = document.createElement("img") let img = document.createElement("img")
if(!songObj.music && this.touchEnabled && imgLoad[i].type === "song"){ let force = imgLoad[i].type === "song" && this.touchEnabled
if(!songObj.music && (this.imgScale !== 1 || force)){
img.crossOrigin = "Anonymous" img.crossOrigin = "Anonymous"
} }
let promise = pageEvents.load(img) let promise = pageEvents.load(img)
if(imgLoad[i].type === "song"){ promises.push(promise.then(() => {
promises.push(promise.then(() => { return this.scaleImg(img, filename, prefix, force)
return this.scaleImg(img, filename, prefix) }))
}))
}else{
promises.push(promise.then(() => {
assets.image[prefix + filename] = img
}))
}
if(songObj.music){ if(songObj.music){
img.src = URL.createObjectURL(song.songSkin[filename + ".png"]) img.src = URL.createObjectURL(song.songSkin[filename + ".png"])
}else{ }else{
@ -91,32 +102,53 @@ class LoadSong{
if(songObj.sound){ if(songObj.sound){
songObj.sound.gain = snd.musicGain songObj.sound.gain = snd.musicGain
resolve() resolve()
}else if(songObj.music){ }else if(!songObj.music){
snd.musicGain.load(gameConfig.songs_baseurl + id + "/main.mp3").then(sound => {
songObj.sound = sound
resolve()
}, reject)
}else if(songObj.music !== "muted"){
snd.musicGain.load(songObj.music, true).then(sound => { snd.musicGain.load(songObj.music, true).then(sound => {
songObj.sound = sound songObj.sound = sound
resolve() resolve()
}, reject) }, reject)
}else{ }else{
snd.musicGain.load(gameConfig.songs_baseurl + id + "/main.ogg").then(sound => { resolve()
songObj.sound = sound
resolve()
}, reject)
} }
})) }))
if(songObj.chart){ if(songObj.chart){
this.songData = songObj.chart var reader = new FileReader()
promises.push(pageEvents.load(reader).then(event => {
this.songData = event.target.result.replace(/\0/g, "").split("\n")
}))
if(song.type === "tja"){
reader.readAsText(songObj.chart, "sjis")
}else{
reader.readAsText(songObj.chart)
}
}else{ }else{
promises.push(loader.ajax(this.getSongPath(song)).then(data => { promises.push(loader.ajax(this.getSongPath(song)).then(data => {
this.songData = data.replace(/\0/g, "").split("\n") this.songData = data.replace(/\0/g, "").split("\n")
})) }))
} }
if(this.touchEnabled && !assets.image["touch_drum"]){
let img = document.createElement("img")
if(this.imgScale !== 1){
img.crossOrigin = "Anonymous"
}
promises.push(pageEvents.load(img).then(() => {
return this.scaleImg(img, "touch_drum", "")
}))
img.src = gameConfig.assets_baseurl + "img/touch_drum.png"
}
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
this.setupMultiplayer() this.setupMultiplayer()
}, error => { }, error => {
console.error(error)
if(Array.isArray(error) && error[1] instanceof HTMLElement){ if(Array.isArray(error) && error[1] instanceof HTMLElement){
error = error[0] + ": " + error[1].outerHTML error = error[0] + ": " + error[1].outerHTML
} }
console.error(error)
pageEvents.send("load-song-error", error)
errorMessage(new Error(error).stack) errorMessage(new Error(error).stack)
alert("An error occurred, please refresh") alert("An error occurred, please refresh")
}) })
@ -134,23 +166,23 @@ class LoadSong{
filenames.push("bg_don2_" + this.selectedSong.donBg) filenames.push("bg_don2_" + this.selectedSong.donBg)
} }
} }
if(this.selectedSong.songStage !== null){
filenames.push("bg_stage_" + this.selectedSong.songStage)
}
for(var i = 0; i < filenames.length; i++){ for(var i = 0; i < filenames.length; i++){
for(var letter = 0; letter < 2; letter++){ var filename = filenames[i]
let filenameAb = filenames[i] + (letter === 0 ? "a" : "b") var stage = filename.startsWith("bg_stage_")
for(var letter = 0; letter < (stage ? 1 : 2); letter++){
let filenameAb = filenames[i] + (stage ? "" : (letter === 0 ? "a" : "b"))
if(!(filenameAb in assets.image)){ if(!(filenameAb in assets.image)){
let img = document.createElement("img") let img = document.createElement("img")
if(filenameAb.startsWith("bg_song_")){ let force = filenameAb.startsWith("bg_song_") && this.touchEnabled
if(this.touchEnabled){ if(this.imgScale !== 1 || force){
img.crossOrigin = "Anonymous" img.crossOrigin = "Anonymous"
}
promises.push(pageEvents.load(img).then(() => {
return this.scaleImg(img, filenameAb, "")
}))
}else{
promises.push(pageEvents.load(img).then(() => {
assets.image[filenameAb] = img
}))
} }
promises.push(pageEvents.load(img).then(() => {
return this.scaleImg(img, filenameAb, "", force)
}))
img.src = gameConfig.assets_baseurl + "img/" + filenameAb + ".png" img.src = gameConfig.assets_baseurl + "img/" + filenameAb + ".png"
} }
} }
@ -158,12 +190,16 @@ class LoadSong{
Promise.all(promises).then(resolve, reject) Promise.all(promises).then(resolve, reject)
}) })
} }
scaleImg(img, filename, prefix){ scaleImg(img, filename, prefix, force){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if(this.touchEnabled){ var scale = this.imgScale
if(force && scale > 0.5){
scale = 0.5
}
if(scale !== 1){
var canvas = document.createElement("canvas") var canvas = document.createElement("canvas")
var w = Math.floor(img.width / 2) var w = Math.floor(img.width * scale)
var h = Math.floor(img.height / 2) var h = Math.floor(img.height * scale)
canvas.width = w canvas.width = w
canvas.height = h canvas.height = h
var ctx = canvas.getContext("2d") var ctx = canvas.getContext("2d")
@ -243,6 +279,7 @@ class LoadSong{
var taikoGame1 = new Controller(song, this.songData, false, 1, this.touchEnabled) var taikoGame1 = new Controller(song, this.songData, false, 1, this.touchEnabled)
var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2, this.touchEnabled) var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2, this.touchEnabled)
taikoGame1.run(taikoGame2) taikoGame1.run(taikoGame2)
pageEvents.send("load-song-player2", this.selectedSong2)
}else if(event.type === "left" || event.type === "gameend"){ }else if(event.type === "left" || event.type === "gameend"){
this.clean() this.clean()
new SongSelect(false, false, this.touchEnabled) new SongSelect(false, false, this.touchEnabled)
@ -264,6 +301,7 @@ class LoadSong{
}else{ }else{
if(!repeat){ if(!repeat){
assets.sounds["v_sanka"].play() assets.sounds["v_sanka"].play()
pageEvents.send("load-song-unfocused")
} }
setTimeout(() => { setTimeout(() => {
this.startMultiplayer(true) this.startMultiplayer(true)
@ -281,6 +319,7 @@ class LoadSong{
p2.send("leave") p2.send("leave")
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
this.cancelButton.style.pointerEvents = "none" this.cancelButton.style.pointerEvents = "none"
pageEvents.send("load-song-cancel")
} }
clean(){ clean(){
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")

View File

@ -2,7 +2,7 @@
constructor(){ constructor(){
this.canvas = document.getElementById("logo") this.canvas = document.getElementById("logo")
this.ctx = this.canvas.getContext("2d") this.ctx = this.canvas.getContext("2d")
this.pathSvg = failedTests.indexOf("Path2D SVG") === -1 this.pathSvg = failedTests.indexOf("Path2D SVG") === -1 && vectors.logo1
this.symbolFont = "TnT, Meiryo, sans-serif" this.symbolFont = "TnT, Meiryo, sans-serif"
this.symbols = [{ this.symbols = [{
x: 315, y: 18, xAlt: 15, scale: true, text: "ブ", x: 315, y: 18, xAlt: 15, scale: true, text: "ブ",

View File

@ -82,6 +82,7 @@ var perf = {
} }
var strings var strings
var vectors var vectors
var settings
pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => { pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){ if(event.cancelable && cancelTouch && event.target.tagName !== "SELECT"){
@ -90,8 +91,12 @@ pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => {
}) })
var versionDiv = document.getElementById("version") var versionDiv = document.getElementById("version")
var versionLink = document.getElementById("version-link") var versionLink = document.getElementById("version-link")
pageEvents.add(versionDiv, ["click", "touchend"], () => { versionLink.tabIndex = -1
versionLink.click() pageEvents.add(versionDiv, ["click", "touchend"], event => {
if(event.target === versionDiv){
versionLink.click()
pageEvents.send("version-link")
}
}) })
resizeRoot() resizeRoot()
setInterval(resizeRoot, 100) setInterval(resizeRoot, 100)
@ -103,7 +108,9 @@ pageEvents.keyAdd(debugObj, "all", "down", event => {
}else if(debugObj.state === "minimised"){ }else if(debugObj.state === "minimised"){
debugObj.debug.restore() debugObj.debug.restore()
}else{ }else{
debugObj.debug = new Debug() try{
debugObj.debug = new Debug()
}catch(e){}
} }
} }
if(event.keyCode === 82 && debugObj.debug && debugObj.controller){ if(event.keyCode === 82 && debugObj.debug && debugObj.controller){
@ -112,7 +119,7 @@ pageEvents.keyAdd(debugObj, "all", "down", event => {
} }
}) })
var loader = new Loader(() => { var loader = new Loader(songId => {
new Titlescreen() new Titlescreen(songId)
}) })

View File

@ -6,22 +6,25 @@ class Mekadon{
this.lastHit = -Infinity this.lastHit = -Infinity
} }
play(circle){ play(circle){
var type = circle.getType() var type = circle.type
if((type === "balloon" || type === "drumroll" || type === "daiDrumroll") && this.getMS() > circle.getEndTime()){ if((type === "balloon" || type === "drumroll" || type === "daiDrumroll") && this.getMS() > circle.endTime){
if(circle.section && circle.timesHit === 0){
this.game.resetSection()
}
circle.played(-1, false) circle.played(-1, false)
this.game.updateCurrentCircle() this.game.updateCurrentCircle()
} }
type = circle.getType() type = circle.type
if(type === "balloon"){ if(type === "balloon"){
this.playDrumrollAt(circle, 0, 30) return this.playDrumrollAt(circle, 0, 30)
}else if(type === "drumroll" || type === "daiDrumroll"){ }else if(type === "drumroll" || type === "daiDrumroll"){
this.playDrumrollAt(circle, 0, 60) return this.playDrumrollAt(circle, 0, 60)
}else{ }else{
this.playAt(circle, 0, 450) return this.playAt(circle, 0, 450)
} }
} }
playAt(circle, ms, score, dai, reverse){ playAt(circle, ms, score, dai, reverse){
var currentMs = circle.getMS() - this.getMS() var currentMs = circle.ms - this.getMS()
if(ms > currentMs - 10){ if(ms > currentMs - 10){
return this.playNow(circle, score, dai, reverse) return this.playNow(circle, score, dai, reverse)
} }
@ -32,22 +35,22 @@ class Mekadon{
if(kaAmount > 0){ if(kaAmount > 0){
score = Math.random() > kaAmount ? 1 : 2 score = Math.random() > kaAmount ? 1 : 2
} }
this.playAt(circle, ms, score) return this.playAt(circle, ms, score)
} }
} }
miss(circle){ miss(circle){
var currentMs = circle.getMS() - this.getMS() var currentMs = circle.ms - this.getMS()
if(0 >= currentMs - 10){ if(0 >= currentMs - 10){
this.controller.displayScore(0, true) this.controller.displayScore(0, true)
this.game.updateCurrentCircle() this.game.updateCurrentCircle()
this.game.updateCombo(0) this.game.updateCombo(0)
this.game.updateGlobalScore(0, 1, circle.gogoTime) this.game.updateGlobalScore(0, 1, circle.gogoTime)
this.game.sectionNotes.push(0)
return true return true
} }
} }
playNow(circle, score, dai, reverse){ playNow(circle, score, dai, reverse){
var kbd = this.controller.getBindings() var type = circle.type
var type = circle.getType()
var keyDai = false var keyDai = false
var playDai = !dai || dai === 2 var playDai = !dai || dai === 2
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
@ -55,7 +58,7 @@ class Mekadon{
if(drumrollNotes){ if(drumrollNotes){
var ms = this.getMS() var ms = this.getMS()
}else{ }else{
var ms = circle.getMS() var ms = circle.ms
} }
if(reverse){ if(reverse){
@ -65,25 +68,25 @@ class Mekadon{
type = "don" type = "don"
} }
} }
if(type == "daiDon" && playDai){ if(type === "daiDon" && playDai){
this.setKey(kbd["don_l"], ms) this.setKey("don_l", ms)
this.setKey(kbd["don_r"], ms) this.setKey("don_r", ms)
this.lr = false this.lr = false
keyDai = true keyDai = true
}else if(type == "don" || type == "daiDon" || drumrollNotes && score !== 2){ }else if(type === "don" || type === "daiDon" || drumrollNotes && score !== 2){
this.setKey(this.lr ? kbd["don_l"] : kbd["don_r"], ms) this.setKey(this.lr ? "don_l" : "don_r", ms)
this.lr = !this.lr this.lr = !this.lr
}else if(type == "daiKa" && playDai){ }else if(type === "daiKa" && playDai){
this.setKey(kbd["ka_l"], ms) this.setKey("ka_l", ms)
this.setKey(kbd["ka_r"], ms) this.setKey("ka_r", ms)
this.lr = false this.lr = false
keyDai = true keyDai = true
}else if(type == "ka" || type == "daiKa" || drumrollNotes){ }else if(type === "ka" || type === "daiKa" || drumrollNotes){
this.setKey(this.lr ? kbd["ka_l"] : kbd["ka_r"], ms) this.setKey(this.lr ? "ka_l" : "ka_r", ms)
this.lr = !this.lr this.lr = !this.lr
} }
if(type === "balloon"){ if(type === "balloon"){
if(circle.requiredHits == 1){ if(circle.requiredHits === 1){
assets.sounds["se_balloon"].play() assets.sounds["se_balloon"].play()
} }
this.game.checkBalloon(circle) this.game.checkBalloon(circle)
@ -95,6 +98,10 @@ class Mekadon{
this.game.updateGlobalScore(score, keyDai ? 2 : 1, circle.gogoTime) this.game.updateGlobalScore(score, keyDai ? 2 : 1, circle.gogoTime)
this.game.updateCurrentCircle() this.game.updateCurrentCircle()
circle.played(score, keyDai) circle.played(score, keyDai)
if(circle.section){
this.game.resetSection()
}
this.game.sectionNotes.push(score === 450 ? 1 : (score === 230 ? 0.5 : 0))
} }
this.lastHit = ms this.lastHit = ms
return true return true
@ -102,8 +109,7 @@ class Mekadon{
getMS(){ getMS(){
return this.controller.getElapsedTime() return this.controller.getElapsedTime()
} }
setKey(keyCode, ms){ setKey(name, ms){
this.controller.setKey(keyCode, false) this.controller.setKey(true, name, ms)
this.controller.setKey(keyCode, true, ms)
} }
} }

View File

@ -58,6 +58,7 @@ class P2Connection{
this.open() this.open()
} }
}, 500) }, 500)
pageEvents.send("p2-disconnected")
} }
var addedType = this.allEvents.get("close") var addedType = this.allEvents.get("close")
if(addedType){ if(addedType){
@ -108,9 +109,15 @@ class P2Connection{
this.dai = 2 this.dai = 2
this.kaAmount = 0 this.kaAmount = 0
this.results = false this.results = false
this.branch = "normal"
break break
case "gameend": case "gameend":
this.otherConnected = false this.otherConnected = false
if(this.session){
pageEvents.send("session-end")
}else if(!this.results){
pageEvents.send("p2-game-end")
}
this.session = false this.session = false
if(this.hashLock){ if(this.hashLock){
this.hash("") this.hash("")
@ -135,6 +142,10 @@ class P2Connection{
this.kaAmount = response.value.kaAmount this.kaAmount = response.value.kaAmount
} }
break break
case "branch":
this.branch = response.value
this.branchSet = false
break
case "session": case "session":
this.clearMessage("users") this.clearMessage("users")
this.otherConnected = true this.otherConnected = true
@ -155,10 +166,10 @@ class P2Connection{
} }
play(circle, mekadon){ play(circle, mekadon){
if(this.otherConnected || this.notes.length > 0){ if(this.otherConnected || this.notes.length > 0){
var type = circle.getType() var type = circle.type
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
if(drumrollNotes && mekadon.getMS() > circle.getEndTime()){ if(drumrollNotes && mekadon.getMS() > circle.endTime){
circle.played(-1, false) circle.played(-1, false)
mekadon.game.updateCurrentCircle() mekadon.game.updateCurrentCircle()
} }
@ -171,7 +182,7 @@ class P2Connection{
var note = this.notes[0] var note = this.notes[0]
if(note.score >= 0){ if(note.score >= 0){
var dai = 1 var dai = 1
if(circle.getType() === "daiDon" || circle.getType() === "daiKa"){ if(circle.type === "daiDon" || circle.type === "daiKa"){
dai = this.dai dai = this.dai
} }
if(mekadon.playAt(circle, note.ms, note.score, dai, note.reverse)){ if(mekadon.playAt(circle, note.ms, note.score, dai, note.reverse)){

View File

@ -3,10 +3,13 @@ class PageEvents{
this.allEvents = new Map() this.allEvents = new Map()
this.keyListeners = new Map() this.keyListeners = new Map()
this.mouseListeners = new Map() this.mouseListeners = new Map()
this.blurListeners = new Map()
this.lastKeyEvent = -Infinity this.lastKeyEvent = -Infinity
this.add(window, "keydown", this.keyEvent.bind(this)) this.add(window, "keydown", this.keyEvent.bind(this))
this.add(window, "keyup", this.keyEvent.bind(this)) this.add(window, "keyup", this.keyEvent.bind(this))
this.add(window, "mousemove", this.mouseEvent.bind(this)) this.add(window, "mousemove", this.mouseEvent.bind(this))
this.add(window, "blur", this.blurEvent.bind(this))
this.kbd = []
} }
add(target, type, callback){ add(target, type, callback){
if(Array.isArray(type)){ if(Array.isArray(type)){
@ -81,8 +84,9 @@ class PageEvents{
}) })
} }
keyEvent(event){ keyEvent(event){
if ([68, 70, 74, 75].indexOf(event.keyCode) > -1) { // D, F, J, K if(this.kbd.indexOf(event.key.toLowerCase()) !== -1){
this.lastKeyEvent = Date.now() this.lastKeyEvent = Date.now()
event.preventDefault()
} }
this.keyListeners.forEach(addedKeyCode => { this.keyListeners.forEach(addedKeyCode => {
this.checkListener(addedKeyCode.get("all"), event) this.checkListener(addedKeyCode.get("all"), event)
@ -140,7 +144,29 @@ class PageEvents{
mouseRemove(target){ mouseRemove(target){
this.mouseListeners.delete(target) this.mouseListeners.delete(target)
} }
blurEvent(event){
this.blurListeners.forEach(callback => callback(event))
}
blurAdd(target, callback){
this.blurListeners.set(target, callback)
}
blurRemove(target){
this.blurListeners.delete(target)
}
getMouse(){ getMouse(){
return this.lastMouse return this.lastMouse
} }
send(name, detail){
dispatchEvent(new CustomEvent(name, {detail: detail}))
}
setKbd(){
this.kbd = []
var kbdSettings = settings.getItem("keyboardSettings")
for(var name in kbdSettings){
var keys = kbdSettings[name]
for(var i in keys){
this.kbd.push(keys[i])
}
}
}
} }

View File

@ -181,7 +181,8 @@ class ParseOsu{
measures.push({ measures.push({
ms: ms, ms: ms,
originalMS: ms, originalMS: ms,
speed: speed speed: speed,
visible: true
}) })
} }
} }

View File

@ -10,18 +10,20 @@
this.difficulty = difficulty this.difficulty = difficulty
this.offset = (offset || 0) * -1000 this.offset = (offset || 0) * -1000
this.soundOffset = 0 this.soundOffset = 0
this.noteTypes = [ this.noteTypes = {
{name: false, txt: false}, "0": {name: false, txt: false},
{name: "don", txt: strings.note.don}, "1": {name: "don", txt: strings.note.don},
{name: "ka", txt: strings.note.ka}, "2": {name: "ka", txt: strings.note.ka},
{name: "daiDon", txt: strings.note.daiDon}, "3": {name: "daiDon", txt: strings.note.daiDon},
{name: "daiKa", txt: strings.note.daiKa}, "4": {name: "daiKa", txt: strings.note.daiKa},
{name: "drumroll", txt: strings.note.drumroll}, "5": {name: "drumroll", txt: strings.note.drumroll},
{name: "daiDrumroll", txt: strings.note.daiDrumroll}, "6": {name: "daiDrumroll", txt: strings.note.daiDrumroll},
{name: "balloon", txt: strings.note.balloon}, "7": {name: "balloon", txt: strings.note.balloon},
{name: false, txt: false}, "8": {name: false, txt: false},
{name: "balloon", txt: strings.note.balloon} "9": {name: "balloon", txt: strings.note.balloon},
] "A": {name: "daiDon", txt: strings.note.daiDon},
"B": {name: "daiKa", txt: strings.note.daiKa}
}
this.courseTypes = { this.courseTypes = {
"0": "easy", "0": "easy",
"1": "normal", "1": "normal",
@ -44,7 +46,7 @@
var hasSong = false var hasSong = false
var courses = {} var courses = {}
var currentCourse = {} var currentCourse = {}
var courseName = this.difficulty var courseName = "oni"
for(var lineNum = 0; lineNum < this.data.length; lineNum++){ for(var lineNum = 0; lineNum < this.data.length; lineNum++){
var line = this.data[lineNum] var line = this.data[lineNum]
@ -55,11 +57,13 @@
inSong = true inSong = true
if(!hasSong){ if(!hasSong){
if(!(courseName in courses)){
courses[courseName] = {}
}
for(var name in currentCourse){ for(var name in currentCourse){
if(!(courseName in courses)){ if(name !== "branch"){
courses[courseName] = {} courses[courseName][name] = currentCourse[name]
} }
courses[courseName][name] = currentCourse[name]
} }
courses[courseName].start = lineNum + 1 courses[courseName].start = lineNum + 1
courses[courseName].end = this.data.length courses[courseName].end = this.data.length
@ -70,6 +74,8 @@
hasSong = true hasSong = true
courses[courseName].end = lineNum courses[courseName].end = lineNum
} }
}else if(name.startsWith("branchstart") && inSong){
courses[courseName].branch = true
} }
}else if(!inSong){ }else if(!inSong){
@ -114,10 +120,7 @@
parseCircles(){ parseCircles(){
var meta = this.metadata[this.difficulty] var meta = this.metadata[this.difficulty]
var ms = (meta.offset || 0) * -1000 + this.offset var ms = (meta.offset || 0) * -1000 + this.offset
var bpm = meta.bpm || 0 var bpm = Math.abs(meta.bpm) || 120
if(bpm <= 0){
bpm = 1
}
var scroll = 1 var scroll = 1
var measure = 4 var measure = 4
this.beatInfo.beatInterval = 60000 / bpm this.beatInfo.beatInterval = 60000 / bpm
@ -128,29 +131,36 @@
var balloons = meta.balloon || [] var balloons = meta.balloon || []
var lastDrumroll = false var lastDrumroll = false
var branch = false var branch = false
var branchType var branchObj = {}
var branchPreference = "m" var currentBranch = false
var branchSettings = {}
var branchFirstMeasure = false
var sectionBegin = true
var currentMeasure = [] var currentMeasure = []
var firstNote = true var firstNote = true
var circles = [] var circles = []
var circleID = 0 var circleID = 0
var regexAZ = /[A-Z]/
var pushMeasure = () => { var pushMeasure = () => {
if(barLine){ var note = currentMeasure[0]
var note = currentMeasure[0] if(note){
if(note){ var speed = note.bpm * note.scroll / 60
var speed = note.bpm * note.scroll / 60 }else{
}else{ var speed = bpm * scroll / 60
var speed = bpm * scroll / 60
}
this.measures.push({
ms: ms,
originalMS: ms,
speed: speed
})
} }
this.measures.push({
ms: ms,
originalMS: ms,
speed: speed,
visible: barLine,
branch: currentBranch,
branchFirst: branchFirstMeasure
})
branchFirstMeasure = false
if(currentMeasure.length){ if(currentMeasure.length){
for(var i = 0; i < currentMeasure.length; i++){ for(var i = 0; i < currentMeasure.length; i++){
var note = currentMeasure[i] var note = currentMeasure[i]
@ -182,7 +192,9 @@
gogoTime: note.gogo, gogoTime: note.gogo,
endTime: note.endTime, endTime: note.endTime,
requiredHits: note.requiredHits, requiredHits: note.requiredHits,
beatMS: 60000 / note.bpm beatMS: 60000 / note.bpm,
branch: currentBranch,
section: note.section
}) })
if(lastDrumroll === note){ if(lastDrumroll === note){
lastDrumroll = circleObj lastDrumroll = circleObj
@ -204,61 +216,115 @@
var line = line.slice(1).toLowerCase() var line = line.slice(1).toLowerCase()
var [name, value] = this.split(line, " ") var [name, value] = this.split(line, " ")
if(!branch || branch && branchType === branchPreference){
switch(name){
case "gogostart":
gogo = true
break
case "gogoend":
gogo = false
break
case "bpmchange":
bpm = parseFloat(value)
break
case "scroll":
scroll = parseFloat(value)
break
case "measure":
var [numerator, denominator] = value.split("/")
measure = numerator / denominator * 4
break
case "delay":
ms += (parseFloat(value) || 0) * 1000
break
case "barlineon":
barLine = true
break
case "barlineoff":
barLine = false
break
}
}
switch(name){ switch(name){
case "gogostart":
gogo = true
break
case "gogoend":
gogo = false
break
case "bpmchange":
bpm = parseFloat(value) || bpm
break
case "scroll":
scroll = Math.abs(parseFloat(value)) || scroll
break
case "measure":
var [numerator, denominator] = value.split("/")
measure = numerator / denominator * 4 || measure
break
case "delay":
ms += (parseFloat(value) || 0) * 1000
break
case "barlineon":
barLine = true
break
case "barlineoff":
barLine = false
break
case "branchstart": case "branchstart":
branch = true branch = true
branchType = "" currentBranch = false
branchFirstMeasure = true
branchSettings = {
ms: ms,
gogo: gogo,
bpm: bpm,
scroll: scroll,
sectionBegin: sectionBegin
}
value = value.split(",") value = value.split(",")
var forkType = value[0].toLowerCase() if(!this.branches){
if(forkType === "r" || parseFloat(value[2]) <= 100){ this.branches = []
branchPreference = "m" }
}else if(parseFloat(value[1]) <= 100){ var req = {
branchPreference = "e" advanced: parseFloat(value[1]) || 0,
master: parseFloat(value[2]) || 0
}
if(req.advanced > 0){
var active = req.master > 0 ? "normal" : "master"
}else{ }else{
branchPreference = "n" var active = req.master > 0 ? "advanced" : "master"
}
branchObj = {
ms: ms,
originalMS: ms,
active: active,
type: value[0].trim().toLowerCase() === "r" ? "drumroll" : "accuracy",
requirement: req
}
this.branches.push(branchObj)
if(this.measures.length === 1 && branchObj.type === "drumroll"){
for(var i = circles.length; i--;){
var circle = circles[i]
if(circle.endTime && circle.type === "drumroll" || circle.type === "daiDrumroll" || circle.type === "balloon"){
this.measures.push({
ms: circle.endTime,
originalMS: circle.endTime,
speed: circle.bpm * circle.scroll / 60,
visible: false,
branch: circle.branch
})
break
}
}
}
if(this.measures.length !== 0){
this.measures[this.measures.length - 1].nextBranch = branchObj
} }
break break
case "branchend": case "branchend":
case "section":
branch = false branch = false
currentBranch = false
break
case "section":
sectionBegin = true
if(branch && !currentBranch){
branchSettings.sectionBegin = true
}
break break
case "n": case "e": case "m": case "n": case "e": case "m":
branchType = name if(!branch){
break
}
ms = branchSettings.ms
gogo = branchSettings.gogo
bpm = branchSettings.bpm
scroll = branchSettings.scroll
sectionBegin = branchSettings.sectionBegin
branchFirstMeasure = true
var branchName = name === "m" ? "master" : (name === "e" ? "advanced" : "normal")
currentBranch = {
name: branchName,
active: branchName === branchObj.active
}
branchObj[branchName] = currentBranch
break break
} }
}else if(!branch || branch && branchType === branchPreference){ }else{
var string = line.split("") var string = line.toUpperCase().split("")
for(let symbol of string){ for(let symbol of string){
@ -271,15 +337,17 @@
scroll: scroll scroll: scroll
}) })
break break
case "1": case "2": case "3": case "4": case "1": case "2": case "3": case "4": case "A": case "B":
var type = this.noteTypes[symbol] var type = this.noteTypes[symbol]
var circleObj = { var circleObj = {
type: type.name, type: type.name,
txt: type.txt, txt: type.txt,
gogo: gogo, gogo: gogo,
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll,
section: sectionBegin
} }
sectionBegin = false
if(lastDrumroll){ if(lastDrumroll){
circleObj.endDrumroll = lastDrumroll circleObj.endDrumroll = lastDrumroll
lastDrumroll = false lastDrumroll = false
@ -293,15 +361,19 @@
txt: type.txt, txt: type.txt,
gogo: gogo, gogo: gogo,
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll,
section: sectionBegin
} }
sectionBegin = false
if(lastDrumroll){ if(lastDrumroll){
if(symbol === "9"){ if(symbol === "9"){
currentMeasure.push({ currentMeasure.push({
endDrumroll: lastDrumroll, endDrumroll: lastDrumroll,
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll,
section: sectionBegin
}) })
sectionBegin = false
lastDrumroll = false lastDrumroll = false
}else{ }else{
currentMeasure.push({ currentMeasure.push({
@ -327,8 +399,10 @@
currentMeasure.push({ currentMeasure.push({
endDrumroll: lastDrumroll, endDrumroll: lastDrumroll,
bpm: bpm, bpm: bpm,
scroll: scroll scroll: scroll,
section: sectionBegin
}) })
sectionBegin = false
lastDrumroll = false lastDrumroll = false
}else{ }else{
currentMeasure.push({ currentMeasure.push({
@ -342,7 +416,14 @@
currentMeasure = [] currentMeasure = []
break break
default: default:
error = true if(regexAZ.test(symbol)){
currentMeasure.push({
bpm: bpm,
scroll: scroll
})
}else{
error = true
}
break break
} }
@ -359,6 +440,11 @@
lastDrumroll.originalEndTime = ms lastDrumroll.originalEndTime = ms
} }
if(this.branches){
circles.sort((a, b) => a.ms > b.ms ? 1 : -1)
this.measures.sort((a, b) => a.ms > b.ms ? 1 : -1)
circles.forEach((circle, i) => circle.id = i + 1)
}
return circles return circles
} }
} }

View File

@ -31,9 +31,12 @@ class Scoresheet{
this.draw = new CanvasDraw() this.draw = new CanvasDraw()
this.canvasCache = new CanvasCache() this.canvasCache = new CanvasCache()
this.keyboard = new Keyboard({
confirm: ["enter", "space", "esc", "don_l", "don_r"]
}, this.keyDown.bind(this))
this.gamepad = new Gamepad({ this.gamepad = new Gamepad({
"13": ["a", "b", "start", "ls", "rs"] confirm: ["a", "b", "start", "ls", "rs"]
}) }, this.keyDown.bind(this))
this.difficulty = { this.difficulty = {
"easy": 0, "easy": 0,
@ -60,24 +63,20 @@ class Scoresheet{
} }
}) })
} }
pageEvents.send("scoresheet", {
selectedSong: controller.selectedSong,
autoPlayEnabled: controller.autoPlayEnabled,
multiplayer: multiplayer,
touchEnabled: touchEnabled,
results: this.results,
p2results: multiplayer ? p2.results : null,
keyboardEvents: controller.keyboard.keyboardEvents,
gamepadEvents: controller.keyboard.gamepad.gamepadEvents,
touchEvents: controller.view.touchEvents
})
} }
keyDown(event, code){ keyDown(pressed){
if(!code){ if(pressed && this.redrawing){
if(event.repeat){
return
}
code = event.keyCode
}
var key = {
confirm: code == 13 || code == 32 || code == 70 || code == 74,
// Enter, Space, F, J
cancel: code == 27 || code == 8
// Esc, Backspace
}
if(key.cancel && event){
event.preventDefault()
}
if(key.confirm || key.cancel){
this.toNext() this.toNext()
} }
} }
@ -126,7 +125,6 @@ class Scoresheet{
this.winW = null this.winW = null
this.winH = null this.winH = null
pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this))
pageEvents.add(this.canvas, ["mousedown", "touchstart"], this.mouseDown.bind(this)) pageEvents.add(this.canvas, ["mousedown", "touchstart"], this.mouseDown.bind(this))
if(!this.multiplayer){ if(!this.multiplayer){
@ -166,12 +164,6 @@ class Scoresheet{
} }
var ms = this.getMS() var ms = this.getMS()
this.gamepad.play((pressed, keyCode) => {
if(pressed){
this.keyDown(false, keyCode)
}
})
if(!this.redrawRunning){ if(!this.redrawRunning){
return return
} }
@ -182,6 +174,14 @@ class Scoresheet{
var winW = innerWidth var winW = innerWidth
var winH = lastHeight var winH = lastHeight
this.pixelRatio = window.devicePixelRatio || 1 this.pixelRatio = window.devicePixelRatio || 1
var resolution = settings.getItem("resolution")
if(resolution === "medium"){
this.pixelRatio *= 0.75
}else if(resolution === "low"){
this.pixelRatio *= 0.5
}else if(resolution === "lowest"){
this.pixelRatio *= 0.25
}
winW *= this.pixelRatio winW *= this.pixelRatio
winH *= this.pixelRatio winH *= this.pixelRatio
var ratioX = winW / 1280 var ratioX = winW / 1280
@ -842,12 +842,13 @@ class Scoresheet{
} }
clean(){ clean(){
this.keyboard.clean()
this.gamepad.clean()
this.draw.clean() this.draw.clean()
this.canvasCache.clean() this.canvasCache.clean()
assets.sounds["bgm_result"].stop() assets.sounds["bgm_result"].stop()
snd.musicGain.fadeIn() snd.buffer.loadSettings()
this.redrawRunning = false this.redrawRunning = false
pageEvents.keyRemove(this, "all")
pageEvents.remove(this.canvas, ["mousedown", "touchstart"]) pageEvents.remove(this.canvas, ["mousedown", "touchstart"])
if(this.multiplayer !== 2 && this.touchEnabled){ if(this.multiplayer !== 2 && this.touchEnabled){
pageEvents.remove(document.getElementById("touch-full-btn"), "touchend") pageEvents.remove(document.getElementById("touch-full-btn"), "touchend")

View File

@ -2,13 +2,13 @@ class Session{
constructor(touchEnabled){ constructor(touchEnabled){
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
loader.changePage("session", true) loader.changePage("session", true)
this.endButton = document.getElementById("tutorial-end-button") this.endButton = this.getElement("view-end-button")
if(touchEnabled){ if(touchEnabled){
document.getElementById("tutorial-outer").classList.add("touch-enabled") this.getElement("view-outer").classList.add("touch-enabled")
} }
this.sessionInvite = document.getElementById("session-invite") this.sessionInvite = document.getElementById("session-invite")
var tutorialTitle = document.getElementById("tutorial-title") var tutorialTitle = this.getElement("view-title")
tutorialTitle.innerText = strings.session.multiplayerSession tutorialTitle.innerText = strings.session.multiplayerSession
tutorialTitle.setAttribute("alt", strings.session.multiplayerSession) tutorialTitle.setAttribute("alt", strings.session.multiplayerSession)
this.sessionInvite.parentNode.insertBefore(document.createTextNode(strings.session.linkTutorial), this.sessionInvite) this.sessionInvite.parentNode.insertBefore(document.createTextNode(strings.session.linkTutorial), this.sessionInvite)
@ -16,11 +16,12 @@ class Session{
this.endButton.setAttribute("alt", strings.session.cancel) this.endButton.setAttribute("alt", strings.session.cancel)
pageEvents.add(window, ["mousedown", "touchstart"], this.mouseDown.bind(this)) pageEvents.add(window, ["mousedown", "touchstart"], this.mouseDown.bind(this))
pageEvents.keyOnce(this, 27, "down").then(this.onEnd.bind(this)) this.keyboard = new Keyboard({
confirm: ["esc"]
}, this.keyPress.bind(this))
this.gamepad = new Gamepad({ this.gamepad = new Gamepad({
"confirm": ["start", "b", "ls", "rs"] confirm: ["start", "b", "ls", "rs"]
}, this.onEnd.bind(this)) }, this.keyPress.bind(this))
p2.hashLock = true p2.hashLock = true
pageEvents.add(p2, "message", response => { pageEvents.add(p2, "message", response => {
@ -29,12 +30,20 @@ class Session{
p2.hash(response.value) p2.hash(response.value)
}else if(response.type === "songsel"){ }else if(response.type === "songsel"){
p2.clearMessage("users") p2.clearMessage("users")
this.onEnd(false, true) this.onEnd(true)
pageEvents.send("session-start", "host")
} }
}) })
p2.send("invite") p2.send("invite")
pageEvents.send("session")
}
getElement(name){
return loader.screen.getElementsByClassName(name)[0]
} }
mouseDown(event){ mouseDown(event){
if(event.type === "mousedown" && event.which !== 1){
return
}
if(event.target === this.sessionInvite){ if(event.target === this.sessionInvite){
this.sessionInvite.focus() this.sessionInvite.focus()
}else{ }else{
@ -45,17 +54,20 @@ class Session{
this.onEnd() this.onEnd()
} }
} }
onEnd(event, fromP2){ keyPress(pressed){
if(pressed){
this.onEnd()
}
}
onEnd(fromP2){
if(!p2.session){ if(!p2.session){
p2.send("leave") p2.send("leave")
p2.hash("") p2.hash("")
p2.hashLock = false p2.hashLock = false
pageEvents.send("session-cancel")
}else if(!fromP2){ }else if(!fromP2){
return p2.send("songsel") return p2.send("songsel")
} }
if(event && event.type === "keydown"){
event.preventDefault()
}
this.clean() this.clean()
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
setTimeout(() => { setTimeout(() => {
@ -63,9 +75,9 @@ class Session{
}, 500) }, 500)
} }
clean(){ clean(){
this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
pageEvents.remove(window, ["mousedown", "touchstart"]) pageEvents.remove(window, ["mousedown", "touchstart"])
pageEvents.keyRemove(this, 27)
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")
delete this.endButton delete this.endButton
delete this.sessionInvite delete this.sessionInvite

528
public/src/js/settings.js Normal file
View File

@ -0,0 +1,528 @@
class Settings{
constructor(){
var ios = /iPhone|iPad/.test(navigator.userAgent)
var phone = /Android|iPhone|iPad/.test(navigator.userAgent)
this.items = {
language: {
type: "language",
options: ["ja", "en", "cn", "tw", "ko"],
default: this.getLang()
},
resolution: {
type: "select",
options: ["high", "medium", "low", "lowest"],
default: phone ? "medium" : "high"
},
touchAnimation: {
type: "toggle",
default: !ios,
touch: true
},
keyboardSettings: {
type: "keyboard",
default: {
ka_l: ["d"],
don_l: ["f"],
don_r: ["j"],
ka_r: ["k"]
},
touch: false
},
gamepadLayout: {
type: "gamepad",
options: ["a", "b", "c"],
default: "a",
gamepad: true
}
}
this.storage = {}
try{
var storage = JSON.parse(localStorage.getItem("settings") || "{}")
for(var i in this.items){
var current = this.items[i]
if(current.type === "language"){
this.storage[i] = localStorage.getItem("lang")
if(current.options.indexOf(this.storage[i]) === -1){
this.storage[i] = null
}
}else if(i in storage){
if((current.type === "select" || current.type === "gamepad") && current.options.indexOf(storage[i]) === -1){
this.storage[i] = null
}else if(current.type === "keyboard"){
var obj = {}
for(var j in current.default){
if(storage[i] && storage[i][j] && storage[i][j][0]){
obj[j] = storage[i][j]
}else{
obj = null
break
}
}
this.storage[i] = obj
}else{
this.storage[i] = storage[i]
}
}else{
this.storage[i] = null
}
}
}catch(e){
for(var i in this.items){
this.storage[i] = null
}
}
}
getItem(name){
var value = this.storage[name]
return value === null ? this.items[name].default : value
}
setItem(name, value){
this.storage[name] = value
try{
if(name === "language"){
if(value){
localStorage.setItem("lang", value)
}else{
localStorage.removeItem("lang")
}
}else{
var language = this.storage.language
delete this.storage.language
localStorage.setItem("settings", JSON.stringify(this.storage))
this.storage.language = language
}
}catch(e){}
}
getLang(){
if("languages" in navigator){
var userLang = navigator.languages.slice()
userLang.unshift(navigator.language)
for(var i in userLang){
for(var j in allStrings){
if(allStrings[j].regex.test(userLang[i])){
return j
}
}
}
}
return "ja"
}
setLang(lang, noEvent){
strings = lang
var boldFonts = strings.font === "Microsoft YaHei, sans-serif"
loader.screen.style.fontFamily = strings.font
loader.screen.style.fontWeight = boldFonts ? "bold" : ""
loader.screen.classList[boldFonts ? "add" : "remove"]("bold-fonts")
if(!noEvent){
pageEvents.send("language-change", lang.id)
}
}
}
class SettingsView{
constructor(touchEnabled, tutorial, songId){
this.touchEnabled = touchEnabled
this.tutorial = tutorial
this.songId = songId
loader.changePage("settings", tutorial)
assets.sounds["bgm_settings"].playLoop(0.1, false, 0, 1.392, 26.992)
this.defaultButton = document.getElementById("settings-default")
if(touchEnabled){
this.getElement("view-outer").classList.add("touch-enabled")
}
var gamepadEnabled = false
if("getGamepads" in navigator){
var gamepads = navigator.getGamepads()
for(var i = 0; i < gamepads.length; i++){
if(gamepads[i]){
gamepadEnabled = true
break
}
}
}
this.mode = "settings"
this.keyboard = new Keyboard({
"confirm": ["enter", "space", "don_l", "don_r"],
"up": ["up"],
"previous": ["left", "ka_l"],
"next": ["right", "down", "ka_r"],
"back": ["esc"],
"other": ["wildcard"]
}, this.keyPressed.bind(this))
this.gamepad = new Gamepad({
"confirm": ["b", "ls", "rs"],
"up": ["u", "lsu"],
"previous": ["l", "lb", "lt", "lsl"],
"next": ["d", "r", "rb", "rt", "lsd", "lsr"],
"back": ["start", "a"]
}, this.keyPressed.bind(this))
this.viewTitle = this.getElement("view-title")
this.endButton = this.getElement("view-end-button")
this.resolution = settings.getItem("resolution")
var content = this.getElement("view-content")
this.items = []
this.selected = 0
for(let i in settings.items){
var current = settings.items[i]
if(
!touchEnabled && current.touch === true ||
touchEnabled && current.touch === false ||
!gamepadEnabled && current.gamepad === true
){
continue
}
var settingBox = document.createElement("div")
settingBox.classList.add("setting-box")
var nameDiv = document.createElement("div")
nameDiv.classList.add("setting-name", "stroke-sub")
var name = strings.settings[i].name
nameDiv.innerText = name
nameDiv.setAttribute("alt", name)
settingBox.appendChild(nameDiv)
var valueDiv = document.createElement("div")
valueDiv.classList.add("setting-value")
this.getValue(i, valueDiv)
settingBox.appendChild(valueDiv)
content.appendChild(settingBox)
if(this.items.length === this.selected){
settingBox.classList.add("selected")
}
this.addTouch(settingBox, event => this.setValue(i))
this.items.push({
id: i,
settingBox: settingBox,
nameDiv: nameDiv,
valueDiv: valueDiv
})
}
this.items.push({
id: "default",
settingBox: this.defaultButton
})
this.addTouch(this.defaultButton, this.defaultSettings.bind(this))
this.items.push({
id: "back",
settingBox: this.endButton
})
this.addTouch(this.endButton, this.onEnd.bind(this))
this.gamepadSettings = document.getElementById("settings-gamepad")
this.addTouch(this.gamepadSettings, event => {
if(event.target === event.currentTarget){
this.gamepadBack()
}
})
this.gamepadTitle = this.gamepadSettings.getElementsByClassName("view-title")[0]
this.gamepadEndButton = this.gamepadSettings.getElementsByClassName("view-end-button")[0]
this.addTouch(this.gamepadEndButton, event => this.gamepadBack(true))
this.gamepadBox = this.gamepadSettings.getElementsByClassName("setting-box")[0]
this.addTouch(this.gamepadBox, event => this.gamepadSet(1))
this.gamepadButtons = document.getElementById("gamepad-buttons")
this.gamepadValue = document.getElementById("gamepad-value")
this.setStrings()
pageEvents.send("settings")
}
getElement(name){
return loader.screen.getElementsByClassName(name)[0]
}
addTouch(element, callback){
pageEvents.add(element, ["mousedown", "touchstart"], event => {
if(event.type === "touchstart"){
event.preventDefault()
this.touched = true
}else if(event.which !== 1){
return
}else{
this.touched = false
}
callback(event)
})
}
removeTouch(element){
pageEvents.remove(element, ["mousedown", "touchstart"])
}
getValue(name, valueDiv){
var current = settings.items[name]
var value = settings.getItem(name)
if(current.type === "language"){
value = allStrings[value].name + " (" + value + ")"
}else if(current.type === "select" || current.type === "gamepad"){
value = strings.settings[name][value]
}else if(current.type === "toggle"){
value = value ? strings.settings.on : strings.settings.off
}else if(current.type === "keyboard"){
valueDiv.innerHTML = ""
for(var i in value){
var keyDiv = document.createElement("div")
keyDiv.style.color = i === "ka_l" || i === "ka_r" ? "#009aa5" : "#ef2c10"
var key = value[i][0]
for(var j in this.keyboard.substitute){
if(this.keyboard.substitute[j] === key){
key = j
break
}
}
keyDiv.innerText = key.toUpperCase()
valueDiv.appendChild(keyDiv)
}
return
}
valueDiv.innerText = value
}
setValue(name){
var current = settings.items[name]
var value = settings.getItem(name)
var selectedIndex = this.items.findIndex(item => item.id === name)
var selected = this.items[selectedIndex]
if(this.mode !== "settings"){
if(this.selected === selectedIndex){
this.keyboardBack(selected)
}
return
}
if(this.selected !== selectedIndex){
this.items[this.selected].settingBox.classList.remove("selected")
this.selected = selectedIndex
selected.settingBox.classList.add("selected")
}
if(current.type === "language" || current.type === "select"){
value = current.options[this.mod(current.options.length, current.options.indexOf(value) + 1)]
}else if(current.type === "toggle"){
value = !value
}else if(current.type === "keyboard"){
this.mode = "keyboard"
selected.settingBox.style.animation = "none"
selected.valueDiv.classList.add("selected")
this.keyboardKeys = {}
this.keyboardSet()
assets.sounds["se_don"].play()
return
}else if(current.type === "gamepad"){
this.mode = "gamepad"
this.gamepadSelected = current.options.indexOf(value)
this.gamepadSet()
assets.sounds["se_don"].play()
return
}
settings.setItem(name, value)
this.getValue(name, this.items[this.selected].valueDiv)
assets.sounds["se_ka"].play()
if(current.type === "language"){
this.setLang(allStrings[value])
}
}
keyPressed(pressed, name, event){
if(!pressed){
return
}
this.touched = false
var selected = this.items[this.selected]
if(this.mode === "settings"){
if(name === "confirm"){
if(selected.id === "back"){
this.onEnd()
}else if(selected.id === "default"){
this.defaultSettings()
}else{
this.setValue(selected.id)
}
}else if(name === "up" || name === "previous" || name === "next"){
selected.settingBox.classList.remove("selected")
do{
this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1))
}while(this.items[this.selected].id === "default" && name !== "previous")
selected = this.items[this.selected]
selected.settingBox.classList.add("selected")
selected.settingBox.scrollIntoView()
assets.sounds["se_ka"].play()
}else if(name === "back"){
this.onEnd()
}
}else if(this.mode === "gamepad"){
if(name === "confirm"){
this.gamepadBack(true)
}else if(name === "up" || name === "previous" || name === "next"){
this.gamepadSet(name === "next" ? 1 : -1)
}else if(name === "back"){
this.gamepadBack()
}
}else if(this.mode === "keyboard"){
if(name === "back"){
this.keyboardBack(selected)
assets.sounds["se_cancel"].play()
}else{
event.preventDefault()
var currentKey = event.key.toLowerCase()
for(var i in this.keyboardKeys){
if(this.keyboardKeys[i][0] === currentKey || !currentKey){
return
}
}
var current = this.keyboardCurrent
assets.sounds[current === "ka_l" || current === "ka_r" ? "se_ka" : "se_don"].play()
this.keyboardKeys[current] = [currentKey]
this.keyboardSet()
}
}
}
keyboardSet(){
var selected = this.items[this.selected]
var current = settings.items[selected.id]
selected.valueDiv.innerHTML = ""
for(var i in current.default){
var keyDiv = document.createElement("div")
keyDiv.style.color = i === "ka_l" || i === "ka_r" ? "#009aa5" : "#ef2c10"
if(this.keyboardKeys[i]){
var key = this.keyboardKeys[i][0]
for(var j in this.keyboard.substitute){
if(this.keyboard.substitute[j] === key){
key = j
break
}
}
keyDiv.innerText = key.toUpperCase()
selected.valueDiv.appendChild(keyDiv)
}else{
keyDiv.innerText = "[" + strings.settings[selected.id][i] + "]"
selected.valueDiv.appendChild(keyDiv)
this.keyboardCurrent = i
return
}
}
settings.setItem(selected.id, this.keyboardKeys)
this.keyboardBack(selected)
this.keyboard.update()
pageEvents.setKbd()
}
keyboardBack(selected){
this.mode = "settings"
selected.settingBox.style.animation = ""
selected.valueDiv.classList.remove("selected")
this.getValue(selected.id, selected.valueDiv)
}
gamepadSet(diff){
if(this.mode !== "gamepad"){
return
}
var selected = this.items[this.selected]
var current = settings.items[selected.id]
if(diff){
this.gamepadSelected = this.mod(current.options.length, this.gamepadSelected + diff)
assets.sounds["se_ka"].play()
}
var opt = current.options[this.gamepadSelected]
var value = strings.settings[selected.id][opt]
this.gamepadValue.innerText = value
this.gamepadValue.setAttribute("alt", value)
this.gamepadButtons.style.backgroundPosition = "0 " + (-318 - 132 * this.gamepadSelected) + "px"
this.gamepadSettings.style.display = "block"
}
gamepadBack(confirm){
if(this.mode !== "gamepad"){
return
}
var selected = this.items[this.selected]
var current = settings.items[selected.id]
settings.setItem(selected.id, current.options[this.gamepadSelected])
this.getValue(selected.id, selected.valueDiv)
assets.sounds[confirm ? "se_don" : "se_cancel"].play()
this.gamepadSettings.style.display = ""
this.mode = "settings"
}
defaultSettings(){
if(this.mode === "keyboard"){
this.keyboardBack(this.items[this.selected])
}
for(var i in settings.items){
settings.setItem(i, null)
}
this.setLang(allStrings[settings.getItem("language")])
this.keyboard.update()
pageEvents.setKbd()
assets.sounds["se_don"].play()
}
onEnd(){
this.clean()
assets.sounds["se_don"].play()
setTimeout(() => {
if(this.tutorial && !this.touched){
new Tutorial(false, this.songId)
}else{
try{
localStorage.setItem("tutorial", "true")
}catch(e){}
new SongSelect(this.tutorial ? false : "settings", false, this.touched, this.songId)
}
}, 500)
}
setLang(lang){
settings.setLang(lang)
if(failedTests.length !== 0){
showUnsupported(strings)
}
for(var i in this.items){
var item = this.items[i]
if(item.valueDiv){
var name = strings.settings[item.id].name
item.nameDiv.innerText = name
item.nameDiv.setAttribute("alt", name)
this.getValue(item.id, item.valueDiv)
}
}
this.setStrings()
}
setStrings(){
this.viewTitle.innerText = strings.gameSettings
this.viewTitle.setAttribute("alt", strings.gameSettings)
this.endButton.innerText = strings.settings.ok
this.endButton.setAttribute("alt", strings.settings.ok)
this.gamepadTitle.innerText = strings.settings.gamepadLayout.name
this.gamepadTitle.setAttribute("alt", strings.settings.gamepadLayout.name)
this.gamepadEndButton.innerText = strings.settings.ok
this.gamepadEndButton.setAttribute("alt", strings.settings.ok)
this.defaultButton.innerText = strings.settings.default
this.defaultButton.setAttribute("alt", strings.settings.default)
}
mod(length, index){
return ((index % length) + length) % length
}
clean(){
this.keyboard.clean()
this.gamepad.clean()
assets.sounds["bgm_settings"].stop()
for(var i in this.items){
this.removeTouch(this.items[i].settingBox)
}
if(this.defaultButton){
delete this.defaultButton
}
this.removeTouch(this.gamepadSettings)
this.removeTouch(this.gamepadEndButton)
this.removeTouch(this.gamepadBox)
delete this.tutorialTitle
delete this.endButton
delete this.items
delete this.gamepadSettings
delete this.gamepadTitle
delete this.gamepadEndButton
delete this.gamepadBox
delete this.gamepadButtons
delete this.gamepadValue
if(this.resolution !== settings.getItem("resolution")){
for(var i in assets.image){
if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_")){
URL.revokeObjectURL(assets.image[i].src)
delete assets.image[i]
}
}
}
}
}

View File

@ -1,5 +1,5 @@
class SongSelect{ class SongSelect{
constructor(fromTutorial, fadeIn, touchEnabled){ constructor(fromTutorial, fadeIn, touchEnabled, songId){
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
loader.changePage("songselect", false) loader.changePage("songselect", false)
@ -25,21 +25,27 @@ class SongSelect{
}, },
"tutorial": { "tutorial": {
sort: 7, sort: 7,
background: "#9afbe1", background: "#29e8aa",
border: ["#d6ffff", "#6bae9c"], border: ["#86ffbd", "#009a8c"],
outline: "#31ae94" outline: "#08a28c"
}, },
"about": { "about": {
sort: 7, sort: 7,
background: "#91cfff", background: "#a2d0e7",
border: ["#dff0ff", "#6890b2"], border: ["#c6dfff", "#4485d9"],
outline: "#217abb" outline: "#2390d9"
},
"settings": {
sort: 7,
background: "#ce93fa",
border: ["#dec4fd", "#a543ef"],
outline: "#a741ef"
}, },
"browse": { "browse": {
sort: 7, sort: 7,
background: "#9791ff", background: "#fab5d3",
border: ["#e2dfff", "#6d68b2"], border: ["#ffe7ef", "#d36aa2"],
outline: "#5350ba" outline: "#d36aa2"
}, },
"J-POP": { "J-POP": {
sort: 0, sort: 0,
@ -107,7 +113,8 @@ class SongSelect{
type: song.type, type: song.type,
offset: song.offset, offset: song.offset,
songSkin: song.song_skin || {}, songSkin: song.song_skin || {},
music: song.music music: song.music,
volume: song.volume
}) })
} }
this.songs.sort((a, b) => { this.songs.sort((a, b) => {
@ -148,6 +155,12 @@ class SongSelect{
action: "about", action: "about",
category: strings.random category: strings.random
}) })
this.songs.push({
title: strings.gameSettings,
skin: this.songSkin.settings,
action: "settings",
category: strings.random
})
if("webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))){ if("webkitdirectory" in HTMLInputElement.prototype && !(/Android|iPhone|iPad/.test(navigator.userAgent))){
this.browse = document.getElementById("browse") this.browse = document.getElementById("browse")
pageEvents.add(this.browse, "change", this.browseChange.bind(this)) pageEvents.add(this.browse, "change", this.browseChange.bind(this))
@ -213,23 +226,32 @@ class SongSelect{
this.selectedDiff = 0 this.selectedDiff = 0
assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506) assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506)
if(!assets.customSongs && !fromTutorial && !("selectedSong" in localStorage)){ if(!assets.customSongs && !fromTutorial && !("selectedSong" in localStorage) && !songId){
fromTutorial = touchEnabled ? "about" : "tutorial" fromTutorial = touchEnabled ? "about" : "tutorial"
} }
if(p2.session){ if(p2.session){
fromTutorial = false fromTutorial = false
} }
var songIdIndex = -1
if(fromTutorial){ if(fromTutorial){
this.selectedSong = this.songs.findIndex(song => song.action === fromTutorial) this.selectedSong = this.songs.findIndex(song => song.action === fromTutorial)
this.playBgm(true) this.playBgm(true)
}else{ }else{
if(assets.customSongs){ if(songId){
songIdIndex = this.songs.findIndex(song => song.id === songId)
if(songIdIndex === -1){
this.clearHash()
}
}
if(songIdIndex !== -1){
this.selectedSong = songIdIndex
}else if(assets.customSongs){
this.selectedSong = assets.customSelected this.selectedSong = assets.customSelected
}else if((!p2.session || fadeIn) && "selectedSong" in localStorage){ }else if((!p2.session || fadeIn) && "selectedSong" in localStorage){
this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length - 1) this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length - 1)
} }
assets.sounds["v_songsel"].play() assets.sounds[songIdIndex !== -1 ? "v_diffsel" : "v_songsel"].play()
snd.musicGain.fadeOut() snd.musicGain.fadeOut()
this.playBgm(false) this.playBgm(false)
} }
@ -247,7 +269,7 @@ class SongSelect{
var skipStart = fromTutorial || p2.session var skipStart = fromTutorial || p2.session
this.state = { this.state = {
screen: fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title"), screen: songIdIndex !== -1 ? "difficulty" : (fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title")),
screenMS: this.getMS(), screenMS: this.getMS(),
move: 0, move: 0,
moveMS: 0, moveMS: 0,
@ -267,23 +289,33 @@ class SongSelect{
this.startPreview(true) this.startPreview(true)
this.pressedKeys = {} this.pressedKeys = {}
this.keyboard = new Keyboard({
confirm: ["enter", "space", "don_l", "don_r"],
back: ["escape"],
left: ["left", "ka_l"],
right: ["right", "ka_r"],
up: ["up"],
down: ["down"],
session: ["backspace"],
ctrl: ["ctrl"],
shift: ["shift"]
}, this.keyPress.bind(this))
this.gamepad = new Gamepad({ this.gamepad = new Gamepad({
"13": ["b", "start", "ls", "rs"], confirm: ["b", "start", "ls", "rs"],
"27": ["a"], back: ["a"],
"37": ["l", "lb", "lt", "lsl"], left: ["l", "lb", "lt", "lsl"],
"39": ["r", "rb", "rt", "lsr"], right: ["r", "rb", "rt", "lsr"],
"38": ["u", "lsu"], up: ["u", "lsu"],
"40": ["d", "lsd"], down: ["d", "lsd"],
"8": ["back"], session: ["back"],
"ctrl": ["y"], ctrl: ["y"],
"shift": ["x"] shift: ["x"]
}) }, this.keyPress.bind(this))
if(!assets.customSongs){ if(!assets.customSongs){
this.startP2() this.startP2()
} }
pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this))
pageEvents.add(loader.screen, "mousemove", this.mouseMove.bind(this)) pageEvents.add(loader.screen, "mousemove", this.mouseMove.bind(this))
pageEvents.add(loader.screen, "mouseleave", () => { pageEvents.add(loader.screen, "mouseleave", () => {
this.state.moveHover = null this.state.moveHover = null
@ -302,73 +334,54 @@ class SongSelect{
this.redrawRunning = true this.redrawRunning = true
this.redrawBind = this.redraw.bind(this) this.redrawBind = this.redraw.bind(this)
this.redraw() this.redraw()
pageEvents.send("song-select")
pageEvents.send("song-select-move", this.songs[this.selectedSong])
if(songIdIndex !== -1){
pageEvents.send("song-select-difficulty", this.songs[this.selectedSong])
}
} }
keyDown(event, code){ keyPress(pressed, name, event){
if(code){ if(pressed){
var modifiers = { if(!this.pressedKeys[name]){
shift: this.pressedKeys["shift"], this.pressedKeys[name] = this.getMS() + 300
ctrl: this.pressedKeys["ctrl"]
} }
}else{ }else{
code = event.keyCode this.pressedKeys[name] = 0
var modifiers = {
shift: event.shiftKey,
ctrl: event.ctrlKey
}
}
if(code === "ctrl" || code === "shift" || !this.redrawRunning){
return return
} }
if(name === "ctrl" || name === "shift" || !this.redrawRunning){
var key = { return
confirm: code == 13 || code == 32 || code == 70 || code == 74,
// Enter, Space, F, J
cancel: code == 27,
// Esc
session: code == 8,
// Backspace
left: code == 37 || code == 68,
// Left, D
right: code == 39 || code == 75,
// Right, K
up: code == 38,
// Up
down: code == 40
// Down
}
if(key.cancel && event){
event.preventDefault()
} }
if(this.state.screen === "song"){ if(this.state.screen === "song"){
if(key.confirm){ if(name === "confirm"){
this.toSelectDifficulty() this.toSelectDifficulty()
}else if(key.cancel){ }else if(name === "back"){
this.toTitleScreen() this.toTitleScreen()
}else if(key.session){ }else if(name === "session"){
this.toSession() this.toSession()
}else if(key.left){ }else if(name === "left"){
this.moveToSong(-1) this.moveToSong(-1)
}else if(key.right){ }else if(name === "right"){
this.moveToSong(1) this.moveToSong(1)
} }
}else if(this.state.screen === "difficulty"){ }else if(this.state.screen === "difficulty"){
if(key.confirm){ if(name === "confirm"){
if(this.selectedDiff === 0){ if(this.selectedDiff === 0){
this.toSongSelect() this.toSongSelect()
}else if(this.selectedDiff === 1){ }else if(this.selectedDiff === 1){
this.toOptions(1) this.toOptions(1)
}else{ }else{
this.toLoadSong(this.selectedDiff - this.diffOptions.length, modifiers.shift, modifiers.ctrl) this.toLoadSong(this.selectedDiff - this.diffOptions.length, this.pressedKeys["shift"], this.pressedKeys["ctrl"])
} }
}else if(key.cancel || key.session){ }else if(name === "back" || name === "session"){
this.toSongSelect() this.toSongSelect()
}else if(key.left){ }else if(name === "left"){
this.moveToDiff(-1) this.moveToDiff(-1)
}else if(key.right){ }else if(name === "right"){
this.moveToDiff(1) this.moveToDiff(1)
}else if(this.selectedDiff === 1 && (key.up || key.down)){ }else if(this.selectedDiff === 1 && (name === "up" || name === "down")){
this.toOptions(key.up ? -1 : 1) this.toOptions(name === "up" ? -1 : 1)
} }
} }
} }
@ -508,7 +521,7 @@ class SongSelect{
}else if(550 < x && x < 1050 && 95 < y && y < 524){ }else if(550 < x && x < 1050 && 95 < y && y < 524){
var moveBy = Math.floor((x - 550) / ((1050 - 550) / 5)) + this.diffOptions.length var moveBy = Math.floor((x - 550) / ((1050 - 550) / 5)) + this.diffOptions.length
var currentSong = this.songs[this.selectedSong] var currentSong = this.songs[this.selectedSong]
if(this.state.ura && moveBy === this.diffOptions + 3 || currentSong.stars[moveBy - this.diffOptions.length]){ if(this.state.ura && moveBy === this.diffOptions.length + 3 || currentSong.stars[moveBy - this.diffOptions.length]){
return moveBy return moveBy
} }
} }
@ -590,6 +603,7 @@ class SongSelect{
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
assets.sounds["v_songsel"].stop() assets.sounds["v_songsel"].stop()
assets.sounds["v_diffsel"].play(0.3) assets.sounds["v_diffsel"].play(0.3)
pageEvents.send("song-select-difficulty", currentSong)
}else if(currentSong.action === "back"){ }else if(currentSong.action === "back"){
this.clean() this.clean()
this.toTitleScreen() this.toTitleScreen()
@ -603,10 +617,13 @@ class SongSelect{
setTimeout(() => { setTimeout(() => {
this.moveToSong(moveBy) this.moveToSong(moveBy)
}, 200) }, 200)
pageEvents.send("song-select-random")
}else if(currentSong.action === "tutorial"){ }else if(currentSong.action === "tutorial"){
this.toTutorial() this.toTutorial()
}else if(currentSong.action === "about"){ }else if(currentSong.action === "about"){
this.toAbout() this.toAbout()
}else if(currentSong.action === "settings"){
this.toSettings()
}else if(currentSong.action === "browse"){ }else if(currentSong.action === "browse"){
this.toBrowse() this.toBrowse()
} }
@ -630,6 +647,8 @@ class SongSelect{
assets.sounds["v_diffsel"].stop() assets.sounds["v_diffsel"].stop()
assets.sounds["se_cancel"].play() assets.sounds["se_cancel"].play()
} }
this.clearHash()
pageEvents.send("song-select-back")
} }
toLoadSong(difficulty, shift, ctrl, touch){ toLoadSong(difficulty, shift, ctrl, touch){
this.clean() this.clean()
@ -703,6 +722,13 @@ class SongSelect{
new About(this.touchEnabled) new About(this.touchEnabled)
}, 500) }, 500)
} }
toSettings(){
assets.sounds["se_don"].play()
this.clean()
setTimeout(() => {
new SettingsView(this.touchEnabled)
}, 500)
}
toSession(){ toSession(){
if(p2.socket.readyState !== 1 || assets.customSongs){ if(p2.socket.readyState !== 1 || assets.customSongs){
return return
@ -728,6 +754,7 @@ class SongSelect{
setTimeout(() => { setTimeout(() => {
new SongSelect("browse", false, this.touchEnabled) new SongSelect("browse", false, this.touchEnabled)
}, 500) }, 500)
pageEvents.send("import-songs-default")
}else{ }else{
this.browse.click() this.browse.click()
} }
@ -740,20 +767,10 @@ class SongSelect{
requestAnimationFrame(this.redrawBind) requestAnimationFrame(this.redrawBind)
var ms = this.getMS() var ms = this.getMS()
this.gamepad.play((pressed, keyCode) => {
if(pressed){
if(!this.pressedKeys[keyCode]){
this.pressedKeys[keyCode] = ms + 300
this.keyDown(false, keyCode)
}
}else{
this.pressedKeys[keyCode] = 0
}
})
for(var key in this.pressedKeys){ for(var key in this.pressedKeys){
if(this.pressedKeys[key]){ if(this.pressedKeys[key]){
if(ms >= this.pressedKeys[key] + 50){ if(ms >= this.pressedKeys[key] + 50){
this.keyDown(false, key) this.keyPress(true, key)
this.pressedKeys[key] = ms this.pressedKeys[key] = ms
} }
} }
@ -770,6 +787,14 @@ class SongSelect{
winW = winH / 9 * 32 winW = winH / 9 * 32
} }
this.pixelRatio = window.devicePixelRatio || 1 this.pixelRatio = window.devicePixelRatio || 1
var resolution = settings.getItem("resolution")
if(resolution === "medium"){
this.pixelRatio *= 0.75
}else if(resolution === "low"){
this.pixelRatio *= 0.5
}else if(resolution === "lowest"){
this.pixelRatio *= 0.25
}
winW *= this.pixelRatio winW *= this.pixelRatio
winH *= this.pixelRatio winH *= this.pixelRatio
var ratioX = winW / 1280 var ratioX = winW / 1280
@ -783,9 +808,11 @@ class SongSelect{
this.canvas.style.height = (winH / this.pixelRatio) + "px" this.canvas.style.height = (winH / this.pixelRatio) + "px"
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2 var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
var songsLength = Math.ceil(winW / ratio / (this.songAsset.width + this.songAsset.marginLeft)) + 1
this.songTitleCache.resize( this.songTitleCache.resize(
(this.songAsset.width - borders + 1) * Math.ceil(this.songs.length / 3), (this.songAsset.width - borders + 1) * songsLength,
(this.songAsset.height - borders + 1) * 3, this.songAsset.height - borders + 1,
ratio + 0.2 ratio + 0.2
) )
@ -807,7 +834,7 @@ class SongSelect{
categories++ categories++
} }
}) })
this.categoryCache.resize(280, (this.songAsset.marginTop + 1) * categories , ratio + 0.5) this.categoryCache.resize(280, this.songAsset.marginTop + 1 , ratio + 0.5)
this.difficultyCache.resize((44 + 56 + 2) * 5, 135 + 10, ratio + 0.5) this.difficultyCache.resize((44 + 56 + 2) * 5, 135 + 10, ratio + 0.5)
@ -952,7 +979,11 @@ class SongSelect{
var elapsed = ms - this.state.moveMS var elapsed = ms - this.state.moveMS
if(this.state.move && ms > this.state.moveMS + resize2 - scrollDelay){ if(this.state.move && ms > this.state.moveMS + resize2 - scrollDelay){
assets.sounds["se_ka"].play() assets.sounds["se_ka"].play()
var previousSelectedSong = this.selectedSong
this.selectedSong = this.mod(this.songs.length, this.selectedSong + this.state.move) this.selectedSong = this.mod(this.songs.length, this.selectedSong + this.state.move)
if(previousSelectedSong !== this.selectedSong){
pageEvents.send("song-select-move", this.songs[this.selectedSong])
}
this.state.move = 0 this.state.move = 0
this.state.locked = 2 this.state.locked = 2
@ -1299,27 +1330,47 @@ class SongSelect{
outlineSize: currentUra ? this.songAsset.letterBorder : 0 outlineSize: currentUra ? this.songAsset.letterBorder : 0
}) })
}) })
var songStars = currentUra ? currentSong.stars[4] : currentSong.stars[i] var songStarsArray = (currentUra ? currentSong.stars[4] : currentSong.stars[i]).toString().split(" ")
for(var j = 0; j < 10; j++){ var songStars = songStarsArray[0]
if(songSel){ var songBranch = songStarsArray[1] === "B"
var yPos = _y + 113 + j * 17 var elapsedMS = this.state.screenMS > this.state.moveMS || !songSel ? this.state.screenMS : this.state.moveMS
}else{ var fade = ((ms - elapsedMS) % 2000) / 2000
var yPos = _y + 178 + j * 19.5 if(songBranch && fade > 0.25 && fade < 0.75){
} this.draw.verticalText({
if(10 - j > songStars){ ctx: ctx,
ctx.fillStyle = currentUra ? "#187085" : (songSel ? "#e97526" : "#e7e7e7") text: strings.songBranch,
ctx.beginPath() x: _x,
ctx.arc(_x, yPos, songSel ? 4.5 : 5, 0, Math.PI * 2) y: _y + (songSel ? 110 : 185),
ctx.fill() width: songSel ? 44 : 56,
}else{ height: songSel ? 160 : 170,
this.draw.diffStar({ fill: songSel && !currentUra ? "#c85200" : "#fff",
ctx: ctx, fontSize: songSel ? 25 : 27,
songSel: songSel, fontFamily: songSel ? "Meiryo, Microsoft YaHei, sans-serif" : this.font,
ura: currentUra, outline: songSel ? false : "#f22666",
x: _x, outlineSize: songSel ? 0 : this.songAsset.letterBorder
y: yPos, })
ratio: ratio }else{
}) for(var j = 0; j < 10; j++){
if(songSel){
var yPos = _y + 113 + j * 17
}else{
var yPos = _y + 178 + j * 19.5
}
if(10 - j > songStars){
ctx.fillStyle = currentUra ? "#187085" : (songSel ? "#e97526" : "#e7e7e7")
ctx.beginPath()
ctx.arc(_x, yPos, songSel ? 4.5 : 5, 0, Math.PI * 2)
ctx.fill()
}else{
this.draw.diffStar({
ctx: ctx,
songSel: songSel,
ura: currentUra,
x: _x,
y: yPos,
ratio: ratio
})
}
} }
} }
var currentDiff = this.selectedDiff - this.diffOptions.length var currentDiff = this.selectedDiff - this.diffOptions.length
@ -1687,55 +1738,45 @@ class SongSelect{
if("id" in currentSong){ if("id" in currentSong){
var startLoad = this.getMS() var startLoad = this.getMS()
if(loadOnly){ if(loadOnly){
var resolveLoading var currentId = null
this.previewLoading = currentSong.id
}else{ }else{
var currentId = this.previewId
this.previewing = this.selectedSong this.previewing = this.selectedSong
if(this.previewLoading === currentSong.id){
this.previewLoading = null
return
}
} }
var currentId = this.previewId
var songObj = this.previewList.find(song => song && song.id === id) var songObj = this.previewList.find(song => song && song.id === id)
if(songObj){ if(songObj){
if(!loadOnly){ if(!loadOnly){
this.preview = songObj.preview_sound this.preview = songObj.preview_sound
this.preview.gain = snd.previewGain this.preview.gain = snd.previewGain
this.previewLoaded(startLoad, songObj.preview_time) this.previewLoaded(startLoad, songObj.preview_time, currentSong.volume)
} }
}else{ }else{
songObj = {id: id} songObj = {id: id}
var previewFilename = prvTime > 0.1 ? "/preview.mp3" : "/main.ogg" var previewFilename = prvTime > 0 ? "/preview.mp3" : "/main.ogg"
var loadPreview = previewFilename => { var loadPreview = previewFilename => {
return snd.previewGain.load(gameConfig.songs_baseurl + id + previewFilename) return snd.previewGain.load(gameConfig.songs_baseurl + id + previewFilename)
} }
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if(currentSong.music){ if(!currentSong.music){
songObj.preview_time = prvTime
snd.previewGain.load(currentSong.music, true).then(resolve, reject)
}else{
songObj.preview_time = 0 songObj.preview_time = 0
loadPreview(previewFilename).catch(() => { loadPreview(previewFilename).catch(() => {
songObj.preview_time = prvTime songObj.preview_time = prvTime
return loadPreview("/main.ogg") return loadPreview("/main.ogg")
}).then(resolve, reject) }).then(resolve, reject)
}else if(currentSong.music !== "muted"){
songObj.preview_time = prvTime
snd.previewGain.load(currentSong.music, true).then(resolve, reject)
} }
}).then(sound => { }).then(sound => {
if(currentId === this.previewId){ if(currentId === this.previewId){
songObj.preview_sound = sound songObj.preview_sound = sound
if(!loadOnly || !this.previewLoading){ this.preview = sound
this.previewing = this.selectedSong this.previewLoaded(startLoad, songObj.preview_time, currentSong.volume)
this.preview = sound
this.previewLoaded(startLoad, songObj.preview_time)
}
if(loadOnly){
this.previewLoading = null
}
var oldPreview = this.previewList.shift() var oldPreview = this.previewList.shift()
if(oldPreview){ if(oldPreview){
oldPreview.preview_sound.clean() oldPreview.preview_sound.clean()
@ -1748,12 +1789,13 @@ class SongSelect{
} }
} }
} }
previewLoaded(startLoad, prvTime){ previewLoaded(startLoad, prvTime, volume){
var endLoad = this.getMS() var endLoad = this.getMS()
var difference = endLoad - startLoad var difference = endLoad - startLoad
var minDelay = 300 var minDelay = 300
var delay = minDelay - Math.min(minDelay, difference) var delay = minDelay - Math.min(minDelay, difference)
this.preview.playLoop(delay / 1000, false, prvTime / 1000) snd.previewGain.setVolumeMul(volume || 1)
this.preview.playLoop(delay / 1000, false, prvTime)
} }
endPreview(){ endPreview(){
this.previewId++ this.previewId++
@ -1880,22 +1922,32 @@ class SongSelect{
return title return title
} }
clearHash(){
if(location.hash.toLowerCase().startsWith("#song=")){
p2.hash("")
}
}
getMS(){ getMS(){
return Date.now() return Date.now()
} }
clean(){ clean(){
this.keyboard.clean()
this.gamepad.clean()
this.clearHash()
this.draw.clean() this.draw.clean()
this.songTitleCache.clean() this.songTitleCache.clean()
this.selectTextCache.clean() this.selectTextCache.clean()
this.categoryCache.clean() this.categoryCache.clean()
this.difficultyCache.clean() this.difficultyCache.clean()
this.sessionCache.clean() this.sessionCache.clean()
this.currentSongCache.clean()
assets.sounds["bgm_songsel"].stop() assets.sounds["bgm_songsel"].stop()
if(!this.bgmEnabled){ if(!this.bgmEnabled){
snd.musicGain.fadeIn() snd.musicGain.fadeIn()
setTimeout(() => { setTimeout(() => {
snd.musicGain.fadeIn() snd.buffer.loadSettings()
}, 500) }, 500)
} }
this.redrawRunning = false this.redrawRunning = false
@ -1905,7 +1957,6 @@ class SongSelect{
song.preview_sound.clean() song.preview_sound.clean()
} }
}) })
pageEvents.keyRemove(this, "all")
pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"]) pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"])
pageEvents.remove(this.canvas, "touchend") pageEvents.remove(this.canvas, "touchend")
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")

View File

@ -3,6 +3,7 @@
var AudioContext = window.AudioContext || window.webkitAudioContext var AudioContext = window.AudioContext || window.webkitAudioContext
this.context = new AudioContext() this.context = new AudioContext()
pageEvents.add(window, ["click", "touchend"], this.pageClicked.bind(this)) pageEvents.add(window, ["click", "touchend"], this.pageClicked.bind(this))
this.gainList = []
} }
load(url, local, gain){ load(url, local, gain){
if(local){ if(local){
@ -27,7 +28,9 @@
}) })
} }
createGain(channel){ createGain(channel){
return new SoundGain(this, channel) var gain = new SoundGain(this, channel)
this.gainList.push(gain)
return gain
} }
setCrossfade(gain1, gain2, median){ setCrossfade(gain1, gain2, median){
if(!Array.isArray(gain1)){ if(!Array.isArray(gain1)){
@ -60,6 +63,18 @@
this.context.resume() this.context.resume()
} }
} }
saveSettings(){
for(var i = 0; i < this.gainList.length; i++){
var gain = this.gainList[i]
gain.defaultVol = gain.volume
}
}
loadSettings(){
for(var i = 0; i < this.gainList.length; i++){
var gain = this.gainList[i]
gain.setVolume(gain.defaultVol)
}
}
} }
class SoundGain{ class SoundGain{
constructor(soundBuffer, channel){ constructor(soundBuffer, channel){
@ -85,8 +100,11 @@ class SoundGain{
this.gainNode.gain.value = amount * amount this.gainNode.gain.value = amount * amount
this.volume = amount this.volume = amount
} }
setVolumeMul(amount){
this.setVolume(amount * this.defaultVol)
}
setCrossfade(amount){ setCrossfade(amount){
this.setVolume(Math.pow(Math.sin(Math.PI / 2 * amount), 1 / 4)) this.setVolume(Math.sqrt(Math.sin(Math.PI / 2 * amount)))
} }
fadeIn(duration, time, absolute){ fadeIn(duration, time, absolute){
this.fadeVolume(0, this.volume * this.volume, duration, time, absolute) this.fadeVolume(0, this.volume * this.volume, duration, time, absolute)

View File

@ -1,7 +1,7 @@
function StringsJa(){ function StringsJa(){
this.id = "ja" this.id = "ja"
this.name = "日本語" this.name = "日本語"
this.regex = /^ja$/ this.regex = /^ja$|^ja-/
this.font = "TnT, Meiryo, sans-serif" this.font = "TnT, Meiryo, sans-serif"
this.taikoWeb = "たいこウェブ" this.taikoWeb = "たいこウェブ"
@ -24,6 +24,7 @@
this.randomSong = "ランダムに曲をえらぶ" this.randomSong = "ランダムに曲をえらぶ"
this.howToPlay = "あそびかた説明" this.howToPlay = "あそびかた説明"
this.aboutSimulator = "このシミュレータについて" this.aboutSimulator = "このシミュレータについて"
this.gameSettings = "ゲーム設定"
this.browse = "参照する…" this.browse = "参照する…"
this.defaultSongList = "デフォルト曲リスト" this.defaultSongList = "デフォルト曲リスト"
this.songOptions = "演奏オプション" this.songOptions = "演奏オプション"
@ -34,6 +35,7 @@
this.normal = "ふつう" this.normal = "ふつう"
this.hard = "むずかしい" this.hard = "むずかしい"
this.oni = "おに" this.oni = "おに"
this.songBranch = "譜面分岐あり"
this.sessionStart = "オンラインセッションを開始する!" this.sessionStart = "オンラインセッションを開始する!"
this.sessionEnd = "オンラインセッションを終了する" this.sessionEnd = "オンラインセッションを終了する"
this.loading = "ロード中..." this.loading = "ロード中..."
@ -53,6 +55,11 @@
this.good = "良" this.good = "良"
this.ok = "可" this.ok = "可"
this.bad = "不可" this.bad = "不可"
this.branch = {
"normal": "普通譜面",
"advanced": "玄人譜面",
"master": "達人譜面"
}
this.pauseOptions = [ this.pauseOptions = [
"演奏をつづける", "演奏をつづける",
"はじめからやりなおす", "はじめからやりなおす",
@ -65,16 +72,16 @@
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "流れてくる音符がワクに重なったらバチで太鼓をたたこう!",
"For red notes, hit the face of the drum (%s or %s)...", "赤い音符は面をたたこう(%sまたは%s",
"...and for blue notes, hit the rim! (%s or %s)", "青い音符はフチをたたこう(%sまたは%s",
"USB controllers are also supported!" "USBコントローラがサポートされています!"
], ],
otherControls: "Other controls", otherControls: "他のコントロール",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%sはゲームを一時停止します",
"%s while selecting difficulty \u2014 enable autoplay mode", "むずかしさをえらぶしながら%sキーを押しながらオートモードを有効",
"%s while selecting difficulty \u2014 enable 2P mode" "むずかしさをえらぶしながら%sキーを押しながらネットプレイモードを有効"
], ],
ok: "OK" ok: "OK"
} }
@ -92,6 +99,38 @@
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "キャンセル" cancel: "キャンセル"
} }
this.settings = {
language: {
name: "言語"
},
resolution: {
name: "ゲームの解像度",
high: "高",
medium: "中",
low: "低",
lowest: "最低"
},
touchAnimation: {
name: "タッチアニメーション"
},
keyboardSettings: {
name: "キーボード設定",
ka_l: "ふち(左)",
don_l: "面(左)",
don_r: "面(右)",
ka_r: "ふち(右)"
},
gamepadLayout: {
name: "そうさタイプ設定",
a: "タイプA",
b: "タイプB",
c: "タイプC"
},
on: "オン",
off: "オフ",
default: "既定値にリセット",
ok: "OK"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "サポートされていないブラウザを実行しています (%s)", browserWarning: "サポートされていないブラウザを実行しています (%s)",
details: "詳しく", details: "詳しく",
@ -125,6 +164,7 @@ function StringsEn(){
this.randomSong = "Random Song" this.randomSong = "Random Song"
this.howToPlay = "How to Play" this.howToPlay = "How to Play"
this.aboutSimulator = "About Simulator" this.aboutSimulator = "About Simulator"
this.gameSettings = "Game Settings"
this.browse = "Browse…" this.browse = "Browse…"
this.defaultSongList = "Default Song List" this.defaultSongList = "Default Song List"
this.songOptions = "Song Options" this.songOptions = "Song Options"
@ -135,6 +175,7 @@ function StringsEn(){
this.normal = "Normal" this.normal = "Normal"
this.hard = "Hard" this.hard = "Hard"
this.oni = "Extreme" this.oni = "Extreme"
this.songBranch = "Diverge Notes"
this.sessionStart = "Begin an Online Session!" this.sessionStart = "Begin an Online Session!"
this.sessionEnd = "End Online Session" this.sessionEnd = "End Online Session"
this.loading = "Loading..." this.loading = "Loading..."
@ -154,6 +195,11 @@ function StringsEn(){
this.good = "GOOD" this.good = "GOOD"
this.ok = "OK" this.ok = "OK"
this.bad = "BAD" this.bad = "BAD"
this.branch = {
"normal": "Normal",
"advanced": "Professional",
"master": "Master"
}
this.pauseOptions = [ this.pauseOptions = [
"Continue", "Continue",
"Retry", "Retry",
@ -166,8 +212,8 @@ function StringsEn(){
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "When a note overlaps the frame, that is your cue to hit the drum!",
"For red notes, hit the face of the drum (%s or %s)...", "For red notes, hit the surface of the drum (%s or %s)...",
"...and for blue notes, hit the rim! (%s or %s)", "...and for blue notes, hit the rim! (%s or %s)",
"USB controllers are also supported!" "USB controllers are also supported!"
], ],
@ -193,6 +239,38 @@ function StringsEn(){
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "Cancel" cancel: "Cancel"
} }
this.settings = {
language: {
name: "Language"
},
resolution: {
name: "Game Resolution",
high: "High",
medium: "Medium",
low: "Low",
lowest: "Lowest"
},
touchAnimation: {
name: "Touch Animation"
},
keyboardSettings: {
name: "Keyboard Settings",
ka_l: "Left Rim",
don_l: "Left Surface",
don_r: "Right Surface",
ka_r: "Right Rim"
},
gamepadLayout: {
name: "Gamepad Layout",
a: "Type A",
b: "Type B",
c: "Type C"
},
on: "On",
off: "Off",
default: "Reset to Defaults",
ok: "OK"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
@ -226,6 +304,7 @@ function StringsCn(){
this.randomSong = "随机选曲" this.randomSong = "随机选曲"
this.howToPlay = "操作说明" this.howToPlay = "操作说明"
this.aboutSimulator = "关于模拟器" this.aboutSimulator = "关于模拟器"
this.gameSettings = "游戏设定"
this.browse = "浏览…" this.browse = "浏览…"
this.defaultSongList = "默认歌曲列表" this.defaultSongList = "默认歌曲列表"
this.songOptions = "选项" this.songOptions = "选项"
@ -236,6 +315,7 @@ function StringsCn(){
this.normal = "普通" this.normal = "普通"
this.hard = "困难" this.hard = "困难"
this.oni = "魔王" this.oni = "魔王"
this.songBranch = "有谱面分歧"
this.sessionStart = "开始在线会话!" this.sessionStart = "开始在线会话!"
this.sessionEnd = "结束在线会话" this.sessionEnd = "结束在线会话"
this.loading = "加载中..." this.loading = "加载中..."
@ -255,6 +335,11 @@ function StringsCn(){
this.good = "良" this.good = "良"
this.ok = "可" this.ok = "可"
this.bad = "不可" this.bad = "不可"
this.branch = {
"normal": "一般谱面",
"advanced": "进阶谱面",
"master": "达人谱面"
}
this.pauseOptions = [ this.pauseOptions = [
"继续演奏", "继续演奏",
"从头开始", "从头开始",
@ -267,16 +352,16 @@ function StringsCn(){
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "当流动的音符将与框框重叠时就用鼓棒敲打太鼓吧",
"For red notes, hit the face of the drum (%s or %s)...", "遇到红色音符要敲打鼓面(%s或%s",
"...and for blue notes, hit the rim! (%s or %s)", "遇到蓝色音符则敲打鼓边(%s或%s",
"USB controllers are also supported!" "USB控制器也支持!"
], ],
otherControls: "Other controls", otherControls: "其他控制",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%s暂停游戏",
"%s while selecting difficulty \u2014 enable autoplay mode", "选择难度时按住%s以启用自动模式",
"%s while selecting difficulty \u2014 enable 2P mode" "选择难度时按住%s以启用网络对战模式"
], ],
ok: "确定" ok: "确定"
} }
@ -294,6 +379,38 @@ function StringsCn(){
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "取消" cancel: "取消"
} }
this.settings = {
language: {
name: "语言"
},
resolution: {
name: "游戏分辨率",
high: "高",
medium: "中",
low: "低",
lowest: "最低"
},
touchAnimation: {
name: "触摸动画"
},
keyboardSettings: {
name: "键盘设置",
ka_l: "边缘(左)",
don_l: "表面(左)",
don_r: "表面(右)",
ka_r: "边缘(右)"
},
gamepadLayout: {
name: "操作类型设定",
a: "类型A",
b: "类型B",
c: "类型C"
},
on: "开",
off: "关",
default: "重置为默认值",
ok: "确定"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
@ -327,6 +444,7 @@ function StringsTw(){
this.randomSong = "隨機選曲" this.randomSong = "隨機選曲"
this.howToPlay = "操作說明" this.howToPlay = "操作說明"
this.aboutSimulator = "關於模擬器" this.aboutSimulator = "關於模擬器"
this.gameSettings = "遊戲設定"
this.browse = "開啟檔案…" this.browse = "開啟檔案…"
this.defaultSongList = "默認歌曲列表" this.defaultSongList = "默認歌曲列表"
this.songOptions = "選項" this.songOptions = "選項"
@ -337,6 +455,7 @@ function StringsTw(){
this.normal = "普通" this.normal = "普通"
this.hard = "困難" this.hard = "困難"
this.oni = "魔王" this.oni = "魔王"
this.songBranch = "有譜面分歧"
this.sessionStart = "開始多人模式!" this.sessionStart = "開始多人模式!"
this.sessionEnd = "結束多人模式" this.sessionEnd = "結束多人模式"
this.loading = "讀取中..." this.loading = "讀取中..."
@ -356,6 +475,11 @@ function StringsTw(){
this.good = "良" this.good = "良"
this.ok = "可" this.ok = "可"
this.bad = "不可" this.bad = "不可"
this.branch = {
"normal": "一般譜面",
"advanced": "進階譜面",
"master": "達人譜面"
}
this.pauseOptions = [ this.pauseOptions = [
"繼續演奏", "繼續演奏",
"從頭開始", "從頭開始",
@ -368,16 +492,16 @@ function StringsTw(){
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "當流動的音符將與框框重疊時就用鼓棒敲打太鼓吧",
"For red notes, hit the face of the drum (%s or %s)...", "遇到紅色音符要敲打鼓面(%s或%s",
"...and for blue notes, hit the rim! (%s or %s)", "遇到藍色音符則敲打鼓邊(%s或%s",
"USB controllers are also supported!" "USB控制器也支持!"
], ],
otherControls: "Other controls", otherControls: "其他控制",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%s暫停遊戲",
"%s while selecting difficulty \u2014 enable autoplay mode", "選擇難度時按住%s以啟用自動模式",
"%s while selecting difficulty \u2014 enable 2P mode" "選擇難度時按住%s以啟用網上對打模式"
], ],
ok: "確定" ok: "確定"
} }
@ -395,6 +519,38 @@ function StringsTw(){
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "取消" cancel: "取消"
} }
this.settings = {
language: {
name: "語系"
},
resolution: {
name: "遊戲分辨率",
high: "高",
medium: "中",
low: "低",
lowest: "最低"
},
touchAnimation: {
name: "觸摸動畫"
},
keyboardSettings: {
name: "鍵盤設置",
ka_l: "邊緣(左)",
don_l: "表面(左)",
don_r: "表面(右)",
ka_r: "邊緣(右)"
},
gamepadLayout: {
name: "操作類型設定",
a: "類型A",
b: "類型B",
c: "類型C"
},
on: "開",
off: "關",
default: "重置為默認值",
ok: "確定"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
@ -428,6 +584,7 @@ function StringsKo(){
this.randomSong = "랜덤" this.randomSong = "랜덤"
this.howToPlay = "지도 시간" this.howToPlay = "지도 시간"
this.aboutSimulator = "게임 정보" this.aboutSimulator = "게임 정보"
this.gameSettings = "게임 설정"
this.browse = "찾아보기…" this.browse = "찾아보기…"
this.defaultSongList = "기본 노래 목록" this.defaultSongList = "기본 노래 목록"
this.songOptions = "옵션" this.songOptions = "옵션"
@ -438,6 +595,7 @@ function StringsKo(){
this.normal = "보통" this.normal = "보통"
this.hard = "어려움" this.hard = "어려움"
this.oni = "귀신" this.oni = "귀신"
this.songBranch = "악보 분기 있습니다"
this.sessionStart = "온라인 세션 시작!" this.sessionStart = "온라인 세션 시작!"
this.sessionEnd = "온라인 세션 끝내기" this.sessionEnd = "온라인 세션 끝내기"
this.loading = "로딩 중..." this.loading = "로딩 중..."
@ -457,6 +615,11 @@ function StringsKo(){
this.good = "얼쑤" this.good = "얼쑤"
this.ok = "좋다" this.ok = "좋다"
this.bad = "에구" this.bad = "에구"
this.branch = {
"normal": "보통 악보",
"advanced": "현인 악보",
"master": "달인 악보"
}
this.pauseOptions = [ this.pauseOptions = [
"연주 계속하기", "연주 계속하기",
"처음부터 다시", "처음부터 다시",
@ -469,16 +632,16 @@ function StringsKo(){
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "이동하는 음표가 테두리와 겹쳐졌을 때 북채로 태고를 두드리자!",
"For red notes, hit the face of the drum (%s or %s)...", "빨간 음표는 면을 두드리자 (%s 또는 %s)",
"...and for blue notes, hit the rim! (%s or %s)", "파란 음표는 테를 두드리자 (%s 또는 %s)",
"USB controllers are also supported!" "USB 컨트롤러도 지원됩니다!"
], ],
otherControls: "Other controls", otherControls: "기타 컨트롤",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%s \u2014 게임을 일시 중지합니다",
"%s while selecting difficulty \u2014 enable autoplay mode", "난이도 선택 동안 %s 홀드 \u2014 오토 모드 활성화",
"%s while selecting difficulty \u2014 enable 2P mode" "난이도 선택 동안 %s 홀드 \u2014 넷 플레이 모드 활성화"
], ],
ok: "확인" ok: "확인"
} }
@ -496,6 +659,38 @@ function StringsKo(){
linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.", linkTutorial: "Share this link with your friend to start playing together! Do not leave this screen while they join.",
cancel: "취소" cancel: "취소"
} }
this.settings = {
language: {
name: "언어"
},
resolution: {
name: "게임 해상도",
high: "높은",
medium: "중간",
low: "저",
lowest: "최저"
},
touchAnimation: {
name: "터치 애니메이션"
},
keyboardSettings: {
name: "키보드 설정",
ka_l: "가장자리 (왼쪽)",
don_l: "표면 (왼쪽)",
don_r: "표면 (오른쪽)",
ka_r: "가장자리 (오른쪽)"
},
gamepadLayout: {
name: "조작 타입 설정",
a: "타입 A",
b: "타입 B",
c: "타입 C"
},
on: "온",
off: "오프",
default: "기본값으로 재설정",
ok: "확인"
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",

View File

@ -1,146 +1,102 @@
class Titlescreen{ class Titlescreen{
constructor(){ constructor(songId){
loader.changePage("titlescreen", false) this.songId = songId
this.titleScreen = document.getElementById("title-screen") if(!songId){
this.proceed = document.getElementById("title-proceed") loader.changePage("titlescreen", false)
this.langDropdown = document.getElementById("lang-dropdown")
this.langId = document.getElementById("lang-id")
this.disclaimerText = document.getElementById("title-disclaimer-text")
this.disclaimerCopyright = document.getElementById("title-disclaimer-copyright")
document.getElementById("globe-path").setAttribute("d", vectors.globe)
this.logo = new Logo() this.titleScreen = document.getElementById("title-screen")
this.lang = this.getLang() this.proceed = document.getElementById("title-proceed")
this.setLang(allStrings[this.lang]) this.disclaimerText = document.getElementById("title-disclaimer-text")
this.addLangs() this.disclaimerCopyright = document.getElementById("title-disclaimer-copyright")
this.logo = new Logo()
}
this.setLang(allStrings[settings.getItem("language")])
pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) if(songId){
pageEvents.add(this.titleScreen, ["mousedown", "touchstart"], this.onPressed.bind(this)) if(localStorage.getItem("tutorial") === "true"){
pageEvents.add(this.langDropdown, "change", this.langChange.bind(this)) new SongSelect(false, false, this.touched, this.songId)
}else{
assets.sounds["v_title"].play() new SettingsView(false, true, this.songId)
this.gamepad = new Gamepad({
"13": ["a", "b", "x", "y", "start", "ls", "rs"]
}, pressed => {
if(pressed){
this.onPressed()
} }
}) }else{
if(p2.session){ pageEvents.add(this.titleScreen, ["mousedown", "touchstart"], event => {
pageEvents.add(p2, "message", response => { if(event.type === "touchstart"){
if(response.type === "songsel"){ event.preventDefault()
this.goNext(true) this.touched = true
}else if(event.type === "mousedown" && event.which !== 1){
return
} }
this.onPressed(true)
}) })
assets.sounds["v_title"].play()
this.keyboard = new Keyboard({
confirm: ["enter", "space", "don_l", "don_r"]
}, this.onPressed.bind(this))
this.gamepad = new Gamepad({
confirm: ["a", "b", "x", "y", "start", "ls", "rs"]
}, this.onPressed.bind(this))
if(p2.session){
pageEvents.add(p2, "message", response => {
if(response.type === "songsel"){
this.goNext(true)
}
})
}
pageEvents.send("title-screen")
} }
} }
keyDown(event, code){ onPressed(pressed, name){
if(event && event.target === this.langDropdown){ if(pressed){
return this.titleScreen.style.cursor = "auto"
this.clean()
assets.sounds["se_don"].play()
this.goNext()
} }
if(!code){
code = event.keyCode
}
if(code == 13 || code == 32 || code == 70 || code == 74){
// Enter, Space, F, J
this.onPressed()
}
}
onPressed(event){
if(event){
if(event.type === "touchstart"){
event.preventDefault()
this.touched = true
}else if(event.type === "mousedown" && event.which !== 1){
return
}
}
this.titleScreen.style.cursor = "auto"
this.clean()
assets.sounds["se_don"].play()
this.goNext()
} }
goNext(fromP2){ goNext(fromP2){
if(p2.session && !fromP2){ if(p2.session && !fromP2){
p2.send("songsel") p2.send("songsel")
}else if(fromP2 || this.touched || localStorage.getItem("tutorial") === "true"){ }else if(fromP2 || localStorage.getItem("tutorial") === "true"){
if(this.touched){
localStorage.setItem("tutorial", "true")
}
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")
setTimeout(() => { setTimeout(() => {
new SongSelect(false, false, this.touched) new SongSelect(false, false, this.touched, this.songId)
}, 500) }, 500)
}else{ }else{
setTimeout(() => { setTimeout(() => {
new Tutorial() new SettingsView(this.touched, true, this.songId)
}, 500) }, 500)
} }
} }
setLang(lang, noEvent){
getLang(){ settings.setLang(lang, true)
if(localStorage.lang && localStorage.lang in allStrings){ if(this.songId){
return localStorage.lang return
} }
if("languages" in navigator){
var userLang = navigator.languages.slice()
userLang.unshift(navigator.language)
for(var i in userLang){
for(var j in allStrings){
if(allStrings[j].regex.test(userLang[i])){
return j
}
}
}
}
return "ja"
}
setLang(lang){
strings = lang
this.proceed.innerText = strings.titleProceed this.proceed.innerText = strings.titleProceed
this.proceed.setAttribute("alt", strings.titleProceed) this.proceed.setAttribute("alt", strings.titleProceed)
this.langId.innerText = strings.id.toUpperCase()
this.langId.setAttribute("alt", strings.id.toUpperCase())
this.disclaimerText.innerText = strings.titleDisclaimer this.disclaimerText.innerText = strings.titleDisclaimer
this.disclaimerText.setAttribute("alt", strings.titleDisclaimer) this.disclaimerText.setAttribute("alt", strings.titleDisclaimer)
this.disclaimerCopyright.innerText = strings.titleCopyright this.disclaimerCopyright.innerText = strings.titleCopyright
this.disclaimerCopyright.setAttribute("alt", strings.titleCopyright) this.disclaimerCopyright.setAttribute("alt", strings.titleCopyright)
loader.screen.style.fontFamily = strings.font
loader.screen.style.fontWeight = strings.font === "Microsoft YaHei, sans-serif" ? "bold" : ""
if(failedTests.length !== 0){
showUnsupported(strings)
}
this.logo.updateSubtitle() this.logo.updateSubtitle()
} }
addLangs(){
for(var i in allStrings){
var option = document.createElement("option")
option.value = i
if(i === this.lang){
option.selected = true
}
option.appendChild(document.createTextNode(allStrings[i].name))
this.langDropdown.appendChild(option)
}
}
langChange(){
this.lang = this.langDropdown.value
localStorage.lang = this.lang
this.setLang(allStrings[this.lang])
}
clean(){ clean(){
this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
this.logo.clean() this.logo.clean()
assets.sounds["v_title"].stop() assets.sounds["v_title"].stop()
pageEvents.keyRemove(this, "all")
pageEvents.remove(this.titleScreen, ["mousedown", "touchstart"]) pageEvents.remove(this.titleScreen, ["mousedown", "touchstart"])
pageEvents.remove(this.langDropdown, "change")
delete this.titleScreen delete this.titleScreen
delete this.proceed delete this.proceed
delete this.titleDisclaimer delete this.titleDisclaimer
delete this.titleCopyright delete this.titleCopyright
delete this.langDropdown
} }
} }

View File

@ -1,15 +1,72 @@
class Tutorial{ class Tutorial{
constructor(fromSongSel){ constructor(fromSongSel, songId){
this.fromSongSel = fromSongSel this.fromSongSel = fromSongSel
this.songId = songId
loader.changePage("tutorial", true) loader.changePage("tutorial", true)
assets.sounds["bgm_setsume"].playLoop(0.1, false, 0, 1.054, 16.054) assets.sounds["bgm_setsume"].playLoop(0.1, false, 0, 1.054, 16.054)
this.endButton = document.getElementById("tutorial-end-button") this.endButton = this.getElement("view-end-button")
var tutorialTitle = document.getElementById("tutorial-title") this.tutorialTitle = this.getElement("view-title")
tutorialTitle.innerText = strings.howToPlay this.tutorialDiv = document.createElement("div")
tutorialTitle.setAttribute("alt", strings.howToPlay) this.getElement("view-content").appendChild(this.tutorialDiv)
var tutorialContent = document.getElementById("tutorial-content") this.setStrings()
var keys = ["F", "J", "D", "K", "Q", "SHIFT", "CTRL"]
pageEvents.add(this.endButton, ["mousedown", "touchstart"], event => {
if(event.type === "touchstart"){
event.preventDefault()
this.touched = true
}else if(event.type === "mousedown" && event.which !== 1){
return
}
this.onEnd(true)
})
this.keyboard = new Keyboard({
confirm: ["enter", "space", "esc", "don_l", "don_r"]
}, this.onEnd.bind(this))
this.gamepad = new Gamepad({
confirm: ["start", "b", "ls", "rs"]
}, this.onEnd.bind(this))
pageEvents.send("tutorial")
}
getElement(name){
return loader.screen.getElementsByClassName(name)[0]
}
insertText(text, parent){
parent.appendChild(document.createTextNode(text))
}
insertKey(key, parent){
var kbd = document.createElement("kbd")
kbd.innerText = key
parent.appendChild(kbd)
}
onEnd(pressed, name){
if(pressed){
this.clean()
assets.sounds["se_don"].play()
try{
localStorage.setItem("tutorial", "true")
}catch(e){}
setTimeout(() => {
new SongSelect(this.fromSongSel ? "tutorial" : false, false, this.touched, this.songId)
}, 500)
}
}
setStrings(){
this.tutorialTitle.innerText = strings.howToPlay
this.tutorialTitle.setAttribute("alt", strings.howToPlay)
this.endButton.innerText = strings.tutorial.ok
this.endButton.setAttribute("alt", strings.tutorial.ok)
this.tutorialDiv.innerHTML = ""
var kbdSettings = settings.getItem("keyboardSettings")
var pauseKey = pageEvents.kbd.indexOf("q") === -1 ? "Q" : "ESC"
var keys = [
kbdSettings.don_l[0].toUpperCase(),
kbdSettings.don_r[0].toUpperCase(),
kbdSettings.ka_l[0].toUpperCase(),
kbdSettings.ka_r[0].toUpperCase(),
pauseKey, "SHIFT", "CTRL"
]
var keyIndex = 0 var keyIndex = 0
strings.tutorial.basics.forEach(string => { strings.tutorial.basics.forEach(string => {
var par = document.createElement("p") var par = document.createElement("p")
@ -20,7 +77,7 @@ class Tutorial{
} }
this.insertText(stringKey, par) this.insertText(stringKey, par)
}) })
tutorialContent.appendChild(par) this.tutorialDiv.appendChild(par)
}) })
var par = document.createElement("p") var par = document.createElement("p")
var span = document.createElement("span") var span = document.createElement("span")
@ -37,43 +94,15 @@ class Tutorial{
this.insertText(stringKey, par) this.insertText(stringKey, par)
}) })
}) })
tutorialContent.appendChild(par) this.tutorialDiv.appendChild(par)
this.endButton.innerText = strings.tutorial.ok
this.endButton.setAttribute("alt", strings.tutorial.ok)
pageEvents.once(this.endButton, ["mousedown", "touchstart"]).then(this.onEnd.bind(this))
pageEvents.keyOnce(this, 13, "down").then(this.onEnd.bind(this))
this.gamepad = new Gamepad({
"confirm": ["start", "b", "ls", "rs"]
}, this.onEnd.bind(this))
}
insertText(text, parent){
parent.appendChild(document.createTextNode(text))
}
insertKey(key, parent){
var kbd = document.createElement("kbd")
kbd.innerText = key
parent.appendChild(kbd)
}
onEnd(event){
var touched = false
if(event && event.type === "touchstart"){
event.preventDefault()
touched = true
}
this.clean()
assets.sounds["se_don"].play()
localStorage.setItem("tutorial", "true")
setTimeout(() => {
new SongSelect(this.fromSongSel ? "tutorial" : false, false, touched)
}, 500)
} }
clean(){ clean(){
this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
assets.sounds["bgm_setsume"].stop()
pageEvents.remove(this.endButton, ["mousedown", "touchstart"]) pageEvents.remove(this.endButton, ["mousedown", "touchstart"])
pageEvents.keyRemove(this, 13) assets.sounds["bgm_setsume"].stop()
delete this.tutorialTitle
delete this.endButton delete this.endButton
delete this.tutorialDiv
} }
} }

View File

@ -74,6 +74,34 @@
this.nextBeat = 0 this.nextBeat = 0
this.gogoTime = 0 this.gogoTime = 0
this.drumroll = [] this.drumroll = []
this.touchEvents = 0
if(this.controller.parsedSongData.branches){
this.branch = "normal"
this.branchAnimate = {
ms: -Infinity,
fromBranch: "normal"
}
this.branchMap = {
"normal": {
"bg": "rgba(0, 0, 0, 0)",
"text": "#d3d3d3",
"stroke": "#393939",
"shadow": "#000"
},
"advanced": {
"bg": "rgba(29, 129, 189, 0.4)",
"text": "#94d7e7",
"stroke": "#315973",
"shadow": "#082031"
},
"master": {
"bg": "rgba(230, 29, 189, 0.4)",
"text": "#f796ef",
"stroke": "#7e2e6e",
"shadow": "#3e0836"
}
}
}
this.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval this.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval
this.font = strings.font this.font = strings.font
@ -84,11 +112,13 @@
this.titleCache = new CanvasCache() this.titleCache = new CanvasCache()
this.comboCache = new CanvasCache() this.comboCache = new CanvasCache()
this.pauseCache = new CanvasCache() this.pauseCache = new CanvasCache()
this.branchCache = new CanvasCache()
this.multiplayer = this.controller.multiplayer this.multiplayer = this.controller.multiplayer
this.touchEnabled = this.controller.touchEnabled this.touchEnabled = this.controller.touchEnabled
this.touch = -Infinity this.touch = -Infinity
this.touchAnimation = settings.getItem("touchAnimation")
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
@ -96,6 +126,8 @@
this.touchDrumDiv = document.getElementById("touch-drum") this.touchDrumDiv = document.getElementById("touch-drum")
this.touchDrumImg = document.getElementById("touch-drum-img") this.touchDrumImg = document.getElementById("touch-drum-img")
this.setBgImage(this.touchDrumImg, assets.image["touch_drum"].src)
if(this.controller.autoPlayEnabled){ if(this.controller.autoPlayEnabled){
this.touchDrumDiv.style.display = "none" this.touchDrumDiv.style.display = "none"
} }
@ -131,7 +163,8 @@
} }
this.setDonBg() this.setDonBg()
this.lastMousemove = this.controller.game.getAccurateTime() this.startTime = this.controller.game.getAccurateTime()
this.lastMousemove = this.startTime
pageEvents.mouseAdd(this, this.onmousemove.bind(this)) pageEvents.mouseAdd(this, this.onmousemove.bind(this))
this.refresh() this.refresh()
@ -150,6 +183,14 @@
var touchMultiplayer = this.touchEnabled && this.multiplayer && !this.portrait var touchMultiplayer = this.touchEnabled && this.multiplayer && !this.portrait
this.pixelRatio = window.devicePixelRatio || 1 this.pixelRatio = window.devicePixelRatio || 1
var resolution = settings.getItem("resolution")
if(resolution === "medium"){
this.pixelRatio *= 0.75
}else if(resolution === "low"){
this.pixelRatio *= 0.5
}else if(resolution === "lowest"){
this.pixelRatio *= 0.25
}
winW *= this.pixelRatio winW *= this.pixelRatio
winH *= this.pixelRatio winH *= this.pixelRatio
if(this.portrait){ if(this.portrait){
@ -161,6 +202,7 @@
} }
var ratio = (ratioX < ratioY ? ratioX : ratioY) var ratio = (ratioX < ratioY ? ratioX : ratioY)
var resized = false
if(this.winW !== winW || this.winH !== winH){ if(this.winW !== winW || this.winH !== winH){
this.winW = winW this.winW = winW
this.winH = winH this.winH = winH
@ -180,6 +222,7 @@
} }
this.fillComboCache() this.fillComboCache()
this.setDonBgHeight() this.setDonBgHeight()
resized = true
}else if(this.controller.game.paused && !document.hasFocus()){ }else if(this.controller.game.paused && !document.hasFocus()){
return return
}else if(this.multiplayer !== 2){ }else if(this.multiplayer !== 2){
@ -668,7 +711,7 @@
} }
ctx.restore() ctx.restore()
// Bar pressed keys // Branch background
var keyTime = this.controller.getKeyTime() var keyTime = this.controller.getKeyTime()
var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka" var sound = keyTime["don"] > keyTime["ka"] ? "don" : "ka"
var padding = this.slotPos.paddingLeft var padding = this.slotPos.paddingLeft
@ -676,12 +719,78 @@
var barY = this.slotPos.y - 65 * mul var barY = this.slotPos.y - 65 * mul
var barH = 130 * mul var barH = 130 * mul
if(this.branchAnimate && ms <= this.branchAnimate.ms + 300){
var alpha = Math.max(0, (ms - this.branchAnimate.ms) / 300)
ctx.globalAlpha = 1 - alpha
ctx.fillStyle = this.branchMap[this.branchAnimate.fromBranch].bg
ctx.fillRect(padding, barY, winW - padding, barH)
ctx.globalAlpha = alpha
}
if(this.branch){
ctx.fillStyle = this.branchMap[this.branch].bg
ctx.fillRect(padding, barY, winW - padding, barH)
ctx.globalAlpha = 1
}
// Current branch text
if(this.branch){
if(resized){
this.fillBranchCache()
}
var textW = Math.floor(260 * mul)
var textH = Math.floor(barH)
var textX = winW - textW
var oldOffset = 0
var newOffset = 0
var elapsed = ms - this.startTime
if(elapsed < 250){
textX = winW
}else if(elapsed < 500){
textX += (1 - this.draw.easeOutBack((elapsed - 250) / 250)) * textW
}
if(this.branchAnimate && ms - this.branchAnimate.ms < 310 && ms >= this.branchAnimate.ms){
var fromBranch = this.branchAnimate.fromBranch
var elapsed = ms - this.branchAnimate.ms
var reverse = fromBranch === "master" || fromBranch === "advanced" && this.branch === "normal" ? -1 : 1
if(elapsed < 65){
oldOffset = elapsed / 65 * 12 * mul * reverse
ctx.globalAlpha = 1
var newAlpha = 0
}else if(elapsed < 215){
var animPoint = (elapsed - 65) / 150
oldOffset = (12 - animPoint * 48) * mul * reverse
newOffset = (36 - animPoint * 48) * mul * reverse
ctx.globalAlpha = this.draw.easeIn(1 - animPoint)
var newAlpha = this.draw.easeIn(animPoint)
}else{
newOffset = (1 - (elapsed - 215) / 95) * -12 * mul * reverse
ctx.globalAlpha = 0
var newAlpha = 1
}
this.branchCache.get({
ctx: ctx,
x: textX, y: barY + oldOffset,
w: textW, h: textH,
id: fromBranch
})
ctx.globalAlpha = newAlpha
}
this.branchCache.get({
ctx: ctx,
x: textX, y: barY + newOffset,
w: textW, h: textH,
id: this.branch
})
ctx.globalAlpha = 1
}
// Go go time background
if(this.gogoTime || ms <= this.gogoTimeStarted + 100){ if(this.gogoTime || ms <= this.gogoTimeStarted + 100){
var grd = ctx.createLinearGradient(padding, 0, winW, 0) var grd = ctx.createLinearGradient(padding, 0, winW, 0)
grd.addColorStop(0, "#512a2c") grd.addColorStop(0, "rgba(255, 0, 0, 0.16)")
grd.addColorStop(0.46, "#6f2a2d") grd.addColorStop(0.45, "rgba(255, 0, 0, 0.28)")
grd.addColorStop(0.76, "#8a4763") grd.addColorStop(0.77, "rgba(255, 83, 157, 0.4)")
grd.addColorStop(1, "#2c2a2c") grd.addColorStop(1, "rgba(255, 83, 157, 0)")
ctx.fillStyle = grd ctx.fillStyle = grd
if(!this.touchEnabled){ if(!this.touchEnabled){
var alpha = Math.min(100, ms - this.gogoTimeStarted) / 100 var alpha = Math.min(100, ms - this.gogoTimeStarted) / 100
@ -692,6 +801,8 @@
} }
ctx.fillRect(padding, barY, winW - padding, barH) ctx.fillRect(padding, barY, winW - padding, barH)
} }
// Bar pressed keys
if(keyTime[sound] > ms - 130){ if(keyTime[sound] > ms - 130){
var gradients = { var gradients = {
"don": "255, 0, 0", "don": "255, 0, 0",
@ -719,11 +830,10 @@
) )
// Taiko pressed keys // Taiko pressed keys
var kbd = this.controller.getBindings()
var keys = ["ka_l", "ka_r", "don_l", "don_r"] var keys = ["ka_l", "ka_r", "don_l", "don_r"]
for(var i = 0; i < keys.length; i++){ for(var i = 0; i < keys.length; i++){
var keyMS = ms - keyTime[kbd[keys[i]]] var keyMS = ms - keyTime[keys[i]]
if(keyMS < 130){ if(keyMS < 130){
if(keyMS > 70 && !this.touchEnabled){ if(keyMS > 70 && !this.touchEnabled){
ctx.globalAlpha = this.draw.easeOut(1 - (keyMS - 70) / 60) ctx.globalAlpha = this.draw.easeOut(1 - (keyMS - 70) / 60)
@ -750,7 +860,7 @@
comboScale = this.draw.fade(scoreMS / 100) comboScale = this.draw.fade(scoreMS / 100)
} }
var glyphW = 51 var glyphW = 51
var glyphH = 64 var glyphH = 65
var letterSpacing = (comboText.length >= 4 ? 38 : 42) * mul var letterSpacing = (comboText.length >= 4 ? 38 : 42) * mul
var orange = comboCount >= 100 ? "1" : "0" var orange = comboCount >= 100 ? "1" : "0"
@ -789,6 +899,7 @@
ctx.lineWidth = 7 * mul ctx.lineWidth = 7 * mul
ctx.textAlign = "center" ctx.textAlign = "center"
ctx.miterLimit = 1 ctx.miterLimit = 1
ctx.strokeStyle = "#000"
ctx.strokeText(strings.combo, comboX, comboTextY) ctx.strokeText(strings.combo, comboX, comboTextY)
ctx.miterLimit = 10 ctx.miterLimit = 10
ctx.fillText(strings.combo, comboX, comboTextY) ctx.fillText(strings.combo, comboX, comboTextY)
@ -999,7 +1110,7 @@
var songSkinName = selectedSong.songSkin.name var songSkinName = selectedSong.songSkin.name
var supportsBlend = "mixBlendMode" in this.songBg.style var supportsBlend = "mixBlendMode" in this.songBg.style
var songLayers = [document.getElementById("layer1"), document.getElementById("layer2")] var songLayers = [document.getElementById("layer1"), document.getElementById("layer2")]
var prefix = selectedSong.songSkin.prefix || "" var prefix = ""
if(selectedSong.category in this.categories){ if(selectedSong.category in this.categories){
var catId = this.categories[selectedSong.category].sort var catId = this.categories[selectedSong.category].sort
@ -1010,8 +1121,9 @@
if(!selectedSong.songSkin.song){ if(!selectedSong.songSkin.song){
var id = selectedSong.songBg var id = selectedSong.songBg
this.songBg.classList.add("songbg-" + id) this.songBg.classList.add("songbg-" + id)
this.setLayers(songLayers, prefix + "bg_song_" + id + (supportsBlend ? "" : "a"), supportsBlend) this.setLayers(songLayers, "bg_song_" + id + (supportsBlend ? "" : "a"), supportsBlend)
}else if(selectedSong.songSkin.song !== "none"){ }else if(selectedSong.songSkin.song !== "none"){
var prefix = selectedSong.songSkin.prefix || ""
var notStatic = selectedSong.songSkin.song !== "static" var notStatic = selectedSong.songSkin.song !== "static"
if(notStatic){ if(notStatic){
this.songBg.classList.add("songbg-" + selectedSong.songSkin.song) this.songBg.classList.add("songbg-" + selectedSong.songSkin.song)
@ -1021,7 +1133,9 @@
if(!selectedSong.songSkin.stage){ if(!selectedSong.songSkin.stage){
this.songStage.classList.add("song-stage-" + selectedSong.songStage) this.songStage.classList.add("song-stage-" + selectedSong.songStage)
this.setBgImage(this.songStage, assets.image["bg_stage_" + selectedSong.songStage].src)
}else if(selectedSong.songSkin.stage !== "none"){ }else if(selectedSong.songSkin.stage !== "none"){
var prefix = selectedSong.songSkin.prefix || ""
this.setBgImage(this.songStage, assets.image[prefix + "bg_stage_" + songSkinName].src) this.setBgImage(this.songStage, assets.image[prefix + "bg_stage_" + songSkinName].src)
} }
} }
@ -1030,7 +1144,7 @@
var songSkinName = selectedSong.songSkin.name var songSkinName = selectedSong.songSkin.name
var donLayers = [] var donLayers = []
var filename = !selectedSong.songSkin.don && this.multiplayer === 2 ? "bg_don2_" : "bg_don_" var filename = !selectedSong.songSkin.don && this.multiplayer === 2 ? "bg_don2_" : "bg_don_"
var prefix = selectedSong.songSkin.prefix || "" var prefix = ""
this.donBg = document.createElement("div") this.donBg = document.createElement("div")
this.donBg.classList.add("donbg") this.donBg.classList.add("donbg")
@ -1049,10 +1163,11 @@
var asset1, asset2 var asset1, asset2
if(!selectedSong.songSkin.don){ if(!selectedSong.songSkin.don){
this.donBg.classList.add("donbg-" + selectedSong.donBg) this.donBg.classList.add("donbg-" + selectedSong.donBg)
this.setLayers(donLayers, prefix + filename + selectedSong.donBg, true) this.setLayers(donLayers, filename + selectedSong.donBg, true)
asset1 = filename + selectedSong.donBg + "a" asset1 = filename + selectedSong.donBg + "a"
asset2 = filename + selectedSong.donBg + "b" asset2 = filename + selectedSong.donBg + "b"
}else if(selectedSong.songSkin.don !== "none"){ }else if(selectedSong.songSkin.don !== "none"){
var prefix = selectedSong.songSkin.prefix || ""
var notStatic = selectedSong.songSkin.don !== "static" var notStatic = selectedSong.songSkin.don !== "static"
if(notStatic){ if(notStatic){
this.donBg.classList.add("donbg-" + selectedSong.songSkin.don) this.donBg.classList.add("donbg-" + selectedSong.songSkin.don)
@ -1076,6 +1191,11 @@
} }
setDonBgHeight(){ setDonBgHeight(){
this.donBg.style.setProperty("--h", getComputedStyle(this.donBg).height) this.donBg.style.setProperty("--h", getComputedStyle(this.donBg).height)
var gameDiv = this.gameDiv
gameDiv.classList.add("fix-animations")
setTimeout(()=>{
gameDiv.classList.remove("fix-animations")
}, 50)
} }
setLayers(elements, file, ab){ setLayers(elements, file, ab){
if(ab){ if(ab){
@ -1101,15 +1221,23 @@
var timeForDistance = this.posToMs(distanceForCircle, measure.speed) var timeForDistance = this.posToMs(distanceForCircle, measure.speed)
var startingTime = measure.ms - timeForDistance var startingTime = measure.ms - timeForDistance
var finishTime = measure.ms + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + 3, measure.speed) var finishTime = measure.ms + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + 3, measure.speed)
if(ms >= startingTime && ms <= finishTime){ if(measure.visible && (!measure.branch || measure.branch.active) && ms >= startingTime && ms <= finishTime){
var measureX = this.slotPos.x + this.msToPos(measure.ms - ms, measure.speed) var measureX = this.slotPos.x + this.msToPos(measure.ms - ms, measure.speed)
this.ctx.strokeStyle = "#bdbdbd" this.ctx.strokeStyle = measure.branchFirst ? "#ff0" : "#bdbdbd"
this.ctx.lineWidth = 3 this.ctx.lineWidth = 3
this.ctx.beginPath() this.ctx.beginPath()
this.ctx.moveTo(measureX, measureY) this.ctx.moveTo(measureX, measureY)
this.ctx.lineTo(measureX, measureY + measureH) this.ctx.lineTo(measureX, measureY + measureH)
this.ctx.stroke() this.ctx.stroke()
} }
if(this.multiplayer !== 2 && ms >= measure.ms && measure.nextBranch && !measure.viewChecked && measure.gameChecked){
measure.viewChecked = true
if(measure.nextBranch.active !== this.branch){
this.branchAnimate.ms = ms
this.branchAnimate.fromBranch = this.branch
}
this.branch = measure.nextBranch.active
}
}) })
} }
updateNoteFaces(){ updateNoteFaces(){
@ -1136,21 +1264,21 @@
for(var i = circles.length; i--;){ for(var i = circles.length; i--;){
var circle = circles[i] var circle = circles[i]
var speed = circle.getSpeed() var speed = circle.speed
var timeForDistance = this.posToMs(distanceForCircle + this.slotPos.size / 2, speed) var timeForDistance = this.posToMs(distanceForCircle + this.slotPos.size / 2, speed)
var startingTime = circle.getMS() - timeForDistance var startingTime = circle.ms - timeForDistance
var finishTime = circle.getEndTime() + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + this.slotPos.size * 2, speed) var finishTime = circle.endTime + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + this.slotPos.size * 2, speed)
if(circle.getPlayed() <= 0 || circle.getScore() === 0){ if(circle.isPlayed <= 0 || circle.score === 0){
if(ms >= startingTime && ms <= finishTime && circle.getPlayed() !== -1){ if((!circle.branch || circle.branch.active) && ms >= startingTime && ms <= finishTime && circle.isPlayed !== -1){
this.drawCircle(circle) this.drawCircle(circle)
} }
}else if(!circle.isAnimated()){ }else if(!circle.animating){
// Start animation to gauge // Start animation to gauge
circle.animate(ms) circle.animate(ms)
} }
if(ms >= circle.ms && !circle.gogoChecked){ if(ms >= circle.ms && !circle.gogoChecked && (!circle.branch || circle.branch.active)){
if(this.gogoTime != circle.gogoTime){ if(this.gogoTime != circle.gogoTime){
this.toggleGogoTime(circle) this.toggleGogoTime(circle)
} }
@ -1164,9 +1292,9 @@
for(var i = 0; i < circles.length; i++){ for(var i = 0; i < circles.length; i++){
var circle = circles[i] var circle = circles[i]
if(circle.isAnimated()){ if(circle.animating){
var animT = circle.getAnimT() var animT = circle.animT
if(ms < animT + 490){ if(ms < animT + 490){
if(circle.fixedPos){ if(circle.fixedPos){
@ -1182,7 +1310,7 @@
var pos = this.animateBezier[3] var pos = this.animateBezier[3]
this.drawCircle(circle, pos, (ms - animT - 490) / 160) this.drawCircle(circle, pos, (ms - animT - 490) / 160)
}else{ }else{
circle.endAnimation() circle.animationEnded = true
} }
} }
} }
@ -1210,13 +1338,13 @@
var lyricsSize = 20 * mul var lyricsSize = 20 * mul
var fill, size, faceID var fill, size, faceID
var type = circle.getType() var type = circle.type
var ms = this.getMS() var ms = this.getMS()
var circleMs = circle.getMS() var circleMs = circle.ms
var endTime = circle.getEndTime() var endTime = circle.endTime
var animated = circle.isAnimated() var animated = circle.animating
var speed = circle.getSpeed() var speed = circle.speed
var played = circle.getPlayed() var played = circle.isPlayed
var drumroll = 0 var drumroll = 0
var endX = 0 var endX = 0
@ -1322,9 +1450,9 @@
ctx.fill() ctx.fill()
ctx.globalAlpha = 1 ctx.globalAlpha = 1
} }
if(!circle.isAnimated()){ if(!circle.animating && circle.text){
// Text // Text
var text = circle.getText() var text = circle.text
var textX = circlePos.x var textX = circlePos.x
var textY = circlePos.y + 83 * mul var textY = circlePos.y + 83 * mul
ctx.font = lyricsSize + "px Kozuka, Microsoft YaHei, sans-serif" ctx.font = lyricsSize + "px Kozuka, Microsoft YaHei, sans-serif"
@ -1374,8 +1502,8 @@
fillComboCache(){ fillComboCache(){
var fontSize = 58 var fontSize = 58
var letterSpacing = fontSize * 0.67 var letterSpacing = fontSize * 0.67
var glyphW = 50 var glyphW = 51
var glyphH = 64 var glyphH = 65
var textX = 5 var textX = 5
var textY = 5 var textY = 5
var letterBorder = fontSize * 0.15 var letterBorder = fontSize * 0.15
@ -1435,6 +1563,37 @@
}) })
this.globalAlpha = 1 this.globalAlpha = 1
} }
fillBranchCache(){
var mul = this.slotPos.size / 106
var textW = Math.floor(260 * mul)
var barH = Math.floor(130 * mul)
var branchNames = this.controller.game.branchNames
var textX = textW - 33 * mul
var textY = 63 * mul
var fontSize = (strings.id === "en" ? 33 : (strings.id === "ko" ? 38 : 43)) * mul
this.branchCache.resize((textW + 1), (barH + 1) * 3, this.ratio)
for(var i in branchNames){
this.branchCache.set({
w: textW,
h: barH,
id: branchNames[i]
}, ctx => {
var currentMap = this.branchMap[branchNames[i]]
ctx.font = this.draw.bold(this.font) + fontSize + "px " + this.font
ctx.lineJoin = "round"
ctx.miterLimit = 1
ctx.textAlign = "right"
ctx.textBaseline = "middle"
ctx.lineWidth = 8 * mul
ctx.strokeStyle = currentMap.shadow
ctx.strokeText(strings.branch[branchNames[i]], textX, textY + 4 * mul)
ctx.strokeStyle = currentMap.stroke
ctx.strokeText(strings.branch[branchNames[i]], textX, textY)
ctx.fillStyle = currentMap.text
ctx.fillText(strings.branch[branchNames[i]], textX, textY)
})
}
}
toggleGogoTime(circle){ toggleGogoTime(circle){
this.gogoTime = circle.gogoTime this.gogoTime = circle.gogoTime
this.gogoTimeStarted = circle.ms this.gogoTimeStarted = circle.ms
@ -1465,7 +1624,7 @@
if(this.gogoTime){ if(this.gogoTime){
var circles = this.controller.parsedSongData.circles var circles = this.controller.parsedSongData.circles
var lastCircle = circles[circles.length - 1] var lastCircle = circles[circles.length - 1]
var endTime = lastCircle.getEndTime() + 3000 var endTime = lastCircle.endTime + 3000
if(ms >= endTime){ if(ms >= endTime){
this.toggleGogoTime({ this.toggleGogoTime({
gogoTime: 0, gogoTime: 0,
@ -1553,14 +1712,16 @@
this.touchDrumDiv.style.width = drumWidth + "px" this.touchDrumDiv.style.width = drumWidth + "px"
this.touchDrumDiv.style.height = drumHeight + "px" this.touchDrumDiv.style.height = drumHeight + "px"
} }
if(this.touch > ms - 100){ if(this.touchAnimation){
if(!this.drumPadding){ if(this.touch > ms - 100){
this.drumPadding = true if(!this.drumPadding){
this.touchDrumImg.style.backgroundPositionY = "7px" this.drumPadding = true
this.touchDrumImg.style.backgroundPositionY = "7px"
}
}else if(this.drumPadding){
this.drumPadding = false
this.touchDrumImg.style.backgroundPositionY = ""
} }
}else if(this.drumPadding){
this.drumPadding = false
this.touchDrumImg.style.backgroundPositionY = ""
} }
} }
} }
@ -1601,16 +1762,16 @@
this.touchNote("ka_r") this.touchNote("ka_r")
} }
} }
this.touchEvents++
} }
} }
} }
touchNote(note){ touchNote(note){
var keyboard = this.controller.keyboard var keyboard = this.controller.keyboard
var kbd = keyboard.getBindings()
var ms = this.controller.game.getAccurateTime() var ms = this.controller.game.getAccurateTime()
this.touch = ms this.touch = ms
keyboard.setKey(kbd[note], false) keyboard.setKey(false, note)
keyboard.setKey(kbd[note], true, ms) keyboard.setKey(true, note, ms)
} }
mod(length, index){ mod(length, index){
return ((index % length) + length) % length return ((index % length) + length) % length
@ -1631,12 +1792,17 @@
switch(pos){ switch(pos){
case 1: case 1:
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
return this.controller.restartSong() this.controller.restartSong()
pageEvents.send("pause-restart")
break
case 2: case 2:
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
return this.controller.songSelection() this.controller.songSelection()
pageEvents.send("pause-song-select")
break
default: default:
return this.controller.togglePause() this.controller.togglePause()
break
} }
} }
onmousedown(event){ onmousedown(event){
@ -1721,6 +1887,8 @@
this.assets.clean() this.assets.clean()
this.titleCache.clean() this.titleCache.clean()
this.comboCache.clean() this.comboCache.clean()
this.pauseCache.clean()
this.branchCache.clean()
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
if(this.touchEnabled){ if(this.touchEnabled){

View File

@ -1,9 +1,9 @@
<div id="tutorial-outer"> <div class="view-outer">
<div id="tutorial"> <div class="view">
<div id="tutorial-title" class="stroke-sub"></div> <div class="view-title stroke-sub"></div>
<div id="tutorial-content"></div> <div class="view-content"></div>
<div id="diag-txt"></div> <div id="diag-txt"></div>
<div id="about-link-btns"> <div class="left-buttons">
<div id="link-issues" class="taibtn stroke-sub link-btn" alt="Issues"> <div id="link-issues" class="taibtn stroke-sub link-btn" alt="Issues">
<a target="_blank">Issues</a> <a target="_blank">Issues</a>
</div> </div>
@ -11,6 +11,6 @@
<a href="mailto:taiko@bui.pm">taiko@bui.pm</a> <a href="mailto:taiko@bui.pm">taiko@bui.pm</a>
</div> </div>
</div> </div>
<div id="tutorial-end-button" class="taibtn stroke-sub"></div> <div class="view-end-button taibtn stroke-sub selected"></div>
</div> </div>
</div> </div>

View File

@ -9,6 +9,21 @@
<div class="measure-num input-slider"> <div class="measure-num input-slider">
<span class="reset">x</span><input type="text" value="" readonly><span class="minus">-</span><span class="plus">+</span> <span class="reset">x</span><input type="text" value="" readonly><span class="minus">-</span><span class="plus">+</span>
</div> </div>
<div class="branch-hide">
<div>Branch:</div>
<div class="branch-select select">
<span class="reset">x</span><select>
<option value="auto" selected style="background:#fff">Auto</option>
<option value="normal" style="background:#ccc">Normal</option>
<option value="advanced" style="background:#bdf">Professional</option>
<option value="master" style="background:#ebf">Master</option>
</select>
</div>
</div>
<div>Music volume:</div>
<div class="music-volume input-slider">
<span class="reset">x</span><input type="text" value="" readonly><span class="minus">-</span><span class="plus">+</span>
</div>
<label><input class="change-restart" type="checkbox">Restart on change</label> <label><input class="change-restart" type="checkbox">Restart on change</label>
<label class="autoplay-label"><input class="autoplay" type="checkbox">Auto play</label> <label class="autoplay-label"><input class="autoplay" type="checkbox">Auto play</label>
<div class="bottom-btns"> <div class="bottom-btns">

View File

@ -1,9 +1,9 @@
<div id="tutorial-outer"> <div class="view-outer">
<div id="tutorial"> <div class="view">
<div id="tutorial-title" class="stroke-sub"></div> <div class="view-title stroke-sub"></div>
<div id="tutorial-content"> <div class="view-content">
<div id="session-invite"></div> <div id="session-invite"></div>
</div> </div>
<div id="tutorial-end-button" class="taibtn stroke-sub"></div> <div class="view-end-button taibtn stroke-sub"></div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,24 @@
<div class="view-outer settings-outer">
<div class="view">
<div class="view-title stroke-sub"></div>
<div class="view-content"></div>
<div class="left-buttons">
<div id="settings-default" class="taibtn stroke-sub"></div>
</div>
<div class="view-end-button taibtn stroke-sub"></div>
<div class="view-outer shadow-outer" id="settings-gamepad">
<div class="view">
<div class="view-title stroke-sub"></div>
<div class="view-content">
<div class="setting-box">
<div id="gamepad-bg">
<div id="gamepad-value" class="stroke-sub"></div>
<div id="gamepad-buttons"></div>
</div>
</div>
</div>
<div class="view-end-button taibtn stroke-sub selected"></div>
</div>
</div>
</div>
</div>

View File

@ -6,8 +6,3 @@
<span class="stroke-sub" id="title-disclaimer-copyright"></span> <span class="stroke-sub" id="title-disclaimer-copyright"></span>
</div> </div>
</div> </div>
<div id="lang">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38 38" id="lang-icon"><circle cx="19" cy="19" r="19"/><path id="globe-path" style="fill:none;stroke-width:2;stroke:#fff"/><circle cx="19" cy="19" r="15" style="fill:none;stroke-width:2;stroke:#fff"/></svg>
<div id="lang-id" class="stroke-sub"></div>
<select id="lang-dropdown"></select>
</div>

View File

@ -1,7 +1,7 @@
<div id="tutorial-outer"> <div class="view-outer">
<div id="tutorial"> <div class="view">
<div id="tutorial-title" class="stroke-sub"></div> <div class="view-title stroke-sub"></div>
<div id="tutorial-content"></div> <div class="view-content"></div>
<div id="tutorial-end-button" class="taibtn stroke-sub"></div> <div class="view-end-button taibtn stroke-sub selected"></div>
</div> </div>
</div> </div>

View File

@ -185,6 +185,7 @@ async def connection(ws, path):
if "other_user" in user and "ws" in user["other_user"]: if "other_user" in user and "ws" in user["other_user"]:
if type == "note"\ if type == "note"\
or type == "drumroll"\ or type == "drumroll"\
or type == "branch"\
or type == "gameresults": or type == "gameresults":
await user["other_user"]["ws"].send(msgobj(type, value)) await user["other_user"]["ws"].send(msgobj(type, value))
elif type == "songsel" and user["session"]: elif type == "songsel" and user["session"]:
@ -210,6 +211,7 @@ async def connection(ws, path):
user["other_user"]["ws"].send(sent_msg1), user["other_user"]["ws"].send(sent_msg1),
user["other_user"]["ws"].send(sent_msg2) user["other_user"]["ws"].send(sent_msg2)
]) ])
del user["other_user"]["other_user"]
del user["other_user"] del user["other_user"]
else: else:
# Other user disconnected # Other user disconnected
@ -304,6 +306,7 @@ async def connection(ws, path):
user["other_user"]["ws"].send(sent_msg1), user["other_user"]["ws"].send(sent_msg1),
user["other_user"]["ws"].send(sent_msg2) user["other_user"]["ws"].send(sent_msg2)
]) ])
del user["other_user"]["other_user"]
del user["other_user"] del user["other_user"]
else: else:
# Other user disconnected # Other user disconnected
@ -324,6 +327,7 @@ async def connection(ws, path):
user["other_user"]["ws"].send(msgobj("gameend")), user["other_user"]["ws"].send(msgobj("gameend")),
user["other_user"]["ws"].send(status_event()) user["other_user"]["ws"].send(status_event())
]) ])
del user["other_user"]["other_user"]
if user["action"] == "waiting": if user["action"] == "waiting":
del server_status["waiting"][user["gameid"]] del server_status["waiting"][user["gameid"]]
await notify_status() await notify_status()