internal override void CreateAutoplayReplay() { InputManager.ReplayScore.Replay = new List <bReplayFrame>(); List <bReplayFrame> replay = InputManager.ReplayScore.Replay; HitObjectManagerMania homMania = hitObjectManager as HitObjectManagerMania; replay.Add(new bReplayFrame(0, 0, speed, pButtonState.None)); for (int i = 0; i < hitObjectManager.hitObjectsCount; i++) { HitCircleMania note = (HitCircleMania)hitObjectManager.hitObjects[i]; if (note.EndTime < InputManager.ReplayStartTime) { note.IsHit = true; continue; } int val = 0; //inputmanager adjust hit position based on NeedAdjust //Change columns back to thier original to fit the input require. if (hitObjectManager.ManiaStage.SpecialStyleRight) { val = hitObjectManager.ManiaStage.Columns[note.Column == hitObjectManager.ManiaStage.Columns.Count - 1 ? 0 : note.Column + 1].KeyValue; } else { val = note.Column.KeyValue; } InsertReplay(replay, note.StartTime, val); if (note.ManiaType == ManiaNoteType.Long) { //find exist frames replay.ForEach(f => { if (f.time <= note.StartTime) { return; } if (f.time >= note.EndTime) { return; } f.mouseX = MergeValue((int)f.mouseX, val); }); InsertReplay(replay, note.EndTime - 1, -val); } else { InsertReplay(replay, note.StartTime + 1, -val); } } Player.currentScore.Replay = InputManager.ReplayScore.Replay; Player.currentScore.PlayerName = "osu!topus!"; }
private void AddCounter(HitCircleMania h) { switch (h.ManiaType) { case ManiaNoteType.Normal: CountNormal++; break; case ManiaNoteType.Long: CountLong++; break; } }
protected override void AddCircle(HitCircle h) { HitCircleMania hb = h as HitCircleMania; if (hb != null) { AddManiaNote(hb); return; } foreach (HitCircleMania note in ((HitCircleManiaRow)h).HitObjects) { AddManiaNote(note); } }
internal override IncreaseScoreType Hit(HitObject h) { lastHitObject = h; IncreaseScoreType hitValue = h.Hit(); int index = hitObjects.BinarySearch(h); currentHitObjectIndex = index < 0 ? ~index : index; if (hitValue == IncreaseScoreType.Ignore) { return(hitValue); } if (perfectMod && hitValue < IncreaseScoreType.ManiaHit300) { hitValue = IncreaseScoreType.MissMania; } HitCircleMania be = (HitCircleMania)h; if (ManiaStage.Skin.SeparateScore) { be.Column.Host.TriggerScoreIncrease(hitValue); } else { foreach (StageMania stage in ManiaStage) { stage.TriggerScoreIncrease(hitValue); } } if (hitValue > IncreaseScoreType.Ignore) { Player.Instance.OnHitSuccess(h); be.Column.AddHitLight(); //Rest LN hitlight at the end of notes be.Column.HasLongHitLight = false; } OnHit(hitValue, "", h); return(hitValue); }
private void AddManiaNote(HitCircleMania note) { if (note.StartTime < FirstNoteTime) { FirstNoteTime = note.StartTime; } if (note.EndTime > LastNoteTime) { LastNoteTime = note.EndTime; latestHitObject = note; } if (note.EndTime - note.StartTime > LongestTime) { LongestTime = note.EndTime - note.StartTime; } AddCounter(note); Add(note, false); }
internal override void UpdateHitObjects() { //notes hit here. bool[] hitted = new bool[ManiaStage.Columns.Count];//some notes have already hitted in that column Player.IsSliding = false; foreach (HitObject h in hitObjectsMinimal) { HitCircleMania curr = (HitCircleMania)h; curr.Update(); if (curr.IsHit) { //note been hitted too early, and it's still above judge line. if (curr.StartTime > AudioEngine.Time) { hitted[curr.Column] = true; } continue; } if (hitted[curr.Column] || AudioEngine.Time < curr.StartTime - hitWindowEarly) { continue; } //give a 0 value hit if (AudioEngine.Time - HitWindow100 >= curr.EndTime && !curr.IsHit) { Hit(curr); continue; } if (curr.Column.KeyState && !curr.Column.KeyStateLast) { //press if (curr.ManiaType == ManiaNoteType.Normal || curr.ManiaType == ManiaNoteType.Special) { curr.Pressed = true; curr.TimePress = AudioEngine.Time; hitted[curr.Column] = true; nextSound[curr.Column] = null; Player.Instance.LogHitError(curr); //mania here Hit(curr); } else if (curr.ManiaType == ManiaNoteType.Long && !curr.Pressed) { HitCircleManiaLong currLong = (HitCircleManiaLong)curr; Player.IsSliding = true; curr.TimePress = AudioEngine.Time; hitted[curr.Column] = true; nextSound[curr.Column] = null; curr.Pressed = true; currLong.TimesPressed++; //Only the first press should be logged if (currLong.TimesPressed <= 1) { Player.Instance.LogHitError(curr); } HitStart(currLong); curr.Column.HasLongHitLight = true; } } else if (curr.Column.KeyState && curr.Column.KeyStateLast) { //holding if (curr.ManiaType == ManiaNoteType.Long && curr.Pressed) { HitCircleManiaLong currLong = (HitCircleManiaLong)curr; hitted[curr.Column] = true; nextSound[curr.Column] = null; Holding(currLong); Player.IsSliding = true; curr.Column.HasLongHitLight = true; } } else if (!curr.Column.KeyState) { //not pressing if (curr.ManiaType == ManiaNoteType.Long) { HitCircleManiaLong currLong = (HitCircleManiaLong)curr; if (curr.Pressed) { curr.Pressed = false; curr.TimeRelease = AudioEngine.Time; currLong.TimesReleased++; //Only the first release should be logged if (currLong.TimesReleased <= 1) { Player.Instance.LogHitError(curr, AudioEngine.Time - curr.EndTime); } Hit(curr); } else if (!currLong.IsMissing) { Hit(curr); } } curr.Column.HasLongHitLight = false; } } if (Player.Instance.Status != PlayerStatus.Playing) { return; } for (int i = 0; i < ManiaStage.Columns.Count; i++) { ColumnMania column = ManiaStage.Columns[i]; //release time hax check if (column.KeyState && !column.KeyStateLast) { releaseTime[i] = AudioEngine.Time; } else if (!column.KeyState && column.KeyStateLast && releaseTime[i] != 0) { //press-release interval of normal player should be 40~70ms int interval = AudioEngine.Time - releaseTime[i]; if (interval < 20) { PressHaxCount += 2; } else if (interval < 38) //based on cheat replay analysis { PressHaxCount++; } if (interval < 100) { pressHaxChecked++; //interval less than 100ms should be count as normal hit. } } //empty press if (hitted[i]) { continue; } if (column.KeyState && !column.KeyStateLast) { if (nextSound[i] == null) { HitCircleMania h = (HitCircleMania)hitObjects.Find(obj => obj.StartTime > AudioEngine.Time && ((HitCircleMania)obj).Column == i); nextSound[i] = h; } if (nextSound[i] != null) { nextSound[i].PlaySound(); } } } }
internal void ReCalculate(int audioTime) { bool paused = false; bool upsideDown = ManiaStage.Skin.UpsideDown; if (audioTime > LastNoteTime) { return; } if (AudioEngine.AudioState == AudioStates.Playing) { AudioEngine.TogglePause(); paused = true; } //Clear anything from a previous ReCalculate if (barLines != null) { barLines.ForEach(b => b.Transformations.Clear()); barLines.Clear(); } else { barLines = new List <pSprite>(); } foreach (HitObject h in hitObjects) { if (((HitCircleMania)h).IsFinished) { continue; } h.SpriteCollection.ForEach(s => s.Transformations.RemoveAll(t => (t.Type & transformationsToClear) > 0)); } //Start a fresh forward play queue foreach (StageMania stage in ManiaStage) { stage.SpriteManagerNotes.Clear(false); stage.SpriteManagerNotes.ForwardPlayOptimisedAdd = true; } //Mania-specific maps use all control points while autoconverts only use BPM changes List <ControlPoint> beatmapControlPoints = Beatmap.PlayMode != PlayModes.OsuMania ? Beatmap.TimingPoints : Beatmap.ControlPoints; if (beatmapControlPoints.Count == 0 || hitObjects.Count == 0) { return; } //For moving things on the play field at the correct speed. It is "collapsed" so you can iterate it in both directions speedChanges = new List <ControlPoint>(); //For placing bar lines timingChanges = new List <ControlPoint>(); double lastBeatLength = beatmapControlPoints[0].BeatLength; foreach (ControlPoint p in beatmapControlPoints) { ControlPoint t = new ControlPoint(); t.TimingChange = false; //So that we can guarantee the ordering when searching t.BeatLength = p.BeatLength < 0 ? lastBeatLength * p.BpmMultiplier : p.BeatLength; t.TimeSignature = p.TimeSignature; t.Offset = p.Offset; //If an SV change right after the initial BPM is before the first note, apply it from the start of time just like the initial BPM if (speedChanges.Count == 1 && p.BeatLength < 0 && t.Offset < FirstNoteTime) { t.Offset = speedChanges[0].Offset; } //Collapse changes at the same offset if (speedChanges.Count > 0 && t.Offset <= speedChanges[speedChanges.Count - 1].Offset) { speedChanges.RemoveAt(speedChanges.Count - 1); } //Collapse changes that leave us at the same beat length if (speedChanges.Count == 0 || t.BeatLength != speedChanges[speedChanges.Count - 1].BeatLength) { speedChanges.Add(t); } //Uncollapsed timing changes if (p.BeatLength >= 0) { timingChanges.Add(t); lastBeatLength = t.BeatLength; } } //Move the first offset of the control points to before the song starts //Doing this after collapsing speed changes makes it so that SV changes to the opening BPM apply from the very start of the song double preTimeStart = beatmapControlPoints[0].Offset; while (preTimeStart >= 0) { preTimeStart -= beatmapControlPoints[0].BeatLength * (int)beatmapControlPoints[0].TimeSignature; } preTimeStart -= beatmapControlPoints[0].BeatLength * (int)beatmapControlPoints[0].TimeSignature; speedChanges[0].Offset = preTimeStart; timingChanges[0].Offset = preTimeStart; //Add a final control point to speed changes which is useful as an endpoint speedChanges.Add(new ControlPoint() { Offset = Double.PositiveInfinity }); //Add notes for (int i = 0; i < hitObjects.Count; i++) { HitCircleMania note = (HitCircleMania)hitObjects[i]; HitCircleManiaLong noteLong = hitObjects[i] as HitCircleManiaLong; if (note.IsFinished) { continue; } float headHeight = SpriteHeight(note.s_note); addTransformations(note.s_note, note.StartTime, headHeight); if (noteLong != null) { float rearHeight = SpriteHeight(noteLong.s_rear); addTransformations(noteLong.s_rear, note.EndTime, rearHeight); addTransformations(noteLong.s_body, note.StartTime, headHeight / 2f, -headHeight / 2f, note.EndTime - note.StartTime); //addTransformations very deliberately breaks up the transformations into three parts //From offscreen to the StartTime -> from the StartTime to the EndTime -> from the end time to offscreen //Using these StartTime and EndTime endpoints, we can find the distance between the head and rear sprites float start = 0, end = 0; if (noteLong.Length > 0) { start = noteLong.s_body.Transformations.Find(t => t.Type == TransformationType.MovementY && t.TagNumeric == TransformationTag.BetweenStartEndTime).StartFloat; end = noteLong.s_body.Transformations.FindLast(t => t.Type == TransformationType.MovementY && t.TagNumeric == TransformationTag.BetweenStartEndTime).EndFloat; } float length = Math.Abs(end - start) * 1.6f; noteLong.s_body.VectorScale.Y = noteLong.s_body.FlipVertical ? -1f : 1f; if (noteLong.s_body.DrawDimensionsManualOverride) { noteLong.s_body.WrapTexture = false; noteLong.s_body.DrawDimensionsManualOverride = false; noteLong.s_body.UpdateTextureSize(); } ManiaNoteBodyStyle style = ManiaStage.Skin.GetNoteBodyStyle(note.Column); if (style == ManiaNoteBodyStyle.Stretch) { noteLong.s_body.VectorScale.Y *= length / noteLong.s_body.DrawHeight; } else { noteLong.s_body.WrapTexture = true; noteLong.s_body.VectorScale.Y *= ManiaStage.HeightRatio; float height = noteLong.s_body.DrawHeight / ManiaStage.HeightRatio; noteLong.s_body.DrawHeight = noteLong.s_body.Height = (int)(length / ManiaStage.HeightRatio); noteLong.s_body.DrawDimensionsManualOverride = true; switch (style) { case ManiaNoteBodyStyle.RepeatBottom: default: noteLong.s_body.DrawTop = 0; break; case ManiaNoteBodyStyle.RepeatTop: noteLong.s_body.DrawTop = (int)(height - length); break; case ManiaNoteBodyStyle.RepeatTopAndBottom: noteLong.s_body.DrawTop = (int)((height - length) / 2f); break; } } noteLong.s_body.UpdateTextureAlignment(); if (noteLong.IsFrozen || noteLong.IsUnfrozen) { FreezeNote(noteLong); } if (noteLong.IsUnfrozen) { UnfreezeNote(noteLong); } } note.Column.Host.SpriteManagerNotes.Add(note.SpriteCollection); } //Add barlines if (!Configuration.ConfigManager.sIgnoreBarline) { Color c = ManiaStage.Skin.GetColour(@"Barline"); for (int i = 0; i < timingChanges.Count; i++) { double beatTime = timingChanges[i].Offset; double timeEnd = LastNoteTime + 1; if (i + 1 < timingChanges.Count) { timeEnd = timingChanges[i + 1].Offset - 1; } if (timeEnd < audioTime - 1000) { continue; } while (beatTime < timeEnd) { foreach (StageMania stage in ManiaStage) { pSprite p = new pSprite(GameBase.WhitePixel, Fields.TopLeft, Origins.CentreLeft, Clocks.Audio, new Vector2(0, -100), 0.62F, false, c); p.VectorScale = new Vector2(stage.Width * 1.6f, ManiaStage.Skin.BarlineHeight); addTransformations(p, beatTime, ManiaStage.Skin.BarlineHeight); barLines.Add(p); stage.SpriteManagerNotes.Add(p); } beatTime += timingChanges[i].BeatLength * (int)timingChanges[i].TimeSignature; } } } //Add arrows int arrowTime = FirstNoteTime - 1000; for (int arrowCount = 0; arrowTime > speedChanges[0].Offset && arrowCount < 3; arrowCount++) { foreach (StageMania stage in ManiaStage) { pSprite arrow = new pSprite(ManiaStage.Skin.Load(@"WarningArrow", @"mania-warningarrow", ManiaStage.SkinSource), Fields.TopLeft, ManiaStage.FlipOrigin(Origins.TopCentre), Clocks.Audio, new Vector2(stage.Width / 2, 0), 0.63F, false, Color.White); if (SkinManager.Current.Version >= 2.4 || stage.HeightRatio < 1f) { arrow.Scale = stage.MinimumRatio; } arrow.FlipVertical = upsideDown; //The origin of the arrow is the end of its tail. This is because the tail needs to be 1 second before the first note addTransformations(arrow, arrowTime, -SpriteHeight(arrow)); barLines.Add(arrow); stage.SpriteManagerNotes.Add(arrow); } arrowTime -= 1000; } //Stop adding to the forward play queue foreach (StageMania stage in ManiaStage) { stage.SpriteManagerNotes.ForwardPlayOptimisedAdd = false; } if (paused) { AudioEngine.TogglePause(); } }