More refactoring

This commit is contained in:
Luke Hubmayer-Werner 2021-11-27 23:22:42 +10:30
parent ab848312e2
commit d45a4ae0fa
6 changed files with 260 additions and 185 deletions

View File

@ -68,6 +68,27 @@ static func directory_list(directory: String, hidden: bool, sort:=true) -> Dicti
return output return output
static func find_by_extensions(array, extensions=null) -> Dictionary:
# Both args can be Array or PoolStringArray
# If extensions omitted, do all extensions
var output = {}
if extensions:
for ext in extensions:
output[ext] = []
for filename in array:
for ext in extensions:
if filename.ends_with(ext):
output[ext].append(filename)
else:
for filename in array:
var ext = filename.rsplit('.', false, 1)[1]
if ext in output:
output[ext].append(filename)
else:
output[ext] = [filename]
return output
static func init_directory(directory: String): static func init_directory(directory: String):
var dir = Directory.new() var dir = Directory.new()
var err = dir.make_dir_recursive(directory) var err = dir.make_dir_recursive(directory)

View File

@ -1,4 +1,5 @@
extends Control extends Control
var RadialMeshTools := preload('res://scripts/RadialMeshTools.gd')
var screen_height := 1080 var screen_height := 1080
@ -18,11 +19,6 @@ onready var notelines = $'Viewport/Center/notelines'
onready var meshinstance = $'Viewport/Center/meshinstance' onready var meshinstance = $'Viewport/Center/meshinstance'
onready var lbl_combo = $lbl_combo onready var lbl_combo = $lbl_combo
const SQRT2 := sqrt(2)
const DEG45 := deg2rad(45.0)
const DEG90 := deg2rad(90.0)
const DEG135 := deg2rad(135.0)
var time_zero_msec: int = 0 var time_zero_msec: int = 0
var time: float = 0.0 var time: float = 0.0
var t: float = 0.0 # Game time var t: float = 0.0 # Game time
@ -42,37 +38,7 @@ var slide_trail_mesh_instances := {}
var noteline_array_image := Image.new() var noteline_array_image := Image.new()
# Text UVs #----------------------------------------------------------------------------------------------------------------------------------------------
var text_UV_arrays := []
func make_text_UV(row: int, column: int) -> PoolVector2Array:
return PoolVector2Array([Vector2(column/4.0, row/8.0), Vector2((column+1)/4.0, row/8.0), Vector2(column/4.0, (row+1)/8.0), Vector2((column+1)/4.0, (row+1)/8.0)])
func make_text_UVs():
for row in 8:
for column in 4:
text_UV_arrays.append(make_text_UV(row, column))
enum TextStyle {STRAIGHT=0, ARC=1, ARC_EARLY=2, ARC_LATE=3}
enum TextWord {NICE=0, OK=4, NG=8, PERFECT=12, GREAT=16, GOOD=20, ALMOST=24, MISS=28}
const TextJudgement := {
0: TextWord.PERFECT + TextStyle.ARC,
1: TextWord.GREAT + TextStyle.ARC_LATE,
-1: TextWord.GREAT + TextStyle.ARC_EARLY,
2: TextWord.GOOD + TextStyle.ARC_LATE,
-2: TextWord.GOOD + TextStyle.ARC_EARLY,
3: TextWord.ALMOST + TextStyle.ARC_LATE,
-3: TextWord.ALMOST + TextStyle.ARC_EARLY,
'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
}
var current_combo := 0 var current_combo := 0
func increment_combo(): func increment_combo():
current_combo += 1 current_combo += 1
@ -87,132 +53,16 @@ func initialise_scores():
scores = {} scores = {}
for type in [Note.NOTE_TAP, Note.NOTE_HOLD, Note.NOTE_STAR]: for type in [Note.NOTE_TAP, Note.NOTE_HOLD, Note.NOTE_STAR]:
scores[type] = {} scores[type] = {}
for key in TextJudgement: for key in RadialMeshTools.TextJudgement:
scores[type][key] = 0 scores[type][key] = 0
# Release types # Release types
for type in [Note.NOTE_HOLD, Note.NOTE_SLIDE]: for type in [Note.NOTE_HOLD, Note.NOTE_SLIDE]:
scores[Note.RELEASE_SCORE_TYPES[type]] = {} scores[Note.RELEASE_SCORE_TYPES[type]] = {}
for key in TextJudgement: for key in RadialMeshTools.TextJudgement:
scores[Note.RELEASE_SCORE_TYPES[type]][key] = 0 scores[Note.RELEASE_SCORE_TYPES[type]][key] = 0
scores['max_combo'] = 0 scores['max_combo'] = 0
current_combo = 0 current_combo = 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
var vertex_array := PoolVector2Array([
pos+polar2cartesian(r, angle+GameTheme.JUDGE_TEXT_ANG2), # TODO: fix this UV/vertex order mess
pos+polar2cartesian(r, angle+GameTheme.JUDGE_TEXT_ANG1),
pos+polar2cartesian(r, angle+GameTheme.JUDGE_TEXT_ANG4),
pos+polar2cartesian(r, angle+GameTheme.JUDGE_TEXT_ANG3)
])
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
arrays[Mesh.ARRAY_TEX_UV] = text_UV_arrays[text_id]
arrays[Mesh.ARRAY_COLOR] = GameTheme.color_array_text(alpha)
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, arrays)
func make_judgement_text(mesh: ArrayMesh, text_id: int, col: int, progress:=0.0):
make_text_mesh(mesh, text_id,
GameTheme.RADIAL_UNIT_VECTORS[col] * GameTheme.receptor_ring_radius * lerp(0.85, 0.85*0.75, progress),
GameTheme.RADIAL_COL_ANGLES[col]-PI/2.0, lerp(1.0, 0.0, progress), lerp(1.0, 0.75, progress)
)
# ----------------------------------------------------------------------------------------------------------------------------------------------------
# Helper functions to generate meshes from vertex arrays
func make_tap_mesh(mesh: ArrayMesh, note_center: Vector2, scale:=1.0, color_array:=GameTheme.COLOR_ARRAY_TAP):
var dim = GameTheme.sprite_size2 * scale
var vertex_array = PoolVector2Array([note_center + Vector2(-dim, -dim), note_center + Vector2(dim, -dim), note_center + Vector2(-dim, dim), note_center + Vector2(dim, dim)])
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
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)
func make_hold_mesh(mesh: ArrayMesh, note_center: Vector2, note_center_rel: Vector2, scale:=1.0, angle:=0.0, color_array = GameTheme.COLOR_ARRAY_HOLD):
var dim = GameTheme.sprite_size2 * scale
var dim2 = dim * SQRT2
var a1 = angle - DEG45
var a2 = angle + DEG45
var a3 = angle - DEG90
var a4 = angle + DEG90
var a5 = angle - DEG135
var a6 = angle + DEG135
var vertex_array = PoolVector2Array([
note_center + polar2cartesian(dim2, a1), note_center + polar2cartesian(dim2, a2),
note_center + polar2cartesian(dim, a3), note_center + polar2cartesian(dim, a4),
note_center_rel + polar2cartesian(dim, a3), note_center_rel + polar2cartesian(dim, a4),
note_center_rel + polar2cartesian(dim2, a5), note_center_rel + polar2cartesian(dim2, a6)
])
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
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)
func make_star_mesh(mesh: ArrayMesh, note_center: Vector2, scale:=1.0, angle:=0.0, color_array:=GameTheme.COLOR_ARRAY_STAR):
var dim = GameTheme.sprite_size2 * scale * SQRT2
var a1 = angle - DEG45
var a2 = angle + DEG45
var a3 = angle - DEG135
var a4 = angle + DEG135
var vertex_array = PoolVector2Array([
note_center + polar2cartesian(dim, a1), note_center + polar2cartesian(dim, a2),
note_center + polar2cartesian(dim, a3), note_center + polar2cartesian(dim, a4)
])
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
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)
#func make_arrow_mesh(mesh: ArrayMesh, vertex_array, color_array = GameTheme.COLOR_ARRAY_TAP):
# var arrays = []
# arrays.resize(Mesh.ARRAY_MAX)
# arrays[Mesh.ARRAY_VERTEX] = vertex_array
# arrays[Mesh.ARRAY_TEX_UV] = UV_ARRAY_ARROW
# arrays[Mesh.ARRAY_COLOR] = color_array
# mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, arrays)
const slide_arrows_per_unit_length := 10
func make_slide_trail_mesh(note) -> ArrayMesh:
# Generates a mesh centered around origin. Make sure the MeshInstance2D that draws this is centered on the screen.
var mesh = ArrayMesh.new()
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
var vertices := PoolVector2Array()
var uvs := PoolVector2Array()
var colors := PoolColorArray()
var size := GameTheme.sprite_size2 * sqrt(2)
var color := GameTheme.COLOR_DOUBLE_SLIDE if note.double_hit else GameTheme.COLOR_SLIDE
match note.get_points():
[var positions, var angles]:
var trail_length : int = len(positions)
vertices.resize(3*trail_length)
uvs.resize(3*trail_length)
colors.resize(3*trail_length)
for i in trail_length:
var idx = (trail_length-i-1)*3 # We want the earliest ones to be drawn last so that loops/sharp bends will have the first pass on top
var u = GameTheme.UV_ARRAY_SLIDE_ARROW if i%3 else GameTheme.UV_ARRAY_SLIDE_ARROW2
for j in 3:
uvs[idx+j] = u[j]
colors[idx+j] = Color(color.r, color.g, color.b, (1.0+float(i))/float(trail_length))
var angle : float = angles[i]
var offset : Vector2 = positions[i] * GameTheme.receptor_ring_radius
vertices[idx] = offset
vertices[idx+1] = offset + polar2cartesian(size, angle+PI*0.75)
vertices[idx+2] = offset + polar2cartesian(size, angle-PI*0.75)
arrays[Mesh.ARRAY_VERTEX] = vertices
arrays[Mesh.ARRAY_TEX_UV] = uvs
arrays[Mesh.ARRAY_COLOR] = colors
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
return mesh
#---------------------------------------------------------------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------------------------------------------------------------
func make_judgement_column(judgement, column: int): func make_judgement_column(judgement, column: int):
@ -345,11 +195,11 @@ func _draw():
match note.type: match note.type:
Note.NOTE_TAP: Note.NOTE_TAP:
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) RadialMeshTools.make_tap_mesh(mesh, note_center, scale, color)
Note.NOTE_STAR: Note.NOTE_STAR:
color = GameTheme.color_array_star(clamp((note.time_death-t)/Note.DEATH_DELAY, 0.0, 1.0), note.double_hit) 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 var angle = fmod(t/note.duration, 1.0)*TAU
make_star_mesh(mesh, note_center, scale, angle, color) RadialMeshTools.make_star_mesh(mesh, note_center, scale, angle, color)
Note.NOTE_HOLD: Note.NOTE_HOLD:
if note.is_held: if note.is_held:
position = (t+GameTheme.note_forecast_beats-note.time_release)/GameTheme.note_forecast_beats position = (t+GameTheme.note_forecast_beats-note.time_release)/GameTheme.note_forecast_beats
@ -376,7 +226,7 @@ func _draw():
if position_rel < GameTheme.INNER_NOTE_CIRCLE_RATIO: if position_rel < GameTheme.INNER_NOTE_CIRCLE_RATIO:
position_rel = GameTheme.INNER_NOTE_CIRCLE_RATIO position_rel = GameTheme.INNER_NOTE_CIRCLE_RATIO
var note_center_rel = (GameTheme.RADIAL_UNIT_VECTORS[note.column] * position_rel * GameTheme.receptor_ring_radius) 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) RadialMeshTools.make_hold_mesh(mesh, note_center, note_center_rel, scale, GameTheme.RADIAL_COL_ANGLES[note.column], color)
Note.NOTE_SLIDE: Note.NOTE_SLIDE:
var trail_alpha := 1.0 var trail_alpha := 1.0
if position < GameTheme.INNER_NOTE_CIRCLE_RATIO: if position < GameTheme.INNER_NOTE_CIRCLE_RATIO:
@ -387,7 +237,7 @@ func _draw():
var trail_progress : float = clamp((t - note.time_hit - GameTheme.SLIDE_DELAY)/(note.duration - GameTheme.SLIDE_DELAY), 0.0, 1.0) 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) * GameTheme.receptor_ring_radius var star_pos : Vector2 = note.get_position(trail_progress) * GameTheme.receptor_ring_radius
var star_angle : float = note.get_angle(trail_progress) var star_angle : float = note.get_angle(trail_progress)
make_star_mesh(mesh, star_pos, 1.33, star_angle) RadialMeshTools.make_star_mesh(mesh, star_pos, 1.33, star_angle)
if note.progress != INF: 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: if t > note.time_release:
@ -403,7 +253,7 @@ func _draw():
var textmesh := ArrayMesh.new() var textmesh := ArrayMesh.new()
for text in active_judgement_texts: for text in active_judgement_texts:
make_judgement_text(textmesh, TextJudgement[text.judgement], text.col, (t-text.time)/GameTheme.judge_text_duration) RadialMeshTools.make_judgement_text(textmesh, RadialMeshTools.TextJudgement[text.judgement], text.col, (t-text.time)/GameTheme.judge_text_duration)
JudgeText.set_mesh(textmesh) JudgeText.set_mesh(textmesh)
@ -436,7 +286,6 @@ func _input(event):
func _init(): func _init():
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
GameTheme.init_radial_values() GameTheme.init_radial_values()
make_text_UVs()
initialise_scores() initialise_scores()
func set_time(seconds: float): func set_time(seconds: float):
@ -507,7 +356,7 @@ func load_track(song_key: String, difficulty_key: String):
Note.process_note_list(all_notes, false) Note.process_note_list(all_notes, false)
for note in all_notes: for note in all_notes:
if note.type == Note.NOTE_SLIDE: if note.type == Note.NOTE_SLIDE:
slide_trail_meshes[note.slide_id] = make_slide_trail_mesh(note) slide_trail_meshes[note.slide_id] = RadialMeshTools.make_slide_trail_mesh(note)
initialise_scores() # Remove old score initialise_scores() # Remove old score

