1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-15 16:22:55 +08:00

Merge branch 'master' into bdl-cache-changes

This commit is contained in:
Dean Herbert 2018-01-23 14:10:35 +09:00
commit b793c2ec93
34 changed files with 765 additions and 345 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -12,12 +12,12 @@
<description>click the circles. to the beat.</description>
<summary>click the circles.</summary>
<releaseNotes>testing</releaseNotes>
<copyright>Copyright ppy Pty Ltd 2007-2017</copyright>
<copyright>Copyright ppy Pty Ltd 2007-2018</copyright>
<language>en-AU</language>
</metadata>
<files>
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
<file src="*.dll" target="lib\net45\"/>
<file src="*.dll" target="lib\net45\"/>
<file src="*.config" target="lib\net45\"/>
<file src="x86\*.dll" target="lib\net45\x86\"/>
<file src="x64\*.dll" target="lib\net45\x64\"/>

View File

@ -23,6 +23,5 @@ namespace osu.Game.Rulesets.Catch
MoveRight,
[Description("Engage dash")]
Dash,
PositionUpdate
}
}

View File

@ -1,9 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
@ -23,15 +26,78 @@ namespace osu.Game.Rulesets.Catch.Replays
public override Replay Generate()
{
// todo: add support for HT DT
const double dash_speed = CatcherArea.Catcher.BASE_SPEED;
const double movement_speed = dash_speed / 2;
float lastPosition = 0.5f;
double lastTime = 0;
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
Replay.Frames.Add(new CatchReplayFrame(-100000, 0));
Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition));
void moveToNext(CatchHitObject h)
{
float positionChange = Math.Abs(lastPosition - h.X);
double timeAvailable = h.StartTime - lastTime;
//So we can either make it there without a dash or not.
double speedRequired = positionChange / timeAvailable;
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
// todo: get correct catcher size, based on difficulty CS.
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
{
//we are already in the correct range.
lastTime = h.StartTime;
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition));
return;
}
if (h is BananaShower.Banana)
{
// auto bananas unrealistically warp to catch 100% combo.
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
else if (h.HyperDash)
{
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, ReplayButtonState.Right1));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
else if (dashRequired)
{
//we do a movement in two parts - the dash part then the normal part...
double timeAtNormalSpeed = positionChange / movement_speed;
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
double timeAtDashSpeed = timeWeNeedToSave / 2;
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
//dash movement
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, ReplayButtonState.Left1));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
else
{
double timeBefore = positionChange / movement_speed;
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, ReplayButtonState.Right1));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
lastTime = h.StartTime;
lastPosition = h.X;
}
foreach (var obj in Beatmap.HitObjects)
{
switch (obj)
{
case Fruit _:
Replay.Frames.Add(new CatchReplayFrame(obj.StartTime, obj.X));
moveToNext(obj);
break;
}
@ -42,7 +108,7 @@ namespace osu.Game.Rulesets.Catch.Replays
case BananaShower.Banana _:
case TinyDroplet _:
case Droplet _:
Replay.Frames.Add(new CatchReplayFrame(nestedObj.StartTime, nestedObj.X));
moveToNext(nestedObj);
break;
}
}

View File

@ -14,15 +14,29 @@ namespace osu.Game.Rulesets.Catch.Replays
{
}
public override List<InputState> GetPendingStates() => new List<InputState>
public override List<InputState> GetPendingStates()
{
new CatchReplayState
if (!Position.HasValue) return new List<InputState>();
var action = new List<CatchAction>();
if (CurrentFrame.ButtonState == ReplayButtonState.Left1)
action.Add(CatchAction.Dash);
if (Position.Value.X > CurrentFrame.Position.X)
action.Add(CatchAction.MoveRight);
else if (Position.Value.X < CurrentFrame.Position.X)
action.Add(CatchAction.MoveLeft);
return new List<InputState>
{
PressedActions = new List<CatchAction> { CatchAction.PositionUpdate },
CatcherX = ((CatchReplayFrame)CurrentFrame).MouseX
},
new CatchReplayState { PressedActions = new List<CatchAction>() },
};
new CatchReplayState
{
PressedActions = action,
CatcherX = Position.Value.X
},
};
}
public class CatchReplayState : ReplayState<CatchAction>
{

View File

@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Catch.Replays
{
public override bool IsImportant => MouseX > 0;
public CatchReplayFrame(double time, float? x = null)
: base(time, x ?? -1, null, ReplayButtonState.None)
public CatchReplayFrame(double time, float? x = null, ReplayButtonState button = ReplayButtonState.None)
: base(time, x ?? -1, null, button)
{
}
}

View File

@ -21,7 +21,7 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatcherArea : Container, IKeyBindingHandler<CatchAction>
public class CatcherArea : Container
{
public const float CATCHER_SIZE = 172;
@ -84,16 +84,14 @@ namespace osu.Game.Rulesets.Catch.UI
}
}
public bool OnPressed(CatchAction action)
protected override void Update()
{
if (action != CatchAction.PositionUpdate) return false;
base.Update();
CatchFramedReplayInputHandler.CatchReplayState state = (CatchFramedReplayInputHandler.CatchReplayState)GetContainingInputManager().CurrentState;
var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState;
if (state.CatcherX.HasValue)
if (state?.CatcherX != null)
MovableCatcher.X = state.CatcherX.Value;
return true;
}
public bool OnReleased(CatchAction action) => false;

