Add GHOST GANG! (#13734)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
@@ -5,6 +5,7 @@ using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
using Content.Server.Physics.Components;
|
||||
using Content.Shared.Follower.Components;
|
||||
using Content.Shared.Throwing;
|
||||
|
||||
namespace Content.Server.Physics.Controllers;
|
||||
@@ -41,7 +42,8 @@ internal sealed class RandomWalkController : VirtualController
|
||||
foreach(var (randomWalk, physics) in EntityManager.EntityQuery<RandomWalkComponent, PhysicsComponent>())
|
||||
{
|
||||
if (EntityManager.HasComponent<ActorComponent>(randomWalk.Owner)
|
||||
|| EntityManager.HasComponent<ThrownItemComponent>(randomWalk.Owner))
|
||||
|| EntityManager.HasComponent<ThrownItemComponent>(randomWalk.Owner)
|
||||
|| EntityManager.HasComponent<FollowerComponent>(randomWalk.Owner))
|
||||
continue;
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
@@ -136,6 +136,7 @@ public sealed partial class RevenantSystem : EntitySystem
|
||||
|
||||
if (component.Essence <= 0)
|
||||
{
|
||||
Spawn(component.SpawnOnDeathPrototype, Transform(uid).Coordinates);
|
||||
QueueDel(uid);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace Content.Shared.Construction.Steps
|
||||
return typeof(TagConstructionGraphStep);
|
||||
}
|
||||
|
||||
if (node.Has("prototype"))
|
||||
{
|
||||
return typeof(PrototypeConstructionGraphStep);
|
||||
}
|
||||
|
||||
if (node.Has("allTags") || node.Has("anyTags"))
|
||||
{
|
||||
return typeof(MultipleTagsConstructionGraphStep);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Shared.Construction.Steps
|
||||
{
|
||||
[DataDefinition]
|
||||
public sealed class PrototypeConstructionGraphStep : ArbitraryInsertConstructionGraphStep
|
||||
{
|
||||
[DataField("prototype")]
|
||||
private string _prototype = "BaseItem";
|
||||
|
||||
[DataField("allowParents")]
|
||||
private bool _allowParents;
|
||||
|
||||
public override bool EntityValid(EntityUid uid, IEntityManager entityManager, IComponentFactory compFactory)
|
||||
{
|
||||
entityManager.TryGetComponent(uid, out MetaDataComponent? metaDataComponent);
|
||||
if (metaDataComponent?.EntityPrototype == null)
|
||||
return false;
|
||||
|
||||
if (metaDataComponent.EntityPrototype.ID == _prototype)
|
||||
return true;
|
||||
if (_allowParents && metaDataComponent.EntityPrototype.Parents != null)
|
||||
return metaDataComponent.EntityPrototype.Parents.Contains(_prototype);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,27 @@
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Follower.Components;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Physics.Pull;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
namespace Content.Shared.Follower;
|
||||
|
||||
public sealed class FollowerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly SharedJointSystem _jointSystem = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -20,6 +29,8 @@ public sealed class FollowerSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<GetVerbsEvent<AlternativeVerb>>(OnGetAlternativeVerbs);
|
||||
SubscribeLocalEvent<FollowerComponent, MoveInputEvent>(OnFollowerMove);
|
||||
SubscribeLocalEvent<FollowerComponent, PullStartedMessage>(OnPullStarted);
|
||||
SubscribeLocalEvent<FollowerComponent, GotEquippedHandEvent>(OnGotEquippedHand);
|
||||
SubscribeLocalEvent<FollowedComponent, EntityTerminatingEvent>(OnFollowedTerminating);
|
||||
SubscribeLocalEvent<BeforeSaveEvent>(OnBeforeSave);
|
||||
}
|
||||
@@ -44,25 +55,38 @@ public sealed class FollowerSystem : EntitySystem
|
||||
|
||||
private void OnGetAlternativeVerbs(GetVerbsEvent<AlternativeVerb> ev)
|
||||
{
|
||||
if (!HasComp<SharedGhostComponent>(ev.User))
|
||||
return;
|
||||
|
||||
if (ev.User == ev.Target || ev.Target.IsClientSide())
|
||||
return;
|
||||
|
||||
var verb = new AlternativeVerb
|
||||
if (HasComp<SharedGhostComponent>(ev.User))
|
||||
{
|
||||
Priority = 10,
|
||||
Act = (() =>
|
||||
var verb = new AlternativeVerb()
|
||||
{
|
||||
StartFollowingEntity(ev.User, ev.Target);
|
||||
}),
|
||||
Impact = LogImpact.Low,
|
||||
Text = Loc.GetString("verb-follow-text"),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/open.svg.192dpi.png")),
|
||||
};
|
||||
Priority = 10,
|
||||
Act = () => StartFollowingEntity(ev.User, ev.Target),
|
||||
Impact = LogImpact.Low,
|
||||
Text = Loc.GetString("verb-follow-text"),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/open.svg.192dpi.png"))
|
||||
};
|
||||
ev.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
ev.Verbs.Add(verb);
|
||||
if (_tagSystem.HasTag(ev.Target, "ForceableFollow"))
|
||||
{
|
||||
if (!ev.CanAccess || !ev.CanInteract)
|
||||
return;
|
||||
|
||||
var verb = new AlternativeVerb
|
||||
{
|
||||
Priority = 10,
|
||||
Act = () => StartFollowingEntity(ev.Target, ev.User),
|
||||
Impact = LogImpact.Low,
|
||||
Text = Loc.GetString("verb-follow-me-text"),
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/close.svg.192dpi.png")),
|
||||
};
|
||||
|
||||
ev.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFollowerMove(EntityUid uid, FollowerComponent component, ref MoveInputEvent args)
|
||||
@@ -70,6 +94,16 @@ public sealed class FollowerSystem : EntitySystem
|
||||
StopFollowingEntity(uid, component.Following);
|
||||
}
|
||||
|
||||
private void OnPullStarted(EntityUid uid, FollowerComponent component, PullStartedMessage args)
|
||||
{
|
||||
StopFollowingEntity(uid, component.Following);
|
||||
}
|
||||
|
||||
private void OnGotEquippedHand(EntityUid uid, FollowerComponent component, GotEquippedHandEvent args)
|
||||
{
|
||||
StopFollowingEntity(uid, component.Following, deparent:false);
|
||||
}
|
||||
|
||||
// Since we parent our observer to the followed entity, we need to detach
|
||||
// before they get deleted so that we don't get recursively deleted too.
|
||||
private void OnFollowedTerminating(EntityUid uid, FollowedComponent component, ref EntityTerminatingEvent args)
|
||||
@@ -102,24 +136,35 @@ public sealed class FollowerSystem : EntitySystem
|
||||
if (!followedComp.Following.Add(follower))
|
||||
return;
|
||||
|
||||
if (TryComp<JointComponent>(follower, out var joints))
|
||||
_jointSystem.ClearJoints(follower, joints);
|
||||
|
||||
_physicsSystem.SetLinearVelocity(follower, Vector2.Zero);
|
||||
|
||||
var xform = Transform(follower);
|
||||
_transform.SetCoordinates(follower, xform, new EntityCoordinates(entity, Vector2.Zero), Angle.Zero);
|
||||
_containerSystem.AttachParentToContainerOrGrid(xform);
|
||||
|
||||
// If we didn't get to parent's container.
|
||||
if (xform.ParentUid != Transform(xform.ParentUid).ParentUid)
|
||||
{
|
||||
_transform.SetCoordinates(follower, xform, new EntityCoordinates(entity, Vector2.Zero), rotation: Angle.Zero);
|
||||
}
|
||||
|
||||
EnsureComp<OrbitVisualsComponent>(follower);
|
||||
|
||||
var followerEv = new StartedFollowingEntityEvent(entity, follower);
|
||||
var entityEv = new EntityStartedFollowingEvent(entity, follower);
|
||||
|
||||
RaiseLocalEvent(follower, followerEv, true);
|
||||
RaiseLocalEvent(entity, entityEv, false);
|
||||
RaiseLocalEvent(follower, followerEv);
|
||||
RaiseLocalEvent(entity, entityEv);
|
||||
Dirty(followedComp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces an entity to stop following another entity, if it is doing so.
|
||||
/// </summary>
|
||||
public void StopFollowingEntity(EntityUid uid, EntityUid target,
|
||||
FollowedComponent? followed=null)
|
||||
/// <param name="deparent">Should the entity deparent itself</param>
|
||||
public void StopFollowingEntity(EntityUid uid, EntityUid target, FollowedComponent? followed = null, bool deparent = true)
|
||||
{
|
||||
if (!Resolve(target, ref followed, false))
|
||||
return;
|
||||
@@ -130,24 +175,27 @@ public sealed class FollowerSystem : EntitySystem
|
||||
followed.Following.Remove(uid);
|
||||
if (followed.Following.Count == 0)
|
||||
RemComp<FollowedComponent>(target);
|
||||
|
||||
RemComp<FollowerComponent>(uid);
|
||||
|
||||
var xform = Transform(uid);
|
||||
_transform.AttachToGridOrMap(uid, xform);
|
||||
if (xform.MapID == MapId.Nullspace)
|
||||
{
|
||||
QueueDel(uid);
|
||||
return;
|
||||
}
|
||||
|
||||
RemComp<OrbitVisualsComponent>(uid);
|
||||
|
||||
var uidEv = new StoppedFollowingEntityEvent(target, uid);
|
||||
var targetEv = new EntityStoppedFollowingEvent(target, uid);
|
||||
|
||||
RaiseLocalEvent(uid, uidEv, true);
|
||||
RaiseLocalEvent(target, targetEv, false);
|
||||
Dirty(followed);
|
||||
RaiseLocalEvent(uid, uidEv);
|
||||
RaiseLocalEvent(target, targetEv);
|
||||
|
||||
if (!Deleted(uid) && deparent)
|
||||
{
|
||||
var xform = Transform(uid);
|
||||
_transform.AttachToGridOrMap(uid, xform);
|
||||
if (xform.MapUid == null)
|
||||
{
|
||||
QueueDel(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Store;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Revenant.Components;
|
||||
@@ -18,6 +19,12 @@ public sealed class RevenantComponent : Component
|
||||
[DataField("stolenEssenceCurrencyPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<CurrencyPrototype>))]
|
||||
public string StolenEssenceCurrencyPrototype = "StolenEssence";
|
||||
|
||||
/// <summary>
|
||||
/// Prototype to spawn when the entity dies.
|
||||
/// </summary>
|
||||
[DataField("spawnOnDeathPrototype", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string SpawnOnDeathPrototype = "Ectoplasm";
|
||||
|
||||
/// <summary>
|
||||
/// The entity's current max amount of essence. Can be increased
|
||||
/// through harvesting player souls.
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
verb-follow-text = Follow
|
||||
verb-follow-me-text = Make follow
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
- type: RandomSpawner
|
||||
rarePrototypes:
|
||||
- FoamBlade
|
||||
- PlushieGhost
|
||||
rareChance: 0.03
|
||||
prototypes:
|
||||
- PlushieBee
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
orGroup: GiftPool
|
||||
- id: JetpackMiniFilled
|
||||
orGroup: GiftPool
|
||||
- id: PlushieGhost
|
||||
orGroup: GiftPool
|
||||
- id: PlushieBee
|
||||
orGroup: GiftPool
|
||||
- id: PlushieRGBee
|
||||
|
||||
@@ -35,6 +35,59 @@
|
||||
- type: StaticPrice
|
||||
price: 5
|
||||
|
||||
- type: entity
|
||||
parent: BasePlushie
|
||||
id: PlushieGhost
|
||||
name: ghost soft toy
|
||||
description: The start of your personal GHOST GANG!
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Mobs/Ghosts/ghost_human.rsi
|
||||
state: icon
|
||||
noRot: true
|
||||
- type: Item
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeAabb
|
||||
bounds: "-0.25,-0.25,0.25,0.25"
|
||||
density: 20
|
||||
mask:
|
||||
- ItemMask
|
||||
restitution: 0.98
|
||||
friction: 0.01
|
||||
- type: Physics
|
||||
angularDamping: 0.02
|
||||
linearDamping: 0.02
|
||||
fixedRotation: true
|
||||
bodyType: Dynamic
|
||||
- type: TileFrictionModifier
|
||||
modifier: 0.1
|
||||
- type: Tag
|
||||
tags:
|
||||
- ForceableFollow
|
||||
- type: RandomWalk
|
||||
accumulatorRatio: 0.5
|
||||
maxSpeed: 1
|
||||
minSpeed: 0.25
|
||||
|
||||
- type: entity
|
||||
parent: PlushieGhost
|
||||
id: PlushieGhostRevenant
|
||||
name: revenant soft toy
|
||||
suffix: DO NOT MAP
|
||||
description: So soft it almost makes you want to take a nap...
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Mobs/Ghosts/revenant.rsi
|
||||
state: icon
|
||||
netsync: false
|
||||
noRot: true
|
||||
- type: Construction
|
||||
graph: PlushieGhostRevenant
|
||||
node: plushie
|
||||
|
||||
- type: entity
|
||||
parent: BasePlushie
|
||||
id: PlushieBee
|
||||
|
||||
@@ -123,3 +123,26 @@
|
||||
- type: DeleteOnTrigger
|
||||
- type: Extractable
|
||||
grindableSolutionName: food
|
||||
|
||||
- type: entity
|
||||
parent: Ash
|
||||
id: Ectoplasm
|
||||
name: ectoplasm
|
||||
description: Much less deadly in this form.
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Mobs/Ghosts/revenant.rsi
|
||||
state: ectoplasm
|
||||
- type: Tag
|
||||
tags:
|
||||
- Trash
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 50
|
||||
reagents:
|
||||
- ReagentId: Ash
|
||||
Quantity: 5
|
||||
- ReagentId: SpaceLube
|
||||
Quantity: 5
|
||||
|
||||
15
Resources/Prototypes/Recipes/Crafting/Graphs/toys.yml
Normal file
15
Resources/Prototypes/Recipes/Crafting/Graphs/toys.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
- type: constructionGraph
|
||||
id: PlushieGhostRevenant
|
||||
start: start
|
||||
graph:
|
||||
- node: start
|
||||
edges:
|
||||
- to: plushie
|
||||
steps:
|
||||
- prototype: PlushieGhost
|
||||
name: a ghost plushie
|
||||
- prototype: Ectoplasm
|
||||
name: ectoplasm
|
||||
doAfter: 10
|
||||
- node: plushie
|
||||
entity: PlushieGhostRevenant
|
||||
12
Resources/Prototypes/Recipes/Crafting/toys.yml
Normal file
12
Resources/Prototypes/Recipes/Crafting/toys.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
- type: construction
|
||||
name: revenant plushie
|
||||
id: PlushieGhostRevenant
|
||||
graph: PlushieGhostRevenant
|
||||
startNode: start
|
||||
targetNode: plushie
|
||||
category: construction-category-misc
|
||||
objectType: Item
|
||||
description: A toy to scare the medbay with.
|
||||
icon:
|
||||
sprite: Mobs/Ghosts/revenant.rsi
|
||||
state: icon
|
||||
@@ -284,6 +284,9 @@
|
||||
- type: Tag
|
||||
id: FootstepSound
|
||||
|
||||
- type: Tag
|
||||
id: ForceableFollow
|
||||
|
||||
- type: Tag
|
||||
id: ForceFixRotations # fixrotations command WILL target this
|
||||
|
||||
|
||||
Reference in New Issue
Block a user