492 lines
19 KiB
Python
492 lines
19 KiB
Python
# vim: set fileencoding=utf-8
|
||
import math
|
||
import random
|
||
from typing import Any, Dict, List, Tuple
|
||
|
||
from bemani.backend.popn.base import PopnMusicBase
|
||
from bemani.backend.popn.common import PopnMusicModernBase
|
||
from bemani.backend.popn.peace import PopnMusicPeace
|
||
from bemani.common import VersionConstants
|
||
from bemani.common.validateddict import Profile
|
||
from bemani.data.types import UserID
|
||
from bemani.protocol.node import Node
|
||
|
||
|
||
class PopnMusicKaimei(PopnMusicModernBase):
|
||
|
||
name: str = "Pop'n Music 解明リドルズ"
|
||
version: int = VersionConstants.POPN_MUSIC_KAIMEI_RIDDLES
|
||
|
||
# Biggest ID in the music DB
|
||
GAME_MAX_MUSIC_ID: int = 2019
|
||
|
||
# Biggest deco part ID in the game
|
||
GAME_MAX_DECO_ID: int = 133
|
||
|
||
# Item limits are as follows:
|
||
# 0: 1877 - ID is the music ID that the player purchased/unlocked.
|
||
# 1: 2344
|
||
# 2: 3
|
||
# 3: 133 - ID points at a character part that can be purchased on the character screen.
|
||
# 4: 1
|
||
# 5: 1
|
||
# 6: 60
|
||
|
||
def previous_version(self) -> PopnMusicBase:
|
||
return PopnMusicPeace(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': 'Music Open Phase',
|
||
'tip': 'Default music phase for all players.',
|
||
'category': 'game_config',
|
||
'setting': 'music_phase',
|
||
'values': {
|
||
# The value goes to 30 now, but it starts where usaneko left off at 23
|
||
# Unlocks a total of 10 songs
|
||
23: 'No music unlocks',
|
||
24: 'Phase 1',
|
||
25: 'Phase 2',
|
||
26: 'Phase 3',
|
||
27: 'Phase 4',
|
||
28: 'Phase 5',
|
||
29: 'Phase 6',
|
||
30: 'Phase MAX',
|
||
}
|
||
},
|
||
{
|
||
'name': 'Kaimei! MN tanteisha event Phase',
|
||
'tip': 'Kaimei! MN tanteisha event phase for all players.',
|
||
'category': 'game_config',
|
||
'setting': 'mn_tanteisha_phase',
|
||
'values': {
|
||
0: 'Disabled',
|
||
1: 'Roki',
|
||
2: 'shiroro',
|
||
3: 'PIERRE&JILL',
|
||
4: 'ROSA',
|
||
5: 'taoxiang',
|
||
6: 'TangTang',
|
||
7: 'OTOBEAR',
|
||
8: 'kaorin',
|
||
9: 'CHARLY',
|
||
10: 'ALOE',
|
||
11: 'RIE♥chan',
|
||
12: 'hina',
|
||
13: 'PAPYRUS',
|
||
14: '雷蔵, miho, RIE♥chan, Ryusei Honey',
|
||
15: 'Murasaki',
|
||
16: 'Lucifelle',
|
||
17: '六',
|
||
18: 'stella',
|
||
19: 'ちせ',
|
||
20: 'LISA',
|
||
21: 'SUMIRE',
|
||
22: 'SHISHITUGU',
|
||
23: 'WALKER',
|
||
24: 'Candy',
|
||
25: 'Jade',
|
||
26: 'AYA',
|
||
27: 'kaorin',
|
||
28: 'Lord Meh',
|
||
29: 'HAMANOV',
|
||
30: 'Agent',
|
||
31: 'Yima',
|
||
32: 'ikkei',
|
||
33: 'echidna',
|
||
34: 'lithos',
|
||
35: 'SMOKE',
|
||
36: 'the KING',
|
||
37: 'Kicoro',
|
||
38: 'DEBORAH',
|
||
39: 'Teruo',
|
||
40: 'the TOWER',
|
||
41: 'Mamoru-kun',
|
||
42: 'Canopus',
|
||
43: 'Mimi Nyami',
|
||
44: 'iO-LOWER',
|
||
45: 'BOY',
|
||
46: 'Sergei',
|
||
47: 'SAPPHIRE',
|
||
48: 'Chocky',
|
||
49: 'HAPPPY',
|
||
50: 'SHOLLKEE',
|
||
51: 'CHARA-O',
|
||
52: 'Hugh, GRIM, SUMIKO',
|
||
53: 'Peetan',
|
||
54: 'SHARK',
|
||
55: 'Nakajima-san',
|
||
56: 'KIKYO',
|
||
57: 'SUMIRE',
|
||
58: 'NAKAJI',
|
||
59: 'moi moi',
|
||
60: 'TITICACA',
|
||
61: 'MASAMUNE',
|
||
62: 'YUMMY'
|
||
},
|
||
},
|
||
{
|
||
# For festive times, it's possible to change the welcome greeting. I'm not sure why you would want to change this, but now you can.
|
||
'name': 'Holiday Greeting',
|
||
'tip': 'Changes the payment selection confirmation sound.',
|
||
'category': 'game_config',
|
||
'setting': 'holiday_greeting',
|
||
'values': {
|
||
0: 'Okay!',
|
||
1: 'Merry Christmas!',
|
||
2: 'Happy New Year!',
|
||
}
|
||
},
|
||
{
|
||
# peace soundtrack hatsubai kinen SP event, 0 = off, 1 = active, 2 = off (0-2)
|
||
'name': 'peace soundtrack hatsubai kinen SP',
|
||
'tip': 'peace soundtrack hatsubai kinen SP for all players.',
|
||
'category': 'game_config',
|
||
'setting': 'peace_soundtrack',
|
||
'values': {
|
||
0: 'Not stated',
|
||
1: 'Active',
|
||
2: 'Ended',
|
||
}
|
||
},
|
||
{
|
||
'name': 'MZD no kimagure tanteisha joshu',
|
||
'tip': 'Boost increasing the Clarification Level, if four or more Requests still unresolved.',
|
||
'category': 'game_config',
|
||
'setting': 'tanteisha_joshu',
|
||
'values': {
|
||
0: 'Not stated',
|
||
1: 'Active',
|
||
2: 'Ended',
|
||
}
|
||
},
|
||
{
|
||
# Shutchou! pop'n quest Lively II event
|
||
'name': 'Shutchou! pop\'n quest Lively phase',
|
||
'tip': 'Shutchou! pop\'n quest Lively phase for all players.',
|
||
'category': 'game_config',
|
||
'setting': 'popn_quest_lively',
|
||
'values': {
|
||
0: 'Not started',
|
||
1: 'fes 1',
|
||
2: 'fes 2',
|
||
3: 'fes FINAL',
|
||
4: 'fes EXTRA',
|
||
5: 'Ended',
|
||
}
|
||
},
|
||
{
|
||
# Shutchou! pop'n quest Lively II event
|
||
'name': 'Shutchou! pop\'n quest Lively II phase',
|
||
'tip': 'Shutchou! pop\'n quest Lively II phase for all players.',
|
||
'category': 'game_config',
|
||
'setting': 'popn_quest_lively_2',
|
||
'values': {
|
||
0: 'Not started',
|
||
1: 'fes 1',
|
||
2: 'fes 2',
|
||
3: 'fes FINAL',
|
||
4: 'fes EXTRA',
|
||
5: 'fes THE END',
|
||
6: 'Ended',
|
||
}
|
||
},
|
||
],
|
||
'bools': [
|
||
# We don't currently support lobbies or anything, so this is commented out until
|
||
# somebody gets around to implementing it.
|
||
# {
|
||
# 'name': 'Net Taisen',
|
||
# 'tip': 'Enable Net Taisen, including win/loss display on song select',
|
||
# 'category': 'game_config',
|
||
# 'setting': 'enable_net_taisen',
|
||
# },
|
||
{
|
||
'name': 'Force Song Unlock',
|
||
'tip': 'Force unlock all songs.',
|
||
'category': 'game_config',
|
||
'setting': 'force_unlock_songs',
|
||
},
|
||
],
|
||
}
|
||
|
||
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
||
game_config = self.get_game_config()
|
||
music_phase = game_config.get_int('music_phase')
|
||
holiday_greeting = game_config.get_int('holiday_greeting')
|
||
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
||
mn_tanteisha_phase = game_config.get_int('mn_tanteisha_phase')
|
||
peace_soundtrack = game_config.get_int('peace_soundtrack')
|
||
tanteisha_joshu = game_config.get_int('tanteisha_joshu')
|
||
popn_quest_lively = game_config.get_int('popn_quest_lively')
|
||
popn_quest_lively_2 = game_config.get_int('popn_quest_lively_2')
|
||
|
||
# Event phases
|
||
return (
|
||
{
|
||
# Default song phase availability (0-30)
|
||
# The following songs are unlocked when the phase is at or above the number specified:
|
||
# For 23 and before, see usaneko/peace
|
||
# 24 - 1929, 1930
|
||
# 25 - 1964
|
||
# 26 - 1966, 1967
|
||
# 27 - 1975
|
||
# 28 - 1994
|
||
# 29 - 1995, 1996
|
||
# 30 - 1997
|
||
0: music_phase,
|
||
# Unknown event (0-4)
|
||
1: 4,
|
||
# Holiday Greeting (0-2)
|
||
2: holiday_greeting,
|
||
# Unknown event (0-4)
|
||
3: 4,
|
||
# Unknown event (0-1)
|
||
4: 1,
|
||
# Enable Net Taisen, including win/loss display on song select (0-1)
|
||
5: 1 if enable_net_taisen else 0,
|
||
# Enable NAVI-kun shunkyoku toujou, allows song 1608 to be unlocked (0-1)
|
||
6: 1,
|
||
# Unknown event (0-1)
|
||
7: 1,
|
||
# Unknown event (0-2)
|
||
8: 2,
|
||
# Daily Mission (0-2)
|
||
9: 2,
|
||
# NAVI-kun Song phase availability (0-30)
|
||
10: 30,
|
||
# Unknown event (0-1)
|
||
11: 1,
|
||
# Unknown event (0-2)
|
||
12: 2,
|
||
# Enable Pop'n Peace preview song (0-1)
|
||
13: 1,
|
||
# Stamp Card Rally (0-39)
|
||
14: 39,
|
||
# Unknown event (0-2)
|
||
15: 2,
|
||
# Unknown event (0-3)
|
||
16: 3,
|
||
# Unknown event (0-8)
|
||
17: 8,
|
||
# FLOOR INFECTION event (0-1)
|
||
18: 1,
|
||
# pop'n music × NOSTALGIA kyouenkai (0-1)
|
||
19: 1,
|
||
# Event archive event (0-13)
|
||
20: 13,
|
||
# Pop'n event archive song phase availability (0-20)
|
||
21: 20,
|
||
# バンめし♪ ふるさとグランプリunlocks (split into two rounds) (0-2)
|
||
22: 2,
|
||
# いちかのBEMANI投票選抜戦2019 unlocks (0-1)
|
||
23: 1,
|
||
# ダンキラ!!! × pop'n music unlocks (0-1)
|
||
24: 1,
|
||
# Kaimei riddles events starts here
|
||
# Kaimei! MN tanteisha event Phase (0-62)
|
||
# When active, the following songs are available for unlock
|
||
# 1: 1914
|
||
# 2: 195
|
||
# 3: 1915
|
||
# 4: 1916
|
||
# 5: 1896
|
||
# 6: 1908
|
||
# 7: 1931
|
||
# 8: 1924
|
||
# 9: 1925
|
||
# 10: 1894
|
||
# 11: 1926
|
||
# 12: 1927
|
||
# 13: 1928
|
||
# 14: 1932, 1933, 1934, 1935
|
||
# 15: 521
|
||
# 16: 1936
|
||
# 17: 1943
|
||
# 18: 1937
|
||
# 19: 1938
|
||
# 20: 1939
|
||
# 21: 1943
|
||
# 22: 1941
|
||
# 23: 1942
|
||
# 24: 323
|
||
# 25: 1946
|
||
# 26: 575
|
||
# 27: 1947
|
||
# 28: 1955
|
||
# 29: 1957
|
||
# 30: 1958
|
||
# 31: 1959
|
||
# 32: 1960
|
||
# 33: 1961
|
||
# 34: 1963
|
||
# 35: 1962
|
||
# 36: 1968
|
||
# 37: 1969
|
||
# 38: 1965
|
||
# 39: 1970
|
||
# 40: 1976
|
||
# 41: 1977
|
||
# 42: 1978
|
||
# 43: 1945
|
||
# 44: 1944
|
||
# 45: 1999
|
||
# 46: 2000
|
||
# 47: 2001
|
||
# 48: 2002
|
||
# 49: 2003
|
||
# 50: 2004
|
||
# 51: 2005
|
||
# 52: 267, 1998, 2006
|
||
# 53: 2011
|
||
# 54: 2007
|
||
# 55: 2008
|
||
# 56: 2009
|
||
# 57: 2010
|
||
# 58: 2016
|
||
# 59: 2012
|
||
# 60: 2018
|
||
# 61: 2013
|
||
# 62: 2015
|
||
25: mn_tanteisha_phase,
|
||
# Unknown event (0-3)
|
||
26: 3,
|
||
# peace soundtrack hatsubai kinen SP (0-2)
|
||
# When active, the following songs are available for unlock: 1971, 1972, 1973
|
||
27: peace_soundtrack,
|
||
# MZD no kimagure tanteisha joshu (0-2)
|
||
28: tanteisha_joshu,
|
||
# Shutchou! pop'n quest Lively (0-5)
|
||
# When active, the following songs are available for unlock
|
||
# 1: 1917, 1918
|
||
# 2: 1919, 1921
|
||
# 3: 1920, 1922, 1923
|
||
# 4: 1974
|
||
29: popn_quest_lively,
|
||
# Shutchou! pop'n quest Lively II (0-6)
|
||
# When active, the following songs are available for unlock
|
||
# 1: 1989, 1990, 1991
|
||
# 2: 1984, 1985, 1992
|
||
# 3: 1982, 1983, 1988
|
||
# 4: 1986, 1987, 1993
|
||
# 5: 2017
|
||
30: popn_quest_lively_2,
|
||
},
|
||
False,
|
||
)
|
||
|
||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||
root = super().format_profile(userid, profile)
|
||
|
||
account = root.child('account')
|
||
account.add_child(Node.s16('card_again_count', profile.get_int('point')))
|
||
account.add_child(Node.s16('sp_riddles_id', profile.get_int('step')))
|
||
|
||
# Kaimei riddles events
|
||
event2021 = Node.void('event2021')
|
||
root.add_child(event2021)
|
||
event2021.add_child(Node.u32('point', profile.get_int('point')))
|
||
event2021.add_child(Node.u8('step', profile.get_int('step')))
|
||
event2021.add_child(Node.u32_array('quest_point', profile.get_int_array('quest_point', 8, [0] * 8)))
|
||
event2021.add_child(Node.u8('step_nos', profile.get_int('step_nos')))
|
||
event2021.add_child(Node.u32_array('quest_point_nos', profile.get_int_array('quest_point_nos', 13, [0] * 13)))
|
||
|
||
riddles_data = Node.void('riddles_data')
|
||
root.add_child(riddles_data)
|
||
|
||
# Generate Short Riddles for MN tanteisha
|
||
randomRiddles: List[int] = []
|
||
for _ in range(3):
|
||
riddle = 0
|
||
while True:
|
||
riddle = math.floor(random.randrange(1, 21, 1))
|
||
try:
|
||
randomRiddles.index(riddle)
|
||
except ValueError:
|
||
break
|
||
|
||
randomRiddles.append(riddle)
|
||
|
||
sh_riddles = Node.void('sh_riddles')
|
||
riddles_data.add_child(sh_riddles)
|
||
sh_riddles.add_child(Node.u32('sh_riddles_id', riddle))
|
||
|
||
# Set up kaimei riddles achievements
|
||
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
||
for achievement in achievements:
|
||
if achievement.type == 'riddle':
|
||
kaimei_gauge = achievement.data.get_int('kaimei_gauge')
|
||
is_cleared = achievement.data.get_bool('is_cleared')
|
||
riddles_cleared = achievement.data.get_bool('riddles_cleared')
|
||
select_count = achievement.data.get_int('select_count')
|
||
other_count = achievement.data.get_int('other_count')
|
||
|
||
sp_riddles = Node.void('sp_riddles')
|
||
riddles_data.add_child(sp_riddles)
|
||
sp_riddles.add_child(Node.u16('kaimei_gauge', kaimei_gauge))
|
||
sp_riddles.add_child(Node.bool('is_cleared', is_cleared))
|
||
sp_riddles.add_child(Node.bool('riddles_cleared', riddles_cleared))
|
||
sp_riddles.add_child(Node.u8('select_count', select_count))
|
||
sp_riddles.add_child(Node.u32('other_count', other_count))
|
||
|
||
return root
|
||
|
||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||
newprofile = super().unformat_profile(userid, request, oldprofile)
|
||
|
||
account = request.child('account')
|
||
if account is not None:
|
||
newprofile.replace_int('card_again_count', account.child_value('card_again_count'))
|
||
newprofile.replace_int('sp_riddles_id', account.child_value('sp_riddles_id'))
|
||
|
||
# Kaimei riddles events
|
||
event2021 = request.child('event2021')
|
||
if event2021 is not None:
|
||
newprofile.replace_int('point', event2021.child_value('point'))
|
||
newprofile.replace_int('step', event2021.child_value('step'))
|
||
newprofile.replace_int_array('quest_point', 8, event2021.child_value('quest_point'))
|
||
newprofile.replace_int('step_nos', event2021.child_value('step_nos'))
|
||
newprofile.replace_int_array('quest_point_nos', 13, event2021.child_value('quest_point_nos'))
|
||
|
||
# Extract kaimei riddles achievements
|
||
for node in request.children:
|
||
if node.name == 'riddles_data':
|
||
riddle_id = 0
|
||
playedRiddle = request.child('account').child_value('sp_riddles_id')
|
||
for riddle in node.children:
|
||
kaimei_gauge = riddle.child_value('kaimei_gauge')
|
||
is_cleared = riddle.child_value('is_cleared')
|
||
riddles_cleared = riddle.child_value('riddles_cleared')
|
||
select_count = riddle.child_value('select_count')
|
||
other_count = riddle.child_value('other_count')
|
||
|
||
if (riddles_cleared or select_count >= 3):
|
||
select_count = 3
|
||
elif (playedRiddle == riddle_id):
|
||
select_count += 1
|
||
|
||
self.data.local.user.put_achievement(
|
||
self.game,
|
||
self.version,
|
||
userid,
|
||
riddle_id,
|
||
'riddle',
|
||
{
|
||
'kaimei_gauge': kaimei_gauge,
|
||
'is_cleared': is_cleared,
|
||
'riddles_cleared': riddles_cleared,
|
||
'select_count': select_count,
|
||
'other_count': other_count,
|
||
},
|
||
)
|
||
|
||
riddle_id += 1
|
||
|
||
return newprofile
|