From 9a9c7462a85013a7796369d3ebd1390d52d973b2 Mon Sep 17 00:00:00 2001 From: 544146 Date: Sat, 23 Sep 2023 16:47:47 +0100 Subject: [PATCH] Add todo comments and feet saber progress --- fs2dd.py | 69 +++++++++++++++++++++++++++++------ model/dancerush.py | 2 +- model/feetsaber.py | 90 ++++++++++++++++++++++++++++++++++++++++++---- util.py | 4 +-- 4 files changed, 145 insertions(+), 20 deletions(-) diff --git a/fs2dd.py b/fs2dd.py index cd71356c..dadce33d 100644 --- a/fs2dd.py +++ b/fs2dd.py @@ -5,36 +5,80 @@ import os import shutil from datetime import datetime +from model.dancedash import DD_LEFT from model.dancedash import DDBeatMap from model.dancedash import DDBeatMapData from model.dancedash import DDBeatMapInfoFile from model.dancedash import DDLineNode from model.dancedash import DDRoadBlockNode from model.dancedash import DDSphereNode +from model.dancedash import ORDER_COUNT_PER_BEAT +from model.dancedash import X_Y from model.feetsaber import FSBeatMapFile from model.feetsaber import FSInfoDat from util import convert_egg_to_ogg_and_get_length from util import get_first_image_file_in_folder -from util import random_10_digit_int +from util import random_9_digit_int from util import yyyymmdd_to_ticks -def map_sphere_nodes(fs_beat_map: FSBeatMapFile) -> list[DDSphereNode]: +def map_sphere_nodes(fs_beat_map: FSBeatMapFile, bps: int, total_time_seconds: float) -> list[DDSphereNode]: spheres = [] ... return spheres -def map_line_nodes(fs_beat_map: FSBeatMapFile) -> list[DDLineNode]: +def map_line_nodes(fs_beat_map: FSBeatMapFile, bps: int, total_time_seconds: float) -> list[DDLineNode]: lines = [] - ... + + line_obstacles = [ + o for o in fs_beat_map.obstacles if o.customData.is_fs_slider + ] + left_obstacles = [ + o for o in line_obstacles if o.customData.dd_note_type == DD_LEFT + ] + right_obstacles = [ # noqa + o for o in line_obstacles if o.customData.dd_note_type != DD_LEFT + ] # noqa + line_group_id = 1 + + index_in_line = 0 + for idx, obstacle in enumerate(left_obstacles): + + for multiplier in (0, obstacle.duration): # line start and line end + index_in_line += 1 + lines.append( + DDLineNode( + lineGroupId=line_group_id, + indexInLine=index_in_line, + noteOrder=round( + bps * (obstacle.time + multiplier) * + ORDER_COUNT_PER_BEAT, + ), + time=fs_beat_map.customData.time / total_time_seconds, + position=X_Y(x=obstacle.customData.dd_x, y=0), + noteType=obstacle.customData.dd_note_type, + ), + ) + + # todo(aggg figure this out i give up for today) + + is_last_obstacle = idx == len(left_obstacles) - 1 + next_obstacle = left_obstacles[ + idx + + 1 + ] if not is_last_obstacle else None + if next_obstacle and not next_obstacle.is_part_of_last_obstacle(obstacle): + line_group_id += 1 + index_in_line = 0 + return lines -def map_down_and_jump_notes(fs_beat_map: FSBeatMapFile) -> list[DDRoadBlockNode]: - road_blocks = [] +def map_down_and_jump_notes(fs_beat_map: FSBeatMapFile, bps: int, total_time_seconds: float) -> list[DDRoadBlockNode]: + spheres = [] ... - return road_blocks + return spheres def create_dd_tracks_from_fs(fs_map_dir: str) -> DDBeatMapInfoFile: @@ -69,9 +113,11 @@ def create_dd_tracks_from_fs(fs_map_dir: str) -> DDBeatMapInfoFile: for difficulty_set in difficulty_set.difficultyBeatmaps: beat_map: FSBeatMapFile = difficulty_set.get_beatmap(fs_map_dir) - sphere_notes = map_sphere_nodes(beat_map) - line_notes = map_line_nodes(beat_map) - road_block_notes = map_down_and_jump_notes(beat_map) + sphere_notes = map_sphere_nodes(beat_map, fs_info.bps, song_length) + line_notes = map_line_nodes(beat_map, fs_info.bps, song_length) + road_block_notes = map_down_and_jump_notes( + beat_map, fs_info.bps, song_length, + ) total_note_count = len(sphere_notes + line_notes + road_block_notes) # noqa dd_beat_map = DDBeatMap( @@ -106,7 +152,7 @@ def create_dd_tracks_from_fs(fs_map_dir: str) -> DDBeatMapInfoFile: dd_beat_map_info = DDBeatMapInfoFile( CreateTicks=create_ticks, CreateTime=str(create_ticks), - BeatMapId=random_10_digit_int(), + BeatMapId=random_9_digit_int(), SongName=fs_info.songName, SongLength=str(song_length), SongAuthorName=fs_info.songAuthorName, @@ -114,6 +160,7 @@ def create_dd_tracks_from_fs(fs_map_dir: str) -> DDBeatMapInfoFile: Bpm=str(fs_info.beatsPerMinute), SongPath=os.path.basename(song_path), CoverPath=os.path.basename(song_cover_path or '') or None, + SongPreviewSection=fs_info.previewStartTime, **difficulties, ) diff --git a/model/dancerush.py b/model/dancerush.py index 71fa060c..923a952a 100644 --- a/model/dancerush.py +++ b/model/dancerush.py @@ -543,7 +543,7 @@ class DRSTrack: def from_xml_dict(cls, data: dict): data = data['data'] - # not really representing the data properly, but we need this for timings on 9 songs (i think) + # not really representing the data properly, but we need this for timings on ver 9 songs (i think) clip = None if 'rec_data' in data and data['rec_data'] and type(data['rec_data']['clip']) is not list: clip = DRSClip.from_xml_dict(data['rec_data']['clip']) diff --git a/model/feetsaber.py b/model/feetsaber.py index 11e3a194..9c366d18 100644 --- a/model/feetsaber.py +++ b/model/feetsaber.py @@ -3,6 +3,10 @@ from __future__ import annotations import json import os from dataclasses import dataclass +from dataclasses import field + +from model.dancedash import DD_LINE_LEFT +from model.dancedash import DD_LINE_RIGHT @dataclass @@ -79,6 +83,10 @@ class FSInfoDat: customData: FSDatCustomData difficultyBeatmapSets: list[FSDifficultyBeatMapSets] + @property + def bps(self): + return self.beatsPerMinute / 60 + @classmethod def from_json_dict(cls, json_dict: dict) -> FSInfoDat: custom_data_raw = json_dict['_customData'] @@ -195,15 +203,72 @@ class FSBeatMapFileNote: customData: FSBeatMapFileNoteCustomData +FS_RIGHT_COLOUR = (0.0, 1.0, 3.0, 1.0) +FS_LEFT_COLOUR = (2.0, 1.5, 0.0, 1.0) + +FS_LONG_NOTE_TO_DD_NOTE_TYPE = { + FS_RIGHT_COLOUR: DD_LINE_RIGHT, + FS_LEFT_COLOUR: DD_LINE_LEFT, +} + + @dataclass class FSBeatMapFileObstacleCustomData: interactable: bool fake: bool position: tuple[float, float] - scale: tuple[float, float] + scale: tuple color: tuple[float, float, float, float] localRotation: tuple[float, float, float] | None = None + @property + def height(self): + return self.scale[1] + + @property + def y(self): + _, y = self.position + return y + + @property + def x(self): + x, _ = self.position + return x + + @property + def is_fs(self): + return all( + [ + self.height == 0.1, + self.y == -0.25, + self.fake, + ], + ) + + @property + def is_fs_slider(self) -> bool: + return self.scale[0] == 1.0 and self.is_fs + + @property + def is_tail(self) -> bool: + return self.scale[0] == 1.5 and self.is_fs + + @property + def dd_x(self): + if not self.is_fs: + raise ValueError('This is not a FS note') + + if self.x < -1.5 or self.x > 1.5: + raise ValueError('Input should be between -1.5 and 1.5') + + normalized = (self.x + 1.5) / 3.0 + mapped_value = round(normalized * 8 + 1) + return int(mapped_value) + + @property + def dd_note_type(self): + return FS_LONG_NOTE_TO_DD_NOTE_TYPE[self.color] + @dataclass class FSBeatMapFileObstacle: @@ -214,15 +279,24 @@ class FSBeatMapFileObstacle: width: int customData: FSBeatMapFileObstacleCustomData + def is_part_of_last_obstacle(self, last_obstacle: FSBeatMapFileObstacle) -> bool: + if self.time < last_obstacle.time: + return False + + if self.time > last_obstacle.time + last_obstacle.duration: + return False + + return True + @dataclass class FSBeatMapFile: version: str customData: FSBeatMapFileCustomData - events: list - notes: list - obstacles: list - waypoints: list + notes: list[FSBeatMapFileNote] + obstacles: list[FSBeatMapFileObstacle] + events: list[FSBeatMapFileEvent] = field(default_factory=list) + waypoints: list = field(default_factory=list) @classmethod def from_json_dict(cls, json_dict: dict) -> FSBeatMapFile: @@ -273,7 +347,11 @@ class FSBeatMapFile: '_customData' ] else None, scale=tuple(obstacle['_customData']['_scale']), - color=tuple(obstacle['_customData']['_color']), + # type: ignore + color=tuple( + float(c) + for c in obstacle['_customData']['_color'] + ), ), ) for obstacle in json_dict['_obstacles'] ] diff --git a/util.py b/util.py index 4f6f671f..767226d1 100644 --- a/util.py +++ b/util.py @@ -80,8 +80,8 @@ def zipdir(path, ziph, archiveroot): ziph.write(actual_file_path, archive_file_path) -def random_10_digit_int(): - return random.randint(10 ** 9, 10 ** 10 - 1) +def random_9_digit_int(): + return random.randint(10**8, 10**9 - 1) def convert_egg_to_ogg_and_get_length(egg_path: str) -> tuple[str, float]: