1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-16 11:42:56 +08:00

Merge pull request #427 from smoogipooo/tournament_drawings

Tournament drawings
This commit is contained in:
Dean Herbert 2017-03-03 20:53:24 +09:00 committed by GitHub
commit 7c1599a0de
16 changed files with 1405 additions and 4 deletions

@ -1 +1 @@
Subproject commit 51f2b9b37f38cd349a3dd728a78f8fffcb3a54f5
Subproject commit 93eb5bf99bb642bf339d7dce09c2d946412dadd6

View File

@ -0,0 +1,94 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Platform;
using osu.Framework.Screens.Testing;
using osu.Game.Screens.Tournament;
using osu.Game.Screens.Tournament.Teams;
namespace osu.Desktop.VisualTests.Tests
{
class TestCaseDrawings : TestCase
{
public override string Name => @"Drawings";
public override string Description => "Tournament drawings";
[BackgroundDependencyLoader]
private void load(Storage storage)
{
}
public override void Reset()
{
base.Reset();
Add(new Drawings
{
TeamList = new TestTeamList(),
});
}
class TestTeamList : ITeamList
{
public IEnumerable<Team> Teams { get; } = new[]
{
new Team
{
FlagName = "GB",
FullName = "United Kingdom",
Acronym = "UK"
},
new Team
{
FlagName = "FR",
FullName = "France",
Acronym = "FRA"
},
new Team
{
FlagName = "CN",
FullName = "China",
Acronym = "CHN"
},
new Team
{
FlagName = "AU",
FullName = "Australia",
Acronym = "AUS"
},
new Team
{
FlagName = "JP",
FullName = "Japan",
Acronym = "JPN"
},
new Team
{
FlagName = "RO",
FullName = "Romania",
Acronym = "ROM"
},
new Team
{
FlagName = "IT",
FullName = "Italy",
Acronym = "PIZZA"
},
new Team
{
FlagName = "VE",
FullName = "Venezuela",
Acronym = "VNZ"
},
new Team
{
FlagName = "US",
FullName = "United States of America",
Acronym = "USA"
},
};
}
}
}

View File

@ -179,6 +179,7 @@
<Compile Include="Benchmark.cs" />
<Compile Include="Program.cs" />
<Compile Include="Tests\TestCaseChatDisplay.cs" />
<Compile Include="Tests\TestCaseDrawings.cs" />
<Compile Include="Tests\TestCaseGamefield.cs" />
<Compile Include="Tests\TestCaseMusicController.cs" />
<Compile Include="Tests\TestCaseNotificationManager.cs" />

View File

@ -11,6 +11,7 @@ using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
@ -211,7 +212,7 @@ namespace osu.Game.Overlays.Dialog
},
},
},
header = new SpriteText
header = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
@ -219,7 +220,7 @@ namespace osu.Game.Overlays.Dialog
TextSize = 25,
Shadow = true,
},
body = new SpriteText
body = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,

View File

