[Feat] Внешний вид и название канистр можно изменить прямо в игре (#317)

* feat: Теперь можно менять внешний вид канистр газов

* fix: русский язык (я его не знаю)
This commit is contained in:
Remuchi
2024-05-30 21:28:53 +07:00
committed by GitHub
parent 26c7788d9b
commit d4be4d3445
10 changed files with 353 additions and 1 deletions

View File

@@ -0,0 +1,36 @@
using Content.Shared._White.PolymorphableCanister;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Client._White.PolymorphableCanister;
public sealed class PolymorphableCanisterSystem : SharedPolymorphableCanisterSystem
{
[Dependency] private readonly IComponentFactory _componentFactory = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PolymorphableCanisterComponent, AfterAutoHandleStateEvent>(HandleState);
}
private void HandleState(EntityUid uid,
PolymorphableCanisterComponent component,
ref AfterAutoHandleStateEvent args)
{
UpdateAppearance(uid, component.CurrentPrototype);
}
protected override void UpdateSprite(EntityUid uid, EntityPrototype proto)
{
base.UpdateSprite(uid, proto);
if (!TryComp(uid, out SpriteComponent? sprite) ||
!proto.TryGetComponent(out SpriteComponent? otherSprite, _componentFactory))
{
return;
}
sprite.CopyFrom(otherSprite);
}
}

View File

@@ -0,0 +1,37 @@
using Content.Shared._White.PolymorphableCanister;
using JetBrains.Annotations;
namespace Content.Client._White.PolymorphableCanister.UI;
[UsedImplicitly]
// ReSharper disable once InconsistentNaming
public sealed class PolymorphableCanisterBUI : BoundUserInterface
{
private PolymorphableCanisterMenu? _menu;
public PolymorphableCanisterBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}
protected override void Open()
{
_menu = new PolymorphableCanisterMenu(Owner, this);
_menu.OnClose += Close;
_menu.OpenCentered();
}
public void SendMessage(string protoId)
{
SendMessage(new PolymorphableCanisterMessage(protoId));
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_menu?.Dispose();
}
}

View File

@@ -0,0 +1,12 @@
<ui:RadialMenu xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
BackButtonStyleClass="RadialMenuBackButton"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="450 450">
<!-- Main container to hold all canister states-->
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="140"
ReserveSpaceForHiddenChildren="False" />
</ui:RadialMenu>

View File

@@ -0,0 +1,57 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared._White.PolymorphableCanister;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._White.PolymorphableCanister.UI;
[GenerateTypedNameReferences]
public sealed partial class PolymorphableCanisterMenu : RadialMenu
{
[Dependency] private readonly EntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
public PolymorphableCanisterMenu(EntityUid owner, PolymorphableCanisterBUI bui)
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
if (!_entManager.TryGetComponent<PolymorphableCanisterComponent>(owner, out var canister))
return;
var spriteSystem = _entManager.System<SpriteSystem>();
var main = FindControl<RadialContainer>("Main");
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (main is null)
return;
foreach (var protoId in canister.Prototypes)
{
if (canister.CurrentPrototype == protoId)
continue;
if (!_protoManager.TryIndex(protoId, out var proto))
continue;
var button = new RadialMenuTextureButton
{
ToolTip = Loc.GetString(proto.Name),
TextureNormal = spriteSystem.GetPrototypeIcon(protoId).Default,
StyleClasses = { "RadialMenuButton" },
SetSize = new Vector2(64f, 64f)
};
button.OnButtonUp += _ =>
{
bui.SendMessage(protoId);
Close();
};
main.AddChild(button);
}
}
}

View File

@@ -0,0 +1,53 @@
using Content.Server.Atmos.Piping.Unary.Components;
using Content.Shared._White.PolymorphableCanister;
using Content.Shared.Lock;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Content.Server._White.PolymorphableCanister;
public sealed class PolymorphableCanisterSystem : SharedPolymorphableCanisterSystem
{
[Dependency] private readonly UserInterfaceSystem _ui = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PolymorphableCanisterComponent, GetVerbsEvent<Verb>>(GetVerb);
}
private void GetVerb(EntityUid uid, PolymorphableCanisterComponent component, GetVerbsEvent<Verb> args)
{
if (TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked)
{
return;
}
if (TryComp(uid, out GasCanisterComponent? gasCanister) && gasCanister.Air.Pressure > 100)
{
return;
}
var changeAppearanceVerb = new Verb
{
Text = Loc.GetString("polymorphable-canister-change-appearance-verb"),
Icon = new SpriteSpecifier.Rsi(new ResPath("Structures/Storage/canister.rsi"), "yellow"),
Act = () => TryOpenUi(uid, args.User, component)
};
args.Verbs.Add(changeAppearanceVerb);
}
private void TryOpenUi(EntityUid uid, EntityUid user, PolymorphableCanisterComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
if (!TryComp(user, out ActorComponent? actor))
return;
_ui.TryToggleUi(uid, PolymorphableCanisterUiKey.Key, actor.PlayerSession);
}
}

