Ghost orbiting (#6784)

This commit is contained in:
mirrorcult
2022-02-19 12:16:44 -07:00
committed by GitHub
parent a45d69e4e4
commit 162af7add5
5 changed files with 267 additions and 1 deletions

View File

@@ -0,0 +1,151 @@
using Content.Shared.Follower;
using Content.Shared.Follower.Components;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
using Robust.Shared.Random;
namespace Content.Client.Orbit;
public sealed class OrbitVisualsSystem : VisualizerSystem<OrbitVisualsComponent>
{
[Dependency] private readonly IRobustRandom _robustRandom = default!;
private readonly string _orbitAnimationKey = "orbiting";
private readonly string _orbitStopKey = "orbiting_stop";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<OrbitVisualsComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<OrbitVisualsComponent, AnimationCompletedEvent>(OnAnimationCompleted);
}
private void OnComponentInit(EntityUid uid, OrbitVisualsComponent component, ComponentInit args)
{
component.OrbitDistance =
_robustRandom.NextFloat(0.75f * component.OrbitDistance, 1.25f * component.OrbitDistance);
component.OrbitLength = _robustRandom.NextFloat(0.5f * component.OrbitLength, 1.5f * component.OrbitLength);
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
foreach (var (orbit, sprite) in EntityManager.EntityQuery<OrbitVisualsComponent, ISpriteComponent>())
{
var angle = new Angle(Math.PI * 2 * orbit.Orbit);
var vec = angle.RotateVec(new Vector2(orbit.OrbitDistance, 0));
sprite.Rotation = angle;
sprite.Offset = vec;
}
}
protected override void OnAppearanceChange(EntityUid uid, OrbitVisualsComponent component, ref AppearanceChangeEvent args)
{
if (!args.Component.TryGetData<bool>(OrbitingVisuals.IsOrbiting, out var orbiting))
return;
if (!TryComp<ISpriteComponent>(uid, out var sprite))
return;
var animationPlayer = EntityManager.EnsureComponent<AnimationPlayerComponent>(uid);
if (orbiting)
{
if (animationPlayer.HasRunningAnimation(_orbitAnimationKey))
return;
if (animationPlayer.HasRunningAnimation(_orbitStopKey))
{
animationPlayer.Stop(_orbitStopKey);
}
animationPlayer.Play(GetOrbitAnimation(component), _orbitAnimationKey);
}
else
{
RemComp<OrbitVisualsComponent>(uid);
if (animationPlayer.HasRunningAnimation(_orbitAnimationKey))
{
animationPlayer.Stop(_orbitAnimationKey);
}
if (!animationPlayer.HasRunningAnimation(_orbitStopKey))
{
animationPlayer.Play(GetStopAnimation(component, sprite), _orbitStopKey);
}
}
}
private void OnAnimationCompleted(EntityUid uid, OrbitVisualsComponent component, AnimationCompletedEvent args)
{
if (args.Key == _orbitAnimationKey)
{
if(EntityManager.TryGetComponent(uid, out AnimationPlayerComponent? animationPlayer))
animationPlayer.Play(GetOrbitAnimation(component), _orbitAnimationKey);
}
}
private Animation GetOrbitAnimation(OrbitVisualsComponent component)
{
var length = component.OrbitLength;
return new Animation()
{
Length = TimeSpan.FromSeconds(length),
AnimationTracks =
{
new AnimationTrackComponentProperty()
{
ComponentType = typeof(OrbitVisualsComponent),
Property = nameof(OrbitVisualsComponent.Orbit),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(0.0f, 0f),
new AnimationTrackProperty.KeyFrame(1.0f, length),
},
InterpolationMode = AnimationInterpolationMode.Linear
}
}
};
}
private Animation GetStopAnimation(OrbitVisualsComponent component, ISpriteComponent sprite)
{
var length = component.OrbitStopLength;
return new Animation()
{
Length = TimeSpan.FromSeconds(length),
AnimationTracks =
{
new AnimationTrackComponentProperty()
{
ComponentType = typeof(ISpriteComponent),
Property = nameof(ISpriteComponent.Offset),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(sprite.Offset, 0f),
new AnimationTrackProperty.KeyFrame(Vector2.Zero, length),
},
InterpolationMode = AnimationInterpolationMode.Linear
},
new AnimationTrackComponentProperty()
{
ComponentType = typeof(ISpriteComponent),
Property = nameof(ISpriteComponent.Rotation),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(sprite.Rotation.Reduced(), 0f),
new AnimationTrackProperty.KeyFrame(Angle.Zero, length),
},
InterpolationMode = AnimationInterpolationMode.Linear
}
}
};
}
}

View File

