Merge pull request #69 from LoveEevee/songselect-add-sub-titles

SongSelect: Add sub-titles
This commit is contained in:
Bui 2018-11-10 21:09:53 +00:00 committed by GitHub
commit 76108d47d6
6 changed files with 106 additions and 24 deletions

View File

@ -10,7 +10,7 @@ Still in developement. Works best with Chrome.
Create a SQLite databse named `taiko.db` with the following schema: Create a SQLite databse named `taiko.db` with the following schema:
CREATE TABLE "songs" ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `title_en` TEXT, `easy` INTEGER, `normal` INTEGER, `hard` INTEGER, `oni` INTEGER, `ura` INTEGER, `enabled` INTEGER NOT NULL, `category` INTEGER, `type` TEXT , `offset` REAL NOT NULL ) CREATE TABLE "songs" ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `title_en` TEXT, `subtitle` TEXT, `subtitle_en` TEXT, `easy` INTEGER, `normal` INTEGER, `hard` INTEGER, `oni` INTEGER, `ura` INTEGER, `enabled` INTEGER NOT NULL, `category` INTEGER, `type` TEXT , `offset` REAL NOT NULL )
CREATE TABLE "categories" ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `title_en` TEXT NOT NULL ) CREATE TABLE "categories" ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `title` TEXT NOT NULL, `title_en` TEXT NOT NULL )
When inserting song rows, leave any difficulty columns as NULL if you don't intend to add notecharts for them. When inserting song rows, leave any difficulty columns as NULL if you don't intend to add notecharts for them.

10
app.py
View File

@ -161,22 +161,24 @@ def route_api_songs():
songs_out = [] songs_out = []
for song in songs: for song in songs:
song_id = song[0] song_id = song[0]
song_type = song[10] song_type = song[12]
preview = get_preview(song_id, song_type) preview = get_preview(song_id, song_type)
category_out = categories[song[9]] if song[9] in categories else def_category category_out = categories[song[11]] if song[11] in categories else def_category
songs_out.append({ songs_out.append({
'id': song_id, 'id': song_id,
'title': song[1], 'title': song[1],
'title_en': song[2], 'title_en': song[2],
'subtitle': song[3],
'subtitle_en': song[4],
'stars': [ 'stars': [
song[3], song[4], song[5], song[6], song[7] song[5], song[6], song[7], song[8], song[9]
], ],
'preview': preview, 'preview': preview,
'category': category_out['title'], 'category': category_out['title'],
'category_en': category_out['title_en'], 'category_en': category_out['title_en'],
'type': song_type, 'type': song_type,
'offset': song[11] 'offset': song[13]
}) })
return jsonify(songs_out) return jsonify(songs_out)

View File

