Add .tja file support

This commit is contained in:
LoveEevee
2018-10-11 01:13:24 +03:00
parent 9b2c740b9d
commit 39655fc534
11 changed files with 395 additions and 70 deletions

View File

@@ -8,8 +8,13 @@ class Controller{
this.snd = this.multiplayer ? "_p" + this.multiplayer : ""
var backgroundURL = "/songs/" + this.selectedSong.folder + "/bg.png"
var songParser = new ParseSong(songData)
this.parsedSongData = songParser.getData()
if(selectedSong.type === "tja"){
this.parsedSongData = new ParseTja(songData, selectedSong.difficulty, selectedSong.offset)
}else{
this.parsedSongData = new ParseOsu(songData, selectedSong.offset)
}
this.offset = this.parsedSongData.soundOffset
assets.songs.forEach(song => {
if(song.id == this.selectedSong.folder){
@@ -168,9 +173,6 @@ class Controller{
getBindings(){
return this.keyboard.getBindings()
}
getSongData(){
return this.game.getSongData()
}
getElapsedTime(){
return this.game.elapsedTime
}

View File

@@ -49,7 +49,6 @@ class Game{
update(){
// Main operations
this.updateTime()
this.checkTiming()
this.updateCirclesStatus()
this.checkPlays()
// Event operations
@@ -277,6 +276,7 @@ class Game{
var started = this.fadeOutStarted
if(started){
var ms = this.elapsedTime
var musicDuration = this.controller.mainAsset.duration * 1000 - this.controller.offset
if(this.musicFadeOut === 0){
if(this.controller.multiplayer === 1){
p2.send("gameresults", this.getGlobalScore())
@@ -286,10 +286,10 @@ class Game{
this.controller.gameEnded()
p2.send("gameend")
this.musicFadeOut++
}else if(this.musicFadeOut === 2 && (ms >= started + 8600 && ms >= this.controller.mainAsset.duration * 1000 + 250)){
}else if(this.musicFadeOut === 2 && (ms >= started + 8600 && ms >= musicDuration + 250)){
this.controller.displayResults()
this.musicFadeOut++
}else if(this.musicFadeOut === 3 && (ms >= started + 9600 && ms >= this.controller.mainAsset.duration * 1000 + 1250)){
}else if(this.musicFadeOut === 3 && (ms >= started + 9600 && ms >= musicDuration + 1250)){
this.controller.clean()
if(this.controller.scoresheet){
this.controller.scoresheet.startRedraw()
@@ -297,16 +297,9 @@ class Game{
}
}
}
checkTiming(){
if(this.songData.timingPoints[this.currentTimingPoint + 1]){
if(this.elapsedTime >= this.songData.timingPoints[this.currentTimingPoint + 1].start){
this.currentTimingPoint++
}
}
}
playMainMusic(){
var ms = this.elapsedTime
if(!this.mainMusicPlaying && (!this.fadeOutStarted || ms<this.fadeOutStarted + 1600)){
var ms = this.elapsedTime + this.controller.offset
if(!this.mainMusicPlaying && (!this.fadeOutStarted || ms < this.fadeOutStarted + 1600)){
if(this.controller.multiplayer !== 2){
this.mainAsset.play((ms < 0 ? -ms : 0) / 1000, false, Math.max(0, ms / 1000))
}
@@ -362,9 +355,6 @@ class Game{
getCircles(){
return this.songData.circles
}
getSongData(){
return this.songData
}
updateCurrentCircle(){
this.currentCircle++
}

View File

@@ -40,7 +40,7 @@ class loadSong{
}, reject)
}
}))
promises.push(loader.ajax(this.getOsuPath(this.selectedSong)).then(data => {
promises.push(loader.ajax(this.getSongPath(this.selectedSong)).then(data => {
this.songData = data.replace(/\0/g, "").split("\n")
}))
Promise.all(promises).then(() => {
@@ -50,8 +50,13 @@ class loadSong{
alert("An error occurred, please refresh")
})
}
getOsuPath(selectedSong){
return "/songs/" + selectedSong.folder + "/" + selectedSong.difficulty + ".osu"
getSongPath(selectedSong){
var directory = "/songs/" + selectedSong.folder + "/"
if(selectedSong.type === "tja"){
return directory + "main.tja"
}else{
return directory + selectedSong.difficulty + ".osu"
}
}
setupMultiplayer(){
if(this.multiplayer){

View File

@@ -1,5 +1,5 @@
class ParseSong{
constructor(fileContent){
class ParseOsu{
constructor(fileContent, offset){
this.osu = {
OFFSET: 0,
MSPERBEAT: 1,
@@ -36,11 +36,13 @@ class ParseSong{
}
this.data = []
for(let line of fileContent){
line = line.trim().replace(/\/\/.*/, "")
line = line.replace(/\/\/.*/, "").trim()
if(line !== ""){
this.data.push(line)
}
}
this.offset = (offset || 0) * -1000
this.soundOffset = 0
this.beatInfo = {
beatInterval: 0,
lastBeatInterval: 0,
@@ -126,7 +128,7 @@ class ParseSong{
this.difficulty.lastMultiplier = sliderMultiplier
}
timingPoints.push({
start: start,
start: start + this.offset,
sliderMultiplier: sliderMultiplier,
measure: parseInt(values[this.osu.METER]),
gogoTime: parseInt(values[this.osu.KIAIMODE])
@@ -139,20 +141,18 @@ class ParseSong{
var measureNumber = 0
for(var i = 0; i<this.timingPoints.length; i++){
if(this.timingPoints[i + 1]){
var limit = this.timingPoints[i + 1].start
var limit = this.timingPoints[i + 1].start - this.offset
}else{
var limit = this.circles[this.circles.length - 1].getMS()
var limit = this.circles[this.circles.length - 1].getMS() - this.offset
}
for(var j = this.timingPoints[i].start; j <= limit; j += this.beatInfo.beatInterval){
measures.push({
ms: j,
nb: measureNumber,
speed: this.timingPoints[i].sliderMultiplier
})
measureNumber++
if(measureNumber === this.timingPoints[i].measure + 1){
measureNumber = 0
for(var start = this.timingPoints[i].start; start <= limit; start += this.beatInfo.beatInterval){
if(measureNumber === 0){
measures.push({
ms: start + this.offset,
speed: this.timingPoints[i].sliderMultiplier
})
}
measureNumber = (measureNumber + 1) % (this.timingPoints[i].measure + 1)
}
}
return measures
@@ -242,9 +242,14 @@ class ParseSong{
var hitSound = parseInt(values[this.osu.HITSOUND])
var beatLength = speed
var lastMultiplier = this.difficulty.lastMultiplier
if(circleID === 1 && start + this.offset < 0){
var offset = start + this.offset
this.soundOffset = offset
this.offset -= offset
}
for(var j = 0; j < this.timingPoints.length; j++){
if(this.timingPoints[j].start > start){
if(this.timingPoints[j].start - this.offset > start){
break
}
speed = this.timingPoints[j].sliderMultiplier
@@ -258,11 +263,11 @@ class ParseSong{
var requiredHits = Math.floor(Math.max(1, (endTime - start) / 1000 * hitMultiplier))
circles.push(new Circle({
id: circleID,
start: start,
start: start + this.offset,
type: "balloon",
txt: "ふうせん",
speed: speed,
endTime: endTime,
endTime: endTime + this.offset,
requiredHits: requiredHits,
gogoTime: gogoTime
}))
@@ -284,11 +289,11 @@ class ParseSong{
}
circles.push(new Circle({
id: circleID,
start: start,
start: start + this.offset,
type: type,
txt: txt,
speed: speed,
endTime: endTime,
endTime: endTime + this.offset,
gogoTime: gogoTime
}))
@@ -318,7 +323,7 @@ class ParseSong{
if(!emptyValue){
circles.push(new Circle({
id: circleID,
start: start,
start: start + this.offset,
type: type,
txt: txt,
speed: speed,
@@ -334,16 +339,4 @@ class ParseSong{
}
return circles
}
getData(){
return {
generalInfo: this.generalInfo,
metaData: this.metadata,
editor: this.editor,
beatInfo: this.beatInfo,
difficulty: this.difficulty,
timingPoints: this.timingPoints,
circles: this.circles,
measures: this.measures
}
}
}

330
public/src/js/parsetja.js Normal file
View File

@@ -0,0 +1,330 @@
class ParseTja{
constructor(file, difficulty, offset){
this.data = []
for(let line of file){
line = line.replace(/\/\/.*/, "").trim()
if(line !== ""){
this.data.push(line)
}
}
this.difficulty = difficulty
this.offset = (offset || 0) * -1000
this.soundOffset = 0
this.noteTypes = [
{name: false, txt: false},
{name: "don", txt: "ドン"},
{name: "ka", txt: "カッ"},
{name: "daiDon", txt: "ドン(大)"},
{name: "daiKa", txt: "カッ(大)"},
{name: "drumroll", txt: "連打ーっ!!"},
{name: "daiDrumroll", txt: "連打(大)ーっ!!"},
{name: "balloon", txt: "ふうせん"},
{name: false, txt: false},
{name: "balloon", txt: "ふうせん"}
]
this.courseTypes = ["easy", "normal", "hard", "oni"]
this.metadata = this.parseMetadata()
this.measures = []
this.beatInfo = {}
this.circles = this.parseCircles()
}
parseMetadata(){
var metaNumbers = ["bpm", "offset"]
var inSong = false
var courses = {}
var currentCourse = {}
var courseName = this.difficulty
for(var lineNum = 0; lineNum < this.data.length; lineNum++){
var line = this.data[lineNum]
if(line.slice(0, 1) === "#"){
var name = line.slice(1).toLowerCase()
if(name === "start" && !inSong){
inSong = true
for(var name in currentCourse){
if(!(courseName in courses)){
courses[courseName] = {}
}
courses[courseName][name] = currentCourse[name]
}
courses[courseName].start = lineNum + 1
courses[courseName].end = this.data.length
}else if(name === "end" && inSong){
inSong = false
courses[courseName].end = lineNum
}
}else if(!inSong){
if(line.indexOf(":") > 0){
var [name, value] = this.split(line, ":")
name = name.toLowerCase().trim()
value = value.trim()
if(name === "course"){
if(value in this.courseTypes){
courseName = this.courseTypes[value]
}else{
courseName = value.toLowerCase()
}
}else if(name === "balloon"){
value = value ? value.split(",").map(digit => parseInt(digit)) : []
}else if(this.inArray(name, metaNumbers)){
value = parseFloat(value)
}
currentCourse[name] = value
}
}
}
return courses
}
inArray(string, array){
return array.indexOf(string) >= 0
}
split(string, delimiter){
var index = string.indexOf(delimiter)
if(index < 0){
return [string, ""]
}
return [string.slice(0, index), string.slice(index + delimiter.length)]
}
parseCircles(){
var meta = this.metadata[this.difficulty]
var ms = (meta.offset || 0) * -1000 + this.offset
var bpm = meta.bpm || 0
if(bpm <= 0){
bpm = 1
}
var scroll = 1
var measure = 4
this.beatInfo.beatInterval = 60000 / bpm
var gogo = false
var barLine = true
var balloonID = 0
var balloons = meta.balloon || []
var lastDrumroll = false
var branch = false
var branchType
var branchPreference = "m"
var currentMeasure = []
var firstMeasure = true
var firstNote = true
var circles = []
var circleID = 0
var pushMeasure = () => {
for(var i = 0; i < currentMeasure.length; i++){
var note = currentMeasure[i]
if(firstNote && note.type){
firstNote = false
if(ms < 0){
this.soundOffset = ms
ms = 0
}
}
note.start = ms
if(note.endDrumroll){
note.endDrumroll.endTime = ms
}
var msPerMeasure = 60000 * measure / bpm
ms += msPerMeasure / currentMeasure.length
}
for(var i = 0; i < currentMeasure.length; i++){
var note = currentMeasure[i]
if(note.type){
circleID++
var circleObj = new Circle({
id: circleID,
start: note.start,
type: note.type,
txt: note.txt,
speed: note.bpm * note.scroll / 60,
gogoTime: note.gogo,
endTime: note.endTime,
requiredHits: note.requiredHits
})
if(lastDrumroll === note){
lastDrumroll = circleObj
}
circles.push(circleObj)
}
}
if(barLine){
var note = currentMeasure[0]
if(note){
var speed = note.bpm * note.scroll / 60
}else{
var speed = bpm * scroll / 60
}
this.measures.push({
ms: ms,
speed: speed
})
if(firstMeasure){
firstMeasure = false
var msPerMeasure = 60000 * measure / bpm
for(var measureMs = ms - msPerMeasure; measureMs > 0; measureMs -= msPerMeasure){
this.measures.push({
ms: measureMs,
speed: speed
})
}
}
}
}
for(var lineNum = meta.start; lineNum < meta.end; lineNum++){
var line = this.data[lineNum]
if(line.slice(0, 1) === "#"){
var line = line.slice(1).toLowerCase()
var [name, value] = this.split(line, " ")
switch(name){
case "gogostart":
gogo = true
break
case "gogoend":
gogo = false
break
case "bpmchange":
bpm = parseFloat(value)
break
case "scroll":
scroll = parseFloat(value)
break
case "branchstart":
branch = true
branchType = ""
value = value.split(",")
var forkType = value[0].toLowerCase()
if(forkType === "r" || parseFloat(value[2]) <= 100){
branchPreference = "m"
}else if(parseFloat(value[1]) <= 100){
branchPreference = "e"
}else{
branchPreference = "n"
}
break
case "branchend":
case "section":
branch = false
break
case "n": case "e": case "m":
branchType = name
break
case "measure":
var [numerator, denominator] = value.split("/")
measure = numerator / denominator * 4
break
case "delay":
ms += (parseFloat(value) || 0) * 1000
break
case "barlineoff":
barLine = false
break
}
}else if(!branch || branch && branchType === branchPreference){
var string = line.split("")
for(let symbol of string){
var error = false
switch(symbol){
case "0":
currentMeasure.push({
bpm: bpm,
scroll: scroll
})
break
case "1": case "2": case "3": case "4":
var type = this.noteTypes[symbol]
var circleObj = {
type: type.name,
txt: type.txt,
gogo: gogo,
bpm: bpm,
scroll: scroll
}
if(lastDrumroll){
circleObj.endDrumroll = lastDrumroll
lastDrumroll = false
}
currentMeasure.push(circleObj)
break
case "5": case "6": case "7": case "9":
var type = this.noteTypes[symbol]
var circleObj = {
type: type.name,
txt: type.txt,
gogo: gogo,
bpm: bpm,
scroll: scroll
}
if(lastDrumroll){
circleObj.endDrumroll = lastDrumroll
}
if(symbol === "7" || symbol === "9"){
var hits = balloons[balloonID]
if(!hits || hits < 1){
hits = 1
}
circleObj.requiredHits = hits
balloonID++
}
lastDrumroll = circleObj
currentMeasure.push(circleObj)
break
case "8":
if(lastDrumroll){
currentMeasure.push({
endDrumroll: lastDrumroll,
bpm: bpm,
scroll: scroll
})
lastDrumroll = false
}else{
currentMeasure.push({
bpm: bpm,
scroll: scroll
})
}
break
case ",":
pushMeasure()
currentMeasure = []
break
default:
error = true
break
}
if(error){
break
}
}
}
}
pushMeasure()
if(lastDrumroll){
lastDrumroll.endTime = ms
}
return circles
}
}

View File

@@ -88,7 +88,9 @@ class SongSelect{
skin: song.category in this.songSkin ? this.songSkin[song.category] : this.songSkin.default,
stars: song.stars,
category: song.category,
preview: song.preview || 0
preview: song.preview || 0,
type: song.type,
offset: song.offset
})
}
this.songs.sort((a, b) => {
@@ -470,7 +472,9 @@ class SongSelect{
"title": selectedSong.title,
"folder": selectedSong.id,
"difficulty": this.difficultyId[difficulty],
"category": selectedSong.category
"category": selectedSong.category,
"type": selectedSong.type,
"offset": selectedSong.offset
}, shift, ctrl, touch)
}
toTitleScreen(){

View File

@@ -48,7 +48,7 @@ class View{
this.drumroll = []
this.beatInterval = this.controller.getSongData().beatInfo.beatInterval
this.beatInterval = this.controller.parsedSongData.beatInfo.beatInterval
this.assets = new ViewAssets(this)
this.touch = -Infinity
@@ -289,16 +289,12 @@ class View{
}
}
drawMeasures(){
var measures = this.controller.getSongData().measures
var measures = this.controller.parsedSongData.measures
var currentTime = this.controller.getElapsedTime()
measures.forEach((measure, index)=>{
var timeForDistance = this.posToMs(this.distanceForCircle, measure.speed)
if(
currentTime >= measure.ms - timeForDistance
&& currentTime <= measure.ms + 350
&& measure.nb == 0
){
if(currentTime >= measure.ms - timeForDistance && currentTime <= measure.ms + 350){
this.drawMeasure(measure)
}
})