More refactoring
This commit is contained in:
parent
ab848312e2
commit
d45a4ae0fa
|
@ -68,6 +68,27 @@ static func directory_list(directory: String, hidden: bool, sort:=true) -> Dicti
|
|||
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):
|
||||
var dir = Directory.new()
|
||||
var err = dir.make_dir_recursive(directory)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
extends Control
|
||||
var RadialMeshTools := preload('res://scripts/RadialMeshTools.gd')
|
||||
|
||||
var screen_height := 1080
|
||||
|
||||
|
@ -18,11 +19,6 @@ onready var notelines = $'Viewport/Center/notelines'
|
|||
onready var meshinstance = $'Viewport/Center/meshinstance'
|
||||
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: float = 0.0
|
||||
var t: float = 0.0 # Game time
|
||||
|
@ -42,37 +38,7 @@ var slide_trail_mesh_instances := {}
|
|||
|
||||
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
|
||||
func increment_combo():
|
||||
current_combo += 1
|
||||
|
@ -87,132 +53,16 @@ func initialise_scores():
|
|||
scores = {}
|
||||
for type in [Note.NOTE_TAP, Note.NOTE_HOLD, Note.NOTE_STAR]:
|
||||
scores[type] = {}
|
||||
for key in TextJudgement:
|
||||
for key in RadialMeshTools.TextJudgement:
|
||||
scores[type][key] = 0
|
||||
# Release types
|
||||
for type in [Note.NOTE_HOLD, Note.NOTE_SLIDE]:
|
||||
scores[Note.RELEASE_SCORE_TYPES[type]] = {}
|
||||
for key in TextJudgement:
|
||||
for key in RadialMeshTools.TextJudgement:
|
||||
scores[Note.RELEASE_SCORE_TYPES[type]][key] = 0
|
||||
scores['max_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):
|
||||
|
@ -345,11 +195,11 @@ func _draw():
|
|||
match note.type:
|
||||
Note.NOTE_TAP:
|
||||
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:
|
||||
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)
|
||||
RadialMeshTools.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
|
||||
|
@ -376,7 +226,7 @@ func _draw():
|
|||
if 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)
|
||||
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:
|
||||
var trail_alpha := 1.0
|
||||
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 star_pos : Vector2 = note.get_position(trail_progress) * GameTheme.receptor_ring_radius
|
||||
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:
|
||||
slide_trail_mesh_instances[note.slide_id].material.set_shader_param('trail_progress', note.progress)
|
||||
if t > note.time_release:
|
||||
|
@ -403,7 +253,7 @@ func _draw():
|
|||
|
||||
var textmesh := ArrayMesh.new()
|
||||
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)
|
||||
|
||||
|
||||
|
@ -436,7 +286,6 @@ func _input(event):
|
|||
func _init():
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
|
||||
GameTheme.init_radial_values()
|
||||
make_text_UVs()
|
||||
initialise_scores()
|
||||
|
||||
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)
|
||||
for note in all_notes:
|
||||
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
|
||||
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
|
@ -51,27 +51,6 @@ func load_image(name: String) -> ImageTexture: # Searches through all of the pa
|
|||
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:
|
||||
print('Scanning library')
|
||||
var song_defs = {}
|
||||
|
@ -131,7 +110,7 @@ func scan_library() -> Dictionary:
|
|||
genres[song_defs[song_key]['genre']] = [song_key]
|
||||
|
||||
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:
|
||||
var sm_filename = files_by_ext['sm'][0]
|
||||
print(sm_filename)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
class MultilangStr:
|
||||
# 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
|
||||
|
@ -28,6 +29,7 @@ class MultilangStr:
|
|||
func _to_string() -> String:
|
||||
return self[GameTheme.display_language]
|
||||
|
||||
|
||||
class Song:
|
||||
var title: 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 charts_cache = {}
|
||||
|
||||
|
||||
func add_song(key: String, data: Dictionary):
|
||||
if not data.has('index'):
|
||||
data['index'] = key
|
||||
|
@ -109,6 +112,7 @@ func add_song(key: String, data: Dictionary):
|
|||
genre_songs.append({})
|
||||
genre_songs[genre_ids[song.genre]][key] = song
|
||||
|
||||
|
||||
func get_song_tile_texture(song_key):
|
||||
if song_key in tile_tex_cache:
|
||||
return tile_tex_cache[song_key]
|
||||
|
@ -118,6 +122,7 @@ func get_song_tile_texture(song_key):
|
|||
else:
|
||||
print_debug('Invalid song_key: ', song_key)
|
||||
|
||||
|
||||
func get_song_charts(song_key):
|
||||
if song_key in charts_cache:
|
||||
return charts_cache[song_key]
|
||||
|
@ -143,5 +148,6 @@ func get_song_charts(song_key):
|
|||
else:
|
||||
print_debug('Invalid song_key: ', song_key)
|
||||
|
||||
|
||||
func initialize():
|
||||
pass
|
||||
|
|
|
@ -13,6 +13,7 @@ const RELEASE_SCORE_TYPES := {
|
|||
NOTE_ROLL: -NOTE_ROLL
|
||||
}
|
||||
|
||||
|
||||
class NoteBase extends Resource:
|
||||
var time_hit: float setget set_time_hit
|
||||
var time_death: float
|
||||
|
@ -26,26 +27,31 @@ class NoteBase extends Resource:
|
|||
time_hit = value
|
||||
time_death = time_hit + DEATH_DELAY
|
||||
|
||||
|
||||
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 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
|
||||
|
@ -72,16 +78,19 @@ class NoteHoldBase extends NoteHittableBase:
|
|||
time_release = time_hit + duration
|
||||
time_death = time_release + DEATH_DELAY
|
||||
|
||||
|
||||
class NoteHold extends NoteHoldBase:
|
||||
var type := NOTE_HOLD
|
||||
func _init(time_hit: float, column: int, duration: float).(time_hit, column, duration):
|
||||
pass
|
||||
|
||||
|
||||
class NoteRoll extends NoteHoldBase:
|
||||
var type := NOTE_ROLL
|
||||
func _init(time_hit: float, column: int, duration: float).(time_hit, column, duration):
|
||||
pass
|
||||
|
||||
|
||||
class NoteSlide extends NoteBase: # Fancy charts have naked slides which necessitates separation of Star and Slide :(
|
||||
const hittable := false
|
||||
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 0.0
|
||||
|
||||
|
||||
static func copy_note(note: NoteBase):
|
||||
# Honestly disappointed I couldn't find a better, more OOP solution for this.
|
||||
var newnote: NoteBase
|
||||
|
@ -223,16 +233,20 @@ static func copy_note(note: NoteBase):
|
|||
newnote.double_hit = note.double_hit
|
||||
return newnote
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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}
|
||||
|
||||
|
||||
static func make_touch_hold(time_hit: float, duration: float, location: Vector2) -> Dictionary:
|
||||
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, check_doubles:=true):
|
||||
# Preprocess double hits, assign Slide IDs
|
||||
# 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
|
||||
slide_id += 1
|
||||
|
||||
|
||||
# These should probably get their own singleton later
|
||||
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)
|
||||
|
||||
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 rad_2 = rad_in+PI*3/8*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(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 sideorbit_center := polar2cartesian(inner_radius, rad_in-PI*0.5*d_sign)
|
||||
var rad_orbit_in := rad_in + PI*0.5*d_sign
|
||||
|
|
Loading…
Reference in New Issue