diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index ab0afb08d7..c7ea29f8c0 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -74,42 +74,42 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private void initialiseHyperDash(List objects) { - // todo: add difficulty adjust. - double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2; + List objectWithDroplets = new List(); + foreach (var currentObject in objects) + { + if (currentObject is Fruit) + objectWithDroplets.Add(currentObject); + if (currentObject is JuiceStream) + foreach (var currentJuiceElement in currentObject.NestedHitObjects) + if (!(currentJuiceElement is TinyDroplet)) + objectWithDroplets.Add((CatchHitObject)currentJuiceElement); + } + + objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); + + double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2; int lastDirection = 0; double lastExcess = halfCatcherWidth; - int objCount = objects.Count; - - for (int i = 0; i < objCount - 1; i++) + for (int i = 0; i < objectWithDroplets.Count - 1; i++) { - CatchHitObject currentObject = objects[i]; - - // not needed? - // if (currentObject is TinyDroplet) continue; - - CatchHitObject nextObject = objects[i + 1]; - - // while (nextObject is TinyDroplet) - // { - // if (++i == objCount - 1) break; - // nextObject = objects[i + 1]; - // } + CatchHitObject currentObject = objectWithDroplets[i]; + CatchHitObject nextObject = objectWithDroplets[i + 1]; int thisDirection = nextObject.X > currentObject.X ? 1 : -1; - double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; + double timeToNext = nextObject.StartTime - currentObject.StartTime; double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); - - if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) + float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext); + if (distanceToHyper < 0) { currentObject.HyperDashTarget = nextObject; lastExcess = halfCatcherWidth; } else { - //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; - lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth); + currentObject.DistanceToHyperDash = distanceToHyper; + lastExcess = MathHelper.Clamp(distanceToHyper, 0, halfCatcherWidth); } lastDirection = thisDirection; diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 2346a55bbe..0c50dae9fb 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -27,7 +27,9 @@ namespace osu.Game.Rulesets.Catch.Objects public int ComboIndex { get; set; } /// - /// The distance for a fruit to to next hyper if it's not a hyper. + /// Difference between the distance to the next object + /// and the distance that would have triggered a hyper dash. + /// A value close to 0 indicates a difficult jump (for difficulty calculation). /// public float DistanceToHyperDash { get; set; } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9460512a8d..be56ccf8c1 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -107,6 +107,11 @@ namespace osu.Game.Rulesets.Catch.UI public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); + public static float GetCatcherSize(BeatmapDifficulty difficulty) + { + return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + } + public class Catcher : Container, IKeyBindingHandler { /// diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs new file mode 100644 index 0000000000..30837d7db9 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -0,0 +1,51 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + internal class OsuModTransform : Mod, IApplicableToDrawableHitObjects + { + public override string Name => "Transform"; + public override string ShortenedName => "TR"; + public override FontAwesome Icon => FontAwesome.fa_arrows; + public override ModType Type => ModType.Fun; + public override string Description => "Everything rotates. EVERYTHING."; + public override double ScoreMultiplier => 1; + private float theta; + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables) + { + var hitObject = (OsuHitObject) drawable.HitObject; + + float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2; + + Vector2 originalPosition = drawable.Position; + Vector2 appearOffset = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * appearDistance; + + //the - 1 and + 1 prevents the hit objects to appear in the wrong position. + double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1; + double moveDuration = hitObject.TimePreempt + 1; + + using (drawable.BeginAbsoluteSequence(appearTime, true)) + { + drawable + .MoveToOffset(appearOffset) + .MoveTo(originalPosition, moveDuration, Easing.InOutSine); + } + + theta += (float) hitObject.TimeFadeIn / 1000; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs new file mode 100644 index 0000000000..5b69247451 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + internal class OsuModWiggle : Mod, IApplicableToDrawableHitObjects + { + public override string Name => "Wiggle"; + public override string ShortenedName => "WG"; + public override FontAwesome Icon => FontAwesome.fa_certificate; + public override ModType Type => ModType.Fun; + public override string Description => "They just won't stay still..."; + public override double ScoreMultiplier => 1; + + private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles + private const int wiggle_strength = 10; // Higher = stronger wiggles + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables) + drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState; + } + + private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state) + { + var osuObject = (OsuHitObject)drawable.HitObject; + Vector2 origin = drawable.Position; + + Random objRand = new Random((int)osuObject.StartTime); + + // Wiggle all objects during TimePreempt + int amountWiggles = (int)osuObject.TimePreempt / wiggle_duration; + + void wiggle() + { + float nextAngle = (float)(objRand.NextDouble() * 2 * Math.PI); + float nextDist = (float)(objRand.NextDouble() * wiggle_strength); + drawable.MoveTo(new Vector2((float)(nextDist * Math.Cos(nextAngle) + origin.X), (float)(nextDist * Math.Sin(nextAngle) + origin.Y)), wiggle_duration); + } + + for (int i = 0; i < amountWiggles; i++) + using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true)) + wiggle(); + + // Keep wiggling sliders and spinners for their duration + if (!(osuObject is IHasEndTime endTime)) + return; + + amountWiggles = (int)(endTime.Duration / wiggle_duration); + + for (int i = 0; i < amountWiggles; i++) + using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true)) + wiggle(); + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index fa6e9a018a..6736d10dab 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -117,6 +117,11 @@ namespace osu.Game.Rulesets.Osu new OsuModRelax(), new OsuModAutopilot(), }; + case ModType.Fun: + return new Mod[] { + new OsuModTransform(), + new OsuModWiggle(), + }; default: return new Mod[] { }; } diff --git a/osu.Game/Online/Chat/InfoMessage.cs b/osu.Game/Online/Chat/InfoMessage.cs index 2ff901deb1..103a999591 100644 --- a/osu.Game/Online/Chat/InfoMessage.cs +++ b/osu.Game/Online/Chat/InfoMessage.cs @@ -15,11 +15,7 @@ namespace osu.Game.Online.Chat Timestamp = DateTimeOffset.Now; Content = message; - Sender = new User - { - Username = @"system", - Colour = @"0000ff", - }; + Sender = User.SYSTEM_USER; } } } diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index ea077ff645..c106446fe0 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -77,9 +77,11 @@ namespace osu.Game.Overlays public void ShowUser(User user, bool fetchOnline = true) { + if (user == User.SYSTEM_USER) return; + Show(); - if (user.Id == Header?.User.Id) + if (user.Id == Header?.User?.Id) return; userReq?.Cancel(); diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index f42df4023f..6c3d2bfa63 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -144,5 +144,14 @@ namespace osu.Game.Users public Badge[] Badges; public override string ToString() => Username; + + /// + /// A user instance for displaying locally created system messages. + /// + public static readonly User SYSTEM_USER = new User + { + Username = "system", + Id = 0 + }; } }