mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 08:32:57 +08:00
ActionMapping doesn't support concurrent actions by default
But can when required. Also supports key combination bindings now.
This commit is contained in:
parent
720bd38d8e
commit
30bd1d70b5
@ -9,11 +9,11 @@ namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
public class CatchInputManager : ActionMappingInputManager<CatchAction>
|
||||
{
|
||||
public CatchInputManager(RulesetInfo ruleset) : base(ruleset)
|
||||
public CatchInputManager(RulesetInfo ruleset) : base(ruleset, allowConcurrentActions: true)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IDictionary<Key, CatchAction> CreateDefaultMappings() => new Dictionary<Key, CatchAction>
|
||||
protected override IDictionary<KeyCombination, CatchAction> CreateDefaultMappings() => new Dictionary<KeyCombination, CatchAction>
|
||||
{
|
||||
{ Key.Z, CatchAction.MoveLeft },
|
||||
{ Key.Left, CatchAction.MoveLeft },
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Input
|
||||
{
|
||||
@ -21,58 +21,92 @@ namespace osu.Game.Input
|
||||
|
||||
private readonly int? variant;
|
||||
|
||||
private readonly bool allowConcurrentActions;
|
||||
|
||||
private readonly List<Binding> mappings = new List<Binding>();
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance.
|
||||
/// </summary>
|
||||
/// <param name="ruleset">A reference to identify the current <see cref="Ruleset"/>. Used to lookup mappings. Null for global mappings.</param>
|
||||
/// <param name="variant">An optional variant for the specified <see cref="Ruleset"/>. Used when a ruleset has more than one possible keyboard layouts.</param>
|
||||
protected ActionMappingInputManager(RulesetInfo ruleset = null, int? variant = null)
|
||||
/// <param name="allowConcurrentActions">Allow concurrent actions to be actuated at once. Note that this disables chord bindings.</param>
|
||||
protected ActionMappingInputManager(RulesetInfo ruleset = null, int? variant = null, bool allowConcurrentActions = false)
|
||||
{
|
||||
this.ruleset = ruleset;
|
||||
this.variant = variant;
|
||||
|
||||
Mappings = CreateDefaultMappings();
|
||||
this.allowConcurrentActions = allowConcurrentActions;
|
||||
}
|
||||
|
||||
protected IDictionary<Key, T> Mappings { get; private set; }
|
||||
protected abstract IDictionary<KeyCombination, T> CreateDefaultMappings();
|
||||
|
||||
protected abstract IDictionary<Key, T> CreateDefaultMappings();
|
||||
private BindingStore store;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BindingStore bindings)
|
||||
{
|
||||
var rulesetId = ruleset?.ID;
|
||||
foreach (var b in bindings.Query<Binding>(b => b.RulesetID == rulesetId && b.Variant == variant))
|
||||
Mappings[b.Key] = (T)(object)b.Action;
|
||||
store = bindings;
|
||||
ReloadMappings();
|
||||
}
|
||||
|
||||
protected void ReloadMappings()
|
||||
{
|
||||
var rulesetId = ruleset?.ID;
|
||||
|
||||
mappings.Clear();
|
||||
|
||||
foreach (var kvp in CreateDefaultMappings())
|
||||
mappings.Add(new Binding(kvp.Key, kvp.Value));
|
||||
|
||||
if (store != null)
|
||||
{
|
||||
foreach (var b in store.Query<Binding>(b => b.RulesetID == rulesetId && b.Variant == variant))
|
||||
mappings.Add(b);
|
||||
}
|
||||
|
||||
if (allowConcurrentActions)
|
||||
{
|
||||
// ensure we have no overlapping bindings.
|
||||
foreach (var m in mappings)
|
||||
foreach (var colliding in mappings.Where(k => !k.Keys.Equals(m.Keys) && k.Keys.CheckValid(m.Keys.Keys)))
|
||||
throw new InvalidOperationException($"Multiple partially overlapping bindings are not supported ({m} and {colliding} are colliding)!");
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Binding> pressedBindings = new List<Binding>();
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
mapKey(state, args.Key);
|
||||
if (!args.Repeat && (allowConcurrentActions || pressedBindings.Count == 0))
|
||||
{
|
||||
Binding validBinding;
|
||||
|
||||
if ((validBinding = mappings.Except(pressedBindings).LastOrDefault(m => m.Keys.CheckValid(state.Keyboard.Keys))) != null)
|
||||
{
|
||||
// store both the pressed combination and the resulting action, just in case the assignments change while we are actuated.
|
||||
pressedBindings.Add(validBinding);
|
||||
state.Data = validBinding.GetAction<T>();
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
||||
{
|
||||
mapKey(state, args.Key);
|
||||
foreach (var binding in pressedBindings.ToList())
|
||||
{
|
||||
if (!binding.Keys.CheckValid(state.Keyboard.Keys))
|
||||
{
|
||||
// set data as KeyUp.
|
||||
state.Data = binding.GetAction<T>();
|
||||
|
||||
// and clear the no-longer-valid combination/action.
|
||||
pressedBindings.Remove(binding);
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnKeyUp(state, args);
|
||||
}
|
||||
|
||||
private void mapKey(InputState state, Key key)
|
||||
{
|
||||
T mappedData;
|
||||
if (Mappings.TryGetValue(key, out mappedData))
|
||||
state.Data = mappedData;
|
||||
}
|
||||
|
||||
private T parseStringRepresentation(string str)
|
||||
{
|
||||
T res;
|
||||
|
||||
if (Enum.TryParse(str, out res))
|
||||
return res;
|
||||
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets;
|
||||
using OpenTK.Input;
|
||||
using SQLite.Net.Attributes;
|
||||
using SQLiteNetExtensions.Attributes;
|
||||
|
||||
@ -16,8 +15,31 @@ namespace osu.Game.Input
|
||||
[Indexed]
|
||||
public int? Variant { get; set; }
|
||||
|
||||
public Key Key { get; set; }
|
||||
[Column("Keys")]
|
||||
public string KeysString
|
||||
{
|
||||
get { return Keys.ToString(); }
|
||||
set { Keys = value; }
|
||||
}
|
||||
|
||||
public int Action { get; set; }
|
||||
[Ignore]
|
||||
public KeyCombination Keys { get; private set; }
|
||||
|
||||
public int Action { get; private set; }
|
||||
|
||||
public Binding()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Binding(KeyCombination keys, object action)
|
||||
{
|
||||
Keys = keys;
|
||||
Action = (int)action;
|
||||
}
|
||||
|
||||
public virtual T GetAction<T>() => (T)(object)Action;
|
||||
|
||||
public override string ToString() => $"{KeysString}=>{Action}";
|
||||
}
|
||||
}
|
@ -15,6 +15,24 @@ namespace osu.Game.Input
|
||||
{
|
||||
}
|
||||
|
||||
protected override int StoreVersion => 2;
|
||||
|
||||
protected override void PerformMigration(int currentVersion, int targetVersion)
|
||||
{
|
||||
base.PerformMigration(currentVersion, targetVersion);
|
||||
|
||||
while (currentVersion++ < targetVersion)
|
||||
{
|
||||
switch (currentVersion)
|
||||
{
|
||||
case 1:
|
||||
// cannot migrate; breaking underlying changes.
|
||||
Reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Prepare(bool reset = false)
|
||||
{
|
||||
Connection.CreateTable<Binding>();
|
||||
|
@ -8,7 +8,7 @@ namespace osu.Game.Input
|
||||
{
|
||||
public class GlobalActionMappingInputManager : ActionMappingInputManager<OsuAction>
|
||||
{
|
||||
protected override IDictionary<Key, OsuAction> CreateDefaultMappings() => new Dictionary<Key, OsuAction>
|
||||
protected override IDictionary<KeyCombination, OsuAction> CreateDefaultMappings() => new Dictionary<KeyCombination, OsuAction>
|
||||
{
|
||||
{ Key.F8, OsuAction.ToggleChat },
|
||||
{ Key.F9, OsuAction.ToggleSocial },
|
||||
|
58
osu.Game/Input/KeyCombination.cs
Normal file
58
osu.Game/Input/KeyCombination.cs
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2007-2017 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 OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a combination of more than one <see cref="Key"/>s.
|
||||
/// </summary>
|
||||
public class KeyCombination : IEquatable<KeyCombination>
|
||||
{
|
||||
public readonly IEnumerable<Key> Keys;
|
||||
|
||||
public KeyCombination(params Key[] keys)
|
||||
{
|
||||
Keys = keys;
|
||||
}
|
||||
|
||||
public KeyCombination(IEnumerable<Key> keys)
|
||||
{
|
||||
Keys = keys;
|
||||
}
|
||||
|
||||
public KeyCombination(string stringRepresentation)
|
||||
{
|
||||
Keys = stringRepresentation.Split(',').Select(s => (Key)int.Parse(s));
|
||||
}
|
||||
|
||||
public bool CheckValid(IEnumerable<Key> keys) => !Keys.Except(keys).Any();
|
||||
|
||||
public bool Equals(KeyCombination other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Keys.SequenceEqual(other.Keys);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != GetType()) return false;
|
||||
return Equals((KeyCombination)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Keys != null ? Keys.Select(k => k.GetHashCode()).Aggregate((h1, h2) => h1 + h2) : 0;
|
||||
|
||||
public static implicit operator KeyCombination(Key singleKey) => new KeyCombination(singleKey);
|
||||
|
||||
public static implicit operator KeyCombination(string stringRepresentation) => new KeyCombination(stringRepresentation);
|
||||
|
||||
public override string ToString() => Keys.Select(k => ((int)k).ToString()).Aggregate((s1, s2) => $"{s1},{s2}");
|
||||
}
|
||||
}
|
@ -94,6 +94,7 @@
|
||||
<Compile Include="Graphics\UserInterface\OsuContextMenuItem.cs" />
|
||||
<Compile Include="Input\Binding.cs" />
|
||||
<Compile Include="Input\BindingStore.cs" />
|
||||
<Compile Include="Input\KeyCombination.cs" />
|
||||
<Compile Include="Input\OsuAction.cs" />
|
||||
<Compile Include="Input\GlobalActionMappingInputManager.cs" />
|
||||
<Compile Include="IO\FileStore.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user