Skip to content

Commit

Permalink
Add Chameleon PDA (#30514)
Browse files Browse the repository at this point in the history
* V1 commit

* Remove PDA name and unnecessary pda state

* Adds PDA to Chameleon backpack & thief toolbox

* Change to use AppearanceDataInit

* Add basic PDA state to ensure there's always a sprite before AppearanceData can be applied

* Revert PDA name (this will be changed to another way later)

* Update PDA name updating to new system

* Fix yaml, and fix Agent ID chameleon

* Updated based on review
  • Loading branch information
SlamBamActionman authored Jan 2, 2025
1 parent 34960c5 commit 21351df
Show file tree
Hide file tree
Showing 17 changed files with 458 additions and 117 deletions.
10 changes: 10 additions & 0 deletions Content.Client/Clothing/Systems/ChameleonClothingSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Content.Client.PDA;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Inventory;
Expand Down Expand Up @@ -51,6 +52,15 @@ protected override void UpdateSprite(EntityUid uid, EntityPrototype proto)
{
sprite.CopyFrom(otherSprite);
}

// Edgecase for PDAs to include visuals when UI is open
if (TryComp(uid, out PdaBorderColorComponent? borderColor)
&& proto.TryGetComponent(out PdaBorderColorComponent? otherBorderColor, _factory))
{
borderColor.BorderColor = otherBorderColor.BorderColor;
borderColor.AccentHColor = otherBorderColor.AccentHColor;
borderColor.AccentVColor = otherBorderColor.AccentVColor;
}
}

/// <summary>
Expand Down
28 changes: 26 additions & 2 deletions Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
using Content.Client.Clothing.Systems;
using Content.Client.Clothing.Systems;
using Content.Shared.Clothing.Components;
using Content.Shared.Tag;
using Content.Shared.Prototypes;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;

namespace Content.Client.Clothing.UI;

[UsedImplicitly]
public sealed class ChameleonBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private readonly ChameleonClothingSystem _chameleon;
private readonly TagSystem _tag;

[ViewVariables]
private ChameleonMenu? _menu;

public ChameleonBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_chameleon = EntMan.System<ChameleonClothingSystem>();
_tag = EntMan.System<TagSystem>();
}

protected override void Open()
Expand All @@ -34,7 +41,24 @@ protected override void UpdateState(BoundUserInterfaceState state)
return;

var targets = _chameleon.GetValidTargets(st.Slot);
_menu?.UpdateState(targets, st.SelectedId);
if (st.RequiredTag != null)
{
var newTargets = new List<string>();
foreach (var target in targets)
{
if (string.IsNullOrEmpty(target) || !_proto.TryIndex(target, out EntityPrototype? proto))
continue;

if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, st.RequiredTag))
continue;

newTargets.Add(target);
}
_menu?.UpdateState(newTargets, st.SelectedId);
} else
{
_menu?.UpdateState(targets, st.SelectedId);
}
}

private void OnIdSelected(string selectedId)
Expand Down
5 changes: 5 additions & 0 deletions Content.Client/PDA/PdaMenu.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ public void UpdateState(PdaUpdateState state)
_pdaOwner = state.PdaOwnerInfo.ActualOwnerName;
PdaOwnerLabel.SetMarkup(Loc.GetString("comp-pda-ui-owner",
("actualOwnerName", _pdaOwner)));
PdaOwnerLabel.Visible = true;
}
else
{
PdaOwnerLabel.Visible = false;
}


Expand Down
40 changes: 0 additions & 40 deletions Content.Client/PDA/PdaSystem.cs
Original file line number Diff line number Diff line change
@@ -1,48 +1,8 @@
using Content.Shared.PDA;
using Content.Shared.Light;
using Robust.Client.GameObjects;

namespace Content.Client.PDA;

public sealed class PdaSystem : SharedPdaSystem
{
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<PdaComponent, AppearanceChangeEvent>(OnAppearanceChange);
}

private void OnAppearanceChange(EntityUid uid, PdaComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;

if (Appearance.TryGetData<bool>(uid, UnpoweredFlashlightVisuals.LightOn, out var isFlashlightOn, args.Component))
args.Sprite.LayerSetVisible(PdaVisualLayers.Flashlight, isFlashlightOn);

if (Appearance.TryGetData<bool>(uid, PdaVisuals.IdCardInserted, out var isCardInserted, args.Component))
args.Sprite.LayerSetVisible(PdaVisualLayers.IdLight, isCardInserted);
}

protected override void OnComponentInit(EntityUid uid, PdaComponent component, ComponentInit args)
{
base.OnComponentInit(uid, component, args);

if (!TryComp<SpriteComponent>(uid, out var sprite))
return;

if (component.State != null)
sprite.LayerSetState(PdaVisualLayers.Base, component.State);

sprite.LayerSetVisible(PdaVisualLayers.Flashlight, component.FlashlightOn);
sprite.LayerSetVisible(PdaVisualLayers.IdLight, component.IdSlot.StartingItem != null);
}

