diff --git a/FileLoader.gd b/FileLoader.gd index 68952e2..f07a637 100644 --- a/FileLoader.gd +++ b/FileLoader.gd @@ -108,7 +108,7 @@ class SRT: var notes = [] var beats_per_measure := 4 var length = file.get_len() - var slide_idxs = {} + var slide_ids = {} while (file.get_position() < (length-2)): var noteline = file.get_csv_line() var time_hit := (float(noteline[0]) + (float(noteline[1]))-1.0) * beats_per_measure @@ -125,16 +125,15 @@ class SRT: notes.push_back(Note.NoteTap.new(time_hit, column, true)) ID_SLIDE_END: # id2 is slide ID - if id2 in slide_idxs: - notes[slide_idxs[id2]].column_release = column - notes[slide_idxs[id2]].update_slide_variables() + if id2 in slide_ids: + slide_ids[id2].column_release = column + slide_ids[id2].update_slide_variables() _: if id2 == 0: notes.push_back(Note.NoteTap.new(time_hit, column)) else: # id2 is slide ID, id3 is slide pattern # In order to properly declare the slide, we need the paired endcap which may not be the next note - slide_idxs[id2] = len(notes) var slide_type = Note.SlideType.CHORD match id3: ID3_SLIDE_CHORD: @@ -145,7 +144,12 @@ class SRT: slide_type = Note.SlideType.ARC_ACW _: print("Unknown slide type: ", id3) - notes.push_back(Note.NoteSlide.new(time_hit, column, duration, -1, slide_type)) + var note = Note.NoteStar.new(time_hit, column) + note.duration = duration + notes.push_back(note) + var slide = Note.NoteSlide.new(time_hit, column, duration, -1, slide_type) + notes.push_back(slide) + slide_ids[id2] = slide return notes @@ -206,6 +210,11 @@ class RGT: static func parse_rgts(lines): var notes = [] var slide_ids = {} + var slide_stars = {} # Multiple stars might link to one star. We only care about linking for the spin speed. + var last_star = [] + for i in Rules.COLS: + last_star.append(null) + for line in lines: if len(line) < 4: # shortest legal line would be like '1:1t' continue @@ -228,19 +237,41 @@ class RGT: var duration = float(n) note_hits.append(Note.NoteHold.new(time, column, duration)) 's': # slide star + var star = Note.NoteStar.new(time, column) + note_hits.append(star) + last_star[column] = star var slide_type = n[0] # numeric digit, left as str just in case var slide_id = int(n.substr(1)) -# var note = Note.NoteSlide.new(time, column) -# if slide_id > 0: -# slide_ids[slide_id] = note + if slide_id > 0: + slide_stars[slide_id] = star + var slide = Note.NoteSlide.new(time, column) + slide_ids[slide_id] = slide + note_nonhits.append(slide) 'e': # slide end var slide_type = n[0] # numeric digit, left as str just in case var slide_id = int(n.substr(1)) + if slide_id in slide_ids: # Classic slide end + slide_ids[slide_id].time_release = time + if slide_id in slide_stars: + slide_stars[slide_id].duration = slide_ids[slide_id].duration # Should probably recalc in case start time is different but w/e + slide_ids[slide_id].column_release = column + slide_ids[slide_id].slide_type = SLIDE_TYPES[slide_type] + slide_ids[slide_id].update_slide_variables() + else: # Naked slide start + if last_star[column] != null: + slide_stars[slide_id] = last_star[column] + else: + print_debug('Naked slide with no prior star in column!') + var note = Note.NoteSlide.new(time, column) + slide_ids[slide_id] = note + note_nonhits.append(note) 'x': # not sure pass if len(note_hits) > 1: - pass # Set multihit on each one + for note in note_hits: # Set multihit on each one + note.double_hit = true + notes += note_hits + note_nonhits return notes diff --git a/GameTheme.gd b/GameTheme.gd index b88d220..bda64bf 100644 --- a/GameTheme.gd +++ b/GameTheme.gd @@ -19,6 +19,21 @@ var judge_text_size2 := 0.5*judge_text_size/cos(JUDGE_TEXT_ANG2) var judge_text_duration := 2.0 + +# UV vertex arrays for our sprites +# tap/star/arrow are 4-vertex 2-triangle simple squares +# hold is 8-vertex 6-triangle to enable stretching in the middle +const UV_ARRAY_TAP := PoolVector2Array([Vector2(0, 0.5), Vector2(0.5, 0.5), Vector2(0, 1), Vector2(0.5, 1)]) +const UV_ARRAY_HOLD := PoolVector2Array([ + Vector2(0.5, 0.5), Vector2(1, 0.5), Vector2(0.5, 0.75), Vector2(1, 0.75), + Vector2(0.5, 0.75), Vector2(1, 0.75), Vector2(0.5, 1), Vector2(1, 1) + ]) +const UV_ARRAY_STAR := PoolVector2Array([Vector2(0.5, 0), Vector2(1, 0), Vector2(0.5, 0.5), Vector2(1, 0.5)]) +const UV_ARRAY_ARROW := PoolVector2Array([Vector2(0, 0), Vector2(0.5, 0), Vector2(0, 0.5), Vector2(0.5, 0.5)]) +# Slide trail arrow. Single tri. +const UV_ARRAY_SLIDE_ARROW := PoolVector2Array([Vector2(0, 0), Vector2(1, 0), Vector2(0, 1)]) +const UV_ARRAY_SLIDE_ARROW2 := PoolVector2Array([Vector2(1, 1), Vector2(0, 1), Vector2(1, 0)]) + # Color definitions const COLOR_TAP := Color(1, 0.15, 0.15, 1) const COLOR_TAP2 := Color(0.75, 0.5, 0, 1) # High-score taps ("breaks" in maimai) @@ -71,6 +86,8 @@ signal screen_filter_changed() var receptor_color := Color.blue var bezel_color := Color.black if not Engine.editor_hint else Color.red +var slide_trail_alpha := 0.88 + var RADIAL_COL_ANGLES := PoolRealArray() # ideally const var RADIAL_UNIT_VECTORS := PoolVector2Array() # ideally const @@ -101,3 +118,11 @@ func color_array_tap(alpha: float, double:=false) -> PoolColorArray: var col := COLOR_DOUBLE if double else COLOR_TAP var color = Color(col.r, col.g, col.b, alpha) return PoolColorArray([color, color, color, color]) + +func color_array_star(alpha: float, double:=false) -> PoolColorArray: + if alpha >= 1.0: + return COLOR_ARRAY_DOUBLE_4 if double else COLOR_ARRAY_STAR + else: + var col := COLOR_DOUBLE if double else COLOR_STAR + var color = Color(col.r, col.g, col.b, alpha) + return PoolColorArray([color, color, color, color]) diff --git a/InputHandler.gd b/InputHandler.gd index f47f02a..6f4ee06 100644 --- a/InputHandler.gd +++ b/InputHandler.gd @@ -30,7 +30,8 @@ func _init(): touchbuttons_pressed[i] = 0 func _ready(): - set_process_unhandled_input(true) # process user input + Input.set_use_accumulated_input(false) # Gotta go fast + set_process_unhandled_input(true) # process user input set_fingers(0) # connect("button_pressed", self, "print_pressed") $"/root".connect("size_changed", self, "resize") diff --git a/Menu.gd b/Menu.gd index 75cb109..e293d65 100644 --- a/Menu.gd +++ b/Menu.gd @@ -219,9 +219,10 @@ func _draw_chart_select(center: Vector2) -> Array: # TODO: This is relatively expensive so we probably want to calculate this stuff once instead of every frame var all_notes = FileLoader.SRT.load_file(song_defs[song_key].directory + "/" + song_defs[song_key].chart_filelist[selected_difficulty]) - var note_counts = {Note.NOTE_TAP: 0, Note.NOTE_HOLD: 0, Note.NOTE_SLIDE: 0} + var note_counts = {Note.NOTE_TAP: 0, Note.NOTE_HOLD: 0, Note.NOTE_STAR: 0} for note in all_notes: - note_counts[note.type] += 1 + if note.type in note_counts: + note_counts[note.type] += 1 var notestrs = ["Taps:", "Holds:", "Slides:"] var notetypes = [0, 1, 2] diff --git a/Note.gd b/Note.gd index 76a5012..4bdf225 100644 --- a/Note.gd +++ b/Note.gd @@ -3,10 +3,15 @@ extends Node #class_name Note -enum {NOTE_TAP, NOTE_HOLD, NOTE_SLIDE, NOTE_ARROW, NOTE_TOUCH, NOTE_TOUCH_HOLD, NOTE_ROLL} +enum {NOTE_TAP, NOTE_HOLD, NOTE_STAR=2, NOTE_SLIDE=-2, NOTE_TOUCH=3, NOTE_TOUCH_HOLD=4, NOTE_ARROW, NOTE_ROLL} enum SlideType {CHORD, ARC_CW, ARC_ACW} const DEATH_DELAY := 1.0 # This is touchy with the judgement windows and variable bpm. -const RELEASE_SCORE_TYPES := [NOTE_HOLD, NOTE_SLIDE, NOTE_TOUCH_HOLD, NOTE_ROLL] +const RELEASE_SCORE_TYPES := { + NOTE_HOLD: -NOTE_HOLD, + NOTE_SLIDE: NOTE_SLIDE, + NOTE_TOUCH_HOLD: -NOTE_TOUCH_HOLD, + NOTE_ROLL: -NOTE_ROLL +} class NoteBase: var time_hit: float setget set_time_hit @@ -21,14 +26,27 @@ class NoteBase: time_hit = value time_death = time_hit + DEATH_DELAY -class NoteTap extends NoteBase: - var type := NOTE_TAP +class NoteHittableBase extends NoteBase: + const hittable := true + +class NoteTapBase extends NoteHittableBase: func _init(time_hit: float, column: int, is_break:=false): self.time_hit = time_hit self.column = column self.is_break = is_break -class NoteHoldBase extends NoteBase: +class NoteTap extends NoteTapBase: + var type := NOTE_TAP + func _init(time_hit: float, column: int, is_break:=false).(time_hit, column, is_break): + pass + +class NoteStar extends NoteTapBase: # Fancy charts have naked slides which necessitates separation of Star and Slide :( + var type := NOTE_STAR + var duration := 1.0 # This is required for the spin speed + func _init(time_hit: float, column: int, is_break:=false).(time_hit, column, is_break): + pass + +class NoteHoldBase extends NoteHittableBase: var time_release: float setget set_time_release var time_released := INF var duration: float setget set_duration @@ -64,7 +82,8 @@ class NoteRoll extends NoteHoldBase: func _init(time_hit: float, column: int, duration: float).(time_hit, column, duration): pass -class NoteSlide extends NoteBase: +class NoteSlide extends NoteBase: # Fancy charts have naked slides which necessitates separation of Star and Slide :( + const hittable := false var type := NOTE_SLIDE var time_release: float setget set_time_release var duration: float setget set_duration @@ -75,8 +94,8 @@ class NoteSlide extends NoteBase: var missed_slide := false var values: Dictionary - func _init(time_hit: float, column: int, duration: float, column_release: int, slide_type: int): - self.time_hit = time_hit + func _init(time_hit: float, column: int, duration:=0.0, column_release:=0, slide_type:=0): + self.time_hit = time_hit # The hit doesn't actually count for anything self.column = column self.duration = duration self.time_release = time_hit + duration @@ -164,17 +183,18 @@ static func make_touch_hold(time_hit: float, duration: float, location: Vector2) var time_release := time_hit + duration return {type=NOTE_TOUCH_HOLD, time_hit=time_hit, time_release=time_release, time_death=time_release+DEATH_DELAY, location=location, double_hit=false} -static func process_note_list(note_array: Array): +static func process_note_list(note_array: Array, check_doubles:=true): # Preprocess double hits, assign Slide IDs # If this were performance-critical, we'd single iterate it # It's not though, so we lay it out simply var slide_id := 0 if len(note_array): # Doubles - for i in len(note_array)-1: - if note_array[i].time_hit == note_array[i+1].time_hit: - note_array[i].double_hit = true - note_array[i+1].double_hit = true + if check_doubles: + for i in len(note_array)-1: + if note_array[i].time_hit == note_array[i+1].time_hit: + note_array[i].double_hit = true + note_array[i+1].double_hit = true # Slides for i in len(note_array): if note_array[i].type == NOTE_SLIDE: diff --git a/NoteHandler.gd b/NoteHandler.gd index 100022c..a2d353e 100644 --- a/NoteHandler.gd +++ b/NoteHandler.gd @@ -5,20 +5,20 @@ var screen_height := 1080 # This script will draw all note events. signal finished_song(song_key, score_data) var running := false -var song_key = "" +var song_key = '' -var tex := preload("res://assets/spritesheet-4k.png") -var tex_judgement_text := preload("res://assets/text-4k.png") -var tex_slide_arrow := preload("res://assets/slide-arrow-4k.png") -var slide_trail_shadermaterial := preload("res://shaders/slidetrail.tres") +var tex := preload('res://assets/spritesheet-4k.png') +var tex_judgement_text := preload('res://assets/text-4k.png') +var tex_slide_arrow := preload('res://assets/slide-arrow-4k.png') +var slide_trail_shadermaterial := preload('res://shaders/slidetrail.tres') -var SlideTrailHandler -var JudgeText -var notelines -var meshinstance +onready var SlideTrailHandler = $'Viewport/Center/SlideTrailHandler' +onready var JudgeText = $'Viewport/Center/JudgeText' +onready var notelines = $'Viewport/Center/notelines' +onready var meshinstance = $'Viewport/Center/meshinstance' -var snd_miss := preload("res://assets/miss.wav") -var snd_clap := preload("res://assets/softclap.wav") +var snd_miss := preload('res://assets/miss.wav') +var snd_clap := preload('res://assets/softclap.wav') var snd_count_in := snd_clap var snd_judgement := { 0: snd_clap, @@ -28,7 +28,7 @@ var snd_judgement := { -2: snd_clap, 3: snd_miss, -3: snd_miss, - "MISS": snd_miss + 'MISS': snd_miss } var db_judgement := { 0: 0.0, @@ -38,7 +38,7 @@ var db_judgement := { -2: -3.0, 3: -6.0, -3: -6.0, - "MISS": 0.0 + 'MISS': 0.0 } var pitch_judgement := { 0: 1.0, @@ -48,7 +48,7 @@ var pitch_judgement := { 2: 0.60, -3: 1.5, 3: 1.5, - "MISS": 1.0 + 'MISS': 1.0 } const SQRT2 := sqrt(2) @@ -75,19 +75,6 @@ var slide_trail_mesh_instances := {} var noteline_array_image := Image.new() -# UV vertex arrays for our sprites -# tap/star/arrow are 4-vertex 2-triangle simple squares -# hold is 8-vertex 6-triangle to enable stretching in the middle -const UV_ARRAY_TAP := PoolVector2Array([Vector2(0, 0.5), Vector2(0.5, 0.5), Vector2(0, 1), Vector2(0.5, 1)]) -const UV_ARRAY_HOLD := PoolVector2Array([ - Vector2(0.5, 0.5), Vector2(1, 0.5), Vector2(0.5, 0.75), Vector2(1, 0.75), - Vector2(0.5, 0.75), Vector2(1, 0.75), Vector2(0.5, 1), Vector2(1, 1) - ]) -const UV_ARRAY_STAR := PoolVector2Array([Vector2(0.5, 0), Vector2(1, 0), Vector2(0.5, 0.5), Vector2(1, 0.5)]) -const UV_ARRAY_ARROW := PoolVector2Array([Vector2(0, 0), Vector2(0.5, 0), Vector2(0, 0.5), Vector2(0.5, 0.5)]) -# Slide trail arrow. Single tri. -const UV_ARRAY_SLIDE_ARROW := PoolVector2Array([Vector2(0, 0), Vector2(1, 0), Vector2(0, 1)]) -const UV_ARRAY_SLIDE_ARROW2 := PoolVector2Array([Vector2(1, 1), Vector2(0, 1), Vector2(1, 0)]) # Normal vertex arrays for our sprites. Might be unnecessary? const DEFAULT_NORMAL := Vector3(0, 0, 1) @@ -115,20 +102,30 @@ const TextJudgement := { -2: TextWord.GOOD + TextStyle.ARC_EARLY, 3: TextWord.ALMOST + TextStyle.ARC_LATE, -3: TextWord.ALMOST + TextStyle.ARC_EARLY, - "MISS": TextWord.MISS + TextStyle.ARC + 'MISS': TextWord.MISS + TextStyle.ARC +} +const TextJudgementStraight := { + 0: TextWord.PERFECT + TextStyle.STRAIGHT, + 1: TextWord.GREAT + TextStyle.STRAIGHT, + -1: TextWord.GREAT + TextStyle.STRAIGHT, + 2: TextWord.GOOD + TextStyle.STRAIGHT, + -2: TextWord.GOOD + TextStyle.STRAIGHT, + 3: TextWord.ALMOST + TextStyle.STRAIGHT, + -3: TextWord.ALMOST + TextStyle.STRAIGHT, + 'MISS': TextWord.MISS + TextStyle.STRAIGHT } func initialise_scores(): scores = {} - for type in [Note.NOTE_TAP, Note.NOTE_HOLD, Note.NOTE_SLIDE]: + for type in [Note.NOTE_TAP, Note.NOTE_HOLD, Note.NOTE_STAR]: scores[type] = {} for key in TextJudgement: scores[type][key] = 0 # Release types for type in [Note.NOTE_HOLD, Note.NOTE_SLIDE]: - scores[-type] = {} + scores[Note.RELEASE_SCORE_TYPES[type]] = {} for key in TextJudgement: - scores[-type][key] = 0 + scores[Note.RELEASE_SCORE_TYPES[type]][key] = 0 func make_text_mesh(mesh: ArrayMesh, text_id: int, pos: Vector2, angle: float, alpha:=1.0, scale:=1.0): var r := GameTheme.judge_text_size2 * scale @@ -160,7 +157,7 @@ func make_tap_mesh(mesh: ArrayMesh, note_center: Vector2, scale:=1.0, color_arra arrays.resize(Mesh.ARRAY_MAX) arrays[Mesh.ARRAY_VERTEX] = vertex_array # arrays[Mesh.ARRAY_NORMAL] = NORMAL_ARRAY_4 - arrays[Mesh.ARRAY_TEX_UV] = UV_ARRAY_TAP + arrays[Mesh.ARRAY_TEX_UV] = GameTheme.UV_ARRAY_TAP arrays[Mesh.ARRAY_COLOR] = color_array mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, arrays) @@ -183,7 +180,7 @@ func make_hold_mesh(mesh: ArrayMesh, note_center: Vector2, note_center_rel: Vect arrays.resize(Mesh.ARRAY_MAX) arrays[Mesh.ARRAY_VERTEX] = vertex_array # arrays[Mesh.ARRAY_NORMAL] = NORMAL_ARRAY_8 - arrays[Mesh.ARRAY_TEX_UV] = UV_ARRAY_HOLD + arrays[Mesh.ARRAY_TEX_UV] = GameTheme.UV_ARRAY_HOLD arrays[Mesh.ARRAY_COLOR] = color_array mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, arrays) @@ -201,7 +198,7 @@ func make_star_mesh(mesh: ArrayMesh, note_center: Vector2, scale:=1.0, angle:=0. arrays.resize(Mesh.ARRAY_MAX) arrays[Mesh.ARRAY_VERTEX] = vertex_array # arrays[Mesh.ARRAY_NORMAL] = NORMAL_ARRAY_4 - arrays[Mesh.ARRAY_TEX_UV] = UV_ARRAY_STAR + arrays[Mesh.ARRAY_TEX_UV] = GameTheme.UV_ARRAY_STAR arrays[Mesh.ARRAY_COLOR] = color_array mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, arrays) @@ -235,9 +232,9 @@ func make_slide_trail_mesh(note) -> ArrayMesh: # uvs.resize(3*trail_length) colors.resize(3*trail_length) for i in trail_length: - uvs.append_array(UV_ARRAY_SLIDE_ARROW if i%3 else UV_ARRAY_SLIDE_ARROW2) + uvs.append_array(GameTheme.UV_ARRAY_SLIDE_ARROW if i%3 else GameTheme.UV_ARRAY_SLIDE_ARROW2) for j in 3: -# uvs[i*3+j] = UV_ARRAY_SLIDE_ARROW[j] if i%2 else UV_ARRAY_SLIDE_ARROW2[j] +# uvs[i*3+j] = GameTheme.UV_ARRAY_SLIDE_ARROW[j] if i%2 else GameTheme.UV_ARRAY_SLIDE_ARROW2[j] colors[i*3+j] = Color(color.r, color.g, color.b, (1.0+float(i))/float(trail_length)) match note.slide_type: @@ -273,9 +270,18 @@ func make_slide_trail_mesh(note) -> ArrayMesh: return mesh #---------------------------------------------------------------------------------------------------------------------------------------------- -func activate_note(note, judgement): - active_judgement_texts.append({col=note.column, judgement=judgement, time=t}) +func make_judgement_column(judgement, column: int): + active_judgement_texts.append({col=column, judgement=judgement, time=t}) SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_judgement[judgement], db_judgement[judgement], pitch_judgement[judgement]) + +func make_judgement_pos(judgement, pos: Vector2): + # Positional judgement text not yet implemented, will do if touches are ever added + #active_judgement_texts.append({judgement=judgement, time=t}) + SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_judgement[judgement], db_judgement[judgement], pitch_judgement[judgement]) + + +func activate_note(note, judgement): + make_judgement_column(judgement, note.column) scores[note.type][judgement] += 1 note.time_activated = t @@ -289,26 +295,23 @@ func activate_note(note, judgement): func activate_note_release(note, judgement): # Only for Hold, Slide - SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_judgement[judgement], db_judgement[judgement], pitch_judgement[judgement]) - scores[-note.type][judgement] += 1 + scores[Note.RELEASE_SCORE_TYPES[note.type]][judgement] += 1 match note.type: Note.NOTE_HOLD: note.is_held = false note.time_released = t + make_judgement_column(judgement, note.column) active_judgement_texts.append({col=note.column, judgement=judgement, time=t}) Note.NOTE_SLIDE: - active_judgement_texts.append({col=note.column_release, judgement=judgement, time=t}) + make_judgement_column(judgement, note.column_release) Note.NOTE_TOUCH_HOLD: pass func button_pressed(col): for note in active_notes: - if note.column != col: + if (not note.hittable) or (note.column != col) or (note.time_activated != INF) or note.missed: continue - if note.time_activated != INF: - continue - #var hit_delta = real_time(t - note.time_hit) # Judgement times are in seconds not gametime var hit_delta = get_realtime_precise() - real_time(note.time_hit) # Judgement times are in seconds not gametime if hit_delta >= 0.0: if hit_delta > Rules.JUDGEMENT_TIMES_POST[-1]: @@ -335,14 +338,14 @@ func do_hold_release(note): if hit_delta <= Rules.JUDGEMENT_TIMES_RELEASE_POST[i]: activate_note_release(note, i) return - activate_note_release(note, Rules.JUDGEMENT_TIERS-1) # No "miss" for releasing, only worst judgement. + activate_note_release(note, Rules.JUDGEMENT_TIERS-1) # No 'miss' for releasing, only worst judgement. return else: for i in Rules.JUDGEMENT_TIERS-1: if -hit_delta <= Rules.JUDGEMENT_TIMES_RELEASE_PRE[i]: activate_note_release(note, -i) return - activate_note_release(note, Rules.JUDGEMENT_TIERS-1) # No "miss" for releasing, only worst judgement. + activate_note_release(note, -(Rules.JUDGEMENT_TIERS-1)) # No 'miss' for releasing, only worst judgement. return func do_slide_release(note): @@ -364,15 +367,15 @@ func check_hold_release(col): continue if note.type == Note.NOTE_HOLD: if note.is_held == true: - do_hold_release(note) # Separate function since there's no need to "consume" releases + do_hold_release(note) # Separate function since there's no need to 'consume' releases func button_released(col): # We only care about hold release. # For that particular case, we want both to be unheld. - if $"/root/main/InputHandler".touchbuttons_pressed[col] == 0: + if $'/root/main/InputHandler'.touchbuttons_pressed[col] == 0: check_hold_release(col) func touchbutton_released(col): - if $"/root/main/InputHandler".buttons_pressed[col] == 0: + if $'/root/main/InputHandler'.buttons_pressed[col] == 0: check_hold_release(col) #---------------------------------------------------------------------------------------------------------------------------------------------- @@ -402,11 +405,12 @@ func _draw(): var color: PoolColorArray match note.type: Note.NOTE_TAP: - if note.time_hit >= t: - color = GameTheme.color_array_tap(1.0, note.double_hit) - else: - color = GameTheme.color_array_tap(clamp((note.time_death-t)/Note.DEATH_DELAY, 0.0, 1.0), note.double_hit) + color = GameTheme.color_array_tap(clamp((note.time_death-t)/Note.DEATH_DELAY, 0.0, 1.0), note.double_hit) make_tap_mesh(mesh, note_center, scale, color) + Note.NOTE_STAR: + color = GameTheme.color_array_star(clamp((note.time_death-t)/Note.DEATH_DELAY, 0.0, 1.0), note.double_hit) + var angle = fmod(t/note.duration, 1.0)*TAU + make_star_mesh(mesh, note_center, scale, angle, color) Note.NOTE_HOLD: if note.is_held: position = (t+GameTheme.note_forecast_beats-note.time_release)/GameTheme.note_forecast_beats @@ -435,9 +439,6 @@ func _draw(): var note_center_rel = (GameTheme.RADIAL_UNIT_VECTORS[note.column] * position_rel * GameTheme.receptor_ring_radius) make_hold_mesh(mesh, note_center, note_center_rel, scale, GameTheme.RADIAL_COL_ANGLES[note.column], color) Note.NOTE_SLIDE: - color = GameTheme.COLOR_ARRAY_DOUBLE_4 if note.double_hit else GameTheme.COLOR_ARRAY_STAR - var angle = fmod(t/note.duration, 1.0)*TAU - make_star_mesh(mesh, note_center, scale, angle, color) var trail_alpha := 1.0 if position < GameTheme.INNER_NOTE_CIRCLE_RATIO: trail_alpha = 0.0 @@ -447,12 +448,12 @@ func _draw(): var trail_progress : float = clamp((t - note.time_hit - GameTheme.SLIDE_DELAY)/(note.duration - GameTheme.SLIDE_DELAY), 0.0, 1.0) var star_pos : Vector2 = note.get_position(trail_progress) var star_angle : float = note.get_angle(trail_progress) - make_star_mesh(mesh, star_pos, 1.33, star_angle, color) + make_star_mesh(mesh, star_pos, 1.33, star_angle) if note.progress != INF: - slide_trail_mesh_instances[note.slide_id].material.set_shader_param("trail_progress", note.progress) + slide_trail_mesh_instances[note.slide_id].material.set_shader_param('trail_progress', note.progress) if t > note.time_release: trail_alpha = max(1 - (t - note.time_release)/Note.DEATH_DELAY, 0.0) - slide_trail_mesh_instances[note.slide_id].material.set_shader_param("base_alpha", trail_alpha*0.88) + slide_trail_mesh_instances[note.slide_id].material.set_shader_param('base_alpha', trail_alpha*GameTheme.slide_trail_alpha) noteline_data.unlock() var noteline_data_tex = ImageTexture.new() @@ -542,41 +543,36 @@ func make_noteline_mesh(vertices := 32) -> ArrayMesh: # Called when the node enters the scene tree for the first time. func _ready(): - SlideTrailHandler = $"Viewport/Center/SlideTrailHandler" - JudgeText = $"Viewport/Center/JudgeText" - notelines = $"Viewport/Center/notelines" - meshinstance = $"Viewport/Center/meshinstance" - notelines.set_mesh(make_noteline_mesh()) - notelines.material.set_shader_param("bps", bpm/60.0) - notelines.material.set_shader_param("array_postmul", arr_div) + notelines.material.set_shader_param('bps', bpm/60.0) + notelines.material.set_shader_param('array_postmul', arr_div) noteline_array_image.create(16, 16, false, Image.FORMAT_RGBF) noteline_array_image.fill(Color(0.0, 0.0, 0.0)) # Format: first 15 rows are for hit events, last row is for releases only (no ring glow) - $"/root/main/InputHandler".connect("button_pressed", self, "button_pressed") - $"/root/main/InputHandler".connect("touchbutton_pressed", self, "touchbutton_pressed") - $"/root/main/InputHandler".connect("button_released", self, "button_released") - $"/root/main/InputHandler".connect("touchbutton_released", self, "touchbutton_released") + $'/root/main/InputHandler'.connect('button_pressed', self, 'button_pressed') + $'/root/main/InputHandler'.connect('touchbutton_pressed', self, 'touchbutton_pressed') + $'/root/main/InputHandler'.connect('button_released', self, 'button_released') + $'/root/main/InputHandler'.connect('touchbutton_released', self, 'touchbutton_released') func load_track(data: Dictionary, difficulty_idx: int): set_time(-3.0) active_notes = [] all_notes = [] next_note_to_load = 0 - self.song_key = data.directory.rsplit("/", true, 1)[1] - all_notes = FileLoader.SRT.load_file(data.directory + "/" + data.chart_filelist[difficulty_idx]) + self.song_key = data.directory.rsplit('/', true, 1)[1] + all_notes = FileLoader.SRT.load_file(data.directory + '/' + data.chart_filelist[difficulty_idx]) bpm = data.bpm_values[0] sync_offset_audio = data.audio_offsets[0] sync_offset_video = data.video_offsets[0] - var audiostream = FileLoader.load_ogg(data.directory + "/" + data.audio_filelist[0]) - var videostream = load(data.directory + "/" + data.video_filelist[0]) + var audiostream = FileLoader.load_ogg(data.directory + '/' + data.audio_filelist[0]) + var videostream = load(data.directory + '/' + data.video_filelist[0]) - $"/root/main/music".set_stream(audiostream) - $"/root/main/video".set_stream(videostream) - $"/root/main/video".update_aspect_ratio(data.video_dimensions[0]/data.video_dimensions[1]) + $'/root/main/music'.set_stream(audiostream) + $'/root/main/video'.set_stream(videostream) + $'/root/main/video'.update_aspect_ratio(data.video_dimensions[0]/data.video_dimensions[1]) # all_notes = FileLoader.Test.stress_pattern() Note.process_note_list(all_notes) @@ -584,16 +580,16 @@ func load_track(data: Dictionary, difficulty_idx: int): if note.type == Note.NOTE_SLIDE: slide_trail_meshes[note.slide_id] = make_slide_trail_mesh(note) - meshinstance.material.set_shader_param("star_color", GameTheme.COLOR_STAR) - meshinstance.material.set_shader_param("held_color", GameTheme.COLOR_HOLD_HELD) - meshinstance.material.set_shader_param("bps", bpm/60.0) - meshinstance.material.set_shader_param("screen_size", get_viewport().get_size()) + meshinstance.material.set_shader_param('star_color', GameTheme.COLOR_STAR) + meshinstance.material.set_shader_param('held_color', GameTheme.COLOR_HOLD_HELD) + meshinstance.material.set_shader_param('bps', bpm/60.0) + meshinstance.material.set_shader_param('screen_size', get_viewport().get_size()) meshinstance.set_texture(tex) initialise_scores() # Remove old score func stop(): - $"/root/main/music".stop() - $"/root/main/video".stop() + $'/root/main/music'.stop() + $'/root/main/video'.stop() # running = false next_note_to_load = 1000000 # Hacky but whatever @@ -622,8 +618,8 @@ func _process(delta): if !running: return - meshinstance.material.set_shader_param("bps", bpm/60.0) - notelines.material.set_shader_param("bps", bpm/60.0) + meshinstance.material.set_shader_param('bps', bpm/60.0) + notelines.material.set_shader_param('bps', bpm/60.0) var t_old := game_time(time) # time += delta @@ -639,56 +635,55 @@ func _process(delta): timer.set_one_shot(false) # timer.set_timer_process_mode(Timer.TIMER_PROCESS_FIXED) timer.set_wait_time(delay) - timer.connect("timeout", self, "intro_click") + timer.connect('timeout', self, 'intro_click') timer.start() - timer.connect("timeout", timer, "queue_free") + timer.connect('timeout', timer, 'queue_free') # if (t_old < 0) and (t >= 0): -# get_node("/root/main/video").play() +# get_node('/root/main/video').play() var vt_delta := time - video_start_time() - if (0.0 <= vt_delta) and (vt_delta < 1.0) and not get_node("/root/main/video").is_playing(): - get_node("/root/main/video").play() - get_node("/root/main/video").set_stream_position(vt_delta) + if (0.0 <= vt_delta) and (vt_delta < 1.0) and not get_node('/root/main/video').is_playing(): + get_node('/root/main/video').play() + get_node('/root/main/video').set_stream_position(vt_delta) var at_delta := time - audio_start_time() - if (0.0 <= at_delta) and (at_delta < 1.0) and not get_node("/root/main/music").is_playing(): -# get_node("/root/main/music").play() -# get_node("/root/main/music").seek(at_delta) - get_node("/root/main/music").play(at_delta) + if (0.0 <= at_delta) and (at_delta < 1.0) and not get_node('/root/main/music').is_playing(): +# get_node('/root/main/music').play() +# get_node('/root/main/music').seek(at_delta) + get_node('/root/main/music').play(at_delta) # Clean out expired notes var miss_time: float = Rules.JUDGEMENT_TIMES_POST[-1] * bpm/60.0 - for i in range(len(active_notes)-1, -1, -1): + for i in range(len(active_notes)-1, -1, -1): # Iterate backwards as we're potentially removing things from the array var note = active_notes[i] - if note.time_death < t: + if note.time_death < t: # Delete notes match note.type: Note.NOTE_HOLD: if note.is_held: # Held too long - scores[-Note.NOTE_HOLD][3] += 1 - active_judgement_texts.append({col=note.column, judgement=3, time=t}) - SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_judgement[3], db_judgement[3]) + scores[Note.RELEASE_SCORE_TYPES[Note.NOTE_HOLD]][3] += 1 + make_judgement_column(3, note.column) Note.NOTE_SLIDE: SlideTrailHandler.remove_child(slide_trail_mesh_instances[note.slide_id]) slide_trail_mesh_instances.erase(note.slide_id) var idx = active_slide_trails.find(note) if idx >= 0: active_slide_trails.remove(idx) - active_judgement_texts.append({col=note.column_release, judgement="MISS", time=t}) - scores[-Note.NOTE_SLIDE]["MISS"] += 1 + make_judgement_column('MISS', note.column_release) + scores[Note.NOTE_SLIDE]['MISS'] += 1 note.missed_slide = true - SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_judgement["MISS"], db_judgement["MISS"]) active_notes.remove(i) - elif note.time_activated == INF: - if ((t-note.time_hit) > miss_time) and not note.missed: - active_judgement_texts.append({col=note.column, judgement="MISS", time=t}) - scores[note.type]["MISS"] += 1 - if Note.RELEASE_SCORE_TYPES.has(note.type): - scores[-note.type]["MISS"] += 1 - note.missed = true - SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_judgement["MISS"], db_judgement["MISS"]) - if note.type == Note.NOTE_SLIDE: - # Even if you miss the hit you can still slide, we're so nice + elif not note.hittable: + if note.type == Note.NOTE_SLIDE: + if (t >= note.time_hit) and (note.time_activated == INF): active_slide_trails.append(note) note.progress = 0.0 + note.time_activated = t + elif note.time_activated == INF: # Check if notes have been missed + if ((t-note.time_hit) > miss_time) and not note.missed: + note.missed = true + make_judgement_column('MISS', note.column) + scores[note.type]['MISS'] += 1 + if Note.RELEASE_SCORE_TYPES.has(note.type): + scores[Note.RELEASE_SCORE_TYPES[note.type]]['MISS'] += 1 # Clean out expired judgement texts # By design they will always be in order so we can ignore anything past the first index @@ -710,19 +705,26 @@ func _process(delta): var meshi = MeshInstance2D.new() meshi.set_mesh(slide_trail_meshes[note.slide_id]) meshi.set_material(slide_trail_shadermaterial.duplicate()) - meshi.material.set_shader_param("trail_progress", 0.0) + meshi.material.set_shader_param('trail_progress', 0.0) meshi.set_texture(tex_slide_arrow) slide_trail_mesh_instances[note.slide_id] = meshi SlideTrailHandler.add_child(meshi) next_note_to_load += 1 - if (len(active_notes) < 1) and (next_note_to_load >= len(all_notes)) and not get_node("/root/main/music").is_playing() and (len(active_judgement_texts) < 1): + if ( + next_note_to_load >= len(all_notes) + and not get_node('/root/main/video').is_playing() + and not get_node('/root/main/music').is_playing() + and active_notes.empty() + and active_judgement_texts.empty() + and slide_trail_mesh_instances.empty() + ): self.running = false self.timers_set = false - emit_signal("finished_song", song_key, scores) + emit_signal('finished_song', song_key, scores) # Redraw - meshinstance.material.set_shader_param("screen_size", get_viewport().get_size()) + meshinstance.material.set_shader_param('screen_size', get_viewport().get_size()) update() $Painter.update()