sdvx initial support

This commit is contained in:
Freddie 2020-05-28 01:35:57 +01:00
parent db46898cdb
commit ff16ea5f96
17 changed files with 7479 additions and 81 deletions

11
.prettierrc Normal file
View File

@ -0,0 +1,11 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"useTabs": false,
"endOfLine": "lf",
"semi": true,
"singleQuote": true,
"bracketSpacing": true,
"arrowParens": "avoid",
"quoteProps": "consistent"
}

View File

@ -1,5 +1,3 @@
# SOUND VOLTEX
Supported Versions:
* HEAVENLY HAVEN
* VIVID WAVE
(WORK IN PROGRESS)

4521
sdvx@asphyxia/data/hvn.ts Normal file

File diff suppressed because it is too large Load Diff

2594
sdvx@asphyxia/data/vvw.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
import { EVENT4, COURSES4, EXTENDS4 } from '../data/hvn';
import { EVENT5, COURSES5, EXTENDS5 } from '../data/vvw';
export const common: EPR = async (info, data, send) => {
let events = [];
let courses = [];
let extend = [];
switch (info.method) {
case 'sv4_common': {
events = EVENT4;
courses = COURSES4;
extend = EXTENDS4;
break;
}
case 'sv5_common': {
events = EVENT5;
courses = COURSES5;
extend = EXTENDS5;
break;
}
}
let songs = [];
if (U.GetConfig('unlock_all_songs')) {
for (let i = 1; i < 1600; ++i) {
for (let j = 0; j < 5; ++j) {
songs.push({
music_id: K.ITEM('s32', i),
music_type: K.ITEM('u8', j),
limited: K.ITEM('u8', 3),
});
}
}
}
send.object(
{
event: {
info: events.map(e => ({
event_id: K.ITEM('str', e),
})),
},
extend: {
info: extend.map(e => ({
extend_id: K.ITEM('u32', e.id),
extend_type: K.ITEM('u32', e.type),
param_num_1: K.ITEM('s32', e.params[0]),
param_num_2: K.ITEM('s32', e.params[1]),
param_num_3: K.ITEM('s32', e.params[2]),
param_num_4: K.ITEM('s32', e.params[3]),
param_num_5: K.ITEM('s32', e.params[4]),
param_str_1: K.ITEM('str', e.params[5]),
param_str_2: K.ITEM('str', e.params[6]),
param_str_3: K.ITEM('str', e.params[7]),
param_str_4: K.ITEM('str', e.params[8]),
param_str_5: K.ITEM('str', e.params[9]),
})),
},
music_limited: { info: songs },
skill_course: {
info: courses.reduce(
(acc, s) =>
acc.concat(
s.courses.map(c => ({
season_id: K.ITEM('s32', s.id),
season_name: K.ITEM('str', s.name),
season_new_flg: K.ITEM('bool', s.isNew),
course_type: K.ITEM('s16', 0),
course_id: K.ITEM('s16', c.id),
course_name: K.ITEM('str', c.name),
skill_level: K.ITEM('s16', c.level),
skill_name_id: K.ITEM('s16', c.nameID),
matching_assist: K.ITEM('bool', c.assist),
clear_rate: K.ITEM('s32', 5000),
avg_score: K.ITEM('u32', 15000000),
track: c.tracks.map(t => ({
track_no: K.ITEM('s16', t.no),
music_id: K.ITEM('s32', t.mid),
music_type: K.ITEM('s8', t.mty),
})),
}))
),
[]
),
},
},
{ encoding: 'utf8' }
);
};

View File

@ -1,53 +0,0 @@
/*
EamusePluginRoute. Handle your game message like this
You can send a plain XML request to test this route:
<call model="NULL:example">
<example method="method" card="E0040123456789AB"></example>
</call>
*/
export const example: EPR = async (info, data, send) => {
/* [Check documentation for the entire API] */
/*
Access data from request like this
NOTE: all card number will be automatically converted to refid.
This is to support older game that doesn't use cardmng,
yet still allow them to register with internal profile manager.
And they can show up in WebUI as a profile, along with card binding feature.
*/
const refid = $(data).attr().card;
/* Access config like this */
const event = U.GetConfig('event');
/*
Create user data in profile space if not exists
WebUI will try to find a "name" field in profile documents and display them.
If you are using a collection of data for each profile,
make sure to avoid using name field in supplementary documents.
If you have multiple documents per refid, it is recommended to provide a field to
simulate collections in NoSQL database (e.g. MongoDB)
*/
await DB.Upsert(
refid,
{
collection: 'profile',
name: '',
},
{ $inc: { login_count: 1 } }
);
/*
Send your response like this
There are more methods for sending request.
*/
send.pugFile('templates/example.pug', { refid, event });
/* Or you can send ejs template (plain xml works as well) */
// send.xmlFile('templates/example.xml', { refid, event });
};
export const changeName = async (data: any) => {
await DB.Update(data.refid, { collection: 'profile' }, { $set: { name: data.name } });
};

