mirror of
https://github.com/asphyxia-core/plugins.git
synced 2026-09-22 22:27:56 +03:00
+9
-1
@@ -1,4 +1,12 @@
|
||||
# Editor configs
|
||||
.vscode
|
||||
|
||||
# External modules
|
||||
node_modules
|
||||
package-lock.json
|
||||
|
||||
# Project files
|
||||
asphyxia-core.d.ts
|
||||
package.json
|
||||
package-lock.json
|
||||
typedoc.json
|
||||
tsconfig.json
|
||||
|
||||
@@ -21,3 +21,5 @@ I'll do my best to merge PR, but please make sure you are submitting code target
|
||||
## 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.
|
||||
|
||||
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.
|
||||
|
||||
Vendored
-1115
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
GITADORA Plugin for Asphyxia-Core
|
||||
=================================
|
||||
This plugin is converted from public-exported Asphyxia's Routes.
|
||||
|
||||
Supported Versions
|
||||
==================
|
||||
- Matixx
|
||||
- Exchain
|
||||
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
v1.0.0 (Current)
|
||||
----------------
|
||||
* Initial release for public
|
||||
@@ -0,0 +1 @@
|
||||
export const PLUGIN_VER = 1;
|
||||
@@ -0,0 +1,5 @@
|
||||
mdb_ex.xml
|
||||
mdb_mt.xml
|
||||
mdb_ex.b64
|
||||
mdb_mt.b64
|
||||
custom_mdb.xml
|
||||
@@ -0,0 +1,58 @@
|
||||
import { CommonMusicData, readJSONOrXML, readXML } from './helper';
|
||||
|
||||
export async function processData() {
|
||||
const { music } = await readJSONOrXML('data/mdb_ex.json', 'data/mdb_ex.xml', processRawData)
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
|
||||
export async function processRawData(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', 0),
|
||||
is_hot: K.ITEM('bool', type == 2 ? 0 : 1),
|
||||
data_ver: K.ITEM('s32', m.number("data_ver")),
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { CommonMusicData, readJSONOrXML, readXML } from './helper';
|
||||
|
||||
export async function processData() {
|
||||
const { music } = await readJSONOrXML('data/mdb_mt.json', 'data/mdb_mt.xml', processRawData)
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
|
||||
export async function processRawData(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', 0),
|
||||
is_hot: K.ITEM('bool', type == 2 ? 0 : 1),
|
||||
data_ver: K.ITEM('s32', m.number("data_ver")),
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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[]
|
||||
}
|
||||
|
||||
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, processHandler: (path: string) => Promise<CommonMusicData>): Promise<CommonMusicData> {
|
||||
if (!IO.Exists(jsonPath)) {
|
||||
const data = await processHandler(xmlPath)
|
||||
await IO.WriteFile(jsonPath, JSON.stringify(data))
|
||||
return data
|
||||
} else {
|
||||
const json = JSON.parse(await IO.ReadFile(jsonPath, 'utf-8'))
|
||||
return json
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,80 @@
|
||||
import { getVersion } from "../utils";
|
||||
import { processData as ExchainMusic } from "../data/Exchain"
|
||||
import { processData as MatixxMusic } from "../data/Matixx"
|
||||
import { CommonMusicDataField, readJSONOrXML, readXML } from "../data/helper";
|
||||
|
||||
export const playableMusic: EPR = async (info, data, send) => {
|
||||
const version = getVersion(info);
|
||||
let music: CommonMusicDataField[] = [];
|
||||
try {
|
||||
if (U.GetConfig("enable_custom_mdb")) {
|
||||
const data = await readXML('data/custom_mdb.xml')
|
||||
const mdb = $(data).elements("mdb.mdb_data");
|
||||
|
||||
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', 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],
|
||||
]),
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
console.error("Fallback: Using default MDB method.")
|
||||
music = [];
|
||||
}
|
||||
|
||||
if (music.length == 0) {
|
||||
if (version == 'exchain') {
|
||||
music = _.get(await ExchainMusic(), 'music', []);
|
||||
} else {
|
||||
music = _.get(await MatixxMusic(), 'music', []);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await send.object({
|
||||
hot: {
|
||||
major: K.ITEM('s32', 1),
|
||||
minor: K.ITEM('s32', 1),
|
||||
},
|
||||
musicinfo: K.ATTR({ nr: `${music.length}` }, {
|
||||
music,
|
||||
}),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
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 addition: any = {
|
||||
monstar_subjugation: {
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
bear_fes: {},
|
||||
};
|
||||
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: {},
|
||||
};
|
||||
} 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),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
await send.object({
|
||||
now_date: K.ITEM('u64', time),
|
||||
extra: {
|
||||
extra_lv: K.ITEM('u8', 10),
|
||||
extramusic: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
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]),
|
||||
},
|
||||
},
|
||||
},
|
||||
...addition,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,993 @@
|
||||
import { PlayerInfo } from "../models/playerinfo";
|
||||
import { Profile } from "../models/profile";
|
||||
import { Record } from "../models/record";
|
||||
import { Extra } from "../models/extra";
|
||||
import { getVersion, isDM } from "../utils";
|
||||
import { Scores } from "../models/scores";
|
||||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export const regist: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('player.refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const no = getPlayerNo(data);
|
||||
const version = getVersion(info);
|
||||
|
||||
const playerInfo = await DB.FindOne<PlayerInfo>(refid, {
|
||||
collection: 'playerinfo',
|
||||
version
|
||||
})
|
||||
|
||||
if (playerInfo) {
|
||||
send.object({
|
||||
player: K.ATTR({ no: `${no}` }, {
|
||||
is_succession: K.ITEM("bool", 0), //FIX THIS with upsert result.
|
||||
did: K.ITEM("s32", playerInfo.id)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
let info = await registerUser(refid, version)
|
||||
send.object({
|
||||
player: K.ATTR({ no: `${no}` }, {
|
||||
is_succession: K.ITEM("bool", 0), //FIX THIS with upsert result.
|
||||
did: K.ITEM("s32", info.id)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const check: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('player.refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const no = getPlayerNo(data);
|
||||
const version = getVersion(info)
|
||||
|
||||
const playerInfo = await DB.FindOne<PlayerInfo>(refid, {
|
||||
collection: 'playerinfo',
|
||||
})
|
||||
|
||||
if (playerInfo) {
|
||||
send.object({
|
||||
player: K.ATTR({ no: `${no}`, state: '2' }, {
|
||||
name: K.ITEM('str', playerInfo.name),
|
||||
charaid: K.ITEM('s32', 0),
|
||||
did: K.ITEM('s32', playerInfo.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),
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
let info = await registerUser(refid, version)
|
||||
send.object({
|
||||
player: K.ATTR({ no: `${no}`, state: '2' }, {
|
||||
name: K.ITEM('str', info.name),
|
||||
charaid: K.ITEM('s32', 0),
|
||||
did: K.ITEM('s32', info.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),
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const getPlayer: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('player.refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const no = getPlayerNo(data);
|
||||
const version = getVersion(info);
|
||||
const time = BigInt(31536000);
|
||||
const dm = isDM(info);
|
||||
|
||||
const name = await DB.FindOne<PlayerInfo>(refid, {
|
||||
collection: 'playerinfo',
|
||||
})
|
||||
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;
|
||||
|
||||
const record: any = {
|
||||
gf: {},
|
||||
dm: {},
|
||||
};
|
||||
for (const mode of ['dm', 'gf']) {
|
||||
let game = mode == 'gf' ? gfProfile : dmProfile;
|
||||
let rec = mode == 'gf' ? gfRecord : dmRecord;
|
||||
record[mode] = {
|
||||
max_record: {
|
||||
skill: K.ITEM('s32', game.max_skill),
|
||||
all_skill: K.ITEM('s32', game.max_all_skill),
|
||||
clear_diff: K.ITEM('s32', game.clear_diff),
|
||||
full_diff: K.ITEM('s32', game.full_diff),
|
||||
exce_diff: K.ITEM('s32', game.exce_diff),
|
||||
clear_music_num: K.ITEM('s32', game.clear_music_num),
|
||||
full_music_num: K.ITEM('s32', game.full_music_num),
|
||||
exce_music_num: K.ITEM('s32', game.exce_music_num),
|
||||
clear_seq_num: K.ITEM('s32', game.clear_seq_num),
|
||||
classic_all_skill: K.ITEM('s32', game.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),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// 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: any[] = [];
|
||||
|
||||
if (_.isArray(name.card)) {
|
||||
for (const item of name.card) {
|
||||
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;
|
||||
}
|
||||
|
||||
sticker.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),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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: {
|
||||
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),
|
||||
},
|
||||
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),
|
||||
},
|
||||
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 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) };
|
||||
}
|
||||
}
|
||||
|
||||
send.object({
|
||||
player: K.ATTR({ 'no': `${no}` }, {
|
||||
now_date: K.ITEM('u64', time),
|
||||
secretmusic: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
seq: K.ITEM('u16', 255),
|
||||
kind: K.ITEM('s32', 40),
|
||||
},
|
||||
},
|
||||
chara_list: {},
|
||||
title_parts: {},
|
||||
information: {
|
||||
info: K.ARRAY('u32', Array(50).fill(0)),
|
||||
},
|
||||
reward: {
|
||||
status: K.ARRAY('u32', Array(50).fill(0)),
|
||||
},
|
||||
rivaldata: {},
|
||||
frienddata: {},
|
||||
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', [0, 0, 0, 0, 0]),
|
||||
skindata: {
|
||||
skin: K.ARRAY('u32', Array(100).fill(-1)),
|
||||
},
|
||||
battledata: {
|
||||
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: {},
|
||||
},
|
||||
is_free_ok: K.ITEM('bool', 0),
|
||||
ranking: {
|
||||
skill: { rank: K.ITEM('s32', 1), total_nr: K.ITEM('s32', 1) },
|
||||
all_skill: { rank: K.ITEM('s32', 1), total_nr: K.ITEM('s32', 1) },
|
||||
},
|
||||
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),
|
||||
},
|
||||
...addition,
|
||||
...playerData,
|
||||
finish: K.ITEM('bool', 1),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
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<PlayerInfo>(null, { collecttion: 'profile', id })) {
|
||||
id = _.random(0, 99999999);
|
||||
}
|
||||
|
||||
const defaultInfo: PlayerInfo = {
|
||||
collection: 'playerinfo',
|
||||
pluginVer: PLUGIN_VER,
|
||||
id,
|
||||
version,
|
||||
name: 'ASPHYXIA-CORE USER',
|
||||
title: 'Please edit on WebUI',
|
||||
}
|
||||
|
||||
const defaultProfile = (game: 'gf' | 'dm'): 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
|
||||
}
|
||||
};
|
||||
|
||||
const defaultRecord = (game: 'gf' | 'dm'): 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],
|
||||
}
|
||||
}
|
||||
|
||||
const defaultExtra = (game: 'gf' | 'dm'): Extra => {
|
||||
return {
|
||||
collection: 'extra',
|
||||
pluginVer: PLUGIN_VER,
|
||||
|
||||
game,
|
||||
version,
|
||||
id,
|
||||
|
||||
playstyle: [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
],
|
||||
custom: Array(50).fill(0),
|
||||
list_1: Array(100).fill(-1),
|
||||
list_2: Array(100).fill(-1),
|
||||
list_3: Array(100).fill(-1),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const defaultScores = (game: 'gf' | 'dm'): Scores => {
|
||||
return {
|
||||
collection: 'scores',
|
||||
version,
|
||||
pluginVer: PLUGIN_VER,
|
||||
game,
|
||||
scores: {}
|
||||
}
|
||||
};
|
||||
|
||||
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 }, defaultProfile('gf'));
|
||||
await DB.Upsert(refid, { collection: 'profile', ...dm }, defaultProfile('dm'));
|
||||
await DB.Upsert(refid, { collection: 'record', ...gf }, defaultRecord('gf'));
|
||||
await DB.Upsert(refid, { collection: 'record', ...dm }, defaultRecord('dm'));
|
||||
await DB.Upsert(refid, { collection: 'extra', ...gf }, defaultExtra('gf'));
|
||||
await DB.Upsert(refid, { collection: 'extra', ...dm }, defaultExtra('dm'));
|
||||
await DB.Upsert(refid, { collection: 'scores', ...gf }, defaultScores('gf'));
|
||||
await DB.Upsert(refid, { collection: 'scores', ...dm }, defaultScores('dm'));
|
||||
|
||||
return defaultInfo
|
||||
}
|
||||
|
||||
export const savePlayer: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('player.refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const no = getPlayerNo(data);
|
||||
const version = getVersion(info);
|
||||
const dm = isDM(info);
|
||||
|
||||
const game = dm ? 'dm' : 'gf';
|
||||
|
||||
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 dataplayer = $(data).element("player")
|
||||
|
||||
const autoSet = (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])
|
||||
}
|
||||
};
|
||||
|
||||
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('playstyle', 'customdata.playstyle', true);
|
||||
autoExtra('custom', 'customdata.custom', 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 stages = $(data).elements('player.stage');
|
||||
const scores = (await getScore(refid, version, game)).scores;
|
||||
for (const stage of stages) {
|
||||
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] = {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
await saveScore(refid, version, game, scores);
|
||||
|
||||
await send.object({
|
||||
player: K.ATTR({ no: `${no}` }, {
|
||||
skill: { rank: K.ITEM('s32', 1), total_nr: K.ITEM('s32', 1) },
|
||||
all_skill: { rank: K.ITEM('s32', 1), total_nr: K.ITEM('s32', 1) },
|
||||
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]),
|
||||
},
|
||||
},
|
||||
}),
|
||||
gamemode: _.get(data, 'gamemode'),
|
||||
});
|
||||
};
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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 }
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import { gameInfoGet, shopInfoRegist } from "./handlers/info";
|
||||
import { playableMusic } from "./handlers/MusicList"
|
||||
import { getPlayer, check, regist, savePlayer } from "./handlers/profiles";
|
||||
import { updatePlayerInfo } from "./handlers/webui";
|
||||
import { isRequiredVersion } from "./utils";
|
||||
|
||||
export function register() {
|
||||
if(!isRequiredVersion(1, 20)) {
|
||||
console.error("You need newer version of Core. v1.20 or newer required.")
|
||||
}
|
||||
|
||||
R.GameCode('M32');
|
||||
|
||||
R.Config("enable_custom_mdb", {
|
||||
name: "Enable Custom MDB",
|
||||
desc: "For who uses own MDB",
|
||||
type: "boolean",
|
||||
default: false,
|
||||
})
|
||||
|
||||
R.DataFile("data/custom_mdb.xml", {
|
||||
accept: ".xml",
|
||||
name: "Custom MDB",
|
||||
desc: "You need to enable Custom MDB option first."
|
||||
})
|
||||
|
||||
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);
|
||||
// TODO: NEXTAGE
|
||||
// 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', savePlayer);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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[];
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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.
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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[];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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 isRequiredVersion(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_minor >= minor
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//DATA//
|
||||
info: DB.Find(refid, { collection: 'playerinfo' })
|
||||
|
||||
-
|
||||
|
||||
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
|
||||
@@ -0,0 +1,27 @@
|
||||
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.
|
||||
@@ -0,0 +1 @@
|
||||
*.xml
|
||||
@@ -0,0 +1,12 @@
|
||||
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")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
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");
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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
@@ -0,0 +1,88 @@
|
||||
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()
|
||||
}**/
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
import { Profile } from "../models/profile";
|
||||
import { Scores } from "../models/scores";
|
||||
import { IDToCode } from "../utils";
|
||||
|
||||
export const load: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const profile = await DB.FindOne<Profile>(refid, { collection: "profile" })
|
||||
if (profile == null) {
|
||||
// Request New Profile from game side.
|
||||
return send.object({
|
||||
result: K.ITEM("u8", 1)
|
||||
})
|
||||
}
|
||||
|
||||
const item = _.map(profile.item, (v, k) => {
|
||||
const id = k.replace("g", "")
|
||||
return {
|
||||
type: K.ITEM("u8", v.type),
|
||||
id: K.ITEM("u32", parseInt(id)),
|
||||
param: K.ITEM("u32", v.param)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
send.object({
|
||||
hidden_param: K.ARRAY("s32", profile.hidden_param),
|
||||
play_count: K.ITEM("u32", profile.play_count),
|
||||
daily_count: K.ITEM("u32", profile.daily_count),
|
||||
play_chain: K.ITEM("u32", profile.play_chain),
|
||||
last: {
|
||||
headphone: K.ITEM("u8", profile.last.headphone),
|
||||
appeal_id: K.ITEM("u16", profile.last.appeal_id),
|
||||
comment_id: K.ITEM("u16", profile.last.comment_id),
|
||||
music_id: K.ITEM("s32", profile.last.music_id),
|
||||
music_type: K.ITEM("u8", profile.last.music_type),
|
||||
sort_type: K.ITEM("u8", profile.last.sort_type),
|
||||
narrow_down: K.ITEM("u8", profile.last.narrow_down),
|
||||
gauge_option: K.ITEM("u8", profile.last.gauge_option),
|
||||
},
|
||||
blaster_energy: K.ITEM("u32", profile.blaster_energy),
|
||||
blaster_count: K.ITEM("u32", profile.blaster_count),
|
||||
code: K.ITEM("str", IDToCode(profile.code)),
|
||||
name: K.ITEM("str", profile.name),
|
||||
creator_id: K.ITEM("u32", profile.creator_id),
|
||||
skill_level: K.ITEM("s16", profile.skill_level),
|
||||
skill_name_id: K.ITEM("s16", profile.skill_name_id),
|
||||
gamecoin_packet: K.ITEM("u32", profile.gamecoin_packet),
|
||||
gamecoin_block: K.ITEM("u32", profile.gamecoin_block),
|
||||
item: {
|
||||
info: item
|
||||
},
|
||||
param: {},
|
||||
result: K.ITEM("u8", 0),
|
||||
ea_shop: {
|
||||
packet_booster: K.ITEM("s32", 0),
|
||||
block_booster: K.ITEM("s32", 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const load_m: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('dataid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const scores = (await DB.FindOne<Scores>(refid, { collection: 'scores'})).scores
|
||||
|
||||
const music: any[] = [];
|
||||
for (const mid in scores) {
|
||||
for (const type in scores[mid]) {
|
||||
let score = scores[mid][type]
|
||||
music.push({
|
||||
music_id: K.ITEM("u32", parseInt(mid)),
|
||||
music_type: K.ITEM("u32", parseInt(type)),
|
||||
score: K.ITEM("u32", score.score),
|
||||
cnt: K.ITEM("u32", score.count),
|
||||
clear_type: K.ITEM("u32", score.clear_type),
|
||||
score_grade: K.ITEM("u32", score.score_grade),
|
||||
btn_rate: K.ITEM("u32", score.btn_rate),
|
||||
long_rate: K.ITEM("u32", score.long_rate),
|
||||
vol_rate: K.ITEM("u32", score.vol_rate)
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
send.object({
|
||||
new: {
|
||||
music
|
||||
},
|
||||
// This field seems used on Museca 1, Ignore this.
|
||||
old: {}
|
||||
})
|
||||
}
|
||||
|
||||
export const save: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const dbItem = (await DB.FindOne<Profile>(refid, { collection: "profile" })).item
|
||||
for(const item of $(data).elements("item.info")) {
|
||||
const id = item.number("id");
|
||||
const type = item.number("type")
|
||||
// Grafica and Mission shares same ID. Why?????
|
||||
dbItem[type == 16 ? `g${id}` : id] = {
|
||||
type,
|
||||
param : item.number("param")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await DB.Upsert<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
last: {
|
||||
headphone: $(data).number("headphone"),
|
||||
appeal_id: $(data).number("appeal_id"),
|
||||
comment_id: $(data).number("comment_id"),
|
||||
music_id: $(data).number("music_id"),
|
||||
music_type: $(data).number("music_type"),
|
||||
sort_type: $(data).number("sort_type"),
|
||||
narrow_down: $(data).number("narrow_down"),
|
||||
gauge_option: $(data).number("gauge_option"),
|
||||
},
|
||||
hidden_param: $(data).numbers("hidden_param"),
|
||||
blaster_count: $(data).number("blaster_count"),
|
||||
item: dbItem,
|
||||
},
|
||||
$inc: {
|
||||
blaster_energy: $(data).number("earned_blaster_energy"),
|
||||
gamecoin_block: $(data).number("earned_gamecoin_block"),
|
||||
gamecoin_packet: $(data).number("earned_gamecoin_packet")
|
||||
}
|
||||
})
|
||||
|
||||
send.success()
|
||||
}
|
||||
|
||||
export const save_m: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const scores = (await DB.FindOne<Scores>(refid, { collection: "scores" })).scores
|
||||
const mid = $(data).number("music_id")
|
||||
const type = $(data).number("music_type")
|
||||
|
||||
if (!scores[mid]) {
|
||||
scores[mid] = {};
|
||||
}
|
||||
|
||||
scores[mid][type] = {
|
||||
score: Math.max(_.get(scores[mid][type], 'score', 0), $(data).number("score")),
|
||||
clear_type: Math.max(_.get(scores[mid][type], 'clear_type', 0), $(data).number("clear_type")),
|
||||
score_grade: Math.max(_.get(scores[mid][type], 'score_grade', 0), $(data).number("score_grade")),
|
||||
count: _.get(scores[mid][type], 'count', 0) + 1,
|
||||
btn_rate: Math.max(_.get(scores[mid][type], 'btn_rate', 0), $(data).number("btn_rate")),
|
||||
long_rate: Math.max(_.get(scores[mid][type], 'long_rate', 0), $(data).number("long_rate")),
|
||||
vol_rate: Math.max(_.get(scores[mid][type], 'vol_rate', 0), $(data).number("vol_rate")),
|
||||
};
|
||||
|
||||
const store: Scores = {
|
||||
collection: "scores",
|
||||
scores
|
||||
}
|
||||
|
||||
await DB.Upsert<Scores>(refid, { collection: "scores" }, store)
|
||||
|
||||
send.success()
|
||||
}
|
||||
|
||||
export const newProfile: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const name = $(data).str('name', 'NONAME');
|
||||
let code = _.random(0, 99999999);
|
||||
while (await DB.FindOne<Profile>(null, { collecttion: 'profile', code })) {
|
||||
code = _.random(0, 99999999);
|
||||
}
|
||||
|
||||
let defItem = {};
|
||||
for(let i = 1; i < 801; i++) {
|
||||
defItem[i] = {
|
||||
type: 4,
|
||||
param : 1
|
||||
}
|
||||
}
|
||||
|
||||
const profile: Profile = {
|
||||
collection: "profile",
|
||||
code,
|
||||
name,
|
||||
|
||||
hidden_param: Array(20).fill(0),
|
||||
play_count: 0,
|
||||
daily_count: 0,
|
||||
play_chain: 0,
|
||||
last: {
|
||||
headphone: 0,
|
||||
appeal_id: 0,
|
||||
comment_id: 0,
|
||||
music_id: 0,
|
||||
music_type: 0,
|
||||
sort_type: 0,
|
||||
narrow_down: 0,
|
||||
gauge_option: 0,
|
||||
},
|
||||
blaster_energy: 0,
|
||||
blaster_count: 0,
|
||||
creator_id: 0,
|
||||
skill_level: 0,
|
||||
skill_name_id: 0,
|
||||
gamecoin_packet: 0,
|
||||
gamecoin_block: 0,
|
||||
|
||||
item: defItem,
|
||||
|
||||
packet_booster: 0,
|
||||
block_booster: 0,
|
||||
}
|
||||
await DB.Upsert<Profile>(refid, { collection: "profile"}, profile)
|
||||
await DB.Upsert<Scores>(refid, { collection: "scores" }, { collection: "scores", scores: {}})
|
||||
send.success()
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { common, exception, lounge, shop, hiscore, frozen } from "./handlers/common";
|
||||
import { load, load_m, newProfile, save, save_m } from "./handlers/player";
|
||||
import { isRequiredVersion } from "./utils";
|
||||
|
||||
export function register() {
|
||||
if(!isRequiredVersion(1, 19)) {
|
||||
console.error("You need newer version of Core. v1.19 or newer required.")
|
||||
}
|
||||
|
||||
R.GameCode('PIX');
|
||||
|
||||
R.Config("unlock_all_songs", {
|
||||
name: "Force unlock all songs",
|
||||
type: "boolean",
|
||||
default: false
|
||||
})
|
||||
|
||||
R.Config("enable_custom_mdb", {
|
||||
name: "Enable Custom MDB",
|
||||
desc: "For who uses own MDB",
|
||||
type: "boolean",
|
||||
default: false,
|
||||
})
|
||||
|
||||
R.DataFile("data/custom_mdb.xml", {
|
||||
accept: ".xml",
|
||||
name: "Custom MDB",
|
||||
desc: "You need to enable Custom MDB option first."
|
||||
})
|
||||
|
||||
const Route = (method: string, handler: EPR | boolean) => {
|
||||
// Helper for register multiple versions.
|
||||
// Use this when plugin supports first version.
|
||||
R.Route(`game_3.${method}`, handler);
|
||||
};
|
||||
|
||||
// Common
|
||||
Route("common", common)
|
||||
Route("shop", shop)
|
||||
Route("exception", exception)
|
||||
Route("hiscore", hiscore),
|
||||
Route("lounge", lounge),
|
||||
Route("frozen", frozen)
|
||||
Route("play_e", true)
|
||||
|
||||
// Player
|
||||
Route("new", newProfile)
|
||||
Route("save", save)
|
||||
Route("save_m", save_m)
|
||||
//Route("save_c", save_c)
|
||||
Route("load", load)
|
||||
Route("load_m", load_m)
|
||||
|
||||
R.Unhandled(async (info, data, send) => {
|
||||
if (["eventlog"].includes(info.module)) return;
|
||||
console.error(`Received Unhandled Response on ${info.method} by ${info.model}/${info.module}`)
|
||||
console.error(`Received Request: ${JSON.stringify(data, null, 4)}`)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
export interface Profile {
|
||||
collection: 'profile';
|
||||
|
||||
code: number;
|
||||
name: string;
|
||||
|
||||
hidden_param: number[];
|
||||
play_count: number;
|
||||
daily_count: number;
|
||||
play_chain: number;
|
||||
last: {
|
||||
headphone: number;
|
||||
appeal_id: number;
|
||||
comment_id: number;
|
||||
music_id: number;
|
||||
music_type: number;
|
||||
sort_type: number;
|
||||
narrow_down: number;
|
||||
gauge_option: number;
|
||||
},
|
||||
blaster_energy: number;
|
||||
blaster_count: number;
|
||||
creator_id: number;
|
||||
skill_level: number;
|
||||
skill_name_id: number;
|
||||
gamecoin_packet: number;
|
||||
gamecoin_block: number;
|
||||
|
||||
item: {
|
||||
[id: number]: {
|
||||
type: number,
|
||||
param: number
|
||||
}
|
||||
}
|
||||
|
||||
packet_booster: number;
|
||||
block_booster: number;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface Scores {
|
||||
collection: 'scores',
|
||||
|
||||
scores: {
|
||||
[mid: string]: {
|
||||
[type: string]: {
|
||||
score: number;
|
||||
count: number;
|
||||
clear_type: number;
|
||||
score_grade: number;
|
||||
btn_rate: number;
|
||||
long_rate: number;
|
||||
vol_rate: number;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export function IDToCode(id: number) {
|
||||
const padded = _.padStart(id.toString(), 8);
|
||||
return `${padded.slice(0, 4)}-${padded.slice(4)}`;
|
||||
}
|
||||
|
||||
export function isRequiredVersion(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_minor >= minor
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# Nostalgia
|
||||
|
||||
Plugin Version: **v1.2.0**
|
||||
|
||||
Supported Versions
|
||||
-------------------
|
||||
- ノスタルジア/ First Version (Experiment-Old)
|
||||
- Forte (Experiment-Old)
|
||||
- Op.2
|
||||
|
||||
About Experiment-Old Support
|
||||
----------------------------
|
||||
A version that marked as **Experiment-Old** is _Not_ Primary supported experiment version.
|
||||
Since This plugin is mainly focused on Op.2, other versions may not work correctly.
|
||||
|
||||
If you have a problem that move from old version to new version, There's webui for mitigate the issue.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
1.2.0 (Current)
|
||||
---------------
|
||||
- Nostalgia First version support.
|
||||
|
||||
1.1.0
|
||||
-----
|
||||
- Fix saving issue with brooch, island, and kentei.
|
||||
- Moved to Base64 encoded data base.
|
||||
- Forte support.
|
||||
|
||||
1.0.0
|
||||
-----
|
||||
Initial Release.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
*.json
|
||||
*.json
|
||||
*.xml
|
||||
@@ -0,0 +1,11 @@
|
||||
import { CommonMusicData, readJSONOrXML } from "./ForteMusic";
|
||||
import { readB64JSON } from "./helper";
|
||||
|
||||
export async function processData(): Promise<CommonMusicData> {
|
||||
if (IO.Exists("data/first_mdb.json.b64")) {
|
||||
return await readB64JSON("data/first_mdb.json.b64");
|
||||
}
|
||||
const data = await readJSONOrXML("data/first_mdb.json", "data/first_mdb.xml")
|
||||
// await IO.WriteFile("data/first_mdb.json.b64", Buffer.from(JSON.stringify(data)).toString("base64"))
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { CommonMusicDataField, readB64JSON, readXML } from "./helper";
|
||||
|
||||
export async function processData(): Promise<CommonMusicData> {
|
||||
if (IO.Exists("data/forte_mdb.json.b64")) {
|
||||
return await readB64JSON("data/forte_mdb.json.b64");
|
||||
}
|
||||
const data = await readJSONOrXML("data/forte_mdb.json", "data/forte_mdb.xml")
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function processMdbData(path: string): Promise<CommonMusicData> {
|
||||
const data = await readXML(path);
|
||||
const attr = $(data).attr("music_list");
|
||||
const mdb = $(data).elements("music_list.music_spec");
|
||||
const music: CommonMusicDataField[] = [];
|
||||
for (const m of mdb) {
|
||||
music.push(K.ATTR({ index: m.attr().index }, {
|
||||
basename: K.ITEM("str", m.str("basename")),
|
||||
title: K.ITEM("str", m.str("title", "title")),
|
||||
title_kana: K.ITEM("str", m.str("title_kana", "title_kana")),
|
||||
artist: K.ITEM("str", m.str("artist", "artist")),
|
||||
artist_kana: K.ITEM("str", m.str("artist_kana", "artist_kana")),
|
||||
priority: K.ITEM("s8", m.number("priority")),
|
||||
category_flag: K.ARRAY("s32", m.numbers("category_flag")),
|
||||
primary_category: K.ITEM("s8", m.number("primary_category")),
|
||||
level_normal: K.ITEM("s8", m.number("level_normal")),
|
||||
level_hard: K.ITEM("s8", m.number("level_hard")),
|
||||
level_extreme: K.ITEM("s8", m.number("level_extreme")),
|
||||
demo_popular: K.ITEM("bool", m.bool("demo_popular")),
|
||||
demo_bemani: K.ITEM("bool", m.bool("demo_bemani")),
|
||||
destination_j: K.ITEM("bool", true),
|
||||
destination_a: K.ITEM("bool", true),
|
||||
destination_y: K.ITEM("bool", true),
|
||||
destination_k: K.ITEM("bool", true),
|
||||
offline: K.ITEM("bool", m.bool("offline")),
|
||||
unlock_type: K.ITEM("s8", m.number("unlock_type") == 3 ? 1 : m.number("unlock_type")),
|
||||
volume_bgm: K.ITEM("s8", m.number("volume_bgm")),
|
||||
volume_key: K.ITEM("s8", m.number("volume_key")),
|
||||
start_date: K.ITEM("str", m.str("start_date")),
|
||||
end_date: K.ITEM("str", "9999-12-31 23:59"),
|
||||
description: K.ITEM("str", m.str("description", "description"))
|
||||
}));
|
||||
}
|
||||
return K.ATTR({
|
||||
release_code: attr.release_code,
|
||||
revision: attr.revision,
|
||||
}, {
|
||||
music_spec: music,
|
||||
});
|
||||
}
|
||||
|
||||
export async function readJSONOrXML(jsonPath: string, xmlPath: string): Promise<CommonMusicData> {
|
||||
if (!IO.Exists(jsonPath)) {
|
||||
const data = await processMdbData(xmlPath)
|
||||
await IO.WriteFile(jsonPath, JSON.stringify(data))
|
||||
await IO.WriteFile(jsonPath.replace(".json", ".json.b64"), Buffer.from((JSON.stringify(data))).toString('base64'));
|
||||
return data
|
||||
} else {
|
||||
const json = JSON.parse(await IO.ReadFile(jsonPath, 'utf-8'))
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommonMusicData {
|
||||
"@attr": {
|
||||
revision: string,
|
||||
release_code: string
|
||||
}
|
||||
music_spec: CommonMusicDataField[]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,43 @@
|
||||
export interface CommonMusicDataField {
|
||||
basename: KITEM<"str">;
|
||||
title: KITEM<"str">;
|
||||
title_kana: KITEM<"str">;
|
||||
artist: KITEM<"str">;
|
||||
artist_kana: KITEM<"str">
|
||||
priority: KITEM<"s8">;
|
||||
category_flag: KARRAY<"s32">;
|
||||
primary_category: KITEM<"s8">;
|
||||
level_normal: KITEM<"s8">;
|
||||
level_hard: KITEM<"s8">;
|
||||
level_extreme: KITEM<"s8">;
|
||||
demo_popular: KITEM<"bool">;
|
||||
demo_bemani: KITEM<"bool">
|
||||
destination_j: KITEM<"bool">;
|
||||
destination_a: KITEM<"bool">;
|
||||
destination_y: KITEM<"bool">;
|
||||
destination_k: KITEM<"bool">;
|
||||
unlock_type: KITEM<"s8">;
|
||||
offline: KITEM<"bool">;
|
||||
volume_bgm: KITEM<"s8">;
|
||||
volume_key: KITEM<"s8">;
|
||||
start_date: KITEM<"str">;
|
||||
end_date: KITEM<"str">;
|
||||
description: KITEM<"str">;
|
||||
}
|
||||
|
||||
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 readB64JSON(b64path: string) {
|
||||
const buff = await IO.ReadFile(b64path, 'utf-8');
|
||||
return JSON.parse(Buffer.from(buff, 'base64').toString('utf-8'));
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,141 @@
|
||||
|
||||
import { processData as firstData } from "../data/FirstMusic";
|
||||
import { processData as forteData } from "../data/ForteMusic";
|
||||
import { readB64JSON } from "../data/helper";
|
||||
import { NosVersionHelper } from "../utils";
|
||||
|
||||
export const permitted_list = {
|
||||
flag: [
|
||||
K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '0' }),
|
||||
K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '1' }),
|
||||
K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '2' }),
|
||||
K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '3' }),
|
||||
],
|
||||
};
|
||||
|
||||
export const forte_permitted_list = {
|
||||
flag: [
|
||||
K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '0' }),
|
||||
K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '1' }),
|
||||
K.ARRAY('s32', Array(32).fill(-1), { sheet_type: '2' }),
|
||||
],
|
||||
}
|
||||
|
||||
async function ReadData(filename: string) {
|
||||
if (!IO.Exists(`data/${filename}.json.b64`)) {
|
||||
const xml = await IO.ReadFile(`data/${filename}.xml`, 'utf-8');
|
||||
const json = U.parseXML(xml, false)
|
||||
// await IO.WriteFile(`data/${filename}.json.b64`, Buffer.from(JSON.stringify(json)).toString('base64'));
|
||||
return json
|
||||
}
|
||||
return readB64JSON(`data/${filename}.json.b64`)
|
||||
}
|
||||
|
||||
async function processIslandData() {
|
||||
const islandData = (await ReadData('island')).island_data_list.island_data;
|
||||
for (const island of islandData) {
|
||||
island.flag_permitted = K.ITEM('bool', 1);
|
||||
island.cost['@content'][0] = ~~(island.cost['@content'][0] / 10);
|
||||
let containers = island.get_music_index_list.container;
|
||||
if (!_.isArray(containers)) {
|
||||
containers = [containers];
|
||||
}
|
||||
|
||||
for (const cont of containers) {
|
||||
cont['@attr'].no = cont['@attr'].container_no;
|
||||
delete cont['@attr'].container_no;
|
||||
if (!_.isArray(cont.music)) {
|
||||
cont.music = [cont.music];
|
||||
}
|
||||
|
||||
for (const m of cont.music) {
|
||||
m.get_point['@content'][0] = ~~(m.get_point['@content'][0] / 6);
|
||||
}
|
||||
}
|
||||
|
||||
island.get_music_index_list.container = containers;
|
||||
};
|
||||
|
||||
return { island_data: islandData };
|
||||
}
|
||||
|
||||
async function processCourseData() {
|
||||
const courseData = (await ReadData('course')).course_data_list.course_data;
|
||||
for (const course of courseData) {
|
||||
course.req_nos['@content'][0] = ~~(course.req_nos['@content'][0] / 10);
|
||||
if (course.seq_list && course.seq_list.tune) {
|
||||
course.seq_list = course.seq_list.tune;
|
||||
course.is_open = K.ITEM('bool', 1);
|
||||
}
|
||||
}
|
||||
|
||||
return { course_data: courseData };
|
||||
}
|
||||
|
||||
export const get_common_info = async (info, data, send) => {
|
||||
send.object({
|
||||
permitted_list,
|
||||
olupdate: {
|
||||
delete_flag: K.ITEM('bool', 0),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const get_music_info: EPR = async (info, data, send) => {
|
||||
const version = new NosVersionHelper(info)
|
||||
|
||||
const music_spec: any = [];
|
||||
for (let i = 1; i < 400; ++i) {
|
||||
music_spec.push(K.ATTR({ index: `${i}` }, {
|
||||
jk_jpn: K.ITEM('bool', 1),
|
||||
jk_asia: K.ITEM('bool', 1),
|
||||
jk_kor: K.ITEM('bool', 1),
|
||||
jk_idn: K.ITEM('bool', 1),
|
||||
start_date: K.ITEM('str', '2017-01-11 10:00'),
|
||||
end_date: K.ITEM('str', '9999-12-31 23:59'),
|
||||
expiration_date: K.ITEM('str', '9999-12-31 23:59'),
|
||||
real_start_date: K.ITEM('str', '2017-01-11 10:00'),
|
||||
real_end_date: K.ITEM('str', '9999-12-31 23:59'),
|
||||
real_once_price: K.ITEM('s32', 300),
|
||||
real_forever_price: K.ITEM('s32', 7500),
|
||||
}));
|
||||
}
|
||||
|
||||
const music_list = async () => version.version === 'Forte' ? await forteData() : await firstData()
|
||||
|
||||
const versionObject = version.isFirstOrForte()
|
||||
? {
|
||||
permitted_list: forte_permitted_list,
|
||||
music_list: await music_list()
|
||||
}
|
||||
: {
|
||||
permitted_list,
|
||||
island_data_list: await processIslandData(),
|
||||
course_data_list: await processCourseData(),
|
||||
|
||||
overwrite_music_list: K.ATTR({
|
||||
revision: '16706',
|
||||
release_code: '2019100200',
|
||||
}, {
|
||||
music_spec: music_spec,
|
||||
}),
|
||||
};
|
||||
send.object({
|
||||
...versionObject,
|
||||
|
||||
gamedata_flag_list: {
|
||||
event: {
|
||||
index: K.ITEM('s32', 0),
|
||||
status: K.ITEM('s8', 1),
|
||||
start_time: K.ITEM('u64', BigInt(0)),
|
||||
end_time: K.ITEM('u64', BigInt(0)),
|
||||
param1: K.ITEM('u64', BigInt(0)),
|
||||
param2: K.ITEM('u64', BigInt(0)),
|
||||
},
|
||||
},
|
||||
|
||||
olupdate: {
|
||||
delete_flag: K.ITEM('bool', 0),
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,610 @@
|
||||
import { Profile } from '../models/profile';
|
||||
import { Scores } from '../models/scores';
|
||||
import { NosVersionHelper } from '../utils';
|
||||
import { permitted_list, forte_permitted_list } from './common';
|
||||
|
||||
// export const event_list = {
|
||||
// event: {
|
||||
// '@attr': {
|
||||
// index: 1,
|
||||
// },
|
||||
// 'status': K.ITEM('s8', 1),
|
||||
// 'start_time': K.ITEM('u64', BigInt(0)),
|
||||
// 'end_time': K.ITEM('u64', BigInt(0)),
|
||||
// },
|
||||
// };
|
||||
|
||||
const getEventInfo = (version: NosVersionHelper) => {
|
||||
const event: any[] = [];
|
||||
const event_num = version.getEventMaxIndex()
|
||||
for (let i = 1; i <= event_num; ++i) {
|
||||
event.push({
|
||||
type: K.ITEM('s32', 4),
|
||||
index: K.ITEM('s32', i),
|
||||
status: K.ITEM('s8', 1),
|
||||
start_time: K.ITEM('u64', BigInt(0)),
|
||||
end_time: K.ITEM('u64', BigInt(0)),
|
||||
param1: K.ITEM('u64', BigInt(0)),
|
||||
param2: K.ITEM('u64', BigInt(0)),
|
||||
});
|
||||
}
|
||||
return event;
|
||||
};
|
||||
|
||||
const getPlayerData = async (refid: string, info: EamuseInfo, name?: string) => {
|
||||
const p = await readProfile(refid);
|
||||
const version = new NosVersionHelper(info)
|
||||
|
||||
if (name && name.length > 0) {
|
||||
p.name = name;
|
||||
await writeProfile(refid, p);
|
||||
}
|
||||
|
||||
const param: any[] = [];
|
||||
for (const t in p.params) {
|
||||
const para = p.params[t];
|
||||
param.push(K.ATTR({ type: t }, {
|
||||
count: K.ITEM('s32', para.length),
|
||||
params_array: K.ARRAY('s32', para),
|
||||
}));
|
||||
}
|
||||
|
||||
const brooch: any[] = [];
|
||||
for (const b in p.brooches) {
|
||||
if (parseInt(b, 10) > version.getBroochMaxIndex()) continue;
|
||||
const bData = p.brooches[b];
|
||||
brooch.push(K.ATTR({ index: b }, {
|
||||
watch_count: K.ITEM('s32', bData.watch),
|
||||
level: K.ITEM('s8', bData.level),
|
||||
invested_steps: K.ITEM('s32', bData.steps),
|
||||
is_new_brooch: K.ITEM('bool', bData.new),
|
||||
}));
|
||||
}
|
||||
|
||||
// Unlock brooches
|
||||
for (let i = 101; i <= 124; ++i) {
|
||||
if (i > version.getBroochMaxIndex()) continue;
|
||||
brooch.push(K.ATTR({ index: `${i}` }, {
|
||||
'watch_count': K.ITEM('s32', 0),
|
||||
'level': K.ITEM('s8', 1),
|
||||
'invested_steps': K.ITEM('s32', 0),
|
||||
'is_new_brooch': K.ITEM('bool', 0),
|
||||
}));
|
||||
}
|
||||
|
||||
// Forte
|
||||
const stairs: any[] = [];
|
||||
for (const s in (p.cat_stairs || defaultProfile.cat_stairs)) {
|
||||
const stair = (p.cat_stairs || defaultProfile.cat_stairs)[s];
|
||||
|
||||
stairs.push(K.ATTR({ index: s }, {
|
||||
total_steps: K.ITEM("s32", stair.total),
|
||||
chapter_index: K.ITEM("s32", stair.index),
|
||||
chapter_steps: K.ITEM("s32", stair.steps),
|
||||
chapter_goal: K.ITEM("s32", stair.goal)
|
||||
}));
|
||||
}
|
||||
|
||||
// >= Op2
|
||||
const kentei_record: any[] = [];
|
||||
for (const k in p.kentei) {
|
||||
const kentei = p.kentei[k];
|
||||
|
||||
kentei_record.push({
|
||||
stage_prog: K.ITEM('s8', kentei.stage),
|
||||
kentei_index: K.ITEM('s32', parseInt(k, 10)),
|
||||
score: K.ARRAY('s32', kentei.score),
|
||||
clear_rate: K.ITEM('s32', kentei.rate),
|
||||
clear_flag: K.ITEM('u32', kentei.flag),
|
||||
play_count: K.ITEM('s32', kentei.count),
|
||||
});
|
||||
}
|
||||
|
||||
// >= Op2
|
||||
const island_progress: any[] = [];
|
||||
for (const i in p.islands) {
|
||||
const island = p.islands[i];
|
||||
|
||||
const container: any[] = [];
|
||||
for (const c in island.containers) {
|
||||
const cont = island.containers[c];
|
||||
|
||||
const reward: any[] = [];
|
||||
for (const r in cont.rewards) {
|
||||
const rew = cont.rewards[r];
|
||||
|
||||
reward.push({
|
||||
reward_index: K.ITEM('s32', parseInt(r, 10)),
|
||||
point: K.ITEM('s32', rew),
|
||||
});
|
||||
}
|
||||
|
||||
container.push({
|
||||
container_no: K.ITEM('s32', parseInt(c, 10)),
|
||||
movie_prog: K.ITEM('s8', cont.prog),
|
||||
reward,
|
||||
});
|
||||
}
|
||||
|
||||
island_progress.push(K.ATTR({ index: i }, {
|
||||
lookUnlockWin: K.ITEM('s32', island.look),
|
||||
select_container: K.ITEM('s32', island.select),
|
||||
travelledTime: K.ITEM('u32', island.time),
|
||||
container,
|
||||
}));
|
||||
}
|
||||
|
||||
const correct_permitted_list = !version.isFirstOrForte() ? permitted_list : forte_permitted_list
|
||||
const music_list = [
|
||||
K.ARRAY('s32', p.musicList.type_0, { sheet_type: '0' }),
|
||||
K.ARRAY('s32', p.musicList.type_1, { sheet_type: '1' }),
|
||||
K.ARRAY('s32', p.musicList.type_2, { sheet_type: '2' }),
|
||||
K.ARRAY('s32', p.musicList.type_3, { sheet_type: '3' }),
|
||||
];
|
||||
const music_list2 = [
|
||||
K.ARRAY('s32', p.musicList2.type_0, { sheet_type: '0' }),
|
||||
K.ARRAY('s32', p.musicList2.type_1, { sheet_type: '1' }),
|
||||
K.ARRAY('s32', p.musicList2.type_2, { sheet_type: '2' }),
|
||||
K.ARRAY('s32', p.musicList2.type_3, { sheet_type: '3' }),
|
||||
];
|
||||
|
||||
if(version.isFirstOrForte()) {
|
||||
music_list.pop();
|
||||
music_list2.pop();
|
||||
}
|
||||
|
||||
return {
|
||||
name: K.ITEM('str', p.name),
|
||||
play_count: K.ITEM('s32', p.playCount),
|
||||
today_play_count: K.ITEM('s32', p.todayPlayCount),
|
||||
permitted_list: correct_permitted_list,
|
||||
event_info_list: { event: getEventInfo(version) }, // Op2
|
||||
event_control_list: { event: getEventInfo(version) }, // Forte
|
||||
music_list: {
|
||||
flag: music_list,
|
||||
},
|
||||
free_for_play_music_list: {
|
||||
flag: music_list2,
|
||||
},
|
||||
last: {
|
||||
music_index: K.ITEM('s32', version.numericHandler('music_index', p.music, 0)),
|
||||
sheet_type: K.ITEM('s8', version.numericHandler('sheet_type', p.sheet, 0)),
|
||||
brooch_index: K.ITEM('s32', version.numericHandler('brooch_index', p.brooch, 0)),
|
||||
hi_speed_level: K.ITEM('s32', p.hispeed),
|
||||
beat_guide: K.ITEM('s8', p.beatGuide),
|
||||
headphone_volume: K.ITEM('s8', p.headphone),
|
||||
judge_bar_pos: K.ITEM('s32', p.judgeBar),
|
||||
music_group: K.ITEM('s32', p.group),
|
||||
hands_mode: version.isFirstOrForte() ? K.ITEM('s32', p.mode) : K.ITEM('s8', p.mode),
|
||||
near_setting: K.ITEM('s8', p.near),
|
||||
judge_delay_offset: version.isFirstOrForte() ? K.ITEM('s32', p.offset) : K.ITEM('s8', p.offset),
|
||||
bingo_index: K.ITEM('s32', p.bingo),
|
||||
total_skill_value: K.ITEM('u64', BigInt(p.skill)),
|
||||
key_beam_level: K.ITEM('s8', p.keyBeam),
|
||||
orbit_type: K.ITEM('s8', p.orbit),
|
||||
note_height: K.ITEM('s8', p.noteHeight),
|
||||
note_width: K.ITEM('s8', p.noteWidth),
|
||||
judge_width_type: K.ITEM('s8', p.judgeWidth),
|
||||
beat_guide_volume: K.ITEM('s8', p.beatVolume),
|
||||
beat_guide_type: K.ITEM('s8', p.beatType),
|
||||
key_volume_offset: K.ITEM('s8', p.keyVolume),
|
||||
bgm_volume_offset: K.ITEM('s8', p.bgmVolume),
|
||||
note_disp_type: K.ITEM('s8', p.note),
|
||||
slow_fast: K.ITEM('s8', p.sf),
|
||||
judge_effect_adjust: K.ITEM('s8', p.judgeFX),
|
||||
simple_bg: K.ITEM('s8', p.simple),
|
||||
},
|
||||
// TODO: Full unlock instead of saving?
|
||||
cat_progress: {
|
||||
stair: stairs
|
||||
},
|
||||
brooch_list: { brooch },
|
||||
extra_param: { param },
|
||||
present_list: {},
|
||||
various_music_list: {
|
||||
data: [
|
||||
K.ATTR({ list_type: '0' }, {
|
||||
cond_flag: K.ITEM('s32', 0),
|
||||
flag: K.ITEM('s32', 0, { sheet_type: '0' }),
|
||||
}),
|
||||
],
|
||||
},
|
||||
island_progress_list: { island_progress },
|
||||
player_information_list: {},
|
||||
kentei_record_list: { kentei_record },
|
||||
linkage_data_list: {},
|
||||
travel: {
|
||||
money: K.ITEM('s32', p.money),
|
||||
fame: K.ITEM('s32', p.fame),
|
||||
fame_index: K.ITEM('s32', p.fameId),
|
||||
island_id: K.ITEM('s32', p.island),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const regist_playdata: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const name = $(data).str('name');
|
||||
console.debug(`nos op2 regist: ${name}`);
|
||||
|
||||
send.object(await getPlayerData(refid, info, name));
|
||||
};
|
||||
|
||||
export const get_playdata: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
send.object(await getPlayerData(refid, info));
|
||||
};
|
||||
|
||||
// export const set_stage_result: EPR = async (info, data, send) => {
|
||||
// return send.object();
|
||||
// };
|
||||
|
||||
export const set_total_result: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const isForte = new NosVersionHelper(info).isFirstOrForte()
|
||||
const p = await readProfile(refid);
|
||||
|
||||
p.playCount = $(data).number('play_count', p.playCount);
|
||||
p.todayPlayCount = $(data).number('today_play_count', p.todayPlayCount);
|
||||
|
||||
const last = $(data).element('last');
|
||||
|
||||
p.music = last.number('music_index', p.music);
|
||||
p.sheet = last.number('sheet_type', p.sheet);
|
||||
p.brooch = last.number('brooch_index', p.brooch);
|
||||
p.hispeed = last.number('hi_speed_level', p.hispeed);
|
||||
p.beatGuide = last.number('beat_guide', p.beatGuide);
|
||||
p.headphone = last.number('headphone_volume', p.headphone);
|
||||
p.judgeBar = last.number('judge_bar_pos', p.judgeBar);
|
||||
p.group = last.number('music_group', p.group);
|
||||
p.mode = last.number('hands_mode', p.mode);
|
||||
p.near = last.number('near_setting', p.near);
|
||||
p.offset = last.number('judge_delay_offset', p.offset);
|
||||
p.bingo = last.number('bingo_index', p.bingo);
|
||||
p.skill = `${last.bigint('total_skill_value') || p.skill}`;
|
||||
p.keyBeam = last.number('key_beam_level', p.keyBeam);
|
||||
p.orbit = last.number('orbit_type', p.orbit);
|
||||
p.noteHeight = last.number('note_height', p.noteHeight);
|
||||
p.noteWidth = last.number('note_width', p.noteWidth);
|
||||
p.judgeWidth = last.number('judge_width_type', p.judgeWidth);
|
||||
p.beatVolume = last.number('beat_guide_volume', p.beatVolume);
|
||||
p.beatType = last.number('beat_guide_type', p.beatType);
|
||||
p.keyVolume = last.number('key_volume_offset', p.keyVolume);
|
||||
p.bgmVolume = last.number('bgm_volume_offset', p.bgmVolume);
|
||||
p.note = last.number('note_disp_type', p.note);
|
||||
p.sf = last.number('slow_fast', p.sf);
|
||||
p.judgeFX = last.number('judge_effect_adjust', p.judgeFX);
|
||||
p.simple = last.number('simple_bg', p.simple);
|
||||
|
||||
p.money = $(data).number('travel.money', p.money);
|
||||
p.fame = $(data).number('travel.fame', p.fame);
|
||||
p.fameId = $(data).number('travel.fame_index', p.fameId);
|
||||
p.island = $(data).number('travel.island_id', p.island);
|
||||
|
||||
let flags = _.get($(data).obj, 'music_list.flag', []);
|
||||
if (!_.isArray(flags)) flags = [flags];
|
||||
for (const flag of flags) {
|
||||
const sheet = _.get(flag, '@attr.sheet_type', -1);
|
||||
if (sheet == '0') {
|
||||
p.musicList.type_0 = _.get(flag, '@content', p.musicList.type_0);
|
||||
} else if (sheet == '1') {
|
||||
p.musicList.type_1 = _.get(flag, '@content', p.musicList.type_1);
|
||||
} else if (sheet == '2') {
|
||||
p.musicList.type_2 = _.get(flag, '@content', p.musicList.type_2);
|
||||
} else if (sheet == '3') {
|
||||
p.musicList.type_3 = _.get(flag, '@content', p.musicList.type_3);
|
||||
}
|
||||
}
|
||||
|
||||
let freeFlags = _.get($(data).obj, 'free_for_play_music_list.flag', []);
|
||||
if (!_.isArray(freeFlags)) freeFlags = [freeFlags];
|
||||
for (const flag of freeFlags) {
|
||||
const sheet = _.get(flag, '@attr.sheet_type', -1);
|
||||
if (sheet == '0') {
|
||||
p.musicList2.type_0 = _.get(flag, '@content', p.musicList2.type_0);
|
||||
} else if (sheet == '1') {
|
||||
p.musicList2.type_1 = _.get(flag, '@content', p.musicList2.type_1);
|
||||
} else if (sheet == '2') {
|
||||
p.musicList2.type_2 = _.get(flag, '@content', p.musicList2.type_2);
|
||||
} else if (sheet == '3') {
|
||||
p.musicList2.type_3 = _.get(flag, '@content', p.musicList2.type_3);
|
||||
}
|
||||
}
|
||||
|
||||
// KENTEI
|
||||
let kenteis = $(data).elements('kentei_result_list.kentei_result');
|
||||
for (const kentei of kenteis) {
|
||||
const index = kentei.number('kentei_index', -1);
|
||||
if (index < 0) continue;
|
||||
|
||||
const clearRate = kentei.number('clear_rate', 0);
|
||||
const oldClearRate = _.get(p, `kentei.${index}.rate`, 0);
|
||||
const isHigh = clearRate >= oldClearRate;
|
||||
|
||||
p.kentei[index] = {
|
||||
rate: isHigh ? clearRate : oldClearRate,
|
||||
score: isHigh ? kentei.number('score', 0) : _.get(p, `kentei.${index}.score`, 0),
|
||||
stage: Math.max(kentei.number('stage_prog', 0), _.get(p, `kentei.${index}.stage`, 0)),
|
||||
flag: Math.max(kentei.number('clear_flag', 0), _.get(p, `kentei.${index}.flag`, 0)),
|
||||
count: Math.max(kentei.number('play_count', 0), _.get(p, `kentei.${index}.count`, 0)),
|
||||
};
|
||||
}
|
||||
|
||||
// PARAMS
|
||||
let params = $(data).elements('extra_param.param');
|
||||
for (const param of params) {
|
||||
const type = param.attr().type;
|
||||
const parray = param.numbers('params_array');
|
||||
if (type == null || parray == null) continue;
|
||||
|
||||
p.params[type] = parray;
|
||||
}
|
||||
|
||||
// BROOCHES
|
||||
let broochs = $(data).elements('brooch_list.brooch');
|
||||
for (const brooch of broochs) {
|
||||
const index = parseInt(brooch.attr().index || '-1', 10);
|
||||
if (index < 0) continue;
|
||||
|
||||
p.brooches[index] = {
|
||||
watch: brooch.number('watch_count', 0),
|
||||
level: brooch.number('level', 1),
|
||||
steps: brooch.number('invested_steps', 0),
|
||||
new: brooch.number('is_new_brooch', 0),
|
||||
};
|
||||
}
|
||||
|
||||
// ISLAND
|
||||
let islands = $(data).elements('island_progress_list.island_progress');
|
||||
for (const island of islands) {
|
||||
const index = parseInt(island.attr().index || '-1', 10);
|
||||
if (index < 0) continue;
|
||||
|
||||
const containers: Profile['islands']['0']['containers'] = {};
|
||||
let conts = $(data).elements('container');
|
||||
for (const cont of conts) {
|
||||
const index = cont.number('container_no', -1);
|
||||
if (index < 0) continue;
|
||||
|
||||
const rewards: { [key: string]: number } = {};
|
||||
let rews = $(data).elements('reward');
|
||||
for (const rew of rews) {
|
||||
const index = rew.number('reward_index', -1);
|
||||
if (index < 0) continue;
|
||||
rewards[index] = rew.number('point', 0);
|
||||
}
|
||||
|
||||
containers[index] = {
|
||||
prog: cont.number('movie_prog', 1),
|
||||
rewards,
|
||||
};
|
||||
}
|
||||
|
||||
p.islands[index] = {
|
||||
look: island.number('lookUnlockWin', 0),
|
||||
select: island.number('select_container', 0),
|
||||
time: island.number('travelledTime', 0),
|
||||
containers,
|
||||
};
|
||||
}
|
||||
|
||||
// CAT STAIR
|
||||
let stairs = $(data).elements('cat_progress.stair');
|
||||
if (!p.cat_stairs) {
|
||||
p.cat_stairs = defaultProfile.cat_stairs
|
||||
}
|
||||
for (const stair of stairs) {
|
||||
const index = parseInt(stair.attr().index || '-1', 10);
|
||||
if (index < 0) continue;
|
||||
|
||||
p.cat_stairs[index] = {
|
||||
total: stair.number('total_steps', 0),
|
||||
index: stair.number('chapter_index', 1),
|
||||
steps: stair.number('chapter_steps', 0),
|
||||
goal: stair.number('chapter_goal', 0),
|
||||
};
|
||||
}
|
||||
|
||||
await writeProfile(refid, p);
|
||||
|
||||
const scoreData = await readScores(refid);
|
||||
// Save Scores
|
||||
let stages = $(data).elements('stageinfo.stage');
|
||||
for (const stage of stages) {
|
||||
const mid = stage.attr().music_index
|
||||
const type = stage.attr().sheet_type
|
||||
|
||||
const key = `${mid}:${type}`;
|
||||
const c = isForte ? stage : stage.element('common');
|
||||
const o = _.get(scoreData, `scores.${key}`, {});
|
||||
const isHigh = c.number('score', 0) >= _.get(o, 'score', 0);
|
||||
scoreData.scores[key] = {
|
||||
score: Math.max(c.number('score', 0), _.get(o, 'score', 0)),
|
||||
grade: Math.max(Math.max(c.number('grade_basic', 0), c.number('evaluation', 0)), _.get(o, 'grade', 0)),
|
||||
recital: Math.max(c.number('grade_recital', 0), _.get(o, 'recital', 0)),
|
||||
mode: isHigh ? c.number('hands_mode', 0) : _.get(o, 'mode', 0),
|
||||
count: Math.max(c.number('play_count', 0), _.get(o, 'count', 1)),
|
||||
clear: c.number('clear_count', _.get(o, 'clear', 0)),
|
||||
multi: c.number('multi_count', _.get(o, 'multi', 0)),
|
||||
flag: Math.max(c.number('clear_flag', 0), _.get(o, 'flag', 0)),
|
||||
};
|
||||
}
|
||||
|
||||
// Save Recitals
|
||||
const rInfo = $(data).element('recital_info.recital');
|
||||
if (rInfo) {
|
||||
const rIndex = rInfo.number('recital_index', -1);
|
||||
if (rIndex >= 0) {
|
||||
const r = rInfo.element('result');
|
||||
const o = _.get(scoreData, `recitals.${rIndex}`);
|
||||
const isHigh = r.number('total_score', 0) >= _.get(o, 'score', 0);
|
||||
scoreData.recitals[rIndex] = {
|
||||
count: Math.max(rInfo.number('recital_count', 0), _.get(o, 'count', 1)),
|
||||
hall: isHigh ? rInfo.number('hall_index') : _.get(o, 'hall', 0),
|
||||
cat: isHigh ? rInfo.numbers('cat_index') : _.get(o, 'cat', [3, 7, 9, 5, 16]),
|
||||
audience: isHigh ? r.number('audience') : _.get(o, 'audience', 0),
|
||||
money: isHigh ? r.number('money') : _.get(o, 'money', 0),
|
||||
fame: isHigh ? r.number('fame') : _.get(o, 'fame', 0),
|
||||
player: isHigh ? r.number('player_fame') : _.get(o, 'player', 0),
|
||||
score: isHigh ? r.number('total_score', 0) : _.get(o, 'score', 0),
|
||||
start: (isHigh ? r.number('recital_start_time', 0) : _.get(o, 'start', 0)).toString(),
|
||||
end: (isHigh ? r.number('recital_end_time', 0) : _.get(o, 'end', 0)).toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
await writeScores(refid, scoreData);
|
||||
|
||||
send.success()
|
||||
};
|
||||
|
||||
export const get_musicdata: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const version = new NosVersionHelper(info)
|
||||
const scoreData = await readScores(refid);
|
||||
|
||||
const recital_record: any[] = [];
|
||||
const music: any[] = [];
|
||||
|
||||
for (const r in scoreData.recitals) {
|
||||
const reci = scoreData.recitals[r];
|
||||
recital_record.push({
|
||||
recital_index: K.ITEM('s32', parseInt(r, 10)),
|
||||
recital_count: K.ITEM('s32', reci.count),
|
||||
hall_index: K.ITEM('s8', reci.hall),
|
||||
cat_index: K.ARRAY('s16', reci.cat),
|
||||
audience: K.ITEM('s32', reci.audience),
|
||||
money: K.ITEM('s32', reci.money),
|
||||
fame: K.ITEM('s32', reci.fame),
|
||||
player_fame: K.ITEM('s32', reci.player),
|
||||
total_score: K.ITEM('s32', reci.score),
|
||||
total_base_score: K.ITEM('s32', reci.score),
|
||||
best_start_time: K.ITEM('u64', BigInt(reci.start)),
|
||||
best_end_time: K.ITEM('u64', BigInt(reci.end)),
|
||||
});
|
||||
}
|
||||
|
||||
for (const m in scoreData.scores) {
|
||||
const mdata = m.split(':');
|
||||
const musi = scoreData.scores[m];
|
||||
|
||||
if (parseInt(mdata[0], 10) > version.getMusicMaxIndex()) continue;
|
||||
|
||||
music.push(K.ATTR({
|
||||
music_index: mdata[0],
|
||||
sheet_type: mdata[1],
|
||||
}, {
|
||||
score: K.ITEM('s32', musi.score),
|
||||
evaluation: K.ITEM('u32', musi.grade), // Forte
|
||||
grade_basic: K.ITEM('u32', musi.grade),
|
||||
grade_recital: K.ITEM('u32', musi.recital),
|
||||
play_count: K.ITEM('s32', musi.count),
|
||||
clear_count: K.ITEM('s32', musi.clear),
|
||||
multi_count: K.ITEM('s32', musi.multi),
|
||||
hands_mode: K.ITEM('s8', musi.mode),
|
||||
clear_flag: K.ITEM('s32', musi.flag),
|
||||
}));
|
||||
}
|
||||
|
||||
send.object({
|
||||
recital_record,
|
||||
music,
|
||||
});
|
||||
};
|
||||
|
||||
async function readProfile(refid: string): Promise<Profile> {
|
||||
const profile = await DB.FindOne<Profile>(refid, { collection: 'profile' })
|
||||
return profile || defaultProfile
|
||||
}
|
||||
|
||||
async function writeProfile(refid: string, profile: Profile) {
|
||||
await DB.Upsert<Profile>(refid, { collection: 'profile' }, profile)
|
||||
}
|
||||
|
||||
async function readScores(refid: string): Promise<Scores> {
|
||||
const score = await DB.FindOne<Scores>(refid, { collection: 'scores' })
|
||||
return score || { collection: 'scores', recitals: {}, scores: {} }
|
||||
}
|
||||
|
||||
async function writeScores(refid: string, scores: Scores) {
|
||||
await DB.Upsert<Scores>(refid, { collection: 'scores' }, scores)
|
||||
}
|
||||
|
||||
const defaultProfile: Profile = {
|
||||
collection: 'profile',
|
||||
|
||||
name: 'GUEST',
|
||||
music: 0,
|
||||
sheet: 0,
|
||||
brooch: 0,
|
||||
hispeed: 0,
|
||||
beatGuide: 1,
|
||||
headphone: 0,
|
||||
judgeBar: 250,
|
||||
group: 0,
|
||||
mode: 0,
|
||||
near: 0,
|
||||
offset: 0,
|
||||
bingo: 0,
|
||||
skill: '0',
|
||||
playCount: 0,
|
||||
todayPlayCount: 0,
|
||||
keyBeam: 0,
|
||||
orbit: 0,
|
||||
noteHeight: 10,
|
||||
noteWidth: 0,
|
||||
judgeWidth: 0,
|
||||
beatVolume: 0,
|
||||
beatType: 0,
|
||||
keyVolume: 0,
|
||||
bgmVolume: 0,
|
||||
note: 0,
|
||||
sf: 0,
|
||||
judgeFX: 0,
|
||||
simple: 0,
|
||||
money: 0,
|
||||
fame: 0,
|
||||
fameId: 0,
|
||||
island: 0,
|
||||
brooches: {
|
||||
'1': {
|
||||
level: 1,
|
||||
watch: 0,
|
||||
steps: 0,
|
||||
new: 0,
|
||||
},
|
||||
},
|
||||
islands: {},
|
||||
kentei: {},
|
||||
cat_stairs: {
|
||||
'0': {
|
||||
total: 0,
|
||||
index: 0,
|
||||
steps: 0,
|
||||
goal: 0
|
||||
}
|
||||
},
|
||||
params: {
|
||||
'1': [0],
|
||||
},
|
||||
musicList: {
|
||||
type_0: Array(32).fill(-1),
|
||||
type_1: Array(32).fill(-1),
|
||||
type_2: Array(32).fill(-1),
|
||||
type_3: Array(32).fill(-1),
|
||||
},
|
||||
musicList2: {
|
||||
type_0: Array(32).fill(-1),
|
||||
type_1: Array(32).fill(-1),
|
||||
type_2: Array(32).fill(-1),
|
||||
type_3: Array(32).fill(-1),
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Profile } from "../models/profile";
|
||||
|
||||
export const fixIndexBug = async (data: {
|
||||
refid: string;
|
||||
confirm: string;
|
||||
}) => {
|
||||
if (data.confirm == "on") {
|
||||
console.warn(`refid "${data.refid}" performs index reset!`)
|
||||
await DB.Update<Profile>(
|
||||
data.refid,
|
||||
{ collection: 'profile' },
|
||||
{ $set: {
|
||||
music: 0,
|
||||
sheet: 0,
|
||||
brooch: 0
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import { get_common_info, get_music_info } from "./handler/common";
|
||||
import { get_musicdata, get_playdata, regist_playdata, set_total_result } from "./handler/player"
|
||||
import { fixIndexBug } from "./handler/webui";
|
||||
|
||||
export function register() {
|
||||
R.GameCode('PAN');
|
||||
|
||||
R.WebUIEvent("nosFixIndexBug", fixIndexBug)
|
||||
|
||||
const MultiRoute = (method: string, handler: EPR | boolean) => {
|
||||
// Helper for register multiple versions.
|
||||
R.Route(method, handler); // First version and Forte.
|
||||
R.Route(`op2_${method}`, handler);
|
||||
};
|
||||
|
||||
const CommonRoute = (method: string, handler: EPR | boolean) =>
|
||||
MultiRoute(`common.${method}`, handler)
|
||||
|
||||
const PlayerRoute = (method: string, handler: EPR | boolean) =>
|
||||
MultiRoute(`player.${method}`, handler)
|
||||
|
||||
// Common
|
||||
CommonRoute('get_common_info', get_common_info);
|
||||
CommonRoute('get_music_info', get_music_info);
|
||||
|
||||
// Player
|
||||
PlayerRoute('get_musicdata', get_musicdata)
|
||||
PlayerRoute('get_playdata', get_playdata)
|
||||
PlayerRoute('regist_playdata', regist_playdata)
|
||||
PlayerRoute('set_total_result', set_total_result)
|
||||
|
||||
//TODO: Fix this things with actual working handler.
|
||||
PlayerRoute('set_stage_result', true)
|
||||
|
||||
R.Unhandled(async (info, data, send) => {
|
||||
if (["eventlog"].includes(info.module)) return;
|
||||
console.error(`Received Unhandled Response on ${info.method} by ${info.model}/${info.module}`)
|
||||
console.error(`Received Request: ${JSON.stringify(data, null, 4)}`)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
export interface Profile {
|
||||
collection: 'profile',
|
||||
|
||||
name: string;
|
||||
playCount: number;
|
||||
todayPlayCount: number;
|
||||
music: number;
|
||||
sheet: number;
|
||||
brooch: number;
|
||||
hispeed: number;
|
||||
beatGuide: number;
|
||||
headphone: number;
|
||||
judgeBar: number;
|
||||
group: number;
|
||||
mode: number;
|
||||
near: number;
|
||||
offset: number;
|
||||
bingo: number;
|
||||
skill: string;
|
||||
keyBeam: number;
|
||||
orbit: number;
|
||||
noteHeight: number;
|
||||
noteWidth: number;
|
||||
judgeWidth: number;
|
||||
beatVolume: number;
|
||||
beatType: number;
|
||||
keyVolume: number;
|
||||
bgmVolume: number;
|
||||
note: number;
|
||||
sf: number;
|
||||
judgeFX: number;
|
||||
simple: number;
|
||||
money: number;
|
||||
fame: number;
|
||||
fameId: number;
|
||||
island: number;
|
||||
params: {
|
||||
[key: string]: number[];
|
||||
};
|
||||
brooches: {
|
||||
[key: string]: {
|
||||
watch: number;
|
||||
level: number;
|
||||
steps: number;
|
||||
new: number;
|
||||
};
|
||||
};
|
||||
islands: {
|
||||
[key: string]: {
|
||||
look: number;
|
||||
select: number;
|
||||
time: number;
|
||||
containers: {
|
||||
[key: string]: {
|
||||
prog: number;
|
||||
rewards: { [key: string]: number };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
kentei: {
|
||||
[key: string]: {
|
||||
stage: number;
|
||||
score: number[];
|
||||
rate: number;
|
||||
flag: number;
|
||||
count: number;
|
||||
};
|
||||
};
|
||||
cat_stairs: {
|
||||
[key: string]: {
|
||||
total: number,
|
||||
index: number,
|
||||
steps: number,
|
||||
goal: number
|
||||
}
|
||||
};
|
||||
musicList: {
|
||||
type_0: number[];
|
||||
type_1: number[];
|
||||
type_2: number[];
|
||||
type_3: number[];
|
||||
};
|
||||
musicList2: {
|
||||
type_0: number[];
|
||||
type_1: number[];
|
||||
type_2: number[];
|
||||
type_3: number[];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
export interface Scores {
|
||||
collection: 'scores',
|
||||
|
||||
recitals: {
|
||||
[key: string]: {
|
||||
count: number;
|
||||
hall: number;
|
||||
cat: number[];
|
||||
audience: number;
|
||||
money: number;
|
||||
fame: number;
|
||||
player: number;
|
||||
score: number;
|
||||
start: string;
|
||||
end: string;
|
||||
};
|
||||
};
|
||||
scores: {
|
||||
[key: string]: {
|
||||
score: number;
|
||||
grade: number;
|
||||
recital: number;
|
||||
count: number;
|
||||
clear: number;
|
||||
multi: number;
|
||||
mode: number;
|
||||
flag: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
type NostalgiaVersions = 'First' | 'Forte' | 'Op2' | 'Op3'
|
||||
type NostalgiaNumericTypes = 'music_index' | 'sheet_type' | 'brooch_index' | 'event_index'
|
||||
|
||||
export class NosVersionHelper {
|
||||
public version: NostalgiaVersions
|
||||
|
||||
private table = { // FIXME: All of Op3 values are placeholder
|
||||
music_index: { First: 87, Forte: 195, Op2: 315, Op3: 500 },
|
||||
brooch_index: { First: 120, Forte: 147, Op2: 148, Op3: 200 },
|
||||
sheet_type: { First: 2, Forte: 2, Op2: 3, Op3: 3 },
|
||||
event_index: { First: 10, Forte: 10, Op2: 17, Op3: 20 }
|
||||
}
|
||||
|
||||
constructor (info: EamuseInfo) {
|
||||
const version = parseInt(info.model.trim().substr(10), 10)
|
||||
if (version >= 2020000000) {
|
||||
this.version = 'Op3'
|
||||
} else if (version >= 2019000000) {
|
||||
this.version = 'Op2'
|
||||
} else if (version >= 2018000000) {
|
||||
this.version = 'Forte'
|
||||
} else {
|
||||
this.version = 'First'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getMusicMaxIndex() {
|
||||
return this.table['music_index'][this.version]
|
||||
}
|
||||
|
||||
getBroochMaxIndex() {
|
||||
return this.table['brooch_index'][this.version]
|
||||
}
|
||||
|
||||
getEventMaxIndex() {
|
||||
return this.table['event_index'][this.version]
|
||||
}
|
||||
|
||||
numericHandler(type: NostalgiaNumericTypes, input: number, def: number = 0) {
|
||||
return input > this.table[type][this.version] ? def : input;
|
||||
}
|
||||
|
||||
isFirstOrForte() {
|
||||
return this.version === 'First' || this.version === 'Forte'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
div
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-account-edit
|
||||
| Fix Error with Login
|
||||
.card-content
|
||||
p If you unable to login after travels of between versions, This may helpful.
|
||||
p Normally, login issue caused you played deleted song last time in previous version that not deleted time.
|
||||
p This page is about reset of last indexes that causing login problem.
|
||||
form(method="post" action="/emit/nosFixIndexBug")
|
||||
.field
|
||||
label.label ID
|
||||
.control
|
||||
input.input(type="text" name="refid", value=refid readonly)
|
||||
.field
|
||||
label.label Are you sure to reset indexes?
|
||||
.control
|
||||
input(type="checkbox" name="confirm")
|
||||
| This is not recoverable. Do this if you have a problem.
|
||||
.field
|
||||
button.button.is-primary(type="submit")
|
||||
span.icon
|
||||
i.mdi.mdi-check
|
||||
span Submit
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "asphyxia-plugins",
|
||||
"version": "1.0.0",
|
||||
"description": "Community Plugins for Asphyxia CORE",
|
||||
"dependencies": {
|
||||
"@types/lodash": "^4.14.153",
|
||||
"@types/node": "^14.0.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
Hello! Pop'n Music
|
||||
==================
|
||||
|
||||
This plugin supports _Hello! Pop'n Music_.
|
||||
|
||||
# ! IMPORTANT !
|
||||
You may need proper data to connect a network. Otherwise, it won't work!
|
||||
@@ -0,0 +1,161 @@
|
||||
import { Profile } from '../models/profile';
|
||||
import { Scores } from '../models/scores';
|
||||
|
||||
export const newProfile: EPR = async (req, data, send) => {
|
||||
const refid = $(data).attr().refid;
|
||||
if (!refid) return send.deny();
|
||||
|
||||
await writeProfile(refid, await readProfile(refid));
|
||||
send.success();
|
||||
};
|
||||
|
||||
export const load: EPR = async (req, data, send) => {
|
||||
try{
|
||||
const refid = $(data).attr().refid;
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const profile = await readProfile(refid);
|
||||
const chara: any[] = [];
|
||||
|
||||
if (profile.unlockAll) {
|
||||
for (let i = 0; i < 11; ++i) {
|
||||
chara.push(K.ATTR({ id: `${i}`, love: '5' }));
|
||||
}
|
||||
} else {
|
||||
for (const i in profile.charas) {
|
||||
chara.push(K.ATTR({ id: `${i}`, love: `${profile.charas[i]}` }));
|
||||
}
|
||||
}
|
||||
|
||||
send.object({
|
||||
last: K.ATTR({
|
||||
chara: `${profile.history.chara}`,
|
||||
level: `${profile.history.level}`,
|
||||
music_id: `${profile.history.mid}`,
|
||||
style: `${profile.history.style}`,
|
||||
}),
|
||||
chara,
|
||||
// threshold: {
|
||||
// '@attr': { id: 0, point: 100 },
|
||||
// },
|
||||
})
|
||||
} catch (e) { console.log(e.stack || e.stacktrace )}
|
||||
};
|
||||
|
||||
export const load_m: EPR = async (req, data, send) => {
|
||||
const refid = $(data).attr().refid;
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const scores = (await readScores(refid)).scores;
|
||||
|
||||
const music: any[] = [];
|
||||
for (const mid in scores) {
|
||||
const style: any[] = [];
|
||||
for (const sid in scores[mid]) {
|
||||
const level: any[] = [];
|
||||
for (const lid in scores[mid][sid]) {
|
||||
level.push(K.ATTR({
|
||||
id: lid,
|
||||
score: `${scores[mid][sid][lid].score}`,
|
||||
clear_type: `${scores[mid][sid][lid].clear}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
style.push(K.ATTR({ id: sid }, { level }));
|
||||
}
|
||||
music.push(K.ATTR({ music_id: mid }, { style }));
|
||||
}
|
||||
|
||||
send.object({
|
||||
music,
|
||||
});
|
||||
};
|
||||
|
||||
export const save_m: EPR = async (req, data, send) => {
|
||||
const refid = $(data).attr().refid;
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const scores = (await readScores(refid)).scores;
|
||||
|
||||
const clear = parseInt($(data).attr().clear_type || '-1');
|
||||
const level = parseInt($(data).attr().level || '-1');
|
||||
const mid = parseInt($(data).attr().music_id || '-1');
|
||||
const score = parseInt($(data).attr().score || '-1');
|
||||
const style = parseInt($(data).attr().style || '-1');
|
||||
|
||||
if (clear < 0 || level < 0 || mid < 0 || score < 0 || style < 0) {
|
||||
return send.success();
|
||||
}
|
||||
|
||||
if (!scores[mid]) {
|
||||
scores[mid] = {};
|
||||
}
|
||||
|
||||
if (!scores[mid][style]) {
|
||||
scores[mid][style] = {};
|
||||
}
|
||||
|
||||
scores[mid][style][level] = {
|
||||
score: Math.max(_.get(scores[mid][style][level], 'score', 0), score),
|
||||
clear: Math.max(_.get(scores[mid][style][level], 'clear', 0), clear),
|
||||
};
|
||||
|
||||
writeScores(refid, { collection: 'scores', scores });
|
||||
|
||||
send.success();
|
||||
};
|
||||
|
||||
export const save: EPR = async (req, data, send) => {
|
||||
const refid = $(data).attr().refid;
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const profile = await readProfile(refid);
|
||||
|
||||
const chara = parseInt($(data).attr('last').chara || '-1');
|
||||
const level = parseInt($(data).attr('last').level || '-1');
|
||||
const love = parseInt($(data).attr('last').love || '-1');
|
||||
const mid = parseInt($(data).attr('last').music_id || '-1');
|
||||
const style = parseInt($(data).attr('last').style || '-1');
|
||||
|
||||
if (chara < 0 || level < 0 || mid < 0 || love < 0 || style < 0) {
|
||||
return send.success();
|
||||
}
|
||||
|
||||
if (!profile.unlockAll) {
|
||||
profile.charas[chara] = _.get(profile.charas, chara, 0) + love;
|
||||
}
|
||||
|
||||
profile.history = {
|
||||
chara,
|
||||
mid,
|
||||
level,
|
||||
style,
|
||||
};
|
||||
|
||||
await writeProfile(refid, profile);
|
||||
|
||||
send.success();
|
||||
};
|
||||
|
||||
async function readProfile(refid: string): Promise<Profile> {
|
||||
const profile = await DB.FindOne<Profile>(refid, { collection: 'profile'} )
|
||||
return profile || {
|
||||
collection: 'profile',
|
||||
unlockAll: false,
|
||||
history: { chara: 0, level: 0, mid: 0, style: 0 },
|
||||
charas: {},
|
||||
}
|
||||
}
|
||||
|
||||
async function writeProfile(refid: string, profile: Profile) {
|
||||
await DB.Upsert<Profile>(refid, { collection: 'profile'}, profile)
|
||||
}
|
||||
|
||||
async function readScores(refid: string): Promise<Scores> {
|
||||
const score = await DB.FindOne<Scores>(refid, { collection: 'scores'} )
|
||||
return score || { collection: 'scores', scores: {}}
|
||||
}
|
||||
|
||||
async function writeScores(refid: string, scores: Scores) {
|
||||
await DB.Upsert<Scores>(refid, { collection: 'scores'}, scores)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Profile } from "../models/profile";
|
||||
|
||||
export const setUnlockState = async (data: {
|
||||
refid: string;
|
||||
unlock_all: string;
|
||||
}) => {
|
||||
await DB.Update<Profile>(
|
||||
data.refid,
|
||||
{ collection: 'profile' },
|
||||
{ $set: {
|
||||
unlockAll: data.unlock_all == "on"
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { load, load_m, newProfile, save, save_m } from "./handler/player";
|
||||
import { setUnlockState } from "./handler/webui";
|
||||
|
||||
const common: EPR = async (req, data, send) => {
|
||||
send.object(
|
||||
{
|
||||
flag: K.ATTR({ id: '1', s1: '1', s2: '1', t: '1' }),
|
||||
cnt_music: K.ITEM('u32', 36),
|
||||
},
|
||||
{ encoding: 'euc-jp' }
|
||||
);
|
||||
};
|
||||
|
||||
const shop: EPR = async (req, data, send) => {
|
||||
send.success()
|
||||
}
|
||||
|
||||
export function register() {
|
||||
R.GameCode("JMP")
|
||||
|
||||
R.WebUIEvent("setUnlockState", setUnlockState)
|
||||
|
||||
// const Route = (method: string, handler: EPR) =>
|
||||
// R.Route(`game.${method}`, handler)
|
||||
|
||||
// Common
|
||||
R.Route("game.common", common)
|
||||
R.Route("game.shop", shop)
|
||||
|
||||
// Player
|
||||
R.Route("game.new", newProfile)
|
||||
R.Route("game.load", load)
|
||||
R.Route("game.load_m", load_m)
|
||||
R.Route("game.save", save)
|
||||
R.Route("game.save_m", save_m)
|
||||
|
||||
|
||||
R.Unhandled()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export interface Profile {
|
||||
collection: 'profile',
|
||||
|
||||
unlockAll: boolean;
|
||||
history: {
|
||||
chara: number;
|
||||
level: number;
|
||||
mid: number;
|
||||
style: number;
|
||||
};
|
||||
charas: { [id: string]: number };
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export interface Scores {
|
||||
collection: 'scores',
|
||||
|
||||
scores: {
|
||||
[mid: string]: {
|
||||
[style: string]: {
|
||||
[level: string]: {
|
||||
score: number;
|
||||
clear: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//DATA//
|
||||
profile: DB.FindOne(refid, { collection: 'profile' })
|
||||
|
||||
div
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-account-edit
|
||||
| Detail Change
|
||||
.card-content
|
||||
form(method="post" action="/emit/setUnlockState")
|
||||
.field
|
||||
label.label ID
|
||||
.control
|
||||
input.input(type="text" name="refid", value=refid readonly)
|
||||
.field
|
||||
label.label Unlock All
|
||||
.control
|
||||
input(type="checkbox" name="unlock_all", checked=profile.unlockAll)
|
||||
| Turn this on to max out all characters and unlock 11 hidden songs.
|
||||
.field
|
||||
button.button.is-primary(type="submit")
|
||||
span.icon
|
||||
i.mdi.mdi-check
|
||||
span Submit
|
||||
@@ -0,0 +1,23 @@
|
||||
# Pop'n Music
|
||||
|
||||
Plugin Version: **v1.1.0**
|
||||
|
||||
## Supported Versions
|
||||
- pop'n music 23 Eclale
|
||||
- pop'n music 24 Usagi to Neko to Shōnen no Yume
|
||||
- pop'n music 25 peace
|
||||
|
||||
## Changelog
|
||||
#### 1.1.0
|
||||
Update phase data : All versions are on latest phase.
|
||||
|
||||
#### 1.0.0
|
||||
Initial Release.
|
||||
|
||||
## How to import data from non-core Asphyxia
|
||||
To import data, you have to :
|
||||
* Create your popn profile in Asphyxia-core. You just have to insert your card in the game and follow the process until coming to the select mode select screen. Once here, quit the game.
|
||||
* Create a backup of your savedata.db file (in case something goes wrong).
|
||||
* In the web UI of Asphyxia, go to POPN -> Profile and click detail on your profile
|
||||
* Put the content of your non-core asphyxia popn music files in the text fields (pop.json and popn_scores.json) and click Import.
|
||||
* Data is imported. Run the game, insert your card and your scores are available.
|
||||
@@ -0,0 +1,322 @@
|
||||
import { getVersion } from './utils';
|
||||
|
||||
interface Phase {
|
||||
id: number;
|
||||
p: number;
|
||||
}
|
||||
|
||||
const PHASE23 : Phase[] = [
|
||||
{ id: 0, p: 16 },
|
||||
{ id: 1, p: 3 },
|
||||
{ id: 2, p: 1 },
|
||||
{ id: 3, p: 2 },
|
||||
{ id: 4, p: 1 },
|
||||
{ id: 5, p: 2 },
|
||||
{ id: 6, p: 1 },
|
||||
{ id: 7, p: 4 },
|
||||
{ id: 8, p: 3 },
|
||||
{ id: 9, p: 4 },
|
||||
{ id: 10, p: 4 },
|
||||
{ id: 11, p: 1 },
|
||||
{ id: 12, p: 1 },
|
||||
{ id: 13, p: 4 },
|
||||
];
|
||||
|
||||
const PHASE24 : Phase[] = [
|
||||
{ id: 0, p: 11 },
|
||||
{ id: 1, p: 2 },
|
||||
{ id: 2, p: 2 },
|
||||
{ id: 3, p: 4 },
|
||||
{ id: 4, p: 1 },
|
||||
{ id: 5, p: 1 },
|
||||
{ id: 6, p: 1 },
|
||||
{ id: 7, p: 1 },
|
||||
{ id: 8, p: 2 },
|
||||
{ id: 9, p: 2 },
|
||||
{ id: 10, p: 15 },
|
||||
{ id: 11, p: 1 },
|
||||
{ id: 12, p: 2 },
|
||||
{ id: 13, p: 1 },
|
||||
];
|
||||
|
||||
const PHASE25 : Phase[] = [
|
||||
{ id: 0, p: 23 },
|
||||
{ id: 1, p: 4 },
|
||||
{ id: 2, p: 2 },
|
||||
{ id: 3, p: 4 },
|
||||
{ id: 4, p: 1 },
|
||||
{ id: 5, p: 1 },
|
||||
{ id: 6, p: 1 },
|
||||
{ id: 7, p: 1 },
|
||||
{ id: 8, p: 2 },
|
||||
{ id: 9, p: 2 },
|
||||
{ id: 10, p: 30 },
|
||||
{ id: 11, p: 1 },
|
||||
{ id: 12, p: 2 },
|
||||
{ id: 13, p: 1 },
|
||||
{ id: 14, p: 39 },
|
||||
{ id: 15, p: 2 },
|
||||
{ id: 16, p: 3 },
|
||||
{ id: 17, p: 8 },
|
||||
{ id: 18, p: 1 },
|
||||
{ id: 19, p: 1 },
|
||||
{ id: 20, p: 13 },
|
||||
{ id: 21, p: 20 },
|
||||
{ id: 22, p: 2 },
|
||||
{ id: 23, p: 1 },
|
||||
{ id: 24, p: 1 },
|
||||
];
|
||||
|
||||
export const getInfo = (req: EamuseInfo) => {
|
||||
const version = getVersion(req);
|
||||
|
||||
if (version == 'v23') {
|
||||
return getInfo23();
|
||||
} else if (version == 'v24') {
|
||||
if(req.model == 'M39:J:A:A:2020092800') {
|
||||
return getInfo24(PHASE25);
|
||||
} else {
|
||||
return getInfo24(PHASE24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getInfo23 = () => {
|
||||
const result: any = {
|
||||
phase: [],
|
||||
area: [],
|
||||
goods: [],
|
||||
};
|
||||
|
||||
for (const phase of PHASE23) {
|
||||
result.phase.push({
|
||||
event_id: K.ITEM('s16', phase.id),
|
||||
phase: K.ITEM('s16', phase.p),
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 100; ++i) {
|
||||
result.area.push({
|
||||
area_id: K.ITEM('s16', i),
|
||||
end_date: K.ITEM('u64', BigInt(0)),
|
||||
medal_id: K.ITEM('s16', i),
|
||||
is_limit: K.ITEM('bool', 0),
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 420; ++i) {
|
||||
result.goods.push({
|
||||
goods_id: K.ITEM('s16', i),
|
||||
price: K.ITEM('s32', i <= 80 ? 60 : 100),
|
||||
goods_type: K.ITEM('s16', 0),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const getInfo24 = (phaseData : Phase[]) => {
|
||||
const result: any = {
|
||||
phase: [],
|
||||
goods: [],
|
||||
};
|
||||
|
||||
for (const phase of phaseData) {
|
||||
result.phase.push({
|
||||
event_id: K.ITEM('s16', phase.id),
|
||||
phase: K.ITEM('s16', phase.p),
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 3; i <= 96; ++i) {
|
||||
result.goods.push({
|
||||
item_id: K.ITEM('s32', i),
|
||||
item_type: K.ITEM('s16', 3),
|
||||
price: K.ITEM('s32', 60),
|
||||
goods_type: K.ITEM('s16', 0),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const M39_EXTRA_DATA: {
|
||||
[ver: string]: {
|
||||
[field: string]: {
|
||||
path: string;
|
||||
type: string;
|
||||
default: any;
|
||||
isArray?: true;
|
||||
};
|
||||
};
|
||||
} = {
|
||||
v23: {
|
||||
tutorial: { type: 's8', path: 'account', default: 0 },
|
||||
area_id: { type: 's16', path: 'account', default: 0 },
|
||||
lumina: { type: 's16', path: 'account', default: 300 },
|
||||
medal_set: { type: 's16', path: 'account', default: [0, 0, 0, 0], isArray: true },
|
||||
read_news: { type: 's16', path: 'account', default: 0 },
|
||||
staff: { type: 's8', path: 'account', default: 0 },
|
||||
item_type: { type: 's16', path: 'account', default: 0 },
|
||||
item_id: { type: 's16', path: 'account', default: 0 },
|
||||
is_conv: { type: 's8', path: 'account', default: 0 },
|
||||
active_fr_num: { type: 'u8', path: 'account', default: 0 },
|
||||
nice: { type: 's16', path: 'account', default: Array(30).fill(-1), isArray: true },
|
||||
favorite_chara: { type: 's16', path: 'account', default: Array(20).fill(-1), isArray: true },
|
||||
special_area: { type: 's16', path: 'account', default: Array(8).fill(0), isArray: true },
|
||||
chocolate_charalist: {
|
||||
type: 's16',
|
||||
path: 'account',
|
||||
default: Array(5).fill(-1),
|
||||
isArray: true,
|
||||
},
|
||||
teacher_setting: { type: 's16', path: 'account', default: Array(10).fill(0), isArray: true },
|
||||
license_data: { type: 's16', path: 'account', default: [-1, -1], isArray: true },
|
||||
welcom_pack: { type: 'bool', path: 'account', default: 1 },
|
||||
meteor_flg: { type: 'bool', path: 'account', default: 1 },
|
||||
|
||||
hispeed: { type: 's16', path: 'option', default: 0 },
|
||||
popkun: { type: 'u8', path: 'option', default: 0 },
|
||||
hidden: { type: 'bool', path: 'option', default: 0 },
|
||||
hidden_rate: { type: 's16', path: 'option', default: 0 },
|
||||
sudden: { type: 'bool', path: 'option', default: 0 },
|
||||
sudden_rate: { type: 's16', path: 'option', default: 0 },
|
||||
randmir: { type: 's8', path: 'option', default: 0 },
|
||||
gauge_type: { type: 's8', path: 'option', default: 0 },
|
||||
ojama_0: { type: 'u8', path: 'option', default: 0 },
|
||||
ojama_1: { type: 'u8', path: 'option', default: 0 },
|
||||
forever_0: { type: 'bool', path: 'option', default: 0 },
|
||||
forever_1: { type: 'bool', path: 'option', default: 0 },
|
||||
full_setting: { type: 'bool', path: 'option', default: 0 },
|
||||
judge: { type: 'u8', path: 'option', default: 0 },
|
||||
|
||||
ep: { type: 'u16', path: 'info', default: 0 },
|
||||
|
||||
effect_left: { type: 'u16', path: 'customize', default: 0 },
|
||||
effect_center: { type: 'u16', path: 'customize', default: 0 },
|
||||
effect_right: { type: 'u16', path: 'customize', default: 0 },
|
||||
hukidashi: { type: 'u16', path: 'customize', default: 0 },
|
||||
comment_1: { type: 'u16', path: 'customize', default: 0 },
|
||||
comment_2: { type: 'u16', path: 'customize', default: 0 },
|
||||
|
||||
mode: { type: 'u8', path: 'config', default: 0 },
|
||||
chara: { type: 's16', path: 'config', default: -1 },
|
||||
music: { type: 's16', path: 'config', default: -1 },
|
||||
sheet: { type: 'u8', path: 'config', default: 0 },
|
||||
category: { type: 's8', path: 'config', default: -1 },
|
||||
sub_category: { type: 's8', path: 'config', default: -1 },
|
||||
chara_category: { type: 's8', path: 'config', default: -1 },
|
||||
course_id: { type: 's16', path: 'config', default: 0 },
|
||||
course_folder: { type: 's8', path: 'config', default: 0 },
|
||||
ms_banner_disp: { type: 's8', path: 'config', default: 0 },
|
||||
ms_down_info: { type: 's8', path: 'config', default: 0 },
|
||||
ms_side_info: { type: 's8', path: 'config', default: 0 },
|
||||
ms_raise_type: { type: 's8', path: 'config', default: 0 },
|
||||
ms_rnd_type: { type: 's8', path: 'config', default: 0 },
|
||||
|
||||
enemy_medal: { type: 's16', path: 'event', default: 0 },
|
||||
hp: { type: 's16', path: 'event', default: 0 },
|
||||
|
||||
valid: { type: 's8', path: 'custom_cate', default: 0 },
|
||||
lv_min: { type: 's8', path: 'custom_cate', default: -1 },
|
||||
lv_max: { type: 's8', path: 'custom_cate', default: -1 },
|
||||
medal_min: { type: 's8', path: 'custom_cate', default: -1 },
|
||||
medal_max: { type: 's8', path: 'custom_cate', default: -1 },
|
||||
friend_no: { type: 's8', path: 'custom_cate', default: -1 },
|
||||
score_flg: { type: 's8', path: 'custom_cate', default: -1 },
|
||||
},v24: {
|
||||
enemy_medal: { type: 's16', path: 'event', default: 0 },
|
||||
hp: { type: 's16', path: 'event', default: 0 },
|
||||
|
||||
tutorial: { type: 's16', path: 'account', default: -1 },
|
||||
area_id: { type: 's16', path: 'account', default: 51 },
|
||||
lumina: { type: 's16', path: 'account', default: 0 },
|
||||
medal_set: { type: 's16', path: 'account', default: [0, 0], isArray: true },
|
||||
read_news: { type: 's16', path: 'account', default: 0 },
|
||||
staff: { type: 's8', path: 'account', default: 0 },
|
||||
is_conv: { type: 's8', path: 'account', default: 0 },
|
||||
item_type: { type: 's16', path: 'account', default: 0 },
|
||||
item_id: { type: 's16', path: 'account', default: 0 },
|
||||
license_data: { type: 's16', path: 'account', default: Array(10).fill(-1), isArray: true },
|
||||
active_fr_num: { type: 'u8', path: 'account', default: 0 },
|
||||
nice: { type: 's16', path: 'account', default: Array(30).fill(-1), isArray: true },
|
||||
favorite_chara: { type: 's16', path: 'account', default: Array(10).fill(-1), isArray: true },
|
||||
special_area: { type: 's16', path: 'account', default: Array(8).fill(-1), isArray: true },
|
||||
chocolate_charalist: {
|
||||
type: 's16',
|
||||
path: 'account',
|
||||
default: Array(5).fill(-1),
|
||||
isArray: true,
|
||||
},
|
||||
chocolate_sp_chara: { type: 's32', path: 'account', default: 0 },
|
||||
chocolate_pass_cnt: { type: 's32', path: 'account', default: 0 },
|
||||
chocolate_hon_cnt: { type: 's32', path: 'account', default: 0 },
|
||||
chocolate_giri_cnt: { type: 's32', path: 'account', default: 0 },
|
||||
chocolate_kokyu_cnt: { type: 's32', path: 'account', default: 0 },
|
||||
teacher_setting: { type: 's16', path: 'account', default: Array(10).fill(-1), isArray: true },
|
||||
welcom_pack: { type: 'bool', path: 'account', default: 0 },
|
||||
meteor_flg: { type: 'bool', path: 'account', default: 0 },
|
||||
use_navi: { type: 's16', path: 'account', default: 0 },
|
||||
ranking_node: { type: 's32', path: 'account', default: 0 },
|
||||
chara_ranking_kind_id: { type: 's32', path: 'account', default: 0 },
|
||||
navi_evolution_flg: { type: 's8', path: 'account', default: 0 },
|
||||
ranking_news_last_no: { type: 's32', path: 'account', default: 0 },
|
||||
power_point: { type: 's32', path: 'account', default: 0 },
|
||||
player_point: { type: 's32', path: 'account', default: 0 },
|
||||
power_point_list: { type: 's32', path: 'account', default: [0], isArray: true },
|
||||
|
||||
mode: { type: 'u8', path: 'config', default: 0 },
|
||||
chara: { type: 's16', path: 'config', default: 0 },
|
||||
music: { type: 's16', path: 'config', default: 0 },
|
||||
sheet: { type: 'u8', path: 'config', default: 0 },
|
||||
category: { type: 's8', path: 'config', default: 0 },
|
||||
sub_category: { type: 's8', path: 'config', default: 0 },
|
||||
chara_category: { type: 's8', path: 'config', default: 0 }, // check
|
||||
story_id: { type: 's16', path: 'config', default: 0 },
|
||||
ms_banner_disp: { type: 's8', path: 'config', default: 0 },
|
||||
ms_down_info: { type: 's8', path: 'config', default: 0 },
|
||||
ms_side_info: { type: 's8', path: 'config', default: 0 },
|
||||
ms_raise_type: { type: 's8', path: 'config', default: 0 },
|
||||
ms_rnd_type: { type: 's8', path: 'config', default: 0 },
|
||||
banner_sort: { type: 's8', path: 'config', default: 0 },
|
||||
course_id: { type: 's16', path: 'config', default: 0 },
|
||||
course_folder: { type: 's8', path: 'config', default: 0 },
|
||||
story_folder: { type: 's8', path: 'config', default: 0 },
|
||||
|
||||
hispeed: { type: 's16', path: 'option', default: 10 },
|
||||
popkun: { type: 'u8', path: 'option', default: 0 },
|
||||
hidden: { type: 'bool', path: 'option', default: 0 },
|
||||
hidden_rate: { type: 's16', path: 'option', default: -1 },
|
||||
sudden: { type: 'bool', path: 'option', default: 0 },
|
||||
sudden_rate: { type: 's16', path: 'option', default: -1 },
|
||||
randmir: { type: 's8', path: 'option', default: 0 },
|
||||
gauge_type: { type: 's8', path: 'option', default: 0 },
|
||||
ojama_0: { type: 'u8', path: 'option', default: 0 },
|
||||
ojama_1: { type: 'u8', path: 'option', default: 0 },
|
||||
forever_0: { type: 'bool', path: 'option', default: 0 },
|
||||
forever_1: { type: 'bool', path: 'option', default: 0 },
|
||||
full_setting: { type: 'bool', path: 'option', default: 0 },
|
||||
guide_se: { type: 's8', path: 'option', default: 0 },
|
||||
judge: { type: 'u8', path: 'option', default: 0 },
|
||||
slow: { type: 's16', path: 'option', default: 0 },
|
||||
fast: { type: 's16', path: 'option', default: 0 },
|
||||
|
||||
valid: { type: 's8', path: 'custom_cate', default: 0 },
|
||||
lv_min: { type: 's8', path: 'custom_cate', default: 0 },
|
||||
lv_max: { type: 's8', path: 'custom_cate', default: 0 },
|
||||
medal_min: { type: 's8', path: 'custom_cate', default: 0 },
|
||||
medal_max: { type: 's8', path: 'custom_cate', default: 0 },
|
||||
friend_no: { type: 's8', path: 'custom_cate', default: 0 },
|
||||
score_flg: { type: 's8', path: 'custom_cate', default: 0 },
|
||||
|
||||
ep: { type: 'u16', path: 'info', default: 0 },
|
||||
ap: { type: 'u16', path: 'info', default: 0 },
|
||||
|
||||
effect_left: { type: 'u16', path: 'customize', default: 0 },
|
||||
effect_center: { type: 'u16', path: 'customize', default: 0 },
|
||||
effect_right: { type: 'u16', path: 'customize', default: 0 },
|
||||
hukidashi: { type: 'u16', path: 'customize', default: 0 },
|
||||
comment_1: { type: 'u16', path: 'customize', default: 0 },
|
||||
comment_2: { type: 'u16', path: 'customize', default: 0 },
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,519 @@
|
||||
import { Profile } from '../models/profile';
|
||||
import { Scores } from '../models/scores';
|
||||
import { getInfo, M39_EXTRA_DATA } from './common';
|
||||
import { getVersion } from './utils';
|
||||
|
||||
const getPlayer = async (refid: string, version: string, name?: string) => {
|
||||
const profile = await readProfile(refid);
|
||||
|
||||
if (name && name.length > 0) {
|
||||
profile.name = name;
|
||||
await writeProfile(refid, profile);
|
||||
}
|
||||
|
||||
let player: any = {
|
||||
result: K.ITEM('s8', 0),
|
||||
account: {
|
||||
name: K.ITEM('str', profile.name),
|
||||
g_pm_id: K.ITEM('str', 'ASPHYXIAPLAY'),
|
||||
|
||||
// Fixed stats
|
||||
total_play_cnt: K.ITEM('s16', 100),
|
||||
today_play_cnt: K.ITEM('s16', 50),
|
||||
consecutive_days: K.ITEM('s16', 365),
|
||||
total_days: K.ITEM('s16', 366),
|
||||
interval_day: K.ITEM('s16', 1),
|
||||
|
||||
// TODO: do these
|
||||
my_best: K.ARRAY('s16', Array(10).fill(-1)),
|
||||
latest_music: K.ARRAY('s16', [-1, -1, -1, -1, -1]),
|
||||
},
|
||||
|
||||
netvs: {
|
||||
record: K.ARRAY('s16', [0, 0, 0, 0, 0, 0]),
|
||||
dialog: [
|
||||
K.ITEM('str', 'dialog#0'),
|
||||
K.ITEM('str', 'dialog#1'),
|
||||
K.ITEM('str', 'dialog#2'),
|
||||
K.ITEM('str', 'dialog#3'),
|
||||
K.ITEM('str', 'dialog#4'),
|
||||
K.ITEM('str', 'dialog#5'),
|
||||
],
|
||||
ojama_condition: K.ARRAY('s8', Array(74).fill(0)),
|
||||
set_ojama: K.ARRAY('s8', [0, 0, 0]),
|
||||
set_recommend: K.ARRAY('s8', [0, 0, 0]),
|
||||
netvs_play_cnt: K.ITEM('u32', 0),
|
||||
},
|
||||
|
||||
eaappli: {
|
||||
relation: K.ITEM('s8', 0),
|
||||
},
|
||||
|
||||
stamp: [],
|
||||
item: [],
|
||||
chara_param: [],
|
||||
medal: [],
|
||||
};
|
||||
|
||||
const profileStamps = profile.stamps[version] || { '0': 0 };
|
||||
|
||||
for (const stamp in profileStamps) {
|
||||
player.stamp.push({
|
||||
stamp_id: K.ITEM('s16', parseInt(stamp, 10)),
|
||||
cnt: K.ITEM('s16', profileStamps[stamp]),
|
||||
});
|
||||
}
|
||||
|
||||
const profileCharas = profile.charas[version] || {};
|
||||
|
||||
for (const chara_id in profileCharas) {
|
||||
player.chara_param.push({
|
||||
chara_id: K.ITEM('u16', parseInt(chara_id, 10)),
|
||||
friendship: K.ITEM('u16', profileCharas[chara_id]),
|
||||
});
|
||||
}
|
||||
|
||||
const profileMedals = profile.medals[version] || {};
|
||||
|
||||
for (const medal_id in profileMedals) {
|
||||
const medal = profileMedals[medal_id];
|
||||
player.medal.push({
|
||||
medal_id: K.ITEM('s16', parseInt(medal_id, 10)),
|
||||
level: K.ITEM('s16', medal.level),
|
||||
exp: K.ITEM('s32', medal.exp),
|
||||
set_count: K.ITEM('s32', medal.set_count),
|
||||
get_count: K.ITEM('s32', medal.get_count),
|
||||
});
|
||||
}
|
||||
|
||||
const profileItems = profile.items[version] || {};
|
||||
|
||||
for (const key in profileItems) {
|
||||
const keyData = key.split(':');
|
||||
const type = parseInt(keyData[0], 10);
|
||||
const id = parseInt(keyData[1], 10);
|
||||
|
||||
const item: any = {
|
||||
type: K.ITEM('u8', type),
|
||||
id: K.ITEM('u16', id),
|
||||
param: K.ITEM('u16', profileItems[key]),
|
||||
is_new: K.ITEM('bool', 0),
|
||||
};
|
||||
|
||||
if (version != 'v23') {
|
||||
item.get_time = K.ITEM('u64', BigInt(0));
|
||||
}
|
||||
|
||||
player.item.push(item);
|
||||
}
|
||||
|
||||
// EXTRA DATA
|
||||
if (M39_EXTRA_DATA[version]) {
|
||||
for (const field in M39_EXTRA_DATA[version]) {
|
||||
const fieldMetaData = M39_EXTRA_DATA[version][field];
|
||||
if (fieldMetaData.isArray) {
|
||||
_.set(
|
||||
player,
|
||||
`${fieldMetaData.path}.${field}`,
|
||||
K.ARRAY(
|
||||
fieldMetaData.type as any,
|
||||
_.get(profile, `extras.${version}.${field}`, fieldMetaData.default)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
_.set(
|
||||
player,
|
||||
`${fieldMetaData.path}.${field}`,
|
||||
K.ITEM(
|
||||
fieldMetaData.type as any,
|
||||
_.get(profile, `extras.${version}.${field}`, fieldMetaData.default)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extra Fixed Data
|
||||
if (version == 'v24') {
|
||||
player = {
|
||||
...player,
|
||||
navi_data: {
|
||||
raisePoint: K.ARRAY('s32', [-1, -1, -1, -1, -1]),
|
||||
navi_param: {
|
||||
navi_id: K.ITEM('u16', 0),
|
||||
friendship: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
|
||||
area: {
|
||||
area_id: K.ITEM('u32', 0),
|
||||
chapter_index: K.ITEM('u8', 0),
|
||||
gauge_point: K.ITEM('u16', 0),
|
||||
is_cleared: K.ITEM('bool', 0),
|
||||
diary: K.ITEM('u32', 0),
|
||||
},
|
||||
|
||||
mission: [
|
||||
{
|
||||
mission_id: K.ITEM('u32', 170),
|
||||
gauge_point: K.ITEM('u32', 0),
|
||||
mission_comp: K.ITEM('u32', 0),
|
||||
},
|
||||
{
|
||||
mission_id: K.ITEM('u32', 157),
|
||||
gauge_point: K.ITEM('u32', 0),
|
||||
mission_comp: K.ITEM('u32', 0),
|
||||
},
|
||||
{
|
||||
mission_id: K.ITEM('u32', 47),
|
||||
gauge_point: K.ITEM('u32', 0),
|
||||
mission_comp: K.ITEM('u32', 0),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
return player;
|
||||
};
|
||||
|
||||
export const newPlayer: EPR = async (req, data, send) => {
|
||||
const refid = $(data).str('ref_id');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const version = getVersion(req);
|
||||
const name = $(data).str('name');
|
||||
|
||||
send.object(await getPlayer(refid, version, name));
|
||||
};
|
||||
|
||||
export const read: EPR = async (req, data, send) => {
|
||||
const refid = $(data).str('ref_id');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const version = getVersion(req);
|
||||
|
||||
send.object(await getPlayer(refid, version));
|
||||
};
|
||||
|
||||
export const readScore: EPR = async (req, data, send) => {
|
||||
const refid = $(data).str('ref_id');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const scoresData = await readScores(refid);
|
||||
const version = getVersion(req);
|
||||
const result: any = {
|
||||
music: [],
|
||||
};
|
||||
|
||||
if(version == 'v24') {
|
||||
for (const key in scoresData.scores) {
|
||||
const keyData = key.split(':');
|
||||
const score = scoresData.scores[key];
|
||||
const music = parseInt(keyData[0], 10);
|
||||
const sheet = parseInt(keyData[1], 10);
|
||||
result.music.push({
|
||||
music_num: K.ITEM('s16', music),
|
||||
sheet_num: K.ITEM('u8', sheet),
|
||||
score: K.ITEM('s32', score.score),
|
||||
clear_type: K.ITEM('u8', score.clear_type || 0),
|
||||
clear_rank: K.ITEM('u8', score.clear_rank || 0),
|
||||
cnt: K.ITEM('s16', score.cnt),
|
||||
});
|
||||
}
|
||||
} else if(version == 'v23') {
|
||||
for (const key in scoresData.scores) {
|
||||
const keyData = key.split(':');
|
||||
const score = scoresData.scores[key];
|
||||
const music = parseInt(keyData[0], 10);
|
||||
const sheet = parseInt(keyData[1], 10);
|
||||
result.music.push({
|
||||
music_num: K.ITEM('s16', music),
|
||||
sheet_num: K.ITEM('u8', sheet),
|
||||
score: K.ITEM('s32', score.score),
|
||||
clear_type: K.ITEM('u8', score.clearmedal || 0),
|
||||
cnt: K.ITEM('s16', score.cnt),
|
||||
old_score: K.ITEM('s32', 0),
|
||||
old_clear_type: K.ITEM('u8', 0),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
send.object(result);
|
||||
};
|
||||
|
||||
export const writeMusic: EPR = async (req, data, send) => {
|
||||
const refid = $(data).str('ref_id');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const music = $(data).number('music_num', -1);
|
||||
const sheet = $(data).number('sheet_num', -1);
|
||||
const clear_type = $(data).number('clear_type');
|
||||
const clear_rank = $(data).number('clear_rank');
|
||||
const clearmedal = $(data).number('clearmedal');
|
||||
const score = $(data).number('score', 0);
|
||||
|
||||
if (music < 0 || sheet < 0) {
|
||||
return send.deny();
|
||||
}
|
||||
|
||||
const key = `${music}:${sheet}`;
|
||||
|
||||
const scoresData = await readScores(refid);
|
||||
if (!scoresData.scores[key]) {
|
||||
scoresData.scores[key] = {
|
||||
score,
|
||||
cnt: 1,
|
||||
};
|
||||
} else {
|
||||
scoresData.scores[key] = {
|
||||
score: Math.max(score, scoresData.scores[key].score),
|
||||
cnt: scoresData.scores[key].cnt + 1,
|
||||
};
|
||||
}
|
||||
|
||||
if (clear_type) {
|
||||
scoresData.scores[key].clear_type = Math.max(clear_type, scoresData.scores[key].clear_type || 0);
|
||||
}
|
||||
|
||||
if (clear_rank) {
|
||||
scoresData.scores[key].clear_rank = Math.max(clear_rank, scoresData.scores[key].clear_rank || 0);
|
||||
}
|
||||
|
||||
if (clearmedal) {
|
||||
scoresData.scores[key].clearmedal = Math.max(clearmedal, scoresData.scores[key].clearmedal || 0);
|
||||
}
|
||||
|
||||
writeScores(refid, scoresData);
|
||||
|
||||
const version = getVersion(req);
|
||||
if (version == 'v24') {
|
||||
|
||||
const p = await readProfile(refid);
|
||||
|
||||
const settings = [
|
||||
'hispeed',
|
||||
'popkun',
|
||||
'hidden',
|
||||
'hidden_rate',
|
||||
'sudden',
|
||||
'sudden_rate',
|
||||
'randmir',
|
||||
'ojama_0',
|
||||
'ojama_1',
|
||||
'forever_0',
|
||||
'forever_1',
|
||||
'full_setting',
|
||||
'guide_se',
|
||||
'judge',
|
||||
'slow',
|
||||
'fast',
|
||||
'mode',
|
||||
];
|
||||
|
||||
for (const setting of settings) {
|
||||
_.set(
|
||||
p,
|
||||
`extras.v24.${setting}`,
|
||||
_.get(data, `${setting}.@content.0`, _.get(p, `extras.v24.${setting}`, 0))
|
||||
);
|
||||
}
|
||||
|
||||
_.set(p, `extras.v24.tutorial`, 32767);
|
||||
|
||||
const chara = $(data).number('chara_num');
|
||||
if (chara) {
|
||||
_.set(p, 'extras.v24.chara', chara);
|
||||
}
|
||||
|
||||
const music = $(data).number('music_num');
|
||||
if (music) {
|
||||
_.set(p, 'extras.v24.music', music);
|
||||
}
|
||||
|
||||
const sheet = $(data).number('sheet_num');
|
||||
if (sheet) {
|
||||
_.set(p, 'extras.v24.sheet', sheet);
|
||||
}
|
||||
|
||||
writeProfile(refid, p);
|
||||
}
|
||||
|
||||
send.success();
|
||||
};
|
||||
|
||||
export const write: EPR = async (req, data, send) => {
|
||||
const refid = $(data).str('ref_id');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const version = getVersion(req);
|
||||
const profile = await readProfile(refid);
|
||||
|
||||
const writeData: Partial<Profile> = {};
|
||||
|
||||
if (M39_EXTRA_DATA[version]) {
|
||||
const extraFields = M39_EXTRA_DATA[version];
|
||||
for (const field in extraFields) {
|
||||
const fieldMetaData = extraFields[field];
|
||||
let value = _.get(data, `${fieldMetaData.path}.${field}.@content`);
|
||||
if ( value == 'undefined' && value == null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_.isArray(value) && value.length == 1) {
|
||||
value = value[0];
|
||||
}
|
||||
|
||||
_.set(writeData, `extras.${version}.${field}`, value);
|
||||
}
|
||||
}
|
||||
|
||||
const newProfile:Profile = {
|
||||
...profile,
|
||||
...writeData,
|
||||
};
|
||||
|
||||
// stamps
|
||||
let stamps = _.get(data, 'stamp', []);
|
||||
if (!newProfile.stamps[version]) {
|
||||
newProfile.stamps[version] = { '0': 0 };
|
||||
}
|
||||
|
||||
if (!_.isArray(stamps)) {
|
||||
stamps = [stamps];
|
||||
}
|
||||
|
||||
for (const stamp of stamps) {
|
||||
const id = $(stamp).number('stamp_id');
|
||||
const cnt = $(stamp).number('cnt');
|
||||
|
||||
newProfile.stamps[version][id] = cnt;
|
||||
}
|
||||
|
||||
// medals
|
||||
let medals = _.get(data, 'medal', []);
|
||||
if (!newProfile.medals[version]) {
|
||||
newProfile.medals[version] = {};
|
||||
}
|
||||
|
||||
if (!_.isArray(medals)) {
|
||||
medals = [medals];
|
||||
}
|
||||
|
||||
for (const medal of medals) {
|
||||
const id = $(medal).number('medal_id');
|
||||
const level = $(medal).number('level');
|
||||
const exp = $(medal).number('exp');
|
||||
const set_count = $(medal).number('set_count');
|
||||
const get_count = $(medal).number('get_count');
|
||||
|
||||
newProfile.medals[version][id] = {
|
||||
level,
|
||||
exp,
|
||||
set_count,
|
||||
get_count,
|
||||
};
|
||||
}
|
||||
|
||||
// items
|
||||
let items = _.get(data, 'item', []);
|
||||
if (!newProfile.items[version]) {
|
||||
newProfile.items[version] = {};
|
||||
}
|
||||
|
||||
if (!_.isArray(items)) {
|
||||
items = [items];
|
||||
}
|
||||
|
||||
for (const item of items) {
|
||||
const type = $(item).number('type');
|
||||
const id = $(item).number('id');
|
||||
const param = $(item).number('param');
|
||||
|
||||
const key = `${type}:${id}`;
|
||||
|
||||
newProfile.items[version][key] = param;
|
||||
}
|
||||
|
||||
// charas
|
||||
let charas = _.get(data, 'chara_param', []);
|
||||
if (!newProfile.charas[version]) {
|
||||
newProfile.charas[version] = {};
|
||||
}
|
||||
|
||||
if (!_.isArray(charas)) {
|
||||
charas = [charas];
|
||||
}
|
||||
|
||||
for (const chara of charas) {
|
||||
const id = $(chara).number('chara_id');
|
||||
const param = $(chara).number('friendship');
|
||||
|
||||
newProfile.charas[version][id] = param;
|
||||
}
|
||||
|
||||
await writeProfile(refid, newProfile);
|
||||
send.success();
|
||||
};
|
||||
|
||||
export const start: EPR = async (req, data, send) => {
|
||||
const result = {
|
||||
play_id: K.ITEM('s32', 1),
|
||||
...getInfo(req),
|
||||
};
|
||||
await send.object(result);
|
||||
};
|
||||
|
||||
export const buy: EPR = async (req, data, send) => {
|
||||
const refid = $(data).str('ref_id');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const type = $(data).number('type', -1);
|
||||
const id = $(data).number('id', -1);
|
||||
const param = $(data).number('param', 0);
|
||||
const version = getVersion(req);
|
||||
|
||||
if (type < 0 || id < 0) {
|
||||
return send.deny();
|
||||
}
|
||||
|
||||
const key = `${type}:${id}`;
|
||||
|
||||
const profile = await readProfile(refid);
|
||||
if (!profile.items[version]) {
|
||||
profile.items[version] = {};
|
||||
}
|
||||
profile.items[version][key] = param;
|
||||
await writeProfile(refid, profile);
|
||||
send.success();
|
||||
};
|
||||
|
||||
const defaultProfile:Profile = {
|
||||
collection: 'profile',
|
||||
|
||||
name: 'ゲスト',
|
||||
|
||||
stamps: {},
|
||||
medals: {},
|
||||
items: {},
|
||||
charas: {},
|
||||
|
||||
extras: {},
|
||||
};
|
||||
|
||||
async function readProfile(refid: string): Promise<Profile> {
|
||||
const profile = await DB.FindOne<Profile>(refid, { collection: 'profile'} )
|
||||
return profile || defaultProfile
|
||||
}
|
||||
|
||||
async function writeProfile(refid: string, profile: Profile) {
|
||||
await DB.Upsert<Profile>(refid, { collection: 'profile'}, profile)
|
||||
}
|
||||
|
||||
async function readScores(refid: string): Promise<Scores> {
|
||||
const score = await DB.FindOne<Scores>(refid, { collection: 'scores'} )
|
||||
return score || { collection: 'scores', scores: {}}
|
||||
}
|
||||
|
||||
async function writeScores(refid: string, scores: Scores) {
|
||||
await DB.Upsert<Scores>(refid, { collection: 'scores'}, scores)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export const getVersion = (info: EamuseInfo) => {
|
||||
const moduleName: string = info.module;
|
||||
return `v${moduleName.match(/[0-9]+/)[0]}`;
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Profile } from "../models/profile";
|
||||
import { Scores } from "../models/scores";
|
||||
|
||||
export const importPnmData = async (data: {
|
||||
refid: string;
|
||||
profile: string;
|
||||
scores: string;
|
||||
}) => {
|
||||
const profile = JSON.parse(data.profile);
|
||||
const scores = JSON.parse(data.scores);
|
||||
|
||||
await DB.Update<Profile>(
|
||||
data.refid,
|
||||
{ collection: 'profile' },
|
||||
{
|
||||
$set: {
|
||||
...profile
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
await DB.Upsert<Scores>(
|
||||
data.refid,
|
||||
{ collection: 'scores' },
|
||||
{
|
||||
$set: {
|
||||
scores: {
|
||||
...scores
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { getInfo } from "./handler/common";
|
||||
import { newPlayer, read, readScore, start, writeMusic, write, buy } from "./handler/player";
|
||||
import { importPnmData } from "./handler/webui";
|
||||
|
||||
export function register() {
|
||||
R.GameCode('M39');
|
||||
|
||||
R.WebUIEvent('importPnmData', importPnmData);
|
||||
|
||||
const PlayerRoute = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`player24.${method}`, handler);
|
||||
R.Route(`player23.${method}`, handler);
|
||||
};
|
||||
|
||||
const CommonRoute = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`info24.${method}`, handler);
|
||||
R.Route(`info23.${method}`, handler);
|
||||
};
|
||||
|
||||
// Common
|
||||
CommonRoute('common', (req, data, send) => {
|
||||
return send.object(getInfo(req));
|
||||
});
|
||||
|
||||
// Player
|
||||
PlayerRoute('new', newPlayer);
|
||||
PlayerRoute('read', read);
|
||||
PlayerRoute('read_score', readScore);
|
||||
PlayerRoute('write_music', writeMusic);
|
||||
PlayerRoute('write', write);
|
||||
PlayerRoute('start', start);
|
||||
PlayerRoute('buy', buy);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
export interface Profile {
|
||||
collection: 'profile',
|
||||
|
||||
name: string;
|
||||
|
||||
stamps: {
|
||||
[ver: string]: {
|
||||
[stamp_id: string]: number;
|
||||
};
|
||||
};
|
||||
|
||||
medals: {
|
||||
[ver: string]: {
|
||||
[id: string]: {
|
||||
level: number;
|
||||
exp: number;
|
||||
set_count: number;
|
||||
get_count: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
items: {
|
||||
[ver: string]: {
|
||||
[key: string]: number;
|
||||
};
|
||||
};
|
||||
|
||||
charas: {
|
||||
[ver: string]: {
|
||||
[chara_id: string]: number;
|
||||
};
|
||||
};
|
||||
|
||||
extras: {
|
||||
[ver: string]: {
|
||||
[key: string]: any;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface Scores {
|
||||
collection: 'scores',
|
||||
|
||||
scores: {
|
||||
[key: string]: {
|
||||
clearmedal?: number;
|
||||
clear_type?: number;
|
||||
clear_rank?: number;
|
||||
score: number;
|
||||
cnt: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
$('#import-click').on('click', () => {
|
||||
data = {
|
||||
refid: $('#refid').val(),
|
||||
profile: $('#profile').val(),
|
||||
scores: $('#scores').val()
|
||||
}
|
||||
|
||||
emit('importPnmData', data).then(() => {
|
||||
$('#import-success').removeClass("is-hidden");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
div
|
||||
div.notification.is-success.is-hidden#import-success
|
||||
.field
|
||||
label.label.is-small Data imported
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-account-edit
|
||||
| Import data
|
||||
.card-content
|
||||
input(type="hidden" id="refid" name="refid" value=refid)
|
||||
.field
|
||||
label.label Import data from previous Asphyxia (non-core)
|
||||
.field
|
||||
input.input(type="text" id="profile" name="profile" placeholder="Put content of popn.json")
|
||||
.field
|
||||
input.input(type="text" id="scores" name="scores" placeholder="Put content of popn_scores.json")
|
||||
.field
|
||||
label.help.is-danger /!\ Please backup your savedata.db before importing data /!\
|
||||
.field
|
||||
button.button.is-primary#import-click Import
|
||||
|
||||
script(src="static/js/profile_page.js")
|
||||
@@ -1,6 +1,6 @@
|
||||
# SOUND VOLTEX
|
||||
|
||||
Plugin Version: **v1.0**
|
||||
Plugin Version: **v1.1**
|
||||
|
||||
Supported Versions:
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "esnext",
|
||||
"noImplicitAny": false,
|
||||
"moduleResolution": "node",
|
||||
"types": ["./asphyxia-core"],
|
||||
"sourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"includeDeclarations": true,
|
||||
"excludeExternals": true,
|
||||
"externalPattern": ["!asphyxia-core.d.ts"],
|
||||
"mode": "file",
|
||||
"readme": "../docs/_typedoc.md",
|
||||
"name": "Asphyxia CORE Module API",
|
||||
"out": "../docs/typedoc"
|
||||
}
|
||||
Reference in New Issue
Block a user