From b4588e6c479a07017b808d30537cc36e419279ea Mon Sep 17 00:00:00 2001 From: cracrayol Date: Sun, 18 Sep 2022 02:55:18 +0200 Subject: [PATCH] Kaimei Riddles : Support added Usaneko : Add Daily Missions support Usaneko+ : Remove game id check for Omnimix --- popn@asphyxia/README.md | 16 +- popn@asphyxia/handler/usaneko.ts | 253 ++++++++++++++++++++++----- popn@asphyxia/models/achievements.ts | 19 +- 3 files changed, 235 insertions(+), 53 deletions(-) diff --git a/popn@asphyxia/README.md b/popn@asphyxia/README.md index fcce465..5680d79 100644 --- a/popn@asphyxia/README.md +++ b/popn@asphyxia/README.md @@ -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 diff --git a/popn@asphyxia/handler/usaneko.ts b/popn@asphyxia/handler/usaneko.ts index 0fc0c85..5ea9683 100644 --- a/popn@asphyxia/handler/usaneko.ts +++ b/popn@asphyxia/handler/usaneko.ts @@ -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 = 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 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 = 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 { + 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 }, -} \ No newline at end of file +} diff --git a/popn@asphyxia/models/achievements.ts b/popn@asphyxia/models/achievements.ts index 768de45..afe41d0 100644 --- a/popn@asphyxia/models/achievements.ts +++ b/popn@asphyxia/models/achievements.ts @@ -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; + }; + }; } \ No newline at end of file