Exemple #1
0
        public LanePlayfield(LanedHitLane type)
        {
            bool isAirLane = type == LanedHitLane.Air;

            lane = type;

            Name    = $"{(isAirLane ? "Air" : "Ground")} Playfield";
            Padding = new MarginPadding {
                Left = RushPlayfield.HIT_TARGET_OFFSET
            };
            Anchor           = Origin = isAirLane ? Anchor.TopCentre : Anchor.BottomCentre;
            RelativeSizeAxes = Axes.Both;
            Size             = new Vector2(1, 0);

            AddRangeInternal(new Drawable[]
            {
                new Container
                {
                    Name   = "Hit Target",
                    Anchor = Anchor.CentreLeft,
                    Origin = Anchor.Centre,
                    Size   = new Vector2(RushPlayfield.HIT_TARGET_SIZE),
                    Child  = new SkinnableDrawable(new RushSkinComponent(isAirLane ? RushSkinComponents.AirHitTarget : RushSkinComponents.GroundHitTarget), _ => new HitTarget
                    {
                        RelativeSizeAxes = Axes.Both,
                    }, confineMode: ConfineMode.ScaleToFit),
                },
                effectsContainer   = new Container(),
                judgementContainer = new JudgementContainer <DrawableJudgement>(),
                HitObjectContainer,
            });
        }
        protected override void OnApply()
        {
            base.OnApply();

            Lane               = HitObject.Lane;
            Anchor             = LaneAnchor;
            AccentColour.Value = LaneAccentColour;
        }
Exemple #3
0
        private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
        {
            DrawableRushHitObject rushJudgedObject = (DrawableRushHitObject)judgedObject;
            RushJudgementResult   rushResult       = (RushJudgementResult)result;

            PlayerSprite.HandleResult(rushJudgedObject, result);

            // Display hit explosions for objects that allow it.
            if (result.IsHit && rushJudgedObject.DisplayExplosion)
            {
                Drawable explosion = rushJudgedObject switch
                {
                    DrawableStarSheetHead head => sheetExplosionPool.Get(s => s.Apply(head)),
                    DrawableStarSheetTail tail => sheetExplosionPool.Get(s => s.Apply(tail)),
                    DrawableHeart heart => heartExplosionPool.Get(h => h.Apply(heart)),
                    _ => explosionPool.Get(h => h.Apply(rushJudgedObject)),
                };

                if (rushJudgedObject is IDrawableLanedHit laned)
                {
                    playfieldForLane(laned.Lane).AddExplosion(explosion);
                }
            }

            // Display health point difference if the judgement result implies it.
            var pointDifference = rushResult.Judgement.HealthPointIncreaseFor(rushResult);

            if (pointDifference != 0)
            {
                overPlayerEffectsContainer.Add(healthTextPool.Get(h => h.Apply(pointDifference)));
            }

            // Display judgement results in a drawable for objects that allow it.
            if (rushJudgedObject.DisplayResult)
            {
                DrawableRushJudgement judgementDrawable = judgementPool.Get(j => j.Apply(result, judgedObject));
                LanedHitLane          judgementLane     = LanedHitLane.Air;

                // TODO: showing judgements based on the judged object suggests that
                //       this may want to be inside the object class as well.
                switch (rushJudgedObject.HitObject)
                {
                case Sawblade sawblade:
                    judgementLane = sawblade.Lane.Opposite();
                    break;

                case LanedHit lanedHit:
                    judgementLane = lanedHit.Lane;
                    break;

                case MiniBoss _:
                    break;
                }

                playfieldForLane(judgementLane).AddJudgement(judgementDrawable);
            }
        }
Exemple #4
0
 public DrawableLanedObjectPool(LanedHitLane lane, int initialSize, int?maximumSize)
     : base(initialSize, maximumSize)
 {
     this.lane = lane;
 }
Exemple #5
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);
        }
Exemple #6
0
 private LanePlayfield playfieldForLane(LanedHitLane lane) => lane == LanedHitLane.Air ? airLane : groundLane;
