Merge branch 'asphyxia-core:stable' into stable

This commit is contained in:
LatoWolf 2023-08-24 15:49:56 -05:00 committed by GitHub
commit 5bdd2f2c7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 2434 additions and 538 deletions

View File

@ -11,6 +11,20 @@ export function getEncoreStageData(info: EamuseInfo): EncoreStageData {
const level: number = U.GetConfig("encore_version")
const ntDummyEncore = U.GetConfig("nextage_dummy_encore")
switch (getVersion(info)) {
case 'highvoltage':
return {
level,
musics: [
2686, // CYCLONICxSTORM
2687, // Heptagram
2700, // Saiph
2706, // LUCID NIGHTMARE
2740, // Mobius
2748, // Under The Shades Of The Divine Ray
2772, // REBELLION
2812, // THE LAST OF FIREFACE
]
}
case 'nextage':
return {
level,

File diff suppressed because one or more lines are too long

View File

@ -42,7 +42,7 @@ export async function readMDBFile(path: string, processHandler?: processRawDataH
processHandler = processHandler ?? defaultProcessRawXmlData
result = await processHandler(path)
// Uncomment to save the loaded XML file as JSON.
// await IO.WriteFile(path.replace(".xml", ".json"), JSON.stringify(data))
// await IO.WriteFile(path.replace(".xml", ".json"), JSON.stringify(result))
break;
case '.b64':
const buff = await IO.ReadFile(path, 'utf-8');

View File

@ -8,7 +8,7 @@ import { getDefaultScores, Scores } from "../models/scores";
import { PLUGIN_VER } from "../const";
import Logger from "../utils/logger"
import { isAsphyxiaDebugMode } from "../Utils/index";
import { isAsphyxiaDebugMode } from "../utils/index";
import { SecretMusicEntry } from "../models/secretmusicentry";
import { CheckPlayerResponse, getCheckPlayerResponse } from "../models/Responses/checkplayerresponse";
import { getPlayerStickerResponse, PlayerStickerResponse } from "../models/Responses/playerstickerresponse";
@ -102,22 +102,22 @@ export const getPlayer: EPR = async (info, data, send) => {
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),
_.get(score, 'diffs.1.clear', false) ? _.get(score, 'diffs.1.perc', -2) : -1,
_.get(score, 'diffs.2.clear', false) ? _.get(score, 'diffs.2.perc', -2) : -1,
_.get(score, 'diffs.3.clear', false) ? _.get(score, 'diffs.3.perc', -2) : -1,
_.get(score, 'diffs.4.clear', false) ? _.get(score, 'diffs.4.perc', -2) : -1,
_.get(score, 'diffs.5.clear', false) ? _.get(score, 'diffs.5.perc', -2) : -1,
_.get(score, 'diffs.6.clear', false) ? _.get(score, 'diffs.6.perc', -2) : -1,
_.get(score, 'diffs.7.clear', false) ? _.get(score, 'diffs.7.perc', -2) : -1,
_.get(score, 'diffs.8.clear', false) ? _.get(score, 'diffs.8.perc', -2) : -1,
_.get(score, 'diffs.1.clear', false) ? _.get(score, 'diffs.1.rank', 0) : -1,
_.get(score, 'diffs.2.clear', false) ? _.get(score, 'diffs.2.rank', 0) : -1,
_.get(score, 'diffs.3.clear', false) ? _.get(score, 'diffs.3.rank', 0) : -1,
_.get(score, 'diffs.4.clear', false) ? _.get(score, 'diffs.4.rank', 0) : -1,
_.get(score, 'diffs.5.clear', false) ? _.get(score, 'diffs.5.rank', 0) : -1,
_.get(score, 'diffs.6.clear', false) ? _.get(score, 'diffs.6.rank', 0) : -1,
_.get(score, 'diffs.7.clear', false) ? _.get(score, 'diffs.7.rank', 0) : -1,
_.get(score, 'diffs.8.clear', false) ? _.get(score, 'diffs.8.rank', 0) : -1,
0,
0,
0,
@ -629,6 +629,7 @@ async function updatePlayerScoreCollection(refid, playedStages, version, game) {
const clear = stage.bool('clear');
const fc = stage.bool('fullcombo');
const ex = stage.bool('excellent');
const newMeter = stage.bool('is_new_meter');
const perc = stage.number('perc', 0);
const rank = stage.number('rank', 0);
@ -650,7 +651,7 @@ async function updatePlayerScoreCollection(refid, playedStages, version, game) {
scores[mid].diffs[seq] = { //FIXME: Real server is bit complicated. this one is too buggy.
perc: Math.max(_.get(scores[mid].diffs[seq], 'perc', 0), perc),
rank: Math.max(_.get(scores[mid].diffs[seq], 'rank', 0), rank),
meter: meter.toString(),
meter: newMeter ? meter.toString() : _.get(scores[mid].diffs[seq], 'meter', 0),
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,

View File

@ -59,6 +59,7 @@ export function register() {
R.Route(`exchain_${method}`, handler);
R.Route(`matixx_${method}`, handler);
R.Route(`nextage_${method}`, handler)
R.Route(`highvoltage_${method}`, handler)
// TODO: TB, TBRE and more older version?
};

View File

@ -7,15 +7,28 @@
function getFullGameName(shortName) {
switch (shortName) {
case "dm" :
return "Drummania"
case "gf":
return "Guitar Freaks"
default:
return "DrumMania"
case "gf":
return "GuitarFreaks"
default:
return "Unknown"
}
}
const versions = ["exchain", "nextage"]
function getFullVersionName(shortName) {
switch (shortName) {
case "exchain" :
return "GITADORA EXCHAIN"
case "nextage":
return "GITADORA NEX+AGE"
case "highvoltage":
return "GITADORA HIGH-VOLTAGE"
default:
return "Unknown"
}
}
const versions = ["exchain", "nextage", "highvoltage"]
const games = ["gf", "dm"]
function generateLeaderboards(infos, profiles) {
@ -64,7 +77,7 @@
-
each board in generateLeaderboards(infos, profiles)
h3 #{getFullGameName(board.game)} #{board.version}
h3 #{getFullVersionName(board.version)} #{getFullGameName(board.game)}
table
tr
th Rank

View File

@ -1,18 +1,26 @@
# jubeat
# Jubeat Plugin
Plugin Version: **v1.0.0**
Jubeat Plugin for Asphyxia Core
---
# Supported Versions
**jubeat** is music simulation game.
Touch the panel on a screen at the right timing and rhythm to the music.
- Festo
## Supported Versions
# Versions
- ripples
- V1.0.0 (2021/12/16)
- Only support normal mode score saving.
## Changelog
- V2.0.0 (2022/08/14)
- Now Support Festo Final
- Support hard mode score saving
- Support Turn Run
### 1.0.0
# TODO
- Initial Release
- [ ] Customized Turn Run. (Currently can't cuz Jubeat courses limit is 60, need someone to find how to patch it.)
# Credits
- Thanks [asesidaa](https://github.com/asesidaa?tab=repositories) for help!
- And also the other open-soured Jubeat lovers!

View File

@ -1,22 +0,0 @@
import { getVersion } from '../utils';
export const shopRegist: EPR = (i, data, send) => {
const locId = $(data).content('shop.locationid');
const cabId = $(data).number('shop.testmode.network.cabinet_id', 1);
const version = getVersion(i);
if (version <= 0) return send.deny();
return send.object({
data: {
cabid: K.ITEM('u32', cabId),
locationid: K.ITEM('str', locId),
...(version >= 1 && version <= 3 && { is_send: K.ITEM('u8', 1) }),
},
});
};
export const demodata = {
getNews: (_, __, send) =>
send.object({ data: { officialnews: K.ATTR({ count: '0' }) } }),
};

View File

@ -1,55 +0,0 @@
import { getVersion } from '../utils';
export const lobby = {
check: (info, data, send) => {
const version = getVersion(info);
if (version <= 0) return send.deny();
return send.object({
data: {
entrant_nr: K.ITEM('u32', 1, { time: 0 + '' }),
interval: K.ITEM('s16', 3),
entry_timeout: K.ITEM('s16', 15),
waitlist: K.ATTR({ count: '0' }),
},
});
},
entry: (info, data, send) => {
const version = getVersion(info);
if (version <= 0) return send.deny();
const musicId = $(data).number('data.music.id', 20000037);
const musicSeq = $(data).number('data.music', 0);
return send.object({
data: {
roomid: K.ITEM('s64', BigInt(1), { master: '1' }),
refresh_intr: K.ITEM('s16', 5),
music: {
id: K.ITEM('u32', musicId),
seq: K.ITEM('u8', musicSeq),
},
},
});
},
refresh: (info, data, send) => {
const version = getVersion(info);
if (version <= 0) return send.deny();
return send.object({
data: {
refresh_intr: K.ITEM('s16', 3),
},
});
},
report: (info, data, send) => {
const version = getVersion(info);
if (version <= 0) return send.deny();
return send.object({
data: {
refresh_intr: K.ITEM('s16', 3),
},
});
},
};

View File

@ -1,232 +0,0 @@
import { getVersion } from '../utils';
import Profile from '../models/profile';
import { Score } from '../models/score';
export const profile: { regist: EPR; get: EPR; meeting: EPR; save: EPR } = {
regist: async (info, data, send) => {
const version = getVersion(info);
const refId = $(data).str('data.player.pass.refid');
const newName = $(data).str('data.player.name', 'JUBEAT');
if (version <= 0) return send.deny();
if (!refId || !newName) return send.deny();
let profile = await DB.FindOne<Profile>(refId, { collection: 'profile' });
if (profile) return send.deny();
await DB.Insert<Profile>(refId, {
collection: 'profile',
jid: _.random(1, 99999999),
name: newName.toUpperCase(),
});
profile = await DB.FindOne<Profile>(refId, { collection: 'profile' });
if (version === 2)
return send.pugFile('templates/profile/ripples.pug', {
profile,
scores: [],
});
},
get: async (info, data, send) => {
const version = getVersion(info);
const refId = $(data).str('data.player.pass.refid');
if (version <= 0) return send.deny();
if (!refId) return send.deny();
const profile = await DB.FindOne<Profile>(refId, { collection: 'profile' });
if (!profile) return send.deny();
const playerScores = await DB.Find<Score>(refId, { collection: 'score' });
const scores = {};
console.dir(playerScores, { depth: null });
playerScores.map(v => {
if (!scores[v.musicId]) scores[v.musicId] = [{}, {}, {}];
scores[v.musicId][v.seqId] = {
score: v.score,
clearFlag: v.clearFlag,
mbar: v.musicBar,
};
});
console.dir(scores, { depth: null });
if (version === 2)
return send.pugFile('templates/profile/ripples.pug', { profile, scores });
},
meeting: async (info, data, send) => {
const version = getVersion(info);
if (version <= 0) return send.deny();
if (version === 2)
return send.object({
data: {
entryinfo: K.ATTR(
{ count: '0' },
{
meeting: [],
}
),
holdinfo: {
meeting: [],
},
reward: {
total: K.ITEM('s32', 0),
point: K.ITEM('s32', 0),
},
},
});
},
save: async (info, data, send) => {
const version = getVersion(info);
if (version <= 0) return send.deny();
const retry = $(data).number('retry', 0);
if (retry > 0) return send.deny();
const refId = $(data).str('data.player.refid');
if (!refId) return send.deny();
const profile = await DB.FindOne<Profile>(refId, { collection: 'profile' });
if (!profile) return send.deny();
if (version === 2) {
let lastMusicId: number;
let lastSeqId: number;
let lastUsedMarker: number;
let lastUsedTheme: number;
let lastUsedTitle: number;
let lastUsedSort: number;
let lastUsedFilter: number;
let lastShowRankSort: number;
let lastComboDisp: number;
const tunes = $(data).elements('data.result.tune');
for (let i = 0; i < tunes.length; i++) {
const musicId = tunes[i].number('music');
const seqId = Number(tunes[i].attr('music')?.seq);
const marker = tunes[i].number('marker');
const theme = tunes[i].number('theme');
const title = tunes[i].number('title');
const sort = tunes[i].number('sort');
const filter = tunes[i].number('filter');
const rankSort = tunes[i].number('rank_sort');
const comboDisp = tunes[i].number('combo_disp');
const score = tunes[i].number('player.score');
const clearFlag = Number(tunes[i].attr('player.score')?.clear);
const maxCombo = Number(tunes[i].attr('player.score')?.combo);
const mbar = tunes[i].numbers('player.mbar');
lastMusicId = musicId;
lastSeqId = seqId;
lastUsedMarker = marker;
lastUsedTheme = theme;
lastUsedTitle = title;
lastUsedSort = sort;
lastUsedFilter = filter;
lastShowRankSort = rankSort;
lastComboDisp = comboDisp;
let oldScore = await DB.FindOne<Score>(refId, {
collection: 'score',
musicId,
seqId,
});
if (!oldScore) {
await DB.Insert<Score>(refId, {
collection: 'score',
musicId,
seqId,
score: 0,
clearFlag: 0,
maxCombo: 0,
marker: 0,
theme: 0,
musicBar: new Array(20).fill(0),
playCount: 0,
clearCount: 0,
fullComboCount: 0,
excCount: 0,
});
oldScore = await DB.FindOne<Score>(refId, {
collection: 'score',
musicId,
seqId,
});
}
await DB.Update<Score>(
refId,
{ collection: 'score', musicId, seqId },
{
$set: {
score: Math.max(oldScore.score, score),
clearFlag: Math.max(oldScore.clearFlag, clearFlag),
maxCombo: Math.max(oldScore.maxCombo, maxCombo),
marker,
theme,
...(score > oldScore.score && { musicBar: mbar }),
},
$inc: {
playCount: 1,
...(clearFlag === 2 && { clearCount: 1 }),
...(clearFlag === 4 && { fullComboCount: 1 }),
...(clearFlag === 8 && { excCount: 1 }),
},
}
);
}
const mode = $(data).number('data.player.mode', 0);
await DB.Update<Profile>(
refId,
{ collection: 'profile' },
{
$set: {
'ripples.info': {
grade: $(data).number('data.player.grade', 0),
gradePoint: $(data).number('data.player.grade_point', 0),
},
'ripples.last': {
mode,
musicId: lastMusicId,
seqId: lastSeqId,
marker: lastUsedMarker,
title: lastUsedTitle,
theme: lastUsedTheme,
sort: lastUsedSort,
filter: lastUsedFilter,
rankSort: lastShowRankSort,
comboDisp: lastComboDisp,
},
},
$inc: {
...(mode === 1 && { 'ripples.info.onlineCount': 1 }),
...(mode === 0 && { 'ripples.info.multiCount': 1 }),
},
}
);
return send.object({
data: {
player: {
session_id: K.ITEM('u32', 1),
ranking: K.ITEM('u32', 0),
},
},
});
}
return send.deny();
},
};

View File

@ -1,38 +1,31 @@
// nodemon --exec "asphyxia-core-x64 --dev" -e ts --watch plugins
import ShopInfo from "./routes/shopinfo";
import {getProfile, Getinfo, loadScore, Meeting} from "./routes/gametop";
import {saveProfile} from "./routes/gameend";
import {Check, Entry, Refresh, Report} from "./routes/lobby";
import { demodata, shopRegist } from './handlers/common';
import { lobby } from './handlers/matching';
import { profile } from './handlers/profile';
export async function register() {
if (CORE_VERSION_MAJOR <= 1 && CORE_VERSION_MINOR < 31) {
console.error("The current version of Asphyxia Core is not supported. Requires version '1.31' or later.");
return;
}
R.GameCode("L44");
R.Contributor("yuanqiuye", "https://github.com/yuanqiuye")
R.Route("gametop.regist",getProfile);
R.Route("gametop.get_info", Getinfo);
R.Route("gametop.get_pdata", getProfile);
R.Route("gametop.get_mdata", loadScore);
R.Route("gametop.get_meeting", Meeting);
R.Route("gameend.final", true);
R.Route("gameend.regist", saveProfile);
export function register() {
if (CORE_VERSION_MAJOR <= 1 && CORE_VERSION_MINOR < 31) {
console.error(
"The current version of Asphyxia Core is not supported. Requires version '1.31' or later."
);
return;
}
R.Contributor('Kirito', 'https://github.com/Kirito3481');
R.GameCode('I44');
R.Route('shopinfo.regist', shopRegist);
R.Route('demodata.getnews', demodata.getNews);
R.Route('gametop.regist', profile.regist);
R.Route('gametop.get', profile.get);
R.Route('meeting.get', profile.meeting);
R.Route('gameend.regist', profile.save);
R.Route('gameend.log', true);
R.Route('lobby.check', lobby.check);
R.Route('lobby.entry', lobby.entry);
R.Route('lobby.refresh', lobby.refresh);
R.Route('lobby.report', lobby.report);
R.Route('netlog.send', true);
R.Unhandled();
}
R.Route("shopinfo.regist", ShopInfo);
R.Route("lobby.check", Check);
R.Route("lobby.entry", Entry);
R.Route("lobby.refresh", Refresh);
R.Route("lobby.report", Report);
R.Route("netlog.send", true);
R.Route("logger.report", true);
R.Unhandled();
}

View File

@ -0,0 +1,8 @@
export interface Course {
collection: "course";
courseId: number;
seen: boolean,
played: boolean,
cleared: boolean
}

View File

@ -1,34 +1,57 @@
export default interface Profile {
collection: 'profile';
collection: "profile";
navi?: number,
jubeatId: number;
eventFlag: number;
name: string;
emo: number[];
lastPlayTime?: number;
lastShopname: string;
lastAreaname: string;
isFirstplay: boolean;
musicId?: number;
seqId?: number;
seqEditId?: string;
rankSort?: number;
comboDisp?: number;
jid: number;
name: string;
jubility?: number;
jubilityYday?: number;
tuneCount?: number;
clearCount?: number;
saveCount?: number;
savedCount?: number;
fcCount?: number;
exCount?: number;
matchCount?: number;
bonusPoints?: number;
isBonusPlayed?: boolean;
totalBestScore?: number;
clearMaxLevel?: number;
fcMaxLevel?: number;
exMaxLevel?: number;
ripples?: Ripples;
}
emblem?: number[];
marker?: number;
theme?: number;
title?: number;
parts?: number;
sort?: number;
category?: number;
expertOption?: number;
matching?: number;
hazard?: number;
hard?: number;
export interface Ripples {
info?: {
onlineCount: number;
multiCount: number;
matchCount: number;
beatCount: number;
saveCount: number;
savedCount: number;
grade: number;
gradePoint: number;
};
secretList?: number[];
themeList?: number;
markerList?: number[];
titleList?: number[];
commuList?: number[];
partsList?: number[];
last?: {
mode: number;
musicId: number;
seqId: number;
marker: number;
title: number;
theme: number;
sort: number;
filter: number;
rankSort: number;
comboDisp: number;
};
}
secretListNew?: number[];
themeListNew?: number[];
markerListNew?: number[];
titleListNew?: number[];
}

View File

@ -1,16 +1,15 @@
export interface Score {
collection: 'score';
musicId: number;
seqId: number;
score: number;
clearFlag: number;
maxCombo: number;
marker: number;
theme: number;
musicBar: number[];
playCount: number;
clearCount: number;
fullComboCount: number;
excCount: number;
}
collection: "score";
musicId: number;
seq: number;
score: number;
clear: number;
musicRate: number;
bar: number[];
playCount: number;
clearCount: number;
fullcomboCount: number;
excellentCount: number;
isHardMode: boolean;
}

View File

@ -0,0 +1,179 @@
import {Score} from "../models/score"
import Profile from "../models/profile";
import { Course } from "../models/course";
import { COURSE_STATUS } from "../static/data";
export const saveProfile = async (info, {data}, send) => {
console.log("gameend.regist");
console.log(data, {depth:null});
const refId = $(data).str("player.refid");
if (!refId) return send.deny();
const profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
if (!profile) return send.deny();
let lastMarker = 0;
let lastTheme = 0;
let lastTitle = 0;
let lastParts = 0;
let lastSort = 0;
let lastCategory = 0;
const courses = $(data).elements("player.course_list.course");
const tunes = $(data).elements("result.tune");
const select_course = $(data).elements("player.select_course");
const course_cleared : { [couseId: number]: { is_cleared: boolean} } = {};
if(select_course){
for(const course of select_course){
course_cleared[course.attr("").id] = course.bool("is_cleared");
}
}
for (const course of courses){
const courseID = course.attr("").id;
await updateCourse(refId, {
courseID: courseID,
seen: (course.number("status") & COURSE_STATUS.SEEN) != 0,
played: (course.number("status") & COURSE_STATUS.PLAYED) != 0,
cleared: course_cleared[courseID] || (course.number("status") & COURSE_STATUS.CLEARED) != 0,
});
}
for (const tune of tunes) {
profile.musicId = tune.number("music");
profile.seqId = parseInt(tune.attr("player.score").seq);
await updateScore(refId, {
bestmusicRate: tune.number("player.best_music_rate"),
musicRate: tune.number("player.music_rate"),
musicId: tune.number("music"),
seq: parseInt(tune.attr("player.score").seq),
score: tune.number("player.score"),
clear: parseInt(tune.attr("player.score").clear),
isHard: tune.bool("player.is_hard_mode"),
bestScore: tune.number("player.best_score"),
bestClear: tune.number("player.best_clear"),
playCount: tune.number("player.play_cnt"),
clearCount: tune.number("player.clear_cnt"),
fullcomboCount: tune.number("player.fc_cnt"),
excellentCount: tune.number("player.ex_cnt"),
...tune.element("player.mbar") && { mbar: tune.numbers("player.mbar") }
});
}
lastMarker = $(data).number("player.last.settings.marker");
lastTheme = $(data).number("player.last.settings.theme");
lastTitle = $(data).number("player.last.settings.title");
lastParts = $(data).number("player.last.settings.parts");
lastSort = $(data).number("player.last.sort");
lastCategory = $(data).number("player.last.category");
profile.eventFlag = Number($(data).bigint("player.event_flag"));
profile.rankSort = $(data).number("player.last.settings.rank_sort");
profile.comboDisp = $(data).number("player.last.settings.combo_disp");
profile.lastPlayTime = Number($(data).bigint("info.play_time"));
profile.lastShopname = $(data).str("info.shopname");
profile.lastAreaname = $(data).str("info.areaname");
profile.tuneCount = $(data).number("player.info.tune_cnt");
profile.saveCount = $(data).number("player.info.save_cnt");
profile.savedCount = $(data).number("player.info.saved_cnt");
profile.fcCount = $(data).number("player.info.fc_cnt");
profile.exCount = $(data).number("player.info.ex_cnt");
profile.clearCount = $(data).number("player.info.clear_cnt");
profile.matchCount = $(data).number("player.info.match_cnt");
profile.expertOption = $(data).number("player.last.expert_option");
profile.matching = $(data).number("player.last.settings.matching");
profile.hazard =$(data).number("player.last.settings.hazard");
profile.hard = $(data).number("player.last.settings.hard");
profile.bonusPoints = $(data).number("player.info.bonus_tune_points");
profile.isBonusPlayed = $(data).bool("player.info.is_bonus_tune_played");
profile.totalBestScore = $(data).number("player.info.total_best_score.normal");
profile.clearMaxLevel = $(data).number("player.info.clear_max_level");
profile.fcMaxLevel = $(data).number("player.info.fc_max_level");
profile.exMaxLevel = $(data).number("player.info.ex_max_level");
profile.navi = Number($(data).bigint("player.navi.flag"));
profile.isFirstplay = $(data).bool("player.free_first_play.is_applied");
profile.marker = lastMarker;
profile.theme = lastTheme;
profile.title = lastTitle;
profile.parts = lastParts;
profile.sort = lastSort;
profile.category = lastCategory;
profile.commuList = $(data).numbers("player.item.commu_list");
profile.secretList = $(data).numbers("player.item.secret_list");
profile.themeList = $(data).number("player.item.theme_list");
profile.markerList = $(data).numbers("player.item.marker_list");
profile.titleList = $(data).numbers("player.item.title_list");
profile.partsList = $(data).numbers("player.item.parts_list");
profile.secretListNew = $(data).numbers("player.item.new.secret_list");
profile.themeListNew = $(data).numbers("player.item.new.theme_list");
profile.markerListNew = $(data).numbers("player.item.new.marker_list");
try {
await DB.Update<Profile>(refId, { collection: "profile" }, profile);
return send.object({
data: {
player: { session_id: K.ITEM("s32", 1) },
collabo: { deller: K.ITEM("s32", 0) }
}
}, {compress:true});
} catch (e) {
console.error(`Profile save failed: ${e.message}`);
return send.deny();
}
}
const updateScore = async (refId: string, data: any): Promise<boolean> => {
try {
await DB.Upsert<Score>(refId, {
collection: "score",
musicId: data.musicId,
seq: data.seq,
isHardMode: data.isHard,
}, {
$set: {
musicId: data.musicId,
seq: data.seq,
score: data.bestScore,
clear: data.bestClear,
musicRate: data.musicRate>data.bestmusicRate?data.musicRate:data.bestmusicRate,
...data.mbar && { bar: data.mbar, },
playCount: data.playCount,
clearCount: data.clearCount,
fullcomboCount: data.fullcomboCount,
excellentCount: data.excellentCount,
isHardMode: data.isHard
}
});
return true;
} catch (e) {
console.error("Score saving failed: ", e.stack);
return false;
}
};
const updateCourse = async (refId: string, data: any): Promise<boolean> => {
try {
await DB.Upsert<Course>(refId, {
collection: "course",
courseId: data.courseID,
}, {
$set: {
courseId: data.courseID,
seen: data.seen,
played: data.played,
cleared: data.cleared
}
});
return true;
} catch (e) {
console.error("Course saving failed: ", e.stack);
return false;
}
};

View File

@ -0,0 +1,211 @@
import Profile from '../models/profile';
import { Score } from '../models/score';
export const getProfile = async (
info: EamuseInfo,
data: any,
send: EamuseSend
) => {
console.log('gametop.regist');
let refId = $(data).str('data.player.refid');
const name = $(data).str('data.player.name');
console.log(data, { depth: null });
if (!refId) return send.deny();
let profile = await DB.FindOne<Profile>(refId, { collection: 'profile' });
if (!profile && name) {
const newProfile: Profile = {
collection: 'profile',
jubeatId: Math.round(Math.random() * 99999999),
eventFlag: 0,
name: name,
isFirstplay: true,
emo: [],
lastShopname: '',
lastAreaname: '',
};
await DB.Upsert<Profile>(refId, { collection: 'profile' }, newProfile);
profile = newProfile;
} else if (!profile && !name) {
return send.deny();
}
return send.object(
{
data: {
...require('../templates/gameInfos.ts')(),
player: {
jid: K.ITEM('s32', profile.jubeatId),
session_id: K.ITEM('s32', 1),
name: K.ITEM('str', profile.name),
event_flag: K.ITEM('u64', BigInt(profile.eventFlag || 0)),
...(await require('../templates/profiles.ts')(profile)),
},
},
},
{ compress: true }
);
};
export const Getinfo = (info: EamuseInfo, data: any, send: EamuseSend) => {
console.log(data, { depth: null });
return send.object(
{ data: require('../templates/gameInfos')() },
{ compress: true }
);
};
export const loadScore = async (info, data, send) => {
console.log('gametop.get_mdata');
console.log(data, { depth: null });
const mdata_ver = $(data).number('data.player.mdata_ver');
const jubeatId = $(data).number('data.player.jid');
if (!jubeatId) return send.deny();
const profile = await DB.FindOne<Profile>(null, {
collection: 'profile',
jubeatId,
});
if (!profile) return send.deny();
const scores = await DB.Find<Score>(profile.__refid, { collection: 'score' });
const scoreData: {
[musicId: number]: {
[isHardMode: number]: {
musicRate: number[];
score: number[];
clear: number[];
playCnt: number[];
clearCnt: number[];
fcCnt: number[];
exCnt: number[];
bar: number[][];
};
};
} = {};
for (const score of scores) {
if (!scoreData[score.musicId]) {
scoreData[score.musicId] = {};
}
if (!scoreData[score.musicId][score.isHardMode ? 1 : 0]) {
scoreData[score.musicId][score.isHardMode ? 1 : 0] = {
musicRate: [0, 0, 0],
playCnt: [0, 0, 0],
clearCnt: [0, 0, 0],
fcCnt: [0, 0, 0],
exCnt: [0, 0, 0],
clear: [0, 0, 0],
score: [0, 0, 0],
bar: [Array(30).fill(0), Array(30).fill(0), Array(30).fill(0)],
};
}
const data = scoreData[score.musicId][score.isHardMode ? 1 : 0];
data.musicRate[score.seq] = score.musicRate;
data.playCnt[score.seq] = score.playCount;
data.clearCnt[score.seq] = score.clearCount;
data.fcCnt[score.seq] = score.fullcomboCount;
data.exCnt[score.seq] = score.excellentCount;
data.clear[score.seq] = score.clear;
data.score[score.seq] = score.score;
data.bar[score.seq] = score.bar;
}
var sendobj = {
data: {
player: {
jid: K.ITEM('s32', jubeatId),
mdata_list: {
music: (() => {
var musicArray = [];
Object.keys(scoreData).forEach(musicId =>
Object.keys(scoreData[musicId]).forEach(isHardMode => {
musicArray.push(
K.ATTR(
{ music_id: String(musicId) },
{
[isHardMode === '1' ? 'hard' : 'normal']: {
score: K.ARRAY(
's32',
scoreData[musicId][isHardMode].score
),
clear: K.ARRAY(
's8',
scoreData[musicId][isHardMode].clear
),
music_rate: K.ARRAY(
's32',
scoreData[musicId][isHardMode].musicRate
),
play_cnt: K.ARRAY(
's32',
scoreData[musicId][isHardMode].playCnt
),
clear_cnt: K.ARRAY(
's32',
scoreData[musicId][isHardMode].clearCnt
),
fc_cnt: K.ARRAY(
's32',
scoreData[musicId][isHardMode].fcCnt
),
ex_cnt: K.ARRAY(
's32',
scoreData[musicId][isHardMode].exCnt
),
bar: scoreData[musicId][isHardMode].bar.map(
(bar, seq) => K.ARRAY('u8', bar, { seq: String(seq) })
),
},
}
)
);
})
);
return musicArray;
})(),
},
},
},
};
if (mdata_ver != 1) {
sendobj = {
data: {
player: {
jid: K.ITEM('s32', jubeatId),
mdata_list: {
music: [],
},
},
},
};
}
console.log(sendobj, { depth: null });
return send.object(sendobj, { compress: true });
};
export const Meeting = (req: EamuseInfo, data: any, send: EamuseSend) => {
return send.object(
{
data: {
meeting: {
single: K.ATTR({ count: '0' }),
},
reward: {
total: K.ITEM('s32', 0),
point: K.ITEM('s32', 0),
},
},
},
{ compress: true }
);
};

View File

@ -0,0 +1,56 @@
export const Check = (req, reqData, send) => {
const { data } = reqData;
const enter = $(data).content("enter");
const time = $(data).content("time");
return send.object(
{
data: {
entrant_nr: K.ITEM("u32", 0, { time }),
interval: K.ITEM("s16", 0),
entry_timeout: K.ITEM("s16", 15),
waitlist: K.ATTR({ count: "0" }, { music: [] }),
},
},
{ compress: true }
);
};
export const Entry = (req: EamuseInfo, data: any, send: EamuseSend) => {
const {
data: { music },
} = data;
const musicId = $(music).content("id");
const musicSeq = $(music).content("seq");
return send.object(
{
data: {
roomid: K.ITEM("s64", BigInt(1), { master: "1" }),
refresh_intr: K.ITEM("s16", 0),
music: {
id: K.ITEM("u32", musicId),
seq: K.ITEM("u8", musicSeq),
},
},
},
{ compress: true }
);
};
export const Refresh = (req: EamuseInfo, data: any, send: EamuseSend) => {
return send.object(
{
data: { refresh_intr: K.ITEM("s16", 0), start: K.ITEM("bool", true) },
},
{ compress: true }
);
};
export const Report = (req: EamuseInfo, data: any, send: EamuseSend) =>
send.object(
{
data: { refresh_intr: K.ITEM("s16", 0) },
},
{ compress: true }
);

View File

@ -0,0 +1,20 @@
export default (_: EamuseInfo, data: any, send: EamuseSend) => {
const locId = $(data).element("shop").content("locationid");
console.log({...require("../templates/gameInfos.ts")()}, {depth:null});
return send.object(
{
data: {
cabid: K.ITEM("u32", 1),
locationid: K.ITEM("str", locId),
tax_phase: K.ITEM("u8", 0),
facility: {
exist: K.ITEM("u32", 0),
},
...require("../templates/gameInfos.ts")(),
},
},
{ compress: true }
);
};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,919 @@
[
{
name: "オレのユビティズム",
difficulty: 3,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2100000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[20000042, 0, 0], [20000042, 1, 0], [20000042, 2, 0]],
[[70000119, 0, 0], [70000119, 1, 0], [70000119, 2, 0]],
[[50000115, 0, 0], [50000115, 1, 0], [50000115, 2, 0]],
],
},
{
name: "はじめてのビーチ",
difficulty: 1,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[60000080, 0, 0], [90000077, 0, 0], [90000139, 0, 0]],
[[60000086, 0, 0], [70000047, 0, 0]],
[[90000141, 0, 0]],
],
},
{
name: "【初段】超幸せハイテンション",
difficulty: 1,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[20000031, 0, 0], [60000100, 0, 0], [90000078, 0, 0]],
[[70000125, 0, 0], [90000050, 0, 0]],
[[70000106, 0, 1]],
],
},
{
name: "アニメランニング",
difficulty: 2,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 750000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[60000092, 0, 0], [90000031, 0, 0], [90000172, 0, 0]],
[[30000004, 0, 0], [80000059, 0, 0]],
[[50000209, 0, 0]],
],
},
{
name: "パブリックリゾート",
difficulty: 2,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 750000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[80000097, 0, 0], [90000029, 0, 0], [90000076, 0, 0]],
[[80000093, 0, 0], [90000048, 0, 0]],
[[80000038, 0, 0]],
],
},
{
name: "【二段】その笑顔は甘く蕩ける",
difficulty: 3,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 800000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000268, 0, 0], [70000039, 0, 0], [90000051, 0, 0]],
[[70000091, 0, 0], [80000014, 0, 0]],
[[60000053, 0, 1]],
],
},
{
name: "シャレを言いなシャレ",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2400000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[70000003, 0, 0], [70000003, 1, 0], [70000003, 2, 0]],
[[70000045, 0, 0], [70000045, 1, 0], [70000045, 2, 0]],
[[70000076, 0, 0], [70000076, 1, 0], [70000076, 2, 0]],
],
},
{
name: "電脳享受空間",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 800000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[70000046, 1, 0], [70000160, 1, 0], [50000233, 1, 0]],
[[80000031, 1, 0], [80000097, 1, 0]],
[[90000049, 1, 0]],
],
},
{
name: "孤高の少女は破滅を願う",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 850000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000202, 0, 0], [70000117, 0, 0], [70000134, 0, 0]],
[[50000212, 0, 0], [80000124, 1, 0]],
[[90001008, 1, 1]],
],
},
{
name: "スタミナアップ!",
difficulty: 5,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2600000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000242, 0, 0], [50000277, 1, 0], [50000294, 1, 0]],
[[50000260, 1, 0], [50000261, 1, 0]],
[[90000143, 1, 0]],
],
},
{
name: "【四段】嗚呼、大繁盛!",
difficulty: 6,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2600000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000085, 2, 0], [50000237, 2, 0], [80000080, 2, 0]],
[[50000172, 2, 0], [50000235, 2, 0]],
[[70000065, 2, 1]],
],
},
{
name: "jubeat大回顧展 ROOM 1",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 950000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000277, 0, 0], [50000277, 1, 0], [50000277, 2, 0]],
[[50000325, 0, 0], [50000325, 1, 0], [50000325, 2, 0]],
[[90000014, 0, 0], [90000014, 1, 0], [90000014, 2, 0]],
],
},
{
name: "jubeat大回顧展 ROOM 2",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2750000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[30000048, 0, 0], [30000048, 1, 0], [30000048, 2, 0]],
[[30000121, 0, 0], [30000121, 1, 0], [30000121, 2, 0]],
[[90000012, 0, 0], [90000012, 1, 0], [90000012, 2, 0]],
],
},
{
name: "jubeat大回顧展 ROOM 3",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 925000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[60000007, 0, 0], [60000007, 1, 0], [60000007, 2, 0]],
[[60000070, 0, 0], [60000070, 1, 0], [60000070, 2, 0]],
[[90000016, 0, 0], [90000016, 1, 0], [90000016, 2, 0]],
],
},
{
name: "jubeat大回顧展 ROOM 4",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2800000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[40000051, 0, 0], [40000051, 1, 0], [40000051, 2, 0]],
[[40000129, 0, 0], [40000129, 1, 0], [40000129, 2, 0]],
[[90000013, 0, 0], [90000013, 1, 0], [90000013, 2, 0]],
],
},
{
name: "jubeat大回顧展 ROOM 5",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2775000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[70000177, 0, 0], [70000177, 1, 0], [70000177, 2, 0]],
[[70000011, 0, 0], [70000011, 1, 0], [70000011, 2, 0]],
[[90000017, 0, 0], [90000017, 1, 0], [90000017, 2, 0]],
],
},
{
name: "jubeat大回顧展 ROOM 6",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 940000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[20000123, 0, 0], [20000123, 1, 0], [20000123, 2, 0]],
[[20000038, 0, 0], [20000038, 1, 0], [20000038, 2, 0]],
[[90000011, 0, 0], [90000011, 1, 0], [90000011, 2, 0]],
],
},
{
name: "jubeat大回顧展 ROOM 7",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 950000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000021, 0, 0], [50000021, 1, 0], [50000021, 2, 0]],
[[50000078, 0, 0], [50000078, 1, 0], [50000078, 2, 0]],
[[90000015, 0, 0], [90000015, 1, 0], [90000015, 2, 0]],
],
},
{
name: "jubeat大回顧展 ROOM 8",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2800000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[80000028, 0, 0], [80000028, 1, 0], [80000028, 2, 0]],
[[80000087, 0, 0], [80000087, 1, 0], [80000087, 2, 0]],
[[90000018, 0, 0], [90000018, 1, 0], [90000018, 2, 0]],
],
},
{
name: "jubeat大回顧展 ROOM 9",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 930000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[10000038, 0, 0], [10000038, 1, 0], [10000038, 2, 0]],
[[10000065, 0, 0], [10000065, 1, 0], [10000065, 2, 0]],
[[90000010, 0, 0], [90000010, 1, 0], [90000010, 2, 0]],
],
},
{
name: "【三段】この花を貴方へ",
difficulty: 4,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 850000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000034, 1, 0], [90000107, 1, 0], [90000140, 1, 0]],
[[80000052, 1, 0], [80001010, 1, 0]],
[[40000051, 1, 1]],
],
},
{
name: "雨上がりレインボー",
difficulty: 9,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2650000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000138, 2, 0]],
[[80000057, 2, 0]],
[[90000011, 2, 0]],
],
},
{
name: "Rain時々雨チ雨",
difficulty: 9,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2650000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[30000050, 2, 0]],
[[80000123, 2, 0]],
[[50000092, 2, 0]],
],
},
{
name: "心に残った曲",
difficulty: 7,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2700000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[80000136, 0, 0], [80000136, 1, 0], [80000136, 2, 0]],
[[20000038, 0, 0], [20000038, 1, 0], [20000038, 2, 0]],
[[60000065, 0, 0], [60000065, 1, 0], [70000084, 1, 0]],
],
},
{
name: "黒船来航",
difficulty: 7,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 850000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000086, 2, 0], [60000066, 2, 0], [80000040, 1, 0]],
[[50000096, 2, 0], [80000048, 2, 0]],
[[50000091, 2, 0]],
],
},
{
name: "【五段】濁流を乗り越えて",
difficulty: 7,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2650000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000343, 2, 0], [60000060, 2, 0], [60000071, 2, 0]],
[[60000027, 2, 0], [80000048, 2, 0]],
[[20000038, 2, 1]],
],
},
{
name: "のんびり。ゆったり。ほがらかに。",
difficulty: 8,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 950000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[40000154, 2, 0], [80000124, 2, 0], [90000139, 2, 0]],
[[60000048, 2, 0], [80000041, 2, 0]],
[[90000050, 2, 0]],
],
},
{
name: "海・KOI・スィニョーレ",
difficulty: 8,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2650000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000201, 2, 0]],
[[50000339, 2, 0]],
[[50000038, 2, 0]],
],
},
{
name: "【六段】電柱を見ると思出す",
difficulty: 9,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2750000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000288, 2, 0], [80000046, 2, 0], [80001008, 2, 0]],
[[50000207, 2, 0], [70000117, 2, 0]],
[[30000048, 2, 1]],
],
},
{
name: "コクがある曲",
difficulty: 12,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2400000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000139, 0, 0], [50000139, 1, 0], [50000139, 2, 0]],
[[90000002, 0, 0], [90000002, 1, 0], [90000002, 2, 0]],
[[50000060, 0, 0], [50000060, 1, 0], [50000060, 2, 0]],
],
},
{
name: "超フェスタ!",
difficulty: 10,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 930000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[70000076, 2, 0], [70000077, 2, 0]],
[[20000038, 2, 0], [40000160, 2, 0]],
[[70000145, 2, 0]],
],
},
{
name: "【七段】操り人形はほくそ笑む",
difficulty: 10,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2800000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[70000006, 2, 0], [70000171, 2, 0], [80000003, 2, 0]],
[[50000078, 2, 0], [50000324, 2, 0]],
[[80000118, 2, 1]],
],
},
{
name: "絶体絶命スリーチャレンジ!",
difficulty: 11,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_HAZARD,
score: 0,
is_hard: false,
hazard_type: COURSE_HAZARD_FC3,
tune_list: [
[[50000238, 2, 0], [70000003, 2, 0], [90000051, 1, 0]],
[[50000027, 2, 0], [50000387, 2, 0]],
[[80000056, 2, 0]],
],
},
{
name: "天国の舞踏会",
difficulty: 11,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2800000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[60000065, 1, 0]],
[[80001007, 2, 0]],
[[90001007, 2, 1]],
],
},
{
name: "【八段】山の賽子",
difficulty: 12,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2820000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000200, 2, 0], [50000291, 2, 0], [60000003, 2, 0]],
[[50000129, 2, 0], [80000021, 2, 0]],
[[80000087, 2, 1]],
],
},
{
name: "The 8th KAC 個人部門",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000052, 2, 0]],
[[90000013, 2, 0]],
[[70000167, 2, 0]],
],
},
{
name: "The 8th KAC 団体部門",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000009, 2, 0]],
[[80000133, 2, 0]],
[[80000101, 2, 0]],
],
},/*
{
name: "BEMANI MASTER KOREA 2019",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000003, 2, 0]],
[[80000090, 2, 0]],
[[90000009, 2, 0]],
],
},*/
{
name: "The 9th KAC 1st Stage 個人部門",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000125, 2, 0]],
[[60000065, 2, 0]],
[[90000023, 2, 0]],
],
},
{
name: "The 9th KAC 1st Stage 団体部門",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000125, 2, 0]],
[[50000135, 2, 0]],
[[90000045, 2, 0]],
],
},
{
name: "The 9th KAC 2nd Stage 個人部門",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000095, 2, 0]],
[[80000085, 2, 0]],
[[80000090, 2, 0]],
],
},
{
name: "The 9th KAC 2nd Stage 団体部門",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000113, 2, 0]],
[[50000344, 2, 0]],
[[90000096, 2, 0]],
],
},
{
name: "The 10th KAC 1st Stage",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000003, 2, 0]],
[[90000151, 2, 0]],
[[90000174, 2, 0]],
],
},
{
name: "The 10th KAC 2nd Stage",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000121, 2, 0]],
[[90000113, 2, 0]],
[[90000124, 2, 0]],
],
},
{
name: "どうやって押してる?",
difficulty: 13,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2600000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[40000127, 0, 0]],
[[50000123, 0, 0]],
[[50000126, 0, 0]],
],
},
/*
{
name: "BEMANI MASTER KOREA 2021",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 700000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000180, 2, 0]],
[[90000095, 2, 0]],
[[90000047, 2, 0]],
],
},
*/
{
name: "初めてのHARD MODE再び",
difficulty: 13,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2750000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000096, 2, 0], [50000263, 2, 0], [80000119, 2, 0]],
[[60000021, 2, 0], [60000075, 2, 0]],
[[60000039, 2, 0]],
],
},
{
name: "【九段】2人からの挑戦状",
difficulty: 13,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2830000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000023, 2, 0], [80000025, 2, 0], [80000106, 2, 0]],
[[50000124, 2, 0], [80000082, 2, 0]],
[[60000115, 2, 1]],
],
},
{
name: "天空の庭 太陽の園",
difficulty: 13,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 965000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[40000153, 2, 0]],
[[80000007, 2, 0]],
[[70000173, 2, 0]],
],
},
{
name: "緊急!迅速!大混乱!",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2900000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[20000040, 2, 0], [50000244, 2, 0], [60000074, 2, 0]],
[[40000152, 2, 0], [50000158, 2, 0]],
[[40000057, 2, 0]],
],
},
{
name: "【十段】時の超越者",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2820000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[20000051, 2, 0], [50000249, 2, 0], [70000145, 2, 0]],
[[40000046, 2, 0], [50000180, 2, 0]],
[[50000134, 2, 1]],
],
},
{
name: "jubeat大回顧展 ROOM 10",
difficulty: 13,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2850000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[30000127, 2, 1]],
[[60000078, 2, 1]],
[[90000047, 2, 1]],
],
},
{
name: "【伝導】10代目最強に挑戦",
difficulty: 14,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2998179,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000100, 2, 0]],
[[90000047, 2, 0]],
[[90000057, 2, 0]],
],
},
{
name: "あなたのjubeatはどこから",
difficulty: 15,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2900000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[10000065, 0, 0], [10000065, 1, 0], [10000065, 2, 0]],
[[30000048, 0, 0], [30000048, 1, 0], [30000048, 2, 0]],
[[90000047, 0, 0], [90000047, 1, 0], [90000047, 2, 0]],
],
},
{
name: "【皆伝】甘味なのに甘くない",
difficulty: 15,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2850000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[90000010, 2, 1]],
[[80000101, 2, 1]],
[[50000102, 2, 1]],
],
},
{
name: "【伝導】真の青が魅せた空",
difficulty: 15,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_SCORE,
score: 970000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000332, 2, 0]],
[[70000098, 2, 0]],
[[90001005, 2, 1]],
],
},
{
name: "豪華絢爛高揚絶頂",
difficulty: 16,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2960000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[10000065, 2, 1]],
[[50000323, 2, 1]],
[[50000208, 2, 1]],
],
},
{
name: "絢爛豪華激情無常",
difficulty: 16,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2960000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[60000010, 2, 1]],
[[70000110, 2, 1]],
[[90000047, 2, 1]],
],
},
{
name: "【指神】王の降臨",
difficulty: 16,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2980000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[70000094, 2, 1]],
[[80000088, 2, 1]],
[[70000110, 2, 1]],
],
},
{
name: "【伝導】1116全てを超越した日",
difficulty: 16,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2975000,
is_hard: true,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000208, 2, 0]],
[[80000050, 2, 0]],
[[90000057, 2, 1]],
],
},
{
name: "My Top9 Fav Songs",
difficulty: 7,
course_type: COURSE_TYPE_PERMANENT,
etime: 0,
clear_type: COURSE_CLEAR_COMBINED_SCORE,
score: 2700000,
is_hard: false,
hazard_type: COURSE_HAZARD_NONE,
tune_list: [
[[50000049, 2, 1], [50000101, 2, 1], [80000136, 2, 1]],
[[80000084, 2, 1], [50000071, 2, 1], [50000084, 2, 1]],
[[60000009, 2, 1], [50000024, 2, 1], [90000173, 2, 1]],
],
},
];

