Familiars respawn (#7640)

This commit is contained in:
Rane
2022-04-23 22:27:19 -04:00
committed by GitHub
parent a74e6842d9
commit 6054c5bc53
16 changed files with 129 additions and 10 deletions

View File

@@ -16,6 +16,7 @@ namespace Content.Server.Atmos.Components
/// Accumulates time when yeeted by high pressure deltas. /// Accumulates time when yeeted by high pressure deltas.
/// </summary> /// </summary>
[ViewVariables] [ViewVariables]
[DataField("accumulator")]
public float Accumulator = 0f; public float Accumulator = 0f;
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]

View File

@@ -1,9 +1,9 @@
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.MobState.Components; using Content.Shared.MobState.Components;
using Content.Shared.MobState;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Content.Shared.Tag;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Server.Cooldown; using Content.Server.Cooldown;
@@ -23,7 +23,6 @@ namespace Content.Server.Bible
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly ActionBlockerSystem _blocker = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
@@ -35,6 +34,51 @@ namespace Content.Server.Bible
SubscribeLocalEvent<SummonableComponent, GetVerbsEvent<AlternativeVerb>>(AddSummonVerb); SubscribeLocalEvent<SummonableComponent, GetVerbsEvent<AlternativeVerb>>(AddSummonVerb);
SubscribeLocalEvent<SummonableComponent, GetItemActionsEvent>(GetSummonAction); SubscribeLocalEvent<SummonableComponent, GetItemActionsEvent>(GetSummonAction);
SubscribeLocalEvent<SummonableComponent, SummonActionEvent>(OnSummon); SubscribeLocalEvent<SummonableComponent, SummonActionEvent>(OnSummon);
SubscribeLocalEvent<FamiliarComponent, MobStateChangedEvent>(OnFamiliarDeath);
}
private Queue<EntityUid> AddQueue = new();
private Queue<EntityUid> RemQueue = new();
/// <summary>
/// This handles familiar respawning.
/// </summary>
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach(var entity in AddQueue)
{
EnsureComp<SummonableRespawningComponent>(entity);
}
AddQueue.Clear();
foreach(var entity in RemQueue)
{
RemComp<SummonableRespawningComponent>(entity);
}
RemQueue.Clear();
foreach (var (respawning, summonableComp) in EntityQuery<SummonableRespawningComponent, SummonableComponent>())
{
summonableComp.Accumulator += frameTime;
if (summonableComp.Accumulator < summonableComp.RespawnTime)
{
continue;
}
/// Clean up the old body
if (summonableComp.Summon != null)
{
EntityManager.DeleteEntity(summonableComp.Summon.Value);
summonableComp.Summon = null;
}
summonableComp.AlreadySummoned = false;
_popupSystem.PopupEntity(Loc.GetString("bible-summon-respawn-ready", ("book", summonableComp.Owner)), summonableComp.Owner, Filter.Pvs(summonableComp.Owner));
SoundSystem.Play(Filter.Pvs(summonableComp.Owner), "/Audio/Effects/radpulse9.ogg", summonableComp.Owner, AudioParams.Default.WithVolume(-4f));
/// Clean up the accumulator and respawn tracking component
summonableComp.Accumulator = 0;
RemQueue.Enqueue(respawning.Owner);
}
} }
private void OnAfterInteract(EntityUid uid, BibleComponent component, AfterInteractEvent args) private void OnAfterInteract(EntityUid uid, BibleComponent component, AfterInteractEvent args)
@@ -67,7 +111,8 @@ namespace Content.Server.Bible
return; return;
} }
if (!_invSystem.TryGetSlotEntity(args.Target.Value, "head", out var entityUid) && !_tagSystem.HasTag(args.Target.Value, "Familiar")) // This only has a chance to fail if the target is not wearing anything on their head and is not a familiar.
if (!_invSystem.TryGetSlotEntity(args.Target.Value, "head", out var entityUid) && !HasComp<FamiliarComponent>(args.Target.Value))
{ {
if (_random.Prob(component.FailChance)) if (_random.Prob(component.FailChance))
{ {
@@ -125,6 +170,24 @@ namespace Content.Server.Bible
{ {
AttemptSummon(component, args.Performer, Transform(args.Performer)); AttemptSummon(component, args.Performer, Transform(args.Performer));
} }
/// <summary>
/// Starts up the respawn stuff when
/// the chaplain's familiar dies.
/// </summary>
private void OnFamiliarDeath(EntityUid uid, FamiliarComponent component, MobStateChangedEvent args)
{
if (!args.Component.IsDead() || component.Source == null)
return;
var source = component.Source;
if (source != null && TryComp<SummonableComponent>(source, out var summonable))
{
AddQueue.Enqueue(summonable.Owner);
}
}
private void AttemptSummon(SummonableComponent component, EntityUid user, TransformComponent? position) private void AttemptSummon(SummonableComponent component, EntityUid user, TransformComponent? position)
{ {
if (component.AlreadySummoned || component.SpecialItemPrototype == null) if (component.AlreadySummoned || component.SpecialItemPrototype == null)
@@ -138,7 +201,17 @@ namespace Content.Server.Bible
if (!_blocker.CanInteract(user, component.Owner)) if (!_blocker.CanInteract(user, component.Owner))
return; return;
EntityManager.SpawnEntity(component.SpecialItemPrototype, position.Coordinates); // Make this familiar the component's summon
var familiar = EntityManager.SpawnEntity(component.SpecialItemPrototype, position.Coordinates);
component.Summon = familiar;
/// We only want to add the familiar component to mobs
if (HasComp<MobStateComponent>(familiar))
{
/// Make this Summon the familiar's source
var familiarComp = EnsureComp<FamiliarComponent>(familiar);
familiarComp.Source = component.Owner;
}
component.AlreadySummoned = true; component.AlreadySummoned = true;
_actionsSystem.RemoveAction(user, component.SummonAction); _actionsSystem.RemoveAction(user, component.SummonAction);
} }

View File

@@ -0,0 +1,17 @@
namespace Content.Server.Bible.Components
{
/// <summary>
/// This component is for the chaplain's familiars, and mostly
/// used to track their current state and to give a component to check for
/// if any special behavior is needed.
/// </summary>
[RegisterComponent]
public sealed class FamiliarComponent : Component
{
/// <summary>
/// The entity this familiar was summoned from.
/// </summary>
[ViewVariables]
public EntityUid? Source = null;
}
}

View File

@@ -1,7 +1,6 @@
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Content.Shared.Actions.ActionTypes; using Content.Shared.Actions.ActionTypes;
using Content.Server.Bible;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Bible.Components namespace Content.Server.Bible.Components
@@ -22,6 +21,12 @@ namespace Content.Server.Bible.Components
[DataField("requriesBibleUser")] [DataField("requriesBibleUser")]
public bool RequiresBibleUser = true; public bool RequiresBibleUser = true;
/// <summary>
/// The specific creature this summoned, if the SpecialItemPrototype has a mobstate.
/// </summary>
[ViewVariables]
public EntityUid? Summon = null;
[DataField("summonAction")] [DataField("summonAction")]
public InstantAction SummonAction = new() public InstantAction SummonAction = new()
{ {
@@ -30,5 +35,13 @@ namespace Content.Server.Bible.Components
Description = "bible-summon-verb-desc", Description = "bible-summon-verb-desc",
Event = new SummonActionEvent(), Event = new SummonActionEvent(),
}; };
/// Used for respawning
[ViewVariables]
[DataField("accumulator")]
public float Accumulator = 0f;
[ViewVariables]
[DataField("respawnTime")]
public float RespawnTime = 180f;
} }
} }