View File

@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// <summary>
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
/// </summary>
public readonly List<StageDefinition> Stages = new List<StageDefinition>();
public List<StageDefinition> Stages = new List<StageDefinition>();
/// <summary>
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
@ -24,10 +24,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// <summary>
/// Creates a new <see cref="ManiaBeatmap"/>.
/// </summary>
/// <param name="initialStage">The initial stage.</param>
public ManiaBeatmap(StageDefinition initialStage)
/// <param name="defaultStage">The initial stages.</param>
public ManiaBeatmap(StageDefinition defaultStage)
{
Stages.Add(initialStage);
Stages.Add(defaultStage);
}
}
}

View File

@ -17,8 +17,13 @@ namespace osu.Game.Rulesets.Mania
public enum ManiaAction
{
[Description("Special")]
Special,
[Description("Special 1")]
Special1 = 1,
[Description("Special 2")]
Special2,
// This offsets the start value of normal keys in-case we add more special keys
// above at a later time, without breaking replays/configs.
[Description("Key 1")]
Key1 = 10,
[Description("Key 2")]
@ -36,6 +41,24 @@ namespace osu.Game.Rulesets.Mania
[Description("Key 8")]
Key8,
[Description("Key 9")]
Key9
Key9,
[Description("Key 10")]
Key10,
[Description("Key 11")]
Key11,
[Description("Key 12")]
Key12,
[Description("Key 13")]
Key13,
[Description("Key 14")]
Key14,
[Description("Key 15")]
Key15,
[Description("Key 16")]
Key16,
[Description("Key 17")]
Key17,
[Description("Key 18")]
Key18,
}
}

View File

@ -1,12 +1,14 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
@ -86,7 +88,7 @@ namespace osu.Game.Rulesets.Mania
},
},
new ManiaModRandom(),
new ManiaModKeyCoop(),
new ManiaModDualStages(),
new MultiMod
{
Mods = new Mod[]
@ -117,42 +119,189 @@ namespace osu.Game.Rulesets.Mania
{
}
public override IEnumerable<int> AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
public override IEnumerable<int> AvailableVariants
{
get
{
for (int i = 1; i <= 9; i++)
yield return (int)PlayfieldType.Single + i;
for (int i = 2; i <= 18; i += 2)
yield return (int)PlayfieldType.Dual + i;
}
}
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
{
var leftKeys = new[]
switch (getPlayfieldType(variant))
{
InputKey.A,
InputKey.S,
InputKey.D,
InputKey.F
};
case PlayfieldType.Single:
return new VariantMappingGenerator
{
LeftKeys = new[]
{
InputKey.A,
InputKey.S,
InputKey.D,
InputKey.F
},
RightKeys = new[]
{
InputKey.J,
InputKey.K,
InputKey.L,
InputKey.Semicolon
},
SpecialKey = InputKey.Space,
SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1,
}.GenerateKeyBindingsFor(variant, out _);
case PlayfieldType.Dual:
int keys = getDualStageKeyCount(variant);
var rightKeys = new[]
{
InputKey.J,
InputKey.K,
InputKey.L,
InputKey.Semicolon
};
var stage1Bindings = new VariantMappingGenerator
{
LeftKeys = new[]
{
InputKey.Number1,
InputKey.Number2,
InputKey.Number3,
InputKey.Number4,
},
RightKeys = new[]
{
InputKey.Z,
InputKey.X,
InputKey.C,
InputKey.V
},
SpecialKey = InputKey.Tilde,
SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1
}.GenerateKeyBindingsFor(keys, out var nextNormal);
ManiaAction currentKey = ManiaAction.Key1;
var stage2Bindings = new VariantMappingGenerator
{
LeftKeys = new[]
{
InputKey.Number7,
InputKey.Number8,
InputKey.Number9,
InputKey.Number0
},
RightKeys = new[]
{
InputKey.O,
InputKey.P,
InputKey.BracketLeft,
InputKey.BracketRight
},
SpecialKey = InputKey.BackSlash,
SpecialAction = ManiaAction.Special2,
NormalActionStart = nextNormal
}.GenerateKeyBindingsFor(keys, out _);
var bindings = new List<KeyBinding>();
return stage1Bindings.Concat(stage2Bindings);
}
for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
for (int i = 0; i < variant / 2; i++)
bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
if (variant % 2 == 1)
bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
return bindings;
return new KeyBinding[0];
}
public override string GetVariantName(int variant) => $"{variant}K";
public override string GetVariantName(int variant)
{
switch (getPlayfieldType(variant))
{
default:
return $"{variant}K";
case PlayfieldType.Dual:
{
var keys = getDualStageKeyCount(variant);
return $"{keys}K + {keys}K";
}
}
}
/// <summary>
/// Finds the number of keys for each stage in a <see cref="PlayfieldType.Dual"/> variant.
/// </summary>
/// <param name="variant">The variant.</param>
private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2;
/// <summary>
/// Finds the <see cref="PlayfieldType"/> that corresponds to a variant value.
/// </summary>
/// <param name="variant">The variant value.</param>
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
private PlayfieldType getPlayfieldType(int variant)
{
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderByDescending(i => i).First(v => variant >= v);
}
private class VariantMappingGenerator
{
/// <summary>
/// All the <see cref="InputKey"/>s available to the left hand.
/// </summary>
public InputKey[] LeftKeys;
/// <summary>
/// All the <see cref="InputKey"/>s available to the right hand.
/// </summary>
public InputKey[] RightKeys;
/// <summary>
/// The <see cref="InputKey"/> for the special key.
/// </summary>
public InputKey SpecialKey;
/// <summary>
/// The <see cref="ManiaAction"/> at which the normal columns should begin.
/// </summary>
public ManiaAction NormalActionStart;
/// <summary>
/// The <see cref="ManiaAction"/> for the special column.
/// </summary>
public ManiaAction SpecialAction;
/// <summary>
/// Generates a list of <see cref="KeyBinding"/>s for a specific number of columns.
/// </summary>
/// <param name="columns">The number of columns that need to be bound.</param>
/// <param name="nextNormalAction">The next <see cref="ManiaAction"/> to use for normal columns.</param>
/// <returns>The keybindings.</returns>
public IEnumerable<KeyBinding> GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction)
{
ManiaAction currentNormalAction = NormalActionStart;
var bindings = new List<KeyBinding>();
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++));
for (int i = 0; i < columns / 2; i++)
bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
if (columns % 2 == 1)
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
nextNormalAction = currentNormalAction;
return bindings;
}
}
}
public enum PlayfieldType
{
/// <summary>
/// Columns are grouped into a single stage.
/// Number of columns in this stage lies at (item - Single).
/// </summary>
Single = 0,
/// <summary>
/// Columns are grouped into two stages.
/// Overall number of columns lies at (item - Dual), further computation is required for
/// number of columns in each individual stage.
/// </summary>
Dual = 1000,
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public interface IPlayfieldTypeMod : IApplicableMod
{
/// <summary>
/// The <see cref="PlayfieldType"/> which this <see cref="IPlayfieldTypeMod"/> requires.
/// </summary>
PlayfieldType PlayfieldType { get; }
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter<ManiaHitObject>, IApplicableToRulesetContainer<ManiaHitObject>
{
public override string Name => "Dual Stages";
public override string ShortenedName => "DS";
public override string Description => @"Double the stages, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => false;
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
{
var mbc = (ManiaBeatmapConverter)beatmapConverter;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (mbc.IsForCurrentRuleset)
return;
mbc.TargetColumns *= 2;
}
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
var mrc = (ManiaRulesetContainer)rulesetContainer;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (mrc.IsForCurrentRuleset)
return;
var newDefinitions = new List<StageDefinition>();
foreach (var existing in mrc.Beatmap.Stages)
{
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
}
mrc.Beatmap.Stages = newDefinitions;
}
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModKeyCoop : Mod
{
public override string Name => "KeyCoop";
public override string ShortenedName => "2P";
public override string Description => @"Double the key amount, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
}
}

View File

@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Tests
RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
new DrawableHoldNote(new HoldNote { Duration = 1 }, ManiaAction.Key1)
new DrawableHoldNote(new HoldNote { Duration = 1000 } , ManiaAction.Key1)
{
Y = 5000,
Height = 1000,

View File

@ -2,11 +2,13 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
@ -31,15 +33,43 @@ namespace osu.Game.Rulesets.Mania.Tests
{
var rng = new Random(1337);
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(4, SpecialColumnPosition.Right));
AddStep("5 columns", () => createPlayfield(5, SpecialColumnPosition.Normal));
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
AddStep("1 column", () => createPlayfield(1));
AddStep("4 columns", () => createPlayfield(4));
AddStep("5 columns", () => createPlayfield(5));
AddStep("8 columns", () => createPlayfield(8));
AddStep("4 + 4 columns", () =>
{
var stages = new List<StageDefinition>
{
new StageDefinition { Columns = 4 },
new StageDefinition { Columns = 4 },
};
createPlayfield(stages);
});
AddStep("2 + 4 + 2 columns", () =>
{
var stages = new List<StageDefinition>
{
new StageDefinition { Columns = 2 },
new StageDefinition { Columns = 4 },
new StageDefinition { Columns = 2 },
};
createPlayfield(stages);
});
AddStep("1 + 8 + 1 columns", () =>
{
var stages = new List<StageDefinition>
{
new StageDefinition { Columns = 1 },
new StageDefinition { Columns = 8 },
new StageDefinition { Columns = 1 },
};
createPlayfield(stages);
});
AddStep("Reversed", () => createPlayfield(4, true));
AddStep("Notes with input", () => createPlayfieldWithNotes());
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
@ -48,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Tests
AddStep("Hit explosion", () =>
{
var playfield = createPlayfield(4, SpecialColumnPosition.Normal);
var playfield = createPlayfield(4);
int col = rng.Next(0, 4);
@ -58,6 +88,7 @@ namespace osu.Game.Rulesets.Mania.Tests
};
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
playfield.Columns[col].OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
});
}
@ -67,19 +98,29 @@ namespace osu.Game.Rulesets.Mania.Tests
maniaRuleset = rulesets.GetRuleset(3);
}
private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
{
var stages = new List<StageDefinition>
{
new StageDefinition { Columns = cols },
};
return createPlayfield(stages, inverted);
}
private ManiaPlayfield createPlayfield(List<StageDefinition> stages, bool inverted = false)
{
Clear();
var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both };
var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both };
Add(inputManager);
ManiaPlayfield playfield;
inputManager.Add(playfield = new ManiaPlayfield(cols)
inputManager.Add(playfield = new ManiaPlayfield(stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = specialPos
});
playfield.Inverted.Value = inverted;
@ -97,7 +138,12 @@ namespace osu.Game.Rulesets.Mania.Tests
Add(inputManager);
ManiaPlayfield playfield;
inputManager.Add(playfield = new ManiaPlayfield(4)
var stages = new List<StageDefinition>
{
new StageDefinition { Columns = 4 },
};
inputManager.Add(playfield = new ManiaPlayfield(stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -47,6 +47,7 @@ namespace osu.Game.Rulesets.Mania.UI
public Column()
: base(ScrollingDirection.Up)
{
RelativeSizeAxes = Axes.Y;
Width = column_width;
InternalChildren = new Drawable[]
@ -61,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
Name = "Hit target + hit objects",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION },
Padding = new MarginPadding { Top = ManiaStage.HIT_TARGET_POSITION },
Children = new Drawable[]
{
new Container
@ -115,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
Name = "Key",
RelativeSizeAxes = Axes.X,
Height = ManiaPlayfield.HIT_TARGET_POSITION,
Height = ManiaStage.HIT_TARGET_POSITION,
Children = new Drawable[]
{
new Box
@ -205,12 +206,12 @@ namespace osu.Game.Rulesets.Mania.UI
{
hitObject.Depth = (float)hitObject.HitObject.StartTime;
hitObject.AccentColour = AccentColour;
hitObject.OnJudgement += onJudgement;
hitObject.OnJudgement += OnJudgement;
HitObjects.Add(hitObject);
}
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (!judgement.IsHit)
return;

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal class DrawableManiaJudgement : DrawableJudgement
{
public DrawableManiaJudgement(Judgement judgement)
: base(judgement)
: base(judgement)
{
JudgementText.TextSize = 25;
}

View File

@ -3,237 +3,84 @@
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Containers;
using System;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : ScrollingPlayfield
{
public const float HIT_TARGET_POSITION = 50;
private SpecialColumnPosition specialColumnPosition;
/// <summary>
/// The style to use for the special column.
/// </summary>
public SpecialColumnPosition SpecialColumnPosition
{
get { return specialColumnPosition; }
set
{
if (IsLoaded)
throw new InvalidOperationException($"Setting {nameof(SpecialColumnPosition)} after the playfield is loaded requires re-creating the playfield.");
specialColumnPosition = value;
}
}
/// <summary>
/// Whether this playfield should be inverted. This flips everything inside the playfield.
/// </summary>
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
private readonly FlowContainer<Column> columns;
public IEnumerable<Column> Columns => columns.Children;
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
private readonly List<ManiaStage> stages = new List<ManiaStage>();
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour;
private readonly Container<DrawableManiaJudgement> judgements;
private readonly int columnCount;
public ManiaPlayfield(int columnCount)
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
: base(ScrollingDirection.Up)
{
this.columnCount = columnCount;
if (stageDefinitions == null)
throw new ArgumentNullException(nameof(stageDefinitions));
if (columnCount <= 0)
throw new ArgumentException("Can't have zero or fewer columns.");
if (stageDefinitions.Count <= 0)
throw new ArgumentException("Can't have zero or fewer stages.");
Inverted.Value = true;
Container topLevelContainer;
InternalChildren = new Drawable[]
GridContainer playfieldGrid;
InternalChild = playfieldGrid = new GridContainer
{
new Container
{
Name = "Playfield elements",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
new Container
{
Name = "Columns mask",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Masking = true,
Children = new Drawable[]
{
new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
columns = new FillFlowContainer<Column>
{
Name = "Columns",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0)
},
}
},
new Container
{
Name = "Barlines mask",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Width = 1366, // Bar lines should only be masked on the vertical axis
BypassAutoSizeAxes = Axes.Both,
Masking = true,
Child = content = new Container
{
Name = "Bar lines",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
}
},
judgements = new Container<DrawableManiaJudgement>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Y = HIT_TARGET_POSITION + 150,
BypassAutoSizeAxes = Axes.Both
},
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
RelativeSizeAxes = Axes.Both,
Content = new[] { new Drawable[stageDefinitions.Count] }
};
var currentAction = ManiaAction.Key1;
for (int i = 0; i < columnCount; i++)
var normalColumnAction = ManiaAction.Key1;
var specialColumnAction = ManiaAction.Special1;
int firstColumnIndex = 0;
for (int i = 0; i < stageDefinitions.Count; i++)
{
var c = new Column();
c.VisibleTimeRange.BindTo(VisibleTimeRange);
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
newStage.Inverted.BindTo(Inverted);
c.IsSpecial = isSpecialColumn(i);
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
playfieldGrid.Content[0][i] = newStage;
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
stages.Add(newStage);
AddNested(newStage);
columns.Add(c);
AddNested(c);
firstColumnIndex += newStage.Columns.Count;
}
Inverted.ValueChanged += invertedChanged;
Inverted.TriggerChange();
}
private void invertedChanged(bool newValue)
public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h);
public void Add(BarLine barline) => stages.ForEach(s => s.Add(barline));
private ManiaStage getStageByColumn(int column)
{
Scale = new Vector2(1, newValue ? -1 : 1);
judgements.Scale = Scale;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
normalColumnColours = new List<Color4>
int sum = 0;
foreach (var stage in stages)
{
colours.RedDark,
colours.GreenDark
};
specialColumnColour = colours.BlueDark;
// Set the special column + colour + key
foreach (var column in Columns)
{
if (!column.IsSpecial)
continue;
column.AccentColour = specialColumnColour;
sum = sum + stage.Columns.Count;
if (sum > column)
return stage;
}
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
// We'll set the colours of the non-special columns in a separate loop, because the non-special
// column colours are mirrored across their centre and special styles mess with this
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
{
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
nonSpecialColumns[i].AccentColour = colour;
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
}
return null;
}
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
}
/// <summary>
/// Whether the column index is a special column for this playfield.
/// </summary>
/// <param name="column">The 0-based column index.</param>
/// <returns>Whether the column is a special column.</returns>
private bool isSpecialColumn(int column)
{
switch (SpecialColumnPosition)
{
default:
case SpecialColumnPosition.Normal:
return columnCount % 2 == 1 && column == columnCount / 2;
case SpecialColumnPosition.Left:
return column == 0;
case SpecialColumnPosition.Right:
return column == columnCount - 1;
}
}
public override void Add(DrawableHitObject h)
{
h.OnJudgement += OnJudgement;
Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h);
}
public void Add(DrawableBarLine barline) => HitObjects.Add(barline);
protected override void Update()
{
// Due to masking differences, it is not possible to get the width of the columns container automatically
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
content.Width = columns.Width;
getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);
}
}
}

View File

@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
@ -12,6 +11,7 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
@ -22,6 +22,7 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
namespace osu.Game.Rulesets.Mania.UI
{
@ -29,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
public IEnumerable<DrawableBarLine> BarLines;
public IEnumerable<BarLine> BarLines;
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.UI
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
var barLines = new List<DrawableBarLine>();
var barLines = new List<BarLine>();
for (int i = 0; i < timingPoints.Count; i++)
{
@ -50,12 +51,12 @@ namespace osu.Game.Rulesets.Mania.UI
int index = 0;
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
{
barLines.Add(new DrawableBarLine(new BarLine
barLines.Add(new BarLine
{
StartTime = t,
ControlPoint = point,
BeatIndex = index
}));
});
}
}
@ -68,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.UI
BarLines.ForEach(Playfield.Add);
}
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.TotalColumns)
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -76,7 +77,11 @@ namespace osu.Game.Rulesets.Mania.UI
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Beatmap.TotalColumns);
public override PassThroughInputManager CreateInputManager()
{
var variantType = Mods.OfType<IPlayfieldTypeMod>().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single;
return new ManiaInputManager(Ruleset.RulesetInfo, (int)variantType + Beatmap.TotalColumns);
}
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);