View File

@ -0,0 +1,141 @@
import {emoList, shopList, FestoCourse, courseCategories} from "../static/data"
/*
if pos_index is not (1230 ~ 1236 or 1204 ~ 1205) and pos_index > 1200:
then all festo songs
*/
var pick_up_array = new Array(64).fill(-1);
for(var i=0; i<=36; i++){
pick_up_array[i] = 0;
}
pick_up_array[37] = -3211264;
pick_up_array[38] = -2080769;
module.exports = () => ({
info: {
white_music_list: K.ARRAY("s32", new Array(64).fill(-1)),
white_marker_list: K.ARRAY("s32", new Array(16).fill(-1)),
white_theme_list: K.ARRAY("s32", new Array(16).fill(-1)),
open_music_list: K.ARRAY("s32", new Array(64).fill(-1)),
add_default_music_list: K.ARRAY("s32", new Array(64).fill(-1)),
hot_music_list: K.ARRAY("s32", pick_up_array),
expert_option: {
is_available: K.ITEM("bool", true),
},
konami_logo_50th: {
is_available: K.ITEM("bool", true),
},
all_music_matching: {
is_available: K.ITEM("bool", false),
},
tsumtsum: {
is_available: K.ITEM("bool", false),
},
nagatanien: {
is_available: K.ITEM("bool", false),
},
digdig: {
stage_list: {
stage: [
K.ATTR({ number: "1" }, { state: K.ITEM("u8", 1) }),
K.ATTR({ number: "2" }, { state: K.ITEM("u8", 1) }),
K.ATTR({ number: "3" }, { state: K.ITEM("u8", 1) }),
K.ATTR({ number: "4" }, { state: K.ITEM("u8", 1) }),
K.ATTR({ number: "5" }, { state: K.ITEM("u8", 1) }),
K.ATTR({ number: "6" }, { state: K.ITEM("u8", 1) }),
K.ATTR({ number: "7" }, { state: K.ITEM("u8", 1) }),
],
},
},
department: {
shop_list: {
shop: shopList.map((shop, i) =>
K.ATTR(
{ id: String(i + 1) },
{
tex_id: K.ITEM("s32", shop.tex_id),
type: K.ITEM("s8", shop.type),
emo_id: K.ITEM("s32", shop.emo_id),
priority: K.ITEM("s32", shop.priority),
etime: K.ITEM("u64", BigInt(0)),
item_list: { item: [] },
}
)
),
},
},
course_list: {
course: FestoCourse.map((course, i) =>
K.ATTR(
{
release_code: "2022052400",
version_id: "0",
id: String(i + 1),
course_type: String(course.course_type),
},
{
difficulty: K.ITEM("s32", course.difficulty),
etime: K.ITEM("u64", BigInt(course.etime)),
name: K.ITEM("str", course.name),
tune_list: {
tune: course.tune_list.map((tune, i) =>
K.ATTR(
{ no: String(i + 1) },
{
seq_list: {
seq: tune.map((seq) => ({
music_id: K.ITEM("s32", seq[0]),
difficulty: K.ITEM("s32", seq[1]),
is_secret: K.ITEM("bool", seq[2]),
})),
},
}
)
),
},
clear: K.ATTR({type:String(course.clear_type)},{
ex_option:{
is_hard: K.ITEM("bool", course.is_hard),
hazard_type: K.ITEM("s32", course.hazard_type),
},
score: K.ITEM("s32", course.score),
reward_list:[],
})
}
)
),
category_list: {
category: courseCategories.map((categorie, i) =>
K.ATTR(
{ id: String(i + 1)},
{
is_secret: K.ITEM("bool", false),
level_min: K.ITEM("s32", categorie[0]),
level_max: K.ITEM("s32", categorie[1]),
}
)
)
},
},
emo_list: {
emo: emoList.map((emo, i) =>
K.ATTR(
{ id: String(i + 1) },
{
tex_id: K.ITEM("s32", emo.tex_id),
is_exchange: K.ITEM("bool", emo.is_exchange),
}
)
),
},
},
});