@ -257,7 +257,8 @@ namespace osu.Game.Screens.Menu
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
if (args.Repeat || state.Keyboard.ControlPressed || state.Keyboard.ShiftPressed || state.Keyboard.AltPressed)
return false;
if (triggerKey == args.Key && triggerKey != Key.Unknown)
{

View File

@ -13,6 +13,9 @@ using osu.Game.Screens.Direct;
using osu.Game.Screens.Multiplayer;
using OpenTK;
using osu.Game.Screens.Select;
using osu.Game.Screens.Tournament;
using osu.Framework.Input;
using OpenTK.Input;
namespace osu.Game.Screens.Menu
{
@ -97,5 +100,16 @@ namespace osu.Game.Screens.Menu
Content.FadeOut(3000);
return base.OnExiting(next);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (!args.Repeat && state.Keyboard.ControlPressed && state.Keyboard.ShiftPressed && args.Key == Key.D)
{
Push(new Drawings());
return true;
}
return base.OnKeyDown(state, args);
}
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Platform;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace osu.Game.Screens.Tournament.Components
{
public class DrawingsConfigManager : ConfigManager<DrawingsConfig>
{
public override string Filename => @"drawings.ini";
protected override void InitialiseDefaults()
{
Set(DrawingsConfig.Groups, 8, 1, 8);
Set(DrawingsConfig.TeamsPerGroup, 8, 1, 8);
}
public DrawingsConfigManager(Storage storage)
: base(storage)
{
}
}
public enum DrawingsConfig
{
Groups,
TeamsPerGroup
}
}

View File

@ -0,0 +1,134 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.OpenGL;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace osu.Game.Screens.Tournament.Components
{
class VisualiserContainer : Container
{
/// <summary>
/// Number of lines in the visualiser.
/// </summary>
public int Lines
{
get { return allLines.Count; }
set
{
while (value > allLines.Count)
addLine();
while (value < allLines.Count)
removeLine();
}
}
private List<VisualiserLine> allLines = new List<VisualiserLine>();
private float offset;
private void addLine()
{
VisualiserLine newLine = new VisualiserLine()
{
RelativeSizeAxes = Axes.Both,
Offset = offset,
CycleTime = RNG.Next(10000, 12000),
};
allLines.Add(newLine);
Add(newLine);
offset += RNG.Next(100, 5000);
}
private void removeLine()
{
if (allLines.Count == 0)
return;
Remove(allLines.First());
allLines.Remove(allLines.First());
}
class VisualiserLine : Container
{
/// <summary>
/// Time offset.
/// </summary>
public float Offset;
public double CycleTime;
private float leftPos => -(float)((Time.Current + Offset) / CycleTime) + expiredCount;
private Texture texture;
private int expiredCount = 0;
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
texture = textures.Get("Drawings/visualiser-line");
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
while (Children.Count() < 3)
addLine();
float pos = leftPos;
foreach (var c in Children)
{
if (c.Position.X < -1)
{
c.ClearTransforms();
c.Expire();
expiredCount++;
}
else
c.MoveToX(pos, 100);
pos += 1;
}
}
private void addLine()
{
Add(new Sprite()
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativePositionAxes = Axes.Both,
RelativeSizeAxes = Axes.Both,
Texture = texture,
X = leftPos + 1
});
}
}
}
}

View File