@@ -16,7 +16,7 @@ namespace Content.Server.Entry
"Icon", "Icon",
"ClientEntitySpawner", "ClientEntitySpawner",
"CharacterInfo", "CharacterInfo",
"ItemCabinetVisuals" "ItemCabinetVisuals",
}; };
} }
} }

View File

@@ -0,0 +1,28 @@
using Robust.Shared.Animations;
namespace Content.Shared.Follower.Components;
[RegisterComponent]
public sealed class OrbitVisualsComponent : Component
{
/// <summary>
/// How long should the orbit animation last in seconds, before being randomized?
/// </summary>
public float OrbitLength = 2.0f;
/// <summary>
/// How far away from the entity should the orbit be, before being randomized?
/// </summary>
public float OrbitDistance = 1.0f;
/// <summary>
/// How long should the orbit stop animation last in seconds?
/// </summary>
public float OrbitStopLength = 1.0f;
/// <summary>
/// How far along in the orbit, from 0 to 1, is this entity?
/// </summary>
[Animatable]
public float Orbit { get; set; } = 0.0f;
}

View File

@@ -6,6 +6,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Content.Shared.Follower.Components; using Content.Shared.Follower.Components;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Serialization;
namespace Content.Shared.Follower; namespace Content.Shared.Follower;
@@ -71,6 +72,18 @@ public sealed class FollowerSystem : EntitySystem
var xform = Transform(follower); var xform = Transform(follower);
xform.AttachParent(entity); xform.AttachParent(entity);
xform.LocalPosition = Vector2.Zero; xform.LocalPosition = Vector2.Zero;
if (TryComp<AppearanceComponent>(follower, out var appearance))
{
EnsureComp<OrbitVisualsComponent>(follower);
appearance.SetData(OrbitingVisuals.IsOrbiting, true);
}
var followerEv = new StartedFollowingEntityEvent(entity, follower);
var entityEv = new EntityStartedFollowingEvent(entity, follower);
RaiseLocalEvent(follower, followerEv);
RaiseLocalEvent(entity, entityEv, false);
} }
/// <summary> /// <summary>
@@ -89,7 +102,21 @@ public sealed class FollowerSystem : EntitySystem
if (followed.Following.Count == 0) if (followed.Following.Count == 0)
RemComp<FollowedComponent>(target); RemComp<FollowedComponent>(target);
RemComp<FollowerComponent>(uid); RemComp<FollowerComponent>(uid);
Transform(uid).AttachToGridOrMap(); Transform(uid).AttachToGridOrMap();
if (TryComp<AppearanceComponent>(uid, out var appearance))
{
// We don't remove OrbitVisuals here since the OrbitVisualsSystem will handle that itself
// during the OnChangeData, which is deferred..
appearance.SetData(OrbitingVisuals.IsOrbiting, false);
}
var uidEv = new StoppedFollowingEntityEvent(target, uid);
var targetEv = new EntityStoppedFollowingEvent(target, uid);
RaiseLocalEvent(uid, uidEv);
RaiseLocalEvent(target, targetEv, false);
} }
/// <summary> /// <summary>
@@ -107,3 +134,62 @@ public sealed class FollowerSystem : EntitySystem
} }
} }
} }
public abstract class FollowEvent : EntityEventArgs
{
public EntityUid Following;
public EntityUid Follower;
protected FollowEvent(EntityUid following, EntityUid follower)
{
Following = following;
Follower = follower;
}
}
/// <summary>
/// Raised on an entity when it start following another entity.
/// </summary>
public sealed class StartedFollowingEntityEvent : FollowEvent
{
public StartedFollowingEntityEvent(EntityUid following, EntityUid follower) : base(following, follower)
{
}
}
/// <summary>
/// Raised on an entity when it stops following another entity.
/// </summary>
public sealed class StoppedFollowingEntityEvent : FollowEvent
{
public StoppedFollowingEntityEvent(EntityUid following, EntityUid follower) : base(following, follower)
{
}
}
/// <summary>
/// Raised on an entity when it start following another entity.
/// </summary>
public sealed class EntityStartedFollowingEvent : FollowEvent
{
public EntityStartedFollowingEvent(EntityUid following, EntityUid follower) : base(following, follower)
{
}
}
/// <summary>
/// Raised on an entity when it starts being followed by another entity.
/// </summary>
public sealed class EntityStoppedFollowingEvent : FollowEvent
{
public EntityStoppedFollowingEvent(EntityUid following, EntityUid follower) : base(following, follower)
{
}
}
[Serializable, NetSerializable]
public enum OrbitingVisuals : byte
{
IsOrbiting
}

View File

@@ -21,6 +21,7 @@
mask: mask:
- GhostImpassable - GhostImpassable
- type: PlayerInputMover - type: PlayerInputMover
- type: Appearance
- type: Eye - type: Eye
drawFov: false drawFov: false
- type: Input - type: Input