diff --git a/public/assets/audio/se_calibration.wav b/public/assets/audio/se_calibration.wav
new file mode 100644
index 0000000..f1459e1
Binary files /dev/null and b/public/assets/audio/se_calibration.wav differ
diff --git a/public/assets/audio/v_combo_1000_meka.wav b/public/assets/audio/v_combo_1000_meka.wav
deleted file mode 100644
index ae8127d..0000000
Binary files a/public/assets/audio/v_combo_1000_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_100_meka.wav b/public/assets/audio/v_combo_100_meka.wav
deleted file mode 100644
index 2636bef..0000000
Binary files a/public/assets/audio/v_combo_100_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_1100_meka.wav b/public/assets/audio/v_combo_1100_meka.wav
deleted file mode 100644
index 616058c..0000000
Binary files a/public/assets/audio/v_combo_1100_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_1200_meka.wav b/public/assets/audio/v_combo_1200_meka.wav
deleted file mode 100644
index ac68af2..0000000
Binary files a/public/assets/audio/v_combo_1200_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_1300_meka.wav b/public/assets/audio/v_combo_1300_meka.wav
deleted file mode 100644
index 1d2d4b4..0000000
Binary files a/public/assets/audio/v_combo_1300_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_1400_meka.wav b/public/assets/audio/v_combo_1400_meka.wav
deleted file mode 100644
index 4ea2511..0000000
Binary files a/public/assets/audio/v_combo_1400_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_over1500.wav b/public/assets/audio/v_combo_1500.wav
similarity index 100%
rename from public/assets/audio/v_combo_over1500.wav
rename to public/assets/audio/v_combo_1500.wav
diff --git a/public/assets/audio/v_combo_over1500_meka.wav b/public/assets/audio/v_combo_1600.wav
similarity index 100%
rename from public/assets/audio/v_combo_over1500_meka.wav
rename to public/assets/audio/v_combo_1600.wav
diff --git a/public/assets/audio/v_combo_1700.wav b/public/assets/audio/v_combo_1700.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_1700.wav differ
diff --git a/public/assets/audio/v_combo_1800.wav b/public/assets/audio/v_combo_1800.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_1800.wav differ
diff --git a/public/assets/audio/v_combo_1900.wav b/public/assets/audio/v_combo_1900.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_1900.wav differ
diff --git a/public/assets/audio/v_combo_2000.wav b/public/assets/audio/v_combo_2000.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2000.wav differ
diff --git a/public/assets/audio/v_combo_200_meka.wav b/public/assets/audio/v_combo_200_meka.wav
deleted file mode 100644
index 99a29ef..0000000
Binary files a/public/assets/audio/v_combo_200_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_2100.wav b/public/assets/audio/v_combo_2100.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2100.wav differ
diff --git a/public/assets/audio/v_combo_2200.wav b/public/assets/audio/v_combo_2200.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2200.wav differ
diff --git a/public/assets/audio/v_combo_2300.wav b/public/assets/audio/v_combo_2300.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2300.wav differ
diff --git a/public/assets/audio/v_combo_2400.wav b/public/assets/audio/v_combo_2400.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2400.wav differ
diff --git a/public/assets/audio/v_combo_2500.wav b/public/assets/audio/v_combo_2500.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2500.wav differ
diff --git a/public/assets/audio/v_combo_2600.wav b/public/assets/audio/v_combo_2600.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2600.wav differ
diff --git a/public/assets/audio/v_combo_2700.wav b/public/assets/audio/v_combo_2700.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2700.wav differ
diff --git a/public/assets/audio/v_combo_2800.wav b/public/assets/audio/v_combo_2800.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2800.wav differ
diff --git a/public/assets/audio/v_combo_2900.wav b/public/assets/audio/v_combo_2900.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_2900.wav differ
diff --git a/public/assets/audio/v_combo_3000.wav b/public/assets/audio/v_combo_3000.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3000.wav differ
diff --git a/public/assets/audio/v_combo_300_meka.wav b/public/assets/audio/v_combo_300_meka.wav
deleted file mode 100644
index c5809c7..0000000
Binary files a/public/assets/audio/v_combo_300_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_3100.wav b/public/assets/audio/v_combo_3100.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3100.wav differ
diff --git a/public/assets/audio/v_combo_3200.wav b/public/assets/audio/v_combo_3200.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3200.wav differ
diff --git a/public/assets/audio/v_combo_3300.wav b/public/assets/audio/v_combo_3300.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3300.wav differ
diff --git a/public/assets/audio/v_combo_3400.wav b/public/assets/audio/v_combo_3400.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3400.wav differ
diff --git a/public/assets/audio/v_combo_3500.wav b/public/assets/audio/v_combo_3500.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3500.wav differ
diff --git a/public/assets/audio/v_combo_3600.wav b/public/assets/audio/v_combo_3600.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3600.wav differ
diff --git a/public/assets/audio/v_combo_3700.wav b/public/assets/audio/v_combo_3700.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3700.wav differ
diff --git a/public/assets/audio/v_combo_3800.wav b/public/assets/audio/v_combo_3800.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3800.wav differ
diff --git a/public/assets/audio/v_combo_3900.wav b/public/assets/audio/v_combo_3900.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_3900.wav differ
diff --git a/public/assets/audio/v_combo_4000.wav b/public/assets/audio/v_combo_4000.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4000.wav differ
diff --git a/public/assets/audio/v_combo_400_meka.wav b/public/assets/audio/v_combo_400_meka.wav
deleted file mode 100644
index d76bf6a..0000000
Binary files a/public/assets/audio/v_combo_400_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_4100.wav b/public/assets/audio/v_combo_4100.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4100.wav differ
diff --git a/public/assets/audio/v_combo_4200.wav b/public/assets/audio/v_combo_4200.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4200.wav differ
diff --git a/public/assets/audio/v_combo_4300.wav b/public/assets/audio/v_combo_4300.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4300.wav differ
diff --git a/public/assets/audio/v_combo_4400.wav b/public/assets/audio/v_combo_4400.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4400.wav differ
diff --git a/public/assets/audio/v_combo_4500.wav b/public/assets/audio/v_combo_4500.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4500.wav differ
diff --git a/public/assets/audio/v_combo_4600.wav b/public/assets/audio/v_combo_4600.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4600.wav differ
diff --git a/public/assets/audio/v_combo_4700.wav b/public/assets/audio/v_combo_4700.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4700.wav differ
diff --git a/public/assets/audio/v_combo_4800.wav b/public/assets/audio/v_combo_4800.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4800.wav differ
diff --git a/public/assets/audio/v_combo_4900.wav b/public/assets/audio/v_combo_4900.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_4900.wav differ
diff --git a/public/assets/audio/v_combo_5000.wav b/public/assets/audio/v_combo_5000.wav
new file mode 100644
index 0000000..a5034f1
Binary files /dev/null and b/public/assets/audio/v_combo_5000.wav differ
diff --git a/public/assets/audio/v_combo_500_meka.wav b/public/assets/audio/v_combo_500_meka.wav
deleted file mode 100644
index 17bd25e..0000000
Binary files a/public/assets/audio/v_combo_500_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_50_meka.wav b/public/assets/audio/v_combo_50_meka.wav
deleted file mode 100644
index 1fb3c2b..0000000
Binary files a/public/assets/audio/v_combo_50_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_600_meka.wav b/public/assets/audio/v_combo_600_meka.wav
deleted file mode 100644
index f57f443..0000000
Binary files a/public/assets/audio/v_combo_600_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_700_meka.wav b/public/assets/audio/v_combo_700_meka.wav
deleted file mode 100644
index b23db58..0000000
Binary files a/public/assets/audio/v_combo_700_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_800_meka.wav b/public/assets/audio/v_combo_800_meka.wav
deleted file mode 100644
index 84fad8a..0000000
Binary files a/public/assets/audio/v_combo_800_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_combo_900_meka.wav b/public/assets/audio/v_combo_900_meka.wav
deleted file mode 100644
index 86ca70b..0000000
Binary files a/public/assets/audio/v_combo_900_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_fullcombo_meka.wav b/public/assets/audio/v_fullcombo_meka.wav
deleted file mode 100644
index 0c683b6..0000000
Binary files a/public/assets/audio/v_fullcombo_meka.wav and /dev/null differ
diff --git a/public/assets/audio/v_renda_meka.wav b/public/assets/audio/v_renda_meka.wav
deleted file mode 100644
index 9cfb6f0..0000000
Binary files a/public/assets/audio/v_renda_meka.wav and /dev/null differ
diff --git a/public/src/css/game.css b/public/src/css/game.css
index f8b7214..69d1127 100644
--- a/public/src/css/game.css
+++ b/public/src/css/game.css
@@ -16,14 +16,6 @@
width: 100%;
height: 100%;
}
-#cursor{
- position: fixed;
- width: 1px;
- height: 1px;
- cursor: none;
- pointer-events: none;
- z-index: 1;
-}
#touch-drum{
display: none;
position: absolute;
diff --git a/public/src/css/view.css b/public/src/css/view.css
index 0340d97..76ed3f4 100644
--- a/public/src/css/view.css
+++ b/public/src/css/view.css
@@ -158,8 +158,7 @@ kbd{
.setting-box:first-child{
margin-top: 0;
}
-.settings-outer .view-content:not(:hover) .setting-box.selected,
-.view-outer:not(.settings-outer) .setting-box.selected,
+.view-content:not(:hover) .setting-box.selected,
.setting-box:hover{
background: #ffb547;
animation: 2s linear border-pulse infinite;
@@ -177,7 +176,6 @@ kbd{
overflow: hidden;
}
.view-content:not(:hover) .setting-box.selected .setting-name,
-.view-outer:not(.settings-outer) .setting-box.selected .setting-name,
.setting-box:hover .setting-name,
.setting-box:hover #gamepad-value{
color: #fff;
@@ -193,6 +191,8 @@ kbd{
border-radius: 0.2em;
padding: 0.5em;
box-sizing: border-box;
+ overflow: hidden;
+ white-space: nowrap;
}
.setting-value.selected{
width: calc(50% + 0.2em);
@@ -215,27 +215,26 @@ kbd{
background: rgba(0, 0, 0, 0.5);
z-index: 1;
}
-#settings-gamepad{
+#settings-gamepad,
+#settings-latency{
display: none;
}
#settings-gamepad .view{
- position: absolute;
- margin: auto;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- width: 574px;
- height: 428px;
- max-height: calc(100vh - 14em + 88px);
+ width: 29.9em;
+ max-width: 100vw;
}
#settings-gamepad .setting-box{
height: auto;
+ overflow: hidden;
+}
+#gamepad-bg,
+#gamepad-buttons{
+ background-size: 20.53em;
}
#gamepad-bg{
position: relative;
- width: 550px;
- height: 317px;
+ width: 20.53em;
+ height: 11.83em;
max-height: none;
background-repeat: no-repeat;
text-align: center;
@@ -244,11 +243,11 @@ kbd{
}
#gamepad-buttons{
position: absolute;
- left: 141px;
- top: 120px;
- width: 282px;
- height: 131px;
- background-position: 0 -318px;
+ left: 5.26em;
+ top: 4.48em;
+ width: 10.52em;
+ height: 4.89em;
+ background-position: 0 -11.87em;
background-repeat: no-repeat;
pointer-events: none;
}
@@ -259,3 +258,36 @@ kbd{
#gamepad-value::before{
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;
+}
diff --git a/public/src/js/about.js b/public/src/js/about.js
index ad571ba..afa4a23 100644
--- a/public/src/js/about.js
+++ b/public/src/js/about.js
@@ -29,23 +29,30 @@
this.endButton.innerText = strings.tutorial.ok
this.endButton.setAttribute("alt", strings.tutorial.ok)
+ this.items = []
+
var versionUrl = gameConfig._version.url
this.getLink(this.linkIssues).href = versionUrl + "issues"
-
+ this.items.push(this.linkIssues)
+
var contactEmail = gameConfig.email
- if (typeof contactEmail === 'string') {
+ this.hasEmail = typeof contactEmail === "string"
+ if(this.hasEmail){
this.linkEmail.setAttribute("alt", contactEmail)
this.getLink(this.linkEmail).href = "mailto:" + contactEmail
- this.getLink(this.linkEmail).text = contactEmail
- } else {
- this.linkEmail.style.display = "none"
+ this.getLink(this.linkEmail).innerText = contactEmail
+ this.items.push(this.linkEmail)
+ }else{
+ this.linkEmail.parentNode.removeChild(this.linkEmail)
}
-
+
pageEvents.add(this.linkIssues, ["click", "touchend"], this.linkButton.bind(this))
- pageEvents.add(this.linkEmail, ["click", "touchend"], this.linkButton.bind(this))
+ if(this.hasEmail){
+ pageEvents.add(this.linkEmail, ["click", "touchend"], this.linkButton.bind(this))
+ }
pageEvents.add(this.endButton, ["mousedown", "touchstart"], this.onEnd.bind(this))
- this.items = [this.linkIssues, this.linkEmail, this.endButton]
- this.selected = 2
+ this.items.push(this.endButton)
+ this.selected = this.items.length - 1
this.keyboard = new Keyboard({
confirm: ["enter", "space", "don_l", "don_r"],
@@ -146,6 +153,8 @@
}
}
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 = {}
if(localStorage["lastError"]){
try{
@@ -195,7 +204,9 @@
}
var issueBody = strings.about.issueTemplate + "\n\n\n\n" + diag
- this.getLink(this.linkEmail).href += "?body=" + encodeURIComponent(issueBody.replace(/\n/g, "
\r\n"))
+ if(this.hasEmail){
+ this.getLink(this.linkEmail).href += "?body=" + encodeURIComponent(issueBody.replace(/\n/g, "
\r\n"))
+ }
return diag
}
@@ -214,7 +225,9 @@
this.keyboard.clean()
this.gamepad.clean()
pageEvents.remove(this.linkIssues, ["click", "touchend"])
- pageEvents.remove(this.linkEmail, ["click", "touchend"])
+ if(this.hasEmail){
+ pageEvents.remove(this.linkEmail, ["click", "touchend"])
+ }
pageEvents.remove(this.endButton, ["mousedown", "touchstart"])
if(this.textarea){
pageEvents.remove(this.textarea, ["focus", "blur"])
diff --git a/public/src/js/assets.js b/public/src/js/assets.js
index 71fe065..39a32b8 100644
--- a/public/src/js/assets.js
+++ b/public/src/js/assets.js
@@ -91,26 +91,8 @@ var assets = {
"se_ka.wav",
"se_pause.wav",
"se_jump.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",
+ "se_calibration.wav",
- "v_fullcombo_meka.wav",
- "v_renda_meka.wav",
"v_results.wav",
"v_sanka.wav",
"v_songsel.wav",
@@ -128,23 +110,6 @@ var assets = {
"se_results_countup.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_renda.wav",
"v_results_fullcombo.wav",
diff --git a/public/src/js/canvascache.js b/public/src/js/canvascache.js
index a731bcb..c3c6913 100644
--- a/public/src/js/canvascache.js
+++ b/public/src/js/canvascache.js
@@ -1,5 +1,6 @@
class CanvasCache{
- constructor(w, h, scale){
+ constructor(noSmoothing, w, h, scale){
+ this.noSmoothing = noSmoothing
if(w){
this.resize(w, h, scale)
}
@@ -11,6 +12,9 @@ class CanvasCache{
this.map = new Map()
this.canvas = document.createElement("canvas")
this.ctx = this.canvas.getContext("2d")
+ if(this.noSmoothing){
+ this.ctx.imageSmoothingEnabled = false
+ }
}
this.scale = scale
this.x = 0
diff --git a/public/src/js/canvasdraw.js b/public/src/js/canvasdraw.js
index 0e77fbb..baea01c 100644
--- a/public/src/js/canvasdraw.js
+++ b/public/src/js/canvasdraw.js
@@ -1,5 +1,5 @@
class CanvasDraw{
- constructor(){
+ constructor(noSmoothing){
this.diffStarPath = new Path2D(vectors.diffStar)
this.longVowelMark = new Path2D(vectors.longVowelMark)
@@ -68,7 +68,8 @@
emCap: /[MWMW]/,
rWidth: /[abdfIjo-rtvabdfIjo-rtv]/,
lWidth: /[ilil]/,
- ura: /\s*[\((]裏[\))]$/
+ ura: /\s*[\((]裏[\))]$/,
+ cjk: /[\u3040-ゞ゠-ヾ一-\u9ffe]/
}
var numbersFull = "0123456789"
@@ -78,10 +79,12 @@
this.numbersFullToHalf[numbersFull[i]] = numbersHalf[i]
this.numbersFullToHalf[numbersHalf[i]] = numbersHalf[i]
}
+ this.wrapOn = [" ", "\n", "%s"]
+ this.stickySymbols = "!,.:;?~‐–‼、。々〜ぁぃぅぇぉっゃゅょァィゥェォッャュョ・ーヽヾ!:;?"
- this.songFrameCache = new CanvasCache()
- this.diffStarCache = new CanvasCache()
- this.crownCache = new CanvasCache()
+ this.songFrameCache = new CanvasCache(noSmoothing)
+ this.diffStarCache = new CanvasCache(noSmoothing)
+ this.crownCache = new CanvasCache(noSmoothing)
this.tmpCanvas = document.createElement("canvas")
this.tmpCtx = this.tmpCanvas.getContext("2d")
@@ -818,6 +821,163 @@
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){
var ctx = config.ctx
var scale = config.scale
diff --git a/public/src/js/controller.js b/public/src/js/controller.js
index 6020d2a..cb20acd 100644
--- a/public/src/js/controller.js
+++ b/public/src/js/controller.js
@@ -7,6 +7,16 @@ class Controller{
this.touchEnabled = touchEnabled
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){
loader.changePage("game", false)
}
@@ -17,19 +27,41 @@ class Controller{
this.parsedSongData = new ParseOsu(songData, selectedSong.difficulty, selectedSong.stars, selectedSong.offset)
}
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)
+ }
- assets.songs.forEach(song => {
- if(song.id == this.selectedSong.folder){
- this.mainAsset = song.sound
- this.volume = song.volume || 1
- }
- })
+ if(this.calibrationMode){
+ this.volume = 1
+ }else{
+ assets.songs.forEach(song => {
+ if(song.id == this.selectedSong.folder){
+ this.mainAsset = song.sound
+ this.volume = song.volume || 1
+ }
+ })
+ }
this.game = new Game(this, this.selectedSong, this.parsedSongData)
this.view = new View(this)
this.mekadon = new Mekadon(this, this.game)
this.keyboard = new GameInput(this)
+ this.drumSounds = settings.getItem("latency").drumSounds
this.playedSounds = {}
}
run(syncWith){
@@ -72,8 +104,8 @@ class Controller{
}
stopMainLoop(){
this.mainLoopRunning = false
- if(this.mainAsset){
- this.mainAsset.stop()
+ if(this.game.mainAsset){
+ this.game.mainAsset.stop()
}
if(this.multiplayer !== 2){
clearInterval(this.gameInterval)
@@ -90,13 +122,18 @@ class Controller{
if(this.game.musicFadeOut < 3){
this.keyboard.checkMenuKeys()
}
+ if(this.calibrationMode){
+ this.game.calibration()
+ }
if(!this.game.isPaused()){
this.keyboard.checkGameKeys()
if(ms < 0){
this.game.updateTime()
}else{
- this.game.update()
+ if(!this.calibrationMode){
+ this.game.update()
+ }
if(!this.mainLoopRunning){
return
}
@@ -137,7 +174,7 @@ class Controller{
if(Math.round(score.gauge / 2) - 1 >= 25){
if(score.bad === 0){
vp = "fullcombo"
- this.playSoundMeka("v_fullcombo", 1.350)
+ this.playSound("v_fullcombo", 1.350)
}else{
vp = "clear"
}
@@ -158,7 +195,11 @@ class Controller{
if(!fadeIn){
this.clean()
}
- new SongSelect(false, fadeIn, this.touchEnabled)
+ if(this.calibrationMode){
+ new SettingsView(this.touchEnabled, false, null, "latency")
+ }else{
+ new SongSelect(false, fadeIn, this.touchEnabled)
+ }
}
restartSong(){
this.clean()
@@ -166,20 +207,24 @@ class Controller{
new LoadSong(this.selectedSong, false, true, this.touchEnabled)
}else{
new Promise(resolve => {
- var songObj = assets.songs.find(song => song.id === this.selectedSong.folder)
- if(songObj.chart){
- var reader = new FileReader()
- var promise = pageEvents.load(reader).then(event => {
- this.songData = event.target.result.replace(/\0/g, "").split("\n")
- resolve()
- })
- if(this.selectedSong.type === "tja"){
- reader.readAsText(songObj.chart, "sjis")
- }else{
- reader.readAsText(songObj.chart)
- }
- }else{
+ if(this.calibrationMode){
resolve()
+ }else{
+ var songObj = assets.songs.find(song => song.id === this.selectedSong.folder)
+ if(songObj.chart && songObj.chart !== "blank"){
+ var reader = new FileReader()
+ var promise = pageEvents.load(reader).then(event => {
+ this.songData = event.target.result.replace(/\0/g, "").split("\n")
+ resolve()
+ })
+ if(this.selectedSong.type === "tja"){
+ reader.readAsText(songObj.chart, "sjis")
+ }else{
+ reader.readAsText(songObj.chart)
+ }
+ }else{
+ resolve()
+ }
}
}).then(() => {
var taikoGame = new Controller(this.selectedSong, this.songData, this.autoPlayEnabled, false, this.touchEnabled)
@@ -187,25 +232,21 @@ class Controller{
})
}
}
- 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
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
}
}
- playSoundMeka(soundID, time){
- var meka = ""
- if(this.autoPlayEnabled && !this.multiplayer){
- meka = "_meka"
- }
- this.playSound(soundID + meka, time)
- }
- togglePause(){
+ togglePause(forcePause, pauseMove, noSound){
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(){
return this.keyboard.getKeys()
diff --git a/public/src/js/game.js b/public/src/js/game.js
index dd05956..bb790ba 100644
--- a/public/src/js/game.js
+++ b/public/src/js/game.js
@@ -45,7 +45,12 @@ class Game{
}
initTiming(){
// 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){
var syncWith = this.controller.syncWith
var syncCircles = syncWith.game.songData.circles
@@ -57,8 +62,8 @@ class Game{
this.startDate = Date.now() + offsetTime
}
update(){
- // Main operations
this.updateTime()
+ // Main operations
this.updateCirclesStatus()
this.checkPlays()
// Event operations
@@ -82,14 +87,14 @@ class Game{
if(circle && (!circle.branch || circle.branch.active) && !circle.isPlayed){
var type = circle.type
var drumrollNotes = type === "balloon" || type === "drumroll" || type === "daiDrumroll"
- var endTime = circle.endTime + (drumrollNotes ? 0 : this.rules.bad)
+ var endTime = circle.endTime + (drumrollNotes ? 0 : this.rules.bad) + this.controller.audioLatency
- if(ms >= circle.ms){
- if(drumrollNotes && !circle.rendaPlayed && ms < endTime){
+ if(ms >= circle.ms + this.controller.audioLatency){
+ if(drumrollNotes && !circle.rendaPlayed && ms < endTime + this.controller.audioLatency){
circle.rendaPlayed = true
if(this.rules.difficulty === "easy"){
assets.sounds["v_renda" + this.controller.snd].stop()
- this.controller.playSoundMeka("v_renda")
+ this.controller.playSound("v_renda")
}
}
if(!circle.beatMSCopied){
@@ -109,7 +114,7 @@ class Game{
this.updateCurrentCircle()
if(this.controller.multiplayer === 1){
var value = {
- pace: (ms - circle.ms) / circle.timesHit
+ pace: (ms - circle.ms - this.controller.audioLatency) / circle.timesHit
}
if(type === "drumroll" || type === "daiDrumroll"){
value.kaAmount = circle.timesKa / circle.timesHit
@@ -211,7 +216,7 @@ class Game{
for(var i = this.currentCircle + 1; i < circles.length; 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((!circleIsNote(circle) || relative < -this.rules.bad)){
break
@@ -310,7 +315,7 @@ class Game{
var keyTime = this.controller.getKeyTime()
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){
var fixedNote = this.fixNoteStream(keysDon)
@@ -366,7 +371,7 @@ class Game{
if(this.controller.multiplayer === 1){
var value = {
score: score,
- ms: circle.ms - currentTime,
+ ms: circle.ms - currentTime - this.controller.audioLatency,
dai: typeDai ? (keyDai ? 2 : 1) : 0
}
if((!keysDon || !typeDon) && (!keysKa || !typeKa)){
@@ -375,7 +380,7 @@ class Game{
p2.send("note", value)
}
}else{
- if(circle.ms > currentTime || currentTime > circle.endTime){
+ if(circle.ms + this.controller.audioLatency > currentTime || currentTime > circle.endTime + this.controller.audioLatency){
return true
}
if(keysDon && type === "balloon"){
@@ -400,7 +405,7 @@ class Game{
circle.played(score)
if(this.controller.multiplayer == 1){
p2.send("drumroll", {
- pace: (this.elapsedTime - circle.ms) / circle.timesHit
+ pace: (this.elapsedTime - circle.ms + this.controller.audioLatency) / circle.timesHit
})
}
}else{
@@ -447,17 +452,19 @@ class Game{
var ms = this.elapsedTime
if(!this.lastCircle){
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){
var syncWith = this.controller.syncWith
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){
this.lastCircle = syncLastCircle
}
}
}
- if(!this.fadeOutStarted && ms >= this.lastCircle + 2000){
+ if(!this.fadeOutStarted && ms >= this.lastCircle + 2000 + this.controller.audioLatency){
this.fadeOutStarted = ms
if(this.controller.multiplayer){
this.controller.syncWith.game.fadeOutStarted = ms
@@ -495,28 +502,51 @@ class Game{
playMainMusic(){
var ms = this.elapsedTime + this.controller.offset
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.mainMusicPlaying = true
}
}
- togglePause(){
+ togglePause(forcePause, pauseMove, noSound){
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.latestDate = Date.now()
if(this.mainAsset){
this.mainAsset.stop()
}
this.mainMusicPlaying = false
- this.view.pauseMove(0, true)
+ this.view.pauseMove(pauseMove || 0, true)
this.view.gameDiv.classList.add("game-paused")
this.view.lastMousemove = this.view.getMS()
this.view.cursorHidden = false
pageEvents.send("pause")
- }else{
- assets.sounds["se_cancel"].play()
+ }else if(!forcePause){
+ 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
var currentDate = Date.now()
this.startDate += currentDate - this.latestDate
@@ -579,8 +609,8 @@ class Game{
if(this.combo > this.globalScore.maxCombo){
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){
- this.controller.playSoundMeka("v_combo_" + (this.combo <= 1400 ? this.combo : "over1500"))
+ if(this.combo === 50 || this.combo > 0 && this.combo % 100 === 0 && this.combo <= 5000){
+ this.controller.playSound("v_combo_" + this.combo)
}
if (this.songData.scoremode == 2 && this.combo > 0 && this.combo % 100 == 0) {
this.globalScore.points += 10000;
@@ -683,7 +713,7 @@ class Game{
if(!circle || circle.branch === currentBranch[pastActive]){
var ms = this.elapsedTime
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){
this.currentCircle = closestCircle
@@ -701,4 +731,104 @@ class Game{
this.sectionNotes = []
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)
+ }
+ }
}
diff --git a/public/src/js/gameinput.js b/public/src/js/gameinput.js
index 4707652..78f7284 100644
--- a/public/src/js/gameinput.js
+++ b/public/src/js/gameinput.js
@@ -94,7 +94,7 @@ class GameInput{
}
}
checkMenuKeys(){
- if(!this.controller.multiplayer && !this.locked){
+ if(!this.controller.multiplayer && !this.locked && this.controller.view.pauseOptions.length !== 0){
var moveMenu = 0
var ms = this.game.getAccurateTime()
this.gamepadMenu.play((pressed, name) => {
@@ -146,7 +146,7 @@ class GameInput{
this.checkKey("don_l", "menu", moveMenuConfirm)
this.checkKey("don_r", "menu", moveMenuConfirm)
if(moveMenu && this.game.isPaused()){
- assets.sounds["se_ka"].play()
+ this.controller.playSound("se_ka", 0, true)
this.controller.view.pauseMove(moveMenu)
}
}
@@ -197,11 +197,19 @@ class GameInput{
return
}
this.keyTime[name] = ms
+ var calibrationState = this.game.calibrationState
+ var calibration = calibrationState && !this.game.paused
if(name == "don_l" || name == "don_r"){
- this.checkKeySound(name, "don")
+ if(calibration){
+ this.game.calibrationHit(ms)
+ }else{
+ this.checkKeySound(name, "don")
+ }
this.keyboardEvents++
}else if(name == "ka_l" || name == "ka_r"){
- this.checkKeySound(name, "ka")
+ if(!calibration){
+ this.checkKeySound(name, "ka")
+ }
this.keyboardEvents++
}
}else{
diff --git a/public/src/js/loadsong.js b/public/src/js/loadsong.js
index d70b799..df82530 100644
--- a/public/src/js/loadsong.js
+++ b/public/src/js/loadsong.js
@@ -35,14 +35,20 @@ class LoadSong{
var song = this.selectedSong
var id = song.folder
var promises = []
- assets.sounds["v_start"].play()
+ if(song.folder !== "calibration"){
+ 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.songStage = this.randInt(1, 3)
song.donBg = this.randInt(1, 6)
- var songObj = assets.songs.find(song => song.id === id)
-
if(song.songSkin && song.songSkin.name){
var imgLoad = []
for(var type in song.songSkin){
@@ -117,14 +123,18 @@ class LoadSong{
}
}))
if(songObj.chart){
- var reader = new FileReader()
- promises.push(pageEvents.load(reader).then(event => {
- this.songData = event.target.result.replace(/\0/g, "").split("\n")
- }))
- if(song.type === "tja"){
- reader.readAsText(songObj.chart, "sjis")
+ if(songObj.chart === "blank"){
+ this.songData = ""
}else{
- reader.readAsText(songObj.chart)
+ var reader = new FileReader()
+ promises.push(pageEvents.load(reader).then(event => {
+ this.songData = event.target.result.replace(/\0/g, "").split("\n")
+ }))
+ if(song.type === "tja"){
+ reader.readAsText(songObj.chart, "sjis")
+ }else{
+ reader.readAsText(songObj.chart)
+ }
}
}else{
promises.push(loader.ajax(this.getSongPath(song)).then(data => {
diff --git a/public/src/js/parsetja.js b/public/src/js/parsetja.js
index 6979c81..1cd996c 100644
--- a/public/src/js/parsetja.js
+++ b/public/src/js/parsetja.js
@@ -25,6 +25,7 @@
"A": {name: "daiDon", txt: strings.note.daiDon},
"B": {name: "daiKa", txt: strings.note.daiKa}
}
+ this.noteTypes_ex = strings.ex_note;
this.courseTypes = {
"0": "easy",
"1": "normal",
@@ -122,7 +123,7 @@
return [string.slice(0, index), string.slice(index + delimiter.length)]
}
parseCircles(){
- var meta = this.metadata[this.difficulty]
+ var meta = this.metadata[this.difficulty] || {}
var ms = (meta.offset || 0) * -1000 + this.offset
var bpm = Math.abs(meta.bpm) || 120
var scroll = 1
@@ -148,7 +149,32 @@
var circles = []
var circleID = 0
var regexAZ = /[A-Z]/
-
+ var isAllDon = (note_chain, start_pos) => {
+ for (var i = start_pos; i < note_chain.length; ++i) {
+ var note = note_chain[i];
+ if (note && note.type !== "don" && note.type !== "daiDon") {
+ return false;
+ }
+ }
+ return true;
+ }
+ var checkChain = (note_chain, measure_length, is_last) => {
+ //console.log(note_chain, measure_length, is_last);
+ /*if (measure_length >= 24) {
+ for (var note of note_chain) {
+ note.text = this.noteTypes_ex[note.type][0];
+ }
+ } else { */
+ var alldon_pos = null;
+ for (var i = 0; i < note_chain.length - (is_last ? 1 : 0); ++i) {
+ var note = note_chain[i];
+ if (alldon_pos === null && is_last && isAllDon(note_chain, i)) {
+ alldon_pos = i;
+ }
+ note.text = this.noteTypes_ex[note.type][alldon_pos != null ? (i - alldon_pos) % 2 : 0];
+ }
+ //}
+ }
var pushMeasure = () => {
var note = currentMeasure[0]
if(note){
@@ -183,9 +209,11 @@
var msPerMeasure = 60000 * measure / note.bpm
ms += msPerMeasure / currentMeasure.length
}
- for(var i = 0; i < currentMeasure.length; i++){
+ var note_chain = [];
+ for (var i = 0; i < currentMeasure.length; i++){
+ //console.log(note_chain.length);
var note = currentMeasure[i]
- if(note.type){
+ if (note.type) {
circleID++
var circleObj = new Circle({
id: circleID,
@@ -200,13 +228,30 @@
branch: currentBranch,
section: note.section
})
- if(lastDrumroll === note){
+ if (note.type === "don" || note.type === "ka" || note.type === "daiDon" || note.type === "daiKa") {
+ note_chain.push(circleObj);
+ } else {
+ if (note_chain.length > 1 && currentMeasure.length >= 8) {
+ checkChain(note_chain, currentMeasure.length, false);
+ }
+ note_chain = [];
+ }
+ if (lastDrumroll === note) {
lastDrumroll = circleObj
}
circles.push(circleObj)
+ } else if (!(currentMeasure.length >= 24 && (!currentMeasure[i + 1] || currentMeasure[i + 1].type))
+ && !(currentMeasure.length >= 48 && (!currentMeasure[i + 2] || currentMeasure[i + 2].type || !currentMeasure[i + 3] || currentMeasure[i + 3].type))) {
+ if (note_chain.length > 1 && currentMeasure.length >= 8) {
+ checkChain(note_chain, currentMeasure.length, true);
+ }
+ note_chain = [];
}
}
+ if (note_chain.length > 1 && currentMeasure.length >= 8) {
+ checkChain(note_chain, currentMeasure.length, false);
+ }
}else{
var msPerMeasure = 60000 * measure / bpm
ms += msPerMeasure
diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js
index 24fde5d..a42ecb9 100644
--- a/public/src/js/scoresheet.js
+++ b/public/src/js/scoresheet.js
@@ -10,6 +10,14 @@ class Scoresheet{
this.canvas = document.getElementById("canvas")
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.fadeScreen = document.createElement("div")
@@ -28,8 +36,8 @@ class Scoresheet{
this.frame = 1000 / 60
this.numbers = "001122334455667788900112233445".split("")
- this.draw = new CanvasDraw()
- this.canvasCache = new CanvasCache()
+ this.draw = new CanvasDraw(noSmoothing)
+ this.canvasCache = new CanvasCache(noSmoothing)
this.keyboard = new Keyboard({
confirm: ["enter", "space", "esc", "don_l", "don_r"]
@@ -105,7 +113,7 @@ class Scoresheet{
if(!p2.session){
this.state.screen = "scoresShown"
this.state.screenMS = this.getMS()
- assets.sounds["neiro_1_don"].play()
+ this.controller.playSound("neiro_1_don", 0, true)
}
}
toSongsel(fromP2){
@@ -114,7 +122,7 @@ class Scoresheet{
this.state.screen = "fadeOut"
this.state.screenMS = this.getMS()
if(!fromP2){
- assets.sounds["neiro_1_don"].play()
+ this.controller.playSound("neiro_1_don", 0, true)
}
}
}
diff --git a/public/src/js/settings.js b/public/src/js/settings.js
index 3c78e6f..b2deaef 100644
--- a/public/src/js/settings.js
+++ b/public/src/js/settings.js
@@ -2,11 +2,15 @@ class Settings{
constructor(){
var ios = /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 = {
language: {
type: "language",
- options: ["ja", "en", "cn", "tw", "ko"],
+ options: this.allLanguages,
default: this.getLang()
},
resolution: {
@@ -34,6 +38,14 @@ class Settings{
options: ["a", "b", "c"],
default: "a",
gamepad: true
+ },
+ latency: {
+ type: "latency",
+ default: {
+ "audio": 0,
+ "video": 0,
+ "drumSounds": true
+ }
}
}
@@ -61,6 +73,22 @@ class Settings{
}
}
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{
this.storage[i] = storage[i]
}
@@ -107,7 +135,7 @@ class Settings{
}
}
}
- return "ja"
+ return this.allLanguages[0]
}
setLang(lang, noEvent){
strings = lang
@@ -122,7 +150,7 @@ class Settings{
}
class SettingsView{
- constructor(touchEnabled, tutorial, songId){
+ constructor(touchEnabled, tutorial, songId, toSetting){
this.touchEnabled = touchEnabled
this.tutorial = tutorial
this.songId = songId
@@ -130,9 +158,15 @@ class SettingsView{
loader.changePage("settings", tutorial)
assets.sounds["bgm_settings"].playLoop(0.1, false, 0, 1.392, 26.992)
this.defaultButton = document.getElementById("settings-default")
+ this.viewOuter = this.getElement("view-outer")
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
if("getGamepads" in navigator){
var gamepads = navigator.getGamepads()
@@ -145,19 +179,22 @@ class SettingsView{
}
this.mode = "settings"
+ this.pressedKeys = {}
this.keyboard = new Keyboard({
"confirm": ["enter", "space", "don_l", "don_r"],
"up": ["up"],
- "previous": ["left", "ka_l"],
- "next": ["right", "down", "ka_r"],
+ "right": ["right", "ka_r"],
+ "down": ["down"],
+ "left": ["left", "ka_l"],
"back": ["esc"],
"other": ["wildcard"]
}, this.keyPressed.bind(this))
this.gamepad = new Gamepad({
"confirm": ["b", "ls", "rs"],
"up": ["u", "lsu"],
- "previous": ["l", "lb", "lt", "lsl"],
- "next": ["d", "r", "rb", "rt", "lsd", "lsr"],
+ "right": ["r", "rb", "rt", "lsr"],
+ "down": ["d", "lsd"],
+ "left": ["l", "lb", "lt", "lsl"],
"back": ["start", "a"]
}, this.keyPressed.bind(this))
@@ -182,15 +219,15 @@ class SettingsView{
var nameDiv = document.createElement("div")
nameDiv.classList.add("setting-name", "stroke-sub")
var name = strings.settings[i].name
- nameDiv.innerText = name
- nameDiv.setAttribute("alt", name)
+ this.setAltText(nameDiv, name)
settingBox.appendChild(nameDiv)
var valueDiv = document.createElement("div")
valueDiv.classList.add("setting-value")
this.getValue(i, valueDiv)
settingBox.appendChild(valueDiv)
content.appendChild(settingBox)
- if(this.items.length === this.selected){
+ if(!toSetting && this.items.length === this.selected || toSetting === i){
+ this.selected = this.items.length
settingBox.classList.add("selected")
}
this.addTouch(settingBox, event => this.setValue(i))
@@ -226,8 +263,99 @@ class SettingsView{
this.gamepadButtons = document.getElementById("gamepad-buttons")
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.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")
}
getElement(name){
@@ -246,6 +374,23 @@ class SettingsView{
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){
pageEvents.remove(element, ["mousedown", "touchstart"])
}
@@ -274,6 +419,17 @@ class SettingsView{
valueDiv.appendChild(keyDiv)
}
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
}
@@ -285,6 +441,7 @@ class SettingsView{
if(this.mode !== "settings"){
if(this.selected === selectedIndex){
this.keyboardBack(selected)
+ this.playSound("se_don")
}
return
}
@@ -303,24 +460,37 @@ class SettingsView{
selected.valueDiv.classList.add("selected")
this.keyboardKeys = {}
this.keyboardSet()
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
return
}else if(current.type === "gamepad"){
this.mode = "gamepad"
this.gamepadSelected = current.options.indexOf(value)
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
}
settings.setItem(name, value)
this.getValue(name, this.items[this.selected].valueDiv)
- assets.sounds["se_ka"].play()
+ this.playSound("se_ka")
if(current.type === "language"){
this.setLang(allStrings[value])
}
}
- keyPressed(pressed, name, event){
- if(!pressed){
+ keyPressed(pressed, name, event, repeat){
+ 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
}
this.touched = false
@@ -334,31 +504,31 @@ class SettingsView{
}else{
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")
do{
- this.selected = this.mod(this.items.length, this.selected + (name === "next" ? 1 : -1))
- }while(this.items[this.selected].id === "default" && name !== "previous")
+ this.selected = this.mod(this.items.length, this.selected + ((name === "right" || name === "down") ? 1 : -1))
+ }while(this.items[this.selected].id === "default" && name !== "left")
selected = this.items[this.selected]
selected.settingBox.classList.add("selected")
selected.settingBox.scrollIntoView()
- assets.sounds["se_ka"].play()
+ this.playSound("se_ka")
}else if(name === "back"){
this.onEnd()
}
}else if(this.mode === "gamepad"){
if(name === "confirm"){
this.gamepadBack(true)
- }else if(name === "up" || name === "previous" || name === "next"){
- this.gamepadSet(name === "next" ? 1 : -1)
+ }else if(name === "up" || name === "right" || name === "down" || name === "left"){
+ this.gamepadSet((name === "right" || name === "down") ? 1 : -1)
}else if(name === "back"){
this.gamepadBack()
}
}else if(this.mode === "keyboard"){
if(name === "back"){
this.keyboardBack(selected)
- assets.sounds["se_cancel"].play()
- }else{
+ this.playSound("se_cancel")
+ }else if(event){
event.preventDefault()
var currentKey = event.key.toLowerCase()
for(var i in this.keyboardKeys){
@@ -367,10 +537,40 @@ class SettingsView{
}
}
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.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(){
@@ -416,14 +616,13 @@ class SettingsView{
var current = settings.items[selected.id]
if(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 value = strings.settings[selected.id][opt]
- this.gamepadValue.innerText = value
- this.gamepadValue.setAttribute("alt", value)
- this.gamepadButtons.style.backgroundPosition = "0 " + (-318 - 132 * this.gamepadSelected) + "px"
- this.gamepadSettings.style.display = "block"
+ this.setAltText(this.gamepadValue, value)
+ this.gamepadButtons.style.backgroundPosition = "0 " + (-11.87 - 4.93 * this.gamepadSelected) + "em"
+ this.gamepadSettings.style.display = "flex"
}
gamepadBack(confirm){
if(this.mode !== "gamepad"){
@@ -433,10 +632,142 @@ class SettingsView{
var current = settings.items[selected.id]
settings.setItem(selected.id, current.options[this.gamepadSelected])
this.getValue(selected.id, selected.valueDiv)
- assets.sounds[confirm ? "se_don" : "se_cancel"].play()
+ this.playSound(confirm ? "se_don" : "se_cancel")
this.gamepadSettings.style.display = ""
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(){
if(this.mode === "keyboard"){
this.keyboardBack(this.items[this.selected])
@@ -447,11 +778,17 @@ class SettingsView{
this.setLang(allStrings[settings.getItem("language")])
this.keyboard.update()
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(){
this.clean()
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
setTimeout(() => {
if(this.tutorial && !this.touched){
new Tutorial(false, this.songId)
@@ -472,41 +809,94 @@ class SettingsView{
var item = this.items[i]
if(item.valueDiv){
var name = strings.settings[item.id].name
- item.nameDiv.innerText = name
- item.nameDiv.setAttribute("alt", name)
+ this.setAltText(item.nameDiv, name)
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()
}
setStrings(){
- this.viewTitle.innerText = strings.gameSettings
- this.viewTitle.setAttribute("alt", strings.gameSettings)
- this.endButton.innerText = strings.settings.ok
- this.endButton.setAttribute("alt", strings.settings.ok)
- this.gamepadTitle.innerText = strings.settings.gamepadLayout.name
- this.gamepadTitle.setAttribute("alt", strings.settings.gamepadLayout.name)
- this.gamepadEndButton.innerText = strings.settings.ok
- this.gamepadEndButton.setAttribute("alt", strings.settings.ok)
- this.defaultButton.innerText = strings.settings.default
- this.defaultButton.setAttribute("alt", strings.settings.default)
+ this.setAltText(this.viewTitle, strings.gameSettings)
+ this.setAltText(this.endButton, strings.settings.ok)
+ this.setAltText(this.gamepadTitle, strings.settings.gamepadLayout.name)
+ this.setAltText(this.gamepadEndButton, strings.settings.ok)
+ this.setAltText(this.latencyTitle, strings.settings.latency.name)
+ this.setAltText(this.latencyDefaultButton, strings.settings.default)
+ this.setAltText(this.latencyEndButton, strings.settings.ok)
+ this.setAltText(this.defaultButton, strings.settings.default)
+ }
+ setAltText(element, text){
+ element.innerText = text
+ element.setAttribute("alt", text)
}
mod(length, index){
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(){
+ this.redrawRunning = false
this.keyboard.clean()
this.gamepad.clean()
assets.sounds["bgm_settings"].stop()
+ pageEvents.remove(this.viewOuter, ["mouseup", "touchend"])
for(var i in this.items){
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){
delete this.defaultButton
}
this.removeTouch(this.gamepadSettings)
this.removeTouch(this.gamepadEndButton)
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.endButton
delete this.items
@@ -516,6 +906,11 @@ class SettingsView{
delete this.gamepadBox
delete this.gamepadButtons
delete this.gamepadValue
+ delete this.latencyItems
+ delete this.latencySettings
+ delete this.latencyTitle
+ delete this.latencyDefaultButton
+ delete this.latencyEndButton
if(this.resolution !== settings.getItem("resolution")){
for(var i in assets.image){
if(i === "touch_drum" || i.startsWith("bg_song_") || i.startsWith("bg_stage_") || i.startsWith("bg_don_")){
diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js
index ad48015..dfa2368 100644
--- a/public/src/js/songselect.js
+++ b/public/src/js/songselect.js
@@ -5,6 +5,14 @@ class SongSelect{
loader.changePage("songselect", false)
this.canvas = document.getElementById("song-sel-canvas")
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 = {
"selected": {
@@ -209,13 +217,13 @@ class SongSelect{
}]
this.optionsList = [strings.none, strings.auto, strings.netplay]
- this.draw = new CanvasDraw()
- this.songTitleCache = new CanvasCache()
- this.selectTextCache = new CanvasCache()
- this.categoryCache = new CanvasCache()
- this.difficultyCache = new CanvasCache()
- this.sessionCache = new CanvasCache()
- this.currentSongCache = new CanvasCache()
+ this.draw = new CanvasDraw(noSmoothing)
+ this.songTitleCache = new CanvasCache(noSmoothing)
+ this.selectTextCache = new CanvasCache(noSmoothing)
+ this.categoryCache = new CanvasCache(noSmoothing)
+ this.difficultyCache = new CanvasCache(noSmoothing)
+ this.sessionCache = new CanvasCache(noSmoothing)
+ this.currentSongCache = new CanvasCache(noSmoothing)
this.difficulty = [strings.easy, strings.normal, strings.hard, strings.oni]
this.difficultyId = ["easy", "normal", "hard", "oni", "ura"]
@@ -236,6 +244,9 @@ class SongSelect{
fromTutorial = false
}
+ this.drumSounds = settings.getItem("latency").drumSounds
+ this.playedSounds = {}
+
var songIdIndex = -1
if(fromTutorial){
this.selectedSong = this.songs.findIndex(song => song.action === fromTutorial)
@@ -254,7 +265,7 @@ class SongSelect{
}else if((!p2.session || fadeIn) && "selectedSong" in localStorage){
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()
this.playBgm(false)
}
@@ -453,7 +464,7 @@ class SongSelect{
window.open(this.songs[this.selectedSong].maker.url)
}else if(moveBy === this.diffOptions.length + 4){
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){
this.state.move = -1
}
@@ -581,7 +592,7 @@ class SongSelect{
var soundsDelay = Math.abs((scroll + resize) / moveBy)
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)
}
@@ -605,7 +616,7 @@ class SongSelect{
this.state.move = moveBy
this.state.moveMS = this.getMS() - 500
this.state.locked = 1
- assets.sounds["se_ka"].play()
+ this.playSound("se_ka")
}
}
@@ -636,15 +647,15 @@ class SongSelect{
this.selectedDiff = this.diffOptions.length + 3
}
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
assets.sounds["v_songsel"].stop()
- assets.sounds["v_diffsel"].play(0.3)
+ this.playSound("v_diffsel", 0.3)
pageEvents.send("song-select-difficulty", currentSong)
}else if(currentSong.action === "back"){
this.clean()
this.toTitleScreen()
}else if(currentSong.action === "random"){
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
this.state.locked = true
do{
var i = Math.floor(Math.random() * this.songs.length)
@@ -681,7 +692,7 @@ class SongSelect{
this.state.moveHover = null
assets.sounds["v_diffsel"].stop()
- assets.sounds["se_cancel"].play()
+ this.playSound("se_cancel")
}
this.clearHash()
pageEvents.send("song-select-back")
@@ -690,7 +701,7 @@ class SongSelect{
this.clean()
var selectedSong = this.songs[this.selectedSong]
assets.sounds["v_diffsel"].stop()
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
try{
if(assets.customSongs){
@@ -729,7 +740,7 @@ class SongSelect{
}
toOptions(moveBy){
if(!p2.session){
- assets.sounds["se_ka"].play()
+ this.playSound("se_ka")
this.selectedDiff = 1
do{
this.state.options = this.mod(this.optionsList.length, this.state.options + moveBy)
@@ -738,7 +749,7 @@ class SongSelect{
}
toTitleScreen(){
if(!p2.session){
- assets.sounds["se_cancel"].play()
+ this.playSound("se_cancel")
this.clean()
setTimeout(() => {
new Titlescreen()
@@ -746,21 +757,21 @@ class SongSelect{
}
}
toTutorial(){
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
this.clean()
setTimeout(() => {
new Tutorial(true)
}, 500)
}
toAbout(){
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
this.clean()
setTimeout(() => {
new About(this.touchEnabled)
}, 500)
}
toSettings(){
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
this.clean()
setTimeout(() => {
new SettingsView(this.touchEnabled)
@@ -775,7 +786,7 @@ class SongSelect{
}else{
localStorage["selectedSong"] = this.selectedSong
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
this.clean()
setTimeout(() => {
new Session(this.touchEnabled)
@@ -786,7 +797,7 @@ class SongSelect{
if(assets.customSongs){
assets.customSongs = false
assets.songs = assets.songsDefault
- assets.sounds["se_don"].play()
+ this.playSound("se_don")
this.clean()
setTimeout(() => {
new SongSelect("browse", false, this.touchEnabled)
@@ -1014,6 +1025,7 @@ class SongSelect{
var resize2 = changeSpeed - resize
var scroll = resize2 - resize - scrollDelay * 2
var elapsed = ms - this.state.moveMS
+
if(this.state.catJump || (this.state.move && ms > this.state.moveMS + resize2 - scrollDelay)){
var isJump = this.state.catJump
var previousSelectedSong = this.selectedSong
@@ -2116,6 +2128,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(){
return Date.now()
}
diff --git a/public/src/js/strings.js b/public/src/js/strings.js
index 02e3dcf..4ff9367 100644
--- a/public/src/js/strings.js
+++ b/public/src/js/strings.js
@@ -50,6 +50,22 @@
daiDrumroll: "連打(大)ーっ!!",
balloon: "ふうせん"
}
+ this.ex_note = {
+ don: [
+ "ド",
+ "コ"
+ ],
+ ka: [
+ "カ"
+ ],
+ daiDon: [
+ "ドン(大)",
+ "ドン(大)"
+ ],
+ daiKa: [
+ "カッ(大)"
+ ]
+ }
this.combo = "コンボ"
this.clear = "クリア"
this.good = "良"
@@ -127,11 +143,42 @@
b: "タイプB",
c: "タイプC"
},
+ latency: {
+ name: "Latency",
+ value: "Audio: %s, Video: %s",
+ calibration: "Latency Calibration",
+ audio: "Audio",
+ video: "Video",
+ drumSounds: "Drum Sounds"
+ },
on: "オン",
off: "オフ",
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 = {
browserWarning: "サポートされていないブラウザを実行しています (%s)",
details: "詳しく",
@@ -195,6 +242,22 @@ function StringsEn(){
daiDrumroll: "DRUM ROLLー!!",
balloon: "Balloon"
}
+ this.ex_note = {
+ don: [
+ "Do",
+ "Do"
+ ],
+ ka: [
+ "Ka"
+ ],
+ daiDon: [
+ "DON",
+ "DON"
+ ],
+ daiKa: [
+ "KA"
+ ]
+ }
this.combo = "Combo"
this.clear = "Clear"
this.good = "GOOD"
@@ -272,11 +335,42 @@ function StringsEn(){
b: "Type B",
c: "Type C"
},
+ latency: {
+ name: "Latency",
+ value: "Audio: %s, Video: %s",
+ calibration: "Latency Calibration",
+ audio: "Audio",
+ video: "Video",
+ drumSounds: "Drum Sounds"
+ },
on: "On",
off: "Off",
default: "Reset to Defaults",
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 = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@@ -340,6 +434,22 @@ function StringsCn(){
daiDrumroll: "连打(大)ー!!",
balloon: "气球"
}
+ this.ex_note = {
+ don: [
+ "咚",
+ "咚"
+ ],
+ ka: [
+ "咔"
+ ],
+ daiDon: [
+ "咚(大)",
+ "咚(大)"
+ ],
+ daiKa: [
+ "咔(大)"
+ ]
+ }
this.combo = "连段"
this.clear = "通关"
this.good = "良"
@@ -417,11 +527,42 @@ function StringsCn(){
b: "类型B",
c: "类型C"
},
+ latency: {
+ name: "Latency",
+ value: "Audio: %s, Video: %s",
+ calibration: "Latency Calibration",
+ audio: "Audio",
+ video: "Video",
+ drumSounds: "Drum Sounds"
+ },
on: "开",
off: "关",
default: "重置为默认值",
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 = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@@ -485,6 +626,22 @@ function StringsTw(){
daiDrumroll: "連打(大)ー!!",
balloon: "氣球"
}
+ this.ex_note = {
+ don: [
+ "咚",
+ "咚"
+ ],
+ ka: [
+ "咔"
+ ],
+ daiDon: [
+ "咚(大)",
+ "咚(大)"
+ ],
+ daiKa: [
+ "咔(大)"
+ ]
+ }
this.combo = "連段"
this.clear = "通關"
this.good = "良"
@@ -562,11 +719,42 @@ function StringsTw(){
b: "類型B",
c: "類型C"
},
+ latency: {
+ name: "Latency",
+ value: "Audio: %s, Video: %s",
+ calibration: "Latency Calibration",
+ audio: "Audio",
+ video: "Video",
+ drumSounds: "Drum Sounds"
+ },
on: "開",
off: "關",
default: "重置為默認值",
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 = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
@@ -630,6 +818,22 @@ function StringsKo(){
daiDrumroll: "연타(대)ー!!",
balloon: "풍선"
}
+ this.ex_note = {
+ don: [
+ "쿠",
+ "쿠"
+ ],
+ ka: [
+ "딱"
+ ],
+ daiDon: [
+ "쿵(대)",
+ "쿵(대)"
+ ],
+ daiKa: [
+ "딱(대)"
+ ]
+ }
this.combo = "콤보"
this.clear = "클리어"
this.good = "얼쑤"
@@ -707,11 +911,42 @@ function StringsKo(){
b: "타입 B",
c: "타입 C"
},
+ latency: {
+ name: "Latency",
+ value: "Audio: %s, Video: %s",
+ calibration: "Latency Calibration",
+ audio: "Audio",
+ video: "Video",
+ drumSounds: "Drum Sounds"
+ },
on: "온",
off: "오프",
default: "기본값으로 재설정",
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 = {
browserWarning: "You are running an unsupported browser (%s)",
details: "Details...",
diff --git a/public/src/js/view.js b/public/src/js/view.js
index 06c30c7..332cac8 100644
--- a/public/src/js/view.js
+++ b/public/src/js/view.js
@@ -4,8 +4,15 @@
this.canvas = document.getElementById("canvas")
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.songBg = document.getElementById("songbg")
this.songStage = document.getElementById("song-stage")
@@ -73,6 +80,7 @@
}
this.nextBeat = 0
this.gogoTime = 0
+ this.gogoTimeStarted = -Infinity
this.drumroll = []
this.touchEvents = 0
if(this.controller.parsedSongData.branches){
@@ -103,16 +111,20 @@
}
}
- this.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval
+ if(this.controller.calibrationMode){
+ this.beatInterval = 512
+ }else{
+ this.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval
+ }
this.font = strings.font
- this.draw = new CanvasDraw()
+ this.draw = new CanvasDraw(noSmoothing)
this.assets = new ViewAssets(this)
- this.titleCache = new CanvasCache()
- this.comboCache = new CanvasCache()
- this.pauseCache = new CanvasCache()
- this.branchCache = new CanvasCache()
+ this.titleCache = new CanvasCache(noSmoothing)
+ this.comboCache = new CanvasCache(noSmoothing)
+ this.pauseCache = new CanvasCache(noSmoothing)
+ this.branchCache = new CanvasCache(noSmoothing)
this.multiplayer = this.controller.multiplayer
@@ -120,6 +132,9 @@
this.touch = -Infinity
this.touchAnimation = settings.getItem("touchAnimation")
+ versionDiv.classList.add("version-hide")
+ loader.screen.parentNode.insertBefore(versionDiv, loader.screen)
+
if(this.multiplayer !== 2){
if(this.controller.touchEnabled){
@@ -134,7 +149,6 @@
pageEvents.add(this.canvas, "touchstart", this.ontouch.bind(this))
this.gameDiv.classList.add("touch-visible")
- document.getElementById("version").classList.add("version-hide")
this.touchFullBtn = document.getElementById("touch-full-btn")
pageEvents.add(this.touchFullBtn, "touchend", toggleFullscreen)
@@ -444,12 +458,14 @@
ctx.fill()
// Difficulty
- ctx.drawImage(assets.image["difficulty"],
- 0, 144 * this.difficulty[this.controller.selectedSong.difficulty],
- 168, 143,
- 126, this.multiplayer === 2 ? 497 : 228,
- 62, 53
- )
+ if(this.controller.selectedSong.difficulty){
+ ctx.drawImage(assets.image["difficulty"],
+ 0, 144 * this.difficulty[this.controller.selectedSong.difficulty],
+ 168, 143,
+ 126, this.multiplayer === 2 ? 497 : 228,
+ 62, 53
+ )
+ }
// Badges
if(this.controller.autoPlayEnabled && !this.controller.multiplayer){
@@ -593,24 +609,26 @@
ctx.globalAlpha = 1
// Difficulty
- ctx.drawImage(assets.image["difficulty"],
- 0, 144 * this.difficulty[this.controller.selectedSong.difficulty],
- 168, 143,
- 16, this.multiplayer === 2 ? 194 : 232,
- 141, 120
- )
- var diff = this.controller.selectedSong.difficulty
- var text = strings[diff === "ura" ? "oni" : diff]
- ctx.font = this.draw.bold(this.font) + "20px " + this.font
- ctx.textAlign = "center"
- ctx.textBaseline = "bottom"
- ctx.strokeStyle = "#000"
- ctx.fillStyle = "#fff"
- ctx.lineWidth = 7
- ctx.miterLimit = 1
- ctx.strokeText(text, 87, this.multiplayer === 2 ? 310 : 348)
- ctx.fillText(text, 87, this.multiplayer === 2 ? 310 : 348)
- ctx.miterLimit = 10
+ if(this.controller.selectedSong.difficulty){
+ ctx.drawImage(assets.image["difficulty"],
+ 0, 144 * this.difficulty[this.controller.selectedSong.difficulty],
+ 168, 143,
+ 16, this.multiplayer === 2 ? 194 : 232,
+ 141, 120
+ )
+ var diff = this.controller.selectedSong.difficulty
+ var text = strings[diff === "ura" ? "oni" : diff]
+ ctx.font = this.draw.bold(this.font) + "20px " + this.font
+ ctx.textAlign = "center"
+ ctx.textBaseline = "bottom"
+ ctx.strokeStyle = "#000"
+ ctx.fillStyle = "#fff"
+ ctx.lineWidth = 7
+ ctx.miterLimit = 1
+ ctx.strokeText(text, 87, this.multiplayer === 2 ? 310 : 348)
+ ctx.fillText(text, 87, this.multiplayer === 2 ? 310 : 348)
+ ctx.miterLimit = 10
+ }
// Badges
if(this.controller.autoPlayEnabled && !this.controller.multiplayer){
@@ -947,6 +965,20 @@
ctx.clip()
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()
// Hit notes explosion
@@ -1001,6 +1033,22 @@
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) => {
this.draw.roundedRect({
ctx: ctx,
@@ -1025,86 +1073,284 @@
dx: 68,
dy: 11
})
-
- ctx.drawImage(assets.image["mimizu"],
- 313, 247, 136, 315
- )
-
- var _y = 108
- var _w = 80
- var _h = 464
- for(var i = 0; i < this.pauseOptions.length; i++){
- var _x = 520 + 110 * i
- 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()
- }
- this.pauseCache.get({
+ if(boldTitle){
+ this.draw.layeredText({
ctx: ctx,
- x: _x - _w / 2,
- y: _y,
- w: _w,
- h: _h,
- id: this.pauseOptions[i] + (selected ? "1" : "0")
- }, ctx => {
- var textConfig = {
+ 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: this.pauseOptions[i],
- x: _w / 2,
- y: 18,
- width: _w,
- height: _h - 54,
+ 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,
- letterSpacing: -1
- }
- if(selected){
- textConfig.fill = "#fff"
- textConfig.outline = "#000"
- textConfig.outlineSize = 10
- }else{
- textConfig.fill = "#000"
- }
- this.draw.verticalText(textConfig)
- })
-
- 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
+ x: 300,
+ y: 130,
+ width: 680,
+ height: 420,
+ lineHeight: 47,
+ fill: "#000",
+ verticalAlign: "middle",
+ textAlign: "center",
})
- }
+ break
+ default:
+ ctx.drawImage(assets.image["mimizu"],
+ 313, 247, 136, 315
+ )
+
+ var _y = 108
+ var _w = 80
+ var _h = 464
+ 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
+ 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()
+ }
+ this.pauseCache.get({
+ ctx: ctx,
+ x: _x - _w / 2,
+ y: _y,
+ w: _w,
+ h: _h,
+ id: text + (selected ? "1" : "0")
+ }, ctx => {
+ var textConfig = {
+ ctx: ctx,
+ text: text,
+ x: _w / 2,
+ y: 18,
+ width: _w,
+ height: _h - 54,
+ fontSize: 40,
+ fontFamily: this.font,
+ letterSpacing: -1
+ }
+ if(selected){
+ textConfig.fill = "#fff"
+ textConfig.outline = "#000"
+ textConfig.outlineSize = 10
+ }else{
+ textConfig.fill = "#000"
+ }
+ this.draw.verticalText(textConfig)
+ })
+
+ 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
}
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(){
var selectedSong = this.controller.selectedSong
var songSkinName = selectedSong.songSkin.name
@@ -1219,10 +1465,10 @@
measures.forEach(measure => {
var timeForDistance = this.posToMs(distanceForCircle, measure.speed)
- var startingTime = measure.ms - timeForDistance
- var finishTime = measure.ms + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + 3, measure.speed)
+ var startingTime = measure.ms - timeForDistance + this.controller.videoLatency
+ 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){
- 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.lineWidth = 3
this.ctx.beginPath()
@@ -1267,8 +1513,8 @@
var speed = circle.speed
var timeForDistance = this.posToMs(distanceForCircle + this.slotPos.size / 2, speed)
- var startingTime = circle.ms - timeForDistance
- var finishTime = circle.endTime + this.posToMs(this.slotPos.x - this.slotPos.paddingLeft + this.slotPos.size * 2, speed)
+ 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) + this.controller.videoLatency
if(circle.isPlayed <= 0 || circle.score === 0){
if((!circle.branch || circle.branch.active) && ms >= startingTime && ms <= finishTime && circle.isPlayed !== -1){
@@ -1350,7 +1596,7 @@
if(!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
}
}
@@ -1388,10 +1634,10 @@
size = circleSize
faceID = noteFace.small
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
- }else if(ms > endTime){
- circlePos.x = this.slotPos.x + this.msToPos(endTime - ms, speed)
+ }else if(ms > endTime + this.controller.audioLatency){
+ circlePos.x = this.slotPos.x + this.msToPos(endTime - ms + this.controller.audioLatency, speed)
}
ctx.drawImage(assets.image["balloon"],
circlePos.x + size - 4,
@@ -1596,7 +1842,9 @@
}
toggleGogoTime(circle){
this.gogoTime = circle.gogoTime
- this.gogoTimeStarted = circle.ms
+ if(circle.gogoTime || this.gogoTimeStarted !== -Infinity){
+ this.gogoTimeStarted = circle.ms
+ }
if(this.gogoTime){
this.assets.fireworks.forEach(fireworksAsset => {
@@ -1789,21 +2037,62 @@
if(typeof pos === "undefined"){
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){
case 1:
- assets.sounds["se_don"].play()
- this.controller.restartSong()
+ this.controller.playSound("se_don", 0, true)
+ if(state === "video"){
+ game.calibrationReset(state)
+ }else{
+ this.controller.restartSong()
+ }
pageEvents.send("pause-restart")
break
case 2:
- assets.sounds["se_don"].play()
+ this.controller.playSound("se_don", 0, true)
this.controller.songSelection()
pageEvents.send("pause-song-select")
break
default:
- this.controller.togglePause()
+ this.controller.togglePause(false)
break
}
+ return true
}
onmousedown(event){
if(this.controller.game.paused){
@@ -1855,23 +2144,30 @@
x = x * pauseScale + 257
y = y * pauseScale - 328
}
- if(104 <= y && y <= 575 && 465 <= x && x <= 465 + 110 * this.pauseOptions.length){
- return Math.floor((x - 465) / 110)
+ 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){
+ return Math.floor((x - 465) / 110)
+ }
+ break
}
return null
}
mouseIdle(){
var lastMouse = pageEvents.getMouse()
- if(lastMouse && !this.cursorHidden){
+ if(lastMouse && !this.cursorHidden && !this.state.hasPointer){
if(this.getMS() >= this.lastMousemove + 2000){
- this.cursor.style.top = lastMouse.clientY + "px"
- this.cursor.style.left = lastMouse.clientX + "px"
- this.cursor.style.pointerEvents = "auto"
+ this.canvas.style.cursor = "none"
this.cursorHidden = true
}else{
- this.cursor.style.top = ""
- this.cursor.style.left = ""
- this.cursor.style.pointerEvents = ""
+ this.canvas.style.cursor = ""
}
}
}
@@ -1890,12 +2186,13 @@
this.pauseCache.clean()
this.branchCache.clean()
+ versionDiv.classList.remove("version-hide")
+ loader.screen.parentNode.appendChild(versionDiv)
if(this.multiplayer !== 2){
if(this.touchEnabled){
pageEvents.remove(this.canvas, "touchstart")
pageEvents.remove(this.touchPauseBtn, "touchend")
this.gameDiv.classList.add("touch-results")
- document.getElementById("version").classList.remove("version-hide")
this.touchDrumDiv.parentNode.removeChild(this.touchDrumDiv)
delete this.touchDrumDiv
delete this.touchDrumImg
@@ -1915,7 +2212,6 @@
pageEvents.mouseRemove(this)
delete this.pauseMenu
- delete this.cursor
delete this.gameDiv
delete this.canvas
delete this.ctx
diff --git a/public/src/views/about.html b/public/src/views/about.html
index 3506eb3..37e0c74 100644
--- a/public/src/views/about.html
+++ b/public/src/views/about.html
@@ -7,8 +7,8 @@