plugins/iidx@asphyxia/handlers/music.ts
2025-09-28 23:45:40 +09:00

1034 lines
33 KiB
TypeScript

import { IDtoRef, GetVersion, OldMidToNewMid, NewMidToOldMid, ReftoProfile, ReftoPcdata, ClidToPlaySide, ReftoQPRO, NumArrayToString, OldMidToVerMid, GetWeekId } from "../util";
import { score, score_top } from "../models/score";
import { profile } from "../models/profile";
import { shop_data } from "../models/shop";
import { tutorial } from "../models/tutorial";
import { badge } from "../models/badge";
import { activity_mybest } from "../models/activity";
export const musicgetrank: EPR = async (info, data, send) => {
const version = GetVersion(info);
const refid = await IDtoRef(Number($(data).attr().iidxid));
const cltype = Number($(data).attr().cltype); // 0 -> SP, 1 -> DP //
const music_data: any = (
await DB.Find(refid, {
collection: "score",
})
);
const rival_refids = [
[Number($(data).attr().iidxid0), await IDtoRef(Number($(data).attr().iidxid0))],
[Number($(data).attr().iidxid1), await IDtoRef(Number($(data).attr().iidxid1))],
[Number($(data).attr().iidxid2), await IDtoRef(Number($(data).attr().iidxid2))],
[Number($(data).attr().iidxid3), await IDtoRef(Number($(data).attr().iidxid3))],
[Number($(data).attr().iidxid4), await IDtoRef(Number($(data).attr().iidxid4))],
];
let m = [], top = [], b = [], t = [];
let score_data: number[];
let indices, temp_mid = 0;
if (version == 14 || version == 15) {
let result = {
r: [], // v - (-1, beginner/-2, tutorial) //
};
indices = cltype === 0 ? [1, 2, 3] : [6, 7, 8];
music_data.forEach((res: score) => {
if (_.isNil(res.cArray)) throw new Error("[music.getrank] There is unsupported entry in Database");
temp_mid = NewMidToOldMid(res.mid);
let verMid = OldMidToVerMid(temp_mid);
// TODO:: determine whether use rid,dj_level from music.reg or make a database that has max exscore of all songs for rid //
if (verMid[0] > version) return;
for (let a = 0; a < 3; a++) {
if (res.esArray[indices[a]] == 0) continue;
let rank_id = _.isNil(res.rArray) ? -1 : res.rArray[indices[a]];
result.r.push(
K.ITEM("str", NumArrayToString(
[7, 4, 13, 3, 3],
[verMid[1], a, res.esArray[indices[a]], rank_id, res.cArray[indices[a]]] // 4th element is rid (rank_id) //
), { v: String(verMid[0]) })
);
}
// BEGINNER //
if (res.cArray[0] == 0) return;
result.r.push(
K.ITEM("str", NumArrayToString(
[12, 6],
[temp_mid, res.cArray[0]]
), { v: String("-1") })
);
});
// TUTORIAL //
const tutorial = await DB.Find<tutorial>(refid, {
collection: "tutorial",
version: version
});
tutorial.sort((a: tutorial, b: tutorial) => a.tid - b.tid);
tutorial.forEach((res) => {
result.r.push(
K.ITEM("str", NumArrayToString(
[5, 1],
[res.tid, res.clr]
), { v: String("-2") })
);
});
return send.object(result);
}
else if (version < 20) {
indices = cltype === 0 ? [1, 2, 3] : [6, 7, 8];
music_data.forEach((res: score) => {
if (_.isNil(res.cArray)) throw new Error("[music.getrank] There is unsupported entry in Database");
temp_mid = NewMidToOldMid(res.mid);
let mVersion = Math.floor(temp_mid / 100);
if (mVersion > version) return;
if (version == 16) score_data = [-1, temp_mid, ...indices.map(i => res.cArray[i]), ...indices.map(i => res.esArray[i])];
else score_data = [-1, temp_mid, ...indices.map(i => res.cArray[i]), ...indices.map(i => res.esArray[i]), ...indices.map(i => res.mArray[i])];
m.push(K.ARRAY("s16", score_data));
if (res.cArray[0] != 0) b.push(K.ARRAY("u16", [temp_mid, res.cArray[0]]));
});
for (let i = 0; i < rival_refids.length; i++) {
if (_.isNaN(rival_refids[i][0])) continue;
const rival_score = await DB.Find<score>(String(rival_refids[i][1]),
{ collection: "score", }
);
rival_score.forEach((res: score) => {
temp_mid = NewMidToOldMid(res.mid);
let mVersion = Math.floor(temp_mid / 100);
if (mVersion > version) return;
if (version == 16) score_data = [i, temp_mid, ...indices.map(i => res.cArray[i]), ...indices.map(i => res.esArray[i])];
else score_data = [i, temp_mid, ...indices.map(i => res.cArray[i]), ...indices.map(i => res.esArray[i]), ...indices.map(i => res.mArray[i])];
m.push(K.ARRAY("s16", score_data));
});
}
// tutorial //
const tutorial = await DB.Find<tutorial>(refid, {
collection: "tutorial",
version: version
});
tutorial.sort((a: tutorial, b: tutorial) => a.tid - b.tid);
tutorial.forEach((res) => {
t.push(K.ARRAY("u16", [res.tid, res.clr]));
});
}
else if (version >= 20) {
if (version >= 27) indices = cltype === 0 ? [0, 1, 2, 3, 4] : [5, 6, 7, 8, 9];
else indices = cltype === 0 ? [1, 2, 3] : [6, 7, 8];
music_data.forEach((res: score) => {
if (_.isNil(res.cArray)) throw new Error("[music.getrank] There is unsupported entry in Database");
let mVersion = Math.floor(res.mid / 1000);
if (mVersion > version) return;
score_data = [-1, res.mid, ...indices.map(i => res.cArray[i]), ...indices.map(i => res.esArray[i]), ...indices.map(i => res.mArray[i])];
m.push(K.ARRAY("s16", score_data));
if (res.cArray[0] != 0) b.push(K.ARRAY("u16", [res.mid, res.cArray[0]]));
});
for (let i = 0; i < rival_refids.length; i++) {
if (_.isNaN(rival_refids[i][0])) continue;
const rival_score = await DB.Find<score>(String(rival_refids[i][1]),
{ collection: "score", }
);
rival_score.forEach((res: score) => { // rival score //
let mVersion = Math.floor(res.mid / 1000);
if (mVersion > version) return;
score_data = [i, res.mid, ...indices.map(i => res.cArray[i]), ...indices.map(i => res.esArray[i]), ...indices.map(i => res.mArray[i])];
m.push(K.ARRAY("s16", score_data));
});
}
const score_top = await DB.Find<score_top>({
collection: "score_top",
play_style: cltype,
});
if (score_top.length > 0) {
if (version >= 27) {
score_top.forEach((res) => {
let mVersion = Math.floor(res.mid / 1000);
if (mVersion > version) return;
top.push({
"@attr": ({
name0: res.names[0],
name1: res.names[1],
name2: res.names[2],
name3: res.names[3],
name4: res.names[4],
}),
detail: K.ARRAY("s16", [res.mid, ...res.clflgs, ...res.scores])
});
});
} else {
score_top.forEach((res) => {
let mVersion = Math.floor(res.mid / 1000);
if (mVersion > version) return;
top.push({
"@attr": ({
name0: res.names[1],
name1: res.names[2],
name2: res.names[3],
}),
detail: K.ARRAY("s16", [res.mid, ...indices.map(i => res.clflgs[i]), ...indices.map(i => res.scores[i])])
});
});
}
}
return send.object({
style: K.ATTR({ type: String(cltype) }),
m,
b,
top,
});
}
else {
return send.success();
}
return send.object({
m,
b,
t
});
}
export const musicgetralive: EPR = async (info, data, send) => {
const version = GetVersion(info);
const refid = await IDtoRef(Number($(data).attr().iidxid));
const cltype = Number($(data).attr().cltype); // 0 -> SP, 1 -> DP //
const music_data: any = (
await DB.Find(refid, {
collection: "score",
})
);
const rival_refids = [
[Number($(data).attr().iidxid0), await IDtoRef(Number($(data).attr().iidxid0))],
[Number($(data).attr().iidxid1), await IDtoRef(Number($(data).attr().iidxid1))],
[Number($(data).attr().iidxid2), await IDtoRef(Number($(data).attr().iidxid2))],
[Number($(data).attr().iidxid3), await IDtoRef(Number($(data).attr().iidxid3))],
[Number($(data).attr().iidxid4), await IDtoRef(Number($(data).attr().iidxid4))],
];
let result = {
d: []
};
let myRecord: Record<number, number[]> = {};
let rRecord: Record<number, string> = {};
let indices = cltype === 0 ? [1, 2, 3] : [6, 7, 8];
music_data.forEach((res: score) => {
if (_.isNil(res.cArray)) throw new Error("[music.getralive] There is unsupported entry in Database");
myRecord[NewMidToOldMid(res.mid)] = [...res.esArray, ...res.cArray];
});
for (let i = 0; i < rival_refids.length; i++) {
if (_.isNaN(rival_refids[i][0])) continue;
const rival_score = await DB.Find<score>(String(rival_refids[i][1]),
{ collection: "score", }
);
// [0~2] - NOPLAY/WIN/LOSE (ANOTHER/HYPER/NORMAL), //
// consider same score as LOSE, tho theres seems DRAW state but game render as LOSE //
// TODO:: figure out what other elements does //
rival_score.forEach((res: score) => {
let mid = NewMidToOldMid(res.mid);
let verMid = OldMidToVerMid(mid);
if (verMid[0] > version) return;
let scoreArray = Array<number>(15).fill(0);
if (!_.isNil(myRecord[mid])) {
for (let a = 0; a < 3; a++) {
let myExscore = myRecord[mid][indices[a]];
let rvExscore = res.esArray[indices[a]];
let mycFlg = myRecord[mid][indices[a] + 10];
let rvcFlg = res.cArray[indices[a]];
if (mycFlg == 0 || rvcFlg == 0) continue;
scoreArray[2 - a] = myExscore > rvExscore ? 1 : 2;
}
}
let strResult = NumArrayToString([6], [verMid[1]]);
strResult += NumArrayToString(Array<number>(15).fill(2), scoreArray);
if (verMid[0] in rRecord) {
rRecord[verMid[0]] += strResult;
} else {
rRecord[verMid[0]] = strResult;
}
});
}
for (const key in rRecord) {
result.d.push(
K.ITEM("str", rRecord[key], { v: key })
);
}
return send.object(result);
}
export const musicappoint: EPR = async (info, data, send) => {
const version = GetVersion(info);
// clid, ctype, grd, iidxid, lv, mid, subtype //
const refid = await IDtoRef(Number($(data).attr().iidxid));
const ctype = Number($(data).attr().ctype);
const subtype = Number($(data).attr().subtype);
let mid = Number($(data).attr().mid);
let clid = Number($(data).attr().clid);
const mapping = [1, 2, 3, 6, 7, 8];
if (version < 20) {
mid = OldMidToNewMid(mid);
clid = mapping[clid];
}
else if (version < 27) {
clid = mapping[clid];
}
let result: any = {};
// MINE //
const music_data: score | null = await DB.FindOne<score>(refid, {
collection: "score",
mid: mid,
[clid]: { $exists: true },
});
let mydata, option = 0, option2 = 0;
if (!_.isNil(music_data)) {
if (version >= 27) {
if (!_.isNil(music_data.optArray) && version > 27) {
option = music_data.optArray[clid];
option2 = music_data.opt2Array[clid];
}
}
if (version < 16) mydata = K.ITEM("str", Buffer.from(music_data[clid], "base64").toString("hex").toUpperCase());
else mydata = K.ITEM("bin", Buffer.from(music_data[clid], "base64"));
}
/*** ctype
[-1] - DEFAULT
[1] - RIVAL
[2] - ALL TOP
[3] - ALL AVG.
[4] - LOCATION TOP
[5] - LOCATION AVG.
[6] - SAME DAN TOP
[7] - SAME DAN AVG.
[8] - RIVAL TOP
[9] - RIVAL AVG.
[10] - STORE TOP
[13] - RIVAL NEXT
[14] - STORE ROTATE
[15] - RIVAL ROTATE
***/
// OTHERS //
let other_refid, other_musicdata: score | null, other_pcdata, other_profile, sdata = null;
if (!_.isNaN(subtype)) {
switch (ctype) {
case 1:
other_refid = await IDtoRef(subtype);
other_profile = await ReftoProfile(other_refid);
other_pcdata = await ReftoPcdata(other_refid, version);
other_musicdata = await DB.FindOne<score>(other_refid, {
collection: "score",
mid: mid,
[clid]: { $exists: true },
});
if (_.isNaN(other_pcdata) || _.isNil(other_musicdata)) break;
if (version < 16) {
sdata = K.ITEM("str", Buffer.from(other_musicdata[clid], "base64").toString("hex").toUpperCase(), {
score: String(other_musicdata.esArray[clid]),
pid: String(other_profile[1]),
name: String(other_profile[0]),
riidxid: String(other_profile[2])
});
}
else {
sdata = K.ITEM("bin", Buffer.from(other_musicdata[clid], "base64"), {
score: String(other_musicdata.esArray[clid]),
pid: String(other_profile[1]),
name: String(other_profile[0]),
riidxid: String(other_profile[2])
});
}
break;
default:
break;
}
}
if (_.isNil(mydata) && _.isNil(sdata)) return send.success();
if (version >= 27) {
let my_gauge_data = Buffer.alloc(0), other_gauge_data = Buffer.alloc(0);
if (!_.isNil(music_data[clid + 10])) my_gauge_data = Buffer.from(music_data[clid + 10], "base64");
if (!_.isNil(sdata)) {
if (_.isNil(other_musicdata.optArray)) { // migration //
other_musicdata.optArray = Array<number>(10).fill(0);
other_musicdata.opt2Array = Array<number>(10).fill(0);
}
let other_data = K.ITEM("bin", Buffer.from(other_musicdata[clid], "base64"), {
score: String(other_musicdata.esArray[clid]),
achieve: String(other_pcdata[ClidToPlaySide(clid) + 2]),
pid: String(other_profile[1]),
name: String(other_profile[0]),
riidxid: String(other_profile[2]),
option: String(other_musicdata.optArray[clid]), // CastHour //
option2: String(other_musicdata.opt2Array[clid]),
});
if (!_.isNil(other_musicdata[clid + 10])) other_gauge_data = Buffer.from(other_musicdata[clid + 10], "base64");
sdata = {
...other_data,
gauge_data: K.ITEM("bin", other_gauge_data)
};
}
if (_.isNil(sdata) && !_.isNil(mydata)) {
result = {
"@attr": { my_option: option, my_option2: option2 },
mydata,
my_gauge_data: K.ITEM("bin", my_gauge_data),
};
}
if (_.isNil(mydata) && !_.isNil(sdata)) result = { sdata };
if (!_.isNil(mydata) && !_.isNil(sdata)) {
result = {
"@attr": { my_option: option, my_option2: option2 }, // CastHour //
mydata,
my_gauge_data: K.ITEM("bin", my_gauge_data),
sdata,
};
}
}
else {
if (_.isNil(sdata) && !_.isNil(mydata)) result = { mydata };
if (_.isNil(mydata) && !_.isNil(sdata)) result = { sdata };
if (!_.isNil(mydata) && !_.isNil(sdata)) result = { mydata, sdata };
}
return send.object(result);
}
export const musicreg: EPR = async (info, data, send) => {
const version = GetVersion(info);
const refid = await IDtoRef(Number($(data).attr().iidxid));
const shop_data = await DB.FindOne<shop_data>({
collection: "shop_data",
});
const profile = await DB.FindOne<profile>(refid, {
collection: "profile",
});
// wid, oppid, opname, opt, opt2, pside, nocnt, anum //
const pgnum = Number($(data).attr().pgnum);
const gnum = Number($(data).attr().gnum);
const mnum = Number($(data).attr().mnum);
const cflg = Number($(data).attr().cflg);
let mid = Number($(data).attr().mid);
let clid = Number($(data).attr().clid);
let exscore = (pgnum * 2 + gnum);
let ghost = null, ghost_gauge = null; // Heroic Verse //
let style = 0, option = 0, option_2 = 0, rid = -1;
// TODO:: Leggendaria until HEROIC VERSE has seperate music_id //
// TODO:: SUPER FUTURE 2323 has seperate music_id //
const mapping = [1, 2, 3, 6, 7, 8];
if (version == -1) return send.deny();
else if (version < 20) {
mid = OldMidToNewMid(mid);
if (mid == -1) return send.deny();
clid = mapping[clid];
}
else if (version < 27) {
clid = mapping[clid];
}
const music_data: score | null = await DB.FindOne<score>(refid, {
collection: "score",
mid: mid,
});
// SPN -> DPA [0~5] -> LINCLE //
// SPB -> DPL [0~9] -> Heroic Verse //
let pgArray = Array<number>(10).fill(0); // PGREAT //
let gArray = Array<number>(10).fill(0); // GREAT //
let mArray = Array<number>(10).fill(-1); // MISS //
let cArray = Array<number>(10).fill(0); // CLEAR FLAGS //
let rArray = Array<number>(10).fill(-1); // RANK ID //
let esArray = Array<number>(10).fill(0); // EXSCORE //
let optArray = Array<number>(10).fill(0); // USED OPTION (CastHour) //
let opt2Array = Array<number>(10).fill(0); // USED OPTION (CastHour) //
let update = 0;
if (!_.isNil($(data).attr().rid)) rid = Number($(data).attr().rid);
else if (!_.isNil($(data).attr().dj_level)) rid = Number($(data).attr().dj_level);
if (rid > -1) console.log(`[music.reg] rank_id : ${rid}`);
if (version < 16) ghost = Buffer.from($(data).str("ghost"), "hex").toString("base64");
else ghost = $(data).buffer("ghost").toString("base64");
if (version >= 27) {
ghost_gauge = $(data).buffer("ghost_gauge").toString("base64");
style = Number($(data).element("music_play_log").attr().play_style);
if (version >= 29) {
option = Number($(data).element("music_play_log").attr().option1);
option_2 = Number($(data).element("music_play_log").attr().option2);
}
}
if (_.isNil(music_data)) {
pgArray[clid] = pgnum;
gArray[clid] = gnum;
mArray[clid] = mnum;
cArray[clid] = cflg;
rArray[clid] = rid;
esArray[clid] = exscore;
optArray[clid] = option;
opt2Array[clid] = option_2;
} else {
pgArray = music_data.pgArray;
gArray = music_data.gArray;
mArray = music_data.mArray;
cArray = music_data.cArray;
esArray = music_data.esArray;
if (!_.isNil(music_data.optArray)) { // migration //
optArray = music_data.optArray;
opt2Array = music_data.opt2Array;
}
if (!_.isNil(music_data.rArray)) {
rArray = music_data.rArray;
}
// migration for invalid miss count //
// if EXSCORE/MISS COUNT is 0 and CLEAR FLAG is NO PLAY then set MISS COUNT to -1 //
for (let a = 0; a < mArray.length; a++) {
if (esArray[a] == 0 && cArray[a] == 0 && mArray[a] == 0) mArray[a] = -1;
}
const pExscore = esArray[clid];
if (exscore > pExscore) {
pgArray[clid] = pgnum;
gArray[clid] = gnum;
rArray[clid] = rid;
esArray[clid] = exscore;
optArray[clid] = option;
opt2Array[clid] = option_2;
update = 1;
} else {
ghost = music_data[clid];
if (version >= 27) ghost_gauge = music_data[clid + 10];
}
if (mnum == -1) mArray[clid] = Math.max(mArray[clid], mnum); // this seems asking for not updating miss count //
else mArray[clid] = mArray[clid] == -1 ? mnum : Math.min(mArray[clid], mnum);
cArray[clid] = Math.max(cArray[clid], cflg);
}
if (version >= 27) { // TODO:: support old version //
const score_top: score_top | null = await DB.FindOne<score_top>(null, {
collection: "score_top",
play_style: style,
mid: mid,
});
let names = Array<string>(5).fill("");
let scores = Array<number>(5).fill(-1);
let clflgs = Array<number>(5).fill(-1);
let tmp_clid = clid;
if (style == 1) tmp_clid -= 5;
if (_.isNil(score_top)) {
if (esArray[clid] > exscore) {
names[tmp_clid] = profile.name;
scores[tmp_clid] = esArray[clid];
clflgs[tmp_clid] = cArray[clid];
} else {
names[tmp_clid] = profile.name;
scores[tmp_clid] = exscore;
clflgs[tmp_clid] = cflg;
}
}
else {
names = score_top.names;
scores = score_top.scores;
clflgs = score_top.clflgs;
if (exscore > scores[tmp_clid]) {
names[tmp_clid] = profile.name;
scores[tmp_clid] = exscore;
clflgs[tmp_clid] = cflg;
}
}
await DB.Upsert<score_top>(
{
collection: "score_top",
play_style: style,
mid: mid,
},
{
$set: {
names,
scores,
clflgs,
}
}
);
}
await DB.Upsert<score>(
refid,
{
collection: "score",
mid: mid,
},
{
$set: {
pgArray,
gArray,
mArray,
cArray,
rArray,
esArray,
optArray,
opt2Array,
[clid]: ghost,
[clid + 10]: ghost_gauge,
}
}
);
if (!_.isNil($(data).element("badge"))) {
if (!_.isNil($(data).attr("badge").djLevel_badge_flg_id)) {
await DB.Upsert<badge>(
refid,
{
collection: "badge",
version: version,
category_name: "djLevel",
flg_id: Number($(data).attr("badge").djLevel_badge_flg_id),
},
{
$set: {
flg: Number($(data).attr("badge").djLevel_badge_flg),
}
}
);
}
if (!_.isNil($(data).attr("badge").clear_badge_flg_id)) {
await DB.Upsert<badge>(
refid,
{
collection: "badge",
version: version,
category_name: "clear",
flg_id: Number($(data).attr("badge").clear_badge_flg_id),
},
{
$set: {
flg: Number($(data).attr("badge").clear_badge_flg),
}
}
);
}
if (!_.isNil($(data).attr("badge").rivalChallenge_badge_flg)) {
await DB.Upsert<badge>(
refid,
{
collection: "badge",
version: version,
category_name: "rivalChallenge",
flg_id: 0,
},
{
$set: {
flg: Number($(data).attr("badge").rivalChallenge_badge_flg),
}
}
);
}
}
let date = new Date();
if (!_.isNil($(data).element("best_result"))) {
await DB.Upsert<activity_mybest>(
refid,
{
collection: "activity_mybest",
version: version,
play_style: Number($(data).attr("best_result").play_style),
play_side: Number($(data).attr("best_result").play_side),
music_id: Number($(data).attr("best_result").music_id),
note_id: Number($(data).attr("best_result").note_id),
},
{
$set: {
target_graph: Number($(data).attr("best_result").target_graph),
target_score: Number($(data).attr("best_result").target_score),
pacemaker: Number($(data).attr("best_result").pacemaker),
best_clear: Number($(data).attr("best_result").best_clear),
best_score: Number($(data).attr("best_result").best_score),
best_misscount: Number($(data).attr("best_result").best_misscount),
now_clear: Number($(data).attr("best_result").now_clear),
now_score: Number($(data).attr("best_result").now_score),
now_misscount: Number($(data).attr("best_result").now_misscount),
now_pgreat: Number($(data).attr("best_result").now_pgreat),
now_great: Number($(data).attr("best_result").now_great),
now_good: Number($(data).attr("best_result").now_good),
now_bad: Number($(data).attr("best_result").now_bad),
now_poor: Number($(data).attr("best_result").now_poor),
now_combo: Number($(data).attr("best_result").now_combo),
now_fast: Number($(data).attr("best_result").now_fast),
now_slow: Number($(data).attr("best_result").now_slow),
option: Number($(data).attr("best_result").option),
option_2: Number($(data).attr("best_result").option2),
ghost_gauge_data: $(data).element("best_result").buffer("ghost_gauge_data").toString("base64"),
gauge_type: Number($(data).attr("best_result").gauge_type),
result_type: Number($(data).attr("best_result").result_type),
is_special_result: Number($(data).element("best_result").bool("is_special_result")),
update_date: Math.floor(date.valueOf() / 1000),
}
}
);
}
let shop_rank = -1, shop_rank_data = [];
let scores: any[][];
scores = (
await DB.Find(null, {
collection: "score",
mid: mid,
cArray: { $exists: true },
esArray: { $exists: true },
})
).map((r) => [r.esArray[clid], r.cArray[clid], r.__refid]);
scores.sort((a, b) => b[0] - a[0]);
shop_rank = scores.findIndex((a) => a[2] == refid);
scores = await Promise.all(
scores.map(async (r) => [
r[0],
r[1],
await ReftoProfile(r[2]),
await ReftoQPRO(r[2], version),
await ReftoPcdata(r[2], version),
])
);
let crate = 0, frate = 0, cflgs = 0, fcflgs = 0;
scores.forEach((rankscore, index) => {
if (rankscore[1] != 1) cflgs += 1;
if (rankscore[1] == 7) fcflgs += 1;
if (index == shop_rank) {
shop_rank_data.push(
K.ATTR({
iidx_id: String(rankscore[2][2]),
name: String(rankscore[2][0]),
opname: shop_data.opname,
rnum: String(index + 1),
score: String(rankscore[0]),
clflg: String(rankscore[1]),
pid: String(rankscore[2][1]),
sgrade: String(rankscore[4][0]),
dgrade: String(rankscore[4][1]),
head: String(rankscore[3][1]),
hair: String(rankscore[3][0]),
face: String(rankscore[3][2]),
body: String(rankscore[3][3]),
hand: String(rankscore[3][4]),
myFlg: String(1),
s_baron: String(0),
p_baron: String(0),
achieve: String(0),
update: String(update),
})
);
}
else if (rankscore[0] != 0 || rankscore[1] != 0) {
shop_rank_data.push(
K.ATTR({
iidx_id: String(rankscore[2][2]),
name: String(rankscore[2][0]),
opname: shop_data.opname,
rnum: String(index + 1),
score: String(rankscore[0]),
clflg: String(rankscore[1]),
pid: String(rankscore[2][1]),
sgrade: String(rankscore[4][0]),
dgrade: String(rankscore[4][1]),
head: String(rankscore[3][1]),
hair: String(rankscore[3][0]),
face: String(rankscore[3][2]),
body: String(rankscore[3][3]),
hand: String(rankscore[3][4]),
myFlg: String(0),
s_baron: String(0),
p_baron: String(0),
achieve: String(0),
update: String(0),
})
);
}
});
let rate = version > 23 ? 1000 : 100;
crate = Math.round((cflgs / shop_rank_data.length) * rate);
frate = Math.round((fcflgs / shop_rank_data.length) * rate);
let result: any = {
"@attr": {
mid: String(mid),
clid: String(clid),
crate: String(crate),
frate: String(frate),
rankside: String(style),
},
ranklist: {
"@attr": { total_user_num: String(shop_rank_data.length) },
data: shop_rank_data,
},
shopdata: K.ATTR({ rank: String(shop_rank) }),
}
return send.object(result);
}
export const musicbreg: EPR = async (info, data, send) => {
const version = GetVersion(info);
// mid pgnum gnum cflg //
const refid = await IDtoRef(Number($(data).attr().iidxid));
const pgnum = Number($(data).attr().pgnum);
const gnum = Number($(data).attr().gnum);
const cflg = Number($(data).attr().cflg);
let mid = Number($(data).attr().mid);
let clid = 0; // SP BEGINNER //
let exscore = (pgnum * 2 + gnum);
if (version < 20) mid = OldMidToNewMid(mid);
const music_data: score | null = await DB.FindOne<score>(refid, {
collection: "score",
mid: mid,
});
let pgArray = Array<number>(10).fill(0); // PGREAT //
let gArray = Array<number>(10).fill(0); // GREAT //
let mArray = Array<number>(10).fill(-1); // MISS //
let cArray = Array<number>(10).fill(0); // CLEAR FLAGS //
let rArray = Array<number>(10).fill(-1); // RANK ID //
let esArray = Array<number>(10).fill(0); // EXSCORE //
let optArray = Array<number>(10).fill(0); // USED OPTION (CastHour) //
let opt2Array = Array<number>(10).fill(0); // USED OPTION (CastHour) //
if (_.isNil(music_data)) {
pgArray[clid] = pgnum;
gArray[clid] = gnum;
mArray[clid] = -1; // this is not being sent //
cArray[clid] = cflg;
rArray[clid] = -1; // this is not being sent //
esArray[clid] = exscore;
optArray[clid] = 0; // this is not being sent //
opt2Array[clid] = 0; // this is not being sent //
} else {
pgArray = music_data.pgArray;
gArray = music_data.gArray;
mArray = music_data.mArray;
cArray = music_data.cArray;
esArray = music_data.esArray;
if (!_.isNil(music_data.optArray)) { // migration //
optArray = music_data.optArray;
opt2Array = music_data.opt2Array;
}
if (!_.isNil(music_data.rArray)) {
rArray = music_data.rArray;
}
const pExscore = esArray[clid];
if (exscore > pExscore) {
pgArray[clid] = Math.max(pgArray[clid], pgnum);
gArray[clid] = Math.max(gArray[clid], gnum);
esArray[clid] = Math.max(esArray[clid], exscore);
}
cArray[clid] = Math.max(cArray[clid], cflg);
}
await DB.Upsert<score>(
refid,
{
collection: "score",
mid: mid,
},
{
$set: {
pgArray,
gArray,
mArray,
cArray,
rArray,
esArray,
optArray,
opt2Array,
[clid]: null,
[clid + 10]: null,
}
}
);
return send.success();
};
export const musiccrate: EPR = async (info, data, send) => {
const version = GetVersion(info);
const scores = await DB.Find<score>(null, {
collection: "score",
});
const cltype = Number($(data).attr().cltype);
let cFlgs: Record<number, number[]> = {},
fcFlgs: Record<number, number[]> = {},
totalFlgs: Record<number, number[]> = {};
scores.forEach((res) => {
let mVersion = Math.floor(res.mid / 1000);
if (mVersion > version) return;
let totalArray = Array<number>(10).fill(0);
let cFlgArray = Array<number>(10).fill(0);
let fcFlgArray = Array<number>(10).fill(0);
if (_.isNil(res.cArray)) throw new Error("[music.crate] There is unsupported entry in Database");
for (let a = 0; a < 10; a++) {
if (res.cArray[a] != 0) totalArray[a] += 1;
if (res.cArray[a] != 1) cFlgArray[a] += 1;
if (res.cArray[a] == 7) fcFlgArray[a] += 1;
}
let temp_mid = version < 20 ? NewMidToOldMid(res.mid) : res.mid;
totalFlgs[temp_mid] = totalArray;
cFlgs[temp_mid] = cFlgArray;
fcFlgs[temp_mid] = fcFlgArray;
});
let result = {}, c = [], cdata = [];
for (const key in totalFlgs) {
let cRate = Array<number>(10).fill(0);
let fcRate = Array<number>(10).fill(0);
for (let a = 0; a < 10; a++) {
if (totalFlgs[key][a] == 0) continue;
if (version > 23) {
cRate[a] = Math.round((cFlgs[key][a] / totalFlgs[key][a]) * 1000);
fcRate[a] = Math.round((fcFlgs[key][a] / totalFlgs[key][a]) * 1000);
} else {
cRate[a] = Math.round((cFlgs[key][a] / totalFlgs[key][a]) * 100);
fcRate[a] = Math.round((fcFlgs[key][a] / totalFlgs[key][a]) * 100);
}
}
let indices = [1, 2, 3, 6, 7, 8];
if (version == 14 || version == 15) {
let verMid = OldMidToVerMid(Number(key));
let str = cltype == 0 ?
`${NumArrayToString([7, 7, 7, 7], [verMid[1], cRate[1], cRate[2], cRate[3]])}ZZZZ` :
`${NumArrayToString([7, 7, 7, 7], [verMid[1], cRate[6], cRate[7], cRate[8]])}ZZZZ`;
cdata.push(
K.ITEM("str", str, { ver: String(verMid[0]) })
);
}
else {
let rateArray = version < 27 ? [...indices.map(i => cRate[i]), ...indices.map(i => fcRate[i])] : [...cRate, ...fcRate];
let rateResult = version < 24 ? K.ARRAY("u8", rateArray, { mid: key }) : K.ARRAY("s32", rateArray, { mid: key });
c.push(rateResult);
}
}
result = (version == 14 || version == 15) ? { cdata } : { c };
return send.object(result);
}
// this is not valid response //
export const musicarenacpu: EPR = async (info, data, send) => {
const version = GetVersion(info);
if (version == -1) return send.deny();
let cpu_score_list = [], total_notes = [];
$(data).elements("music_list").forEach((res) => {
total_notes.push(res.number("total_notes"));
});
for (let a = 0; a < $(data).elements("cpu_list").length; a++) {
let score_list = [];
total_notes.forEach((res) => {
score_list.push({
score: K.ITEM("s32", _.random(res, res * 2)),
ghost: version > 30 ? K.ITEM("s8", 0) : K.ITEM("u8", 0),
enable_score: K.ITEM("bool", 1),
enable_ghost: K.ITEM("bool", 0),
});
})
cpu_score_list.push({
index: K.ITEM("s32", a),
score_list,
});
}
return send.object({
cpu_score_list,
})
}