View File

@ -0,0 +1,229 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI
{
/// <summary>
/// A collection of <see cref="Column"/>s.
/// </summary>
internal class ManiaStage : ScrollingPlayfield
{
public const float HIT_TARGET_POSITION = 50;
/// <summary>
/// Whether this playfield should be inverted. This flips everything inside the playfield.
/// </summary>
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
public IReadOnlyList<Column> Columns => columnFlow.Children;
private readonly FillFlowContainer<Column> columnFlow;
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
public Container<DrawableManiaJudgement> Judgements => judgements;
private readonly Container<DrawableManiaJudgement> judgements;
private readonly Container topLevelContainer;
private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour;
private readonly int firstColumnIndex;
private readonly StageDefinition definition;
public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
: base(ScrollingDirection.Up)
{
this.firstColumnIndex = firstColumnIndex;
this.definition = definition;
Name = "Stage";
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Y;
AutoSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
new Container
{
Name = "Columns mask",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Masking = true,
Children = new Drawable[]
{
new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
columnFlow = new FillFlowContainer<Column>
{
Name = "Columns",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0)
},
}
},
new Container
{
Name = "Barlines mask",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Width = 1366, // Bar lines should only be masked on the vertical axis
BypassAutoSizeAxes = Axes.Both,
Masking = true,
Child = content = new Container
{
Name = "Bar lines",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
}
},
judgements = new Container<DrawableManiaJudgement>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Y = HIT_TARGET_POSITION + 150,
BypassAutoSizeAxes = Axes.Both
},
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
};
for (int i = 0; i < definition.Columns; i++)
{
var isSpecial = isSpecialColumn(i);
var column = new Column
{
IsSpecial = isSpecial,
Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++
};
AddColumn(column);
}
Inverted.ValueChanged += invertedChanged;
Inverted.TriggerChange();
}
private void invertedChanged(bool newValue)
{
Scale = new Vector2(1, newValue ? -1 : 1);
Judgements.Scale = Scale;
}
public void AddColumn(Column c)
{
c.VisibleTimeRange.BindTo(VisibleTimeRange);
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
columnFlow.Add(c);
AddNested(c);
}
/// <summary>
/// Whether the column index is a special column for this playfield.
/// </summary>
/// <param name="column">The 0-based column index.</param>
/// <returns>Whether the column is a special column.</returns>
private bool isSpecialColumn(int column) => definition.Columns % 2 == 1 && column == definition.Columns / 2;
public override void Add(DrawableHitObject h)
{
var maniaObject = (ManiaHitObject)h.HitObject;
int columnIndex = maniaObject.Column - firstColumnIndex;
Columns.ElementAt(columnIndex).Add(h);
h.OnJudgement += OnJudgement;
}
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
normalColumnColours = new List<Color4>
{
colours.RedDark,
colours.GreenDark
};
specialColumnColour = colours.BlueDark;
// Set the special column + colour + key
foreach (var column in Columns)
{
if (!column.IsSpecial)
continue;
column.AccentColour = specialColumnColour;
}
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
// We'll set the colours of the non-special columns in a separate loop, because the non-special
// column colours are mirrored across their centre and special styles mess with this
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
{
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
nonSpecialColumns[i].AccentColour = colour;
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
}
}
protected override void Update()
{
// Due to masking differences, it is not possible to get the width of the columns container automatically
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
content.Width = columnFlow.Width;
}
}
}

