cham projector fixes/rewrite (#27111)

* 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

* add vebr

* add access to the components

* 2/3

* fix

* relay damage from disguise to user

* fix integrity

* :trollface:

* :trollface:

* m

* kill integrity

* fix a bug

* review

* remove them from component

* relay flash effect to the disguise

* fix icon being weird

* change method since multiple systems cant handle same network event

* :trollface:

* actually network Disguise real

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas
2024-11-08 11:15:41 +00:00
committed by GitHub
parent 667daa168f
commit 80e148c265
9 changed files with 311 additions and 140 deletions

View File

@@ -124,6 +124,10 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
continue; continue;
} }
var targetEv = new GetFlashEffectTargetEvent(ent);
RaiseLocalEvent(ent, ref targetEv);
ent = targetEv.Target;
EnsureComp<ColorFlashEffectComponent>(ent, out comp); EnsureComp<ColorFlashEffectComponent>(ent, out comp);
comp.NetSyncEnabled = false; comp.NetSyncEnabled = false;
comp.Color = sprite.Color; comp.Color = sprite.Color;
@@ -132,3 +136,9 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
} }
} }
} }
/// <summary>
/// Raised on an entity to change the target for a color flash effect.
/// </summary>
[ByRefEvent]
public record struct GetFlashEffectTargetEvent(EntityUid Target);

View File

@@ -1,7 +1,10 @@
using Content.Client.Effects;
using Content.Client.Smoking;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Polymorph.Components; using Content.Shared.Polymorph.Components;
using Content.Shared.Polymorph.Systems; using Content.Shared.Polymorph.Systems;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Player;
namespace Content.Client.Polymorph.Systems; namespace Content.Client.Polymorph.Systems;
@@ -10,14 +13,20 @@ public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
private EntityQuery<AppearanceComponent> _appearanceQuery; private EntityQuery<AppearanceComponent> _appearanceQuery;
private EntityQuery<SpriteComponent> _spriteQuery;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_appearanceQuery = GetEntityQuery<AppearanceComponent>(); _appearanceQuery = GetEntityQuery<AppearanceComponent>();
_spriteQuery = GetEntityQuery<SpriteComponent>();
SubscribeLocalEvent<ChameleonDisguiseComponent, AfterAutoHandleStateEvent>(OnHandleState); SubscribeLocalEvent<ChameleonDisguiseComponent, AfterAutoHandleStateEvent>(OnHandleState);
SubscribeLocalEvent<ChameleonDisguisedComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<ChameleonDisguisedComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<ChameleonDisguisedComponent, GetFlashEffectTargetEvent>(OnGetFlashEffectTargetEvent);
} }
private void OnHandleState(Entity<ChameleonDisguiseComponent> ent, ref AfterAutoHandleStateEvent args) private void OnHandleState(Entity<ChameleonDisguiseComponent> ent, ref AfterAutoHandleStateEvent args)
@@ -25,9 +34,30 @@ public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem
CopyComp<SpriteComponent>(ent); CopyComp<SpriteComponent>(ent);
CopyComp<GenericVisualizerComponent>(ent); CopyComp<GenericVisualizerComponent>(ent);
CopyComp<SolutionContainerVisualsComponent>(ent); CopyComp<SolutionContainerVisualsComponent>(ent);
CopyComp<BurnStateVisualsComponent>(ent);
// reload appearance to hopefully prevent any invisible layers // reload appearance to hopefully prevent any invisible layers
if (_appearanceQuery.TryComp(ent, out var appearance)) if (_appearanceQuery.TryComp(ent, out var appearance))
_appearance.QueueUpdate(ent, 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;
}
private void OnGetFlashEffectTargetEvent(Entity<ChameleonDisguisedComponent> ent, ref GetFlashEffectTargetEvent args)
{
args.Target = ent.Comp.Disguise;
}
} }

View File

@@ -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.Polymorph.Systems;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Physics.Components;
namespace Content.Server.Polymorph.Systems; namespace Content.Server.Polymorph.Systems;
public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem 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));
}
}