203
scripts/RadialMeshTools.gd Normal file
View File

@ -0,0 +1,203 @@
extends Node
const SQRT2 := sqrt(2)
const DEG45 := deg2rad(45.0)
const DEG90 := deg2rad(90.0)
const DEG135 := deg2rad(135.0)
# ----------------------------------------------------------------------------------------------------------------------------------------------------
# Helper functions to generate meshes from vertex arrays
static func make_tap_mesh(mesh: ArrayMesh, note_center: Vector2, scale:=1.0, color_array:=GameTheme.COLOR_ARRAY_TAP):
var dim = GameTheme.sprite_size2 * scale
var vertex_array = PoolVector2Array([note_center + Vector2(-dim, -dim), note_center + Vector2(dim, -dim), note_center + Vector2(-dim, dim), note_center + Vector2(dim, dim)])
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
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)
static func make_hold_mesh(mesh: ArrayMesh, note_center: Vector2, note_center_rel: Vector2, scale:=1.0, angle:=0.0, color_array = GameTheme.COLOR_ARRAY_HOLD):
var dim = GameTheme.sprite_size2 * scale
var dim2 = dim * SQRT2
var a1 = angle - DEG45
var a2 = angle + DEG45
var a3 = angle - DEG90
var a4 = angle + DEG90
var a5 = angle - DEG135
var a6 = angle + DEG135
var vertex_array = PoolVector2Array([
note_center + polar2cartesian(dim2, a1), note_center + polar2cartesian(dim2, a2),
note_center + polar2cartesian(dim, a3), note_center + polar2cartesian(dim, a4),
note_center_rel + polar2cartesian(dim, a3), note_center_rel + polar2cartesian(dim, a4),
note_center_rel + polar2cartesian(dim2, a5), note_center_rel + polar2cartesian(dim2, a6)
])
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
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)
static func make_star_mesh(mesh: ArrayMesh, note_center: Vector2, scale:=1.0, angle:=0.0, color_array:=GameTheme.COLOR_ARRAY_STAR):
var dim = GameTheme.sprite_size2 * scale * SQRT2
var a1 = angle - DEG45
var a2 = angle + DEG45
var a3 = angle - DEG135
var a4 = angle + DEG135
var vertex_array = PoolVector2Array([
note_center + polar2cartesian(dim, a1), note_center + polar2cartesian(dim, a2),
note_center + polar2cartesian(dim, a3), note_center + polar2cartesian(dim, a4)
])
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
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)
#func make_arrow_mesh(mesh: ArrayMesh, vertex_array, color_array = GameTheme.COLOR_ARRAY_TAP):
# var arrays = []
# arrays.resize(Mesh.ARRAY_MAX)
# arrays[Mesh.ARRAY_VERTEX] = vertex_array
# arrays[Mesh.ARRAY_TEX_UV] = UV_ARRAY_ARROW
# arrays[Mesh.ARRAY_COLOR] = color_array
# mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, arrays)
const slide_arrows_per_unit_length := 10
static func make_slide_trail_mesh(note) -> ArrayMesh:
# Generates a mesh centered around origin. Make sure the MeshInstance2D that draws this is centered on the screen.
var mesh = ArrayMesh.new()
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
var vertices := PoolVector2Array()
var uvs := PoolVector2Array()
var colors := PoolColorArray()
var size := GameTheme.sprite_size2 * sqrt(2)
var color := GameTheme.COLOR_DOUBLE_SLIDE if note.double_hit else GameTheme.COLOR_SLIDE
match note.get_points():
[var positions, var angles]:
var trail_length : int = len(positions)
vertices.resize(3*trail_length)
uvs.resize(3*trail_length)
colors.resize(3*trail_length)
for i in trail_length:
var idx = (trail_length-i-1)*3 # We want the earliest ones to be drawn last so that loops/sharp bends will have the first pass on top
var u = GameTheme.UV_ARRAY_SLIDE_ARROW if i%3 else GameTheme.UV_ARRAY_SLIDE_ARROW2
for j in 3:
uvs[idx+j] = u[j]
colors[idx+j] = Color(color.r, color.g, color.b, (1.0+float(i))/float(trail_length))
var angle : float = angles[i]
var offset : Vector2 = positions[i] * GameTheme.receptor_ring_radius
vertices[idx] = offset
vertices[idx+1] = offset + polar2cartesian(size, angle+PI*0.75)
vertices[idx+2] = offset + polar2cartesian(size, angle-PI*0.75)
arrays[Mesh.ARRAY_VERTEX] = vertices
arrays[Mesh.ARRAY_TEX_UV] = uvs
arrays[Mesh.ARRAY_COLOR] = colors
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
return mesh
#----------------------------------------------------------------------------------------------------------------------------------------------
# Text UVs
# Old dynamic code:
#var text_UV_arrays := []
#func make_text_UV(row: int, column: int) -> PoolVector2Array:
# return PoolVector2Array([Vector2(column/4.0, row/8.0), Vector2((column+1)/4.0, row/8.0), Vector2(column/4.0, (row+1)/8.0), Vector2((column+1)/4.0, (row+1)/8.0)])
#func make_text_UVs():
# for row in 8:
# for column in 4:
# text_UV_arrays.append(make_text_UV(row, column))
# This is replaced by a quick hacky python codegen:
#>>> def make_text_UV(row, column):
#... return f'PoolVector2Array([Vector2({column}/4.0, {row}/8.0), Vector2({column+1}/4.0, {row}/8.0), Vector2({column}/4.0, {row+1}/8.0), Vector2({column+1}/4.0, {row+1}/8.0)])'
#>>> for row in range(8):
#... for col in range(4):
#... print(make_text_UV(row, col) + ',')
const text_UV_arrays := [
PoolVector2Array([Vector2(0/4.0, 0/8.0), Vector2(1/4.0, 0/8.0), Vector2(0/4.0, 1/8.0), Vector2(1/4.0, 1/8.0)]),
PoolVector2Array([Vector2(1/4.0, 0/8.0), Vector2(2/4.0, 0/8.0), Vector2(1/4.0, 1/8.0), Vector2(2/4.0, 1/8.0)]),
PoolVector2Array([Vector2(2/4.0, 0/8.0), Vector2(3/4.0, 0/8.0), Vector2(2/4.0, 1/8.0), Vector2(3/4.0, 1/8.0)]),
PoolVector2Array([Vector2(3/4.0, 0/8.0), Vector2(4/4.0, 0/8.0), Vector2(3/4.0, 1/8.0), Vector2(4/4.0, 1/8.0)]),
PoolVector2Array([Vector2(0/4.0, 1/8.0), Vector2(1/4.0, 1/8.0), Vector2(0/4.0, 2/8.0), Vector2(1/4.0, 2/8.0)]),
PoolVector2Array([Vector2(1/4.0, 1/8.0), Vector2(2/4.0, 1/8.0), Vector2(1/4.0, 2/8.0), Vector2(2/4.0, 2/8.0)]),
PoolVector2Array([Vector2(2/4.0, 1/8.0), Vector2(3/4.0, 1/8.0), Vector2(2/4.0, 2/8.0), Vector2(3/4.0, 2/8.0)]),
PoolVector2Array([Vector2(3/4.0, 1/8.0), Vector2(4/4.0, 1/8.0), Vector2(3/4.0, 2/8.0), Vector2(4/4.0, 2/8.0)]),
PoolVector2Array([Vector2(0/4.0, 2/8.0), Vector2(1/4.0, 2/8.0), Vector2(0/4.0, 3/8.0), Vector2(1/4.0, 3/8.0)]),
PoolVector2Array([Vector2(1/4.0, 2/8.0), Vector2(2/4.0, 2/8.0), Vector2(1/4.0, 3/8.0), Vector2(2/4.0, 3/8.0)]),
PoolVector2Array([Vector2(2/4.0, 2/8.0), Vector2(3/4.0, 2/8.0), Vector2(2/4.0, 3/8.0), Vector2(3/4.0, 3/8.0)]),
PoolVector2Array([Vector2(3/4.0, 2/8.0), Vector2(4/4.0, 2/8.0), Vector2(3/4.0, 3/8.0), Vector2(4/4.0, 3/8.0)]),
PoolVector2Array([Vector2(0/4.0, 3/8.0), Vector2(1/4.0, 3/8.0), Vector2(0/4.0, 4/8.0), Vector2(1/4.0, 4/8.0)]),
PoolVector2Array([Vector2(1/4.0, 3/8.0), Vector2(2/4.0, 3/8.0), Vector2(1/4.0, 4/8.0), Vector2(2/4.0, 4/8.0)]),
PoolVector2Array([Vector2(2/4.0, 3/8.0), Vector2(3/4.0, 3/8.0), Vector2(2/4.0, 4/8.0), Vector2(3/4.0, 4/8.0)]),
PoolVector2Array([Vector2(3/4.0, 3/8.0), Vector2(4/4.0, 3/8.0), Vector2(3/4.0, 4/8.0), Vector2(4/4.0, 4/8.0)]),
PoolVector2Array([Vector2(0/4.0, 4/8.0), Vector2(1/4.0, 4/8.0), Vector2(0/4.0, 5/8.0), Vector2(1/4.0, 5/8.0)]),
PoolVector2Array([Vector2(1/4.0, 4/8.0), Vector2(2/4.0, 4/8.0), Vector2(1/4.0, 5/8.0), Vector2(2/4.0, 5/8.0)]),
PoolVector2Array([Vector2(2/4.0, 4/8.0), Vector2(3/4.0, 4/8.0), Vector2(2/4.0, 5/8.0), Vector2(3/4.0, 5/8.0)]),
PoolVector2Array([Vector2(3/4.0, 4/8.0), Vector2(4/4.0, 4/8.0), Vector2(3/4.0, 5/8.0), Vector2(4/4.0, 5/8.0)]),
PoolVector2Array([Vector2(0/4.0, 5/8.0), Vector2(1/4.0, 5/8.0), Vector2(0/4.0, 6/8.0), Vector2(1/4.0, 6/8.0)]),
PoolVector2Array([Vector2(1/4.0, 5/8.0), Vector2(2/4.0, 5/8.0), Vector2(1/4.0, 6/8.0), Vector2(2/4.0, 6/8.0)]),
PoolVector2Array([Vector2(2/4.0, 5/8.0), Vector2(3/4.0, 5/8.0), Vector2(2/4.0, 6/8.0), Vector2(3/4.0, 6/8.0)]),
PoolVector2Array([Vector2(3/4.0, 5/8.0), Vector2(4/4.0, 5/8.0), Vector2(3/4.0, 6/8.0), Vector2(4/4.0, 6/8.0)]),
PoolVector2Array([Vector2(0/4.0, 6/8.0), Vector2(1/4.0, 6/8.0), Vector2(0/4.0, 7/8.0), Vector2(1/4.0, 7/8.0)]),
PoolVector2Array([Vector2(1/4.0, 6/8.0), Vector2(2/4.0, 6/8.0), Vector2(1/4.0, 7/8.0), Vector2(2/4.0, 7/8.0)]),
PoolVector2Array([Vector2(2/4.0, 6/8.0), Vector2(3/4.0, 6/8.0), Vector2(2/4.0, 7/8.0), Vector2(3/4.0, 7/8.0)]),
PoolVector2Array([Vector2(3/4.0, 6/8.0), Vector2(4/4.0, 6/8.0), Vector2(3/4.0, 7/8.0), Vector2(4/4.0, 7/8.0)]),
PoolVector2Array([Vector2(0/4.0, 7/8.0), Vector2(1/4.0, 7/8.0), Vector2(0/4.0, 8/8.0), Vector2(1/4.0, 8/8.0)]),
PoolVector2Array([Vector2(1/4.0, 7/8.0), Vector2(2/4.0, 7/8.0), Vector2(1/4.0, 8/8.0), Vector2(2/4.0, 8/8.0)]),
PoolVector2Array([Vector2(2/4.0, 7/8.0), Vector2(3/4.0, 7/8.0), Vector2(2/4.0, 8/8.0), Vector2(3/4.0, 8/8.0)]),
PoolVector2Array([Vector2(3/4.0, 7/8.0), Vector2(4/4.0, 7/8.0), Vector2(3/4.0, 8/8.0), Vector2(4/4.0, 8/8.0)])
]
enum TextStyle {STRAIGHT=0, ARC=1, ARC_EARLY=2, ARC_LATE=3}
enum TextWord {NICE=0, OK=4, NG=8, PERFECT=12, GREAT=16, GOOD=20, ALMOST=24, MISS=28}
const TextJudgement := {
0: TextWord.PERFECT + TextStyle.ARC,
1: TextWord.GREAT + TextStyle.ARC_LATE,
-1: TextWord.GREAT + TextStyle.ARC_EARLY,
2: TextWord.GOOD + TextStyle.ARC_LATE,
-2: TextWord.GOOD + TextStyle.ARC_EARLY,
3: TextWord.ALMOST + TextStyle.ARC_LATE,
-3: TextWord.ALMOST + TextStyle.ARC_EARLY,
'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
}
static 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
var vertex_array := PoolVector2Array([
pos+polar2cartesian(r, angle+GameTheme.JUDGE_TEXT_ANG2), # TODO: fix this UV/vertex order mess
pos+polar2cartesian(r, angle+GameTheme.JUDGE_TEXT_ANG1),
pos+polar2cartesian(r, angle+GameTheme.JUDGE_TEXT_ANG4),
pos+polar2cartesian(r, angle+GameTheme.JUDGE_TEXT_ANG3)
])
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
arrays[Mesh.ARRAY_TEX_UV] = text_UV_arrays[text_id]
arrays[Mesh.ARRAY_COLOR] = GameTheme.color_array_text(alpha)
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, arrays)
static func make_judgement_text(mesh: ArrayMesh, text_id: int, col: int, progress:=0.0):
make_text_mesh(mesh, text_id,
GameTheme.RADIAL_UNIT_VECTORS[col] * GameTheme.receptor_ring_radius * lerp(0.85, 0.85*0.75, progress),
GameTheme.RADIAL_COL_ANGLES[col]-PI/2.0, lerp(1.0, 0.0, progress), lerp(1.0, 0.75, progress)
)