View File

@@ -0,0 +1,46 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._White.PolymorphableCanister;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
public sealed partial class PolymorphableCanisterComponent : Component
{
[DataField]
public ResPath ResPath = new("Structures/Storage/canister.rsi");
[DataField, AutoNetworkedField]
public ProtoId<EntityPrototype>? CurrentPrototype;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public int DoAfterTime = 3;
[DataField]
public List<ProtoId<EntityPrototype>> Prototypes = new()
{
"GasCanister",
"StorageCanister",
"AirCanister",
"OxygenCanister",
"NitrogenCanister",
"CarbonDioxideCanister",
"PlasmaCanister",
"TritiumCanister",
"WaterVaporCanister",
"AmmoniaCanister",
"NitrousOxideCanister",
"FrezonCanister",
"BZCanister",
"PluoxiumCanister",
"HydrogenCanister",
"NitriumCanister",
"HealiumCanister",
"HyperNobliumCanister",
"ProtoNitrateCanister",
"ZaukerCanister",
"HalonCanister",
"HeliumCanister",
"AntiNobliumCanister",
};
}

View File

@@ -0,0 +1,106 @@
using Content.Shared.DoAfter;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._White.PolymorphableCanister;
public abstract class SharedPolymorphableCanisterSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PolymorphableCanisterComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<PolymorphableCanisterComponent, PolymorphableCanisterMessage>(OnMessage);
SubscribeLocalEvent<PolymorphableCanisterComponent, PolymorphableCanisterDoAfterEvent>(OnDoAfter);
}
private void OnInit(Entity<PolymorphableCanisterComponent> ent, ref ComponentInit args)
{
var proto = MetaData(ent.Owner).EntityPrototype;
if (proto is null)
{
return;
}
ent.Comp.CurrentPrototype = proto.ID;
Dirty(ent);
}
private void OnMessage(Entity<PolymorphableCanisterComponent> ent, ref PolymorphableCanisterMessage args)
{
if (!args.Session.AttachedEntity.HasValue)
{
return;
}
var doAfterArgs = new DoAfterArgs(EntityManager,
args.Session.AttachedEntity.Value,
ent.Comp.DoAfterTime,
new PolymorphableCanisterDoAfterEvent(args.ProtoId),
ent.Owner
)
{
BreakOnMove = true,
NeedHand = true
};
_doAfter.TryStartDoAfter(doAfterArgs);
}
private void OnDoAfter(Entity<PolymorphableCanisterComponent> ent, ref PolymorphableCanisterDoAfterEvent args)
{
if (args.Cancelled)
{
return;
}
ent.Comp.CurrentPrototype = args.ProtoId;
UpdateAppearance(ent, args.ProtoId);
Dirty(ent);
}
public void UpdateAppearance(EntityUid uid, ProtoId<EntityPrototype>? protoId)
{
if (string.IsNullOrEmpty(protoId) || !_proto.TryIndex(protoId, out var proto))
{
return;
}
var metadata = MetaData(uid);
_metaData.SetEntityName(uid, proto.Name, metadata);
_metaData.SetEntityDescription(uid, proto.Description, metadata);
UpdateSprite(uid, proto);
}
protected virtual void UpdateSprite(EntityUid uid, EntityPrototype proto)
{
}
}
[NetSerializable, Serializable]
public enum PolymorphableCanisterUiKey : byte
{
Key
}
[Serializable, NetSerializable]
public sealed class PolymorphableCanisterMessage(string protoId) : BoundUserInterfaceMessage
{
public readonly ProtoId<EntityPrototype> ProtoId = protoId;
}
[Serializable, NetSerializable]
public sealed partial class PolymorphableCanisterDoAfterEvent : SimpleDoAfterEvent
{
public readonly ProtoId<EntityPrototype> ProtoId;
public PolymorphableCanisterDoAfterEvent(string protoId)
{
ProtoId = protoId;
}
}

View File

@@ -0,0 +1 @@
polymorphable-canister-change-appearance-verb = Change appearance

View File

@@ -0,0 +1 @@
polymorphable-canister-change-appearance-verb = Перекрасить

View File

@@ -34,10 +34,12 @@
1: { state: can-o1, shader: "unshaded" }
2: { state: can-o2, shader: "unshaded" }
3: { state: can-o3, shader: "unshaded" }
- type: UserInterface
- type: UserInterface # WhiteDream - Added PolymorphableCanisterUiKey
interfaces:
- key: enum.GasCanisterUiKey.Key
type: GasCanisterBoundUserInterface
- key: enum.PolymorphableCanisterUiKey.Key
type: PolymorphableCanisterBUI
- type: Destructible
thresholds:
- trigger:
@@ -104,6 +106,7 @@
access: [["Atmospherics"], ["Engineering"], ["Research"]]
- type: Lock
locked: false
- type: PolymorphableCanister # WhiteDream
- type: entity
parent: GasCanister