View File

@@ -1,3 +1,4 @@
using Content.Shared.Polymorph.Systems;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -7,9 +8,22 @@ namespace Content.Shared.Polymorph.Components;
/// Component added to disguise entities. /// Component added to disguise entities.
/// Used by client to copy over appearance from the disguise's source entity. /// Used by client to copy over appearance from the disguise's source entity.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] [RegisterComponent, NetworkedComponent, Access(typeof(SharedChameleonProjectorSystem))]
[AutoGenerateComponentState(true)]
public sealed partial class ChameleonDisguiseComponent : Component 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> /// <summary>
/// The disguise source entity for copying the sprite. /// The disguise source entity for copying the sprite.
/// </summary> /// </summary>

View File

@@ -0,0 +1,25 @@
using Content.Shared.Polymorph.Systems;
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, Access(typeof(SharedChameleonProjectorSystem))]
[AutoGenerateComponentState]
public sealed partial class ChameleonDisguisedComponent : Component
{
/// <summary>
/// The disguise entity parented to the player.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid Disguise;
/// <summary>
/// For client, whether the user's sprite was previously visible or not.
/// </summary>
[DataField]
public bool WasVisible;
}

View File

@@ -1,4 +1,3 @@
using Content.Shared.Polymorph;
using Content.Shared.Polymorph.Systems; using Content.Shared.Polymorph.Systems;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -25,22 +24,26 @@ public sealed partial class ChameleonProjectorComponent : Component
public EntityWhitelist? Blacklist; public EntityWhitelist? Blacklist;
/// <summary> /// <summary>
/// Polymorph configuration for the disguise entity. /// Disguise entity to spawn and use.
/// </summary> /// </summary>
[DataField(required: true)] [DataField(required: true)]
public PolymorphConfiguration Polymorph = new(); public EntProtoId DisguiseProto = string.Empty;
/// <summary> /// <summary>
/// Action for disabling your disguise's rotation. /// Action for disabling your disguise's rotation.
/// </summary> /// </summary>
[DataField] [DataField]
public EntProtoId NoRotAction = "ActionDisguiseNoRot"; public EntProtoId NoRotAction = "ActionDisguiseNoRot";
[DataField]
public EntityUid? NoRotActionEntity;
/// <summary> /// <summary>
/// Action for anchoring your disguise in place. /// Action for anchoring your disguise in place.
/// </summary> /// </summary>
[DataField] [DataField]
public EntProtoId AnchorAction = "ActionDisguiseAnchor"; public EntProtoId AnchorAction = "ActionDisguiseAnchor";
[DataField]
public EntityUid? AnchorActionEntity;
/// <summary> /// <summary>
/// Minimum health to give the disguise. /// Minimum health to give the disguise.
@@ -55,14 +58,8 @@ public sealed partial class ChameleonProjectorComponent : Component
public float MaxHealth = 100f; public float MaxHealth = 100f;
/// <summary> /// <summary>
/// Popup shown to the user when they try to disguise as an invalid entity. /// User currently disguised by this projector, if any
/// </summary> /// </summary>
[DataField] [DataField]
public LocId InvalidPopup = "chameleon-projector-invalid"; public EntityUid? Disguised;
/// <summary>
/// Popup shown to the user when they disguise as a valid entity.
/// </summary>
[DataField]
public LocId SuccessPopup = "chameleon-projector-success";
} }

View File