View File

@ -51,27 +51,6 @@ func load_image(name: String) -> ImageTexture: # Searches through all of the pa
var filename: return FileHelpers.load_image(filename) var filename: return FileHelpers.load_image(filename)
func find_by_extensions(array, extensions=null) -> Dictionary:
# Both args can be Array or PoolStringArray
# If extensions omitted, do all extensions
var output = {}
if extensions:
for ext in extensions:
output[ext] = []
for filename in array:
for ext in extensions:
if filename.ends_with(ext):
output[ext].append(filename)
else:
for filename in array:
var ext = filename.rsplit('.', false, 1)[1]
if ext in output:
output[ext].append(filename)
else:
output[ext] = [filename]
return output
func scan_library() -> Dictionary: func scan_library() -> Dictionary:
print('Scanning library') print('Scanning library')
var song_defs = {} var song_defs = {}
@ -131,7 +110,7 @@ func scan_library() -> Dictionary:
genres[song_defs[song_key]['genre']] = [song_key] genres[song_defs[song_key]['genre']] = [song_key]
else: else:
var files_by_ext = find_by_extensions(FileHelpers.directory_list(full_folder, false).files) var files_by_ext = FileHelpers.find_by_extensions(FileHelpers.directory_list(full_folder, false).files)
if 'sm' in files_by_ext: if 'sm' in files_by_ext:
var sm_filename = files_by_ext['sm'][0] var sm_filename = files_by_ext['sm'][0]
print(sm_filename) print(sm_filename)

