1
0
mirror of synced 2025-02-28 07:30:45 +01:00

Improve a few bits of Fantasia, including working profile conversions and displaying cards at the end of the round.

This commit is contained in:
Jennifer Taylor 2021-09-03 04:35:13 +00:00
parent 4f64cc7e08
commit cbbda0ef7f
3 changed files with 161 additions and 52 deletions

View File

@ -3,7 +3,7 @@ import traceback
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type
from bemani.common import Model, ValidatedDict, Profile, PlayStatistics, GameConstants, Time from bemani.common import Model, ValidatedDict, Profile, PlayStatistics, GameConstants, Time
from bemani.data import Config, Data, UserID, RemoteUser from bemani.data import Config, Data, Machine, UserID, RemoteUser
class ProfileCreationException(Exception): class ProfileCreationException(Exception):
@ -391,18 +391,27 @@ class Base(ABC):
machine = self.data.local.machine.get_machine(self.config.machine.pcbid) machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
return machine.id return machine.id
def get_machine(self) -> Machine:
return self.data.local.machine.get_machine(self.config.machine.pcbid)
def update_machine_name(self, newname: Optional[str]) -> None: def update_machine_name(self, newname: Optional[str]) -> None:
if newname is None: if newname is None:
return return
machine = self.data.local.machine.get_machine(self.config.machine.pcbid) machine = self.get_machine()
machine.name = newname machine.name = newname
self.data.local.machine.put_machine(machine) self.data.local.machine.put_machine(machine)
def update_machine_data(self, newdata: Dict[str, Any]) -> None: def update_machine_data(self, newdata: Dict[str, Any]) -> None:
machine = self.data.local.machine.get_machine(self.config.machine.pcbid) machine = self.get_machine()
machine.data.update(newdata) machine.data.update(newdata)
self.data.local.machine.put_machine(machine) self.data.local.machine.put_machine(machine)
def update_machine(self, newmachine: Machine) -> None:
machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
machine.name = newmachine.name
machine.data = newmachine.data
self.data.local.machine.put_machine(machine)
def get_game_config(self) -> ValidatedDict: def get_game_config(self) -> ValidatedDict:
machine = self.data.local.machine.get_machine(self.config.machine.pcbid) machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
if machine.arcade is not None: if machine.arcade is not None:

View File