@ -0,0 +1,336 @@
// 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.IO;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Tournament.Components;
using osu.Game.Screens.Tournament.Teams;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Tournament
{
public class Drawings : OsuScreen
{
private const string results_filename = "drawings_results.txt";
internal override bool ShowOverlays => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault();
private ScrollingTeamContainer teamsContainer;
private GroupContainer groupsContainer;
private OsuSpriteText fullTeamNameText;
private List<Team> allTeams = new List<Team>();
private DrawingsConfigManager drawingsConfig;
private Task writeOp;
private Storage storage;
public ITeamList TeamList;
[BackgroundDependencyLoader]
private void load(TextureStore textures, Storage storage)
{
this.storage = storage;
if (TeamList == null)
TeamList = new StorageBackedTeamList(storage);
if (!TeamList.Teams.Any())
{
Exit();
return;
}
drawingsConfig = new DrawingsConfigManager(storage);
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(77, 77, 77, 255)
},
new Sprite
{
FillMode = FillMode.Fill,
Texture = textures.Get(@"Backgrounds/Drawings/background.png")
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Right,
Children = new Drawable[]
{
// Main container
new Container
{
RelativeSizeAxes = Axes.Both,
Width = 0.85f,
Children = new Drawable[]
{
// Visualiser
new VisualiserContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Size = new Vector2(1, 10),
Colour = new Color4(255, 204, 34, 255),
Lines = 6
},
// Groups
groupsContainer = new GroupContainer(drawingsConfig.Get<int>(DrawingsConfig.Groups), drawingsConfig.Get<int>(DrawingsConfig.TeamsPerGroup))
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Padding = new MarginPadding
{
Top = 35f,
Bottom = 35f
}
},
// Scrolling teams
teamsContainer = new ScrollingTeamContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
},
// Scrolling team name
fullTeamNameText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, 45f),
Alpha = 0,
Font = "Exo2.0-Light",
TextSize = 42f
}
}
},
// Control panel container
new Container
{
RelativeSizeAxes = Axes.Both,
Width = 0.15f,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(54, 54, 54, 255)
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Control Panel",
TextSize = 22f,
Font = "Exo2.0-Boldd"
},
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.75f,
Position = new Vector2(0, 35f),
Direction = FillDirection.Down,
Spacing = new Vector2(0, 5f),
Children = new Drawable[]
{
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Begin random",
Action = teamsContainer.StartScrolling,
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Stop random",
Action = teamsContainer.StopScrolling,
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Reload",
Action = reloadTeams
}
}
},
new FillFlowContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.75f,
Position = new Vector2(0, -5f),
Direction = FillDirection.Down,
Spacing = new Vector2(0, 5f),
Children = new Drawable[]
{
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Reset",
Action = () => reset(false)
}
}
}
}
}
}
}
};
teamsContainer.OnSelected += onTeamSelected;
teamsContainer.OnScrollStarted += () => fullTeamNameText.FadeOut(200);
reset(true);
}
private void onTeamSelected(Team team)
{
groupsContainer.AddTeam(team);
fullTeamNameText.Text = team.FullName;
fullTeamNameText.FadeIn(200);
writeResults(groupsContainer.GetStringRepresentation());
}
private void writeResults(string text)
{
Action writeAction = () =>
{
try
{
// Write to drawings_results
using (Stream stream = storage.GetStream(results_filename, FileAccess.Write, FileMode.Create))
using (StreamWriter sw = new StreamWriter(stream))
{
sw.Write(text);
}
}
catch (Exception ex)
{
Logger.Error(ex, "Failed to write results.");
}
};
if (writeOp == null)
writeOp = Task.Run(writeAction);
else
writeOp = writeOp.ContinueWith(t => { writeAction(); });
}
private void reloadTeams()
{
teamsContainer.ClearTeams();
allTeams.Clear();
foreach (Team t in TeamList.Teams)
{
if (groupsContainer.ContainsTeam(t.FullName))
continue;
allTeams.Add(t);
teamsContainer.AddTeam(t);
}
}
private void reset(bool loadLastResults = false)
{
groupsContainer.ClearTeams();
reloadTeams();
if (loadLastResults)
{
try
{
// Read from drawings_results
using (Stream stream = storage.GetStream(results_filename, FileAccess.Read, FileMode.Open))
using (StreamReader sr = new StreamReader(stream))
{
string line;
while ((line = sr.ReadLine()?.Trim()) != null)
{
if (string.IsNullOrEmpty(line))
continue;
if (line.ToUpper().StartsWith("GROUP"))
continue;
Team teamToAdd = allTeams.FirstOrDefault(t => t.FullName == line);
if (teamToAdd == null)
continue;
groupsContainer.AddTeam(teamToAdd);
teamsContainer.RemoveTeam(teamToAdd);
}
}
}
catch (Exception ex)
{
Logger.Error(ex, "Failed to read last drawings results.");
}
}
else
{
writeResults(string.Empty);
}
}
}
}

View File