View File

@ -0,0 +1,97 @@
import { Profile } from '../models/profile';
import { VersionData } from '../models/version_data';
export const loadScores: EPR = async (info, data, send) => {
const refid = $(data).str('refid');
const records = await DB.Find(refid, { collection: 'music' });
send.pugFile('templates/load_m.pug', { records });
};
export const load: EPR = async (info, data, send) => {
const refid = $(data).str('refid');
let version = 0;
switch (info.method) {
case 'sv4_load':
version = 4;
break;
case 'sv5_load':
version = 5;
break;
}
const profile = await DB.FindOne(refid, { collection: 'profile' });
let versionData: VersionData = await DB.FindOne(refid, {
collection: 'version',
});
if (!versionData) {
versionData = {
collection: 'version',
version,
items: {},
params: {},
skill: {
base: 0,
level: 0,
name: 0,
},
};
await DB.Insert(refid, versionData);
}
if (!profile) {
send.object({ result: K.ITEM('u8', 1) });
return;
}
const courses = await DB.Find(refid, { collection: 'course' });
send.pugFile('templates/load.pug', {
courses,
...profile,
...versionData,
});
};
export const create: EPR = async (info, data, send) => {
const refid = $(data).str('refid');
const name = $(data).str('name', 'GUEST');
const profile: Profile = {
pluginVer: 1,
collection: 'profile',
name,
appeal: 0,
akaname: 0,
currency: {
blocks: 0,
packets: 0,
},
settings: {
arsOption: 0,
drawAdjust: 0,
earlyLateDisp: 0,
effCLeft: 0,
effCRight: 1,
gaugeOption: 0,
hiSpeed: 0,
laneSpeed: 0,
narrowDown: 0,
notesOption: 0,
},
state: {
blasterCount: 0,
blasterEnergy: 0,
headphone: 0,
lastMusicID: 0,
lastMusicType: 0,
sortType: 0,
},
};
await DB.Upsert(refid, { collection: 'profile' }, profile);
send.object({ result: K.ITEM('u8', 0) });
};

View File

@ -1,7 +1,17 @@
import { example, changeName } from './handlers/example';
import { common } from './handlers/common';
import { load, create, loadScores } from './handlers/profile';
export function register() {
R.GameCode('KFC');
R.Route('example.method', example);
R.Config('unlock_all_songs', { type: 'boolean', default: false });
R.Config('unlock_all_navigators', { type: 'boolean', default: false });
R.Route('game.sv4_common', common);
R.Route('game.sv4_load', load);
R.Route('game.sv4_load_m', loadScores);
R.Route('game.sv4_new', create);
R.Route('game.sv4_frozen', true);
R.Route('game.sv4_load_r', true);
R.Route('game.sv5_common', common);
}

View File

@ -0,0 +1,11 @@
export interface CourseRecord {
collection: 'course';
sid: number;
cid: number;
score: number;
clearType: number;
grade: number;
achieveRate: number;
playCount: number;
}

View File

@ -0,0 +1,12 @@
export interface MusicRecord {
collection: 'music';
mid: number;
type: number;
score: number;
clear: number;
grade: number;
buttonRate: number;
longRate: number;
volRate: number;
}

View File

@ -1,5 +1,7 @@
export interface Profile {
modelVer: string;
collection: 'profile';
pluginVer: number;
name: string;
appeal: number;
@ -9,7 +11,7 @@ export interface Profile {
packets: number;
blocks: number;
};
state: {
lastMusicID: number;
lastMusicType: number;
@ -17,7 +19,7 @@ export interface Profile {
headphone: number;
blasterEnergy: number;
blasterCount: number;
}
};
settings: {
hiSpeed: number;
@ -30,5 +32,5 @@ export interface Profile {
effCLeft: number;
effCRight: number;
narrowDown: number;
}
}
};
}

View File

