webui stuff
This commit is contained in:
@@ -14,56 +14,68 @@
|
||||
- Please back up your save data to prevent unwanted issues, especially while using this experimental plugin.
|
||||
- Please please please feel free to open PRs!
|
||||
|
||||
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.)
|
||||
|
||||
|
||||
|
||||
Changelog
|
||||
===========
|
||||
## fork-mdx-3.0.1
|
||||
### fork-mdx-3.0.1
|
||||
|
||||
### New:
|
||||
|
||||
1. Main:
|
||||
- Updated events list & event loading code:
|
||||
- Galaxy Play
|
||||
- EXTRA SAVIOR
|
||||
- MYSTICAL Re:UNION (songs automatically unlocked on login atm)
|
||||
- Attempts to fix ghost data loading:
|
||||
- Added ghost id to score and ghost data
|
||||
- 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 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)
|
||||
- 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
|
||||
### fork-mdx-3.0.0b
|
||||
|
||||
### New:
|
||||
|
||||
1. Main:
|
||||
- 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)
|
||||
- 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
|
||||
### fork-mdx-3.0.0a
|
||||
|
||||
### New:
|
||||
|
||||
1. Main:
|
||||
- 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)
|
||||
- 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:
|
||||
|
||||
To do:
|
||||
===========
|
||||
|
||||
1. Look for more missing features
|
||||
2. Refactor getLastScoreId
|
||||
3. WebUI stuff
|
||||
4. More work on score_str
|
||||
5. Rival load
|
||||
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)
|
||||
|
||||
+21
-6
@@ -5,13 +5,11 @@ export const SONGS_WORLD = [
|
||||
{ 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] },
|
||||
@@ -37,16 +35,25 @@ export const SONGS_WORLD = [
|
||||
{ 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 = [
|
||||
@@ -54,7 +61,6 @@ export const SONGS_OVERRIDE_WORLD = [
|
||||
{ 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] },
|
||||
@@ -198,7 +204,16 @@ export const SONGS_OVERRIDE_WORLD = [
|
||||
{ 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: 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 = [
|
||||
|
||||
+22
-21
@@ -262,7 +262,6 @@ export const playerdatasave: EPR = async (info, data, send) => {
|
||||
})
|
||||
}
|
||||
else if($(data).number("data.savekind") === 2) {
|
||||
console.log(JSON.stringify($(data), null, 4))
|
||||
let hiScoreInfo = {
|
||||
country: $(data).str("data.country"),
|
||||
region: $(data).str("data.region"),
|
||||
@@ -647,26 +646,29 @@ export const playerdataload: EPR = async (info, data, send) => {
|
||||
export const musicdataload: EPR = async (info, data, send) => {
|
||||
// I personally use the last A3 db for this, will check for missing songs
|
||||
let musicList = []
|
||||
if(IO.Exists('data/musicdb.xml')) {
|
||||
let mdb = U.parseXML(U.DecodeString(await IO.ReadFile('data/musicdb.xml'), "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 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)
|
||||
})
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -737,7 +739,6 @@ export const rivaldataload: EPR = async (info, data, send) => {
|
||||
};
|
||||
|
||||
export const ghostdataload: EPR = async (info, data, send) => {
|
||||
// Choosing world, area and machine #1 target scores doesn't seem to call this endpoint yet (or at all)
|
||||
const refid = $(data).str("data.refid");
|
||||
const ghostId = $(data).number("data.ghostid");
|
||||
console.log("loading ghostid: " + ghostId)
|
||||
|
||||
@@ -18,6 +18,18 @@ export function register() {
|
||||
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: ""
|
||||
});
|
||||
|
||||
const RoutePlayerData = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`playerdata.${method}`, handler);
|
||||
@@ -184,30 +196,51 @@ export function register() {
|
||||
});
|
||||
});
|
||||
|
||||
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 = []
|
||||
if(IO.Exists('data/musicdb.xml')) {
|
||||
let mdb = U.parseXML(U.DecodeString(await IO.ReadFile('data/musicdb.xml'), "shift_jis"), false)
|
||||
mdb['mdb']['music'].forEach(music => {
|
||||
if(SONGS_OVERRIDE_WORLD.findIndex(so => so.mcode === $(music).number('mcode')) < 0) {
|
||||
let diffLv = music.diffLv
|
||||
mdbData.push({
|
||||
mcode: $(music).number('mcode'),
|
||||
diffLv: $(music).numbers('diffLv')
|
||||
})
|
||||
}
|
||||
})
|
||||
SONGS_WORLD.concat(SONGS_OVERRIDE_WORLD).forEach(sw => {
|
||||
let mIndex = mdbData.findIndex(m => m.mcode === sw.mcode)
|
||||
if(mIndex > -1) mdbData[mIndex]['diffLv'] = sw.diffLv
|
||||
else {
|
||||
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,
|
||||
diffLv: sw.diffLv
|
||||
title: songTitle,
|
||||
diffLv: sw.diffLv,
|
||||
series: series
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
send.json({mdb: mdbData})
|
||||
})
|
||||
}
|
||||
|
||||
+7
-1
@@ -30,7 +30,13 @@ export enum Rank {
|
||||
}
|
||||
|
||||
export enum ClearKind {
|
||||
NONE = 6,
|
||||
NONE,
|
||||
FAILED,
|
||||
ASSIST_CLEAR,
|
||||
CLEAR,
|
||||
LIFE4_1,
|
||||
LIFE4_2,
|
||||
LIFE4,
|
||||
GOOD_COMBO,
|
||||
GREAT_COMBO,
|
||||
PERFECT_COMBO,
|
||||
|
||||
+19467
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
||||
let clearRank = ['AAA', 'AA+', 'AA', 'AA-', 'A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'E']
|
||||
let clearKind = ['-', 'Fail', 'Assisted Clear', 'Clear', 'Life4 Clear (1)', 'Life4 Clear (2)', 'Life4 Clear', 'FC', 'GFC', 'PFC', 'MFC']
|
||||
let flareRank = ['None', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'EX']
|
||||
|
||||
async function populateScoreTable(scores, style, info) {
|
||||
|
||||
let scoreTableData = []
|
||||
let scoreIndex = 0
|
||||
let mdbData = []
|
||||
await emit('getMDB', {}).then(
|
||||
function(response) {
|
||||
mdbData = response.data.mdb
|
||||
}
|
||||
)
|
||||
scores.forEach(score => {
|
||||
let scoreIndex = scoreTableData.findIndex(scoreData => scoreData.songId === score.songId)
|
||||
if(scoreIndex < 0 && style === score.style && score.songId !== 38707) {
|
||||
let m = mdbData.find(m => m.mcode === score.songId)
|
||||
scoreTableData.push({
|
||||
'songId': score.songId,
|
||||
'songTitle': m.title,
|
||||
'0': '-',
|
||||
'1': '-',
|
||||
'2': '-',
|
||||
'3': '-',
|
||||
'4': '-'
|
||||
})
|
||||
}
|
||||
scoreIndex = scoreTableData.findIndex(scoreData => scoreData.songId === score.songId && style === score.style)
|
||||
if(scoreIndex > -1) {
|
||||
if(info === 0) scoreTableData[scoreIndex][score.difficulty] = score.score
|
||||
if(info === 1) scoreTableData[scoreIndex][score.difficulty] = clearRank[score.rank]
|
||||
if(info === 2) scoreTableData[scoreIndex][score.difficulty] = clearKind[score.clearKind]
|
||||
if(info === 3) scoreTableData[scoreIndex][score.difficulty] = (score.flareForce > -1) ? flareRank[score.flareForce] : '-'
|
||||
}
|
||||
})
|
||||
|
||||
$('#musicdata').DataTable().clear().destroy()
|
||||
$('#musicdata').DataTable({
|
||||
data: scoreTableData,
|
||||
columns: [
|
||||
{ data: 'songTitle' },
|
||||
{ data: '0' },
|
||||
{ data: '1' },
|
||||
{ data: '2' },
|
||||
{ data: '3' },
|
||||
{ data: '4' }
|
||||
],
|
||||
pageLength: 100,
|
||||
columnDefs: [
|
||||
{
|
||||
targets: 0,
|
||||
width: "1px"
|
||||
}
|
||||
],
|
||||
responsive: {
|
||||
details: {
|
||||
display: $.fn.dataTable.Responsive.display.modal({
|
||||
header: function(row) {
|
||||
var data = row.data();
|
||||
return 'Scores for song: ' + data.songId;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
$(document).ready(async function() {
|
||||
let scores = JSON.parse(document.getElementById("score-pass").innerText)
|
||||
populateScoreTable(scores, parseInt($('#style_select').val()), parseInt($('#info_select').val()))
|
||||
|
||||
$('#style_select,#info_select').change(async function() {
|
||||
await populateScoreTable(scores, parseInt($('#style_select').val()), parseInt($('#info_select').val()));
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,182 @@
|
||||
let flareMatrix = {
|
||||
"1": [145, 153, 162, 171, 179, 188, 197, 205, 214, 223, 232],
|
||||
"2": [155, 164, 182, 192, 201, 210, 220, 220, 229, 238, 248],
|
||||
"3": [170, 180, 190, 200, 210, 221, 231, 241, 251, 261, 272],
|
||||
"4": [185, 196, 207, 218, 229, 240, 251, 262, 273, 284, 296],
|
||||
"5": [205, 217, 229, 241, 254, 266, 278, 291, 303, 315, 328],
|
||||
"6": [230, 243, 257, 271, 285, 299, 312, 326, 340, 354, 368],
|
||||
"7": [255, 270, 285, 300, 316, 331, 346, 362, 377, 392, 408],
|
||||
"8": [290, 307, 324, 342, 359, 377, 394, 411, 429, 446, 464],
|
||||
"9": [335, 355, 375, 395, 415, 435, 455, 475, 495, 515, 536],
|
||||
"10": [400, 424, 448, 472, 496, 520, 544, 568, 592, 616, 640],
|
||||
"11": [465, 492, 520, 548, 576, 604, 632, 660, 688, 716, 744],
|
||||
"12": [510, 540, 571, 601, 632, 663, 693, 724, 754, 785, 816],
|
||||
"13": [545, 577, 610, 643, 675, 708, 741, 773, 806, 839, 872],
|
||||
"14": [575, 609, 644, 678, 713, 747, 782, 816, 851, 885, 920],
|
||||
"15": [600, 636, 672, 708, 744, 780, 816, 852, 888, 924, 960],
|
||||
"16": [620, 657, 694, 731, 768, 806, 843, 880, 917, 954, 992],
|
||||
"17": [635, 673, 711, 749, 787, 825, 863, 901, 939, 977, 1016],
|
||||
"18": [650, 689, 728, 767, 806, 845, 884, 923, 962, 1001, 1040],
|
||||
"19": [665, 704, 744, 784, 824, 864, 904, 944, 984, 1024, 1064]
|
||||
}
|
||||
let flareSkill = [
|
||||
["NONE", 0, 500, 1000, 1500],
|
||||
["MERCURY", 2000, 3000, 4000, 5000],
|
||||
["VENUS", 6000, 7000, 8000, 9000],
|
||||
["EARTH", 10000, 11500, 13000, 14500],
|
||||
["MARS", 16000, 18000, 20000, 22000],
|
||||
["JUPITER", 24000, 26500, 29000, 31500],
|
||||
["SATURN", 34000, 36750, 39500, 42250],
|
||||
["URANUS", 45000, 48750, 52500, 56250],
|
||||
["NEPTUNE", 60000, 63750, 67500, 71250],
|
||||
["SUN", 75000, 78750, 82500, 86250],
|
||||
["WORLD", 90000]
|
||||
]
|
||||
let area = ["Unknown", "Hokkaido", "Aomori", "Iwate", "Miyagi", "Akita", "Yamagata", "Fukushima", "Ibaraki", "Tochigi", "Gunma", "Saitama", "Chiba", "Tokyo", "Kanagawa", "Niigata", "Toyama", "Ishikawa", "Fukui", "Yamanashi",
|
||||
"Nagano", "Gifu", "Shizuoka", "Aichi", "Mie", "Shiga", "Kyoto", "Osaka", "Hyogo", "Nara", "Wakayama", "Tottori", "Shimane", "Okayama", "Hiroshima", "Yamaguchi", "Tokushima", "Kagawa", "Ehime", "Kochi", "Fukuoka", "Saga",
|
||||
"Nagasaki", "Kumamoto", "Oita", "Miyazaki", "Kagoshima", "Okinawa", "Hong Kong", "Korea", "Taiwan", "USA", "Europe", "Overseas", "Alaska", "Alabama", "Arkansas", "Arizona", "California", "Colorado", "Connecticut", "Delaware",
|
||||
"Florida", "Georgia", "Hawaii", "Iowa", "Idaho", "Illinois", "Indiana", "Kansas", "Kentucky", "Louisiana", "Massachusetts", "Maryland", "Maine", "Michigan", "Minnesota", "Missouri", "Mississippi", "Montana", "Nebraska",
|
||||
"North Carolina", "North Dakota", "New Hampshire", "New Jersey", "New Mexico", "Nevada", "New York", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah",
|
||||
"Virginia", "Vermont", "Washington", "Wisconsin", "West Virginia", "Wyoming", "Washington D.C.", "Japan", "Canada", "Singapore", "Thailand", "Australia", "New Zealand", "UK", "Italy", "Spain", "Germany", "France", "Portugal", "Indonesia",
|
||||
"Philippines"
|
||||
]
|
||||
|
||||
$(document).ready(async function() {
|
||||
// classic 1-13 white 14-17 gold 18-21
|
||||
let profile = JSON.parse(document.getElementById("profile3-pass").innerText)
|
||||
let scores = JSON.parse(document.getElementById("score-pass").innerText)
|
||||
let mdbData = []
|
||||
let flareTopScores = {'sg': [], 'db': []}
|
||||
let totalSgFlare = 0
|
||||
let totalDbFlare = 0
|
||||
|
||||
let classicSgTop30 = []
|
||||
let classicSgFlare = 0
|
||||
let classicDbTop30 = []
|
||||
let classicDbFlare = 0
|
||||
|
||||
let whiteSgTop30 = []
|
||||
let whiteSgFlare = 0
|
||||
let whiteDbTop30 = []
|
||||
let whiteDbFlare = 0
|
||||
|
||||
let goldSgTop30 = []
|
||||
let goldSgFlare = 0
|
||||
let goldDbTop30 = []
|
||||
let goldDbFlare = 0
|
||||
|
||||
await emit('getMDB', {}).then(
|
||||
function(response) {
|
||||
mdbData = response.data.mdb
|
||||
}
|
||||
)
|
||||
$("#area").append(
|
||||
$("<td>").append(area[profile.area])
|
||||
)
|
||||
if(mdbData !== undefined) {
|
||||
scores.forEach(score => {
|
||||
let songIndex = mdbData.findIndex(m => m.mcode === score.songId)
|
||||
if(songIndex > -1 && score.songId != 38707 && score.flareForce >= 0) {
|
||||
if(score.style === 0) {
|
||||
flareTopScores.sg.push({
|
||||
flare: flareMatrix[mdbData[songIndex]['diffLv'][score.difficulty]][score.flareForce],
|
||||
series: mdbData[songIndex]['series']
|
||||
})
|
||||
} else {
|
||||
flareTopScores.db.push({
|
||||
flare: flareMatrix[mdbData[songIndex]['diffLv'][score.difficulty]][score.flareForce],
|
||||
series: mdbData[songIndex]['series']
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
classicSgTop30 = flareTopScores.sg.filter(ft => ft.series <= 13).sort((a, b) => b.flare - a.flare)
|
||||
if(classicSgTop30.length > 30) classicSgTop30.slice(0, 30)
|
||||
classicSgFlare = classicSgTop30.reduce((n, {flare}) => n + flare, 0)
|
||||
|
||||
whiteSgTop30 = flareTopScores.sg.filter(ft => ft.series >= 14 && ft.series <= 17).sort((a, b) => b.flare - a.flare)
|
||||
if(whiteSgTop30.length > 30) whiteSgTop30.slice(0, 30)
|
||||
whiteSgFlare = whiteSgTop30.reduce((n, {flare}) => n + flare, 0)
|
||||
|
||||
goldSgTop30 = flareTopScores.sg.filter(ft => ft.series >= 18).sort((a, b) => b.flare - a.flare)
|
||||
if(goldSgTop30.length > 30) goldSgTop30.slice(0, 30)
|
||||
goldSgFlare = goldSgTop30.reduce((n, {flare}) => n + flare, 0)
|
||||
|
||||
totalSgFlare = classicSgTop30.concat(whiteSgTop30, goldSgTop30).reduce((n, {flare}) => n + flare, 0)
|
||||
|
||||
|
||||
classicDbTop30 = flareTopScores.db.filter(ft => ft.series <= 13).sort((a, b) => b.flare - a.flare)
|
||||
if(classicDbTop30.length > 30) classicDbTop30.slice(0, 30)
|
||||
classicDbFlare = classicDbTop30.reduce((n, {flare}) => n + flare, 0)
|
||||
|
||||
whiteDbTop30 = flareTopScores.db.filter(ft => ft.series >= 14 && ft.series <= 17).sort((a, b) => b.flare - a.flare)
|
||||
if(whiteDbTop30.length > 30) whiteDbTop30.slice(0, 30)
|
||||
whiteDbFlare = whiteDbTop30.reduce((n, {flare}) => n + flare, 0)
|
||||
|
||||
goldDbTop30 = flareTopScores.db.filter(ft => ft.series >= 18).sort((a, b) => b.flare - a.flare)
|
||||
if(goldDbTop30.length > 30) goldDbTop30.slice(0, 30)
|
||||
goldDbFlare = goldDbTop30.reduce((n, {flare}) => n + flare, 0)
|
||||
|
||||
totalDbFlare = classicDbTop30.concat(whiteDbTop30, goldDbTop30).reduce((n, {flare}) => n + flare, 0)
|
||||
|
||||
let skillGet = false
|
||||
let sgSkill = ''
|
||||
let dbSkill = ''
|
||||
let skillPlus = '+++'
|
||||
flareSkill.forEach(fs => {
|
||||
if(!skillGet) {
|
||||
for(let sctr = 1; sctr < fs.length; sctr++) {
|
||||
if(totalSgFlare >= fs[sctr]) {
|
||||
sgSkill = fs[0]
|
||||
if(sctr > 1) sgSkill = sgSkill + skillPlus.substring(0, sctr - 1)
|
||||
}
|
||||
else skillGet = true
|
||||
}
|
||||
}
|
||||
})
|
||||
console.log(sgSkill + " (" + totalSgFlare + ")")
|
||||
|
||||
skillGet = false
|
||||
flareSkill.forEach(fs => {
|
||||
if(!skillGet) {
|
||||
for(let sctr = 1; sctr < fs.length; sctr++) {
|
||||
if(totalDbFlare >= fs[sctr]) {
|
||||
dbSkill = fs[0]
|
||||
if(sctr > 1) dbSkill = dbSkill + skillPlus.substring(0, sctr - 1)
|
||||
}
|
||||
else skillGet = true
|
||||
}
|
||||
}
|
||||
})
|
||||
console.log(dbSkill + " (" + totalDbFlare + ")")
|
||||
|
||||
$("#skillrank").append(
|
||||
$("<td>").append(sgSkill)
|
||||
).append(
|
||||
$("<td>").append(dbSkill)
|
||||
)
|
||||
|
||||
$("#skilltotal").append(
|
||||
$("<td>").append(totalSgFlare)
|
||||
).append(
|
||||
$("<td>").append(totalDbFlare)
|
||||
)
|
||||
}
|
||||
|
||||
$("#skillclassic").append(
|
||||
$("<td>").append(classicSgFlare + ' (' + classicSgTop30.length + ' / 30)')
|
||||
).append(
|
||||
$("<td>").append(classicDbFlare + ' (' + classicDbTop30.length + ' / 30)')
|
||||
)
|
||||
$("#skillwhite").append(
|
||||
$("<td>").append(whiteSgFlare + ' (' + whiteSgTop30.length + ' / 30)')
|
||||
).append(
|
||||
$("<td>").append(whiteDbFlare + ' (' + whiteDbTop30.length + ' / 30)')
|
||||
)
|
||||
$("#skillgold").append(
|
||||
$("<td>").append(goldSgFlare + ' (' + goldSgTop30.length + ' / 30)')
|
||||
).append(
|
||||
$("<td>").append(goldDbFlare + ' (' + goldDbTop30.length + ' / 30)')
|
||||
)
|
||||
})
|
||||
@@ -12,21 +12,6 @@ $('#change-weight').on('click', () => {
|
||||
emit('updateWeight', { refid, weight }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-name3').on('click', () => {
|
||||
const name = $('#name3').val().toUpperCase();
|
||||
|
||||
emit('updateName3', { refid, name }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-weight3').on('click', () => {
|
||||
const weight1 = $('#weight31').val();
|
||||
const weight2 = $('#weight32').val();
|
||||
const weight = parseInt(weight1 + '.' + weight2);
|
||||
|
||||
emit('updateWeight3', { refid, weight }).then(() => location.reload());
|
||||
});
|
||||
|
||||
|
||||
$('#change-display-calories').on('click', () => {
|
||||
const selected = $('#display_calories option:selected').val();
|
||||
|
||||
@@ -62,3 +47,24 @@ $('#change-display-timing').on('click', () => {
|
||||
|
||||
emit('updateDisplayTiming', { refid, selected }).then(() => location.reload());
|
||||
});
|
||||
|
||||
|
||||
$('#change-name3').on('click', () => {
|
||||
const name = $('#name3').val().toUpperCase();
|
||||
|
||||
emit('updateName3', { refid, name }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-weight3').on('click', () => {
|
||||
const weight1 = $('#weight31').val();
|
||||
const weight2 = $('#weight32').val();
|
||||
const weight = parseInt(weight1 + '.' + weight2);
|
||||
|
||||
emit('updateWeight3', { refid, weight }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-display-calories3').on('click', () => {
|
||||
const selected = parseInt($('#display_calories3 option:selected').val()) !== 0
|
||||
|
||||
emit('updateDisplayCalories3', { refid, selected }).then(() => location.reload());
|
||||
});
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//DATA//
|
||||
profile: DB.FindOne(refid, { collection: "profile" })
|
||||
profile3: DB.FindOne(refid, { collection: "profile3" })
|
||||
score3: DB.Find(refid, { collection: "score3" })
|
||||
|
||||
-
|
||||
|
||||
if profile3
|
||||
div
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-account
|
||||
| Basic Info
|
||||
|
||||
.card-content
|
||||
table.basic_data
|
||||
tr
|
||||
td Dancer Name
|
||||
td #{profile3.dancerName}
|
||||
tr
|
||||
td DDR Code
|
||||
td #{profile3.ddrCode}
|
||||
tr(id="area")
|
||||
td Area
|
||||
tr
|
||||
td Total Plays
|
||||
td #{profile3.playCount}
|
||||
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-fire
|
||||
| Flare Skill
|
||||
|
||||
.card-content
|
||||
table.flare_skill
|
||||
th
|
||||
th SINGLE
|
||||
th DOUBLE
|
||||
tr(id="skillrank")
|
||||
td Rank
|
||||
tr(id="skilltotal")
|
||||
td Total
|
||||
tr(id="skillclassic")
|
||||
td CLASSIC
|
||||
tr(id="skillwhite")
|
||||
td WHITE
|
||||
tr(id="skillgold")
|
||||
td GOLD
|
||||
|
||||
div(hidden id='profile3-pass') !{JSON.stringify(profile3)}
|
||||
div(hidden id='score-pass') !{JSON.stringify(score3)}
|
||||
script(src="static/js/profile_details.js")
|
||||
@@ -0,0 +1,60 @@
|
||||
//DATA//
|
||||
profile3: DB.FindOne(refid, { collection: "profile3" })
|
||||
score3: DB.Find(refid, { collection: "score3" })
|
||||
|
||||
-
|
||||
|
||||
if profile3
|
||||
div
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-filter
|
||||
| Filter Results
|
||||
.card-content
|
||||
.score-filter
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Chart Style
|
||||
.field-body
|
||||
.control
|
||||
.select
|
||||
select(id="style_select" value=0)
|
||||
option(value=0) SINGLE
|
||||
option(value=1) DOUBLE
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Displayed Info
|
||||
.field-body
|
||||
.control
|
||||
.select
|
||||
select(id="info_select" value=0)
|
||||
option(value=0) Score
|
||||
option(value=1) Clear Rank
|
||||
option(value=2) Clear Type
|
||||
option(value=3) Flare Rank
|
||||
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-music
|
||||
| Music Data
|
||||
|
||||
.card-content
|
||||
.score-table
|
||||
table.table(id="musicdata" style="width:100%")
|
||||
thead
|
||||
tr
|
||||
th Song
|
||||
th BEGINNER
|
||||
th BASIC
|
||||
th DIFFICULT
|
||||
th EXPERT
|
||||
th CHALLENGE
|
||||
tbody
|
||||
|
||||
div(hidden id='score-pass') !{JSON.stringify(score3)}
|
||||
script(src="static/js/musicdata.js")
|
||||
script(src="static/js/datatables.js")
|
||||
@@ -168,6 +168,8 @@ else if profile3
|
||||
input.input(type="text", id="name3", pattern="[A-Z]{8}", maxlength=8, value=name3)
|
||||
p.control
|
||||
a.button.is-primary#change-name3 Change
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Weight
|
||||
.field-body
|
||||
@@ -177,4 +179,21 @@ else if profile3
|
||||
p.control
|
||||
a.button.is-primary#change-weight3 Set
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Calorie display in-game
|
||||
.field-body
|
||||
p.control
|
||||
.select
|
||||
select#display_calories3
|
||||
if (profile3.isDispWeight)
|
||||
option(value=0) Off
|
||||
option(value=1, selected) On
|
||||
else
|
||||
option(value=0, selected) Off
|
||||
option(value=1) On
|
||||
p.control
|
||||
a.button.is-primary#change-display-calories3 Change
|
||||
|
||||
|
||||
script(src="static/js/profile_settings.js")
|
||||
|
||||
Reference in New Issue
Block a user