View File

@ -1,55 +0,0 @@
gametop
data
player
session_id(__type="u32") 1
jid(__type="u32") #{profile.jid}
name(__type="str") #{profile.name}
info
online_cnt(__type="s32") #{profile.ripples ? (profile.ripples.info ? profile.ripples.info.onlineCount : 0) : 0}
multi_cnt(__type="s32") #{profile.ripples ? (profile.ripples.info ? profile.ripples.info.multiCount : 0) : 0}
match_cnt(__type="s32") #{profile.ripples ? (profile.ripples.info ? profile.ripples.info.matchCount : 0) : 0}
beat_cnt(__type="s32") #{profile.ripples ? (profile.ripples.info ? profile.ripples.info.beatCount : 0) : 0}
save_cnt(__type="s32") #{profile.ripples ? (profile.ripples.info ? profile.ripples.info.saveCount : 0) : 0}
saved_cnt(__type="s32") #{profile.ripples ? (profile.ripples.info ? profile.ripples.info.savedCount : 0) : 0}
grade(__type="u8") #{profile.ripples ? (profile.ripples.info ? profile.ripples.info.grade : 0) : 0}
grade_point(__type="s32") #{profile.ripples ? (profile.ripples.info ? profile.ripples.info.gradePoint : 0) : 0}
item
secret_list(__type="u32" __count="2") -1 -1
marker_list(__type="u32") -1
theme_list(__type="u8") -1
title_list(__type="u32" __count="20") -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
new
secret_list(__type="u32" __count="2") 0 0
marker_list(__type="u32") 0
theme_list(__type="u8") 0
title_list(__type="u32" __count="20") 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
bm2dx(__type="u8") 0
last
mode(__type="u8") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.mode : 0) : 0}
music_id(__type="u32") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.musicId : 0) : 0}
seq_id(__type="u8") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.seqId : 0) : 0}
marker(__type="u8") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.marker : 0) : 0}
title(__type="s16") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.title : 0) : 0}
theme(__type="u8") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.theme : 0) : 0}
sort(__type="u8") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.sort : 0) : 0}
filter(__type="u32") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.filter : 0) : 0}
rank_sort(__type="u8") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.rankSort : 0) : 0}
combo_disp(__type="u8") #{profile.ripples ? (profile.ripples.last ? profile.ripples.last.comboDisp : 0) : 0}
playdata
each v, k in scores
musicdata(music_id=k)
score(__type="s32" __count="3") #{(v[0].score || 0) + " " + (v[1].score || 0) + " " + (v[2].score || 0)}
clear(__type="u8" __count="3") #{(v[0].clearFlag || 0) + " " + (v[1].clearFlag || 0) + " " + (v[2].clearFlag || 0)}
bar(__type="u8" __count="30" seq="0") #{v[0].mbar ? v[0].mbar.join(" ") : new Array(30).fill(0).join(" ")}
bar(__type="u8" __count="30" seq="1") #{v[1].mbar ? v[1].mbar.join(" ") : new Array(30).fill(0).join(" ")}
bar(__type="u8" __count="30" seq="2") #{v[2].mbar ? v[2].mbar.join(" ") : new Array(30).fill(0).join(" ")}
today_music
music_id(__type="u32") 0
rate(__type="float") 1.0

