1
0
mirror of synced 2025-02-17 19:19:19 +01:00

Initial stab at getting Town mode working in Tune Street.

This commit is contained in:
Jennifer Taylor 2021-08-31 21:42:49 +00:00
parent 9b731a6818
commit d21431c3b1

View File

@ -19,6 +19,7 @@ class PopnMusicTuneStreet(PopnMusicBase):
# Play modes, as reported by profile save from the game
GAME_PLAY_MODE_CHALLENGE = 3
GAME_PLAY_MODE_CHO_CHALLENGE = 4
GAME_PLAY_MODE_TOWN_CHO_CHALLENGE = 15
# Play flags, as saved into/loaded from the DB
GAME_PLAY_FLAG_FAILED = 0
@ -113,7 +114,6 @@ class PopnMusicTuneStreet(PopnMusicBase):
],
}
def __format_flags_for_score(self, score: Score) -> int:
# Format song flags (cleared/not, combo flags)
playedflag = {
@ -182,6 +182,9 @@ class PopnMusicTuneStreet(PopnMusicBase):
3: 1,
4: 4,
5: 2,
13: 5,
14: 5,
15: 5,
}[profile.get_int('play_mode')]
# Copy miscelaneous values
@ -262,22 +265,60 @@ class PopnMusicTuneStreet(PopnMusicBase):
# Town purchases, including BGM/announcer changes and such.
# The town customization area will show up if the player owns
# one or more customization in any of the following four
# categories.
town = [0] * 12
# purchase locations.
# I think that 4-7 are song unlock flags.
# - 8 appears to be purchased pop-kuns.
# - 9 appears to be purchased themes.
# - 10 appears to be purchased BGMs.
# - 11 appears to be purchased sound effects.
binary_town = [0] * 141
town = profile.get_dict('town')
# Position 8 appears to be purchased pop-kuns.
town[8] = 0
# Position 8 appears to be purchased themes.
town[9] = 0
# Position 10 appears to be purchased BGMs.
town[10] = 0
# Position 11 appears to be purchased sound effects.
town[11] = 0
# Last play flag, so the selection for 5/9/9+cool sticks.
binary_town[140] = town.get_int('play_type')
# Fill in basic town points, tracked here and returned in basic profile for some reason.
binary_town[0] = town.get_int('points') & 0xFF
binary_town[1] = (town.get_int('points') >> 8) & 0xFF
binary_town[2] = (town.get_int('points') >> 16) & 0xFF
binary_town[3] = (town.get_int('points') >> 24) & 0xFF
# Fill in purchase flags (this is for stuff like BGMs, SEs, Pop-kun customizations, etc).
bought_flg = town.get_int_array('bought_flg', 3)
for flg, off in enumerate([4, 8, 12]):
binary_town[off + 0] = bought_flg[flg] & 0xFF
binary_town[off + 1] = (bought_flg[flg] >> 8) & 0xFF
binary_town[off + 2] = (bought_flg[flg] >> 16) & 0xFF
binary_town[off + 3] = (bought_flg[flg] >> 24) & 0xFF
# Fill in build flags (presumably for what parcels of land have been bought and built on).
build_flg = town.get_int_array('build_flg', 8)
for flg, off in enumerate([16, 20, 24, 28, 32, 36, 40, 44]):
binary_town[off + 0] = build_flg[flg] & 0xFF
binary_town[off + 1] = (build_flg[flg] >> 8) & 0xFF
binary_town[off + 2] = (build_flg[flg] >> 16) & 0xFF
binary_town[off + 3] = (build_flg[flg] >> 24) & 0xFF
# Fill in character flags (presumably for character location, orientation, stats, etc).
chara_flg = town.get_int_array('chara_flg', 19)
for flg, off in enumerate([48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120]):
binary_town[off + 0] = chara_flg[flg] & 0xFF
binary_town[off + 1] = (chara_flg[flg] >> 8) & 0xFF
binary_town[off + 2] = (chara_flg[flg] >> 16) & 0xFF
binary_town[off + 3] = (chara_flg[flg] >> 24) & 0xFF
# Fill in miscellaneous event flags.
event_flg = town.get_int_array('event_flg', 4)
for flg, off in enumerate([124, 128, 132, 136]):
binary_town[off + 0] = event_flg[flg] & 0xFF
binary_town[off + 1] = (event_flg[flg] >> 8) & 0xFF
binary_town[off + 2] = (event_flg[flg] >> 16) & 0xFF
binary_town[off + 3] = (event_flg[flg] >> 24) & 0xFF
# Construct final profile
root.add_child(Node.binary('b', bytes(binary_profile)))
root.add_child(Node.binary('hiscore', bytes(hiscore_array)))
root.add_child(Node.binary('town', bytes(town)))
root.add_child(Node.binary('town', bytes(binary_town)))
return root
@ -356,7 +397,7 @@ class PopnMusicTuneStreet(PopnMusicBase):
continue
# Arrange order to be compatible with future mixes
if playmode == self.GAME_PLAY_MODE_CHO_CHALLENGE:
if playmode in {self.GAME_PLAY_MODE_CHO_CHALLENGE, self.GAME_PLAY_MODE_TOWN_CHO_CHALLENGE}:
if chart in [
self.GAME_CHART_TYPE_5_BUTTON,
self.GAME_CHART_TYPE_ENJOY_5_BUTTON,
@ -413,6 +454,34 @@ class PopnMusicTuneStreet(PopnMusicBase):
}[flags]
self.update_score(userid, songid, chart, points, medal)
# Update town mode data.
town = newprofile.get_dict('town')
# Basic stuff that's in the base node for no reason?
if 'tp' in request.attributes:
town.replace_int('points', int(request.attribute('tp')))
# Stuff that is in the town node
townnode = request.child('town')
if townnode is not None:
if 'play_type' in townnode.attributes:
town.replace_int('play_type', int(townnode.attribute('play_type')))
if 'base' in townnode.attributes:
town.replace_int_array('base', 4, [int(x) for x in townnode.attribute('base').split(',')])
if 'bought_flg' in townnode.attributes:
town.replace_int_array('bought_flg', 3, [int(x) for x in townnode.attribute('bought_flg').split(',')])
if 'build_flg' in townnode.attributes:
town.replace_int_array('build_flg', 8, [int(x) for x in townnode.attribute('build_flg').split(',')])
if 'chara_flg' in townnode.attributes:
town.replace_int_array('chara_flg', 19, [int(x) for x in townnode.attribute('chara_flg').split(',')])
if 'event_flg' in townnode.attributes:
town.replace_int_array('event_flg', 4, [int(x) for x in townnode.attribute('event_flg').split(',')])
for bid in range(8):
if f'building_{bid}' in townnode.attributes:
town.replace_int_array(f'building_{bid}', 8, [int(x) for x in townnode.attribute(f'building_{bid}').split(',')])
newprofile.replace_dict('town', town)
return newprofile
def handle_game_get_request(self, request: Node) -> Optional[Node]:
@ -460,6 +529,46 @@ class PopnMusicTuneStreet(PopnMusicBase):
root.set_attribute('status', str(Status.NO_PROFILE))
return root
def handle_playerdata_town_request(self, request: Node) -> Optional[Node]:
refid = request.attribute('ref_id')
root = Node.void('playerdata')
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
if userid is None:
return root
profile = self.get_profile(userid)
if profile is None:
return root
town = profile.get_dict('town')
residence = Node.void('residence')
root.add_child(residence)
residence.set_attribute('id', str(town.get_int('residence')))
# It appears there can be up to 9 map nodes, not sure why. I'm only returning the
# first one. Perhaps if there's multiple towns, the residence ID lets you choose
# between them? Maybe it has to do with friends towns?
mapdata = [0] * 180
# Map over progress for base and buildings. Positions 173-176 are for base flags.
base = town.get_int_array('base', 4)
for i in range(4):
mapdata[173 + i] = base[i]
# Positions 42-105 are for building flags.
for bid, start in enumerate([42, 50, 58, 66, 74, 82, 90, 98]):
building = town.get_int_array(f'building_{bid}', 8)
for i in range(8):
mapdata[start + i] = building[i]
mapnode = Node.binary('map', bytes(mapdata))
root.add_child(mapnode)
mapnode.set_attribute('residence', '0')
return root
def handle_playerdata_new_request(self, request: Node) -> Optional[Node]:
refid = request.attribute('ref_id')
name = request.attribute('name')