CatStation/Content.Shared/MagicMirror/MagicMirrorSystem.cs
ArtisticRoomba 0d2c96e23e
Stable -> Master (#44165)
* [HOTFIX] Fix singularity and tesla friction (#44078)

* Vehicle system: Movement & Buckling (#43525)

* Part one of Vehicles

* Also we need this

* And we need this

* We REALLY need this

* Little fix

* fixes (i'm tired)

* Apply suggestions from code review

Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com>

* update

* fixed

* just for cleaner diff

* just in case

* I think this might be cleaner

* small fix

---------

Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com>

* Fix ID inspect message when ending perma sentence early (#44018)

* Automatic changelog update

* Smarter Entity Targeting (#39571)

* Smart targeting

* use swidge statement

* Undo

* merge conflicts

* works well

* Better logic

* GO TO SLEEP

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* fix: don't delete items on some polymorph reverts (#39423)

* Automatic changelog update

* Fix error texture for harmpack hand solution visuals (#39442)

* fix: check for inhand sprite's existence

* refactor: simplify solution container visuals code

this made my brain hurt but I think I didn't break anything

* Automatic changelog update

* fix role assignment when late joining (#41419)

* fix role assignment when late joining

* push

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Automatic changelog update

* SSD Players now properly stay asleep when being revived. (#39409)

* Fixes and API changes

* aaaaaaaaaa

* super shitcode

* cleanup

* fix admin logs

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Automatic changelog update

* Fix Barber Scissors not playing audio (#43922)

* Fix Barber Scissors not playing audio

* Add code comment

* Requested changes

* More requested changes thanks Warock

* Requested changes

* Automatic changelog update

* [MALD PR] Remove mimic vendor gamerule (#44057)

remove mimic vender rule

* Automatic changelog update

* Destructible Automatic Overkill (#43113)

* Change to file scoped namespace

* Improve variable naming

* Remove unnecessary audiosystem

* Add automatic overkill

* Small cleanups

* Alphabetize dependencies

* Change to file scoped namespace

* Change to OnMapInit

* Fix overkill if no destruction node present

* Add overkill test

* Example cleanup

* Performance improvements

* Tuple event subscriptions

* Correct comment

* Realphabetize

I might be dyslexic. Or just stupid.

* Invert toggle behavior and rename

* Change from MapInit to ComponentInit

* Revert "Change from MapInit to ComponentInit"

This reverts commit 1ed9fa0ad49be00becd9adca5cfee45cf4fef3a2.

* Change to use index instead of raw int

* Explicit check not required

* Clean up overkill test

* Improve commenting

* Overhaul the test

* Little more

* Improve commenting

* Update DestructibleOverkillTest.cs

* Typo

* Change to tuple assignment

* Alphabetize dependencies

* Update Content.IntegrationTests/Tests/Destructible/DestructibleOverkillTest.cs

* Update Content.IntegrationTests/Tests/Destructible/DestructibleOverkillTest.cs

* Update Content.IntegrationTests/Tests/Destructible/DestructibleOverkillTest.cs

---------

Co-authored-by: Tayrtahn <tayrtahn@gmail.com>

* Update Credits (#44060)

Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>

* fix off by 1 in tips (#44062)

fix the thing the uhhhh the thing the thing ynow....   fix uhh the thing. the thing that needs fixing

* Remove background box from main menu (#43977)

* Menu Change

* Change v2

* final change

* Format code

---------

Co-authored-by: Thomas (Aeshus) <git@aeshus.com>

* Mini Gravity Generator requires charging (#44061)

* Starting charge to zero

* Reach starts with gravity

* Revert "Reach starts with gravity"

This reverts commit 49a7e22fc054687b879a5a13562433bc081f19cd.

* Create charged/uncharged prototypes

* Fix formatting

* Change construction

* Review feedback

* Automatic changelog update

* [BUGFIX] Shaken sodas now spill again (#44033)

Tiders will rule the halls once again.

* Automatic changelog update

* add empty tile friction modifiers

* add empty tile friction modifiers

* Revert "[Hotfix] Fix MenuButton Missing Negative Style (#44073)"

This reverts commit 0f1abd73669c6c7ff291bb289f760090dc9454b6.

* Revert "Automatic changelog update"

This reverts commit 4a34a711845f0da49686c750cad7807639b56deb.

* Revert "[BUGFIX] Shaken sodas now spill again (#44033)"

This reverts commit 37447f4d89feeb98baba91d896b0439d30088b80.

* Revert "Automatic changelog update"

This reverts commit 1ced1977fb73186a8aaefab504e3a860aee219f9.

* Revert "Mini Gravity Generator requires charging (#44061)"

This reverts commit 51e0f74f49673146973f8ad4966d1df0d97a506d.

* Revert "Remove background box from main menu (#43977)"

This reverts commit 48f3fd0c5029b384b008fdda9016e78858fcae38.

* Revert "fix off by 1 in tips (#44062)"

This reverts commit 1616ed806eef33bbdcaca64aaa642c44ab95f869.

* Revert "Update Credits (#44060)"

This reverts commit 6a9ffb9b4d5d62d191c173c4670e1c3ea6ffadb0.

* Revert "Destructible Automatic Overkill (#43113)"

This reverts commit 76277b737df94b02cb3fd439d8cda0f63964b85d.

* Revert "Automatic changelog update"

This reverts commit 5e3a647839dd5f2e3fb942b9fc4693b0bfe6baa8.

* Revert "[MALD PR] Remove mimic vendor gamerule (#44057)"

This reverts commit 77ee428ce4ab312b4d91124c884c233328ce871c.

* Revert "Automatic changelog update"

This reverts commit 71c503bf9c8c9fe785110e6b3cd5a5b46138c1d0.

* Revert "Fix Barber Scissors not playing audio (#43922)"

This reverts commit 989ab25751eb494b463d476c502a28d82b1cca6d.

* Revert "Automatic changelog update"

This reverts commit 013962c7160912570e361195c9dfcea694d0c514.

* Revert "SSD Players now properly stay asleep when being revived. (#39409)"

This reverts commit 546c62b38810bd6f4666235046407fb008593289.

* Revert "Automatic changelog update"

This reverts commit f5d65b43621721dda77d6c5ce6b18202c6b02ad7.

* Revert "fix role assignment when late joining (#41419)"

This reverts commit bec4c4c827ad69a2206415fc26a35f2f2e00ff1e.

* Revert "Automatic changelog update"

This reverts commit 0c6d7aa29bb6dbd3b4b57b5f787152a946cc5b39.

* Revert "fix: don't delete items on some polymorph reverts (#39423)"

This reverts commit 01a2dfb9762fc53f3918199360717208c9162695.

* Revert "Fix error texture for harmpack hand solution visuals (#39442)"

This reverts commit 42dbb60e0fe670e5bc35c5dd9f4c1f81c69e15cd.

* Revert "Automatic changelog update"

This reverts commit 1cb150f5bbd8c430eba0d2471fa4d2b4e72ba8ad.

* Revert "Smarter Entity Targeting (#39571)"

This reverts commit 87b389d1492af8404eb3c43a5a0dcd7623d724b9.

* Revert "Automatic changelog update"

This reverts commit 927f9e2652f146686be50750ffc04569893cb5d8.

* Revert "Fix ID inspect message when ending perma sentence early (#44018)"

This reverts commit c7dfbf89484dd332847924643bd6bfed08d37253.

* Revert "Vehicle system: Movement & Buckling (#43525)"

This reverts commit 8c85a5927564701e1e44eca27cb55d295c2eda11.

* revert

---------

Co-authored-by: AndrewFenriz <78079974+AndrewFenriz@users.noreply.github.com>
Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com>
Co-authored-by: themias <89101928+themias@users.noreply.github.com>
Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>
Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>
Co-authored-by: Ignaz "Ian" Kraft <ignaz.k@live.de>
Co-authored-by: Rami <ramialanbagy@gmail.com>
Co-authored-by: Jessica M <jessica@jessicamaybe.com>
Co-authored-by: Connor Huffine <chuffine@gmail.com>
Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Co-authored-by: PicklOH <62030853+PicklOH@users.noreply.github.com>
Co-authored-by: Lye <128915833+Lyroth001@users.noreply.github.com>
Co-authored-by: piskaczek <192605039+piskaczek@users.noreply.github.com>
Co-authored-by: riccardi48 <39341154+riccardi48@users.noreply.github.com>
Co-authored-by: Thomas (Aeshus) <git@aeshus.com>
Co-authored-by: ThatGuyUSA <thatguyusa123@gmail.com>
Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com>
Co-authored-by: Thomas <87614336+Aeshus@users.noreply.github.com>

* [HOTFIX] Fix Barber DoAfter Bug (#44162)

* Update the Salamander server description for the server browser: Redux (#44104)

Update the MRP config

---------

Co-authored-by: Crude Oil <124208219+CroilBird@users.noreply.github.com>
Co-authored-by: AndrewFenriz <78079974+AndrewFenriz@users.noreply.github.com>
Co-authored-by: themias <89101928+themias@users.noreply.github.com>
Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>
Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>
Co-authored-by: Ignaz "Ian" Kraft <ignaz.k@live.de>
Co-authored-by: Rami <ramialanbagy@gmail.com>
Co-authored-by: Jessica M <jessica@jessicamaybe.com>
Co-authored-by: Connor Huffine <chuffine@gmail.com>
Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Co-authored-by: PicklOH <62030853+PicklOH@users.noreply.github.com>
Co-authored-by: Lye <128915833+Lyroth001@users.noreply.github.com>
Co-authored-by: piskaczek <192605039+piskaczek@users.noreply.github.com>
Co-authored-by: riccardi48 <39341154+riccardi48@users.noreply.github.com>
Co-authored-by: Thomas (Aeshus) <git@aeshus.com>
Co-authored-by: ThatGuyUSA <thatguyusa123@gmail.com>
Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com>
Co-authored-by: Thomas <87614336+Aeshus@users.noreply.github.com>
Co-authored-by: Kowlin <10947836+Kowlin@users.noreply.github.com>
2026-05-29 23:11:42 -07:00

294 lines
9.9 KiB
C#

using Content.Shared.Body;
using Content.Shared.DoAfter;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Content.Shared.UserInterface;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.MagicMirror;
public sealed partial class MagicMirrorSystem : EntitySystem
{
[Dependency] private SharedInteractionSystem _interaction = default!;
[Dependency] private SharedUserInterfaceSystem _userInterface = default!;
[Dependency] private InventorySystem _inventory = default!;
[Dependency] private TagSystem _tag = default!;
[Dependency] private SharedPopupSystem _popup = default!;
[Dependency] private SharedDoAfterSystem _doAfter = default!;
[Dependency] private SharedAudioSystem _audio = default!;
[Dependency] private SharedVisualBodySystem _visualBody = default!;
private static readonly ProtoId<TagPrototype> HidesHairTag = "HidesHair";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MagicMirrorComponent, AfterInteractEvent>(OnMagicMirrorInteract);
SubscribeLocalEvent<MagicMirrorComponent, BeforeActivatableUIOpenEvent>(OnBeforeUIOpen);
SubscribeLocalEvent<MagicMirrorComponent, ActivatableUIOpenAttemptEvent>(OnAttemptOpenUI);
Subs.BuiEvents<MagicMirrorComponent>(MagicMirrorUiKey.Key,
subs =>
{
subs.Event<BoundUIClosedEvent>(OnUiClosed);
subs.Event<MagicMirrorSelectMessage>(OnMagicMirrorSelect);
});
SubscribeLocalEvent<MagicMirrorComponent, MagicMirrorSelectDoAfterEvent>(OnSelectSlotDoAfter);
SubscribeLocalEvent<MagicMirrorComponent, BoundUserInterfaceCheckRangeEvent>(OnMirrorRangeCheck);
}
private void OnMagicMirrorSelect(Entity<MagicMirrorComponent> ent, ref MagicMirrorSelectMessage args)
{
if (ent.Comp.Target is not { } target)
return;
// Check if the target getting their hair altered has any clothes that hides their hair
if (CheckHeadSlotOrClothes(target))
{
_popup.PopupEntity(
ent.Comp.Target == args.Actor
? Loc.GetString("magic-mirror-blocked-by-hat-self")
: Loc.GetString("magic-mirror-blocked-by-hat-self-target", ("target", Identity.Entity(target, EntityManager))),
args.Actor,
args.Actor,
PopupType.Medium);
return;
}
if (ent.Comp.DoAfter.HasValue)
{
_doAfter.Cancel(args.Actor, ent.Comp.DoAfter.Value);
ent.Comp.DoAfter = null;
}
var doafterTime = ent.Comp.ModifyTime;
if (ent.Comp.Target == args.Actor)
doafterTime /= 3;
var doAfter = new MagicMirrorSelectDoAfterEvent()
{
Markings = args.Markings,
};
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Actor, doafterTime, doAfter, ent, target: target, used: ent)
{
DistanceThreshold = SharedInteractionSystem.InteractionRange,
BreakOnDamage = true,
BreakOnMove = true,
NeedHand = true,
},
out var doAfterId);
if (target == args.Actor)
{
_popup.PopupEntity(Loc.GetString("magic-mirror-change-slot-self"), target, target, PopupType.Medium);
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-change-slot-target", ("user", Identity.Entity(args.Actor, EntityManager))), target, target, PopupType.Medium);
}
_audio.PlayPredicted(ent.Comp.ChangeHairSound, ent, args.Actor);
ent.Comp.DoAfter = doAfterId?.Index;
}
private void OnSelectSlotDoAfter(Entity<MagicMirrorComponent> ent, ref MagicMirrorSelectDoAfterEvent args)
{
ent.Comp.DoAfter = null;
if (args.Handled || args.Target == null || args.Cancelled)
return;
if (ent.Comp.Target != args.Target)
return;
// what plays the audio
_audio.PlayPredicted(ent.Comp.ChangeHairSound, args.Target.Value, args.User);
foreach (var (organ, markings) in args.Markings)
{
if (!ent.Comp.Organs.Contains(organ))
{
args.Markings.Remove(organ);
continue;
}
foreach (var layer in markings.Keys)
{
if (!ent.Comp.Layers.Contains(layer))
markings.Remove(layer);
}
}
_visualBody.ApplyMarkings(args.Target.Value, args.Markings);
}
private void OnUiClosed(Entity<MagicMirrorComponent> ent, ref BoundUIClosedEvent args)
{
ent.Comp.Target = null;
Dirty(ent);
}
private void OnMagicMirrorInteract(Entity<MagicMirrorComponent> mirror, ref AfterInteractEvent args)
{
if (!args.CanReach || args.Target == null)
return;
if (!HasComp<VisualBodyComponent>(args.Target.Value))
return;
UpdateInterface(mirror, args.Target.Value);
_userInterface.TryOpenUi(mirror.Owner, MagicMirrorUiKey.Key, args.User);
}
private void OnMirrorRangeCheck(EntityUid uid, MagicMirrorComponent component, ref BoundUserInterfaceCheckRangeEvent args)
{
if (args.Result == BoundUserInterfaceRangeResult.Fail)
return;
if (component.Target == null || !Exists(component.Target))
{
component.Target = null;
args.Result = BoundUserInterfaceRangeResult.Fail;
return;
}
if (!_interaction.InRangeUnobstructed(component.Target.Value, uid))
args.Result = BoundUserInterfaceRangeResult.Fail;
}
private void OnAttemptOpenUI(EntityUid uid, MagicMirrorComponent component, ref ActivatableUIOpenAttemptEvent args)
{
var user = component.Target ?? args.User;
if (!HasComp<VisualBodyComponent>(user))
args.Cancel();
}
private void OnBeforeUIOpen(Entity<MagicMirrorComponent> ent, ref BeforeActivatableUIOpenEvent args)
{
UpdateInterface(ent, args.User);
}
private void UpdateInterface(Entity<MagicMirrorComponent> ent, EntityUid target)
{
if (!_visualBody.TryGatherMarkingsData(target, ent.Comp.Layers, out var profiles, out var markings, out var applied))
return;
ent.Comp.Target = target;
foreach (var profile in profiles)
{
if (!ent.Comp.Organs.Contains(profile.Key))
profiles.Remove(profile.Key);
}
foreach (var marking in markings)
{
if (!ent.Comp.Organs.Contains(marking.Key))
{
profiles.Remove(marking.Key);
continue;
}
marking.Value.Layers.IntersectWith(ent.Comp.Layers);
}
foreach (var appliedPair in applied)
{
if (!ent.Comp.Organs.Contains(appliedPair.Key))
applied.Remove(appliedPair.Key);
}
// TODO: Component states
var state = new MagicMirrorUiState(profiles, markings, applied);
_userInterface.SetUiState(ent.Owner, MagicMirrorUiKey.Key, state);
Dirty(ent);
}
/// <summary>
/// Helper function that checks if the wearer has anything on their head
/// Or if they have any clothes that hides their hair
/// </summary>
private bool CheckHeadSlotOrClothes(EntityUid target)
{
if (!TryComp<InventoryComponent>(target, out var inventoryComp))
return false;
// any hat whatsoever will block haircutting
if (_inventory.TryGetSlotEntity(target, "head", out _, inventoryComp))
{
return true;
}
// maybe there's some kind of armor that has the HidesHair tag as well, so check every slot for it
var slots = _inventory.GetSlotEnumerator((target, inventoryComp), SlotFlags.WITHOUT_POCKET);
while (slots.MoveNext(out var slot))
{
if (slot.ContainedEntity != null && _tag.HasTag(slot.ContainedEntity.Value, HidesHairTag))
{
return true;
}
}
return false;
}
}
[Serializable, NetSerializable]
public enum MagicMirrorUiKey : byte
{
Key
}
[Serializable, NetSerializable]
public sealed class MagicMirrorSelectMessage : BoundUserInterfaceMessage
{
public MagicMirrorSelectMessage(Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> markings)
{
Markings = markings;
}
public Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> Markings { get; }
}
[Serializable, NetSerializable]
public sealed class MagicMirrorUiState : BoundUserInterfaceState
{
public MagicMirrorUiState(Dictionary<ProtoId<OrganCategoryPrototype>, OrganProfileData> profiles,
Dictionary<ProtoId<OrganCategoryPrototype>, OrganMarkingData> markings,
Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> applied)
{
OrganProfileData = profiles;
OrganMarkingData = markings;
AppliedMarkings = applied;
}
public NetEntity Target;
public Dictionary<ProtoId<OrganCategoryPrototype>, OrganProfileData> OrganProfileData;
public Dictionary<ProtoId<OrganCategoryPrototype>, OrganMarkingData> OrganMarkingData;
public Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> AppliedMarkings;
}
[Serializable, NetSerializable]
public sealed partial class MagicMirrorSelectDoAfterEvent : DoAfterEvent
{
public Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> Markings;
public override DoAfterEvent Clone() => this;
}