Files
OpenRA/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverShroudSelectorLogic.cs
2024-11-03 16:52:47 +02:00

248 lines
7.6 KiB
C#

#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Lint;
using OpenRA.Network;
using OpenRA.Primitives;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
[ChromeLogicArgsHotkeys("CombinedViewKey", "WorldViewKey")]
public class ObserverShroudSelectorLogic : ChromeLogic
{
[FluentReference]
const string CameraOptionAllPlayers = "options-shroud-selector.all-players";
[FluentReference]
const string CameraOptionDisableShroud = "options-shroud-selector.disable-shroud";
[FluentReference]
const string CameraOptionOther = "options-shroud-selector.other";
[FluentReference]
const string Players = "label-players";
[FluentReference("team")]
const string TeamNumber = "label-team-name";
[FluentReference]
const string NoTeam = "label-no-team";
readonly CameraOption combined, disableShroud;
readonly IGrouping<int, CameraOption>[] teams;
readonly bool limitViews;
readonly HotkeyReference combinedViewKey = new();
readonly HotkeyReference worldViewKey = new();
readonly World world;
CameraOption selected;
readonly LabelWidget shroudLabel;
sealed class CameraOption
{
public readonly Player Player;
public readonly string Label;
public readonly Color Color;
public readonly string Faction;
public readonly Func<bool> IsSelected;
public readonly Action OnClick;
public CameraOption(ObserverShroudSelectorLogic logic, Player p)
{
Player = p;
Label = p.ResolvedPlayerName;
Color = p.Color;
Faction = p.Faction.InternalName;
IsSelected = () => p.World.RenderPlayer == p;
OnClick = () =>
{
p.World.RenderPlayer = p;
logic.selected = this;
p.World.Selection.Clear();
WidgetUtils.BindPlayerNameAndStatus(logic.shroudLabel, p);
};
}
public CameraOption(ObserverShroudSelectorLogic logic, World w, string label, Player p)
{
Player = p;
Label = label;
Color = Color.White;
Faction = null;
IsSelected = () => w.RenderPlayer == p;
OnClick = () => { w.RenderPlayer = p; logic.selected = this; };
}
}
[ObjectCreator.UseCtor]
public ObserverShroudSelectorLogic(Widget widget, ModData modData, World world, WorldRenderer worldRenderer, Dictionary<string, MiniYaml> logicArgs)
{
this.world = world;
if (logicArgs.TryGetValue("CombinedViewKey", out var yaml))
combinedViewKey = modData.Hotkeys[yaml.Value];
if (logicArgs.TryGetValue("WorldViewKey", out yaml))
worldViewKey = modData.Hotkeys[yaml.Value];
limitViews = world.Map.Visibility.HasFlag(MapVisibility.MissionSelector);
var groups = new Dictionary<string, IEnumerable<CameraOption>>();
combined = new CameraOption(this, world, FluentProvider.GetMessage(CameraOptionAllPlayers), world.Players.First(p => p.InternalName == "Everyone"));
disableShroud = new CameraOption(this, world, FluentProvider.GetMessage(CameraOptionDisableShroud), null);
if (!limitViews)
groups.Add(FluentProvider.GetMessage(CameraOptionOther), new List<CameraOption>() { combined, disableShroud });
teams = world.Players.Where(p => !p.NonCombatant && p.Playable)
.Select(p => new CameraOption(this, p))
.GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team)
.OrderBy(g => g.Key)
.ToArray();
var noTeams = teams.Length == 1;
var totalPlayers = 0;
foreach (var t in teams)
{
totalPlayers += t.Count();
var label = noTeams ? FluentProvider.GetMessage(Players) : t.Key > 0
? FluentProvider.GetMessage(TeamNumber, "team", t.Key)
: FluentProvider.GetMessage(NoTeam);
groups.Add(label, t);
}
var shroudSelectorDisabled = limitViews && totalPlayers < 2;
var shroudSelector = widget.Get<DropDownButtonWidget>("SHROUD_SELECTOR");
shroudSelector.IsDisabled = () => shroudSelectorDisabled;
shroudSelector.OnMouseDown = _ =>
{
ScrollItemWidget SetupItem(CameraOption option, ScrollItemWidget template)
{
var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick);
var showFlag = option.Faction != null;
var label = item.Get<LabelWidget>("LABEL");
label.IsVisible = () => showFlag;
label.GetColor = () => option.Color;
if (showFlag)
WidgetUtils.BindPlayerNameAndStatus(label, option.Player);
else
label.GetText = () => option.Label;
var flag = item.Get<ImageWidget>("FLAG");
flag.IsVisible = () => showFlag;
flag.GetImageCollection = () => "flags";
flag.GetImageName = () => option.Faction;
var labelAlt = item.Get<LabelWidget>("NOFLAG_LABEL");
labelAlt.IsVisible = () => !showFlag;
labelAlt.GetText = () => option.Label;
labelAlt.GetColor = () => option.Color;
return item;
}
shroudSelector.ShowDropDown("SPECTATOR_DROPDOWN_TEMPLATE", 400, groups, SetupItem);
};
shroudLabel = shroudSelector.Get<LabelWidget>("LABEL");
shroudLabel.IsVisible = () => selected.Faction != null;
shroudLabel.GetText = () => selected.Label;
shroudLabel.GetColor = () => selected.Color;
var shroudFlag = shroudSelector.Get<ImageWidget>("FLAG");
shroudFlag.IsVisible = () => selected.Faction != null;
shroudFlag.GetImageCollection = () => "flags";
shroudFlag.GetImageName = () => selected.Faction;
var shroudLabelAlt = shroudSelector.Get<LabelWidget>("NOFLAG_LABEL");
shroudLabelAlt.IsVisible = () => selected.Faction == null;
shroudLabelAlt.GetText = () => selected.Label;
shroudLabelAlt.GetColor = () => selected.Color;
var keyhandler = shroudSelector.Get<LogicKeyListenerWidget>("SHROUD_KEYHANDLER");
keyhandler.AddHandler(HandleKeyPress);
selected = limitViews ? groups.First().Value.First() : world.WorldActor.Owner.Shroud.ExploreMapEnabled ? combined : disableShroud;
selected.OnClick();
// Enable zooming out to fractional zoom levels
worldRenderer.Viewport.UnlockMinimumZoom(0.5f);
}
public bool HandleKeyPress(KeyInput e)
{
if (e.Event == KeyInputEvent.Down && !e.IsRepeat)
{
if (combinedViewKey.IsActivatedBy(e) && !limitViews)
{
selected = combined;
selected.OnClick();
return true;
}
if (worldViewKey.IsActivatedBy(e) && !limitViews)
{
selected = disableShroud;
selected.OnClick();
return true;
}
if (e.Key >= Keycode.NUMBER_0 && e.Key <= Keycode.NUMBER_9)
{
var key = (int)e.Key - (int)Keycode.NUMBER_0;
var team = teams.SingleOrDefault(t => t.Key == key)?.ToList();
if (team == null || team.Count == 0)
return false;
if (e.Modifiers == Modifiers.Shift)
team.Reverse();
selected = team.SkipWhile(t => t.Player != selected.Player).Skip(1).FirstOrDefault() ?? team.FirstOrDefault();
selected.OnClick();
return true;
}
}
return false;
}
public override void Tick()
{
// Fix the selector if something else has changed the render player
if (selected != null && world.RenderPlayer != selected.Player)
{
if (combined.Player == world.RenderPlayer)
combined.OnClick();
else if (disableShroud.Player == world.RenderPlayer)
disableShroud.OnClick();
else
foreach (var group in teams)
foreach (var option in group)
if (option.Player == world.RenderPlayer)
option.OnClick();
}
}
}
}