Skip to content

Commit

Permalink
early merge projector rewrite/fixes (#1128)
Browse files Browse the repository at this point in the history
* cant disguise to thing in a container

* copy cigarette visualiser

* prevent aghost throwing an error

* make disguises die in space

* fuck it rewrite it to not use polymorph

* fix action troll

* oop

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
  • Loading branch information
deltanedas authored May 1, 2024
1 parent 511685e commit fccee49
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 121 deletions.
22 changes: 22 additions & 0 deletions Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Client.Smoking;
using Content.Shared.Chemistry.Components;
using Content.Shared.Polymorph.Components;
using Content.Shared.Polymorph.Systems;
Expand All @@ -10,24 +11,45 @@ public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;

private EntityQuery<AppearanceComponent> _appearanceQuery;
private EntityQuery<SpriteComponent> _spriteQuery;

public override void Initialize()
{
base.Initialize();

_appearanceQuery = GetEntityQuery<AppearanceComponent>();
_spriteQuery = GetEntityQuery<SpriteComponent>();

SubscribeLocalEvent<ChameleonDisguiseComponent, AfterAutoHandleStateEvent>(OnHandleState);

SubscribeLocalEvent<ChameleonDisguisedComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<ChameleonDisguisedComponent, ComponentShutdown>(OnShutdown);
}

private void OnHandleState(Entity<ChameleonDisguiseComponent> ent, ref AfterAutoHandleStateEvent args)
{
CopyComp<SpriteComponent>(ent);
CopyComp<GenericVisualizerComponent>(ent);
CopyComp<SolutionContainerVisualsComponent>(ent);
CopyComp<BurnStateVisualsComponent>(ent);

// reload appearance to hopefully prevent any invisible layers
if (_appearanceQuery.TryComp(ent, out var appearance))
_appearance.QueueUpdate(ent, appearance);
}

private void OnStartup(Entity<ChameleonDisguisedComponent> ent, ref ComponentStartup args)
{
if (!_spriteQuery.TryComp(ent, out var sprite))
return;

ent.Comp.WasVisible = sprite.Visible;
sprite.Visible = false;
}

private void OnShutdown(Entity<ChameleonDisguisedComponent> ent, ref ComponentShutdown args)
{
if (_spriteQuery.TryComp(ent, out var sprite))
sprite.Visible = ent.Comp.WasVisible;
}
}
96 changes: 1 addition & 95 deletions Content.Server/Polymorph/Systems/ChameleonProjectorSystem.cs
Original file line number Diff line number Diff line change
@@ -1,99 +1,5 @@
using Content.Server.Polymorph.Components;
using Content.Shared.Actions;
using Content.Shared.Construction.Components;
using Content.Shared.Hands;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Polymorph;
using Content.Shared.Polymorph.Components;
using Content.Shared.Polymorph.Systems;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Physics.Components;

namespace Content.Server.Polymorph.Systems;

public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem
{
[Dependency] private readonly MetaDataSystem _meta = default!;
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
[Dependency] private readonly PolymorphSystem _polymorph = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedTransformSystem _xform = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<ChameleonDisguiseComponent, GotEquippedHandEvent>(OnEquippedHand);
SubscribeLocalEvent<ChameleonDisguiseComponent, DisguiseToggleNoRotEvent>(OnToggleNoRot);
SubscribeLocalEvent<ChameleonDisguiseComponent, DisguiseToggleAnchoredEvent>(OnToggleAnchored);
}

private void OnEquippedHand(Entity<ChameleonDisguiseComponent> ent, ref GotEquippedHandEvent args)
{
if (!TryComp<PolymorphedEntityComponent>(ent, out var poly))
return;

_polymorph.Revert((ent, poly));
args.Handled = true;
}

public override void Disguise(ChameleonProjectorComponent proj, EntityUid user, EntityUid entity)
{
if (_polymorph.PolymorphEntity(user, proj.Polymorph) is not {} disguise)
return;

// make disguise look real (for simple things at least)
var meta = MetaData(entity);
_meta.SetEntityName(disguise, meta.EntityName);
_meta.SetEntityDescription(disguise, meta.EntityDescription);

var comp = EnsureComp<ChameleonDisguiseComponent>(disguise);
comp.SourceEntity = entity;
comp.SourceProto = Prototype(entity)?.ID;
Dirty(disguise, comp);

// no sechud trolling
RemComp<StatusIconComponent>(disguise);

_appearance.CopyData(entity, disguise);

var mass = CompOrNull<PhysicsComponent>(entity)?.Mass ?? 0f;

// let the disguise die when its taken enough damage, which then transfers to the player
// health is proportional to mass, and capped to not be insane
if (TryComp<MobThresholdsComponent>(disguise, out var thresholds))
{
// if the player is of flesh and blood, cap max health to theirs
// so that when reverting damage scales 1:1 and not round removing
var playerMax = _mobThreshold.GetThresholdForState(user, MobState.Dead).Float();
var max = playerMax == 0f ? proj.MaxHealth : Math.Max(proj.MaxHealth, playerMax);

var health = Math.Clamp(mass, proj.MinHealth, proj.MaxHealth);
_mobThreshold.SetMobStateThreshold(disguise, health, MobState.Critical, thresholds);
_mobThreshold.SetMobStateThreshold(disguise, max, MobState.Dead, thresholds);
}

// add actions for controlling transform aspects
_actions.AddAction(disguise, proj.NoRotAction);
_actions.AddAction(disguise, proj.AnchorAction);
}