@ -1,13 +1,17 @@
// Version specific data (e.g. skills level)
export interface VersionData {
collection: 'version';
version: number;
skillLevel: number;
skillBaseID: number;
skillNameID: number;
skill: {
level: number;
base: number;
name: number;
};
items: {
[key: string]: number;
};
params: {
[key: string]: number[];
};
}
}

View File

@ -1,5 +0,0 @@
//-
Learn pug here: https://pugjs.org/api/getting-started.html
example(status="0")
refid(__type="str") #{refid}
event(__type="str") #{event}

View File

@ -1,7 +0,0 @@
<!--
Learn ejs here: https://ejs.co/#docs
-->
<example status="0">
<refid __type="str"> <%= refid %> </refid>
<event __type="str"> <%= event %> </event>
</example>

View File

@ -0,0 +1,98 @@
game
result(__type="u8") 0
name(__type="str") #{name}
code(__type="str") 1337-6666
sdvx_id(__type="str") 1337-6666
gamecoin_packet(__type="u32") #{currency.packets}
gamecoin_block(__type="u32") #{currency.blocks}
appeal_id(__type="u16") #{appeal}
last_music_id(__type="s32") #{state.lastMusicID}
last_music_type(__type="u8") #{state.lastMusicType}
sort_type(__type="u8") #{state.sortType}
headphone(__type="u8") #{state.headphone}
blaster_energy(__type="u32") #{state.blasterEnergy}
blaster_count(__type="u32") #{state.blasterCount}
hispeed(__type="s32") #{settings.hiSpeed}
lanespeed(__type="u32") #{settings.laneSpeed}
gauge_option(__type="u8") #{settings.gaugeOption}
ars_option(__type="u8") #{settings.arsOption}
notes_option(__type="u8") #{settings.notesOption}
early_late_disp(__type="u8") #{settings.earlyLateDisp}
draw_adjust(__type="s32") #{settings.drawAdjust}
eff_c_left(__type="u8") #{settings.effCLeft}
eff_c_right(__type="u8") #{settings.effCRight}
narrow_down(__type="u8") #{settings.narrowDown}
kac_id(__type="str") #{name}
skill_level(__type="s16") #{skill.level}
skill_base_id(__type="s16") #{skill.base}
skill_name_id(__type="s16") #{skill.name}
ea_shop
packet_booster(__type="s32") 1
if version != 5
block_booster(__type="s32") 1
eaappli
relation(__type="s8") 1
cloud
relation(__type="s8") 1
block_no(__type="s32") 0
skill
each course in courses
course
ssnid(__type="s16") #{course.sid}
crsid(__type="s16") #{course.cid}
sc(__type="s32") #{course.score}
ct(__type="s16") #{course.clearType}
gr(__type="s16") #{course.grade}
ar(__type="s16") #{course.achieveRate}
cnt(__type="s16") #{course.playCount}
item
each item in items
info
type(__type="u8") #{item.type}
id(__type="u32") #{item.id}
param(__type="u32") #{item.param}
param
each param in params
info
type(__type="u8") #{param.type}
id(__type="u32") #{param.id}
param(__type="u32" __count=param.param.length) #{param.param.join(" ")}
//- Akaname
each id in [0, 1, 2]
info
type(__type="u8") 6
id(__type="u32") #{id}
param(__type="u32" __count="1") #{akaname}
play_count(__type="u32") 1001
day_count(__type="u32") 301
today_count(__type="u32") 21
play_chain(__type="u32") 31
max_play_chain(__type="u32") 31
week_count(__type="u32") 9
week_play_count(__type="u32") 101
week_chain(__type="u32") 31
max_week_chain(__type="u32") 31
if mixes
each mix in mixes
automation
mix_id(__type="s32") #{mix.id}
mix_code(__type="str") #{mix.code}
seq(__type="str")
mix_name(__type="str") #{mix.name}
player_name(__type="str") #{mix.creator}
generate_param(__type="str") #{mix.param}
distribution_date(__type="u32") 19990101
jacket_id(__type="s32") #{mix.jacketID}
tag_bit(__type="s32") #{mix.tag}
like_flg(__type="u8") 0

View File

@ -0,0 +1,15 @@
game
music
for record in records
info
param(__type="u32" __count="16")
| #{record.mid}
| #{record.type}
| #{record.score}
| #{record.type}
| #{record.grade}
| 0 0
| #{record.buttonRate}
| #{record.longRate}
| #{record.volRate}
| 0 0 0 0 0 0