Esempio n. 1
0
        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();
            }
        }