@ -50,8 +50,9 @@
comma: /[,.]/, comma: /[,.]/,
ideographicComma: /[、。]/, ideographicComma: /[、。]/,
apostrophe: /[']/, apostrophe: /[']/,
degree: /[゚°]/,
brackets: /[\(\))「」『』]/, brackets: /[\(\))「」『』]/,
tilde: /[\-~]/, tilde: /[\-~]/,
tall: /[bdfh-l-t0-9-9♪]/, tall: /[bdfh-l-t0-9-9♪]/,
uppercase: /[A-Z-]/, uppercase: /[A-Z-]/,
lowercase: /[a-z-z・]/, lowercase: /[a-z-z・]/,
@ -283,15 +284,20 @@
}else if(symbol === "ー"){ }else if(symbol === "ー"){
// Long-vowel mark // Long-vowel mark
drawn.push({realText: symbol, svg: this.longVowelMark, x: -4, y: 5, h: 33, scale: [mul, mul]}) drawn.push({realText: symbol, svg: this.longVowelMark, x: -4, y: 5, h: 33, scale: [mul, mul]})
}else if(symbol === "∀"){
drawn.push({text: symbol, x: 0, y: 3, h: 39, rotate: true})
}else if(r.comma.test(symbol)){ }else if(r.comma.test(symbol)){
// Comma, full stop // Comma, full stop
drawn.push({text: symbol, x: 16, y: -7, h: 0, scale: [1.2, 0.7]}) drawn.push({text: symbol, x: 13, y: -9, h: 13, scale: [1.2, 0.7]})
}else if(r.ideographicComma.test(symbol)){ }else if(r.ideographicComma.test(symbol)){
// Ideographic comma, full stop // Ideographic comma, full stop
drawn.push({text: symbol, x: 16, y: -16, h: 18}) drawn.push({text: symbol, x: 16, y: -16, h: 18})
}else if(r.apostrophe.test(symbol)){ }else if(r.apostrophe.test(symbol)){
// Apostrophe // Apostrophe
drawn.push({realText: symbol, text: ",", x: 20, y: -39, h: 0, scale: [1.2, 0.7]}) drawn.push({realText: symbol, text: ",", x: 20, y: -39, h: 0, scale: [1.2, 0.7]})
}else if(r.degree.test(symbol)){
// Degree
drawn.push({text: symbol, x: 16, y: 3, h: 18})
}else if(r.brackets.test(symbol)){ }else if(r.brackets.test(symbol)){
// Rotated brackets // Rotated brackets
drawn.push({text: symbol, x: 0, y: -5, h: 25, rotate: true}) drawn.push({text: symbol, x: 0, y: -5, h: 25, rotate: true})
@ -359,6 +365,10 @@
} }
} }
if(config.align === "bottom"){
drawn.reverse()
}
var drawnHeight = 0 var drawnHeight = 0
for(let symbol of drawn){ for(let symbol of drawn){
if(config.letterSpacing){ if(config.letterSpacing){
@ -382,9 +392,17 @@
style.transform = "" style.transform = ""
} }
var scaling = 1
if(config.height && drawnHeight > config.height){ if(config.height && drawnHeight > config.height){
var scaling = config.height / drawnHeight if(config.align === "bottom"){
ctx.scale(1, scaling) scaling = Math.max(0.6, config.height / drawnHeight)
ctx.translate(40 * mul, 0)
ctx.scale(scaling, config.height / drawnHeight)
ctx.translate(-40 * mul, 0)
}else{
scaling = config.height / drawnHeight
ctx.scale(1, scaling)
}
if(config.selectable){ if(config.selectable){
style.transform = "scale(1, " + scaling + ")" style.transform = "scale(1, " + scaling + ")"
style.top = (config.y + (config.height - drawnHeight) / 2 - 15 / 2 * scaling) * scale + "px" style.top = (config.y + (config.height - drawnHeight) / 2 - 15 / 2 * scaling) * scale + "px"
@ -407,12 +425,19 @@
if(action === "stroke"){ if(action === "stroke"){
ctx.strokeStyle = config.outline ctx.strokeStyle = config.outline
ctx.lineWidth = config.outlineSize * mul ctx.lineWidth = config.outlineSize * mul
if(config.align === "bottom"){
ctx.lineWidth /= scaling
}
ctx.lineJoin = "round" ctx.lineJoin = "round"
ctx.miterLimit = 1 ctx.miterLimit = 1
}else if(action === "fill"){ }else if(action === "fill"){
ctx.fillStyle = config.fill ctx.fillStyle = config.fill
} }
var offsetY = 0 if(config.align === "bottom"){
var offsetY = drawnHeight > config.height ? drawnHeight : config.height
}else{
var offsetY = 0
}
for(let symbol of drawn){ for(let symbol of drawn){
var saved = false var saved = false
@ -421,7 +446,10 @@
currentX += 20 * mul currentX += 20 * mul
} }
var currentY = offsetY + symbol.y * mul var currentY = offsetY + symbol.y * mul
offsetY += symbol.h * mul if(config.align === "bottom"){
currentY -= symbol.h * mul
}
offsetY = offsetY + symbol.h * mul * (config.align === "bottom" ? -1 : 1)
if(action === "selectable"){ if(action === "selectable"){
let div = document.createElement("div") let div = document.createElement("div")
div.classList.add("stroke-sub") div.classList.add("stroke-sub")

View File

@ -85,7 +85,8 @@ pageEvents.add(versionDiv, ["click", "touchend"], () => {
resizeRoot() resizeRoot()
setInterval(resizeRoot, 100) setInterval(resizeRoot, 100)
pageEvents.keyAdd(debugObj, "all", "down", event => { pageEvents.keyAdd(debugObj, "all", "down", event => {
if(event.keyCode === 186 && event.ctrlKey && event.shiftKey && !event.altKey){ if((event.keyCode === 186 || event.keyCode === 59) && event.ctrlKey && event.shiftKey && !event.altKey){
// Semicolon
if(debugObj.state === "open"){ if(debugObj.state === "open"){
debugObj.debug.minimise() debugObj.debug.minimise()
}else if(debugObj.state === "minimised"){ }else if(debugObj.state === "minimised"){
@ -95,6 +96,7 @@ pageEvents.keyAdd(debugObj, "all", "down", event => {
} }
} }
if(event.keyCode === 82 && debugObj.debug && debugObj.controller){ if(event.keyCode === 82 && debugObj.debug && debugObj.controller){
// R
debugObj.controller.restartSong() debugObj.controller.restartSong()
} }
}) })

View File

@ -91,6 +91,7 @@ class SongSelect{
this.songs.push({ this.songs.push({
id: song.id, id: song.id,
title: song.title, title: song.title,
subtitle: song.subtitle,
skin: song.category in this.songSkin ? this.songSkin[song.category] : this.songSkin.default, skin: song.category in this.songSkin ? this.songSkin[song.category] : this.songSkin.default,
stars: song.stars, stars: song.stars,
category: song.category, category: song.category,
@ -218,6 +219,8 @@ class SongSelect{
this.songSelect.style.backgroundImage = "url('" + assets.image["bg_genre_" + sort].src + "')" this.songSelect.style.backgroundImage = "url('" + assets.image["bg_genre_" + sort].src + "')"
this.previewId = 0 this.previewId = 0
this.previewList = Array(5)
var skipStart = fromTutorial || p2.session var skipStart = fromTutorial || p2.session
this.state = { this.state = {
screen: fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title"), screen: fadeIn ? "titleFadeIn" : (skipStart ? "song" : "title"),
@ -708,7 +711,11 @@ class SongSelect{
this.canvas.style.height = (winH / this.pixelRatio) + "px" this.canvas.style.height = (winH / this.pixelRatio) + "px"
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2 var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
this.songTitleCache.resize((this.songAsset.width - borders + 1) * this.songs.length, (this.songAsset.height - borders + 1) * 2, ratio + 0.5) this.songTitleCache.resize(
(this.songAsset.width - borders + 1) * this.songs.length,
(this.songAsset.height - borders + 1) * 3,
ratio + 0.5
)
this.selectTextCache.resize((280 + 53 + 60 + 1) * 2, this.songAsset.marginTop + 15, ratio + 0.5) this.selectTextCache.resize((280 + 53 + 60 + 1) * 2, this.songAsset.marginTop + 15, ratio + 0.5)
@ -1285,30 +1292,55 @@ class SongSelect{
drawDifficulty(ctx, i, currentUra) drawDifficulty(ctx, i, currentUra)
} }
} }
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
var textW = this.songAsset.width - borders
var textH = this.songAsset.height - borders
var textX = Math.max(w - 37 - textW / 2, w / 2 - textW / 2)
var textY = opened * 12 + (1 - opened) * 7
if(currentSong.subtitle){
this.songTitleCache.get({
ctx: ctx,
x: x + textX - textW,
y: y + textY,
w: textW,
h: textH,
id: currentSong.subtitle,
}, ctx => {
this.draw.verticalText({
ctx: ctx,
text: currentSong.subtitle,
x: textW / 2,
y: 7,
width: textW,
height: textH - 35,
fill: "#fff",
outline: "#000",
outlineSize: 14,
fontSize: 28,
fontFamily: this.font,
align: "bottom"
})
})
}
if(!songSel && currentSong.stars[4]){ if(!songSel && currentSong.stars[4]){
var fade = ((ms - this.state.screenMS) % 1200) / 1200 var fade = ((ms - this.state.screenMS) % 1200) / 1200
var _x = x + 402 + 4 * 100 + fade * 25 var _x = x + 402 + 4 * 100 + fade * 25
var _y = y + 258 var _y = y + 258
ctx.save() ctx.fillStyle = "rgba(0, 0, 0, " + 0.2 * this.draw.easeInOut(1 - fade) + ")"
ctx.globalAlpha = this.draw.easeInOut(1 - fade)
ctx.fillStyle = "#e0be28"
ctx.beginPath() ctx.beginPath()
ctx.moveTo(_x - 35, _y - 25) ctx.moveTo(_x - 35, _y - 25)
ctx.lineTo(_x - 10, _y) ctx.lineTo(_x - 10, _y)
ctx.lineTo(_x - 35, _y + 25) ctx.lineTo(_x - 35, _y + 25)
ctx.fill() ctx.fill()
ctx.restore()
} }
ctx.globalAlpha = 1 - Math.max(0, opened - 0.5) * 2 ctx.globalAlpha = 1 - Math.max(0, opened - 0.5) * 2
ctx.fillStyle = selectedSkin.background ctx.fillStyle = selectedSkin.background
ctx.fillRect(x, y, w, h) ctx.fillRect(x, y, w, h)
ctx.globalAlpha = 1 ctx.globalAlpha = 1
var borders = (this.songAsset.border + this.songAsset.innerBorder) * 2
var textW = this.songAsset.width - borders
var textH = this.songAsset.height - borders
var textX = Math.max(w - 37 - textW / 2, w / 2 - textW / 2)
var textY = opened * 12 + (1 - opened) * 7
this.songTitleCache.get({ this.songTitleCache.get({
ctx: ctx, ctx: ctx,
x: x + textX, x: x + textX,
@ -1551,15 +1583,17 @@ class SongSelect{
var currentId = this.previewId var currentId = this.previewId
this.previewing = this.selectedSong this.previewing = this.selectedSong
} }
var songObj = assets.songs.find(song => song.id == id) var songObj = this.previewList.find(song => song && song.id === id)
if(songObj.preview_sound){ if(songObj){
if(!loadOnly){ if(!loadOnly){
this.preview = songObj.preview_sound this.preview = songObj.preview_sound
this.preview.gain = snd.previewGain this.preview.gain = snd.previewGain
this.previewLoaded(startLoad, songObj.preview_time) this.previewLoaded(startLoad, songObj.preview_time)
} }
}else{ }else{
songObj = {id: id}
var previewFilename = prvTime > 0.1 ? "/preview.mp3" : "/main.mp3" var previewFilename = prvTime > 0.1 ? "/preview.mp3" : "/main.mp3"
var loadPreview = previewFilename => { var loadPreview = previewFilename => {
@ -1575,6 +1609,14 @@ class SongSelect{
songObj.preview_sound = sound songObj.preview_sound = sound
this.preview = sound this.preview = sound
this.previewLoaded(startLoad, songObj.preview_time) this.previewLoaded(startLoad, songObj.preview_time)
var oldPreview = this.previewList.shift()
if(oldPreview){
oldPreview.preview_sound.clean()
}
this.previewList.push(songObj)
}else{
sound.clean()
} }
}) })
} }
@ -1714,6 +1756,11 @@ class SongSelect{
} }
this.redrawRunning = false this.redrawRunning = false
this.endPreview() this.endPreview()
this.previewList.forEach(song => {
if(song){
song.preview_sound.clean()
}
})
pageEvents.keyRemove(this, "all") pageEvents.keyRemove(this, "all")
pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"]) pageEvents.remove(loader.screen, ["mousemove", "mouseleave", "mousedown", "touchstart"])
pageEvents.remove(p2, "message") pageEvents.remove(p2, "message")

View File

@ -206,4 +206,7 @@ class Sound{
} }
} }
} }
clean(){
delete this.buffer
}
} }