Files
Ez2Lazer/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs

321 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.LAsEZMania;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Skinning;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.LAsEzExtensions;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.UI
{
public partial class DrawableManiaRuleset : DrawableScrollingRuleset<ManiaHitObject>, IPlayfieldDimensionProvider
{
/// <summary>
/// The minimum time range. This occurs at a <see cref="ManiaRulesetSetting.ScrollSpeed"/> of 40.
/// </summary>
public const double MIN_TIME_RANGE = 290;
/// <summary>
/// The maximum time range. This occurs with a <see cref="ManiaRulesetSetting.ScrollSpeed"/> of 1.
/// </summary>
public const double MAX_TIME_RANGE = 11485;
public new ManiaPlayfield Playfield => (ManiaPlayfield)base.Playfield;
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
public IEnumerable<BarLine> BarLines;
public override bool RequiresPortraitOrientation => Beatmap.Stages.Count == 1 && mobileLayout.Value == ManiaMobileLayout.Portrait;
protected override bool RelativeScaleBeatLengths => true;
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
private readonly BindableDouble configScrollSpeed = new BindableDouble();
private readonly Bindable<ManiaMobileLayout> mobileLayout = new Bindable<ManiaMobileLayout>();
private readonly Bindable<bool> touchOverlay = new Bindable<bool>();
//自定义判定系统
private readonly Bindable<EzManiaScrollingStyle> scrollingStyle = new Bindable<EzManiaScrollingStyle>();
private readonly BindableDouble configBaseMs = new BindableDouble();
private readonly BindableDouble configTimePerSpeed = new BindableDouble();
// private readonly ManiaHitModeConvertor hitModeConvertor;
// private readonly Bindable<MUGHitMode> hitMode = new Bindable<MUGHitMode>();
public double TargetTimeRange { get; protected set; }
private double currentTimeRange;
// Stores the current speed adjustment active in gameplay.
private readonly Track speedAdjustmentTrack = new TrackVirtual(0);
private ISkinSource currentSkin = null!;
[Resolved]
private GameHost gameHost { get; set; } = null!;
[Resolved]
private EzSkinSettingsManager ezSkinConfig { get; set; } = null!;
private Bindable<double> hitPositonBindable = new Bindable<double>();
private readonly Bindable<bool> globalHitPosition = new Bindable<bool>();
// 背景虚化管理器
private readonly ManiaBackgroundBlurManager blurManager = null!;
public void SetGameplayBackgroundSource(IGameplayBackgroundSource source)
{
// 可能在Load前或后调用blurManager在load()里创建因此先判断null
blurManager.SetSource(source);
}
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
: base(ruleset, beatmap, mods)
{
BarLines = new BarLineGenerator<BarLine>(Beatmap).BarLines;
TimeRange.MinValue = 1;
TimeRange.MaxValue = MAX_TIME_RANGE;
}
[BackgroundDependencyLoader]
private void load(ISkinSource source)
{
currentSkin = source;
currentSkin.SourceChanged += onSkinChange;
skinChanged();
foreach (var mod in Mods.OfType<IApplicableToTrack>())
mod.ApplyToTrack(speedAdjustmentTrack);
bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo);
foreach (var p in ControlPoints)
{
// Mania doesn't care about global velocity
p.Velocity = 1;
p.BaseBeatLength *= Beatmap.Difficulty.SliderMultiplier;
// For non-mania beatmap, speed changes should only happen through timing points
if (!isForCurrentRuleset)
p.EffectPoint = new EffectControlPoint();
}
BarLines.ForEach(Playfield.Add);
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
Config.BindWith(ManiaRulesetSetting.ScrollBaseSpeed, configBaseMs);
Config.BindWith(ManiaRulesetSetting.ScrollTimePerSpeed, configTimePerSpeed);
Config.BindWith(ManiaRulesetSetting.ScrollSpeed, configScrollSpeed);
configScrollSpeed.BindValueChanged(speed =>
{
if (!AllowScrollSpeedAdjustment)
return;
TargetTimeRange = ComputeScrollTime(speed.NewValue, configBaseMs.Value, configTimePerSpeed.Value);
});
TimeRange.Value = TargetTimeRange = currentTimeRange = ComputeScrollTime(configScrollSpeed.Value, configBaseMs.Value, configTimePerSpeed.Value);
Config.BindWith(ManiaRulesetSetting.MobileLayout, mobileLayout);
mobileLayout.BindValueChanged(_ => updateMobileLayout(), true);
Config.BindWith(ManiaRulesetSetting.TouchOverlay, touchOverlay);
touchOverlay.BindValueChanged(_ => updateMobileLayout(), true);
hitPositonBindable = ezSkinConfig.GetBindable<double>(EzSkinSetting.HitPosition);
hitPositonBindable.BindValueChanged(_ => skinChanged(), true);
ezSkinConfig.BindWith(EzSkinSetting.GlobalHitPosition, globalHitPosition);
globalHitPosition.BindValueChanged(_ => skinChanged(), true);
}
protected override void LoadComplete()
{
base.LoadComplete();
// 启动独立的异步任务预加载EzPro皮肤中会用到的贴图
Schedule(() =>
{
_ = Task.Run(async () =>
{
try
{
var factory = Dependencies.Get<EzLocalTextureFactory>();
if (factory != null)
{
await factory.PreloadGameTextures().ConfigureAwait(false);
}
}
catch (Exception ex)
{
Logger.Log($"[DrawableManiaRuleset] Preload textures failed: {ex.Message}",
LoggingTarget.Runtime, LogLevel.Error);
}
});
});
}
private ManiaTouchInputArea? touchInputArea;
private void updateMobileLayout()
{
if (touchOverlay.Value)
KeyBindingInputManager.Add(touchInputArea = new ManiaTouchInputArea(this));
else
{
if (touchInputArea != null)
KeyBindingInputManager.Remove(touchInputArea, true);
touchInputArea = null;
}
}
protected override void AdjustScrollSpeed(int amount) => configScrollSpeed.Value += amount;
protected override void Update()
{
base.Update();
updateTimeRange();
}
private ScheduledDelegate? pendingSkinChange;
private float hitPosition;
private void onSkinChange()
{
// schedule required to avoid calls after disposed.
// note that this has the side-effect of components only performing a skin change when they are alive.
pendingSkinChange?.Cancel();
pendingSkinChange = Scheduler.Add(skinChanged);
}
private void skinChanged()
{
if (globalHitPosition.Value)
hitPosition = (float)hitPositonBindable.Value;
else
{
hitPosition = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
?? (float)hitPositonBindable.Value;
}
pendingSkinChange = null;
}
private void updateTimeRange()
{
const float length_to_default_hit_position = 768 - LegacyManiaSkinConfiguration.DEFAULT_HIT_POSITION;
float lengthToHitPosition = 768 - hitPosition;
// This scaling factor preserves the scroll speed as the scroll length varies from changes to the hit position.
float scale = 1.0f;
switch (scrollingStyle.Value)
{
case EzManiaScrollingStyle.ScrollSpeedStyle:
case EzManiaScrollingStyle.ScrollTimeStyle:
// Preserve the scroll speed as the scroll length varies from changes to the hit position.
scale = lengthToHitPosition / length_to_default_hit_position;
break;
case EzManiaScrollingStyle.ScrollTimeStyleFixed:
// Ensure the travel time from the top of the screen to the hit position remains constant.
scale = lengthToHitPosition / 768;
break;
}
// we're intentionally using the game host's update clock here to decouple the time range tween from the gameplay clock (which can be arbitrarily paused, or even rewinding)
currentTimeRange = Interpolation.DampContinuously(currentTimeRange, TargetTimeRange, 50, gameHost.UpdateThread.Clock.ElapsedFrameTime);
TimeRange.Value = currentTimeRange * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value * scale;
}
/// <summary>
/// Computes a scroll time (in milliseconds) from a scroll speed in the range of 1-40.
/// </summary>
/// <param name="scrollSpeed">The scroll speed.</param>
/// <param name="baseSpeed"></param>
/// <param name="timePerSpeed"></param>
/// <returns>The scroll time.</returns>
public static double ComputeScrollTime(double scrollSpeed, double baseSpeed, double timePerSpeed)
{
return baseSpeed - (scrollSpeed - 200) * timePerSpeed;
}
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer(this);
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
public override DrawableHitObject<ManiaHitObject>? CreateDrawableRepresentation(ManiaHitObject h) => null;
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
protected override ReplayRecorder CreateReplayRecorder(Score score) => new ManiaReplayRecorder(score);
protected override ResumeOverlay CreateResumeOverlay() => new DelayedResumeOverlay();
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (currentSkin.IsNotNull())
currentSkin.SourceChanged -= onSkinChange;
}
public bool SupportsBackgroundBlurMasking => true;
public RectangleF? GetPlayfieldBounds()
{
try
{
return Playfield.ScreenSpaceDrawQuad.AABBFloat;
}
catch
{
return null;
}
}
}
}