@ -1,6 +1,6 @@
# vim: set fileencoding=utf-8 # vim: set fileencoding=utf-8
import copy import copy
from typing import Dict, List from typing import Any, Dict, List
from bemani.backend.popn.base import PopnMusicBase from bemani.backend.popn.base import PopnMusicBase
from bemani.backend.popn.tunestreet import PopnMusicTuneStreet from bemani.backend.popn.tunestreet import PopnMusicTuneStreet
@ -46,6 +46,41 @@ class PopnMusicFantasia(PopnMusicBase):
def previous_version(self) -> PopnMusicBase: def previous_version(self) -> PopnMusicBase:
return PopnMusicTuneStreet(self.data, self.config, self.model) return PopnMusicTuneStreet(self.data, self.config, self.model)
@classmethod
def get_settings(cls) -> Dict[str, Any]:
"""
Return all of our front-end modifiably settings.
"""
return {
'ints': [
{
'name': 'Game Phase',
'tip': 'Game unlock phase for all players.',
'category': 'game_config',
'setting': 'game_phase',
'values': {
0: 'NO PHASE',
1: 'SECRET DATA RELEASE',
2: 'MAX: ALL DATA RELEASE',
}
},
{
'name': 'Soreyuke! pop\'n quest Event Phase',
'tip': 'Event phase for all players.',
'category': 'game_config',
'setting': 'event_phase',
'values': {
0: 'No event',
1: 'Phase 1',
2: 'Phase 2',
3: 'Phase 3',
4: 'Phase 4',
5: 'Phase MAX',
}
},
]
}
def __format_medal_for_score(self, score: Score) -> int: def __format_medal_for_score(self, score: Score) -> int:
medal = { medal = {
self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED, self.PLAY_MEDAL_CIRCLE_FAILED: self.GAME_PLAY_MEDAL_CIRCLE_FAILED,
@ -109,6 +144,7 @@ class PopnMusicFantasia(PopnMusicBase):
player_card.add_child(Node.s32('get_frame', player_card_dict.get_int('get_frame'))) player_card.add_child(Node.s32('get_frame', player_card_dict.get_int('get_frame')))
player_card.add_child(Node.s32('get_base', player_card_dict.get_int('get_base'))) player_card.add_child(Node.s32('get_base', player_card_dict.get_int('get_base')))
player_card.add_child(Node.s32_array('get_seal', player_card_dict.get_int_array('get_seal', 2))) player_card.add_child(Node.s32_array('get_seal', player_card_dict.get_int_array('get_seal', 2)))
player_card.add_child(Node.s8('is_open', 1))
# Player card EX section # Player card EX section
player_card_ex = Node.void('player_card_ex') player_card_ex = Node.void('player_card_ex')
@ -201,7 +237,6 @@ class PopnMusicFantasia(PopnMusicBase):
netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0])) netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0]))
netvs.add_child(Node.s8_array('jewelry', [0] * 15)) netvs.add_child(Node.s8_array('jewelry', [0] * 15))
for dialog in [0, 1, 2, 3, 4, 5]: for dialog in [0, 1, 2, 3, 4, 5]:
# TODO: Configure this, maybe?
netvs.add_child(Node.string('dialog', f'dialog#{dialog}')) netvs.add_child(Node.string('dialog', f'dialog#{dialog}'))
sp_data = Node.void('sp_data') sp_data = Node.void('sp_data')
@ -213,17 +248,18 @@ class PopnMusicFantasia(PopnMusicBase):
reflec_data.add_child(Node.s8_array('reflec', profile.get_int_array('reflec', 2))) reflec_data.add_child(Node.s8_array('reflec', profile.get_int_array('reflec', 2)))
# Navigate section # Navigate section
navigate_dict = profile.get_dict('navigate') for i in range(3):
navigate_dict = profile.get_dict(f'navigate_{i}')
navigate = Node.void('navigate') navigate = Node.void('navigate')
root.add_child(navigate) root.add_child(navigate)
navigate.add_child(Node.s8('genre', navigate_dict.get_int('genre'))) navigate.add_child(Node.s8('genre', navigate_dict.get_int('genre', -1)))
navigate.add_child(Node.s8('image', navigate_dict.get_int('image'))) navigate.add_child(Node.s8('image', navigate_dict.get_int('image', -1)))
navigate.add_child(Node.s8('level', navigate_dict.get_int('level'))) navigate.add_child(Node.s8('level', navigate_dict.get_int('level', -1)))
navigate.add_child(Node.s8('ojama', navigate_dict.get_int('ojama'))) navigate.add_child(Node.s8('ojama', navigate_dict.get_int('ojama', -1)))
navigate.add_child(Node.s16('limit_num', navigate_dict.get_int('limit_num'))) navigate.add_child(Node.s16('limit_num', navigate_dict.get_int('limit_num', -1)))
navigate.add_child(Node.s8('button', navigate_dict.get_int('button'))) navigate.add_child(Node.s8('button', navigate_dict.get_int('button', -1)))
navigate.add_child(Node.s8('life', navigate_dict.get_int('life'))) navigate.add_child(Node.s8('life', navigate_dict.get_int('life', -1)))
navigate.add_child(Node.s16('progress', navigate_dict.get_int('progress'))) navigate.add_child(Node.s16('progress', navigate_dict.get_int('progress', -1)))
return root return root
@ -238,6 +274,7 @@ class PopnMusicFantasia(PopnMusicBase):
root.add_child(Node.u8('season', 0)) root.add_child(Node.u8('season', 0))
clear_medal = [0] * self.GAME_MAX_MUSIC_ID clear_medal = [0] * self.GAME_MAX_MUSIC_ID
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
scores = self.data.remote.music.get_scores(self.game, self.version, userid) scores = self.data.remote.music.get_scores(self.game, self.version, userid)
for score in scores: for score in scores:
@ -254,8 +291,21 @@ class PopnMusicFantasia(PopnMusicBase):
continue continue
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score) clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
hiscore_index = (score.id * 4) + {
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION,
self.CHART_TYPE_HYPER: self.GAME_CHART_TYPE_HYPER_POSITION,
self.CHART_TYPE_EX: self.GAME_CHART_TYPE_EX_POSITION,
}[score.chart]
hiscore_byte_pos = int((hiscore_index * 17) / 8)
hiscore_bit_pos = int((hiscore_index * 17) % 8)
hiscore_value = score.points << hiscore_bit_pos
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (hiscore_value & 0xFF)
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 0xFF)
root.add_child(Node.u16_array('clear_medal', clear_medal)) root.add_child(Node.u16_array('clear_medal', clear_medal))
root.add_child(Node.binary('hiscore', bytes(hiscore_array)))
return root return root
@ -317,9 +367,10 @@ class PopnMusicFantasia(PopnMusicBase):
newprofile.replace_dict('player_card', player_card_dict) newprofile.replace_dict('player_card', player_card_dict)
# Extract navigate stuff # Extract navigate stuff
navigate_dict = newprofile.get_dict('navigate') nav_id = 0
navigate = request.child('navigate') for navigate in request.children:
if navigate is not None: if navigate.name == 'navigate':
navigate_dict = newprofile.get_dict(f'navigate_{nav_id}')
navigate_dict.replace_int('genre', navigate.child_value('genre')) navigate_dict.replace_int('genre', navigate.child_value('genre'))
navigate_dict.replace_int('image', navigate.child_value('image')) navigate_dict.replace_int('image', navigate.child_value('image'))
navigate_dict.replace_int('level', navigate.child_value('level')) navigate_dict.replace_int('level', navigate.child_value('level'))
@ -328,7 +379,11 @@ class PopnMusicFantasia(PopnMusicBase):
navigate_dict.replace_int('button', navigate.child_value('button')) navigate_dict.replace_int('button', navigate.child_value('button'))
navigate_dict.replace_int('life', navigate.child_value('life')) navigate_dict.replace_int('life', navigate.child_value('life'))
navigate_dict.replace_int('progress', navigate.child_value('progress')) navigate_dict.replace_int('progress', navigate.child_value('progress'))
newprofile.replace_dict('navigate', navigate_dict) newprofile.replace_dict(f'navigate_{nav_id}', navigate_dict)
nav_id += 1
if nav_id >= 3:
break
# Extract scores # Extract scores
for node in request.children: for node in request.children:
@ -397,22 +452,61 @@ class PopnMusicFantasia(PopnMusicBase):
def handle_playerdata_set_request(self, request: Node) -> Node: def handle_playerdata_set_request(self, request: Node) -> Node:
refid = request.attribute('ref_id') refid = request.attribute('ref_id')
machine = self.get_machine()
root = Node.void('playerdata') root = Node.void('playerdata')
root.add_child(Node.s8('pref', -1)) root.add_child(Node.s8('pref', machine.data.get_int('pref', -1)))
if refid is None: if refid is None:
root.add_child(Node.string('name', ''))
root.add_child(Node.s16('chara', -1))
root.add_child(Node.u8('frame', 0))
root.add_child(Node.u8('base', 0))
root.add_child(Node.u8('seal_1', 0))
root.add_child(Node.u8('seal_2', 0))
root.add_child(Node.u8('title_1', 0))
root.add_child(Node.u8('title_2', 0))
root.add_child(Node.s16('recommend_1', -1))
root.add_child(Node.s16('recommend_2', -1))
root.add_child(Node.s16('recommend_3', -1))
root.add_child(Node.string('message', ''))
return root return root
userid = self.data.remote.user.from_refid(self.game, self.version, refid) userid = self.data.remote.user.from_refid(self.game, self.version, refid)
if userid is None: if userid is None:
root.add_child(Node.string('name', ''))
root.add_child(Node.s16('chara', -1))
root.add_child(Node.u8('frame', 0))
root.add_child(Node.u8('base', 0))
root.add_child(Node.u8('seal_1', 0))
root.add_child(Node.u8('seal_2', 0))
root.add_child(Node.u8('title_1', 0))
root.add_child(Node.u8('title_2', 0))
root.add_child(Node.s16('recommend_1', -1))
root.add_child(Node.s16('recommend_2', -1))
root.add_child(Node.s16('recommend_3', -1))
root.add_child(Node.string('message', ''))
return root return root
oldprofile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0) oldprofile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0)
newprofile = self.unformat_profile(userid, request, oldprofile) newprofile = self.unformat_profile(userid, request, oldprofile)
if newprofile is not None: if newprofile is not None:
player_card_dict = newprofile.get_dict('player_card')
self.put_profile(userid, newprofile) self.put_profile(userid, newprofile)
root.add_child(Node.string('name', newprofile['name'])) root.add_child(Node.string('name', newprofile.get_str('name', 'なし')))
root.add_child(Node.s16('chara', newprofile.get_int('chara', -1)))
root.add_child(Node.u8('frame', player_card_dict.get_int('frame')))
root.add_child(Node.u8('base', player_card_dict.get_int('base')))
root.add_child(Node.u8('seal_1', player_card_dict.get_int_array('seal', 2)[0]))
root.add_child(Node.u8('seal_2', player_card_dict.get_int_array('seal', 2)[1]))
root.add_child(Node.u8('title_1', player_card_dict.get_int_array('title', 2, [0, 1])[0]))
root.add_child(Node.u8('title_2', player_card_dict.get_int_array('title', 2, [0, 1])[1]))
root.add_child(Node.s16('recommend_1', -1))
root.add_child(Node.s16('recommend_2', -1))
root.add_child(Node.s16('recommend_3', -1))
root.add_child(Node.string('message', ''))
return root return root
@ -492,38 +586,48 @@ class PopnMusicFantasia(PopnMusicBase):
friend.add_child(Node.u16_array('clear_medal', clear_medal)) friend.add_child(Node.u16_array('clear_medal', clear_medal))
friend.add_child(Node.binary('hiscore', hiscore)) friend.add_child(Node.binary('hiscore', hiscore))
# Note that if we ever support internet ranking mode, there's an 'ir_hiscore' node here as well.
# Also note that if we support lobbies, there's a few extra nodes in each friend for current lobby
# that they're in as well as previous lobby logs.
return root return root
def handle_game_get_request(self, request: Node) -> Node: def handle_game_get_request(self, request: Node) -> Node:
# TODO: Hook these up to config so we can change this game_config = self.get_game_config()
game_phase = game_config.get_int('game_phase')
event_phase = game_config.get_int('event_phase')
root = Node.void('game') root = Node.void('game')
root.add_child(Node.s32('game_phase', 2)) root.add_child(Node.s32('game_phase', game_phase))
root.add_child(Node.s32('ir_phase', 0)) root.add_child(Node.s32('ir_phase', 0))
root.add_child(Node.s32('event_phase', 5)) root.add_child(Node.s32('event_phase', event_phase))
root.add_child(Node.s32('netvs_phase', 0)) root.add_child(Node.s32('netvs_phase', 0)) # Net taisen mode, we don't support lobbies.
root.add_child(Node.s32('card_phase', 6)) root.add_child(Node.s32('card_phase', 6))
root.add_child(Node.s32('illust_phase', 2)) root.add_child(Node.s32('illust_phase', 2))
root.add_child(Node.s32('psp_phase', 5)) root.add_child(Node.s32('psp_phase', 5)) # Unlock songs from Pop'n Music Portable.
root.add_child(Node.s32('other_phase', 1)) root.add_child(Node.s32('other_phase', 1))
root.add_child(Node.s32('jubeat_phase', 1)) root.add_child(Node.s32('jubeat_phase', 1))
root.add_child(Node.s32('public_phase', 3)) root.add_child(Node.s32('public_phase', 3))
root.add_child(Node.s32('kac_phase', 2)) root.add_child(Node.s32('kac_phase', 2))
root.add_child(Node.s32('local_matching', 1)) root.add_child(Node.s32('local_matching_enable', 1))
root.add_child(Node.s32('n_matching_sec', 60)) root.add_child(Node.s32('n_matching_sec', 60))
root.add_child(Node.s32('l_matching_sec', 60)) root.add_child(Node.s32('l_matching_sec', 60))
root.add_child(Node.s32('is_check_cpu', 0)) root.add_child(Node.s32('is_check_cpu', 0))
root.add_child(Node.s32('week_no', 0)) root.add_child(Node.s32('week_no', 0))
root.add_child(Node.s32_array('ng_illust', [0] * 10)) root.add_child(Node.s32('team_day', 0))
root.add_child(Node.s32_array('ng_illust', [-1] * 64))
root.add_child(Node.s16_array('sel_ranking', [-1] * 10)) root.add_child(Node.s16_array('sel_ranking', [-1] * 10))
root.add_child(Node.s16_array('up_ranking', [-1] * 10)) root.add_child(Node.s16_array('up_ranking', [-1] * 10))
return root return root
def handle_game_active_request(self, request: Node) -> Node: def handle_game_active_request(self, request: Node) -> Node:
# Update the name of this cab for admin purposes # Update the name of this cab for admin purposes. Also store the prefecture.
self.update_machine_name(request.child_value('shop_name')) machine = self.get_machine()
return Node.void('game') machine.name = request.child_value('shop_name') or machine.name
machine.data.replace_int('pref', request.child_value('pref'))
def handle_game_taxphase_request(self, request: Node) -> Node: self.update_machine(machine)
return Node.void('game') return Node.void('game')
def handle_lobby_request(self, request: Node) -> Node: def handle_lobby_request(self, request: Node) -> Node:

