public CircleObject(CircleObject baseInstance) { //Copy from baseInstance BaseLocation = baseInstance.BaseLocation; StartTime = baseInstance.StartTime; Type = baseInstance.Type; Effect = baseInstance.Effect; Beatmap = baseInstance.Beatmap; }
public CircleObject(CircleObject baseInstance) { //Copy from baseInstance Location = baseInstance.Location; Radius = baseInstance.Radius; StartTime = baseInstance.StartTime; Type = baseInstance.Type; Effect = baseInstance.Effect; }
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); } }
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); } }