View File

@ -0,0 +1,441 @@
import Profile from "../models/profile";
import {Course} from "../models/course";
import {emoList, shopList, FestoCourse, courseCategories, COURSE_STATUS} from "../static/data"
module.exports = async (data: Profile) => ({
info: {
tune_cnt: K.ITEM("s32", data?.tuneCount || 0),
save_cnt: K.ITEM("s32", data?.saveCount || 0),
saved_cnt: K.ITEM("s32", data?.savedCount || 0),
fc_cnt: K.ITEM("s32", data?.fcCount || 0),
ex_cnt: K.ITEM("s32", data?.exCount || 0),
clear_cnt: K.ITEM("s32", data?.clearCount || 0),
match_cnt: K.ITEM("s32", data?.matchCount || 0),
beat_cnt: K.ITEM("s32", 0),
mynews_cnt: K.ITEM("s32", 0),
mtg_entry_cnt: K.ITEM("s32", 0),
mtg_hold_cnt: K.ITEM("s32", 0),
mtg_result: K.ITEM("u8", 0),
bonus_tune_points: K.ITEM("s32", data?.bonusPoints || 0),
is_bonus_tune_played: K.ITEM("bool", data?.isBonusPlayed || false),
last_play_time: K.ITEM("s64", data?.lastPlayTime || 0),
},
last: {
play_time: K.ITEM("s64", data?.lastPlayTime || 0),
shopname: K.ITEM("str", data.lastShopname),
areaname: K.ITEM("str", data.lastAreaname),
music_id: K.ITEM("s32", data.musicId || 0),
seq_id: K.ITEM("s8", data.seqId || 0),
seq_edit_id: K.ITEM("str", data.seqEditId || ""),
sort: K.ITEM("s8", data?.sort || 0),
category: K.ITEM("s8", data?.category || 0),
expert_option: K.ITEM("s8", data?.expertOption || 0),
dig_select: K.ITEM("s32", 0),
settings: {
marker: K.ITEM("s8", data?.marker || 0),
theme: K.ITEM("s8", data?.theme || 0),
title: K.ITEM("s16", data?.title || 0),
parts: K.ITEM("s16", data?.parts || 0),
rank_sort: K.ITEM("s8", data?.rankSort || 0),
combo_disp: K.ITEM("s8", data?.comboDisp || 0),
emblem: K.ARRAY("s16", data?.emblem || [0, 0, 0, 0, 0]),
matching: K.ITEM("s8", data?.matching || 0),
hard: K.ITEM("s8", data?.hard || 0),
hazard: K.ITEM("s8", data?.hazard || 0),
},
},
item: {
music_list: K.ARRAY("s32", new Array(64).fill(-1)),
secret_list: K.ARRAY("s32", new Array(64).fill(-1)),
theme_list: K.ARRAY("s32", new Array(16).fill(-1)),
marker_list: K.ARRAY("s32", new Array(16).fill(-1)),
title_list: K.ARRAY("s32", new Array(160).fill(-1)),
parts_list: K.ARRAY("s32", data?.partsList || new Array(160).fill(0)),
emblem_list: K.ARRAY("s32", new Array(96).fill(-1)),
commu_list: K.ARRAY("s32", data?.commuList || new Array(16).fill(0)),
new: {
secret_list: K.ARRAY("s32", new Array(64).fill(0)),
theme_list: K.ARRAY("s32", new Array(16).fill(0)),
marker_list: K.ARRAY("s32", new Array(16).fill(0)),
},
},
rivallist: {
rival: [].map((rival) => ({
jid: K.ITEM("s32", rival.jubeatId),
name: K.ITEM("str", rival.name),
career: {
level: K.ITEM("s16", 0),
},
})),
},
lab_edit_seq: K.ATTR({ count: "0" }, { seq: [] }),
fc_challenge: {
today: {
music_id: K.ITEM("s32", -1),
state: K.ITEM("u8", 0),
},
whim: {
music_id: K.ITEM("s32", -1),
state: K.ITEM("u8", 0),
},
},
official_news: {
news_list: { news: [] },
},
news: {
checked: K.ITEM("s16", 0),
checked_flag: K.ITEM("u32", 0),
},
history: K.ATTR({ count: "0" }, { tune: [] }),
free_first_play: {
is_available: K.ITEM("bool", data?.isFirstplay || false),
},
event_info: { event: [] },
jbox: {
point: K.ITEM("s32", 0),
emblem: {
normal: { index: K.ITEM("s16", 2) },
premium: { index: K.ITEM("s16", 1) },
},
},
new_music: {},
navi: {
flag: K.ITEM("u64", BigInt(data?.navi || 0)),
},
gift_list: {},
question_list: {},
team_battle: {},
server: {},
course_list: {
course: await (async () =>{
let courseData = await DB.Find<Course>(data.__refid, { collection: "course" });
let courseStatus = {};
courseData.forEach(course =>{
courseStatus[course.courseId] |= (course.seen ? COURSE_STATUS.SEEN : 0);
courseStatus[course.courseId] |= (course.played ? COURSE_STATUS.PLAYED : 0);
courseStatus[course.courseId] |= (course.cleared ? COURSE_STATUS.CLEARED : 0);
});
return FestoCourse.map((course, i) =>
K.ATTR({ id: String(i + 1) }, { status: K.ITEM("s8", courseStatus[i+1] || 0) })
);
})()
},
category_list: {
category: courseCategories.map((categorie, i) =>
K.ATTR(
{ id: String(i + 1)},
{
is_display: K.ITEM("bool", true),
}
)
)
},
fill_in_category: {
no_gray_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
all_yellow_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
full_combo_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
excellent_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
normal: {
no_gray_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
all_yellow_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
full_combo_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
excellent_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
},
hard: {
no_gray_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
all_yellow_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
full_combo_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
excellent_flag_list: K.ARRAY("s32", [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]),
},
},
emo_list: {
emo: emoList.map((emo, i) => {
return K.ATTR({ id: String(i + 1) }, { num: K.ITEM("s32", 0) });
}),
},
eamuse_gift_list: { gift: [] },
department: {
shop_list: { shop: [] },
},
clan_course_list: {},
team: K.ATTR(
{ id: "0" },
{
section: K.ITEM("s32", 0),
street: K.ITEM("s32", 0),
house_number_1: K.ITEM("s32", 0),
house_number_2: K.ITEM("s32", 0),
move: K.ATTR({
id: "1",
section: "1",
street: "1",
house_number_1: "1",
house_number_2: "1",
}),
}
),
daily_bonus_list: {},
ticket_list: {},
digdig: {
flag: K.ITEM("u64", BigInt(0)),
main: {
stage: K.ATTR(
{ number: "0" },
{
point: K.ITEM("s32", 0),
param: K.ITEM("s32", 0),
}
),
},
eternal: {
ratio: K.ITEM("s32", 0),
used_point: K.ITEM("s64", BigInt(0)),
point: K.ITEM("s64", BigInt(0)),
cube: {
state: K.ITEM("s8", 0),
item: [],
},
norma: {
till_time: K.ITEM("s64", BigInt(0)),
kind: K.ITEM("s32", 0),
value: K.ITEM("s32", 0),
param: K.ITEM("s32", 0),
},
old: {
need_point: K.ITEM("s32", 0),
point: K.ITEM("s32", 0),
excavated_point: K.ITEM("s32", 0),
excavated: K.ITEM("s32", 0),
param: K.ITEM("s32", 0),
music_list: {},
},
},
},
unlock: {},
generic_dig: {},
});