View File

@ -1,21 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Mania.UI
{
public enum SpecialColumnPosition
{
/// <summary>
/// The special column will lie in the center of the columns.
/// </summary>
Normal,
/// <summary>
/// The special column will lie to the left of the columns.
/// </summary>
Left,
/// <summary>
/// The special column will lie to the right of the columns.
/// </summary>
Right
}
}

View File

@ -64,6 +64,7 @@
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Mods\IPlayfieldTypeMod.cs" />
<Compile Include="Mods\ManiaKeyMod.cs" />
<Compile Include="Mods\ManiaModAutoplay.cs" />
<Compile Include="Mods\ManiaModDaycore.cs" />
@ -83,7 +84,7 @@
<Compile Include="Mods\ManiaModKey7.cs" />
<Compile Include="Mods\ManiaModKey8.cs" />
<Compile Include="Mods\ManiaModKey9.cs" />
<Compile Include="Mods\ManiaModKeyCoop.cs" />
<Compile Include="Mods\ManiaModDualStages.cs" />
<Compile Include="Mods\ManiaModNightcore.cs" />
<Compile Include="Mods\ManiaModPerfect.cs" />
<Compile Include="Mods\ManiaModRandom.cs" />
@ -115,12 +116,12 @@
<Compile Include="Tests\TestCasePerformancePoints.cs" />
<Compile Include="UI\Column.cs" />
<Compile Include="UI\DrawableManiaJudgement.cs" />
<Compile Include="UI\ManiaStage.cs" />
<Compile Include="UI\HitExplosion.cs" />
<Compile Include="UI\ManiaRulesetContainer.cs" />
<Compile Include="UI\ManiaPlayfield.cs" />
<Compile Include="ManiaRuleset.cs" />
<Compile Include="Mods\ManiaModNoFail.cs" />
<Compile Include="UI\SpecialColumnPosition.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />

View File

@ -60,7 +60,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Samples = s.Samples,
SampleControlPoint = s.SampleControlPoint,
TimePreempt = s.TimePreempt,
TimeFadein = s.TimeFadein
TimeFadein = s.TimeFadein,
HitWindow300 = s.HitWindow300,
HitWindow100 = s.HitWindow100,
HitWindow50 = s.HitWindow50
})
};

View File

@ -16,9 +16,9 @@ namespace osu.Game.Rulesets.Osu.Objects
public const double OBJECT_RADIUS = 64;
private const double hittable_range = 300;
private const double hit_window_50 = 150;
private const double hit_window_100 = 80;
private const double hit_window_300 = 30;
public double HitWindow50 = 150;
public double HitWindow100 = 80;
public double HitWindow300 = 30;
public float TimePreempt = 600;
public float TimeFadein = 400;
@ -50,13 +50,13 @@ namespace osu.Game.Rulesets.Osu.Objects
switch (result)
{
default:
return 300;
return hittable_range;
case HitResult.Meh:
return 150;
return HitWindow50;
case HitResult.Good:
return 80;
return HitWindow100;
case HitResult.Great:
return 30;
return HitWindow300;
}
}
@ -78,6 +78,10 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
HitWindow50 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 200, 150, 100);
HitWindow100 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 140, 100, 60);
HitWindow300 = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 80, 50, 20);
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
@ -15,6 +16,7 @@ using osu.Game.Screens.Edit.Screens.Compose.Timeline;
namespace osu.Game.Tests.Visual
{
[Ignore("CI regularly hangs on this TestCase...")]
public class TestCaseWaveform : OsuTestCase
{
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();

View File

@ -287,15 +287,16 @@ namespace osu.Game.Beatmaps
Import(archive);
downloadNotification.State = ProgressNotificationState.Completed;
currentDownloads.Remove(request);
}, TaskCreationOptions.LongRunning);
currentDownloads.Remove(request);
};
request.Failure += data =>
request.Failure += error =>
{
if (error is OperationCanceledException) return;
downloadNotification.State = ProgressNotificationState.Completed;
Logger.Error(data, "Failed to get beatmap download information");
Logger.Error(error, "Beatmap download failed!");
currentDownloads.Remove(request);
};

