Custom scripting, #song=, translations

- A song can be linked directly by adding "#song=<id>" to the url, replace `<id>` with the id in the database, after loading it jumps immediately jumps to the difficulty selection
- Added tutorial translations
- Fixed song preview not playing
- Use text fallback for the logo when there are no vectors
- Increased combo cache by 1 pixel
- A custom javascript file can be loaded from config.json by defining "custom_js" value
- Added lots of events to help writing custom js files: `version-link, title-screen, language-change, song-select, song-select-move, song-select-difficulty, song-select-back, about, about-link, tutorial, import-songs, import-songs-default, session, session-start, session-end, debug, load-song, load-song-player2, load-song-unfocused, load-song-cancel, load-song-error, game-start, key-events, p2-game-end, p2-disconnected, p2-abandoned, pause, unpause, pause-restart, pause-song-select, game-lag, scoresheet, scoresheet-player2`
- Event syntax example:
```js
addEventListener("game-start", event => {
	console.log("game-start", event.detail)
})
```
This commit is contained in:
LoveEevee 2019-02-14 12:32:45 +03:00
parent ca85bc1b2d
commit adc45cb652
20 changed files with 224 additions and 104 deletions

View File

@ -41,7 +41,7 @@
"confirm": ["start", "b", "ls", "rs"] "confirm": ["start", "b", "ls", "rs"]
}, this.onEnd.bind(this)) }, this.onEnd.bind(this))
this.addDiag() pageEvents.send("about", this.addDiag())
} }
onEnd(event){ onEnd(event){
var touched = false var touched = false
@ -145,12 +145,15 @@
var issueBody = strings.issueTemplate + "\n\n\n\n" + diag var issueBody = strings.issueTemplate + "\n\n\n\n" + diag
this.getLink(this.linkEmail).href += "?body=" + encodeURIComponent(issueBody.replace(/\n/g, "<br>\r\n")) this.getLink(this.linkEmail).href += "?body=" + encodeURIComponent(issueBody.replace(/\n/g, "<br>\r\n"))
return diag
} }
getLink(target){ getLink(target){
return target.getElementsByTagName("a")[0] return target.getElementsByTagName("a")[0]
} }
linkButton(event){ linkButton(event){
this.getLink(event.currentTarget).click() this.getLink(event.currentTarget).click()
pageEvents.send("about-link", event)
} }
clean(){ clean(){
cancelTouch = true cancelTouch = true

View File

@ -58,6 +58,12 @@ class Controller{
this.viewLoop() this.viewLoop()
if(this.multiplayer !== 2){ if(this.multiplayer !== 2){
this.gameInterval = setInterval(this.gameLoop.bind(this), 1000 / 60) this.gameInterval = setInterval(this.gameLoop.bind(this), 1000 / 60)
pageEvents.send("game-start", {
selectedSong: this.selectedSong,
autoPlayEnabled: this.autoPlayEnabled,
multiplayer: this.multiplayer,
touchEnabled: this.touchEnabled
})
} }
} }
stopMainLoop(){ stopMainLoop(){

View File

@ -37,6 +37,7 @@ class Debug{
this.moveTo(100, 100) this.moveTo(100, 100)
this.restore() this.restore()
this.updateStatus() this.updateStatus()
pageEvents.send("debug")
} }
startMove(event){ startMove(event){
if(event.which === 1){ if(event.which === 1){

View File

@ -362,6 +362,7 @@ class Game{
this.view.gameDiv.classList.add("game-paused") this.view.gameDiv.classList.add("game-paused")
this.view.lastMousemove = this.view.getMS() this.view.lastMousemove = this.view.getMS()
this.view.cursorHidden = false this.view.cursorHidden = false
pageEvents.send("pause")
}else{ }else{
assets.sounds["se_cancel"].play() assets.sounds["se_cancel"].play()
this.paused = false this.paused = false
@ -370,6 +371,7 @@ class Game{
this.sndTime = currentDate - snd.buffer.getTime() * 1000 this.sndTime = currentDate - snd.buffer.getTime() * 1000
this.view.gameDiv.classList.remove("game-paused") this.view.gameDiv.classList.remove("game-paused")
this.view.pointer() this.view.pointer()
pageEvents.send("unpause", currentDate - this.latestDate)
} }
} }
isPaused(){ isPaused(){
@ -391,6 +393,7 @@ class Game{
if(Math.abs(lag) >= 50){ if(Math.abs(lag) >= 50){
this.startDate += lag this.startDate += lag
this.sndTime = sndTime this.sndTime = sndTime
pageEvents.send("game-lag", lag)
} }
} }
this.elapsedTime = currentDate - this.startDate this.elapsedTime = currentDate - this.startDate

View File

@ -25,6 +25,7 @@ class Gamepad{
"lsl": "lsl" "lsl": "lsl"
} }
this.btn = {} this.btn = {}
this.gamepadEvents = 0
if(callback){ if(callback){
this.interval = setInterval(() => { this.interval = setInterval(() => {
this.play(callback) this.play(callback)
@ -123,6 +124,7 @@ class Gamepad{
if(pressed){ if(pressed){
callback(true, keyCode) callback(true, keyCode)
this.gamepadEvents++
}else if(!button){ }else if(!button){
if(released){ if(released){
this.toRelease[keyCode + "released"] = true this.toRelease[keyCode + "released"] = true

View File

@ -386,6 +386,7 @@
document.head.appendChild(style) document.head.appendChild(style)
} }
if(this.songs.length){ if(this.songs.length){
var length = this.songs.length
assets.songs = this.songs assets.songs = this.songs
assets.customSongs = true assets.customSongs = true
assets.customSelected = 0 assets.customSelected = 0
@ -395,6 +396,7 @@
loader.screen.removeChild(this.loaderDiv) loader.screen.removeChild(this.loaderDiv)
this.clean() this.clean()
new SongSelect("browse", false, this.songSelect.touchEnabled) new SongSelect("browse", false, this.songSelect.touchEnabled)
pageEvents.send("import-songs", length)
}, 500) }, 500)
}else{ }else{
loader.screen.removeChild(this.loaderDiv) loader.screen.removeChild(this.loaderDiv)

View File

@ -28,6 +28,7 @@ class Keyboard{
"don": -Infinity, "don": -Infinity,
"ka": -Infinity "ka": -Infinity
} }
this.keyboardEvents = 0
var gameBtn = {} var gameBtn = {}
gameBtn[this.kbd["don_l"]] = ["u", "d", "l", "r", "ls"] gameBtn[this.kbd["don_l"]] = ["u", "d", "l", "r", "ls"]
@ -66,8 +67,19 @@ class Keyboard{
if(key && !event.repeat && this.buttonEnabled(key)){ if(key && !event.repeat && this.buttonEnabled(key)){
var ms = this.game.getAccurateTime() var ms = this.game.getAccurateTime()
this.setKey(key, event.type === "keydown", ms) this.setKey(key, event.type === "keydown", ms)
if(event.type === "keydown"){
this.keyboardEvents++
}
} }
}) })
if(controller.multiplayer === 1){
pageEvents.add(window, "beforeunload", event => {
if(p2.otherConnected){
pageEvents.send("p2-abandoned", event)
}
})
}
} }
getBindings(){ getBindings(){
return this.kbd return this.kbd
@ -165,8 +177,9 @@ class Keyboard{
} }
if(this.controller.multiplayer !== 2){ if(this.controller.multiplayer !== 2){
this.checkKey(this.kbd["back"], "menu", () => { this.checkKey(this.kbd["back"], "menu", () => {
if(this.controller.multiplayer === 1){ if(this.controller.multiplayer === 1 && p2.otherConnected){
p2.send("gameend") p2.send("gameend")
pageEvents.send("p2-abandoned")
} }
this.controller.togglePause() this.controller.togglePause()
this.controller.songSelection() this.controller.songSelection()
@ -243,5 +256,14 @@ class Keyboard{
clean(){ clean(){
pageEvents.keyRemove(this, "all") pageEvents.keyRemove(this, "all")
clearInterval(this.gamepadInterval) clearInterval(this.gamepadInterval)
if(this.controller.multiplayer === 1){
pageEvents.remove(window, "beforeunload")
}
if(this.controller.multiplayer !== 2){
pageEvents.send("key-events", {
keyboard: this.keyboardEvents,
gamepad: this.gamepad.gamepadEvents
})
}
} }
} }

View File

@ -25,6 +25,12 @@ class Loader{
var queryString = gameConfig._version.commit_short ? "?" + gameConfig._version.commit_short : "" var queryString = gameConfig._version.commit_short ? "?" + gameConfig._version.commit_short : ""
if(gameConfig.custom_js){
var script = document.createElement("script")
this.addPromise(pageEvents.load(script))
script.src = gameConfig.custom_js + queryString
document.head.appendChild(script)
}
assets.js.forEach(name => { assets.js.forEach(name => {
var script = document.createElement("script") var script = document.createElement("script")
this.addPromise(pageEvents.load(script)) this.addPromise(pageEvents.load(script))
@ -147,8 +153,15 @@ class Loader{
} }
})) }))
var songId
var hashLower = location.hash.toLowerCase()
p2 = new P2Connection() p2 = new P2Connection()
if(location.hash.length === 6){ if(hashLower.startsWith("#song=")){
var number = parseInt(location.hash.slice(6))
if(number > 0){
songId = number
}
}else if(location.hash.length === 6){
p2.hashLock = true p2.hashLock = true
this.addPromise(new Promise(resolve => { this.addPromise(new Promise(resolve => {
p2.open() p2.open()
@ -182,7 +195,7 @@ class Loader{
perf.load = Date.now() - this.startTime perf.load = Date.now() - this.startTime
this.canvasTest.clean() this.canvasTest.clean()
this.clean() this.clean()
this.callback() this.callback(songId)
}) })
}, this.errorMsg.bind(this)) }, this.errorMsg.bind(this))

View File

@ -15,6 +15,11 @@ class LoadSong{
cancel.setAttribute("alt", strings.cancel) cancel.setAttribute("alt", strings.cancel)
} }
this.run() this.run()
pageEvents.send("load-song", {
selectedSong: selectedSong,
autoPlayEnabled: autoPlayEnabled,
multiplayer: multiplayer
})
} }
run(){ run(){
var song = this.selectedSong var song = this.selectedSong
@ -117,6 +122,7 @@ class LoadSong{
if(Array.isArray(error) && error[1] instanceof HTMLElement){ if(Array.isArray(error) && error[1] instanceof HTMLElement){
error = error[0] + ": " + error[1].outerHTML error = error[0] + ": " + error[1].outerHTML
} }
pageEvents.send("load-song-error", error)
errorMessage(new Error(error).stack) errorMessage(new Error(error).stack)
alert("An error occurred, please refresh") alert("An error occurred, please refresh")
}) })
@ -243,6 +249,7 @@ class LoadSong{
var taikoGame1 = new Controller(song, this.songData, false, 1, this.touchEnabled) var taikoGame1 = new Controller(song, this.songData, false, 1, this.touchEnabled)
var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2, this.touchEnabled) var taikoGame2 = new Controller(this.selectedSong2, this.song2Data, true, 2, this.touchEnabled)
taikoGame1.run(taikoGame2) taikoGame1.run(taikoGame2)
pageEvents.send("load-song-player2", this.selectedSong2)
}else if(event.type === "left" || event.type === "gameend"){ }else if(event.type === "left" || event.type === "gameend"){
this.clean() this.clean()
new SongSelect(false, false, this.touchEnabled) new SongSelect(false, false, this.touchEnabled)
@ -264,6 +271,7 @@ class LoadSong{
}else{ }else{
if(!repeat){ if(!repeat){
assets.sounds["v_sanka"].play() assets.sounds["v_sanka"].play()
pageEvents.send("load-song-unfocused")
} }
setTimeout(() => { setTimeout(() => {
this.startMultiplayer(true) this.startMultiplayer(true)
@ -281,6 +289,7 @@ class LoadSong{
p2.send("leave") p2.send("leave")
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
this.cancelButton.style.pointerEvents = "none" this.cancelButton.style.pointerEvents = "none"
pageEvents.send("load-song-cancel")
} }
clean(){ clean(){
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")

View File

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

View File

@ -92,6 +92,7 @@ var versionDiv = document.getElementById("version")
var versionLink = document.getElementById("version-link") var versionLink = document.getElementById("version-link")
pageEvents.add(versionDiv, ["click", "touchend"], () => { pageEvents.add(versionDiv, ["click", "touchend"], () => {
versionLink.click() versionLink.click()
pageEvents.send("version-link")
}) })
resizeRoot() resizeRoot()
setInterval(resizeRoot, 100) setInterval(resizeRoot, 100)
@ -112,7 +113,7 @@ pageEvents.keyAdd(debugObj, "all", "down", event => {
} }
}) })
var loader = new Loader(() => { var loader = new Loader(songId => {
new Titlescreen() new Titlescreen(songId)
}) })

View File

@ -58,6 +58,7 @@ class P2Connection{
this.open() this.open()
} }
}, 500) }, 500)
pageEvents.send("p2-disconnected")
} }
var addedType = this.allEvents.get("close") var addedType = this.allEvents.get("close")
if(addedType){ if(addedType){
@ -111,6 +112,11 @@ class P2Connection{
break break
case "gameend": case "gameend":
this.otherConnected = false this.otherConnected = false
if(this.session){
pageEvents.send("session-end")
}else if(!this.results){
pageEvents.send("p2-game-end")
}
this.session = false this.session = false
if(this.hashLock){ if(this.hashLock){
this.hash("") this.hash("")
@ -122,6 +128,7 @@ class P2Connection{
for(var i in response.value){ for(var i in response.value){
this.results[i] = response.value[i].toString() this.results[i] = response.value[i].toString()
} }
pageEvents.send("scoresheet-player2", this.results)
break break
case "note": case "note":
this.notes.push(response.value) this.notes.push(response.value)

View File

@ -143,4 +143,7 @@ class PageEvents{
getMouse(){ getMouse(){
return this.lastMouse return this.lastMouse
} }
send(name, detail){
dispatchEvent(new CustomEvent(name, {detail: detail}))
}
} }

View File

@ -60,6 +60,10 @@ class Scoresheet{
} }
}) })
} }
pageEvents.send("scoresheet", {
results: this.results,
multiplayer: multiplayer
})
} }
keyDown(event, code){ keyDown(event, code){
if(!code){ if(!code){

View File

@ -30,9 +30,11 @@ class Session{
}else if(response.type === "songsel"){ }else if(response.type === "songsel"){
p2.clearMessage("users") p2.clearMessage("users")
this.onEnd(false, true) this.onEnd(false, true)
pageEvents.send("session-start")
} }
}) })
p2.send("invite") p2.send("invite")
pageEvents.send("session")
} }
mouseDown(event){ mouseDown(event){
if(event.target === this.sessionInvite){ if(event.target === this.sessionInvite){
@ -50,6 +52,7 @@ class Session{
p2.send("leave") p2.send("leave")
p2.hash("") p2.hash("")
p2.hashLock = false p2.hashLock = false
pageEvents.send("session-cancel")
}else if(!fromP2){ }else if(!fromP2){
return p2.send("songsel") return p2.send("songsel")
} }

View File

@ -1,5 +1,5 @@
class SongSelect{ class SongSelect{
constructor(fromTutorial, fadeIn, touchEnabled){ constructor(fromTutorial, fadeIn, touchEnabled, songId){
this.touchEnabled = touchEnabled this.touchEnabled = touchEnabled
loader.changePage("songselect", false) loader.changePage("songselect", false)
@ -213,23 +213,32 @@ class SongSelect{
this.selectedDiff = 0 this.selectedDiff = 0
assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506) assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506)
if(!assets.customSongs && !fromTutorial && !("selectedSong" in localStorage)){ if(!assets.customSongs && !fromTutorial && !("selectedSong" in localStorage) && !songId){
fromTutorial = touchEnabled ? "about" : "tutorial" fromTutorial = touchEnabled ? "about" : "tutorial"
} }
if(p2.session){ if(p2.session){
fromTutorial = false fromTutorial = false
} }
var songIdIndex = -1
if(fromTutorial){ if(fromTutorial){
this.selectedSong = this.songs.findIndex(song => song.action === fromTutorial) this.selectedSong = this.songs.findIndex(song => song.action === fromTutorial)
this.playBgm(true) this.playBgm(true)
}else{ }else{
if(assets.customSongs){ if(songId){
songIdIndex = this.songs.findIndex(song => song.id === songId)
if(songIdIndex === -1){
this.clearHash()
}
}
if(songIdIndex !== -1){
this.selectedSong = songIdIndex
}else if(assets.customSongs){
this.selectedSong = assets.customSelected this.selectedSong = assets.customSelected
}else if((!p2.session || fadeIn) && "selectedSong" in localStorage){ }else if((!p2.session || fadeIn) && "selectedSong" in localStorage){
this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length - 1) this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length - 1)
} }
assets.sounds["v_songsel"].play() assets.sounds[songIdIndex !== -1 ? "v_diffsel" : "v_songsel"].play()
snd.musicGain.fadeOut() snd.musicGain.fadeOut()
this.playBgm(false) this.playBgm(false)
} }
@ -247,7 +256,7 @@ class SongSelect{
var skipStart = fromTutorial || p2.session var skipStart = fromTutorial || p2.session
this.state = { this.state = {
screen: fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title"), screen: songIdIndex !== -1 ? "difficulty" : (fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title")),
screenMS: this.getMS(), screenMS: this.getMS(),
move: 0, move: 0,
moveMS: 0, moveMS: 0,
@ -302,6 +311,8 @@ class SongSelect{
this.redrawRunning = true this.redrawRunning = true
this.redrawBind = this.redraw.bind(this) this.redrawBind = this.redraw.bind(this)
this.redraw() this.redraw()
pageEvents.send("song-select")
pageEvents.send("song-select-move", this.songs[this.selectedSong])
} }
keyDown(event, code){ keyDown(event, code){
@ -590,6 +601,7 @@ class SongSelect{
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
assets.sounds["v_songsel"].stop() assets.sounds["v_songsel"].stop()
assets.sounds["v_diffsel"].play(0.3) assets.sounds["v_diffsel"].play(0.3)
pageEvents.send("song-select-difficulty", currentSong)
}else if(currentSong.action === "back"){ }else if(currentSong.action === "back"){
this.clean() this.clean()
this.toTitleScreen() this.toTitleScreen()
@ -630,6 +642,8 @@ class SongSelect{
assets.sounds["v_diffsel"].stop() assets.sounds["v_diffsel"].stop()
assets.sounds["se_cancel"].play() assets.sounds["se_cancel"].play()
} }
this.clearHash()
pageEvents.send("song-select-back")
} }
toLoadSong(difficulty, shift, ctrl, touch){ toLoadSong(difficulty, shift, ctrl, touch){
this.clean() this.clean()
@ -728,6 +742,7 @@ class SongSelect{
setTimeout(() => { setTimeout(() => {
new SongSelect("browse", false, this.touchEnabled) new SongSelect("browse", false, this.touchEnabled)
}, 500) }, 500)
pageEvents.send("import-songs-default")
}else{ }else{
this.browse.click() this.browse.click()
} }
@ -952,7 +967,11 @@ class SongSelect{
var elapsed = ms - this.state.moveMS var elapsed = ms - this.state.moveMS
if(this.state.move && ms > this.state.moveMS + resize2 - scrollDelay){ if(this.state.move && ms > this.state.moveMS + resize2 - scrollDelay){
assets.sounds["se_ka"].play() assets.sounds["se_ka"].play()
var previousSelectedSong = this.selectedSong
this.selectedSong = this.mod(this.songs.length, this.selectedSong + this.state.move) this.selectedSong = this.mod(this.songs.length, this.selectedSong + this.state.move)
if(previousSelectedSong !== this.selectedSong){
pageEvents.send("song-select-move", this.songs[this.selectedSong])
}
this.state.move = 0 this.state.move = 0
this.state.locked = 2 this.state.locked = 2
@ -1687,16 +1706,11 @@ class SongSelect{
if("id" in currentSong){ if("id" in currentSong){
var startLoad = this.getMS() var startLoad = this.getMS()
if(loadOnly){ if(loadOnly){
var resolveLoading var currentId = null
this.previewLoading = currentSong.id
}else{ }else{
var currentId = this.previewId
this.previewing = this.selectedSong this.previewing = this.selectedSong
if(this.previewLoading === currentSong.id){
this.previewLoading = null
return
}
} }
var currentId = this.previewId
var songObj = this.previewList.find(song => song && song.id === id) var songObj = this.previewList.find(song => song && song.id === id)
if(songObj){ if(songObj){
@ -1728,14 +1742,9 @@ class SongSelect{
}).then(sound => { }).then(sound => {
if(currentId === this.previewId){ if(currentId === this.previewId){
songObj.preview_sound = sound songObj.preview_sound = sound
if(!loadOnly || !this.previewLoading){ this.preview = sound
this.previewing = this.selectedSong this.previewLoaded(startLoad, songObj.preview_time)
this.preview = sound
this.previewLoaded(startLoad, songObj.preview_time)
}
if(loadOnly){
this.previewLoading = null
}
var oldPreview = this.previewList.shift() var oldPreview = this.previewList.shift()
if(oldPreview){ if(oldPreview){
oldPreview.preview_sound.clean() oldPreview.preview_sound.clean()
@ -1880,11 +1889,18 @@ class SongSelect{
return title return title
} }
clearHash(){
if(location.hash.toLowerCase().startsWith("#song=")){
p2.hash("")
}
}
getMS(){ getMS(){
return Date.now() return Date.now()
} }
clean(){ clean(){
this.clearHash()
this.draw.clean() this.draw.clean()
this.songTitleCache.clean() this.songTitleCache.clean()
this.selectTextCache.clean() this.selectTextCache.clean()

View File

@ -65,16 +65,16 @@
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "流れてくる音符がワクに重なったらバチで太鼓をたたこう!",
"For red notes, hit the face of the drum (%s or %s)...", "赤い音符は面をたたこう(%sまたは%s",
"...and for blue notes, hit the rim! (%s or %s)", "青い音符はフチをたたこう(%sまたは%s",
"USB controllers are also supported!" "USBコントローラがサポートされています!"
], ],
otherControls: "Other controls", otherControls: "他のコントロール",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%sはゲームを一時停止します",
"%s while selecting difficulty \u2014 enable autoplay mode", "むずかしさをえらぶしながら%sキーを押しながらオートモードを有効",
"%s while selecting difficulty \u2014 enable 2P mode" "むずかしさをえらぶしながら%sキーを押しながらネットプレイモードを有効"
], ],
ok: "OK" ok: "OK"
} }
@ -166,8 +166,8 @@ function StringsEn(){
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "When a note overlaps the frame, that is your cue to hit the drum!",
"For red notes, hit the face of the drum (%s or %s)...", "For red notes, hit the surface of the drum (%s or %s)...",
"...and for blue notes, hit the rim! (%s or %s)", "...and for blue notes, hit the rim! (%s or %s)",
"USB controllers are also supported!" "USB controllers are also supported!"
], ],
@ -267,16 +267,16 @@ function StringsCn(){
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "当流动的音符将与框框重叠时就用鼓棒敲打太鼓吧",
"For red notes, hit the face of the drum (%s or %s)...", "遇到红色音符要敲打鼓面(%s或%s",
"...and for blue notes, hit the rim! (%s or %s)", "遇到蓝色音符则敲打鼓边(%s或%s",
"USB controllers are also supported!" "USB控制器也支持!"
], ],
otherControls: "Other controls", otherControls: "其他控制",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%s暂停游戏",
"%s while selecting difficulty \u2014 enable autoplay mode", "选择难度时按住%s以启用自动模式",
"%s while selecting difficulty \u2014 enable 2P mode" "选择难度时按住%s以启用网络对战模式"
], ],
ok: "确定" ok: "确定"
} }
@ -368,16 +368,16 @@ function StringsTw(){
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "當流動的音符將與框框重疊時就用鼓棒敲打太鼓吧",
"For red notes, hit the face of the drum (%s or %s)...", "遇到紅色音符要敲打鼓面(%s或%s",
"...and for blue notes, hit the rim! (%s or %s)", "遇到藍色音符則敲打鼓邊(%s或%s",
"USB controllers are also supported!" "USB控制器也支持!"
], ],
otherControls: "Other controls", otherControls: "其他控制",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%s暫停遊戲",
"%s while selecting difficulty \u2014 enable autoplay mode", "選擇難度時按住%s以啟用自動模式",
"%s while selecting difficulty \u2014 enable 2P mode" "選擇難度時按住%s以啟用網上對打模式"
], ],
ok: "確定" ok: "確定"
} }
@ -469,16 +469,16 @@ function StringsKo(){
this.tutorial = { this.tutorial = {
basics: [ basics: [
"Hit the drum when the notes reach the taiko!", "이동하는 음표가 테두리와 겹쳐졌을 때 북채로 태고를 두드리자!",
"For red notes, hit the face of the drum (%s or %s)...", "빨간 음표는 면을 두드리자 (%s 또는 %s)",
"...and for blue notes, hit the rim! (%s or %s)", "파란 음표는 테를 두드리자 (%s 또는 %s)",
"USB controllers are also supported!" "USB 컨트롤러도 지원됩니다!"
], ],
otherControls: "Other controls", otherControls: "기타 컨트롤",
otherTutorial: [ otherTutorial: [
"%s \u2014 pause game", "%s \u2014 게임을 일시 중지합니다",
"%s while selecting difficulty \u2014 enable autoplay mode", "난이도 선택 동안 %s 홀드 \u2014 오토 모드 활성화",
"%s while selecting difficulty \u2014 enable 2P mode" "난이도 선택 동안 %s 홀드 \u2014 넷 플레이 모드 활성화"
], ],
ok: "확인" ok: "확인"
} }

View File

@ -1,38 +1,47 @@
class Titlescreen{ class Titlescreen{
constructor(){ constructor(songId){
loader.changePage("titlescreen", false) this.songId = songId
this.titleScreen = document.getElementById("title-screen") if(!songId){
this.proceed = document.getElementById("title-proceed") loader.changePage("titlescreen", false)
this.langDropdown = document.getElementById("lang-dropdown")
this.langId = document.getElementById("lang-id") this.titleScreen = document.getElementById("title-screen")
this.disclaimerText = document.getElementById("title-disclaimer-text") this.proceed = document.getElementById("title-proceed")
this.disclaimerCopyright = document.getElementById("title-disclaimer-copyright") this.langDropdown = document.getElementById("lang-dropdown")
document.getElementById("globe-path").setAttribute("d", vectors.globe) this.langId = document.getElementById("lang-id")
this.disclaimerText = document.getElementById("title-disclaimer-text")
this.logo = new Logo() this.disclaimerCopyright = document.getElementById("title-disclaimer-copyright")
document.getElementById("globe-path").setAttribute("d", vectors.globe)
this.logo = new Logo()
}
this.lang = this.getLang() this.lang = this.getLang()
this.setLang(allStrings[this.lang]) this.setLang(allStrings[this.lang])
this.addLangs()
pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this)) if(songId){
pageEvents.add(this.titleScreen, ["mousedown", "touchstart"], this.onPressed.bind(this)) this.goNext()
pageEvents.add(this.langDropdown, "change", this.langChange.bind(this)) }else{
this.addLangs()
assets.sounds["v_title"].play()
this.gamepad = new Gamepad({ pageEvents.keyAdd(this, "all", "down", this.keyDown.bind(this))
"13": ["a", "b", "x", "y", "start", "ls", "rs"] pageEvents.add(this.titleScreen, ["mousedown", "touchstart"], this.onPressed.bind(this))
}, pressed => { pageEvents.add(this.langDropdown, "change", this.langChange.bind(this))
if(pressed){
this.onPressed() assets.sounds["v_title"].play()
} this.gamepad = new Gamepad({
}) "13": ["a", "b", "x", "y", "start", "ls", "rs"]
if(p2.session){ }, pressed => {
pageEvents.add(p2, "message", response => { if(pressed){
if(response.type === "songsel"){ this.onPressed()
this.goNext(true)
} }
}) })
if(p2.session){
pageEvents.add(p2, "message", response => {
if(response.type === "songsel"){
this.goNext(true)
}
})
}
pageEvents.send("title-screen")
} }
} }
@ -66,13 +75,16 @@ class Titlescreen{
if(p2.session && !fromP2){ if(p2.session && !fromP2){
p2.send("songsel") p2.send("songsel")
}else if(fromP2 || this.touched || localStorage.getItem("tutorial") === "true"){ }else if(fromP2 || this.touched || localStorage.getItem("tutorial") === "true"){
if(this.touched){
localStorage.setItem("tutorial", "true")
}
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")
setTimeout(() => { setTimeout(() => {
new SongSelect(false, false, this.touched) new SongSelect(false, false, this.touched, this.songId)
}, 500) }, 500)
}else{ }else{
setTimeout(() => { setTimeout(() => {
new Tutorial() new Tutorial(false, this.songId)
}, 500) }, 500)
} }
} }
@ -96,6 +108,16 @@ class Titlescreen{
} }
setLang(lang){ setLang(lang){
strings = lang strings = lang
loader.screen.style.fontFamily = strings.font
loader.screen.style.fontWeight = strings.font === "Microsoft YaHei, sans-serif" ? "bold" : ""
if(failedTests.length !== 0){
showUnsupported(strings)
}
if(this.songId){
return
}
this.proceed.innerText = strings.titleProceed this.proceed.innerText = strings.titleProceed
this.proceed.setAttribute("alt", strings.titleProceed) this.proceed.setAttribute("alt", strings.titleProceed)
this.langId.innerText = strings.id.toUpperCase() this.langId.innerText = strings.id.toUpperCase()
@ -106,12 +128,8 @@ class Titlescreen{
this.disclaimerCopyright.innerText = strings.titleCopyright this.disclaimerCopyright.innerText = strings.titleCopyright
this.disclaimerCopyright.setAttribute("alt", strings.titleCopyright) this.disclaimerCopyright.setAttribute("alt", strings.titleCopyright)
loader.screen.style.fontFamily = strings.font
loader.screen.style.fontWeight = strings.font === "Microsoft YaHei, sans-serif" ? "bold" : ""
if(failedTests.length !== 0){
showUnsupported(strings)
}
this.logo.updateSubtitle() this.logo.updateSubtitle()
pageEvents.send("language-change", lang.id)
} }
addLangs(){ addLangs(){
for(var i in allStrings){ for(var i in allStrings){

View File

@ -1,6 +1,7 @@
class Tutorial{ class Tutorial{
constructor(fromSongSel){ constructor(fromSongSel, songId){
this.fromSongSel = fromSongSel this.fromSongSel = fromSongSel
this.songId = songId
loader.changePage("tutorial", true) loader.changePage("tutorial", true)
assets.sounds["bgm_setsume"].playLoop(0.1, false, 0, 1.054, 16.054) assets.sounds["bgm_setsume"].playLoop(0.1, false, 0, 1.054, 16.054)
this.endButton = document.getElementById("tutorial-end-button") this.endButton = document.getElementById("tutorial-end-button")
@ -47,6 +48,7 @@ class Tutorial{
this.gamepad = new Gamepad({ this.gamepad = new Gamepad({
"confirm": ["start", "b", "ls", "rs"] "confirm": ["start", "b", "ls", "rs"]
}, this.onEnd.bind(this)) }, this.onEnd.bind(this))
pageEvents.send("tutorial")
} }
insertText(text, parent){ insertText(text, parent){
parent.appendChild(document.createTextNode(text)) parent.appendChild(document.createTextNode(text))
@ -66,7 +68,7 @@ class Tutorial{
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
localStorage.setItem("tutorial", "true") localStorage.setItem("tutorial", "true")
setTimeout(() => { setTimeout(() => {
new SongSelect(this.fromSongSel ? "tutorial" : false, false, touched) new SongSelect(this.fromSongSel ? "tutorial" : false, false, touched, this.songId)
}, 500) }, 500)
} }
clean(){ clean(){

View File

@ -750,7 +750,7 @@
comboScale = this.draw.fade(scoreMS / 100) comboScale = this.draw.fade(scoreMS / 100)
} }
var glyphW = 51 var glyphW = 51
var glyphH = 64 var glyphH = 65
var letterSpacing = (comboText.length >= 4 ? 38 : 42) * mul var letterSpacing = (comboText.length >= 4 ? 38 : 42) * mul
var orange = comboCount >= 100 ? "1" : "0" var orange = comboCount >= 100 ? "1" : "0"
@ -1374,8 +1374,8 @@
fillComboCache(){ fillComboCache(){
var fontSize = 58 var fontSize = 58
var letterSpacing = fontSize * 0.67 var letterSpacing = fontSize * 0.67
var glyphW = 50 var glyphW = 51
var glyphH = 64 var glyphH = 65
var textX = 5 var textX = 5
var textY = 5 var textY = 5
var letterBorder = fontSize * 0.15 var letterBorder = fontSize * 0.15
@ -1631,12 +1631,17 @@
switch(pos){ switch(pos){
case 1: case 1:
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
return this.controller.restartSong() this.controller.restartSong()
pageEvents.send("pause-restart")
break
case 2: case 2:
assets.sounds["se_don"].play() assets.sounds["se_don"].play()
return this.controller.songSelection() this.controller.songSelection()
pageEvents.send("pause-song-select")
break
default: default:
return this.controller.togglePause() this.controller.togglePause()
break
} }
} }
onmousedown(event){ onmousedown(event){