View File

@ -2,6 +2,7 @@ extends Node
const difficulty_translations = {'01': 'Z', '02': 'B', '03': 'A', '04': 'E', '05': 'M', '06': 'R', '10': ''} # A bit redundant now but might be useful later for other hacks const difficulty_translations = {'01': 'Z', '02': 'B', '03': 'A', '04': 'E', '05': 'M', '06': 'R', '10': ''} # A bit redundant now but might be useful later for other hacks
class MultilangStr: class MultilangStr:
# Automatically propogate higher langs to lower ones if lower ones are missing. # Automatically propogate higher langs to lower ones if lower ones are missing.
# e.g. if we don't have a proper english title, return the transliterated one instead # e.g. if we don't have a proper english title, return the transliterated one instead
@ -28,6 +29,7 @@ class MultilangStr:
func _to_string() -> String: func _to_string() -> String:
return self[GameTheme.display_language] return self[GameTheme.display_language]
class Song: class Song:
var title: MultilangStr var title: MultilangStr
var subtitle: MultilangStr var subtitle: MultilangStr
@ -98,6 +100,7 @@ var genre_songs = [] # Dictionaries of key: Song
var tile_tex_cache = {} # We'll need some way of managing this later since holding all the tiles in memory might be expensive var tile_tex_cache = {} # We'll need some way of managing this later since holding all the tiles in memory might be expensive
var charts_cache = {} var charts_cache = {}
func add_song(key: String, data: Dictionary): func add_song(key: String, data: Dictionary):
if not data.has('index'): if not data.has('index'):
data['index'] = key data['index'] = key
@ -109,6 +112,7 @@ func add_song(key: String, data: Dictionary):
genre_songs.append({}) genre_songs.append({})
genre_songs[genre_ids[song.genre]][key] = song genre_songs[genre_ids[song.genre]][key] = song
func get_song_tile_texture(song_key): func get_song_tile_texture(song_key):
if song_key in tile_tex_cache: if song_key in tile_tex_cache:
return tile_tex_cache[song_key] return tile_tex_cache[song_key]
@ -118,6 +122,7 @@ func get_song_tile_texture(song_key):
else: else:
print_debug('Invalid song_key: ', song_key) print_debug('Invalid song_key: ', song_key)
func get_song_charts(song_key): func get_song_charts(song_key):
if song_key in charts_cache: if song_key in charts_cache:
return charts_cache[song_key] return charts_cache[song_key]
@ -143,5 +148,6 @@ func get_song_charts(song_key):
else: else:
print_debug('Invalid song_key: ', song_key) print_debug('Invalid song_key: ', song_key)
func initialize(): func initialize():
pass pass