View File

@ -1,6 +0,0 @@
export function getVersion({ model }: EamuseInfo): number {
if (model.startsWith('H44')) return 1;
if (model.startsWith('I44')) return 2;
return 0;
}

View File

@ -1,6 +1,6 @@
# Pop'n Music
Plugin Version: **v2.2.3**
Plugin Version: **v3.0.0**
## Supported Versions
- pop'n music 19 Tune Street
@ -10,23 +10,29 @@ Plugin Version: **v2.2.3**
- pop'n music éclale
- pop'n music Usagi to Neko to Shōnen no Yume
- pop'n music peace
- pop'n music Kaimei riddles
Important : require minimum Asphyxia Core **v1.31**
## Changelog
### 3.0.0
* Kaimei riddles: Support added
* Usaneko: Add Daily Missions support
* Usaneko/Peace/Kaimei: Remove game id check for Omnimix
### 2.2.3
* All : Send 0 if clear_type is not existing.
* All: Send 0 if clear_type is not existing.
### 2.2.2
* Usaneko/Peace : Add Omnimix support (songs with id >= 3000).
* Usaneko/Peace: Add Omnimix support (songs with id >= 3000).
### 2.2.1
* Tune Street : User customization is now saved
* Tune Street: User customization is now saved
* Fix 1.x to 2.x conversion code when there are multiple profiles
### 2.2.0
* Tune Street : Add Town Mode + enable Net Taisen (only CPU will works)
* Tune Street: Add Town Mode + enable Net Taisen (only CPU will works)
* Some fixes
### 2.1.0