View File

@@ -0,0 +1,9 @@
namespace Content.Server.Bible.Components
{
/// <summary>
/// EntityQuery Tracking Component for Summonables that are counting up a respawn.
/// </summary>
[RegisterComponent]
public sealed class SummonableRespawningComponent : Component
{}
}

View File

@@ -19,6 +19,7 @@ namespace Content.Server.Cargo.Components
/// How much time we've accumulated until next teleport. /// How much time we've accumulated until next teleport.
/// </summary> /// </summary>
[ViewVariables] [ViewVariables]
[DataField("accumulator")]
public float Accumulator = 0f; public float Accumulator = 0f;
[ViewVariables] [ViewVariables]

View File

@@ -16,6 +16,7 @@ namespace Content.Server.Disease.Components
/// How much time we've accumulated processing /// How much time we've accumulated processing
/// </summary> /// </summary>
[ViewVariables] [ViewVariables]
[DataField("accumulator")]
public float Accumulator = 0f; public float Accumulator = 0f;
/// <summary> /// <summary>
/// The disease prototype currently being diagnosed /// The disease prototype currently being diagnosed

View File

@@ -47,6 +47,7 @@ namespace Content.Server.Explosion.Components
/// <summary> /// <summary>
/// How much cooldown has elapsed (if relevant). /// How much cooldown has elapsed (if relevant).
/// </summary> /// </summary>
[DataField("accumulator")]
public float Accumulator = 0f; public float Accumulator = 0f;
/// <summary> /// <summary>