public enum PdaVisualLayers : byte
{
Base,
Flashlight,
IdLight
}
}
30 changes: 30 additions & 0 deletions Content.Client/PDA/PdaVisualizerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Content.Shared.Light;
using Content.Shared.PDA;
using Robust.Client.GameObjects;

namespace Content.Client.PDA;

public sealed class PdaVisualizerSystem : VisualizerSystem<PdaVisualsComponent>
{
protected override void OnAppearanceChange(EntityUid uid, PdaVisualsComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;

if (AppearanceSystem.TryGetData<string>(uid, PdaVisuals.PdaType, out var pdaType, args.Component))
args.Sprite.LayerSetState(PdaVisualLayers.Base, pdaType);

if (AppearanceSystem.TryGetData<bool>(uid, UnpoweredFlashlightVisuals.LightOn, out var isFlashlightOn, args.Component))
args.Sprite.LayerSetVisible(PdaVisualLayers.Flashlight, isFlashlightOn);

if (AppearanceSystem.TryGetData<bool>(uid, PdaVisuals.IdCardInserted, out var isCardInserted, args.Component))
args.Sprite.LayerSetVisible(PdaVisualLayers.IdLight, isCardInserted);
}

public enum PdaVisualLayers : byte
{
Base,
Flashlight,
IdLight
}
}
14 changes: 14 additions & 0 deletions Content.Client/PDA/PdaVisualsComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Content.Client.PDA;

/// <summary>
/// Used for visualizing PDA visuals.
/// </summary>
[RegisterComponent]
public sealed partial class PdaVisualsComponent : Component
{
public string? BorderColor;

public string? AccentHColor;

public string? AccentVColor;
}
6 changes: 3 additions & 3 deletions Content.Server/Clothing/Systems/ChameleonClothingSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Content.Server.IdentityManagement;
using Content.Server.IdentityManagement;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.IdentityManagement.Components;
Expand Down Expand Up @@ -63,7 +63,7 @@ private void UpdateUi(EntityUid uid, ChameleonClothingComponent? component = nul
if (!Resolve(uid, ref component))
return;

var state = new ChameleonBoundUserInterfaceState(component.Slot, component.Default);
var state = new ChameleonBoundUserInterfaceState(component.Slot, component.Default, component.RequireTag);
_uiSystem.SetUiState(uid, ChameleonUiKey.Key, state);
}

Expand All @@ -84,7 +84,7 @@ public void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdat
// make sure that it is valid change
if (string.IsNullOrEmpty(protoId) || !_proto.TryIndex(protoId, out EntityPrototype? proto))
return;
if (!IsValidTarget(proto, component.Slot))
if (!IsValidTarget(proto, component.Slot, component.RequireTag))
return;
component.Default = protoId;

Expand Down
21 changes: 16 additions & 5 deletions Content.Server/PDA/PdaSystem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Server.Access.Systems;
using Content.Server.AlertLevel;
using Content.Server.CartridgeLoader;
using Content.Server.Chat.Managers;
Expand Down Expand Up @@ -36,6 +37,7 @@ public sealed class PdaSystem : SharedPdaSystem
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly UnpoweredFlashlightSystem _unpoweredFlashlight = default!;
[Dependency] private readonly ContainerSystem _containerSystem = default!;
[Dependency] private readonly IdCardSystem _idCard = default!;

public override void Initialize()
{
Expand All @@ -55,19 +57,25 @@ public override void Initialize()
SubscribeLocalEvent<PdaComponent, CartridgeLoaderNotificationSentEvent>(OnNotification);

SubscribeLocalEvent<StationRenamedEvent>(OnStationRenamed);
SubscribeLocalEvent<EntityRenamedEvent>(OnEntityRenamed);
SubscribeLocalEvent<EntityRenamedEvent>(OnEntityRenamed, after: new[] { typeof(IdCardSystem) });
SubscribeLocalEvent<AlertLevelChangedEvent>(OnAlertLevelChanged);
}

