1010 */
1111public partial class BattleDirector : Node2D
1212{
13- [ Export ]
14- public ChartManager CM ;
13+ #region Declarations
14+ private HealthBar Player ;
15+ private HealthBar Enemy ;
1516
1617 [ Export ]
17- public NoteManager NM ;
18+ private ChartManager CM ;
1819
19- private HealthBar Player ;
20- private HealthBar Enemy ;
20+ [ Export ]
21+ private InputHandler IH ;
2122
23+ [ Export ]
2224 private NotePlacementBar NotePlacementBar ;
2325
26+ [ Export ]
27+ private AudioStreamPlayer Audio ;
28+
2429 private double _timingInterval = .1 ; //secs
2530
2631 [ Signal ]
@@ -29,148 +34,231 @@ public partial class BattleDirector : Node2D
2934 [ Signal ]
3035 public delegate void EnemyDamageEventHandler ( int damage ) ;
3136
37+ private SongData _curSong ;
38+
3239 public struct SongData
3340 {
3441 public int Bpm ;
3542 public double SongLength ;
3643 public int NumLoops ;
3744 }
45+ #endregion
3846
39- private SongData _curSong ;
40-
47+ #region Note Handling
4148 //Assume queue structure for notes in each lane.
42- private readonly Note [ ] [ ] _laneNotes = new Note [ ] [ ]
43- {
44- Array . Empty < Note > ( ) ,
45- Array . Empty < Note > ( ) ,
46- Array . Empty < Note > ( ) ,
47- Array . Empty < Note > ( ) ,
49+ //Can eventually make this its own structure
50+ private NoteArrow [ ] [ ] _laneData = Array . Empty < NoteArrow [ ] > ( ) ;
51+ private int [ ] _laneLastBeat = new int [ ]
52+ { //Temporary (hopefully) measure to bridge from note queue structure to ordered array
53+ 0 ,
54+ 0 ,
55+ 0 ,
56+ 0 ,
4857 } ;
4958 private Note [ ] _notes = Array . Empty < Note > ( ) ;
5059
51- public override void _Ready ( )
60+ //Returns first note of lane without modifying lane data
61+ private Note GetNoteAt ( NoteArrow . ArrowType dir , int beat )
5262 {
53- AddExampleNote ( ) ;
54- CM . PrepChart ( _curSong , _notes ) ;
55-
56- Player = GetNode < HealthBar > ( "PlayerHP" ) ;
57- Enemy = GetNode < HealthBar > ( "EnemyHP" ) ;
58- NotePlacementBar = GetNode < NotePlacementBar > ( "NotePlacementBar" ) ;
63+ return GetNote ( _laneData [ ( int ) dir ] [ beat ] ) ;
64+ }
5965
60- CM . Connect ( nameof ( NoteManager . NotePressed ) , new Callable ( this , nameof ( OnNotePressed ) ) ) ;
61- CM . Connect ( nameof ( NoteManager . NoteReleased ) , new Callable ( this , nameof ( OnNoteReleased ) ) ) ;
66+ //Get note of a note arrow
67+ private Note GetNote ( NoteArrow arrow )
68+ {
69+ return _notes [ arrow . NoteIdx ] ;
6270 }
6371
64- public override void _Process ( double delta )
72+ private bool AddNoteToLane ( Note note , bool isActive = true )
6573 {
66- TimeKeeper . CurrentTime += delta ;
67- //Check beats for each lane for passive misses
68- double curBeat = TimeKeeper . CurrentTime / ( 60 / ( double ) _curSong . Bpm ) ;
69- for ( int i = 0 ; i < _laneNotes . Length ; i ++ )
74+ note . Beat %= CM . BeatsPerLoop ;
75+ //Don't add dupe notes
76+ if ( note . Beat == 0 || _notes . Any ( nt => nt . Type == note . Type && nt . Beat == note . Beat ) )
7077 {
71- if ( _laneNotes [ i ] . Length <= 0 )
72- continue ;
73- double beatDif = ( curBeat - _laneNotes [ i ] . First ( ) . Beat ) ;
74- if ( beatDif > 1 )
75- {
76- handleTiming ( ( NoteArrow . ArrowType ) i , Math . Abs ( beatDif ) ) ;
77- }
78+ return false ; //Beat at 0 is too messy.
7879 }
80+ _notes = _notes . Append ( note ) . ToArray ( ) ;
81+ //Get noteArrow from CM
82+ var arrow = CM . AddArrowToLane ( note , _notes . Length - 1 ) ;
83+ arrow . IsActive = isActive ;
84+ _laneData [ ( int ) note . Type ] [ note . Beat ] = arrow ;
85+ return true ;
7986 }
87+ #endregion
8088
81- //Creeate dummy song data and notes
82- private void AddExampleNote ( )
89+ //Creeate dummy notes
90+ private void AddExampleNotes ( )
8391 {
84- _curSong = new SongData
92+ GD . Print ( CM . BeatsPerLoop ) ;
93+ for ( int i = 1 ; i < 15 ; i ++ )
8594 {
86- Bpm = 120 ,
87- SongLength = 100 ,
88- NumLoops = 5 ,
89- } ;
90- for ( int i = 0 ; i < 4 ; i ++ )
95+ Note exampleNote = new Note ( NoteArrow . ArrowType . Up , i * 4 ) ;
96+ AddNoteToLane ( exampleNote ) ;
97+ }
98+ for ( int i = 1 ; i < 15 ; i ++ )
99+ {
100+ Note exampleNote = new Note ( NoteArrow . ArrowType . Left , 4 * i + 1 ) ;
101+ AddNoteToLane ( exampleNote ) ;
102+ }
103+ for ( int i = 0 ; i < 10 ; i ++ )
91104 {
92- Note exampleNote = new Note ( NoteArrow . ArrowType . Up , i + 3 ) ;
105+ Note exampleNote = new Note ( NoteArrow . ArrowType . Right , 3 * i + 32 ) ;
93106 AddNoteToLane ( exampleNote ) ;
94107 }
95- for ( int i = 0 ; i < 1 ; i ++ )
108+ for ( int i = 0 ; i < 3 ; i ++ )
96109 {
97- Note exampleNote = new Note ( NoteArrow . ArrowType . Left , i + 4 ) ;
110+ Note exampleNote = new Note ( NoteArrow . ArrowType . Down , 8 * i + 16 ) ;
98111 AddNoteToLane ( exampleNote ) ;
99112 }
100113 }
101114
102- private void AddNoteToLane ( Note note )
115+ public override void _Ready ( )
103116 {
104- _notes = _notes . Append ( note ) . ToArray ( ) ;
105- _laneNotes [ ( int ) note . Type ] = _laneNotes [ ( int ) note . Type ] . Append ( note ) . ToArray ( ) ;
117+ _curSong = new SongData
118+ {
119+ Bpm = 120 ,
120+ SongLength = Audio . Stream . GetLength ( ) ,
121+ NumLoops = 5 ,
122+ } ;
123+
124+ var timer = GetTree ( ) . CreateTimer ( AudioServer . GetTimeToNextMix ( ) ) ;
125+ timer . Timeout += Begin ;
106126 }
107127
128+ private void Begin ( )
129+ {
130+ CM . PrepChart ( _curSong ) ;
131+ _laneData = new NoteArrow [ ] [ ]
132+ {
133+ new NoteArrow [ CM . BeatsPerLoop ] ,
134+ new NoteArrow [ CM . BeatsPerLoop ] ,
135+ new NoteArrow [ CM . BeatsPerLoop ] ,
136+ new NoteArrow [ CM . BeatsPerLoop ] ,
137+ } ;
138+ AddExampleNotes ( ) ;
139+
140+ Player = GetNode < HealthBar > ( "PlayerHP" ) ;
141+ Player . GetNode < Sprite2D > ( "Sprite2D" ) . Scale *= .5f ; //TEMP
142+ Player . GetNode < Sprite2D > ( "Sprite2D" ) . Position += Vector2 . Down * 30 ; //TEMP
143+ Enemy = GetNode < HealthBar > ( "EnemyHP" ) ;
144+
145+ //TEMP
146+ var enemTween = CreateTween ( ) ;
147+ enemTween
148+ . TweenProperty ( Enemy . GetNode < Sprite2D > ( "Sprite2D" ) , "position" , Vector2 . Down * 5 , 1f )
149+ . AsRelative ( ) ;
150+ enemTween
151+ . TweenProperty ( Enemy . GetNode < Sprite2D > ( "Sprite2D" ) , "position" , Vector2 . Up * 5 , 1f )
152+ . AsRelative ( ) ;
153+ enemTween . SetTrans ( Tween . TransitionType . Spring ) ;
154+ enemTween . SetEase ( Tween . EaseType . In ) ;
155+ enemTween . SetLoops ( ) ;
156+ enemTween . Play ( ) ;
157+
158+ CM . Connect ( nameof ( InputHandler . NotePressed ) , new Callable ( this , nameof ( OnNotePressed ) ) ) ;
159+ CM . Connect ( nameof ( InputHandler . NoteReleased ) , new Callable ( this , nameof ( OnNoteReleased ) ) ) ;
160+
161+ Audio . Play ( ) ;
162+ }
163+
164+ public override void _Process ( double delta )
165+ {
166+ TimeKeeper . CurrentTime = Audio . GetPlaybackPosition ( ) ;
167+ CheckMiss ( ) ;
168+ }
169+
170+ #region Input&Timing
108171 private void OnNotePressed ( NoteArrow . ArrowType type )
109172 {
110173 CheckNoteTiming ( type ) ;
111174 }
112175
113176 private void OnNoteReleased ( NoteArrow . ArrowType arrowType ) { }
114177
115- private void handleTiming ( NoteArrow . ArrowType type , double beatDif )
178+ //Check all lanes for misses from missed inputs
179+ private void CheckMiss ( )
180+ {
181+ //On current beat, if prev beat is active and not inputted
182+ double realBeat = TimeKeeper . CurrentTime / ( 60 / ( double ) _curSong . Bpm ) % CM . BeatsPerLoop ;
183+ for ( int i = 0 ; i < _laneData . Length ; i ++ )
184+ {
185+ if (
186+ _laneLastBeat [ i ] < Math . Floor ( realBeat )
187+ || ( _laneLastBeat [ i ] == CM . BeatsPerLoop - 1 && Math . Floor ( realBeat ) == 0 )
188+ )
189+ { //If above, a note has been missed
190+ //GD.Print("Last beat " + _laneLastBeat[i]);
191+ if (
192+ _laneData [ i ] [ _laneLastBeat [ i ] ] == null
193+ || ! _laneData [ i ] [ _laneLastBeat [ i ] ] . IsActive
194+ )
195+ {
196+ _laneLastBeat [ i ] = ( _laneLastBeat [ i ] + 1 ) % CM . BeatsPerLoop ;
197+ continue ;
198+ }
199+ //Note exists and has been missed
200+ _laneData [ i ] [ _laneLastBeat [ i ] ] . NoteHit ( ) ;
201+ HandleTiming ( ( NoteArrow . ArrowType ) i , 1 ) ;
202+ _laneLastBeat [ i ] = ( _laneLastBeat [ i ] + 1 ) % CM . BeatsPerLoop ;
203+ }
204+ }
205+ }
206+
207+ private void CheckNoteTiming ( NoteArrow . ArrowType type )
208+ {
209+ double realBeat = TimeKeeper . CurrentTime / ( 60 / ( double ) _curSong . Bpm ) % CM . BeatsPerLoop ;
210+ int curBeat = ( int ) Math . Round ( realBeat ) ;
211+ GD . Print ( "Cur beat " + curBeat + "Real: " + realBeat . ToString ( "#.###" ) ) ;
212+ if (
213+ _laneData [ ( int ) type ] [ curBeat % CM . BeatsPerLoop ] == null
214+ || ! _laneData [ ( int ) type ] [ curBeat % CM . BeatsPerLoop ] . IsActive
215+ )
216+ {
217+ _laneLastBeat [ ( int ) type ] = ( curBeat ) % CM . BeatsPerLoop ;
218+ PlayerAddNote ( type , curBeat ) ;
219+ return ;
220+ }
221+ double beatDif = Math . Abs ( realBeat - curBeat ) ;
222+ _laneData [ ( int ) type ] [ curBeat % CM . BeatsPerLoop ] . NoteHit ( ) ;
223+ _laneLastBeat [ ( int ) type ] = ( curBeat ) % CM . BeatsPerLoop ;
224+ HandleTiming ( type , beatDif ) ;
225+ }
226+
227+ private void HandleTiming ( NoteArrow . ArrowType type , double beatDif )
116228 {
117- //Cycle note queue
118- _laneNotes [ ( int ) type ] . First ( ) . Beat += CM . BeatsPerLoop ;
119- _laneNotes [ ( int ) type ] = _laneNotes [ ( int ) type ] //Credit: Stackoverflow https://stackoverflow.com/questions/49494535/moving-the-first-array-element-to-end-in-c-sharp
120- . Skip ( 1 )
121- . Concat ( _laneNotes [ ( int ) type ] . Take ( 1 ) )
122- . ToArray ( ) ; //TODO: No stackoverflow code
123- //Do timing stuff
124- if ( beatDif < _timingInterval * 2 )
229+ if ( beatDif < _timingInterval * 1 )
125230 {
126231 GD . Print ( "Perfect" ) ;
127- Enemy . TakeDamage ( 10 ) ;
232+ Enemy . TakeDamage ( 3 ) ;
128233 NotePlacementBar . HitNote ( ) ;
234+ NotePlacementBar . ComboText ( "Perfect!" ) ;
129235 }
130- else if ( beatDif < _timingInterval * 4 )
236+ else if ( beatDif < _timingInterval * 2 )
131237 {
132238 GD . Print ( "Good" ) ;
133- Enemy . TakeDamage ( 5 ) ;
239+ Enemy . TakeDamage ( 1 ) ;
134240 NotePlacementBar . HitNote ( ) ;
241+ NotePlacementBar . ComboText ( "Good" ) ;
135242 }
136- else if ( beatDif < _timingInterval * 6 )
243+ else if ( beatDif < _timingInterval * 3 )
137244 {
138- GD . Print ( "Okay " ) ;
139- Enemy . TakeDamage ( 1 ) ;
245+ GD . Print ( "Ok " ) ;
246+ Player . TakeDamage ( 1 ) ;
140247 NotePlacementBar . HitNote ( ) ;
248+ NotePlacementBar . ComboText ( "Okay" ) ;
141249 }
142250 else
143251 {
144252 GD . Print ( "Miss" ) ;
145- Player . TakeDamage ( 10 ) ;
253+ Player . TakeDamage ( 2 ) ;
146254 NotePlacementBar . MissNote ( ) ;
255+ NotePlacementBar . ComboText ( "Miss" ) ;
147256 }
148257 }
149-
150- private void CheckNoteTiming ( NoteArrow . ArrowType type )
151- {
152- double curBeat = TimeKeeper . CurrentTime / ( 60 / ( double ) _curSong . Bpm ) ;
153- if ( _laneNotes [ ( int ) type ] . Length == 0 )
154- {
155- PlayerAddNote ( type , ( int ) curBeat ) ;
156- return ;
157- }
158- double beatDif = Math . Abs ( curBeat - _laneNotes [ ( int ) type ] . First ( ) . Beat ) ;
159- if ( beatDif > 1 )
160- {
161- PlayerAddNote ( type , ( int ) curBeat ) ;
162- return ;
163- }
164- GD . Print ( "Note Hit. Dif: " + beatDif ) ;
165- CM . HandleNote ( type ) ;
166- handleTiming ( type , beatDif ) ;
167- }
258+ #endregion
168259
169260 private void PlayerAddNote ( NoteArrow . ArrowType type , int beat )
170261 {
171- //TODO: notes currently can only be placed in first loop.
172- // placed notes are also non-interactable
173-
174262 // can also add some sort of keybind here to also have pressed
175263 // in case the user just presses the note too early and spawns a note
176264 GD . Print (
@@ -181,8 +269,9 @@ private void PlayerAddNote(NoteArrow.ArrowType type, int beat)
181269 ) ;
182270 if ( NotePlacementBar . CanPlaceNote ( ) )
183271 {
184- CM . CreateNote ( type , beat ) ;
185- NotePlacementBar . PlacedNote ( ) ;
272+ Note exampleNote = new Note ( type , beat % CM . BeatsPerLoop ) ;
273+ if ( AddNoteToLane ( exampleNote , false ) )
274+ NotePlacementBar . PlacedNote ( ) ;
186275 }
187276 }
188277}
0 commit comments