import { Skill } from '../models/skill'; import { Item } from '../models/item'; import { Param } from '../models/param'; import { MusicRecord } from '../models/music_record'; import { CourseRecord } from '../models/course_record'; import { Profile } from '../models/profile'; import { getVersion, IDToCode } from '../utils'; import { Mix } from '../models/mix'; async function getAutomationMixes(params: Param[]) { const mixids = params .filter(p => p.id == 3) .reduce((res, p) => _.union(res, p.param), []); return DB.Find({ collection: 'mix', id: { $in: mixids } }); } function unlockNavigators(items: Partial[]) { for (let i = 0; i < 300; ++i) items.push({ type: 11, id: i, param: 15 }); console.log("Unlocking Navigators"); // 10 genesis card for MITSURU's voice items.push({ type: 4, id: 599, param: 10 }); // items.push({ type: 21, id: 1, param: 1 }); return items; } function unlockAppealCards(items: Partial[]) { for (let i = 0; i < 6000; ++i) items.push({ type: 1, id: i, param: 1 }); console.log("Unlocking Appeal Cards"); return items; } function unlock_all_valkgen(items: Partial[]) { // for (let i = 0; i < 500; ++i) items.push({ type: 17, id: i, param: 1 }); // stamp // for (let i = 0; i < 600; ++i) items.push({ type: 18, id: i, param: 1 }); // subbg // for (let i = 0; i < 100; ++i) items.push({ type: 19, id: i, param: 1 }); // bgm // for (let i = 0; i < 100; ++i) items.push({ type: 20, id: i, param: 1 }); // nemsys // for (let i = 0; i < 30; ++i) items.push({ type: 21, id: i, param: 1 }); // mainbg return items; } export const loadScore: EPR = async (info, data, send) => { console.log("Now loading score"); const version = getVersion(info); console.log("Got version:" + version); let refid = $(data).str('refid', $(data).attr().dataid); if (version === 2) refid = $(data).str('dataid', '0'); //console.log('loading score'); console.log("DataID:" + refid); if (!refid) return send.deny(); console.log('Finding record'); const records = await DB.Find(refid, { collection: 'music' }); return send.object({ music: { info: records.map(r => { let tempArr = [ r.mid, r.type, r.score, r.exscore, r.clear, r.grade, 0, // max chain 0, r.buttonRate, r.longRate, r.volRate, r.volforce ? r.volforce : 0, 0, 0, 0, 0, 0, // last update time 0, 0, 0, 0, 0, ]; return { param: K.ARRAY('u32', tempArr), } }), }, }); }; export const saveScore: EPR = async (info, data, send) => { const refid = $(data).str('refid', $(data).attr().dataid); if (!refid) return send.deny(); const tracks = $(data).elements('track'); for (const i of tracks) { const mid = i.number('music_id'); const type = i.number('music_type'); if (_.isNil(mid) || _.isNil(type)) return send.deny(); const record = (await DB.FindOne(refid, { collection: 'music', mid, type, })) || { collection: 'music', mid, type, score: 0, exscore: 0, clear: 0, grade: 0, buttonRate: 0, longRate: 0, volRate: 0, volforce: 0, judge: [], }; if (record.judge.length == 0) { console.log("No judge data found, save them for the first time so use current data as baseline."); record.judge = i.numbers('judge', []); if (record.judge.length == 7) { console.log("Judge data length is valid with s-crit."); } } const score = i.number('score', 0); const exscore = i.number('exscore', 0); if (score > record.score) { record.score = score; record.buttonRate = i.number('btn_rate', 0); record.longRate = i.number('long_rate', 0); record.volRate = i.number('vol_rate', 0); } if (exscore > record.exscore) { record.exscore = exscore; } if (score >= record.score || exscore >= record.exscore) { const newJudge = i.numbers('judge', []); if (newJudge.length == record.judge.length) { for (let j = 0; j < record.judge.length; j++) { if (newJudge[j] > record.judge[j]) { record.judge[j] = newJudge[j]; } } } } const volforce = i.number('volforce', 0); if (isNaN(record.volforce) || record.volforce === null) { console.log("Old Volforce is NaN or null, setting to 0"); record.volforce = 0; } if (volforce > record.volforce) { record.volforce = volforce; } if(i.number('clear_type', 0) == 6 && record.clear >= 4){ console.log("Detected Maxxive Clear, but originally UC or PUC, no override.") }else{ record.clear = Math.max(i.number('clear_type', 0), record.clear); } record.grade = Math.max(i.number('score_grade', 0), record.grade); await DB.Upsert( refid, { collection: 'music', mid, type }, record ); } return send.success(); }; export const saveCourse: EPR = async (info, data, send) => { const refid = $(data).str('refid'); if (!refid) return send.deny(); const version = Math.abs(getVersion(info)); if (version == 0) return send.deny(); const sid = $(data).number('ssnid'); const cid = $(data).number('crsid'); if (_.isNil(sid) || _.isNil(cid)) return send.deny(); await DB.Upsert( refid, { collection: 'course', sid, cid, version }, { $max: { score: $(data).number('sc', 0), clear: $(data).number('ct', 0), grade: $(data).number('gr', 0), rate: $(data).number('ar', 0), }, $inc: { count: 1, }, } ); return send.success(); }; export const save: EPR = async (info, data, send) => { const refid = $(data).str('refid', $(data).attr().refid); if (!refid) return send.deny(); const version = Math.abs(getVersion(info)); if (version == 0) return send.deny(); await DB.Update( refid, { collection: 'profile' }, { $set: { appeal: $(data).number('appeal_id'), musicID: $(data).number('music_id'), musicType: $(data).number('music_type'), sortType: $(data).number('sort_type'), headphone: $(data).number('headphone'), blasterCount: $(data).number('blaster_count'), hiSpeed: $(data).number('hispeed'), laneSpeed: $(data).number('lanespeed'), gaugeOption: $(data).number('gauge_option'), arsOption: $(data).number('ars_option'), notesOption: $(data).number('notes_option'), earlyLateDisp: $(data).number('early_late_disp'), drawAdjust: $(data).number('draw_adjust'), effCLeft: $(data).number('eff_c_left'), effCRight: $(data).number('eff_c_right'), narrowDown: $(data).number('narrow_down'), vGateOverRadar: $(data).element('variant_gate').numbers("over_radar") }, $inc: { packets: $(data).number('earned_gamecoin_packet'), blocks: $(data).number('earned_gamecoin_block'), blasterEnergy: $(data).number('earned_blaster_energy'), vGatePower: $(data).element('variant_gate').number('earned_power'), vGateNotes: $(data).element('variant_gate').element('earned_element').number('notes'), vGatePeak: $(data).element('variant_gate').element('earned_element').number('peak'), vGateTsumami: $(data).element('variant_gate').element('earned_element').number('tsumami'), vGateTricky: $(data).element('variant_gate').element('earned_element').number('tricky'), vGateOnehand: $(data).element('variant_gate').element('earned_element').number('onehand'), vGateHandtrip: $(data).element('variant_gate').element('earned_element').number('handtrip'), }, } ); // New course saving function found in sdvx 20220214 const course = $(data).element('course'); if(!_.isNil(course)){ const sid = course.number('ssnid'); const cid = course.number('crsid'); const skill_type = course.number('st'); if (!(_.isNil(sid) || _.isNil(cid))){ await DB.Upsert( refid, { collection: 'course', sid, cid, version, skill_type }, { $max: { score: course.number('sc', 0), exscore: course.number('ex', 0), clear: course.number('ct', 0), grade: course.number('gr', 0), rate: course.number('ar', 0), }, $inc: { count: 1, }, } ); } } // Save Items const items = $(data).elements('item.info'); for (const i of items) { const type = i.number('type'); const id = i.number('id'); const param = i.number('param'); if (_.isNil(type) || _.isNil(id) || _.isNil(param)) continue; await DB.Upsert( refid, { collection: 'item', type, id }, { $set: { param } } ); } // Save Param const params = $(data).elements('param.info'); for (const p of params) { const type = p.number('type'); const id = p.number('id'); const param = p.numbers('param'); if (_.isNil(type) || _.isNil(id) || _.isNil(param)) continue; await DB.Upsert( refid, { collection: 'param', type, id }, { $set: { param } } ); } // Save version specific data await DB.Upsert( refid, { collection: 'skill', version, }, { $set: { base: $(data).number('skill_base_id'), level: $(data).number('skill_level'), name: $(data).number('skill_name_id'), stype: $(data).number('skill_type') }, } ); return send.success(); }; export const load: EPR = async (info, data, send) => { console.log("Loading savedata"); const refid = $(data).str('refid', $(data).attr().dataid); if (!refid) return send.deny(); const version = Math.abs(getVersion(info)); console.log("Got version" + version); console.log("DataID" + refid); if (version == 0) return send.deny(); const profile = await DB.FindOne(refid, { collection: 'profile', }); if (!profile) { if (version === 1) return send.object(K.ATTR({ none: "1" })); return send.object({ result: K.ITEM('u8', 1) }); } let skill = (await DB.FindOne(refid, { collection: 'skill', version, })) || { base: 0, name: 0, level: 0, stype:0 }; skill.stype = skill.stype ?? 0; const courses = await DB.Find(refid, { collection: 'course', version }); for(const c of courses){ c.skill_type = c.skill_type ?? 0; c.exscore = c.exscore ?? 0; } const items = await DB.Find(refid, { collection: 'item' }); const params = await DB.Find(refid, { collection: 'param' }); let time = new Date(); let tempHour = time.getHours(); let tempDate = time.getDate(); tempHour += 12; tempDate += 1; time.setDate(tempDate); time.setHours(tempHour); const currentTime = time.getTime(); const mixes = version == 5 ? await getAutomationMixes(params) : []; if (!profile.extrackEnergy) { profile.extrackEnergy = 0; } const bgm = profile.bgm ? profile.bgm : 0; const subbg = profile.subbg ? profile.subbg : 0; const nemsys = profile.nemsys ? profile.nemsys : 0; const stampA = profile.stampA ? profile.stampA : 0; const stampB = profile.stampB ? profile.stampB : 0; const stampC = profile.stampC ? profile.stampC : 0; const stampD = profile.stampD ? profile.stampD : 0; const stampA_R = profile.stampA_R ? profile.stampA_R : 0; const stampB_R = profile.stampB_R ? profile.stampB_R : 0; const stampC_R = profile.stampC_R ? profile.stampC_R : 0; const stampD_R = profile.stampD_R ? profile.stampD_R : 0; const mainbg = profile.mainbg ? profile.mainbg : 0; const customize = []; customize.push(bgm, subbg, nemsys, stampA, stampB, stampC, stampD, stampA_R, stampB_R, stampC_R, stampD_R, mainbg); let tempCustom = params.findIndex((e) => (e.type == 2 && e.id == 2)) if (tempCustom == -1) { const tempParam: Param = { collection: 'param', type: 2, id: 2, param: [] }; params.push(tempParam); tempCustom = params.findIndex((e) => (e.type == 2 && e.id == 2)) } if (params[tempCustom]) { params[tempCustom].param = customize; } let blasterpass = U.GetConfig('use_blasterpass') ? 1 : 0; let tempItem = U.GetConfig('unlock_all_navigators') ? unlockNavigators(items) : items; tempItem = U.GetConfig('unlock_all_appeal_cards') ? unlockAppealCards(tempItem) : tempItem; tempItem = unlock_all_valkgen(tempItem); // Make generator power always 100%, for (let i = 0; i < 50; i++) { const tempGene: Item = { collection: 'item', type: 7, id: i, param: 10 }; tempItem.push(tempGene); } profile.appeal_frame = profile.appeal_frame ? profile.appeal_frame : 0; profile.support_team = profile.support_team ? profile.support_team : 0; profile.use_pro_team = profile.use_pro_team ? profile.use_pro_team : false; profile.vGatePower = profile.vGatePower ? profile.vGatePower : 0; profile.vGateNotes = profile.vGateNotes ? profile.vGateNotes : 0; profile.vGatePeak = profile.vGatePeak ? profile.vGatePeak : 0; profile.vGateTsumami = profile.vGateTsumami ? profile.vGateTsumami : 0; profile.vGateTricky = profile.vGateTricky ? profile.vGateTricky : 0; profile.vGateOnehand = profile.vGateOnehand ? profile.vGateOnehand : 0; profile.vGateHandtrip = profile.vGateHandtrip ? profile.vGateHandtrip : 0; profile.vGateOverRadar = profile.vGateOverRadar ? profile.vGateOverRadar : []; if(!profile.vGatePower){ // Data migration await DB.Update( refid, { collection: 'profile' }, { $set: { vGatePower: 0, vGateNotes: 0, vGatePeak: 0, vGateTsumami: 0, vGateTricky: 0, vGateOnehand: 0, vGateHandtrip: 0, vGateOverRadar: [], } } ) } return send.pugFile('templates/load.pug', { courses, items: tempItem, params, skill, currentTime, mixes, version, blasterpass, automation: [], code: IDToCode(profile.id), ...profile, }); }; export const create: EPR = async (info, data, send) => { console.log("Creating profile"); const refid = $(data).str('refid', $(data).attr().refid); if (!refid) return send.deny(); console.log("DataID" + refid); const name = $(data).str('name', $(data).attr().name ? $(data).attr().name : 'GUEST'); let id = _.random(0, 99999999); while (await DB.FindOne(null, { collecttion: 'profile', id })) { id = _.random(0, 99999999); } const profile: Profile = { pluginVer: 1, collection: 'profile', id, name, appeal: 0, akaname: 0, blocks: 0, packets: 0, arsOption: 0, drawAdjust: 0, earlyLateDisp: 0, effCLeft: 0, effCRight: 1, gaugeOption: 0, hiSpeed: 0, laneSpeed: 0, narrowDown: 0, notesOption: 0, blasterCount: 0, blasterEnergy: 0, extrackEnergy: 0, vGatePower: 0, vGateNotes: 0, vGatePeak: 0, vGateTsumami: 0, vGateTricky: 0, vGateOnehand: 0, vGateHandtrip: 0, vGateOverRadar: [], bgm: 0, subbg: 0, nemsys: 0, stampA: 0, stampB: 0, stampC: 0, stampD: 0, stampA_R: 0, stampB_R: 0, stampC_R: 0, stampD_R: 0, mainbg: 0, appeal_frame: 0, support_team: 0, use_pro_team: false, headphone: 0, musicID: 0, musicType: 0, sortType: 0, expPoint: 0, mUserCnt: 0, boothFrame: [0, 0, 0, 0, 0] }; await DB.Upsert(refid, { collection: 'profile' }, profile); return send.object({ result: K.ITEM('u8', 0) }); }; export const buy: EPR = async (info, data, send) => { const refid = $(data).str('refid'); if (!refid) return send.deny(); const growth = { blocks: $(data).number('earned_gamecoin_block', 0), packets: $(data).number('earned_gamecoin_packet', 0), }; const currency = $(data).bool('currency_type') ? 'blocks' : 'packets'; const cost = _.sum($(data).numbers('item.price', [])); const balanceChange = growth[currency] - cost; const updated = await DB.Update( refid, { collection: 'profile', [currency]: { $gte: -balanceChange } }, { $inc: { [currency]: balanceChange } } ); if (updated.updated) { const items = _.zipWith( $(data).numbers('item.item_type', []), $(data).numbers('item.item_id', []), $(data).numbers('item.param', []), (type, id, param) => ({ type, id, param }) ); for (const item of items) { await DB.Upsert( refid, { collection: 'item', type: item.type, id: item.id }, { $set: { param: item.param } } ); } return send.object({ gamecoin_packet: K.ITEM('u32', updated.docs[0].packets), gamecoin_block: K.ITEM('u32', updated.docs[0].blocks), }); } else { return send.success(); } }; export const print: EPR = async (info, data, send) => { const genesisCards = $(data).elements('genesis_card'); let generatorArray = []; for (const g of genesisCards) { let tempGeneratorID = g.number('generator_id'); let exist = generatorArray.findIndex((e) => (e == tempGeneratorID)); if (exist == -1) { generatorArray.push(tempGeneratorID); } } send.object({ result: K.ITEM('s8', 0), genesis_cards: genesisCards.map(r => ({ index: K.ITEM('s32', r.number('index')), print_id: K.ITEM('s32', r.number('print_id')) })), after_power: generatorArray.map(r => ({ generator_id: K.ITEM('s32', r), param: K.ITEM('s32', 10), })) }); }