View File

@ -84,7 +84,7 @@ class PopnMusicFantasiaClient(BaseClient):
'jubeat_phase', 'jubeat_phase',
'public_phase', 'public_phase',
'kac_phase', 'kac_phase',
'local_matching', 'local_matching_enable',
'n_matching_sec', 'n_matching_sec',
'l_matching_sec', 'l_matching_sec',
'is_check_cpu', 'is_check_cpu',
@ -101,22 +101,18 @@ class PopnMusicFantasiaClient(BaseClient):
up_ranking = resp.child('game').child('up_ranking') up_ranking = resp.child('game').child('up_ranking')
ng_illust = resp.child('game').child('ng_illust') ng_illust = resp.child('game').child('ng_illust')
for nodepair in [ for name, node, dtype, length in [
('sel_ranking', sel_ranking, 's16'), ('sel_ranking', sel_ranking, 's16', 10),
('up_ranking', up_ranking, 's16'), ('up_ranking', up_ranking, 's16', 10),
('ng_illust', ng_illust, 's32'), ('ng_illust', ng_illust, 's32', 64),
]: ]:
name = nodepair[0]
node = nodepair[1]
dtype = nodepair[2]
if node is None: if node is None:
raise Exception(f'Missing node \'{name}\' in response!') raise Exception(f'Missing node \'{name}\' in response!')
if node.data_type != dtype: if node.data_type != dtype:
raise Exception(f'Node \'{name}\' has wrong data type!') raise Exception(f'Node \'{name}\' has wrong data type!')
if not node.is_array: if not node.is_array:
raise Exception(f'Node \'{name}\' is not array!') raise Exception(f'Node \'{name}\' is not array!')
if len(node.value) != 10: if len(node.value) != length:
raise Exception(f'Node \'{name}\' is wrong array length!') raise Exception(f'Node \'{name}\' is wrong array length!')
def verify_playerdata_get(self, ref_id: str, msg_type: str) -> Optional[Dict[str, Any]]: def verify_playerdata_get(self, ref_id: str, msg_type: str) -> Optional[Dict[str, Any]]: