Example #1
0
 public CircleObject(CircleObject baseInstance)
 {
     //Copy from baseInstance
     BaseLocation = baseInstance.BaseLocation;
     StartTime    = baseInstance.StartTime;
     Type         = baseInstance.Type;
     Effect       = baseInstance.Effect;
     Beatmap      = baseInstance.Beatmap;
 }
Example #2
0
 public CircleObject(CircleObject baseInstance)
 {
     //Copy from baseInstance
     Location = baseInstance.Location;
     Radius = baseInstance.Radius;
     StartTime = baseInstance.StartTime;
     Type = baseInstance.Type;
     Effect = baseInstance.Effect;
 }
 public CircleObject(CircleObject baseInstance)
 {
     //Copy from baseInstance
     Location  = baseInstance.Location;
     Radius    = baseInstance.Radius;
     StartTime = baseInstance.StartTime;
     Type      = baseInstance.Type;
     Effect    = baseInstance.Effect;
 }
Example #4
0
 public SliderObject(CircleObject baseInstance) : base(baseInstance)
 {
 }
        public tpHitObject(CircleObject BaseHitObject)
        {
            this.BaseHitObject = BaseHitObject;

            // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
            float ScalingFactor = (52 / BaseHitObject.Radius);
            NormalizedStartPosition = BaseHitObject.Location * ScalingFactor;
            

            // Calculate approximation of lazy movement on the slider
            if (BaseHitObject.GetType() == typeof(SliderObject))
            {
                SliderObject hO = (SliderObject)BaseHitObject;

                float SliderFollowCircleRadius = BaseHitObject.Radius * 3; // Not sure if this is correct, but here we do not need 100% exact values. This comes pretty darn close in my tests.

                double SegmentLength = hO.Length / hO.RepeatCount;
                double SegmentEndTime = hO.StartTime + SegmentLength;

                // For simplifying this step we use actual osu! coordinates and simply scale the length, that we obtain by the ScalingFactor later
                Point2 CursorPos = hO.Location; // 

                // Actual computation of the first lazy curve
                for (int Time = (int)hO.StartTime + LAZY_SLIDER_STEP_LENGTH; Time < SegmentEndTime; Time += LAZY_SLIDER_STEP_LENGTH)
                {
                    Point2 Difference = hO.PositionAtTime(Time) - CursorPos;
                    float Distance = Difference.Length;

                    // Did we move away too far?
                    if (Distance > SliderFollowCircleRadius)
                    {
                        // Yep, we need to move the cursor
                        Difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference
                        Distance -= SliderFollowCircleRadius;
                        CursorPos += Difference * Distance; // We move the cursor just as far as needed to stay in the follow circle
                        LazySliderLengthFirst += Distance;
                    }
                }

                LazySliderLengthFirst *= ScalingFactor;
                // If we have an odd amount of repetitions the current position will be the end of the slider. Note that this will -always- be triggered if
                // hO.SegmentCount <= 1, because hO.SegmentCount can not be smaller than 1. Therefore NormalizedEndPosition will always be initialized
                if (hO.RepeatCount % 2 == 1)
                {
                    NormalizedEndPosition = CursorPos * ScalingFactor;
                }

                // If we have more than one segment, then we also need to compute the length ob subsequent lazy curves. They are different from the first one, since the first
                // one starts right at the beginning of the slider.
                if(hO.RepeatCount > 1)
                {
                    // Use the next segment
                    SegmentEndTime += SegmentLength;

                    for (double Time = SegmentEndTime - SegmentLength + LAZY_SLIDER_STEP_LENGTH; Time < SegmentEndTime; Time += LAZY_SLIDER_STEP_LENGTH)
                    {
                        Point2 Difference = hO.PositionAtTime((int)Time) - CursorPos;
                        float Distance = Difference.Length;

                        // Did we move away too far?
                        if (Distance > SliderFollowCircleRadius)
                        {
                            // Yep, we need to move the cursor
                            Difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference
                            Distance -= SliderFollowCircleRadius;
                            CursorPos += Difference * Distance; // We move the cursor just as far as needed to stay in the follow circle
                            LazySliderLengthSubsequent += Distance;
                        }
                    }

                    LazySliderLengthSubsequent *= ScalingFactor;
                    // If we have an even amount of repetitions the current position will be the end of the slider
                    if (hO.RepeatCount % 2 == 1)
                    {
                        NormalizedEndPosition = CursorPos * ScalingFactor;
                    }
                }
            }
            // We have a normal HitCircle or a spinner
            else
            {
                NormalizedEndPosition = (BaseHitObject.Location * ScalingFactor);
            }
        }
Example #6
0
        private void Parse(string bm)
        {
            Info.Filename = bm;
            Info.BeatmapHash = MD5FromFile(bm);
            using (StreamReader sR = new StreamReader(bm))
            {
                string currentSection = "";

                while (sR.Peek() != -1)
                {
                    string line = sR.ReadLine();

                    //Check for section tag
                    if (line.StartsWith("["))
                    {
                        currentSection = line;
                        continue;
                    }

                    //Check for commented-out line
                    //or blank lines
                    if (line.StartsWith("//") || line.Length == 0)
                        continue;

                    //Check for version string
                    if (line.StartsWith("osu file format"))
                        Info.Format = Convert.ToInt32(line.Substring(17).Replace(Environment.NewLine, "").Replace(" ", ""));

                    //Do work for [General], [Metadata], [Difficulty] and [Editor] sections
                    if ((currentSection == "[General]") || (currentSection == "[Metadata]") || (currentSection == "[Difficulty]") || (currentSection == "[Editor]"))
                    {
                        string[] reSplit = line.Split(':');
                        string cProperty = reSplit[0].TrimEnd();

                        bool isValidProperty = false;
                        foreach (string k in BM_Sections.Keys)
                        {
                            if (k.Contains(cProperty))
                                isValidProperty = true;
                        }
                        if (!isValidProperty)
                            continue;

                        //Check for blank value
                        string cValue = reSplit[1].Trim();

                        //Import properties into Info
                        switch (cProperty)
                        {
                            case "EditorBookmarks":
                                {
                                    string[] marks = cValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                                    foreach (string m in marks.Where(m => m != ""))
                                        Info.EditorBookmarks.Add(Convert.ToInt32(m));
                                }
                                break;
                            case "Bookmarks":
                                {
                                    string[] marks = cValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                                    foreach (string m in marks.Where(m => m != ""))
                                        Info.Bookmarks.Add(Convert.ToInt32(m));
                                }
                                break;
                            case "Tags":
                                string[] tags = cValue.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                                foreach (string t in tags)
                                    Info.Tags.Add(t);
                                break;
                            case "Mode":
                                Info.Mode = (GameMode)Convert.ToInt32(cValue);
                                break;
                            case "OverlayPosition":
                                Info.OverlayPosition = (OverlayOptions)Enum.Parse(typeof(OverlayOptions), cValue);
                                break;
                            case "AlwaysShowPlayfield":
                                Info.AlwaysShowPlayfield = Convert.ToBoolean(Convert.ToInt32(cValue));
                                break;
                            default:
                                FieldInfo fi = Info.GetType().GetField(cProperty);
                                PropertyInfo pi = Info.GetType().GetProperty(cProperty);
                                if (fi != null)
                                {
                                    if (fi.FieldType == typeof(float?))
                                        fi.SetValue(Info, (float?)Convert.ToDouble(cValue));
                                    if (fi.FieldType == typeof(float))
                                        fi.SetValue(Info, (float)Convert.ToDouble(cValue));
                                    else if ((fi.FieldType == typeof(int?)) || (fi.FieldType == typeof(int)))
                                        fi.SetValue(Info, Convert.ToInt32(cValue));
                                    else if (fi.FieldType == typeof(string))
                                        fi.SetValue(Info, cValue);
                                    break;
                                }
                                if (pi.PropertyType == typeof(float?))
                                    pi.SetValue(Info, (float?)Convert.ToDouble(cValue), null);
                                if (pi.PropertyType == typeof(float))
                                    pi.SetValue(Info, (float)Convert.ToDouble(cValue), null);
                                else if ((pi.PropertyType == typeof(int?)) || (pi.PropertyType == typeof(int)))
                                    pi.SetValue(Info, Convert.ToInt32(cValue), null);
                                else if (pi.PropertyType == typeof(string))
                                    pi.SetValue(Info, cValue, null);
                                break;
                        }
                        continue;
                    }

                    //The following are version-dependent, the version is stored as a numeric value inside Info.Format
                    //Do work for [Events] section
                    if (currentSection == "[Events]")
                    {
                        string[] reSplit = line.Split(',');
                        switch (reSplit[0].ToLower())
                        {
                            case "0":
                            case "1":
                            case "video":
                                Info.Events.Add(new ContentEvent
                                {
                                    Type = reSplit[0].ToLower() == "1" || reSplit[0].ToLower() == "video" ? ContentType.Video : ContentType.Image,
                                    StartTime = Convert.ToInt32(reSplit[1]),
                                    Filename = reSplit[2].Replace("\"", "")
                                });
                                break;
                            case "2":
                                Info.Events.Add(new BreakEvent
                                {
                                    StartTime = Convert.ToInt32(reSplit[1]),
                                    EndTime = Convert.ToInt32(reSplit[2])
                                });
                                break;
                            case "3":
                                Info.Events.Add(new BackgroundColourEvent
                                {
                                    StartTime = Convert.ToInt32(reSplit[1]),
                                    Colour = new Colour
                                    {
                                        R = Convert.ToInt32(reSplit[2]),
                                        G = Convert.ToInt32(reSplit[3]),
                                        B = Convert.ToInt32(reSplit[4])
                                    },
                                });
                                break;
                        }
                    }

                    //Do work for [TimingPoints] section
                    if (currentSection == "[TimingPoints]")
                    {
                        TimingPoint tempTimingPoint = new TimingPoint();

                        float[] values = { 0, 0, 4, 0, 0, 100, 0, 0, 0 };
                        string[] reSplit = line.Split(',');
                        for (int i = 0; i < reSplit.Length; i++)
                            values[i] = (float)Convert.ToDouble(reSplit[i]);
                        tempTimingPoint.Time = (float)Convert.ToDouble(values[0]);
                        tempTimingPoint.BpmDelay = (float)Convert.ToDouble(values[1]);
                        tempTimingPoint.TimeSignature = Convert.ToInt32(values[2]);
                        tempTimingPoint.SampleSet = Convert.ToInt32(values[3]);
                        tempTimingPoint.CustomSampleSet = Convert.ToInt32(values[4]);
                        tempTimingPoint.VolumePercentage = Convert.ToInt32(values[5]);
                        tempTimingPoint.InheritsBPM = !Convert.ToBoolean(Convert.ToInt32(values[6]));
                        tempTimingPoint.VisualOptions = (TimingPointOptions)Convert.ToInt32(values[7]);
                        Info.TimingPoints.Add(tempTimingPoint);
                    }

                    //Do work for [Colours] section
                    if (currentSection == "[Colours]")
                    {
                        string property = line.Substring(0, line.IndexOf(':', 1)).Trim();
                        string value = line.Substring(line.IndexOf(':', 1) + 1).Trim();
                        string[] reSplit = value.Split(',');

                        if (property.Length > 5 && property.Substring(0, 5) == "Combo")
                        {
                            Combo newCombo = new Combo
                            {
                                Colour = new Colour
                                {
                                    R = Convert.ToInt32(reSplit[0]),
                                    G = Convert.ToInt32(reSplit[1]),
                                    B = Convert.ToInt32(reSplit[2])
                                }
                            };
                            try
                            {
                                newCombo.ComboNumber = Convert.ToInt32(property.Substring(5, 1));
                            }
                            catch
                            {
                                Debug.Assert(false, "Invalid combonumber at index 5. " + line);
                                continue;
                            }
                        }
                        else if (property.Length > 5 && property == "SliderBorder")
                        {
                            Info.SliderBorder = new Colour
                            {
                                R = Convert.ToInt32(reSplit[0]),
                                G = Convert.ToInt32(reSplit[1]),
                                B = Convert.ToInt32(reSplit[2])
                            };
                        }
                    }

                    //Do work for [HitObjects] section
                    if (currentSection == "[HitObjects]")
                    {
                        string[] reSplit = line.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                        CircleObject newObject = new CircleObject
                        {
                            Radius = 40 - 4 * (Info.CircleSize - 2),
                            Location = new Point2(Convert.ToInt32(reSplit[0]), Convert.ToInt32(reSplit[1])),
                            StartTime = (float)Convert.ToDouble(reSplit[2]),
                            Type = (HitObjectType)Convert.ToInt32(reSplit[3]),
                            Effect = (EffectType)Convert.ToInt32(reSplit[4])
                        };
                        if ((newObject.Type & HitObjectType.Slider) > 0)
                        {
                            newObject = new SliderObject(newObject);
                            ((SliderObject)newObject).Velocity = Info.SliderMultiplier;
                            switch (reSplit[5].Substring(0, 1))
                            {
                                case "B":
                                    ((SliderObject)newObject).Type = SliderType.Bezier;
                                    break;
                                case "C":
                                    ((SliderObject)newObject).Type = SliderType.CSpline;
                                    break;
                                case "L":
                                    ((SliderObject)newObject).Type = SliderType.Linear;
                                    break;
                                case "P":
                                    ((SliderObject)newObject).Type = SliderType.PSpline;
                                    break;
                            }
                            string[] pts = reSplit[5].Split(new[] { "|" }, StringSplitOptions.None);

                            //Todo: Check this
                            if (Format <= 4)
                                ((SliderObject)newObject).Points.Add(newObject.Location);

                            //Always exclude index 1, this will contain the type
                            for (int i = 1; i <= pts.Length - 1; i++)
                            {
                                Point2 p = new Point2((float)Convert.ToDouble(pts[i].Substring(0, pts[i].IndexOf(":", StringComparison.InvariantCulture))),
                                                            (float)Convert.ToDouble(pts[i].Substring(pts[i].IndexOf(":", StringComparison.InvariantCulture) + 1)));
                                ((SliderObject)newObject).Points.Add(p);
                            }
                            ((SliderObject)newObject).RepeatCount = Convert.ToInt32(reSplit[6]);
                            float tempMaxPoints;
                            if (float.TryParse(reSplit[7], out tempMaxPoints))
                                ((SliderObject)newObject).MaxPoints = tempMaxPoints;
                        }
                        if ((newObject.Type & HitObjectType.Spinner) > 0)
                        {
                            newObject = new SpinnerObject(newObject);
                            ((SpinnerObject)newObject).EndTime = (float)Convert.ToDouble(reSplit[5]);
                        }
                        Info.HitObjects.Add(newObject);
                    }
                }
            }

            //Copy the fields/properties of Info locally
            foreach (FieldInfo fi in Info.GetType().GetFields())
            {
                FieldInfo ff = GetType().GetField(fi.Name);
                ff.SetValue(this, fi.GetValue(Info));
            }
            foreach (PropertyInfo pi in Info.GetType().GetProperties())
            {
                PropertyInfo ff = GetType().GetProperty(pi.Name);
                ff.SetValue(this, pi.GetValue(Info, null), null);
            }
        }