View File

@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface
public Color4 FillColour
{
set { fill.Colour = value; }
set { fill.FadeColour(value, 150, Easing.OutQuint); }
}
public Color4 BackgroundColour

View File

@ -23,7 +23,7 @@ namespace osu.Game.Input.Bindings
private KeyBindingStore store;
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings();
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
/// <summary>
/// Create a new instance.

View File

@ -16,7 +16,6 @@ using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Logging;
using osu.Game.Online.API.Requests;
using osu.Framework.Configuration;
using osu.Framework.Audio.Track;
@ -65,11 +64,14 @@ namespace osu.Game.Overlays.Direct
Colour = Color4.Black.Opacity(0.3f),
};
private OsuColour colours;
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{
this.beatmaps = beatmaps;
this.beatmapSetOverlay = beatmapSetOverlay;
this.colours = colours;
AddInternal(content = new Container
{
@ -106,6 +108,8 @@ namespace osu.Game.Overlays.Direct
beatmaps.BeatmapDownloadBegan += attachDownload;
}
public override bool DisposeOnDeathRemoval => true;
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
@ -180,7 +184,6 @@ namespace osu.Game.Overlays.Direct
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
Logger.Error(e, "Failed to get beatmap download information");
};
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
@ -188,7 +191,7 @@ namespace osu.Game.Overlays.Direct
request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FadeOut(500);
progressBar.FillColour = colours.Yellow;
};
}