@@ -1,67 +1,264 @@
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Coordinates;
using Content.Shared.Damage;
using Content.Shared.Hands;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Polymorph; using Content.Shared.Item;
using Content.Shared.Polymorph.Components; using Content.Shared.Polymorph.Components;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Shared.Serialization.Manager; using Content.Shared.Storage.Components;
using Robust.Shared.Prototypes; using Content.Shared.Verbs;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
using System.Diagnostics.CodeAnalysis;
namespace Content.Shared.Polymorph.Systems; namespace Content.Shared.Polymorph.Systems;
/// <summary> /// <summary>
/// Handles whitelist/blacklist checking. /// Handles disguise validation, disguising and revealing.
/// Actual polymorphing and deactivation is done serverside. /// Most appearance copying is done clientside.
/// </summary> /// </summary>
public abstract class SharedChameleonProjectorSystem : EntitySystem public abstract class SharedChameleonProjectorSystem : EntitySystem
{ {
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ISerializationManager _serMan = default!; [Dependency] private readonly ISerializationManager _serMan = default!;
[Dependency] private readonly MetaDataSystem _meta = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly SharedTransformSystem _xform = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ChameleonDisguiseComponent, InteractHandEvent>(OnDisguiseInteractHand, before: [typeof(SharedItemSystem)]);
SubscribeLocalEvent<ChameleonDisguiseComponent, DamageChangedEvent>(OnDisguiseDamaged);
SubscribeLocalEvent<ChameleonDisguiseComponent, InsertIntoEntityStorageAttemptEvent>(OnDisguiseInsertAttempt);
SubscribeLocalEvent<ChameleonDisguiseComponent, ComponentShutdown>(OnDisguiseShutdown);
SubscribeLocalEvent<ChameleonProjectorComponent, AfterInteractEvent>(OnInteract); SubscribeLocalEvent<ChameleonProjectorComponent, AfterInteractEvent>(OnInteract);
SubscribeLocalEvent<ChameleonProjectorComponent, GetVerbsEvent<UtilityVerb>>(OnGetVerbs);
SubscribeLocalEvent<ChameleonProjectorComponent, DisguiseToggleNoRotEvent>(OnToggleNoRot);
SubscribeLocalEvent<ChameleonProjectorComponent, DisguiseToggleAnchoredEvent>(OnToggleAnchored);
SubscribeLocalEvent<ChameleonProjectorComponent, HandDeselectedEvent>(OnDeselected);
SubscribeLocalEvent<ChameleonProjectorComponent, GotUnequippedHandEvent>(OnUnequipped);
SubscribeLocalEvent<ChameleonProjectorComponent, ComponentShutdown>(OnProjectorShutdown);
} }
#region Disguise entity
private void OnDisguiseInteractHand(Entity<ChameleonDisguiseComponent> ent, ref InteractHandEvent args)
{
TryReveal(ent.Comp.User);
args.Handled = true;
}
private void OnDisguiseDamaged(Entity<ChameleonDisguiseComponent> ent, ref DamageChangedEvent args)
{
// this mirrors damage 1:1
if (args.DamageDelta is {} damage)
_damageable.TryChangeDamage(ent.Comp.User, damage);
}
private void OnDisguiseInsertAttempt(Entity<ChameleonDisguiseComponent> ent, ref InsertIntoEntityStorageAttemptEvent args)
{
// stay parented to the user, not the storage
args.Cancelled = true;
}
private void OnDisguiseShutdown(Entity<ChameleonDisguiseComponent> ent, ref ComponentShutdown args)
{
_actions.RemoveProvidedActions(ent.Comp.User, ent.Comp.Projector);
}
#endregion
#region Projector
private void OnInteract(Entity<ChameleonProjectorComponent> ent, ref AfterInteractEvent args) private void OnInteract(Entity<ChameleonProjectorComponent> ent, ref AfterInteractEvent args)
{ {
if (!args.CanReach || args.Target is not {} target) if (args.Handled || !args.CanReach || args.Target is not {} target)
return;
args.Handled = true;
TryDisguise(ent, args.User, target);
}
private void OnGetVerbs(Entity<ChameleonProjectorComponent> ent, ref GetVerbsEvent<UtilityVerb> args)
{
if (!args.CanAccess)
return; return;
var user = args.User; var user = args.User;
args.Handled = true; var target = args.Target;
args.Verbs.Add(new UtilityVerb()
{
Act = () =>
{
TryDisguise(ent, user, target);
},
Text = Loc.GetString("chameleon-projector-set-disguise")
});
}
public bool TryDisguise(Entity<ChameleonProjectorComponent> ent, EntityUid user, EntityUid target)
{
if (_container.IsEntityInContainer(target))
{
_popup.PopupClient(Loc.GetString("chameleon-projector-inside-container"), target, user);
return false;
}
if (IsInvalid(ent.Comp, target)) if (IsInvalid(ent.Comp, target))
{ {
_popup.PopupClient(Loc.GetString(ent.Comp.InvalidPopup), target, user); _popup.PopupClient(Loc.GetString("chameleon-projector-invalid"), target, user);
return; return false;
} }
_popup.PopupClient(Loc.GetString(ent.Comp.SuccessPopup), target, user); _popup.PopupClient(Loc.GetString("chameleon-projector-success"), target, user);
Disguise(ent.Comp, user, target); Disguise(ent, user, target);
return true;
} }
private void OnToggleNoRot(Entity<ChameleonProjectorComponent> ent, ref DisguiseToggleNoRotEvent args)
{
if (ent.Comp.Disguised is not {} uid)
return;
var xform = Transform(uid);
_xform.SetLocalRotationNoLerp(uid, 0, xform);
xform.NoLocalRotation = !xform.NoLocalRotation;
args.Handled = true;
}
private void OnToggleAnchored(Entity<ChameleonProjectorComponent> ent, ref DisguiseToggleAnchoredEvent args)
{
if (ent.Comp.Disguised is not {} uid)
return;
var xform = Transform(uid);
if (xform.Anchored)
_xform.Unanchor(uid, xform);
else
_xform.AnchorEntity((uid, xform));
args.Handled = true;
}
private void OnDeselected(Entity<ChameleonProjectorComponent> ent, ref HandDeselectedEvent args)
{
RevealProjector(ent);
}
private void OnUnequipped(Entity<ChameleonProjectorComponent> ent, ref GotUnequippedHandEvent args)
{
RevealProjector(ent);
}
private void OnProjectorShutdown(Entity<ChameleonProjectorComponent> ent, ref ComponentShutdown args)
{
RevealProjector(ent);
}
#endregion
#region API
/// <summary> /// <summary>
/// Returns true if an entity cannot be used as a disguise. /// Returns true if an entity cannot be used as a disguise.
/// </summary> /// </summary>
public bool IsInvalid(ChameleonProjectorComponent comp, EntityUid target) public bool IsInvalid(ChameleonProjectorComponent comp, EntityUid target)
{ {
return _whitelistSystem.IsWhitelistFail(comp.Whitelist, target) return _whitelist.IsWhitelistFail(comp.Whitelist, target)
|| _whitelistSystem.IsBlacklistPass(comp.Blacklist, target); || _whitelist.IsBlacklistPass(comp.Blacklist, target);
} }
/// <summary> /// <summary>
/// On server, polymorphs the user into an entity and sets up the disguise. /// On server, polymorphs the user into an entity and sets up the disguise.
/// </summary> /// </summary>
public virtual void Disguise(ChameleonProjectorComponent comp, EntityUid user, EntityUid entity) public void Disguise(Entity<ChameleonProjectorComponent> ent, EntityUid user, EntityUid entity)
{ {
var proj = ent.Comp;
// no spawning prediction sorry
if (_net.IsClient)
return;
// reveal first to allow quick switching
TryReveal(user);
// add actions for controlling transform aspects
_actions.AddAction(user, ref proj.NoRotActionEntity, proj.NoRotAction, container: ent);
_actions.AddAction(user, ref proj.AnchorActionEntity, proj.AnchorAction, container: ent);
proj.Disguised = user;
var disguise = SpawnAttachedTo(proj.DisguiseProto, user.ToCoordinates());
var disguised = AddComp<ChameleonDisguisedComponent>(user);
disguised.Disguise = disguise;
Dirty(user, disguised);
// 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.User = user;
comp.Projector = ent;
comp.SourceEntity = entity;
comp.SourceProto = Prototype(entity)?.ID;
Dirty(disguise, comp);
// item disguises can be picked up to be revealed, also makes sure their examine size is correct
CopyComp<ItemComponent>((disguise, comp));
_appearance.CopyData(entity, disguise);
} }
/// <summary>
/// Removes the disguise, if the user is disguised.
/// </summary>
public bool TryReveal(Entity<ChameleonDisguisedComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return false;
if (TryComp<ChameleonDisguiseComponent>(ent.Comp.Disguise, out var disguise)
&& TryComp<ChameleonProjectorComponent>(disguise.Projector, out var proj))
{
proj.Disguised = null;
}
var xform = Transform(ent);
xform.NoLocalRotation = false;
_xform.Unanchor(ent, xform);
Del(ent.Comp.Disguise);
RemComp<ChameleonDisguisedComponent>(ent);
return true;
}
/// <summary>
/// Reveal a projector's user, if any.
/// </summary>
public void RevealProjector(Entity<ChameleonProjectorComponent> ent)
{
if (ent.Comp.Disguised is {} user)
TryReveal(user);
}
#endregion
/// <summary> /// <summary>
/// Copy a component from the source entity/prototype to the disguise entity. /// Copy a component from the source entity/prototype to the disguise entity.
/// </summary> /// </summary>

