RhythmGame/scripts/Note.gd

220 lines
7.8 KiB
GDScript3
Raw Normal View History

2019-11-10 15:09:14 +10:30
#extends Object
extends Node
#class_name Note
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, CHORD_TRIPLE}
const DEATH_DELAY := 1.0 # This is touchy with the judgement windows and variable bpm.
const RELEASE_SCORE_TYPES := {
NOTE_HOLD: -NOTE_HOLD,
NOTE_SLIDE: NOTE_SLIDE,
NOTE_TOUCH_HOLD: -NOTE_TOUCH_HOLD,
NOTE_ROLL: -NOTE_ROLL
}
2019-11-10 15:09:14 +10:30
class NoteBase extends Resource:
var time_hit: float setget set_time_hit
var time_death: float
var column: int
var double_hit := false
2019-11-19 20:08:35 +10:30
var time_activated := INF
var missed := false
var is_break := false
2019-11-19 20:08:35 +10:30
func set_time_hit(value: float):
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):
2019-11-19 20:08:35 +10:30
self.time_hit = time_hit
self.column = column
self.is_break = is_break
2019-11-19 20:08:35 +10:30
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
2019-11-19 20:08:35 +10:30
var is_held: bool
func _init(time_hit: float, column: int, duration: float):
2019-11-19 20:08:35 +10:30
self.time_hit = time_hit
self.column = column
self.duration = duration
2019-11-19 20:08:35 +10:30
self.is_held = false
func set_time_hit(value: float):
time_hit = value
time_release = time_hit + duration
time_death = time_release + DEATH_DELAY
func set_time_release(value: float):
time_release = value
time_death = time_release + DEATH_DELAY
duration = time_release - time_hit
func set_duration(value: float):
duration = value
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
var time_release: float setget set_time_release
var duration: float setget set_duration
var column_release: int
var slide_type: int
var slide_id: int
var progress := INF
var missed_slide := false
var values: Dictionary
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
self.time_death = time_release + DEATH_DELAY
self.column_release = column_release
self.slide_type = slide_type
self.values = {}
update_slide_variables()
func set_time_hit(value: float):
time_hit = value
time_release = time_hit + duration
time_death = time_release + DEATH_DELAY
func set_time_release(value: float):
time_release = value
time_death = time_release + DEATH_DELAY
duration = time_release - time_hit
func set_duration(value: float):
duration = value
time_release = time_hit + duration
time_death = time_release + DEATH_DELAY
func update_slide_variables():
match slide_type:
Note.SlideType.CHORD, Note.SlideType.CHORD_TRIPLE:
values.start = GameTheme.RADIAL_UNIT_VECTORS[column] * GameTheme.receptor_ring_radius
values.end = GameTheme.RADIAL_UNIT_VECTORS[column_release] * GameTheme.receptor_ring_radius
values.angle = (values.end - values.start).angle()
Note.SlideType.ARC_CW:
values.start_a = GameTheme.RADIAL_COL_ANGLES[column]
values.end_a = GameTheme.RADIAL_COL_ANGLES[column_release]
if values.end_a < values.start_a:
values.end_a += TAU
Note.SlideType.ARC_ACW:
values.start_a = GameTheme.RADIAL_COL_ANGLES[column]
values.end_a = GameTheme.RADIAL_COL_ANGLES[column_release]
if values.end_a > values.start_a:
values.end_a -= TAU
func get_position(progress: float) -> Vector2:
match slide_type:
Note.SlideType.CHORD, Note.SlideType.CHORD_TRIPLE:
return lerp(values.start, values.end, progress)
Note.SlideType.ARC_CW:
var circle_angle : float = lerp(values.start_a, values.end_a, progress)
return polar2cartesian(GameTheme.receptor_ring_radius, circle_angle)
Note.SlideType.ARC_ACW:
var circle_angle : float = lerp(values.start_a, values.end_a, progress)
return polar2cartesian(GameTheme.receptor_ring_radius, circle_angle)
2020-03-12 23:05:12 +10:30
return Vector2(0.0, 0.0)
func get_angle(progress: float) -> float:
match slide_type:
Note.SlideType.CHORD, Note.SlideType.CHORD_TRIPLE:
return values.angle
Note.SlideType.ARC_CW:
var circle_angle : float = lerp(values.start_a, values.end_a, progress)
return circle_angle + PI/2.0
Note.SlideType.ARC_ACW:
var circle_angle : float = lerp(values.start_a, values.end_a, progress)
return circle_angle - PI/2.0
2020-03-12 23:05:12 +10:30
return 0.0
2019-11-14 22:49:36 +10:30
func get_slide_length() -> float:
# Return unit-circle (r=1) length of slide trail
match slide_type:
Note.SlideType.CHORD, Note.SlideType.CHORD_TRIPLE:
return 2*abs(sin((GameTheme.RADIAL_COL_ANGLES[column_release] - GameTheme.RADIAL_COL_ANGLES[column])/2))
2019-11-14 22:49:36 +10:30
Note.SlideType.ARC_CW:
return fposmod(GameTheme.RADIAL_COL_ANGLES[column_release] - GameTheme.RADIAL_COL_ANGLES[column], TAU)
2019-11-14 22:49:36 +10:30
Note.SlideType.ARC_ACW:
return fposmod(GameTheme.RADIAL_COL_ANGLES[column] - GameTheme.RADIAL_COL_ANGLES[column_release], TAU)
2020-03-12 23:05:12 +10:30
return 0.0
2019-11-14 22:49:36 +10:30
static func copy_note(note: NoteBase):
# Honestly disappointed I couldn't find a better, more OOP solution for this.
var newnote: NoteBase
match note.type:
NOTE_TAP:
newnote = NoteTap.new(note.time_hit, note.column, note.is_break)
NOTE_STAR:
newnote = NoteStar.new(note.time_hit, note.column, note.is_break)
NOTE_HOLD:
newnote = NoteHold.new(note.time_hit, note.column, note.duration)
NOTE_SLIDE:
newnote = NoteSlide.new(note.time_hit, note.column, note.duration, note.column_release, note.slide_type)
NOTE_ROLL:
newnote = NoteRoll.new(note.time_hit, note.column, note.duration)
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)
2019-11-10 15:09:14 +10:30
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
# It's not though, so we lay it out simply
var slide_id := 0
2019-11-10 15:09:14 +10:30
if len(note_array):
# Doubles
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:
note_array[i].slide_id = slide_id
slide_id += 1