private void OnEntityRenamed(ref EntityRenamedEvent ev)
{
var query = EntityQueryEnumerator<PdaComponent>();
if (HasComp<IdCardComponent>(ev.Uid))
return;

while (query.MoveNext(out var uid, out var comp))
if (_idCard.TryFindIdCard(ev.Uid, out var idCard))
{
if (comp.PdaOwner == ev.Uid)
var query = EntityQueryEnumerator<PdaComponent>();

while (query.MoveNext(out var uid, out var comp))
{
SetOwner(uid, comp, ev.Uid, ev.NewName);
if (comp.ContainedId == idCard)
{
SetOwner(uid, comp, ev.Uid, ev.NewName);
}
}
}
}
Expand All @@ -86,6 +94,9 @@ protected override void OnComponentInit(EntityUid uid, PdaComponent pda, Compone
protected override void OnItemInserted(EntityUid uid, PdaComponent pda, EntInsertedIntoContainerMessage args)
{
base.OnItemInserted(uid, pda, args);
var id = CompOrNull<IdCardComponent>(pda.ContainedId);
if (id != null)
pda.OwnerName = id.FullName;
UpdatePdaUi(uid, pda);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,26 @@ public sealed partial class ChameleonClothingComponent : Component
/// </summary>
[ViewVariables]
public EntityUid? User;

/// <summary>
/// Filter possible chameleon options by a tag in addition to WhitelistChameleon.
/// </summary>
[DataField]
public string? RequireTag;
}

[Serializable, NetSerializable]
public sealed class ChameleonBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly SlotFlags Slot;
public readonly string? SelectedId;
public readonly string? RequiredTag;

public ChameleonBoundUserInterfaceState(SlotFlags slot, string? selectedId)
public ChameleonBoundUserInterfaceState(SlotFlags slot, string? selectedId, string? requiredTag)
{
Slot = slot;
SelectedId = selectedId;
RequiredTag = requiredTag;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@
using Content.Shared.Item;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;

namespace Content.Shared.Clothing.EntitySystems;

public abstract class SharedChameleonClothingSystem : EntitySystem
{
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ISerializationManager _serialization = default!;
[Dependency] private readonly ClothingSystem _clothingSystem = default!;
[Dependency] private readonly ContrabandSystem _contraband = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly TagSystem _tag = default!;

public override void Initialize()
Expand Down Expand Up @@ -71,6 +74,14 @@ protected void UpdateVisuals(EntityUid uid, ChameleonClothingComponent component
_clothingSystem.CopyVisuals(uid, otherClothing, clothing);
}

// appearance data logic
if (TryComp(uid, out AppearanceComponent? appearance) &&
proto.TryGetComponent("Appearance", out AppearanceComponent? appearanceOther))
{
_appearance.AppendData(appearanceOther, uid);
Dirty(uid, appearance);
}

// properly mark contraband
if (proto.TryGetComponent("Contraband", out ContrabandComponent? contra))
{
Expand All @@ -88,7 +99,7 @@ protected virtual void UpdateSprite(EntityUid uid, EntityPrototype proto) { }
/// <summary>
/// Check if this entity prototype is valid target for chameleon item.
/// </summary>
public bool IsValidTarget(EntityPrototype proto, SlotFlags chameleonSlot = SlotFlags.NONE)
public bool IsValidTarget(EntityPrototype proto, SlotFlags chameleonSlot = SlotFlags.NONE, string? requiredTag = null)
{
// check if entity is valid
if (proto.Abstract || proto.HideSpawnMenu)
Expand All @@ -98,6 +109,9 @@ public bool IsValidTarget(EntityPrototype proto, SlotFlags chameleonSlot = SlotF
if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, "WhitelistChameleon"))
return false;

if (requiredTag != null && !_tag.HasTag(tag, requiredTag))
return false;

// check if it's valid clothing
if (!proto.TryGetComponent("Clothing", out ClothingComponent? clothing))
return false;
Expand Down
6 changes: 0 additions & 6 deletions Content.Shared/PDA/PdaComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ public sealed partial class PdaComponent : Component
public const string PdaPenSlotId = "PDA-pen";
public const string PdaPaiSlotId = "PDA-pai";

/// <summary>
/// The base PDA sprite state, eg. "pda", "pda-clown"
/// </summary>
[DataField("state")]
public string? State;

[DataField("idSlot")]
public ItemSlot IdSlot = new();

Expand Down
3 changes: 2 additions & 1 deletion Content.Shared/PDA/PdaVisuals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ namespace Content.Shared.PDA
[Serializable, NetSerializable]
public enum PdaVisuals
{
IdCardInserted
IdCardInserted,
PdaType
}

[Serializable, NetSerializable]
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
components:
- type: StorageFill
contents:
- id: ChameleonPDA
- id: ClothingUniformJumpsuitChameleon
- id: ClothingOuterChameleon
- id: ClothingNeckChameleon
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Catalog/thief_toolbox_sets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
sprite: /Textures/Clothing/OuterClothing/Misc/black_hoodie.rsi
state: icon
content:
- ChameleonPDA
- ClothingUniformJumpsuitChameleon
- ClothingOuterChameleon
- ClothingNeckChameleon
Expand Down
Loading

0 comments on commit 21351df

Please sign in to comment.