Merge pull request #73 from cracrayol/stable

[PnM] Add Unilab support
This commit is contained in:
Freddie W
2025-09-25 00:13:01 +08:00
committed by GitHub
9 changed files with 268 additions and 147 deletions

View File

@@ -11,11 +11,18 @@ Plugin Version: **v3.0.0**
- pop'n music Usagi to Neko to Shōnen no Yume - pop'n music Usagi to Neko to Shōnen no Yume
- pop'n music peace - pop'n music peace
- pop'n music Kaimei riddles - pop'n music Kaimei riddles
- pop'n music Unilab
Important : require minimum Asphyxia Core **v1.31** Important : require minimum Asphyxia Core **v1.31**
## Changelog ## Changelog
### 4.0.0
* Unilab: Support added
* Lapistoria+ : Add Force Unlock
* Remove non-core Asphyxia data import
* Bugfix: Send correct number of Goods
### 3.0.0 ### 3.0.0
* Kaimei riddles: Support added * Kaimei riddles: Support added
* Usaneko: Add Daily Missions support * Usaneko: Add Daily Missions support
@@ -57,14 +64,6 @@ Update phase data : All versions are on latest phase.
#### 1.0.0 #### 1.0.0
Initial Release. Initial Release.
## How to import data from non-core Asphyxia
To import data, you have to :
* Create your popn profile in Asphyxia-core. You just have to insert your card in the game and follow the process until coming to the select mode select screen. Once here, quit the game.
* Create a backup of your savedata.db file (in case something goes wrong).
* In the web UI of Asphyxia, go to POPN -> Profile and click detail on your profile
* Put the content of your non-core asphyxia popn music files in the text fields (pop.json and popn_scores.json) and click Import.
* Data is imported. Run the game, insert your card and your scores are available.
## Known limitations ## Known limitations
* No rival support for Tune Street * No rival support for Tune Street
* Some stats are not implemented * Some stats are not implemented

View File

@@ -369,6 +369,18 @@ const getProfile = async (refid: string, name?: string) => {
player.item.push(item); player.item.push(item);
} }
if(U.GetConfig("enable_force_unlock")) {
for(let i = 1; i <= GAME_MAX_MUSIC_ID; i++) {
player.item.push({
type: K.ITEM('u8', 0),
id: K.ITEM('u16', i),
param: K.ITEM('u16', 15),
is_new: K.ITEM('bool', 0),
get_time: K.ITEM('u64', BigInt(0)),
});
}
}
// Add version specific datas // Add version specific datas
const params = await utils.readParams(refid, version); const params = await utils.readParams(refid, version);
utils.addExtraData(player, params, extraData); utils.addExtraData(player, params, extraData);

View File

@@ -275,6 +275,18 @@ const getProfile = async (refid: string, name?: string) => {
player.item.push(item); player.item.push(item);
} }
if(U.GetConfig("enable_force_unlock")) {
for(let i = 1; i <= GAME_MAX_MUSIC_ID; i++) {
player.item.push({
type: K.ITEM('u8', 0),
id: K.ITEM('u16', i),
param: K.ITEM('u16', 15),
is_new: K.ITEM('bool', 0),
get_time: K.ITEM('u64', BigInt(0)),
});
}
}
// Add version specific datas // Add version specific datas
const params = await utils.readParams(refid, version); const params = await utils.readParams(refid, version);
utils.addExtraData(player, params, extraData); utils.addExtraData(player, params, extraData);

View File

