Commit ce4d8ce3 authored by Bui's avatar Bui Committed by GitHub

Merge pull request #181 from bui/audio-update

Audio update
parents c900f133 b030624a
...@@ -16,14 +16,6 @@ ...@@ -16,14 +16,6 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
#cursor{
position: fixed;
width: 1px;
height: 1px;
cursor: none;
pointer-events: none;
z-index: 1;
}
#touch-drum{ #touch-drum{
display: none; display: none;
position: absolute; position: absolute;
......
...@@ -158,8 +158,7 @@ kbd{ ...@@ -158,8 +158,7 @@ kbd{
.setting-box:first-child{ .setting-box:first-child{
margin-top: 0; margin-top: 0;
} }
.settings-outer .view-content:not(:hover) .setting-box.selected, .view-content:not(:hover) .setting-box.selected,
.view-outer:not(.settings-outer) .setting-box.selected,
.setting-box:hover{ .setting-box:hover{
background: #ffb547; background: #ffb547;
animation: 2s linear border-pulse infinite; animation: 2s linear border-pulse infinite;
...@@ -177,7 +176,6 @@ kbd{ ...@@ -177,7 +176,6 @@ kbd{
overflow: hidden; overflow: hidden;
} }
.view-content:not(:hover) .setting-box.selected .setting-name, .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 .setting-name,
.setting-box:hover #gamepad-value{ .setting-box:hover #gamepad-value{
color: #fff; color: #fff;
...@@ -193,6 +191,8 @@ kbd{ ...@@ -193,6 +191,8 @@ kbd{
border-radius: 0.2em; border-radius: 0.2em;
padding: 0.5em; padding: 0.5em;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden;
white-space: nowrap;
} }
.setting-value.selected{ .setting-value.selected{
width: calc(50% + 0.2em); width: calc(50% + 0.2em);
...@@ -215,27 +215,26 @@ kbd{ ...@@ -215,27 +215,26 @@ kbd{
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
z-index: 1; z-index: 1;
} }
#settings-gamepad{ #settings-gamepad,
#settings-latency{
display: none; display: none;
} }
#settings-gamepad .view{ #settings-gamepad .view{
position: absolute; width: 29.9em;
margin: auto; max-width: 100vw;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 574px;
height: 428px;
max-height: calc(100vh - 14em + 88px);
} }
#settings-gamepad .setting-box{ #settings-gamepad .setting-box{
height: auto; height: auto;
overflow: hidden;
}
#gamepad-bg,
#gamepad-buttons{
background-size: 20.53em;
} }
#gamepad-bg{ #gamepad-bg{
position: relative; position: relative;
width: 550px; width: 20.53em;
height: 317px; height: 11.83em;
max-height: none; max-height: none;
background-repeat: no-repeat; background-repeat: no-repeat;
text-align: center; text-align: center;
...@@ -244,11 +243,11 @@ kbd{ ...@@ -244,11 +243,11 @@ kbd{
} }
#gamepad-buttons{ #gamepad-buttons{
position: absolute; position: absolute;
left: 141px; left: 5.26em;
top: 120px; top: 4.48em;
width: 282px; width: 10.52em;
height: 131px; height: 4.89em;
background-position: 0 -318px; background-position: 0 -11.87em;
background-repeat: no-repeat; background-repeat: no-repeat;
pointer-events: none; pointer-events: none;
} }
...@@ -259,3 +258,36 @@ kbd{ ...@@ -259,3 +258,36 @@ kbd{
#gamepad-value::before{ #gamepad-value::before{
left: auto; left: auto;
} }
#settings-latency .view{
width: 30em;
}
#settings-latency .setting-value{
position: relative;
}
.setting-value:not(.selected) .latency-buttons{
display: none;
}
.setting-value .latency-buttons{
position: absolute;
top: 0;
right: 0;
bottom: 0;
padding: 0;
}
.latency-buttons span{
display: inline-block;
width: 2em;
height: 100%;
text-align: center;
background-color: #c3862a;
color: #fff;
line-height: 2em;
outline: none;
}
.latency-buttons span:hover,
.latency-buttons span:active{
background-color: #946013;
}
.left-buttons .taibtn{
z-index: 1;
}
...@@ -29,23 +29,30 @@ ...@@ -29,23 +29,30 @@
this.endButton.innerText = strings.tutorial.ok this.endButton.innerText = strings.tutorial.ok
this.endButton.setAttribute("alt", strings.tutorial.ok) this.endButton.setAttribute("alt", strings.tutorial.ok)
this.items = []
var versionUrl = gameConfig._version.url var versionUrl = gameConfig._version.url
this.getLink(this.linkIssues).href = versionUrl + "issues" this.getLink(this.linkIssues).href = versionUrl + "issues"
this.items.push(this.linkIssues)
var contactEmail = gameConfig.email var contactEmail = gameConfig.email
if (typeof contactEmail === 'string') { this.hasEmail = typeof contactEmail === "string"
if(this.hasEmail){
this.linkEmail.setAttribute("alt", contactEmail) this.linkEmail.setAttribute("alt", contactEmail)
this.getLink(this.linkEmail).href = "mailto:" + contactEmail this.getLink(this.linkEmail).href = "mailto:" + contactEmail
this.getLink(this.linkEmail).text = contactEmail this.getLink(this.linkEmail).innerText = contactEmail
} else { this.items.push(this.linkEmail)
this.linkEmail.style.display = "none" }else{
this.linkEmail.parentNode.removeChild(this.linkEmail)
} }
pageEvents.add(this.linkIssues, ["click", "touchend"], this.linkButton.bind(this)) pageEvents.add(this.linkIssues, ["click", "touchend"], this.linkButton.bind(this))
if(this.hasEmail){
pageEvents.add(this.linkEmail, ["click", "touchend"], this.linkButton.bind(this)) pageEvents.add(this.linkEmail, ["click", "touchend"], this.linkButton.bind(this))
}
pageEvents.add(this.endButton, ["mousedown", "touchstart"], this.onEnd.bind(this)) pageEvents.add(this.endButton, ["mousedown", "touchstart"], this.onEnd.bind(this))
this.items = [this.linkIssues, this.linkEmail, this.endButton] this.items.push(this.endButton)
this.selected = 2 this.selected = this.items.length - 1
this.keyboard = new Keyboard({ this.keyboard = new Keyboard({
confirm: ["enter", "space", "don_l", "don_r"], confirm: ["enter", "space", "don_l", "don_r"],
...@@ -146,6 +153,8 @@ ...@@ -146,6 +153,8 @@
} }
} }
diag.push("Language: " + strings.id + userLangStr) diag.push("Language: " + strings.id + userLangStr)
var latency = settings.getItem("latency")
diag.push("Audio Latency: " + (latency.audio > 0 ? "+" : "") + latency.audio.toString() + "ms, Video Latency: " + (latency.video > 0 ? "+" : "") + latency.video.toString() + "ms")
var errorObj = {} var errorObj = {}
if(localStorage["lastError"]){ if(localStorage["lastError"]){
try{ try{
...@@ -195,7 +204,9 @@ ...@@ -195,7 +204,9 @@
} }
var issueBody = strings.about.issueTemplate + "\n\n\n\n" + diag var issueBody = strings.about.issueTemplate + "\n\n\n\n" + diag
if(this.hasEmail){
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 return diag
} }
...@@ -214,7 +225,9 @@ ...@@ -214,7 +225,9 @@
this.keyboard.clean() this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
pageEvents.remove(this.linkIssues, ["click", "touchend"]) pageEvents.remove(this.linkIssues, ["click", "touchend"])
if(this.hasEmail){
pageEvents.remove(this.linkEmail, ["click", "touchend"]) pageEvents.remove(this.linkEmail, ["click", "touchend"])
}
pageEvents.remove(this.endButton, ["mousedown", "touchstart"]) pageEvents.remove(this.endButton, ["mousedown", "touchstart"])
if(this.textarea){ if(this.textarea){
pageEvents.remove(this.textarea, ["focus", "blur"]) pageEvents.remove(this.textarea, ["focus", "blur"])
......
...@@ -90,26 +90,8 @@ var assets = { ...@@ -90,26 +90,8 @@ var assets = {
"se_don.wav", "se_don.wav",
"se_ka.wav", "se_ka.wav",
"se_pause.wav", "se_pause.wav",
"se_calibration.wav",
"v_combo_50_meka.wav",
"v_combo_100_meka.wav",
"v_combo_200_meka.wav",
"v_combo_300_meka.wav",
"v_combo_400_meka.wav",
"v_combo_500_meka.wav",
"v_combo_600_meka.wav",
"v_combo_700_meka.wav",
"v_combo_800_meka.wav",
"v_combo_900_meka.wav",
"v_combo_1000_meka.wav",
"v_combo_1100_meka.wav",
"v_combo_1200_meka.wav",
"v_combo_1300_meka.wav",
"v_combo_1400_meka.wav",
"v_combo_over1500_meka.wav",
"v_fullcombo_meka.wav",
"v_renda_meka.wav",
"v_results.wav", "v_results.wav",
"v_sanka.wav", "v_sanka.wav",
"v_songsel.wav", "v_songsel.wav",
...@@ -127,23 +109,6 @@ var assets = { ...@@ -127,23 +109,6 @@ var assets = {
"se_results_countup.wav", "se_results_countup.wav",
"se_results_crown.wav", "se_results_crown.wav",
"v_combo_50.wav",
"v_combo_100.wav",
"v_combo_200.wav",
"v_combo_300.wav",
"v_combo_400.wav",
"v_combo_500.wav",
"v_combo_600.wav",
"v_combo_700.wav",
"v_combo_800.wav",
"v_combo_900.wav",
"v_combo_1000.wav",
"v_combo_1100.wav",
"v_combo_1200.wav",
"v_combo_1300.wav",
"v_combo_1400.wav",
"v_combo_over1500.wav",
"v_fullcombo.wav", "v_fullcombo.wav",
"v_renda.wav", "v_renda.wav",
"v_results_fullcombo.wav", "v_results_fullcombo.wav",
......
class CanvasCache{ class CanvasCache{
constructor(w, h, scale){ constructor(noSmoothing, w, h, scale){
this.noSmoothing = noSmoothing
if(w){ if(w){
this.resize(w, h, scale) this.resize(w, h, scale)
} }
...@@ -11,6 +12,9 @@ class CanvasCache{ ...@@ -11,6 +12,9 @@ class CanvasCache{
this.map = new Map() this.map = new Map()
this.canvas = document.createElement("canvas") this.canvas = document.createElement("canvas")
this.ctx = this.canvas.getContext("2d") this.ctx = this.canvas.getContext("2d")
if(this.noSmoothing){
this.ctx.imageSmoothingEnabled = false
}
} }
this.scale = scale this.scale = scale
this.x = 0 this.x = 0
......
class CanvasDraw{ class CanvasDraw{
constructor(){ constructor(noSmoothing){
this.diffStarPath = new Path2D(vectors.diffStar) this.diffStarPath = new Path2D(vectors.diffStar)
this.longVowelMark = new Path2D(vectors.longVowelMark) this.longVowelMark = new Path2D(vectors.longVowelMark)
...@@ -68,7 +68,8 @@ ...@@ -68,7 +68,8 @@
emCap: /[MWMW]/, emCap: /[MWMW]/,
rWidth: /[abdfIjo-rtvabdfIjo-rtv]/, rWidth: /[abdfIjo-rtvabdfIjo-rtv]/,
lWidth: /[ilil]/, lWidth: /[ilil]/,
ura: /\s*[\(][\)]$/ ura: /\s*[\(][\)]$/,
cjk: /[\u3040-ゞ゠-ヾ一-\u9ffe]/
} }
var numbersFull = "0123456789" var numbersFull = "0123456789"
...@@ -78,10 +79,12 @@ ...@@ -78,10 +79,12 @@
this.numbersFullToHalf[numbersFull[i]] = numbersHalf[i] this.numbersFullToHalf[numbersFull[i]] = numbersHalf[i]
this.numbersFullToHalf[numbersHalf[i]] = numbersHalf[i] this.numbersFullToHalf[numbersHalf[i]] = numbersHalf[i]
} }
this.wrapOn = [" ", "\n", "%s"]
this.stickySymbols = "!,.:;?~‐–‼、。々〜ぁぃぅぇぉっゃゅょァィゥェォッャュョ・ーヽヾ!:;?"
this.songFrameCache = new CanvasCache() this.songFrameCache = new CanvasCache(noSmoothing)
this.diffStarCache = new CanvasCache() this.diffStarCache = new CanvasCache(noSmoothing)
this.crownCache = new CanvasCache() this.crownCache = new CanvasCache(noSmoothing)
this.tmpCanvas = document.createElement("canvas") this.tmpCanvas = document.createElement("canvas")
this.tmpCtx = this.tmpCanvas.getContext("2d") this.tmpCtx = this.tmpCanvas.getContext("2d")
...@@ -818,6 +821,163 @@ ...@@ -818,6 +821,163 @@
ctx.restore() ctx.restore()
} }
wrappingText(config){
var ctx = config.ctx
var inputText = config.text.toString()
var words = []
var start = 0
var substituteIndex = 0
while(start < inputText.length){
var character = inputText.slice(start, start + 1)
if(words.length !== 0){
var previous = words[words.length - 1]
if(!previous.substitute && previous !== "\n" && this.stickySymbols.indexOf(character) !== -1){
words[words.length - 1] += character
start++
continue
}
}
var index = Infinity
var currentIndex = inputText.slice(start).search(this.regex.cjk)
if(currentIndex !== -1){
index = start + currentIndex
var on = inputText.charAt(index)
}
for(var i = 0; i < this.wrapOn.length; i++){
var currentIndex = inputText.indexOf(this.wrapOn[i], start)
if(currentIndex !== -1 && currentIndex < index){
var on = this.wrapOn[i]
index = currentIndex
}
}
if(index === Infinity){
if(start !== inputText.length){
words.push(inputText.slice(start, inputText.length))
}
break
}
var end = index + (on === " " ? 1 : 0)
if(start !== end){
words.push(inputText.slice(start, end))
}
if(on === "%s" && config.substitute){
words.push({
substitute: true,
index: substituteIndex,
width: config.substitute(config, substituteIndex, true) || 0
})
substituteIndex++
}else if(on !== " "){
words.push(on)
}
start = index + on.length
}
ctx.save()
var bold = this.bold(config.fontFamily)
ctx.font = bold + config.fontSize + "px " + config.fontFamily
ctx.textBaseline = config.baseline || "top"
ctx.textAlign = "left"
ctx.fillStyle = config.fill
var lineHeight = config.lineHeight || config.fontSize
var x = 0
var y = 0
var totalW = 0
var totalH = 0
var line = ""
var toDraw = []
var lastWidth = 0
var addToDraw = obj => {
toDraw.push(obj)
if(x + lastWidth > totalW){
totalW = x + lastWidth
}
if(y + lineHeight > totalH){
totalH = y + lineHeight
}
}
var recenter = () => {
if(config.textAlign === "center"){
for(var j in toDraw){
if(toDraw[j].y === y){
toDraw[j].x += (config.width - x - lastWidth) / 2
}
}
}
}
for(var i in words){
var skip = words[i].substitute || words[i] === "\n"
if(!skip){
var currentWidth = ctx.measureText(line + words[i]).width
}
if(skip || (x !== 0 || line) && x + currentWidth > config.width){
if(line){
addToDraw({
text: line,
x: x, y: y
})
}
if(words[i].substitute){
line = ""
var currentWidth = words[i].width
if(x + lastWidth + currentWidth > config.width){
recenter()
x = 0
y += lineHeight
lastWidth = 0
}
addToDraw({
substitute: true,
index: words[i].index,
x: x + lastWidth, y: y
})
x += lastWidth + currentWidth
lastWidth = currentWidth
}else{
recenter()
x = 0
y += lineHeight
line = words[i] === "\n" ? "" : words[i]
lastWidth = ctx.measureText(line).width
}
}else{
line += words[i]
lastWidth = currentWidth
}
}
if(line){
addToDraw({
text: line,
x: x, y: y
})
recenter()
}
var addX = 0
var addY = 0
if(config.verticalAlign === "middle"){
addY = ((config.height || 0) - totalH) / 2
}
for(var i in toDraw){
var x = config.x + toDraw[i].x + addX
var y = config.y + toDraw[i].y + addY
if(toDraw[i].text){
ctx.fillText(toDraw[i].text, x, y)
}else if(toDraw[i].substitute){
ctx.save()
ctx.translate(x, y)
config.substitute(config, toDraw[i].index)
ctx.restore()
}
}
ctx.restore()
}
diffIcon(config){ diffIcon(config){
var ctx = config.ctx var ctx = config.ctx
var scale = config.scale var scale = config.scale
......
...@@ -7,6 +7,16 @@ class Controller{ ...@@ -7,6 +7,16 @@ class Controller{
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
this.snd = this.multiplayer ? "_p" + this.multiplayer : "" this.snd = this.multiplayer ? "_p" + this.multiplayer : ""
this.calibrationMode = selectedSong.folder === "calibration"
this.audioLatency = 0
this.videoLatency = 0
if(!this.calibrationMode){
var latency = settings.getItem("latency")
if(!autoPlayEnabled){
this.audioLatency = Math.round(latency.audio) || 0
}
this.videoLatency = Math.round(latency.video) || 0 + this.audioLatency
}
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
loader.changePage("game", false) loader.changePage("game", false)
} }
...@@ -18,18 +28,40 @@ class Controller{ ...@@ -18,18 +28,40 @@ class Controller{
} }
this.offset = this.parsedSongData.soundOffset this.offset = this.parsedSongData.soundOffset
var maxCombo = this.parsedSongData.circles.filter(circle => ["don", "ka", "daiDon", "daiKa"].indexOf(circle.type) > -1 && (!circle.branch || circle.branch.name == "master")).length
if (maxCombo >= 50) {
var comboVoices = ["v_combo_50"].concat([...Array(Math.floor(maxCombo/100)).keys()].map(i => "v_combo_" + (i + 1)*100))
var promises = []
comboVoices.forEach(name => {
if (!assets.sounds[name + "_p1"]) {
promises.push(loader.loadSound(name + ".wav", snd.sfxGain).then(sound => {
assets.sounds[name + "_p1"] = assets.sounds[name].copy(snd.sfxGainL)
assets.sounds[name + "_p2"] = assets.sounds[name].copy(snd.sfxGainR)
}))
}
})
Promise.all(promises)
}
if(this.calibrationMode){
this.volume = 1
}else{
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.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 GameInput(this) this.keyboard = new GameInput(this)
this.drumSounds = settings.getItem("latency").drumSounds
this.playedSounds = {} this.playedSounds = {}
} }
run(syncWith){ run(syncWith){
...@@ -72,8 +104,8 @@ class Controller{ ...@@ -72,8 +104,8 @@ class Controller{
} }
stopMainLoop(){ stopMainLoop(){
this.mainLoopRunning = false this.mainLoopRunning = false
if(this.mainAsset){ if(this.game.mainAsset){
this.mainAsset.stop() this.game.mainAsset.stop()
} }
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
clearInterval(this.gameInterval) clearInterval(this.gameInterval)
...@@ -90,13 +122,18 @@ class Controller{ ...@@ -90,13 +122,18 @@ class Controller{
if(this.game.musicFadeOut < 3){ if(this.game.musicFadeOut < 3){
this.keyboard.checkMenuKeys() this.keyboard.checkMenuKeys()
} }
if(this.calibrationMode){
this.game.calibration()
}
if(!this.game.isPaused()){ if(!this.game.isPaused()){
this.keyboard.checkGameKeys() this.keyboard.checkGameKeys()
if(ms < 0){ if(ms < 0){
this.game.updateTime() this.game.updateTime()
}else{ }else{
if(!this.calibrationMode){
this.game.update() this.game.update()
}
if(!this.mainLoopRunning){ if(!this.mainLoopRunning){
return return
} }
...@@ -137,7 +174,7 @@ class Controller{ ...@@ -137,7 +174,7 @@ class Controller{
if(Math.round(score.gauge / 2) - 1 >= 25){ if(Math.round(score.gauge / 2) - 1 >= 25){
if(score.bad === 0){ if(score.bad === 0){
vp = "fullcombo" vp = "fullcombo"
this.playSoundMeka("v_fullcombo", 1.350) this.playSound("v_fullcombo", 1.350)
}else{ }else{
vp = "clear" vp = "clear"
} }
...@@ -158,16 +195,23 @@ class Controller{ ...@@ -158,16 +195,23 @@ class Controller{
if(!fadeIn){ if(!fadeIn){
this.clean() this.clean()
} }
if(this.calibrationMode){
new SettingsView(this.touchEnabled, false, null, "latency")
}else{
new SongSelect(false, fadeIn, this.touchEnabled) new SongSelect(false, fadeIn, this.touchEnabled)
} }
}
restartSong(){ restartSong(){
this.clean() this.clean()
if(this.multiplayer){ if(this.multiplayer){
new LoadSong(this.selectedSong, false, true, this.touchEnabled) new LoadSong(this.selectedSong, false, true, this.touchEnabled)
}else{ }else{
new Promise(resolve => { new Promise(resolve => {
if(this.calibrationMode){
resolve()
}else{
var songObj = assets.songs.find(song => song.id === this.selectedSong.folder) var songObj = assets.songs.find(song => song.id === this.selectedSong.folder)
if(songObj.chart){ if(songObj.chart && songObj.chart !== "blank"){
var reader = new FileReader() var reader = new FileReader()
var promise = pageEvents.load(reader).then(event => { var promise = pageEvents.load(reader).then(event => {
this.songData = event.target.result.replace(/\0/g, "").split("\n") this.songData = event.target.result.replace(/\0/g, "").split("\n")
...@@ -181,31 +225,28 @@ class Controller{ ...@@ -181,31 +225,28 @@ class Controller{
}else{ }else{
resolve() resolve()
} }
}
}).then(() => { }).then(() => {
var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled) var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled)
taikoGame.run() taikoGame.run()
}) })
} }
} }
playSound(id, time){ playSound(id, time, noSnd){
if(!this.drumSounds && (id === "neiro_1_don" || id === "neiro_1_ka" || id === "se_don" || id === "se_ka")){
return
}
var ms = Date.now() + (time || 0) * 1000 var ms = Date.now() + (time || 0) * 1000
if(!(id in this.playedSounds) || ms > this.playedSounds[id] + 30){ if(!(id in this.playedSounds) || ms > this.playedSounds[id] + 30){
assets.sounds[id + this.snd].play(time) assets.sounds[id + (noSnd ? "" : this.snd)].play(time)
this.playedSounds[id] = ms this.playedSounds[id] = ms
} }
} }
playSoundMeka(soundID, time){ togglePause(forcePause, pauseMove, noSound){
var meka = ""
if(this.autoPlayEnabled && !this.multiplayer){
meka = "_meka"
}
this.playSound(soundID + meka, time)
}
togglePause(){
if(this.multiplayer === 1){ if(this.multiplayer === 1){
this.syncWith.game.togglePause() this.syncWith.game.togglePause(forcePause, pauseMove, noSound)
} }
this.game.togglePause() this.game.togglePause(forcePause, pauseMove, noSound)
} }
getKeys(){ getKeys(){
return this.keyboard.getKeys() return this.keyboard.getKeys()
......
...@@ -45,7 +45,12 @@ class Game{ ...@@ -45,7 +45,12 @@ class Game{
} }
initTiming(){ initTiming(){
// Date when the chrono is started (before the game begins) // Date when the chrono is started (before the game begins)
var offsetTime = Math.max(0, this.timeForDistanceCircle - this.songData.circles[0].ms) |0 var firstCircle = this.songData.circles[0]
if(this.controller.calibrationMode){
var offsetTime = 0
}else{
var offsetTime = Math.max(0, this.timeForDistanceCircle - (firstCircle ? firstCircle.ms : 0)) |0
}
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
...@@ -57,8 +62,8 @@ class Game{ ...@@ -57,8 +62,8 @@ class Game{
this.startDate = Date.now() + offsetTime this.startDate = Date.now() + offsetTime
} }
update(){ update(){
// Main operations
this.updateTime() this.updateTime()
// Main operations
this.updateCirclesStatus() this.updateCirclesStatus()
this.checkPlays() this.checkPlays()
// Event operations // Event operations
...@@ -82,14 +87,14 @@ class Game{ ...@@ -82,14 +87,14 @@ class Game{
if(circle && (!circle.branch || circle.branch.active) && !circle.isPlayed){ if(circle && (!circle.branch || circle.branch.active) && !circle.isPlayed){
var type = circle.type var type = circle.type
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll" var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
var endTime = circle.endTime + (drumrollNotes ? 0 : this.rules.bad) var endTime = circle.endTime + (drumrollNotes ? 0 : this.rules.bad) + this.controller.audioLatency
if(ms >= circle.ms){ if(ms >= circle.ms + this.controller.audioLatency){
if(drumrollNotes && !circle.rendaPlayed && ms < endTime){ if(drumrollNotes && !circle.rendaPlayed && ms < endTime + this.controller.audioLatency){
circle.rendaPlayed = true circle.rendaPlayed = true
if(this.rules.difficulty === "easy"){ if(this.rules.difficulty === "easy"){
assets.sounds["v_renda" + this.controller.snd].stop() assets.sounds["v_renda" + this.controller.snd].stop()
this.controller.playSoundMeka("v_renda") this.controller.playSound("v_renda")
} }
} }
if(!circle.beatMSCopied){ if(!circle.beatMSCopied){
...@@ -109,7 +114,7 @@ class Game{ ...@@ -109,7 +114,7 @@ class Game{
this.updateCurrentCircle() this.updateCurrentCircle()
if(this.controller.multiplayer === 1){ if(this.controller.multiplayer === 1){
var value = { var value = {
pace: (ms - circle.ms) / circle.timesHit pace: (ms - circle.ms - this.controller.audioLatency) / circle.timesHit
} }
if(type === "drumroll" || type === "daiDrumroll"){ if(type === "drumroll" || type === "daiDrumroll"){
value.kaAmount = circle.timesKa / circle.timesHit value.kaAmount = circle.timesKa / circle.timesHit
...@@ -211,7 +216,7 @@ class Game{ ...@@ -211,7 +216,7 @@ class Game{
for(var i = this.currentCircle + 1; i < circles.length; i++){ for(var i = this.currentCircle + 1; i < circles.length; i++){
var circle = circles[i] var circle = circles[i]
var relative = ms - circle.ms var relative = ms - circle.ms - this.controller.audioLatency
if(!circle.branch || circle.branch.active){ if(!circle.branch || circle.branch.active){
if((!circleIsNote(circle) || relative < -this.rules.bad)){ if((!circleIsNote(circle) || relative < -this.rules.bad)){
break break
...@@ -310,7 +315,7 @@ class Game{ ...@@ -310,7 +315,7 @@ 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.ms var relative = currentTime - circle.ms - this.controller.audioLatency
if(relative >= this.rules.ok){ if(relative >= this.rules.ok){
var fixedNote = this.fixNoteStream(keysDon) var fixedNote = this.fixNoteStream(keysDon)
...@@ -366,7 +371,7 @@ class Game{ ...@@ -366,7 +371,7 @@ class Game{
if(this.controller.multiplayer === 1){ if(this.controller.multiplayer === 1){
var value = { var value = {
score: score, score: score,
ms: circle.ms - currentTime, ms: circle.ms - currentTime - this.controller.audioLatency,
dai: typeDai ? (keyDai ? 2 : 1) : 0 dai: typeDai ? (keyDai ? 2 : 1) : 0
} }
if((!keysDon || !typeDon) && (!keysKa || !typeKa)){ if((!keysDon || !typeDon) && (!keysKa || !typeKa)){
...@@ -375,7 +380,7 @@ class Game{ ...@@ -375,7 +380,7 @@ class Game{
p2.send("note", value) p2.send("note", value)
} }
}else{ }else{
if(circle.ms > currentTime || currentTime > circle.endTime){ if(circle.ms + this.controller.audioLatency > currentTime || currentTime > circle.endTime + this.controller.audioLatency){
return true return true
} }
if(keysDon && type === "balloon"){ if(keysDon && type === "balloon"){
...@@ -400,7 +405,7 @@ class Game{ ...@@ -400,7 +405,7 @@ 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.ms) / circle.timesHit pace: (this.elapsedTime - circle.ms + this.controller.audioLatency) / circle.timesHit
}) })
} }
}else{ }else{
...@@ -447,17 +452,19 @@ class Game{ ...@@ -447,17 +452,19 @@ 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].endTime var circle = circles[circles.length - 1]
this.lastCircle = circle ? circle.endTime : 0
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].endTime circle = syncCircles[syncCircles.length - 1]
var syncLastCircle = circle ? circle.endTime : 0
if(syncLastCircle > this.lastCircle){ if(syncLastCircle > this.lastCircle){
this.lastCircle = syncLastCircle this.lastCircle = syncLastCircle
} }
} }
} }
if(!this.fadeOutStarted && ms >= this.lastCircle + 2000){ if(!this.fadeOutStarted && ms >= this.lastCircle + 2000 + this.controller.audioLatency){
this.fadeOutStarted = ms this.fadeOutStarted = ms
if(this.controller.multiplayer){ if(this.controller.multiplayer){
this.controller.syncWith.game.fadeOutStarted = ms this.controller.syncWith.game.fadeOutStarted = ms
...@@ -495,28 +502,51 @@ class Game{ ...@@ -495,28 +502,51 @@ 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 && this.mainAsset){ if(this.calibrationState === "audio"){
var beatInterval = this.controller.view.beatInterval
var startAt = ms % beatInterval
var duration = this.mainAsset.duration * 1000
if(startAt < duration){
this.mainAsset.playLoop(0, false, startAt / 1000, 0, beatInterval / 1000)
}else{
this.mainAsset.playLoop((startAt - duration) / 1000, false, 0, 0, beatInterval / 1000)
}
}else 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
} }
} }
togglePause(){ togglePause(forcePause, pauseMove, noSound){
if(!this.paused){ if(!this.paused){
assets.sounds["se_pause"].play() if(forcePause === false){
return
}
if(!noSound){
this.controller.playSound("se_pause", 0, true)
}
this.paused = true this.paused = true
this.latestDate = Date.now() this.latestDate = Date.now()
if(this.mainAsset){ if(this.mainAsset){
this.mainAsset.stop() this.mainAsset.stop()
} }
this.mainMusicPlaying = false this.mainMusicPlaying = false
this.view.pauseMove(0, true) this.view.pauseMove(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") pageEvents.send("pause")
}else{ }else if(!forcePause){
assets.sounds["se_cancel"].play() if(forcePause !== false && this.calibrationState && ["audioHelp", "audioComplete", "videoHelp", "videoComplete", "results"].indexOf(this.calibrationState) !== -1){
return
}
if(this.calibrationState === "audioHelp" || this.calibrationState === "videoHelp"){
this.calibrationState = this.calibrationState === "audioHelp" ? "audio" : "video"
this.controller.view.pauseOptions = strings.pauseOptions
this.controller.playSound("se_don", 0, true)
}else if(!noSound){
this.controller.playSound("se_cancel", 0, true)
}
this.paused = false this.paused = false
var currentDate = Date.now() var currentDate = Date.now()
this.startDate += currentDate - this.latestDate this.startDate += currentDate - this.latestDate
...@@ -579,8 +609,8 @@ class Game{ ...@@ -579,8 +609,8 @@ class Game{
if(this.combo > this.globalScore.maxCombo){ if(this.combo > this.globalScore.maxCombo){
this.globalScore.maxCombo = this.combo this.globalScore.maxCombo = this.combo
} }
if(this.combo === 50 || this.combo > 0 && this.combo % 100 === 0 && this.combo < 1500 || this.combo > 0 && this.combo % 500 === 0){ if(this.combo === 50 || this.combo > 0 && this.combo % 100 === 0 && this.combo <= 5000){
this.controller.playSoundMeka("v_combo_" + (this.combo <= 1400 ? this.combo : "over1500")) this.controller.playSound("v_combo_" + this.combo)
} }
if (this.songData.scoremode == 2 && this.combo > 0 && this.combo % 100 == 0) { if (this.songData.scoremode == 2 && this.combo > 0 && this.combo % 100 == 0) {
this.globalScore.points += 10000; this.globalScore.points += 10000;
...@@ -683,7 +713,7 @@ class Game{ ...@@ -683,7 +713,7 @@ class Game{
if(!circle || circle.branch === currentBranch[pastActive]){ if(!circle || circle.branch === currentBranch[pastActive]){
var ms = this.elapsedTime var ms = this.elapsedTime
var closestCircle = circles.findIndex(circle => { var closestCircle = circles.findIndex(circle => {
return (!circle.branch || circle.branch.active) && circle.endTime >= ms return (!circle.branch || circle.branch.active) && circle.endTime + this.controller.audioLatency >= ms
}) })
if(closestCircle !== -1){ if(closestCircle !== -1){
this.currentCircle = closestCircle this.currentCircle = closestCircle
...@@ -701,4 +731,104 @@ class Game{ ...@@ -701,4 +731,104 @@ class Game{
this.sectionNotes = [] this.sectionNotes = []
this.sectionDrumroll = 0 this.sectionDrumroll = 0
} }
clearKeyTime(){
var keyboard = this.controller.keyboard
for(var key in keyboard.keyTime){
keyboard.keys[key] = null
keyboard.keyTime[key] = -Infinity
}
}
calibration(){
var view = this.controller.view
if(!this.calibrationState){
this.controller.parsedSongData.measures = []
this.calibrationProgress = {
audio: 0,
video: 0,
requirement: 40
}
this.calibrationReset("audio", true)
}
var progress = this.calibrationProgress
var state = this.calibrationState
switch(state){
case "audio":
case "video":
if(state === "audio" && !this.mainAsset){
this.mainAsset = assets.sounds["se_calibration"]
this.mainMusicPlaying = false
}
if(progress.hit >= progress.requirement){
var reduced = 0
for(var i = 2; i < progress.offsets.length; i++){
reduced += progress.offsets[i]
}
progress[state] = Math.max(0, Math.round(reduced / progress.offsets.length - 2))
this.calibrationState += "Complete"
view.pauseOptions = []
this.clearKeyTime()
this.togglePause(true, 1)
this.mainAsset = null
}
break
case "audioComplete":
case "videoComplete":
if(Date.now() - this.latestDate > 3000){
var audioComplete = this.calibrationState === "audioComplete"
this.controller.playSound("se_pause", 0, true)
if(audioComplete){
this.calibrationReset("video")
}else{
view.pauseOptions = [
strings.calibration.retryPrevious,
strings.calibration.finish
]
}
this.calibrationState = audioComplete ? "videoHelp" : "results"
}
break
}
}
calibrationHit(ms){
var progress = this.calibrationProgress
var beatInterval = this.controller.view.beatInterval
var current = Math.floor((ms + 100) / beatInterval)
if(current !== progress.last){
var offset = ((ms + 100) % beatInterval) - 100
var offsets = progress.offsets
if(offsets.length >= progress.requirement){
offsets.shift()
}
offsets.push(offset)
progress.hit++
progress.last = current
this.globalScore.gauge = 100 / (progress.requirement / progress.hit)
}
}
calibrationReset(to, togglePause){
var view = this.controller.view
this.songData.circles = []
view.pauseOptions = [
to === "audio" ? strings.calibration.back : strings.calibration.retryPrevious,
strings.calibration.start
]
this.calibrationState = to + "Help"
var progress = this.calibrationProgress
progress.offsets = []
progress.hit = 0
progress.last = null
this.globalScore.gauge = 0
if(to === "video"){
this.clearKeyTime()
this.initTiming()
this.latestDate = this.startDate
this.elapsedTime = 0
view.ms = 0
}
if(togglePause){
this.togglePause(true, 1, true)
}else{
view.pauseMove(1, true)
}
}
} }
...@@ -94,7 +94,7 @@ class GameInput{ ...@@ -94,7 +94,7 @@ class GameInput{
} }
} }
checkMenuKeys(){ checkMenuKeys(){
if(!this.controller.multiplayer && !this.locked){ if(!this.controller.multiplayer && !this.locked && this.controller.view.pauseOptions.length !== 0){
var moveMenu = 0 var moveMenu = 0
var ms = this.game.getAccurateTime() var ms = this.game.getAccurateTime()
this.gamepadMenu.play((pressed, name) => { this.gamepadMenu.play((pressed, name) => {
...@@ -146,7 +146,7 @@ class GameInput{ ...@@ -146,7 +146,7 @@ class GameInput{
this.checkKey("don_l", "menu", moveMenuConfirm) this.checkKey("don_l", "menu", moveMenuConfirm)
this.checkKey("don_r", "menu", moveMenuConfirm) this.checkKey("don_r", "menu", moveMenuConfirm)
if(moveMenu && this.game.isPaused()){ if(moveMenu && this.game.isPaused()){
assets.sounds["se_ka"].play() this.controller.playSound("se_ka", 0, true)
this.controller.view.pauseMove(moveMenu) this.controller.view.pauseMove(moveMenu)
} }
} }
...@@ -197,11 +197,19 @@ class GameInput{ ...@@ -197,11 +197,19 @@ class GameInput{
return return
} }
this.keyTime[name] = ms this.keyTime[name] = ms
var calibrationState = this.game.calibrationState
var calibration = calibrationState && !this.game.paused
if(name == "don_l" || name == "don_r"){ if(name == "don_l" || name == "don_r"){
if(calibration){
this.game.calibrationHit(ms)
}else{
this.checkKeySound(name, "don") this.checkKeySound(name, "don")
}
this.keyboardEvents++ this.keyboardEvents++
}else if(name == "ka_l" || name == "ka_r"){ }else if(name == "ka_l" || name == "ka_r"){
if(!calibration){
this.checkKeySound(name, "ka") this.checkKeySound(name, "ka")
}
this.keyboardEvents++ this.keyboardEvents++
} }
}else{ }else{
......
...@@ -35,14 +35,20 @@ class LoadSong{ ...@@ -35,14 +35,20 @@ class LoadSong{
var song = this.selectedSong var song = this.selectedSong
var id = song.folder var id = song.folder
var promises = [] var promises = []
if(song.folder !== "calibration"){
assets.sounds["v_start"].play() assets.sounds["v_start"].play()
var songObj = assets.songs.find(song => song.id === id)
}else{
var songObj = {
"music": "muted",
"chart": "blank"
}
}
song.songBg = this.randInt(1, 5) song.songBg = this.randInt(1, 5)
song.songStage = this.randInt(1, 3) song.songStage = this.randInt(1, 3)
song.donBg = this.randInt(1, 6) song.donBg = this.randInt(1, 6)
var songObj = assets.songs.find(song => song.id === id)
if(song.songSkin && song.songSkin.name){ if(song.songSkin && song.songSkin.name){
var imgLoad = [] var imgLoad = []
for(var type in song.songSkin){ for(var type in song.songSkin){
...@@ -117,6 +123,9 @@ class LoadSong{ ...@@ -117,6 +123,9 @@ class LoadSong{
} }
})) }))
if(songObj.chart){ if(songObj.chart){
if(songObj.chart === "blank"){
this.songData = ""
}else{
var reader = new FileReader() var reader = new FileReader()
promises.push(pageEvents.load(reader).then(event => { promises.push(pageEvents.load(reader).then(event => {
this.songData = event.target.result.replace(/\0/g, "").split("\n") this.songData = event.target.result.replace(/\0/g, "").split("\n")
...@@ -126,6 +135,7 @@ class LoadSong{ ...@@ -126,6 +135,7 @@ class LoadSong{
}else{ }else{
reader.readAsText(songObj.chart) 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")
......
...@@ -123,7 +123,7 @@ ...@@ -123,7 +123,7 @@
return [string.slice(0, index), string.slice(index + delimiter.length)] return [string.slice(0, index), string.slice(index + delimiter.length)]
} }
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 = Math.abs(meta.bpm) || 120 var bpm = Math.abs(meta.bpm) || 120
var scroll = 1 var scroll = 1
......
...@@ -10,6 +10,14 @@ class Scoresheet{ ...@@ -10,6 +10,14 @@ class Scoresheet{
this.canvas = document.getElementById("canvas") this.canvas = document.getElementById("canvas")
this.ctx = this.canvas.getContext("2d") this.ctx = this.canvas.getContext("2d")
var resolution = settings.getItem("resolution")
var noSmoothing = resolution === "low" || resolution === "lowest"
if(noSmoothing){
this.ctx.imageSmoothingEnabled = false
}
if(resolution === "lowest"){
this.canvas.style.imageRendering = "pixelated"
}
this.game = document.getElementById("game") this.game = document.getElementById("game")
this.fadeScreen = document.createElement("div") this.fadeScreen = document.createElement("div")
...@@ -28,8 +36,8 @@ class Scoresheet{ ...@@ -28,8 +36,8 @@ class Scoresheet{
this.frame = 1000 / 60 this.frame = 1000 / 60
this.numbers = "001122334455667788900112233445".split("") this.numbers = "001122334455667788900112233445".split("")
this.draw = new CanvasDraw() this.draw = new CanvasDraw(noSmoothing)
this.canvasCache = new CanvasCache() this.canvasCache = new CanvasCache(noSmoothing)
this.keyboard = new Keyboard({ this.keyboard = new Keyboard({
confirm: ["enter", "space", "esc", "don_l", "don_r"] confirm: ["enter", "space", "esc", "don_l", "don_r"]
...@@ -105,7 +113,7 @@ class Scoresheet{ ...@@ -105,7 +113,7 @@ class Scoresheet{
if(!p2.session){ if(!p2.session){
this.state.screen = "scoresShown" this.state.screen = "scoresShown"
this.state.screenMS = this.getMS() this.state.screenMS = this.getMS()
assets.sounds["neiro_1_don"].play() this.controller.playSound("neiro_1_don", 0, true)
} }
} }
toSongsel(fromP2){ toSongsel(fromP2){
...@@ -114,7 +122,7 @@ class Scoresheet{ ...@@ -114,7 +122,7 @@ class Scoresheet{
this.state.screen = "fadeOut" this.state.screen = "fadeOut"
this.state.screenMS = this.getMS() this.state.screenMS = this.getMS()
if(!fromP2){ if(!fromP2){
assets.sounds["neiro_1_don"].play() this.controller.playSound("neiro_1_don", 0, true)
} }
} }
} }
......
...@@ -2,11 +2,15 @@ class Settings{ ...@@ -2,11 +2,15 @@ class Settings{
constructor(){ constructor(){
var ios = /iPhone|iPad/.test(navigator.userAgent) var ios = /iPhone|iPad/.test(navigator.userAgent)
var phone = /Android|iPhone|iPad/.test(navigator.userAgent) var phone = /Android|iPhone|iPad/.test(navigator.userAgent)
this.allLanguages = []
for(var i in allStrings){
this.allLanguages.push(i)
}
this.items = { this.items = {
language: { language: {
type: "language", type: "language",
options: ["ja", "en", "cn", "tw", "ko"], options: this.allLanguages,
default: this.getLang() default: this.getLang()
}, },
resolution: { resolution: {
...@@ -34,6 +38,14 @@ class Settings{ ...@@ -34,6 +38,14 @@ class Settings{
options: ["a", "b", "c"], options: ["a", "b", "c"],
default: "a", default: "a",
gamepad: true gamepad: true
},
latency: {
type: "latency",
default: {
"audio": 0,
"video": 0,
"drumSounds": true
}
} }
} }
...@@ -61,6 +73,22 @@ class Settings{ ...@@ -61,6 +73,22 @@ class Settings{
} }
} }
this.storage[i] = obj this.storage[i] = obj
}else if(current.type === "latency"){
var obj = {}
for(var j in current.default){
if(storage[i] && j in storage[i]){
if(j === "drumSounds"){
obj[j] = !!storage[i][j]
continue
}else if(!isNaN(storage[i][j])){
obj[j] = Math.round(parseFloat(storage[i][j]) || 0)
continue
}
}
obj = null
break
}
this.storage[i] = obj
}else{ }else{
this.storage[i] = storage[i] this.storage[i] = storage[i]
} }
...@@ -107,7 +135,7 @@ class Settings{ ...@@ -107,7 +135,7 @@ class Settings{
} }
} }
} }
return "ja" return this.allLanguages[0]
} }
setLang(lang, noEvent){ setLang(lang, noEvent){
strings = lang strings = lang
...@@ -122,7 +150,7 @@ class Settings{ ...@@ -122,7 +150,7 @@ class Settings{
} }
class SettingsView{ class SettingsView{
constructor(touchEnabled, tutorial, songId){ constructor(touchEnabled, tutorial, songId, toSetting){
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
this.tutorial = tutorial this.tutorial = tutorial
this.songId = songId this.songId = songId
...@@ -130,9 +158,15 @@ class SettingsView{ ...@@ -130,9 +158,15 @@ class SettingsView{
loader.changePage("settings", tutorial) loader.changePage("settings", tutorial)
assets.sounds["bgm_settings"].playLoop(0.1, false, 0, 1.392, 26.992) assets.sounds["bgm_settings"].playLoop(0.1, false, 0, 1.392, 26.992)
this.defaultButton = document.getElementById("settings-default") this.defaultButton = document.getElementById("settings-default")
this.viewOuter = this.getElement("view-outer")
if(touchEnabled){ if(touchEnabled){
this.getElement("view-outer").classList.add("touch-enabled") this.viewOuter.classList.add("touch-enabled")
} }
this.touchEnd = []
pageEvents.add(this.viewOuter, ["mouseup", "touchend"], event => {
this.touchEnd.forEach(func => func(event))
})
var gamepadEnabled = false var gamepadEnabled = false
if("getGamepads" in navigator){ if("getGamepads" in navigator){
var gamepads = navigator.getGamepads() var gamepads = navigator.getGamepads()
...@@ -145,19 +179,22 @@ class SettingsView{ ...@@ -145,19 +179,22 @@ class SettingsView{
} }
this.mode = "settings" this.mode = "settings"
this.pressedKeys = {}
this.keyboard = new Keyboard({ this.keyboard = new Keyboard({
"confirm": ["enter", "space", "don_l", "don_r"], "confirm": ["enter", "space", "don_l", "don_r"],
"up": ["up"], "up": ["up"],
"previous": ["left", "ka_l"], "right": ["right", "ka_r"],
"next": ["right", "down", "ka_r"], "down": ["down"],
"left": ["left", "ka_l"],
"back": ["esc"], "back": ["esc"],
"other": ["wildcard"] "other": ["wildcard"]
}, this.keyPressed.bind(this)) }, this.keyPressed.bind(this))
this.gamepad = new Gamepad({ this.gamepad = new Gamepad({
"confirm": ["b", "ls", "rs"], "confirm": ["b", "ls", "rs"],
"up": ["u", "lsu"], "up": ["u", "lsu"],
"previous": ["l", "lb", "lt", "lsl"], "right": ["r", "rb", "rt", "lsr"],
"next": ["d", "r", "rb", "rt", "lsd", "lsr"], "down": ["d", "lsd"],
"left": ["l", "lb", "lt", "lsl"],
"back": ["start", "a"] "back": ["start", "a"]
}, this.keyPressed.bind(this)) }, this.keyPressed.bind(this))
...@@ -182,15 +219,15 @@ class SettingsView{ ...@@ -182,15 +219,15 @@ class SettingsView{
var nameDiv = document.createElement("div") var nameDiv = document.createElement("div")
nameDiv.classList.add("setting-name", "stroke-sub") nameDiv.classList.add("setting-name", "stroke-sub")
var name = strings.settings[i].name var name = strings.settings[i].name
nameDiv.innerText = name this.setAltText(nameDiv, name)
nameDiv.setAttribute("alt", name)
settingBox.appendChild(nameDiv) settingBox.appendChild(nameDiv)
var valueDiv = document.createElement("div") var valueDiv = document.createElement("div")
valueDiv.classList.add("setting-value") valueDiv.classList.add("setting-value")
this.getValue(i, valueDiv) this.getValue(i, valueDiv)
settingBox.appendChild(valueDiv) settingBox.appendChild(valueDiv)
content.appendChild(settingBox) content.appendChild(settingBox)
if(this.items.length === this.selected){ if(!toSetting && this.items.length === this.selected || toSetting === i){
this.selected = this.items.length
settingBox.classList.add("selected") settingBox.classList.add("selected")
} }
this.addTouch(settingBox, event => this.setValue(i)) this.addTouch(settingBox, event => this.setValue(i))
...@@ -226,8 +263,99 @@ class SettingsView{ ...@@ -226,8 +263,99 @@ class SettingsView{
this.gamepadButtons = document.getElementById("gamepad-buttons") this.gamepadButtons = document.getElementById("gamepad-buttons")
this.gamepadValue = document.getElementById("gamepad-value") this.gamepadValue = document.getElementById("gamepad-value")
this.latencySettings = document.getElementById("settings-latency")
this.addTouch(this.latencySettings, event => {
if(event.target === event.currentTarget){
this.latencyBack()
}
})
this.latencyTitle = this.latencySettings.getElementsByClassName("view-title")[0]
this.latencyItems = []
this.latencySelected = 0
var latencyContent = this.latencySettings.getElementsByClassName("view-content")[0]
var latencyWindow = ["calibration", "audio", "video", "drumSounds"]
for(let i in latencyWindow){
let current = latencyWindow[i]
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.latency[current]
this.setAltText(nameDiv, name)
settingBox.appendChild(nameDiv)
let outputObject = {
id: current,
settingBox: settingBox,
nameDiv: nameDiv
}
if(current === "calibration"){
nameDiv.style.width = "100%"
}else{
var valueDiv = document.createElement("div")
valueDiv.classList.add("setting-value")
settingBox.appendChild(valueDiv)
var valueText = document.createTextNode("")
valueDiv.appendChild(valueText)
this.latencyGetValue(current, valueText)
if(current !== "drumSounds"){
var buttons = document.createElement("div")
buttons.classList.add("latency-buttons")
var buttonMinus = document.createElement("span")
buttonMinus.innerText = "-"
buttons.appendChild(buttonMinus)
this.addTouchRepeat(buttonMinus, event => {
this.latencySetAdjust(outputObject, -1)
})
var buttonPlus = document.createElement("span")
buttonPlus.innerText = "+"
buttons.appendChild(buttonPlus)
this.addTouchRepeat(buttonPlus, event => {
this.latencySetAdjust(outputObject, 1)
})
valueDiv.appendChild(buttons)
}
}
latencyContent.appendChild(settingBox)
if(this.latencyItems.length === this.latencySelected){
settingBox.classList.add("selected")
}
this.addTouch(settingBox, event => {
if(event.target.tagName !== "SPAN"){
this.latencySetValue(current, event.type === "touchstart")
}
})
if(current !== "calibration"){
outputObject.valueDiv = valueDiv
outputObject.valueText = valueText
outputObject.buttonMinus = buttonMinus
outputObject.buttonPlus = buttonPlus
}
this.latencyItems.push(outputObject)
}
this.latencyDefaultButton = document.getElementById("latency-default")
this.latencyItems.push({
id: "default",
settingBox: this.latencyDefaultButton
})
this.addTouch(this.latencyDefaultButton, event => this.latencyDefault())
this.latencyEndButton = this.latencySettings.getElementsByClassName("view-end-button")[0]
this.latencyItems.push({
id: "back",
settingBox: this.latencyEndButton
})
this.addTouch(this.latencyEndButton, event => this.latencyBack(true))
this.setStrings() this.setStrings()
this.drumSounds = settings.getItem("latency").drumSounds
this.playedSounds = {}
this.redrawRunning = true
this.redrawBind = this.redraw.bind(this)
this.redraw()
if(toSetting === "latency"){
this.mode = "latency"
this.latencySet()
}
pageEvents.send("settings") pageEvents.send("settings")
} }
getElement(name){ getElement(name){
...@@ -246,6 +374,23 @@ class SettingsView{ ...@@ -246,6 +374,23 @@ class SettingsView{
callback(event) callback(event)
}) })
} }
addTouchRepeat(element, callback){
this.addTouch(element, event => {
var active = true
var func = () => {
active = false
this.touchEnd.splice(this.touchEnd.indexOf(func), 1)
}
this.touchEnd.push(func)
var repeat = delay => {
if(active){
callback()
setTimeout(() => repeat(50), delay)
}
}
repeat(400)
})
}
removeTouch(element){ removeTouch(element){
pageEvents.remove(element, ["mousedown", "touchstart"]) pageEvents.remove(element, ["mousedown", "touchstart"])
} }
...@@ -274,6 +419,17 @@ class SettingsView{ ...@@ -274,6 +419,17 @@ class SettingsView{
valueDiv.appendChild(keyDiv) valueDiv.appendChild(keyDiv)
} }
return return
}else if(current.type === "latency"){
var audioVideo = [Math.round(value.audio), Math.round(value.video)]
var latencyValue = strings.settings[name].value.split("%s")
var latencyIndex = 0
value = ""
latencyValue.forEach((string, i) => {
if(i !== 0){
value += this.addMs(audioVideo[latencyIndex++])
}
value += string
})
} }
valueDiv.innerText = value valueDiv.innerText = value
} }
...@@ -285,6 +441,7 @@ class SettingsView{ ...@@ -285,6 +441,7 @@ class SettingsView{
if(this.mode !== "settings"){ if(this.mode !== "settings"){
if(this.selected === selectedIndex){ if(this.selected === selectedIndex){
this.keyboardBack(selected) this.keyboardBack(selected)
this.playSound("se_don")
} }
return return
} }
...@@ -303,24 +460,37 @@ class SettingsView{ ...@@ -303,24 +460,37 @@ class SettingsView{
selected.valueDiv.classList.add("selected") selected.valueDiv.classList.add("selected")
this.keyboardKeys = {} this.keyboardKeys = {}
this.keyboardSet() this.keyboardSet()
assets.sounds["se_don"].play() this.playSound("se_don")
return return
}else if(current.type === "gamepad"){ }else if(current.type === "gamepad"){
this.mode = "gamepad" this.mode = "gamepad"
this.gamepadSelected = current.options.indexOf(value) this.gamepadSelected = current.options.indexOf(value)
this.gamepadSet() this.gamepadSet()
assets.sounds["se_don"].play() this.playSound("se_don")
return
}else if(current.type === "latency"){
this.mode = "latency"
this.latencySet()
this.playSound("se_don")
return return
} }
settings.setItem(name, value) settings.setItem(name, value)
this.getValue(name, this.items[this.selected].valueDiv) this.getValue(name, this.items[this.selected].valueDiv)
assets.sounds["se_ka"].play() this.playSound("se_ka")
if(current.type === "language"){ if(current.type === "language"){
this.setLang(allStrings[value]) this.setLang(allStrings[value])
} }
} }
keyPressed(pressed, name, event){ keyPressed(pressed, name, event, repeat){
if(!pressed){ if(pressed){
if(!this.pressedKeys[name]){
this.pressedKeys[name] = this.getMS() + 300
}
}else{
this.pressedKeys[name] = 0
return
}
if(repeat && name !== "up" && name !== "right" && name !== "down" && name !== "left"){
return return
} }
this.touched = false this.touched = false
...@@ -334,31 +504,31 @@ class SettingsView{ ...@@ -334,31 +504,31 @@ class SettingsView{
}else{ }else{
this.setValue(selected.id) this.setValue(selected.id)
} }
}else if(name === "up" || name === "previous" || name === "next"){ }else if(name === "up" || name === "right" || name === "down" || name === "left"){
selected.settingBox.classList.remove("selected") selected.settingBox.classList.remove("selected")
do{ do{
this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1)) this.selected = this.mod(this.items.length, this.selected + ((name === "right" || name === "down") ? 1 : -1))
}while(this.items[this.selected].id === "default" && name !== "previous") }while(this.items[this.selected].id === "default" && name !== "left")
selected = this.items[this.selected] selected = this.items[this.selected]
selected.settingBox.classList.add("selected") selected.settingBox.classList.add("selected")
selected.settingBox.scrollIntoView() selected.settingBox.scrollIntoView()
assets.sounds["se_ka"].play() this.playSound("se_ka")
}else if(name === "back"){ }else if(name === "back"){
this.onEnd() this.onEnd()
} }
}else if(this.mode === "gamepad"){ }else if(this.mode === "gamepad"){
if(name === "confirm"){ if(name === "confirm"){
this.gamepadBack(true) this.gamepadBack(true)
}else if(name === "up" || name === "previous" || name === "next"){ }else if(name === "up" || name === "right" || name === "down" || name === "left"){
this.gamepadSet(name === "next" ? 1 : -1) this.gamepadSet((name === "right" || name === "down") ? 1 : -1)
}else if(name === "back"){ }else if(name === "back"){
this.gamepadBack() this.gamepadBack()
} }
}else if(this.mode === "keyboard"){ }else if(this.mode === "keyboard"){
if(name === "back"){ if(name === "back"){
this.keyboardBack(selected) this.keyboardBack(selected)
assets.sounds["se_cancel"].play() this.playSound("se_cancel")
}else{ }else if(event){
event.preventDefault() event.preventDefault()
var currentKey = event.key.toLowerCase() var currentKey = event.key.toLowerCase()
for(var i in this.keyboardKeys){ for(var i in this.keyboardKeys){
...@@ -367,10 +537,40 @@ class SettingsView{ ...@@ -367,10 +537,40 @@ class SettingsView{
} }
} }
var current = this.keyboardCurrent var current = this.keyboardCurrent
assets.sounds[current === "ka_l" || current === "ka_r" ? "se_ka" : "se_don"].play() this.playSound(current === "ka_l" || current === "ka_r" ? "se_ka" : "se_don")
this.keyboardKeys[current] = [currentKey] this.keyboardKeys[current] = [currentKey]
this.keyboardSet() this.keyboardSet()
} }
}else if(this.mode === "latency"){
var latencySelected = this.latencyItems[this.latencySelected]
if(name === "confirm"){
if(latencySelected.id === "back"){
this.latencyBack(true)
}else if(latencySelected.id === "default"){
this.latencyDefault()
}else{
this.latencySetValue(latencySelected.id)
}
}else if(name === "up" || name === "right" || name === "down" || name === "left"){
latencySelected.settingBox.classList.remove("selected")
do{
this.latencySelected = this.mod(this.latencyItems.length, this.latencySelected + ((name === "right" || name === "down") ? 1 : -1))
}while(this.latencyItems[this.latencySelected].id === "default" && name !== "left")
latencySelected = this.latencyItems[this.latencySelected]
latencySelected.settingBox.classList.add("selected")
latencySelected.settingBox.scrollIntoView()
this.playSound("se_ka")
}else if(name === "back"){
this.latencyBack()
}
}else if(this.mode === "latencySet"){
var latencySelected = this.latencyItems[this.latencySelected]
if(name === "confirm" || name === "back"){
this.latencySetBack(latencySelected)
this.playSound(name === "confirm" ? "se_don" : "se_cancel")
}else if(name === "up" || name === "right" || name === "down" || name === "left"){
this.latencySetAdjust(latencySelected, (name === "up" || name === "right") ? 1 : -1)
}
} }
} }
keyboardSet(){ keyboardSet(){
...@@ -416,14 +616,13 @@ class SettingsView{ ...@@ -416,14 +616,13 @@ class SettingsView{
var current = settings.items[selected.id] var current = settings.items[selected.id]
if(diff){ if(diff){
this.gamepadSelected = this.mod(current.options.length, this.gamepadSelected + diff) this.gamepadSelected = this.mod(current.options.length, this.gamepadSelected + diff)
assets.sounds["se_ka"].play() this.playSound("se_ka")
} }
var opt = current.options[this.gamepadSelected] var opt = current.options[this.gamepadSelected]
var value = strings.settings[selected.id][opt] var value = strings.settings[selected.id][opt]
this.gamepadValue.innerText = value this.setAltText(this.gamepadValue, value)
this.gamepadValue.setAttribute("alt", value) this.gamepadButtons.style.backgroundPosition = "0 " + (-11.87 - 4.93 * this.gamepadSelected) + "em"
this.gamepadButtons.style.backgroundPosition = "0 " + (-318 - 132 * this.gamepadSelected) + "px" this.gamepadSettings.style.display = "flex"
this.gamepadSettings.style.display = "block"
} }
gamepadBack(confirm){ gamepadBack(confirm){
if(this.mode !== "gamepad"){ if(this.mode !== "gamepad"){
...@@ -433,10 +632,142 @@ class SettingsView{ ...@@ -433,10 +632,142 @@ class SettingsView{
var current = settings.items[selected.id] var current = settings.items[selected.id]
settings.setItem(selected.id, current.options[this.gamepadSelected]) settings.setItem(selected.id, current.options[this.gamepadSelected])
this.getValue(selected.id, selected.valueDiv) this.getValue(selected.id, selected.valueDiv)
assets.sounds[confirm ? "se_don" : "se_cancel"].play() this.playSound(confirm ? "se_don" : "se_cancel")
this.gamepadSettings.style.display = "" this.gamepadSettings.style.display = ""
this.mode = "settings" this.mode = "settings"
} }
latencySet(){
if(this.mode !== "latency"){
return
}
var selected = this.items[this.selected]
var current = settings.items[selected.id]
this.latencySettings.style.display = "flex"
}
latencyGetValue(name, valueText){
var currentLatency = settings.getItem("latency")
if(name === "drumSounds"){
valueText.data = currentLatency[name] ? strings.settings.on : strings.settings.off
}else{
valueText.data = this.addMs(currentLatency[name] || 0)
}
}
latencySetValue(name, touched){
var selectedIndex = this.latencyItems.findIndex(item => item.id === name)
var selected = this.latencyItems[selectedIndex]
if(this.mode === "latencySet"){
this.latencySetBack(this.latencyItems[this.latencySelected])
if(this.latencySelected === selectedIndex){
this.playSound("se_don")
return
}
}else if(this.mode !== "latency"){
return
}
if(name === "calibration"){
this.playSound("se_don")
this.clean()
new LoadSong({
"title": strings.calibration.title,
"folder": "calibration",
"type": "tja",
"songSkin": {}
}, false, false, touched)
}else if(name === "drumSounds"){
this.drumSounds = !settings.getItem("latency")[name]
this.latencySave(name, this.drumSounds)
this.latencyGetValue(name, selected.valueText)
this.playSound("se_don")
}else{
var value = Math.round(settings.getItem("latency")[name] || 0)
if(this.latencySelected !== selectedIndex){
this.latencyItems[this.latencySelected].settingBox.classList.remove("selected")
this.latencySelected = selectedIndex
selected.settingBox.classList.add("selected")
}
this.mode = "latencySet"
selected.settingBox.style.animation = "none"
selected.valueDiv.classList.add("selected")
selected.value = value
this.playSound("se_don")
}
}
latencySetAdjust(selected, add){
selected.value += add
if(selected.value > 500){
selected.value = 500
}else if(selected.value < -200){
selected.value = -200
}else{
this.playSound("se_ka")
}
selected.valueText.data = this.addMs(selected.value)
}
latencySetBack(selected){
this.mode = "latency"
selected.settingBox.style.animation = ""
selected.valueDiv.classList.remove("selected")
this.latencySave(selected.id, selected.value)
this.latencyGetValue(selected.id, selected.valueText)
}
latencySave(id, value){
var input = settings.getItem("latency")
var output = {}
for(var i in input){
if(i === id){
output[i] = value
}else{
output[i] = input[i]
}
}
settings.setItem("latency", output)
}
latencyDefault(){
if(this.mode === "latencySet"){
this.latencySetBack(this.latencyItems[this.latencySelected])
}else if(this.mode !== "latency"){
return
}
settings.setItem("latency", null)
this.latencyItems.forEach(item => {
if(item.id === "audio" || item.id === "video" || item.id === "drumSounds"){
this.latencyGetValue(item.id, item.valueText)
}
})
this.drumSounds = settings.getItem("latency").drumSounds
this.playSound("se_don")
}
latencyBack(confirm){
if(this.mode === "latencySet"){
this.latencySetBack(this.latencyItems[this.latencySelected])
if(!confirm){
this.playSound("se_don")
return
}
}
if(this.mode !== "latency"){
return
}
var selected = this.items[this.selected]
var current = settings.items[selected.id]
this.getValue(selected.id, selected.valueDiv)
this.playSound(confirm ? "se_don" : "se_cancel")
this.latencySettings.style.display = ""
this.mode = "settings"
}
addMs(input){
var split = strings.calibration.ms.split("%s")
var index = 0
var output = ""
var inputStrings = [(input > 0 ? "+" : "") + input.toString()]
split.forEach((string, i) => {
if(i !== 0){
output += inputStrings[index++]
}
output += string
})
return output
}
defaultSettings(){ defaultSettings(){
if(this.mode === "keyboard"){ if(this.mode === "keyboard"){
this.keyboardBack(this.items[this.selected]) this.keyboardBack(this.items[this.selected])
...@@ -447,11 +778,17 @@ class SettingsView{ ...@@ -447,11 +778,17 @@ class SettingsView{
this.setLang(allStrings[settings.getItem("language")]) this.setLang(allStrings[settings.getItem("language")])
this.keyboard.update() this.keyboard.update()
pageEvents.setKbd() pageEvents.setKbd()
assets.sounds["se_don"].play() this.latencyItems.forEach(item => {
if(item.id === "audio" || item.id === "video" || item.id === "drumSounds"){
this.latencyGetValue(item.id, item.valueText)
}
})
this.drumSounds = settings.getItem("latency").drumSounds
this.playSound("se_don")
} }
onEnd(){ onEnd(){
this.clean() this.clean()
assets.sounds["se_don"].play() this.playSound("se_don")
setTimeout(() => { setTimeout(() => {
if(this.tutorial && !this.touched){ if(this.tutorial && !this.touched){
new Tutorial(false, this.songId) new Tutorial(false, this.songId)
...@@ -472,41 +809,94 @@ class SettingsView{ ...@@ -472,41 +809,94 @@ class SettingsView{
var item = this.items[i] var item = this.items[i]
if(item.valueDiv){ if(item.valueDiv){
var name = strings.settings[item.id].name var name = strings.settings[item.id].name
item.nameDiv.innerText = name this.setAltText(item.nameDiv, name)
item.nameDiv.setAttribute("alt", name)
this.getValue(item.id, item.valueDiv) this.getValue(item.id, item.valueDiv)
} }
} }
for(var i in this.latencyItems){
var current = this.latencyItems[i]
if(current.nameDiv){
this.setAltText(current.nameDiv, strings.settings.latency[current.id])
}
if(current.valueText){
this.latencyGetValue(current.id, current.valueText)
}
}
this.setStrings() this.setStrings()
} }
setStrings(){ setStrings(){
this.viewTitle.innerText = strings.gameSettings this.setAltText(this.viewTitle, strings.gameSettings)
this.viewTitle.setAttribute("alt", strings.gameSettings) this.setAltText(this.endButton, strings.settings.ok)
this.endButton.innerText = strings.settings.ok this.setAltText(this.gamepadTitle, strings.settings.gamepadLayout.name)
this.endButton.setAttribute("alt", strings.settings.ok) this.setAltText(this.gamepadEndButton, strings.settings.ok)
this.gamepadTitle.innerText = strings.settings.gamepadLayout.name this.setAltText(this.latencyTitle, strings.settings.latency.name)
this.gamepadTitle.setAttribute("alt", strings.settings.gamepadLayout.name) this.setAltText(this.latencyDefaultButton, strings.settings.default)
this.gamepadEndButton.innerText = strings.settings.ok this.setAltText(this.latencyEndButton, strings.settings.ok)
this.gamepadEndButton.setAttribute("alt", strings.settings.ok) this.setAltText(this.defaultButton, strings.settings.default)
this.defaultButton.innerText = strings.settings.default }
this.defaultButton.setAttribute("alt", strings.settings.default) setAltText(element, text){
element.innerText = text
element.setAttribute("alt", text)
} }
mod(length, index){ mod(length, index){
return ((index % length) + length) % length return ((index % length) + length) % length
} }
playSound(id, time){
if(!this.drumSounds && (id === "se_don" || id === "se_ka" || id === "se_cancel")){
return
}
var ms = Date.now() + (time || 0) * 1000
if(!(id in this.playedSounds) || ms > this.playedSounds[id] + 30){
assets.sounds[id].play(time)
this.playedSounds[id] = ms
}
}
redraw(){
if(!this.redrawRunning){
return
}
requestAnimationFrame(this.redrawBind)
var ms = this.getMS()
for(var key in this.pressedKeys){
if(this.pressedKeys[key]){
if(ms >= this.pressedKeys[key] + 50){
this.keyPressed(true, key, null, true)
this.pressedKeys[key] = ms
}
}
}
}
getMS(){
return Date.now()
}
clean(){ clean(){
this.redrawRunning = false
this.keyboard.clean() this.keyboard.clean()
this.gamepad.clean() this.gamepad.clean()
assets.sounds["bgm_settings"].stop() assets.sounds["bgm_settings"].stop()
pageEvents.remove(this.viewOuter, ["mouseup", "touchend"])
for(var i in this.items){ for(var i in this.items){
this.removeTouch(this.items[i].settingBox) this.removeTouch(this.items[i].settingBox)
} }
for(var i in this.latencyItems){
this.removeTouch(this.latencyItems[i].settingBox)
if(this.latencyItems[i].buttonMinus){
this.removeTouch(this.latencyItems[i].buttonMinus)
this.removeTouch(this.latencyItems[i].buttonPlus)
}
}
if(this.defaultButton){ if(this.defaultButton){
delete this.defaultButton delete this.defaultButton
} }
this.removeTouch(this.gamepadSettings) this.removeTouch(this.gamepadSettings)
this.removeTouch(this.gamepadEndButton) this.removeTouch(this.gamepadEndButton)
this.removeTouch(this.gamepadBox) this.removeTouch(this.gamepadBox)
this.removeTouch(this.latencySettings)
this.removeTouch(this.latencyDefaultButton)
this.removeTouch(this.latencyEndButton)
delete this.viewOuter
delete this.touchEnd
delete this.tutorialTitle delete this.tutorialTitle
delete this.endButton delete this.endButton
delete this.items delete this.items
...@@ -516,6 +906,11 @@ class SettingsView{ ...@@ -516,6 +906,11 @@ class SettingsView{
delete this.gamepadBox delete this.gamepadBox
delete this.gamepadButtons delete this.gamepadButtons
delete this.gamepadValue delete this.gamepadValue
delete this.latencyItems
delete this.latencySettings
delete this.latencyTitle
delete this.latencyDefaultButton
delete this.latencyEndButton
if(this.resolution !== settings.getItem("resolution")){ if(this.resolution !== settings.getItem("resolution")){
for(var i in assets.image){ for(var i in assets.image){
if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_")){ if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_")){
......
...@@ -5,6 +5,14 @@ class SongSelect{ ...@@ -5,6 +5,14 @@ class SongSelect{
loader.changePage("songselect", false) loader.changePage("songselect", false)
this.canvas = document.getElementById("song-sel-canvas") this.canvas = document.getElementById("song-sel-canvas")
this.ctx = this.canvas.getContext("2d") this.ctx = this.canvas.getContext("2d")
var resolution = settings.getItem("resolution")
var noSmoothing = resolution === "low" || resolution === "lowest"
if(noSmoothing){
this.ctx.imageSmoothingEnabled = false
}
if(resolution === "lowest"){
this.canvas.style.imageRendering = "pixelated"
}
this.songSkin = { this.songSkin = {
"selected": { "selected": {
...@@ -207,13 +215,13 @@ class SongSelect{ ...@@ -207,13 +215,13 @@ class SongSelect{
}] }]
this.optionsList = [strings.none, strings.auto, strings.netplay] this.optionsList = [strings.none, strings.auto, strings.netplay]
this.draw = new CanvasDraw() this.draw = new CanvasDraw(noSmoothing)
this.songTitleCache = new CanvasCache() this.songTitleCache = new CanvasCache(noSmoothing)
this.selectTextCache = new CanvasCache() this.selectTextCache = new CanvasCache(noSmoothing)
this.categoryCache = new CanvasCache() this.categoryCache = new CanvasCache(noSmoothing)
this.difficultyCache = new CanvasCache() this.difficultyCache = new CanvasCache(noSmoothing)
this.sessionCache = new CanvasCache() this.sessionCache = new CanvasCache(noSmoothing)
this.currentSongCache = new CanvasCache() this.currentSongCache = new CanvasCache(noSmoothing)
this.difficulty = [strings.easy, strings.normal, strings.hard, strings.oni] this.difficulty = [strings.easy, strings.normal, strings.hard, strings.oni]
this.difficultyId = ["easy", "normal", "hard", "oni", "ura"] this.difficultyId = ["easy", "normal", "hard", "oni", "ura"]
...@@ -234,6 +242,9 @@ class SongSelect{ ...@@ -234,6 +242,9 @@ class SongSelect{
fromTutorial = false fromTutorial = false
} }
this.drumSounds = settings.getItem("latency").drumSounds
this.playedSounds = {}
var songIdIndex = -1 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)
...@@ -252,7 +263,7 @@ class SongSelect{ ...@@ -252,7 +263,7 @@ class SongSelect{
}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[songIdIndex !== -1 ? "v_diffsel" : "v_songsel"].play() this.playSound(songIdIndex !== -1 ? "v_diffsel" : "v_songsel")
snd.musicGain.fadeOut() snd.musicGain.fadeOut()
this.playBgm(false) this.playBgm(false)
} }
...@@ -436,7 +447,7 @@ class SongSelect{ ...@@ -436,7 +447,7 @@ class SongSelect{
window.open(this.songs[this.selectedSong].maker.url) window.open(this.songs[this.selectedSong].maker.url)
}else if(moveBy === this.diffOptions.length + 4){ }else if(moveBy === this.diffOptions.length + 4){
this.state.ura = !this.state.ura this.state.ura = !this.state.ura
assets.sounds["se_ka"].play() this.playSound("se_ka")
if(this.selectedDiff === this.diffOptions.length + 4 && !this.state.ura){ if(this.selectedDiff === this.diffOptions.length + 4 && !this.state.ura){
this.state.move = -1 this.state.move = -1
} }
...@@ -564,7 +575,7 @@ class SongSelect{ ...@@ -564,7 +575,7 @@ class SongSelect{
var soundsDelay = Math.abs((scroll + resize) / moveBy) var soundsDelay = Math.abs((scroll + resize) / moveBy)
for(var i = 0; i < Math.abs(moveBy) - 1; i++){ for(var i = 0; i < Math.abs(moveBy) - 1; i++){
assets.sounds["se_ka"].play((resize + i * soundsDelay) / 1000) this.playSound("se_ka", (resize + i * soundsDelay) / 1000)
} }
this.pointer(false) this.pointer(false)
} }
...@@ -574,7 +585,7 @@ class SongSelect{ ...@@ -574,7 +585,7 @@ class SongSelect{
this.state.move = moveBy this.state.move = moveBy
this.state.moveMS = this.getMS() - 500 this.state.moveMS = this.getMS() - 500
this.state.locked = 1 this.state.locked = 1
assets.sounds["se_ka"].play() this.playSound("se_ka")
} }
} }
...@@ -605,15 +616,15 @@ class SongSelect{ ...@@ -605,15 +616,15 @@ class SongSelect{
this.selectedDiff = this.diffOptions.length + 3 this.selectedDiff = this.diffOptions.length + 3
} }
assets.sounds["se_don"].play() this.playSound("se_don")
assets.sounds["v_songsel"].stop() assets.sounds["v_songsel"].stop()
assets.sounds["v_diffsel"].play(0.3) this.playSound("v_diffsel", 0.3)
pageEvents.send("song-select-difficulty", currentSong) pageEvents.send("song-select-difficulty", currentSong)
}else if(currentSong.action === "back"){ }else if(currentSong.action === "back"){
this.clean() this.clean()
this.toTitleScreen() this.toTitleScreen()
}else if(currentSong.action === "random"){ }else if(currentSong.action === "random"){
assets.sounds["se_don"].play() this.playSound("se_don")
this.state.locked = true this.state.locked = true
do{ do{
var i = Math.floor(Math.random() * this.songs.length) var i = Math.floor(Math.random() * this.songs.length)
...@@ -650,7 +661,7 @@ class SongSelect{ ...@@ -650,7 +661,7 @@ class SongSelect{
this.state.moveHover = null this.state.moveHover = null
assets.sounds["v_diffsel"].stop() assets.sounds["v_diffsel"].stop()
assets.sounds["se_cancel"].play() this.playSound("se_cancel")
} }
this.clearHash() this.clearHash()
pageEvents.send("song-select-back") pageEvents.send("song-select-back")
...@@ -659,7 +670,7 @@ class SongSelect{ ...@@ -659,7 +670,7 @@ class SongSelect{
this.clean() this.clean()
var selectedSong = this.songs[this.selectedSong] var selectedSong = this.songs[this.selectedSong]
assets.sounds["v_diffsel"].stop() assets.sounds["v_diffsel"].stop()
assets.sounds["se_don"].play() this.playSound("se_don")
try{ try{
if(assets.customSongs){ if(assets.customSongs){
...@@ -698,7 +709,7 @@ class SongSelect{ ...@@ -698,7 +709,7 @@ class SongSelect{
} }
toOptions(moveBy){ toOptions(moveBy){
if(!p2.session){ if(!p2.session){
assets.sounds["se_ka"].play() this.playSound("se_ka")
this.selectedDiff = 1 this.selectedDiff = 1
do{ do{
this.state.options = this.mod(this.optionsList.length, this.state.options + moveBy) this.state.options = this.mod(this.optionsList.length, this.state.options + moveBy)
...@@ -707,7 +718,7 @@ class SongSelect{ ...@@ -707,7 +718,7 @@ class SongSelect{
} }
toTitleScreen(){ toTitleScreen(){
if(!p2.session){ if(!p2.session){
assets.sounds["se_cancel"].play() this.playSound("se_cancel")
this.clean() this.clean()
setTimeout(() => { setTimeout(() => {
new Titlescreen() new Titlescreen()
...@@ -715,21 +726,21 @@ class SongSelect{ ...@@ -715,21 +726,21 @@ class SongSelect{
} }
} }
toTutorial(){ toTutorial(){
assets.sounds["se_don"].play() this.playSound("se_don")
this.clean() this.clean()
setTimeout(() => { setTimeout(() => {
new Tutorial(true) new Tutorial(true)
}, 500) }, 500)
} }
toAbout(){ toAbout(){
assets.sounds["se_don"].play() this.playSound("se_don")
this.clean() this.clean()
setTimeout(() => { setTimeout(() => {
new About(this.touchEnabled) new About(this.touchEnabled)
}, 500) }, 500)
} }
toSettings(){ toSettings(){
assets.sounds["se_don"].play() this.playSound("se_don")
this.clean() this.clean()
setTimeout(() => { setTimeout(() => {
new SettingsView(this.touchEnabled) new SettingsView(this.touchEnabled)
...@@ -744,7 +755,7 @@ class SongSelect{ ...@@ -744,7 +755,7 @@ class SongSelect{
}else{ }else{
localStorage["selectedSong"] = this.selectedSong localStorage["selectedSong"] = this.selectedSong
assets.sounds["se_don"].play() this.playSound("se_don")
this.clean() this.clean()
setTimeout(() => { setTimeout(() => {
new Session(this.touchEnabled) new Session(this.touchEnabled)
...@@ -755,7 +766,7 @@ class SongSelect{ ...@@ -755,7 +766,7 @@ class SongSelect{
if(assets.customSongs){ if(assets.customSongs){
assets.customSongs = false assets.customSongs = false
assets.songs = assets.songsDefault assets.songs = assets.songsDefault
assets.sounds["se_don"].play() this.playSound("se_don")
this.clean() this.clean()
setTimeout(() => { setTimeout(() => {
new SongSelect("browse", false, this.touchEnabled) new SongSelect("browse", false, this.touchEnabled)
...@@ -984,7 +995,7 @@ class SongSelect{ ...@@ -984,7 +995,7 @@ class SongSelect{
var scroll = resize2 - resize - scrollDelay * 2 var scroll = resize2 - resize - scrollDelay * 2
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() this.playSound("se_ka")
var previousSelectedSong = this.selectedSong 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){ if(previousSelectedSong !== this.selectedSong){
...@@ -2041,6 +2052,17 @@ class SongSelect{ ...@@ -2041,6 +2052,17 @@ class SongSelect{
} }
} }
playSound(id, time){
if(!this.drumSounds && (id === "se_don" || id === "se_ka" || id === "se_cancel")){
return
}
var ms = Date.now() + (time || 0) * 1000
if(!(id in this.playedSounds) || ms > this.playedSounds[id] + 30){
assets.sounds[id].play(time)
this.playedSounds[id] = ms
}
}
getMS(){ getMS(){
return Date.now() return Date.now()
} }
......
...@@ -142,11 +142,42 @@ ...@@ -142,11 +142,42 @@
b: "タイプB", b: "タイプB",
c: "タイプC" c: "タイプC"
}, },
latency: {
name: "Latency",
value: "Audio: %s, Video: %s",
calibration: "Latency Calibration",
audio: "Audio",
video: "Video",
drumSounds: "Drum Sounds"
},
on: "オン", on: "オン",
off: "オフ", off: "オフ",
default: "既定値にリセット", default: "既定値にリセット",
ok: "OK" ok: "OK"
} }
this.calibration = {
title: "Latency Calibration",
ms: "%sms",
back: "Back to Settings",
retryPrevious: "Retry Previous",
start: "Start",
finish: "Finish",
audioHelp: {
title: "Audio Latency Calibration",
content: "Listen to a sound playing in the background.\n\nHit the surface of the drum (%s or %s) as you hear it!",
contentAlt: "Listen to a sound playing in the background.\n\nHit the surface of the drum as you hear it!"
},
audioComplete: "Audio Latency Calibration completed!",
videoHelp: {
title: "Video Latency Calibration",
content: "This time there will be no sounds.\n\nInstead, watch for notes blinking on the circle-shaped frame, hit the drum as they appear!"
},
videoComplete: "Video Latency Calibration completed!",
results: {
title: "Latency Calibration Results",
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.browserSupport = { this.browserSupport = {
browserWarning: "サポートされていないブラウザを実行しています (%s)", browserWarning: "サポートされていないブラウザを実行しています (%s)",
details: "詳しく", details: "詳しく",
...@@ -302,11 +333,42 @@ function StringsEn(){ ...@@ -302,11 +333,42 @@ function StringsEn(){
b: "Type B", b: "Type B",
c: "Type C" c: "Type C"
}, },
latency: {
name: "Latency",
value: "Audio: %s, Video: %s",
calibration: "Latency Calibration",
audio: "Audio",
video: "Video",
drumSounds: "Drum Sounds"
},
on: "On", on: "On",
off: "Off", off: "Off",
default: "Reset to Defaults", default: "Reset to Defaults",
ok: "OK" ok: "OK"
} }
this.calibration = {
title: "Latency Calibration",
ms: "%sms",
back: "Back to Settings",
retryPrevious: "Retry Previous",
start: "Start",
finish: "Finish",
audioHelp: {
title: "Audio Latency Calibration",
content: "Listen to a sound playing in the background.\n\nHit the surface of the drum (%s or %s) as you hear it!",
contentAlt: "Listen to a sound playing in the background.\n\nHit the surface of the drum as you hear it!"
},
audioComplete: "Audio Latency Calibration completed!",
videoHelp: {
title: "Video Latency Calibration",
content: "This time there will be no sounds.\n\nInstead, watch for notes blinking on the circle-shaped frame, hit the drum as they appear!"
},
videoComplete: "Video Latency Calibration completed!",
results: {
title: "Latency Calibration Results",
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
...@@ -462,11 +524,42 @@ function StringsCn(){ ...@@ -462,11 +524,42 @@ function StringsCn(){
b: "类型B", b: "类型B",
c: "类型C" c: "类型C"
}, },
latency: {
name: "Latency",
value: "Audio: %s, Video: %s",
calibration: "Latency Calibration",
audio: "Audio",
video: "Video",
drumSounds: "Drum Sounds"
},
on: "", on: "",
off: "", off: "",
default: "重置为默认值", default: "重置为默认值",
ok: "确定" ok: "确定"
} }
this.calibration = {
title: "Latency Calibration",
ms: "%sms",
back: "Back to Settings",
retryPrevious: "Retry Previous",
start: "Start",
finish: "Finish",
audioHelp: {
title: "Audio Latency Calibration",
content: "Listen to a sound playing in the background.\n\nHit the surface of the drum (%s or %s) as you hear it!",
contentAlt: "Listen to a sound playing in the background.\n\nHit the surface of the drum as you hear it!"
},
audioComplete: "Audio Latency Calibration completed!",
videoHelp: {
title: "Video Latency Calibration",
content: "This time there will be no sounds.\n\nInstead, watch for notes blinking on the circle-shaped frame, hit the drum as they appear!"
},
videoComplete: "Video Latency Calibration completed!",
results: {
title: "Latency Calibration Results",
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
...@@ -622,11 +715,42 @@ function StringsTw(){ ...@@ -622,11 +715,42 @@ function StringsTw(){
b: "類型B", b: "類型B",
c: "類型C" c: "類型C"
}, },
latency: {
name: "Latency",
value: "Audio: %s, Video: %s",
calibration: "Latency Calibration",
audio: "Audio",
video: "Video",
drumSounds: "Drum Sounds"
},
on: "", on: "",
off: "", off: "",
default: "重置為默認值", default: "重置為默認值",
ok: "確定" ok: "確定"
} }
this.calibration = {
title: "Latency Calibration",
ms: "%sms",
back: "Back to Settings",
retryPrevious: "Retry Previous",
start: "Start",
finish: "Finish",
audioHelp: {
title: "Audio Latency Calibration",
content: "Listen to a sound playing in the background.\n\nHit the surface of the drum (%s or %s) as you hear it!",
contentAlt: "Listen to a sound playing in the background.\n\nHit the surface of the drum as you hear it!"
},
audioComplete: "Audio Latency Calibration completed!",
videoHelp: {
title: "Video Latency Calibration",
content: "This time there will be no sounds.\n\nInstead, watch for notes blinking on the circle-shaped frame, hit the drum as they appear!"
},
videoComplete: "Video Latency Calibration completed!",
results: {
title: "Latency Calibration Results",
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
...@@ -782,11 +906,42 @@ function StringsKo(){ ...@@ -782,11 +906,42 @@ function StringsKo(){
b: "타입 B", b: "타입 B",
c: "타입 C" c: "타입 C"
}, },
latency: {
name: "Latency",
value: "Audio: %s, Video: %s",
calibration: "Latency Calibration",
audio: "Audio",
video: "Video",
drumSounds: "Drum Sounds"
},
on: "", on: "",
off: "오프", off: "오프",
default: "기본값으로 재설정", default: "기본값으로 재설정",
ok: "확인" ok: "확인"
} }
this.calibration = {
title: "Latency Calibration",
ms: "%sms",
back: "Back to Settings",
retryPrevious: "Retry Previous",
start: "Start",
finish: "Finish",
audioHelp: {
title: "Audio Latency Calibration",
content: "Listen to a sound playing in the background.\n\nHit the surface of the drum (%s or %s) as you hear it!",
contentAlt: "Listen to a sound playing in the background.\n\nHit the surface of the drum as you hear it!"
},
audioComplete: "Audio Latency Calibration completed!",
videoHelp: {
title: "Video Latency Calibration",
content: "This time there will be no sounds.\n\nInstead, watch for notes blinking on the circle-shaped frame, hit the drum as they appear!"
},
videoComplete: "Video Latency Calibration completed!",
results: {
title: "Latency Calibration Results",
content: "Audio latency: %s\nVideo latency: %s\n\nYou can configure these latency values in the settings."
}
}
this.browserSupport = { this.browserSupport = {
browserWarning: "You are running an unsupported browser (%s)", browserWarning: "You are running an unsupported browser (%s)",
details: "Details...", details: "Details...",
......
...@@ -4,8 +4,15 @@ ...@@ -4,8 +4,15 @@
this.canvas = document.getElementById("canvas") this.canvas = document.getElementById("canvas")
this.ctx = this.canvas.getContext("2d") this.ctx = this.canvas.getContext("2d")
var resolution = settings.getItem("resolution")
var noSmoothing = resolution === "low" || resolution === "lowest"
if(noSmoothing){
this.ctx.imageSmoothingEnabled = false
}
if(resolution === "lowest"){
this.canvas.style.imageRendering = "pixelated"
}
this.cursor = document.getElementById("cursor")
this.gameDiv = document.getElementById("game") this.gameDiv = document.getElementById("game")
this.songBg = document.getElementById("songbg") this.songBg = document.getElementById("songbg")
this.songStage = document.getElementById("song-stage") this.songStage = document.getElementById("song-stage")
...@@ -73,6 +80,7 @@ ...@@ -73,6 +80,7 @@
} }
this.nextBeat = 0 this.nextBeat = 0
this.gogoTime = 0 this.gogoTime = 0
this.gogoTimeStarted = -Infinity
this.drumroll = [] this.drumroll = []
this.touchEvents = 0 this.touchEvents = 0
if(this.controller.parsedSongData.branches){ if(this.controller.parsedSongData.branches){
...@@ -103,16 +111,20 @@ ...@@ -103,16 +111,20 @@
} }
} }
if(this.controller.calibrationMode){
this.beatInterval = 512
}else{
this.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval this.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval
}
this.font = strings.font this.font = strings.font
this.draw = new CanvasDraw() this.draw = new CanvasDraw(noSmoothing)
this.assets = new ViewAssets(this) this.assets = new ViewAssets(this)
this.titleCache = new CanvasCache() this.titleCache = new CanvasCache(noSmoothing)
this.comboCache = new CanvasCache() this.comboCache = new CanvasCache(noSmoothing)
this.pauseCache = new CanvasCache() this.pauseCache = new CanvasCache(noSmoothing)
this.branchCache = new CanvasCache() this.branchCache = new CanvasCache(noSmoothing)
this.multiplayer = this.controller.multiplayer this.multiplayer = this.controller.multiplayer
...@@ -120,6 +132,9 @@ ...@@ -120,6 +132,9 @@
this.touch = -Infinity this.touch = -Infinity
this.touchAnimation = settings.getItem("touchAnimation") this.touchAnimation = settings.getItem("touchAnimation")
versionDiv.classList.add("version-hide")
loader.screen.parentNode.insertBefore(versionDiv, loader.screen)
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
if(this.controller.touchEnabled){ if(this.controller.touchEnabled){
...@@ -134,7 +149,6 @@ ...@@ -134,7 +149,6 @@
pageEvents.add(this.canvas, "touchstart", this.ontouch.bind(this)) pageEvents.add(this.canvas, "touchstart", this.ontouch.bind(this))
this.gameDiv.classList.add("touch-visible") this.gameDiv.classList.add("touch-visible")
document.getElementById("version").classList.add("version-hide")
this.touchFullBtn = document.getElementById("touch-full-btn") this.touchFullBtn = document.getElementById("touch-full-btn")
pageEvents.add(this.touchFullBtn, "touchend", toggleFullscreen) pageEvents.add(this.touchFullBtn, "touchend", toggleFullscreen)
...@@ -444,12 +458,14 @@ ...@@ -444,12 +458,14 @@
ctx.fill() ctx.fill()
// Difficulty // Difficulty
if(this.controller.selectedSong.difficulty){
ctx.drawImage(assets.image["difficulty"], ctx.drawImage(assets.image["difficulty"],
0, 144 * this.difficulty[this.controller.selectedSong.difficulty], 0, 144 * this.difficulty[this.controller.selectedSong.difficulty],
168, 143, 168, 143,
126, this.multiplayer === 2 ? 497 : 228, 126, this.multiplayer === 2 ? 497 : 228,
62, 53 62, 53
) )
}
// Badges // Badges
if(this.controller.autoPlayEnabled && !this.controller.multiplayer){ if(this.controller.autoPlayEnabled && !this.controller.multiplayer){
...@@ -593,6 +609,7 @@ ...@@ -593,6 +609,7 @@
ctx.globalAlpha = 1 ctx.globalAlpha = 1
// Difficulty // Difficulty
if(this.controller.selectedSong.difficulty){
ctx.drawImage(assets.image["difficulty"], ctx.drawImage(assets.image["difficulty"],
0, 144 * this.difficulty[this.controller.selectedSong.difficulty], 0, 144 * this.difficulty[this.controller.selectedSong.difficulty],
168, 143, 168, 143,
...@@ -611,6 +628,7 @@ ...@@ -611,6 +628,7 @@
ctx.strokeText(text, 87, this.multiplayer === 2 ? 310 : 348) ctx.strokeText(text, 87, this.multiplayer === 2 ? 310 : 348)
ctx.fillText(text, 87, this.multiplayer === 2 ? 310 : 348) ctx.fillText(text, 87, this.multiplayer === 2 ? 310 : 348)
ctx.miterLimit = 10 ctx.miterLimit = 10
}
// Badges // Badges
if(this.controller.autoPlayEnabled && !this.controller.multiplayer){ if(this.controller.autoPlayEnabled && !this.controller.multiplayer){
...@@ -947,6 +965,20 @@ ...@@ -947,6 +965,20 @@
ctx.clip() ctx.clip()
this.drawCircles(this.controller.getCircles()) this.drawCircles(this.controller.getCircles())
if(this.controller.game.calibrationState === "video"){
if(ms % this.beatInterval < 1000 / 60 * 5){
this.drawCircle({
ms: ms,
type: "don",
endTime: ms + 100,
speed: 0
}, {
x: this.slotPos.x,
y: this.slotPos.y
})
}
}
ctx.restore() ctx.restore()
// Hit notes explosion // Hit notes explosion
...@@ -1001,6 +1033,22 @@ ...@@ -1001,6 +1033,22 @@
ctx.translate(frameLeft, frameTop) ctx.translate(frameLeft, frameTop)
} }
var state = this.controller.game.calibrationState
if(state && state in strings.calibration){
var boldTitle = strings.calibration[state].title
}
if(boldTitle){
this.draw.layeredText({
ctx: ctx,
text: boldTitle,
fontSize: 35,
fontFamily: this.font,
x: 300,
y: 70
}, [
{outline: "#fff", letterBorder: 22}
])
}
var pauseRect = (ctx, mul) => { var pauseRect = (ctx, mul) => {
this.draw.roundedRect({ this.draw.roundedRect({
ctx: ctx, ctx: ctx,
...@@ -1025,7 +1073,186 @@ ...@@ -1025,7 +1073,186 @@
dx: 68, dx: 68,
dy: 11 dy: 11
}) })
if(boldTitle){
this.draw.layeredText({
ctx: ctx,
text: boldTitle,
fontSize: 35,
fontFamily: this.font,
x: 300,
y: 70
}, [
{outline: "#000", letterBorder: 10},
{fill: "#fff"}
])
}
switch(state){
case "audioHelp":
case "videoHelp":
case "results":
var content = state === "audioHelp" && this.touchEnabled ? "contentAlt" : "content"
if(state === "audioHelp"){
var kbdSettings = settings.getItem("keyboardSettings")
var keys = [
kbdSettings.don_l[0].toUpperCase(),
kbdSettings.don_r[0].toUpperCase()
]
var substitute = (config, index, width) => {
var ctx = config.ctx
var bold = this.draw.bold(config.fontFamily)
ctx.font = bold + (config.fontSize * 0.66) + "px " + config.fontFamily
var w = config.fontSize * 0.6 + ctx.measureText(keys[index]).width
if(width){
return w
}else{
var h = 30
ctx.lineWidth = 3
ctx.strokeStyle = "rgba(0, 0, 0, 0.2)"
this.draw.roundedRect({
ctx: ctx,
x: 0, y: 1, w: w, h: h,
radius: 3
})
ctx.stroke()
ctx.strokeStyle = "#ccc"
ctx.fillStyle = "#fff"
this.draw.roundedRect({
ctx: ctx,
x: 0, y: 0, w: w, h: h,
radius: 3
})
ctx.stroke()
ctx.fill()
ctx.fillStyle = "#f7f7f7"
ctx.fillRect(2, 2, w - 4, h - 4)
ctx.fillStyle = "#333"
ctx.textBaseline = "middle"
ctx.textAlign = "center"
ctx.fillText(keys[index], w / 2, h / 2)
}
}
}else if(state === "results"){
var progress = this.controller.game.calibrationProgress
var latency = [
progress.audio,
progress.video
]
var substitute = (config, index, width) => {
var ctx = config.ctx
var bold = this.draw.bold(config.fontFamily)
ctx.font = bold + (config.fontSize * 1.1) + "px " + config.fontFamily
var text = this.addMs(latency[index])
if(width){
return ctx.measureText(text).width
}else{
ctx.fillText(text, 0, 0)
}
}
}else{
var substitute = null
}
this.draw.wrappingText({
ctx: ctx,
text: strings.calibration[state][content],
fontSize: 30,
fontFamily: this.font,
x: 300,
y: 130,
width: 680,
height: 240,
lineHeight: 35,
fill: "#000",
verticalAlign: "middle",
substitute: substitute
})
var _x = 640
var _w = 464
var _h = 80
for(var i = 0; i < this.pauseOptions.length; i++){
var text = this.pauseOptions[i]
var _y = 470 - 90 * (this.pauseOptions.length - i - 1)
if(this.state.moveHover !== null){
var selected = i === this.state.moveHover
}else{
var selected = i === this.state.pausePos
}
if(selected){
ctx.fillStyle = "#ffb447"
this.draw.roundedRect({
ctx: ctx,
x: _x - _w / 2,
y: _y,
w: _w,
h: _h,
radius: 30
})
ctx.fill()
}
if(selected){
var layers = [
{outline: "#000", letterBorder: 10},
{fill: "#fff"}
]
}else{
var layers = [
{fill: "#000"}
]
}
this.draw.layeredText({
ctx: ctx,
text: text,
x: _x,
y: _y + 18,
width: _w,
height: _h - 54,
fontSize: 40,
fontFamily: this.font,
letterSpacing: -1,
align: "center"
}, layers)
var highlight = 0
if(this.state.moveHover === i){
highlight = 2
}else if(selected){
highlight = 1
}
if(highlight){
this.draw.highlight({
ctx: ctx,
x: _x - _w / 2 - 3.5,
y: _y - 3.5,
w: _w + 7,
h: _h + 7,
animate: highlight === 1,
animateMS: this.state.moveMS,
opacity: highlight === 2 ? 0.8 : 1,
radius: 30
})
}
}
break
case "audioComplete":
case "videoComplete":
this.draw.wrappingText({
ctx: ctx,
text: strings.calibration[state],
fontSize: 40,
fontFamily: this.font,
x: 300,
y: 130,
width: 680,
height: 420,
lineHeight: 47,
fill: "#000",
verticalAlign: "middle",
textAlign: "center",
})
break
default:
ctx.drawImage(assets.image["mimizu"], ctx.drawImage(assets.image["mimizu"],
313, 247, 136, 315 313, 247, 136, 315
) )
...@@ -1034,6 +1261,10 @@ ...@@ -1034,6 +1261,10 @@
var _w = 80 var _w = 80
var _h = 464 var _h = 464
for(var i = 0; i < this.pauseOptions.length; i++){ for(var i = 0; i < this.pauseOptions.length; i++){
var text = this.pauseOptions[i]
if(this.controller.calibrationMode && i === this.pauseOptions.length - 1){
text = strings.calibration.back
}
var _x = 520 + 110 * i var _x = 520 + 110 * i
if(this.state.moveHover !== null){ if(this.state.moveHover !== null){
var selected = i === this.state.moveHover var selected = i === this.state.moveHover
...@@ -1058,11 +1289,11 @@ ...@@ -1058,11 +1289,11 @@
y: _y, y: _y,
w: _w, w: _w,
h: _h, h: _h,
id: this.pauseOptions[i] + (selected ? "1" : "0") id: text + (selected ? "1" : "0")
}, ctx => { }, ctx => {
var textConfig = { var textConfig = {
ctx: ctx, ctx: ctx,
text: this.pauseOptions[i], text: text,
x: _w / 2, x: _w / 2,
y: 18, y: 18,
width: _w, width: _w,
...@@ -1101,10 +1332,25 @@ ...@@ -1101,10 +1332,25 @@
}) })
} }
} }
break
}
ctx.restore() ctx.restore()
} }
} }
addMs(input){
var split = strings.calibration.ms.split("%s")
var index = 0
var output = ""
var inputStrings = [(input > 0 ? "+" : "") + input.toString()]
split.forEach((string, i) => {
if(i !== 0){
output += inputStrings[index++]
}
output += string
})
return output
}
setBackground(){ setBackground(){
var selectedSong = this.controller.selectedSong var selectedSong = this.controller.selectedSong
var songSkinName = selectedSong.songSkin.name var songSkinName = selectedSong.songSkin.name
...@@ -1219,10 +1465,10 @@ ...@@ -1219,10 +1465,10 @@
measures.forEach(measure => { measures.forEach(measure => {
var timeForDistance = this.posToMs(distanceForCircle, measure.speed) var timeForDistance = this.posToMs(distanceForCircle, measure.speed)
var startingTime = measure.ms - timeForDistance var startingTime = measure.ms - timeForDistance + this.controller.videoLatency
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) + this.controller.videoLatency
if(measure.visible && (!measure.branch || measure.branch.active) && 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 + this.controller.videoLatency, measure.speed)
this.ctx.strokeStyle = measure.branchFirst ? "#ff0" : "#bdbdbd" this.ctx.strokeStyle = measure.branchFirst ? "#ff0" : "#bdbdbd"
this.ctx.lineWidth = 3 this.ctx.lineWidth = 3
this.ctx.beginPath() this.ctx.beginPath()
...@@ -1267,8 +1513,8 @@ ...@@ -1267,8 +1513,8 @@
var speed = circle.speed 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.ms - timeForDistance var startingTime = circle.ms - timeForDistance + this.controller.videoLatency
var finishTime = circle.endTime + 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) + this.controller.videoLatency
if(circle.isPlayed <= 0 || circle.score === 0){ if(circle.isPlayed <= 0 || circle.score === 0){
if((!circle.branch || circle.branch.active) && ms >= startingTime && ms <= finishTime && circle.isPlayed !== -1){ if((!circle.branch || circle.branch.active) && ms >= startingTime && ms <= finishTime && circle.isPlayed !== -1){
...@@ -1350,7 +1596,7 @@ ...@@ -1350,7 +1596,7 @@
if(!circlePos){ if(!circlePos){
circlePos = { circlePos = {
x: this.slotPos.x + this.msToPos(circleMs - ms, speed), x: this.slotPos.x + this.msToPos(circleMs - ms + this.controller.videoLatency, speed),
y: this.slotPos.y y: this.slotPos.y
} }
} }
...@@ -1388,10 +1634,10 @@ ...@@ -1388,10 +1634,10 @@
size = circleSize size = circleSize
faceID = noteFace.small faceID = noteFace.small
var h = size * 1.8 var h = size * 1.8
if(circleMs < ms && ms <= endTime){ if(circleMs + this.controller.audioLatency < ms && ms <= endTime + this.controller.audioLatency){
circlePos.x = this.slotPos.x circlePos.x = this.slotPos.x
}else if(ms > endTime){ }else if(ms > endTime + this.controller.audioLatency){
circlePos.x = this.slotPos.x + this.msToPos(endTime - ms, speed) circlePos.x = this.slotPos.x + this.msToPos(endTime - ms + this.controller.audioLatency, speed)
} }
ctx.drawImage(assets.image["balloon"], ctx.drawImage(assets.image["balloon"],
circlePos.x + size - 4, circlePos.x + size - 4,
...@@ -1596,7 +1842,9 @@ ...@@ -1596,7 +1842,9 @@
} }
toggleGogoTime(circle){ toggleGogoTime(circle){
this.gogoTime = circle.gogoTime this.gogoTime = circle.gogoTime
if(circle.gogoTime || this.gogoTimeStarted !== -Infinity){
this.gogoTimeStarted = circle.ms this.gogoTimeStarted = circle.ms
}
if(this.gogoTime){ if(this.gogoTime){
this.assets.fireworks.forEach(fireworksAsset => { this.assets.fireworks.forEach(fireworksAsset => {
...@@ -1789,21 +2037,62 @@ ...@@ -1789,21 +2037,62 @@
if(typeof pos === "undefined"){ if(typeof pos === "undefined"){
pos = this.state.pausePos pos = this.state.pausePos
} }
var game = this.controller.game
var state = game.calibrationState
switch(state){
case "audioHelp":
pos = pos === 0 ? 2 : 0
break
case "videoHelp":
if(pos === 0){
assets.sounds["se_don"].play()
game.calibrationReset("audio")
return
}else{
pos = 0
}
break
case "results":
if(pos === 0){
assets.sounds["se_don"].play()
game.calibrationReset("video")
return
}else{
var input = settings.getItem("latency")
var output = {}
var progress = game.calibrationProgress
for(var i in input){
if(i === "audio" || i === "video"){
output[i] = progress[i]
}else{
output[i] = input[i]
}
}
settings.setItem("latency", output)
pos = 2
}
break
}
switch(pos){ switch(pos){
case 1: case 1:
assets.sounds["se_don"].play() this.controller.playSound("se_don", 0, true)
if(state === "video"){
game.calibrationReset(state)
}else{
this.controller.restartSong() this.controller.restartSong()
}
pageEvents.send("pause-restart") pageEvents.send("pause-restart")
break break
case 2: case 2:
assets.sounds["se_don"].play() this.controller.playSound("se_don", 0, true)
this.controller.songSelection() this.controller.songSelection()
pageEvents.send("pause-song-select") pageEvents.send("pause-song-select")
break break
default: default:
this.controller.togglePause() this.controller.togglePause(false)
break break
} }
return true
} }
onmousedown(event){ onmousedown(event){
if(this.controller.game.paused){ if(this.controller.game.paused){
...@@ -1855,23 +2144,30 @@ ...@@ -1855,23 +2144,30 @@
x = x * pauseScale + 257 x = x * pauseScale + 257
y = y * pauseScale - 328 y = y * pauseScale - 328
} }
switch(this.controller.game.calibrationState){
case "audioHelp":
case "videoHelp":
case "results":
if(554 - 90 * this.pauseOptions.length <= y && y <= 554 && 404 <= x && x <= 876){
return Math.floor((y - 554 + 90 * this.pauseOptions.length) / 90)
}
break
default:
if(104 <= y && y <= 575 && 465 <= x && x <= 465 + 110 * this.pauseOptions.length){ if(104 <= y && y <= 575 && 465 <= x && x <= 465 + 110 * this.pauseOptions.length){
return Math.floor((x - 465) / 110) return Math.floor((x - 465) / 110)
} }
break
}
return null return null
} }
mouseIdle(){ mouseIdle(){
var lastMouse = pageEvents.getMouse() var lastMouse = pageEvents.getMouse()
if(lastMouse && !this.cursorHidden){ if(lastMouse && !this.cursorHidden && !this.state.hasPointer){
if(this.getMS() >= this.lastMousemove + 2000){ if(this.getMS() >= this.lastMousemove + 2000){
this.cursor.style.top = lastMouse.clientY + "px" this.canvas.style.cursor = "none"
this.cursor.style.left = lastMouse.clientX + "px"
this.cursor.style.pointerEvents = "auto"
this.cursorHidden = true this.cursorHidden = true
}else{ }else{
this.cursor.style.top = "" this.canvas.style.cursor = ""
this.cursor.style.left = ""
this.cursor.style.pointerEvents = ""
} }
} }
} }
...@@ -1890,12 +2186,13 @@ ...@@ -1890,12 +2186,13 @@
this.pauseCache.clean() this.pauseCache.clean()
this.branchCache.clean() this.branchCache.clean()
versionDiv.classList.remove("version-hide")
loader.screen.parentNode.appendChild(versionDiv)
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
if(this.touchEnabled){ if(this.touchEnabled){
pageEvents.remove(this.canvas, "touchstart") pageEvents.remove(this.canvas, "touchstart")
pageEvents.remove(this.touchPauseBtn, "touchend") pageEvents.remove(this.touchPauseBtn, "touchend")
this.gameDiv.classList.add("touch-results") this.gameDiv.classList.add("touch-results")
document.getElementById("version").classList.remove("version-hide")
this.touchDrumDiv.parentNode.removeChild(this.touchDrumDiv) this.touchDrumDiv.parentNode.removeChild(this.touchDrumDiv)
delete this.touchDrumDiv delete this.touchDrumDiv
delete this.touchDrumImg delete this.touchDrumImg
...@@ -1915,7 +2212,6 @@ ...@@ -1915,7 +2212,6 @@
pageEvents.mouseRemove(this) pageEvents.mouseRemove(this)
delete this.pauseMenu delete this.pauseMenu
delete this.cursor
delete this.gameDiv delete this.gameDiv
delete this.canvas delete this.canvas
delete this.ctx delete this.ctx
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
<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>
<div id="link-email" class="taibtn stroke-sub link-btn" alt="taiko@bui.pm"> <div id="link-email" class="taibtn stroke-sub link-btn">
<a href="mailto:taiko@bui.pm">taiko@bui.pm</a> <a></a>
</div> </div>
</div> </div>
<div class="view-end-button taibtn stroke-sub selected"></div> <div class="view-end-button taibtn stroke-sub selected"></div>
......
...@@ -11,5 +11,4 @@ ...@@ -11,5 +11,4 @@
<div id="touch-buttons"> <div id="touch-buttons">
<div id="touch-full-btn"></div><div id="touch-pause-btn"></div> <div id="touch-full-btn"></div><div id="touch-pause-btn"></div>
</div> </div>
<div id="cursor"></div>
</div> </div>
...@@ -20,5 +20,15 @@ ...@@ -20,5 +20,15 @@
<div class="view-end-button taibtn stroke-sub selected"></div> <div class="view-end-button taibtn stroke-sub selected"></div>
</div> </div>
</div> </div>
<div class="view-outer shadow-outer" id="settings-latency">
<div class="view">
<div class="view-title stroke-sub"></div>
<div class="view-content"></div>
<div class="left-buttons">
<div id="latency-default" class="taibtn stroke-sub"></div>
</div>
<div class="view-end-button taibtn stroke-sub"></div>
</div>
</div>
</div> </div>
</div> </div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment