main cult
This commit is contained in:
@@ -2,7 +2,9 @@ using Content.Client.Administration.Managers;
|
||||
using Content.Client.Ghost;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Chat.Managers
|
||||
@@ -12,6 +14,9 @@ namespace Content.Client.Chat.Managers
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IClientAdminManager _adminMgr = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _systems = default!;
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
@@ -47,6 +52,12 @@ namespace Content.Client.Chat.Managers
|
||||
_consoleHost.ExecuteCommand($"me \"{CommandParsing.Escape(str)}\"");
|
||||
break;
|
||||
|
||||
case ChatSelectChannel.Cult:
|
||||
var localEnt = _player.LocalPlayer != null ? _player.LocalPlayer.ControlledEntity : null;
|
||||
if (_entities.TryGetComponent(localEnt, out CultistComponent? comp))
|
||||
_consoleHost.ExecuteCommand($"csay \"{CommandParsing.Escape(str)}\"");
|
||||
break;
|
||||
|
||||
case ChatSelectChannel.Dead:
|
||||
if (_systems.GetEntitySystemOrNull<GhostSystem>() is {IsGhost: true})
|
||||
goto case ChatSelectChannel.Local;
|
||||
|
||||
@@ -20,6 +20,8 @@ using Content.Shared.Input;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.White;
|
||||
using Content.Shared.White.Utils;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.Systems;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
@@ -50,6 +52,8 @@ public sealed class ChatUIController : UIController
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly CultistWordGeneratorManager _wordGenerator = default!;
|
||||
|
||||
|
||||
[UISystemDependency] private readonly ExamineSystem? _examine = default;
|
||||
[UISystemDependency] private readonly GhostSystem? _ghost = default;
|
||||
@@ -69,7 +73,8 @@ public sealed class ChatUIController : UIController
|
||||
{SharedChatSystem.EmotesAltPrefix, ChatSelectChannel.Emotes},
|
||||
{SharedChatSystem.AdminPrefix, ChatSelectChannel.Admin},
|
||||
{SharedChatSystem.RadioCommonPrefix, ChatSelectChannel.Radio},
|
||||
{SharedChatSystem.DeadPrefix, ChatSelectChannel.Dead}
|
||||
{SharedChatSystem.DeadPrefix, ChatSelectChannel.Dead},
|
||||
{SharedChatSystem.CultPrefix, ChatSelectChannel.Cult}, //WD EDIT
|
||||
};
|
||||
|
||||
public static readonly Dictionary<ChatSelectChannel, char> ChannelPrefixes = new()
|
||||
@@ -82,7 +87,9 @@ public sealed class ChatUIController : UIController
|
||||
{ChatSelectChannel.Emotes, SharedChatSystem.EmotesPrefix},
|
||||
{ChatSelectChannel.Admin, SharedChatSystem.AdminPrefix},
|
||||
{ChatSelectChannel.Radio, SharedChatSystem.RadioCommonPrefix},
|
||||
{ChatSelectChannel.Dead, SharedChatSystem.DeadPrefix}
|
||||
{ChatSelectChannel.Dead, SharedChatSystem.DeadPrefix},
|
||||
{ChatSelectChannel.Cult, SharedChatSystem.CultPrefix} // WD EDIT
|
||||
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -195,6 +202,9 @@ public sealed class ChatUIController : UIController
|
||||
_input.SetInputCommand(ContentKeyFunctions.FocusAdminChat,
|
||||
InputCmdHandler.FromDelegate(_ => FocusChannel(ChatSelectChannel.Admin)));
|
||||
|
||||
_input.SetInputCommand(ContentKeyFunctions.FocusCultChat,
|
||||
InputCmdHandler.FromDelegate(_ => FocusChannel(ChatSelectChannel.Cult)));
|
||||
|
||||
_input.SetInputCommand(ContentKeyFunctions.FocusRadio,
|
||||
InputCmdHandler.FromDelegate(_ => FocusChannel(ChatSelectChannel.Radio)));
|
||||
|
||||
@@ -210,11 +220,22 @@ public sealed class ChatUIController : UIController
|
||||
_input.SetInputCommand(ContentKeyFunctions.CycleChatChannelBackward,
|
||||
InputCmdHandler.FromDelegate(_ => CycleChatChannel(false)));
|
||||
|
||||
// WD EDIT
|
||||
SubscribeLocalEvent<EventCultistComponentState>(OnUpdateCultState);
|
||||
// WD EDIT END
|
||||
|
||||
var gameplayStateLoad = UIManager.GetUIController<GameplayStateLoadController>();
|
||||
gameplayStateLoad.OnScreenLoad += OnScreenLoad;
|
||||
gameplayStateLoad.OnScreenUnload += OnScreenUnload;
|
||||
}
|
||||
|
||||
// WD EDIT
|
||||
private void OnUpdateCultState(EventCultistComponentState ev)
|
||||
{
|
||||
UpdateChannelPermissions();
|
||||
}
|
||||
// WD EDIT END
|
||||
|
||||
public void OnScreenLoad()
|
||||
{
|
||||
SetMainChat(true);
|
||||
@@ -512,6 +533,15 @@ public sealed class ChatUIController : UIController
|
||||
CanSendChannels |= ChatSelectChannel.Admin;
|
||||
}
|
||||
|
||||
// WD EDIT
|
||||
var localEnt = _player.LocalPlayer != null ? _player.LocalPlayer.ControlledEntity : null;
|
||||
if (_entities.TryGetComponent(localEnt, out CultistComponent? comp))
|
||||
{
|
||||
FilterableChannels |= ChatChannel.Cult;
|
||||
CanSendChannels |= ChatSelectChannel.Cult;
|
||||
}
|
||||
// WD EDIT END
|
||||
|
||||
SelectableChannels = CanSendChannels;
|
||||
|
||||
// Necessary so that we always have a channel to fall back to.
|
||||
@@ -801,6 +831,13 @@ public sealed class ChatUIController : UIController
|
||||
AddSpeechBubble(msg, SpeechBubble.SpeechType.Whisper);
|
||||
break;
|
||||
|
||||
// WD EDIT
|
||||
case ChatChannel.Cult:
|
||||
msg.Message = _wordGenerator.GenerateText(msg.Message);
|
||||
AddSpeechBubble(msg, SpeechBubble.SpeechType.Whisper);
|
||||
break;
|
||||
// WD EDIT END
|
||||
|
||||
case ChatChannel.Dead:
|
||||
if (_ghost is not {IsGhost: true})
|
||||
break;
|
||||
|
||||
@@ -22,7 +22,8 @@ public sealed partial class ChannelFilterPopup : Popup
|
||||
ChatChannel.Admin,
|
||||
ChatChannel.AdminAlert,
|
||||
ChatChannel.AdminChat,
|
||||
ChatChannel.Server
|
||||
ChatChannel.Server,
|
||||
ChatChannel.Cult // WD EDIT
|
||||
};
|
||||
|
||||
private readonly Dictionary<ChatChannel, ChannelFilterCheckbox> _filterStates = new();
|
||||
|
||||
@@ -64,6 +64,7 @@ public sealed class ChannelSelectorButton : ChatPopupButton<ChannelSelectorPopup
|
||||
ChatSelectChannel.OOC => Color.LightSkyBlue,
|
||||
ChatSelectChannel.Dead => Color.MediumPurple,
|
||||
ChatSelectChannel.Admin => Color.HotPink,
|
||||
ChatSelectChannel.Cult => Color.DarkRed,
|
||||
_ => Color.DarkGray
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ public sealed class ChannelSelectorPopup : Popup
|
||||
ChatSelectChannel.LOOC,
|
||||
ChatSelectChannel.OOC,
|
||||
ChatSelectChannel.Dead,
|
||||
ChatSelectChannel.Admin
|
||||
ChatSelectChannel.Admin,
|
||||
ChatSelectChannel.Cult // WD EDIT
|
||||
// NOTE: Console is not in there and it can never be permanently selected.
|
||||
// You can, however, still submit commands as console by prefixing with /.
|
||||
};
|
||||
|
||||
86
Content.Client/_White/Cult/CultHudOverlay.cs
Normal file
86
Content.Client/_White/Cult/CultHudOverlay.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._White.Cult;
|
||||
|
||||
public sealed class CultHudOverlay : Overlay
|
||||
{
|
||||
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SharedTransformSystem _transformSystem;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
|
||||
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var handle = args.WorldHandle;
|
||||
var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero;
|
||||
var spriteQuery = _entityManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3.CreateRotation(-rotation);
|
||||
|
||||
foreach (var cultist in _entityManager.EntityQuery<CultistComponent>(true))
|
||||
{
|
||||
if (!xformQuery.TryGetComponent(cultist.Owner, out var xform) ||
|
||||
xform.MapID != args.MapId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var worldPosition = _transformSystem.GetWorldPosition(xform);
|
||||
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
|
||||
|
||||
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
|
||||
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
|
||||
|
||||
handle.SetTransform(matty);
|
||||
|
||||
var cultistIcon = new SpriteSpecifier.Rsi(new ResPath("/Textures/White/Cult/cult_hud.rsi"), "cult");
|
||||
var iconTexture = _entityManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(cultistIcon);
|
||||
|
||||
float yOffset;
|
||||
float xOffset;
|
||||
|
||||
if (spriteQuery.TryGetComponent(cultist.Owner, out var sprite))
|
||||
{
|
||||
yOffset = sprite.Bounds.Height - 10f; //sprite.Bounds.Height + 7f;
|
||||
xOffset = sprite.Bounds.Width - 40f; //sprite.Bounds.Width + 7f;
|
||||
}
|
||||
else
|
||||
{
|
||||
yOffset = 1f;
|
||||
xOffset = 1f;
|
||||
}
|
||||
|
||||
// Position above the entity (we've already applied the matrix transform to the entity itself)
|
||||
// Offset by the texture size for every do_after we have.
|
||||
var position = new Vector2(xOffset / EyeManager.PixelsPerMeter, yOffset / EyeManager.PixelsPerMeter);
|
||||
|
||||
// Draw the underlying bar texture
|
||||
if (sprite != null && !sprite.ContainerOccluded)
|
||||
{
|
||||
handle.DrawTexture(iconTexture, position);
|
||||
}
|
||||
}
|
||||
|
||||
handle.UseShader(null);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
public CultHudOverlay(IEntityManager entityManager)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_transformSystem = _entityManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
}
|
||||
}
|
||||
65
Content.Client/_White/Cult/CultPentagramSystem.cs
Normal file
65
Content.Client/_White/Cult/CultPentagramSystem.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._White.Cult;
|
||||
|
||||
public sealed class CultPentagramSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
private const string Rsi = "White/Cult/pentagram.rsi";
|
||||
private static readonly string[] States =
|
||||
{
|
||||
"halo1",
|
||||
"halo2",
|
||||
"halo3",
|
||||
"halo4",
|
||||
"halo5",
|
||||
"halo6"
|
||||
};
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PentagramComponent, ComponentStartup>(PentagramAdded);
|
||||
SubscribeLocalEvent<PentagramComponent, ComponentShutdown>(PentagramRemoved);
|
||||
}
|
||||
|
||||
private void PentagramAdded(EntityUid uid, PentagramComponent component, ComponentStartup args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (sprite.LayerMapTryGet(PentagramKey.Key, out var _))
|
||||
return;
|
||||
|
||||
var adj = sprite.Bounds.Height / 2 + ((1.0f/32) * 10.0f);
|
||||
|
||||
var randomIndex = _robustRandom.Next(0, States.Length);
|
||||
|
||||
var randomState = States[randomIndex];
|
||||
|
||||
var layer = sprite.AddLayer(new SpriteSpecifier.Rsi(new ResPath(Rsi), randomState));
|
||||
|
||||
sprite.LayerMapSet(PentagramKey.Key, layer);
|
||||
sprite.LayerSetOffset(layer, new Vector2(0.0f, adj));
|
||||
}
|
||||
|
||||
private void PentagramRemoved(EntityUid uid, PentagramComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (!sprite.LayerMapTryGet(PentagramKey.Key, out var layer))
|
||||
return;
|
||||
|
||||
sprite.RemoveLayer(layer);
|
||||
}
|
||||
|
||||
private enum PentagramKey
|
||||
{
|
||||
Key
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Content.Shared.White.Cult.Items;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client._White.Cult.Items.VeilShifter;
|
||||
|
||||
public sealed class VeilVisualizerSystem : VisualizerSystem<VeilVisualsComponent>
|
||||
{
|
||||
private const string StateOn = "icon-on";
|
||||
private const string StateOff = "icon";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<VoidTeleportComponent, ComponentInit>(OnInit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, VoidTeleportComponent component, ComponentInit args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, VeilVisuals.Activated, out var activated))
|
||||
return;
|
||||
|
||||
sprite.LayerSetState(VeilVisualsLayers.Activated, activated ? StateOn : StateOff);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, VeilVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, VeilVisuals.Activated, out var activated))
|
||||
return;
|
||||
|
||||
args.Sprite.LayerSetState(VeilVisualsLayers.Activated, activated ? component.StateOn : component.StateOff);
|
||||
}
|
||||
}
|
||||
|
||||
public enum VeilVisualsLayers : byte
|
||||
{
|
||||
Activated
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Content.Client._White.Cult.Items.VeilShifter;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class VeilVisualsComponent : Component
|
||||
{
|
||||
[DataField("stateOn")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StateOn = "icon-on";
|
||||
|
||||
[DataField("stateOff")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StateOff = "icon";
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.White.Cult.Items;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client._White.Cult.Items.VoidTorch;
|
||||
|
||||
public sealed class VoidTorchVisualizerSystem : VisualizerSystem<VoidTorchVisualsComponent>
|
||||
{
|
||||
protected override void OnAppearanceChange(EntityUid uid, VoidTorchVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
base.OnAppearanceChange(uid, component, ref args);
|
||||
|
||||
if (args.Sprite == null
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, VoidTorchVisuals.Activated, out var activated))
|
||||
return;
|
||||
|
||||
args.Sprite.LayerSetState(VoidTorchVisualsLayers.Activated, activated ? component.StateOn : component.StateOff);
|
||||
}
|
||||
}
|
||||
|
||||
public enum VoidTorchVisualsLayers : byte
|
||||
{
|
||||
Activated
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Content.Client._White.Cult.Items.VoidTorch;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class VoidTorchVisualsComponent : Component
|
||||
{
|
||||
[DataField("stateOn")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StateOn = "icon-on";
|
||||
|
||||
[DataField("stateOff")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StateOff = "icon";
|
||||
}
|
||||
6
Content.Client/_White/Cult/Narsie/NarsieLayer.cs
Normal file
6
Content.Client/_White/Cult/Narsie/NarsieLayer.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Content.Client._White.Cult;
|
||||
|
||||
public enum NarsieLayer
|
||||
{
|
||||
Default
|
||||
}
|
||||
69
Content.Client/_White/Cult/Narsie/NarsieVisualizer.cs
Normal file
69
Content.Client/_White/Cult/Narsie/NarsieVisualizer.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client._White.Cult;
|
||||
|
||||
public sealed class NarsieVisualizer : VisualizerSystem<NarsieComponent>
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<NarsieComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(EntityUid uid, NarsieComponent component, AnimationCompletedEvent args)
|
||||
{
|
||||
SetDefaultState(Comp<SpriteComponent>(uid));
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, NarsieComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
base.OnAppearanceChange(uid, component, ref args);
|
||||
|
||||
if(args.Sprite == null) return;
|
||||
|
||||
if (!args.AppearanceData.TryGetValue(NarsieVisualState.VisualState, out var narsieVisualsObject) || narsieVisualsObject is not NarsieVisuals narsieVisual)
|
||||
return;
|
||||
|
||||
switch (narsieVisual)
|
||||
{
|
||||
case NarsieVisuals.Spawning:
|
||||
PlaySpawnAnimation(uid);
|
||||
break;
|
||||
case NarsieVisuals.Spawned:
|
||||
if(_animationSystem.HasRunningAnimation(uid, "narsie_spawn")) break;
|
||||
SetDefaultState(args.Sprite);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void PlaySpawnAnimation(EntityUid uid)
|
||||
{
|
||||
_animationSystem.Play(uid, NarsieSpawnAnimation, "narsie_spawn");
|
||||
}
|
||||
|
||||
private void SetDefaultState(SpriteComponent component)
|
||||
{
|
||||
component.LayerSetVisible(NarsieLayer.Default, true);
|
||||
component.LayerSetState(NarsieLayer.Default, new RSI.StateId("narsie"));
|
||||
component.LayerSetAutoAnimated(NarsieLayer.Default, true);
|
||||
}
|
||||
|
||||
private static readonly Animation NarsieSpawnAnimation = new()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(3.5),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = NarsieLayer.Default,
|
||||
KeyFrames = {new AnimationTrackSpriteFlick.KeyFrame(new RSI.StateId("narsie_spawn_anim"), 0f)}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
9
Content.Client/_White/Cult/PentagramComponent.cs
Normal file
9
Content.Client/_White/Cult/PentagramComponent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Content.Shared.White.Cult.Pentagram;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client._White.Cult;
|
||||
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class PentagramComponent : SharedPentagramComponent
|
||||
{
|
||||
}
|
||||
40
Content.Client/_White/Cult/Pylon/PylonVisualizerSystem.cs
Normal file
40
Content.Client/_White/Cult/Pylon/PylonVisualizerSystem.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Content.Shared.White.Cult.Pylon;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client._White.Cult.Pylon;
|
||||
|
||||
public sealed class PylonVisualizerSystem : VisualizerSystem<PylonVisualsComponent>
|
||||
{
|
||||
private const string StateOn = "pylon";
|
||||
private const string StateOff = "pylon_off";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SharedPylonComponent, ComponentInit>(OnInit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, SharedPylonComponent component, ComponentInit args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, PylonVisualsLayers.Activated, out var activated))
|
||||
return;
|
||||
|
||||
sprite.LayerSetState(PylonVisualsLayers.Activated, activated ? StateOn : StateOff);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, PylonVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, PylonVisuals.Activated, out var activated))
|
||||
return;
|
||||
|
||||
args.Sprite.LayerSetState(PylonVisualsLayers.Activated, activated ? component.StateOn : component.StateOff);
|
||||
}
|
||||
}
|
||||
|
||||
public enum PylonVisualsLayers : byte
|
||||
{
|
||||
Activated
|
||||
}
|
||||
15
Content.Client/_White/Cult/Pylon/PylonVisualsComponent.cs
Normal file
15
Content.Client/_White/Cult/Pylon/PylonVisualsComponent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Content.Client.Storage.Visualizers;
|
||||
|
||||
namespace Content.Client._White.Cult.Pylon;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class PylonVisualsComponent : Component
|
||||
{
|
||||
[DataField("stateOn")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StateOn = "pylon";
|
||||
|
||||
[DataField("stateOff")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StateOff = "pylon_off";
|
||||
}
|
||||
48
Content.Client/_White/Cult/ShowCultHudSystem.cs
Normal file
48
Content.Client/_White/Cult/ShowCultHudSystem.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
|
||||
namespace Content.Client._White.Cult;
|
||||
|
||||
public sealed class ShowCultHudSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
|
||||
private Overlay _overlay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<CultistComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<CultistComponent, ComponentRemove>(OnComponentRemoved);
|
||||
SubscribeLocalEvent<CultistComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<CultistComponent, PlayerDetachedEvent>(OnPlayerDetached);
|
||||
|
||||
_overlay = new CultHudOverlay(EntityManager);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, CultistComponent component, ComponentInit args)
|
||||
{
|
||||
if (_player.LocalPlayer?.ControlledEntity != uid) return;
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
|
||||
}
|
||||
|
||||
private void OnComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args)
|
||||
{
|
||||
if (_player.LocalPlayer?.ControlledEntity != uid) return;
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(EntityUid uid, CultistComponent component, PlayerAttachedEvent args)
|
||||
{
|
||||
_overlayManager.AddOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(EntityUid uid, CultistComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
_overlayManager.RemoveOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client._White.Cult.Structures;
|
||||
|
||||
public sealed class CultCraftStructureVisualizerSystem : VisualizerSystem<CultCraftStructureVisualsComponent>
|
||||
{
|
||||
protected override void OnAppearanceChange(EntityUid uid, CultCraftStructureVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
base.OnAppearanceChange(uid, component, ref args);
|
||||
|
||||
if (args.Sprite == null
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, CultCraftStructureVisuals.Activated, out var activated))
|
||||
return;
|
||||
|
||||
args.Sprite.LayerSetState(CultCraftStructureVisualsLayers.Activated, activated ? component.StateOn : component.StateOff);
|
||||
}
|
||||
}
|
||||
|
||||
public enum CultCraftStructureVisualsLayers : byte
|
||||
{
|
||||
Activated
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Content.Client._White.Cult.Structures;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultCraftStructureVisualsComponent : Component
|
||||
{
|
||||
[DataField("stateOn")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StateOn = "icon";
|
||||
|
||||
[DataField("stateOff")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StateOff = "icon-off";
|
||||
}
|
||||
56
Content.Client/_White/Cult/UI/Altar/AltarBUI.cs
Normal file
56
Content.Client/_White/Cult/UI/Altar/AltarBUI.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.Altar;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class AltarBUI : BoundUserInterface
|
||||
{
|
||||
private AltarWindow? _window;
|
||||
|
||||
public AltarBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new AltarWindow();
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OnItemSelected += OnItemSelected;
|
||||
}
|
||||
|
||||
private void OnItemSelected(string item)
|
||||
{
|
||||
var evt = new AltarBuyRequest(item);
|
||||
SendMessage(evt);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing) return;
|
||||
_window?.Dispose();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is AltarListingBUIState listingState)
|
||||
{
|
||||
_window?.SetListing(listingState.Items);
|
||||
}
|
||||
else if(state is AltarTimerBUIState timerState)
|
||||
{
|
||||
_window?.SetTimer(timerState.NextTimeUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Control SetSize="50 50" MaxSize="50 50" xmlns="https://spacestation14.io"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#000000FF" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<TextureButton Name="BuyListingButton" Access="Public" HorizontalAlignment="Center" VerticalAlignment="Center" SetSize="48 48" MaxSize="48 48"/>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,21 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.Altar;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class AltarListingControl : Control
|
||||
{
|
||||
public AltarListingControl(EntityPrototype prototype, Texture icon, Action<string>? clickAction)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
ToolTip = $"{prototype.Name}\n{prototype.Description}";
|
||||
|
||||
BuyListingButton.TextureNormal = icon;
|
||||
BuyListingButton.OnButtonDown += _ => clickAction?.Invoke(prototype.ID);
|
||||
}
|
||||
}
|
||||
7
Content.Client/_White/Cult/UI/Altar/AltarWindow.xaml
Normal file
7
Content.Client/_White/Cult/UI/Altar/AltarWindow.xaml
Normal file
@@ -0,0 +1,7 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{Loc agent-id-menu-title}">
|
||||
<RichTextLabel Name="TimerLabel"></RichTextLabel>
|
||||
<BoxContainer Name="ListingContainer" Orientation="Horizontal" SeparationOverride="4" MinWidth="150">
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
92
Content.Client/_White/Cult/UI/Altar/AltarWindow.xaml.cs
Normal file
92
Content.Client/_White/Cult/UI/Altar/AltarWindow.xaml.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using Content.Client.GameTicking.Managers;
|
||||
using Content.Client.TextScreen;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.Altar;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class AltarWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly SpriteSystem _spriteSystem = default!;
|
||||
[Dependency] private readonly PrototypeManager _prototypeManager = default!;
|
||||
|
||||
|
||||
public event Action<string>? OnItemSelected;
|
||||
private TimeSpan? _nextTimeUse = null!;
|
||||
|
||||
private List<AltarListingControl> _listingControls = new();
|
||||
|
||||
public AltarWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_nextTimeUse == null) return;
|
||||
|
||||
var remainingTime = _nextTimeUse.Value - _gameTiming.CurTime;
|
||||
|
||||
if (remainingTime.TotalSeconds < 0)
|
||||
{
|
||||
remainingTime = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
var remainingTimeText = TextScreenSystem.TimeToString(remainingTime);
|
||||
|
||||
TimerLabel.SetMessage(remainingTimeText);
|
||||
}
|
||||
|
||||
public void SetListing(List<string> prototypes)
|
||||
{
|
||||
foreach (var prototypeId in prototypes)
|
||||
{
|
||||
var prototype = _prototypeManager.Index<EntityPrototype>(prototypeId);
|
||||
if(prototype == null) return;
|
||||
var prototypeIcon = _spriteSystem.GetPrototypeIcon(prototype).Default;
|
||||
AddListingControl(prototype);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddListingControl(EntityPrototype entityPrototype)
|
||||
{
|
||||
var icon = _spriteSystem.GetPrototypeIcon(entityPrototype).Default;
|
||||
var control = new AltarListingControl(entityPrototype, icon, OnItemSelected);
|
||||
|
||||
ListingContainer.AddChild(control);
|
||||
_listingControls.Add(control);
|
||||
}
|
||||
|
||||
public void SetTimer(TimeSpan? timer)
|
||||
{
|
||||
_nextTimeUse = timer;
|
||||
|
||||
if (timer == null)
|
||||
{
|
||||
TimerLabel.SetMessage("Алтарь готов к использованию");
|
||||
SetListingButtonsState(true);
|
||||
return;
|
||||
}
|
||||
|
||||
SetListingButtonsState(false);
|
||||
}
|
||||
|
||||
private void SetListingButtonsState(bool enabled)
|
||||
{
|
||||
foreach (var listingControl in _listingControls)
|
||||
{
|
||||
listingControl.BuyListingButton.Disabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Linq;
|
||||
using Content.Client._White.UserInterface.Radial;
|
||||
using Content.Shared.White.Cult.Runes.Components;
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.ConstructSelector;
|
||||
|
||||
public sealed class ConstructSelectorBui : BoundUserInterface
|
||||
{
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
private SpriteSystem _spriteSystem = default!;
|
||||
|
||||
private bool _selected;
|
||||
|
||||
public ConstructSelectorBui(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_spriteSystem = _entityManager.EntitySysManager.GetEntitySystem<SpriteSystem>();
|
||||
var shellComponent = _entityManager.GetComponent<ConstructShellComponent>(Owner);
|
||||
|
||||
var shellSelector = new RadialContainer();
|
||||
|
||||
shellSelector.Closed += () =>
|
||||
{
|
||||
if(_selected) return;
|
||||
|
||||
SendMessage(new ConstructFormSelectedEvent(shellComponent.ConstructForms.First()));
|
||||
};
|
||||
|
||||
foreach (var form in shellComponent.ConstructForms)
|
||||
{
|
||||
var formPrototype = _prototypeManager.Index<EntityPrototype>(form);
|
||||
var button = shellSelector.AddButton(formPrototype.Name, _spriteSystem.GetPrototypeIcon(formPrototype).Default);
|
||||
|
||||
button.Controller.OnPressed += _ =>
|
||||
{
|
||||
_selected = true;
|
||||
SendMessage(new ConstructFormSelectedEvent(form));
|
||||
shellSelector.Close();
|
||||
};
|
||||
}
|
||||
|
||||
shellSelector.OpenCentered();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using Content.Client._White.UserInterface.Radial;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.CultistFactory;
|
||||
|
||||
public sealed class CultistFactoryBUI : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
private RadialContainer? _radialContainer;
|
||||
|
||||
private bool _updated = false;
|
||||
|
||||
public CultistFactoryBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
private void ResetUI()
|
||||
{
|
||||
_radialContainer?.Close();
|
||||
_radialContainer = null;
|
||||
_updated = false;
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (_radialContainer != null)
|
||||
ResetUI();
|
||||
|
||||
_radialContainer = new RadialContainer();
|
||||
|
||||
if (State != null)
|
||||
UpdateState(State);
|
||||
}
|
||||
|
||||
private void PopulateRadial(IReadOnlyCollection<string> ids)
|
||||
{
|
||||
foreach (var id in ids)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<CultistFactoryProductionPrototype>(id, out var prototype))
|
||||
return;
|
||||
|
||||
if (_radialContainer == null)
|
||||
continue;
|
||||
|
||||
var button = _radialContainer.AddButton(prototype.Name, prototype.Icon);
|
||||
button.Controller.OnPressed += _ =>
|
||||
{
|
||||
Select(id);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void Select(string id)
|
||||
{
|
||||
SendMessage(new CultistFactoryItemSelectedMessage(id));
|
||||
ResetUI();
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
ResetUI();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (_updated)
|
||||
return;
|
||||
|
||||
if (state is CultistFactoryBUIState newState)
|
||||
{
|
||||
PopulateRadial(newState.Ids);
|
||||
}
|
||||
|
||||
if (_radialContainer == null)
|
||||
return;
|
||||
|
||||
_radialContainer?.OpenAttachedLocalPlayer();
|
||||
_updated = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.ListViewSelector;
|
||||
|
||||
|
||||
public sealed class ListViewSelectorBUI : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private ListViewSelectorWindow? _window;
|
||||
|
||||
public ListViewSelectorBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new ListViewSelectorWindow(_prototypeManager);
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.ItemSelected += (item, index) =>
|
||||
{
|
||||
var msg = new ListViewItemSelectedMessage(item, index);
|
||||
SendMessage(msg);
|
||||
};
|
||||
|
||||
if(State != null)
|
||||
UpdateState(State);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is ListViewBUIState newState)
|
||||
{
|
||||
_window?.PopulateList(newState.Items, newState.IsUsingPrototypes);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
_window?.Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{Loc runes-window-title}"
|
||||
MinWidth="350"
|
||||
MinHeight="400">
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Name="ItemsContainer" Orientation="Vertical"></BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,45 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.ListViewSelector;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class ListViewSelectorWindow : DefaultWindow
|
||||
{
|
||||
public Action<string, int>? ItemSelected;
|
||||
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
public ListViewSelectorWindow(IPrototypeManager prototypeManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_prototypeManager = prototypeManager;
|
||||
}
|
||||
|
||||
public void PopulateList(List<string> items, bool isPrototypes)
|
||||
{
|
||||
ItemsContainer.RemoveAllChildren();
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
var button = new Button();
|
||||
var itemName = Loc.GetString($"ent-{item}");
|
||||
|
||||
if (isPrototypes)
|
||||
{
|
||||
if(_prototypeManager.TryIndex<EntityPrototype>(item, out var itemPrototype))
|
||||
{
|
||||
itemName = itemPrototype.Name;
|
||||
}
|
||||
}
|
||||
|
||||
button.Text = itemName;
|
||||
|
||||
button.OnPressed += _ => ItemSelected?.Invoke(item, items.IndexOf(item));
|
||||
|
||||
ItemsContainer.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.NameSelector;
|
||||
|
||||
public sealed class NameSelectorBUI : BoundUserInterface
|
||||
{
|
||||
public NameSelectorBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private NameSelectorWindow? _window;
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new();
|
||||
_window.OpenCentered();
|
||||
_window.OnNameChange += OnNameSelected;
|
||||
_window.OnClose += Close;
|
||||
}
|
||||
|
||||
private void OnNameSelected(string name)
|
||||
{
|
||||
SendMessage(new NameSelectorMessage(name));
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
if (state is not NameSelectorBuiState cast || _window == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window.UpdateState(cast.Name);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
_window?.Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Time to choose..">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Text="Задайте название для метки." />
|
||||
<LineEdit Name="NameSelector" HorizontalExpand="True" />
|
||||
<Button Name="NameSelectorSet" Text="Принять" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,26 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.NameSelector;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class NameSelectorWindow: DefaultWindow
|
||||
{
|
||||
public Action<string>? OnNameChange;
|
||||
|
||||
public NameSelectorWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
NameSelectorSet.OnPressed += _ =>
|
||||
{
|
||||
OnNameChange!(NameSelector.Text);
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateState(string name)
|
||||
{
|
||||
NameSelector.Text = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Content.Client._White.UserInterface.Radial;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Utility;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.SpellSelector;
|
||||
|
||||
public sealed class SpellSelectorBUI : BoundUserInterface
|
||||
{
|
||||
|
||||
private RadialContainer? _radialContainer;
|
||||
|
||||
public SpellSelectorBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_radialContainer = new RadialContainer();
|
||||
_radialContainer.Closed += Close;
|
||||
|
||||
foreach (var action in CultistComponent.CultistActions)
|
||||
{
|
||||
var button = _radialContainer.AddButton(action.DisplayName, action.Icon?.Frame0());
|
||||
|
||||
button.Controller.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new CultEmpowerSelectedBuiMessage(action));
|
||||
Close();
|
||||
};
|
||||
}
|
||||
|
||||
_radialContainer.OpenAttachedLocalPlayer();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
_radialContainer?.Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using Content.Client._White.UserInterface.Radial;
|
||||
using Content.Client.Construction;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.White.Cult.Structures;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.StructureRadial;
|
||||
|
||||
public sealed class StructureCraftBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlacementManager _placement = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
private RadialContainer? _radialContainer;
|
||||
|
||||
public StructureCraftBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
private void CreateUI()
|
||||
{
|
||||
if (_radialContainer != null)
|
||||
ResetUI();
|
||||
|
||||
_radialContainer = new RadialContainer();
|
||||
|
||||
foreach (var prototype in _prototypeManager.EnumeratePrototypes<CultStructurePrototype>())
|
||||
{
|
||||
var radialButton = _radialContainer.AddButton(prototype.StructureName, prototype.Icon);
|
||||
radialButton.Controller.OnPressed += _ =>
|
||||
{
|
||||
Select(prototype.StructureId);
|
||||
};
|
||||
}
|
||||
|
||||
_radialContainer.OpenAttachedLocalPlayer();
|
||||
}
|
||||
|
||||
private void ResetUI()
|
||||
{
|
||||
_radialContainer?.Close();
|
||||
_radialContainer = null;
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
CreateUI();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
ResetUI();
|
||||
}
|
||||
|
||||
private void Select(string id)
|
||||
{
|
||||
CreateBlueprint(id);
|
||||
ResetUI();
|
||||
Close();
|
||||
}
|
||||
|
||||
private void CreateBlueprint(string id)
|
||||
{
|
||||
var newObj = new PlacementInformation
|
||||
{
|
||||
Range = 2,
|
||||
IsTile = false,
|
||||
EntityType = id,
|
||||
PlacementOption = "SnapgridCenter"
|
||||
};
|
||||
|
||||
_prototypeManager.TryIndex<ConstructionPrototype>(id, out var construct);
|
||||
|
||||
if (construct == null)
|
||||
return;
|
||||
|
||||
var player = _player.LocalPlayer?.ControlledEntity;
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
if (construct.ID == "CultPylon" && CheckForStructure(player, id))
|
||||
{
|
||||
var popup = _entMan.System<SharedPopupSystem>();
|
||||
popup.PopupClient(Loc.GetString("cult-structure-craft-another-structure-nearby"), player.Value, player.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
var constructSystem = _systemManager.GetEntitySystem<ConstructionSystem>();
|
||||
var hijack = new ConstructionPlacementHijack(constructSystem, construct);
|
||||
|
||||
_placement.BeginPlacing(newObj, hijack);
|
||||
}
|
||||
|
||||
private bool CheckForStructure(EntityUid? uid, string id)
|
||||
{
|
||||
if (uid == null)
|
||||
return false;
|
||||
|
||||
if (!_entMan.TryGetComponent<TransformComponent>(uid, out var transform))
|
||||
return false;
|
||||
|
||||
var lookupSystem = _entMan.System<EntityLookupSystem>();
|
||||
var entities = lookupSystem.GetEntitiesInRange(transform.Coordinates, 15f);
|
||||
foreach (var ent in entities)
|
||||
{
|
||||
if (!_entMan.TryGetComponent<MetaDataComponent>(ent, out var metadata))
|
||||
continue;
|
||||
|
||||
if (metadata.EntityPrototype?.ID == id)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{Loc 'Choose'}"
|
||||
MinWidth="300"
|
||||
MinHeight="400">
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Name="ItemsContainer" Orientation="Vertical"></BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,36 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.SummonCultistList;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class SummonCultistListWindow : DefaultWindow
|
||||
{
|
||||
public Action<int, int>? ItemSelected;
|
||||
|
||||
public SummonCultistListWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void PopulateList(List<int> items, List<string> labels)
|
||||
{
|
||||
ItemsContainer.RemoveAllChildren();
|
||||
|
||||
var count = Math.Min(items.Count, labels.Count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var button = new Button();
|
||||
|
||||
button.Text = labels[i];
|
||||
|
||||
button.OnPressed += _ => ItemSelected?.Invoke(item, items.IndexOf(item));
|
||||
|
||||
ItemsContainer.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.SummonCultistList;
|
||||
|
||||
public sealed class SummonCultistListWindowBUI : BoundUserInterface
|
||||
{
|
||||
private SummonCultistListWindow? _window;
|
||||
|
||||
public SummonCultistListWindowBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new();
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.ItemSelected += (item, index) =>
|
||||
{
|
||||
var msg = new SummonCultistListWindowItemSelectedMessage(item, index);
|
||||
SendMessage(msg);
|
||||
_window.Close();
|
||||
};
|
||||
|
||||
if (State != null)
|
||||
UpdateState(State);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is SummonCultistListWindowBUIState newState)
|
||||
{
|
||||
_window?.PopulateList(newState.Items, newState.Label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{Loc 'Teleport to'}"
|
||||
MinWidth="300"
|
||||
MinHeight="400">
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Name="ItemsContainer" Orientation="Vertical"></BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,41 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.TeleportRunesList;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class TeleportRunesListWindow : DefaultWindow
|
||||
{
|
||||
public Action<int, int>? ItemSelected;
|
||||
|
||||
public TeleportRunesListWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void PopulateList(List<int> items, List<string> labels)
|
||||
{
|
||||
ItemsContainer.RemoveAllChildren();
|
||||
|
||||
var count = Math.Min(items.Count, labels.Count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var button = new Button();
|
||||
|
||||
button.Text = labels[i];
|
||||
|
||||
button.OnPressed += _ => ItemSelected?.Invoke(item, items.IndexOf(item));
|
||||
|
||||
ItemsContainer.AddChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ItemsContainer.RemoveAllChildren();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.TeleportRunesList;
|
||||
|
||||
public sealed class TeleportRunesListWindowBUI : BoundUserInterface
|
||||
{
|
||||
private TeleportRunesListWindow? _window;
|
||||
|
||||
public TeleportRunesListWindowBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new();
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.ItemSelected += (item, index) =>
|
||||
{
|
||||
var msg = new TeleportRunesListWindowItemSelectedMessage(item, index);
|
||||
SendMessage(msg);
|
||||
_window.Close();
|
||||
};
|
||||
|
||||
if (State != null)
|
||||
UpdateState(State);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is TeleportRunesListWindowBUIState newState)
|
||||
{
|
||||
_window?.PopulateList(newState.Items, newState.Label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Eui;
|
||||
using Content.Client._White.Cult.UI.TeleportRunesList;
|
||||
using Content.Shared.Eui;
|
||||
using Content.Shared.White.Cult.UI;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.TeleportSpell;
|
||||
|
||||
public sealed class TeleportSpellEui : BaseEui
|
||||
{
|
||||
|
||||
private TeleportRunesListWindow _window;
|
||||
|
||||
public TeleportSpellEui()
|
||||
{
|
||||
_window = new TeleportRunesListWindow();
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
_window.OpenCentered();
|
||||
_window.ItemSelected += (index, _) => SendMessage(new TeleportSpellTargetRuneSelected(){RuneUid = index});
|
||||
_window.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
|
||||
base.Opened();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
{
|
||||
if(state is not TeleportSpellEuiState cast) return;
|
||||
|
||||
_window.Clear();
|
||||
_window.PopulateList(cast.Runes.Keys.ToList(), cast.Runes.Values.ToList());
|
||||
}
|
||||
}
|
||||
9
Content.Client/_White/Cult/UI/Torch/TorchWindow.xaml
Normal file
9
Content.Client/_White/Cult/UI/Torch/TorchWindow.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{Loc 'cult-torch-window-title'}"
|
||||
MinWidth="300"
|
||||
MinHeight="400">
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Name="ItemsContainer" Orientation="Vertical"></BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
35
Content.Client/_White/Cult/UI/Torch/TorchWindow.xaml.cs
Normal file
35
Content.Client/_White/Cult/UI/Torch/TorchWindow.xaml.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.Torch;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class TorchWindow : DefaultWindow
|
||||
{
|
||||
public Action<string, string>? ItemSelected;
|
||||
|
||||
public TorchWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void PopulateList(Dictionary<string, string> items)
|
||||
{
|
||||
ItemsContainer.RemoveAllChildren();
|
||||
|
||||
foreach (var item in items.Keys)
|
||||
{
|
||||
var button = new Button();
|
||||
var itemName = items[item];
|
||||
|
||||
button.Text = itemName;
|
||||
|
||||
button.OnPressed += _ => ItemSelected?.Invoke(item, items[item]);
|
||||
|
||||
ItemsContainer.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Content.Client/_White/Cult/UI/Torch/TorchWindowBUI.cs
Normal file
47
Content.Client/_White/Cult/UI/Torch/TorchWindowBUI.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Data;
|
||||
using Content.Shared.White.Cult.Items;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Cult.UI.Torch;
|
||||
|
||||
public sealed class TorchWindowBUI : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private TorchWindow? _window;
|
||||
|
||||
public TorchWindowBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new();
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.ItemSelected += (uid, item) =>
|
||||
{
|
||||
var msg = new TorchWindowItemSelectedMessage(uid, item);
|
||||
SendMessage(msg);
|
||||
_window.Close();
|
||||
};
|
||||
|
||||
if (State != null)
|
||||
UpdateState(State);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is TorchWindowBUIState newState)
|
||||
{
|
||||
_window?.PopulateList(newState.Items);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Content.Client/_White/UserInterface/Radial/RadialButton.xaml
Normal file
18
Content.Client/_White/UserInterface/Radial/RadialButton.xaml
Normal file
@@ -0,0 +1,18 @@
|
||||
<Control
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client._White.UserInterface.Radial">
|
||||
|
||||
<BoxContainer>
|
||||
<TextureButton
|
||||
Name="Controller"
|
||||
Access="Public"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True">
|
||||
<TextureRect Access="Public" Name="BackgroundTexture"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="KeepAspect">
|
||||
</TextureRect>
|
||||
</TextureButton>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._White.UserInterface.Radial;
|
||||
|
||||
[GenerateTypedNameReferences, Virtual, PublicAPI]
|
||||
public sealed partial class RadialButton : Control
|
||||
{
|
||||
[Animatable] public Vector2 Offset { get; set; }
|
||||
public string? Content { get; set; }
|
||||
|
||||
public string Texture
|
||||
{
|
||||
set => Controller.TexturePath = value;
|
||||
}
|
||||
|
||||
public string? Tooltip
|
||||
{
|
||||
set => Controller.ToolTip = value;
|
||||
get => Controller.ToolTip;
|
||||
}
|
||||
|
||||
public float? TooltipDelay
|
||||
{
|
||||
set => Controller.TooltipDelay = value;
|
||||
get => Controller.TooltipDelay;
|
||||
}
|
||||
|
||||
[Animatable]
|
||||
public Vector2 ButtonSize
|
||||
{
|
||||
get => this.Size;
|
||||
set => this.SetSize = value;
|
||||
}
|
||||
|
||||
public RadialButton()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
Offset = Vector2.Zero;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<controls:RadialContainer
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client._White.UserInterface.Radial"
|
||||
Visible="False"
|
||||
MaxSize="0 0">
|
||||
<BoxContainer>
|
||||
<controls:RadialButton
|
||||
Name="CloseButton"
|
||||
Access="Public"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
Content="{Loc 'radial-ui-close'}"
|
||||
Texture="/Textures/Interface/Default/blocked.png"/>
|
||||
<LayoutContainer Access="Public" Name="Layout" HorizontalExpand="True" VerticalExpand="True"></LayoutContainer>
|
||||
<RichTextLabel Access="Public" Name="ActionLabel" HorizontalExpand="True" Visible="False"/>
|
||||
</BoxContainer>
|
||||
</controls:RadialContainer>
|
||||
@@ -0,0 +1,428 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Resources;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._White.UserInterface.Radial;
|
||||
|
||||
public sealed class RadialContainerCommandTest : LocalizedCommands
|
||||
{
|
||||
public override string Command => "radialtest";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
string[] tips =
|
||||
{
|
||||
"Testovый туултип. Здесь можете расписать разную инфу о кнопке/действии",
|
||||
"Из окна дуло. Штирлиц закрыл окно. Дуло исчезло.",
|
||||
"Негры пидорасы Негры пидорасы Негры пидорасы Негры пидорасы"
|
||||
};
|
||||
var radial = new RadialContainer();
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
var testButton = radial.AddButton("Action " + i, "/Textures/Interface/emotions.svg.192dpi.png");
|
||||
testButton.Tooltip = tips[IoCManager.Resolve<IRobustRandom>().Next(0, 2)];
|
||||
testButton.Controller.OnPressed += (_) => { Logger.Debug("Press gay"); };
|
||||
}
|
||||
|
||||
radial.CloseButton.Controller.OnPressed += (_) =>
|
||||
{
|
||||
Logger.Debug("Close event for your own logic");
|
||||
};
|
||||
radial.OpenAttachedLocalPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
[GenerateTypedNameReferences, Virtual]
|
||||
public partial class RadialContainer : Control
|
||||
{
|
||||
private EntityUid? _attachedEntity;
|
||||
private bool _isAttached = false;
|
||||
|
||||
private bool _isOpened = false;
|
||||
|
||||
private Vector2 _focusSize = new Vector2(64, 64);
|
||||
private Vector2 _normalSize = new Vector2(50, 50);
|
||||
|
||||
private float _moveAniTime = 0.3f;
|
||||
private float _focusAniTime = 0.25f;
|
||||
|
||||
private string _backgroundTexture = "/Textures/Interface/Default/SlotBackground.png";
|
||||
|
||||
private const int MaxButtons = 8;
|
||||
|
||||
public const string MoveAnimationKey = "move";
|
||||
public const string InSizeAnimationKey = "insize";
|
||||
public const string OutSizeAnimationKey = "outsize";
|
||||
|
||||
public Action? Closed;
|
||||
|
||||
public float FocusSize
|
||||
{
|
||||
get => _focusSize.Y;
|
||||
set => _focusSize = new Vector2(value, value);
|
||||
}
|
||||
public float NormalSize
|
||||
{
|
||||
get => _normalSize.Y;
|
||||
set => _normalSize = new Vector2(value, value);
|
||||
}
|
||||
|
||||
public float MoveAnimationTime
|
||||
{
|
||||
get => _moveAniTime;
|
||||
set => _moveAniTime = value;
|
||||
}
|
||||
public float FocusAnimationTime
|
||||
{
|
||||
get => _focusAniTime;
|
||||
set => _focusAniTime = value;
|
||||
}
|
||||
|
||||
public bool IsAction = true;
|
||||
|
||||
public float VerticalOffset = 0.0f;
|
||||
public float DistanceAvaible = 10.0f;
|
||||
|
||||
public RadialContainer() : base()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void Open(Vector2 position)
|
||||
{
|
||||
AddToRoot();
|
||||
LayoutContainer.SetPosition(this, position);
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
public void OpenCentered()
|
||||
{
|
||||
AddToRoot();
|
||||
if (Parent != null)
|
||||
LayoutContainer.SetPosition(this, (Parent.Size/2) - (this.Size/2));
|
||||
else
|
||||
LayoutContainer.SetPosition(this, (UserInterfaceManager.MainViewport.Size/2) - (this.Size/2));
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
public void OpenCenteredLeft() => OpenCenteredAt(new Vector2(0.37f, 0.5f));
|
||||
|
||||
public void OpenCenteredAt(Vector2 position)
|
||||
{
|
||||
AddToRoot();
|
||||
if (Parent == null)
|
||||
return;
|
||||
LayoutContainer.SetPosition(this, (Parent.Size * position) - (this.Size/2));
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open on attached entity in the world.
|
||||
/// </summary>
|
||||
public void OpenAttached(EntityUid uid)
|
||||
{
|
||||
if (uid == EntityUid.Invalid)
|
||||
return;
|
||||
|
||||
AddToRoot();
|
||||
_attachedEntity = uid;
|
||||
_isAttached = true;
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open on our (player) attached entity.
|
||||
/// </summary>
|
||||
public void OpenAttachedLocalPlayer()
|
||||
{
|
||||
var localPlayer = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
|
||||
if (localPlayer == null)
|
||||
return;
|
||||
|
||||
AddToRoot();
|
||||
_attachedEntity = localPlayer.ControlledEntity;
|
||||
_isAttached = true;
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
public void Close(bool canDispose = true)
|
||||
{
|
||||
Parent?.RemoveChild(this);
|
||||
Visible = false;
|
||||
_isOpened = false;
|
||||
Closed?.Invoke();
|
||||
if (canDispose)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public RadialButton AddButton(string action, string? texture = null)
|
||||
{
|
||||
var button = new RadialButton();
|
||||
button.Content = action;
|
||||
button.Controller.TextureNormal = IoCManager.Resolve<IResourceCache>().GetTexture(_backgroundTexture);
|
||||
if (texture != null)
|
||||
button.BackgroundTexture.Texture = IoCManager.Resolve<IResourceCache>().GetTexture(texture);
|
||||
Layout.AddChild(button);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
public RadialButton AddButton(string action, Texture? texture)
|
||||
{
|
||||
var button = new RadialButton();
|
||||
button.Content = action;
|
||||
button.Controller.TextureNormal = IoCManager.Resolve<IResourceCache>().GetTexture(_backgroundTexture);
|
||||
if (texture != null)
|
||||
button.BackgroundTexture.Texture = texture;
|
||||
Layout.AddChild(button);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private void AddToRoot()
|
||||
{
|
||||
if (_isOpened)
|
||||
return;
|
||||
UserInterfaceManager.WindowRoot.AddChild(this);
|
||||
_isOpened = !_isOpened;
|
||||
}
|
||||
|
||||
private void UpdateButtons()
|
||||
{
|
||||
Visible = true;
|
||||
|
||||
var angleDegrees = 360/Layout.ChildCount;
|
||||
var stepAngle = -angleDegrees + -90;
|
||||
var distance = GetDistance();
|
||||
|
||||
foreach (var child in Layout.Children)
|
||||
{
|
||||
var button = (RadialButton)child;
|
||||
button.ButtonSize = _normalSize;
|
||||
stepAngle += angleDegrees;
|
||||
var pos = GetPointFromPolar(stepAngle, distance);
|
||||
PlayRadialAnimation(button, pos, MoveAnimationKey);
|
||||
|
||||
button.Controller.OnMouseEntered += (_) =>
|
||||
{
|
||||
PlaySizeAnimation(button, _focusSize, OutSizeAnimationKey, InSizeAnimationKey);
|
||||
ActionLabel.SetMarkup(button.Content ?? string.Empty);
|
||||
ActionLabel.Visible = IsAction;
|
||||
};
|
||||
button.Controller.OnMouseExited += (_) =>
|
||||
{
|
||||
PlaySizeAnimation(button, _normalSize, InSizeAnimationKey, OutSizeAnimationKey);
|
||||
ActionLabel.Visible = false;
|
||||
};
|
||||
}
|
||||
|
||||
CloseButton.ButtonSize = _normalSize;
|
||||
|
||||
CloseButton.Controller.OnMouseEntered += (_) =>
|
||||
{
|
||||
PlaySizeAnimation(CloseButton, _focusSize, OutSizeAnimationKey, InSizeAnimationKey);
|
||||
ActionLabel.SetMarkup(CloseButton.Content ?? string.Empty);
|
||||
ActionLabel.Visible = true;
|
||||
};
|
||||
|
||||
CloseButton.Controller.OnMouseExited += (_) =>
|
||||
{
|
||||
PlaySizeAnimation(CloseButton, _normalSize, InSizeAnimationKey, OutSizeAnimationKey);
|
||||
ActionLabel.Visible = false;
|
||||
};
|
||||
CloseButton.Controller.OnPressed += (_) =>
|
||||
{
|
||||
Close();
|
||||
};
|
||||
}
|
||||
|
||||
private void PlayRadialAnimation(Control button, Vector2 pos, string playKey)
|
||||
{
|
||||
var anim = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromMilliseconds(_moveAniTime * 1000),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackControlProperty
|
||||
{
|
||||
Property = nameof(RadialButton.Offset),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(0,0), 0f),
|
||||
new AnimationTrackProperty.KeyFrame(pos, _moveAniTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!button.HasRunningAnimation(playKey))
|
||||
button.PlayAnimation(anim, playKey);
|
||||
}
|
||||
|
||||
private void PlaySizeAnimation(Control button, Vector2 size, string playKey, string? stopKey)
|
||||
{
|
||||
var anim = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromMilliseconds(_focusAniTime * 1000),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackControlProperty
|
||||
{
|
||||
Property = nameof(RadialButton.ButtonSize),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(button.Size, 0f),
|
||||
new AnimationTrackProperty.KeyFrame(size, _focusAniTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (stopKey != null && button.HasRunningAnimation(stopKey))
|
||||
button.StopAnimation(stopKey);
|
||||
if (!button.HasRunningAnimation(playKey))
|
||||
button.PlayAnimation(anim, playKey);
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
base.Draw(handle);
|
||||
|
||||
foreach (var child in Layout.Children)
|
||||
{
|
||||
var button = (RadialButton)child;
|
||||
LayoutContainer.SetPosition(child, button.Offset - (button.Size/2));
|
||||
}
|
||||
LayoutContainer.SetPosition(CloseButton, CloseButton.Offset - (CloseButton.Size/2));
|
||||
LayoutContainer.SetPosition(ActionLabel,
|
||||
new Vector2(0 - (GetTextWidth(ActionLabel) / 4), GetDistance(4.5f) ));
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
if (!_isAttached)
|
||||
return;
|
||||
|
||||
base.FrameUpdate(args);
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var eyeManager = IoCManager.Resolve<IEyeManager>();
|
||||
|
||||
if (entityManager.Deleted(_attachedEntity))
|
||||
{
|
||||
Timer.Spawn(0, Die);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entityManager.TryGetComponent<TransformComponent>(_attachedEntity, out var xform) || xform.MapID != eyeManager.CurrentMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var localPlayer = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
|
||||
if (localPlayer == null)
|
||||
return;
|
||||
// Check distance beetween entities
|
||||
if (entityManager.TryGetComponent<TransformComponent>(localPlayer.ControlledEntity, out var myxform))
|
||||
{
|
||||
var onePoint = xform.WorldPosition;
|
||||
var twoPoint = myxform.WorldPosition;
|
||||
var distance = (onePoint - twoPoint).Length();
|
||||
|
||||
if (DistanceAvaible < distance)
|
||||
{
|
||||
Timer.Spawn(0, Die);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var offset = (-eyeManager.CurrentEye.Rotation).ToWorldVec() * -VerticalOffset;
|
||||
var worldPos = xform.WorldPosition + offset;
|
||||
|
||||
var lowerCenter = eyeManager.WorldToScreen(worldPos) / UIScale;
|
||||
var screenPos = lowerCenter - new Vector2(DesiredSize.X / 2, DesiredSize.Y / 2);
|
||||
// Round to nearest 0.5
|
||||
screenPos = (screenPos * 2).Rounded() / 2;
|
||||
LayoutContainer.SetPosition(this, screenPos);
|
||||
}
|
||||
|
||||
private int GetTextWidth(RichTextLabel text)
|
||||
{
|
||||
var font = GetFont(text);
|
||||
var msg = text.GetMessage();
|
||||
if (msg == null)
|
||||
return 0;
|
||||
|
||||
var width = 0;
|
||||
|
||||
foreach (var t in msg)
|
||||
{
|
||||
var metrics = font.GetCharMetrics(new Rune(t), 1);
|
||||
|
||||
if (metrics != null)
|
||||
width += metrics.Value.Width + metrics.Value.Advance;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
private void Die()
|
||||
{
|
||||
if (Disposed)
|
||||
return;
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
private Font GetFont(Control element)
|
||||
{
|
||||
if (element.TryGetStyleProperty<Font>("font", out var font))
|
||||
{
|
||||
return font;
|
||||
}
|
||||
|
||||
return UserInterfaceManager.ThemeDefaults.DefaultFont;
|
||||
}
|
||||
|
||||
private float GetDistance(float offset = 0.0f)
|
||||
{
|
||||
var distance = FocusSize * 1.2f;
|
||||
|
||||
if (Layout.Children.Count() <= MaxButtons)
|
||||
return distance + offset;
|
||||
|
||||
for (var i = 0; i < (Layout.Children.Count() - MaxButtons); i++)
|
||||
{
|
||||
distance += (NormalSize/3);
|
||||
}
|
||||
|
||||
return distance + offset;
|
||||
}
|
||||
|
||||
private static Vector2 GetPointFromPolar(double angleDegrees, double distance)
|
||||
{
|
||||
var angleRadians = angleDegrees * (Math.PI / 180.0);
|
||||
|
||||
var x = distance * Math.Cos(angleRadians);
|
||||
var y = distance * Math.Sin(angleRadians);
|
||||
|
||||
return new Vector2((int)Math.Round(x), (int)Math.Round(y));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user