@@ -1,5 +1,5 @@
import { AchievementsUsaneko } from "../models/achievements"; import { AchievementsUsaneko } from "../models/achievements";
import { ExtraData, Params, Phase } from "../models/common"; import { ExtraData, Phase } from "../models/common";
import * as utils from "./utils"; import * as utils from "./utils";
export const setRoutes = () => { export const setRoutes = () => {
@@ -37,7 +37,7 @@ const getInfoCommon = (req: EamuseInfo) => {
} }
// Choco // Choco
for (let i = 1; i <= 5; ++i) { for (let i = 0; i < 5; ++i) {
result.choco.push({ result.choco.push({
choco_id: K.ITEM('s16', i), choco_id: K.ITEM('s16', i),
param: K.ITEM('s32', -1), param: K.ITEM('s32', -1),
@@ -45,8 +45,8 @@ const getInfoCommon = (req: EamuseInfo) => {
} }
// Goods // Goods
for (let i = 1; i <= 98; ++i) { for (let i = 0; i < GAME_MAX_DECO_ID[version]; ++i) {
let price = 200; let price = 250;
if (i < 15) { if (i < 15) {
price = 30; price = 30;
} else if (i < 30) { } else if (i < 30) {
@@ -55,10 +55,12 @@ const getInfoCommon = (req: EamuseInfo) => {
price = 60; price = 60;
} else if (i < 60) { } else if (i < 60) {
price = 80; price = 80;
} else if (i < 98) {
price = 200;
} }
result.goods.push({ result.goods.push({
item_id: K.ITEM('s32', i), item_id: K.ITEM('s32', i + 1),
item_type: K.ITEM('s16', 3), item_type: K.ITEM('s16', 3),
price: K.ITEM('s32', price), price: K.ITEM('s32', price),
goods_type: K.ITEM('s16', 0), goods_type: K.ITEM('s16', 0),
@@ -66,13 +68,15 @@ const getInfoCommon = (req: EamuseInfo) => {
} }
// Area // Area
for (let i = 1; i <= 16; ++i) { if(version == 'v24') {
result.area.push({ for (let i = 0; i < 16; ++i) {
area_id: K.ITEM('s16', i), result.area.push({
end_date: K.ITEM('u64', BigInt(0)), area_id: K.ITEM('s16', i),
medal_id: K.ITEM('s16', i), end_date: K.ITEM('u64', BigInt(0)),
is_limit: K.ITEM('bool', 0), medal_id: K.ITEM('s16', i),
}); is_limit: K.ITEM('bool', 0),
});
}
} }
// TODO : Course ranking // TODO : Course ranking
@@ -388,7 +392,7 @@ const getProfile = async (refid: string, version: string, name?: string) => {
// Add version specific datas // Add version specific datas
let params = await utils.readParams(refid, version); let params = await utils.readParams(refid, version);
utils.addExtraData(player, params, EXTRA_DATA); utils.addExtraData(player, params, getExtraData(version));
const achievements = <AchievementsUsaneko>await utils.readAchievements(refid, version, { ...defaultAchievements, version }); const achievements = <AchievementsUsaneko>await utils.readAchievements(refid, version, { ...defaultAchievements, version });
@@ -462,6 +466,18 @@ const getProfile = async (refid: string, version: string, name?: string) => {
}); });
} }
if(U.GetConfig("enable_force_unlock")) {
for(let i = 1; i <= GAME_MAX_MUSIC_ID[version]; i++) {
player.item.push({
type: K.ITEM('u8', 0),
id: K.ITEM('u16', i),
param: K.ITEM('u16', 15),
is_new: K.ITEM('bool', 0),
get_time: K.ITEM('u64', BigInt(0)),
});
}
}
// Usaneko events // Usaneko events
if (version == 'v24') { if (version == 'v24') {
const date = new Date(); const date = new Date();
@@ -542,6 +558,34 @@ const getProfile = async (refid: string, version: string, name?: string) => {
} }
} }
// Unilab events
if (version == 'v27') {
const teams = achievements.team || [];
const batteries = achievements.battery || [];
player.event_p27.first_play = K.ITEM('bool', teams.length == 0);
player.event_p27.elem_first_play = K.ITEM('bool', batteries.length == 0);
player.event_p27.team = [];
for (const team of teams) {
player.event_p27.team.push({
team_id: K.ITEM('s16', team.team_id || 0),
ex_no: K.ITEM('s16', team.ex_no || 0),
point: K.ITEM('u32', team.point || 0),
is_cleared: K.ITEM('bool', team.is_cleared || false),
});
};
player.event_p27.battery = [];
for (const battery of batteries) {
player.event_p27.battery.push({
battery_id: K.ITEM('s16', battery.battery_id || 0),
energy: K.ITEM('u32', battery.energy || 0),
is_cleared: K.ITEM('bool', battery.is_cleared || false),
});
};
}
return player; return player;
} }
@@ -557,7 +601,7 @@ const write = async (req: EamuseInfo, data: any, send: EamuseSend): Promise<any>
const params = await utils.readParams(refid, version); const params = await utils.readParams(refid, version);
const achievements = <AchievementsUsaneko>await utils.readAchievements(refid, version, { ...defaultAchievements, version }); const achievements = <AchievementsUsaneko>await utils.readAchievements(refid, version, { ...defaultAchievements, version });
utils.getExtraData(data, params, EXTRA_DATA); utils.getExtraData(data, params, getExtraData(version, true));
// areas // areas
let areas = _.get(data, 'area', []); let areas = _.get(data, 'area', []);
@@ -753,6 +797,59 @@ const write = async (req: EamuseInfo, data: any, send: EamuseSend): Promise<any>
} }
} }
// Unilab (v27)
if (version == 'v27') {
let eventData = _.get(data, 'event_p27', []);
let team = _.get(eventData, 'team', null);
if(_.isPlainObject(team)) {
if (_.isNil(achievements.team)) {
achievements.team = [];
}
const team_id = $(team).number('team_id');
const ex_no = $(team).number('ex_no');
const point = $(team).number('point');
const is_cleared = $(team).bool('is_cleared');
let savedTeam = _.find(achievements.team, {'team_id': team_id});
if(_.isUndefined(savedTeam)) {
achievements.team.push({
team_id,
ex_no,
point,
is_cleared
});
} else {
savedTeam.ex_no = ex_no;
savedTeam.point = point;
savedTeam.is_cleared = is_cleared;
}
}
let battery = _.get(eventData, 'battery', null);
if(_.isPlainObject(battery)) {
if (_.isNil(achievements.battery)) {
achievements.battery = [];
}
const battery_id = $(battery).number('battery_id');
const energy = $(battery).number('energy');
const is_cleared = $(battery).bool('is_cleared');
let savedBattery = _.find(achievements.battery, {'battery_id': battery_id});
if(_.isUndefined(savedBattery)) {
achievements.battery.push({
battery_id,
energy,
is_cleared
});
} else {
savedBattery.energy = energy;
savedBattery.is_cleared = is_cleared;
}
}
}
await utils.writeParams(refid, version, params); await utils.writeParams(refid, version, params);
await utils.writeAchievements(refid, version, achievements); await utils.writeAchievements(refid, version, achievements);
@@ -794,6 +891,9 @@ const friend = async (req: EamuseInfo, data: any, send: EamuseSend): Promise<any
const getPhase = (version: String): Phase[] => { const getPhase = (version: String): Phase[] => {
let phase = []; let phase = [];
switch(version) { switch(version) {
case 'v27':
phase = PHASE['v27'];
break;
case 'v26': case 'v26':
phase = PHASE['v26']; phase = PHASE['v26'];
case 'v25': case 'v25':
@@ -804,6 +904,23 @@ const getPhase = (version: String): Phase[] => {
return _.sortBy(phase, 'id'); return _.sortBy(phase, 'id');
} }
const getExtraData = (version: String, full: boolean = false): ExtraData => {
let extraData = EXTRA_DATA_COMMON;
if (full) {
extraData = _.merge(extraData, EXTRA_DATA_V27, EXTRA_DATA_V26);
} else {
switch(version) {
case 'v27':
extraData = _.merge(extraData, EXTRA_DATA_V27);
break;
case 'v26':
extraData = _.merge(extraData, EXTRA_DATA_V26);
break;
}
}
return extraData;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let isOmni = false; let isOmni = false;
@@ -814,9 +931,11 @@ const getVersion = (req: EamuseInfo): string => {
} }
const date: number = parseInt(req.model.match(/:(\d*)$/)[1]); const date: number = parseInt(req.model.match(/:(\d*)$/)[1]);
if (date > 2020120900) { if (date >= 2022091300) {
return 'v27';
} else if (date >= 2021042600 && date < 2022091300) {
return 'v26'; return 'v26';
} else if (date >= 2018101700 && date <= 2020120900) { } else if (date >= 2018101700 && date < 2021042600 ) {
return 'v25'; return 'v25';
} else { } else {
return 'v24'; return 'v24';
@@ -826,7 +945,15 @@ const getVersion = (req: EamuseInfo): string => {
const GAME_MAX_MUSIC_ID = { const GAME_MAX_MUSIC_ID = {
v24: 1704, v24: 1704,
v25: 1877, v25: 1877,
v26: 2019 v26: 2019,
v27: 2188
}
const GAME_MAX_DECO_ID = {
v24: 97,
v25: 133,
v26: 133,
v27: 81
} }
const defaultAchievements: AchievementsUsaneko = { const defaultAchievements: AchievementsUsaneko = {
@@ -840,64 +967,71 @@ const defaultAchievements: AchievementsUsaneko = {
stamps: {}, stamps: {},
riddles: {}, riddles: {},
missions: {}, missions: {},
team: [],
battery: []
} }
const PHASE = { const PHASE = {
v24: [ v24: [
{ id: 0, p: 11 }, // Default song phase availability (0-11) { id: 0, p: 11 }, // Default song phase availability (0-11)
{ id: 1, p: 2 }, { id: 1, p: 2 }, // Unknown event (0-2)
{ id: 2, p: 2 }, { id: 2, p: 2 }, // Holiday Greeting (0-2)
{ id: 3, p: 4 }, { id: 3, p: 4 }, // Unknown event (0-2)
{ id: 4, p: 1 }, { id: 4, p: 1 }, // Unknown event (0-1)
{ id: 5, p: 0 }, // Enable Net Taisen (0-1) { id: 5, p: 0 }, // Enable Net Taisen (0-1)
{ id: 6, p: 1 }, // Enable NAVI-kun shunkyoku toujou, allows song 1608 to be unlocked (0-1) { id: 6, p: 1 }, // Enable NAVI-kun shunkyoku toujou, allows song 1608 to be unlocked (0-1)
{ id: 7, p: 1 }, { id: 7, p: 1 }, // Unknown event (0-1)
{ id: 8, p: 2 }, { id: 8, p: 2 }, // Unknown event (0-2)
{ id: 9, p: 2 }, // Daily Mission (0-2) { id: 9, p: 2 }, // Daily Mission (0-2)
{ id: 10, p: 15 }, // NAVI-kun Song phase availability (0-15) { id: 10, p: 15 }, // NAVI-kun Song phase availability (0-15)
{ id: 11, p: 1 }, { id: 11, p: 1 }, // Unknown event (0-1)
{ id: 12, p: 2 }, { id: 12, p: 2 }, // Unknown event (0-2)
{ id: 13, p: 1 }, // Enable Pop'n Peace preview song (0-1) { id: 13, p: 1 }, // Enable Pop'n Peace preview song (0-1)
], ],
v25: [ v25: [
{ id: 0, p: 23 }, { id: 0, p: 23 }, // Default song phase availability (0-23)
{ id: 1, p: 4 }, { id: 1, p: 4 }, // Unknown event (0-4)
{ id: 10, p: 30 }, { id: 10, p: 30 }, // NAVI-kun Song phase availability (0-30)
// New params { id: 14, p: 39 }, // Stamp Card Rally (0-39)
{ id: 14, p: 39 }, { id: 15, p: 2 }, // Unknown event (0-2)
{ id: 15, p: 2 }, { id: 16, p: 3 }, // Unknown event (0-3)
{ id: 16, p: 3 }, { id: 17, p: 8 }, // Unknown event (0-8)
{ id: 17, p: 8 }, { id: 18, p: 1 }, // FLOOR INFECTION event (0-1)
{ id: 18, p: 1 }, { id: 19, p: 1 }, // Pop'n music × NOSTALGIA kyouenkai (0-1)
{ id: 19, p: 1 }, { id: 20, p: 13 }, // Event archive (0-13)
{ id: 20, p: 13 }, { id: 21, p: 20 }, // Pop'n event archive (0-20)
{ id: 21, p: 20 }, // pop'n event archive { id: 22, p: 2 }, // バンめし♪ ふるさとグランプリ (0-2)
{ id: 22, p: 2 }, { id: 23, p: 1 }, // いちかのBEMANI投票選抜戦2019 (0-1)
{ id: 23, p: 1 }, { id: 24, p: 1 }, // ダンキラ!!! × pop'n music (0-1)
{ id: 24, p: 1 },
], ],
v26: [ v26: [
// Music phase { id: 0, p: 30 }, // Music phase (0: No unlock, 1-30: steps)
// 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: 25, p: 62 }, // M&N event (0: disable, 62: all characters)
{ id: 26, p: 3 }, // Unknown event (0-3) { 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: 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: 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: 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) { id: 30, p: 6 }, // Shutchou! pop'n quest Lively II (0: not started, 1-5: step enabled, 6: ended)
],
v27: [
{ id: 0, p: 6 }, // Music phase (0: No unlock, 1-6: steps)
{ id: 1, p: 6 }, // Shutchou! pop'n quest Lively II (0: not started, 1-5: steps, 6: ended)
{ id: 2, p: 4 }, // KAC 2023 (0/2/4: disabled, 1: Caldwell 99, 3: Hexer / mathematical good-bye)
{ id: 3, p: 0 }, // Net Taisen (0: diabled, 1: enabled, 2: enabled + local)
{ id: 4, p: 7 }, // Unknown event (0-7)
{ id: 5, p: 48 }, // Narunaru♪ UniLab jikkenshitsu! event (0: not started, 1-47: steps, 48: ended)
{ id: 6, p: 2 }, // Super Unilab BOOST! (0: disabled, 1: enabled, 2: ended)
{ id: 7, p: 6 }, // Unknown event (0-6)
{ id: 8, p: 2 }, // Unknown event (0-2)
{ id: 9, p: 44 }, // Kakusei no Elem event (0: not started, 1-44: steps)
{ id: 10, p: 1 }, // Awakening Elem (0: disabled, 1: enabled)
{ id: 11, p: 2 }, // CanCan's Super Awakening Boost (0: disabled, 1: enabled, 2: ended)
{ id: 12, p: 2 }, // Unknown event (0-2)
{ id: 13, p: 2 }, // Unknown event (0-2)
] ]
} }
const EXTRA_DATA: ExtraData = { const EXTRA_DATA_COMMON: ExtraData = {
play_id: { type: 's32', path: 'account', default: 0 }, play_id: { type: 's32', path: 'account', default: 0 },
start_type: { type: 's8', path: 'account', default: 0 }, start_type: { type: 's8', path: 'account', default: 0 },
tutorial: { type: 's16', path: 'account', default: -1 }, tutorial: { type: 's16', path: 'account', default: -1 },
@@ -923,15 +1057,6 @@ const EXTRA_DATA: ExtraData = {
player_point: { type: 's32', path: 'account', default: 300 }, player_point: { type: 's32', path: 'account', default: 300 },
power_point_list: { type: 's32', path: 'account', default: [0], isArray: true }, 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 }, mode: { type: 'u8', path: 'config', default: 0 },
chara: { type: 's16', path: 'config', default: 0 }, chara: { type: 's16', path: 'config', default: 0 },
music: { type: 's16', path: 'config', default: 0 }, music: { type: 's16', path: 'config', default: 0 },
@@ -973,3 +1098,21 @@ const EXTRA_DATA: ExtraData = {
comment_1: { type: 'u16', path: 'customize', default: 0 }, comment_1: { type: 'u16', path: 'customize', default: 0 },
comment_2: { type: 'u16', path: 'customize', default: 0 }, comment_2: { type: 'u16', path: 'customize', default: 0 },
} }
const EXTRA_DATA_V26: ExtraData = {
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
}
const EXTRA_DATA_V27: ExtraData = {
lift: { type: 'bool', path: 'option', default: 0 },
lift_rate: { type: 's16', path: 'option', default: 0 },
team_id: { type: 's16', path: 'event_p27', default: 0 },
select_battery_id: { type: 's16', path: 'event_p27', default: 1 },
today_first_play: { type: 'bool', path: 'event_p27', default: 1 },
}

View File

@@ -1,32 +0,0 @@
import { Profile, Scores } from "../models/common";
export const importPnmData = async (data: {
refid: string;
profile: string;
scores: string;
}) => {
const profile = JSON.parse(data.profile);
const scores = JSON.parse(data.scores);
await DB.Update<Profile>(
data.refid,
{ collection: 'profile' },
{
$set: {
...profile
}
}
);
await DB.Upsert<Scores>(
data.refid,
{ collection: 'scores' },
{
$set: {
scores: {
...scores
}
}
}
);
};

View File

@@ -4,7 +4,6 @@ import * as sunny from "./handler/sunny";
import * as lapistoria from "./handler/lapistoria"; import * as lapistoria from "./handler/lapistoria";
import * as eclale from "./handler/eclale"; import * as eclale from "./handler/eclale";
import * as usaneko from "./handler/usaneko"; import * as usaneko from "./handler/usaneko";
import { importPnmData } from "./handler/webui";
import { Rivals } from "./models/common"; import { Rivals } from "./models/common";
const getVersion = (req: any) => { const getVersion = (req: any) => {
@@ -30,7 +29,12 @@ export function register() {
default: true, default: true,
}); });
R.WebUIEvent('importPnmData', importPnmData); R.Config("enable_force_unlock", {
name: "Unlock all songs",
desc: "Force unlocking all songs (Lapistoria and later).",
type: "boolean",
default: true,
});
R.WebUIEvent('updatePnmPlayerInfo', async (data: any) => { R.WebUIEvent('updatePnmPlayerInfo', async (data: any) => {
await DB.Update(data.refid, { collection: 'profile' }, { $set: { name: data.name } }); await DB.Update(data.refid, { collection: 'profile' }, { $set: { name: data.name } });

View File

@@ -122,4 +122,20 @@ export interface AchievementsUsaneko extends Achievements {
other_count: number; other_count: number;
}; };
}; };
team: Team[];
battery: Battery[]
}
interface Team {
team_id: number;
ex_no: number;
point: number;
is_cleared: boolean;
}
interface Battery {
battery_id: number;
energy: number;
is_cleared: boolean;
} }

View File

@@ -1,11 +0,0 @@
$('#import-click').on('click', () => {
data = {
refid: $('#refid').val(),
profile: $('#profile').val(),
scores: $('#scores').val()
}
emit('importPnmData', data).then(() => {
$('#import-success').removeClass("is-hidden");
});
});

View File

@@ -62,25 +62,3 @@ div
label There is a limit of 4 rivals maximum (only the 2 firsts will be used for Sunny Park and lower). label There is a limit of 4 rivals maximum (only the 2 firsts will be used for Sunny Park and lower).
div div
label The score sharing option also affect scores get from rivals. label The score sharing option also affect scores get from rivals.
.card
.card-header
p.card-header-title
span.icon
i.mdi.mdi-account-edit
| Import data
.card-content
.field
label.label Import data from previous Asphyxia (non-core)
.field
input.input(type="text" id="profile" name="profile" placeholder="Put content of popn.json")
.field
input.input(type="text" id="scores" name="scores" placeholder="Put content of popn_scores.json")
.field
label.help.is-danger /!\ Please backup your savedata.db before importing data /!\
.field
button.button.is-primary#import-click
span.icon
i.mdi.mdi-file-import-outline
span Import
script(src="static/js/profile_page.js")