private void OnToggleNoRot(Entity<ChameleonDisguiseComponent> ent, ref DisguiseToggleNoRotEvent args)
{
var xform = Transform(ent);
xform.NoLocalRotation = !xform.NoLocalRotation;
}

private void OnToggleAnchored(Entity<ChameleonDisguiseComponent> ent, ref DisguiseToggleAnchoredEvent args)
{
var uid = ent.Owner;
var xform = Transform(uid);
if (xform.Anchored)
_xform.Unanchor(uid, xform);
else
_xform.AnchorEntity((uid, xform));
}
}
public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem;
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@ public bool SetEnabled(EntityUid uid, EntityUid user, bool enabled, StealthCloth
if (MetaData(user).EntityLifeStage >= EntityLifeStage.Terminating)
return false;

// can't enable stealth if something else already enabled it, and vice versa
var stealth = EnsureComp<StealthComponent>(user);
if (stealth.Enabled == enabled)
return false;

comp.Enabled = enabled;
Dirty(uid, comp);

var stealth = EnsureComp<StealthComponent>(user);
// slightly visible, but doesn't change when moving so it's ok
var visibility = enabled ? stealth.MinVisibility + comp.Visibility : stealth.MaxVisibility;
_stealth.SetVisibility(user, visibility, stealth);
Expand Down
12 changes: 12 additions & 0 deletions Content.Shared/Polymorph/Components/ChameleonDisguiseComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ namespace Content.Shared.Polymorph.Components;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
public sealed partial class ChameleonDisguiseComponent : Component
{
/// <summary>
/// The user of this disguise.
/// </summary>
[DataField]
public EntityUid User;

/// <summary>
/// The projector that created this disguise.
/// </summary>
[DataField]
public EntityUid Projector;

/// <summary>
/// The disguise source entity for copying the sprite.
/// </summary>
Expand Down
30 changes: 30 additions & 0 deletions Content.Shared/Polymorph/Components/ChameleonDisguisedComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Content.Shared.FixedPoint;
using Robust.Shared.GameStates;

namespace Content.Shared.Polymorph.Components;

/// <summary>
/// Added to a player when they use a chameleon projector.
/// Handles making them invisible and revealing when damaged enough or switching hands.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class ChameleonDisguisedComponent : Component
{
/// <summary>
/// How much damage can be taken before revealing automatically.
/// </summary>
[DataField]
public FixedPoint2 Integrity = FixedPoint2.Zero;

/// <summary>
/// The disguise entity parented to the player.
/// </summary>
[DataField]
public EntityUid Disguise;

/// <summary>
/// For client, whether the user's sprite was previously visible or not.
/// </summary>
[DataField]
public bool WasVisible;
}
20 changes: 18 additions & 2 deletions Content.Shared/Polymorph/Components/ChameleonProjectorComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,26 @@ public sealed partial class ChameleonProjectorComponent : Component
public EntityWhitelist? Blacklist;

/// <summary>
/// Polymorph configuration for the disguise entity.
/// Disguise entity to spawn and use.
/// </summary>
[DataField(required: true)]
public PolymorphConfiguration Polymorph = new();
public EntProtoId DisguiseProto = string.Empty;

/// <summary>
/// Action for disabling your disguise's rotation.
/// </summary>
[DataField]
public EntProtoId NoRotAction = "ActionDisguiseNoRot";
[DataField]
public EntityUid? NoRotActionEntity;

/// <summary>
/// Action for anchoring your disguise in place.
/// </summary>
[DataField]
public EntProtoId AnchorAction = "ActionDisguiseAnchor";
[DataField]
public EntityUid? AnchorActionEntity;

/// <summary>
/// Minimum health to give the disguise.
Expand All @@ -54,6 +58,12 @@ public sealed partial class ChameleonProjectorComponent : Component
[DataField]
public float MaxHealth = 100f;

/// <summary>
/// Popup shown to the user when they try to disguise as an entity inside a container.
/// </summary>
[DataField]
public LocId ContainerPopup = "chameleon-projector-inside-container";

/// <summary>
/// Popup shown to the user when they try to disguise as an invalid entity.
/// </summary>
Expand All @@ -65,4 +75,10 @@ public sealed partial class ChameleonProjectorComponent : Component
/// </summary>
[DataField]
public LocId SuccessPopup = "chameleon-projector-success";

/// <summary>
/// User currently disguised by this projector, if any
/// </summary>
[DataField]
public EntityUid? Disguised;
}
Loading

0 comments on commit fccee49

Please sign in to comment.