515 lines
20 KiB
Python
515 lines
20 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: 2019 - 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("card_again_count"))
|
||
)
|
||
account.add_child(Node.s16("sp_riddles_id", profile.get_int("sp_riddles_id")))
|
||
|
||
# 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
|