@@ -32,6 +32,18 @@ public class VirtualReplayPlayer
3232 /// The score processor for the virtual replay.
3333 /// </summary>
3434 public ScoreProcessorKeys ScoreProcessor { get ; }
35+
36+
37+ /// <summary>
38+ /// All of the mines that are currently active and available.
39+ /// </summary>
40+ public List < HitObjectInfo > ActiveMines { get ; }
41+
42+
43+ /// <summary>
44+ /// The list of active mines that are scheduled for removal.
45+ /// </summary>
46+ public List < HitObjectInfo > ActiveMinesToRemove { get ; set ; }
3547
3648 /// <summary>
3749 /// All of the HitObjects that are currently active and available.
@@ -95,8 +107,22 @@ public VirtualReplayPlayer(Replay replay, Qua map, JudgementWindows windows = nu
95107
96108 ActiveHitObjects = new List < HitObjectInfo > ( ) ;
97109 ActiveHeldLongNotes = new List < HitObjectInfo > ( ) ;
110+ ActiveMines = new List < HitObjectInfo > ( ) ;
98111
99- map . HitObjects . ForEach ( x => ActiveHitObjects . Add ( x ) ) ;
112+ map . HitObjects . ForEach ( x =>
113+ {
114+ switch ( x . Type )
115+ {
116+ case HitObjectType . Normal :
117+ ActiveHitObjects . Add ( x ) ;
118+ break ;
119+ case HitObjectType . Mine :
120+ ActiveMines . Add ( x ) ;
121+ break ;
122+ default :
123+ throw new ArgumentOutOfRangeException ( ) ;
124+ }
125+ } ) ;
100126
101127 // Add virtual key bindings based on the game mode of the replay.
102128 switch ( Map . Mode )
@@ -171,6 +197,7 @@ public void PlayNextFrame()
171197 // Store the objects that need to be removed from the list of active objects.
172198 ActiveHitObjectsToRemove = new List < HitObjectInfo > ( ) ;
173199 ActiveHeldLongNotesToRemove = new List < HitObjectInfo > ( ) ;
200+ ActiveMinesToRemove = new List < HitObjectInfo > ( ) ;
174201
175202 if ( CurrentFrame < Replay . Frames . Count )
176203 {
@@ -207,6 +234,8 @@ private void HandleKeyPressesInFrame()
207234 // Retrieve a list of the key press states in integer form.
208235 var currentFramePressed = Replay . KeyPressStateToLanes ( Replay . Frames [ CurrentFrame ] . Keys ) ;
209236 var previousFramePressed = CurrentFrame > 0 ? Replay . KeyPressStateToLanes ( Replay . Frames [ CurrentFrame - 1 ] . Keys ) : new List < int > ( ) ;
237+
238+ var previousFrameTime = CurrentFrame > 0 ? Replay . Frames [ CurrentFrame - 1 ] . Time : Time ;
210239
211240 // Update the key press state in the store.
212241 for ( var i = 0 ; i < InputKeyStore . Count ; i ++ )
@@ -217,6 +246,33 @@ private void HandleKeyPressesInFrame()
217246 . Concat ( previousFramePressed . Except ( currentFramePressed ) )
218247 . ToList ( ) ;
219248
249+ foreach ( var lane in previousFramePressed )
250+ {
251+ foreach ( var mine in ActiveMines )
252+ {
253+ var endTime = mine . IsLongNote ? mine . EndTime : mine . StartTime ;
254+ if ( mine . Lane == lane + 1
255+ && endTime + ScoreProcessor . JudgementWindow [ Judgement . Marv ] > previousFrameTime
256+ && Time >= mine . StartTime - ScoreProcessor . JudgementWindow [ Judgement . Marv ] )
257+ {
258+ // Calculate the hit difference.
259+ var hitDifference =
260+ mine . StartTime - ScoreProcessor . JudgementWindow [ Judgement . Marv ] > previousFrameTime
261+ ? ( int ) ScoreProcessor . JudgementWindow [ Judgement . Marv ]
262+ : mine . StartTime - previousFrameTime ;
263+
264+ // Add a new hit stat to the score processor.
265+ var stat = new HitStat ( HitStatType . Miss , KeyPressType . Press , mine , Time , Judgement . Miss , hitDifference ,
266+ ScoreProcessor . Accuracy , ScoreProcessor . Health ) ;
267+
268+ ScoreProcessor . Stats . Add ( stat ) ;
269+
270+ // Object needs to be removed from ActiveObjects.
271+ ActiveMinesToRemove . Add ( mine ) ;
272+ }
273+ }
274+ }
275+
220276 // Go through each frame and handle key presses/releases.
221277 foreach ( var key in keyDifferences )
222278 {
@@ -323,6 +379,7 @@ private void HandleKeyPressesInFrame()
323379 // Remove all active objects after handling key presses/releases.
324380 ActiveHitObjectsToRemove . ForEach ( x => ActiveHitObjects . Remove ( x ) ) ;
325381 ActiveHeldLongNotesToRemove . ForEach ( x => ActiveHeldLongNotes . Remove ( x ) ) ;
382+ ActiveMinesToRemove . ForEach ( x => ActiveMines . Remove ( x ) ) ;
326383 }
327384
328385 /// <summary>
@@ -390,10 +447,32 @@ private void HandleMissedHitObjects()
390447 break ;
391448 }
392449 }
450+ // Handle missed mines.
451+ foreach ( var hitObject in ActiveMines )
452+ {
453+ var endTime = hitObject . IsLongNote ? hitObject . EndTime : hitObject . StartTime ;
454+ if ( Time > endTime + ScoreProcessor . JudgementWindow [ Judgement . Marv ] )
455+ {
456+ // Add a miss to the score.
457+ ScoreProcessor . CalculateScore ( Judgement . Marv ) ;
458+
459+ // Create a new HitStat to add to the ScoreProcessor.
460+ var stat = new HitStat ( HitStatType . Hit , KeyPressType . None , hitObject , hitObject . StartTime , Judgement . Marv , 0 ,
461+ ScoreProcessor . Accuracy , ScoreProcessor . Health ) ;
462+
463+ ScoreProcessor . Stats . Add ( stat ) ;
464+ ActiveMinesToRemove . Add ( hitObject ) ;
465+ }
466+ else if ( Time < hitObject . StartTime - ScoreProcessor . JudgementWindow [ Judgement . Marv ] )
467+ {
468+ break ;
469+ }
470+ }
393471
394472 // Remove all objects
395473 ActiveHitObjectsToRemove . ForEach ( x => ActiveHitObjects . Remove ( x ) ) ;
396474 ActiveHeldLongNotesToRemove . ForEach ( x => ActiveHeldLongNotes . Remove ( x ) ) ;
475+ ActiveMinesToRemove . ForEach ( x => ActiveMines . Remove ( x ) ) ;
397476 }
398477
399478 /// <summary>
0 commit comments