mirror of
https://github.com/asphyxia-core/plugins.git
synced 2026-03-28 12:54:39 -05:00
commit
d95b93077c
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -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.
|
||||
|
|
|
|||
1115
asphyxia-core.d.ts
vendored
1115
asphyxia-core.d.ts
vendored
File diff suppressed because it is too large
Load Diff
15
gitadora@asphyxia/README.md
Normal file
15
gitadora@asphyxia/README.md
Normal file
|
|
@ -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
|
||||
1
gitadora@asphyxia/const.ts
Normal file
1
gitadora@asphyxia/const.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const PLUGIN_VER = 1;
|
||||
5
gitadora@asphyxia/data/.gitignore
vendored
Normal file
5
gitadora@asphyxia/data/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
mdb_ex.xml
|
||||
mdb_mt.xml
|
||||
mdb_ex.b64
|
||||
mdb_mt.b64
|
||||
custom_mdb.xml
|
||||
58
gitadora@asphyxia/data/Exchain.ts
Normal file
58
gitadora@asphyxia/data/Exchain.ts
Normal file
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
58
gitadora@asphyxia/data/Matixx.ts
Normal file
58
gitadora@asphyxia/data/Matixx.ts
Normal file
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
36
gitadora@asphyxia/data/helper.ts
Normal file
36
gitadora@asphyxia/data/helper.ts
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
1
gitadora@asphyxia/data/mdb_ex.json
Normal file
1
gitadora@asphyxia/data/mdb_ex.json
Normal file
File diff suppressed because one or more lines are too long
1
gitadora@asphyxia/data/mdb_mt.json
Normal file
1
gitadora@asphyxia/data/mdb_mt.json
Normal file
File diff suppressed because one or more lines are too long
80
gitadora@asphyxia/handlers/MusicList.ts
Normal file
80
gitadora@asphyxia/handlers/MusicList.ts
Normal file
|
|
@ -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,
|
||||
}),
|
||||
});
|
||||
};
|
||||
143
gitadora@asphyxia/handlers/info.ts
Normal file
143
gitadora@asphyxia/handlers/info.ts
Normal file
|
|
@ -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,
|
||||
});
|
||||
};
|
||||
993
gitadora@asphyxia/handlers/profiles.ts
Normal file
993
gitadora@asphyxia/handlers/profiles.ts
Normal file
|
|
@ -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
|
||||
})
|
||||
}
|
||||
28
gitadora@asphyxia/handlers/webui.ts
Normal file
28
gitadora@asphyxia/handlers/webui.ts
Normal file
|
|
@ -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 }
|
||||
);
|
||||
};
|
||||
49
gitadora@asphyxia/index.ts
Normal file
49
gitadora@asphyxia/index.ts
Normal file
|
|
@ -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);
|
||||
}
|
||||
14
gitadora@asphyxia/models/extra.ts
Normal file
14
gitadora@asphyxia/models/extra.ts
Normal file
|
|
@ -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[];
|
||||
}
|
||||
19
gitadora@asphyxia/models/playerinfo.ts
Normal file
19
gitadora@asphyxia/models/playerinfo.ts
Normal file
|
|
@ -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.
|
||||
}
|
||||
57
gitadora@asphyxia/models/profile.ts
Normal file
57
gitadora@asphyxia/models/profile.ts
Normal file
|
|
@ -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;
|
||||
}
|
||||
44
gitadora@asphyxia/models/record.ts
Normal file
44
gitadora@asphyxia/models/record.ts
Normal file
|
|
@ -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[];
|
||||
}
|
||||
24
gitadora@asphyxia/models/scores.ts
Normal file
24
gitadora@asphyxia/models/scores.ts
Normal file
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
19
gitadora@asphyxia/utils.ts
Normal file
19
gitadora@asphyxia/utils.ts
Normal file
|
|
@ -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
|
||||
}
|
||||
36
gitadora@asphyxia/webui/profile_player_info.pug
Normal file
36
gitadora@asphyxia/webui/profile_player_info.pug
Normal file
|
|
@ -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
|
||||
27
museca@asphyxia/README.md
Normal file
27
museca@asphyxia/README.md
Normal file
|
|
@ -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.
|
||||
1
museca@asphyxia/data/.gitignore
vendored
Normal file
1
museca@asphyxia/data/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.xml
|
||||
12
museca@asphyxia/data/CommunityPlusMDB.ts
Normal file
12
museca@asphyxia/data/CommunityPlusMDB.ts
Normal file
|
|
@ -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")
|
||||
}
|
||||
12
museca@asphyxia/data/OnePlusHalfMDB.ts
Normal file
12
museca@asphyxia/data/OnePlusHalfMDB.ts
Normal file
|
|
@ -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");
|
||||
}
|
||||
57
museca@asphyxia/data/helper.ts
Normal file
57
museca@asphyxia/data/helper.ts
Normal file
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
1
museca@asphyxia/data/mdb_community_plus.json
Normal file
1
museca@asphyxia/data/mdb_community_plus.json
Normal file
File diff suppressed because one or more lines are too long
1
museca@asphyxia/data/mdb_one_plus_half.json
Normal file
1
museca@asphyxia/data/mdb_one_plus_half.json
Normal file
File diff suppressed because one or more lines are too long
88
museca@asphyxia/handlers/common.ts
Normal file
88
museca@asphyxia/handlers/common.ts
Normal file
|
|
@ -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()
|
||||
}**/
|
||||
}
|
||||
223
museca@asphyxia/handlers/player.ts
Normal file
223
museca@asphyxia/handlers/player.ts
Normal file
|
|
@ -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()
|
||||
}
|
||||
59
museca@asphyxia/index.ts
Normal file
59
museca@asphyxia/index.ts
Normal file
|
|
@ -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)}`)
|
||||
})
|
||||
}
|
||||
38
museca@asphyxia/models/profile.ts
Normal file
38
museca@asphyxia/models/profile.ts
Normal file
|
|
@ -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;
|
||||
}
|
||||
17
museca@asphyxia/models/scores.ts
Normal file
17
museca@asphyxia/models/scores.ts
Normal file
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
11
museca@asphyxia/utils.ts
Normal file
11
museca@asphyxia/utils.ts
Normal file
|
|
@ -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
|
||||
}
|
||||
33
nostalgia@asphyxia/README.md
Normal file
33
nostalgia@asphyxia/README.md
Normal file
|
|
@ -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.
|
||||
|
||||
3
nostalgia@asphyxia/data/.gitignore
vendored
Normal file
3
nostalgia@asphyxia/data/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
*.json
|
||||
*.json
|
||||
*.xml
|
||||
11
nostalgia@asphyxia/data/FirstMusic.ts
Normal file
11
nostalgia@asphyxia/data/FirstMusic.ts
Normal file
|
|
@ -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;
|
||||
}
|
||||
70
nostalgia@asphyxia/data/ForteMusic.ts
Normal file
70
nostalgia@asphyxia/data/ForteMusic.ts
Normal file
|
|
@ -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[]
|
||||
}
|
||||
1
nostalgia@asphyxia/data/course.json.b64
Normal file
1
nostalgia@asphyxia/data/course.json.b64
Normal file
File diff suppressed because one or more lines are too long
1
nostalgia@asphyxia/data/first_mdb.json.b64
Normal file
1
nostalgia@asphyxia/data/first_mdb.json.b64
Normal file
File diff suppressed because one or more lines are too long
1
nostalgia@asphyxia/data/forte_mdb.json.b64
Normal file
1
nostalgia@asphyxia/data/forte_mdb.json.b64
Normal file
File diff suppressed because one or more lines are too long
43
nostalgia@asphyxia/data/helper.ts
Normal file
43
nostalgia@asphyxia/data/helper.ts
Normal file
|
|
@ -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'));
|
||||
}
|
||||
1
nostalgia@asphyxia/data/island.json.b64
Normal file
1
nostalgia@asphyxia/data/island.json.b64
Normal file
File diff suppressed because one or more lines are too long
141
nostalgia@asphyxia/handler/common.ts
Normal file
141
nostalgia@asphyxia/handler/common.ts
Normal file
|
|
@ -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),
|
||||
},
|
||||
});
|
||||
};
|
||||
610
nostalgia@asphyxia/handler/player.ts
Normal file
610
nostalgia@asphyxia/handler/player.ts
Normal file
|
|
@ -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),
|
||||
},
|
||||
}
|
||||
20
nostalgia@asphyxia/handler/webui.ts
Normal file
20
nostalgia@asphyxia/handler/webui.ts
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
40
nostalgia@asphyxia/index.ts
Normal file
40
nostalgia@asphyxia/index.ts
Normal file
|
|
@ -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)}`)
|
||||
})
|
||||
}
|
||||
90
nostalgia@asphyxia/models/profile.ts
Normal file
90
nostalgia@asphyxia/models/profile.ts
Normal file
|
|
@ -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[];
|
||||
};
|
||||
}
|
||||
30
nostalgia@asphyxia/models/scores.ts
Normal file
30
nostalgia@asphyxia/models/scores.ts
Normal file
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
47
nostalgia@asphyxia/utils.ts
Normal file
47
nostalgia@asphyxia/utils.ts
Normal file
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
26
nostalgia@asphyxia/webui/profile_fix_login.pug
Normal file
26
nostalgia@asphyxia/webui/profile_fix_login.pug
Normal file
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
7
popn-hello@asphyxia/README.md
Normal file
7
popn-hello@asphyxia/README.md
Normal file
|
|
@ -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!
|
||||
161
popn-hello@asphyxia/handler/player.ts
Normal file
161
popn-hello@asphyxia/handler/player.ts
Normal file
|
|
@ -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)
|
||||
}
|
||||
15
popn-hello@asphyxia/handler/webui.ts
Normal file
15
popn-hello@asphyxia/handler/webui.ts
Normal file
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
39
popn-hello@asphyxia/index.ts
Normal file
39
popn-hello@asphyxia/index.ts
Normal file
|
|
@ -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()
|
||||
}
|
||||
12
popn-hello@asphyxia/models/profile.ts
Normal file
12
popn-hello@asphyxia/models/profile.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export interface Profile {
|
||||
collection: 'profile',
|
||||
|
||||
unlockAll: boolean;
|
||||
history: {
|
||||
chara: number;
|
||||
level: number;
|
||||
mid: number;
|
||||
style: number;
|
||||
};
|
||||
charas: { [id: string]: number };
|
||||
}
|
||||
14
popn-hello@asphyxia/models/scores.ts
Normal file
14
popn-hello@asphyxia/models/scores.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export interface Scores {
|
||||
collection: 'scores',
|
||||
|
||||
scores: {
|
||||
[mid: string]: {
|
||||
[style: string]: {
|
||||
[level: string]: {
|
||||
score: number;
|
||||
clear: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
26
popn-hello@asphyxia/webui/profile_unlock.pug
Normal file
26
popn-hello@asphyxia/webui/profile_unlock.pug
Normal file
|
|
@ -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
|
||||
23
popn@asphyxia/README.md
Normal file
23
popn@asphyxia/README.md
Normal file
|
|
@ -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.
|
||||
322
popn@asphyxia/handler/common.ts
Normal file
322
popn@asphyxia/handler/common.ts
Normal file
|
|
@ -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 },
|
||||
},
|
||||
};
|
||||
519
popn@asphyxia/handler/player.ts
Normal file
519
popn@asphyxia/handler/player.ts
Normal file
|
|
@ -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)
|
||||
}
|
||||
4
popn@asphyxia/handler/utils.ts
Normal file
4
popn@asphyxia/handler/utils.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const getVersion = (info: EamuseInfo) => {
|
||||
const moduleName: string = info.module;
|
||||
return `v${moduleName.match(/[0-9]+/)[0]}`;
|
||||
};
|
||||
33
popn@asphyxia/handler/webui.ts
Normal file
33
popn@asphyxia/handler/webui.ts
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
33
popn@asphyxia/index.ts
Normal file
33
popn@asphyxia/index.ts
Normal file
|
|
@ -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);
|
||||
}
|
||||
40
popn@asphyxia/models/profile.ts
Normal file
40
popn@asphyxia/models/profile.ts
Normal file
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
13
popn@asphyxia/models/scores.ts
Normal file
13
popn@asphyxia/models/scores.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export interface Scores {
|
||||
collection: 'scores',
|
||||
|
||||
scores: {
|
||||
[key: string]: {
|
||||
clearmedal?: number;
|
||||
clear_type?: number;
|
||||
clear_rank?: number;
|
||||
score: number;
|
||||
cnt: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
11
popn@asphyxia/webui/js/profile_page.js
Normal file
11
popn@asphyxia/webui/js/profile_page.js
Normal file
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
24
popn@asphyxia/webui/profile_page.pug
Normal file
24
popn@asphyxia/webui/profile_page.pug
Normal file
|
|
@ -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"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user