private void load(HitObjectType mode) { this.mode = mode; switch (mode) { case HitObjectType.Circle: const int count = 10; for (int i = 0; i < count; i++) { var h = new HitCircle { StartTime = framedClock.CurrentTime + 600 + i * 80, Position = new Vector2((i - count / 2) * 14), }; add(new DrawableHitCircle(h)); } break; case HitObjectType.Slider: add(new DrawableSlider(new Slider { StartTime = framedClock.CurrentTime + 600, ControlPoints = new List <Vector2> { new Vector2(-200, 0), new Vector2(400, 0), }, Length = 400, Position = new Vector2(-200, 0), Velocity = 1, TickDistance = 100, })); break; case HitObjectType.Spinner: add(new DrawableSpinner(new Spinner { StartTime = framedClock.CurrentTime + 600, Length = 1000, Position = new Vector2(0, 0), })); break; } }
public HitObject(Vector2 pos, double time, HitObjectType type, bool newCombo, int comboSkip, bool normal, bool whistle, bool finish, bool clap, SampleSet sampleSet, SampleSet additionSet, int index, double volume, string filename) { Pos = pos; Time = time; SetObjectType(type); NewCombo = newCombo; ComboSkip = comboSkip; Normal = normal; Whistle = whistle; Finish = finish; Clap = clap; SampleSet = sampleSet; AdditionSet = additionSet; CustomIndex = index; SampleVolume = volume; Filename = filename; }
public Slider(HitObjectType Type, Vector2 Position, double Time, Color Color, double[] VelocityParameters, String[] Parameters) : base(Type, Position, Time, Color) { this.Parameters = Parameters; PointsPosition = new List <Vector2>(); Velocity = ToVelocity(VelocityParameters[0], VelocityParameters[1]); String[] SliderValues = Parameters[0].Split('|'); CurveType = getCurveType(SliderValues[0]); for (var i = 0; i < SliderValues.Length - 1; i++) { String[] RawPoint = SliderValues[i + 1].Split(':'); int X = Int32.Parse(RawPoint[0]) + 64; int Y = Int32.Parse(RawPoint[1]) + 56; PointsPosition.Add(new Vector2(X, Y)); } Length = Math.Round(Double.Parse(Parameters[2]), 4); }
public HitSlider(Vector2 startPosition, int time, HitObjectType hitType, HitObjectSoundType hitsound, SliderCurveType sliderType, int repeat, float velocity, int numCurves, int length) { this.startPosition = startPosition; this.Position = startPosition; this.Time = time; this.HitSound = (int)hitsound; this.SliderType = sliderType; this.HitType = (int)hitType; this.Repeat = repeat; this.Velocity = velocity; this.Length = length; this.NumCurves = numCurves; ConstructSlider(); }
public HitObject(Vector2 pos, double time, HitObjectType type, bool newCombo, int comboSkip, bool normal, bool whistle, bool finish, bool clap, SampleSet sampleSet, SampleSet additionSet, int index, double volume, string filename) { Pos = pos; // Let the end position be the same as the start position before changed later for sliders EndPos = Pos; Time = time; SetObjectType(type); NewCombo = newCombo; ComboSkip = comboSkip; Normal = normal; Whistle = whistle; Finish = finish; Clap = clap; SampleSet = sampleSet; AdditionSet = additionSet; CustomIndex = index; SampleVolume = volume; Filename = filename; }
public static HitObject createObject(string str) { string[] split = str.Split(','); int pos_x = int.Parse(split[0]); int pos_y = int.Parse(split[1]); int offset = int.Parse(split[2]); int typeNo = int.Parse(split[3]); int comboSkipCount = (typeNo & 0b0100) > 0 ? (typeNo % 128) / 16 + 1 : 0; HitObjectType noteType = (HitObjectType)(typeNo & 0b1011); int hitsound = int.Parse(split[4]); /* rest of the strings are object type dependent */ string [] etc = new String[split.Length - 5]; Array.Copy(split, 5, etc, 0, split.Length - 5); /* last string is extras */ string extras = split[split.Length - 1]; HitObject obj = new HitObject(pos_x, pos_y, offset, noteType, comboSkipCount, hitsound, extras); switch (noteType) { case HitObjectType.Circle: return(new Circle(obj, etc)); case HitObjectType.Slider: return(new Slider(obj, etc)); case HitObjectType.Spinner: return(new Spinner(obj, etc)); default: Console.WriteLine("Invalid Object Type Detected"); return(null); } }
//Gets a count of each type of hitobject in the beatmap //Returns an array of 3 ints, which are the counts of Circles, Sliders, //and Spinners respectively public int[] GetHitObjectsCount() { //0 = circles, 1 = sliders, 2 = spinners int[] counts = new int[3]; for (int i = 0; i < hitobjects.GetSize(); i++) { HitObjectType hobject = hitobjects.GetHitObjectType(i); if (hobject == HitObjectType.Circle) { counts[0]++; } else if (hobject == HitObjectType.Slider) { counts[1]++; } else if (hobject == HitObjectType.Spinner) { counts[2]++; } } return(counts); }
private HitObject[] parseHitObjects() { String[] RawObjects = getContent("HitObjects"); List <HitObject> ParsedObjects = new List <HitObject>(); foreach (String RawObject in RawObjects) { String[] Parameters = RawObject.Split(','); HitObject Object = new HitObject(); HitObjectType Type = (HitObjectType)Int32.Parse(Parameters[3]); ColorIndex += (Type.HasFlag(HitObjectType.SkipColor3) ? 4 : 0) + (Type.HasFlag(HitObjectType.SkipColor2) ? 2 : 0) + (Type.HasFlag(HitObjectType.SkipColor1) ? 1 : 0) + (Type.HasFlag(HitObjectType.NewCombo) ? 1 : 0); Vector2 Vector = new Vector2(Int32.Parse(Parameters[0]) + 64, Int32.Parse(Parameters[1]) + 56); if (Type.HasFlag(HitObjectType.Circle)) { Object = new Circle(Type, Vector, Int32.Parse(Parameters[2]), Colors[ColorIndex % Colors.Length]); } else if (Type.HasFlag(HitObjectType.Slider)) { List <String> SliderParameters = new List <String>(); for (int i = 5; i < Parameters.Length; i++) { SliderParameters.Add(Parameters[i]); } int Time = Int32.Parse(Parameters[2]); Object = new Slider(Type, Vector, Time, Colors[ColorIndex % Colors.Length], new double[] { getBPMAt(Time), getSliderVelocityAt(Time) }, SliderParameters.ToArray()); } else if (Type.HasFlag(HitObjectType.Spinner)) { Object = new Spinner(Type, Vector, Int32.Parse(Parameters[2]), new Color(255, 255, 255), Int32.Parse(Parameters[5])); } ParsedObjects.Add(Object); } return(ParsedObjects.ToArray()); }
public HitState GetHitState(int index) { UIntPtr hitObjectListPointer = (UIntPtr)OsuProcess.ReadUInt32(BaseAddress + 0x48); UIntPtr hitObjectListItemsPointer = (UIntPtr)OsuProcess.ReadUInt32(hitObjectListPointer + 0x4); UIntPtr hitObjectPointer = (UIntPtr)OsuProcess.ReadUInt32(hitObjectListItemsPointer + 0x8 + 0x4 * index); HitObjectType type = (HitObjectType)OsuProcess.ReadInt32(hitObjectPointer + 0x18); type &= ~HitObjectType.ComboOffset; type &= ~HitObjectType.NewCombo; var hitState = OsuProcess.ReadBool(hitObjectPointer + 0x84) ? HitState.Hit : HitState.NotHit; switch (type) { case HitObjectType.Slider: UIntPtr startHitCirclePointer = (UIntPtr)OsuProcess.ReadUInt32(hitObjectPointer + 0xCC); return(OsuProcess.ReadBool(startHitCirclePointer + 0x84) ? hitState | HitState.SliderStartHit : hitState); default: return(hitState); } }
public bool IsType(HitObjectType type) { return(Type.HasFlag(type)); }
public bool IsType(HitObjectType type) { return(Type.IsType(type)); }
// Returns the string of a Hit Circle to be added to the beatmap. public string GetHitCircleData(Vector2 position, int time, HitObjectType hitType, HitObjectSoundType hitSound, Vector2 prevPoint) { var hc = new HitCircle(position, time, hitType, hitSound, prevPoint, maxNoteDistance); return(hc.SerializeForOsu()); }
public static string Process(string dir, bool quick = false, bool usem4a = true, bool free = false, bool previewMode = false) { Console.WriteLine("Combinating beatmap: " + dir.Split('\\').Last()); Console.WriteLine(); if (dir.Length < 1) { Console.WriteLine("No path specified!"); return(null); } List <string> osuFiles = new List <string>(Directory.GetFiles(dir, "*.osu")); if (osuFiles.Count < 1) { Console.WriteLine("No .osu files found!"); return(null); } List <string> orderedDifficulties = new List <string>(); orderedDifficulties.Add(osuFiles.Find(f => f.EndsWith("[Easy].osu"))); orderedDifficulties.Add(osuFiles.Find(f => f.EndsWith("[Normal].osu"))); orderedDifficulties.Add(osuFiles.Find(f => f.EndsWith("[Hard].osu"))); orderedDifficulties.Add(osuFiles.Find(f => f.EndsWith("[Expert].osu"))); if (orderedDifficulties.FindAll(t => t != null).Count < 1) { return(null); } Console.WriteLine("Files found:"); foreach (string s in orderedDifficulties) { Console.WriteLine(" * " + Path.GetFileName(s)); } Console.WriteLine(); Console.WriteLine(); List <BeatmapDifficulty> difficulties = new List <BeatmapDifficulty>(); foreach (string f in orderedDifficulties) { if (f == null) { difficulties.Add(null); continue; } BeatmapDifficulty bd = new BeatmapDifficulty(); difficulties.Add(bd); string currentSection = ""; foreach (string line in File.ReadAllLines(f)) { string writeLine = line; if (line.StartsWith("Version:")) { bd.VersionName = line.Replace("Version:", ""); continue; } if (line.StartsWith("[")) { currentSection = line.Replace("[", "").Replace("]", ""); } else if (line.Length > 0) { string[] split = line.Split(','); string[] var = line.Split(':'); string key = string.Empty; string val = string.Empty; if (var.Length > 1) { key = var[0].Trim(); val = var[1].Trim(); } switch (currentSection) { case "General": switch (key) { case "AudioFilename": writeLine = "AudioFilename: audio.mp3"; break; } break; case "Difficulty": switch (key) { case "HPDrainRate": bd.DifficultyHpDrainRate = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val))); break; case "CircleSize": bd.DifficultyCircleSize = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val))); break; case "OverallDifficulty": bd.DifficultyOverall = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val))); //if (!hasApproachRate) DifficultyApproachRate = DifficultyOverall; break; case "SliderMultiplier": bd.DifficultySliderMultiplier = Math.Max(0.4, Math.Min(3.6, Double.Parse(val, nfi))); break; case "SliderTickRate": bd.DifficultySliderTickRate = Math.Max(0.5, Math.Min(8, Double.Parse(val, nfi))); break; } break; case "HitObjects": { HitObjectType type = (HitObjectType)Int32.Parse(split[3]) & ~HitObjectType.ColourHax; bool slider = (type & HitObjectType.Slider) > 0; bool spinner = (type & HitObjectType.Spinner) > 0; int time = (int)Decimal.Parse(split[2], nfi); int endTime = spinner ? (int)Decimal.Parse(split[5], nfi) : time; int repeatCount = 0; double length = 0; bool hadEndpointSamples = false; bool hold = false; SampleSet ss = SampleSet.None, ssa = SampleSet.None; string[] samplestring = null; if (slider) { repeatCount = Int32.Parse(split[6], nfi); length = double.Parse(split[7], nfi); hadEndpointSamples = split.Length >= 9; hold = (repeatCount > 1 && length < 50) || (repeatCount > 4 && length < 100) || (hadEndpointSamples && split[4] == "4"); if (split.Length > 10) { samplestring = split[10].Split(':'); } } else if (spinner) { if (split.Length > 6) { samplestring = split[6].Split(':'); } } else { if (split.Length > 5) { samplestring = split[5].Split(':'); } } if (samplestring != null) { ss = (SampleSet)Convert.ToInt32(samplestring[0]); if (samplestring.Length > 0) { ssa = (SampleSet)Convert.ToInt32(samplestring[1]); } } // take the slider's slide sampleset from 20ms after the head in case the head has a different sampleset ControlPoint cp = bd.controlPointAt(slider ? time + 20 : endTime + 5); StringBuilder builder = new StringBuilder(); builder.Append(MakeSampleset(cp, ss, ssa)); // Object commons builder.Append(','); builder.Append(split[0]); // X builder.Append(','); builder.Append(split[1]); // Y builder.Append(','); builder.Append(time.ToString(nfi)); // time HitObjectType type2 = (HitObjectType)Int32.Parse(split[3]); builder.Append(','); builder.Append(hold ? (int)(type2 | HitObjectType.Hold) : (int)type2); // object type string soundAdditions = MakeSoundAdditions(split[4]); builder.Append(','); builder.Append(soundAdditions); // sound additions //add addition difficulty-specific information if (slider) { builder.Append(','); builder.Append(split[5]); // curve type, all control points builder.Append(','); builder.Append(repeatCount.ToString(nfi)); // repeat count builder.Append(','); builder.Append(length.ToString(nfi)); // curve length string[] additions; if (hadEndpointSamples) { additions = split[8].Split('|'); } else { additions = new string[0]; } // nodal hitsamples builder.Append(','); for (int repeatNo = 0; repeatNo <= repeatCount; repeatNo++) { if (repeatNo > 0) { builder.Append('|'); } if (repeatNo < additions.Length) { builder.Append(MakeSoundAdditions(additions[repeatNo])); } else { builder.Append(soundAdditions); } } double velocity = bd.VelocityAt(time); //velocity and scoring distance. builder.Append(','); builder.Append(velocity.ToString(nfi)); builder.Append(','); builder.Append(bd.ScoringDistanceAt(time).ToString(nfi)); double ReboundTime = 1000 * length / velocity; double currTime = time; cp = bd.controlPointAt(currTime + 5); string[] node_samples; if (split.Length > 9) { // osu!'s separator is different node_samples = split[9].Split('|'); } else { node_samples = new string[0]; } // nodal samplesets for (int repeatNo = 0; repeatNo <= repeatCount; repeatNo++) { SampleSet node_ss = ss; SampleSet node_ssa = ssa; if (repeatNo < node_samples.Length) { string[] pair = node_samples[repeatNo].Split(':'); node_ss = (SampleSet)Convert.ToInt32(pair[0]); if (pair.Length > 0) { node_ssa = (SampleSet)Convert.ToInt32(pair[1]); } } cp = bd.controlPointAt(currTime + 5); builder.Append(repeatNo == 0 ? ',' : ':'); builder.Append(MakeSampleset(cp, node_ss, node_ssa)); currTime += ReboundTime; } } if (spinner) { builder.Append(','); builder.Append(split[5]); // end time } bd.HitObjectLines.Add(new HitObjectLine { StringRepresentation = builder.ToString(), Time = Int32.Parse(line.Split(',')[2]) }); continue; //skip direct output } case "TimingPoints": { ControlPoint cp = new ControlPoint(Double.Parse(split[0], nfi), Double.Parse(split[1], nfi), split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Int32.Parse(split[2]), (SampleSet)Int32.Parse(split[3]), split.Length > 4 ? (CustomSampleSet)Int32.Parse(split[4]) : CustomSampleSet.Default, Int32.Parse(split[5]), split.Length > 6 ? split[6][0] == '1' : true, split.Length > 7 ? split[7][0] == '1' : false); bd.ControlPoints.Add(cp); break; } } } bd.HeaderLines.Add(writeLine); } } string metadata = dir + "\\metadata.txt"; string Artist = "", Title = "", Creator = ""; if (File.Exists(metadata)) { foreach (string line in File.ReadAllLines(metadata)) { if (line.Length == 0) { continue; } string[] var = line.Split(':'); string key = string.Empty; string val = string.Empty; if (var.Length > 1) { key = line.Substring(0, line.IndexOf(':')); val = line.Substring(line.IndexOf(':') + 1).Trim(); switch (key) { case "Artist": Artist = val; break; case "Title": Title = val; break; case "Creator": Creator = val; break; } } } } string baseName = Artist + " - " + Title + " (" + Creator + ")"; string oscFilename = baseName + ".osc"; foreach (BeatmapDifficulty d in difficulties) { if (d != null) { ListHelper.StableSort(d.HitObjectLines); } } headerContent = difficulties.Find(d => d != null).HeaderLines; string[] splitdir = dir.Split('\\'); string osz2Filename; string baseFileWithLocation = baseName.Substring(baseName.LastIndexOf("\\", StringComparison.Ordinal) + 1); if (free && DistBuild) { osz2Filename = baseFileWithLocation + ".osf2"; } else { osz2Filename = baseFileWithLocation + (usem4a && !DistBuild ? ".m4a.osz2" : ".osz2"); } string audioFilename = null; if (usem4a) { audioFilename = ""; foreach (string s in Directory.GetFiles(dir, "*.m4a")) { if (s.Contains("_lq")) { continue; } audioFilename = s; break; } } else { audioFilename = Directory.GetFiles(dir, "*.mp3")[0]; } File.Delete(osz2Filename); //write the package initially so we can use it for score testing purposes. writePackage(oscFilename, osz2Filename, audioFilename, difficulties, orderedDifficulties); //scoring Player.Beatmap = new Beatmap(osz2Filename); Player.Autoplay = true; //Working on the scoring algorithm for osu!s //Basically I need to calculate the total possible score from hitobjects before any multipliers kick in... //but I need to know this before the beatmap is loaded. //So this means running through the beatmap as if it was being played at the time of package creation //(inside BeatmapCombinator). After I find the score that can be achieved, I can figure out what multiplier //i need in order to pad it out to a fixed 1,000,000 max score. //I am sure I will run into some rounding issues once I get that far, but we'll see how things go :p. ITimeSource oldTimeSource = Clock.AudioTimeSource; FakeAudioTimeSource source = new FakeAudioTimeSource(); Clock.AudioTimeSource = source; SoundEffectPlayer oldEffect = AudioEngine.Effect; BackgroundAudioPlayer oldMusic = AudioEngine.Music; AudioEngine.Music = null; AudioEngine.Effect = null; headerContent.Remove("[HitObjects]"); headerContent.Add(string.Empty); headerContent.Add("[ScoringMultipliers]"); if (quick) { processDifficulty(Difficulty.Easy, true); processDifficulty(Difficulty.Normal, true); processDifficulty(Difficulty.Hard, true); processDifficulty(Difficulty.Expert, true); } else { if (orderedDifficulties[(int)Difficulty.Easy] != null) { headerContent.Add("0: " + processDifficulty(Difficulty.Easy).ToString("G17", nfi)); } if (orderedDifficulties[(int)Difficulty.Normal] != null) { headerContent.Add("1: " + processDifficulty(Difficulty.Normal).ToString("G17", nfi)); } if (orderedDifficulties[(int)Difficulty.Expert] != null) { headerContent.Add("3: " + processDifficulty(Difficulty.Expert).ToString("G17", nfi)); } } if (healthMultiplier != 0) { headerContent.Add("HP:" + healthMultiplier.ToString("G17", nfi)); } headerContent.Add(string.Empty); headerContent.Add("[HitObjects]"); Player.Beatmap.Dispose(); Clock.AudioTimeSource = oldTimeSource; AudioEngine.Effect = oldEffect; AudioEngine.Music = oldMusic; //only change the filename here so it is not treated as a preview above (else previewpoints will not be filled before necessary). if (previewMode) { osz2Filename = osz2Filename.Replace(".osf2", "_preview.osf2"); } //write the package a second time with new multiplier header data. writePackage(oscFilename, osz2Filename, audioFilename, difficulties, orderedDifficulties); return(osz2Filename); }
public static bool IsType(this HitObjectType a, HitObjectType b) { return((a & b) > 0); //adding in binary results in 0 if both are equal }
Class321 class = new Class321(this.class297_0, this.Position, this.StartTime, this.Type.IsType(HitObjectType.NewCombo), this.SoundType, this.curveTypes_0, this.SegmentCount, this.SpatialLength, list2, list, this.int_0);
private void ParseHitObjects(string line) { string[] tokens = line.Split(','); Point position = new Point(Convert.ToInt32(tokens[0]), Convert.ToInt32(tokens[1])); int startTime = Convert.ToInt32(tokens[2]); HitObjectType type = (HitObjectType)int.Parse(tokens[3]); int comboOffset = (int)(type & HitObjectType.ComboOffset) >> 4; type &= ~HitObjectType.ComboOffset; bool isNewCombo = type.HasFlag(HitObjectType.NewCombo); type &= ~HitObjectType.NewCombo; HitSoundType hitSound = (HitSoundType)Convert.ToInt32(tokens[4]); HitObject hitObject = null; string[] extrasSplit = tokens.Last().Split(':'); int extrasOffset = type.HasFlag(HitObjectType.Hold) ? 1 : 0; Extras extras = tokens.Last().Contains(":") ? new Extras { SampleSet = (SampleSet)Convert.ToInt32(extrasSplit[0 + extrasOffset]), AdditionSet = (SampleSet)Convert.ToInt32(extrasSplit[1 + extrasOffset]), CustomIndex = Convert.ToInt32(extrasSplit[2 + extrasOffset]), Volume = Convert.ToInt32(extrasSplit[3 + extrasOffset]), SampleFileName = string.IsNullOrEmpty(extrasSplit[4 + extrasOffset]) ? null : extrasSplit[4 + extrasOffset] } : new Extras(); switch (type) { case HitObjectType.Circle: { if (Beatmap.GeneralSection.Mode == Ruleset.Standard) { hitObject = new Circle(position, startTime, startTime, hitSound, extras, isNewCombo, comboOffset); } else if (Beatmap.GeneralSection.Mode == Ruleset.Taiko) { hitObject = new TaikoHit(position, startTime, startTime, hitSound, extras, isNewCombo, comboOffset); } else if (Beatmap.GeneralSection.Mode == Ruleset.Fruits) { hitObject = new CatchFruit(position, startTime, startTime, hitSound, extras, isNewCombo, comboOffset); } else if (Beatmap.GeneralSection.Mode == Ruleset.Mania) { hitObject = new ManiaHit(position, startTime, startTime, hitSound, extras, isNewCombo, comboOffset); } } break; case HitObjectType.Slider: { CurveType curveType = ParseHelper.GetCurveType(tokens[5].Split('|')[0][0]); List <Point> sliderPoints = ParseHelper.GetSliderPoints(tokens[5].Split('|')); int repeats = Convert.ToInt32(tokens[6]); double pixelLength = ParseHelper.ToDouble(tokens[7]); int endTime = CalculateEndTime(startTime, repeats, pixelLength); List <HitSoundType> edgeHitSounds = new List <HitSoundType>(); if (tokens.Length > 8 && tokens[8].Length > 0) { edgeHitSounds = new List <HitSoundType>(); edgeHitSounds = Array.ConvertAll(tokens[8].Split('|'), s => (HitSoundType)Convert.ToInt32(s)).ToList(); } List <Tuple <SampleSet, SampleSet> > edgeAdditions = new List <Tuple <SampleSet, SampleSet> >(); if (tokens.Length > 9 && tokens[9].Length > 0) { edgeAdditions = new List <Tuple <SampleSet, SampleSet> >(); foreach (var s in tokens[9].Split('|')) { edgeAdditions.Add(new Tuple <SampleSet, SampleSet>((SampleSet)Convert.ToInt32(s.Split(':').First()), (SampleSet)Convert.ToInt32(s.Split(':').Last()))); } } if (Beatmap.GeneralSection.Mode == Ruleset.Standard) { hitObject = new Slider(position, startTime, endTime, hitSound, curveType, sliderPoints, repeats, pixelLength, edgeHitSounds, edgeAdditions, extras, isNewCombo, comboOffset); } else if (Beatmap.GeneralSection.Mode == Ruleset.Taiko) { hitObject = new TaikoDrumroll(position, startTime, endTime, hitSound, curveType, sliderPoints, repeats, pixelLength, edgeHitSounds, edgeAdditions, extras, isNewCombo, comboOffset); } else if (Beatmap.GeneralSection.Mode == Ruleset.Fruits) { hitObject = new CatchDroplets(position, startTime, endTime, hitSound, curveType, sliderPoints, repeats, pixelLength, edgeHitSounds, edgeAdditions, extras, isNewCombo, comboOffset); } } break; case HitObjectType.Spinner: { int endTime = Convert.ToInt32(tokens[5].Trim()); if (Beatmap.GeneralSection.Mode == Ruleset.Standard) { hitObject = new Spinner(position, startTime, endTime, hitSound, extras, isNewCombo, comboOffset); } else if (Beatmap.GeneralSection.Mode == Ruleset.Taiko) { hitObject = new TaikoSpinner(position, startTime, endTime, hitSound, extras, isNewCombo, comboOffset); } else if (Beatmap.GeneralSection.Mode == Ruleset.Fruits) { hitObject = new CatchSpinner(position, startTime, endTime, hitSound, extras, isNewCombo, comboOffset); } } break; case HitObjectType.Hold: { string[] additions = tokens[5].Split(':'); int endTime = Convert.ToInt32(additions[0].Trim()); hitObject = new ManiaHold(position, startTime, endTime, hitSound, extras, isNewCombo, comboOffset); } break; } Beatmap.HitObjects.Add(hitObject); }
// Token: 0x0600176B RID: 5995 // RVA: 0x0001484E File Offset: 0x00012A4E public bool IsType(HitObjectType type) { return this.Type.IsType(type); }
public void LoadFile() { spriteManager.ForwardPlayOptimisedAdd = true; beatmap.ControlPoints.Clear(); FileSection currentSection = FileSection.Unknown; //Check file just before load -- ensures no modifications have occurred. //BeatmapManager.Current.UpdateChecksum(); List <string> readableFiles = new List <string>(); readableFiles.Add(beatmap.BeatmapFilename); string storyBoardFile = beatmap.StoryboardFilename; //if (beatmap.CheckFileExists(storyBoardFile)) // readableFiles.Add(storyBoardFile); //bool hasCustomColours = false; //bool firstColour = true; bool hitObjectPreInit = false; bool lastAddedSpinner = false; //bool verticalFlip = (GameBase.Mode == OsuModes.Play && Player.currentScore != null && // ModManager.CheckActive(Player.currentScore.enabledMods, Mods.HardRock)); int linenumber; //The first file will be the actual .osu file. //The second file is the .osb for now. for (int fn = 0; fn < readableFiles.Count; fn++) { if (fn > 0) { break; //don't handle storyboarding yet. //baseReader = new StreamReader(BeatmapManager.Current.GetFileStream(readableFiles[fn])); //LocatedTextReaderWrapper ltr = new LocatedTextReaderWrapper(baseReader); //osqEngine = new osq.Encoder(ltr); } //using (TextReader reader = (fn == 0 ? (TextReader)new StreamReader(BeatmapManager.Current.GetFileStream(readableFiles[fn])) : new StringReader(osqEngine.Encode()))) TextReader reader = new StreamReader(beatmap.GetFileStream(readableFiles[fn])); { linenumber = 0; string line = null; bool readNew = true; int objnumber = 0; bool headerReadFinished = false; while (true) { if (readNew) { line = reader.ReadLine(); if (line == null) { break; } linenumber++; } readNew = true; if (line.Length == 0 || line.StartsWith(" ") || line.StartsWith("_") || line.StartsWith("//")) { continue; } //if (currentSection == FileSection.Events) ParseVariables(ref line); string key = string.Empty; string val = string.Empty; if (!headerReadFinished) { string[] var = line.Split(':'); if (var.Length > 1) { key = var[0].Trim(); val = var[1].Trim(); } } if (line[0] == '[') { try { currentSection = (FileSection)Enum.Parse(typeof(FileSection), line.Trim('[', ']')); if (currentSection == FileSection.HitObjects) { headerReadFinished = true; } } catch (Exception) { } continue; } switch (currentSection) { case FileSection.ScoringMultipliers: if (key == "HP") { beatmap.HpStreamAdjustmentMultiplier = double.Parse(val, GameBase.nfi); } else { Difficulty diff = (Difficulty)int.Parse(key); beatmap.DifficultyInfo[diff] = new BeatmapDifficultyInfo(diff) { ComboMultiplier = double.Parse(val, GameBase.nfi) }; } break; case FileSection.General: switch (key) { case "CountdownOffset": if (val.Length > 0) { beatmap.CountdownOffset = int.Parse(val); } break; } break; case FileSection.TimingPoints: { string[] split = line.Split(','); if (split.Length > 2) { beatmap.ControlPoints.Add( new ControlPoint(double.Parse(split[0], GameBase.nfi), double.Parse(split[1], GameBase.nfi), split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]), (SampleSet)int.Parse(split[3]), split.Length > 4 ? (CustomSampleSet)int.Parse(split[4]) : CustomSampleSet.Default, int.Parse(split[5]), split.Length > 6 ? split[6][0] == '1' : true, split.Length > 7 ? split[7][0] == '1' : false)); } break; } case FileSection.Editor: switch (key) { case "Bookmarks": if (val.Length > 0) { beatmap.StreamSwitchPoints = new List <int>(); string[] points = val.Split(','); foreach (string point in points) { beatmap.StreamSwitchPoints.Add(int.Parse(point.Trim())); } } break; } //not relevant continue; case FileSection.Difficulty: switch (key) { case "HPDrainRate": beatmap.DifficultyHpDrainRate = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val))); break; case "CircleSize": beatmap.DifficultyCircleSize = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val))); break; case "OverallDifficulty": beatmap.DifficultyOverall = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val))); //if (!hasApproachRate) DifficultyApproachRate = DifficultyOverall; break; case "SliderMultiplier": beatmap.DifficultySliderMultiplier = Math.Max(0.4, Math.Min(3.6, double.Parse(val, GameBase.nfi))); break; case "SliderTickRate": beatmap.DifficultySliderTickRate = Math.Max(0.5, Math.Min(8, double.Parse(val, GameBase.nfi))); break; /*case "ApproachRate": * beatmap.DifficultyApproachRate = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val))); * hasApproachRate = true; * break;*/ } break; case FileSection.HitObjects: { if (fn > 0) { continue; } if (!hitObjectPreInit) { //ComboColoursReset(); hitObjectPreInit = true; } string[] split = line.Split(','); int offset = 0; Difficulty difficulty = (Difficulty)int.Parse(split[offset++]); SampleSetInfo ssi = parseSampleSet(split[offset++]); int x = (int)Math.Max(0, Math.Min(512, decimal.Parse(split[offset++], GameBase.nfi))); int y = (int)Math.Max(0, Math.Min(512, decimal.Parse(split[offset++], GameBase.nfi))); int time = (int)decimal.Parse(split[offset++], GameBase.nfi); if (objnumber == 0) { CountdownTime = time; } else { CountdownTime = Math.Min(CountdownTime, time); } objnumber++; if (!shouldLoadDifficulty(difficulty)) { continue; } HitObjectType type = (HitObjectType)int.Parse(split[offset], GameBase.nfi) & ~HitObjectType.ColourHax; int comboOffset = (Convert.ToInt32(split[offset++], GameBase.nfi) >> 4) & 7; // mask out bits 5-7 for combo offset. HitObjectSoundType soundType = (HitObjectSoundType)int.Parse(split[offset++], GameBase.nfi); Vector2 pos = new Vector2(x, y); bool newCombo = (type & HitObjectType.NewCombo) > 0 || lastAddedSpinner || StreamHitObjects[(int)difficulty] == null || StreamHitObjects[(int)difficulty].Count == 0; HitObject h = null; //used for new combo forcing after a spinner. lastAddedSpinner = h is Spinner; if ((type & HitObjectType.Circle) > 0) { h = hitFactory.CreateHitCircle(pos, time, newCombo, soundType, newCombo ? comboOffset : 0); } else if ((type & (HitObjectType.Slider | HitObjectType.Hold)) > 0) { CurveTypes curveType = CurveTypes.Bezier; int repeatCount = 0; double length = 0; List <Vector2> points = new List <Vector2>(); List <HitObjectSoundType> sounds = null; string[] pointsplit = split[offset++].Split('|'); for (int i = 0; i < pointsplit.Length; i++) { if (pointsplit[i].Length == 1) { switch (pointsplit[i]) { case "C": curveType = CurveTypes.Catmull; break; case "B": curveType = CurveTypes.Bezier; break; case "L": curveType = CurveTypes.Linear; break; case "P": curveType = CurveTypes.PerfectCurve; break; } continue; } string[] temp = pointsplit[i].Split(':'); Vector2 v = new Vector2((float)Convert.ToDouble(temp[0], GameBase.nfi), (float)Convert.ToDouble(temp[1], GameBase.nfi)); points.Add(v); } repeatCount = Convert.ToInt32(split[offset++], GameBase.nfi); length = Convert.ToDouble(split[offset++], GameBase.nfi); List <SampleSetInfo> listSampleSets = null; //Per-endpoint Sample Additions if (split[offset].Length > 0) { string[] adds = split[offset++].Split('|'); if (adds.Length > 0) { sounds = new List <HitObjectSoundType>(adds.Length); for (int i = 0; i < adds.Length; i++) { int sound; int.TryParse(adds[i], out sound); sounds.Add((HitObjectSoundType)sound); } } } else { offset += 1; } if (split.Length > 13) { string[] samplesets = split[13].Split(':'); listSampleSets = new List <SampleSetInfo>(samplesets.Length); for (int i = 0; i < samplesets.Length; i++) { SampleSetInfo node_ssi = parseSampleSet(samplesets[i]); listSampleSets.Add(node_ssi); } } if ((repeatCount > 1 && length < 50) || (repeatCount > 4 && length < 100) || (type & HitObjectType.Hold) > 0) { h = hitFactory.CreateHoldCircle(pos, time, newCombo, soundType, repeatCount, length, sounds, newCombo ? comboOffset : 0, Convert.ToDouble(split[offset++], GameBase.nfi), Convert.ToDouble(split[offset++], GameBase.nfi), listSampleSets); } else { h = hitFactory.CreateSlider(pos, time, newCombo, soundType, curveType, repeatCount, length, points, sounds, newCombo ? comboOffset : 0, Convert.ToDouble(split[offset++], GameBase.nfi), Convert.ToDouble(split[offset++], GameBase.nfi), listSampleSets); } } else if ((type & HitObjectType.Spinner) > 0) { h = hitFactory.CreateSpinner(time, Convert.ToInt32(split[offset++], GameBase.nfi), soundType); } //Make sure we have a valid hitObject and actually add it to this manager. if (h != null) { h.SampleSet = ssi; Add(h, difficulty); } } break; case FileSection.Unknown: continue; //todo: readd this? not sure if we need it anymore. } } } } PostProcessing(); }
protected override IEnumerable <RushHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap) { Random random = new Random((int)original.StartTime); const float air_position_cutoff = 180f; const float ground_position_cutoff = 220f; const double etna_cutoff = 200d; const double repeat_cutoff = 100d; const double sawblade_cutoff = 0.9f; const double airsawblade_cutoff = 0.95f; var sampleLane = original.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE) ? LanedHitLane.Air : LanedHitLane.Ground; LanedHitLane? positionLane = null, sawbladeLane = null; HitObjectType hitObjectType = HitObjectType.Minion; bool bothLanes = false; if (original is IHasPosition hasPosition) { if (hasPosition.Y < air_position_cutoff) { positionLane = LanedHitLane.Air; } else if (hasPosition.Y > ground_position_cutoff) { positionLane = LanedHitLane.Ground; } else { bothLanes = true; } } if (original is IHasEndTime hasEndTime) { // etna sliders don't convert well, so just make them regular minions if (hasEndTime.Duration <= etna_cutoff) { hitObjectType = HitObjectType.Minion; } else if (original is IHasDistance) { hitObjectType = HitObjectType.NoteSheet; } else { hitObjectType = HitObjectType.MiniBoss; } } // temporary sawblade selection logic // 1) can only convert minions or dual orbs // 2) ground sawblades are more common than air sawblades // 3) air sawblades can only exist during kiai sections // if (hitObjectType == HitObjectType.Minion) // { // var rnd = random.NextDouble(); // sawbladeLane = LanedHitLane.Ground; // if (rnd >= sawblade_cutoff) // hitObjectType = HitObjectType.Sawblade; // if (original.Kiai && rnd >= airsawblade_cutoff) // sawbladeLane = LanedHitLane.Air; // } switch (hitObjectType) { case HitObjectType.Sawblade: if (bothLanes) { yield return(new Minion { Lane = (sawbladeLane ?? sampleLane).Opposite(), Samples = original.Samples, StartTime = original.StartTime }); } yield return(new Sawblade { Lane = sawbladeLane ?? sampleLane, StartTime = original.StartTime }); break; case HitObjectType.Minion: if (bothLanes) { yield return(new DualOrb { Samples = original.Samples, StartTime = original.StartTime, }); } else { yield return(new Minion { Lane = positionLane ?? sampleLane, Samples = original.Samples, StartTime = original.StartTime, }); } break; case HitObjectType.MiniBoss: yield return(new MiniBoss { Samples = original.Samples, StartTime = original.StartTime, EndTime = original.GetEndTime() }); break; case HitObjectType.NoteSheet: var sheetLane = positionLane ?? sampleLane; if (bothLanes || sheetLane == LanedHitLane.Ground) { yield return(new NoteSheet { Lane = LanedHitLane.Ground, Samples = original.Samples, StartTime = original.StartTime, EndTime = original.GetEndTime() }); } if (bothLanes || sheetLane == LanedHitLane.Air) { yield return(new NoteSheet { Lane = LanedHitLane.Air, Samples = bothLanes ? new List <HitSampleInfo>() : original.Samples, StartTime = original.StartTime, EndTime = original.GetEndTime() }); } if (!bothLanes && original is IHasRepeats hasRepeats && hasRepeats.RepeatCount > 0) { var duration = original.GetEndTime() - original.StartTime; var repeatDuration = duration / hasRepeats.SpanCount(); var skip = 1; // Currently an issue where an odd number of repeats (span count) will skip // the final minion if repeats are too short. Not sure what to do here since // it doesn't make rhythmic sense to add an extra hit object. // Examples: // *-*-*-*-* becomes *---*---* (good) // *-*-*-* becomes *---*-- (looks bad) instead of *---*-* (rhythmically worse) while (repeatDuration < repeat_cutoff) { repeatDuration *= 2; skip *= 2; } var otherLane = sheetLane.Opposite(); var repeatCurrent = original.StartTime; var index = -1; foreach (var nodeSample in hasRepeats.NodeSamples) { index++; if (index % skip != 0) { continue; } yield return(new Minion { Lane = otherLane, Samples = nodeSample, StartTime = repeatCurrent }); repeatCurrent += repeatDuration; } } break; } }
//automatically returns the correct type public static HitObject FromString(string s) { string[] split = s.Split(','); HitObjectType t = (HitObjectType)int.Parse(split[3], Constants.NumberFormat); HitObject h = null; switch (t & (HitObjectType)0b1000_1011) { case HitObjectType.Normal: h = new HitObjectCircle(); if (split.Length > 5) { (h as HitObjectCircle).SoundSampleData = split[5]; } break; case HitObjectType.Slider: h = new HitObjectSlider(); (h as HitObjectSlider).ParseSliderSegments(split[5]); (h as HitObjectSlider).RepeatCount = int.Parse(split[6], Constants.NumberFormat); if (split.Length > 7) { (h as HitObjectSlider).Length = double.Parse(split[7], Constants.NumberFormat); } //if (split.Length > 8) // (h as HitObjectSlider).HitSoundData = split[8]; //if (split.Length > 9) // (h as HitObjectSlider).SoundSampleData = split[9]; //if (split.Length > 10) // (h as HitObjectSlider).MoreSoundSampleData = split[10]; break; case HitObjectType.Spinner: h = new HitObjectSpinner(); (h as HitObjectSpinner).EndTime = int.Parse(split[5]); if (split.Length > 6) { (h as HitObjectSpinner).SoundSampleData = split[6]; } break; case HitObjectType.Hold: throw new NotImplementedException("Hold notes are not yet parsed."); default: throw new ArgumentOutOfRangeException(nameof(t), "Bad hitobject type"); } //note: parsed as decimal but cast to int in osu! if (h != null) { h.X = int.Parse(split[0], Constants.NumberFormat); h.Y = int.Parse(split[1], Constants.NumberFormat); h.Time = int.Parse(split[2], Constants.NumberFormat); h.Type = t; h.HitSound = (HitSound)int.Parse(split[4]); } else { Debug.Fail("unhandled hitobject type"); } return(h); }
public HoldCircle(Position position, int starttime, int endtime, bool isNewCombo = false, HitObjectType type = HitObjectType.HoldCircle, HitObjectSoundType soundType = HitObjectSoundType.Normal) : base(position, starttime, isNewCombo, type | HitObjectType.HoldCircle, soundType) { EndTime = endtime; }
//Gets the property requested from the hitobject, specified by index //null is returned if the property is not found static public string GetProperty(string hitobject, string property) { property = property.ToUpper(); //Divides the hitobject string into an array seperated by commas string[] hobject = hitobject.Split(','); int tag = -1; //TODO: Cleanup the property-selection code //Standard tags that all hitobjects have if (property == "X") { tag = 0; } else if (property == "Y") { tag = 1; } else if (property == "TIME") { tag = 2; } else if (property == "TYPE") { tag = 3; } else if (property == "HITSOUND") { tag = 4; } else { //If it's not one of the previous tags, then the location of the tag depends on the hitobject type //Be careful, GetHitObjectType calls this method, so make sure to avoid infinite recursion HitObjectType objecttype = GetHitObjectType(hitobject); if (objecttype == HitObjectType.Circle) { if (property == "ADDITION") { tag = 5; } } else if (objecttype == HitObjectType.Slider) { //Special case: Slidertype contains info about the slidertype and its control points //so I have separated it into two tags //Ex. B|380:120|332:96|332:96|304:124 //The slidertype tag is the first char in the entire tag if (property == "SLIDERTYPE") { return(hobject[5][0].ToString()); } //Custom tag: represents the control points within a slider //is everything after the slidertype char else if (property == "CONTROLPOINTS") { return(hobject[5].Substring(2)); } else if (property == "REPEAT") { tag = 6; } else if (property == "PIXELLENGTH") { tag = 7; } else if (property == "EDGEHITSOUND") { tag = 8; } else if (property == "EDGEADDITION") { tag = 9; } else if (property == "ADDITION") { tag = 10; } } else if (objecttype == HitObjectType.Spinner) { if (property == "ENDTIME") { tag = 5; } else if (property == "ADDITION") { tag = 6; } } } //Protects against accessing a tag that's out of bounds //Not an exception since this method returns null if the tag wasn't found if (tag == -1 || tag >= hobject.Length) { return(null); } return(hobject[tag]); }
public static bool IsType(this HitObjectType Type, HitObjectType type) { return((Type & type) > 0); }
private bool typeForObject(HitObject hitObject, out HitObjectType hitObjectType, out LanedHitLane lane, out MinionSize minionSize) { const float vertical_left = 170f; const float vertical_right = 340f; const float horizontal_top = 160f; const float horizontal_middle = 192f; const float horizontal_bottom = 224f; bool hasClap() => hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP); bool hasFinish() => hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); bool hasWhistle() => hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_WHISTLE); hitObjectType = HitObjectType.Minion; lane = LanedHitLane.Air; minionSize = MinionSize.Small; // this should never happen, honestly if (!(hitObject is IHasPosition position)) { return(false); } if (hitObject is IHasDuration && !(hitObject is IHasDistance)) { hitObjectType = HitObjectType.MiniBoss; return(true); } if (position.X < vertical_left) { if (position.Y >= horizontal_top && position.Y < horizontal_bottom) { hitObjectType = hitObject is IHasDuration ? HitObjectType.DualStarSheet : HitObjectType.DualHit; return(true); } lane = position.Y < horizontal_top ? LanedHitLane.Air : LanedHitLane.Ground; if (hitObject is IHasDuration) { hitObjectType = HitObjectType.StarSheet; } else { hitObjectType = HitObjectType.Minion; if (hasWhistle()) { minionSize = MinionSize.Medium; } else if (hasClap()) { minionSize = MinionSize.Large; } } return(true); } lane = position.Y < horizontal_middle ? LanedHitLane.Air : LanedHitLane.Ground; if (position.X >= vertical_right) { hitObjectType = HitObjectType.Hammer; return(true); } hitObjectType = hasFinish() ? HitObjectType.Heart : HitObjectType.Sawblade; return(true); }
public void CreateRandomBeatmap(AudioAnalyzer analyzer) { if (!File.Exists(this.filePath)) { Console.WriteLine("Error: File not found.(" + this.filePath + ")"); return; } if (beatmapOccupied) { Console.WriteLine("Error: Beatmap already occupied with HitObjects, Clear all objects before continuing"); return; } Console.WriteLine("Generating Random Beatmap. Appending to file..." + this.filePath); using (var osuFile = new StreamWriter(this.filePath, true)) { int numCircles = 0; // Basic Beatmap Creation var prevPoint = PlayField.GetRandomPointInside(); currentTimingPoint = (timingPoints.Count > 0) ? timingPoints[0] : null; float timestamp = (float)offset; // for (float timestamp = (float)offset; timestamp < songLength; timestamp += mpb) while (timestamp < songLength) { Vector2 pos = Vector2.Zero; float x = RandomHelper.Range(Beatmap.PlayField.Left, Beatmap.PlayField.Right + 1); float y = RandomHelper.Range(Beatmap.PlayField.Top, Beatmap.PlayField.Bottom + 1); bool newCombo = false; if (currentTimingPoint != null) { currentTimingPoint = TimingPoint.UpdateCurrentTimingPoint((int)timestamp, timingPoints, currentTimingPoint); } //Gets a point on a circle whose center lies at the previous point. do { float radius = RandomHelper.Range(30f, maxNoteDistance); //<---- This could be a function of bpm, i.e. time-distance relation between beats pos = prevPoint + RandomHelper.OnCircle(prevPoint, radius); } while (!PlayField.Contains(pos)); ////EXAMPLE USE CASE double threshold = Double.Parse("-10"); var peakData = analyzer.CreatePeakDataAt((int)timestamp, 10000); Console.WriteLine(peakData.ToString()); if (peakData.value < threshold) { //Continue without adding a beat here if no sound was detected. timestamp += AddTime(NoteDuration.Half); continue; } ////END EXAMPLE // Determine if new Combo is needed if (currentComboLength > comboChangeFlag) { newCombo = true; currentComboLength = currentComboLength % comboChangeFlag; } if (currentBeat % beatsPerMeasure == 0) { // Generate a random slider. var sliderType = EnumHelper.GetRandom <SliderCurveType>(); //sliderTypes[rnd.Next(0, 3)]; float sliderTimespan = (RandomHelper.NextFloat < 0.5f) ? difficulty.sliderTimestamp1 : difficulty.sliderTimestamp2; string sliderData = GetSliderData(pos, (int)timestamp, HitObjectType.Slider, HitObjectSoundType.None, sliderType, 1, sliderVelocity * currentTimingPoint.SliderVelocityMultiplier, RandomHelper.Range(minSliderCurves, maxSliderCurves + 1), sliderTimespan); osuFile.WriteLine(sliderData); numCircles++; timestamp += AddTime(sliderTimespan * 2); currentBeat += sliderTimespan * 2; currentComboLength += sliderTimespan * 2; } else { HitObjectType hitType = (newCombo) ? HitObjectType.NormalNewCombo : HitObjectType.Normal; // Test patterns! if (RandomHelper.NextFloat < 0.12) { Triple triple = new Triple(PlayField.Center, (int)timestamp, HitObjectSoundType.None, prevPoint, mpb, difficulty); osuFile.WriteLine(triple.SerializeForOsu()); currentComboLength += triple.totalLength; timestamp += AddTime(triple.totalLength); currentBeat += triple.totalLength; prevPoint = PlayField.Center; } else { string circleData = GetHitCircleData(new Vector2(x, y), (int)timestamp, hitType, HitObjectSoundType.None, prevPoint); osuFile.WriteLine(circleData); numCircles++; currentComboLength += difficulty.baseCircleTimestamp; timestamp += AddTime(difficulty.baseCircleTimestamp); currentBeat += difficulty.baseCircleTimestamp; prevPoint = new Vector2(x, y); } } } Console.WriteLine("Number of circles " + numCircles); } }
public static bool IsType(this HitObjectType Type, HitObjectType type) { return (Type & type) > 0; }
public Parser(Beatmap bm, StreamReader reader = null) { if (reader == null) { Console.WriteLine(bm.Title); List <HitObject> _hitObjects = bm.HitObjects; for (int i = 0; i < bm.ObjectsCount; i++) { bm.HitObjects[i].offset = (int)(bm.HitObjects[i].offset / speedMtpr); } bm.CS *= CSMtpr; bm.AR *= statMtpr; bm.OD *= statMtpr; bm.HP *= statMtpr; bm.CS = Math.Min(10, bm.CS); bm.AR = Math.Min(10, bm.AR); bm.OD = Math.Min(10, bm.OD); bm.HP = Math.Min(10, bm.HP); return; } string line, section = null; while ((line = reader.ReadLine()?.Trim()) != null) { if (line.StartsWith("[") && line.Contains("]")) { section = line.Substring(1, line.Length - 2); continue; } if (line.Length <= 0) { continue; } switch (section) { case "Metadata": String key = line.Split(':')[0]; String value = line.Split(':')[1]; switch (key) { case "Title": bm.Title = value; break; case "TitleUnicode": bm.TitleUnicode = value; break; case "Artist": bm.Artist = value; break; case "ArtistUnicode": bm.ArtistUnicode = value; break; case "Version": bm.Version = value; break; case "Creator": bm.Creator = value; break; } break; case "General": String key_gen = line.Split(':')[0]; String value_gen = line.Split(new string[] { ": " }, StringSplitOptions.None)[1]; switch (key_gen) { case "AudioFilename": bm.AudioFileName = value_gen; break; case "AudioLeadIn": bm.AudioLeadIn = value_gen; break; case "PreviewTime": bm.PreviewTime = value_gen; break; case "Countdown": bm.Countdown = value_gen; break; case "SampleSet": bm.SampleSet = value_gen; break; case "StackLeniency": bm.StackLeniency = value_gen; break; case "Mode": bm.Mode = value_gen; break; case "LetterboxInBreaks": bm.LetterboxInBreaks = value_gen; break; case "WidescreenStoryboard": bm.WidescreenStoryboard = value_gen; break; } break; case "Editor": bm.isEditorExist = true; String key_edit = line.Split(':')[0]; String value_edit = line.Split(new string[] { ": " }, StringSplitOptions.None)[1]; switch (key_edit) { case "Bookmarks": int count = value_edit.Split(',').Length; for (int i = 0; i < count; i++) { bm.Bookmarks.Add(int.Parse(value_edit.Split(',')[i])); } break; case "DistanceSpacing": bm.DistanceSpacing = value_edit; break; case "BeatDivisor": bm.BeatDivisor = value_edit; break; case "GridSize": bm.GridSize = value_edit; break; case "TimelineZoom": bm.TimelineZoom = value_edit; break; } break; case "Difficulty": String key_diff = line.Split(':')[0]; float val_diff = float.Parse(line.Split(':')[1]); switch (key_diff) { case "CircleSize": bm.CS = val_diff; break; case "OverallDifficulty": bm.OD = val_diff; break; case "ApproachRate": isARExists = true; bm.AR = val_diff; break; case "HPDrainRate": bm.HP = val_diff; break; case "SliderMultiplier": bm.SV = val_diff; break; case "SliderTickRate": bm.ST = val_diff; break; } break; case "Events": if (line.Contains(".png") || line.Contains(".jpg")) { if (isBGfound) { continue; } isBGfound = true; bm.Events_bg = line; break; } if (line.StartsWith("2,")) { int[] breaks = new int[2]; breaks[0] = int.Parse(line.Split(',')[1]); breaks[1] = int.Parse(line.Split(',')[2]); bm.Events_break.Add(breaks); } break; case "TimingPoints": double[] tp = new double[line.Split(',').Length]; for (int i = 0; i < line.Split(',').Length; i++) { tp[i] = double.Parse(line.Split(',')[i]); if (i == 2) { tp[i] = double.Parse(line.Split(',')[i]) / speedMtpr; } } bm.TimingPoints.Add(tp); break; case "HitObjects": if (bm.ObjectsCount == 0) { firstnoteOffset = int.Parse(line.Split(',')[2]); } bm.ObjectsCount++; String[] str = line.Split(','); HitObjectType objType = (HitObjectType)int.Parse(str[3]); HitObject obj = new HitObject(int.Parse(str[0]), int.Parse(str[1]), (int)(int.Parse(str[2]) / speedMtpr), objType, line.Split(new string[] { str[2] }, StringSplitOptions.None)[1], line); bm.HitObjects.Add(obj); break; } } lastnoteOffset = bm.HitObjects[bm.ObjectsCount - 1].offset; double mainBPM = 0; double prevBPM = 0; int prevOffset = 0, offsetGap = 0; Boolean isSingleBPM = true; double currBPM = 0; int currOffset = 0; foreach (double[] tmp in bm.TimingPoints) { if (tmp[1] < 0) { continue; } currBPM = tmp[1]; currOffset = (int)tmp[0]; if (offsetGap < currOffset - prevOffset) { if (offsetGap != 0) { isSingleBPM = false; } offsetGap = currOffset - prevOffset; mainBPM = prevBPM; } prevOffset = currOffset; prevBPM = currBPM; } if (lastnoteOffset - currOffset > offsetGap) { mainBPM = prevBPM; } if (isSingleBPM) { mainBPM = prevBPM; } bm.mainBPM = (float)Math.Round((1000d / mainBPM) * 60d); if (!isARExists) { bm.AR = bm.OD; } bm.CS *= CSMtpr; bm.AR *= statMtpr; bm.OD *= statMtpr; bm.HP *= statMtpr; bm.CS = Math.Min(10, bm.CS); bm.AR = Math.Min(10, bm.AR); bm.OD = Math.Min(10, bm.OD); bm.HP = Math.Min(10, bm.HP); bm.ARms = bm.AR < 5.0f ? AR0Ms - ARMsStep1 * bm.AR : AR5Ms - ARMsStep2 * (bm.AR - 5.0f); bm.ARms = Math.Min(AR0Ms, Math.Max(AR10Ms, bm.ARms)); bm.ARms /= speedMtpr; bm.AR = (float)( bm.ARms > AR5Ms ? (AR0Ms - bm.ARms) / ARMsStep1 : 5.0 + (AR5Ms - bm.ARms) / ARMsStep2 ); bm.ODms = OD0Ms - Math.Ceiling(ODMsStep * bm.OD); bm.ODms = Math.Min(OD0Ms, Math.Max(OD10Ms, bm.ODms)); bm.ODms /= speedMtpr; bm.OD = (float)((OD0Ms - bm.ODms) / ODMsStep); }
public Circle(HitObjectType Type, Vector2 Position, double Time, Color Color) : base(Type, Position, Time, Color) { }
// Creates a random beatmap. This APPENDS to the current file. // TODO: Need to make sure we are not overwriting other data, and starting from the correct location. public void CreateRandomBeatmap() { if (!File.Exists(this.filePath)) { Console.WriteLine("Error: File not found.(" + this.filePath + ")"); return; } Console.WriteLine("Generating Random Beatmap. Appending to file..." + this.filePath); using (var osuFile = new StreamWriter(this.filePath, true)) { int numCircles = 0; // Basic Beatmap Creation var prevPoint = Vector2.NegativeOne; float timestamp = (float)offset; // for (float timestamp = (float)offset; timestamp < songLength; timestamp += mpb) while (timestamp < songLength) { float x = RandomHelper.Range(Beatmap.PlayField.Left, Beatmap.PlayField.Right + 1); float y = RandomHelper.Range(Beatmap.PlayField.Top, Beatmap.PlayField.Bottom + 1); bool newCombo = false; // Determine if new Combo is needed if (currentComboLength > comboChangeFlag) { newCombo = true; currentComboLength = currentComboLength % comboChangeFlag; } Console.WriteLine("currentBeat" + currentBeat); /* * float getBeatType = RandomHelper.NextFloat; * if (getBeatType < 0.05f) * { * timestamp += AddTime(NoteDuration.Quarter); * currentBeat += NoteDuration.Quarter; * continue; * } */ if (currentBeat % beatsPerMeasure == 0) { // Generate a random slider. // Test Random Slider Durations var sliderType = EnumHelper.GetRandom <SliderCurveType>(); HitObjectType hitType = (newCombo) ? HitObjectType.SliderNewCombo : HitObjectType.Slider; float sliderTimespan = (RandomHelper.NextFloat < 0.5f) ? difficulty.sliderTimestamp1 : difficulty.sliderTimestamp2; string sliderData = GetSliderData(new Vector2(x, y), (int)timestamp, hitType, HitObjectSoundType.None, sliderType, 1, sliderVelocity, RandomHelper.Range(minSliderCurves, maxSliderCurves + 1), sliderTimespan); osuFile.WriteLine(sliderData); numCircles++; timestamp += AddTime(sliderTimespan * 2); currentBeat += sliderTimespan * 2; currentComboLength += sliderTimespan * 2; } else { HitObjectType hitType = (newCombo) ? HitObjectType.NormalNewCombo : HitObjectType.Normal; // Test patterns! if (RandomHelper.NextFloat < 0.12) { Triple triple = new Triple(PlayField.Center, (int)timestamp, HitObjectSoundType.None, prevPoint, mpb, difficulty); osuFile.WriteLine(triple.SerializeForOsu()); currentComboLength += triple.totalLength; timestamp += AddTime(triple.totalLength); currentBeat += triple.totalLength; prevPoint = PlayField.Center; } else { string circleData = GetHitCircleData(new Vector2(x, y), (int)timestamp, hitType, HitObjectSoundType.None, prevPoint); osuFile.WriteLine(circleData); numCircles++; currentComboLength += difficulty.baseCircleTimestamp; timestamp += AddTime(difficulty.baseCircleTimestamp); currentBeat += difficulty.baseCircleTimestamp; prevPoint = new Vector2(x, y); } } // New Combo } Console.WriteLine("Number of circles " + numCircles); } }
public HitCircle(Position position, int startTime, bool isNewCombo = false, HitObjectType type = HitObjectType.HitCircle, HitObjectSoundType soundType = HitObjectSoundType.Normal) : base(startTime, position, isNewCombo, type | HitObjectType.HitCircle, soundType) { }
/// <summary> /// Constructs a new <see cref="HitObjectTypeAttribute"/> with the given unique type. /// </summary> /// <param name="type">The type to uniquely identify the annotated <see cref="IHitObject"/>-implementing class.</param> public HitObjectTypeAttribute(HitObjectType type) { Type = type; }
public BaseHitObject Parse(string text) { try { string[] splits = text.Split(','); Vector2 pos = new Vector2(ParseUtils.ParseFloat(splits[0]), ParseUtils.ParseFloat(splits[1])); float startTime = ParseUtils.ParseFloat(splits[2]) + offset; HitObjectType type = (HitObjectType)ParseUtils.ParseInt(splits[3]); int comboOffset = (int)(type & HitObjectType.ComboOffset) >> 4; type &= ~HitObjectType.ComboOffset; bool isNewCombo = (int)(type & HitObjectType.NewCombo) != 0; type &= ~HitObjectType.NewCombo; var soundType = (SoundType)ParseUtils.ParseInt(splits[4]); var customSample = new CustomSampleInfo(); // Now parse the actual hit objects. BaseHitObject result = null; // If this object is a hit circle if ((type & HitObjectType.Circle) != 0) { result = CreateCircle(pos, isNewCombo, comboOffset); if (splits.Length > 5) { ParseCustomSample(splits[5], customSample); } } else if ((type & HitObjectType.Slider) != 0) { PathType pathType = PathType.Catmull; float length = 0; string[] pointSplits = splits[5].Split('|'); // Find the number of valid slider node points. int pointCount = 1; foreach (var p in pointSplits) { if (p.Length > 1) { pointCount++; } } // Parse node points var nodePoints = new Vector2[pointCount]; nodePoints[0] = Vector2.zero; int pointIndex = 1; foreach (var p in pointSplits) { // Determine which path type was found. if (p.Length == 1) { switch (p) { case "C": pathType = PathType.Catmull; break; case "B": pathType = PathType.Bezier; break; case "L": pathType = PathType.Linear; break; case "P": pathType = PathType.PerfectCurve; break; } continue; } // Parse point position string[] pointPos = p.Split(':'); nodePoints[pointIndex++] = new Vector2(ParseUtils.ParseFloat(pointPos[0]), ParseUtils.ParseFloat(pointPos[1])) - pos; } // Change perfect curve to linear if certain conditions meet. if (nodePoints.Length == 3 && pathType == PathType.PerfectCurve && IsLinearPerfectCurve(nodePoints)) { pathType = PathType.Linear; } // Parse slider repeat count int repeatCount = ParseUtils.ParseInt(splits[6]); if (repeatCount > 9000) { throw new Exception(); } // Osu file has +1 addition to the actual number of repeats. repeatCount = Math.Max(0, repeatCount - 1); if (splits.Length > 7) { length = Math.Max(0, ParseUtils.ParseFloat(splits[7])); } if (splits.Length > 10) { ParseCustomSample(splits[10], customSample); } // Number of repeats + start(1) + end(1) int nodeCount = repeatCount + 2; // Parse per-node sound samples var nodeCustomSamples = new List <CustomSampleInfo>(); for (int i = 0; i < nodeCount; i++) { nodeCustomSamples.Add(customSample.Clone()); } if (splits.Length > 9 && splits[9].Length > 0) { string[] sets = splits[9].Split('|'); for (int i = 0; i < nodeCount; i++) { if (i >= sets.Length) { break; } ParseCustomSample(sets[i], nodeCustomSamples[i]); } } // Set all nodes' sample types to default. var nodeSampleTypes = new List <SoundType>(); for (int i = 0; i < nodeCount; i++) { nodeSampleTypes.Add(soundType); } // Parse per-node sample types if (splits.Length > 8 && splits[8].Length > 0) { string[] nodeSampleSplits = splits[8].Split('|'); for (int i = 0; i < nodeCount; i++) { if (i > nodeSampleSplits.Length) { break; } nodeSampleTypes[i] = (SoundType)ParseUtils.ParseInt(nodeSampleSplits[i]); } } // Map sample types to custom sample infos. var nodeSamples = new List <List <SoundInfo> >(nodeCount); for (int i = 0; i < nodeCount; i++) { nodeSamples.Add(GetSamples(nodeSampleTypes[i], nodeCustomSamples[i])); } result = CreateSlider(pos, isNewCombo, comboOffset, nodePoints, length, pathType, repeatCount, nodeSamples); // Hit sound for the root slider should be played at the end. result.Samples = nodeSamples[nodeSamples.Count - 1]; } else if ((type & HitObjectType.Spinner) != 0) { float endTime = Math.Max(startTime, ParseUtils.ParseFloat(splits[5]) + offset); result = CreateSpinner(pos, isNewCombo, comboOffset, endTime); if (splits.Length > 6) { ParseCustomSample(splits[6], customSample); } } else if ((type & HitObjectType.Hold) != 0) { float endTime = Math.Max(startTime, ParseUtils.ParseFloat(splits[2] + offset)); // I can understand all others except this, because Hold type only exists for Mania mode. if (splits.Length > 5 && !string.IsNullOrEmpty(splits[5])) { string[] sampleSplits = splits[5].Split(':'); endTime = Math.Max(startTime, ParseUtils.ParseFloat(sampleSplits[0])); ParseCustomSample(string.Join(":", sampleSplits.Skip(1).ToArray()), customSample); } result = CreateHold(pos, isNewCombo, comboOffset, endTime + offset); } if (result == null) { Logger.LogVerbose("HitObjectParser.Parse - Unknown hit object for line: " + text); return(null); } result.StartTime = startTime; if (result.Samples.Count == 0) { result.Samples = GetSamples(soundType, customSample); } isFirstObject = false; return(result); } catch (Exception e) { Logger.LogError($"HitObjectParser.Parse - Failed to parse line: {text}, Error: {e.Message}"); } return(null); }
private void Parse(string beatmapFile) { StreamReader sr = new StreamReader(beatmapFile); string line, currentSection = ""; while ((line = sr.ReadLine()) != null) { //Skip commented or blank lines if (line.StartsWith("//") || line.Length == 0) { continue; } //Get section tag if line matches "[Section Name"] if (line.StartsWith("[")) { currentSection = line; continue; } //[General], [Metadata], [Difficulty] sections if ((currentSection == "[General]") || (currentSection == "[Metadata]") || (currentSection == "[Difficulty]") || (currentSection == "[Editor]")) { //Split line into values string[] lineSplit = line.Split(':'); string property = lineSplit[0].TrimEnd(); string value = lineSplit[1].Trim(); //Assign values to fields FieldInfo fi = this.GetType().GetField(property); if (fi != null) { if (fi.FieldType == typeof(float?)) { fi.SetValue(this, (float?)Convert.ToDouble(value)); } if (fi.FieldType == typeof(float)) { fi.SetValue(this, (float)Convert.ToDouble(value)); } else if ((fi.FieldType == typeof(int?)) || (fi.FieldType == typeof(int))) { fi.SetValue(this, Convert.ToInt32(value)); } else if (fi.FieldType == typeof(string)) { fi.SetValue(this, value); } } //[TimingPoints] section } else if (currentSection == "[TimingPoints]") { //Split line into values string[] values = line.Split(','); //Create new timing point (time, time per beat, time signature, frenzy mode) this.TimingPoints.Add(new TimingPoint((float)Convert.ToDouble(values[0]), (float)Convert.ToDouble(values[1]), Convert.ToInt32(values[2]), Convert.ToBoolean(Convert.ToDouble(values[7])))); //[HitObjects] section } else if (currentSection == "[HitObjects]") { //Split line into values string[] values = line.Split(','); //Get hit object type HitObjectType type = (HitObjectType)Convert.ToInt32(values[3]); //[Circle] if ((type & HitObjectType.Circle) > 0) { //Add new circle hit object HitObjects.Add(new CircleObject(Convert.ToInt32(values[0]), Convert.ToInt32(values[1]), (float)Convert.ToDouble(values[2]))); } //[Slider] else if ((type & HitObjectType.Slider) > 0) { //Add new slider hit object HitObjects.Add(new SliderObject(Convert.ToInt32(values[0]), Convert.ToInt32(values[1]), (float)Convert.ToDouble(values[2]), (float)Convert.ToDouble(Convert.ToInt32(values[7])))); } //[Spinner] else if ((type & HitObjectType.Spinner) > 0) { //Add new spinner hit object HitObjects.Add(new SpinnerObject(Convert.ToInt32(values[0]), Convert.ToInt32(values[1]), (float)Convert.ToDouble(values[2]), (float)Convert.ToDouble(values[5]))); } } } }
/// <summary> /// Updates the visible (combined) score and triggers any replay alterations required. /// </summary> /// <param name="trigger">The player that triggered this update.</param> private void UpdateVisibleScore(ScoreboardEntryExtended trigger) { int count300 = combinedScore.Count300; int count100 = combinedScore.Count100; int count50 = combinedScore.Count50; int countMiss = combinedScore.CountMiss; int totalScore = combinedScore.TotalScore; combinedScore.HpGraph = Player.currentScore.HpGraph; combinedScore.Reset(); foreach (int i in usedPlayerSlots) { Score thisScore = player.ScoreEntries[i].Score; if (thisScore == null) { continue; } combinedScore.TotalScore += thisScore.TotalScore; combinedScore.Count50 += thisScore.Count50; combinedScore.Count100 += thisScore.Count100; combinedScore.Count300 += thisScore.Count300; combinedScore.CountMiss += thisScore.CountMiss; combinedScore.CountGeki += thisScore.CountGeki; combinedScore.CountKatu += thisScore.CountKatu; combinedScore.CurrentHp = Math.Max(combinedScore.CurrentHp, thisScore.CurrentHp); combinedScore.MaxCombo = Math.Max(combinedScore.MaxCombo, thisScore.MaxCombo); } if (trigger != player.scoreEntry) { HitObjectType type = (HitObjectType)trigger.Frame.tagByte; if (type.IsType(HitObjectType.Normal)) //HitCircle { //Wasn't a result of the active player. if (combinedScore.Count50 > count50) { AlterReplay(IncreaseScoreType.Hit50); } else if (combinedScore.Count100 > count100) { AlterReplay(IncreaseScoreType.Hit100); } else if (combinedScore.CountMiss > countMiss) { AlterReplay(IncreaseScoreType.Miss); } } else if (type.IsType(HitObjectType.Slider)) { if (combinedScore.Count50 > count50) { AlterReplaySlider(IncreaseScoreType.Hit50); } else if (combinedScore.Count100 > count100) { AlterReplaySlider(IncreaseScoreType.Hit100); } else if (combinedScore.Count300 > count300) { AlterReplaySlider(IncreaseScoreType.Hit300); } else if (combinedScore.CountMiss > countMiss) { AlterReplaySlider(IncreaseScoreType.Miss); } } } player.Ruleset.ScoreDisplay.Update(combinedScore.TotalScore); }