Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6b0115fea | ||
|
|
d07f822f70 | ||
|
|
5ca14ab461 | ||
|
|
02a2c7d973 | ||
|
|
649a30a90e | ||
|
|
9996c5ead4 | ||
|
|
39d517ba5a | ||
|
|
97a9e65682 | ||
|
|
c9c59e0872 |
-13
@@ -1,13 +0,0 @@
|
||||
# Editor configs
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# External modules
|
||||
node_modules
|
||||
|
||||
# Project files
|
||||
asphyxia-core.d.ts
|
||||
package.json
|
||||
package-lock.json
|
||||
typedoc.json
|
||||
tsconfig.json
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"endOfLine": "lf",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid",
|
||||
"quoteProps": "consistent"
|
||||
}
|
||||
@@ -1,26 +1,81 @@
|
||||
# Asphyxia CORE Community Plugins
|
||||
# DanceDanceRevolution
|
||||
|
||||
These plugins are provided by community members and considered officially supported by Team Asphyxia.
|
||||
**Plugin Version:** fork-mdx-3.0.1
|
||||
|
||||
## How to use it?
|
||||
**Supported game versions:** WORLD (2024091000)
|
||||
|
||||
0. Make sure you have [Asphyxia CORE](https://asphyxia-core.github.io/) installed.
|
||||
1. Go to [Releases](https://github.com/asphyxia-core/plugins/releases) page.
|
||||
2. Download the latest source code.
|
||||
3. Extract the code in Asphyxia CORE's `plugins` folder.
|
||||
**Unsupported game versions:** Everything below A20 is unsupported. A20 untested. A3 unsupported.
|
||||
|
||||
## How do I contribute?
|
||||
**Required Asphyxia Core version** [1.50c](https://github.com/asphyxia-core/asphyxia-core.github.io/releases/tag/v1.50)
|
||||
|
||||
I don't actually follow any coding rules for this jank so neither should you. There is, however, a prettier configuration if you want to format the code automatically and forget about it.
|
||||
**Notes:**
|
||||
- This is a WIP plugin for DDR World and things might break so please use this at your own risk.
|
||||
- Please be aware that this plugin and the savefile it produces are incompatible with the official plugin, or any other forks. Please be cautious of using other plugins' savefiles with this fork of the plugin, or using this fork's savefile with other plugins.
|
||||
- Please back up your save data to prevent unwanted issues, especially while using this experimental plugin.
|
||||
- Please please please feel free to open PRs!
|
||||
|
||||
I'll do my best to merge PR, but please make sure you are submitting code targeted for "public" releases. (Unless it is some ancient rare stuff and you feel generous enough to provide support for it)
|
||||
musicdb.xml Usage Guide
|
||||
===========
|
||||
1. Put your musicdb.xml file(s) in the _ddr@asphyxia/data_ directory.
|
||||
2. Configure plugin settings:
|
||||
- There are 2 related fields in the plugin settings:
|
||||
- **_musicdb.xml for musicdata\_load_**: mdb file where song difficulty and limited info will be retrieved. Set this config to the filename of the musicdb.xml file you copied. This file is required for other songs to appear.
|
||||
- **_musicdb.xml for WebUI_**: mdb file where song titles will be retrieved, this will be used for WebUI stuff. If you have a different xml file you want to use, set this config to the filename of that other musicdb.xml file, or keep it blank to use the same musicdb.xml as above.
|
||||
- **Sample setup 1**: I use a modified World musicdb with all difficulties and limited info defined for all songs. I do not need to use a separate musicdb.xml for WebUI (but I would also update this file to include upcoming songs.)
|
||||
- **Sample setup 2**: I use the last A3 musicdb for musicdata\_load. This will set difficulty and limited data for songs released up until A3. World songs info will be set using SONGS\_WORLD defined in _data/world.ts_. Then I use World musicdb for WebUI, so I could get all titles of songs released up until World (I would need to replace this file every time the game updates its song list.)
|
||||
|
||||
- For new plugins: please use `@asphyxia` identifier for your plugin since you are submitting code as the community.
|
||||
- This way we prevent third-party plugins (e.g. `popn` or `popn@someoneelse`) from conflicting with our database.
|
||||
- For existing plugins: please inlude a changelog in your PR so it is easier for me to tell what it is for.
|
||||
|
||||
## How do I make plugins?
|
||||
|
||||
Checkout our [Documentation](https://asphyxia-core.github.io/typedoc/) and maybe consider join our [Discord](https://discord.gg/3TW3BDm) server. Make sure to familiar yourself with at least XML and Typescript/Javascript.
|
||||
Changelog
|
||||
===========
|
||||
### fork-mdx-3.0.1
|
||||
|
||||
Note that you should run `npm install` to install typing for node and lodash, and launch CORE using `--dev` arguments to enable console log and typechecking when using typescript.
|
||||
- Support for 2024091000.
|
||||
- Added list of old songs' re-rates in _data/world.ts:SONGS\_OVERRIDE\_WORLD_.
|
||||
- Updated events list & event loading code:
|
||||
- Galaxy Play
|
||||
- EXTRA SAVIOR
|
||||
- MYSTICAL Re:UNION (songs automatically unlocked on login)
|
||||
- Attempts to fix ghost data loading:
|
||||
- Added ghost id to score and ghost data
|
||||
- Ghost data gets created when there is no existing \[compatible\] data; and it gets updated when getting a higher or the same score. Side effect of this is when you have set a high score prior to this plugin update, the ghost data won't be updated unless you break or match that high score.
|
||||
- Unfortunately I could not find a way to match the old scores to their respective ghost data, since the old ghost data only stored the song id and difficulty, and not the style (single/double.)
|
||||
- World, Area and Machine #1 high score saving/loading (WIP)
|
||||
- Area high score: uses locationid to search, save and load scores.
|
||||
- Machine high score: uses locationid and pcbid.
|
||||
- Ghost data also gets created for each new high score (overwritten when high score is broken/matched)
|
||||
- WebUI stuff (WIP) - musicdb.xml required for proper score display.
|
||||
- Basic profile info
|
||||
- Flare skill info
|
||||
- Music/score table
|
||||
|
||||
|
||||
### fork-mdx-3.0.0b
|
||||
|
||||
- Flare skill song unlock event.... on Doubles.
|
||||
- Fixes crashes on Flare Skill rank up.... on Doubles (again, just tested until Mercury.)
|
||||
- Event list updated
|
||||
- ghostdata saving and loading (WIP, idk what ghost is)
|
||||
|
||||
|
||||
### fork-mdx-3.0.0a
|
||||
|
||||
- Flare skill song unlock event
|
||||
- Added event data (ddr@asphyxia/data/world.ts:EVENTS_WORLD)
|
||||
- Fixes crashes on Flare Skill rank up (at least until Mercury, haven't tested higher than that yet.)
|
||||
- musicdata_load
|
||||
- Put your musicdb.xml on 'ddr@asphyxia/data' folder. Tested with A3 musicdb.xml
|
||||
- Added musicdata for new WORLD songs (ddr@asphyxia/data/world.ts:SONGS_WORLD)
|
||||
|
||||
|
||||
|
||||
To do:
|
||||
===========
|
||||
|
||||
1. Look for more missing features
|
||||
2. Refactor getLastGhostId
|
||||
3. Refactor hiscore code
|
||||
4. More WebUI stuff
|
||||
5. Figure out unknown values in score_str
|
||||
6. Rival loading and figure out unknown values in rival record_str
|
||||
7. Proper loading of Flare skill in-game (make sure to include only top 30 scores from classic, white and gold)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# BeatStream
|
||||
|
||||
Plugin Version: **v1.0.2**
|
||||
|
||||
Supported Versions:
|
||||
|
||||
- BeatStream アニムトライヴ
|
||||
- Back end ✔
|
||||
- Web UI ✔
|
||||
@@ -1,247 +0,0 @@
|
||||
import { Bst2EventParamsMap, getKEventControl } from "../../models/bst2/event_params"
|
||||
import { Bst2AccountMap, Bst2BiscoMap, Bst2CourseMap, Bst2MusicRecordMap, Bst2PlayerMap, Bst2SurveyMap, Bst2TipsMap, Bst2UnlockingInfoMap, IBst2Account, IBst2Base, IBst2Bisco, IBst2Course, IBst2CrysisLog, IBst2Customization, IBst2Hacker, IBst2MusicRecord, IBst2Player, IBst2Survey, IBst2Tips, IBst2UnlockingInfo } from "../../models/bst2/profile"
|
||||
import { Bst2CourseLogMap, Bst2StageLogMap, IBst2StageLog } from "../../models/bst2/stagelog"
|
||||
import { bacK, BigIntProxy, boolme, fromMap, mapK, s16me, s32me, s8me, strme, toBigInt } from "../../utility/mapping"
|
||||
import { isToday } from "../../utility/utility_functions"
|
||||
import { DBM } from "../utility/db_manager"
|
||||
import { readPlayerPostProcess, writePlayerPreProcess } from "./processing"
|
||||
|
||||
export namespace Bst2HandlersCommon {
|
||||
export const Common: EPR = async (_0, _1, send) => await send.object({ event_ctrl: { data: getKEventControl() } })
|
||||
|
||||
export const BootPcb: EPR = async (_0, _1, send) => await send.object({ sinfo: { nm: K.ITEM("str", "Asphyxia"), cl_enbl: K.ITEM("bool", 1), cl_h: K.ITEM("u8", 0), cl_m: K.ITEM("u8", 0) } })
|
||||
|
||||
export const StartPlayer: EPR = async (_, data, send) => {
|
||||
let params = fromMap(Bst2EventParamsMap)
|
||||
let rid = $(data).str("rid")
|
||||
let account = DB.FindOne<IBst2Account>(rid, { collection: "bst.bst2.player.account" })
|
||||
if (account == null) params.playerId = -1
|
||||
params.startTime = BigInt(Date.now())
|
||||
send.object(mapK(params, Bst2EventParamsMap))
|
||||
}
|
||||
|
||||
export const PlayerSucceeded: EPR = async (_, data, send) => {
|
||||
let rid = $(data).str("rid")
|
||||
let account: IBst2Account = await DB.FindOne<IBst2Account>(rid, { collection: "bst.bst2.player.account" })
|
||||
let result
|
||||
if (account == null) {
|
||||
result = {
|
||||
play: false,
|
||||
data: { name: "" },
|
||||
record: {},
|
||||
hacker: {},
|
||||
phantom: {}
|
||||
}
|
||||
} else {
|
||||
let base: IBst2Base = await DB.FindOne<IBst2Base>(rid, { collection: "bst.bst2.player.base" })
|
||||
let records: IBst2MusicRecord[] = await DB.Find<IBst2MusicRecord>({ collection: "bst.bst2.playData.musicRecord#userId", userId: account.userId })
|
||||
result = {
|
||||
play: true,
|
||||
data: { name: base.name },
|
||||
record: {},
|
||||
hacker: {},
|
||||
phantom: {}
|
||||
}
|
||||
}
|
||||
send.object(mapK(result, {
|
||||
play: boolme(),
|
||||
data: { name: strme() },
|
||||
record: {},
|
||||
hacker: {},
|
||||
phantom: {}
|
||||
}))
|
||||
}
|
||||
|
||||
export const ReadPlayer: EPR = async (_, data, send) => {
|
||||
let refid = $(data).str("rid")
|
||||
let account = await DB.FindOne<IBst2Account>(refid, { collection: "bst.bst2.player.account" })
|
||||
if (account == null) return await send.deny()
|
||||
|
||||
let base = await DB.FindOne<IBst2Base>(refid, { collection: "bst.bst2.player.base" })
|
||||
let survey = await DB.FindOne<IBst2Survey>(refid, { collection: "bst.bst2.player.survey" }) || fromMap(Bst2SurveyMap)
|
||||
let unlocking = await DB.Find<IBst2UnlockingInfo>(refid, { collection: "bst.bst2.player.unlockingInfo" })
|
||||
let customize = await DB.FindOne<IBst2Customization>(refid, { collection: "bst.bst2.player.customization" })
|
||||
let tips = await DB.FindOne<IBst2Tips>(refid, { collection: "bst.bst2.player.tips" }) || fromMap(Bst2TipsMap)
|
||||
let hacker = await DB.Find<IBst2Hacker>(refid, { collection: "bst.bst2.player.hacker" })
|
||||
let crysis = await DB.Find<IBst2CrysisLog>(refid, { collection: "bst.bst2.player.event.crysis" })
|
||||
let bisco = await DB.FindOne<IBst2Bisco>(refid, { collection: "bst.bst2.player.bisco" }) || fromMap(Bst2BiscoMap)
|
||||
let records = await DB.Find<IBst2MusicRecord>({ collection: "bst.bst2.playData.musicRecord#userId", userId: account.userId })
|
||||
let courses = await DB.Find<IBst2Course>({ collection: "bst.bst2.playData.course#userId", userId: account.userId })
|
||||
|
||||
account.previousStartTime = account.standardTime
|
||||
account.standardTime = BigInt(Date.now())
|
||||
account.ea = true
|
||||
account.intrvld = 0
|
||||
account.playCount++
|
||||
account.playCountToday++
|
||||
let eventPlayLog: { crysis?: IBst2CrysisLog[] } = {}
|
||||
if (crysis.length != 0) eventPlayLog.crysis = crysis
|
||||
|
||||
let player: IBst2Player = {
|
||||
pdata: {
|
||||
account: account,
|
||||
base: base,
|
||||
survey: survey,
|
||||
opened: {},
|
||||
item: (unlocking.length == 0) ? {} : { info: unlocking },
|
||||
customize: customize,
|
||||
tips: tips,
|
||||
hacker: (hacker.length == 0) ? {} : { info: hacker },
|
||||
playLog: eventPlayLog,
|
||||
bisco: { pinfo: bisco },
|
||||
record: (records.length == 0) ? {} : { rec: records },
|
||||
course: (courses.length == 0) ? {} : { record: courses }
|
||||
}
|
||||
}
|
||||
send.object(readPlayerPostProcess(mapK(player, Bst2PlayerMap)))
|
||||
}
|
||||
|
||||
export const WritePlayer: EPR = async (_, data, send) => {
|
||||
let player = bacK(writePlayerPreProcess(data), Bst2PlayerMap).data
|
||||
let refid = player.pdata.account.refid
|
||||
let userId = player.pdata.account.userId
|
||||
let now = BigIntProxy(BigInt(Date.now()))
|
||||
|
||||
let opm = new DBM.DBOperationManager()
|
||||
|
||||
let oldAccount = await DB.FindOne<IBst2Account>(refid, { collection: "bst.bst2.player.account" })
|
||||
if (!oldAccount) {
|
||||
do {
|
||||
userId = Math.round(Math.random() * 99999999)
|
||||
} while ((await DB.Find<IBst2Account>(null, { collection: "bst.bst2.player.account", userId: userId })).length > 0)
|
||||
oldAccount = fromMap(Bst2AccountMap)
|
||||
oldAccount.userId = userId
|
||||
} else {
|
||||
oldAccount.playCount++
|
||||
if (!isToday(toBigInt(oldAccount.standardTime))) {
|
||||
oldAccount.dayCount++
|
||||
oldAccount.playCountToday = 1
|
||||
} else oldAccount.playCountToday++
|
||||
}
|
||||
oldAccount.standardTime = BigIntProxy(BigInt(Date.now()))
|
||||
opm.upsert<IBst2Account>(refid, { collection: "bst.bst2.player.account" }, oldAccount)
|
||||
if (player.pdata.base) opm.upsert<IBst2Base>(refid, { collection: "bst.bst2.player.base" }, player.pdata.base)
|
||||
if (player.pdata.item?.info?.length > 0) for (let u of player.pdata.item.info) opm.upsert<IBst2UnlockingInfo>(refid, { collection: "bst.bst2.player.unlockingInfo", type: u.type, id: u.id }, u)
|
||||
if (player.pdata.customize) opm.upsert<IBst2Customization>(refid, { collection: "bst.bst2.player.customization" }, player.pdata.customize)
|
||||
if (player.pdata.tips) opm.upsert<IBst2Base>(refid, { collection: "bst.bst2.player.base" }, player.pdata.base)
|
||||
if (player.pdata.hacker?.info?.length > 0) for (let h of player.pdata.hacker.info) {
|
||||
h.updateTime = now
|
||||
opm.upsert<IBst2Hacker>(refid, { collection: "bst.bst2.player.hacker", id: h.id }, h)
|
||||
}
|
||||
if (player.pdata.playLog?.crysis?.length > 0) for (let c of player.pdata.playLog.crysis) opm.upsert<IBst2CrysisLog>(refid, { collection: "bst.bst2.player.event.crysis", id: c.id, stageId: c.stageId }, c)
|
||||
|
||||
await DBM.operate(opm)
|
||||
send.object({ uid: K.ITEM("s32", oldAccount.userId) })
|
||||
}
|
||||
|
||||
export const WriteStageLog: EPR = async (_, data, send) => {
|
||||
await updateRecordFromStageLog(bacK(data, Bst2StageLogMap).data, false)
|
||||
send.success()
|
||||
}
|
||||
|
||||
export const WriteCourseStageLog: EPR = async (_, data, send) => {
|
||||
await updateRecordFromStageLog(bacK(data, Bst2StageLogMap).data, true)
|
||||
send.success()
|
||||
}
|
||||
|
||||
async function updateRecordFromStageLog(stageLog: IBst2StageLog, isCourseStage: boolean) {
|
||||
let query: Query<IBst2MusicRecord> = { collection: "bst.bst2.playData.musicRecord#userId", userId: stageLog.userId, musicId: stageLog.musicId, chart: stageLog.chart }
|
||||
let oldRecord = await DB.FindOne<IBst2MusicRecord>(query)
|
||||
|
||||
let time = Date.now()
|
||||
stageLog.time = time
|
||||
stageLog.isCourseStage = isCourseStage
|
||||
|
||||
if (oldRecord == null) {
|
||||
oldRecord = fromMap(Bst2MusicRecordMap)
|
||||
oldRecord.musicId = stageLog.musicId
|
||||
oldRecord.chart = stageLog.chart
|
||||
oldRecord.clearCount = (stageLog.medal >= 3) ? 1 : 0
|
||||
oldRecord.score = stageLog.score
|
||||
oldRecord.grade = stageLog.grade
|
||||
oldRecord.gaugeTimes10 = stageLog.gaugeTimes10
|
||||
oldRecord.playCount = 1
|
||||
oldRecord.medal = stageLog.medal
|
||||
oldRecord.combo = stageLog.combo
|
||||
oldRecord.lastPlayTime = time
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.userId = stageLog.userId
|
||||
} else {
|
||||
if (stageLog.medal >= 3) oldRecord.clearCount++
|
||||
if (oldRecord.score < stageLog.score) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.score = stageLog.score
|
||||
}
|
||||
if (oldRecord.grade < stageLog.grade) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.grade = stageLog.grade
|
||||
}
|
||||
if (oldRecord.gaugeTimes10 < stageLog.gaugeTimes10) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.gaugeTimes10 = stageLog.gaugeTimes10
|
||||
}
|
||||
if (oldRecord.medal < stageLog.medal) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.medal = stageLog.medal
|
||||
}
|
||||
if (oldRecord.combo < stageLog.combo) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.combo = stageLog.combo
|
||||
}
|
||||
oldRecord.lastPlayTime = time
|
||||
oldRecord.playCount++
|
||||
}
|
||||
DBM.upsert(null, query, oldRecord)
|
||||
DBM.insert(null, stageLog)
|
||||
}
|
||||
|
||||
export const WriteCourseLog: EPR = async (_, data, send) => {
|
||||
let courseLog = bacK(data, Bst2CourseLogMap).data
|
||||
let query: Query<IBst2Course> = { collection: "bst.bst2.playData.course#userId", userId: courseLog.userId, courseId: courseLog.courseId }
|
||||
let oldRecord = await DB.FindOne<IBst2Course>(query)
|
||||
|
||||
let time = Date.now()
|
||||
courseLog.time = time
|
||||
|
||||
if (oldRecord == null) {
|
||||
oldRecord = fromMap(Bst2CourseMap)
|
||||
oldRecord.courseId = courseLog.courseId
|
||||
oldRecord.score = courseLog.score
|
||||
oldRecord.grade = courseLog.grade
|
||||
oldRecord.gauge = courseLog.gauge
|
||||
oldRecord.playCount = 1
|
||||
oldRecord.medal = courseLog.medal
|
||||
oldRecord.combo = courseLog.combo
|
||||
oldRecord.lastPlayTime = time
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.userId = courseLog.userId
|
||||
} else {
|
||||
if (oldRecord.score < courseLog.score) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.score = courseLog.score
|
||||
}
|
||||
if (oldRecord.grade < courseLog.grade) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.grade = courseLog.grade
|
||||
}
|
||||
if (oldRecord.gauge < courseLog.gauge) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.gauge = courseLog.gauge
|
||||
}
|
||||
if (oldRecord.medal < courseLog.medal) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.medal = courseLog.medal
|
||||
}
|
||||
if (oldRecord.combo < courseLog.combo) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.combo = courseLog.combo
|
||||
}
|
||||
oldRecord.lastPlayTime = time
|
||||
oldRecord.playCount++
|
||||
}
|
||||
DBM.upsert(null, query, oldRecord)
|
||||
DBM.insert(null, courseLog)
|
||||
|
||||
send.success()
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { IBst2Player } from "../../models/bst2/profile"
|
||||
import { KITEM2 } from "../../utility/mapping"
|
||||
import { toFullWidth, toHalfWidth } from "../../utility/utility_functions"
|
||||
|
||||
export function readPlayerPostProcess(player: KITEM2<IBst2Player>): KITEM2<IBst2Player> {
|
||||
if (player.pdata.base?.name != null) player.pdata.base.name["@content"] = toFullWidth(player.pdata.base.name["@content"])
|
||||
return player
|
||||
}
|
||||
export function writePlayerPreProcess(player: KITEM2<IBst2Player>): KITEM2<IBst2Player> {
|
||||
if (player.pdata.base?.name != null) player.pdata.base.name["@content"] = toHalfWidth(player.pdata.base.name["@content"])
|
||||
return player
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { IBst2Base, IBst2Customization } from "../../models/bst2/profile"
|
||||
import { WebUIMessageType } from "../../models/utility/webui_message"
|
||||
import { DBM } from "../utility/db_manager"
|
||||
import { UtilityHandlersWebUI } from "../utility/webui"
|
||||
|
||||
export namespace Bst2HandlersWebUI {
|
||||
export const UpdateSettings = async (data: {
|
||||
refid: string
|
||||
name: string
|
||||
rippleNote: number
|
||||
sfxNormalNote: number
|
||||
sfxRippleNote: number
|
||||
sfxSlashNote: number
|
||||
sfxStreamNote: number
|
||||
backgroundBrightness: number
|
||||
judgeText: number
|
||||
rippleNoteGuide: number
|
||||
streamNoteGuide: number
|
||||
sfxFine: number
|
||||
sfxStreamNoteTail: number
|
||||
}) => {
|
||||
try {
|
||||
let base = await DB.FindOne<IBst2Base>(data.refid, { collection: "bst.bst2.player.base" })
|
||||
let customization = await DB.FindOne<IBst2Customization>(data.refid, { collection: "bst.bst2.player.customization" })
|
||||
if (!customization || !base) throw new Error("No profile for refid=" + data.refid)
|
||||
base.name = data.name
|
||||
customization.custom[0] = data.rippleNote
|
||||
customization.custom[2] = data.sfxNormalNote
|
||||
customization.custom[3] = data.sfxRippleNote
|
||||
customization.custom[4] = data.sfxSlashNote
|
||||
customization.custom[5] = data.sfxStreamNote
|
||||
customization.custom[6] = data.backgroundBrightness
|
||||
customization.custom[7] = (data.judgeText << 0) | (data.rippleNoteGuide << 1) | (data.streamNoteGuide << 2) | (data.sfxStreamNoteTail << 3) | (data.sfxFine << 4)
|
||||
customization.custom[9] = data.judgeText
|
||||
DBM.update<IBst2Base>(data.refid, { collection: "bst.bst2.player.base" }, base)
|
||||
DBM.update<IBst2Customization>(data.refid, { collection: "bst.bst2.player.customization" }, customization)
|
||||
UtilityHandlersWebUI.pushMessage("Save BeatStream Animtribe settings succeeded!", 2, WebUIMessageType.success, data.refid)
|
||||
} catch (e) {
|
||||
UtilityHandlersWebUI.pushMessage("Error while save BeatStream Animtribe settings: " + e.message, 2, WebUIMessageType.error, data.refid)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { IBatchResult } from "../../models/utility/batch"
|
||||
import { IPluginVersion } from "../../models/utility/plugin_version"
|
||||
import { isHigherVersion } from "../../utility/utility_functions"
|
||||
import { DBM } from "./db_manager"
|
||||
|
||||
export namespace Batch {
|
||||
let registeredBatch = <{ id: string, version: string, batch: () => Promise<any> }[]>[]
|
||||
|
||||
export async function execute(version: string): Promise<void> {
|
||||
for (let b of registeredBatch) {
|
||||
if ((await DB.Find<IBatchResult>({ collection: "bst.batchResult", batchId: b.id })).length == 0) if (!isHigherVersion(version, b.version)) {
|
||||
await b.batch()
|
||||
await DBM.insert<IBatchResult>(null, { collection: "bst.batchResult", batchId: b.id })
|
||||
}
|
||||
}
|
||||
}
|
||||
export function register(id: string, version: string, batch: () => Promise<any>) {
|
||||
registeredBatch.push({ id: id, version: version, batch: batch })
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Batch } from "./batch"
|
||||
import { DBM } from "./db_manager"
|
||||
import { bufferToBase64, log } from "../../utility/utility_functions"
|
||||
|
||||
export function initializeBatch() {
|
||||
/* Register batch here **/
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
export namespace UtilityHandlersCommon {
|
||||
export const WriteShopInfo: EPR = async (__, ___, send) => {
|
||||
let result = {
|
||||
sinfo: {
|
||||
lid: K.ITEM("str", "ea"),
|
||||
nm: K.ITEM("str", "Asphyxia shop"),
|
||||
cntry: K.ITEM("str", "Japan"),
|
||||
rgn: K.ITEM("str", "1"),
|
||||
prf: K.ITEM("s16", 13),
|
||||
cl_enbl: K.ITEM("bool", 0),
|
||||
cl_h: K.ITEM("u8", 8),
|
||||
cl_m: K.ITEM("u8", 0)
|
||||
}
|
||||
}
|
||||
send.object(result)
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
import { ICollection } from "../../models/utility/definitions"
|
||||
import { log } from "../../utility/utility_functions"
|
||||
|
||||
export namespace DBM {
|
||||
export interface IDBCollectionName extends ICollection<"dbManager.collectionName"> {
|
||||
name: string
|
||||
}
|
||||
export interface IDBOperation<T = any, TOperation extends "insert" | "update" | "upsert" | "remove" | "skip" = "insert" | "update" | "upsert" | "remove" | "skip"> {
|
||||
refid?: string
|
||||
query: TOperation extends "insert" ? null : Query<T>
|
||||
operation: TOperation
|
||||
doc: TOperation extends "remove" ? null : T | Doc<T>
|
||||
isPublicDoc?: boolean
|
||||
}
|
||||
export class DBOperationManager {
|
||||
public operations: IDBOperation[] = []
|
||||
|
||||
public push(...op: IDBOperation[]): void {
|
||||
this.operations.push(...op)
|
||||
}
|
||||
public update<T extends ICollection<any>>(refid: string | null, query: Query<T>, data: Doc<T>, isPublicDoc: boolean = true): void {
|
||||
for (let o of this.operations) if (o.doc && DBOperationManager.isMatch(o.doc, query)) o.operation = "skip"
|
||||
this.operations.push({ refid: refid, query: query, operation: "update", doc: data, isPublicDoc: isPublicDoc })
|
||||
}
|
||||
public upsert<T extends ICollection<any>>(refid: string | null, query: Query<T>, data: Doc<T>, isPublicDoc: boolean = true): void {
|
||||
for (let o of this.operations) if (o.doc && DBOperationManager.isMatch(o.doc, query)) o.operation = "skip"
|
||||
this.operations.push({ refid: refid, query: query, operation: "upsert", doc: data, isPublicDoc: isPublicDoc })
|
||||
}
|
||||
public insert<T extends ICollection<any>>(refid: string | null, data: Doc<T>, isPublicDoc: boolean = true): void {
|
||||
this.operations.push({ refid: refid, operation: "insert", query: null, doc: data, isPublicDoc: isPublicDoc })
|
||||
}
|
||||
public remove<T extends ICollection<any>>(refid: string | null, query: Query<T>, isPublicDoc: boolean = true): void {
|
||||
for (let o of this.operations) if (o.doc && DBOperationManager.isMatch(o.doc, query)) o.operation = "skip"
|
||||
this.operations.push({ refid: refid, query: query, operation: "remove", doc: null, isPublicDoc: isPublicDoc })
|
||||
}
|
||||
public async findOne<T extends ICollection<any>>(refid: string | null, query: Query<T>, isPublicDoc: boolean = true): Promise<T | Doc<T>> {
|
||||
for (let i = this.operations.length - 1; i >= 0; i--) {
|
||||
let o = this.operations[i]
|
||||
if (o.doc == null) continue
|
||||
if (DBOperationManager.isMatch(o.doc, query) && ((o.refid && refid) ? (o.refid == refid) : true)) return o.doc
|
||||
}
|
||||
return ((refid == null) && isPublicDoc) ? await DB.FindOne<T>(query) : await DB.FindOne<T>(refid, query)
|
||||
}
|
||||
public async find<T extends ICollection<any>>(refid: string | null, query: Query<T>, isPublicDoc: boolean = true): Promise<(T | Doc<T>)[]> {
|
||||
let result: (T | Doc<T>)[] = []
|
||||
for (let o of this.operations) {
|
||||
if (o.doc == null) continue
|
||||
if (DBOperationManager.isMatch(o.doc, query) && ((o.refid && refid) ? (o.refid == refid) : true)) result.push(o.doc)
|
||||
}
|
||||
return result.concat(await (((refid == null) && isPublicDoc) ? DB.Find<T>(query) : DB.Find<T>(refid, query)))
|
||||
}
|
||||
private static isMatch<T>(entry: T | Doc<T>, query: Query<T>): boolean {
|
||||
if (entry == null) return query == null
|
||||
if (query.$where && !query.$where.apply(entry)) return false
|
||||
let $orResult = null
|
||||
let skipKeys = ["$where", "_id"]
|
||||
for (let qk in query) {
|
||||
if (skipKeys.includes(qk)) continue
|
||||
switch (qk) {
|
||||
case "$or": {
|
||||
if ($orResult == null) $orResult = false
|
||||
for (let or of query.$or) if (this.isMatch(entry, or)) $orResult = true
|
||||
break
|
||||
}
|
||||
case "$and": {
|
||||
for (let and of query.$and) if (!this.isMatch(entry, and)) return false
|
||||
break
|
||||
}
|
||||
case "$not": {
|
||||
if (this.isMatch(entry, query.$not)) return false
|
||||
break
|
||||
}
|
||||
default: {
|
||||
let value = entry[qk]
|
||||
let q = query[qk]
|
||||
if (value == q) continue
|
||||
if ((typeof q != "object") && (typeof q != "function")) return false
|
||||
if ((q.$exists != null)) if ((q.$exists && (value == null)) || (!q.$exists && (value != null))) return false
|
||||
if (Array.isArray(value)) {
|
||||
if (q.$elemMatch && !this.isMatch(value, q.$elemMatch)) return false
|
||||
if (q.$size && (value.length != q.$size)) return false
|
||||
continue
|
||||
} else if ((typeof value == "number") || (typeof value == "string")) {
|
||||
if (q.$lt) if (value >= q.$lt) return false
|
||||
if (q.$lte) if (value > q.$lte) return false
|
||||
if (q.$gt) if (value <= q.$gt) return false
|
||||
if (q.$gte) if (value < q.$gte) return false
|
||||
if (q.$in) if (!value.toString().includes(q.$in)) return false
|
||||
if (q.$nin) if (value.toString().includes(q.$nin)) return false
|
||||
if (q.$ne) if (value == q.$ne) return false
|
||||
if (q.$regex) if (value.toString().match(q.$regex).length == 0) return false
|
||||
continue
|
||||
} else if (typeof value == "object") {
|
||||
if (!this.isMatch(value, q)) return false
|
||||
continue
|
||||
} else if (q != null) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return ($orResult == null) || $orResult
|
||||
}
|
||||
}
|
||||
export async function getCollectionNames(filter?: string): Promise<IDBCollectionName[]> {
|
||||
let result = await DB.Find<IDBCollectionName>({ collection: "dbManager.collectionName" })
|
||||
if (filter != null) {
|
||||
let filters = filter.split(",")
|
||||
for (let i = 0; i < filter.length; i++) filters[i] = filters[i].trim()
|
||||
let i = 0
|
||||
while (i < result.length) {
|
||||
let removeFlag = false
|
||||
for (let f of filters) if (f.startsWith("!") ? !result[i].name.includes(f) : result[i].name.includes(f)) {
|
||||
result.splice(i, 1)
|
||||
removeFlag = true
|
||||
break
|
||||
}
|
||||
if (!removeFlag) i++
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async function checkData<T extends ICollection<any>>(data: T): Promise<void> {
|
||||
for (let k in data) if (k.startsWith("__")) delete data[k]
|
||||
if (await DB.FindOne<IDBCollectionName>({ collection: "dbManager.collectionName", name: data.collection }) == null) {
|
||||
await DB.Insert<IDBCollectionName>({ collection: "dbManager.collectionName", name: data.collection })
|
||||
}
|
||||
}
|
||||
export async function update<T extends ICollection<any>>(refid: string | null, query: Query<T>, data: Doc<T>, isPublicDoc: boolean = true) {
|
||||
checkData(data)
|
||||
if (refid == null) return isPublicDoc ? await DB.Update(query, data) : await DB.Update(null, query, data)
|
||||
else return await DB.Update(refid, query, data)
|
||||
}
|
||||
export async function upsert<T extends ICollection<any>>(refid: string | null, query: Query<T>, data: Doc<T>, isPublicDoc: boolean = true) {
|
||||
checkData(data)
|
||||
if (refid == null) return isPublicDoc ? await DB.Upsert(query, data) : await DB.Upsert(null, query, data)
|
||||
else return await DB.Upsert(refid, query, data)
|
||||
}
|
||||
export async function insert<T extends ICollection<any>>(refid: string | null, data: Doc<T>, isPublicDoc: boolean = true) {
|
||||
checkData(data)
|
||||
if (refid == null) return isPublicDoc ? await DB.Insert(data) : await DB.Insert(null, data)
|
||||
else return await DB.Insert(refid, data)
|
||||
}
|
||||
export async function remove<T extends ICollection<any>>(refid: string | null, query: Query<T>, isPublicDoc: boolean = true) {
|
||||
if (refid == null) return isPublicDoc ? await DB.Remove(query) : await DB.Remove(null, query)
|
||||
else return await DB.Remove(refid, query)
|
||||
}
|
||||
|
||||
export async function operate(operations: DBOperationManager) {
|
||||
let result = []
|
||||
for (let o of operations.operations) {
|
||||
if (o.operation == "skip") continue
|
||||
if (o.doc) delete o.doc._id
|
||||
try {
|
||||
switch (o.operation) {
|
||||
case "insert":
|
||||
result.push(await insert(o.refid, o.doc, o.isPublicDoc))
|
||||
break
|
||||
case "update":
|
||||
result.push(await update(o.refid, o.query, o.doc, o.isPublicDoc))
|
||||
break
|
||||
case "upsert":
|
||||
result.push(await upsert(o.refid, o.query, o.doc, o.isPublicDoc))
|
||||
break
|
||||
case "remove":
|
||||
result.push(await remove(o.refid, o.query, o.isPublicDoc))
|
||||
break
|
||||
}
|
||||
} catch (e) {
|
||||
await log(new Date().toLocaleString() + " Error: " + (e as Error).message)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export async function removeAllData(refid?: string, filter?: string) {
|
||||
for (let c of await getCollectionNames(filter)) remove(refid, { collection: c.name })
|
||||
|
||||
if ((refid == null) && (filter == null)) remove(null, { collection: "dbManager.collectionName" })
|
||||
}
|
||||
export async function overall(refid: string, userId: number, filter: string, operation: "delete" | "export" | "override", data?: any) {
|
||||
if (refid == null) return
|
||||
try {
|
||||
let collections = await DBM.getCollectionNames(filter)
|
||||
let traverse = async (f: (rid: string | null, query: Query<ICollection<any>>) => Promise<any>) => {
|
||||
let result = []
|
||||
for (let c of collections) {
|
||||
if (c.name.includes("#userId") && (userId != null)) result.concat(...await f(null, { collection: c.name, userId: userId }))
|
||||
else result.concat(...await f(refid, { collection: c.name }))
|
||||
}
|
||||
return result
|
||||
}
|
||||
switch (operation) {
|
||||
case "delete":
|
||||
await traverse((rid, query) => DBM.remove(rid, query))
|
||||
break
|
||||
case "export":
|
||||
let result = await traverse((rid, query) => DB.Find(rid, query))
|
||||
return JSON.stringify(result)
|
||||
case "override":
|
||||
if (!Array.isArray(data)) return "The data may not be an Asphyxia CORE savedata."
|
||||
await traverse((rid, query) => DBM.remove(rid, query))
|
||||
for (let d of data) if ((typeof (d?.collection) == "string") && (!d.collection.includes(filter))) DB.Insert(d)
|
||||
break
|
||||
}
|
||||
} catch (e) {
|
||||
return e.message
|
||||
}
|
||||
return null
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { initializeBatch } from "./batch_initialize"
|
||||
import { IPluginVersion } from "../../models/utility/plugin_version"
|
||||
import { isHigherVersion } from "../../utility/utility_functions"
|
||||
import { Batch } from "./batch"
|
||||
import { DBM } from "./db_manager"
|
||||
import { version } from "../../utility/about"
|
||||
|
||||
export async function initialize() {
|
||||
let oldVersion = await DB.FindOne<IPluginVersion>({ collection: "bst.pluginVersion" })
|
||||
if ((oldVersion == null) || isHigherVersion(oldVersion.version, version)) {
|
||||
initializeBatch()
|
||||
await Batch.execute(version)
|
||||
await DBM.upsert<IPluginVersion>(null, { collection: "bst.pluginVersion" }, { collection: "bst.pluginVersion", version: version })
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { IWebUIMessage, WebUIMessageType } from "../../models/utility/webui_message"
|
||||
import { DBM } from "./db_manager"
|
||||
|
||||
export namespace UtilityHandlersWebUI {
|
||||
export function pushMessage(message: string, version: number, type: WebUIMessageType, rid?: string) {
|
||||
DBM.upsert<IWebUIMessage>(null, { collection: "utility.webuiMessage" }, { collection: "utility.webuiMessage", message: message, type: type, refid: rid, version: version })
|
||||
}
|
||||
|
||||
export const removeWebUIMessage = async () => {
|
||||
await DBM.remove<IWebUIMessage>(null, { collection: "utility.webuiMessage" })
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { UtilityHandlersCommon } from "./handlers/utility/common"
|
||||
import { UtilityHandlersWebUI } from "./handlers/utility/webui"
|
||||
import { initialize } from "./handlers/utility/initialize"
|
||||
import { Bst2HandlersCommon } from "./handlers/bst2/common"
|
||||
import { Bst2HandlersWebUI } from "./handlers/bst2/webui"
|
||||
|
||||
export function register() {
|
||||
R.GameCode("NBT")
|
||||
|
||||
routeBst2()
|
||||
|
||||
R.WebUIEvent("removeWebUIMessage", UtilityHandlersWebUI.removeWebUIMessage)
|
||||
|
||||
R.Unhandled()
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
function routeBst2() {
|
||||
R.Route("info2.common", Bst2HandlersCommon.Common)
|
||||
R.Route("pcb2.boot", Bst2HandlersCommon.BootPcb)
|
||||
R.Route("player2.start", Bst2HandlersCommon.StartPlayer)
|
||||
R.Route("player2.continue", Bst2HandlersCommon.StartPlayer)
|
||||
R.Route("player2.succeed", Bst2HandlersCommon.PlayerSucceeded)
|
||||
R.Route("player2.read", Bst2HandlersCommon.ReadPlayer)
|
||||
R.Route("player2.write", Bst2HandlersCommon.WritePlayer)
|
||||
R.Route("player2.stagedata_write", Bst2HandlersCommon.WriteStageLog)
|
||||
R.Route("player2.course_stage_data_write", Bst2HandlersCommon.WriteCourseStageLog)
|
||||
R.Route("player2.course_data_write", Bst2HandlersCommon.WriteCourseLog)
|
||||
|
||||
R.WebUIEvent("bst2UpdateSettings", Bst2HandlersWebUI.UpdateSettings)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import { BigIntProxy, boolme, KITEM2, KM, s32me, u64me } from "../../utility/mapping"
|
||||
|
||||
export interface IFloorInfectionEventParams {
|
||||
id: number
|
||||
musicList: number
|
||||
isCompleted: boolean
|
||||
}
|
||||
export const FloorInfectionEventParamsMap: KM<IFloorInfectionEventParams> = {
|
||||
id: s32me("infection_id", 20),
|
||||
musicList: s32me("music_list", 7),
|
||||
isCompleted: boolme("is_complete", true)
|
||||
}
|
||||
|
||||
export interface IBst2EventParams {
|
||||
playerId: number
|
||||
startTime: bigint | BigIntProxy
|
||||
hasRbCollaboration: boolean
|
||||
hasPopnCollaboration: boolean
|
||||
floorInfection: { event: IFloorInfectionEventParams }
|
||||
museca: { isPlayedMuseca: boolean }
|
||||
}
|
||||
export const Bst2EventParamsMap: KM<IBst2EventParams> = {
|
||||
playerId: s32me("plyid"),
|
||||
startTime: u64me("start_time"),
|
||||
hasRbCollaboration: boolme("reflec_collabo", true),
|
||||
hasPopnCollaboration: boolme("pop_collabo", true),
|
||||
floorInfection: { event: FloorInfectionEventParamsMap, $targetKey: "floor_infection" },
|
||||
museca: { isPlayedMuseca: boolme("is_play_museca", true) },
|
||||
}
|
||||
|
||||
export interface IBst2EventControl {
|
||||
type: number
|
||||
phase: number
|
||||
}
|
||||
export const Bst2EventControlMap: KM<IBst2EventControl> = {
|
||||
type: s32me(),
|
||||
phase: s32me()
|
||||
}
|
||||
|
||||
let kEventControl: KITEM2<IBst2EventControl>[]
|
||||
export function getKEventControl(): KITEM2<IBst2EventControl>[] {
|
||||
if (kEventControl == null) {
|
||||
kEventControl = []
|
||||
for (let i = 0; i <= 40; i++) for (let j = 0; j <= 25; j++) kEventControl.push(<any>{ type: K.ITEM("s32", i), phase: K.ITEM("s32", j) })
|
||||
}
|
||||
return kEventControl
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
import { BigIntProxy, boolme, colme, ignoreme, KM, s16me, s32me, s8me, strme, u16me, u64me, u8me } from "../../utility/mapping"
|
||||
import { FixedSizeArray } from "../../utility/type"
|
||||
import { ICollection } from "../utility/definitions"
|
||||
|
||||
export interface IBst2Account extends ICollection<"bst.bst2.player.account"> {
|
||||
userId: number
|
||||
isTakeOver: number
|
||||
playerId: number
|
||||
continueCount: number
|
||||
playCount: number
|
||||
playCountToday: number
|
||||
crd: number
|
||||
brd: number
|
||||
dayCount: number
|
||||
refid: string
|
||||
lobbyId: string
|
||||
mode: number
|
||||
version: number
|
||||
pp: boolean
|
||||
ps: boolean
|
||||
pay: number
|
||||
payedPlayCount: number
|
||||
standardTime: bigint | BigIntProxy
|
||||
intrvld?: number
|
||||
previousStartTime?: bigint | BigIntProxy
|
||||
ea?: boolean
|
||||
}
|
||||
export const Bst2AccountMap: KM<IBst2Account> = {
|
||||
collection: colme<IBst2Account>("bst.bst2.player.account"),
|
||||
userId: s32me("usrid"),//
|
||||
isTakeOver: s32me("is_takeover"),//
|
||||
playerId: s32me("plyid"),
|
||||
continueCount: s32me("continue_cnt"),
|
||||
playCount: s32me("tpc"),//
|
||||
playCountToday: s32me("dpc"),//
|
||||
crd: s32me(),//
|
||||
brd: s32me(),//
|
||||
dayCount: s32me("tdc"),//
|
||||
refid: strme("rid"),
|
||||
lobbyId: strme("lid", "Asphyxia"),
|
||||
mode: u8me(null, 2),
|
||||
version: s16me("ver"),//
|
||||
pp: boolme(),
|
||||
ps: boolme(),
|
||||
pay: s16me(),
|
||||
payedPlayCount: s16me("pay_pc"),
|
||||
standardTime: u64me("st", BigInt(Date.now())),//
|
||||
intrvld: s32me(),//
|
||||
previousStartTime: u64me("pst"),//
|
||||
ea: boolme()//
|
||||
}
|
||||
|
||||
export interface IBst2Base extends ICollection<"bst.bst2.player.base"> {
|
||||
name: string
|
||||
brnk: number
|
||||
bcnum: number
|
||||
lcnum: number
|
||||
volt: number
|
||||
gold: number
|
||||
lastMusicId: number
|
||||
lastChart: number
|
||||
lastSort: number
|
||||
lastTab: number
|
||||
splv: number
|
||||
preference: number
|
||||
lcid: number
|
||||
hat: number
|
||||
}
|
||||
export const Bst2BaseMap: KM<IBst2Base> = {
|
||||
collection: colme<IBst2Base>("bst.bst2.player.base"),
|
||||
name: strme(),
|
||||
brnk: s8me(),
|
||||
bcnum: s8me(),
|
||||
lcnum: s8me(),
|
||||
volt: s32me(),
|
||||
gold: s32me(),
|
||||
lastMusicId: s32me("lmid"),
|
||||
lastChart: s8me("lgrd"),
|
||||
lastSort: s8me("lsrt"),
|
||||
lastTab: s8me("ltab"),
|
||||
splv: s8me(),
|
||||
preference: s8me("pref"),
|
||||
lcid: s32me(),
|
||||
hat: s32me()
|
||||
}
|
||||
|
||||
export interface IBst2Survey extends ICollection<"bst.bst2.player.survey"> {
|
||||
motivate: number
|
||||
}
|
||||
export const Bst2SurveyMap: KM<IBst2Survey> = {
|
||||
collection: colme<IBst2Survey>("bst.bst2.player.survey"),
|
||||
motivate: s8me()
|
||||
}
|
||||
|
||||
export interface IBst2UnlockingInfo extends ICollection<"bst.bst2.player.unlockingInfo"> {
|
||||
type: number
|
||||
id: number
|
||||
param: number
|
||||
count: number
|
||||
}
|
||||
export const Bst2UnlockingInfoMap: KM<IBst2UnlockingInfo> = {
|
||||
collection: colme<IBst2UnlockingInfo>("bst.bst2.player.unlockingInfo"),
|
||||
type: s32me(),
|
||||
id: s32me(),
|
||||
param: s32me(),
|
||||
count: s32me()
|
||||
}
|
||||
|
||||
export interface IBst2Customization extends ICollection<"bst.bst2.player.customization"> {
|
||||
// [rippleNote, rippleNoteColor, sfxNormalNote, sfxRippleNote, sfxSlashNote, sfxStreamNote, backgroundBrightnessTimes2, (000{sfxFine}{sfxStreamTail}{streamNoteGuide}{rippleNoteGuide}{judgeText}, ?, ?, ?, ?, ?, ?, ?, ?)]
|
||||
custom: FixedSizeArray<number, 16>
|
||||
}
|
||||
export const Bst2CustomizationMap: KM<IBst2Customization> = {
|
||||
collection: colme<IBst2Customization>("bst.bst2.player.customization"),
|
||||
custom: u16me(null, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
export interface IBst2Tips extends ICollection<"bst.bst2.player.tips"> {
|
||||
lastTips: number
|
||||
}
|
||||
export const Bst2TipsMap: KM<IBst2Tips> = {
|
||||
collection: colme<IBst2Tips>("bst.bst2.player.tips"),
|
||||
lastTips: s32me("last_tips")
|
||||
}
|
||||
|
||||
export interface IBst2Hacker extends ICollection<"bst.bst2.player.hacker"> {
|
||||
id: number
|
||||
state0: number
|
||||
state1: number
|
||||
state2: number
|
||||
state3: number
|
||||
state4: number
|
||||
updateTime: bigint | BigIntProxy
|
||||
}
|
||||
export const Bst2HackerMap: KM<IBst2Hacker> = {
|
||||
collection: colme<IBst2Hacker>("bst.bst2.player.hacker"),
|
||||
id: s32me(),
|
||||
state0: s8me(),
|
||||
state1: s8me(),
|
||||
state2: s8me(),
|
||||
state3: s8me(),
|
||||
state4: s8me(),
|
||||
updateTime: u64me("update_time")
|
||||
}
|
||||
|
||||
export interface IBst2CrysisLog extends ICollection<"bst.bst2.player.event.crysis"> {
|
||||
id: number
|
||||
stageId: number
|
||||
step: number
|
||||
gauge: number
|
||||
state: number
|
||||
}
|
||||
export const Bst2CrysisLogMap: KM<IBst2CrysisLog> = {
|
||||
collection: colme<IBst2CrysisLog>("bst.bst2.player.event.crysis"),
|
||||
id: s32me(),
|
||||
stageId: s32me("stage_no"),
|
||||
step: s8me(),
|
||||
gauge: s32me("r_gauge"),
|
||||
state: s8me("r_state")
|
||||
}
|
||||
|
||||
export interface IBst2Bisco extends ICollection<"bst.bst2.player.bisco"> {
|
||||
bnum: number
|
||||
jbox: number
|
||||
}
|
||||
export const Bst2BiscoMap: KM<IBst2Bisco> = {
|
||||
collection: colme<IBst2Bisco>("bst.bst2.player.bisco"),
|
||||
bnum: s32me(),
|
||||
jbox: s32me(),
|
||||
}
|
||||
|
||||
export interface IBst2MusicRecord extends ICollection<"bst.bst2.playData.musicRecord#userId"> {
|
||||
musicId: number
|
||||
chart: number
|
||||
playCount: number
|
||||
clearCount: number
|
||||
gaugeTimes10: number
|
||||
score: number
|
||||
grade: number
|
||||
medal: number
|
||||
combo: number
|
||||
userId: number
|
||||
updateTime: number
|
||||
lastPlayTime: number
|
||||
}
|
||||
export const Bst2MusicRecordMap: KM<IBst2MusicRecord> = {
|
||||
collection: colme<IBst2MusicRecord>("bst.bst2.playData.musicRecord#userId"),
|
||||
musicId: s32me("music_id"),
|
||||
chart: s32me("note_level"),
|
||||
playCount: s32me("play_count"),
|
||||
clearCount: s32me("clear_count"),
|
||||
gaugeTimes10: s32me("best_gauge"),
|
||||
score: s32me("best_score"),
|
||||
grade: s32me("best_grade"),
|
||||
medal: s32me("best_medal"),
|
||||
combo: ignoreme(),
|
||||
userId: ignoreme(),
|
||||
updateTime: ignoreme(),
|
||||
lastPlayTime: ignoreme()
|
||||
}
|
||||
|
||||
export interface IBst2Course extends ICollection<"bst.bst2.playData.course#userId"> {
|
||||
courseId: number
|
||||
playCount: number
|
||||
isTouched: boolean
|
||||
clearType: number
|
||||
gauge: number
|
||||
score: number
|
||||
grade: number
|
||||
medal: number
|
||||
combo: number
|
||||
userId: number
|
||||
updateTime: number
|
||||
lastPlayTime: number
|
||||
}
|
||||
export const Bst2CourseMap: KM<IBst2Course> = {
|
||||
collection: colme<IBst2Course>("bst.bst2.playData.course#userId"),
|
||||
courseId: s32me("course_id"),
|
||||
playCount: s32me("play"),
|
||||
isTouched: boolme("is_touch"),
|
||||
clearType: s32me("clear"),
|
||||
gauge: s32me("gauge"),
|
||||
score: s32me(),
|
||||
grade: s32me(),
|
||||
medal: s32me(),
|
||||
combo: s32me(),
|
||||
userId: ignoreme(),
|
||||
updateTime: ignoreme(),
|
||||
lastPlayTime: ignoreme()
|
||||
}
|
||||
|
||||
export interface IBst2Player {
|
||||
pdata: {
|
||||
account: IBst2Account
|
||||
base: IBst2Base
|
||||
opened: {}
|
||||
survey: IBst2Survey
|
||||
item: { info?: IBst2UnlockingInfo[] }
|
||||
customize: IBst2Customization
|
||||
tips: IBst2Tips
|
||||
hacker: { info?: IBst2Hacker[] }
|
||||
playLog: { crysis?: IBst2CrysisLog[] }
|
||||
bisco: { pinfo: IBst2Bisco }
|
||||
record: { rec?: IBst2MusicRecord[] }
|
||||
course: { record?: IBst2Course[] }
|
||||
}
|
||||
}
|
||||
export const Bst2PlayerMap: KM<IBst2Player> = {
|
||||
pdata: {
|
||||
account: Bst2AccountMap,
|
||||
base: Bst2BaseMap,
|
||||
opened: {},
|
||||
survey: Bst2SurveyMap,
|
||||
item: { info: { 0: Bst2UnlockingInfoMap } },
|
||||
customize: Bst2CustomizationMap,
|
||||
tips: Bst2TipsMap,
|
||||
hacker: { info: { 0: Bst2HackerMap } },
|
||||
playLog: { crysis: { 0: Bst2CrysisLogMap }, $targetKey: "play_log" },
|
||||
bisco: { pinfo: Bst2BiscoMap },
|
||||
record: { rec: { 0: Bst2MusicRecordMap } },
|
||||
course: { record: { 0: Bst2CourseMap } }
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import { colme, ignoreme, KM, s32me, strme } from "../../utility/mapping"
|
||||
import { ICollection } from "../utility/definitions"
|
||||
|
||||
export interface IBst2StageLog extends ICollection<"bst.bst2.playData.stageLog#userId"> {
|
||||
playerId: number
|
||||
continueCount: number
|
||||
stageId: number
|
||||
userId: number
|
||||
lobbyId: string
|
||||
musicId: number
|
||||
chart: number
|
||||
gaugeTimes10: number
|
||||
score: number
|
||||
combo: number
|
||||
grade: number
|
||||
medal: number
|
||||
fantasticCount: number
|
||||
greatCount: number
|
||||
fineCount: number
|
||||
missCount: number
|
||||
isCourseStage: boolean
|
||||
time: number
|
||||
}
|
||||
export const Bst2StageLogMap: KM<IBst2StageLog> = {
|
||||
collection: colme<IBst2StageLog>("bst.bst2.playData.stageLog#userId"),
|
||||
playerId: s32me("play_id"),
|
||||
continueCount: s32me("continue_count"),
|
||||
stageId: s32me("stage_no"),
|
||||
userId: s32me("user_id"),
|
||||
lobbyId: strme("location_id"),
|
||||
musicId: s32me("select_music_id"),
|
||||
chart: s32me("select_grade"),
|
||||
gaugeTimes10: s32me("result_clear_gauge"),
|
||||
score: s32me("result_score"),
|
||||
combo: s32me("result_max_combo"),
|
||||
grade: s32me("result_grade"),
|
||||
medal: s32me("result_medal"),
|
||||
fantasticCount: s32me("result_fanta"),
|
||||
greatCount: s32me("result_great"),
|
||||
fineCount: s32me("result_fine"),
|
||||
missCount: s32me("result_miss"),
|
||||
isCourseStage: ignoreme(),
|
||||
time: ignoreme(),
|
||||
}
|
||||
|
||||
export interface IBst2CourseLog extends ICollection<"bst.bst2.playData.courseLog#userId"> {
|
||||
playerId: number
|
||||
continueCount: number
|
||||
userId: number
|
||||
courseId: number
|
||||
gauge: number
|
||||
score: number
|
||||
grade: number
|
||||
medal: number
|
||||
combo: number
|
||||
fantasticCount: number
|
||||
greatCount: number
|
||||
fineCount: number
|
||||
missCount: number
|
||||
lobbyId: string
|
||||
time: number
|
||||
}
|
||||
export const Bst2CourseLogMap: KM<IBst2CourseLog> = {
|
||||
collection: colme<IBst2CourseLog>("bst.bst2.playData.courseLog#userId"),
|
||||
playerId: s32me("play_id"),
|
||||
continueCount: s32me("continue_count"),
|
||||
userId: s32me("user_id"),
|
||||
courseId: s32me("course_id"),
|
||||
lobbyId: strme("lid"),
|
||||
gauge: s32me(),
|
||||
score: s32me(),
|
||||
combo: s32me(),
|
||||
grade: s32me(),
|
||||
medal: s32me(),
|
||||
fantasticCount: s32me("fanta"),
|
||||
greatCount: s32me("great"),
|
||||
fineCount: s32me("fine"),
|
||||
missCount: s32me("miss"),
|
||||
time: ignoreme()
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { ICollection } from "./definitions"
|
||||
|
||||
export interface IBatchResult extends ICollection<"bst.batchResult"> {
|
||||
batchId: string
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export interface ICollection<TCollectionName extends string> {
|
||||
collection: TCollectionName
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { ICollection } from "./definitions"
|
||||
|
||||
export interface IPluginVersion<TMajor extends number = number, TMinor extends number = number, TRevision extends number = number> extends ICollection<"bst.pluginVersion"> {
|
||||
version: string
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ICollection } from "./definitions"
|
||||
|
||||
export interface IWebUIMessage extends ICollection<"utility.webuiMessage"> {
|
||||
message: string
|
||||
type: WebUIMessageType
|
||||
refid?: string
|
||||
version: number
|
||||
}
|
||||
|
||||
export enum WebUIMessageType {
|
||||
info = 0,
|
||||
success = 1,
|
||||
error = 2
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export type Game = "bst"
|
||||
export const game: Game = "bst"
|
||||
export type PluginVersion = "1.0.0"
|
||||
export const version: PluginVersion = "1.0.0"
|
||||
@@ -1,480 +0,0 @@
|
||||
import { ICollection } from "../models/utility/definitions"
|
||||
|
||||
export type KArrayType = KNumberType | KBigIntType
|
||||
export type KGroupType = KNumberGroupType | KBigIntGroupType
|
||||
export type KType = KArrayType | KGroupType | "str" | "bin" | "ip4" | "bool"
|
||||
export type KTypeExtended = KType | null | "kignore"
|
||||
export type TypeForKItem = number | string | bigint | BigIntProxy | boolean | Buffer | number[] | bigint[] | boolean[] | BufferArray | NumberGroup<number[] | bigint[]>
|
||||
export type TypeForKObject<T> = T extends TypeForKItem ? never : T
|
||||
export type TypeForKArray = number[] | bigint[] | BufferArray
|
||||
|
||||
export type KKey<T> = keyof T & (
|
||||
T extends string ? Exclude<keyof T, keyof string> :
|
||||
T extends Buffer ? Exclude<keyof T, keyof Buffer> :
|
||||
T extends boolean ? Exclude<keyof T, keyof boolean> :
|
||||
T extends number[] | bigint[] | boolean[] ? Exclude<keyof T, (keyof number[]) | (keyof bigint[]) | (keyof boolean[])> :
|
||||
T extends any[] ? Exclude<keyof T, keyof any[]> | number :
|
||||
T extends number ? Exclude<keyof T, keyof number> :
|
||||
T extends bigint | BigIntProxy ? Exclude<keyof T, keyof bigint> :
|
||||
T extends BufferArray ? Exclude<keyof T, keyof BufferArray> :
|
||||
T extends NumberGroup<infer TGroup> ? Exclude<keyof T, keyof NumberGroup<TGroup>> :
|
||||
keyof T)
|
||||
|
||||
export type KTypeConvert<T extends string | Buffer | number | bigint | boolean | number[] | bigint[] | unknown> =
|
||||
T extends string ? "str" :
|
||||
T extends Buffer ? "bin" :
|
||||
T extends number ? KNumberType | "ip4" | "bool" :
|
||||
T extends bigint | BigIntProxy ? KBigIntType :
|
||||
T extends boolean | boolean[] ? "bool" :
|
||||
T extends number[] ? KNumberType : // KARRAY
|
||||
T extends bigint[] ? KBigIntType : // KARRAY
|
||||
T extends NumberGroup<number[]> ? KNumberGroupType :
|
||||
T extends NumberGroup<bigint[]> ? KBigIntGroupType :
|
||||
T extends BufferArray ? "u8" | "s8" :
|
||||
never
|
||||
|
||||
export type KArrayTypeConvert<T extends Buffer | number[] | bigint[] | unknown> =
|
||||
T extends Buffer ? "s8" | "u8" :
|
||||
T extends number[] ? KNumberType :
|
||||
T extends bigint[] ? KBigIntType :
|
||||
never
|
||||
|
||||
export type KTypeConvertBack<TKType extends KTypeExtended> =
|
||||
TKType extends "str" ? string :
|
||||
TKType extends "bin" ? { type: "Buffer"; data: number[] } :
|
||||
TKType extends "s8" | "u8" ? [number] | number[] | { type: "Buffer"; data: number[] } :
|
||||
TKType extends KNumberType ? [number] | number[] :
|
||||
TKType extends KBigIntType ? [bigint] | bigint[] :
|
||||
TKType extends KNumberGroupType ? number[] :
|
||||
TKType extends KBigIntGroupType ? bigint[] :
|
||||
unknown
|
||||
|
||||
export type NumberGroup<T extends number[] | bigint[] = number[]> = {
|
||||
"@numberGroupValue": T
|
||||
}
|
||||
export const NumberGroup = <T extends number[] | bigint[] = number[]>(ng: T) => <NumberGroup>{ "@numberGroupValue": ng }
|
||||
export function isNumberGroup(value: any): value is NumberGroup {
|
||||
try {
|
||||
return Array.isArray(BigInt(value["@numberGroupValue"]))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export type BufferArray = {
|
||||
"@bufferArrayValue": Buffer
|
||||
}
|
||||
export const BufferArray = (ba: Buffer) => <BufferArray>{ "@bufferArrayValue": ba }
|
||||
export function isBufferArray(value: any): value is BufferArray {
|
||||
try {
|
||||
return value["@bufferArrayValue"] instanceof Buffer
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export type BigIntProxy = {
|
||||
"@serializedBigInt": string
|
||||
}
|
||||
export const BigIntProxy = (value: bigint) => <BigIntProxy>{ "@serializedBigInt": value.toString() }
|
||||
export function isBigIntProxy(value: any): value is BigIntProxy {
|
||||
try {
|
||||
return BigInt(value["@serializedBigInt"]).toString() == value["@serializedBigInt"]
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export function toBigInt(value: bigint | BigIntProxy): bigint {
|
||||
if (value == null) return null
|
||||
if (value instanceof BigInt) return <bigint>value
|
||||
else if (value["@serializedBigInt"] != null) return BigInt(value["@serializedBigInt"])
|
||||
else return BigInt(0)
|
||||
}
|
||||
|
||||
export type KITEM2<T> = { [K in keyof T]?: K extends KKey<T> ? KITEM2<T[K]> : never } &
|
||||
{
|
||||
["@attr"]: KAttrMap2<T>
|
||||
["@content"]:
|
||||
T extends string | Buffer | boolean | number[] | bigint[] ? T :
|
||||
T extends number | bigint ? [T] :
|
||||
T extends BufferArray ? Buffer :
|
||||
T extends NumberGroup<infer TGroup> ? TGroup :
|
||||
T extends BigIntProxy ? [bigint] : never
|
||||
}
|
||||
|
||||
export type KAttrMap2<T> = { [key: string]: string } & {
|
||||
__type?: T extends TypeForKItem ? KTypeConvert<T> : never
|
||||
__count?: T extends TypeForKArray ? number : never
|
||||
}
|
||||
|
||||
export function ITEM2<T>(ktype: KTypeConvert<T>, value: T, attr?: KAttrMap2<T>): KITEM2<T> {
|
||||
// let result
|
||||
// if (value instanceof NumberGroup && IsNumberGroupKType(ktype)) {
|
||||
// result = K.ITEM(<KTypeConvert<T & NumberGroup>>ktype, value.value, attr)
|
||||
// } else if (Array.isArray(value) && IsNumericKType(ktype)) {
|
||||
// result = K.ARRAY(<KTypeConvert<T & number[]>>ktype, <any>value, <any>attr)
|
||||
// } else if (value instanceof BufferArray && IsNumericKType(ktype)) {
|
||||
// result = K.ARRAY(<KTypeConvert<T & BufferArray>>ktype, value.value, attr)
|
||||
// } else if (typeof value != "object" && typeof value != "function") {
|
||||
// result = K.ITEM(<any>ktype, <any>value, attr)
|
||||
// } else {
|
||||
// Object.assign(result, value, { ["@attr"]: attr })
|
||||
// result["@attr"].__type = ktype
|
||||
// }
|
||||
|
||||
// return <KITEM2<T>>result
|
||||
let result = <KITEM2<T>>{}
|
||||
result["@attr"] = Object.assign({}, attr, (!isNumberGroupKType(ktype) && isNumericKType(ktype) && Array.isArray(value)) ? { __type: ktype, __count: (<any[]>value).length } : { __type: ktype })
|
||||
|
||||
if ((ktype == "bool") && (typeof value == "boolean")) {
|
||||
result["@content"] = <any>(value ? [1] : [0])
|
||||
} else if ((ktype == "bin") && value instanceof Buffer) {
|
||||
result = <any>K.ITEM("bin", value, result["@attr"])
|
||||
} else if (((ktype == "s8") || (ktype == "u8")) && isBufferArray(value)) {
|
||||
result["@content"] = <any>value["@bufferArrayValue"].toJSON()
|
||||
result["@attr"].__count = <any>value["@bufferArrayValue"].byteLength
|
||||
} else if (isNumericKType(ktype) && !Array.isArray(value)) {
|
||||
result["@content"] = <any>[value]
|
||||
} else if (isNumberGroupKType(ktype) && isNumberGroup(value)) {
|
||||
result["@content"] = <any>value["@numberGroupValue"]
|
||||
} else if (isBigIntProxy(value)) {
|
||||
result["@content"] = <any>BigInt(value["@serializedBigInt"])
|
||||
}
|
||||
else {
|
||||
result["@content"] = <any>value
|
||||
}
|
||||
if (isKIntType(ktype) && Array.isArray(result["@content"])) for (let i = 0; i < result["@content"].length; i++) (<number[]>result["@content"])[i] = Math.trunc(result["@content"][i])
|
||||
return result
|
||||
}
|
||||
|
||||
export type KObjectMappingRecord<T> = { [K in KKey<T>]: T[K] extends TypeForKItem ? KObjectMappingElementInfer<T[K]> : KObjectMappingRecord<T[K]> } & KObjectMappingElementInfer<T>
|
||||
export interface KObjectMappingElement<T = any, TKType extends KTypeExtended = KTypeExtended> {
|
||||
$type?: TKType,
|
||||
$targetKey?: string,
|
||||
$convert?: (source: T) => T
|
||||
$convertBack?: (target: T) => T
|
||||
$fallbackValue?: TKType extends "kignore" ? T : never
|
||||
$defaultValue?: T
|
||||
}
|
||||
type KObjectMappingElementInfer<T> = KObjectMappingElement<T, (KTypeConvert<T> extends KType ? KTypeConvert<T> : never) | never | "kignore">
|
||||
|
||||
export type KAttrRecord<T> = { [K in keyof T]?: T extends TypeForKItem ? KAttrMap2<T[K]> : KAttrRecord<T[K]> } & { selfAttr?: KAttrMap2<T> }
|
||||
|
||||
export function getCollectionMappingElement<TCollection extends ICollection<any>>(collectionName: TCollection extends ICollection<infer TName> ? TName : never): KObjectMappingElement<TCollection extends ICollection<infer TName> ? TName : unknown, "kignore"> {
|
||||
return ignoreme("collection", collectionName)
|
||||
}
|
||||
|
||||
function isKType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["s8", "u8", "s16", "u16", "s32", "u32", "time", "ip4", "float", "double", "bool", "s64", "u64", "2s8", "2u8", "2s16", "2u16", "2s32", "2u32", "2f", "2d", "3s8", "3u8", "3s16", "3u16", "3s32", "3u32", "3f", "3d", "4s8", "4u8", "4s16", "4u16", "4s32", "4u32", "4f", "4d", "2b", "3b", "4b", "vb", "2s64", "2u64", "3s64", "3u64", "4s64", "4u64", "vs8", "vu8", "vs16", "vu16", "str", "bin"].includes(type)
|
||||
}
|
||||
function isKIntType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["s8", "u8", "s16", "u16", "s32", "u32", "2s8", "2u8", "2s16", "2u16", "2s32", "2u32", "3s8", "3u8", "3s16", "3u16", "3s32", "3u32", "4s8", "4u8", "4s16", "4u16", "4s32", "4u32", "2b", "3b", "4b", "vb", "vs8", "vu8", "vs16", "vu16"].includes(type)
|
||||
}
|
||||
function isKBigIntType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["s64", "u64"].includes(type)
|
||||
}
|
||||
function isNumericKType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["s8", "u8", "s16", "u16", "s32", "u32", "time", "ip4", "float", "double", "bool", "s64", "u64"].includes(type)
|
||||
}
|
||||
function isNumberGroupKType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["2s8", "2u8", "2s16", "2u16", "2s32", "2u32", "2f", "2d", "3s8", "3u8", "3s16", "3u16", "3s32", "3u32", "3f", "3d", "4s8", "4u8", "4s16", "4u16", "4s32", "4u32", "4f", "4d", "2b", "3b", "4b", "vb", "2s64", "2u64", "3s64", "3u64", "4s64", "4u64", "vs8", "vu8", "vs16", "vu16"].includes(type)
|
||||
}
|
||||
function isNumericKey(k: any): boolean {
|
||||
return (typeof k == "number") || (parseInt(k).toString() == k)
|
||||
}
|
||||
function increaseNumericKey<T>(k: T, step: number = 1): T {
|
||||
return (typeof k == "number") ? <T><unknown>(k + step) : (typeof k == "string" && parseInt(k).toString() == k) ? <T><unknown>(parseInt(k) + step) : k
|
||||
}
|
||||
function isEmptyKObject(o: object): boolean {
|
||||
return (Object.keys(o).length == 0) || ((Object.keys(o).length == 1) && (o["@attr"] != null))
|
||||
}
|
||||
function isKMapRecordReservedKey(key: string): boolean {
|
||||
return ["$type", "$targetKey", "$convert", "$convertBack", "$fallbackValue", "$defaultValue"].includes(key)
|
||||
}
|
||||
function isKArray<T>(data: KITEM2<T>): boolean {
|
||||
return (data["@attr"] != null) && (data["@attr"].__count != null)
|
||||
}
|
||||
|
||||
export function appendMappingElement<T>(map: KObjectMappingRecord<T>, element: KObjectMappingElementInfer<T>): KObjectMappingRecord<T> {
|
||||
let result = <KObjectMappingRecord<T>>{}
|
||||
Object.assign(result, map, element)
|
||||
return result
|
||||
}
|
||||
|
||||
export function mapKObject<T>(data: T, kMapRecord: KObjectMappingRecord<T>, kAttrRecord: KAttrRecord<T> = <KAttrRecord<T>>{}): KITEM2<T> {
|
||||
if (data == null) return <KITEM2<T>>{}
|
||||
let result: KITEM2<T> = <any>(((0 in data) && data instanceof Object) ? [] : {})
|
||||
if (kAttrRecord.selfAttr != null) result["@attr"] = kAttrRecord.selfAttr
|
||||
|
||||
if (data instanceof Object) {
|
||||
for (let __k in data) {
|
||||
let k: keyof T = __k
|
||||
let mapK: keyof T = __k
|
||||
let attrK: keyof T = __k
|
||||
if (!(k in kMapRecord) && isNumericKey(k)) {
|
||||
for (let i = parseInt(<string>k) - 1; i >= 0; i--) if (kMapRecord[i]) {
|
||||
mapK = <keyof T>i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!(k in kAttrRecord) && isNumericKey(k)) {
|
||||
for (let i = parseInt(<string>k) - 1; i >= 0; i--) if (kAttrRecord[i]) {
|
||||
attrK = <keyof T>i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (mapK in kMapRecord) {
|
||||
let target = <KITEM2<T>[keyof T]>{}
|
||||
let targetMap = kMapRecord[<KKey<T>>mapK]
|
||||
let targetKey: keyof T = (targetMap.$targetKey != null) ? <keyof T>targetMap.$targetKey : k
|
||||
let targetValue = (targetMap.$convert != null) ? <KTypeConvertBack<KTypeConvert<T[keyof T]>>>targetMap.$convert(<any>data[k]) : data[k]
|
||||
let targetAttr = kAttrRecord[attrK]
|
||||
if (targetMap.$type) {
|
||||
let tt = targetMap.$type
|
||||
if (tt == "kignore") continue
|
||||
target["@attr"] = <any>Object.assign({}, targetAttr, (!isNumberGroupKType(tt) && isNumericKType(tt) && Array.isArray(data[k]) && Array.isArray(targetValue)) ? { __type: tt, __count: (<any[]>targetValue).length } : { __type: tt })
|
||||
|
||||
if ((tt == "bool") && (typeof targetValue == "boolean")) {
|
||||
target["@content"] = <any>(targetValue ? [1] : [0])
|
||||
} else if ((tt == "bin") && targetValue instanceof Buffer) {
|
||||
target = <any>K.ITEM("bin", targetValue, target["@attr"])
|
||||
} else if (((tt == "s8") || (tt == "u8")) && isBufferArray(targetValue)) {
|
||||
target["@content"] = <any>targetValue["@bufferArrayValue"]
|
||||
} else if (isNumericKType(tt) && !Array.isArray(targetValue)) {
|
||||
target["@content"] = <any>[targetValue]
|
||||
} else if (isNumberGroupKType(tt) && isNumberGroup(targetValue)) {
|
||||
target["@content"] = <any>targetValue["@numberGroupValue"]
|
||||
} else if (isBufferArray(targetValue)) {
|
||||
target["@content"] = <any>targetValue["@bufferArrayValue"].toJSON()
|
||||
target["@attr"].__count = <any>targetValue["@bufferArrayValue"].byteLength
|
||||
} else if (isBigIntProxy(targetValue)) {
|
||||
target["@content"] = <any>BigInt(targetValue["@serializedBigInt"])
|
||||
} else {
|
||||
target["@content"] = <any>targetValue
|
||||
}
|
||||
if (isKIntType(tt) && Array.isArray(target["@content"])) for (let i = 0; i < target["@content"].length; i++) (<number[]>target["@content"])[i] = Math.trunc(target["@content"][i])
|
||||
} else {
|
||||
target = <any>mapKObject(<T[keyof T]>targetValue, <KObjectMappingRecord<T[keyof T]>><unknown>targetMap, <KAttrRecord<T[keyof T]>>targetAttr)
|
||||
}
|
||||
result[targetKey] = target
|
||||
}
|
||||
}
|
||||
} else result = ITEM2<T>(<KTypeConvert<T>>kAttrRecord.selfAttr.$type, data, kAttrRecord.selfAttr)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export type MapBackResult<T> = {
|
||||
data: T,
|
||||
attr?: KAttrRecord<T>
|
||||
}
|
||||
export function mapBackKObject<T extends object>(data: KITEM2<T>, kMapRecord?: KObjectMappingRecord<T>): MapBackResult<T> {
|
||||
if (kMapRecord == null) {
|
||||
if (data["@content"] || data["@attr"]) return { data: <any>data["@content"], attr: <any>data["@attr"] }
|
||||
else return { data: <T>data }
|
||||
}
|
||||
let result: T = <T>((Array.isArray(data) || 0 in kMapRecord) ? [] : {})
|
||||
let resultAttr: KAttrRecord<T> = <any>{ selfAttr: data["@attr"] ? data["@attr"] : null }
|
||||
|
||||
for (let __k in kMapRecord) {
|
||||
if (isKMapRecordReservedKey(__k)) continue
|
||||
let k = <keyof T>__k
|
||||
let preservK = <keyof T>__k
|
||||
do {
|
||||
let targetMap = kMapRecord[<KKey<T>>preservK]
|
||||
let targetKey = <keyof T>(targetMap.$targetKey ? targetMap.$targetKey : k)
|
||||
let doOnceFlag = (isNumericKey(targetKey) && (data[targetKey] == null) && !isEmptyKObject(data))
|
||||
let targetValue = <KITEM2<T>[keyof T]>(doOnceFlag ? data : data[targetKey])
|
||||
|
||||
if (targetMap.$type == "kignore") {
|
||||
result[k] = targetMap.$fallbackValue
|
||||
if ((targetValue != null) && (targetValue["@attr"] != null)) resultAttr[k] = <KAttrRecord<T>[keyof T]>{ selfAttr: targetValue["@attr"] }
|
||||
continue
|
||||
}
|
||||
|
||||
if (targetValue == null) {
|
||||
if (targetMap.$convertBack != null) result[k] = targetMap.$convertBack(<any>null)
|
||||
continue
|
||||
}
|
||||
|
||||
if (targetValue["@attr"] != null) {
|
||||
let targetAttr: KAttrMap2<T[keyof T]> = targetValue["@attr"]
|
||||
let targetResult
|
||||
|
||||
if (targetAttr.__type != null) { // KITEM
|
||||
targetResult = targetValue["@content"]
|
||||
if (isNumberGroupKType(targetAttr.__type)) { // KITEM2<NumberGroup>
|
||||
// TODO: bigint number group
|
||||
targetResult = NumberGroup(targetResult)
|
||||
} else if (targetAttr.__type == "bin") { // KITEM<"bin">
|
||||
targetResult = targetResult
|
||||
} else if ((targetAttr.__type == "s8" || targetAttr.__type == "u8") && (targetResult?.type == "Buffer") && Array.isArray(targetResult?.data)) { // KITEM2<BufferArray>
|
||||
targetResult = BufferArray(Buffer.from(<number[]>targetResult.data))
|
||||
} else if (targetAttr.__type == "bool") { // KITEM<"bool">
|
||||
targetResult = targetResult[0] == 1 ? true : false
|
||||
} else if (Array.isArray(targetResult) && (targetAttr.__count == null) && isNumericKType(targetAttr.__type)) { // KITEM<KNumberType>
|
||||
targetResult = ((targetAttr.__type == "s64") || (targetAttr.__type == "u64")) ? BigIntProxy(BigInt(targetResult[0])) : targetResult[0]
|
||||
}
|
||||
result[k] = (targetMap.$convertBack != null) ? targetMap.$convertBack(<any>targetResult) : targetResult
|
||||
} else { // KObject
|
||||
targetResult = (targetMap.$convertBack != null) ? targetMap.$convertBack(<any>targetValue) : targetValue;
|
||||
let partial = mapBackKObject<T[keyof T] & object>(targetResult, <any>targetMap)
|
||||
result[k] = partial.data
|
||||
resultAttr[k] = <any>partial.attr
|
||||
}
|
||||
} else { // KObject
|
||||
let targetResult = (targetMap.$convertBack != null) ? targetMap.$convertBack(<any>targetValue) : targetValue;
|
||||
let partial = <any>mapBackKObject<T[keyof T] & object>(<any>targetResult, <any>targetMap)
|
||||
result[k] = partial.data
|
||||
resultAttr[k] = <any>partial.attr
|
||||
}
|
||||
k = increaseNumericKey(k)
|
||||
if (doOnceFlag || (isNumericKey(k) && (data[<keyof T>(targetMap.$targetKey ? targetMap.$targetKey : k)] == null))) break
|
||||
} while (isNumericKey(k) && !(k in kMapRecord))
|
||||
}
|
||||
return { data: result, attr: resultAttr }
|
||||
}
|
||||
|
||||
export function s8me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "s8"> {
|
||||
return {
|
||||
$type: "s8",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function u8me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "u8"> {
|
||||
return {
|
||||
$type: "u8",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function s16me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "s16"> {
|
||||
return {
|
||||
$type: "s16",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function u16me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "u16"> {
|
||||
return {
|
||||
$type: "u16",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function s32me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "s32"> {
|
||||
return {
|
||||
$type: "s32",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function u32me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "u32"> {
|
||||
return {
|
||||
$type: "u32",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function s64me(targetKey?: string, defaultValue?: bigint | BigIntProxy, convert?: (source: bigint | BigIntProxy) => bigint | BigIntProxy, convertBack?: (target: bigint | BigIntProxy) => bigint | BigIntProxy): KObjectMappingElement<bigint | BigIntProxy, "s64"> {
|
||||
return {
|
||||
$type: "s64",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function u64me(targetKey?: string, defaultValue?: bigint | BigIntProxy, convert?: (source: bigint | BigIntProxy) => bigint | BigIntProxy, convertBack?: (target: bigint | BigIntProxy) => bigint | BigIntProxy): KObjectMappingElement<bigint | BigIntProxy, "u64"> {
|
||||
return {
|
||||
$type: "u64",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
export function boolme<T extends boolean | boolean[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "bool"> {
|
||||
return {
|
||||
$type: "bool",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function strme<TName extends string>(targetKey?: string, defaultValue?: TName, convert?: (source: TName) => TName, convertBack?: (target: TName) => TName): KObjectMappingElement<TName, "str"> {
|
||||
return {
|
||||
$type: "str",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
export function binme(targetKey?: string, defaultValue?: Buffer, convert?: (source: Buffer) => Buffer, convertBack?: (target: Buffer) => Buffer): KObjectMappingElement<Buffer, "bin"> {
|
||||
return {
|
||||
$type: "bin",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
export function ignoreme<T = any>(targetKey?: string, fallbackValue?: T): KObjectMappingElement<T, "kignore"> {
|
||||
return {
|
||||
$type: "kignore",
|
||||
$fallbackValue: fallbackValue
|
||||
}
|
||||
}
|
||||
export function me<T extends object>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, null> {
|
||||
return {
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export const colme = getCollectionMappingElement
|
||||
export const appendme = appendMappingElement
|
||||
export const mapK = mapKObject
|
||||
export const bacK = mapBackKObject
|
||||
|
||||
export function fromMap<T>(map: KObjectMappingRecord<T>): T {
|
||||
let result = <T>{}
|
||||
if (map.$type == "kignore") return map.$fallbackValue
|
||||
if (map.$defaultValue != null) return map.$defaultValue
|
||||
if (map.$type != null) {
|
||||
if (isNumericKType(map.$type)) {
|
||||
if (map.$type == "bool") return <any>false
|
||||
else return <any>0
|
||||
} else if (isKBigIntType(map.$type)) return <any>BigInt(0)
|
||||
else if (isNumberGroupKType(map.$type)) return <any>NumberGroup([0])
|
||||
else if (map.$type == "str") return <any>""
|
||||
|
||||
else return null
|
||||
}
|
||||
for (let k in map) {
|
||||
if (isKMapRecordReservedKey(k)) continue
|
||||
let value = fromMap(map[k])
|
||||
if (value != null) result[k] = value
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export type KM<T> = KObjectMappingRecord<T>
|
||||
@@ -1,4 +0,0 @@
|
||||
export type FixedSizeArray<T, TSize extends number> = [T, ...T[]] & { readonly length: TSize }
|
||||
export function fillArray<T, TSize extends number>(size: TSize, fillValue: T): FixedSizeArray<T, TSize> {
|
||||
return <any>Array(size).fill(fillValue)
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
export function toFullWidth(s: string): string {
|
||||
let resultCharCodes: number[] = []
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
let cc = s.charCodeAt(i)
|
||||
if ((cc >= 33) && (cc <= 126)) resultCharCodes.push(cc + 65281 - 33)
|
||||
else if (cc == 32) resultCharCodes.push(12288) // Full-width space
|
||||
else resultCharCodes.push(cc)
|
||||
}
|
||||
return String.fromCharCode(...resultCharCodes)
|
||||
}
|
||||
export function toHalfWidth(s: string): string {
|
||||
let resultCharCodes: number[] = []
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
let cc = s.charCodeAt(i)
|
||||
if ((cc >= 65281) && (cc <= 65374)) resultCharCodes.push(cc - 65281 + 33)
|
||||
else if (cc == 12288) resultCharCodes.push(32) // Full-width space
|
||||
else resultCharCodes.push(cc)
|
||||
}
|
||||
return String.fromCharCode(...resultCharCodes)
|
||||
}
|
||||
export function isToday(st: bigint): boolean {
|
||||
let now = new Date()
|
||||
let today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
let tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
|
||||
return (st >= (today.valueOf())) && (st < (tomorrow.valueOf()))
|
||||
}
|
||||
export async function log(data: any, file?: string) {
|
||||
if (file == null) file = "./log.txt"
|
||||
let s = IO.Exists(file) ? await IO.ReadFile(file, "") : ""
|
||||
if (typeof data == "string") s += data + "\n"
|
||||
else {
|
||||
let n = ""
|
||||
try {
|
||||
n = JSON.stringify(data)
|
||||
} catch { }
|
||||
s += n + "\n"
|
||||
}
|
||||
await IO.WriteFile(file, s)
|
||||
}
|
||||
export function base64ToBuffer(str: string, size?: number): Buffer {
|
||||
if (size != null) {
|
||||
let rem = size - Math.trunc(size / 3) * 3
|
||||
str = str.replace("=", "A").replace("=", "A").padEnd(Math.trunc(size / 3) * 4 + rem + 1, "A")
|
||||
if (rem == 1) str += "=="
|
||||
else if (rem == 2) str += "="
|
||||
let result = Buffer.alloc(size, str, "base64")
|
||||
return result
|
||||
}
|
||||
else return Buffer.from(str, "base64")
|
||||
}
|
||||
export function bufferToBase64(buffer: Buffer, isTrimZero: boolean = true): string {
|
||||
if (isTrimZero) for (let i = buffer.length - 1; i >= 0; i--) if (buffer.readInt8(i) != 0) return buffer.toString("base64", 0, i + 1)
|
||||
return buffer.toString("base64")
|
||||
}
|
||||
export function isHigherVersion(left: string, right: string): boolean {
|
||||
let splitedLeft = left.split(".")
|
||||
let splitedRight = right.split(".")
|
||||
|
||||
if (parseInt(splitedLeft[0]) < parseInt(splitedRight[0])) return true
|
||||
else if (parseInt(splitedLeft[0]) == parseInt(splitedRight[0])) {
|
||||
if (parseInt(splitedLeft[1]) < parseInt(splitedRight[1])) return true
|
||||
else if (parseInt(splitedLeft[1]) == parseInt(splitedRight[1])) {
|
||||
if (parseInt(splitedLeft[2]) < parseInt(splitedRight[2])) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
#tab-content, .tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tab-content.is-active, .tab-content.is-active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
tr#tab-content.is-active, tr.tab-content.is-active {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
#tabs li.disabled a {
|
||||
background-color: #c0c0c0;
|
||||
border-color: #c0c0c0;
|
||||
color: #7f7f7f;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#form-pagination ul.pagination-list {
|
||||
margin: 0!important;
|
||||
}
|
||||
.pagination-link, .pagination-next, .pagination-previous {
|
||||
border-color: transparent;
|
||||
transition: .2s linear;
|
||||
}
|
||||
.pagination-next, .pagination-previous {
|
||||
color: #209CEE;
|
||||
}
|
||||
.pagination-next:not([disabled]):hover, .pagination-previous:not([disabled]):hover {
|
||||
color: #118fe4;
|
||||
}
|
||||
/* Set all link color to Asphyxia CORE blue */
|
||||
::selection {
|
||||
color: white;
|
||||
background-color: #209CEE;
|
||||
}
|
||||
a {
|
||||
color: #209CEE;
|
||||
}
|
||||
.tabs.is-toggle li.is-active a {
|
||||
background-color: #209CEE;
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.tabs li.is-active a {
|
||||
color: #209CEE;
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.pagination-link.is-current {
|
||||
background-color: #209CEE;
|
||||
border-color: #209CEE;
|
||||
cursor: default;
|
||||
}
|
||||
.select:not(.is-multiple):not(.is-loading):after {
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.select select:active, .select select:focus {
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.button.is-link {
|
||||
background-color: #209CEE;
|
||||
}
|
||||
.button.is-link.is-active, .button.is-link:active, .button.is-link.is-hovered, .button.is-link:hover {
|
||||
background-color: #118fe4;
|
||||
}
|
||||
.input:active, .input:focus {
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.table tr.is-selected {
|
||||
background-color: #209CEE;
|
||||
}
|
||||
|
||||
#card-content.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
#card-content {
|
||||
display: block;
|
||||
}
|
||||
.marquee-label {
|
||||
display: inline-block;
|
||||
}
|
||||
.marquee-label-container {
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* from Bulma */
|
||||
.button.is-danger.is-light {
|
||||
background-color: #feecf0;
|
||||
color: #cc0f35;
|
||||
}
|
||||
.button.is-link.is-light {
|
||||
background-color: #edf8ff;
|
||||
color: #209CEE;
|
||||
}
|
||||
.button.is-danger.is-light.is-hovered, .button.is-danger.is-light:hover {
|
||||
background-color: #fde0e6;
|
||||
color: #cc0f35;
|
||||
}
|
||||
.button.is-link.is-light.is-hovered, .button.is-link.is-light:hover {
|
||||
background-color: #e0f1fc;
|
||||
color: #209CEE;
|
||||
}
|
||||
.tag.is-link.is-light {
|
||||
background-color: #edf8ff;
|
||||
color: #0D7DC6;
|
||||
}
|
||||
.tag.is-link.is-light:hover {
|
||||
background-color: #209CEE;
|
||||
color: white;
|
||||
}
|
||||
.tag.is-delete:hover {
|
||||
background-color: #FF3860!important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.pagination {
|
||||
flex-wrap: nowrap;
|
||||
justify-content: left;
|
||||
}
|
||||
}
|
||||
.pagination-list {
|
||||
flex-wrap: nowrap;
|
||||
list-style: none!important;
|
||||
margin-top: 0.25em!important;
|
||||
margin-bottom: 0.25em!important;
|
||||
}
|
||||
|
||||
.content li + li {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.one-quarter#forwide, .one-third#forwide {
|
||||
display: block;
|
||||
min-width: 100px;
|
||||
}
|
||||
.one-quarter#fornarrow, .one-third#fornarrow {
|
||||
display: none;
|
||||
min-width: 50px;
|
||||
}
|
||||
@media only screen and (max-width: 1023px) {
|
||||
.one-quarter#forwide {
|
||||
display: none;
|
||||
}
|
||||
.one-quarter#fornarrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 700px) {
|
||||
.one-third#forwide {
|
||||
display: none;
|
||||
}
|
||||
.one-third#fornarrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes notification-fadeout {
|
||||
0% {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
}
|
||||
80% {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
}
|
||||
99.99% {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.notification {
|
||||
animation: notification-fadeout 8s forwards;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.notification:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.modal {
|
||||
padding-bottom: 13px;
|
||||
}
|
||||
@media screen and (max-width:1024px) {
|
||||
.modal {
|
||||
transition: padding-left .2s ease-in-out 50ms;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width:1023px) {
|
||||
.modal {
|
||||
padding-left: 256px;
|
||||
transition: padding-left .2s ease-in-out 50ms;
|
||||
}
|
||||
}
|
||||
.tag {
|
||||
transition: linear .2s;
|
||||
}
|
||||
.tags .tag:not(:last-child) {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.modal table tr {
|
||||
border: solid #dbdbdb;
|
||||
border-width: 0 0 1px;
|
||||
}
|
||||
.modal table tbody tr:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.hidden-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
.hidden-x-wrapper {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.hidden-y-wrapper {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
.scrolling-wrapper {
|
||||
overflow: auto;
|
||||
}
|
||||
.scrolling-x-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.scrolling-y-wrapper {
|
||||
overflow-y: auto;
|
||||
}
|
||||
a.pagination-previous {
|
||||
overflow: hidden;
|
||||
}
|
||||
a.pagination-next {
|
||||
overflow: hidden;
|
||||
}
|
||||
.button.checkbox, .button.checkbox .checkmark {
|
||||
transition: linear .2s;
|
||||
}
|
||||
@@ -1,618 +0,0 @@
|
||||
function initializePaginatedContent() {
|
||||
let containers = document.querySelectorAll(".paginated-container")
|
||||
|
||||
for (let container of containers) {
|
||||
let pageSizeInput = container.querySelector("input.page-size")
|
||||
let paginations = container.querySelectorAll(".pagination")
|
||||
let contents = container.querySelectorAll(".paginated-content")
|
||||
let group = container.getAttribute("pagination-group")
|
||||
let flags = { isFirst: true }
|
||||
let refreshEllipsis = (param) => {
|
||||
if (flags.isFirst) return
|
||||
let maxWidth = container.offsetWidth / 2
|
||||
for (let pagination of paginations) {
|
||||
let buttons = pagination.querySelector("ul.pagination-list")
|
||||
if (buttons.childElementCount == 0) return
|
||||
let show = (index) => buttons.querySelector("li[tab-index=\"" + index + "\"]").style.display = "block"
|
||||
let hide = (index) => buttons.querySelector("li[tab-index=\"" + index + "\"]").style.display = "none"
|
||||
let previousButton = pagination.querySelector("a.pagination-previous")
|
||||
let nextButton = pagination.querySelector("a.pagination-next")
|
||||
let leftEllipsis = buttons.querySelector("li.ellipsis-left")
|
||||
let rightEllipsis = buttons.querySelector("li.ellipsis-right")
|
||||
let width = buttons.firstChild.offsetWidth.toString()
|
||||
leftEllipsis.style.width = width + "px"
|
||||
rightEllipsis.style.width = width + "px"
|
||||
let count = buttons.childElementCount - 2
|
||||
let maxButtonCount = Math.max((buttons.firstChild.offsetWidth == 0) ? 5 : Math.trunc(maxWidth / buttons.firstChild.offsetWidth), 5)
|
||||
let current = (param instanceof HTMLElement) ? param : buttons.querySelector("li.is-active")
|
||||
let index = parseInt((current == null) ? 0 : current.getAttribute("tab-index"))
|
||||
if (index == 0) previousButton.setAttribute("disabled", "")
|
||||
else previousButton.removeAttribute("disabled")
|
||||
if (index == (count - 1)) nextButton.setAttribute("disabled", "")
|
||||
else nextButton.removeAttribute("disabled")
|
||||
if (count <= maxButtonCount) {
|
||||
for (let i = 0; i < count; i++) buttons.querySelector("li[tab-index=\"" + i + "\"]").style.display = "block"
|
||||
leftEllipsis.style.display = "none"
|
||||
rightEllipsis.style.display = "none"
|
||||
} else {
|
||||
maxButtonCount = Math.trunc((maxButtonCount - 1) / 2) * 2 + 1
|
||||
let maxSurroundingButtonCount = (maxButtonCount - 5) / 2
|
||||
let maxNoEllipsisIndex = maxButtonCount - 2 - maxSurroundingButtonCount - 1
|
||||
|
||||
if (index <= maxNoEllipsisIndex) {
|
||||
for (let i = 0; i <= (maxNoEllipsisIndex + maxSurroundingButtonCount); i++) show(i)
|
||||
for (let i = (maxNoEllipsisIndex + maxSurroundingButtonCount) + 1; i < count - 1; i++) hide(i)
|
||||
show(count - 1)
|
||||
leftEllipsis.style.display = "none"
|
||||
rightEllipsis.style.display = "block"
|
||||
} else if (index >= (count - maxNoEllipsisIndex - 1)) {
|
||||
for (let i = 1; i < (count - maxNoEllipsisIndex - maxSurroundingButtonCount - 1); i++) hide(i)
|
||||
for (let i = (count - maxNoEllipsisIndex - maxSurroundingButtonCount - 1); i < count; i++) show(i)
|
||||
show(0)
|
||||
leftEllipsis.style.display = "block"
|
||||
rightEllipsis.style.display = "none"
|
||||
} else {
|
||||
for (let i = 1; i < (index - maxSurroundingButtonCount); i++) hide(i)
|
||||
for (let i = (index - maxSurroundingButtonCount); i <= (index + maxSurroundingButtonCount); i++) show(i)
|
||||
for (let i = (index + maxSurroundingButtonCount) + 1; i < count - 1; i++) hide(i)
|
||||
show(0)
|
||||
show(count - 1)
|
||||
leftEllipsis.style.display = "block"
|
||||
rightEllipsis.style.display = "block"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let refresh = () => {
|
||||
if ((pageSizeInput == null) || (parseInt(pageSizeInput.value) <= 0)) {
|
||||
for (let pagination of paginations) pagination.style.display = "none"
|
||||
return
|
||||
}
|
||||
let pageSize = parseInt(pageSizeInput.value)
|
||||
let pageCount = Math.ceil(contents.length / pageSize)
|
||||
if (!flags.isFirst && (flags.pageSize == pageSize) && (flags.pageCount == pageCount)) return
|
||||
for (let pagination of paginations) {
|
||||
let buttons = pagination.querySelector("ul.pagination-list")
|
||||
buttons.innerHTML = ""
|
||||
buttons.id = "tabs"
|
||||
}
|
||||
for (let i = 0; i < pageCount; i++) {
|
||||
for (let j = i * pageSize; j < (i + 1) * pageSize; j++) {
|
||||
if (contents[j] == null) break
|
||||
contents[j].classList.add("tab-content")
|
||||
contents[j].setAttribute("tab-group", group)
|
||||
contents[j].setAttribute("tab-index", i)
|
||||
if ((i == 0) && (flags.isFirst || (flags.pageCount != pageCount))) contents[j].classList.add("is-active")
|
||||
if (j == ((i + 1) * pageSize - 1)) for (let td of contents[j].querySelectorAll("td")) td.style.borderBottom = "0"
|
||||
}
|
||||
if (pageCount > 1) for (let pagination of paginations) {
|
||||
let buttons = pagination.querySelector("ul.pagination-list")
|
||||
let a = document.createElement("a")
|
||||
a.classList.add("pagination-link")
|
||||
a.innerText = i + 1
|
||||
let li = document.createElement("li")
|
||||
li.appendChild(a)
|
||||
if ((i == 0) && (flags.isFirst || (flags.pageCount != pageCount))) {
|
||||
li.classList.add("is-active")
|
||||
a.classList.add("is-current")
|
||||
}
|
||||
li.setAttribute("tab-group", group)
|
||||
li.setAttribute("tab-index", i)
|
||||
buttons.appendChild(li)
|
||||
li.addEventListener("click", () => {
|
||||
refreshEllipsis(li)
|
||||
})
|
||||
}
|
||||
}
|
||||
if (pageCount > 1) for (let pagination of paginations) {
|
||||
pagination.style.display = "flex"
|
||||
let buttons = pagination.querySelector("ul.pagination-list")
|
||||
let leftEllipsis = document.createElement("li")
|
||||
leftEllipsis.style.display = "none"
|
||||
leftEllipsis.classList.add("ellipsis-left", "ignore")
|
||||
leftEllipsis.innerHTML = "<span class=\"pagination-ellipsis\">…</span>"
|
||||
let rightEllipsis = document.createElement("li")
|
||||
rightEllipsis.style.display = "none"
|
||||
rightEllipsis.classList.add("ellipsis-right", "ignore")
|
||||
rightEllipsis.innerHTML = "<span class=\"pagination-ellipsis\">…</span>"
|
||||
buttons.firstChild.after(leftEllipsis)
|
||||
buttons.lastChild.before(rightEllipsis)
|
||||
|
||||
let previousButton = pagination.querySelector("a.pagination-previous")
|
||||
let nextButton = pagination.querySelector("a.pagination-next")
|
||||
previousButton.addEventListener("click", () => {
|
||||
let current = buttons.querySelector("li.is-active")
|
||||
let index = parseInt(current.getAttribute("tab-index"))
|
||||
if (index <= 0) return
|
||||
let prev = buttons.querySelector("li[tab-index=\"" + (index - 1) + "\"]")
|
||||
prev.dispatchEvent(new Event("click"))
|
||||
})
|
||||
nextButton.addEventListener("click", () => {
|
||||
let current = buttons.querySelector("li.is-active")
|
||||
let index = parseInt(current.getAttribute("tab-index"))
|
||||
if (index >= (buttons.childElementCount - 3)) return // includes left & right ellipsis
|
||||
let next = buttons.querySelector("li[tab-index=\"" + (index + 1) + "\"]")
|
||||
next.dispatchEvent(new Event("click"))
|
||||
})
|
||||
} else for (let pagination of paginations) pagination.style.display = "none"
|
||||
flags.pageCount = pageCount
|
||||
flags.pageSize = pageSize
|
||||
flags.isFirst = false
|
||||
}
|
||||
refresh()
|
||||
pageSizeInput.addEventListener("change", refresh)
|
||||
let o = new ResizeObserver(refreshEllipsis)
|
||||
o.observe(container)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTabs() {
|
||||
let tabs = document.querySelectorAll("#tabs li")
|
||||
let tabContents = document.querySelectorAll("#tab-content, .tab-content")
|
||||
let updateActiveTab = (tabGroup, tabIndex) => {
|
||||
for (let t of tabs) if (t && (t.getAttribute("tab-group") == tabGroup)) {
|
||||
if (t.getAttribute("tab-index") != tabIndex) {
|
||||
t.classList.remove("is-active")
|
||||
for (let a of t.querySelectorAll("a")) a.classList.remove("is-current")
|
||||
} else {
|
||||
t.classList.add("is-active")
|
||||
for (let a of t.querySelectorAll("a")) a.classList.add("is-current")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let updateActiveContent = (tabGroup, tabIndex) => {
|
||||
for (let item of tabContents) {
|
||||
let group = item.getAttribute("tab-group")
|
||||
let index = item.getAttribute("tab-index")
|
||||
if (item && (group == tabGroup)) item.classList.remove("is-active")
|
||||
if ((index == tabIndex) && (group == tabGroup)) item.classList.add("is-active")
|
||||
}
|
||||
}
|
||||
for (let t of tabs) {
|
||||
if (!t.classList.contains("disabled") && !t.classList.contains("ignore")) t.addEventListener("click", () => {
|
||||
let group = t.getAttribute("tab-group")
|
||||
let index = t.getAttribute("tab-index")
|
||||
updateActiveTab(group, index)
|
||||
updateActiveContent(group, index)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeToggles() {
|
||||
let toggles = document.querySelectorAll(".card-header .card-toggle")
|
||||
let contents = document.querySelectorAll(".card-content")
|
||||
|
||||
for (let t of toggles) {
|
||||
let card = t.getAttribute("card")
|
||||
if (card == null) continue
|
||||
let cc = []
|
||||
for (let c of contents) if (c.getAttribute("card") == card) cc.push(c)
|
||||
t.style.transition = "0.2s linear"
|
||||
t.addEventListener("click", (e) => {
|
||||
if (e.currentTarget.style.transform == "rotate(180deg)") {
|
||||
e.currentTarget.style.transform = ""
|
||||
for (let c of cc) c.classList.remove("is-hidden")
|
||||
} else {
|
||||
e.currentTarget.style.transform = "rotate(180deg)"
|
||||
for (let c of cc) c.classList.add("is-hidden")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeModals() {
|
||||
let modaltriggers = $(".modal-trigger")
|
||||
for (let t of modaltriggers) {
|
||||
let m = t.querySelector(".modal")
|
||||
let c = m.querySelectorAll("#close")
|
||||
t.addEventListener("click", (e) => { m.style.display = "flex" })
|
||||
for (let v of c) v.addEventListener("click", (e) => {
|
||||
m.style.display = "none"
|
||||
e.stopPropagation()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormSelects() {
|
||||
let formSelects = document.querySelectorAll("#form-select")
|
||||
for (let s of formSelects) {
|
||||
let input = s.querySelector("input#form-select-input")
|
||||
let select = s.querySelector("select#form-select-select")
|
||||
let options = select.querySelectorAll("option")
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let o = options[i]
|
||||
let value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||
let enabled = (o.getAttribute("disabled") == null) ? true : false
|
||||
if (value == input.value) select.selectedIndex = i
|
||||
if (!enabled) o.style.display = "none"
|
||||
}
|
||||
select.addEventListener("change", () => {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let o = options[i]
|
||||
if (o.selected) {
|
||||
input.value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||
input.dispatchEvent(new Event("change"))
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormPaginations() {
|
||||
let formPags = document.querySelectorAll("#form-pagination")
|
||||
for (let p of formPags) {
|
||||
let input = p.querySelector("input#form-pagination-input")
|
||||
let options = p.querySelectorAll("ul.pagination-list li a.pagination-link")
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let o = options[i]
|
||||
let value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||
if (value == input.value) {
|
||||
if (!o.classList.contains("is-current")) o.classList.add("is-current")
|
||||
} else o.classList.remove("is-current")
|
||||
o.addEventListener("click", () => {
|
||||
for (let i = 0; i < options.length; i++) options[i].classList.remove("is-current")
|
||||
if (!o.classList.contains("is-current")) o.classList.add("is-current")
|
||||
input.value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormValidation() {
|
||||
let forms = document.querySelectorAll("form#validatable")
|
||||
for (let f of forms) {
|
||||
let validatableFields = f.querySelectorAll(".field#validatable")
|
||||
let validatableButtons = f.querySelectorAll("button#validatable")
|
||||
|
||||
let getParams = (input) => {
|
||||
return {
|
||||
minLength: input.getAttribute("min-length"),
|
||||
maxLength: input.getAttribute("max-length"),
|
||||
recommendedLength: input.getAttribute("recommended-length"),
|
||||
minPattern: input.getAttribute("min-pattern"),
|
||||
recommendedPattern: input.getAttribute("recommended-pattern"),
|
||||
isNumeric: (input.getAttribute("numeric") != null) ? true : false
|
||||
}
|
||||
}
|
||||
let isValid = (value, params) => {
|
||||
let t = value.trim()
|
||||
if (params.minLength != null) if (t.length < parseInt(params.minLength)) return false
|
||||
if (params.maxLength != null) if (t.length > parseInt(params.maxLength)) return false
|
||||
if (params.minPattern != null) if (!(new RegExp(params.minPattern).test(t))) return false
|
||||
if (params.isNumeric == true) if (parseInt(t).toString() != t) return false
|
||||
return true
|
||||
}
|
||||
|
||||
let isFormValid = () => {
|
||||
for (let field of validatableFields) for (let i of field.querySelectorAll("input#validatable")) if (!isValid(i.value, getParams(i))) return false
|
||||
return true
|
||||
}
|
||||
|
||||
for (let field of validatableFields) {
|
||||
let inputs = field.querySelectorAll("input#validatable")
|
||||
let tips = field.querySelectorAll(".help")
|
||||
for (let i of inputs) i.addEventListener("change", () => {
|
||||
let params = getParams(i)
|
||||
// inputs
|
||||
if (isValid(i.value, params)) {
|
||||
i.classList.remove("is-danger")
|
||||
for (let t of tips) t.classList.remove("is-danger")
|
||||
} else if (!i.classList.contains("is-danger")) {
|
||||
i.classList.add("is-danger")
|
||||
for (let t of tips) t.classList.add("is-danger")
|
||||
}
|
||||
// buttons
|
||||
if (isFormValid()) {
|
||||
for (let b of validatableButtons) b.removeAttribute("disabled")
|
||||
} else {
|
||||
for (let b of validatableButtons) if (b.getAttribute("disabled") == null) b.setAttribute("disabled", "")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormCollections() {
|
||||
let collections = document.querySelectorAll("#form-collection")
|
||||
for (let c of collections) {
|
||||
let maxLength = parseInt(c.getAttribute("max-length"))
|
||||
let fallbackValue = JSON.parse(c.getAttribute("fallback"))
|
||||
let input = c.querySelector("#form-collection-input")
|
||||
let tags = c.querySelectorAll("#form-collection-tag")
|
||||
let modButton = c.querySelector("#form-collection-modify")
|
||||
let modTable = c.querySelector("table#multi-select")
|
||||
let modInput = modTable.querySelector("input#multi-select-input")
|
||||
let modTitle = modTable.querySelector("input#multi-select-title")
|
||||
let deleteButtonClickEventListener = (tag) => () => {
|
||||
let tvalue = JSON.parse(tag.getAttribute("value"))
|
||||
let value = JSON.parse(input.value)
|
||||
value.splice(value.indexOf(tvalue), 1)
|
||||
if (fallbackValue != null) value.push(fallbackValue)
|
||||
input.value = JSON.stringify(value)
|
||||
modInput.value = input.value
|
||||
modInput.dispatchEvent(new Event("change"))
|
||||
tag.remove()
|
||||
}
|
||||
|
||||
for (let t of tags) {
|
||||
let d = t.querySelector(".delete, .is-delete")
|
||||
d.addEventListener("click", deleteButtonClickEventListener(t))
|
||||
}
|
||||
modInput.value = input.value
|
||||
modInput.setAttribute("max-length", maxLength)
|
||||
modInput.setAttribute("fallback", JSON.stringify(fallbackValue))
|
||||
modInput.addEventListener("change", () => {
|
||||
let fallbackValue = JSON.parse(c.getAttribute("fallback"))
|
||||
let oldValue = JSON.parse(input.value)
|
||||
let newValue = JSON.parse(modInput.value)
|
||||
let tags = c.querySelectorAll("#form-collection-tag")
|
||||
for (let o of oldValue) if (!newValue.includes(o) && (o != fallbackValue)) {
|
||||
for (let t of tags) if (JSON.parse(t.getAttribute("value")) == o) t.remove()
|
||||
}
|
||||
for (let n = 0; n < newValue.length; n++) if (!oldValue.includes(newValue[n]) && (newValue[n] != fallbackValue)) {
|
||||
let tag = document.createElement("div")
|
||||
tag.classList.add("control")
|
||||
tag.id = "form-collection-tag"
|
||||
tag.setAttribute("value", newValue[n])
|
||||
tag.innerHTML = "<span class=\"tags has-addons\"><span class=\"tag is-link is-light\" id=\"form-collection-tag-title\">" + JSON.parse(modTitle.value)[n] + "</span><a class=\"tag is-delete\" /></span>"
|
||||
tag.querySelector("a.is-delete").addEventListener("click", deleteButtonClickEventListener(tag))
|
||||
modButton.before(tag)
|
||||
}
|
||||
input.value = modInput.value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeMultiSelectTables() {
|
||||
let tables = document.querySelectorAll("table#multi-select")
|
||||
for (let table of tables) {
|
||||
let valueInput = table.querySelector("input#multi-select-input")
|
||||
let titleInput = table.querySelector("input#multi-select-title")
|
||||
let trimValues = (values, fallback) => {
|
||||
while (values.includes(fallback)) values.splice(values.indexOf(fallback), 1)
|
||||
return values
|
||||
}
|
||||
let fillValues = (values, fallback) => {
|
||||
let maxLength = (valueInput.getAttribute("max-length") == null) ? -1 : parseInt(valueInput.getAttribute("max-length"))
|
||||
while (values.length < maxLength) values.push(fallback)
|
||||
return values
|
||||
}
|
||||
let lines = table.querySelectorAll("tbody tr")
|
||||
let refresh = () => {
|
||||
let fallbackValue = JSON.parse(valueInput.getAttribute("fallback"))
|
||||
let value = trimValues(JSON.parse(valueInput.value), fallbackValue)
|
||||
let title = []
|
||||
for (let l of lines) {
|
||||
let lvalue = JSON.parse(l.getAttribute("multi-select-value"))
|
||||
if (value.includes(lvalue)) {
|
||||
if (!l.classList.contains("is-selected")) l.classList.add("is-selected")
|
||||
title[value.indexOf(lvalue)] = l.getAttribute("multi-select-title")
|
||||
l.style.fontWeight = "bold"
|
||||
} else {
|
||||
l.classList.remove("is-selected")
|
||||
l.style.fontWeight = ""
|
||||
}
|
||||
}
|
||||
titleInput.value = JSON.stringify(title)
|
||||
}
|
||||
|
||||
for (let l of lines) {
|
||||
l.onclick = () => {
|
||||
let fallbackValue = JSON.parse(valueInput.getAttribute("fallback"))
|
||||
let maxLength = (valueInput.getAttribute("max-length") == null) ? -1 : parseInt(valueInput.getAttribute("max-length"))
|
||||
let value = trimValues(JSON.parse(valueInput.value), fallbackValue)
|
||||
let lvalue = JSON.parse(l.getAttribute("multi-select-value"))
|
||||
if (value.includes(lvalue)) value.splice(value.indexOf(lvalue), 1)
|
||||
else if (maxLength >= 0) {
|
||||
if (value.length < maxLength) value.push(lvalue)
|
||||
else alert("Cannot add more items, items are up to " + maxLength + ".")
|
||||
} else value.push(lvalue)
|
||||
valueInput.value = JSON.stringify(fillValues(value, fallbackValue))
|
||||
refresh()
|
||||
valueInput.dispatchEvent(new Event("change"))
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
valueInput.addEventListener("change", refresh)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormNumerics() {
|
||||
let numerics = document.querySelectorAll("#form-numeric")
|
||||
for (let n of numerics) {
|
||||
let add = n.querySelector("#form-numeric-add")
|
||||
let sub = n.querySelector("#form-numeric-sub")
|
||||
let inputs = n.querySelectorAll("#form-numeric-input")
|
||||
add.addEventListener("click", (e) => {
|
||||
for (let i of inputs) {
|
||||
let maxValue = parseFloat(i.getAttribute("max-value"))
|
||||
let step = parseFloat(i.getAttribute("step"))
|
||||
|
||||
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||
let value = (parseFloat(i.value) * 10 + step * 10) / 10
|
||||
if (value * Math.sign(step) <= maxValue * Math.sign(step)) i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||
}
|
||||
e.stopPropagation()
|
||||
})
|
||||
sub.addEventListener("click", (e) => {
|
||||
for (let i of inputs) {
|
||||
let minValue = parseFloat(i.getAttribute("min-value"))
|
||||
let step = parseFloat(i.getAttribute("step"))
|
||||
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||
let value = (parseFloat(i.value) * 10 - step * 10) / 10
|
||||
if (value * Math.sign(step) >= minValue * Math.sign(step)) i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||
}
|
||||
e.stopPropagation()
|
||||
})
|
||||
for (let i of inputs) {
|
||||
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||
let value = parseFloat(i.value)
|
||||
i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeUploader() {
|
||||
let uploaders = document.querySelectorAll("div#uploader")
|
||||
for (let uploader of uploaders) {
|
||||
let input = uploader.querySelector("input#uploader-input")
|
||||
let text = uploader.querySelector("input#uploader-text")
|
||||
let placeholder = uploader.querySelector("#uploader-placeholder")
|
||||
let remove = uploader.querySelector("#uploader-delete")
|
||||
let reader = new FileReader()
|
||||
input.addEventListener("change", () => {
|
||||
if (input.files.length > 0) {
|
||||
remove.style.display = "block"
|
||||
placeholder.innerText = input.files[0].name
|
||||
reader.readAsText(input.files[0])
|
||||
reader.onload = () => text.value = reader.result
|
||||
} else {
|
||||
placeholder.innerText = ""
|
||||
remove.style.display = "none"
|
||||
text.value = null
|
||||
}
|
||||
})
|
||||
remove.addEventListener("click", (e) => {
|
||||
e.stopPropagation()
|
||||
input.value = null
|
||||
input.dispatchEvent(new Event("change"))
|
||||
})
|
||||
|
||||
remove.style.display = "none"
|
||||
}
|
||||
}
|
||||
|
||||
function checkImg() {
|
||||
let imgs = document.querySelectorAll("#exist-or-not")
|
||||
for (let img of imgs) {
|
||||
let general = img.querySelector("img#general")
|
||||
let specified = img.querySelector("img#specified")
|
||||
|
||||
if (specified.width == 0) specified.style.display = "none"
|
||||
else general.style.display = "none"
|
||||
}
|
||||
}
|
||||
|
||||
function initializeMarqueeLabels() {
|
||||
let marqueeContainers = document.querySelectorAll(".marquee-label-container")
|
||||
for (let c of marqueeContainers) {
|
||||
let marquees = c.querySelectorAll(".marquee-label")
|
||||
for (let marquee of marquees) {
|
||||
if (marquee.closest(".marquee-label-container") != c) continue
|
||||
let refresh = () => {
|
||||
let lpad = parseInt(window.getComputedStyle(c, null).getPropertyValue("padding-left"))
|
||||
if (lpad == NaN) lpad = 0
|
||||
let rpad = parseInt(window.getComputedStyle(c, null).getPropertyValue("padding-right"))
|
||||
if (rpad == NaN) rpad = 20
|
||||
let hpad = lpad + rpad
|
||||
let speed = marquee.getAttribute("speed")
|
||||
if (speed == null) speed = 1
|
||||
let stopingTime = 0.5
|
||||
let duration = (20 * (marquee.offsetWidth - c.offsetWidth + hpad)) / speed + 2 * stopingTime
|
||||
if ((marquee.offsetWidth > 0) && (marquee.offsetWidth > c.offsetWidth - hpad)) {
|
||||
marquee.animate([
|
||||
{ transform: "translateX(0)", offset: 0 },
|
||||
{ transform: "translateX(0)", easing: "cubic-bezier(0.67, 0, 0.33, 1)", offset: stopingTime / duration },
|
||||
{ transform: "translateX(" + (c.offsetWidth - marquee.offsetWidth - hpad) + "px)", easing: "cubic-bezier(0.67, 0, 0.33, 1)", offset: 1 - stopingTime / duration },
|
||||
{ transform: "translateX(" + (c.offsetWidth - marquee.offsetWidth - hpad) + "px)", offset: 1 }
|
||||
], { duration: (20 * (marquee.offsetWidth - c.offsetWidth) + 1000) / speed, direction: "alternate-reverse", iterations: Infinity })
|
||||
} else marquee.style.animation = "none"
|
||||
}
|
||||
let o = new ResizeObserver(refresh)
|
||||
o.observe(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeNotificatioAnimation() {
|
||||
let notifications = document.querySelectorAll(".notification.temporary")
|
||||
for (let n of notifications) {
|
||||
let remove = n.querySelector(".delete")
|
||||
let startSubmitter = n.querySelector("form.start")
|
||||
let startPath = startSubmitter.getAttribute("action")
|
||||
let startRequest = new XMLHttpRequest()
|
||||
startRequest.open("POST", startPath, true)
|
||||
startRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
let endSubmitter = n.querySelector("form.end")
|
||||
let endPath = startSubmitter.getAttribute("action")
|
||||
let endRequest = new XMLHttpRequest()
|
||||
endRequest.open("POST", endPath, true)
|
||||
endRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
if (startSubmitter != null) startRequest.send()
|
||||
let end = () => {
|
||||
n.style.display = "none"
|
||||
if (endSubmitter != null) endRequest.send()
|
||||
}
|
||||
|
||||
n.style.animationPlayState = "running"
|
||||
remove.addEventListener("click", end)
|
||||
n.addEventListener("animationend", end)
|
||||
n.addEventListener("webkitAnimationEnd", end)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeCheckBoxes() {
|
||||
let checks = document.querySelectorAll(".checkbox")
|
||||
for (let c of checks) {
|
||||
let input = c.querySelector("input[type=checkbox]")
|
||||
let mark = c.querySelector(".checkmark")
|
||||
let refresh = (value) => {
|
||||
value = input.getAttribute("checked")
|
||||
if (value == null) {
|
||||
input.removeAttribute("checked")
|
||||
mark.style.opacity = 0
|
||||
if (!c.classList.contains("is-light")) c.classList.add("is-light")
|
||||
} else {
|
||||
input.setAttribute("checked", "checked")
|
||||
mark.style.opacity = 100
|
||||
c.classList.remove("is-light")
|
||||
}
|
||||
}
|
||||
c.addEventListener("click", () => {
|
||||
let value = input.getAttribute("checked")
|
||||
if (value == null) input.setAttribute("checked", "checked")
|
||||
else input.removeAttribute("checked")
|
||||
refresh()
|
||||
})
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
function removeLoadingModal() {
|
||||
let loading = document.querySelector(".loading")
|
||||
setTimeout(() => (loading == null) ? null : loading.remove(), 505)
|
||||
try {
|
||||
let a = loading.animate([
|
||||
{ offset: 0, opacity: 1 },
|
||||
{ offset: 0.25, opacity: 0 },
|
||||
{ offset: 1, opacity: 0 }
|
||||
], { duration: 2000 })
|
||||
a.onfinish = loading.remove
|
||||
a.play()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
initializeNotificatioAnimation()
|
||||
initializePaginatedContent()
|
||||
initializeTabs()
|
||||
initializeToggles()
|
||||
initializeModals()
|
||||
initializeFormSelects()
|
||||
initializeFormNumerics()
|
||||
initializeFormPaginations()
|
||||
initializeFormValidation()
|
||||
initializeFormCollections()
|
||||
initializeMultiSelectTables()
|
||||
initializeUploader()
|
||||
checkImg()
|
||||
initializeMarqueeLabels()
|
||||
initializeCheckBoxes()
|
||||
|
||||
removeLoadingModal()
|
||||
})
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
*.xml
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
export const SONGS_WORLD = [
|
||||
{ mcode: 38665, limited_ary: [2, 2, 2, 2, 2, -1, 2, 2, 2, 2], diffLv: [3, 6, 10, 13, 15, 0, 6, 10, 13, 15] },
|
||||
{ mcode: 38661, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [3, 5, 9, 12, 0, 0, 5, 9, 12, 0] },
|
||||
{ mcode: 38659, limited_ary: [2, 2, 2, 2, 2, -1, 2, 2, 2, 2], diffLv: [3, 7, 12, 14, 16, 0, 7, 12, 14, 16] },
|
||||
{ mcode: 38669, limited_ary: [2, 2, 2, 2, 2, -1, 2, 2, 2, 2], diffLv: [3, 7, 11, 14, 15, 0, 7, 11, 14, 15] },
|
||||
{ mcode: 38668, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [3, 6, 10, 13, 0, 0, 6, 10, 13, 0] },
|
||||
{ mcode: 38667, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [3, 6, 9, 12, 0, 0, 6, 9, 12, 0] },
|
||||
{ mcode: 38663, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 7, 10, 13, 0, 0, 7, 10, 13, 0] },
|
||||
{ mcode: 38660, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [1, 4, 8, 12, 0, 0, 4, 8, 12, 0] },
|
||||
{ mcode: 38688, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 7, 11, 14, 0, 0, 7, 11, 14, 0] },
|
||||
{ mcode: 38672, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 4, 9, 13, 0, 0, 4, 9, 13, 0] },
|
||||
{ mcode: 38662, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 5, 8, 13, 0, 0, 4, 8, 13, 0] },
|
||||
{ mcode: 38731, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [1, 3, 9, 13, 0, 0, 3, 9, 12, 0] },
|
||||
{ mcode: 38722, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 6, 10, 14, 0, 0, 6, 10, 14, 0] },
|
||||
{ mcode: 38704, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [1, 4, 9, 12, 0, 0, 4, 9, 12, 0] },
|
||||
{ mcode: 38724, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 5, 9, 13, 0, 0, 4, 9, 13, 0] },
|
||||
{ mcode: 38730, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 4, 8, 13, 0, 0, 4, 8, 13, 0] },
|
||||
{ mcode: 38723, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [2, 4, 9, 14, 12, 0, 5, 10, 14, 12] },
|
||||
{ mcode: 38721, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 4, 8, 12, 0, 0, 4, 8, 12, 0] },
|
||||
{ mcode: 38706, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [1, 4, 9, 13, 0, 0, 4, 9, 13, 0] },
|
||||
{ mcode: 38705, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 6, 10, 13, 0, 0, 6, 10, 13, 0] },
|
||||
{ mcode: 38727, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 7, 11, 14, 0, 0, 7, 11, 14, 0] },
|
||||
{ mcode: 38739, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [3, 5, 9, 12, 15, 0, 5, 9, 12, 15] },
|
||||
{ mcode: 38664, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 7, 9, 12, 0, 0, 7, 9, 12, 0] },
|
||||
{ mcode: 38670, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [4, 7, 10, 13, 0, 0, 7, 10, 13, 0] },
|
||||
{ mcode: 38680, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 6, 11, 14, 0, 0, 6, 11, 14, 0] },
|
||||
{ mcode: 38666, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 7, 10, 14, 0, 0, 7, 10, 14, 0] },
|
||||
{ mcode: 38671, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [1, 4, 8, 13, 0, 0, 4, 8, 13, 0] },
|
||||
{ mcode: 38677, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 6, 9, 12, 0, 0, 6, 9, 12, 0] },
|
||||
{ mcode: 38678, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [1, 5, 9, 11, 0, 0, 3, 9, 12, 0] },
|
||||
{ mcode: 38707, limited_ary: [0, -1, -1, -1, -1, -1, -1, -1, -1, -1], diffLv: [6, 0, 0, 0, 0, 0, 0, 0, 0, 0] },
|
||||
{ mcode: 38695, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 7, 11, 14, 0, 0, 7, 11, 14, 0] },
|
||||
{ mcode: 38692, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 7, 12, 15, 0, 0, 7, 12, 15, 0] },
|
||||
{ mcode: 38694, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 6, 11, 14, 0, 0, 6, 11, 14, 0] },
|
||||
{ mcode: 38691, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 8, 12, 15, 0, 0, 7, 12, 15, 0] },
|
||||
{ mcode: 38693, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [3, 7, 11, 15, 0, 0, 7, 11, 15, 0] },
|
||||
{ mcode: 38733, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 5, 9, 12, 0, 0, 5, 9, 12, 0] },
|
||||
{ mcode: 38736, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [2, 5, 8, 13, 0, 0, 5, 8, 13, 0] },
|
||||
{ mcode: 38735, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [3, 6, 10, 16, 0, 0, 6, 10, 16, 0] },
|
||||
{ mcode: 38728, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [3, 7, 12, 15, 0, 0, 7, 12, 15, 0] },
|
||||
{ mcode: 38746, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [4, 8, 14, 16, 0, 0, 8, 14, 16, 0] },
|
||||
{ mcode: 38745, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [3, 7, 12, 16, 0, 0, 7, 12, 16, 0] },
|
||||
{ mcode: 38744, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [4, 7, 13, 17, 0, 0, 7, 13, 17, 0] },
|
||||
{ mcode: 38747, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [4, 10, 15, 18, 0, 0, 10, 15, 18, 0] },
|
||||
{ mcode: 38748, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [4, 9, 13, 16, 0, 0, 9, 13, 16, 0] },
|
||||
{ mcode: 38734, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [2, 2, 7, 11, 13, 0, 2, 7, 11, 14] },
|
||||
{ mcode: 38749, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [3, 6, 10, 13, 8, 0, 6, 10, 13, 9] },
|
||||
|
||||
{ mcode: 38737, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [3, 6, 11, 13, 16, 0, 6, 11, 13, 16] },
|
||||
{ mcode: 38738, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [2, 5, 8, 12, 17, 5, 8, 12, 17] },
|
||||
|
||||
{ mcode: 37222, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 4, 10, 13, 0, 0, 4, 9, 13, 0] },
|
||||
{ mcode: 37604, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [2, 6, 9, 12, 14, 0, 5, 9, 12, 14] },
|
||||
{ mcode: 37234, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [1, 3, 7, 11, 0, 0, 3, 7, 11, 0] },
|
||||
{ mcode: 37363, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 5, 9, 13, 0, 0, 5, 9, 13, 0] }
|
||||
|
||||
]
|
||||
|
||||
export const SONGS_OVERRIDE_WORLD = [
|
||||
// Override info for old songs
|
||||
{ mcode: 38624, limited_ary: [2, 2, 2, 2, 2, -1, 2, 2, 2, 2], diffLv: [2, 5, 10, 15, 17, 0, 5, 10, 15, 17] },
|
||||
{ mcode: 38623, limited_ary: [2, 2, 2, 2, 2, -1, 2, 2, 2, 2], diffLv: [3, 11, 15, 17, 18, 0, 11, 14, 17, 18] },
|
||||
{ mcode: 38628, limited_ary: [2, 2, 2, 2, -1, -1, 2, 2, 2, -1], diffLv: [5, 9, 14, 18, 0, 0, 9, 14, 18, 0] },
|
||||
{ mcode: 11, limited_ary: [], diffLv: [3, 6, 6, 10, 10, 0, 6, 8, 9, 10] },
|
||||
{ mcode: 13, limited_ary: [], diffLv: [4, 6, 8, 9, 12, 0, 7, 8, 9, 12] },
|
||||
{ mcode: 84, limited_ary: [], diffLv: [3, 7, 8, 11, 0, 0, 9, 11, 12, 0] },
|
||||
{ mcode: 137, limited_ary: [], diffLv: [2, 3, 6, 8, 0, 0, 5, 6, 8, 0] },
|
||||
{ mcode: 126, limited_ary: [], diffLv: [3, 6, 10, 12, 12, 0, 7, 8, 11, 12] },
|
||||
{ mcode: 128, limited_ary: [], diffLv: [4, 7, 8, 10, 0, 0, 5, 9, 13, 0] },
|
||||
{ mcode: 182, limited_ary: [], diffLv: [2, 2, 4, 7, 0, 0, 3, 5, 8, 0] },
|
||||
{ mcode: 155, limited_ary: [], diffLv: [3, 5, 7, 11, 0, 0, 5, 7, 12, 0] },
|
||||
{ mcode: 221, limited_ary: [], diffLv: [3, 6, 8, 11, 0, 0, 7, 8, 10, 0] },
|
||||
{ mcode: 159, limited_ary: [], diffLv: [5, 7, 8, 11, 0, 0, 4, 8, 10, 0] },
|
||||
{ mcode: 160, limited_ary: [], diffLv: [5, 9, 10, 12, 0, 0, 7, 10, 13, 0] },
|
||||
{ mcode: 257, limited_ary: [], diffLv: [3, 5, 7, 10, 0, 0, 6, 8, 11, 0] },
|
||||
{ mcode: 267, limited_ary: [], diffLv: [4, 7, 9, 12, 0, 0, 8, 9, 13, 0] },
|
||||
{ mcode: 238, limited_ary: [], diffLv: [5, 6, 9, 12, 0, 0, 7, 9, 12, 0] },
|
||||
{ mcode: 365, limited_ary: [], diffLv: [4, 5, 7, 10, 0, 0, 4, 7, 10, 0] },
|
||||
{ mcode: 472, limited_ary: [], diffLv: [3, 4, 6, 9, 0, 0, 5, 7, 9, 0] },
|
||||
{ mcode: 480, limited_ary: [], diffLv: [5, 6, 8, 12, 12, 0, 7, 8, 12, 11] },
|
||||
{ mcode: 478, limited_ary: [], diffLv: [3, 4, 7, 10, 0, 0, 6, 9, 11, 0] },
|
||||
{ mcode: 436, limited_ary: [], diffLv: [4, 5, 7, 12, 0, 0, 5, 8, 12, 0] },
|
||||
{ mcode: 429, limited_ary: [], diffLv: [3, 6, 7, 9, 0, 0, 5, 7, 11, 0] },
|
||||
{ mcode: 476, limited_ary: [], diffLv: [3, 4, 7, 10, 0, 0, 4, 7, 10, 0] },
|
||||
{ mcode: 467, limited_ary: [], diffLv: [2, 4, 7, 9, 0, 0, 4, 7, 11, 0] },
|
||||
{ mcode: 437, limited_ary: [], diffLv: [3, 5, 7, 8, 0, 0, 4, 7, 9, 0] },
|
||||
{ mcode: 33811, limited_ary: [], diffLv: [2, 3, 9, 11, 0, 0, 3, 6, 10, 0] },
|
||||
{ mcode: 36914, limited_ary: [], diffLv: [2, 3, 7, 12, 0, 0, 4, 9, 11, 0] },
|
||||
{ mcode: 415, limited_ary: [], diffLv: [3, 4, 7, 11, 0, 0, 5, 7, 10, 0] },
|
||||
{ mcode: 33818, limited_ary: [], diffLv: [7, 10, 13, 15, 16, 0, 10, 12, 15, 17] },
|
||||
{ mcode: 33806, limited_ary: [], diffLv: [3, 7, 9, 10, 0, 0, 8, 10, 12, 0] },
|
||||
{ mcode: 33048, limited_ary: [], diffLv: [3, 6, 7, 11, 0, 0, 5, 7, 8, 0] },
|
||||
{ mcode: 36931, limited_ary: [], diffLv: [4, 4, 8, 10, 0, 0, 4, 8, 10, 0] },
|
||||
{ mcode: 36873, limited_ary: [], diffLv: [5, 5, 9, 11, 0, 0, 4, 9, 11, 0] },
|
||||
{ mcode: 36935, limited_ary: [], diffLv: [3, 6, 8, 11, 0, 0, 5, 8, 11, 0] },
|
||||
{ mcode: 36878, limited_ary: [], diffLv: [3, 4, 7, 9, 0, 0, 4, 7, 9, 0] },
|
||||
{ mcode: 37267, limited_ary: [], diffLv: [3, 4, 7, 10, 0, 0, 5, 7, 11, 0] },
|
||||
{ mcode: 37190, limited_ary: [], diffLv: [3, 5, 7, 9, 11, 0, 5, 7, 9, 12] },
|
||||
{ mcode: 37121, limited_ary: [], diffLv: [3, 7, 9, 13, 0, 0, 6, 9, 12, 0] },
|
||||
{ mcode: 37290, limited_ary: [], diffLv: [2, 5, 9, 12, 0, 0, 5, 7, 12, 0] },
|
||||
{ mcode: 37295, limited_ary: [], diffLv: [4, 6, 9, 11, 0, 0, 7, 8, 11, 0] },
|
||||
{ mcode: 37408, limited_ary: [], diffLv: [2, 3, 7, 9, 0, 0, 5, 6, 9, 0] },
|
||||
{ mcode: 37228, limited_ary: [], diffLv: [2, 4, 7, 11, 0, 0, 5, 7, 11, 0] },
|
||||
{ mcode: 37686, limited_ary: [], diffLv: [2, 4, 7, 10, 8, 0, 4, 7, 11, 8] },
|
||||
{ mcode: 37623, limited_ary: [], diffLv: [2, 4, 6, 9, 12, 0, 4, 7, 10, 12] },
|
||||
{ mcode: 37694, limited_ary: [], diffLv: [2, 5, 8, 11, 13, 0, 5, 8, 11, 13] },
|
||||
{ mcode: 37666, limited_ary: [], diffLv: [3, 5, 8, 11, 0, 0, 4, 8, 13, 0] },
|
||||
{ mcode: 37749, limited_ary: [], diffLv: [4, 6, 9, 12, 16, 0, 6, 9, 12, 16] },
|
||||
{ mcode: 37511, limited_ary: [], diffLv: [2, 4, 7, 9, 0, 0, 4, 7, 9, 0] },
|
||||
{ mcode: 37698, limited_ary: [], diffLv: [2, 2, 8, 11, 0, 0, 2, 9, 11, 0] },
|
||||
{ mcode: 37662, limited_ary: [], diffLv: [3, 8, 10, 14, 16, 0, 9, 11, 15, 16] },
|
||||
{ mcode: 37532, limited_ary: [], diffLv: [2, 4, 6, 9, 0, 0, 5, 7, 10, 0] },
|
||||
{ mcode: 37644, limited_ary: [], diffLv: [3, 6, 11, 15, 18, 0, 6, 9, 14, 18] },
|
||||
{ mcode: 37803, limited_ary: [], diffLv: [2, 4, 8, 12, 15, 0, 4, 8, 12, 15] },
|
||||
{ mcode: 37775, limited_ary: [], diffLv: [2, 5, 7, 10, 13, 0, 5, 8, 11, 13] },
|
||||
{ mcode: 37784, limited_ary: [], diffLv: [2, 4, 7, 9, 0, 0, 4, 6, 9, 0] },
|
||||
{ mcode: 37765, limited_ary: [], diffLv: [2, 4, 7, 10, 0, 0, 4, 7, 11, 0] },
|
||||
{ mcode: 37586, limited_ary: [], diffLv: [2, 4, 7, 9, 0, 0, 5, 8, 10, 0] },
|
||||
{ mcode: 37793, limited_ary: [], diffLv: [3, 6, 7, 10, 0, 0, 6, 7, 11, 0] },
|
||||
{ mcode: 37736, limited_ary: [], diffLv: [2, 4, 8, 10, 0, 0, 4, 8, 10, 0] },
|
||||
{ mcode: 37794, limited_ary: [], diffLv: [3, 4, 7, 11, 12, 0, 5, 7, 11, 12] },
|
||||
{ mcode: 37798, limited_ary: [], diffLv: [2, 5, 7, 11, 0, 0, 3, 7, 11, 0] },
|
||||
{ mcode: 37862, limited_ary: [], diffLv: [2, 5, 8, 13, 0, 0, 5, 9, 13, 0] },
|
||||
{ mcode: 37657, limited_ary: [], diffLv: [2, 3, 8, 10, 0, 0, 4, 8, 10, 0] },
|
||||
{ mcode: 37587, limited_ary: [], diffLv: [2, 5, 7, 9, 0, 0, 5, 7, 10, 0] },
|
||||
{ mcode: 37840, limited_ary: [], diffLv: [2, 4, 8, 11, 10, 0, 4, 8, 11, 11] },
|
||||
{ mcode: 37800, limited_ary: [], diffLv: [3, 5, 10, 12, 16, 0, 6, 9, 12, 16] },
|
||||
{ mcode: 37565, limited_ary: [], diffLv: [2, 5, 7, 12, 0, 0, 5, 7, 11, 0] },
|
||||
{ mcode: 37588, limited_ary: [], diffLv: [2, 4, 6, 9, 0, 0, 4, 6, 9, 0] },
|
||||
{ mcode: 37907, limited_ary: [], diffLv: [3, 7, 9, 13, 16, 0, 7, 10, 13, 16] },
|
||||
{ mcode: 37930, limited_ary: [], diffLv: [2, 7, 9, 14, 13, 0, 7, 9, 14, 13] },
|
||||
{ mcode: 37827, limited_ary: [], diffLv: [2, 5, 8, 11, 0, 0, 5, 9, 11, 0] },
|
||||
{ mcode: 37543, limited_ary: [], diffLv: [2, 3, 7, 9, 13, 0, 4, 7, 9, 13] },
|
||||
{ mcode: 37239, limited_ary: [], diffLv: [3, 4, 7, 12, 15, 0, 4, 7, 12, 15] },
|
||||
{ mcode: 37934, limited_ary: [], diffLv: [3, 7, 10, 14, 0, 0, 6, 10, 14, 0] },
|
||||
{ mcode: 37908, limited_ary: [], diffLv: [3, 7, 9, 11, 13, 0, 6, 9, 11, 13] },
|
||||
{ mcode: 37597, limited_ary: [], diffLv: [2, 4, 6, 10, 0, 0, 4, 6, 10, 0] },
|
||||
{ mcode: 37927, limited_ary: [], diffLv: [2, 6, 9, 13, 0, 0, 6, 9, 13, 0] },
|
||||
{ mcode: 37932, limited_ary: [], diffLv: [3, 4, 11, 16, 9, 0, 7, 12, 16, 9] },
|
||||
{ mcode: 37889, limited_ary: [], diffLv: [2, 7, 9, 12, 15, 0, 7, 10, 12, 15] },
|
||||
{ mcode: 37965, limited_ary: [], diffLv: [2, 4, 8, 10, 0, 0, 5, 9, 13, 0] },
|
||||
{ mcode: 37887, limited_ary: [], diffLv: [2, 5, 8, 11, 13, 0, 5, 8, 11, 13] },
|
||||
{ mcode: 38030, limited_ary: [], diffLv: [6, 10, 13, 16, 18, 0, 9, 13, 16, 18] },
|
||||
{ mcode: 38032, limited_ary: [], diffLv: [8, 12, 15, 16, 19, 0, 12, 16, 18, 19] },
|
||||
{ mcode: 38027, limited_ary: [], diffLv: [3, 7, 12, 15, 0, 0, 7, 12, 15, 0] },
|
||||
{ mcode: 38051, limited_ary: [], diffLv: [7, 10, 13, 17, 0, 0, 10, 13, 18, 0] },
|
||||
{ mcode: 37988, limited_ary: [], diffLv: [3, 4, 10, 14, 0, 0, 4, 10, 14, 0] },
|
||||
{ mcode: 38010, limited_ary: [], diffLv: [4, 8, 11, 15, 18, 0, 7, 12, 15, 18] },
|
||||
{ mcode: 38054, limited_ary: [], diffLv: [4, 6, 11, 15, 0, 0, 6, 11, 15, 0] },
|
||||
{ mcode: 38079, limited_ary: [], diffLv: [2, 5, 8, 10, 0, 0, 5, 8, 10, 0] },
|
||||
{ mcode: 38149, limited_ary: [], diffLv: [9, 13, 16, 18, 19, 0, 13, 16, 18, 19] },
|
||||
{ mcode: 38092, limited_ary: [], diffLv: [2, 5, 9, 12, 0, 0, 4, 9, 12, 0] },
|
||||
{ mcode: 38069, limited_ary: [], diffLv: [2, 5, 9, 13, 0, 0, 5, 9, 13, 0] },
|
||||
{ mcode: 38218, limited_ary: [], diffLv: [9, 12, 16, 18, 19, 0, 12, 16, 18, 19] },
|
||||
{ mcode: 38215, limited_ary: [], diffLv: [3, 6, 8, 11, 14, 0, 5, 8, 12, 14] },
|
||||
{ mcode: 38199, limited_ary: [], diffLv: [2, 3, 8, 11, 0, 0, 3, 8, 11, 0] },
|
||||
{ mcode: 38187, limited_ary: [], diffLv: [3, 5, 9, 12, 0, 0, 5, 9, 12, 0] },
|
||||
{ mcode: 38122, limited_ary: [], diffLv: [4, 6, 10, 13, 0, 0, 7, 10, 13, 0] },
|
||||
{ mcode: 38207, limited_ary: [], diffLv: [2, 4, 8, 13, 15, 0, 5, 9, 13, 15] },
|
||||
{ mcode: 38073, limited_ary: [], diffLv: [2, 4, 10, 13, 0, 0, 5, 11, 13, 0] },
|
||||
{ mcode: 38160, limited_ary: [], diffLv: [4, 5, 10, 13, 0, 0, 5, 11, 13, 0] },
|
||||
{ mcode: 38104, limited_ary: [], diffLv: [4, 6, 9, 12, 0, 0, 6, 10, 12, 0] },
|
||||
{ mcode: 38103, limited_ary: [], diffLv: [3, 7, 11, 15, 16, 0, 7, 11, 15, 17] },
|
||||
{ mcode: 38209, limited_ary: [], diffLv: [2, 3, 8, 11, 0, 0, 3, 8, 11, 0] },
|
||||
{ mcode: 38186, limited_ary: [], diffLv: [3, 5, 10, 14, 0, 0, 5, 10, 14, 0] },
|
||||
{ mcode: 38158, limited_ary: [], diffLv: [3, 6, 10, 13, 0, 0, 7, 11, 13, 0] },
|
||||
{ mcode: 38189, limited_ary: [], diffLv: [2, 4, 8, 12, 0, 0, 5, 9, 12, 0] },
|
||||
{ mcode: 38219, limited_ary: [], diffLv: [4, 10, 14, 17, 18, 0, 10, 14, 18, 18] },
|
||||
{ mcode: 38268, limited_ary: [], diffLv: [2, 5, 10, 13, 0, 0, 5, 10, 14, 0] },
|
||||
{ mcode: 38274, limited_ary: [], diffLv: [2, 3, 7, 11, 0, 0, 4, 7, 11, 0] },
|
||||
{ mcode: 38273, limited_ary: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1], diffLv: [3, 5, 9, 12, 0, 0, 5, 9, 11, 0] },
|
||||
{ mcode: 38231, limited_ary: [], diffLv: [2, 5, 8, 11, 0, 0, 5, 8, 10, 0] },
|
||||
{ mcode: 38332, limited_ary: [], diffLv: [5, 8, 13, 16, 0, 0, 8, 13, 16, 0] },
|
||||
{ mcode: 38395, limited_ary: [], diffLv: [3, 6, 10, 14, 18, 0, 6, 10, 14, 18] },
|
||||
{ mcode: 38316, limited_ary: [], diffLv: [2, 6, 10, 14, 15, 0, 6, 10, 14, 14] },
|
||||
{ mcode: 38410, limited_ary: [], diffLv: [5, 10, 14, 17, 0, 0, 10, 14, 18, 0] },
|
||||
{ mcode: 38343, limited_ary: [], diffLv: [3, 7, 10, 14, 0, 0, 7, 10, 14, 0] },
|
||||
{ mcode: 38377, limited_ary: [], diffLv: [3, 7, 10, 15, 0, 0, 7, 10, 15, 0] },
|
||||
{ mcode: 38423, limited_ary: [], diffLv: [4, 9, 12, 16, 0, 0, 9, 12, 16, 0] },
|
||||
{ mcode: 38380, limited_ary: [], diffLv: [3, 5, 8, 11, 0, 0, 4, 8, 10, 0] },
|
||||
{ mcode: 38384, limited_ary: [], diffLv: [2, 4, 7, 10, 0, 0, 4, 7, 10, 0] },
|
||||
{ mcode: 38440, limited_ary: [], diffLv: [2, 4, 8, 12, 14, 0, 4, 8, 12, 14] },
|
||||
{ mcode: 38562, limited_ary: [], diffLv: [2, 5, 10, 13, 0, 0, 5, 11, 14, 0] },
|
||||
{ mcode: 38591, limited_ary: [], diffLv: [6, 10, 15, 18, 19, 0, 10, 15, 18, 19] },
|
||||
{ mcode: 37224, limited_ary: [], diffLv: [2, 6, 10, 15, 0, 0, 6, 11, 15, 0] },
|
||||
{ mcode: 38598, limited_ary: [], diffLv: [2, 7, 12, 17, 0, 0, 7, 12, 17, 0] },
|
||||
{ mcode: 38406, limited_ary: [], diffLv: [6, 11, 15, 17, 18, 0, 11, 15, 17, 18] },
|
||||
{ mcode: 38555, limited_ary: [], diffLv: [2, 4, 10, 14, 0, 0, 5, 10, 14, 0] },
|
||||
{ mcode: 38614, limited_ary: [], diffLv: [2, 5, 10, 14, 0, 0, 5, 10, 14, 0] },
|
||||
{ mcode: 38547, limited_ary: [], diffLv: [4, 9, 14, 17, 0, 0, 9, 15, 17, 0] },
|
||||
{ mcode: 38558, limited_ary: [], diffLv: [2, 5, 10, 14, 0, 0, 5, 10, 13, 0] },
|
||||
{ mcode: 37233, limited_ary: [], diffLv: [2, 5, 7, 11, 0, 0, 5, 8, 11, 0] },
|
||||
{ mcode: 38552, limited_ary: [], diffLv: [2, 8, 13, 16, 0, 0, 8, 13, 16, 0] },
|
||||
{ mcode: 37523, limited_ary: [], diffLv: [2, 6, 9, 12, 0, 0, 5, 9, 12, 0] },
|
||||
{ mcode: 38597, limited_ary: [], diffLv: [2, 7, 12, 18, 0, 0, 7, 12, 18, 0] },
|
||||
{ mcode: 38570, limited_ary: [], diffLv: [2, 3, 7, 13, 0, 0, 3, 7, 13, 0] },
|
||||
{ mcode: 37563, limited_ary: [], diffLv: [2, 5, 7, 11, 0, 0, 5, 7, 11, 0] },
|
||||
{ mcode: 37308, limited_ary: [], diffLv: [2, 5, 9, 13, 0, 0, 4, 9, 13, 0] },
|
||||
{ mcode: 38605, limited_ary: [], diffLv: [4, 7, 11, 15, 18, 0, 7, 11, 15, 17] },
|
||||
{ mcode: 38602, limited_ary: [], diffLv: [2, 4, 7, 11, 0, 0, 4, 7, 11, 0] },
|
||||
{ mcode: 37435, limited_ary: [], diffLv: [2, 6, 11, 14, 0, 0, 7, 12, 15, 0] },
|
||||
{ mcode: 38643, limited_ary: [], diffLv: [2, 5, 9, 13, 16, 0, 4, 9, 13, 16] },
|
||||
{ mcode: 38516, limited_ary: [], diffLv: [4, 8, 11, 14, 17, 0, 7, 11, 14, 17] },
|
||||
{ mcode: 38269, limited_ary: [0, 0, 0, 0, -1, -1, 0, 0, 0, -1], diffLv: [2, 5, 7, 10, 0, 0, 5, 7, 10, 0] },
|
||||
{ mcode: 38652, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [2, 6, 10, 15, 17, 0, 6, 10, 15, 17] },
|
||||
{ mcode: 37251, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [6, 10, 12, 15, 18, 0, 10, 11, 16, 17] },
|
||||
|
||||
{ mcode: 38212, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [3, 7, 10, 15, 16, 0, 8, 11, 15, 16] },
|
||||
{ mcode: 38295, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [3, 7, 11, 14, 17, 0, 7, 11, 14, 17] },
|
||||
{ mcode: 38258, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [2, 6, 10, 14, 16, 0, 6, 11, 14, 16] },
|
||||
{ mcode: 38263, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [4, 7, 11, 14, 16, 0, 8, 12, 15, 16] },
|
||||
{ mcode: 38195, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [5, 7, 10, 13, 17, 0, 7, 10, 13, 17] },
|
||||
{ mcode: 38420, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [4, 8, 12, 15, 17, 0, 8, 12, 15, 17] },
|
||||
{ mcode: 38405, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [3, 6, 9, 13, 17, 0, 5, 9, 14, 17] },
|
||||
{ mcode: 38442, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [3, 8, 13, 16, 18, 0, 8, 13, 16, 18] },
|
||||
{ mcode: 38436, limited_ary: [0, 0, 0, 0, 0, -1, 0, 0, 0, 0], diffLv: [4, 7, 11, 14, 17, 0, 7, 11, 14, 17] }
|
||||
]
|
||||
|
||||
export const EVENTS_WORLD = [
|
||||
{ id: 89, no: 0, cond: 0, rwrd: 0, type: 9999 },
|
||||
{ id: 96, no: 0, cond: 0, rwrd: 0, type: 9999 },
|
||||
|
||||
{ id: 1, no: 1, cond: 1, rwrd: 38667, type: 70 }, { id: 2, no: 1, cond: 1, rwrd: 38667, type: 71 }, { id: 3, no: 1, cond: 1, rwrd: 38667, type: 72 }, { id: 4, no: 1, cond: 1, rwrd: 38667, type: 73 },
|
||||
{ id: 5, no: 1, cond: 1, rwrd: 38661, type: 70 }, { id: 6, no: 1, cond: 1, rwrd: 38661, type: 71 }, { id: 7, no: 1, cond: 1, rwrd: 38661, type: 72 }, { id: 8, no: 1, cond: 1, rwrd: 38661, type: 73 },
|
||||
{ id: 9, no: 1, cond: 1, rwrd: 38668, type: 70 }, { id: 10, no: 1, cond: 1, rwrd: 38668, type: 71 }, { id: 11, no: 1, cond: 1, rwrd: 38668, type: 72 }, { id: 12, no: 1, cond: 1, rwrd: 38668, type: 73 },
|
||||
{ id: 13, no: 1, cond: 1, rwrd: 38665, type: 70 }, { id: 14, no: 1, cond: 1, rwrd: 38665, type: 71 }, { id: 15, no: 1, cond: 1, rwrd: 38665, type: 72 }, { id: 16, no: 1, cond: 1, rwrd: 38665, type: 73 }, { id: 17, no: 1, cond: 1, rwrd: 38665, type: 74 },
|
||||
{ id: 18, no: 1, cond: 1, rwrd: 38669, type: 70 }, { id: 19, no: 1, cond: 1, rwrd: 38669, type: 71 }, { id: 20, no: 1, cond: 1, rwrd: 38669, type: 72 }, { id: 21, no: 1, cond: 1, rwrd: 38669, type: 73 }, { id: 22, no: 1, cond: 1, rwrd: 38669, type: 74 },
|
||||
{ id: 23, no: 1, cond: 1, rwrd: 38659, type: 70 }, { id: 24, no: 1, cond: 1, rwrd: 38659, type: 71 }, { id: 25, no: 1, cond: 1, rwrd: 38659, type: 72 }, { id: 26, no: 1, cond: 1, rwrd: 38659, type: 73 }, { id: 27, no: 1, cond: 1, rwrd: 38659, type: 74 },
|
||||
{ id: 28, no: 1, cond: 1, rwrd: 38667, type: 81 }, { id: 29, no: 1, cond: 1, rwrd: 38667, type: 82 }, { id: 30, no: 1, cond: 1, rwrd: 38667, type: 83 },
|
||||
{ id: 31, no: 1, cond: 1, rwrd: 38661, type: 81 }, { id: 32, no: 1, cond: 1, rwrd: 38661, type: 82 }, { id: 33, no: 1, cond: 1, rwrd: 38661, type: 83 },
|
||||
{ id: 34, no: 1, cond: 1, rwrd: 38668, type: 81 }, { id: 35, no: 1, cond: 1, rwrd: 38668, type: 82 }, { id: 36, no: 1, cond: 1, rwrd: 38668, type: 83 },
|
||||
{ id: 37, no: 1, cond: 1, rwrd: 38665, type: 81 }, { id: 38, no: 1, cond: 1, rwrd: 38665, type: 82 }, { id: 39, no: 1, cond: 1, rwrd: 38665, type: 83 }, { id: 40, no: 1, cond: 1, rwrd: 38665, type: 84 },
|
||||
{ id: 41, no: 1, cond: 1, rwrd: 38669, type: 81 }, { id: 42, no: 1, cond: 1, rwrd: 38669, type: 82 }, { id: 43, no: 1, cond: 1, rwrd: 38669, type: 83 }, { id: 44, no: 1, cond: 1, rwrd: 38669, type: 84 },
|
||||
{ id: 45, no: 1, cond: 1, rwrd: 38659, type: 81 }, { id: 46, no: 1, cond: 1, rwrd: 38659, type: 82 }, { id: 47, no: 1, cond: 1, rwrd: 38659, type: 83 }, { id: 48, no: 1, cond: 1, rwrd: 38659, type: 84 },
|
||||
{ id: 49, no: 0, cond: 2, rwrd: 38736, type: 27 }, { id: 50, no: 0, cond: 2, rwrd: 38735, type: 27 }, { id: 51, no: 0, cond: 2, rwrd: 38728, type: 27 },
|
||||
{ id: 52, no: 0, cond: 1, rwrd: 38624, type: 27 }, { id: 53, no: 0, cond: 1, rwrd: 38623, type: 27 }, { id: 54, no: 0, cond: 1, rwrd: 38628, type: 27 },
|
||||
{ id: 55, no: 0, cond: 1, rwrd: 38736, type: 25 }, { id: 56, no: 1, cond: 1, rwrd: 38736, type: 25 }, { id: 57, no: 2, cond: 1, rwrd: 38736, type: 25 }, { id: 58, no: 3, cond: 1, rwrd: 38736, type: 25 },
|
||||
{ id: 59, no: 0, cond: 1, rwrd: 38735, type: 25 }, { id: 60, no: 1, cond: 1, rwrd: 38735, type: 25 }, { id: 61, no: 2, cond: 1, rwrd: 38735, type: 25 }, { id: 62, no: 3, cond: 1, rwrd: 38735, type: 25 },
|
||||
{ id: 63, no: 0, cond: 1, rwrd: 38728, type: 25 }, { id: 64, no: 1, cond: 1, rwrd: 38728, type: 25 }, { id: 65, no: 2, cond: 1, rwrd: 38728, type: 25 }, { id: 66, no: 3, cond: 1, rwrd: 38728, type: 25 },
|
||||
{ id: 67, no: 0, cond: 1, rwrd: 38624, type: 25 }, { id: 68, no: 1, cond: 1, rwrd: 38624, type: 25 }, { id: 69, no: 2, cond: 1, rwrd: 38624, type: 25 }, { id: 70, no: 3, cond: 1, rwrd: 38624, type: 25 }, { id: 71, no: 4, cond: 1, rwrd: 38624, type: 25, dep: [67, 68, 69, 70, 72, 73, 74, 75, 77, 78, 79, 80] },
|
||||
{ id: 72, no: 0, cond: 1, rwrd: 38623, type: 25 }, { id: 73, no: 1, cond: 1, rwrd: 38623, type: 25 }, { id: 74, no: 2, cond: 1, rwrd: 38623, type: 25 }, { id: 75, no: 3, cond: 1, rwrd: 38623, type: 25 }, { id: 76, no: 4, cond: 1, rwrd: 38623, type: 25, dep: [67, 68, 69, 70, 71, 72, 73, 74, 75, 77, 78, 79, 80] },
|
||||
{ id: 77, no: 0, cond: 1, rwrd: 38628, type: 25 }, { id: 78, no: 1, cond: 1, rwrd: 38628, type: 25 }, { id: 79, no: 2, cond: 1, rwrd: 38628, type: 25 }, { id: 80, no: 3, cond: 1, rwrd: 38628, type: 25 },
|
||||
{ id: 81, no: 0, cond: 1, rwrd: 1185, type: 201, save: 1, dep: [67, 68, 69, 70, 72, 73, 74, 75, 77, 78, 79, 80] },
|
||||
{ id: 82, no: 0, cond: 1, rwrd: 1186, type: 201, save: 1, dep: [67, 68, 69, 70, 71, 72, 73, 74, 75, 77, 78, 79, 80] },
|
||||
{ id: 83, no: 0, cond: 1, rwrd: 38746, type: 17, comp: 0, save: 1 },
|
||||
{ id: 84, no: 0, cond: 1, rwrd: 38745, type: 17, comp: 0, save: 1 },
|
||||
{ id: 85, no: 0, cond: 1, rwrd: 38744, type: 17, comp: 0, save: 1 },
|
||||
{ id: 86, no: 0, cond: 1, rwrd: 38747, type: 17, comp: 0, save: 1 },
|
||||
{ id: 87, no: 0, cond: 1, rwrd: 38748, type: 17, comp: 0, save: 1 },
|
||||
{ id: 88, no: 0, cond: 1, rwrd: 1189, type: 201, save: 1 },
|
||||
{ id: 89, no: 0, cond: 1, rwrd: 1188, type: 201, save: 1 }
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Dance Dance Revolution
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
Supported version
|
||||
|
||||
- Dance Dance Revolution A20
|
||||
- Dance Dance Revolution A
|
||||
|
||||
---
|
||||
|
||||
Changelogs
|
||||
|
||||
**v1.0.0**
|
||||
|
||||
- Initial release
|
||||
@@ -1 +0,0 @@
|
||||
apisamples/
|
||||
@@ -1,86 +0,0 @@
|
||||
GITADORA Plugin for Asphyxia-Core
|
||||
=================================
|
||||

|
||||
|
||||
This plugin is based on converted from public-exported Asphyxia's Routes.
|
||||
|
||||
Supported Versions
|
||||
==================
|
||||
- Matixx
|
||||
- Exchain
|
||||
- NEX+AGE
|
||||
|
||||
|
||||
When Plugin Doesn't work correctly / Startup Error on Plugin
|
||||
------------------------------------------------------------
|
||||
The folder structure between v1.0 and v1.1 is quite different. Do not overwrite plugin folder.
|
||||
<br>If you encounter errors, Please try these steps:
|
||||
|
||||
1. Remove `gitadora@asphyxia` folder.
|
||||
2. Ctrl-C and Ctrl-V the newest version of `gitadora@asphyxia`
|
||||
3. (Custom MDB Users) Reupload MDB or move `data/custom_mdb.xml` to `data/mdb/custom.xml`
|
||||
|
||||
Known Issues
|
||||
============
|
||||
* ~Information dialog keep showing as plugin doesn't store item data currently.~ (Fixed as of version 1.2.1)
|
||||
* Special Premium Encore on Nextage is unimplemented. However, a workaround is available. Try it.
|
||||
* Friends and Rivals are unimplemented.
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
v1.3.0
|
||||
----------------
|
||||
* Added experimental 'Shared Favorite Songs' option. If disabled, players will be able to keep separate lists of favorite songs for each version of Gitadora, as well as between Guitar Freaks and Drummania. Enable this option to have a single unified list of favorite songs for both games, and across all versions. Default is false, to match original arcade behaviour.
|
||||
* Added a leaderboards page to the WebUI. This page displays the rank of all players per game and version, ordered by Skill rating.
|
||||
* More code cleanups to Profiles.ts
|
||||
|
||||
v1.2.4
|
||||
----------------
|
||||
* Fixed note scroll speed defaulting to 0.5x for newly registered profiles.
|
||||
* Misc code cleanup. No changes expected to plugin behaviour.
|
||||
|
||||
v1.2.3
|
||||
----------------
|
||||
* Fixed bug preventing MDB files in XML format from loading (Thanks to DualEdge for reporting this ).
|
||||
|
||||
v1.2.2
|
||||
----------------
|
||||
* Major improvements to the MDB (song data) loader. MDB files can now be in .json, .xml or .b64 format. This applies to both the per-version defaults and custom MDBs. To use a custom MDB, enable it in the web UI, and place a 'custom.xml', 'custom.json' or 'custom.b64' file in the data/mdb subfolder.
|
||||
* Added several player profile stats to the web UI.
|
||||
* MDB loader now logs the number of loaded songs available to GF and DM when in dev mode.
|
||||
* MDB: Fixed "is_secret" field being ignored (always set to false)
|
||||
|
||||
v1.2.1
|
||||
----------------
|
||||
* Secret Music (unlocked songs) are now saved and loaded correctly. Partially fixes Github issue #34. Note that all songs are already marked as unlocked by the server - there is no need to unlock them manually. If you would like to lock them, consider using a custom MDB.
|
||||
* Rewards field is now saved and loaded correctly. Fixes Github issue #34
|
||||
|
||||
NOTE: Rewards and secret music data is saved at the end of each session, so you will see the unlock notifications one last time after updating the plugin to this version.
|
||||
|
||||
v1.2.0
|
||||
----------------
|
||||
* Fixed server error when saving profiles for two Guitar Freaks players at the end of a session. Fixes Github issue #39.
|
||||
* Fixed another server error when two players are present, but only one player is using a profile.
|
||||
* Added support for the "ranking" field. Gitadora will now correctly display your server ranking (based on Skill) on the post-game screen.
|
||||
* "Recommended to friends" songs are now saved and loaded correctly. Since you don't have any friends, this won't be terribly useful, but it does at least provide an extra five slots for saving your favourite songs.
|
||||
* Fixed "Recommended to friends" song list being incorrectly initialized to "I think about you".
|
||||
* misc: Added logging for profile loading/saving when Asphyxia is running in dev mode.
|
||||
* misc: Added more logging to mdb (song database) loading.
|
||||
* misc: Removed some unneeded duplicate code.
|
||||
* misc: Latest getPlayer() and savePlayers() API requests and responses are now saved to file when Asphyxia is in dev mode. Useful for debugging.
|
||||
|
||||
v1.1.1
|
||||
----------------
|
||||
* fix: Error when create new profile on exchain.
|
||||
* fix: last song doesn't work correctly.
|
||||
* misc: Add logger for tracking problem.
|
||||
|
||||
v1.1.0
|
||||
------
|
||||
* NEX+AGE Support (Not full support.)
|
||||
* Restructure bit for maintaining.
|
||||
|
||||
v1.0.0
|
||||
------
|
||||
* Initial release for public
|
||||
@@ -1 +0,0 @@
|
||||
export const PLUGIN_VER = 1;
|
||||
@@ -1,72 +0,0 @@
|
||||
import { getVersion } from "../utils";
|
||||
|
||||
interface EncoreStageData {
|
||||
level: number
|
||||
musics: number[]
|
||||
unlock_challenge?: number[]
|
||||
}
|
||||
|
||||
export function getEncoreStageData(info: EamuseInfo): EncoreStageData {
|
||||
const fallback = { level: 10, musics: [0] }
|
||||
const level: number = U.GetConfig("encore_version")
|
||||
const ntDummyEncore = U.GetConfig("nextage_dummy_encore")
|
||||
switch (getVersion(info)) {
|
||||
case 'nextage':
|
||||
return {
|
||||
level,
|
||||
musics: !ntDummyEncore ? [
|
||||
2587, // 悪魔のハニープリン
|
||||
2531, // The ULTIMATES -reminiscence-
|
||||
2612, // ECLIPSE 2
|
||||
2622, // Slip Into My Royal Blood
|
||||
2686, // CYCLONICxSTORM
|
||||
// FIXME: Fix special encore.
|
||||
305, 602, 703, 802, 902, 1003, 1201, 1400, 1712, 1916, 2289, 2631, // DD13 and encores.
|
||||
1704, 1811, 2121, 2201, 2624, // Soranaki and encores.
|
||||
1907, 2020, 2282, 2341, 2666 // Stargazer and encores.
|
||||
] : [
|
||||
2622, 305, 1704, 1907, 2686 // Dummy.
|
||||
]
|
||||
}
|
||||
case 'exchain':
|
||||
return {
|
||||
level,
|
||||
musics: [
|
||||
2246, // 箱庭の世界
|
||||
2498, // Cinnamon
|
||||
2500, // キヤロラ衛星の軌跡
|
||||
2529, // グリーンリーフ症候群
|
||||
2548, // Let's Dance
|
||||
2587, // 悪魔のハニープリン
|
||||
5020, // Timepiece phase II (CLASSIC)
|
||||
5033, // MODEL FT2 Miracle Version (CLASSIC)
|
||||
2586, // 美麗的夏日風
|
||||
5060, // EXCELSIOR DIVE (CLASSIC)
|
||||
2530, // The ULTIMATES -CHRONICLE-
|
||||
2581, // 幸せの代償
|
||||
5046 // Rock to Infinity (CLASSIC)
|
||||
]
|
||||
}
|
||||
case 'matixx':
|
||||
return {
|
||||
level,
|
||||
musics: [
|
||||
2432, // Durian
|
||||
2445, // ヤオヨロズランズ
|
||||
2456, // Fate of the Furious
|
||||
2441, // PIRATES BANQUET
|
||||
2444, // Aion
|
||||
2381, // Duella Lyrica
|
||||
2471, // triangulum
|
||||
2476, // MODEL FT4
|
||||
2486, // 煉獄事変
|
||||
2496, // CAPTURING XANADU
|
||||
2497, // Physical Decay
|
||||
2499, // Cinnamon
|
||||
2498 // けもののおうじゃ★めうめう
|
||||
]
|
||||
}
|
||||
default:
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
ex.xml
|
||||
mt.xml
|
||||
nt.xml
|
||||
hv.xml
|
||||
ex.json
|
||||
mt.json
|
||||
nt.json
|
||||
hv.json
|
||||
custom.xml
|
||||
custom.json
|
||||
blacklist.txt
|
||||
File diff suppressed because one or more lines are too long
@@ -1,166 +0,0 @@
|
||||
import Logger from "../../utils/logger";
|
||||
import { CommonMusicData } from "../../models/commonmusicdata";
|
||||
|
||||
|
||||
export enum DATAVersion {
|
||||
HIGHVOLTAGE = "hv",
|
||||
NEXTAGE = "nt",
|
||||
EXCHAIN = "ex",
|
||||
MATTIX = "mt"
|
||||
}
|
||||
|
||||
const allowedFormats = ['.json', '.xml', '.b64']
|
||||
const mdbFolder = "data/mdb/"
|
||||
|
||||
type processRawDataHandler = (path: string) => Promise<CommonMusicData>
|
||||
|
||||
const logger = new Logger("mdb")
|
||||
|
||||
export async function readXML(path: string) {
|
||||
const xml = await IO.ReadFile(path, 'utf-8');
|
||||
const json = U.parseXML(xml, false)
|
||||
return json
|
||||
}
|
||||
|
||||
export async function readMDBFile(path: string, processHandler?: processRawDataHandler): Promise<CommonMusicData> {
|
||||
|
||||
if (!IO.Exists(path)) {
|
||||
throw "Unable to find MDB file at " + path
|
||||
}
|
||||
|
||||
logger.debugInfo(`Loading MDB data from ${path}.`)
|
||||
|
||||
let result : CommonMusicData;
|
||||
const fileType = path.substring(path.lastIndexOf('.')).toLowerCase()
|
||||
|
||||
switch (fileType) {
|
||||
case '.json':
|
||||
const str = await IO.ReadFile(path, 'utf-8');
|
||||
result = JSON.parse(str)
|
||||
break;
|
||||
case '.xml':
|
||||
processHandler = processHandler ?? defaultProcessRawXmlData
|
||||
result = await processHandler(path)
|
||||
// Uncomment to save the loaded XML file as JSON.
|
||||
// await IO.WriteFile(path.replace(".xml", ".json"), JSON.stringify(data))
|
||||
break;
|
||||
case '.b64':
|
||||
const buff = await IO.ReadFile(path, 'utf-8');
|
||||
const json = Buffer.from(buff, 'base64').toString('utf-8')
|
||||
// Uncomment to save the decoded base64 file as JSON.
|
||||
// await IO.WriteFile(path.replace(".b64",".json"), json)
|
||||
result = JSON.parse(json)
|
||||
break;
|
||||
default:
|
||||
throw `Invalid MDB file type: ${fileType}. Only .json, .xml, .b64 are supported.`
|
||||
}
|
||||
|
||||
let gfCount = result.music.filter((e) => e.cont_gf["@content"][0]).length
|
||||
let dmCount = result.music.filter((e) => e.cont_dm["@content"][0]).length
|
||||
logger.debugInfo(`Loaded ${result.music.length} songs from MDB file. ${gfCount} songs for GF, ${dmCount} songs for DM.`)
|
||||
return result
|
||||
}
|
||||
|
||||
export function gameVerToDataVer(ver: string): DATAVersion {
|
||||
switch(ver) {
|
||||
case 'highvoltage':
|
||||
return DATAVersion.HIGHVOLTAGE
|
||||
case 'nextage':
|
||||
return DATAVersion.NEXTAGE
|
||||
case 'exchain':
|
||||
return DATAVersion.EXCHAIN
|
||||
case 'matixx':
|
||||
default:
|
||||
return DATAVersion.MATTIX
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find a .json, .xml, or .b64 file (in that order) matching the given name in the specified folder.
|
||||
* @param fileNameWithoutExtension - The name of the file to find (without the extension).
|
||||
* @param path - The path to the folder to search. If left null, the default MDB folder ('data/mdb' in the plugin folder) will be used.
|
||||
* @returns - The path of the first matching file found, or null if no file was found.
|
||||
*/
|
||||
export function findMDBFile(fileNameWithoutExtension: string, path: string = null): string {
|
||||
|
||||
path = path ?? mdbFolder
|
||||
if (!IO.Exists(path)) {
|
||||
throw `Path does not exist: ${path}`
|
||||
}
|
||||
|
||||
if (!path.endsWith("/")) {
|
||||
path += "/"
|
||||
}
|
||||
|
||||
for (const ext of allowedFormats) {
|
||||
const filePath = path + fileNameWithoutExtension + ext
|
||||
if (IO.Exists(filePath)) {
|
||||
return filePath
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export async function loadSongsForGameVersion(gameVer: string, processHandler?: processRawDataHandler) {
|
||||
const ver = gameVerToDataVer(gameVer)
|
||||
|
||||
let mdbFile = findMDBFile(ver, mdbFolder)
|
||||
|
||||
if (mdbFile == null) {
|
||||
throw `No valid MDB files were found in the data/mdb subfolder. Ensure that this folder contains at least one of the following: ${ver}.json, ${ver}.xml or ${ver}.b64`
|
||||
}
|
||||
|
||||
const music = await readMDBFile(mdbFile, processHandler ?? defaultProcessRawXmlData)
|
||||
return music
|
||||
}
|
||||
|
||||
export async function defaultProcessRawXmlData(path: string): Promise<CommonMusicData> {
|
||||
const data = await readXML(path)
|
||||
const mdb = $(data).elements("mdb.mdb_data");
|
||||
const music: any[] = [];
|
||||
for (const m of mdb) {
|
||||
const d = m.numbers("xg_diff_list");
|
||||
const contain = m.numbers("contain_stat");
|
||||
const gf = contain[0];
|
||||
const dm = contain[1];
|
||||
|
||||
if (gf == 0 && dm == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let type = gf;
|
||||
if (gf == 0) {
|
||||
type = dm;
|
||||
}
|
||||
|
||||
music.push({
|
||||
id: K.ITEM('s32', m.number("music_id")),
|
||||
cont_gf: K.ITEM('bool', gf == 0 ? 0 : 1),
|
||||
cont_dm: K.ITEM('bool', dm == 0 ? 0 : 1),
|
||||
is_secret: K.ITEM('bool', m.number("is_secret", 0)),
|
||||
is_hot: K.ITEM('bool', type == 2 ? 0 : 1),
|
||||
data_ver: K.ITEM('s32', m.number("data_ver", 115)),
|
||||
diff: K.ARRAY('u16', [
|
||||
d[0],
|
||||
d[1],
|
||||
d[2],
|
||||
d[3],
|
||||
d[4],
|
||||
d[10],
|
||||
d[11],
|
||||
d[12],
|
||||
d[13],
|
||||
d[14],
|
||||
d[5],
|
||||
d[6],
|
||||
d[7],
|
||||
d[8],
|
||||
d[9],
|
||||
]),
|
||||
});
|
||||
}
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,93 +0,0 @@
|
||||
import { Extra } from "../models/extra";
|
||||
import { FavoriteMusic } from "../models/favoritemusic";
|
||||
import { isSharedFavoriteMusicEnabled } from "../utils";
|
||||
import Logger from "../utils/logger";
|
||||
|
||||
const logger = new Logger("FavoriteMusic");
|
||||
|
||||
/**
|
||||
* Extracts favorite music data from the given extra data container, and saves it to the database as shared favorite music data for the player with the given refid.
|
||||
* This function has no effect if the 'Shared favorite music' option is not enabled.
|
||||
* Note that shared favorite music is shared across both Guitar Freaks and Drummania, as well as all supported versions of the game.
|
||||
* @param refid The refid of the player.
|
||||
* @param extra The extra data container of the player, containing the favorite music lists to be saved.
|
||||
* @returns {boolean} - whether the favorite music data was successfully saved.
|
||||
*/
|
||||
export async function saveSharedFavoriteMusicFromExtra(refid: string, extra: Extra) : Promise<boolean>
|
||||
{
|
||||
if (!isSharedFavoriteMusicEnabled()) {
|
||||
return false
|
||||
}
|
||||
|
||||
let result : FavoriteMusic = {
|
||||
collection: 'favoritemusic',
|
||||
pluginVer: 1,
|
||||
list_1: extra.list_1,
|
||||
list_2: extra.list_2,
|
||||
list_3: extra.list_3,
|
||||
recommend_musicid_list: extra.recommend_musicid_list,
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await saveFavoriteMusic(refid, result)
|
||||
logger.debugInfo(`Saved shared favorite music for profile ${refid} successfully.`);
|
||||
return true
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
logger.error(`Failed to save shared favorite music for profile ${refid}.`);
|
||||
logger.error(e);
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves shared favorite music data from the database for the player with the given refid, and applies the data to the provided extra data container.
|
||||
* This function has no effect if the 'Shared favorite music' option is not enabled.
|
||||
* Note that shared favorite music is shared across both Guitar Freaks and Drummania, as well as all supported versions of the game.
|
||||
* @param refid - The refid of the player.
|
||||
* @param extra - The destination object where favorite music data should be applied.
|
||||
*/
|
||||
export async function applySharedFavoriteMusicToExtra(refid : string, extra : Extra) : Promise<void>
|
||||
{
|
||||
if (!isSharedFavoriteMusicEnabled()) {
|
||||
return
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
let favoriteMusic = await loadFavoriteMusic(refid)
|
||||
|
||||
if (favoriteMusic == null) {
|
||||
logger.debugInfo(`No shared favourite music available for profile ${refid}. Using game specific favorites. Favorites will be saved as shared favorites at the end of the game session.`);
|
||||
return
|
||||
}
|
||||
|
||||
extra.list_1 = favoriteMusic.list_1
|
||||
extra.list_2 = favoriteMusic.list_2
|
||||
extra.list_3 = favoriteMusic.list_3
|
||||
extra.recommend_musicid_list = favoriteMusic.recommend_musicid_list
|
||||
logger.debugInfo(`Loaded shared favorite music for profile ${refid} successfully.`);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
logger.error(`Failed to load shared favorite music for profile ${refid}.`);
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveFavoriteMusic(refid: string, data : FavoriteMusic) : Promise<any>
|
||||
{
|
||||
return await DB.Upsert<FavoriteMusic>(refid, {
|
||||
collection: 'favoritemusic',
|
||||
}, data)
|
||||
}
|
||||
|
||||
export async function loadFavoriteMusic(refid : string) : Promise<FavoriteMusic>
|
||||
{
|
||||
return await DB.FindOne<FavoriteMusic>(refid, {
|
||||
collection: 'favoritemusic'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { getVersion } from "../utils";
|
||||
import { findMDBFile, readMDBFile, loadSongsForGameVersion } from "../data/mdb";
|
||||
import { CommonMusicDataField } from "../models/commonmusicdata";
|
||||
import Logger from "../utils/logger"
|
||||
import { getPlayableMusicResponse, PlayableMusicResponse } from "../models/Responses/playablemusicresponse";
|
||||
|
||||
const logger = new Logger("MusicList")
|
||||
|
||||
export const playableMusic: EPR = async (info, data, send) => {
|
||||
const version = getVersion(info);
|
||||
const start = Date.now()
|
||||
let music: CommonMusicDataField[] = [];
|
||||
try {
|
||||
if (U.GetConfig("enable_custom_mdb")) {
|
||||
let customMdb = findMDBFile("custom")
|
||||
music = (await readMDBFile(customMdb)).music
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn("Read Custom MDB failed. Using default MDB as a fallback.")
|
||||
logger.debugWarn(e.stack);
|
||||
music = [];
|
||||
}
|
||||
|
||||
if (music.length == 0) {
|
||||
music = (await loadSongsForGameVersion(version)).music
|
||||
}
|
||||
|
||||
const end = Date.now()
|
||||
const timeDiff = end - start
|
||||
logger.debugInfo(`MDB loading took ${timeDiff} ms`)
|
||||
|
||||
let response : PlayableMusicResponse = getPlayableMusicResponse(music)
|
||||
await send.object(response)
|
||||
};
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
import { getEncoreStageData } from "../data/extrastage";
|
||||
import Logger from "../utils/logger";
|
||||
|
||||
const logger = new Logger('info');
|
||||
export const shopInfoRegist: EPR = async (info, data, send) => {
|
||||
send.object({
|
||||
data: {
|
||||
cabid: K.ITEM('u32', 1),
|
||||
locationid: K.ITEM('str', 'Asphyxia'),
|
||||
},
|
||||
temperature: {
|
||||
is_send: K.ITEM('bool', 0),
|
||||
},
|
||||
tax: {
|
||||
tax_phase: K.ITEM('s32', 0),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const gameInfoGet: EPR = async (info, data, send) => {
|
||||
|
||||
const eventData = getEventDataResponse()
|
||||
const extraData = getEncoreStageData(info)
|
||||
|
||||
await send.object({
|
||||
now_date: K.ITEM('u64', BigInt(Date.now())),
|
||||
extra: {
|
||||
extra_lv: K.ITEM('u8', extraData.level),
|
||||
extramusic: {
|
||||
music: extraData.musics.map(mid => {
|
||||
return {
|
||||
musicid: K.ITEM('s32', mid),
|
||||
get_border: K.ITEM('u8', 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
infect_music: { term: K.ITEM('u8', 0) },
|
||||
unlock_challenge: { term: K.ITEM('u8', 0) },
|
||||
battle: { term: K.ITEM('u8', 0) },
|
||||
battle_chara: { term: K.ITEM('u8', 0) },
|
||||
data_ver_limit: { term: K.ITEM('u8', 0) },
|
||||
ea_pass_propel: { term: K.ITEM('u8', 0) },
|
||||
monthly_skill: {
|
||||
term: K.ITEM('u8', 0),
|
||||
target_music: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
update_prog: { term: K.ITEM('u8', 0) },
|
||||
rockwave: { event_list: {} },
|
||||
general_term: {},
|
||||
jubeat_omiyage_challenge: {},
|
||||
kac2017: {},
|
||||
nostalgia_concert: {},
|
||||
trbitemdata: {},
|
||||
ctrl_movie: {},
|
||||
ng_jacket: {},
|
||||
ng_recommend_music: {},
|
||||
ranking: {
|
||||
skill_0_999: {},
|
||||
skill_1000_1499: {},
|
||||
skill_1500_1999: {},
|
||||
skill_2000_2499: {},
|
||||
skill_2500_2999: {},
|
||||
skill_3000_3499: {},
|
||||
skill_3500_3999: {},
|
||||
skill_4000_4499: {},
|
||||
skill_4500_4999: {},
|
||||
skill_5000_5499: {},
|
||||
skill_5500_5999: {},
|
||||
skill_6000_6499: {},
|
||||
skill_6500_6999: {},
|
||||
skill_7000_7499: {},
|
||||
skill_7500_7999: {},
|
||||
skill_8000_8499: {},
|
||||
skill_8500_9999: {},
|
||||
total: {},
|
||||
original: {},
|
||||
bemani: {},
|
||||
famous: {},
|
||||
anime: {},
|
||||
band: {},
|
||||
western: {},
|
||||
},
|
||||
processing_report_state: K.ITEM('u8', 0),
|
||||
assert_report_state: K.ITEM('u8', 0),
|
||||
recommendmusic: { '@attr': { nr: 0 } },
|
||||
demomusic: { '@attr': { nr: 0 } },
|
||||
event_skill: {},
|
||||
temperature: { is_send: K.ITEM('bool', 0) },
|
||||
bemani_summer_2018: { is_open: K.ITEM('bool', 0) },
|
||||
kac2018: {
|
||||
event: {
|
||||
term: K.ITEM('s32', 0),
|
||||
since: K.ITEM('u64', BigInt(0)),
|
||||
till: K.ITEM('u64', BigInt(0)),
|
||||
is_open: K.ITEM('bool', 0),
|
||||
target_music: {
|
||||
music_id: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
},
|
||||
...eventData,
|
||||
});
|
||||
};
|
||||
|
||||
function getEventDataResponse() {
|
||||
const addition: any = {
|
||||
monstar_subjugation: {
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
bear_fes: {},
|
||||
nextadium: {},
|
||||
};
|
||||
const time = BigInt(31536000);
|
||||
|
||||
for (let i = 1; i <= 20; ++i) {
|
||||
const obj = {
|
||||
term: K.ITEM('u8', 0),
|
||||
start_date_ms: K.ITEM('u64', time),
|
||||
end_date_ms: K.ITEM('u64', time),
|
||||
};
|
||||
if (i == 1) {
|
||||
addition[`phrase_combo_challenge`] = obj;
|
||||
addition[`long_otobear_fes_1`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
start_date_ms: K.ITEM('u64', time),
|
||||
end_date_ms: K.ITEM('u64', time),
|
||||
bonus_musicid: {},
|
||||
};
|
||||
addition[`sdvx_stamprally3`] = obj;
|
||||
addition[`chronicle_1`] = obj;
|
||||
addition[`paseli_point_lottery`] = obj;
|
||||
addition['sticker_campaign'] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
sticker_list: {},
|
||||
};
|
||||
addition['thanksgiving'] = {
|
||||
...obj,
|
||||
box_term: {
|
||||
state: K.ITEM('u8', 0)
|
||||
}
|
||||
};
|
||||
addition['lotterybox'] = {
|
||||
...obj,
|
||||
box_term: {
|
||||
state: K.ITEM('u8', 0)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
|
||||
addition[`phrase_combo_challenge_${i}`] = obj;
|
||||
}
|
||||
|
||||
if (i <= 4) {
|
||||
addition['monstar_subjugation'][`monstar_subjugation_${i}`] = obj;
|
||||
addition['bear_fes'][`bear_fes_${i}`] = obj;
|
||||
}
|
||||
|
||||
if (i <= 3) {
|
||||
addition[`kouyou_challenge_${i}`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return addition
|
||||
}
|
||||
@@ -1,777 +0,0 @@
|
||||
import { getDefaultPlayerInfo, PlayerInfo } from "../models/playerinfo";
|
||||
import { PlayerRanking } from "../models/playerranking";
|
||||
import { getDefaultProfile, Profile } from "../models/profile";
|
||||
import { getDefaultRecord, Record } from "../models/record";
|
||||
import { Extra, getDefaultExtra } from "../models/extra";
|
||||
import { getVersion, isDM } from "../utils";
|
||||
import { getDefaultScores, Scores } from "../models/scores";
|
||||
|
||||
import { PLUGIN_VER } from "../const";
|
||||
import Logger from "../utils/logger"
|
||||
import { isAsphyxiaDebugMode } from "../utils/index";
|
||||
import { SecretMusicEntry } from "../models/secretmusicentry";
|
||||
import { CheckPlayerResponse, getCheckPlayerResponse } from "../models/Responses/checkplayerresponse";
|
||||
import { getPlayerStickerResponse, PlayerStickerResponse } from "../models/Responses/playerstickerresponse";
|
||||
import { getSecretMusicResponse, SecretMusicResponse } from "../models/Responses/secretmusicresponse";
|
||||
import { getSaveProfileResponse } from "../models/Responses/saveprofileresponse";
|
||||
import { getDefaultBattleDataResponse } from "../models/Responses/battledataresponse";
|
||||
import { applySharedFavoriteMusicToExtra, saveSharedFavoriteMusicFromExtra } from "./FavoriteMusic";
|
||||
import { getPlayerRecordResponse } from "../models/Responses/playerrecordresponse";
|
||||
import { getPlayerPlayInfoResponse, PlayerPlayInfoResponse } from "../models/Responses/playerplayinforesponse";
|
||||
|
||||
const logger = new Logger("profiles")
|
||||
|
||||
export const regist: EPR = async (info, data, send) => {
|
||||
|
||||
const refid = $(data).str('player.refid');
|
||||
if (!refid) {
|
||||
logger.error("Request data is missing required parameter: player.refid")
|
||||
return send.deny();
|
||||
}
|
||||
|
||||
const no = getPlayerNo(data);
|
||||
const version = getVersion(info);
|
||||
const playerInfo = await getOrRegisterPlayerInfo(refid, version, no);
|
||||
|
||||
await send.object({
|
||||
player: K.ATTR({ no: `${no}` }, {
|
||||
is_succession: K.ITEM("bool", 0), //FIX THIS with upsert result.
|
||||
did: K.ITEM("s32", playerInfo.id)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
export const check: EPR = async (info, data, send) => {
|
||||
|
||||
const refid = $(data).str('player.refid');
|
||||
if (!refid) {
|
||||
logger.error("Request data is missing required parameter: player.refid")
|
||||
return send.deny();
|
||||
}
|
||||
|
||||
const no = getPlayerNo(data);
|
||||
const version = getVersion(info)
|
||||
const playerInfo = await getOrRegisterPlayerInfo(refid, version, no)
|
||||
|
||||
const result : CheckPlayerResponse = getCheckPlayerResponse(no, playerInfo.name, playerInfo.id)
|
||||
await send.object(result)
|
||||
}
|
||||
|
||||
export const getPlayer: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('player.refid');
|
||||
if (!refid) {
|
||||
logger.error("Request data is missing required parameter: player.refid")
|
||||
return send.deny();
|
||||
}
|
||||
|
||||
const no = getPlayerNo(data);
|
||||
const version = getVersion(info);
|
||||
const time = BigInt(31536000);
|
||||
const dm = isDM(info);
|
||||
const game = dm ? 'dm' : 'gf';
|
||||
|
||||
logger.debugInfo(`Loading ${game} profile for player ${no} with refid: ${refid}`)
|
||||
const name = await DB.FindOne<PlayerInfo>(refid, {
|
||||
collection: 'playerinfo',
|
||||
version
|
||||
})
|
||||
const dmProfile = await getProfile(refid, version, 'dm')
|
||||
const gfProfile = await getProfile(refid, version, 'gf')
|
||||
const dmRecord = await getRecord(refid, version, 'dm')
|
||||
const gfRecord = await getRecord(refid, version, 'gf')
|
||||
const dmExtra = await getExtra(refid, version, 'dm')
|
||||
const gfExtra = await getExtra(refid, version, 'gf')
|
||||
const dmScores = (await getScore(refid, version, 'dm')).scores
|
||||
const gfScores = (await getScore(refid, version, 'gf')).scores
|
||||
|
||||
const profile = dm ? dmProfile : gfProfile;
|
||||
const extra = dm ? dmExtra : gfExtra;
|
||||
|
||||
await applySharedFavoriteMusicToExtra(refid, extra)
|
||||
|
||||
const record: any = {
|
||||
gf: getPlayerRecordResponse(gfProfile, gfRecord),
|
||||
dm: getPlayerRecordResponse(dmProfile, dmRecord),
|
||||
};
|
||||
|
||||
// Format scores
|
||||
const musicdata = [];
|
||||
const scores = dm ? dmScores : gfScores;
|
||||
for (const [musicid, score] of _.entries(scores)) {
|
||||
musicdata.push(K.ATTR({ musicid }, {
|
||||
mdata: K.ARRAY('s16', [
|
||||
-1,
|
||||
_.get(score, 'diffs.1.perc', -2),
|
||||
_.get(score, 'diffs.2.perc', -2),
|
||||
_.get(score, 'diffs.3.perc', -2),
|
||||
_.get(score, 'diffs.4.perc', -2),
|
||||
_.get(score, 'diffs.5.perc', -2),
|
||||
_.get(score, 'diffs.6.perc', -2),
|
||||
_.get(score, 'diffs.7.perc', -2),
|
||||
_.get(score, 'diffs.8.perc', -2),
|
||||
_.get(score, 'diffs.1.rank', 0),
|
||||
_.get(score, 'diffs.2.rank', 0),
|
||||
_.get(score, 'diffs.3.rank', 0),
|
||||
_.get(score, 'diffs.4.rank', 0),
|
||||
_.get(score, 'diffs.5.rank', 0),
|
||||
_.get(score, 'diffs.6.rank', 0),
|
||||
_.get(score, 'diffs.7.rank', 0),
|
||||
_.get(score, 'diffs.8.rank', 0),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
flag: K.ARRAY('u16', [
|
||||
_.get(score, 'diffs.1.fc', false) * 2 +
|
||||
_.get(score, 'diffs.2.fc', false) * 4 +
|
||||
_.get(score, 'diffs.3.fc', false) * 8 +
|
||||
_.get(score, 'diffs.4.fc', false) * 16 +
|
||||
_.get(score, 'diffs.5.fc', false) * 32 +
|
||||
_.get(score, 'diffs.6.fc', false) * 64 +
|
||||
_.get(score, 'diffs.7.fc', false) * 128 +
|
||||
_.get(score, 'diffs.8.fc', false) * 256,
|
||||
_.get(score, 'diffs.1.ex', false) * 2 +
|
||||
_.get(score, 'diffs.2.ex', false) * 4 +
|
||||
_.get(score, 'diffs.3.ex', false) * 8 +
|
||||
_.get(score, 'diffs.4.ex', false) * 16 +
|
||||
_.get(score, 'diffs.5.ex', false) * 32 +
|
||||
_.get(score, 'diffs.6.ex', false) * 64 +
|
||||
_.get(score, 'diffs.7.ex', false) * 128 +
|
||||
_.get(score, 'diffs.8.ex', false) * 256,
|
||||
_.get(score, 'diffs.1.clear', false) * 2 +
|
||||
_.get(score, 'diffs.2.clear', false) * 4 +
|
||||
_.get(score, 'diffs.3.clear', false) * 8 +
|
||||
_.get(score, 'diffs.4.clear', false) * 16 +
|
||||
_.get(score, 'diffs.5.clear', false) * 32 +
|
||||
_.get(score, 'diffs.6.clear', false) * 64 +
|
||||
_.get(score, 'diffs.7.clear', false) * 128 +
|
||||
_.get(score, 'diffs.8.clear', false) * 256,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
sdata: K.ARRAY('s16', score.update),
|
||||
meter: K.ARRAY('u64', [
|
||||
BigInt(_.get(score, 'diffs.1.meter', '0')),
|
||||
BigInt(_.get(score, 'diffs.2.meter', '0')),
|
||||
BigInt(_.get(score, 'diffs.3.meter', '0')),
|
||||
BigInt(_.get(score, 'diffs.4.meter', '0')),
|
||||
BigInt(_.get(score, 'diffs.5.meter', '0')),
|
||||
BigInt(_.get(score, 'diffs.6.meter', '0')),
|
||||
BigInt(_.get(score, 'diffs.7.meter', '0')),
|
||||
BigInt(_.get(score, 'diffs.8.meter', '0')),
|
||||
]),
|
||||
meter_prog: K.ARRAY('s16', [
|
||||
_.get(score, 'diffs.1.prog', 0),
|
||||
_.get(score, 'diffs.2.prog', 0),
|
||||
_.get(score, 'diffs.3.prog', 0),
|
||||
_.get(score, 'diffs.4.prog', 0),
|
||||
_.get(score, 'diffs.5.prog', 0),
|
||||
_.get(score, 'diffs.6.prog', 0),
|
||||
_.get(score, 'diffs.7.prog', 0),
|
||||
_.get(score, 'diffs.8.prog', 0),
|
||||
]),
|
||||
}));
|
||||
}
|
||||
|
||||
const sticker: PlayerStickerResponse[] = getPlayerStickerResponse(name.card);
|
||||
const playinfo: PlayerPlayInfoResponse = getPlayerPlayInfoResponse(profile);
|
||||
|
||||
const playerData: any = {
|
||||
playerboard: {
|
||||
index: K.ITEM('s32', 1),
|
||||
is_active: K.ITEM('bool', _.isArray(name.card) ? 1 : 0),
|
||||
sticker,
|
||||
},
|
||||
player_info: {
|
||||
player_type: K.ITEM('s8', 0),
|
||||
did: K.ITEM('s32', 13376666),
|
||||
name: K.ITEM('str', name.name),
|
||||
title: K.ITEM('str', name.title),
|
||||
charaid: K.ITEM('s32', 0),
|
||||
},
|
||||
customdata: {
|
||||
playstyle: K.ARRAY('s32', extra.playstyle),
|
||||
custom: K.ARRAY('s32', extra.custom),
|
||||
},
|
||||
playinfo: playinfo,
|
||||
tutorial: {
|
||||
progress: K.ITEM('s32', profile.progress),
|
||||
disp_state: K.ITEM('u32', profile.disp_state),
|
||||
},
|
||||
skilldata: {
|
||||
skill: K.ITEM('s32', profile.skill),
|
||||
all_skill: K.ITEM('s32', profile.all_skill),
|
||||
old_skill: K.ITEM('s32', 0),
|
||||
old_all_skill: K.ITEM('s32', 0),
|
||||
},
|
||||
favoritemusic: {
|
||||
list_1: K.ARRAY('s32', extra.list_1),
|
||||
list_2: K.ARRAY('s32', extra.list_2),
|
||||
list_3: K.ARRAY('s32', extra.list_3),
|
||||
},
|
||||
recommend_musicid_list: K.ARRAY('s32', extra.recommend_musicid_list ?? Array(5).fill(-1)),
|
||||
record,
|
||||
groove: {
|
||||
extra_gauge: K.ITEM('s32', profile.extra_gauge),
|
||||
encore_gauge: K.ITEM('s32', profile.encore_gauge),
|
||||
encore_cnt: K.ITEM('s32', profile.encore_cnt),
|
||||
encore_success: K.ITEM('s32', profile.encore_success),
|
||||
unlock_point: K.ITEM('s32', profile.unlock_point),
|
||||
},
|
||||
musiclist: { '@attr': { nr: musicdata.length }, musicdata },
|
||||
};
|
||||
|
||||
const playerRanking = await getPlayerRanking(refid, version, game)
|
||||
|
||||
const addition: any = {
|
||||
monstar_subjugation: {},
|
||||
bear_fes: {},
|
||||
};
|
||||
for (let i = 1; i <= 20; ++i) {
|
||||
const obj = { point: K.ITEM('s32', 0) };
|
||||
if (i == 1) {
|
||||
addition['long_otobear_fes_1'] = obj;
|
||||
addition['phrase_combo_challenge'] = obj;
|
||||
addition['sdvx_stamprally3'] = obj;
|
||||
addition['chronicle_1'] = obj;
|
||||
} else {
|
||||
addition[`phrase_combo_challenge_${i}`] = obj;
|
||||
}
|
||||
|
||||
if (i <= 4) {
|
||||
addition.bear_fes[`bear_fes_${i}`] = {
|
||||
stage: K.ITEM('s32', 0),
|
||||
point: K.ARRAY('s32', [0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
};
|
||||
}
|
||||
|
||||
if (i <= 3) {
|
||||
addition.monstar_subjugation[`monstar_subjugation_${i}`] = {
|
||||
stage: K.ITEM('s32', 0),
|
||||
point_1: K.ITEM('s32', 0),
|
||||
point_2: K.ITEM('s32', 0),
|
||||
point_3: K.ITEM('s32', 0),
|
||||
};
|
||||
addition[`kouyou_challenge_${i}`] = { point: K.ITEM('s32', 0) };
|
||||
}
|
||||
}
|
||||
|
||||
const innerSecretMusic = getSecretMusicResponse(profile)
|
||||
const innerFriendData = getFriendDataResponse(profile)
|
||||
const innerBattleData = getDefaultBattleDataResponse()
|
||||
|
||||
const response = {
|
||||
player: K.ATTR({ 'no': `${no}` }, {
|
||||
now_date: K.ITEM('u64', time),
|
||||
secretmusic: {
|
||||
music: innerSecretMusic
|
||||
},
|
||||
chara_list: {},
|
||||
title_parts: {},
|
||||
information: {
|
||||
info: K.ARRAY('u32', Array(50).fill(0)),
|
||||
},
|
||||
reward: {
|
||||
status: K.ARRAY('u32', extra.reward_status ?? Array(50).fill(0)),
|
||||
},
|
||||
rivaldata: {},
|
||||
frienddata: {
|
||||
friend: innerFriendData
|
||||
},
|
||||
|
||||
thanks_medal: {
|
||||
medal: K.ITEM('s32', 0),
|
||||
grant_medal: K.ITEM('s32', 0),
|
||||
grant_total_medal: K.ITEM('s32', 0),
|
||||
},
|
||||
recommend_musicid_list: K.ARRAY('s32', extra.recommend_musicid_list ?? Array(5).fill(-1)),
|
||||
skindata: {
|
||||
skin: K.ARRAY('u32', Array(100).fill(-1)),
|
||||
},
|
||||
battledata: innerBattleData,
|
||||
is_free_ok: K.ITEM('bool', 0),
|
||||
ranking: {
|
||||
skill: { rank: K.ITEM('s32', playerRanking.skill), total_nr: K.ITEM('s32', playerRanking.totalPlayers) },
|
||||
all_skill: { rank: K.ITEM('s32', playerRanking.all_skill), total_nr: K.ITEM('s32', playerRanking.totalPlayers) },
|
||||
},
|
||||
stage_result: {},
|
||||
monthly_skill: {},
|
||||
event_skill: {
|
||||
skill: K.ITEM('s32', 0),
|
||||
ranking: {
|
||||
rank: K.ITEM('s32', 0),
|
||||
total_nr: K.ITEM('s32', 0),
|
||||
},
|
||||
eventlist: {},
|
||||
},
|
||||
event_score: { eventlist: {} },
|
||||
rockwave: { score_list: {} },
|
||||
jubeat_omiyage_challenge: {},
|
||||
light_mode_reward_item: { itemid: K.ITEM('s32', -1), rarity: K.ITEM('s32', 0) },
|
||||
standard_mode_reward_item: { itemid: K.ITEM('s32', -1), rarity: K.ITEM('s32', 0) },
|
||||
delux_mode_reward_item: { itemid: K.ITEM('s32', -1), rarity: K.ITEM('s32', 0) },
|
||||
kac2018: {
|
||||
entry_status: K.ITEM('s32', 0),
|
||||
data: {
|
||||
term: K.ITEM('s32', 0),
|
||||
total_score: K.ITEM('s32', 0),
|
||||
score: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
music_type: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
play_count: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
sticker_campaign: {},
|
||||
kac2017: {
|
||||
entry_status: K.ITEM('s32', 0),
|
||||
},
|
||||
nostalgia_concert: {},
|
||||
bemani_summer_2018: {
|
||||
linkage_id: K.ITEM('s32', -1),
|
||||
is_entry: K.ITEM('bool', 0),
|
||||
target_music_idx: K.ITEM('s32', -1),
|
||||
point_1: K.ITEM('s32', 0),
|
||||
point_2: K.ITEM('s32', 0),
|
||||
point_3: K.ITEM('s32', 0),
|
||||
point_4: K.ITEM('s32', 0),
|
||||
point_5: K.ITEM('s32', 0),
|
||||
point_6: K.ITEM('s32', 0),
|
||||
point_7: K.ITEM('s32', 0),
|
||||
reward_1: K.ITEM('bool', 0),
|
||||
reward_2: K.ITEM('bool', 0),
|
||||
reward_3: K.ITEM('bool', 0),
|
||||
reward_4: K.ITEM('bool', 0),
|
||||
reward_5: K.ITEM('bool', 0),
|
||||
reward_6: K.ITEM('bool', 0),
|
||||
reward_7: K.ITEM('bool', 0),
|
||||
unlock_status_1: K.ITEM('s32', 0),
|
||||
unlock_status_2: K.ITEM('s32', 0),
|
||||
unlock_status_3: K.ITEM('s32', 0),
|
||||
unlock_status_4: K.ITEM('s32', 0),
|
||||
unlock_status_5: K.ITEM('s32', 0),
|
||||
unlock_status_6: K.ITEM('s32', 0),
|
||||
unlock_status_7: K.ITEM('s32', 0),
|
||||
},
|
||||
thanksgiving: {
|
||||
term: K.ITEM("u8", 0),
|
||||
score: {
|
||||
one_day_play_cnt: K.ITEM("s32", 0),
|
||||
one_day_lottery_cnt: K.ITEM("s32", 0),
|
||||
lucky_star: K.ITEM("s32", 0),
|
||||
bear_mark: K.ITEM("s32", 0),
|
||||
play_date_ms: K.ITEM("u64", BigInt(0))
|
||||
},
|
||||
lottery_result: {
|
||||
unlock_bit: K.ITEM("u64", BigInt(0))
|
||||
}
|
||||
},
|
||||
lotterybox: {},
|
||||
...addition,
|
||||
...playerData,
|
||||
finish: K.ITEM('bool', 1),
|
||||
}),
|
||||
}
|
||||
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
await IO.WriteFile(`apisamples/lastGetPlayerRequest.json`, JSON.stringify(data, null, 4))
|
||||
await IO.WriteFile(`apisamples/lastGetPlayerResponse.json`, JSON.stringify(response, null, 4))
|
||||
}
|
||||
send.object(response);
|
||||
}
|
||||
|
||||
async function getOrRegisterPlayerInfo(refid: string, version: string, no: number) {
|
||||
let playerInfo = await DB.FindOne<PlayerInfo>(refid, {
|
||||
collection: 'playerinfo',
|
||||
version
|
||||
});
|
||||
|
||||
if (!playerInfo) {
|
||||
logger.debugInfo(`Registering new profile for player ${no} with refid: ${refid}`);
|
||||
playerInfo = await registerUser(refid, version);
|
||||
}
|
||||
return playerInfo;
|
||||
}
|
||||
|
||||
function getPlayerNo(data: any): number {
|
||||
return parseInt($(data).attr("player").no || '1', 10)
|
||||
}
|
||||
|
||||
async function registerUser(refid: string, version: string, id = _.random(0, 99999999)) {
|
||||
while (await DB.FindOne<Profile>(null, { collection: 'profile', id })) {
|
||||
id = _.random(0, 99999999);
|
||||
}
|
||||
|
||||
const defaultInfo: PlayerInfo = getDefaultPlayerInfo(version, id)
|
||||
|
||||
const gf = { game: 'gf', version };
|
||||
const dm = { game: 'dm', version };
|
||||
|
||||
await DB.Upsert(refid, { collection: 'playerinfo', version }, defaultInfo);
|
||||
await DB.Upsert(refid, { collection: 'profile', ...gf }, getDefaultProfile('gf', version, id));
|
||||
await DB.Upsert(refid, { collection: 'profile', ...dm }, getDefaultProfile('dm', version, id));
|
||||
await DB.Upsert(refid, { collection: 'record', ...gf }, getDefaultRecord('gf', version));
|
||||
await DB.Upsert(refid, { collection: 'record', ...dm }, getDefaultRecord('dm', version));
|
||||
await DB.Upsert(refid, { collection: 'extra', ...gf }, getDefaultExtra('gf', version, id));
|
||||
await DB.Upsert(refid, { collection: 'extra', ...dm }, getDefaultExtra('dm', version, id));
|
||||
await DB.Upsert(refid, { collection: 'scores', ...gf }, getDefaultScores('gf', version));
|
||||
await DB.Upsert(refid, { collection: 'scores', ...dm }, getDefaultScores('dm', version));
|
||||
|
||||
return defaultInfo
|
||||
}
|
||||
|
||||
export const savePlayers: EPR = async (info, data, send) => {
|
||||
|
||||
const version = getVersion(info);
|
||||
const dm = isDM(info);
|
||||
const game = dm ? 'dm' : 'gf';
|
||||
|
||||
let players = $(data).elements("player")
|
||||
|
||||
let response = {
|
||||
player: [],
|
||||
gamemode: _.get(data, 'gamemode'),
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
for (let player of players) {
|
||||
|
||||
const no = parseInt(player.attr().no || '1', 10)
|
||||
// Only save players that are using a profile. Don't try to save guest players.
|
||||
const hasCard = player.attr().card === 'use'
|
||||
if (!hasCard) {
|
||||
logger.debugInfo(`Skipping save for guest ${game} player ${no}.`)
|
||||
continue
|
||||
}
|
||||
|
||||
const refid = player.str('refid')
|
||||
if (!refid) {
|
||||
throw "Request data is missing required parameter: player.refid"
|
||||
}
|
||||
|
||||
await saveSinglePlayer(player, refid, no, version, game);
|
||||
|
||||
let ranking = await getPlayerRanking(refid, version, game)
|
||||
let responsePart = getSaveProfileResponse(no, ranking)
|
||||
response.player.push(responsePart)
|
||||
}
|
||||
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
await IO.WriteFile(`apisamples/lastSavePlayersRequest.json`, JSON.stringify(data, null, 4))
|
||||
await IO.WriteFile(`apisamples/lastSavePlayersResponse.json`, JSON.stringify(response, null, 4))
|
||||
}
|
||||
await send.object(response);
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(e)
|
||||
logger.error(e.stack)
|
||||
return send.deny();
|
||||
}
|
||||
};
|
||||
|
||||
async function saveSinglePlayer(dataplayer: KDataReader, refid: string, no: number, version: string, game: 'gf' | 'dm')
|
||||
{
|
||||
logger.debugInfo(`Saving ${game} profile for player ${no} with refid: ${refid}`)
|
||||
const profile = await getProfile(refid, version, game) as any;
|
||||
const extra = await getExtra(refid, version, game) as any;
|
||||
const rec = await getRecord(refid, version, game) as any;
|
||||
|
||||
const autoSet = function (field: keyof Profile, path: string, array = false): void {
|
||||
if (array) {
|
||||
profile[field] = dataplayer.numbers(path, profile[field])
|
||||
} else {
|
||||
profile[field] = dataplayer.number(path, profile[field])
|
||||
}
|
||||
};
|
||||
|
||||
const autoExtra = (field: keyof Extra, path: string, array = false): void => {
|
||||
if (array) {
|
||||
extra[field] = dataplayer.numbers(path, extra[field])
|
||||
} else {
|
||||
extra[field] = dataplayer.number(path, extra[field])
|
||||
}
|
||||
};
|
||||
|
||||
const autoRec = (field: keyof Record, path: string, array = false): void => {
|
||||
if (array) {
|
||||
rec[field] = dataplayer.numbers(path, rec[field])
|
||||
} else {
|
||||
rec[field] = dataplayer.number(path, rec[field])
|
||||
}
|
||||
};
|
||||
|
||||
let newSecretMusic = parseSecretMusic(dataplayer)
|
||||
profile.secretmusic = {
|
||||
music: newSecretMusic
|
||||
}
|
||||
|
||||
autoSet('max_skill', 'record.max.skill');
|
||||
autoSet('max_all_skill', 'record.max.all_skill');
|
||||
autoSet('clear_diff', 'record.max.clear_diff');
|
||||
autoSet('full_diff', 'record.max.full_diff');
|
||||
autoSet('exce_diff', 'record.max.exce_diff');
|
||||
autoSet('clear_music_num', 'record.max.clear_music_num');
|
||||
autoSet('full_music_num', 'record.max.full_music_num');
|
||||
autoSet('exce_music_num', 'record.max.exce_music_num');
|
||||
autoSet('clear_seq_num', 'record.max.clear_seq_num');
|
||||
autoSet('classic_all_skill', 'record.max.classic_all_skill');
|
||||
|
||||
autoSet('play', 'playinfo.play');
|
||||
autoSet('playtime', 'playinfo.playtime');
|
||||
autoSet('playterm', 'playinfo.playterm');
|
||||
autoSet('session_cnt', 'playinfo.session_cnt');
|
||||
autoSet('extra_stage', 'playinfo.extra_stage');
|
||||
autoSet('extra_play', 'playinfo.extra_play');
|
||||
autoSet('extra_clear', 'playinfo.extra_clear');
|
||||
autoSet('encore_play', 'playinfo.encore_play');
|
||||
autoSet('encore_clear', 'playinfo.encore_clear');
|
||||
autoSet('pencore_play', 'playinfo.pencore_play');
|
||||
autoSet('pencore_clear', 'playinfo.pencore_clear');
|
||||
autoSet('max_clear_diff', 'playinfo.max_clear_diff');
|
||||
autoSet('max_full_diff', 'playinfo.max_full_diff');
|
||||
autoSet('max_exce_diff', 'playinfo.max_exce_diff');
|
||||
autoSet('clear_num', 'playinfo.clear_num');
|
||||
autoSet('full_num', 'playinfo.full_num');
|
||||
autoSet('exce_num', 'playinfo.exce_num');
|
||||
autoSet('no_num', 'playinfo.no_num');
|
||||
autoSet('e_num', 'playinfo.e_num');
|
||||
autoSet('d_num', 'playinfo.d_num');
|
||||
autoSet('c_num', 'playinfo.c_num');
|
||||
autoSet('b_num', 'playinfo.b_num');
|
||||
autoSet('a_num', 'playinfo.a_num');
|
||||
autoSet('s_num', 'playinfo.s_num');
|
||||
autoSet('ss_num', 'playinfo.ss_num');
|
||||
autoSet('last_category', 'playinfo.last_category');
|
||||
autoSet('last_musicid', 'playinfo.last_musicid');
|
||||
autoSet('last_seq', 'playinfo.last_seq');
|
||||
autoSet('disp_level', 'playinfo.disp_level');
|
||||
|
||||
autoSet('extra_gauge', 'groove.extra_gauge');
|
||||
autoSet('encore_gauge', 'groove.encore_gauge');
|
||||
autoSet('encore_cnt', 'groove.encore_cnt');
|
||||
autoSet('encore_success', 'groove.encore_success');
|
||||
autoSet('unlock_point', 'groove.unlock_point');
|
||||
|
||||
autoSet('progress', 'tutorial.progress');
|
||||
autoSet('disp_state', 'tutorial.disp_state');
|
||||
|
||||
autoSet('skill', 'skilldata.skill');
|
||||
autoSet('all_skill', 'skilldata.all_skill');
|
||||
|
||||
autoRec('diff_100_nr', 'record.diff.diff_100_nr');
|
||||
autoRec('diff_150_nr', 'record.diff.diff_150_nr');
|
||||
autoRec('diff_200_nr', 'record.diff.diff_200_nr');
|
||||
autoRec('diff_250_nr', 'record.diff.diff_250_nr');
|
||||
autoRec('diff_300_nr', 'record.diff.diff_300_nr');
|
||||
autoRec('diff_350_nr', 'record.diff.diff_350_nr');
|
||||
autoRec('diff_400_nr', 'record.diff.diff_400_nr');
|
||||
autoRec('diff_450_nr', 'record.diff.diff_450_nr');
|
||||
autoRec('diff_500_nr', 'record.diff.diff_500_nr');
|
||||
autoRec('diff_550_nr', 'record.diff.diff_550_nr');
|
||||
autoRec('diff_600_nr', 'record.diff.diff_600_nr');
|
||||
autoRec('diff_650_nr', 'record.diff.diff_650_nr');
|
||||
autoRec('diff_700_nr', 'record.diff.diff_700_nr');
|
||||
autoRec('diff_750_nr', 'record.diff.diff_750_nr');
|
||||
autoRec('diff_800_nr', 'record.diff.diff_800_nr');
|
||||
autoRec('diff_850_nr', 'record.diff.diff_850_nr');
|
||||
autoRec('diff_900_nr', 'record.diff.diff_900_nr');
|
||||
autoRec('diff_950_nr', 'record.diff.diff_950_nr');
|
||||
autoRec('diff_100_clear', 'record.diff.diff_100_clear', true);
|
||||
autoRec('diff_150_clear', 'record.diff.diff_150_clear', true);
|
||||
autoRec('diff_200_clear', 'record.diff.diff_200_clear', true);
|
||||
autoRec('diff_250_clear', 'record.diff.diff_250_clear', true);
|
||||
autoRec('diff_300_clear', 'record.diff.diff_300_clear', true);
|
||||
autoRec('diff_350_clear', 'record.diff.diff_350_clear', true);
|
||||
autoRec('diff_400_clear', 'record.diff.diff_400_clear', true);
|
||||
autoRec('diff_450_clear', 'record.diff.diff_450_clear', true);
|
||||
autoRec('diff_500_clear', 'record.diff.diff_500_clear', true);
|
||||
autoRec('diff_550_clear', 'record.diff.diff_550_clear', true);
|
||||
autoRec('diff_600_clear', 'record.diff.diff_600_clear', true);
|
||||
autoRec('diff_650_clear', 'record.diff.diff_650_clear', true);
|
||||
autoRec('diff_700_clear', 'record.diff.diff_700_clear', true);
|
||||
autoRec('diff_750_clear', 'record.diff.diff_750_clear', true);
|
||||
autoRec('diff_800_clear', 'record.diff.diff_800_clear', true);
|
||||
autoRec('diff_850_clear', 'record.diff.diff_850_clear', true);
|
||||
autoRec('diff_900_clear', 'record.diff.diff_900_clear', true);
|
||||
autoRec('diff_950_clear', 'record.diff.diff_950_clear', true);
|
||||
|
||||
autoExtra('list_1', 'favoritemusic.music_list_1', true);
|
||||
autoExtra('list_2', 'favoritemusic.music_list_2', true);
|
||||
autoExtra('list_3', 'favoritemusic.music_list_3', true);
|
||||
autoExtra('recommend_musicid_list', 'recommend_musicid_list', true);
|
||||
|
||||
autoExtra('playstyle', 'customdata.playstyle', true);
|
||||
autoExtra('custom', 'customdata.custom', true);
|
||||
autoExtra('reward_status', 'reward.status', true)
|
||||
|
||||
await DB.Upsert(refid, { collection: 'profile', game, version }, profile)
|
||||
await DB.Upsert(refid, { collection: 'record', game, version }, rec)
|
||||
await DB.Upsert(refid, { collection: 'extra', game, version }, extra)
|
||||
|
||||
const playedStages = dataplayer.elements('stage');
|
||||
logStagesPlayed(playedStages)
|
||||
|
||||
const scores = await updatePlayerScoreCollection(refid, playedStages, version, game)
|
||||
await saveScore(refid, version, game, scores);
|
||||
await saveSharedFavoriteMusicFromExtra(refid, extra)
|
||||
}
|
||||
|
||||
async function updatePlayerScoreCollection(refid, playedStages, version, game) {
|
||||
const scores = (await getScore(refid, version, game)).scores;
|
||||
for (const stage of playedStages) {
|
||||
const mid = stage.number('musicid', -1);
|
||||
const seq = stage.number('seq', -1);
|
||||
|
||||
if (mid < 0 || seq < 0) continue;
|
||||
|
||||
// const skill = stage.number('skill', 0);
|
||||
const newSkill = stage.number('new_skill', 0);
|
||||
const clear = stage.bool('clear');
|
||||
const fc = stage.bool('fullcombo');
|
||||
const ex = stage.bool('excellent');
|
||||
|
||||
const perc = stage.number('perc', 0);
|
||||
const rank = stage.number('rank', 0);
|
||||
const meter = stage.bigint('meter', BigInt(0));
|
||||
const prog = stage.number('meter_prog', 0);
|
||||
|
||||
if(!scores[mid]) {
|
||||
scores[mid] = {
|
||||
update: [0, 0],
|
||||
diffs: {}
|
||||
}
|
||||
}
|
||||
|
||||
if (newSkill > scores[mid].update[1]) {
|
||||
scores[mid].update[0] = seq;
|
||||
scores[mid].update[1] = newSkill;
|
||||
}
|
||||
|
||||
scores[mid].diffs[seq] = { //FIXME: Real server is bit complicated. this one is too buggy.
|
||||
perc: Math.max(_.get(scores[mid].diffs[seq], 'perc', 0), perc),
|
||||
rank: Math.max(_.get(scores[mid].diffs[seq], 'rank', 0), rank),
|
||||
meter: meter.toString(),
|
||||
prog: Math.max(_.get(scores[mid].diffs[seq], 'prog', 0), prog),
|
||||
clear: _.get(scores[mid].diffs[seq], 'clear') || clear,
|
||||
fc: _.get(scores[mid].diffs[seq], 'fc') || fc,
|
||||
ex: _.get(scores[mid].diffs[seq], 'ex') || ex,
|
||||
};
|
||||
}
|
||||
|
||||
return scores
|
||||
}
|
||||
|
||||
async function getPlayerRanking(refid: string, version: string, game: 'gf' | 'dm') : Promise<PlayerRanking> {
|
||||
let profiles = await getAllProfiles(version, game)
|
||||
let playerCount = profiles.length
|
||||
let sortedProfilesA = profiles.sort((a,b) => b.skill - a.skill)
|
||||
let sortedProfilesB = profiles.sort((a,b) => b.all_skill - a.all_skill)
|
||||
|
||||
let idxA = _.findIndex(sortedProfilesA, (e) => e.__refid === refid)
|
||||
idxA = idxA > -1 ? idxA + 1 : playerCount // Default to last place if not found in the DB.
|
||||
let idxB = _.findIndex(sortedProfilesB, (e) => e.__refid === refid)
|
||||
idxB = idxB > -1 ? idxB + 1 : playerCount // Default to last place if not found in the DB.
|
||||
|
||||
return {
|
||||
refid,
|
||||
skill: idxA,
|
||||
all_skill: idxB,
|
||||
totalPlayers: playerCount
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllProfiles( version: string, game: 'gf' | 'dm') {
|
||||
return await DB.Find<Profile>(null, {
|
||||
collection: 'profile',
|
||||
version: version,
|
||||
game: game
|
||||
})
|
||||
}
|
||||
|
||||
async function getProfile(refid: string, version: string, game: 'gf' | 'dm') {
|
||||
return await DB.FindOne<Profile>(refid, {
|
||||
collection: 'profile',
|
||||
version: version,
|
||||
game: game
|
||||
})
|
||||
}
|
||||
|
||||
async function getExtra(refid: string, version: string, game: 'gf' | 'dm') {
|
||||
return await DB.FindOne<Extra>(refid, {
|
||||
collection: 'extra',
|
||||
version: version,
|
||||
game: game
|
||||
})
|
||||
}
|
||||
|
||||
async function getRecord(refid: string, version: string, game: 'gf' | 'dm') {
|
||||
return await DB.FindOne<Record>(refid, {
|
||||
collection: 'record',
|
||||
version: version,
|
||||
game: game
|
||||
})
|
||||
}
|
||||
|
||||
async function getScore(refid: string, version: string, game: 'gf' | 'dm'): Promise<Scores> {
|
||||
return (await DB.FindOne<Scores>(refid, {
|
||||
collection: 'scores',
|
||||
version: version,
|
||||
game: game
|
||||
})) || {
|
||||
collection: 'scores',
|
||||
version: version,
|
||||
pluginVer: PLUGIN_VER,
|
||||
game: game,
|
||||
scores: {}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveScore(refid: string, version: string, game: 'gf' | 'dm', scores: Scores['scores']) {
|
||||
return await DB.Upsert<Scores>(refid, {
|
||||
collection: 'scores',
|
||||
version,
|
||||
game
|
||||
}, {
|
||||
collection: 'scores',
|
||||
version,
|
||||
game,
|
||||
scores
|
||||
})
|
||||
}
|
||||
|
||||
function parseSecretMusic(playerData: KDataReader) : SecretMusicEntry[]
|
||||
{
|
||||
let response : SecretMusicEntry[] = []
|
||||
|
||||
let elements = playerData.element('secretmusic')?.elements('music')
|
||||
if (!elements) {
|
||||
return response
|
||||
}
|
||||
|
||||
for (let el of elements) {
|
||||
let item : SecretMusicEntry = {
|
||||
musicid: el.number('musicid'),
|
||||
seq: el.number('seq'),
|
||||
kind: el.number('kind')
|
||||
}
|
||||
|
||||
response.push(item)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
function getFriendDataResponse(profile: Profile) {
|
||||
let response = []
|
||||
return response;
|
||||
}
|
||||
|
||||
function logStagesPlayed(playedStages: KDataReader[]) {
|
||||
|
||||
let result = "Stages played: "
|
||||
for (let stage of playedStages) {
|
||||
let id = stage.number('musicid')
|
||||
result += `${id}, `
|
||||
}
|
||||
|
||||
logger.debugLog(result)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { PlayerInfo } from "../models/playerinfo"
|
||||
|
||||
export const updatePlayerInfo = async (data: {
|
||||
refid: string;
|
||||
version: string;
|
||||
name?: string;
|
||||
title?: string;
|
||||
}) => {
|
||||
if (data.refid == null) return;
|
||||
|
||||
const update: Update<PlayerInfo>['$set'] = {};
|
||||
|
||||
if (data.name && data.name.length > 0) {
|
||||
//TODO: name validator
|
||||
update.name = data.name;
|
||||
}
|
||||
|
||||
if (data.title && data.title.length > 0) {
|
||||
//TODO: title validator
|
||||
update.title = data.title;
|
||||
}
|
||||
|
||||
await DB.Update<PlayerInfo>(
|
||||
data.refid,
|
||||
{ collection: 'playerinfo', version: data.version },
|
||||
{ $set: update }
|
||||
);
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
import { gameInfoGet, shopInfoRegist } from "./handlers/info";
|
||||
import { playableMusic } from "./handlers/MusicList"
|
||||
import { getPlayer, check, regist, savePlayers } from "./handlers/profiles";
|
||||
import { updatePlayerInfo } from "./handlers/webui";
|
||||
import { isAsphyxiaDebugMode, isRequiredCoreVersion } from "./utils";
|
||||
import Logger from "./utils/logger";
|
||||
|
||||
const logger = new Logger("main")
|
||||
|
||||
export function register() {
|
||||
if(!isRequiredCoreVersion(1, 20)) {
|
||||
console.error("A newer version of Asphyxia Core (v1.20 or later) is required.")
|
||||
}
|
||||
|
||||
R.GameCode('M32');
|
||||
|
||||
R.Config("encore_version", {
|
||||
name: "Encore Version",
|
||||
desc: "Set encore version",
|
||||
type: "integer",
|
||||
default: 13,
|
||||
})
|
||||
|
||||
R.Config("nextage_dummy_encore", {
|
||||
name: "Dummy Encore for SPE (Nextage Only)",
|
||||
desc: "Since Nextage's Special Premium Encore system is bit complicated, \n"
|
||||
+ "SPE System isn't fully implemented. \n"
|
||||
+ "This option is a workaround for this issue as limiting some Encores for SPE.",
|
||||
type: "boolean",
|
||||
default: false
|
||||
})
|
||||
|
||||
R.Config("enable_custom_mdb", {
|
||||
name: "Enable Custom MDB",
|
||||
desc: "If disabled, the server will provide the default MDB (song list) to Gitadora clients, depending on which version of the game they are running." +
|
||||
"Enable this option to provide your own custom MDB instead. MDB files are stored in the 'gitadora@asphyxia/data/mdb' folder, and can be in .xml, .json or .b64 format.",
|
||||
type: "boolean",
|
||||
default: false,
|
||||
})
|
||||
|
||||
R.Config("shared_favorite_songs", {
|
||||
name: "Shared Favorite Songs (Experimental)",
|
||||
desc: "If disabled, players will be able to keep separate lists of favorite songs for each version of Gitadora, as well as between Guitar Freaks and Drummania. " +
|
||||
"Enable this option to have a single unified list of favorite songs for both games, and across all versions. Default is false, to match original arcade behaviour.",
|
||||
type: "boolean",
|
||||
default: false,
|
||||
})
|
||||
|
||||
R.DataFile("data/mdb/custom.xml", {
|
||||
accept: ".xml",
|
||||
name: "Custom MDB",
|
||||
desc: "Remember to enable the 'Enable Custom MDB' option for the uploaded file to have any effect."
|
||||
})
|
||||
|
||||
R.WebUIEvent('updatePlayerInfo', updatePlayerInfo);
|
||||
|
||||
const MultiRoute = (method: string, handler: EPR | boolean) => {
|
||||
// Helper for register multiple versions.
|
||||
R.Route(`exchain_${method}`, handler);
|
||||
R.Route(`matixx_${method}`, handler);
|
||||
R.Route(`nextage_${method}`, handler)
|
||||
// TODO: TB, TBRE and more older version?
|
||||
};
|
||||
|
||||
// Info
|
||||
MultiRoute('shopinfo.regist', shopInfoRegist)
|
||||
MultiRoute('gameinfo.get', gameInfoGet)
|
||||
|
||||
// MusicList
|
||||
MultiRoute('playablemusic.get', playableMusic)
|
||||
|
||||
// Profile
|
||||
MultiRoute('cardutil.regist', regist);
|
||||
MultiRoute('cardutil.check', check);
|
||||
MultiRoute('gametop.get', getPlayer);
|
||||
MultiRoute('gameend.regist', savePlayers);
|
||||
|
||||
// Misc
|
||||
R.Route('bemani_gakuen.get_music_info', true)
|
||||
|
||||
R.Unhandled(async (info, data, send) => {
|
||||
if (["eventlog"].includes(info.module)) return;
|
||||
logger.error(`Received Unhandled Request on Method "${info.method}" by ${info.model}/${info.module}`)
|
||||
logger.debugError(`Received Request: ${JSON.stringify(data, null, 4)}`)
|
||||
})
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
export interface BattleDataResponse
|
||||
{
|
||||
info: {
|
||||
orb: KITEM<'s32'>,
|
||||
get_gb_point: KITEM<'s32'>,
|
||||
send_gb_point: KITEM<'s32'>,
|
||||
}
|
||||
greeting: {
|
||||
greeting_1: KITEM<'str'>,
|
||||
greeting_2: KITEM<'str'>,
|
||||
greeting_3: KITEM<'str'>,
|
||||
greeting_4: KITEM<'str'>,
|
||||
greeting_5: KITEM<'str'>,
|
||||
greeting_6: KITEM<'str'>,
|
||||
greeting_7: KITEM<'str'>,
|
||||
greeting_8: KITEM<'str'>,
|
||||
greeting_9: KITEM<'str'>,
|
||||
|
||||
}
|
||||
setting: {
|
||||
matching: KITEM<'s32'>,
|
||||
info_level: KITEM<'s32'>,
|
||||
}
|
||||
|
||||
score: {
|
||||
battle_class: KITEM<'s32'>,
|
||||
max_battle_class: KITEM<'s32'>,
|
||||
battle_point: KITEM<'s32'>,
|
||||
win: KITEM<'s32'>,
|
||||
lose: KITEM<'s32'>,
|
||||
draw: KITEM<'s32'>,
|
||||
consecutive_win: KITEM<'s32'>,
|
||||
max_consecutive_win: KITEM<'s32'>,
|
||||
glorious_win: KITEM<'s32'>,
|
||||
max_defeat_skill: KITEM<'s32'>,
|
||||
latest_result: KITEM<'s32'>,
|
||||
|
||||
}
|
||||
history: {}
|
||||
}
|
||||
|
||||
export function getDefaultBattleDataResponse() : BattleDataResponse {
|
||||
return {
|
||||
info: {
|
||||
orb: K.ITEM('s32', 0),
|
||||
get_gb_point: K.ITEM('s32', 0),
|
||||
send_gb_point: K.ITEM('s32', 0),
|
||||
},
|
||||
greeting: {
|
||||
greeting_1: K.ITEM('str', ''),
|
||||
greeting_2: K.ITEM('str', ''),
|
||||
greeting_3: K.ITEM('str', ''),
|
||||
greeting_4: K.ITEM('str', ''),
|
||||
greeting_5: K.ITEM('str', ''),
|
||||
greeting_6: K.ITEM('str', ''),
|
||||
greeting_7: K.ITEM('str', ''),
|
||||
greeting_8: K.ITEM('str', ''),
|
||||
greeting_9: K.ITEM('str', ''),
|
||||
},
|
||||
setting: {
|
||||
matching: K.ITEM('s32', 0),
|
||||
info_level: K.ITEM('s32', 0),
|
||||
},
|
||||
score: {
|
||||
battle_class: K.ITEM('s32', 0),
|
||||
max_battle_class: K.ITEM('s32', 0),
|
||||
battle_point: K.ITEM('s32', 0),
|
||||
win: K.ITEM('s32', 0),
|
||||
lose: K.ITEM('s32', 0),
|
||||
draw: K.ITEM('s32', 0),
|
||||
consecutive_win: K.ITEM('s32', 0),
|
||||
max_consecutive_win: K.ITEM('s32', 0),
|
||||
glorious_win: K.ITEM('s32', 0),
|
||||
max_defeat_skill: K.ITEM('s32', 0),
|
||||
latest_result: K.ITEM('s32', 0),
|
||||
},
|
||||
history: {},
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
export interface CheckPlayerResponse {
|
||||
player: {
|
||||
name: KITEM<'str'>,
|
||||
charaid: KITEM<'s32'>,
|
||||
did: KITEM<'s32'>,
|
||||
skilldata: {
|
||||
skill: KITEM<'s32'>
|
||||
all_skill: KITEM<'s32'>
|
||||
old_skill: KITEM<'s32'>
|
||||
old_all_skill: KITEM<'s32'>
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function getCheckPlayerResponse(playerNo : number, name: string, id: number) : CheckPlayerResponse
|
||||
{
|
||||
return {
|
||||
player: K.ATTR({ no: `${playerNo}`, state: '2' }, {
|
||||
name: K.ITEM('str', name),
|
||||
charaid: K.ITEM('s32', 0),
|
||||
did: K.ITEM('s32', id),
|
||||
skilldata: {
|
||||
skill: K.ITEM('s32', 0),
|
||||
all_skill: K.ITEM('s32', 0),
|
||||
old_skill: K.ITEM('s32', 0),
|
||||
old_all_skill: K.ITEM('s32', 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { CommonMusicDataField } from "../commonmusicdata"
|
||||
|
||||
export interface PlayableMusicResponse
|
||||
{
|
||||
hot: {
|
||||
major: KITEM<'s32'>,
|
||||
minor: KITEM<'s32'>
|
||||
}
|
||||
musicinfo: KATTR<any>
|
||||
}
|
||||
|
||||
export function getPlayableMusicResponse(music : CommonMusicDataField[]) : PlayableMusicResponse {
|
||||
return {
|
||||
hot: {
|
||||
major: K.ITEM('s32', 1),
|
||||
minor: K.ITEM('s32', 1),
|
||||
},
|
||||
musicinfo: K.ATTR({ nr: `${music.length}` }, {
|
||||
music,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import { Profile } from "../profile";
|
||||
|
||||
export interface PlayerPlayInfoResponse {
|
||||
cabid: KITEM<'s32'>,
|
||||
play: KITEM<'s32'>,
|
||||
playtime: KITEM<'s32'>,
|
||||
playterm: KITEM<'s32'>,
|
||||
session_cnt: KITEM<'s32'>,
|
||||
matching_num: KITEM<'s32'>,
|
||||
extra_stage: KITEM<'s32'>,
|
||||
extra_play: KITEM<'s32'>,
|
||||
extra_clear: KITEM<'s32'>,
|
||||
encore_play: KITEM<'s32'>,
|
||||
encore_clear: KITEM<'s32'>,
|
||||
pencore_play: KITEM<'s32'>,
|
||||
pencore_clear: KITEM<'s32'>,
|
||||
max_clear_diff: KITEM<'s32'>,
|
||||
max_full_diff: KITEM<'s32'>,
|
||||
max_exce_diff: KITEM<'s32'>,
|
||||
clear_num: KITEM<'s32'>,
|
||||
full_num: KITEM<'s32'>,
|
||||
exce_num: KITEM<'s32'>,
|
||||
no_num: KITEM<'s32'>,
|
||||
e_num: KITEM<'s32'>,
|
||||
d_num: KITEM<'s32'>,
|
||||
c_num: KITEM<'s32'>,
|
||||
b_num: KITEM<'s32'>,
|
||||
a_num: KITEM<'s32'>,
|
||||
s_num: KITEM<'s32'>,
|
||||
ss_num: KITEM<'s32'>,
|
||||
last_category: KITEM<'s32'>,
|
||||
last_musicid: KITEM<'s32'>,
|
||||
last_seq: KITEM<'s32'>,
|
||||
disp_level: KITEM<'s32'>,
|
||||
}
|
||||
export function getPlayerPlayInfoResponse(profile : Profile) : PlayerPlayInfoResponse {
|
||||
return {
|
||||
cabid: K.ITEM('s32', 0),
|
||||
play: K.ITEM('s32', profile.play),
|
||||
playtime: K.ITEM('s32', profile.playtime),
|
||||
playterm: K.ITEM('s32', profile.playterm),
|
||||
session_cnt: K.ITEM('s32', profile.session_cnt),
|
||||
matching_num: K.ITEM('s32', 0),
|
||||
extra_stage: K.ITEM('s32', profile.extra_stage),
|
||||
extra_play: K.ITEM('s32', profile.extra_play),
|
||||
extra_clear: K.ITEM('s32', profile.extra_clear),
|
||||
encore_play: K.ITEM('s32', profile.encore_play),
|
||||
encore_clear: K.ITEM('s32', profile.encore_clear),
|
||||
pencore_play: K.ITEM('s32', profile.pencore_play),
|
||||
pencore_clear: K.ITEM('s32', profile.pencore_clear),
|
||||
max_clear_diff: K.ITEM('s32', profile.max_clear_diff),
|
||||
max_full_diff: K.ITEM('s32', profile.max_full_diff),
|
||||
max_exce_diff: K.ITEM('s32', profile.max_exce_diff),
|
||||
clear_num: K.ITEM('s32', profile.clear_num),
|
||||
full_num: K.ITEM('s32', profile.full_num),
|
||||
exce_num: K.ITEM('s32', profile.exce_num),
|
||||
no_num: K.ITEM('s32', profile.no_num),
|
||||
e_num: K.ITEM('s32', profile.e_num),
|
||||
d_num: K.ITEM('s32', profile.d_num),
|
||||
c_num: K.ITEM('s32', profile.c_num),
|
||||
b_num: K.ITEM('s32', profile.b_num),
|
||||
a_num: K.ITEM('s32', profile.a_num),
|
||||
s_num: K.ITEM('s32', profile.s_num),
|
||||
ss_num: K.ITEM('s32', profile.ss_num),
|
||||
last_category: K.ITEM('s32', profile.last_category),
|
||||
last_musicid: K.ITEM('s32', profile.last_musicid),
|
||||
last_seq: K.ITEM('s32', profile.last_seq),
|
||||
disp_level: K.ITEM('s32', profile.disp_level),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import { Profile } from "../profile"
|
||||
import { Record } from "../record"
|
||||
|
||||
export interface PlayerRecordResponse {
|
||||
max_record: {
|
||||
skill: KITEM<'s32'>,
|
||||
all_skill: KITEM<'s32'>,
|
||||
clear_diff: KITEM<'s32'>,
|
||||
full_diff: KITEM<'s32'>,
|
||||
exce_diff: KITEM<'s32'>,
|
||||
clear_music_num: KITEM<'s32'>,
|
||||
full_music_num: KITEM<'s32'>,
|
||||
exce_music_num: KITEM<'s32'>,
|
||||
clear_seq_num: KITEM<'s32'>,
|
||||
classic_all_skill: KITEM<'s32'>
|
||||
},
|
||||
diff_record: {
|
||||
diff_100_nr: KITEM<'s32'>,
|
||||
diff_150_nr: KITEM<'s32'>,
|
||||
diff_200_nr: KITEM<'s32'>,
|
||||
diff_250_nr: KITEM<'s32'>,
|
||||
diff_300_nr: KITEM<'s32'>,
|
||||
diff_350_nr: KITEM<'s32'>,
|
||||
diff_400_nr: KITEM<'s32'>,
|
||||
diff_450_nr: KITEM<'s32'>,
|
||||
diff_500_nr: KITEM<'s32'>,
|
||||
diff_550_nr: KITEM<'s32'>,
|
||||
diff_600_nr: KITEM<'s32'>,
|
||||
diff_650_nr: KITEM<'s32'>,
|
||||
diff_700_nr: KITEM<'s32'>,
|
||||
diff_750_nr: KITEM<'s32'>,
|
||||
diff_800_nr: KITEM<'s32'>,
|
||||
diff_850_nr: KITEM<'s32'>,
|
||||
diff_900_nr: KITEM<'s32'>,
|
||||
diff_950_nr: KITEM<'s32'>,
|
||||
diff_100_clear: KARRAY<'s32'>
|
||||
diff_150_clear: KARRAY<'s32'>
|
||||
diff_200_clear: KARRAY<'s32'>
|
||||
diff_250_clear: KARRAY<'s32'>
|
||||
diff_300_clear: KARRAY<'s32'>
|
||||
diff_350_clear: KARRAY<'s32'>
|
||||
diff_400_clear: KARRAY<'s32'>
|
||||
diff_450_clear: KARRAY<'s32'>
|
||||
diff_500_clear: KARRAY<'s32'>
|
||||
diff_550_clear: KARRAY<'s32'>
|
||||
diff_600_clear: KARRAY<'s32'>
|
||||
diff_650_clear: KARRAY<'s32'>
|
||||
diff_700_clear: KARRAY<'s32'>
|
||||
diff_750_clear: KARRAY<'s32'>
|
||||
diff_800_clear: KARRAY<'s32'>
|
||||
diff_850_clear: KARRAY<'s32'>
|
||||
diff_900_clear: KARRAY<'s32'>
|
||||
diff_950_clear: KARRAY<'s32'>
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlayerRecordResponse(profile: Profile, rec: Record) : PlayerRecordResponse {
|
||||
return {
|
||||
max_record: {
|
||||
skill: K.ITEM('s32', profile.max_skill),
|
||||
all_skill: K.ITEM('s32', profile.max_all_skill),
|
||||
clear_diff: K.ITEM('s32', profile.clear_diff),
|
||||
full_diff: K.ITEM('s32', profile.full_diff),
|
||||
exce_diff: K.ITEM('s32', profile.exce_diff),
|
||||
clear_music_num: K.ITEM('s32', profile.clear_music_num),
|
||||
full_music_num: K.ITEM('s32', profile.full_music_num),
|
||||
exce_music_num: K.ITEM('s32', profile.exce_music_num),
|
||||
clear_seq_num: K.ITEM('s32', profile.clear_seq_num),
|
||||
classic_all_skill: K.ITEM('s32', profile.classic_all_skill),
|
||||
},
|
||||
diff_record: {
|
||||
diff_100_nr: K.ITEM('s32', rec.diff_100_nr),
|
||||
diff_150_nr: K.ITEM('s32', rec.diff_150_nr),
|
||||
diff_200_nr: K.ITEM('s32', rec.diff_200_nr),
|
||||
diff_250_nr: K.ITEM('s32', rec.diff_250_nr),
|
||||
diff_300_nr: K.ITEM('s32', rec.diff_300_nr),
|
||||
diff_350_nr: K.ITEM('s32', rec.diff_350_nr),
|
||||
diff_400_nr: K.ITEM('s32', rec.diff_400_nr),
|
||||
diff_450_nr: K.ITEM('s32', rec.diff_450_nr),
|
||||
diff_500_nr: K.ITEM('s32', rec.diff_500_nr),
|
||||
diff_550_nr: K.ITEM('s32', rec.diff_550_nr),
|
||||
diff_600_nr: K.ITEM('s32', rec.diff_600_nr),
|
||||
diff_650_nr: K.ITEM('s32', rec.diff_650_nr),
|
||||
diff_700_nr: K.ITEM('s32', rec.diff_700_nr),
|
||||
diff_750_nr: K.ITEM('s32', rec.diff_750_nr),
|
||||
diff_800_nr: K.ITEM('s32', rec.diff_800_nr),
|
||||
diff_850_nr: K.ITEM('s32', rec.diff_850_nr),
|
||||
diff_900_nr: K.ITEM('s32', rec.diff_900_nr),
|
||||
diff_950_nr: K.ITEM('s32', rec.diff_950_nr),
|
||||
diff_100_clear: K.ARRAY('s32', rec.diff_100_clear),
|
||||
diff_150_clear: K.ARRAY('s32', rec.diff_150_clear),
|
||||
diff_200_clear: K.ARRAY('s32', rec.diff_200_clear),
|
||||
diff_250_clear: K.ARRAY('s32', rec.diff_250_clear),
|
||||
diff_300_clear: K.ARRAY('s32', rec.diff_300_clear),
|
||||
diff_350_clear: K.ARRAY('s32', rec.diff_350_clear),
|
||||
diff_400_clear: K.ARRAY('s32', rec.diff_400_clear),
|
||||
diff_450_clear: K.ARRAY('s32', rec.diff_450_clear),
|
||||
diff_500_clear: K.ARRAY('s32', rec.diff_500_clear),
|
||||
diff_550_clear: K.ARRAY('s32', rec.diff_550_clear),
|
||||
diff_600_clear: K.ARRAY('s32', rec.diff_600_clear),
|
||||
diff_650_clear: K.ARRAY('s32', rec.diff_650_clear),
|
||||
diff_700_clear: K.ARRAY('s32', rec.diff_700_clear),
|
||||
diff_750_clear: K.ARRAY('s32', rec.diff_750_clear),
|
||||
diff_800_clear: K.ARRAY('s32', rec.diff_800_clear),
|
||||
diff_850_clear: K.ARRAY('s32', rec.diff_850_clear),
|
||||
diff_900_clear: K.ARRAY('s32', rec.diff_900_clear),
|
||||
diff_950_clear: K.ARRAY('s32', rec.diff_950_clear),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
export interface PlayerStickerResponse {
|
||||
id: KITEM<'s32'>,
|
||||
pos_x: KITEM<'float'> ,
|
||||
pos_y: KITEM<'float'>,
|
||||
scale_x: KITEM<'float'> ,
|
||||
scale_y: KITEM<'float'>,
|
||||
rotate: KITEM<'float'>
|
||||
}
|
||||
|
||||
export function getPlayerStickerResponse(playerCard : any[]) : PlayerStickerResponse[] {
|
||||
let stickers : PlayerStickerResponse[] = []
|
||||
if (!_.isArray(playerCard)) {
|
||||
return stickers
|
||||
}
|
||||
|
||||
for (const item of playerCard) {
|
||||
const id = _.get(item, 'id');
|
||||
const posX = _.get(item, 'position.0');
|
||||
const posY = _.get(item, 'position.1');
|
||||
const scaleX = _.get(item, 'scale.0');
|
||||
const scaleY = _.get(item, 'scale.1');
|
||||
const rotation = _.get(item, 'rotation');
|
||||
|
||||
if (
|
||||
!isFinite(id) ||
|
||||
!isFinite(posX) ||
|
||||
!isFinite(posY) ||
|
||||
!isFinite(scaleX) ||
|
||||
!isFinite(scaleY) ||
|
||||
!isFinite(rotation)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stickers.push({
|
||||
id: K.ITEM('s32', id),
|
||||
pos_x: K.ITEM('float', posX),
|
||||
pos_y: K.ITEM('float', posY),
|
||||
scale_x: K.ITEM('float', scaleX),
|
||||
scale_y: K.ITEM('float', scaleY),
|
||||
rotate: K.ITEM('float', rotation),
|
||||
});
|
||||
}
|
||||
|
||||
return stickers
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { PlayerRanking } from "../playerranking"
|
||||
|
||||
export interface SaveProfileResponse
|
||||
{
|
||||
skill: {
|
||||
rank: KITEM<'s32'>,
|
||||
total_nr: KITEM<'s32'>
|
||||
}
|
||||
all_skill: {
|
||||
rank: KITEM<'s32'>,
|
||||
total_nr: KITEM<'s32'>
|
||||
}
|
||||
kac2018: {
|
||||
data: {
|
||||
term: KITEM<'s32'>,
|
||||
total_score: KITEM<'s32'>,
|
||||
score: KARRAY<'s32'>,
|
||||
music_type: KARRAY<'s32'>,
|
||||
play_count: KARRAY<'s32'>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getSaveProfileResponse(playerNo: number, ranking : PlayerRanking)
|
||||
{
|
||||
const result : SaveProfileResponse = K.ATTR({ no: `${playerNo}` }, {
|
||||
skill: { rank: K.ITEM('s32', ranking.skill), total_nr: K.ITEM('s32', ranking.totalPlayers) },
|
||||
all_skill: { rank: K.ITEM('s32', ranking.all_skill), total_nr: K.ITEM('s32', ranking.totalPlayers) },
|
||||
kac2018: {
|
||||
data: {
|
||||
term: K.ITEM('s32', 0),
|
||||
total_score: K.ITEM('s32', 0),
|
||||
score: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
music_type: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
play_count: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Profile } from "../profile";
|
||||
|
||||
export interface SecretMusicResponse {
|
||||
musicid: KITEM<'s32'>;
|
||||
seq: KITEM<'u16'>;
|
||||
kind: KITEM<'s32'>;
|
||||
}
|
||||
|
||||
export function getSecretMusicResponse(profile: Profile) : SecretMusicResponse[] {
|
||||
let response : SecretMusicResponse[] = []
|
||||
|
||||
if (!profile.secretmusic?.music ) {
|
||||
return response
|
||||
}
|
||||
|
||||
for (let music of profile.secretmusic.music) {
|
||||
response.push({
|
||||
musicid: K.ITEM('s32', music.musicid),
|
||||
seq: K.ITEM('u16', music.seq),
|
||||
kind: K.ITEM('s32', music.kind)
|
||||
})
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
export interface CommonMusicDataField {
|
||||
id: KITEM<"s32">;
|
||||
cont_gf: KITEM<"bool">;
|
||||
cont_dm: KITEM<"bool">;
|
||||
is_secret: KITEM<"bool">;
|
||||
is_hot: KITEM<"bool">;
|
||||
data_ver: KITEM<"s32">;
|
||||
diff: KARRAY<"u16">;
|
||||
}
|
||||
|
||||
export interface CommonMusicData {
|
||||
music: CommonMusicDataField[]
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export interface Extra {
|
||||
collection: 'extra';
|
||||
|
||||
game: 'gf' | 'dm';
|
||||
version: string;
|
||||
pluginVer: number
|
||||
id: number;
|
||||
|
||||
playstyle: number[];
|
||||
custom: number[];
|
||||
list_1: number[];
|
||||
list_2: number[];
|
||||
list_3: number[];
|
||||
recommend_musicid_list: number[];
|
||||
reward_status: number[];
|
||||
}
|
||||
|
||||
export function getDefaultExtra(game: 'gf' | 'dm', version: string, id: number) : Extra {
|
||||
const result : Extra = {
|
||||
collection: 'extra',
|
||||
pluginVer: PLUGIN_VER,
|
||||
|
||||
game,
|
||||
version,
|
||||
id,
|
||||
playstyle: Array(50).fill(0),
|
||||
custom: Array(50).fill(0),
|
||||
list_1: Array(100).fill(-1),
|
||||
list_2: Array(100).fill(-1),
|
||||
list_3: Array(100).fill(-1),
|
||||
recommend_musicid_list: Array(5).fill(-1),
|
||||
reward_status: Array(50).fill(0),
|
||||
}
|
||||
result.playstyle[1] = 1 // Note scroll speed (should default to 1.0x)
|
||||
result.playstyle[36] = 20 // Unknown
|
||||
result.playstyle[48] = 20 // Unknown
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
export interface FavoriteMusic {
|
||||
collection: 'favoritemusic',
|
||||
|
||||
pluginVer: number;
|
||||
list_1: number[];
|
||||
list_2: number[];
|
||||
list_3: number[];
|
||||
recommend_musicid_list: number[];
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export interface PlayerInfo {
|
||||
collection: 'playerinfo',
|
||||
|
||||
pluginVer: number;
|
||||
|
||||
id: number;
|
||||
version: string,
|
||||
name: string;
|
||||
title: string;
|
||||
|
||||
card?: {
|
||||
id: number;
|
||||
position: number[];
|
||||
scale: number[];
|
||||
rotation: number;
|
||||
}[];
|
||||
|
||||
// TODO: Add Board things.
|
||||
}
|
||||
|
||||
export function getDefaultPlayerInfo(version: string, id: number) : PlayerInfo {
|
||||
return {
|
||||
collection: 'playerinfo',
|
||||
pluginVer: PLUGIN_VER,
|
||||
id,
|
||||
version,
|
||||
name: 'ASPHYXIA-CORE USER',
|
||||
title: 'Please edit on WebUI',
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export interface PlayerRanking
|
||||
{
|
||||
refid: string;
|
||||
skill: number;
|
||||
all_skill: number;
|
||||
totalPlayers: number;
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
import { PLUGIN_VER } from "../const";
|
||||
import { SecretMusicEntry } from "./secretmusicentry";
|
||||
|
||||
export interface Profile {
|
||||
collection: 'profile';
|
||||
|
||||
game: 'gf' | 'dm';
|
||||
version: string;
|
||||
pluginVer: number
|
||||
id: number;
|
||||
|
||||
play: number;
|
||||
playtime: number;
|
||||
playterm: number;
|
||||
session_cnt: number;
|
||||
extra_stage: number;
|
||||
extra_play: number;
|
||||
extra_clear: number;
|
||||
encore_play: number;
|
||||
encore_clear: number;
|
||||
pencore_play: number;
|
||||
pencore_clear: number;
|
||||
max_clear_diff: number;
|
||||
max_full_diff: number;
|
||||
max_exce_diff: number;
|
||||
clear_num: number;
|
||||
full_num: number;
|
||||
exce_num: number;
|
||||
no_num: number;
|
||||
e_num: number;
|
||||
d_num: number;
|
||||
c_num: number;
|
||||
b_num: number;
|
||||
a_num: number;
|
||||
s_num: number;
|
||||
ss_num: number;
|
||||
last_category: number;
|
||||
last_musicid: number;
|
||||
last_seq: number;
|
||||
disp_level: number;
|
||||
progress: number;
|
||||
disp_state: number;
|
||||
skill: number;
|
||||
all_skill: number;
|
||||
extra_gauge: number;
|
||||
encore_gauge: number;
|
||||
encore_cnt: number;
|
||||
encore_success: number;
|
||||
unlock_point: number;
|
||||
max_skill: number;
|
||||
max_all_skill: number;
|
||||
clear_diff: number;
|
||||
full_diff: number;
|
||||
exce_diff: number;
|
||||
clear_music_num: number;
|
||||
full_music_num: number;
|
||||
exce_music_num: number;
|
||||
clear_seq_num: number;
|
||||
classic_all_skill: number;
|
||||
secretmusic: {
|
||||
music: SecretMusicEntry[];
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultProfile (game: 'gf' | 'dm', version: string, id: number): Profile {
|
||||
return {
|
||||
collection: 'profile',
|
||||
pluginVer: PLUGIN_VER,
|
||||
|
||||
game,
|
||||
version,
|
||||
id,
|
||||
|
||||
play: 0,
|
||||
playtime: 0,
|
||||
playterm: 0,
|
||||
session_cnt: 0,
|
||||
extra_stage: 0,
|
||||
extra_play: 0,
|
||||
extra_clear: 0,
|
||||
encore_play: 0,
|
||||
encore_clear: 0,
|
||||
pencore_play: 0,
|
||||
pencore_clear: 0,
|
||||
max_clear_diff: 0,
|
||||
max_full_diff: 0,
|
||||
max_exce_diff: 0,
|
||||
clear_num: 0,
|
||||
full_num: 0,
|
||||
exce_num: 0,
|
||||
no_num: 0,
|
||||
e_num: 0,
|
||||
d_num: 0,
|
||||
c_num: 0,
|
||||
b_num: 0,
|
||||
a_num: 0,
|
||||
s_num: 0,
|
||||
ss_num: 0,
|
||||
last_category: 0,
|
||||
last_musicid: -1,
|
||||
last_seq: 0,
|
||||
disp_level: 0,
|
||||
progress: 0,
|
||||
disp_state: 0,
|
||||
skill: 0,
|
||||
all_skill: 0,
|
||||
extra_gauge: 0,
|
||||
encore_gauge: 0,
|
||||
encore_cnt: 0,
|
||||
encore_success: 0,
|
||||
unlock_point: 0,
|
||||
max_skill: 0,
|
||||
max_all_skill: 0,
|
||||
clear_diff: 0,
|
||||
full_diff: 0,
|
||||
exce_diff: 0,
|
||||
clear_music_num: 0,
|
||||
full_music_num: 0,
|
||||
exce_music_num: 0,
|
||||
clear_seq_num: 0,
|
||||
classic_all_skill: 0,
|
||||
secretmusic: {
|
||||
music: []
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export interface Record {
|
||||
collection: 'record';
|
||||
|
||||
game: 'gf' | 'dm';
|
||||
version: string;
|
||||
pluginVer: number
|
||||
|
||||
diff_100_nr: number;
|
||||
diff_150_nr: number;
|
||||
diff_200_nr: number;
|
||||
diff_250_nr: number;
|
||||
diff_300_nr: number;
|
||||
diff_350_nr: number;
|
||||
diff_400_nr: number;
|
||||
diff_450_nr: number;
|
||||
diff_500_nr: number;
|
||||
diff_550_nr: number;
|
||||
diff_600_nr: number;
|
||||
diff_650_nr: number;
|
||||
diff_700_nr: number;
|
||||
diff_750_nr: number;
|
||||
diff_800_nr: number;
|
||||
diff_850_nr: number;
|
||||
diff_900_nr: number;
|
||||
diff_950_nr: number;
|
||||
diff_100_clear: number[];
|
||||
diff_150_clear: number[];
|
||||
diff_200_clear: number[];
|
||||
diff_250_clear: number[];
|
||||
diff_300_clear: number[];
|
||||
diff_350_clear: number[];
|
||||
diff_400_clear: number[];
|
||||
diff_450_clear: number[];
|
||||
diff_500_clear: number[];
|
||||
diff_550_clear: number[];
|
||||
diff_600_clear: number[];
|
||||
diff_650_clear: number[];
|
||||
diff_700_clear: number[];
|
||||
diff_750_clear: number[];
|
||||
diff_800_clear: number[];
|
||||
diff_850_clear: number[];
|
||||
diff_900_clear: number[];
|
||||
diff_950_clear: number[];
|
||||
}
|
||||
|
||||
export function getDefaultRecord(game: 'gf' | 'dm', version: string): Record {
|
||||
return {
|
||||
collection: 'record',
|
||||
pluginVer: PLUGIN_VER,
|
||||
game,
|
||||
version,
|
||||
|
||||
diff_100_nr: 0,
|
||||
diff_150_nr: 0,
|
||||
diff_200_nr: 0,
|
||||
diff_250_nr: 0,
|
||||
diff_300_nr: 0,
|
||||
diff_350_nr: 0,
|
||||
diff_400_nr: 0,
|
||||
diff_450_nr: 0,
|
||||
diff_500_nr: 0,
|
||||
diff_550_nr: 0,
|
||||
diff_600_nr: 0,
|
||||
diff_650_nr: 0,
|
||||
diff_700_nr: 0,
|
||||
diff_750_nr: 0,
|
||||
diff_800_nr: 0,
|
||||
diff_850_nr: 0,
|
||||
diff_900_nr: 0,
|
||||
diff_950_nr: 0,
|
||||
diff_100_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_150_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_200_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_250_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_300_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_350_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_400_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_450_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_500_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_550_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_600_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_650_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_700_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_750_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_800_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_850_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_900_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_950_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export interface Scores {
|
||||
collection: 'scores';
|
||||
|
||||
game: 'gf' | 'dm';
|
||||
version?: string;
|
||||
pluginVer: number
|
||||
|
||||
scores: {
|
||||
[mid: string]: {
|
||||
update: number[];
|
||||
diffs: {
|
||||
[seq: string]: {
|
||||
perc: number;
|
||||
rank: number;
|
||||
clear: boolean;
|
||||
fc: boolean;
|
||||
ex: boolean;
|
||||
meter: string;
|
||||
prog: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function getDefaultScores (game: 'gf' | 'dm', version: string): Scores {
|
||||
return {
|
||||
collection: 'scores',
|
||||
version,
|
||||
pluginVer: PLUGIN_VER,
|
||||
game,
|
||||
scores: {}
|
||||
}
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
export interface SecretMusicEntry {
|
||||
musicid: number;
|
||||
seq: number;
|
||||
kind: number;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
export const isGF = (info: EamuseInfo) => {
|
||||
return info.model.split(':')[2] == 'A';
|
||||
};
|
||||
|
||||
export const isDM = (info: EamuseInfo) => {
|
||||
return info.model.split(':')[2] == 'B';
|
||||
};
|
||||
|
||||
export const getVersion = (info: EamuseInfo) => {
|
||||
const moduleName: string = info.module;
|
||||
return moduleName.match(/([^_]*)_(.*)/)[1];
|
||||
};
|
||||
|
||||
export function isRequiredCoreVersion(major: number, minor: number) {
|
||||
// version value exposed since Core v1.19
|
||||
const core_major = typeof CORE_VERSION_MAJOR === "number" ? CORE_VERSION_MAJOR : 1
|
||||
const core_minor = typeof CORE_VERSION_MINOR === "number" ? CORE_VERSION_MINOR : 18
|
||||
return core_major > major || (core_major === major && core_minor >= minor)
|
||||
};
|
||||
|
||||
export function isAsphyxiaDebugMode() : boolean {
|
||||
const argv = process.argv
|
||||
return argv.includes("--dev") || argv.includes("--console")
|
||||
}
|
||||
|
||||
export function isSharedFavoriteMusicEnabled() : boolean{
|
||||
return Boolean(U.GetConfig("shared_favorite_songs"))
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { isAsphyxiaDebugMode } from ".";
|
||||
|
||||
export default class Logger {
|
||||
public category: string | null;
|
||||
|
||||
public constructor(category?: string) {
|
||||
this.category = (category == null) ? null : `[${category}]`
|
||||
}
|
||||
|
||||
public error(...args: any[]) {
|
||||
this.argsHandler(console.error, ...args)
|
||||
}
|
||||
|
||||
public debugError(...args: any[]) {
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
this.argsHandler(console.error, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
public warn(...args: any[]) {
|
||||
this.argsHandler(console.warn, ...args)
|
||||
}
|
||||
|
||||
public debugWarn(...args: any[]) {
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
this.argsHandler(console.warn, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
public info(...args: any[]) {
|
||||
this.argsHandler(console.info, ...args)
|
||||
}
|
||||
|
||||
public debugInfo(...args: any[]) {
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
this.argsHandler(console.info, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
public log(...args: any[]) {
|
||||
this.argsHandler(console.log, ...args)
|
||||
}
|
||||
|
||||
public debugLog(...args: any[]) {
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
this.argsHandler(console.log, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
private argsHandler(target: Function, ...args: any[]) {
|
||||
if (this.category == null) {
|
||||
target(...args)
|
||||
} else {
|
||||
target(this.category, ...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
//DATA//
|
||||
infos: DB.Find(null, { collection: 'playerinfo' })
|
||||
profiles: DB.Find(null, { collection: 'profile' })
|
||||
-
|
||||
|
||||
-
|
||||
function getFullGameName(shortName) {
|
||||
switch (shortName) {
|
||||
case "dm" :
|
||||
return "Drummania"
|
||||
case "gf":
|
||||
return "Guitar Freaks"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const versions = ["exchain", "nextage"]
|
||||
const games = ["gf", "dm"]
|
||||
|
||||
function generateLeaderboards(infos, profiles) {
|
||||
let result = []
|
||||
|
||||
for (const version of versions) {
|
||||
for (const game of games) {
|
||||
result.push(generateLeaderboard(infos, profiles, version, game))
|
||||
}
|
||||
}
|
||||
|
||||
// Hide versions and games with no entries
|
||||
result = result.filter((e) => e.entries.length > 0)
|
||||
return result
|
||||
}
|
||||
|
||||
function generateLeaderboard(infos, profiles, version, game) {
|
||||
let entries = []
|
||||
let idx = 1
|
||||
let currentProfiles = profiles.filter((e) => e.game === game && e.version === version)
|
||||
currentProfiles = currentProfiles.sort((a, b) => b.skill - a.skill)
|
||||
|
||||
for (const profile of currentProfiles) {
|
||||
const info = infos.find(i => i.__refid === profile.__refid)
|
||||
const name = info ? info.name : "Unknown"
|
||||
const scoreData = {
|
||||
rank: idx,
|
||||
name: name,
|
||||
skill: profile.skill / 100,
|
||||
all_skill: profile.all_skill / 100,
|
||||
clear_music_num : profile.clear_music_num,
|
||||
clear_diff: profile.clear_diff / 100
|
||||
}
|
||||
entries.push(scoreData)
|
||||
idx++
|
||||
}
|
||||
|
||||
let result = {
|
||||
version: version,
|
||||
game: game,
|
||||
entries: entries
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
-
|
||||
|
||||
each board in generateLeaderboards(infos, profiles)
|
||||
h3 #{getFullGameName(board.game)} #{board.version}
|
||||
table
|
||||
tr
|
||||
th Rank
|
||||
th Name
|
||||
th Skill
|
||||
th All Skill
|
||||
th Songs Cleared
|
||||
th Hardest Clear
|
||||
each e in board.entries
|
||||
tr
|
||||
td #{e.rank}
|
||||
td #{e.name}
|
||||
td #{e.skill}
|
||||
td #{e.all_skill}
|
||||
td #{e.clear_music_num}
|
||||
td #{e.clear_diff}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
//DATA//
|
||||
info: DB.Find(refid, { collection: 'playerinfo' })
|
||||
profile: DB.Find(refid, { collection: 'profile' })
|
||||
-
|
||||
|
||||
-
|
||||
function getFullGameName(shortName) {
|
||||
switch (shortName) {
|
||||
case "dm" :
|
||||
return "Drummania"
|
||||
case "gf":
|
||||
return "Guitar Freaks"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
-
|
||||
|
||||
div
|
||||
each i in info
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-account-edit
|
||||
| User Detail (#{i.version})
|
||||
.card-content
|
||||
form(method="post" action="/emit/updatePlayerInfo")
|
||||
.field
|
||||
label.label ID
|
||||
.control
|
||||
input.input(type="text" name="refid", value=refid readonly)
|
||||
.field
|
||||
label.label Version
|
||||
.control
|
||||
input.input(type="text" name="version", value=i.version readonly)
|
||||
.field
|
||||
label.label Name
|
||||
.control
|
||||
input.input(type="text" name="name", value=i.name)
|
||||
.field
|
||||
label.label Title
|
||||
.control
|
||||
input.input(type="text" name="title", value=i.title)
|
||||
.field
|
||||
button.button.is-primary(type="submit")
|
||||
span.icon
|
||||
i.mdi.mdi-check
|
||||
span Submit
|
||||
|
||||
div
|
||||
each pr in profile
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-account-details
|
||||
| Profile Detail (#{getFullGameName(pr.game)} #{pr.version})
|
||||
.card-content
|
||||
form(method="post")
|
||||
.field
|
||||
label.label Skill
|
||||
.control
|
||||
input.input(type="text" name="skill", value=(pr.skill/100) readonly)
|
||||
.field
|
||||
label.label Skill (All Songs)
|
||||
.control
|
||||
input.input(type="text" name="all_skill", value=(pr.all_skill/100) readonly)
|
||||
.field
|
||||
label.label Songs Cleared
|
||||
.control
|
||||
input.input(type="text" name="clear_num", value=pr.clear_num readonly)
|
||||
.field
|
||||
label.label Full Combos
|
||||
.control
|
||||
input.input(type="text" name="full_num", value=pr.full_num readonly)
|
||||
.field
|
||||
label.label Excellent Full Combos
|
||||
.control
|
||||
input.input(type="text" name="exce_num", value=pr.exce_num readonly)
|
||||
.field
|
||||
label.label Highest Difficulty Cleared
|
||||
.control
|
||||
input.input(type="text" name="max_clear_diff", value=(pr.max_clear_diff/100) readonly)
|
||||
.field
|
||||
label.label Highest Difficulty Full Combo
|
||||
.control
|
||||
input.input(type="text" name="max_full_diff", value=(pr.max_full_diff/100) readonly)
|
||||
.field
|
||||
label.label Highest Difficulty Excellent Full Combo
|
||||
.control
|
||||
input.input(type="text" name="max_exce_diff", value=(pr.max_exce_diff/100) readonly)
|
||||
.field
|
||||
label.label Sessions
|
||||
.control
|
||||
input.input(type="text" name="session_cnt", value=pr.session_cnt readonly)
|
||||
@@ -0,0 +1,773 @@
|
||||
import { Profile } from "../models/profile";
|
||||
import { ProfileWorld, ScoreWorld, EventWorld, GhostWorld, RivalWorld, HiScoreWorld } from "../models/ddrworld";
|
||||
import { SONGS_WORLD, SONGS_OVERRIDE_WORLD, EVENTS_WORLD } from "../data/world";
|
||||
|
||||
// needs to be faster
|
||||
function getLastGhostId(ghost: any) {
|
||||
return ghost.reduce(function(a, b) {
|
||||
let aId = (a && !a.ghostId) ? 0 : a.ghostId
|
||||
let bId = (!b.ghostId) ? 0 : b.ghostId
|
||||
return (a && aId > bId) ? aId : bId
|
||||
})
|
||||
}
|
||||
|
||||
async function saveScores(refid: string, hiScoreInfo: any, songId: number, style: number, difficulty: number, rank: number, clearKind: number, score: number, exScore: number, maxCombo: number, flareForce: number, ghostSize: number, ghost: string) {
|
||||
let stepScore = await DB.FindOne<ScoreWorld>(refid, {collection: "score3", songId: songId, style: style, difficulty: difficulty})
|
||||
let ghostData = await DB.Find<GhostWorld>(null, {collection: "ghost3"})
|
||||
let lastGhostId = getLastGhostId(ghostData)
|
||||
let hiScoreCheck = score
|
||||
let ghostId = 0
|
||||
if(lastGhostId === 0) {
|
||||
ghostId = 1
|
||||
} else ghostId = lastGhostId += 1
|
||||
/*
|
||||
Ghost data gets created when there is no existing data and it gets updated when getting a higher or the same score
|
||||
Side effect of this is when you have set a high score prior to this update, the ghost data wouldn't be updated unless you break or match that high score.
|
||||
Unfortunately I could not find a way to match the old scores to their respective ghost data, since the old ghost data only stored the song id and difficulty, and not the style (single/double.) Stupid me.
|
||||
Still needs more work.
|
||||
*/
|
||||
if(stepScore) {
|
||||
ghostId = (stepScore.ghostId) ? stepScore.ghostId : ghostId
|
||||
let stepGhost = await DB.FindOne<GhostWorld>(null, {collection: "ghost3", ghostId: ghostId })
|
||||
|
||||
if(stepGhost) {
|
||||
if(score >= stepScore.score) console.log("overwriting ghostdata")
|
||||
ghostSize = (score >= stepScore.score) ? ghostSize : stepGhost.ghostSize
|
||||
ghost = (score >= stepScore.score) ? ghost : stepGhost.ghost
|
||||
}
|
||||
|
||||
rank = (rank < stepScore.rank) ? rank : stepScore.rank;
|
||||
clearKind = (clearKind != 6 && clearKind > stepScore.clearKind) ? clearKind : stepScore.clearKind;
|
||||
score = (score > stepScore.score) ? score : stepScore.score;
|
||||
exScore = (exScore > stepScore.exScore) ? exScore : stepScore.exScore;
|
||||
maxCombo = (maxCombo > stepScore.maxCombo) ? maxCombo : stepScore.maxCombo;
|
||||
flareForce = (flareForce > stepScore.flareForce) ? flareForce : stepScore.flareForce;
|
||||
|
||||
}
|
||||
|
||||
await DB.Upsert<ScoreWorld>(refid, {
|
||||
collection: "score3",
|
||||
songId,
|
||||
style,
|
||||
difficulty
|
||||
}, {
|
||||
$set: {
|
||||
ghostId,
|
||||
rank,
|
||||
clearKind,
|
||||
score,
|
||||
exScore,
|
||||
maxCombo,
|
||||
flareForce
|
||||
}
|
||||
});
|
||||
|
||||
console.log("saving ghostid: " + ghostId)
|
||||
await DB.Upsert<GhostWorld>(refid, {
|
||||
collection: "ghost3",
|
||||
ghostId,
|
||||
}, {
|
||||
$set: {
|
||||
ghostSize,
|
||||
ghost
|
||||
}
|
||||
});
|
||||
|
||||
let slot = 0
|
||||
let country = hiScoreInfo.country
|
||||
let region = hiScoreInfo.region
|
||||
let customerCode = hiScoreInfo.customerCode
|
||||
let companyCode = hiScoreInfo.companyCode
|
||||
let locationId = hiScoreInfo.locationId
|
||||
let pcbid = hiScoreInfo.pcbid
|
||||
let dancerName = hiScoreInfo.dancerName
|
||||
|
||||
let worldBreak = false
|
||||
let areaBreak = false
|
||||
let machineBreak = false
|
||||
|
||||
// Hiscore stuff
|
||||
let hiScoreWorld1 = await DB.FindOne<HiScoreWorld>(null, {collection: 'hiscore3', slot: 1, songId: songId, style: style, difficulty: difficulty})
|
||||
console.log('--------------------------------------------------------------')
|
||||
ghostData = await DB.Find<GhostWorld>(null, {collection: "ghost3"})
|
||||
lastGhostId = getLastGhostId(ghostData)
|
||||
ghostId = 0
|
||||
if(lastGhostId === 0) ghostId = 1
|
||||
else ghostId = lastGhostId += 1
|
||||
|
||||
if(hiScoreWorld1) {
|
||||
console.log('[world1] ' + hiScoreCheck + ' >= ' + hiScoreWorld1.score)
|
||||
if(hiScoreCheck >= hiScoreWorld1.score) {
|
||||
console.log('[world1] high score beat')
|
||||
worldBreak = true
|
||||
ghostId = (hiScoreWorld1.ghostId) ? hiScoreWorld1.ghostId : ghostId
|
||||
console.log('[world1] ghostid exists, saving to id ' + ghostId)
|
||||
await DB.Upsert<GhostWorld>(refid, {collection: "ghost3", ghostId }, {$set: { ghostSize, ghost }});
|
||||
}
|
||||
} else {
|
||||
worldBreak = true
|
||||
console.log('[world1] entry does not exist. creating')
|
||||
await DB.Upsert<GhostWorld>(refid, {collection: "ghost3", ghostId }, {$set: { ghostSize, ghost }});
|
||||
}
|
||||
slot = 1
|
||||
if(worldBreak) {
|
||||
console.log("[world1] saving hiscore - ghost id " + ghostId)
|
||||
await DB.Upsert<HiScoreWorld>(refid, {collection: "hiscore3", slot, songId, style, difficulty}, { $set: { country, region, customerCode, companyCode, locationId, pcbid, dancerName, score: hiScoreCheck, ghostId }});
|
||||
}
|
||||
|
||||
let hiScoreArea1 = await DB.FindOne<HiScoreWorld>(null, {collection: 'hiscore3', slot: 2, locationId: locationId, songId: songId, style: style, difficulty: difficulty})
|
||||
console.log('--------------------------------------------------------------')
|
||||
ghostData = await DB.Find<GhostWorld>(null, {collection: "ghost3"})
|
||||
lastGhostId = getLastGhostId(ghostData)
|
||||
ghostId = 0
|
||||
if(lastGhostId === 0) ghostId = 1
|
||||
else ghostId = lastGhostId += 1
|
||||
|
||||
if(hiScoreArea1) {
|
||||
console.log('[area1] ' + hiScoreCheck + ' >= ' + hiScoreArea1.score)
|
||||
if(hiScoreCheck >= hiScoreArea1.score) {
|
||||
console.log('[area1] high score beat')
|
||||
areaBreak = true
|
||||
ghostId = (hiScoreArea1.ghostId) ? hiScoreArea1.ghostId : ghostId
|
||||
console.log('[area1] ghostid exists, saving to id ' + ghostId)
|
||||
await DB.Upsert<GhostWorld>(refid, {collection: "ghost3", ghostId }, {$set: { ghostSize, ghost }});
|
||||
}
|
||||
} else {
|
||||
areaBreak = true
|
||||
console.log('[area1] entry does not exist. creating')
|
||||
await DB.Upsert<GhostWorld>(refid, {collection: "ghost3", ghostId }, {$set: { ghostSize, ghost }});
|
||||
}
|
||||
slot = 2
|
||||
if(areaBreak) {
|
||||
console.log("[area1] saving hiscore - ghost id " + ghostId)
|
||||
await DB.Upsert<HiScoreWorld>(refid, {collection: "hiscore3", slot, locationId, songId, style, difficulty}, { $set: { country, region, customerCode, companyCode, pcbid, dancerName, score: hiScoreCheck, ghostId }});
|
||||
}
|
||||
|
||||
|
||||
let hiScoreMachine1 = await DB.FindOne<HiScoreWorld>(null, {collection: 'hiscore3', slot: 3, locationId: locationId, pcbid: pcbid, songId: songId, style: style, difficulty: difficulty})
|
||||
console.log('--------------------------------------------------------------')
|
||||
ghostData = await DB.Find<GhostWorld>(null, {collection: "ghost3"})
|
||||
lastGhostId = getLastGhostId(ghostData)
|
||||
ghostId = 0
|
||||
if(lastGhostId === 0) ghostId = 1
|
||||
else ghostId = lastGhostId += 1
|
||||
|
||||
if(hiScoreMachine1) {
|
||||
console.log('[machine1] ' + hiScoreCheck + ' >= ' + hiScoreMachine1.score)
|
||||
if(hiScoreCheck >= hiScoreMachine1.score) {
|
||||
console.log('[machine1] high score beat')
|
||||
machineBreak = true
|
||||
ghostId = (hiScoreMachine1.ghostId) ? hiScoreMachine1.ghostId : ghostId
|
||||
console.log('[machine1] ghostid exists, saving to id ' + ghostId)
|
||||
await DB.Upsert<GhostWorld>(refid, {collection: "ghost3", ghostId }, {$set: { ghostSize, ghost }});
|
||||
}
|
||||
} else {
|
||||
machineBreak = true
|
||||
console.log('[machine1] entry does not exist. creating')
|
||||
await DB.Upsert<GhostWorld>(refid, {collection: "ghost3", ghostId }, {$set: { ghostSize, ghost }});
|
||||
}
|
||||
slot = 3
|
||||
if(machineBreak) {
|
||||
console.log("[machine1] saving hiscore - ghost id " + ghostId)
|
||||
await DB.Upsert<HiScoreWorld>(refid, {collection: "hiscore3", slot, locationId, pcbid, songId, style, difficulty}, { $set: { country, region, customerCode, companyCode, dancerName, score: hiScoreCheck, ghostId }});
|
||||
}
|
||||
}
|
||||
|
||||
export const playerdatanew: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str("data.refid");
|
||||
let ddrCode = _.random(1, 99999999)
|
||||
await DB.Upsert<ProfileWorld>(refid, { collection: "profile3" }, {
|
||||
collection: "profile3",
|
||||
ddrCode: ddrCode
|
||||
})
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
refid: K.ITEM("str", refid),
|
||||
ddrcode: K.ITEM("s32", ddrCode),
|
||||
istakeover: K.ITEM('bool', false)
|
||||
})
|
||||
}
|
||||
|
||||
export const playerdatasave: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str("data.refid");
|
||||
if(!refid.startsWith("X000")) {
|
||||
if($(data).number("data.savekind") === 1) {
|
||||
await DB.Upsert<ProfileWorld>(refid, { collection: "profile3" }, {
|
||||
collection: "profile3",
|
||||
|
||||
ddrCode: $(data).number('data.common.ddrcode'),
|
||||
dancerName: $(data).str('data.common.dancername'),
|
||||
area: $(data).number('data.common.area'),
|
||||
extraStar: $(data).number('data.common.extrastar'),
|
||||
playCount: 0,
|
||||
weight: 0,
|
||||
todayCal: $(data).number('data.common.today_cal'),
|
||||
isDispWeight: true,
|
||||
prePlayableNum: 0,
|
||||
|
||||
opHispeed: $(data).number('data.option.hispeed'),
|
||||
opGauge: $(data).number('data.option.gauge'),
|
||||
opFastSlow: $(data).number('data.option.fastslow'),
|
||||
opGuideline: $(data).number('data.option.guideline'),
|
||||
opStepZone: $(data).number('data.option.stepzone'),
|
||||
opTimingDisp: $(data).number('data.option.timing_disp'),
|
||||
opVisibility: $(data).number('data.option.visibility'),
|
||||
opVisibleTime: $(data).number('data.option.visible_time'),
|
||||
opLane: $(data).number('data.option.lane'),
|
||||
opLaneHiddenPos: $(data).number('data.option.lane_hiddenpos'),
|
||||
opLaneSuddenPos: $(data).number('data.option.lane_suddenpos'),
|
||||
opLaneHidSudPos: $(data).number('data.option.lane_hidsudpos'),
|
||||
opLaneFilter: $(data).number('data.option.lane_filter'),
|
||||
opScrollDirection: $(data).number('data.option.scroll_direction'),
|
||||
opScrollMoving: $(data).number('data.option.scroll_moving'),
|
||||
opArrowPriority: $(data).number('data.option.arrow_priority'),
|
||||
opArrowPlacement: $(data).number('data.option.arrow_placement'),
|
||||
opArrowColor: $(data).number('data.option.arrow_color'),
|
||||
opArrowDesign: $(data).number('data.option.arrow_design'),
|
||||
opCutTiming: $(data).number('data.option.cut_timing'),
|
||||
opCutFreeze: $(data).number('data.option.cut_freeze'),
|
||||
opCutJump: $(data).number('data.option.cut_jump'),
|
||||
|
||||
lpMode: $(data).number('data.lastplay.mode'),
|
||||
lpFolder: $(data).number('data.lastplay.folder'),
|
||||
lpMcode: $(data).number('data.lastplay.mcode'),
|
||||
lpStyle: $(data).number('data.lastplay.style'),
|
||||
lpDifficulty: $(data).number('data.lastplay.difficulty'),
|
||||
lpWindowMain: $(data).number('data.lastplay.window_main'),
|
||||
lpWindowSub: $(data).number('data.lastplay.window_sub'),
|
||||
lpTarget: $(data).number('data.lastplay.target'),
|
||||
lpTabMain: $(data).number('data.lastplay.tab_main'),
|
||||
lpTabSub: $(data).number('data.lastplay.tab_sub'),
|
||||
|
||||
fsTitle: $(data).number('data.filtersort.title'),
|
||||
fsVersion: $(data).number('data.filtersort.version'),
|
||||
fsGenre: $(data).number('data.filtersort.genre'),
|
||||
fsBpm: $(data).number('data.filtersort.bpm'),
|
||||
fsEvent: $(data).number('data.filtersort.event'),
|
||||
fsLevel: $(data).number('data.filtersort.level'),
|
||||
fsFlareRank: $(data).number('data.filtersort.flare_rank'),
|
||||
fsClearRank: $(data).number('data.filtersort.clear_rank'),
|
||||
fsFlareSkillTarget: $(data).number('data.filtersort.flare_skill_target'),
|
||||
fsRivalFlareSkill: $(data).number('data.filtersort.rival_flare_skill'),
|
||||
fsRivalScoreRank: $(data).number('data.filtersort.rival_score_rank'),
|
||||
fsSortType: $(data).number('data.filtersort.sort_type'),
|
||||
fsOrderType: $(data).number('data.filtersort.order_type'),
|
||||
|
||||
cgTipsBasic: $(data).number('data.checkguide.tips_basic'),
|
||||
cgTipsOption: $(data).number('data.checkguide.tips_option'),
|
||||
cgTipsEvent: $(data).number('data.checkguide.tips_event'),
|
||||
cgTipsGimmick: $(data).number('data.checkguide.tips_gimmick'),
|
||||
cgTipsAdvance: $(data).number('data.checkguide.tips_advance'),
|
||||
cgGuideScene: $(data).number('data.checkguide.guide_scene')
|
||||
})
|
||||
}
|
||||
else if($(data).number("data.savekind") === 2) {
|
||||
let hiScoreInfo = {
|
||||
country: $(data).str("data.country"),
|
||||
region: $(data).str("data.region"),
|
||||
customerCode: $(data).str("data.customercode"),
|
||||
companyCode: $(data).str("data.companycode"),
|
||||
locationId: $(data).str("data.locationid"),
|
||||
pcbid: $(data).str("data.pcbid"),
|
||||
dancerName: $(data).str("data.common.dancername")
|
||||
}
|
||||
|
||||
let songId = $(data).number("data.result.mcode");
|
||||
let style = $(data).number("data.result.style");
|
||||
let difficulty = $(data).number("data.result.difficulty");
|
||||
let rank = $(data).number("data.result.rank");
|
||||
let clearKind = $(data).number("data.result.clearkind");
|
||||
let score = $(data).number("data.result.score");
|
||||
let exScore = $(data).number("data.result.exscore");
|
||||
let maxCombo = $(data).number("data.result.maxcombo");
|
||||
let flareForce = $(data).number("data.result.flare_force");
|
||||
let ghostSize = $(data).number("data.result.ghostsize");
|
||||
let ghost = $(data).str("data.result.ghost");
|
||||
await saveScores(refid, hiScoreInfo, songId, style, difficulty, rank, clearKind, score, exScore, maxCombo, flareForce, ghostSize, ghost)
|
||||
}
|
||||
else if($(data).number("data.savekind") === 3) {
|
||||
let profile = await DB.FindOne<ProfileWorld>(refid, {collection: "profile3"})
|
||||
await DB.Upsert<ProfileWorld>(refid, { collection: "profile3" }, {
|
||||
collection: "profile3",
|
||||
|
||||
ddrCode: $(data).number('data.common.ddrcode'),
|
||||
dancerName: $(data).str('data.common.dancername'),
|
||||
area: $(data).number('data.common.area'),
|
||||
extraStar: $(data).number('data.common.extrastar'),
|
||||
playCount: profile.playCount += 1,
|
||||
weight: 0,
|
||||
todayCal: $(data).number('data.common.today_cal'),
|
||||
isDispWeight: true,
|
||||
prePlayableNum: 0,
|
||||
|
||||
opHispeed: $(data).number('data.option.hispeed'),
|
||||
opGauge: $(data).number('data.option.gauge'),
|
||||
opFastSlow: $(data).number('data.option.fastslow'),
|
||||
opGuideline: $(data).number('data.option.guideline'),
|
||||
opStepZone: $(data).number('data.option.stepzone'),
|
||||
opTimingDisp: $(data).number('data.option.timing_disp'),
|
||||
opVisibility: $(data).number('data.option.visibility'),
|
||||
opVisibleTime: $(data).number('data.option.visible_time'),
|
||||
opLane: $(data).number('data.option.lane'),
|
||||
opLaneHiddenPos: $(data).number('data.option.lane_hiddenpos'),
|
||||
opLaneSuddenPos: $(data).number('data.option.lane_suddenpos'),
|
||||
opLaneHidSudPos: $(data).number('data.option.lane_hidsudpos'),
|
||||
opLaneFilter: $(data).number('data.option.lane_filter'),
|
||||
opScrollDirection: $(data).number('data.option.scroll_direction'),
|
||||
opScrollMoving: $(data).number('data.option.scroll_moving'),
|
||||
opArrowPriority: $(data).number('data.option.arrow_priority'),
|
||||
opArrowPlacement: $(data).number('data.option.arrow_placement'),
|
||||
opArrowColor: $(data).number('data.option.arrow_color'),
|
||||
opArrowDesign: $(data).number('data.option.arrow_design'),
|
||||
opCutTiming: $(data).number('data.option.cut_timing'),
|
||||
opCutFreeze: $(data).number('data.option.cut_freeze'),
|
||||
opCutJump: $(data).number('data.option.cut_jump'),
|
||||
|
||||
lpMode: $(data).number('data.lastplay.mode'),
|
||||
lpFolder: $(data).number('data.lastplay.folder'),
|
||||
lpMcode: $(data).number('data.lastplay.mcode'),
|
||||
lpStyle: $(data).number('data.lastplay.style'),
|
||||
lpDifficulty: $(data).number('data.lastplay.difficulty'),
|
||||
lpWindowMain: $(data).number('data.lastplay.window_main'),
|
||||
lpWindowSub: $(data).number('data.lastplay.window_sub'),
|
||||
lpTarget: $(data).number('data.lastplay.target'),
|
||||
lpTabMain: $(data).number('data.lastplay.tab_main'),
|
||||
lpTabSub: $(data).number('data.lastplay.tab_sub'),
|
||||
|
||||
fsTitle: $(data).number('data.filtersort.title'),
|
||||
fsVersion: $(data).number('data.filtersort.version'),
|
||||
fsGenre: $(data).number('data.filtersort.genre'),
|
||||
fsBpm: $(data).number('data.filtersort.bpm'),
|
||||
fsEvent: $(data).number('data.filtersort.event'),
|
||||
fsLevel: $(data).number('data.filtersort.level'),
|
||||
fsFlareRank: $(data).number('data.filtersort.flare_rank'),
|
||||
fsClearRank: $(data).number('data.filtersort.clear_rank'),
|
||||
fsFlareSkillTarget: $(data).number('data.filtersort.flare_skill_target'),
|
||||
fsRivalFlareSkill: $(data).number('data.filtersort.rival_flare_skill'),
|
||||
fsRivalScoreRank: $(data).number('data.filtersort.rival_score_rank'),
|
||||
fsSortType: $(data).number('data.filtersort.sort_type'),
|
||||
fsOrderType: $(data).number('data.filtersort.order_type'),
|
||||
|
||||
cgTipsBasic: $(data).number('data.checkguide.tips_basic'),
|
||||
cgTipsOption: $(data).number('data.checkguide.tips_option'),
|
||||
cgTipsEvent: $(data).number('data.checkguide.tips_event'),
|
||||
cgTipsGimmick: $(data).number('data.checkguide.tips_gimmick'),
|
||||
cgTipsAdvance: $(data).number('data.checkguide.tips_advance'),
|
||||
cgGuideScene: $(data).number('data.checkguide.guide_scene')
|
||||
})
|
||||
|
||||
let eventData = $(data).elements('data.event')
|
||||
if(eventData) {
|
||||
for(const e of eventData) {
|
||||
let eid = e.number('eventid')
|
||||
let eno = e.number('eventno')
|
||||
let etype = e.number('eventtype')
|
||||
let ctime = e.number('comptime')
|
||||
let sdata = e.number('savedata')
|
||||
|
||||
await DB.Upsert<EventWorld>(refid, { collection: "event3", eventId: eid, eventNo: eno, eventType: etype }, {
|
||||
collection: "event3",
|
||||
eventId: eid,
|
||||
eventNo: eno,
|
||||
eventType: etype,
|
||||
compTime: ctime,
|
||||
saveData: sdata
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0)
|
||||
})
|
||||
}
|
||||
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 1)
|
||||
})
|
||||
};
|
||||
|
||||
export const playerdataload: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str("data.refid");
|
||||
const profile = await DB.FindOne<ProfileWorld>(refid, { collection: "profile3" });
|
||||
|
||||
if (!profile || !profile.dancerName || refid.startsWith("X000")) {
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
refid: K.ITEM("str", refid),
|
||||
gamesession: K.ITEM('s64', BigInt(1)),
|
||||
servertime: K.ITEM("u64", BigInt(getDate())),
|
||||
is_locked: K.ITEM('bool', false),
|
||||
common: {
|
||||
ddrcode: K.ITEM("s32", profile ? profile.ddrCode : 0),
|
||||
dancername: K.ITEM("str", ''),
|
||||
is_new: K.ITEM('bool', (!profile || refid.startsWith("X000")) ? true : false),
|
||||
is_registering: K.ITEM('bool', (profile && !profile.dancerName) ? true : false),
|
||||
is_takeover: K.ITEM('bool', false),
|
||||
area: K.ITEM("s32", 0),
|
||||
extrastar: K.ITEM("s32", 0),
|
||||
playcount: K.ITEM("s32", 0),
|
||||
weight: K.ITEM("s32", 0),
|
||||
today_cal: K.ITEM("u64", BigInt(0)),
|
||||
is_disp_weight: K.ITEM("bool", false),
|
||||
pre_playable_num: K.ITEM("s32", 0)
|
||||
},
|
||||
option: {
|
||||
hispeed: K.ITEM("s32", 0),
|
||||
gauge: K.ITEM("s32", 0),
|
||||
fastslow: K.ITEM("s32", 0),
|
||||
guideline: K.ITEM("s32", 0),
|
||||
stepzone: K.ITEM("s32", 0),
|
||||
timing_disp: K.ITEM("s32", 0),
|
||||
visibility: K.ITEM("s32", 0),
|
||||
visible_time: K.ITEM("s32", 0),
|
||||
lane: K.ITEM("s32", 0),
|
||||
lane_hiddenpos: K.ITEM("s32", 0),
|
||||
lane_suddenpos: K.ITEM("s32", 0),
|
||||
lane_hidsudpos: K.ITEM("s32", 0),
|
||||
lane_filter: K.ITEM("s32", 0),
|
||||
scroll_direction: K.ITEM("s32", 0),
|
||||
scroll_moving: K.ITEM("s32", 0),
|
||||
arrow_priority: K.ITEM("s32", 0),
|
||||
arrow_placement: K.ITEM("s32", 0),
|
||||
arrow_color: K.ITEM("s32", 0),
|
||||
arrow_design: K.ITEM("s32", 0),
|
||||
cut_timing: K.ITEM("s32", 0),
|
||||
cut_freeze: K.ITEM("s32", 0),
|
||||
cut_jump: K.ITEM("s32", 0)
|
||||
},
|
||||
lastplay: {
|
||||
mode: K.ITEM("s32", 0),
|
||||
folder: K.ITEM("s32", 0),
|
||||
mcode: K.ITEM("s32", 0),
|
||||
style: K.ITEM("s32", 0),
|
||||
difficulty: K.ITEM("s32", 0),
|
||||
window_main: K.ITEM("s32", 0),
|
||||
window_sub: K.ITEM("s32", 0),
|
||||
target: K.ITEM("s32", 0),
|
||||
tab_main: K.ITEM("s32", 0),
|
||||
tab_sub: K.ITEM("s32", 0)
|
||||
},
|
||||
filtersort: {
|
||||
title: K.ITEM("u64", BigInt(0)),
|
||||
version: K.ITEM("u64", BigInt(0)),
|
||||
genre: K.ITEM("u64", BigInt(0)),
|
||||
bpm: K.ITEM("u64", BigInt(0)),
|
||||
event: K.ITEM("u64", BigInt(0)),
|
||||
level: K.ITEM("u64", BigInt(0)),
|
||||
flare_rank: K.ITEM("u64", BigInt(0)),
|
||||
clear_rank: K.ITEM("u64", BigInt(0)),
|
||||
flare_skill_target: K.ITEM("u64", BigInt(0)),
|
||||
rival_flare_skill: K.ITEM("u64", BigInt(0)),
|
||||
rival_score_rank: K.ITEM("u64", BigInt(0)),
|
||||
sort_type: K.ITEM("u64", BigInt(0)),
|
||||
order_type: K.ITEM("s32", 0),
|
||||
},
|
||||
checkguide: {
|
||||
tips_basic: K.ITEM("u64", BigInt(0)),
|
||||
tips_option: K.ITEM("u64", BigInt(0)),
|
||||
tips_event: K.ITEM("u64", BigInt(0)),
|
||||
tips_gimmick: K.ITEM("u64", BigInt(0)),
|
||||
tips_advance: K.ITEM("u64", BigInt(0)),
|
||||
guide_scene: K.ITEM("u64", BigInt(0)),
|
||||
},
|
||||
rival: [
|
||||
{
|
||||
slot: K.ITEM("s32", 0),
|
||||
rivalcode: K.ITEM("s32", 0)
|
||||
}
|
||||
],
|
||||
score: [
|
||||
{
|
||||
mcode: K.ITEM("s32", 0),
|
||||
score_single: {
|
||||
score_str: K.ITEM("str", "")
|
||||
},
|
||||
score_double: {
|
||||
score_str: K.ITEM("str", "")
|
||||
}
|
||||
}
|
||||
],
|
||||
event: {
|
||||
event_str: K.ITEM("str", "")
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
// await addGhostId(refid)
|
||||
const scores = await DB.Find<ScoreWorld>(refid, { collection: "score3" });
|
||||
let scoreFin = []
|
||||
if(scores) {
|
||||
for(const scoreData of scores) {
|
||||
let mcodeIndex = scoreFin.findIndex(x => $(x).number('mcode') === scoreData.songId)
|
||||
if(mcodeIndex < 0) {
|
||||
let scr = {}
|
||||
scr['mcode'] = K.ITEM('s32', scoreData.songId)
|
||||
scr['score_single'] = []
|
||||
scr['score_double'] = []
|
||||
/*
|
||||
difficulty,idk,grade,clearkind,score,ghostid,flaredisp,flarepoints,idk
|
||||
needs more work, 9 vals
|
||||
*/
|
||||
scr[(scoreData.style === 0) ? 'score_single' : 'score_double'] = [
|
||||
{
|
||||
score_str: K.ITEM('str', scoreData.difficulty + ',1,' + scoreData.rank + ',' + scoreData.clearKind + ',' + scoreData.score + ',' + scoreData.ghostId + ',' + scoreData.flareForce + ',' + scoreData.flareForce)
|
||||
}
|
||||
]
|
||||
scoreFin.push(scr)
|
||||
|
||||
} else {
|
||||
scoreFin[mcodeIndex][(scoreData.style === 0) ? 'score_single' : 'score_double'].push({
|
||||
score_str: K.ITEM('str', scoreData.difficulty + ',1,' + scoreData.rank + ',' + scoreData.clearKind + ',' + scoreData.score + ',' + scoreData.ghostId + ',' + scoreData.flareForce + ',' + scoreData.flareForce)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let eventFin = []
|
||||
let eventData = await DB.Find<EventWorld>(refid, { collection: "event3"});
|
||||
for(const event of EVENTS_WORLD) {
|
||||
let eData = eventData.find(e => e.eventId === event.id)
|
||||
let condmet = true
|
||||
if(event.dep) {
|
||||
event.dep.forEach(dep => {
|
||||
let eData = eventData.find(e => e.eventId === dep)
|
||||
if(eData === undefined) condmet = false
|
||||
else if(eData.compTime === 0) condmet = false
|
||||
})
|
||||
}
|
||||
// id,type,no,condition,reward,comptime,savedata
|
||||
if(condmet) {
|
||||
if(event.type === 17) {
|
||||
if (eData) eData.compTime = eData.compTime ? 0 : 1
|
||||
event.comp = event.comp ? 0 : 1
|
||||
}
|
||||
eventFin.push({
|
||||
event_str: K.ITEM('str', event.id + ',' + event.type + ',' + event.no + ',' + event.cond + ',' + event.rwrd + ',' + (eData ? BigInt(eData.compTime) : (event.comp !== undefined ? event.comp : '0')) + ',' + ((eData) ? eData.saveData : ((event.save !== undefined) ? event.save : '0')))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// test
|
||||
if(IO.Exists('data/test.json')) {
|
||||
let bufTest = await IO.ReadFile('data/test.json')
|
||||
let eventTest = JSON.parse(bufTest.toString())
|
||||
for(const ex in eventTest['eventtest']) {
|
||||
eventFin.push({ event_str: K.ITEM('str', eventTest['eventtest'][ex]) })
|
||||
}
|
||||
}
|
||||
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
refid: K.ITEM("str", refid),
|
||||
gamesession: K.ITEM('s64', BigInt(1)),
|
||||
servertime: K.ITEM("u64", BigInt(getDate())),
|
||||
is_locked: K.ITEM('bool', false),
|
||||
common: {
|
||||
ddrcode: K.ITEM("s32", profile.ddrCode),
|
||||
dancername: K.ITEM("str", profile.dancerName),
|
||||
is_new: K.ITEM('bool', false),
|
||||
is_registering: K.ITEM('bool', false),
|
||||
is_takeover: K.ITEM('bool', false),
|
||||
area: K.ITEM("s32", profile.area),
|
||||
extrastar: K.ITEM("s32", profile.extraStar),
|
||||
playcount: K.ITEM("s32", profile.playCount),
|
||||
weight: K.ITEM("s32", profile.weight),
|
||||
today_cal: K.ITEM("u64", BigInt(profile.todayCal)),
|
||||
is_disp_weight: K.ITEM("bool", profile.isDispWeight),
|
||||
pre_playable_num: K.ITEM("s32", profile.prePlayableNum)
|
||||
},
|
||||
option: {
|
||||
hispeed: K.ITEM("s32", profile.opHispeed),
|
||||
gauge: K.ITEM("s32", profile.opGauge),
|
||||
fastslow: K.ITEM("s32", profile.opFastSlow),
|
||||
guideline: K.ITEM("s32", profile.opGuideline),
|
||||
stepzone: K.ITEM("s32", profile.opStepZone),
|
||||
timing_disp: K.ITEM("s32", profile.opTimingDisp),
|
||||
visibility: K.ITEM("s32", profile.opVisibility),
|
||||
visible_time: K.ITEM("s32", profile.opVisibleTime),
|
||||
lane: K.ITEM("s32", profile.opLane),
|
||||
lane_hiddenpos: K.ITEM("s32", profile.opLaneHiddenPos),
|
||||
lane_suddenpos: K.ITEM("s32", profile.opLaneSuddenPos),
|
||||
lane_hidsudpos: K.ITEM("s32", profile.opLaneHidSudPos),
|
||||
lane_filter: K.ITEM("s32", profile.opLaneFilter),
|
||||
scroll_direction: K.ITEM("s32", profile.opScrollDirection),
|
||||
scroll_moving: K.ITEM("s32", profile.opScrollMoving),
|
||||
arrow_priority: K.ITEM("s32", profile.opArrowPriority),
|
||||
arrow_placement: K.ITEM("s32", profile.opArrowPlacement),
|
||||
arrow_color: K.ITEM("s32", profile.opArrowColor),
|
||||
arrow_design: K.ITEM("s32", profile.opArrowDesign),
|
||||
cut_timing: K.ITEM("s32", profile.opCutTiming),
|
||||
cut_freeze: K.ITEM("s32", profile.opCutFreeze),
|
||||
cut_jump: K.ITEM("s32", profile.opCutJump)
|
||||
},
|
||||
lastplay: {
|
||||
mode: K.ITEM("s32", profile.lpMode),
|
||||
folder: K.ITEM("s32", profile.lpFolder),
|
||||
mcode: K.ITEM("s32", profile.lpMcode),
|
||||
style: K.ITEM("s32", profile.lpStyle),
|
||||
difficulty: K.ITEM("s32", profile.lpDifficulty),
|
||||
window_main: K.ITEM("s32", profile.lpWindowMain),
|
||||
window_sub: K.ITEM("s32", profile.lpWindowSub),
|
||||
target: K.ITEM("s32", profile.lpTarget),
|
||||
tab_main: K.ITEM("s32", profile.lpTabMain),
|
||||
tab_sub: K.ITEM("s32", profile.lpTabSub)
|
||||
},
|
||||
filtersort: {
|
||||
title: K.ITEM("u64", BigInt(profile.fsTitle)),
|
||||
version: K.ITEM("u64", BigInt(profile.fsVersion)),
|
||||
genre: K.ITEM("u64", BigInt(profile.fsGenre)),
|
||||
bpm: K.ITEM("u64", BigInt(profile.fsBpm)),
|
||||
event: K.ITEM("u64", BigInt(profile.fsEvent)),
|
||||
level: K.ITEM("u64", BigInt(profile.fsLevel)),
|
||||
flare_rank: K.ITEM("u64", BigInt(profile.fsFlareRank)),
|
||||
clear_rank: K.ITEM("u64", BigInt(profile.fsClearRank)),
|
||||
flare_skill_target: K.ITEM("u64", BigInt(profile.fsFlareSkillTarget)),
|
||||
rival_flare_skill: K.ITEM("u64", BigInt(profile.fsRivalFlareSkill)),
|
||||
rival_score_rank: K.ITEM("u64", BigInt(profile.fsRivalScoreRank)),
|
||||
sort_type: K.ITEM("u64", BigInt(profile.fsSortType)),
|
||||
order_type: K.ITEM("s32", profile.fsOrderType),
|
||||
},
|
||||
checkguide: {
|
||||
tips_basic: K.ITEM("u64", BigInt(profile.cgTipsBasic)),
|
||||
tips_option: K.ITEM("u64", BigInt(profile.cgTipsOption)),
|
||||
tips_event: K.ITEM("u64", BigInt(profile.cgTipsEvent)),
|
||||
tips_gimmick: K.ITEM("u64", BigInt(profile.cgTipsGimmick)),
|
||||
tips_advance: K.ITEM("u64", BigInt(profile.cgTipsAdvance)),
|
||||
guide_scene: K.ITEM("u64", BigInt(profile.cgGuideScene)),
|
||||
},
|
||||
rival: [],
|
||||
score: scoreFin,
|
||||
event: eventFin
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const musicdataload: EPR = async (info, data, send) => {
|
||||
// I personally use the last A3 db for this, will check for missing songs
|
||||
let musicList = []
|
||||
let mdbLimName = U.GetConfig('mdb_limited')
|
||||
if(mdbLimName !== '') {
|
||||
if(IO.Exists('data/' + mdbLimName)) {
|
||||
let mdb = U.parseXML(U.DecodeString(await IO.ReadFile('data/' + mdbLimName), "shift_jis"), false)
|
||||
for(const music of mdb['mdb']['music']) {
|
||||
let difficultyArr = $(music).numbers('diffLv')
|
||||
let limited = ($(music).number('limited')) ? $(music).number('limited') : 0
|
||||
let limitedCha = ($(music).number('limited_cha')) ? $(music).number('limited_cha') : 0
|
||||
let limitedAry = ($(music).numbers('limited_ary')) ? $(music).numbers('limited_ary') : []
|
||||
|
||||
let overrideIndex = SONGS_OVERRIDE_WORLD.findIndex(s => s.mcode === $(music).number('mcode'))
|
||||
if(overrideIndex > -1) {
|
||||
limitedAry = (SONGS_OVERRIDE_WORLD[overrideIndex]['limited_ary'] !== [] ? SONGS_OVERRIDE_WORLD[overrideIndex]['limited_ary'] : limitedAry)
|
||||
difficultyArr = SONGS_OVERRIDE_WORLD[overrideIndex]['diffLv']
|
||||
}
|
||||
|
||||
for(const [index, diff] of difficultyArr.entries()) {
|
||||
limited = ((index % 5 === 4) && limitedCha) ? limitedCha : limited
|
||||
limited = (limitedAry) ? limitedAry[index] : limited
|
||||
musicList.push({
|
||||
music_str: K.ITEM('str', $(music).number('mcode') + ',' + ((index > 4) ? '1,' : '0,') + (index % 5) + ',' + limited + ',' + diff)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(const music of SONGS_WORLD) {
|
||||
for(const [index, diff] of music.diffLv.entries()) {
|
||||
if(music.limited_ary[index] != -1) {
|
||||
musicList.push({
|
||||
music_str: K.ITEM('str', music.mcode + ',' + ((index > 4) ? '1,' : '0,') + (index % 5) + ',' + music.limited_ary[index] + ',' + diff)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// test
|
||||
if(IO.Exists('data/test.json')) {
|
||||
let bufTest = await IO.ReadFile('data/test.json')
|
||||
let eventTest = JSON.parse(bufTest.toString())
|
||||
for(const ex in eventTest['songstest']) {
|
||||
for(const [index, diff] of eventTest['songstest'][ex].diffLv.entries()) {
|
||||
if(eventTest['songstest'][ex].limited_ary[index] != -1) {
|
||||
musicList.push({
|
||||
music_str: K.ITEM('str', eventTest['songstest'][ex].mcode + ',' + ((index > 4) ? '1,' : '0,') + (index % 5) + ',' + eventTest['songstest'][ex].limited_ary[index] + ',' + diff)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
servertime: K.ITEM("u64", BigInt(getDate())),
|
||||
music: musicList
|
||||
});
|
||||
};
|
||||
|
||||
export const rivaldataload: EPR = async (info, data, send) => {
|
||||
const ddrCode = $(data).number("data.ddrcode");
|
||||
const loadKind = $(data).number("data.loadkind");
|
||||
const country = $(data).str("data.country");
|
||||
const region = $(data).str("data.region");
|
||||
const customerCode = $(data).str("data.customercode");
|
||||
const companyCode = $(data).str("data.companycode");
|
||||
const locationId = $(data).str("data.locationid");
|
||||
const pcbid = $(data).str("data.pcbid");
|
||||
|
||||
// song id, style, difficulty, idk, dancername, idk, idk, scoredisp, score, ghost
|
||||
let ssssss = ''
|
||||
let record = []
|
||||
let hiscore: any
|
||||
if(ddrCode === 0) {
|
||||
if(loadKind === 1) {
|
||||
hiscore = await DB.Find<HiScoreWorld>(null, {collection: 'hiscore3', slot: loadKind})
|
||||
} else if(loadKind === 2) {
|
||||
hiscore = await DB.Find<HiScoreWorld>(null, {collection: 'hiscore3', slot: loadKind, locationId: locationId})
|
||||
} else if(loadKind === 3) {
|
||||
hiscore = await DB.Find<HiScoreWorld>(null, {collection: 'hiscore3', slot: loadKind, locationId: locationId, pcbid: pcbid})
|
||||
}
|
||||
for(const hsi in hiscore) {
|
||||
record.push({
|
||||
record_str: K.ITEM('str', hiscore[hsi].songId + ',' + hiscore[hsi].style + ',' + hiscore[hsi].difficulty + ',0,' + hiscore[hsi].dancerName + ',0,0,1,' + hiscore[hsi].score + ',' + hiscore[hsi].ghostId)
|
||||
})
|
||||
}
|
||||
}
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
record: record
|
||||
})
|
||||
};
|
||||
|
||||
export const ghostdataload: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str("data.refid");
|
||||
const ghostId = $(data).number("data.ghostid");
|
||||
console.log("loading ghostid: " + ghostId)
|
||||
let ghostData = await DB.FindOne<GhostWorld>(null, {collection: 'ghost3', ghostId: ghostId})
|
||||
if(ghostData) {
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
ghostsize: K.ITEM("s32", ghostData.ghostSize),
|
||||
ghost: K.ITEM("str", ghostData.ghost)
|
||||
});
|
||||
}
|
||||
return send.object({ result: K.ITEM("s32", 0) });
|
||||
};
|
||||
|
||||
export const taboowordcheck: EPR = async (info, data, send) => {
|
||||
// Automatically accept word
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
is_taboo: K.ITEM("bool", false)
|
||||
});
|
||||
};
|
||||
|
||||
export const minidump: EPR = async (info, data, send) => {
|
||||
return send.object({ result: K.ITEM('s32', 0) })
|
||||
};
|
||||
|
||||
function getDate(): number {
|
||||
let time = new Date();
|
||||
let tempDate = time.getDate();
|
||||
const currentTime = parseInt((time.getTime()/100000) as unknown as string)*100;
|
||||
return currentTime
|
||||
}
|
||||
@@ -2,24 +2,71 @@ import { convcardnumber, eventLog } from "./handlers/common";
|
||||
import { usergamedata } from "./handlers/usergamedata";
|
||||
import { usergamedata_recv } from "./handlers/usergamedata_recv";
|
||||
import { usergamedata_send } from "./handlers/usergamedata_send";
|
||||
import { musicdataload, playerdatanew, playerdatasave, playerdataload, rivaldataload, ghostdataload, taboowordcheck, minidump } from "./handlers/ddrworld";
|
||||
import { CommonOffset, OptionOffset, Profile } from "./models/profile";
|
||||
import { ProfileWorld } from "./models/ddrworld";
|
||||
import { SONGS_WORLD, SONGS_OVERRIDE_WORLD } from "./data/world"
|
||||
|
||||
export function register() {
|
||||
R.GameCode("MDX");
|
||||
|
||||
R.Unhandled(undefined);
|
||||
|
||||
R.Config("save_option", {
|
||||
name: "Save option",
|
||||
desc: "Gets the previously set options as they are.",
|
||||
default: true,
|
||||
type: "boolean"
|
||||
});
|
||||
R.Config("mdb_limited", {
|
||||
name: "musicdb.xml for musicdata_load",
|
||||
desc: "musicdb.xml file to use for importing limited and diffLv info. Put this xml file in the 'data' directory of the plugin.",
|
||||
type: "string",
|
||||
default: ""
|
||||
});
|
||||
R.Config("mdb_title", {
|
||||
name: "musicdb.xml for WebUI",
|
||||
desc: "musicdb.xml file to retrieve song titles from, for use in WebUI. Put this xml file in the 'data' directory of the plugin, or keep this blank if you want to use the same file as above.",
|
||||
type: "string",
|
||||
default: ""
|
||||
});
|
||||
|
||||
R.Route("playerdata.usergamedata_advanced", usergamedata);
|
||||
R.Route("playerdata.usergamedata_recv", usergamedata_recv);
|
||||
R.Route("playerdata.usergamedata_send", usergamedata_send);
|
||||
const RoutePlayerData = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`playerdata.${method}`, handler);
|
||||
};
|
||||
|
||||
R.Route("system.convcardnumber", convcardnumber);
|
||||
R.Route("eventlog.write", eventLog);
|
||||
const RoutePlayData = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`playdata_3.${method}`, handler);
|
||||
|
||||
};
|
||||
|
||||
const WordCheck = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`wordcheck_3.${method}`, handler);
|
||||
}
|
||||
|
||||
const RouteSystem = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`system.${method}`, handler);
|
||||
R.Route(`system_3.${method}`, handler);
|
||||
};
|
||||
|
||||
const RouteEventLog = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`eventlog.${method}`, handler);
|
||||
R.Route(`eventlog_3.${method}`, handler);
|
||||
};
|
||||
|
||||
RoutePlayerData('usergamedata_advanced', usergamedata);
|
||||
RoutePlayerData('usergamedata_recv', usergamedata_recv);
|
||||
RoutePlayerData('usergamedata_send', usergamedata_send);
|
||||
RoutePlayData('musicdata_load', musicdataload);
|
||||
RoutePlayData('playerdata_new', playerdatanew);
|
||||
RoutePlayData('playerdata_load', playerdataload);
|
||||
RoutePlayData('playerdata_save', playerdatasave);
|
||||
RoutePlayData('rivaldata_load', rivaldataload);
|
||||
RoutePlayData('ghostdata_load', ghostdataload);
|
||||
RouteSystem("convcardnumber", convcardnumber);
|
||||
RouteSystem("minidump", minidump);
|
||||
RouteEventLog("write", eventLog);
|
||||
WordCheck('tabooword_check', taboowordcheck)
|
||||
|
||||
R.WebUIEvent("updateName", async ({ refid, name }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
@@ -132,4 +179,68 @@ export function register() {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateName3", async ({ refid, name }) => {
|
||||
await DB.Update<ProfileWorld>(refid, { collection: "profile3" }, {
|
||||
$set: {
|
||||
dancerName: name
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateWeight3", async ({ refid, weight }) => {
|
||||
await DB.Update<ProfileWorld>(refid, { collection: "profile3" }, {
|
||||
$set: {
|
||||
weight: weight
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateDisplayCalories3", async ({ refid, selected }) => {
|
||||
await DB.Update<ProfileWorld>(refid, { collection: "profile3" }, {
|
||||
$set: {
|
||||
isDispWeight: selected
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
R.WebUIEvent("getMDB", async (data: {}, send: WebUISend) => {
|
||||
let mdbData = []
|
||||
let mdbLimName = U.GetConfig('mdb_limited')
|
||||
let mdbTitleName = U.GetConfig('mdb_title')
|
||||
|
||||
if(mdbLimName !== '') {
|
||||
if(IO.Exists('data/' + mdbLimName)) {
|
||||
let mdbLim = U.parseXML(U.DecodeString(await IO.ReadFile('data/' + mdbLimName), "utf8"), false)
|
||||
|
||||
mdbLim['mdb']['music'].forEach(music => {
|
||||
if(SONGS_WORLD.concat(SONGS_OVERRIDE_WORLD).findIndex(so => so.mcode === $(music).number('mcode')) < 0) {
|
||||
mdbData.push({
|
||||
mcode: $(music).number('mcode'),
|
||||
title: $(music).str('title'),
|
||||
diffLv: $(music).numbers('diffLv'),
|
||||
series: $(music).number('series')
|
||||
})
|
||||
}
|
||||
})
|
||||
let mdbTitle = (mdbTitleName === mdbLimName || mdbTitleName === '') ? mdbLim : U.parseXML(U.DecodeString(await IO.ReadFile('data/' + mdbTitleName), "utf8"), false)
|
||||
SONGS_WORLD.concat(SONGS_OVERRIDE_WORLD).forEach(sw => {
|
||||
let musicInfo = mdbTitle['mdb']['music'].find(m => $(m).number('mcode') === sw.mcode)
|
||||
let songTitle = 'ID ' + sw.mcode
|
||||
let series = 0
|
||||
if(musicInfo) {
|
||||
songTitle = $(musicInfo).str('title')
|
||||
series = $(musicInfo).number('series')
|
||||
}
|
||||
mdbData.push({
|
||||
mcode: sw.mcode,
|
||||
title: songTitle,
|
||||
diffLv: sw.diffLv,
|
||||
series: series
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
send.json({mdb: mdbData})
|
||||
})
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
# Jubeat Plugin
|
||||
|
||||
Jubeat Plugin for Asphyxia Core
|
||||
|
||||
# Supported Versions
|
||||
|
||||
- Festo
|
||||
|
||||
# Versions
|
||||
|
||||
- V1.0.0 (2021/12/16)
|
||||
- Only support normal mode score saving.
|
||||
|
||||
- V2.0.0 (2022/08/14)
|
||||
- Now Support Festo Final
|
||||
- Support hard mode score saving
|
||||
- Support Turn Run
|
||||
|
||||
# TODO
|
||||
|
||||
- [ ] Customized Turn Run. (Currently can't cuz Jubeat courses limit is 60, need someone to find how to patch it.)
|
||||
|
||||
# Credits
|
||||
|
||||
- Thanks [asesidaa](https://github.com/asesidaa?tab=repositories) for help!
|
||||
- And also the other open-soured Jubeat lovers!
|
||||
@@ -1,31 +0,0 @@
|
||||
import ShopInfo from "./routes/shopinfo";
|
||||
import {getProfile, Getinfo, loadScore, Meeting} from "./routes/gametop";
|
||||
import {saveProfile} from "./routes/gameend";
|
||||
import {Check, Entry, Refresh, Report} from "./routes/lobby";
|
||||
|
||||
export async function register() {
|
||||
if (CORE_VERSION_MAJOR <= 1 && CORE_VERSION_MINOR < 31) {
|
||||
console.error("The current version of Asphyxia Core is not supported. Requires version '1.31' or later.");
|
||||
return;
|
||||
}
|
||||
R.GameCode("L44");
|
||||
R.Contributor("yuanqiuye", "https://github.com/yuanqiuye")
|
||||
R.Route("gametop.regist",getProfile);
|
||||
R.Route("gametop.get_info", Getinfo);
|
||||
R.Route("gametop.get_pdata", getProfile);
|
||||
R.Route("gametop.get_mdata", loadScore);
|
||||
R.Route("gametop.get_meeting", Meeting);
|
||||
|
||||
R.Route("gameend.final", true);
|
||||
R.Route("gameend.regist", saveProfile);
|
||||
|
||||
R.Route("shopinfo.regist", ShopInfo);
|
||||
R.Route("lobby.check", Check);
|
||||
R.Route("lobby.entry", Entry);
|
||||
R.Route("lobby.refresh", Refresh);
|
||||
R.Route("lobby.report", Report);
|
||||
|
||||
R.Route("netlog.send", true);
|
||||
R.Route("logger.report", true);
|
||||
R.Unhandled();
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export interface Course {
|
||||
collection: "course";
|
||||
|
||||
courseId: number;
|
||||
seen: boolean,
|
||||
played: boolean,
|
||||
cleared: boolean
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
export default interface Profile {
|
||||
collection: "profile";
|
||||
navi?: number,
|
||||
jubeatId: number;
|
||||
eventFlag: number;
|
||||
name: string;
|
||||
emo: number[];
|
||||
lastPlayTime?: number;
|
||||
lastShopname: string;
|
||||
lastAreaname: string;
|
||||
isFirstplay: boolean;
|
||||
musicId?: number;
|
||||
seqId?: number;
|
||||
seqEditId?: string;
|
||||
rankSort?: number;
|
||||
comboDisp?: number;
|
||||
|
||||
jubility?: number;
|
||||
jubilityYday?: number;
|
||||
tuneCount?: number;
|
||||
clearCount?: number;
|
||||
saveCount?: number;
|
||||
savedCount?: number;
|
||||
fcCount?: number;
|
||||
exCount?: number;
|
||||
matchCount?: number;
|
||||
bonusPoints?: number;
|
||||
isBonusPlayed?: boolean;
|
||||
totalBestScore?: number;
|
||||
clearMaxLevel?: number;
|
||||
fcMaxLevel?: number;
|
||||
exMaxLevel?: number;
|
||||
|
||||
emblem?: number[];
|
||||
marker?: number;
|
||||
theme?: number;
|
||||
title?: number;
|
||||
parts?: number;
|
||||
sort?: number;
|
||||
category?: number;
|
||||
expertOption?: number;
|
||||
matching?: number;
|
||||
hazard?: number;
|
||||
hard?: number;
|
||||
|
||||
secretList?: number[];
|
||||
themeList?: number;
|
||||
markerList?: number[];
|
||||
titleList?: number[];
|
||||
commuList?: number[];
|
||||
partsList?: number[];
|
||||
|
||||
secretListNew?: number[];
|
||||
themeListNew?: number[];
|
||||
markerListNew?: number[];
|
||||
titleListNew?: number[];
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
export interface Score {
|
||||
collection: "score";
|
||||
|
||||
musicId: number;
|
||||
seq: number;
|
||||
score: number;
|
||||
clear: number;
|
||||
musicRate: number;
|
||||
bar: number[];
|
||||
playCount: number;
|
||||
clearCount: number;
|
||||
fullcomboCount: number;
|
||||
excellentCount: number;
|
||||
isHardMode: boolean;
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
import {Score} from "../models/score"
|
||||
import Profile from "../models/profile";
|
||||
import { Course } from "../models/course";
|
||||
import { COURSE_STATUS } from "../static/data";
|
||||
|
||||
export const saveProfile = async (info, {data}, send) => {
|
||||
console.log("gameend.regist");
|
||||
console.log(data, {depth:null});
|
||||
const refId = $(data).str("player.refid");
|
||||
if (!refId) return send.deny();
|
||||
|
||||
const profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
|
||||
if (!profile) return send.deny();
|
||||
|
||||
let lastMarker = 0;
|
||||
let lastTheme = 0;
|
||||
let lastTitle = 0;
|
||||
let lastParts = 0;
|
||||
let lastSort = 0;
|
||||
let lastCategory = 0;
|
||||
|
||||
const courses = $(data).elements("player.course_list.course");
|
||||
const tunes = $(data).elements("result.tune");
|
||||
const select_course = $(data).elements("player.select_course");
|
||||
const course_cleared : { [couseId: number]: { is_cleared: boolean} } = {};
|
||||
|
||||
if(select_course){
|
||||
for(const course of select_course){
|
||||
course_cleared[course.attr("").id] = course.bool("is_cleared");
|
||||
}
|
||||
}
|
||||
|
||||
for (const course of courses){
|
||||
const courseID = course.attr("").id;
|
||||
await updateCourse(refId, {
|
||||
courseID: courseID,
|
||||
seen: (course.number("status") & COURSE_STATUS.SEEN) != 0,
|
||||
played: (course.number("status") & COURSE_STATUS.PLAYED) != 0,
|
||||
cleared: course_cleared[courseID] || (course.number("status") & COURSE_STATUS.CLEARED) != 0,
|
||||
});
|
||||
}
|
||||
|
||||
for (const tune of tunes) {
|
||||
profile.musicId = tune.number("music");
|
||||
profile.seqId = parseInt(tune.attr("player.score").seq);
|
||||
|
||||
await updateScore(refId, {
|
||||
bestmusicRate: tune.number("player.best_music_rate"),
|
||||
musicRate: tune.number("player.music_rate"),
|
||||
musicId: tune.number("music"),
|
||||
seq: parseInt(tune.attr("player.score").seq),
|
||||
score: tune.number("player.score"),
|
||||
clear: parseInt(tune.attr("player.score").clear),
|
||||
isHard: tune.bool("player.is_hard_mode"),
|
||||
bestScore: tune.number("player.best_score"),
|
||||
bestClear: tune.number("player.best_clear"),
|
||||
playCount: tune.number("player.play_cnt"),
|
||||
clearCount: tune.number("player.clear_cnt"),
|
||||
fullcomboCount: tune.number("player.fc_cnt"),
|
||||
excellentCount: tune.number("player.ex_cnt"),
|
||||
...tune.element("player.mbar") && { mbar: tune.numbers("player.mbar") }
|
||||
});
|
||||
}
|
||||
|
||||
lastMarker = $(data).number("player.last.settings.marker");
|
||||
lastTheme = $(data).number("player.last.settings.theme");
|
||||
lastTitle = $(data).number("player.last.settings.title");
|
||||
lastParts = $(data).number("player.last.settings.parts");
|
||||
lastSort = $(data).number("player.last.sort");
|
||||
lastCategory = $(data).number("player.last.category");
|
||||
profile.eventFlag = Number($(data).bigint("player.event_flag"));
|
||||
profile.rankSort = $(data).number("player.last.settings.rank_sort");
|
||||
profile.comboDisp = $(data).number("player.last.settings.combo_disp");
|
||||
|
||||
profile.lastPlayTime = Number($(data).bigint("info.play_time"));
|
||||
profile.lastShopname = $(data).str("info.shopname");
|
||||
profile.lastAreaname = $(data).str("info.areaname");
|
||||
|
||||
profile.tuneCount = $(data).number("player.info.tune_cnt");
|
||||
profile.saveCount = $(data).number("player.info.save_cnt");
|
||||
profile.savedCount = $(data).number("player.info.saved_cnt");
|
||||
profile.fcCount = $(data).number("player.info.fc_cnt");
|
||||
profile.exCount = $(data).number("player.info.ex_cnt");
|
||||
profile.clearCount = $(data).number("player.info.clear_cnt");
|
||||
profile.matchCount = $(data).number("player.info.match_cnt");
|
||||
profile.expertOption = $(data).number("player.last.expert_option");
|
||||
profile.matching = $(data).number("player.last.settings.matching");
|
||||
profile.hazard =$(data).number("player.last.settings.hazard");
|
||||
profile.hard = $(data).number("player.last.settings.hard");
|
||||
profile.bonusPoints = $(data).number("player.info.bonus_tune_points");
|
||||
profile.isBonusPlayed = $(data).bool("player.info.is_bonus_tune_played");
|
||||
profile.totalBestScore = $(data).number("player.info.total_best_score.normal");
|
||||
profile.clearMaxLevel = $(data).number("player.info.clear_max_level");
|
||||
profile.fcMaxLevel = $(data).number("player.info.fc_max_level");
|
||||
profile.exMaxLevel = $(data).number("player.info.ex_max_level");
|
||||
profile.navi = Number($(data).bigint("player.navi.flag"));
|
||||
profile.isFirstplay = $(data).bool("player.free_first_play.is_applied");
|
||||
profile.marker = lastMarker;
|
||||
profile.theme = lastTheme;
|
||||
profile.title = lastTitle;
|
||||
profile.parts = lastParts;
|
||||
profile.sort = lastSort;
|
||||
profile.category = lastCategory;
|
||||
|
||||
profile.commuList = $(data).numbers("player.item.commu_list");
|
||||
profile.secretList = $(data).numbers("player.item.secret_list");
|
||||
profile.themeList = $(data).number("player.item.theme_list");
|
||||
profile.markerList = $(data).numbers("player.item.marker_list");
|
||||
profile.titleList = $(data).numbers("player.item.title_list");
|
||||
profile.partsList = $(data).numbers("player.item.parts_list");
|
||||
profile.secretListNew = $(data).numbers("player.item.new.secret_list");
|
||||
profile.themeListNew = $(data).numbers("player.item.new.theme_list");
|
||||
profile.markerListNew = $(data).numbers("player.item.new.marker_list");
|
||||
|
||||
try {
|
||||
await DB.Update<Profile>(refId, { collection: "profile" }, profile);
|
||||
|
||||
return send.object({
|
||||
data: {
|
||||
player: { session_id: K.ITEM("s32", 1) },
|
||||
collabo: { deller: K.ITEM("s32", 0) }
|
||||
}
|
||||
}, {compress:true});
|
||||
} catch (e) {
|
||||
console.error(`Profile save failed: ${e.message}`);
|
||||
return send.deny();
|
||||
}
|
||||
}
|
||||
|
||||
const updateScore = async (refId: string, data: any): Promise<boolean> => {
|
||||
try {
|
||||
await DB.Upsert<Score>(refId, {
|
||||
collection: "score",
|
||||
musicId: data.musicId,
|
||||
seq: data.seq,
|
||||
isHardMode: data.isHard,
|
||||
}, {
|
||||
$set: {
|
||||
musicId: data.musicId,
|
||||
seq: data.seq,
|
||||
score: data.bestScore,
|
||||
clear: data.bestClear,
|
||||
musicRate: data.musicRate>data.bestmusicRate?data.musicRate:data.bestmusicRate,
|
||||
...data.mbar && { bar: data.mbar, },
|
||||
playCount: data.playCount,
|
||||
clearCount: data.clearCount,
|
||||
fullcomboCount: data.fullcomboCount,
|
||||
excellentCount: data.excellentCount,
|
||||
isHardMode: data.isHard
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Score saving failed: ", e.stack);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const updateCourse = async (refId: string, data: any): Promise<boolean> => {
|
||||
try {
|
||||
await DB.Upsert<Course>(refId, {
|
||||
collection: "course",
|
||||
courseId: data.courseID,
|
||||
}, {
|
||||
$set: {
|
||||
courseId: data.courseID,
|
||||
seen: data.seen,
|
||||
played: data.played,
|
||||
cleared: data.cleared
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Course saving failed: ", e.stack);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -1,211 +0,0 @@
|
||||
import Profile from '../models/profile';
|
||||
import { Score } from '../models/score';
|
||||
|
||||
export const getProfile = async (
|
||||
info: EamuseInfo,
|
||||
data: any,
|
||||
send: EamuseSend
|
||||
) => {
|
||||
console.log('gametop.regist');
|
||||
let refId = $(data).str('data.player.refid');
|
||||
const name = $(data).str('data.player.name');
|
||||
|
||||
console.log(data, { depth: null });
|
||||
if (!refId) return send.deny();
|
||||
|
||||
let profile = await DB.FindOne<Profile>(refId, { collection: 'profile' });
|
||||
|
||||
if (!profile && name) {
|
||||
const newProfile: Profile = {
|
||||
collection: 'profile',
|
||||
jubeatId: Math.round(Math.random() * 99999999),
|
||||
eventFlag: 0,
|
||||
name: name,
|
||||
isFirstplay: true,
|
||||
emo: [],
|
||||
lastShopname: '',
|
||||
lastAreaname: '',
|
||||
};
|
||||
|
||||
await DB.Upsert<Profile>(refId, { collection: 'profile' }, newProfile);
|
||||
|
||||
profile = newProfile;
|
||||
} else if (!profile && !name) {
|
||||
return send.deny();
|
||||
}
|
||||
|
||||
return send.object(
|
||||
{
|
||||
data: {
|
||||
...require('../templates/gameInfos.ts')(),
|
||||
|
||||
player: {
|
||||
jid: K.ITEM('s32', profile.jubeatId),
|
||||
session_id: K.ITEM('s32', 1),
|
||||
name: K.ITEM('str', profile.name),
|
||||
event_flag: K.ITEM('u64', BigInt(profile.eventFlag || 0)),
|
||||
|
||||
...(await require('../templates/profiles.ts')(profile)),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ compress: true }
|
||||
);
|
||||
};
|
||||
|
||||
export const Getinfo = (info: EamuseInfo, data: any, send: EamuseSend) => {
|
||||
console.log(data, { depth: null });
|
||||
return send.object(
|
||||
{ data: require('../templates/gameInfos')() },
|
||||
{ compress: true }
|
||||
);
|
||||
};
|
||||
|
||||
export const loadScore = async (info, data, send) => {
|
||||
console.log('gametop.get_mdata');
|
||||
console.log(data, { depth: null });
|
||||
const mdata_ver = $(data).number('data.player.mdata_ver');
|
||||
const jubeatId = $(data).number('data.player.jid');
|
||||
if (!jubeatId) return send.deny();
|
||||
|
||||
const profile = await DB.FindOne<Profile>(null, {
|
||||
collection: 'profile',
|
||||
jubeatId,
|
||||
});
|
||||
if (!profile) return send.deny();
|
||||
|
||||
const scores = await DB.Find<Score>(profile.__refid, { collection: 'score' });
|
||||
const scoreData: {
|
||||
[musicId: number]: {
|
||||
[isHardMode: number]: {
|
||||
musicRate: number[];
|
||||
score: number[];
|
||||
clear: number[];
|
||||
playCnt: number[];
|
||||
clearCnt: number[];
|
||||
fcCnt: number[];
|
||||
exCnt: number[];
|
||||
bar: number[][];
|
||||
};
|
||||
};
|
||||
} = {};
|
||||
|
||||
for (const score of scores) {
|
||||
if (!scoreData[score.musicId]) {
|
||||
scoreData[score.musicId] = {};
|
||||
}
|
||||
if (!scoreData[score.musicId][score.isHardMode ? 1 : 0]) {
|
||||
scoreData[score.musicId][score.isHardMode ? 1 : 0] = {
|
||||
musicRate: [0, 0, 0],
|
||||
playCnt: [0, 0, 0],
|
||||
clearCnt: [0, 0, 0],
|
||||
fcCnt: [0, 0, 0],
|
||||
exCnt: [0, 0, 0],
|
||||
clear: [0, 0, 0],
|
||||
score: [0, 0, 0],
|
||||
bar: [Array(30).fill(0), Array(30).fill(0), Array(30).fill(0)],
|
||||
};
|
||||
}
|
||||
|
||||
const data = scoreData[score.musicId][score.isHardMode ? 1 : 0];
|
||||
data.musicRate[score.seq] = score.musicRate;
|
||||
data.playCnt[score.seq] = score.playCount;
|
||||
data.clearCnt[score.seq] = score.clearCount;
|
||||
data.fcCnt[score.seq] = score.fullcomboCount;
|
||||
data.exCnt[score.seq] = score.excellentCount;
|
||||
data.clear[score.seq] = score.clear;
|
||||
data.score[score.seq] = score.score;
|
||||
data.bar[score.seq] = score.bar;
|
||||
}
|
||||
|
||||
var sendobj = {
|
||||
data: {
|
||||
player: {
|
||||
jid: K.ITEM('s32', jubeatId),
|
||||
|
||||
mdata_list: {
|
||||
music: (() => {
|
||||
var musicArray = [];
|
||||
Object.keys(scoreData).forEach(musicId =>
|
||||
Object.keys(scoreData[musicId]).forEach(isHardMode => {
|
||||
musicArray.push(
|
||||
K.ATTR(
|
||||
{ music_id: String(musicId) },
|
||||
{
|
||||
[isHardMode === '1' ? 'hard' : 'normal']: {
|
||||
score: K.ARRAY(
|
||||
's32',
|
||||
scoreData[musicId][isHardMode].score
|
||||
),
|
||||
clear: K.ARRAY(
|
||||
's8',
|
||||
scoreData[musicId][isHardMode].clear
|
||||
),
|
||||
music_rate: K.ARRAY(
|
||||
's32',
|
||||
scoreData[musicId][isHardMode].musicRate
|
||||
),
|
||||
play_cnt: K.ARRAY(
|
||||
's32',
|
||||
scoreData[musicId][isHardMode].playCnt
|
||||
),
|
||||
clear_cnt: K.ARRAY(
|
||||
's32',
|
||||
scoreData[musicId][isHardMode].clearCnt
|
||||
),
|
||||
fc_cnt: K.ARRAY(
|
||||
's32',
|
||||
scoreData[musicId][isHardMode].fcCnt
|
||||
),
|
||||
ex_cnt: K.ARRAY(
|
||||
's32',
|
||||
scoreData[musicId][isHardMode].exCnt
|
||||
),
|
||||
bar: scoreData[musicId][isHardMode].bar.map(
|
||||
(bar, seq) => K.ARRAY('u8', bar, { seq: String(seq) })
|
||||
),
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
return musicArray;
|
||||
})(),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (mdata_ver != 1) {
|
||||
sendobj = {
|
||||
data: {
|
||||
player: {
|
||||
jid: K.ITEM('s32', jubeatId),
|
||||
mdata_list: {
|
||||
music: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
console.log(sendobj, { depth: null });
|
||||
return send.object(sendobj, { compress: true });
|
||||
};
|
||||
|
||||
export const Meeting = (req: EamuseInfo, data: any, send: EamuseSend) => {
|
||||
return send.object(
|
||||
{
|
||||
data: {
|
||||
meeting: {
|
||||
single: K.ATTR({ count: '0' }),
|
||||
},
|
||||
reward: {
|
||||
total: K.ITEM('s32', 0),
|
||||
point: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ compress: true }
|
||||
);
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
export const Check = (req, reqData, send) => {
|
||||
const { data } = reqData;
|
||||
const enter = $(data).content("enter");
|
||||
const time = $(data).content("time");
|
||||
return send.object(
|
||||
{
|
||||
data: {
|
||||
entrant_nr: K.ITEM("u32", 0, { time }),
|
||||
interval: K.ITEM("s16", 0),
|
||||
entry_timeout: K.ITEM("s16", 15),
|
||||
waitlist: K.ATTR({ count: "0" }, { music: [] }),
|
||||
},
|
||||
},
|
||||
{ compress: true }
|
||||
);
|
||||
};
|
||||
|
||||
export const Entry = (req: EamuseInfo, data: any, send: EamuseSend) => {
|
||||
const {
|
||||
data: { music },
|
||||
} = data;
|
||||
const musicId = $(music).content("id");
|
||||
const musicSeq = $(music).content("seq");
|
||||
return send.object(
|
||||
{
|
||||
data: {
|
||||
roomid: K.ITEM("s64", BigInt(1), { master: "1" }),
|
||||
refresh_intr: K.ITEM("s16", 0),
|
||||
music: {
|
||||
id: K.ITEM("u32", musicId),
|
||||
seq: K.ITEM("u8", musicSeq),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ compress: true }
|
||||
);
|
||||
};
|
||||
|
||||
export const Refresh = (req: EamuseInfo, data: any, send: EamuseSend) => {
|
||||
|
||||
return send.object(
|
||||
{
|
||||
data: { refresh_intr: K.ITEM("s16", 0), start: K.ITEM("bool", true) },
|
||||
},
|
||||
{ compress: true }
|
||||
);
|
||||
};
|
||||
|
||||
export const Report = (req: EamuseInfo, data: any, send: EamuseSend) =>
|
||||
send.object(
|
||||
{
|
||||
data: { refresh_intr: K.ITEM("s16", 0) },
|
||||
},
|
||||
{ compress: true }
|
||||
);
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
export default (_: EamuseInfo, data: any, send: EamuseSend) => {
|
||||
|
||||
const locId = $(data).element("shop").content("locationid");
|
||||
console.log({...require("../templates/gameInfos.ts")()}, {depth:null});
|
||||
return send.object(
|
||||
{
|
||||
data: {
|
||||
cabid: K.ITEM("u32", 1),
|
||||
locationid: K.ITEM("str", locId),
|
||||
tax_phase: K.ITEM("u8", 0),
|
||||
facility: {
|
||||
exist: K.ITEM("u32", 0),
|
||||
},
|
||||
|
||||
...require("../templates/gameInfos.ts")(),
|
||||
},
|
||||
},
|
||||
{ compress: true }
|
||||
);
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -1,919 +0,0 @@
|
||||
[
|
||||
{
|
||||
name: "オレのユビティズム",
|
||||
difficulty: 3,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2100000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[20000042, 0, 0], [20000042, 1, 0], [20000042, 2, 0]],
|
||||
[[70000119, 0, 0], [70000119, 1, 0], [70000119, 2, 0]],
|
||||
[[50000115, 0, 0], [50000115, 1, 0], [50000115, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "はじめてのビーチ",
|
||||
difficulty: 1,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[60000080, 0, 0], [90000077, 0, 0], [90000139, 0, 0]],
|
||||
[[60000086, 0, 0], [70000047, 0, 0]],
|
||||
[[90000141, 0, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【初段】超幸せハイテンション",
|
||||
difficulty: 1,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[20000031, 0, 0], [60000100, 0, 0], [90000078, 0, 0]],
|
||||
[[70000125, 0, 0], [90000050, 0, 0]],
|
||||
[[70000106, 0, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "アニメランニング",
|
||||
difficulty: 2,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 750000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[60000092, 0, 0], [90000031, 0, 0], [90000172, 0, 0]],
|
||||
[[30000004, 0, 0], [80000059, 0, 0]],
|
||||
[[50000209, 0, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "パブリックリゾート",
|
||||
difficulty: 2,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 750000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[80000097, 0, 0], [90000029, 0, 0], [90000076, 0, 0]],
|
||||
[[80000093, 0, 0], [90000048, 0, 0]],
|
||||
[[80000038, 0, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【二段】その笑顔は甘く蕩ける",
|
||||
difficulty: 3,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 800000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000268, 0, 0], [70000039, 0, 0], [90000051, 0, 0]],
|
||||
[[70000091, 0, 0], [80000014, 0, 0]],
|
||||
[[60000053, 0, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "シャレを言いなシャレ",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2400000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[70000003, 0, 0], [70000003, 1, 0], [70000003, 2, 0]],
|
||||
[[70000045, 0, 0], [70000045, 1, 0], [70000045, 2, 0]],
|
||||
[[70000076, 0, 0], [70000076, 1, 0], [70000076, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "電脳享受空間",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 800000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[70000046, 1, 0], [70000160, 1, 0], [50000233, 1, 0]],
|
||||
[[80000031, 1, 0], [80000097, 1, 0]],
|
||||
[[90000049, 1, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "孤高の少女は破滅を願う",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 850000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000202, 0, 0], [70000117, 0, 0], [70000134, 0, 0]],
|
||||
[[50000212, 0, 0], [80000124, 1, 0]],
|
||||
[[90001008, 1, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "スタミナアップ!",
|
||||
difficulty: 5,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2600000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000242, 0, 0], [50000277, 1, 0], [50000294, 1, 0]],
|
||||
[[50000260, 1, 0], [50000261, 1, 0]],
|
||||
[[90000143, 1, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【四段】嗚呼、大繁盛!",
|
||||
difficulty: 6,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2600000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000085, 2, 0], [50000237, 2, 0], [80000080, 2, 0]],
|
||||
[[50000172, 2, 0], [50000235, 2, 0]],
|
||||
[[70000065, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 1",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 950000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000277, 0, 0], [50000277, 1, 0], [50000277, 2, 0]],
|
||||
[[50000325, 0, 0], [50000325, 1, 0], [50000325, 2, 0]],
|
||||
[[90000014, 0, 0], [90000014, 1, 0], [90000014, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 2",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2750000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[30000048, 0, 0], [30000048, 1, 0], [30000048, 2, 0]],
|
||||
[[30000121, 0, 0], [30000121, 1, 0], [30000121, 2, 0]],
|
||||
[[90000012, 0, 0], [90000012, 1, 0], [90000012, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 3",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 925000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[60000007, 0, 0], [60000007, 1, 0], [60000007, 2, 0]],
|
||||
[[60000070, 0, 0], [60000070, 1, 0], [60000070, 2, 0]],
|
||||
[[90000016, 0, 0], [90000016, 1, 0], [90000016, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 4",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2800000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[40000051, 0, 0], [40000051, 1, 0], [40000051, 2, 0]],
|
||||
[[40000129, 0, 0], [40000129, 1, 0], [40000129, 2, 0]],
|
||||
[[90000013, 0, 0], [90000013, 1, 0], [90000013, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 5",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2775000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[70000177, 0, 0], [70000177, 1, 0], [70000177, 2, 0]],
|
||||
[[70000011, 0, 0], [70000011, 1, 0], [70000011, 2, 0]],
|
||||
[[90000017, 0, 0], [90000017, 1, 0], [90000017, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 6",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 940000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[20000123, 0, 0], [20000123, 1, 0], [20000123, 2, 0]],
|
||||
[[20000038, 0, 0], [20000038, 1, 0], [20000038, 2, 0]],
|
||||
[[90000011, 0, 0], [90000011, 1, 0], [90000011, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 7",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 950000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000021, 0, 0], [50000021, 1, 0], [50000021, 2, 0]],
|
||||
[[50000078, 0, 0], [50000078, 1, 0], [50000078, 2, 0]],
|
||||
[[90000015, 0, 0], [90000015, 1, 0], [90000015, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 8",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2800000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[80000028, 0, 0], [80000028, 1, 0], [80000028, 2, 0]],
|
||||
[[80000087, 0, 0], [80000087, 1, 0], [80000087, 2, 0]],
|
||||
[[90000018, 0, 0], [90000018, 1, 0], [90000018, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 9",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 930000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[10000038, 0, 0], [10000038, 1, 0], [10000038, 2, 0]],
|
||||
[[10000065, 0, 0], [10000065, 1, 0], [10000065, 2, 0]],
|
||||
[[90000010, 0, 0], [90000010, 1, 0], [90000010, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【三段】この花を貴方へ",
|
||||
difficulty: 4,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 850000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000034, 1, 0], [90000107, 1, 0], [90000140, 1, 0]],
|
||||
[[80000052, 1, 0], [80001010, 1, 0]],
|
||||
[[40000051, 1, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "雨上がりレインボー",
|
||||
difficulty: 9,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2650000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000138, 2, 0]],
|
||||
[[80000057, 2, 0]],
|
||||
[[90000011, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rain時々雨ノチ雨",
|
||||
difficulty: 9,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2650000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[30000050, 2, 0]],
|
||||
[[80000123, 2, 0]],
|
||||
[[50000092, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "心に残った曲",
|
||||
difficulty: 7,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2700000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[80000136, 0, 0], [80000136, 1, 0], [80000136, 2, 0]],
|
||||
[[20000038, 0, 0], [20000038, 1, 0], [20000038, 2, 0]],
|
||||
[[60000065, 0, 0], [60000065, 1, 0], [70000084, 1, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "黒船来航",
|
||||
difficulty: 7,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 850000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000086, 2, 0], [60000066, 2, 0], [80000040, 1, 0]],
|
||||
[[50000096, 2, 0], [80000048, 2, 0]],
|
||||
[[50000091, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【五段】濁流を乗り越えて",
|
||||
difficulty: 7,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2650000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000343, 2, 0], [60000060, 2, 0], [60000071, 2, 0]],
|
||||
[[60000027, 2, 0], [80000048, 2, 0]],
|
||||
[[20000038, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "のんびり。ゆったり。ほがらかに。",
|
||||
difficulty: 8,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 950000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[40000154, 2, 0], [80000124, 2, 0], [90000139, 2, 0]],
|
||||
[[60000048, 2, 0], [80000041, 2, 0]],
|
||||
[[90000050, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "海・KOI・スィニョーレ!!",
|
||||
difficulty: 8,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2650000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000201, 2, 0]],
|
||||
[[50000339, 2, 0]],
|
||||
[[50000038, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【六段】電柱を見ると思出す",
|
||||
difficulty: 9,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2750000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000288, 2, 0], [80000046, 2, 0], [80001008, 2, 0]],
|
||||
[[50000207, 2, 0], [70000117, 2, 0]],
|
||||
[[30000048, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "コクがある曲",
|
||||
difficulty: 12,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2400000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000139, 0, 0], [50000139, 1, 0], [50000139, 2, 0]],
|
||||
[[90000002, 0, 0], [90000002, 1, 0], [90000002, 2, 0]],
|
||||
[[50000060, 0, 0], [50000060, 1, 0], [50000060, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "超フェスタ!",
|
||||
difficulty: 10,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 930000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[70000076, 2, 0], [70000077, 2, 0]],
|
||||
[[20000038, 2, 0], [40000160, 2, 0]],
|
||||
[[70000145, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【七段】操り人形はほくそ笑む",
|
||||
difficulty: 10,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2800000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[70000006, 2, 0], [70000171, 2, 0], [80000003, 2, 0]],
|
||||
[[50000078, 2, 0], [50000324, 2, 0]],
|
||||
[[80000118, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "絶体絶命スリーチャレンジ!",
|
||||
difficulty: 11,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_HAZARD,
|
||||
score: 0,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_FC3,
|
||||
tune_list: [
|
||||
[[50000238, 2, 0], [70000003, 2, 0], [90000051, 1, 0]],
|
||||
[[50000027, 2, 0], [50000387, 2, 0]],
|
||||
[[80000056, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "天国の舞踏会",
|
||||
difficulty: 11,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2800000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[60000065, 1, 0]],
|
||||
[[80001007, 2, 0]],
|
||||
[[90001007, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【八段】山の賽子",
|
||||
difficulty: 12,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2820000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000200, 2, 0], [50000291, 2, 0], [60000003, 2, 0]],
|
||||
[[50000129, 2, 0], [80000021, 2, 0]],
|
||||
[[80000087, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "The 8th KAC 個人部門",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000052, 2, 0]],
|
||||
[[90000013, 2, 0]],
|
||||
[[70000167, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "The 8th KAC 団体部門",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000009, 2, 0]],
|
||||
[[80000133, 2, 0]],
|
||||
[[80000101, 2, 0]],
|
||||
],
|
||||
},/*
|
||||
{
|
||||
name: "BEMANI MASTER KOREA 2019",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000003, 2, 0]],
|
||||
[[80000090, 2, 0]],
|
||||
[[90000009, 2, 0]],
|
||||
],
|
||||
},*/
|
||||
{
|
||||
name: "The 9th KAC 1st Stage 個人部門",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000125, 2, 0]],
|
||||
[[60000065, 2, 0]],
|
||||
[[90000023, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "The 9th KAC 1st Stage 団体部門",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000125, 2, 0]],
|
||||
[[50000135, 2, 0]],
|
||||
[[90000045, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "The 9th KAC 2nd Stage 個人部門",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000095, 2, 0]],
|
||||
[[80000085, 2, 0]],
|
||||
[[80000090, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "The 9th KAC 2nd Stage 団体部門",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000113, 2, 0]],
|
||||
[[50000344, 2, 0]],
|
||||
[[90000096, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "The 10th KAC 1st Stage",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000003, 2, 0]],
|
||||
[[90000151, 2, 0]],
|
||||
[[90000174, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "The 10th KAC 2nd Stage",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000121, 2, 0]],
|
||||
[[90000113, 2, 0]],
|
||||
[[90000124, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "どうやって押してる?",
|
||||
difficulty: 13,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2600000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[40000127, 0, 0]],
|
||||
[[50000123, 0, 0]],
|
||||
[[50000126, 0, 0]],
|
||||
],
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: "BEMANI MASTER KOREA 2021",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 700000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000180, 2, 0]],
|
||||
[[90000095, 2, 0]],
|
||||
[[90000047, 2, 0]],
|
||||
],
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: "初めてのHARD MODE再び",
|
||||
difficulty: 13,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2750000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000096, 2, 0], [50000263, 2, 0], [80000119, 2, 0]],
|
||||
[[60000021, 2, 0], [60000075, 2, 0]],
|
||||
[[60000039, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【九段】2人からの挑戦状",
|
||||
difficulty: 13,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2830000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000023, 2, 0], [80000025, 2, 0], [80000106, 2, 0]],
|
||||
[[50000124, 2, 0], [80000082, 2, 0]],
|
||||
[[60000115, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "天空の庭 太陽の園",
|
||||
difficulty: 13,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 965000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[40000153, 2, 0]],
|
||||
[[80000007, 2, 0]],
|
||||
[[70000173, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "緊急!迅速!大混乱!",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2900000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[20000040, 2, 0], [50000244, 2, 0], [60000074, 2, 0]],
|
||||
[[40000152, 2, 0], [50000158, 2, 0]],
|
||||
[[40000057, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【十段】時の超越者",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2820000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[20000051, 2, 0], [50000249, 2, 0], [70000145, 2, 0]],
|
||||
[[40000046, 2, 0], [50000180, 2, 0]],
|
||||
[[50000134, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "jubeat大回顧展 ROOM 10",
|
||||
difficulty: 13,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2850000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[30000127, 2, 1]],
|
||||
[[60000078, 2, 1]],
|
||||
[[90000047, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【伝導】10代目最強に挑戦!",
|
||||
difficulty: 14,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2998179,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000100, 2, 0]],
|
||||
[[90000047, 2, 0]],
|
||||
[[90000057, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "あなたのjubeatはどこから?",
|
||||
difficulty: 15,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2900000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[10000065, 0, 0], [10000065, 1, 0], [10000065, 2, 0]],
|
||||
[[30000048, 0, 0], [30000048, 1, 0], [30000048, 2, 0]],
|
||||
[[90000047, 0, 0], [90000047, 1, 0], [90000047, 2, 0]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【皆伝】甘味なのに甘くない",
|
||||
difficulty: 15,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2850000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[90000010, 2, 1]],
|
||||
[[80000101, 2, 1]],
|
||||
[[50000102, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【伝導】真の青が魅せた空",
|
||||
difficulty: 15,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_SCORE,
|
||||
score: 970000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000332, 2, 0]],
|
||||
[[70000098, 2, 0]],
|
||||
[[90001005, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "豪華絢爛高揚絶頂",
|
||||
difficulty: 16,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2960000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[10000065, 2, 1]],
|
||||
[[50000323, 2, 1]],
|
||||
[[50000208, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "絢爛豪華激情無常",
|
||||
difficulty: 16,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2960000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[60000010, 2, 1]],
|
||||
[[70000110, 2, 1]],
|
||||
[[90000047, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【指神】王の降臨",
|
||||
difficulty: 16,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2980000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[70000094, 2, 1]],
|
||||
[[80000088, 2, 1]],
|
||||
[[70000110, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "【伝導】1116全てを超越した日",
|
||||
difficulty: 16,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2975000,
|
||||
is_hard: true,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000208, 2, 0]],
|
||||
[[80000050, 2, 0]],
|
||||
[[90000057, 2, 1]],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "My Top9 Fav Songs",
|
||||
difficulty: 7,
|
||||
course_type: COURSE_TYPE_PERMANENT,
|
||||
etime: 0,
|
||||
clear_type: COURSE_CLEAR_COMBINED_SCORE,
|
||||
score: 2700000,
|
||||
is_hard: false,
|
||||
hazard_type: COURSE_HAZARD_NONE,
|
||||
tune_list: [
|
||||
[[50000049, 2, 1], [50000101, 2, 1], [80000136, 2, 1]],
|
||||
[[80000084, 2, 1], [50000071, 2, 1], [50000084, 2, 1]],
|
||||
[[60000009, 2, 1], [50000024, 2, 1], [90000173, 2, 1]],
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -1,141 +0,0 @@
|
||||
import {emoList, shopList, FestoCourse, courseCategories} from "../static/data"
|
||||
|
||||
/*
|
||||
if pos_index is not (1230 ~ 1236 or 1204 ~ 1205) and pos_index > 1200:
|
||||
then all festo songs
|
||||
*/
|
||||
var pick_up_array = new Array(64).fill(-1);
|
||||
for(var i=0; i<=36; i++){
|
||||
pick_up_array[i] = 0;
|
||||
}
|
||||
pick_up_array[37] = -3211264;
|
||||
pick_up_array[38] = -2080769;
|
||||
|
||||
module.exports = () => ({
|
||||
info: {
|
||||
white_music_list: K.ARRAY("s32", new Array(64).fill(-1)),
|
||||
white_marker_list: K.ARRAY("s32", new Array(16).fill(-1)),
|
||||
white_theme_list: K.ARRAY("s32", new Array(16).fill(-1)),
|
||||
open_music_list: K.ARRAY("s32", new Array(64).fill(-1)),
|
||||
add_default_music_list: K.ARRAY("s32", new Array(64).fill(-1)),
|
||||
hot_music_list: K.ARRAY("s32", pick_up_array),
|
||||
|
||||
expert_option: {
|
||||
is_available: K.ITEM("bool", true),
|
||||
},
|
||||
|
||||
konami_logo_50th: {
|
||||
is_available: K.ITEM("bool", true),
|
||||
},
|
||||
|
||||
all_music_matching: {
|
||||
is_available: K.ITEM("bool", false),
|
||||
},
|
||||
|
||||
tsumtsum: {
|
||||
is_available: K.ITEM("bool", false),
|
||||
},
|
||||
|
||||
nagatanien: {
|
||||
is_available: K.ITEM("bool", false),
|
||||
},
|
||||
|
||||
digdig: {
|
||||
stage_list: {
|
||||
stage: [
|
||||
K.ATTR({ number: "1" }, { state: K.ITEM("u8", 1) }),
|
||||
K.ATTR({ number: "2" }, { state: K.ITEM("u8", 1) }),
|
||||
K.ATTR({ number: "3" }, { state: K.ITEM("u8", 1) }),
|
||||
K.ATTR({ number: "4" }, { state: K.ITEM("u8", 1) }),
|
||||
K.ATTR({ number: "5" }, { state: K.ITEM("u8", 1) }),
|
||||
K.ATTR({ number: "6" }, { state: K.ITEM("u8", 1) }),
|
||||
K.ATTR({ number: "7" }, { state: K.ITEM("u8", 1) }),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
department: {
|
||||
shop_list: {
|
||||
shop: shopList.map((shop, i) =>
|
||||
K.ATTR(
|
||||
{ id: String(i + 1) },
|
||||
{
|
||||
tex_id: K.ITEM("s32", shop.tex_id),
|
||||
type: K.ITEM("s8", shop.type),
|
||||
emo_id: K.ITEM("s32", shop.emo_id),
|
||||
priority: K.ITEM("s32", shop.priority),
|
||||
etime: K.ITEM("u64", BigInt(0)),
|
||||
item_list: { item: [] },
|
||||
}
|
||||
)
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
course_list: {
|
||||
course: FestoCourse.map((course, i) =>
|
||||
K.ATTR(
|
||||
{
|
||||
release_code: "2022052400",
|
||||
version_id: "0",
|
||||
id: String(i + 1),
|
||||
course_type: String(course.course_type),
|
||||
},
|
||||
{
|
||||
difficulty: K.ITEM("s32", course.difficulty),
|
||||
etime: K.ITEM("u64", BigInt(course.etime)),
|
||||
name: K.ITEM("str", course.name),
|
||||
|
||||
tune_list: {
|
||||
tune: course.tune_list.map((tune, i) =>
|
||||
K.ATTR(
|
||||
{ no: String(i + 1) },
|
||||
{
|
||||
seq_list: {
|
||||
seq: tune.map((seq) => ({
|
||||
music_id: K.ITEM("s32", seq[0]),
|
||||
difficulty: K.ITEM("s32", seq[1]),
|
||||
is_secret: K.ITEM("bool", seq[2]),
|
||||
})),
|
||||
},
|
||||
}
|
||||
)
|
||||
),
|
||||
},
|
||||
clear: K.ATTR({type:String(course.clear_type)},{
|
||||
ex_option:{
|
||||
is_hard: K.ITEM("bool", course.is_hard),
|
||||
hazard_type: K.ITEM("s32", course.hazard_type),
|
||||
},
|
||||
score: K.ITEM("s32", course.score),
|
||||
reward_list:[],
|
||||
})
|
||||
}
|
||||
)
|
||||
),
|
||||
category_list: {
|
||||
category: courseCategories.map((categorie, i) =>
|
||||
K.ATTR(
|
||||
{ id: String(i + 1)},
|
||||
{
|
||||
is_secret: K.ITEM("bool", false),
|
||||
level_min: K.ITEM("s32", categorie[0]),
|
||||
level_max: K.ITEM("s32", categorie[1]),
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
},
|
||||
emo_list: {
|
||||
emo: emoList.map((emo, i) =>
|
||||
K.ATTR(
|
||||
{ id: String(i + 1) },
|
||||
{
|
||||
tex_id: K.ITEM("s32", emo.tex_id),
|
||||
is_exchange: K.ITEM("bool", emo.is_exchange),
|
||||
}
|
||||
)
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,441 +0,0 @@
|
||||
import Profile from "../models/profile";
|
||||
import {Course} from "../models/course";
|
||||
import {emoList, shopList, FestoCourse, courseCategories, COURSE_STATUS} from "../static/data"
|
||||
|
||||
module.exports = async (data: Profile) => ({
|
||||
info: {
|
||||
tune_cnt: K.ITEM("s32", data?.tuneCount || 0),
|
||||
save_cnt: K.ITEM("s32", data?.saveCount || 0),
|
||||
saved_cnt: K.ITEM("s32", data?.savedCount || 0),
|
||||
fc_cnt: K.ITEM("s32", data?.fcCount || 0),
|
||||
ex_cnt: K.ITEM("s32", data?.exCount || 0),
|
||||
clear_cnt: K.ITEM("s32", data?.clearCount || 0),
|
||||
match_cnt: K.ITEM("s32", data?.matchCount || 0),
|
||||
beat_cnt: K.ITEM("s32", 0),
|
||||
mynews_cnt: K.ITEM("s32", 0),
|
||||
mtg_entry_cnt: K.ITEM("s32", 0),
|
||||
mtg_hold_cnt: K.ITEM("s32", 0),
|
||||
mtg_result: K.ITEM("u8", 0),
|
||||
bonus_tune_points: K.ITEM("s32", data?.bonusPoints || 0),
|
||||
is_bonus_tune_played: K.ITEM("bool", data?.isBonusPlayed || false),
|
||||
last_play_time: K.ITEM("s64", data?.lastPlayTime || 0),
|
||||
},
|
||||
|
||||
last: {
|
||||
play_time: K.ITEM("s64", data?.lastPlayTime || 0),
|
||||
shopname: K.ITEM("str", data.lastShopname),
|
||||
areaname: K.ITEM("str", data.lastAreaname),
|
||||
music_id: K.ITEM("s32", data.musicId || 0),
|
||||
seq_id: K.ITEM("s8", data.seqId || 0),
|
||||
seq_edit_id: K.ITEM("str", data.seqEditId || ""),
|
||||
sort: K.ITEM("s8", data?.sort || 0),
|
||||
category: K.ITEM("s8", data?.category || 0),
|
||||
expert_option: K.ITEM("s8", data?.expertOption || 0),
|
||||
dig_select: K.ITEM("s32", 0),
|
||||
|
||||
settings: {
|
||||
marker: K.ITEM("s8", data?.marker || 0),
|
||||
theme: K.ITEM("s8", data?.theme || 0),
|
||||
title: K.ITEM("s16", data?.title || 0),
|
||||
parts: K.ITEM("s16", data?.parts || 0),
|
||||
rank_sort: K.ITEM("s8", data?.rankSort || 0),
|
||||
combo_disp: K.ITEM("s8", data?.comboDisp || 0),
|
||||
emblem: K.ARRAY("s16", data?.emblem || [0, 0, 0, 0, 0]),
|
||||
matching: K.ITEM("s8", data?.matching || 0),
|
||||
hard: K.ITEM("s8", data?.hard || 0),
|
||||
hazard: K.ITEM("s8", data?.hazard || 0),
|
||||
},
|
||||
},
|
||||
|
||||
item: {
|
||||
music_list: K.ARRAY("s32", new Array(64).fill(-1)),
|
||||
secret_list: K.ARRAY("s32", new Array(64).fill(-1)),
|
||||
theme_list: K.ARRAY("s32", new Array(16).fill(-1)),
|
||||
marker_list: K.ARRAY("s32", new Array(16).fill(-1)),
|
||||
title_list: K.ARRAY("s32", new Array(160).fill(-1)),
|
||||
parts_list: K.ARRAY("s32", data?.partsList || new Array(160).fill(0)),
|
||||
emblem_list: K.ARRAY("s32", new Array(96).fill(-1)),
|
||||
commu_list: K.ARRAY("s32", data?.commuList || new Array(16).fill(0)),
|
||||
new: {
|
||||
secret_list: K.ARRAY("s32", new Array(64).fill(0)),
|
||||
theme_list: K.ARRAY("s32", new Array(16).fill(0)),
|
||||
marker_list: K.ARRAY("s32", new Array(16).fill(0)),
|
||||
},
|
||||
},
|
||||
|
||||
rivallist: {
|
||||
rival: [].map((rival) => ({
|
||||
jid: K.ITEM("s32", rival.jubeatId),
|
||||
name: K.ITEM("str", rival.name),
|
||||
career: {
|
||||
level: K.ITEM("s16", 0),
|
||||
},
|
||||
})),
|
||||
},
|
||||
|
||||
lab_edit_seq: K.ATTR({ count: "0" }, { seq: [] }),
|
||||
fc_challenge: {
|
||||
today: {
|
||||
music_id: K.ITEM("s32", -1),
|
||||
state: K.ITEM("u8", 0),
|
||||
},
|
||||
whim: {
|
||||
music_id: K.ITEM("s32", -1),
|
||||
state: K.ITEM("u8", 0),
|
||||
},
|
||||
},
|
||||
official_news: {
|
||||
news_list: { news: [] },
|
||||
},
|
||||
news: {
|
||||
checked: K.ITEM("s16", 0),
|
||||
checked_flag: K.ITEM("u32", 0),
|
||||
},
|
||||
history: K.ATTR({ count: "0" }, { tune: [] }),
|
||||
free_first_play: {
|
||||
is_available: K.ITEM("bool", data?.isFirstplay || false),
|
||||
},
|
||||
event_info: { event: [] },
|
||||
jbox: {
|
||||
point: K.ITEM("s32", 0),
|
||||
emblem: {
|
||||
normal: { index: K.ITEM("s16", 2) },
|
||||
premium: { index: K.ITEM("s16", 1) },
|
||||
},
|
||||
},
|
||||
new_music: {},
|
||||
navi: {
|
||||
flag: K.ITEM("u64", BigInt(data?.navi || 0)),
|
||||
},
|
||||
gift_list: {},
|
||||
question_list: {},
|
||||
team_battle: {},
|
||||
server: {},
|
||||
course_list: {
|
||||
course: await (async () =>{
|
||||
let courseData = await DB.Find<Course>(data.__refid, { collection: "course" });
|
||||
let courseStatus = {};
|
||||
courseData.forEach(course =>{
|
||||
courseStatus[course.courseId] |= (course.seen ? COURSE_STATUS.SEEN : 0);
|
||||
courseStatus[course.courseId] |= (course.played ? COURSE_STATUS.PLAYED : 0);
|
||||
courseStatus[course.courseId] |= (course.cleared ? COURSE_STATUS.CLEARED : 0);
|
||||
});
|
||||
return FestoCourse.map((course, i) =>
|
||||
K.ATTR({ id: String(i + 1) }, { status: K.ITEM("s8", courseStatus[i+1] || 0) })
|
||||
);
|
||||
})()
|
||||
|
||||
},
|
||||
category_list: {
|
||||
category: courseCategories.map((categorie, i) =>
|
||||
K.ATTR(
|
||||
{ id: String(i + 1)},
|
||||
{
|
||||
is_display: K.ITEM("bool", true),
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
fill_in_category: {
|
||||
no_gray_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
all_yellow_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
full_combo_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
excellent_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
normal: {
|
||||
no_gray_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
all_yellow_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
full_combo_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
excellent_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
},
|
||||
hard: {
|
||||
no_gray_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
all_yellow_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
full_combo_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
excellent_flag_list: K.ARRAY("s32", [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
},
|
||||
},
|
||||
emo_list: {
|
||||
emo: emoList.map((emo, i) => {
|
||||
return K.ATTR({ id: String(i + 1) }, { num: K.ITEM("s32", 0) });
|
||||
}),
|
||||
},
|
||||
eamuse_gift_list: { gift: [] },
|
||||
department: {
|
||||
shop_list: { shop: [] },
|
||||
},
|
||||
|
||||
clan_course_list: {},
|
||||
|
||||
team: K.ATTR(
|
||||
{ id: "0" },
|
||||
{
|
||||
section: K.ITEM("s32", 0),
|
||||
street: K.ITEM("s32", 0),
|
||||
house_number_1: K.ITEM("s32", 0),
|
||||
house_number_2: K.ITEM("s32", 0),
|
||||
|
||||
move: K.ATTR({
|
||||
id: "1",
|
||||
section: "1",
|
||||
street: "1",
|
||||
house_number_1: "1",
|
||||
house_number_2: "1",
|
||||
}),
|
||||
}
|
||||
),
|
||||
|
||||
daily_bonus_list: {},
|
||||
ticket_list: {},
|
||||
|
||||
digdig: {
|
||||
flag: K.ITEM("u64", BigInt(0)),
|
||||
|
||||
main: {
|
||||
stage: K.ATTR(
|
||||
{ number: "0" },
|
||||
{
|
||||
point: K.ITEM("s32", 0),
|
||||
param: K.ITEM("s32", 0),
|
||||
}
|
||||
),
|
||||
},
|
||||
|
||||
eternal: {
|
||||
ratio: K.ITEM("s32", 0),
|
||||
used_point: K.ITEM("s64", BigInt(0)),
|
||||
point: K.ITEM("s64", BigInt(0)),
|
||||
|
||||
cube: {
|
||||
state: K.ITEM("s8", 0),
|
||||
|
||||
item: [],
|
||||
},
|
||||
|
||||
norma: {
|
||||
till_time: K.ITEM("s64", BigInt(0)),
|
||||
kind: K.ITEM("s32", 0),
|
||||
value: K.ITEM("s32", 0),
|
||||
param: K.ITEM("s32", 0),
|
||||
},
|
||||
|
||||
old: {
|
||||
need_point: K.ITEM("s32", 0),
|
||||
point: K.ITEM("s32", 0),
|
||||
excavated_point: K.ITEM("s32", 0),
|
||||
excavated: K.ITEM("s32", 0),
|
||||
param: K.ITEM("s32", 0),
|
||||
|
||||
music_list: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
unlock: {},
|
||||
|
||||
generic_dig: {},
|
||||
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
# Metal Gear Arcade
|
||||
|
||||
Plugin Version: **v1.0.0**
|
||||
|
||||
Important : require minimum Asphyxia Core **v1.40c**
|
||||
|
||||
## Changelog
|
||||
|
||||
#### 1.0.0
|
||||
Initial Release.
|
||||
|
||||
## Known limitations
|
||||
* No network/online capabilities
|
||||
@@ -1,92 +0,0 @@
|
||||
interface PlayerData {
|
||||
collection: 'data',
|
||||
str: string[],
|
||||
bin: string[]
|
||||
}
|
||||
|
||||
export function register() {
|
||||
R.GameCode('I36');
|
||||
|
||||
R.Route(`eventlog.write`, async (req, data, send) => {
|
||||
// Don't save event log.
|
||||
send.object({
|
||||
gamesession: K.ITEM('s64', BigInt(1)),
|
||||
logsendflg: K.ITEM('s32', 0),
|
||||
logerrlevel: K.ITEM('s32', 0),
|
||||
evtidnosendflg: K.ITEM('s32', 0),
|
||||
})
|
||||
});
|
||||
|
||||
R.Route(`system.getmaster`, async (req, data, send) => {
|
||||
// Called at game startup
|
||||
// Unlock all contents
|
||||
send.object({
|
||||
result: K.ITEM('s32', 1),
|
||||
strdata1: K.ITEM('str', Buffer.from('2011081000:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1', 'utf-8').toString('base64')),
|
||||
strdata2: K.ITEM('str', Buffer.from('1,1,1,1,1,1,1,1,1,1,1,1,1,1', 'utf-8').toString('base64')),
|
||||
updatedate: K.ITEM('u64', BigInt('1120367223')),
|
||||
})
|
||||
});
|
||||
|
||||
R.Route(`playerdata.usergamedata_send`, async (req, data, send) => {
|
||||
// Save user data
|
||||
const refid = $(data).element('data').str('eaid');
|
||||
const datanum = $(data).element('data').number('datanum');
|
||||
let playerData: PlayerData = {
|
||||
collection: 'data',
|
||||
str: [],
|
||||
bin: []
|
||||
};
|
||||
|
||||
const record = $(data).element('data').element('record').obj;
|
||||
|
||||
for (let i = 0; i < datanum; i++) {
|
||||
playerData.str[i] = Buffer.from(_.get(record.d[i], '@content'), 'base64').toString('utf-8');
|
||||
playerData.bin[i] = _.get(record.d[i].bin1, '@content');
|
||||
}
|
||||
|
||||
DB.Upsert(refid, { collection: 'data' }, playerData);
|
||||
|
||||
send.object({
|
||||
result: K.ITEM('s32', 0),
|
||||
});
|
||||
});
|
||||
|
||||
R.Route(`playerdata.usergamedata_recv`, async (req, data, send) => {
|
||||
// Load user data
|
||||
const refid = $(data).element('data').str('eaid');
|
||||
|
||||
const playerData = await DB.FindOne<PlayerData>(refid, { collection: 'data' });
|
||||
|
||||
let player = {
|
||||
record_num: K.ITEM('u32', playerData.str.length),
|
||||
record: {
|
||||
d: []
|
||||
}
|
||||
};
|
||||
|
||||
for(let i = 0; i < playerData.str.length; i++) {
|
||||
// Remove the 2 firsts elements of player data
|
||||
let data = playerData.str[i].split(',');
|
||||
|
||||
player.record.d[i] = K.ITEM('str', Buffer.from(data.slice(2).join(','), 'utf-8').toString('base64') + playerData.bin[i]);
|
||||
player.record.d[i].bin1 = K.ITEM('str', playerData.bin[i]);
|
||||
}
|
||||
|
||||
send.object({
|
||||
player,
|
||||
result: K.ITEM('s32', 0)
|
||||
});
|
||||
});
|
||||
|
||||
R.Route(`playerdata.usergamedata_scorerank`, async (req, data, send) => {
|
||||
// Not implemented
|
||||
send.object({
|
||||
result: K.ITEM('s32', 0)
|
||||
});
|
||||
});
|
||||
|
||||
R.Unhandled((req: EamuseInfo, data: any, send: EamuseSend) => {
|
||||
return send.success();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import { Difficulty, Rank, ClearKind } from '../models/score'
|
||||
|
||||
export interface RivalWorld {
|
||||
collection: "rival3";
|
||||
|
||||
slot: number;
|
||||
rivalCode: number;
|
||||
}
|
||||
|
||||
export interface ScoreWorld {
|
||||
collection: "score3";
|
||||
|
||||
scoreId: number;
|
||||
songId: number;
|
||||
style: number;
|
||||
difficulty: Difficulty;
|
||||
ghostId: number;
|
||||
rank: Rank;
|
||||
clearKind: ClearKind;
|
||||
score: number;
|
||||
exScore: number;
|
||||
maxCombo: number;
|
||||
flareForce: number;
|
||||
}
|
||||
|
||||
export interface HiScoreWorld {
|
||||
collection: "hiscore3";
|
||||
|
||||
slot: number;
|
||||
country: string;
|
||||
region: string;
|
||||
customerCode: string;
|
||||
companyCode: string;
|
||||
locationId: string;
|
||||
pcbid: string;
|
||||
songId: number;
|
||||
style: number;
|
||||
difficulty: number;
|
||||
dancerName: string;
|
||||
score: number;
|
||||
ghostId: number;
|
||||
}
|
||||
|
||||
export interface GhostWorld {
|
||||
collection: "ghost3"
|
||||
|
||||
ghostId: number;
|
||||
ghostSize: number;
|
||||
ghost: string;
|
||||
}
|
||||
|
||||
export interface EventWorld {
|
||||
collection: "event3";
|
||||
|
||||
eventId: number;
|
||||
eventNo: number;
|
||||
eventType: number;
|
||||
compTime: number;
|
||||
saveData: number;
|
||||
}
|
||||
|
||||
export interface ProfileWorld {
|
||||
collection: "profile3";
|
||||
|
||||
ddrCode: number;
|
||||
dancerName: string;
|
||||
area: number;
|
||||
extraStar: number;
|
||||
playCount: number;
|
||||
weight: number;
|
||||
todayCal: number;
|
||||
isDispWeight: boolean;
|
||||
prePlayableNum: number;
|
||||
|
||||
opHispeed: number;
|
||||
opGauge: number;
|
||||
opFastSlow: number;
|
||||
opGuideline: number;
|
||||
opStepZone: number;
|
||||
opTimingDisp: number;
|
||||
opVisibility: number;
|
||||
opVisibleTime: number;
|
||||
opLane: number;
|
||||
opLaneHiddenPos: number;
|
||||
opLaneSuddenPos: number;
|
||||
opLaneHidSudPos: number;
|
||||
opLaneFilter: number;
|
||||
opScrollDirection: number;
|
||||
opScrollMoving: number;
|
||||
opArrowPriority: number;
|
||||
opArrowPlacement: number;
|
||||
opArrowColor: number;
|
||||
opArrowDesign: number;
|
||||
opCutTiming: number;
|
||||
opCutFreeze: number;
|
||||
opCutJump: number;
|
||||
|
||||
lpMode: number;
|
||||
lpFolder: number;
|
||||
lpMcode: number;
|
||||
lpStyle: number;
|
||||
lpDifficulty: number;
|
||||
lpWindowMain: number;
|
||||
lpWindowSub: number;
|
||||
lpTarget: number;
|
||||
lpTabMain: number;
|
||||
lpTabSub: number;
|
||||
|
||||
fsTitle: number;
|
||||
fsVersion: number;
|
||||
fsGenre: number;
|
||||
fsBpm: number;
|
||||
fsEvent: number;
|
||||
fsLevel: number;
|
||||
fsFlareRank: number;
|
||||
fsClearRank: number;
|
||||
fsFlareSkillTarget: number;
|
||||
fsRivalFlareSkill: number;
|
||||
fsRivalScoreRank: number;
|
||||
fsSortType: number;
|
||||
fsOrderType: number;
|
||||
|
||||
cgTipsBasic: number;
|
||||
cgTipsOption: number;
|
||||
cgTipsEvent: number;
|
||||
cgTipsGimmick: number;
|
||||
cgTipsAdvance: number;
|
||||
cgGuideScene: number;
|
||||
|
||||
|
||||
|
||||
singleGrade?: number;
|
||||
doubleGrade?: number;
|
||||
|
||||
events?: {};
|
||||
|
||||
usergamedata?: {
|
||||
COMMON?: {
|
||||
strdata?: string;
|
||||
bindata?: string;
|
||||
};
|
||||
OPTION?: {
|
||||
strdata?: string;
|
||||
bindata?: string;
|
||||
};
|
||||
LAST?: {
|
||||
strdata?: string;
|
||||
bindata?: string;
|
||||
};
|
||||
RIVAL?: {
|
||||
strdata?: string;
|
||||
bindata?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -30,10 +30,16 @@ export enum Rank {
|
||||
}
|
||||
|
||||
export enum ClearKind {
|
||||
NONE = 6,
|
||||
NONE,
|
||||
FAILED,
|
||||
ASSIST_CLEAR,
|
||||
CLEAR,
|
||||
LIFE4_1,
|
||||
LIFE4_2,
|
||||
LIFE4,
|
||||
GOOD_COMBO,
|
||||
GREAT_COMBO,
|
||||
PERPECT_COMBO,
|
||||
PERFECT_COMBO,
|
||||
MARVELOUS_COMBO
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
MUSECA
|
||||
======
|
||||
|
||||
Plugin Version: **v1.0.0**
|
||||
|
||||
Supported Versions
|
||||
------------------
|
||||
- 1+1/2
|
||||
- [MUSECA PLUS](https://museca.plus/) (2020-11-27)
|
||||
|
||||
|
||||
For who plays MUSECA PLUS
|
||||
-------------------------
|
||||
If you have a version that not supported on plugin, try Custom MDB feature.
|
||||
|
||||
The mdb file is located on `museca-plus/museca/xml/music-info-b.xml`
|
||||
|
||||
Only Initial support for now.
|
||||
-----------------------------
|
||||
Course is not implemented yet.
|
||||
Also, Score-Save is only proofed for working correctly. I didn't tested other features. sorry!
|
||||
|
||||
Changelog
|
||||
=========
|
||||
1.0.0 (Current)
|
||||
---------------
|
||||
Initial Support.
|
||||
@@ -1 +0,0 @@
|
||||
*.xml
|
||||
@@ -1,12 +0,0 @@
|
||||
import { processMdbData,readJSONOrXML } from './helper';
|
||||
|
||||
export async function processData() {
|
||||
const { music } = await readJSONOrXML("./data/mdb_community_plus.json", "./data/mdb_community_plus.xml")
|
||||
return {
|
||||
music
|
||||
};
|
||||
}
|
||||
|
||||
export async function processRawData() {
|
||||
return await processMdbData("./data/mdb_community_plus.xml")
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { processMdbData,readJSONOrXML } from './helper';
|
||||
|
||||
export async function processData() {
|
||||
const { music } = await readJSONOrXML("./data/mdb_one_plus_half.json", "./data/mdb_one_plus_half.xml")
|
||||
return {
|
||||
music
|
||||
};
|
||||
}
|
||||
|
||||
export async function processRawData() {
|
||||
return await processMdbData("./data/mdb_one_plus_half.xml");
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
export interface CommonMusicDataField {
|
||||
music_id: KITEM<"s32">;
|
||||
music_type: KITEM<"u8">;
|
||||
limited: KITEM<"u8">;
|
||||
}
|
||||
|
||||
interface CommonMusicData {
|
||||
music: CommonMusicDataField[]
|
||||
}
|
||||
|
||||
export async function readXML(path: string) {
|
||||
const xml = await IO.ReadFile(path, 'utf-8');
|
||||
const json = U.parseXML(xml, false)
|
||||
return json
|
||||
}
|
||||
|
||||
export async function readJSON(path: string) {
|
||||
const str = await IO.ReadFile(path, 'utf-8');
|
||||
const json = JSON.parse(str)
|
||||
return json
|
||||
}
|
||||
|
||||
export async function readJSONOrXML(jsonPath: string, xmlPath: string): Promise<CommonMusicData> {
|
||||
const str: string | null = await IO.ReadFile(jsonPath, 'utf-8');
|
||||
if (str == null || str.length == 0) {
|
||||
const data = await processMdbData(xmlPath)
|
||||
await IO.WriteFile(jsonPath, JSON.stringify(data))
|
||||
return data
|
||||
} else {
|
||||
const json = JSON.parse(str)
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
export async function processMdbData(path: string): Promise<CommonMusicData> {
|
||||
const data = await readXML(path);
|
||||
const mdb = $(data).elements("mdb.music");
|
||||
const diff_list = ["novice", "advanced", "exhaust", "infinite"]
|
||||
const music: CommonMusicDataField[] = [];
|
||||
for (const m of mdb) {
|
||||
for (const [i, d] of diff_list.entries()) {
|
||||
const elem = m.element(`difficulty.${d}`)
|
||||
if (elem.number("difnum", 0) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
music.push({
|
||||
music_id: K.ITEM("s32", parseInt(m.attr().id)),
|
||||
music_type: K.ITEM("u8", i),
|
||||
limited: K.ITEM("u8", elem.number("limited"))
|
||||
});
|
||||
}
|
||||
};
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,88 +0,0 @@
|
||||
import * as path from "path"
|
||||
import { processMdbData } from "../data/helper"
|
||||
import { processData as processCommunityPlusData } from "../data/CommunityPlusMDB"
|
||||
import { processData as processOnePlusHalfData } from "../data/OnePlusHalfMDB"
|
||||
|
||||
|
||||
export const shop: EPR = async (info, data, send) => {
|
||||
// Ignore shop name setter.
|
||||
send.object({
|
||||
nxt_time: K.ITEM("u32", 1000 * 5 * 60)
|
||||
})
|
||||
}
|
||||
|
||||
export const common: EPR = async (info, data, send) => {
|
||||
let { music } = U.GetConfig("enable_custom_mdb")
|
||||
? await processCustomData()
|
||||
: (await processValidData(info))
|
||||
|
||||
if (music.length === 0) {
|
||||
music = (await processValidData(info)).music
|
||||
}
|
||||
|
||||
if (U.GetConfig("unlock_all_songs")) {
|
||||
music.forEach(element => {
|
||||
element.limited = K.ITEM("u8", 3)
|
||||
});
|
||||
}
|
||||
|
||||
// Flags
|
||||
const event_list = [1, 83, 130, 194, 195, 98, 145, 146, 147, 148, 149, 56, 86, 105, 140, 211, 143]
|
||||
const event = {
|
||||
info: event_list.map((e) => {
|
||||
return {
|
||||
event_id: K.ITEM("u32", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
send.object({
|
||||
music_limited: {
|
||||
info: music
|
||||
},
|
||||
event,
|
||||
// TODO: Skill course, Extended option.
|
||||
// skill_course,
|
||||
// extend
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Implement this.
|
||||
export const hiscore: EPR = async (info, data, send) => {
|
||||
send.success()
|
||||
}
|
||||
|
||||
export const frozen: EPR = async (info, data, send) => {
|
||||
send.object({
|
||||
result: K.ITEM("u8", 0)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Implement this fully.
|
||||
export const lounge: EPR = async (info, data, send) => {
|
||||
send.object({
|
||||
interval: K.ITEM("u32", 10),
|
||||
// wait
|
||||
})
|
||||
}
|
||||
|
||||
export const exception: EPR = async (info, data, send) => {
|
||||
send.success()
|
||||
}
|
||||
|
||||
async function processCustomData() {
|
||||
return processMdbData("data/custom_mdb.xml")
|
||||
}
|
||||
|
||||
async function processValidData(info: EamuseInfo) {
|
||||
const version = parseInt(info.model.trim().substr(10), 10)
|
||||
if (version >= 2020102200) {
|
||||
// MUSECA PLUS
|
||||
processCommunityPlusData();
|
||||
} else /** if (version > 2016071300) */ {
|
||||
return await processOnePlusHalfData()
|
||||
} /** else {
|
||||
// Museca 1
|
||||
return await processOneData()
|
||||
}**/
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user