protected override int[] GetTickLocations() { double length = Math.Round(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture), 4); int sliderruns = Int32.Parse(HitObjectParser.GetProperty(id, "repeat")); //Only need ticks for one slider length (no repeats needed) //Also no need for double conversion since TickCount is always divisible by sliderruns int tickcount = this.GetTickCount() / sliderruns; double slidervelocity = this.GetSliderVelocity(); double tickrate = Double.Parse(map.GetTag("Difficulty", "SliderTickRate"), CultureInfo.InvariantCulture); double ticklength = Math.Round(slidervelocity * (100 / tickrate), 4); if (length <= ticklength) { return(new int[0]); } Point[] ticklocs = curve.GetTickLocations(ticklength, tickcount); List <int> xcoords = new List <int>(); foreach (Point i in ticklocs) { xcoords.Add(i.IntX()); } return(xcoords.ToArray()); }
protected override int[] GetTickLocations() { double length = Math.Round(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture), 4); double slidervelocity = this.GetSliderVelocity(); double tickrate = Double.Parse(map.GetTag("Difficulty", "SliderTickRate"), CultureInfo.InvariantCulture); double ticklength = Math.Round(slidervelocity * (100 / tickrate), 4); if (length <= ticklength) { return(new int[0]); } List <int> ticks = new List <int>(); //Will represent where the next tick is in the slider double calclength = ticklength; //While we haven't fallen off the end of the slider while (calclength < length) { ticks.Add(curve.GetPointAlong(calclength).IntX()); //Move down the slider by a ticklength calclength += ticklength; } return(ticks.ToArray()); }
//Calculates the Milliseconds per Beat at a specified time by searching //through the entire timing points section //Timing points inside sliders don't affect the slider itself protected double GetMpB() { int ms = Int32.Parse(HitObjectParser.GetProperty(id, "time")); //Get all the timing sections of the beatmap string[] timings = this.map.GetSection("TimingPoints"); //Will hold the relevant timing point string timingpoint = null; //Store the index of the timing point just in case we need to iterate backwards later int index; //Find the section that applies to the given time for (index = 0; index < timings.Length; index++) { //Split the string by commas to get all the relevant times string[] attributes = timings[index].Split(','); //Trim each string just in case attributes = LibFuncs.TrimStringArray(attributes); //If the timing point is a higher time, then we want the previous timing section if (Double.Parse(attributes[0]) > ms) { //avoid accessing a negative timing point if (index == 0) { timingpoint = timings[0]; } else { timingpoint = timings[index - 1]; } break; } } //If the timing point needed is the very last one if (timingpoint == null) { timingpoint = timings[timings.Length - 1]; } //If the mpb is negative, then we need to search backwards to find a positive one if (Double.Parse(timingpoint.Split(',')[1], CultureInfo.InvariantCulture) < 0) { for (int i = index - 1; i >= 0; i--) { //Split the string by commas to get all the relevant times string[] attributes = timings[i].Split(','); //Trim each string just in case attributes = LibFuncs.TrimStringArray(attributes); if (Double.Parse(attributes[1]) > 0) { timingpoint = timings[i]; break; } } } return(Double.Parse(timingpoint.Split(',')[1], CultureInfo.InvariantCulture)); }
protected override Point GetLastPoint() { //Necessary to avoid cases where the pixellength is something like 105.000004005432 int length = Convert.ToInt32(Math.Floor(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture))); return(curve.GetPointAlong(length)); }
protected override int[] GetTickLocations() { //Necessary to avoid cases where the pixellength is something like 105.000004005432 int length = Convert.ToInt32(Math.Floor(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture))); double slidervelocity = this.GetSliderVelocity(); double tickrate = Double.Parse(map.GetTag("Difficulty", "SliderTickRate"), CultureInfo.InvariantCulture); int ticklength = (int)Math.Round(slidervelocity * (100 / tickrate)); if (length <= ticklength) { return(new int[0]); } List <int> ticks = new List <int>(); //Will represent where the next tick is in the slider int calclength = ticklength; //While we haven't fallen off the end of the slider while (calclength < length) { ticks.Add(curve.GetPointAlong(calclength).IntX()); //Move down the slider by a ticklength calclength += ticklength; } return(ticks.ToArray()); }
public CatmullSlider(string id, Beatmap amap) : base(id, amap) { Point initialcoord = new Point(); initialcoord.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); initialcoord.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); curve = new CatmullCurve(initialcoord, this.controlpoints); }
public int[] GetHitLocations() { //There is only one hit location for a circle, so just return an array //that holds that hit location string loc = HitObjectParser.GetProperty(circleid, "X"); return(new int[1] { Convert.ToInt32(loc) }); }
public int[] GetHitTimes() { //There is only one hit time for a circle, so just return an array //that holds that hit time string time = HitObjectParser.GetProperty(circleid, "time"); return(new int[1] { Convert.ToInt32(time) }); }
//TODO: Throw error if somethings messed up with the timing section //Calculates the slider velocity at a specified time using the default //velocity and the relevant timing section //Timing points inside sliders don't affect the slider itself protected double GetSliderVelocity() { int ms = Int32.Parse(HitObjectParser.GetProperty(id, "time")); //Get the default slider velocity of the beatmap double slidervelocity = Double.Parse(map.GetTag("Difficulty", "SliderMultiplier"), CultureInfo.InvariantCulture); //Get all the timing sections of the beatmap string[] timings = this.map.GetSection("TimingPoints"); //Will hold the relevant timing point string timingpoint = null; //Find the section that applies to the given time for (int i = 0; i < timings.Length; i++) { //Split the string by commas to get all the relevant times string[] attributes = timings[i].Split(','); //Trim each string just in case attributes = LibFuncs.TrimStringArray(attributes); //If the timing point is a higher time, then we want the previous timing section if (Double.Parse(attributes[0]) > ms) { //avoid accessing a negative timing point if (i == 0) { timingpoint = timings[0]; } else { timingpoint = timings[i - 1]; } break; } } //If the timing point needed is the very last one if (timingpoint == null) { timingpoint = timings[timings.Length - 1]; } string[] properties = timingpoint.Split(','); //If the offset is positive, then there is no slider multiplication if (Double.Parse(properties[1], CultureInfo.InvariantCulture) > 0) { return(slidervelocity); } //Otherwise the slider multiplier is 100 / abs(offset) else { double offset = Double.Parse(properties[1], CultureInfo.InvariantCulture); return(slidervelocity * (100 / Math.Abs(offset))); } }
//Get the correct type of hitobject to put into the hitobject variable public GenericHitObject(string id, Beatmap map) { HitObjectType objecttype = HitObjectParser.GetHitObjectType(id); if (objecttype == HitObjectType.Circle) { hitobject = new Circle(id); } else if (objecttype == HitObjectType.Slider) { string slidertype = HitObjectParser.GetProperty(id, "slidertype"); if (slidertype == "L") { hitobject = new LinearSlider(id, map); } //Special behavior is needed for passthrough sliders else if (slidertype == "P") { //Treat the slider differently depending on the number of control points string[] sliderpoints = HitObjectParser.GetProperty(id, "controlpoints").Split('|'); if (sliderpoints.Length == 1) { hitobject = new LinearSlider(id, map); } else if (sliderpoints.Length == 2) { hitobject = new PassthroughSlider(id, map); } else { hitobject = new BezierSlider(id, map); } } else if (slidertype == "B") { hitobject = new BezierSlider(id, map); } else if (slidertype == "C") { hitobject = new CatmullSlider(id, map); } } else if (objecttype == HitObjectType.Spinner) { hitobject = new Spinner(); } else { throw new ArgumentException("Error: id is invalid"); } }
//Calculated the same regardless of slider type, but depends on GetTickLocations and GetLastPoint public int[] GetHitLocations() { List <int> hitpoints = new List <int>(); List <int> ticklocs = new List <int>(); //Subtracting 1 returns the actual number of repeats int repeats = Int32.Parse(HitObjectParser.GetProperty(id, "repeat")) - 1; //Get the initial hit point of the slider //Split into three lines for readibility Point initialcoord = new Point(); initialcoord.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); initialcoord.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); //Get the first and last x-coordinates of the slider int beginpoint = initialcoord.IntX(); ticklocs.AddRange(this.GetTickLocations()); int endpoint = this.GetLastPoint().IntX(); hitpoints.Add(beginpoint); hitpoints.AddRange(ticklocs); hitpoints.Add(endpoint); if (repeats > 0) { for (int i = 1; i <= repeats; i++) { ticklocs.Reverse(); hitpoints.AddRange(ticklocs); /// Add the endpoint or the beginpoint depending on whether /// the slider is going forwards or backwards (repeat is even /// or odd) //even if (i % 2 == 0) { hitpoints.Add(endpoint); } //odd else { hitpoints.Add(beginpoint); } } } //Return the hitpoints return(hitpoints.ToArray()); }
public PassthroughSlider(string id, Beatmap amap) : base(id, amap) { if (controlpoints.Length != 2) { throw new ArgumentException("Error: Passthrough slider does not have 2 control points\n" + "controlpoints.Length=" + controlpoints.Length); } Point initialcoord = new Point(); initialcoord.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); initialcoord.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); double length = Math.Round(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture), 4); curve = new CircleCurve(initialcoord, controlpoints[0], controlpoints[1], length); }
//Uses the given list of control points to construct a list of curves //to account for red points public LinearSlider(string id, Beatmap amap) : base(id, amap) { //Get the initial hit point of the slider //Split into three lines for readibility Point initialcoord = new Point(); initialcoord.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); initialcoord.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); //List<Point> curvepoints = new List<Point>(); List <LinearCurve> accumulatedcurves = new List <LinearCurve>(); //Normal linear slider if (controlpoints.Length == 1) { accumulatedcurves.Add(new LinearCurve(initialcoord, controlpoints[0])); } else { List <Point> allcontrolpoints = new List <Point>(); //Add first point only if it's not repeated in the control points (old maps) if (initialcoord.IntX() != controlpoints[0].IntX() || initialcoord.IntY() != controlpoints[0].IntY()) { allcontrolpoints.Add(initialcoord); } allcontrolpoints.AddRange(controlpoints); Point[][] curvepoints = LibFuncs.SplitPointList(allcontrolpoints.ToArray()); foreach (Point[] curve in curvepoints) { if (curve.Length > 2) { for (int i = 1; i < curve.Length; i++) { accumulatedcurves.Add(new LinearCurve(curve[i - 1], curve[i])); } } else { accumulatedcurves.Add(new LinearCurve(curve[0], curve[1])); } } } curves = accumulatedcurves.ToArray(); }
protected override Point GetLastPoint() { //Necessary to avoid cases where the pixellength is something like 105.000004005432 int length = Convert.ToInt32(Math.Floor(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture))); //how many steps to travel through the curve //divide by curves.Length to scale this with the number of curves double steps = length * 2 / curves.Length; //how much to increment t by with every loop double increment = 1 / steps; //how much along the curve we have traveled so far double travelled = 0; //where to get the next point on a given curve //assign increment to get the next intended point double t = increment; Point prev = new Point(); prev.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); prev.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); //which curve we are looking at int curvenumber = 0; while (curvenumber < curves.Length) { Point next = curves[curvenumber].Bezier(t); double distance = Dewlib.GetDistance(prev.x, prev.y, next.x, next.y); travelled += distance; prev = next; if (travelled >= length) { return(next); } t += increment; if (t > 1) { curvenumber++; t -= 1; } } //If we reached the end of the slider without accumulated sliderlength distance, //just assume that the last point is the last point of the bezier curve return(curves[curves.Length - 1].Bezier(1)); }
protected override int[] GetTickLocations() { //Necessary to avoid cases where the pixellength is something like 105.000004005432 int length = Convert.ToInt32(Math.Floor(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture))); double slidervelocity = this.GetSliderVelocity(); double tickrate = Double.Parse(map.GetTag("Difficulty", "SliderTickRate"), CultureInfo.InvariantCulture); int ticklength = (int)Math.Round(slidervelocity * (100 / tickrate)); List <int> ticks = new List <int>(); if (length <= ticklength) { return(new int[0]); } //How far along a single curve we have traveled //Initialize to ticklength to make the while loop work for the first curve int accumulatedlength = ticklength; //How much along the entire slider we have traveled //Necessary to keep track of in case there are more curves than the slider length allows int totalaccumulatedlength = ticklength; //Special case for last curve, hence the curves.Length-1 for (int i = 0; i < curves.Length; i++) { //Keep traveling down the curve accumulating ticks until we reach the length of the curve while (accumulatedlength < curves[i].DistanceBetween()) { ticks.Add(curves[i].GetPointAlong(accumulatedlength).IntX()); accumulatedlength += ticklength; totalaccumulatedlength += ticklength; //>= since ticks can't appear on slider ends if (totalaccumulatedlength >= length) { //Don't want to bother with trying to break out of two loops return(ticks.ToArray()); } } accumulatedlength -= (int)Math.Round(curves[i].DistanceBetween()); } return(ticks.ToArray()); }
//Formats a string of control points into an array of points //Does NOT include the first hit point private Point[] FormatControlPoints() { //Control point string will look like: B|380:120|332:96|332:96|304:124 //Gets a list of strings containing each control point by splitting up the control point string string[] sliderpoints = HitObjectParser.GetProperty(id, "controlpoints").Split('|'); List <Point> temppoints = new List <Point>(); //Parse each point as a Point object foreach (string point in sliderpoints) { string[] pair = point.Split(':'); temppoints.Add(new Point(Double.Parse(pair[0], CultureInfo.InvariantCulture), Double.Parse(pair[1], CultureInfo.InvariantCulture))); } //Return this list of points as an array return(temppoints.ToArray()); }
//Gets the number of slider ticks, including slider repeats (but not slider ends) //Calculated the same regardless of slider type protected int GetTickCount() { double slidervelocity = this.GetSliderVelocity(); double tickrate = Double.Parse(map.GetTag("Difficulty", "SliderTickRate"), CultureInfo.InvariantCulture); //Necessary to avoid cases where the pixellength is something like 105.000004005432 int length = Convert.ToInt32(Math.Floor(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture))); int sliderruns = Int32.Parse(HitObjectParser.GetProperty(id, "repeat")); int ticklength = (int)Math.Round(slidervelocity * (100 / tickrate)); int tickcount = length / ticklength; if (length % ticklength == 0) { tickcount--; } return(tickcount * sliderruns); }
//Gets the number of slider ticks, including slider repeats (but not slider ends) //Calculated the same regardless of slider type protected int GetTickCount() { double slidervelocity = this.GetSliderVelocity(); double tickrate = Double.Parse(map.GetTag("Difficulty", "SliderTickRate"), CultureInfo.InvariantCulture); double length = Math.Round(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture), 4); int sliderruns = Int32.Parse(HitObjectParser.GetProperty(id, "repeat")); double ticklength = Math.Round(slidervelocity * (100 / tickrate), 4); int tickcount = (int)Math.Floor(length / ticklength); if (Math.Abs(length % ticklength) < Double.Epsilon) { tickcount--; } return(tickcount * sliderruns); }
protected override Point GetLastPoint() { double length = Math.Round(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture), 4); //Only one curve if (curves.Length == 1) { return(curves[0].GetPointAlong(length)); } else { double accumulatedlength = 0; //Special behavior is needed for the last curve, hence the curves.Length-1 for (int i = 0; i < curves.Length - 1; i++) { accumulatedlength += curves[i].DistanceBetween(); } double lengthdifference = length - accumulatedlength; return(curves[curves.Length - 1].GetPointAlong(lengthdifference)); } }
//Calculates the Milliseconds per Beat at a specified time by searching //through the entire timing points section //Timing points inside sliders don't affect the slider itself protected double GetMpB() { int ms = Int32.Parse(HitObjectParser.GetProperty(id, "time")); //Get all the timing sections of the beatmap string[] timings = this.map.GetSection("TimingPoints"); //Just in case there is only one timing point string timingpoint = timings[0]; //Find the section that applies to the given time for (int i = 0; i < timings.Length; i++) { //Split the string by commas to get all the relevant times string[] attributes = timings[i].Split(','); //Trim each string just in case attributes = Dewlib.TrimStringArray(attributes); if (Int32.Parse(attributes[0]) > ms) { break; } else if (Double.Parse(attributes[1], CultureInfo.InvariantCulture) > 0) { timingpoint = timings[i]; } else { continue; } } if (timingpoint == null) { throw new Exception("Error, no relevant timing point\nms=" + ms); } string[] properties = timingpoint.Split(','); return(Double.Parse(properties[1], CultureInfo.InvariantCulture)); }
protected override Point GetLastPoint() { //Necessary to avoid cases where the pixellength is something like 105.000004005432 int length = Convert.ToInt32(Math.Floor(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture))); //Only one curve if (curves.Length == 1) { return(curves[0].GetPointAlong(length)); } else { double accumulatedlength = 0; //Special behavior is needed for the last curve, hence the curves.Length-1 for (int i = 0; i < curves.Length - 1; i++) { accumulatedlength += curves[i].DistanceBetween(); } double lengthdifference = length - accumulatedlength; return(curves[curves.Length - 1].GetPointAlong(lengthdifference)); } }
//Uses the given list of control points to construct a list of bezier curves //to account for red points public BezierSlider(string id, Beatmap amap) : base(id, amap) { //Get the initial hit point of the slider //Split into three lines for readibility Point initialcoord = new Point(); initialcoord.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); initialcoord.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); List <BezierCurve> accumulatedcurves = new List <BezierCurve>(); List <Point> allcontrolpoints = new List <Point>(); allcontrolpoints.Add(initialcoord); allcontrolpoints.AddRange(controlpoints); Point[][] curvepoints = LibFuncs.SplitPointList(allcontrolpoints.ToArray()); foreach (Point[] curve in curvepoints) { accumulatedcurves.Add(new BezierCurve(curve)); } curves = accumulatedcurves.ToArray(); }
//Calculated the same regardless of slider type so can already be implemented public int[] GetHitTimes() { List <int> times = new List <int>(); //When slider starts int starttime = Int32.Parse(HitObjectParser.GetProperty(id, "time")); double MpB = this.GetMpB(); //How long the slider is in existance (without considering repeats) //slidertime = (pixellength / (slidervelocity * 100)) * MillisecondsPerBeat //(Order of operations is important kids! Otherwise you end up with slidertimes of 5000000 :o) int slidertime = Convert.ToInt32((Double.Parse(HitObjectParser.GetProperty(id, "pixellength"), CultureInfo.InvariantCulture) / (this.GetSliderVelocity() * 100)) * MpB); //How long each tick is apart from each other //ticktime = MillisecondsPerBeat / tickrate int ticktime = Convert.ToInt32(MpB / Double.Parse(map.GetTag("difficulty", "slidertickrate"), CultureInfo.InvariantCulture)); //How many times the slider runs int sliderruns = Int32.Parse(HitObjectParser.GetProperty(id, "repeat")); //How many ticks are in the slider (without repeats) //This is because later we use tickcount to tell how many times to add a time //for a given slider run int tickcount = this.GetTickCount() / sliderruns; //The time from the last tick to the slider end //If there are no ticks, then this just become slidertime int sliderenddiff = (slidertime) - (tickcount * ticktime); //Keeps track of what time we are at when travelling through the slider int currenttime = starttime; for (int runnum = 1; runnum <= sliderruns; runnum++) { if (runnum == 1) { //Add the initial slider hit times.Add(currenttime); //Add the tick times for (int ticknum = 0; ticknum < tickcount; ticknum++) { currenttime += ticktime; times.Add(currenttime); } //Add the slider end currenttime += sliderenddiff; times.Add(currenttime); } else if (runnum % 2 == 0) { //Add the first tick after the slider end currenttime += sliderenddiff; times.Add(currenttime); //Don't skip the first tick since we need to include the slider head too for (int ticknum = 0; ticknum < tickcount; ticknum++) { currenttime += ticktime; times.Add(currenttime); } } else if (runnum % 2 == 1) { //Add the tick times for (int ticknum = 0; ticknum < tickcount; ticknum++) { currenttime += ticktime; times.Add(currenttime); } //Add the slider end currenttime += sliderenddiff; times.Add(currenttime); } } return(times.ToArray()); }
protected override Point GetLastPoint() { double length = Math.Round(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture), 4); return(curve.GetPointAlong(length)); }
protected override int[] GetTickLocations() { double length = Math.Round(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture), 4); int sliderruns = Int32.Parse(HitObjectParser.GetProperty(id, "repeat")); //Only need ticks for one slider length (no repeats needed) //Also no need for double conversion since TickCount is always divisible by sliderruns int tickcount = this.GetTickCount() / sliderruns; double slidervelocity = this.GetSliderVelocity(); double tickrate = Double.Parse(map.GetTag("Difficulty", "SliderTickRate"), CultureInfo.InvariantCulture); double ticklength = Math.Round(slidervelocity * (100 / tickrate), 4); if (length <= ticklength) { return(new int[0]); } List <Point> ticks = new List <Point>(); //how many steps to travel through the curve //divide by curves.Length to scale this with the number of curves double steps = length * 2 / curves.Length; //how much to increment t by with every loop double increment = 1 / steps; //how much along the curve we have traveled so far double travelled = 0; //where to get the next point on a given curve //assign increment to get the next intended point double t = increment; Point prev = new Point(); prev.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); prev.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); //which curve we are looking at int curvenumber = 0; while (curvenumber < curves.Length) { Point next = curves[curvenumber].Bezier(t); double distance = LibFuncs.GetDistance(prev.x, prev.y, next.x, next.y); travelled += distance; prev = next; if (travelled >= ticklength) { ticks.Add(next); travelled = 0; if (ticks.Count == tickcount) { break; } } t += increment; if (t > 1) { curvenumber++; t -= 1; } } if (travelled > 0) { throw new Exception("Error, too many ticks to get in bezier curve, travelled=" + travelled); } List <int> locations = new List <int>(); foreach (Point i in ticks) { locations.Add(i.IntX()); } return(locations.ToArray()); }