View File

@ -145,6 +145,12 @@ namespace osu.Game.Overlays.Direct
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Playing.Value = false;
}
private TrackLoader trackLoader;
private AudioManager audio;

View File

@ -298,7 +298,7 @@ namespace osu.Game.Overlays
Task.Run(() =>
{
var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList();
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID)).Select(r => r.OnlineBeatmapSetID).ToList();
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID) && !s.DeletePending).Select(r => r.OnlineBeatmapSetID).ToList();
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
// may not need scheduling; loads async internally.

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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.Framework.Graphics.Containers;
@ -36,8 +35,7 @@ namespace osu.Game.Rulesets.UI
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width.</param>
protected Playfield(float? customWidth = null)
{
// Default height since we force relative size axes
Size = Vector2.One;
RelativeSizeAxes = Axes.Both;
AddInternal(ScaledContent = new ScaledContainer
{
@ -62,12 +60,6 @@ namespace osu.Game.Rulesets.UI
Add(HitObjects);
}
public override Axes RelativeSizeAxes
{
get { return Axes.Both; }
set { throw new InvalidOperationException($@"{nameof(Playfield)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); }
}
/// <summary>
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
/// </summary>

View File

@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.UI
/// <summary>
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
/// </summary>
protected readonly bool IsForCurrentRuleset;
public readonly bool IsForCurrentRuleset;
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);

View File

@ -12,12 +12,12 @@
<description>click the circles. to the beat.</description>
<summary>click the circles.</summary>
<releaseNotes>testing</releaseNotes>
<copyright>Copyright ppy Pty Ltd 2007-2017</copyright>
<copyright>Copyright ppy Pty Ltd 2007-2018</copyright>
<language>en-AU</language>
</metadata>
<files>
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
<file src="*.dll" target="lib\net45\"/>
<file src="*.dll" target="lib\net45\"/>
<file src="*.config" target="lib\net45\"/>
<file src="x86\*.dll" target="lib\net45\x86\"/>
<file src="x64\*.dll" target="lib\net45\x64\"/>