From c0442f372117a5fa785e7657d91682d9a733417d Mon Sep 17 00:00:00 2001 From: Viv Date: Thu, 27 Jul 2023 15:02:19 -0400 Subject: [PATCH] Fix behavior for the `#LEVELHOLD` command (#54) Previously, I had translated the existing levelhold behavior in WHMHammer's tja parsing code, assuming that it would produce the correct behavior for fumen charts. However, that is not the case -- the behavior is completely wrong. So, this PR takes out all the old `flag_levelhold` behavior and instead properly sets the `branch_info` bytes whenever #LEVELHOLD is present on a branch. This PR also prepares `shoto9.tja` for further testing, but I'm leaving that for later... Fixes #52. --- README.md | 4 ++-- src/tja2fumen/converters.py | 15 ++++++++++++- src/tja2fumen/parsers.py | 10 +++------ src/tja2fumen/types.py | 44 +++++++++++++++++++------------------ testing/data/shoto9.tja | 4 +++- 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 6fd0987..bd781f8 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,8 @@ If there is an unsupported feature that you would like support for, please make | `#DELAY` | `✅` | `❌` | See [#27](https://github.com/Fluto/TakoTako/issues/27) | | `#BRANCHSTART`, `#BRANCHEND` | `✅` | `✅` | | | `#N`, `#E`, `#M` | `✅` | `✅` | | -| `#SECTION` | `⚠️` | `❌` | See [#52](https://github.com/vivaria/tja2fumen/issues/52), [#27](https://github.com/Fluto/TakoTako/issues/27) | -| `#LEVELHOLD` | `⚠️` | `❌` | See [#52](https://github.com/vivaria/tja2fumen/issues/52) | +| `#SECTION` | `⚠️` | `❌` | See [#53](https://github.com/vivaria/tja2fumen/issues/53), [#27](https://github.com/Fluto/TakoTako/issues/27) | +| `#LEVELHOLD` | `✅` | `❌` | | | `#BMSCROLL`, `#LYRIC`,
`#DIRECTION`, etc. | `⚪️` | `❌` | Other simulator-specific chart commands are currently ignored. | ## Reporting bugs diff --git a/src/tja2fumen/converters.py b/src/tja2fumen/converters.py index f949973..9ab40c5 100644 --- a/src/tja2fumen/converters.py +++ b/src/tja2fumen/converters.py @@ -52,6 +52,8 @@ def process_tja_commands(tja): measure_tja_processed.branch_start = data.value elif data.name == 'section': measure_tja_processed.section = data.value + elif data.name == 'levelhold': + measure_tja_processed.levelhold = True elif data.name == 'barline': current_barline = bool(int(data.value)) measure_tja_processed.barline = current_barline @@ -181,6 +183,7 @@ def convert_tja_to_fumen(tja): branch_points_total = 0 branch_points_measure = 0 current_drumroll = None + current_levelhold = False branch_conditions = [] course_balloons = tja.balloon.copy() @@ -226,10 +229,14 @@ def convert_tja_to_fumen(tja): measure_fumen.set_branch_info( branch_condition, branch_points_total, current_branch, first_branch_condition=(not branch_conditions), - has_section=bool(measure_tja.section) + has_section=bool(measure_tja.section), + has_levelhold=current_levelhold ) # Reset the points to prepare for the next `#BRANCHSTART p` branch_points_total = 0 + # Reset the levelhold value (so that future branch_conditions + # work normally) + current_levelhold = False # Keep track of the branch conditions (to later determine how # to set the header bytes for branches) branch_conditions.append(branch_condition) @@ -244,6 +251,12 @@ def convert_tja_to_fumen(tja): # calculation with notes "one measure before". branch_points_total += branch_points_measure + # LEVELHOLD essentially means "ignore the branch condition for + # the next `#BRANCHSTART` command", so we check this value after + # we've already processed the branch condition for this measure. + if measure_tja.levelhold: + current_levelhold = True + # Create notes based on TJA measure data branch_points_measure = 0 for idx_d, data in enumerate(measure_tja.data): diff --git a/src/tja2fumen/parsers.py b/src/tja2fumen/parsers.py index aabeb8a..f9a1be2 100644 --- a/src/tja2fumen/parsers.py +++ b/src/tja2fumen/parsers.py @@ -175,7 +175,6 @@ def parse_tja_course_data(course): has_branches = bool([d for d in course.data if d.startswith('#BRANCH')]) current_branch = 'all' if has_branches else 'normal' branch_condition = None - flag_levelhold = False # Process course lines idx_m = 0 @@ -210,7 +209,7 @@ def parse_tja_course_data(course): # 2. Parse measure commands that produce an "event" elif command in ['GOGOSTART', 'GOGOEND', 'BARLINEON', 'BARLINEOFF', 'DELAY', 'SCROLL', 'BPMCHANGE', 'MEASURE', - 'SECTION', 'BRANCHSTART']: + 'LEVELHOLD', 'SECTION', 'BRANCHSTART']: # Get position of the event for branch in (course.branches.keys() if current_branch == 'all' else [current_branch]): @@ -233,6 +232,8 @@ def parse_tja_course_data(course): current_event = TJAData('bpm', float(value), pos) elif command == 'MEASURE': current_event = TJAData('measure', value, pos) + elif command == 'LEVELHOLD': + current_event = TJAData('levelhold', None, pos) elif command == 'SECTION': # If #SECTION occurs before a #BRANCHSTART, then ensure that # it's present on every branch. Otherwise, #SECTION will only @@ -247,8 +248,6 @@ def parse_tja_course_data(course): current_event = TJAData('branch_start', branch_condition, pos) elif command == 'BRANCHSTART': - if flag_levelhold: - continue # Ensure that the #BRANCHSTART command is added to all branches current_branch = 'all' branch_condition = value.split(',') @@ -272,9 +271,6 @@ def parse_tja_course_data(course): else: if command == 'START' or command == 'END': current_branch = 'all' if has_branches else 'normal' - flag_levelhold = False - elif command == 'LEVELHOLD': - flag_levelhold = True elif command == 'N': current_branch = 'normal' idx_m = idx_m_branchstart diff --git a/src/tja2fumen/types.py b/src/tja2fumen/types.py index 616f648..4d1e4bc 100644 --- a/src/tja2fumen/types.py +++ b/src/tja2fumen/types.py @@ -70,8 +70,8 @@ class TJAMeasureProcessed(DefaultObject): the number of `TJAMeasure` objects for a given song.)) """ def __init__(self, bpm, scroll, gogo, barline, time_sig, subdivisions, - pos_start=0, pos_end=0, delay=0, section=None, - branch_start=None, data=None): + pos_start=0, pos_end=0, delay=0, levelhold=False, + section=None, branch_start=None, data=None): self.bpm = bpm self.scroll = scroll self.gogo = gogo @@ -82,6 +82,7 @@ class TJAMeasureProcessed(DefaultObject): self.pos_end = pos_end self.delay = delay self.section = section + self.levelhold = levelhold self.branch_start = branch_start self.data = [] if data is None else data @@ -160,14 +161,25 @@ class FumenMeasure(DefaultObject): self.offset_end = self.offset_start + self.duration def set_branch_info(self, branch_condition, branch_points_total, - current_branch, first_branch_condition, has_section): + current_branch, first_branch_condition, + has_section, has_levelhold): """Compute the values that represent branching/diverge conditions.""" + # If levelhold is set, force the branch to stay the same, + # regardless of the value of the current branch condition. + if has_levelhold: + if current_branch == 'normal': + self.branch_info[0:2] = [999, 999] # Forces fail/fail + elif current_branch == 'professional': + self.branch_info[2:4] = [0, 999] # Forces pass/fail + elif current_branch == 'master': + self.branch_info[4:6] = [0, 0] # Forces pass/pass + # Handle branch conditions for percentage accuracy # There are three cases for interpreting #BRANCHSTART p: # 1. Percentage is between 0% and 100% # 2. Percentage is above 100% (guaranteed level down) # 3. Percentage is 0% (guaranteed level up) - if branch_condition[0] == 'p': + elif branch_condition[0] == 'p': vals = [] for percent in branch_condition[1:]: if 0 < percent <= 1: @@ -190,24 +202,14 @@ class FumenMeasure(DefaultObject): # has a #SECTION command to reset the accuracy. # 3. It's not the first branching condition, and it # doesn't have a #SECTION command. - # For the first two cases, the branching conditions are the - # same no matter what branch you're currently on, so we just - # use the values as-is: [c1, c2, c1, c2, c1, c2] - # But, for the third case, since there is no #SECTION, the - # accuracy is not reset. This results in the following - # condition: [999, 999, c1, c2, c2, c2] - # - Normal can't advance to professional/master - # - Professional can stay, or advance to master. - # - Master can only stay in master. + # TODO: Determine the behavior for these 3 conditions elif branch_condition[0] == 'r': - if first_branch_condition or has_section: - self.branch_info = branch_condition[1:] * 3 - else: - self.branch_info = ( - [999, 999] + - [branch_condition[1]] + - [branch_condition[2]] * 3 - ) + if current_branch == 'normal': + self.branch_info[0:2] = branch_condition[1:] + elif current_branch == 'professional': + self.branch_info[2:4] = branch_condition[1:] + elif current_branch == 'master': + self.branch_info[4:6] = branch_condition[1:] class FumenBranch(DefaultObject): diff --git a/testing/data/shoto9.tja b/testing/data/shoto9.tja index de230df..95a5221 100644 --- a/testing/data/shoto9.tja +++ b/testing/data/shoto9.tja @@ -32,6 +32,7 @@ SCOREDIFF:95 2021002020102020, #BRANCHSTART r,5,6 #N +#LEVELHOLD 100000000100000000200000500000000008000000100200, 1000202210201120, #E @@ -40,12 +41,13 @@ SCOREDIFF:95 #M 100000000100000000200000500000000008000000100200, 1000202210201120, +#SECTION #BRANCHSTART r,7,8 #BRANCHEND 500000000008000000200200100000200200200200200200, 1010202210201020, +#SECTION #BRANCHSTART r,4,5 - #N 1001001070080000, 100