View File

@ -26,8 +26,8 @@ const getInfoCommon = (req: EamuseInfo) => {
};
// Phase
const date: number = parseInt(req.model.match(/:(\d*)$/)[1]);
let phaseData: Phase[] = PHASE[getVersion(req)];
const version = getVersion(req);
const phaseData = getPhase(version);
for (const phase of phaseData) {
result.phase.push({
@ -166,7 +166,6 @@ const readScore = async (req: EamuseInfo, data: any, send: EamuseSend): Promise<
const getScores = async (refid: string, version: string, forFriend: boolean = false) => {
const scoresData = await utils.readScores(refid, version);
const result = [];
const maxMusicId = GAME_MAX_MUSIC_ID[isOmni ? 'omni' : version];
for (const key in scoresData.scores) {
const keyData = key.split(':');
@ -187,7 +186,7 @@ const getScores = async (refid: string, version: string, forFriend: boolean = fa
1100: 11,
}[score.clear_type] || 0;
if (music > maxMusicId) {
if (!isOmni && (music > GAME_MAX_MUSIC_ID[version])) {
continue;
}
if ([0, 1, 2, 3].indexOf(sheet) == -1) {
@ -377,25 +376,8 @@ const getProfile = async (refid: string, version: string, name?: string) => {
friendship: K.ITEM('s32', 0),
},
},
// TODO: Daily missions
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),
},
],
music: await getScores(refid, version),
mission: [],
area: [],
course_data: [],
fes: [],
@ -404,6 +386,10 @@ const getProfile = async (refid: string, version: string, name?: string) => {
stamp: [],
};
// Add version specific datas
let params = await utils.readParams(refid, version);
utils.addExtraData(player, params, EXTRA_DATA);
const achievements = <AchievementsUsaneko>await utils.readAchievements(refid, version, { ...defaultAchievements, version });
const profileCharas = achievements.charas || {};
@ -467,20 +453,94 @@ const getProfile = async (refid: string, version: string, name?: string) => {
const type = parseInt(keyData[0], 10);
const id = parseInt(keyData[1], 10);
const item: any = {
player.item.push({
type: K.ITEM('u8', type),
id: K.ITEM('u16', id),
param: K.ITEM('u16', profileItems[key]),
is_new: K.ITEM('bool', 0),
get_time: K.ITEM('u64', BigInt(0)),
};
player.item.push(item);
});
}
// Add version specific datas
let params = await utils.readParams(refid, version);
utils.addExtraData(player, params, EXTRA_DATA);
// Usaneko events
if (version == 'v24') {
const date = new Date();
const currentDate = date.getFullYear() + '-' + date.getMonth() + '-' + date.getDate();
if (!params.params.mission_date) {
params.params.mission_date = currentDate;
}
// Daily missions
const missions = achievements.missions || { 1: {}, 2: {}, 3: {} };
let maxId = parseInt(_.max(Object.keys(missions)), 10);
for (const id in missions) {
const mission = missions[id];
if (currentDate != params.params.mission_date) {
// New day => Check completion
if(mission.mission_comp == 1) {
// Mission completed => New mission
_.max(Object.keys(missions)) + 1
player.mission.push({
mission_id: K.ITEM('u32', ++maxId),
gauge_point: K.ITEM('u32', 0),
mission_comp: K.ITEM('u32', 0),
});
} else {
// Mission not completed => Reset counter
player.mission.push({
mission_id: K.ITEM('u32', parseInt(id, 10)),
gauge_point: K.ITEM('u32', 0),
mission_comp: K.ITEM('u32', 0),
});
}
} else {
player.mission.push({
mission_id: K.ITEM('u32', parseInt(id, 10)),
gauge_point: K.ITEM('u32', mission.gauge_point || 0),
mission_comp: K.ITEM('u32', mission.mission_comp || 0),
});
}
}
}
// Kaimei events
if (version == 'v26') {
// Kaimei! MN tanteisha
player.riddles_data = {
sp_riddles: [],
sh_riddles: []
}
player.riddles_data.sp_riddles = [];
const riddles = achievements.riddles || {};
let i = 0;
while (riddles[i] != undefined) {
const riddle = riddles[i];
player.riddles_data.sp_riddles.push({
kaimei_gauge: K.ITEM('u16', riddle.kaimei_gauge || 0),
is_cleared: K.ITEM('bool', riddle.is_cleared || false),
riddles_cleared: K.ITEM('bool', riddle.riddles_cleared || false),
select_count: K.ITEM('u8', riddle.select_count || 0),
other_count: K.ITEM('u32', riddle.other_count || 0),
});
i++;
};
// riddle id : 1 to 20
let randomRiddles = [];
for (let i = 0; i < 3; i++) {
let riddle = 0;
do {
riddle = Math.floor(Math.random() * 20) + 1;
} while (randomRiddles.indexOf(riddle) >= 0);
player.riddles_data.sh_riddles.push({ sh_riddles_id: K.ITEM('u32', riddle) });
}
}
return player;
}
@ -628,6 +688,71 @@ const write = async (req: EamuseInfo, data: any, send: EamuseSend): Promise<any>
achievements.stamps[id] = cnt;
}
// usaneko (v24)
if (version == 'v24') {
// Daily missions
const date = new Date();
params.params.mission_date = date.getFullYear() + '-' + date.getMonth() + '-' + date.getDate();
let missions = _.get(data, 'mission', []);
achievements.missions = {};
if (!_.isArray(missions)) {
missions = [missions];
}
for (const mission of missions) {
const id = $(mission).number('mission_id');
const gauge_point = $(mission).number('gauge_point');
const mission_comp = $(mission).number('mission_comp');
achievements.missions[id] = {
gauge_point,
mission_comp,
};
}
}
// riddles (v26)
if (version == 'v26') {
const playedRiddle = <number>params.params.sp_riddles_id;
let riddlesData = _.get(data, 'riddles_data', []);
let riddles = _.get(riddlesData, 'sp_riddles', []);
if (!achievements.riddles) {
achievements.riddles = {};
}
if (!_.isArray(riddles)) {
riddles = [riddles];
}
let i = 0;
for (const riddle of riddles) {
const kaimei_gauge = $(riddle).number('kaimei_gauge', 0);
const is_cleared = $(riddle).bool('is_cleared');
const riddles_cleared = $(riddle).bool('riddles_cleared');
let select_count = $(riddle).number('select_count', 0);
const other_count = $(riddle).number('other_count', 0);
if (riddles_cleared || select_count >= 3) {
// Show all hint if riddle cleared.
select_count = 3
} else if (playedRiddle == i) {
// Add a hint if riddle is select.
select_count++;
}
achievements.riddles[i] = {
kaimei_gauge,
is_cleared,
riddles_cleared,
select_count,
other_count,
};
i++;
}
}
await utils.writeParams(refid, version, params);
await utils.writeAchievements(refid, version, achievements);
@ -666,17 +791,32 @@ const friend = async (req: EamuseInfo, data: any, send: EamuseSend): Promise<any
send.object(friend);
}
const getPhase = (version: String): Phase[] => {
let phase = [];
switch(version) {
case 'v26':
phase = PHASE['v26'];
case 'v25':
phase = _.unionBy(phase, PHASE['v25'], 'id');
case 'v24':
phase = _.unionBy(phase, PHASE['v24'], 'id');
}
return _.sortBy(phase, 'id');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let isOmni= false;
let isOmni = false;
const getVersion = (req: EamuseInfo): string => {
if(req.model.indexOf('J:A:X') >= 0) {
if (req.model.indexOf('J:A:X') >= 0 || req.model.indexOf('J:B:X') >= 0 || req.model.indexOf('J:C:X') >= 0) {
isOmni = true;
}
const date: number = parseInt(req.model.match(/:(\d*)$/)[1]);
if (date >= 2018101700) {
if (date > 2020120900) {
return 'v26';
} else if (date >= 2018101700 && date <= 2020120900) {
return 'v25';
} else {
return 'v24';
@ -686,7 +826,7 @@ const getVersion = (req: EamuseInfo): string => {
const GAME_MAX_MUSIC_ID = {
v24: 1704,
v25: 1877,
omni: 3155
v26: 2019
}
const defaultAchievements: AchievementsUsaneko = {
@ -698,6 +838,8 @@ const defaultAchievements: AchievementsUsaneko = {
items: {},
charas: {},
stamps: {},
riddles: {},
missions: {},
}
const PHASE = {
@ -711,7 +853,7 @@ const PHASE = {
{ id: 6, p: 1 }, // Enable NAVI-kun shunkyoku toujou, allows song 1608 to be unlocked (0-1)
{ id: 7, p: 1 },
{ id: 8, p: 2 },
{ id: 9, p: 0 }, // Daily Mission (0-2)
{ id: 9, p: 2 }, // Daily Mission (0-2)
{ id: 10, p: 15 }, // NAVI-kun Song phase availability (0-15)
{ id: 11, p: 1 },
{ id: 12, p: 2 },
@ -720,18 +862,8 @@ const PHASE = {
v25: [
{ id: 0, p: 23 },
{ id: 1, p: 4 },
{ id: 2, p: 2 },
{ id: 3, p: 4 },
{ id: 4, p: 1 },
{ id: 5, p: 0 }, // Enable Net Taisen (0-1)
{ id: 6, p: 1 },
{ id: 7, p: 1 },
{ id: 8, p: 2 },
{ id: 9, p: 0 }, // Daily Mission (0-2)
{ id: 10, p: 30 },
{ id: 11, p: 1 },
{ id: 12, p: 2 },
{ id: 13, p: 1 },
// New params
{ id: 14, p: 39 },
{ id: 15, p: 2 },
{ id: 16, p: 3 },
@ -743,6 +875,24 @@ const PHASE = {
{ id: 22, p: 2 },
{ id: 23, p: 1 },
{ id: 24, p: 1 },
],
v26: [
// Music phase
// Phase 24: Seize The Day, 知りたい
// Phase 25: Triple Cross
// Phase 26: GO²TOS, Jailbreaker
// Phase 27: Aftermath
// Phase 28: 「Sweet Love」
// Phase 29: GET WILD (UPPER), シュガーソングとビターステップ (UPPER)
// Phase 30 (MAX): 群像夏
{ id: 0, p: 30 },
// New params
{ id: 25, p: 62 }, // M&N event (0: disable, 62: all characters)
{ id: 26, p: 3 }, // Unknown event (0-3)
{ id: 27, p: 2 }, // peace soundtrack hatsubai kinen SP (0: not started, 1: enabled, 2: ended)
{ id: 28, p: 2 }, // MZD no kimagure tanteisha joshu (0: not started, 1: enabled, 2: ended)
{ id: 29, p: 5 }, // Shutchou! pop'n quest Lively (0: not started, 1-4: step enabled, 5: ended)
{ id: 30, p: 6 }, // Shutchou! pop'n quest Lively II (0: not started, 1-5: step enabled, 6: ended)
]
}
@ -773,6 +923,15 @@ const EXTRA_DATA: ExtraData = {
player_point: { type: 's32', path: 'account', default: 300 },
power_point_list: { type: 's32', path: 'account', default: [0], isArray: true },
//v26
card_again_count: { type: 's16', path: 'account', default: 0 },
sp_riddles_id: { type: 's16', path: 'account', default: -1 },
point: { type: 'u32', path: 'event2021', default: 0 }, // for peace soundtrack hatsubai kinen SP
step: { type: 'u8', path: 'event2021', default: 0 }, // for Shutchou! pop'n quest Lively
quest_point: { type: 'u32', path: 'event2021', default: Array(8).fill(0), isArray: true }, // for Shutchou! pop'n quest Lively
step_nos: { type: 'u8', path: 'event2021', default: 0 }, // for Shutchou! pop'n quest Lively II
quest_point_nos: { type: 'u32', path: 'event2021', default: Array(13).fill(0), isArray: true }, // for Shutchou! pop'n quest Lively II
mode: { type: 'u8', path: 'config', default: 0 },
chara: { type: 's16', path: 'config', default: 0 },
music: { type: 's16', path: 'config', default: 0 },
@ -813,4 +972,4 @@ const EXTRA_DATA: ExtraData = {
hukidashi: { type: 'u16', path: 'customize', default: 0 },
comment_1: { type: 'u16', path: 'customize', default: 0 },
comment_2: { type: 'u16', path: 'customize', default: 0 },
}
}

View File

@ -65,7 +65,7 @@ export interface AchievementsEclale extends Achievements {
}
export interface AchievementsUsaneko extends Achievements {
version: 'v24' | 'v25',
version: 'v24' | 'v25' | 'v26',
areas: {
[id: string]: {
@ -94,6 +94,13 @@ export interface AchievementsUsaneko extends Achievements {
};
};
missions: {
[id: string]: {
gauge_point: number;
mission_comp: number;
};
};
items: {
[key: string]: number;
};
@ -105,4 +112,14 @@ export interface AchievementsUsaneko extends Achievements {
stamps: {
[stamp_id: string]: number;
};
riddles: {
[id: number]: {
kaimei_gauge: number;
is_cleared: boolean;
riddles_cleared: boolean;
select_count: number;
other_count: number;
};
};
}