@ -0,0 +1,187 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Tournament.Teams;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Tournament
{
public class Group : Container
{
public readonly string GroupName;
public int TeamsCount { get; private set; }
private FlowContainer<GroupTeam> teams;
private List<GroupTeam> allTeams = new List<GroupTeam>();
public Group(string name)
{
GroupName = name;
Size = new Vector2(176, 128);
Masking = true;
CornerRadius = 4;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(54, 54, 54, 255)
},
// Group name
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, 7f),
Text = $"GROUP {name.ToUpper()}",
TextSize = 8f,
Font = @"Exo2.0-Bold",
Colour = new Color4(255, 204, 34, 255),
},
teams = new FillFlowContainer<GroupTeam>
{
RelativeSizeAxes = Axes.Both,
Spacing = new Vector2(6f, 22),
Margin = new MarginPadding
{
Top = 21f,
Bottom = 7f,
Left = 7f,
Right = 7f
}
}
};
}
public void AddTeam(Team team)
{
GroupTeam gt = new GroupTeam(team);
if (TeamsCount < 8)
{
teams.Add(gt);
allTeams.Add(gt);
TeamsCount++;
}
}
public bool ContainsTeam(string fullName)
{
return allTeams.Any(t => t.Team.FullName == fullName);
}
public bool RemoveTeam(Team team)
{
allTeams.RemoveAll(gt => gt.Team == team);
if (teams.RemoveAll(gt => gt.Team == team) > 0)
{
TeamsCount--;
return true;
}
return false;
}
public void ClearTeams()
{
allTeams.Clear();
teams.Clear();
TeamsCount = 0;
}
public string GetStringRepresentation()
{
StringBuilder sb = new StringBuilder();
foreach (GroupTeam gt in allTeams)
sb.AppendLine(gt.Team.FullName);
return sb.ToString();
}
class GroupTeam : Container
{
public Team Team;
private FillFlowContainer innerContainer;
private Sprite flagSprite;
public GroupTeam(Team team)
{
Team = team;
Width = 36;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
innerContainer = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Down,
Spacing = new Vector2(0, 5f),
Children = new Drawable[]
{
flagSprite = new Sprite
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
FillMode = FillMode.Fit
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = team.Acronym.ToUpper(),
TextSize = 10f,
Font = @"Exo2.0-Bold"
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
innerContainer.ScaleTo(1.5f);
innerContainer.ScaleTo(1f, 200);
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
flagSprite.Texture = textures.Get($@"Flags/{Team.FlagName}");
}
}
}
}

View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Screens.Tournament.Teams;
using OpenTK;
namespace osu.Game.Screens.Tournament
{
public class GroupContainer : Container
{
private List<Group> groups = new List<Group>();
private int maxTeams;
private int currentGroup;
public GroupContainer(int numGroups, int teamsPerGroup)
{
FlowContainer<Group> bottomGroups;
FlowContainer<Group> topGroups;
maxTeams = teamsPerGroup;
char nextGroupName = 'A';
Children = new[]
{
topGroups = new FillFlowContainer<Group>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(7f, 0)
},
bottomGroups = new FillFlowContainer<Group>
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(7f, 0)
}
};
for (int i = 0; i < numGroups; i++)
{
Group g = new Group(nextGroupName.ToString());
groups.Add(g);
nextGroupName++;
if (i < (int)Math.Ceiling(numGroups / 2f))
topGroups.Add(g);
else
bottomGroups.Add(g);
}
}
public void AddTeam(Team team)
{
if (groups[currentGroup].TeamsCount == maxTeams)
return;
groups[currentGroup].AddTeam(team);
currentGroup = (currentGroup + 1) % groups.Count;
}
public bool ContainsTeam(string fullName)
{
return groups.Any(g => g.ContainsTeam(fullName));
}
public void ClearTeams()
{
foreach (Group g in groups)
g.ClearTeams();
currentGroup = 0;
}
public string GetStringRepresentation()
{
StringBuilder sb = new StringBuilder();
foreach (Group g in groups)
{
if (g != groups.First())
sb.AppendLine();
sb.AppendLine($"Group {g.GroupName}");
sb.Append(g.GetStringRepresentation());
}
return sb.ToString();
}
}
}

View File

@ -0,0 +1,379 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Threading;
using osu.Game.Screens.Tournament.Teams;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Tournament
{
public class ScrollingTeamContainer : Container
{
public event Action OnScrollStarted;
public event Action<Team> OnSelected;
private readonly List<Team> availableTeams = new List<Team>();
private Container tracker;
private float speed;
private int expiredCount;
private float offset;
private float timeOffset;
private float leftPos => offset + timeOffset + expiredCount * ScrollingTeam.WIDTH;
private double lastTime;
private ScheduledDelegate delayedStateChangeDelegate;
public ScrollingTeamContainer()
{
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
tracker = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 10f,
Alpha = 0,
Children = new[]
{
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
Size = new Vector2(2, 55),
ColourInfo = ColourInfo.GradientVertical(Color4.Transparent, Color4.White)
},
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Size = new Vector2(2, 55),
ColourInfo = ColourInfo.GradientVertical(Color4.White, Color4.Transparent)
}
}
}
};
}
private ScrollState _scrollState;
private ScrollState scrollState
{
get { return _scrollState; }
set
{
if (_scrollState == value)
return;
_scrollState = value;
delayedStateChangeDelegate?.Cancel();
switch (value)
{
case ScrollState.Scrolling:
resetSelected();
OnScrollStarted?.Invoke();
speedTo(1000f, 200);
tracker.FadeOut(100);
break;
case ScrollState.Stopping:
speedTo(0f, 2000);
tracker.FadeIn(200);
delayedStateChangeDelegate = Delay(2300).Schedule(() => scrollState = ScrollState.Stopped);
break;
case ScrollState.Stopped:
// Find closest to center
Drawable closest = null;
foreach (var c in Children)
{
if (!(c is ScrollingTeam))
continue;
if (closest == null)
{
closest = c;
continue;
}
float offset = Math.Abs(c.Position.X + c.DrawWidth / 2f - DrawWidth / 2f);
float lastOffset = Math.Abs(closest.Position.X + closest.DrawWidth / 2f - DrawWidth / 2f);
if (offset < lastOffset)
closest = c;
}
offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f);
ScrollingTeam st = closest as ScrollingTeam;
availableTeams.RemoveAll(at => at == st.Team);
st.Selected = true;
OnSelected?.Invoke(st.Team);
delayedStateChangeDelegate = Delay(10000).Schedule(() => scrollState = ScrollState.Idle);
break;
case ScrollState.Idle:
resetSelected();
OnScrollStarted?.Invoke();
speedTo(40f, 200);
tracker.FadeOut(100);
break;
}
}
}
public void AddTeam(Team team)
{
if (availableTeams.Contains(team))
return;
availableTeams.Add(team);
RemoveAll(c => c is ScrollingTeam);
scrollState = ScrollState.Idle;
}
public void AddTeams(IEnumerable<Team> teams)
{
if (teams == null)
return;
foreach (Team t in teams)
AddTeam(t);
}
public void ClearTeams()
{
availableTeams.Clear();
RemoveAll(c => c is ScrollingTeam);
scrollState = ScrollState.Idle;
}
public void RemoveTeam(Team team)
{
availableTeams.Remove(team);
foreach (var c in Children)
{
ScrollingTeam st = c as ScrollingTeam;
if (st == null)
continue;
if (st.Team == team)
{
st.FadeOut(200);
st.Expire();
}
}
}
public void StartScrolling()
{
if (availableTeams.Count == 0)
return;
scrollState = ScrollState.Scrolling;
}
public void StopScrolling()
{
if (availableTeams.Count == 0)
return;
switch (scrollState)
{
case ScrollState.Stopped:
case ScrollState.Idle:
return;
}
scrollState = ScrollState.Stopping;
}
protected override void LoadComplete()
{
base.LoadComplete();
scrollState = ScrollState.Idle;
}
protected override void UpdateAfterChildren()
{
timeOffset -= (float)(Time.Current - lastTime) / 1000 * speed;
lastTime = Time.Current;
if (availableTeams.Count > 0)
{
// Fill more than required to account for transformation + scrolling speed
while (Children.Count(c => c is ScrollingTeam) < DrawWidth * 2 / ScrollingTeam.WIDTH)
addFlags();
}
float pos = leftPos;
foreach (var c in Children)
{
if (!(c is ScrollingTeam))
continue;
if (c.Position.X + c.DrawWidth < 0)
{
c.ClearTransforms();
c.Expire();
expiredCount++;
}
else
{
c.MoveToX(pos, 100);
c.FadeTo(1.0f - Math.Abs(pos - DrawWidth / 2f) / (DrawWidth / 2.5f), 100);
}
pos += ScrollingTeam.WIDTH;
}
}
private void addFlags()
{
for (int i = 0; i < availableTeams.Count; i++)
{
Add(new ScrollingTeam(availableTeams[i])
{
X = leftPos + DrawWidth
});
}
}
private void resetSelected()
{
foreach (var c in Children)
{
ScrollingTeam st = c as ScrollingTeam;
if (st == null)
continue;
if (st.Selected)
{
st.Selected = false;
RemoveTeam(st.Team);
}
}
}
private void speedTo(float value, double duration = 0, EasingTypes easing = EasingTypes.None)
{
DelayReset();
UpdateTransformsOfType(typeof(TransformScrollSpeed));
TransformFloatTo(speed, value, duration, easing, new TransformScrollSpeed());
}
enum ScrollState
{
None,
Idle,
Stopping,
Stopped,
Scrolling
}
public class TransformScrollSpeed : TransformFloat
{
public override void Apply(Drawable d)
{
base.Apply(d);
(d as ScrollingTeamContainer).speed = CurrentValue;
}
}
public class ScrollingTeam : Container
{
public const float WIDTH = 58;
public const float HEIGHT = 41;
public Team Team;
private Sprite flagSprite;
private Box outline;
private bool selected;
public bool Selected
{
get { return selected; }
set
{
selected = value;
if (selected)
outline.FadeIn(100);
else
outline.FadeOut(100);
}
}
public ScrollingTeam(Team team)
{
Team = team;
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
Size = new Vector2(WIDTH, HEIGHT);
Masking = true;
CornerRadius = 8f;
Alpha = 0;
Children = new Drawable[]
{
outline = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0
},
flagSprite = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(WIDTH, HEIGHT) - new Vector2(8)
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
flagSprite.Texture = textures.Get($@"Flags/{Team.FlagName}");
}
}
}
}

