/// <summary> /// Pastes hitobjects that are in the clipboard /// </summary> public void PasteHitObjects() { var clonedObjects = new List <HitObjectInfo>(); var difference = (int)AudioEngine.Track.Time - Clipboard.First().StartTime; foreach (var h in Clipboard) { var hitObject = new HitObjectInfo() { StartTime = h.StartTime + difference, EditorLayer = h.EditorLayer, HitSound = h.HitSound, Lane = h.Lane }; if (h.IsLongNote) { hitObject.EndTime = h.EndTime + difference; } clonedObjects.Add(hitObject); } ActionManager.Perform(new EditorActionBatchPlaceHitObjectKeys(WorkingMap, ScrollContainer, clonedObjects)); }
/// <summary> /// Refreshes a bar sample based on the hitobject received. /// </summary> /// <param name="h"></param> public void RefreshSample(HitObjectInfo h) { var sample = GetSample(h); // Bar needs to be resized if (SampleBars.ContainsKey(sample)) { var objs = Qua.HitObjects.FindAll(x => h.StartTime / SampleTime == x.StartTime / SampleTime).ToList(); var width = GetBarWidth(objs); SampleBars[sample].Width = width; SampleBars[sample].Tint = GetBarColor(width); SampleBars[sample].X = Width - SampleBars[sample].Width - 1; } // Bar needs to be added else { var objs = Qua.HitObjects.FindAll(x => h.StartTime / SampleTime == x.StartTime / SampleTime).ToList(); var width = GetBarWidth(objs); var bar = new Sprite { Parent = this, Alignment = Alignment.BotLeft, Size = new ScalableVector2(width, 5), Y = -Height * (float)(objs.First().StartTime / SampleTime * SampleTime / AudioEngine.Track.Length) - 2, Tint = GetBarColor(width) }; bar.X = Width - bar.Width - 1; SampleBars.Add(GetSample(objs.First()), bar); } }
/// <summary> /// Plays the correct hitsounds based on the note index of the HitObjectPool. /// </summary> public static void PlayObjectHitSounds(HitObjectInfo hitObject) { // Normal if (hitObject.HitSound == 0 || (HitSounds.Normal & hitObject.HitSound) != 0) { SkinManager.Skin.SoundHit.CreateChannel().Play(); } // Clap if ((HitSounds.Clap & hitObject.HitSound) != 0) { SkinManager.Skin.SoundHitClap.CreateChannel().Play(); } // Whistle if ((HitSounds.Whistle & hitObject.HitSound) != 0) { SkinManager.Skin.SoundHitWhistle.CreateChannel().Play(); } // Finish if ((HitSounds.Finish & hitObject.HitSound) != 0) { SkinManager.Skin.SoundHitFinish.CreateChannel().Play(); } }
void OnEnable() { grassPatch = (GrassPatch)target; currentSelectedObject = new HitObjectInfo(); currentSelectedGroundVert = new GroundVert(Vector3.zero); Refresh(); }
public HitObjectInfo getEnemyDetected() { RaycastHit hitInfo; enemies = GameObject.FindGameObjectsWithTag("Enemy"); foreach (GameObject enemy in enemies) { HitObjectInfo hitObjectInfo = new HitObjectInfo(); //Debug.Log("Enemy transform position :: " + (enemy.transform.position)); Vector3 rayDirection = (enemy.transform.position - new Vector3(0.0f, 13.0f, 0.0f)) - this.transform.position; if (Vector3.Angle (rayDirection, this.transform.rotation * Vector3.forward) <= fieldOfView) { if (Physics.Raycast (this.transform.position, enemy.transform.position - new Vector3(0.0f, 13.0f, 0.0f), out hitInfo, 100.0f)) { Debug.Log ("Hit Object from Falcon :: " + hitInfo.transform.name + ", " + hitInfo.transform.tag); if (hitInfo.transform.tag.Contains("Enemy")) { Debug.Log ("I have the Empire ship in my sights!"); hitObjectInfo.setHitPoint(hitInfo.point); hitObjectInfo.setHitObject(hitInfo.transform.gameObject); return hitObjectInfo; } else { continue; } } else { continue; } } else { continue; } } Debug.Log ("Negative on Empire Fleet's location!!"); return null; }
/// <summary> /// Gets the judgement of a particular hitobject in the map /// </summary> /// <param name="ho"></param> /// <returns></returns> public int GetHitObjectJudgementIndex(HitObjectInfo ho) { var index = -1; var total = 0; for (var i = 0; i < HitObjects.Count; i++) { if (HitObjects[i] == ho) { return(total); } if (HitObjects[i].IsLongNote) { total += 2; } else { total += 1; } } return(index); }
/// <summary> /// Gets a hitobject at a particular judgement index /// </summary> /// <param name="index"></param> /// <returns></returns> public HitObjectInfo GetHitObjectAtJudgementIndex(int index) { HitObjectInfo h = null; var total = 0; for (var i = 0; i < HitObjects.Count; i++) { total += 1; if (total - 1 == index) { h = HitObjects[i]; break; } if (HitObjects[i].IsLongNote) { total += 1; } if (total - 1 == index) { h = HitObjects[i]; break; } } return(h); }
void HandleInput(Event e) { if (e.type == EventType.MouseDown && e.button == 0 && e.modifiers == EventModifiers.Control) { currentSelectedObject = ObjectUnderMouse(); } if (e.type == EventType.MouseDown && e.button == 0 && e.modifiers == EventModifiers.Shift) { if (currentSelectedObject.transform != null) { SelectNearestVertex(); } } // to prevent from deselection // if (grassPatch == null) { return; } if (Selection.activeGameObject != grassPatch.transform.gameObject) { Selection.activeGameObject = grassPatch.transform.gameObject; } }
/// <summary> /// Plays the correct keysounds based on the note index of the HitObjectPool. /// </summary> public static void PlayObjectKeySounds(HitObjectInfo hitObject) { foreach (var keySound in hitObject.KeySounds) { CustomAudioSampleCache.Play(keySound.Sample - 1, keySound.Volume); } }
/// <summary> /// Ctor - /// </summary> /// <param name="hitObject"></param> /// <param name="type"></param> /// <param name="time"></param> /// <param name="keys"></param> public ReplayAutoplayFrame(HitObjectInfo hitObject, ReplayAutoplayFrameType type, int time, ReplayKeyPressState keys) { HitObject = hitObject; Type = type; Time = time; Keys = keys; }
/// <summary> /// Adds a HitObject sprite to the container. /// </summary> public void AddHitObjectSprite(HitObjectInfo h) { DrawableEditorHitObject hitObject; var skin = SkinManager.Skin.Keys[Ruleset.WorkingMap.Mode]; var index = skin.ColorObjectsBySnapDistance ? HitObjectManager.GetBeatSnap(h, h.GetTimingPoint(Ruleset.WorkingMap.TimingPoints)) : 0; if (h.IsLongNote) { if (ConfigManager.EditorViewLayers.Value) { hitObject = new DrawableEditorHitObjectLong(this, h, skin.EditorLayerNoteHitObjects[h.Lane - 1], skin.EditorLayerNoteHoldBodies[h.Lane - 1], skin.EditorLayerNoteHoldEnds[h.Lane - 1]); } else { hitObject = new DrawableEditorHitObjectLong(this, h, skin.NoteHoldHitObjects[h.Lane - 1][index], skin.NoteHoldBodies[h.Lane - 1].First(), skin.NoteHoldEnds[h.Lane - 1]); } } else { if (ConfigManager.EditorViewLayers.Value) { hitObject = new DrawableEditorHitObject(this, h, skin.EditorLayerNoteHitObjects[h.Lane - 1]); } else { hitObject = new DrawableEditorHitObject(this, h, skin.NoteHitObjects[h.Lane - 1][index]); } } hitObject.Alignment = Alignment.TopLeft; hitObject.X = ScreenRectangle.X + LaneSize * (h.Lane - 1) + DividerLineWidth; hitObject.Width = LaneSize - DividerLineWidth; // Make sure the width of the long note is updated if this object is indeed an LN. if (hitObject is DrawableEditorHitObjectLong longNote) { longNote.Body.Width = hitObject.Width; longNote.Tail.Width = hitObject.Width; } hitObject.AppearAsActive(); // Check if the layer is hidden that the user is adding the object to and display the object // as that appropriate colour. if (((EditorScreenView)Ruleset.Screen.View).LayerCompositor.ScrollContainer.AvailableItems[hitObject.Info.EditorLayer].Hidden) { hitObject.AppearAsHiddenInLayer(); } lock (HitObjects) HitObjects.Add(hitObject); }
/// <inheritdoc /> /// <summary> /// Ctor - /// </summary> /// <param name="ruleset"></param> /// <param name="info"></param> public GameplayHitObjectKeys(HitObjectInfo info, GameplayRulesetKeys ruleset, HitObjectManagerKeys manager) { HitObjectManager = manager; Ruleset = ruleset; InitializeSprites(ruleset, info.Lane - 1); InitializeObject(manager, info); }
/// <inheritdoc /> /// <summary> /// </summary> /// <param name="container"></param> /// <param name="info"></param> /// <param name="texHead"></param> /// <param name="texBody"></param> /// <param name="texTail"></param> public DrawableEditorHitObjectLong(EditorScrollContainerKeys container, HitObjectInfo info, Texture2D texHead, Texture2D texBody, Texture2D texTail) : base(container, info, texHead) { TextureBody = texBody; TextureTail = texTail; CreateLongNoteSprite(); SelectionSprite.Parent = Body; SelectionSprite.Alignment = Alignment.MidLeft; }
/// <summary> /// Resizes a long note with that given hitobject info. /// </summary> /// <param name="h"></param> public DrawableEditorHitObjectLong ResizeLongNote(HitObjectInfo h) { var note = HitObjects.Find(x => x.Info == h); if (note is DrawableEditorHitObjectLong n) { n.ResizeLongNote(); } return(note as DrawableEditorHitObjectLong); }
/// <inheritdoc /> /// <summary> /// </summary> /// <param name="container"></param> /// <param name="info"></param> /// <param name="texHead"></param> public DrawableEditorHitObject(EditorScrollContainerKeys container, HitObjectInfo info, Texture2D texHead) { Container = container; Info = info; Image = texHead; SetHeight(); DestroyIfParentIsNull = false; SetPositionY(); CreateSelectionSprite(); }
/// <inheritdoc /> /// <summary> /// Ctor - /// </summary> /// <param name="ruleset"></param> /// <param name="info"></param> public GameplayHitObjectKeys(HitObjectInfo info, GameplayRulesetKeys ruleset, HitObjectManagerKeys manager) { HitObjectManager = manager; Ruleset = ruleset; var lane = info.Lane - 1; var playfield = (GameplayPlayfieldKeys)ruleset.Playfield; LongNoteSizeDifference = playfield.LongNoteSizeAdjustment[lane]; InitializeSprites(ruleset, lane, playfield.ScrollDirections[lane]); InitializeObject(manager, info); }
/// <summary> /// Ctor /// </summary> /// <param name="type"></param> /// <param name="keyPressType"></param> /// <param name="hitObject"></param> /// <param name="songPos"></param> /// <param name="judgement"></param> /// <param name="hitDifference"></param> /// <param name="acc"></param> /// <param name="health"></param> public HitStat(HitStatType type, KeyPressType keyPressType, HitObjectInfo hitObject, int songPos, Judgement judgement, int hitDifference, double acc, float health) { HitObject = hitObject; SongPosition = songPos; Judgement = judgement; HitDifference = hitDifference; Accuracy = acc; Health = health; Type = type; KeyPressType = keyPressType; }
HitObjectInfo ObjectUnderMouse() { Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); RaycastHit hit = new RaycastHit(); HitObjectInfo objectInfo = new HitObjectInfo(); if (Physics.Raycast(ray, out hit)) { objectInfo.transform = hit.transform; objectInfo.hitPoint = hit.point; } return(objectInfo); }
public static HitObject QuaHitToManiaHit(HitObjectInfo quaHit, int keyMode) { HitObject maniaHit; int xfix = 512 / keyMode; if (quaHit.IsLongNote) { maniaHit = new HitObject((quaHit.Lane) * xfix - (xfix / 2), quaHit.StartTime, true, quaHit.EndTime); } else { maniaHit = new HitObject((quaHit.Lane) * xfix - (xfix / 2), quaHit.StartTime); } return(maniaHit); }
private List <HitObjectInfo> GetHitObjectsFromGaze(Vector3 gazeOrigin, Vector3 gazeDirection) { RaycastHit[] hitColliders = Physics.RaycastAll(gazeOrigin, gazeDirection); List <HitObjectInfo> hitObjectInfoList = new List <HitObjectInfo>(); foreach (var colliderhit in hitColliders) { HitObjectInfo hitInfo = new HitObjectInfo(); hitInfo.ObjectName = colliderhit.collider.gameObject.name; hitInfo.HitObjectPosition = colliderhit.collider.transform.position; hitInfo.HitPointOnObject = colliderhit.point; hitObjectInfoList.Add(hitInfo); } return(hitObjectInfoList); }
/// <summary> /// Removes a HitObject sprite at a given index. /// </summary> public void RemoveHitObjectSprite(HitObjectInfo h) { lock (HitObjects) { var ho = HitObjects?.Find(x => x.Info == h); HitObjects?.Remove(ho); if (ho != null) { ho.IsInView = false; ho?.Destroy(); } HitObjects = HitObjects.OrderBy(x => x.Info.StartTime).ToList(); } }
public HitObject(HitObjectInfo hitObject, int keyCount, bool dontUseOffset) { var offset = dontUseOffset ? 0 : Osu.OsuBeatmap.QUAVER_TO_OSU_OFFSET; Time = hitObject.StartTime + offset; XPosition = 512 * hitObject.Lane / keyCount - 64; if (hitObject.IsLongNote) { Type = 1 << 7; EndTime = hitObject.EndTime + offset; } else { Type = 1 << 0; } // TODO: Hitsounds }
private List <HitObjectInfo> GetFirstHitObjectFromGaze(Vector3 gazeOrigin, Vector3 gazeDirection, float distance) { RaycastHit hit; bool hitColliders = Physics.Raycast(gazeOrigin, gazeDirection, out hit, distance); List <HitObjectInfo> hitObjectInfoList = new List <HitObjectInfo>(); if (hitColliders) { HitObjectInfo hitInfo = new HitObjectInfo(); hitInfo.ObjectName = hit.collider.gameObject.name; hitInfo.HitObjectPosition = hit.collider.transform.position; hitInfo.HitPointOnObject = hit.point; hitObjectInfoList.Add(hitInfo); } return(hitObjectInfoList); }
/// <summary> /// Returns color of note beatsnap /// </summary> /// <param name="info"></param> /// <param name="timingPoint"></param> /// <returns></returns> public static int GetBeatSnap(HitObjectInfo info, TimingPointInfo timingPoint) { // Add 2ms offset buffer space to offset and get beat length var pos = info.StartTime - timingPoint.StartTime + 2; var beatlength = 60000 / timingPoint.Bpm; // subtract pos until it's less than beat length. multiple loops for efficiency while (pos >= beatlength * (1 << 16)) { pos -= beatlength * (1 << 16); } while (pos >= beatlength * (1 << 12)) { pos -= beatlength * (1 << 12); } while (pos >= beatlength * (1 << 8)) { pos -= beatlength * (1 << 8); } while (pos >= beatlength * (1 << 4)) { pos -= beatlength * (1 << 4); } while (pos >= beatlength) { pos -= beatlength; } // Calculate Note's snap index var index = (int)(Math.Floor(48 * pos / beatlength)); // Return Color of snap index for (var i = 0; i < 8; i++) { if (index % BeatSnaps[i] == 0) { return(i); } } // If it's not snapped to 1/16 or less, return 1/48 snap color return(8); }
/// <summary> /// Initialize Object when created/recycled within its object pool. /// </summary> /// <param name="info"></param> /// <param name="manager"></param> public void InitializeObject(HitObjectManagerKeys manager, HitObjectInfo info) { var playfield = (GameplayPlayfieldKeys)Ruleset.Playfield; HitPosition = info.IsLongNote ? playfield.HoldHitPositionY[info.Lane - 1] : playfield.HitPositionY[info.Lane - 1]; Info = info; // Update Hit Object State HitObjectSprite.Image = GetHitObjectTexture(info.Lane, manager.Ruleset.Mode); HitObjectSprite.Visible = true; HitObjectSprite.Tint = Color.White; InitialTrackPosition = manager.GetPositionFromTime(Info.StartTime); CurrentlyBeingHeld = false; StopLongNoteAnimation(); // Update hit body's size to match image ratio HitObjectSprite.Size = new ScalableVector2(playfield.LaneSize, playfield.LaneSize * HitObjectSprite.Image.Height / HitObjectSprite.Image.Width); LongNoteBodyOffset = HitObjectSprite.Height / 2; // Update Hit Object State depending if its an LN or not if (!Info.IsLongNote) { LongNoteEndSprite.Visible = false; LongNoteBodySprite.Visible = false; InitialLongNoteTrackPosition = InitialTrackPosition; } else { LongNoteBodySprite.Tint = Color.White; LongNoteEndSprite.Tint = Color.White; LongNoteEndSprite.Visible = SkinManager.Skin.Keys[Ruleset.Mode].DrawLongNoteEnd; LongNoteBodySprite.Visible = true; InitialLongNoteTrackPosition = manager.GetPositionFromTime(Info.EndTime); UpdateLongNoteSize(InitialTrackPosition); InitialLongNoteSize = CurrentLongNoteSize; } // Update Positions UpdateSpritePositions(manager.CurrentTrackPosition); }
/// <summary> /// Returns color of note beatsnap /// </summary> /// <param name="info"></param> /// <param name="timingPoint"></param> /// <returns></returns> public static int GetBeatSnap(HitObjectInfo info, TimingPointInfo timingPoint) { // Add 2ms offset buffer space to offset and get beat length var pos = info.StartTime - timingPoint.StartTime + 0; var beatlength = 60000 / timingPoint.Bpm; // Calculate Note's snap index var index = Math.Round(48 * pos / beatlength, MidpointRounding.AwayFromZero); // Return Color of snap index for (var i = 0; i < 8; i++) { // ReSharper disable once CompareOfFloatsByEqualityOperator if (index % BeatSnaps[i] == 0) { return(i); } } // If it's not snapped to 1/16 or less, return 1/48 snap color return(8); }
/// <summary> /// Places a HitObject at a given lane. /// </summary> /// <param name="inputDevice"></param> /// <param name="lane"></param> /// <param name="time"></param> private void PlaceObject(CompositionInputDevice inputDevice, int lane, double time) { var am = ActionManager as EditorActionManagerKeys; if (HandlePendingLongNoteReleases(lane, time)) { return; } // Find an existing object in the current lane at the same time, so we can determine if // the object should be placed or deleted accordingly. HitObjectInfo existingObject = null; DrawableEditorHitObject hoveredObject = null; switch (inputDevice) { case CompositionInputDevice.Keyboard: existingObject = WorkingMap.HitObjects.Find(x => x.StartTime == (int)time && x.Lane == lane); break; case CompositionInputDevice.Mouse: hoveredObject = ScrollContainer.GetHoveredHitObject(); if (hoveredObject != null) { existingObject = hoveredObject.Info; } break; default: throw new ArgumentOutOfRangeException(nameof(inputDevice), inputDevice, null); } // There's no object currently at this position, so add it. if (existingObject == null) { switch (CompositionTool.Value) { case EditorCompositionTool.Note: am?.PlaceHitObject(lane, time); break; case EditorCompositionTool.LongNote: am?.PlaceLongNote(lane, time); // Makes sure the long note is marked as pending, so any future objects placed in this lane // will be awarded to this LN's end. var workingObject = WorkingMap.HitObjects.Find(x => x.StartTime == (int)time && x.Lane == lane); PendingLongNoteReleases[lane - 1] = workingObject; // Make the long note appear as inactive/dead. Gives a visual effect to the user that // they need to do something with the note. if (!(ScrollContainer.HitObjects.Find(x => x.Info == workingObject) is DrawableEditorHitObjectLong drawable)) { return; } drawable.AppearAsInactive(); if (!GaveLongNoteGuide) { NotificationManager.Show(NotificationLevel.Info, "Scroll through the timeline and place the end of the long note."); GaveLongNoteGuide = true; } break; default: NotificationManager.Show(NotificationLevel.Error, "This tool isn't implemented yet. Choose another!"); break; } } // An object exists, so delete it. else { switch (inputDevice) { case CompositionInputDevice.Keyboard: am?.DeleteHitObject(existingObject); break; case CompositionInputDevice.Mouse: // hoveredObject?.AppearAsSelected(); break; default: throw new ArgumentOutOfRangeException(nameof(inputDevice), inputDevice, null); } } }
public AutoModIssueExcessiveBreakTime(HitObjectInfo hitObject) : base(AutoModIssueLevel.Ranking) { HitObject = hitObject; Text = $"The map contains 30 seconds or more of break time at: {HitObject.StartTime} ms."; }
/// <summary> /// Replaces regular notes with long notes and vice versa. /// /// HitObjects and TimingPoints MUST be sorted by StartTime prior to calling this method, /// see <see cref="Sort()"/>. /// </summary> public void ApplyInverse() { // Minimal LN and gap lengths in milliseconds. // // Ideally this should be computed in a smart way using the judgements so that it is always possible to get // perfects, but making map mods depend on the judgements (affected by strict/chill/accuracy adjustments) is // a really bad idea. I'm setting these to values that will probably work fine for the majority of the // cases. const int MINIMAL_LN_LENGTH = 36; const int MINIMAL_GAP_LENGTH = 36; var newHitObjects = new List <HitObjectInfo>(); // An array indicating whether the currently processed HitObject is the first in its lane. var firstInLane = new bool[GetKeyCount()]; for (var i = 0; i < firstInLane.Length; i++) { firstInLane[i] = true; } for (var i = 0; i < HitObjects.Count; i++) { var currentObject = HitObjects[i]; // Find the next and second next hit object in the lane. HitObjectInfo nextObjectInLane = null, secondNextObjectInLane = null; for (var j = i + 1; j < HitObjects.Count; j++) { if (HitObjects[j].Lane == currentObject.Lane) { if (nextObjectInLane == null) { nextObjectInLane = HitObjects[j]; } else { secondNextObjectInLane = HitObjects[j]; break; } } } var isFirstInLane = firstInLane[currentObject.Lane - 1]; firstInLane[currentObject.Lane - 1] = false; // If this is the only object in its lane, keep it as is. if (nextObjectInLane == null && isFirstInLane) { newHitObjects.Add(currentObject); continue; } // Figure out the time gap between the end of the LN which we'll create and the next object. int?timeGap = null; if (nextObjectInLane != null) { var timingPoint = GetTimingPointAt(nextObjectInLane.StartTime); float bpm; // If the timing point starts at the next object, we want to use the previous timing point's BPM. // For example, consider a fast section of the map transitioning into a very low BPM ending starting // with the next hit object. Since the LN release and the gap are still in the fast section, they // should use the fast section's BPM. if ((int)Math.Round(timingPoint.StartTime) == nextObjectInLane.StartTime) { var prevTimingPointIndex = TimingPoints.FindLastIndex(x => x.StartTime < timingPoint.StartTime); // No timing points before the object? Just use the first timing point then, it has the correct // BPM. if (prevTimingPointIndex == -1) { prevTimingPointIndex = 0; } bpm = TimingPoints[prevTimingPointIndex].Bpm; } else { bpm = timingPoint.Bpm; } // The time gap is quarter of the milliseconds per beat. timeGap = (int?)Math.Max(Math.Round(15000 / bpm), MINIMAL_GAP_LENGTH); } // Summary of the changes: // Regular 1 -> Regular 2 => LN until 2 - time gap // Regular 1 -> LN 2 => LN until 2 // LN 1 -> Regular 2 => LN from 1 end until 2 - time gap // LN 1 -> LN 2 => LN from 1 end until 2 // // Exceptions: // - last LNs are kept (treated as regular 2) // - last regular objects are removed and treated as LN 2 if (currentObject.IsLongNote) { // LNs before regular objects are changed so they start where they ended and end a time gap before // the object. // LNs before LNs do the same but without a time gap. if (nextObjectInLane == null) { // If this is the last object in its lane, though, then it's probably a better idea // to leave it be. For example, finishing long LNs in charts. } else { currentObject.StartTime = currentObject.EndTime; // (this part can mess up the ordering) currentObject.EndTime = nextObjectInLane.StartTime - timeGap.Value; // Clear the keysounds as we're moving the start, so they won't make sense. currentObject.KeySounds = new List <KeySoundInfo>(); // If the next object is not an LN and it's the last object in the lane, or if it's an LN and // not the last object in the lane, create a regular object at the next object's start position. if ((secondNextObjectInLane == null) != nextObjectInLane.IsLongNote) { currentObject.EndTime = nextObjectInLane.StartTime; } // Filter out really short LNs or even negative length resulting from jacks or weird BPM values. if (currentObject.EndTime - currentObject.StartTime < MINIMAL_LN_LENGTH) { // These get skipped entirely. // // Actually, there can be a degenerate pattern of multiple LNs with really short gaps // in between them (less than MINIMAL_LN_LENGTH), which this logic will convert // into nothing. That should be pretty rare though. continue; } } } else { // Regular objects are replaced with LNs starting from their start and ending quarter of a beat // before the next object's start. if (nextObjectInLane == null) { // If this is the last object in lane, though, then it's not included, and instead the previous // LN spans up to this object's StartTime. continue; } currentObject.EndTime = nextObjectInLane.StartTime - timeGap.Value; // If the next object is not an LN and it's the last object in the lane, or if it's an LN and // not the last object in the lane, this LN should span until its start. if ((secondNextObjectInLane == null) == (nextObjectInLane.EndTime == 0)) { currentObject.EndTime = nextObjectInLane.StartTime; } // Filter out really short LNs or even negative length resulting from jacks or weird BPM values. if (currentObject.EndTime - currentObject.StartTime < MINIMAL_LN_LENGTH) { // These get converted back into regular objects. currentObject.EndTime = 0; } } newHitObjects.Add(currentObject); } // LN conversion can mess up the ordering, so sort it again. See the (this part can mess up the ordering) // comment above. HitObjects = newHitObjects.OrderBy(x => x.StartTime).ToList(); }
/// <summary> /// </summary> /// <param name="container"></param> /// <param name="hitObject"></param> public EditorActionDeleteHitObjectKeys(EditorScrollContainerKeys container, HitObjectInfo hitObject) { Container = container; HitObject = hitObject; }
public AutoModIssueShortLongNote(HitObjectInfo hitObject) : base(AutoModIssueLevel.Ranking) { HitObject = hitObject; Text = $"The long note in column {HitObject.Lane} at {HitObject.StartTime} is less than {AutoMod.ShortLongNoteThreshold} ms."; }