View File

@@ -1,2 +1,4 @@
chameleon-projector-inside-container = There's no room to scan that!
chameleon-projector-invalid = You can't disguise as that! chameleon-projector-invalid = You can't disguise as that!
chameleon-projector-success = Projected new disguise. chameleon-projector-success = Projected new disguise.
chameleon-projector-set-disguise = Set Disguise

View File

@@ -17,15 +17,12 @@
blacklist: blacklist:
components: components:
- ChameleonDisguise # no becoming kleiner - ChameleonDisguise # no becoming kleiner
- InsideEntityStorage # no clark kent going in phone booth and becoming superman
- MindContainer # no - MindContainer # no
- Pda # PDAs currently make you invisible /!\ - Pda # PDAs currently make you invisible /!\
polymorph: disguiseProto: ChameleonDisguise
entity: ChameleonDisguise
- type: entity - type: entity
categories: [ HideSpawnMenu ] categories: [ HideSpawnMenu ]
parent: BaseMob
id: ChameleonDisguise id: ChameleonDisguise
name: Urist McKleiner name: Urist McKleiner
components: components:
@@ -33,20 +30,11 @@
- type: Sprite - type: Sprite
sprite: /Textures/Mobs/Species/Human/parts.rsi sprite: /Textures/Mobs/Species/Human/parts.rsi
state: full state: full
# so people can attempt to pick it up - type: Transform
- type: Item noRot: true # players rotation and anchor is used instead
# so it can take damage - type: InteractionOutline
# projector system sets health to be proportional to mass - type: Clickable
- type: Damageable - type: Damageable
- type: MobState
- type: MobThresholds
thresholds:
0: Alive
1: Critical
200: Dead
- type: MovementSpeedModifier
baseWalkSpeed: 1 # precise movement for the perfect spot
baseSprintSpeed: 5 # the jig is up
- type: ChameleonDisguise - type: ChameleonDisguise
# actions # actions
@@ -57,6 +45,7 @@
components: components:
- type: InstantAction - type: InstantAction
icon: Interface/VerbIcons/refresh.svg.192dpi.png icon: Interface/VerbIcons/refresh.svg.192dpi.png
itemIconStyle: BigAction
event: !type:DisguiseToggleNoRotEvent event: !type:DisguiseToggleNoRotEvent
- type: entity - type: entity
@@ -68,4 +57,5 @@
icon: icon:
sprite: Objects/Tools/wrench.rsi sprite: Objects/Tools/wrench.rsi
state: icon state: icon
itemIconStyle: BigAction
event: !type:DisguiseToggleAnchoredEvent event: !type:DisguiseToggleAnchoredEvent