View File

@ -0,0 +1,12 @@
// 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.Collections.Generic;
namespace osu.Game.Screens.Tournament.Teams
{
public interface ITeamList
{
IEnumerable<Team> Teams { get; }
}
}

View File

@ -0,0 +1,73 @@
// 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.IO;
using osu.Framework.Logging;
using osu.Framework.Platform;
namespace osu.Game.Screens.Tournament.Teams
{
public class StorageBackedTeamList : ITeamList
{
private const string teams_filename = "drawings.txt";
private Storage storage;
public StorageBackedTeamList(Storage storage)
{
this.storage = storage;
}
public IEnumerable<Team> Teams
{
get
{
var teams = new List<Team>();
try
{
using (Stream stream = storage.GetStream(teams_filename, FileAccess.Read, FileMode.Open))
using (StreamReader sr = new StreamReader(stream))
{
while (sr.Peek() != -1)
{
string line = sr.ReadLine().Trim();
if (string.IsNullOrEmpty(line))
continue;
string[] split = line.Split(':');
if (split.Length < 2)
{
Logger.Log($"Invalid team definition: {line}. Expected \"flag_name : team_name : team_acronym\".");
continue;
}
string flagName = split[0].Trim();
string teamName = split[1].Trim();
string acronym = split.Length >= 3 ? split[2].Trim() : teamName;
acronym = acronym.Substring(0, Math.Min(3, acronym.Length));
teams.Add(new Team()
{
FlagName = flagName,
FullName = teamName,
Acronym = acronym
});
}
}
}
catch (Exception ex)
{
Logger.Error(ex, "Failed to read teams.");
}
return teams;
}
}
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Screens.Tournament.Teams
{
public class Team
{
/// <summary>
/// The name of this team.
/// </summary>
public string FullName;
/// <summary>
/// Short acronym which appears in the group boxes post-selection.
/// </summary>
public string Acronym;
/// <summary>
/// Two-letter flag acronym (ISO 3166 standard)
/// </summary>
public string FlagName;
}
}

View File

@ -211,6 +211,15 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Screens\Select\BeatmapInfoWedge.cs" />
<Compile Include="Screens\Select\WedgeBackground.cs" />
<Compile Include="Screens\Tournament\Components\DrawingsConfigManager.cs" />
<Compile Include="Screens\Tournament\Components\VisualiserContainer.cs" />
<Compile Include="Screens\Tournament\Drawings.cs" />
<Compile Include="Screens\Tournament\Group.cs" />
<Compile Include="Screens\Tournament\GroupContainer.cs" />
<Compile Include="Screens\Tournament\Teams\ITeamList.cs" />
<Compile Include="Screens\Tournament\ScrollingTeamContainer.cs" />
<Compile Include="Screens\Tournament\Teams\Team.cs" />
<Compile Include="Screens\Tournament\Teams\StorageBackedTeamList.cs" />
<Compile Include="Users\User.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeControl.cs" />
<Compile Include="Database\BeatmapDatabase.cs" />