Exemple #7
0
        protected override IEnumerable <RushHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
        {
            void updatePrevious(LanedHitLane?newLane)
            {
                previousLane           = newLane;
                previousSourceTime     = original.GetEndTime();
                previousSourcePosition = (original as IHasPosition)?.Position;
            }

            // if it's definitely a spinner, return a miniboss
            if (original is IHasDuration && !(original is IHasDistance))
            {
                yield return(createMiniBoss(original));

                updatePrevious(null);
                yield break;
            }

            // otherwise do some flag magic
            Random random = new Random((int)original.StartTime);

            HitObjectFlags flags = flagsForHitObject(original);

            // if no flags, completely skip this object
            if (flags == HitObjectFlags.None)
            {
                updatePrevious(previousLane);
                yield break;
            }

            LanedHitLane?lane           = null;
            var          kiaiMultiplier = original.Kiai ? kiai_multiplier : 1;

            // try to get a lane from the force flags
            if (flags.HasFlagFast(HitObjectFlags.ForceSameLane) || flags.HasFlagFast(HitObjectFlags.SuggestSameLane) && random.NextDouble() < suggest_probability)
            {
                lane = previousLane;
            }
            else if (flags.HasFlagFast(HitObjectFlags.ForceNotSameLane) || flags.HasFlagFast(HitObjectFlags.SuggestNotSameLane) && random.NextDouble() < suggest_probability)
            {
                lane = previousLane?.Opposite();
            }

            // get the lane from the object
            lane ??= laneForHitObject(original);

            // if we should end a sheet, try to
            if (currentStarSheets.Count > 0 && (flags.HasFlagFast(HitObjectFlags.ForceEndStarSheet) || flags.HasFlagFast(HitObjectFlags.SuggestEndStarSheet) && random.NextDouble() < starsheet_end_probability))
            {
                // TODO: for now we'll end both sheets where they are and ignore snapping logic
                currentStarSheets.Clear();
            }

            // if we should start a starsheet...
            if (flags.HasFlagFast(HitObjectFlags.ForceStartStarSheet) || flags.HasFlagFast(HitObjectFlags.SuggestStartStarSheet) && random.NextDouble() < starsheet_start_probability)
            {
                // TODO: for now, end all existing sheets
                currentStarSheets.Clear();

                // use the suggested lane or randomly select one
                LanedHitLane sheetLane = lane ?? (random.NextDouble() < 0.5 ? LanedHitLane.Ground : LanedHitLane.Air);

                // create a sheet
                StarSheet    sheet     = currentStarSheets[sheetLane] = createStarSheet(original, sheetLane, original.Samples);
                LanedHitLane otherLane = sheetLane.Opposite();

                // FIXME: surely this is bad, altering the hit object after it's been returned???
                if (sheet != null)
                {
                    yield return(sheet);
                }

                // for sliders with repeats, add extra objects to the lane without a sheet
                if (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 < min_repeat_time)
                    {
                        repeatDuration *= 2;
                        skip           *= 2;
                    }

                    var repeatCurrent = original.StartTime;
                    var index         = -1;

                    foreach (var nodeSample in hasRepeats.NodeSamples)
                    {
                        index++;

                        if (index % skip != 0)
                        {
                            continue;
                        }

                        yield return(createNormalHit(original, otherLane, nodeSample, repeatCurrent));

                        repeatCurrent += repeatDuration;
                    }
                }
                // otherwise we have a chance to make a dual sheet
                else if (random.NextDouble() < starsheet_dual_probability)
                {
                    currentStarSheets[otherLane] = createStarSheet(original, otherLane, null);
                    yield return(currentStarSheets[otherLane]);
                }

                updatePrevious(sheetLane);
                yield break;
            }
Exemple #8
0
 public static LanedHitLane Opposite(this LanedHitLane lane) => lane == LanedHitLane.Air ? LanedHitLane.Ground : LanedHitLane.Air;
Exemple #9
0
 private float judgementPositionForLane(LanedHitLane lane) => lane == LanedHitLane.Air ? -JUDGEMENT_OFFSET : judgementContainer.DrawHeight - JUDGEMENT_OFFSET;