View File

@ -13,6 +13,7 @@ const RELEASE_SCORE_TYPES := {
NOTE_ROLL: -NOTE_ROLL NOTE_ROLL: -NOTE_ROLL
} }
class NoteBase extends Resource: class NoteBase extends Resource:
var time_hit: float setget set_time_hit var time_hit: float setget set_time_hit
var time_death: float var time_death: float
@ -26,26 +27,31 @@ class NoteBase extends Resource:
time_hit = value time_hit = value
time_death = time_hit + DEATH_DELAY time_death = time_hit + DEATH_DELAY
class NoteHittableBase extends NoteBase: class NoteHittableBase extends NoteBase:
const hittable := true const hittable := true
class NoteTapBase extends NoteHittableBase: class NoteTapBase extends NoteHittableBase:
func _init(time_hit: float, column: int, is_break:=false): func _init(time_hit: float, column: int, is_break:=false):
self.time_hit = time_hit self.time_hit = time_hit
self.column = column self.column = column
self.is_break = is_break self.is_break = is_break
class NoteTap extends NoteTapBase: class NoteTap extends NoteTapBase:
var type := NOTE_TAP var type := NOTE_TAP
func _init(time_hit: float, column: int, is_break:=false).(time_hit, column, is_break): func _init(time_hit: float, column: int, is_break:=false).(time_hit, column, is_break):
pass pass
class NoteStar extends NoteTapBase: # Fancy charts have naked slides which necessitates separation of Star and Slide :( class NoteStar extends NoteTapBase: # Fancy charts have naked slides which necessitates separation of Star and Slide :(
var type := NOTE_STAR var type := NOTE_STAR
var duration := 1.0 # This is required for the spin speed 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): func _init(time_hit: float, column: int, is_break:=false).(time_hit, column, is_break):
pass pass
class NoteHoldBase extends NoteHittableBase: class NoteHoldBase extends NoteHittableBase:
var time_release: float setget set_time_release var time_release: float setget set_time_release
var time_released := INF var time_released := INF
@ -72,16 +78,19 @@ class NoteHoldBase extends NoteHittableBase:
time_release = time_hit + duration time_release = time_hit + duration
time_death = time_release + DEATH_DELAY time_death = time_release + DEATH_DELAY
class NoteHold extends NoteHoldBase: class NoteHold extends NoteHoldBase:
var type := NOTE_HOLD var type := NOTE_HOLD
func _init(time_hit: float, column: int, duration: float).(time_hit, column, duration): func _init(time_hit: float, column: int, duration: float).(time_hit, column, duration):
pass pass
class NoteRoll extends NoteHoldBase: class NoteRoll extends NoteHoldBase:
var type := NOTE_ROLL var type := NOTE_ROLL
func _init(time_hit: float, column: int, duration: float).(time_hit, column, duration): func _init(time_hit: float, column: int, duration: float).(time_hit, column, duration):
pass pass
class NoteSlide extends NoteBase: # Fancy charts have naked slides which necessitates separation of Star and Slide :( class NoteSlide extends NoteBase: # Fancy charts have naked slides which necessitates separation of Star and Slide :(
const hittable := false const hittable := false
var type := NOTE_SLIDE var type := NOTE_SLIDE
@ -204,6 +213,7 @@ class NoteSlide extends NoteBase: # Fancy charts have naked slides which necess
return values.curve2d.get_baked_length() return values.curve2d.get_baked_length()
return 0.0 return 0.0
static func copy_note(note: NoteBase): static func copy_note(note: NoteBase):
# Honestly disappointed I couldn't find a better, more OOP solution for this. # Honestly disappointed I couldn't find a better, more OOP solution for this.
var newnote: NoteBase var newnote: NoteBase
@ -223,16 +233,20 @@ static func copy_note(note: NoteBase):
newnote.double_hit = note.double_hit newnote.double_hit = note.double_hit
return newnote return newnote
static func make_slide(time_hit: float, duration: float, column: int, column_release: int, slide_type:=SlideType.CHORD) -> NoteSlide: static func make_slide(time_hit: float, duration: float, column: int, column_release: int, slide_type:=SlideType.CHORD) -> NoteSlide:
return NoteSlide.new(time_hit, column, duration, column_release, slide_type) return NoteSlide.new(time_hit, column, duration, column_release, slide_type)
static func make_touch(time_hit: float, location: Vector2) -> Dictionary: static func make_touch(time_hit: float, location: Vector2) -> Dictionary:
return {type=NOTE_TOUCH, time_hit=time_hit, time_death=time_hit+DEATH_DELAY, location=location, double_hit=false} return {type=NOTE_TOUCH, time_hit=time_hit, time_death=time_hit+DEATH_DELAY, location=location, double_hit=false}
static func make_touch_hold(time_hit: float, duration: float, location: Vector2) -> Dictionary: static func make_touch_hold(time_hit: float, duration: float, location: Vector2) -> Dictionary:
var time_release := time_hit + duration 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} 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, check_doubles:=true): static func process_note_list(note_array: Array, check_doubles:=true):
# Preprocess double hits, assign Slide IDs # Preprocess double hits, assign Slide IDs
# If this were performance-critical, we'd single iterate it # If this were performance-critical, we'd single iterate it
@ -260,11 +274,13 @@ static func process_note_list(note_array: Array, check_doubles:=true):
note_array[i].slide_id = slide_id note_array[i].slide_id = slide_id
slide_id += 1 slide_id += 1
# These should probably get their own singleton later # These should probably get their own singleton later
const ORBIT_INNER_RADIUS = sin(deg2rad(22.5)) # ~0.38 const ORBIT_INNER_RADIUS = sin(deg2rad(22.5)) # ~0.38
const ORBIT_KAPPA = (sqrt(2)-1) * 4.0 / 3.0 # This is the length of control points along a tangent to approximate a circle (multiply by desired radius) const ORBIT_KAPPA = (sqrt(2)-1) * 4.0 / 3.0 # This is the length of control points along a tangent to approximate a circle (multiply by desired radius)
func curve2d_make_orbit(curve2d, rad_in, rad_out, ccw, rad_max_arc:=PI*0.25, kappa:=ORBIT_KAPPA, inner_radius:=ORBIT_INNER_RADIUS):
static func curve2d_make_orbit(curve2d, rad_in, rad_out, ccw, rad_max_arc:=PI*0.25, kappa:=ORBIT_KAPPA, inner_radius:=ORBIT_INNER_RADIUS):
var d_sign = -1 if ccw else 1 var d_sign = -1 if ccw else 1
var rad_2 = rad_in+PI*3/8*d_sign var rad_2 = rad_in+PI*3/8*d_sign
var rad_2t = rad_2+PI*0.5*d_sign var rad_2t = rad_2+PI*0.5*d_sign
@ -285,7 +301,8 @@ func curve2d_make_orbit(curve2d, rad_in, rad_out, ccw, rad_max_arc:=PI*0.25, kap
curve2d.add_point(polar2cartesian(inner_radius, rad_3), polar2cartesian(k, rad_3t)) curve2d.add_point(polar2cartesian(inner_radius, rad_3), polar2cartesian(k, rad_3t))
# curve2d.add_point(polar2cartesian(1.0, rad_out)) # curve2d.add_point(polar2cartesian(1.0, rad_out))
func curve2d_make_sideorbit(curve2d: Curve2D, rad_in: float, rad_out: float, ccw: bool, rad_max_arc:=PI*0.25, kappa:=ORBIT_KAPPA, inner_radius:=ORBIT_INNER_RADIUS):
static func curve2d_make_sideorbit(curve2d: Curve2D, rad_in: float, rad_out: float, ccw: bool, rad_max_arc:=PI*0.25, kappa:=ORBIT_KAPPA, inner_radius:=ORBIT_INNER_RADIUS):
var d_sign := -1 if ccw else 1 var d_sign := -1 if ccw else 1
var sideorbit_center := polar2cartesian(inner_radius, rad_in-PI*0.5*d_sign) var sideorbit_center := polar2cartesian(inner_radius, rad_in-PI*0.5*d_sign)
var rad_orbit_in := rad_in + PI*0.5*d_sign var rad_orbit_in := rad_in + PI*0.5*d_sign