View File

@@ -42,6 +42,7 @@ namespace Content.Server.Fluids.Components
/// <summary> /// <summary>
/// The time accumulated since the start. /// The time accumulated since the start.
/// </summary> /// </summary>
[DataField("accumulator")]
public float Accumulator = 0f; public float Accumulator = 0f;
} }
} }

View File

@@ -32,11 +32,13 @@ namespace Content.Server.Lathe.Components
/// <summary> /// <summary>
/// Update accumulator for the insertion time /// Update accumulator for the insertion time
/// </suummary> /// </suummary>
[DataField("insertionAccumulator")]
public float InsertionAccumulator = 0f; public float InsertionAccumulator = 0f;
/// <summary> /// <summary>
/// Production accumulator for the production time. /// Production accumulator for the production time.
/// </summary> /// </summary>
[ViewVariables] [ViewVariables]
[DataField("producingAccumulator")]
public float ProducingAccumulator = 0f; public float ProducingAccumulator = 0f;
/// <summary> /// <summary>

View File

@@ -60,6 +60,7 @@ namespace Content.Server.Singularity.Components
_ => 0 _ => 0
}; };
[DataField("moveAccumulator")]
public float MoveAccumulator; public float MoveAccumulator;
// This is an interesting little workaround. // This is an interesting little workaround.

View File

@@ -11,6 +11,8 @@ namespace Content.Shared.Emag.Components
[DataField("rechargeTime"), ViewVariables(VVAccess.ReadWrite)] [DataField("rechargeTime"), ViewVariables(VVAccess.ReadWrite)]
public float RechargeTime = 90f; public float RechargeTime = 90f;
[DataField("accumulator")]
public float Accumulator = 0f; public float Accumulator = 0f;
} }
} }

View File

@@ -5,6 +5,7 @@ bible-heal-fail-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE(
bible-sizzle = The book sizzles in your hands! bible-sizzle = The book sizzles in your hands!
bible-summon-verb = Summon familiar bible-summon-verb = Summon familiar
bible-summon-verb-desc = Summon a familiar that will aid you and gain humanlike intelligence once inhabited by a soul. bible-summon-verb-desc = Summon a familiar that will aid you and gain humanlike intelligence once inhabited by a soul.
bible-summon-respawn-ready = {CAPITALIZE(THE($book))} surges with ethereal power. {CAPITALIZE(POSS-ADJ($book))} resident is home again.
necro-heal-success-self = You hit {THE($target)} with {THE($bible)}, and {POSS-ADJ($target)} flesh warps as it melts! necro-heal-success-self = You hit {THE($target)} with {THE($bible)}, and {POSS-ADJ($target)} flesh warps as it melts!
necro-heal-success-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and {POSS-ADJ($target)} flesh warps as it melts! necro-heal-success-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and {POSS-ADJ($target)} flesh warps as it melts!
necro-heal-fail-self = You hit {THE($target)} with {THE($bible)}, and it lands with a sad thwack, failing to smite {OBJECT($target)}. necro-heal-fail-self = You hit {THE($target)} with {THE($bible)}, and it lands with a sad thwack, failing to smite {OBJECT($target)}.

View File

@@ -15,7 +15,6 @@
proper: true proper: true
- type: Tag - type: Tag
tags: tags:
- Familiar
- DoorBumpOpener - DoorBumpOpener
- type: Access - type: Access
tags: tags:
@@ -64,7 +63,6 @@
proper: true proper: true
- type: Tag - type: Tag
tags: tags:
- Familiar
- DoorBumpOpener - DoorBumpOpener
- type: Access - type: Access
tags: tags:

View File

@@ -57,6 +57,7 @@
cooldownTime: 1.3 cooldownTime: 1.3
- type: Summonable - type: Summonable
specialItem: MobCorgiCerberus specialItem: MobCorgiCerberus
respawnTime: 300
- type: Sprite - type: Sprite
netsync: false netsync: false
sprite: Objects/Specific/Chapel/necronomicon.rsi sprite: Objects/Specific/Chapel/necronomicon.rsi

View File

@@ -144,9 +144,6 @@
- type: Tag - type: Tag
id: ExplosivePassable id: ExplosivePassable
- type: Tag
id: Familiar
- type: Tag - type: Tag
id: FireAlarmElectronics id: FireAlarmElectronics