51
Content.Server/Devour/DevourSystem.cs
Normal file
51
Content.Server/Devour/DevourSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Content.Shared.Devour;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Server.Devour.Components;
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Devour;
|
||||
|
||||
public sealed class DevourSystem : SharedDevourSystem
|
||||
{
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DevourerComponent, DevourDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, DevourerComponent component, DevourDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
var ichorInjection = new Solution(component.Chemical, component.HealRate);
|
||||
|
||||
if (component.FoodPreference == FoodPreference.All ||
|
||||
(component.FoodPreference == FoodPreference.Humanoid && HasComp<HumanoidAppearanceComponent>(args.Args.Target)))
|
||||
{
|
||||
ichorInjection.ScaleSolution(0.5f);
|
||||
|
||||
if (component.ShouldStoreDevoured && args.Args.Target is not null)
|
||||
{
|
||||
component.Stomach.Insert(args.Args.Target.Value);
|
||||
}
|
||||
_bloodstreamSystem.TryAddToChemicals(uid, ichorInjection);
|
||||
}
|
||||
|
||||
//TODO: Figure out a better way of removing structures via devour that still entails standing still and waiting for a DoAfter. Somehow.
|
||||
//If it's not human, it must be a structure
|
||||
else if (args.Args.Target != null)
|
||||
{
|
||||
QueueDel(args.Args.Target.Value);
|
||||
}
|
||||
|
||||
_audioSystem.PlayPvs(component.SoundDevour, uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,6 @@ namespace Content.Server.Dragon
|
||||
[RegisterComponent]
|
||||
public sealed class DragonComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The chemical ID injected upon devouring
|
||||
/// </summary>
|
||||
[DataField("devourChemical", customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>))]
|
||||
public string DevourChem = "Ichor";
|
||||
|
||||
/// <summary>
|
||||
/// The amount of ichor injected per devour
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("devourHealRate")]
|
||||
public float DevourHealRate = 15f;
|
||||
|
||||
[DataField("devourActionId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityTargetActionPrototype>))]
|
||||
public string DevourActionId = "DragonDevour";
|
||||
|
||||
[DataField("devourAction")]
|
||||
public EntityTargetAction? DevourAction;
|
||||
|
||||
/// <summary>
|
||||
/// If we have active rifts.
|
||||
@@ -68,58 +51,15 @@ namespace Content.Server.Dragon
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("riftPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string RiftPrototype = "CarpRift";
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time it takes to devour something
|
||||
/// <remarks>
|
||||
/// NOTE: original intended design was to increase this proportionally with damage thresholds, but those proved quite difficult to get consistently. right now it devours the structure at a fixed timer.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[DataField("structureDevourTime")]
|
||||
public float StructureDevourTime = 10f;
|
||||
|
||||
[DataField("devourTime")]
|
||||
public float DevourTime = 3f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("soundDeath")]
|
||||
public SoundSpecifier? SoundDeath = new SoundPathSpecifier("/Audio/Animals/space_dragon_roar.ogg");
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("soundDevour")]
|
||||
public SoundSpecifier? SoundDevour = new SoundPathSpecifier("/Audio/Effects/demon_consume.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(-3f),
|
||||
};
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("soundStructureDevour")]
|
||||
public SoundSpecifier? SoundStructureDevour = new SoundPathSpecifier("/Audio/Machines/airlock_creaking.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(-3f),
|
||||
};
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("soundRoar")]
|
||||
public SoundSpecifier? SoundRoar =
|
||||
new SoundPathSpecifier("/Audio/Animals/space_dragon_roar.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(3f),
|
||||
};
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("devourWhitelist")]
|
||||
public EntityWhitelist? DevourWhitelist = new()
|
||||
{
|
||||
Components = new[]
|
||||
{
|
||||
"Door",
|
||||
"MobState",
|
||||
},
|
||||
Tags = new List<string>
|
||||
{
|
||||
"Wall",
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Where the entities go when dragon devours them, empties when the dragon is butchered.
|
||||
/// </summary>
|
||||
public Container DragonStomach = default!;
|
||||
}
|
||||
|
||||
public sealed class DragonDevourActionEvent : EntityTargetActionEvent {}
|
||||
|
||||
@@ -59,12 +59,9 @@ namespace Content.Server.Dragon
|
||||
|
||||
SubscribeLocalEvent<DragonComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<DragonComponent, DragonDevourActionEvent>(OnDevourAction);
|
||||
SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
|
||||
SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
|
||||
|
||||
SubscribeLocalEvent<DragonComponent, DragonDevourDoAfterEvent>(OnDoAfter);
|
||||
|
||||
SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
|
||||
SubscribeLocalEvent<DragonRiftComponent, ComponentShutdown>(OnRiftShutdown);
|
||||
@@ -75,29 +72,6 @@ namespace Content.Server.Dragon
|
||||
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRiftRoundEnd);
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, DragonComponent component, DragonDevourDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate);
|
||||
|
||||
//Humanoid devours allow dragon to get eggs, corpses included
|
||||
if (HasComp<HumanoidAppearanceComponent>(args.Args.Target))
|
||||
{
|
||||
ichorInjection.ScaleSolution(0.5f);
|
||||
component.DragonStomach.Insert(args.Args.Target.Value);
|
||||
_bloodstreamSystem.TryAddToChemicals(uid, ichorInjection);
|
||||
}
|
||||
|
||||
//TODO: Figure out a better way of removing structures via devour that still entails standing still and waiting for a DoAfter. Somehow.
|
||||
//If it's not human, it must be a structure
|
||||
else if (args.Args.Target != null)
|
||||
EntityManager.QueueDeleteEntity(args.Args.Target.Value);
|
||||
|
||||
_audioSystem.PlayPvs(component.SoundDevour, uid);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -303,8 +277,6 @@ namespace Content.Server.Dragon
|
||||
if (component.SoundDeath != null)
|
||||
_audioSystem.PlayPvs(component.SoundDeath, uid, component.SoundDeath.Params);
|
||||
|
||||
component.DragonStomach.EmptyContainer();
|
||||
|
||||
foreach (var rift in component.Rifts)
|
||||
{
|
||||
QueueDel(rift);
|
||||
@@ -322,62 +294,10 @@ namespace Content.Server.Dragon
|
||||
|
||||
private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args)
|
||||
{
|
||||
//Dragon doesn't actually chew, since he sends targets right into his stomach.
|
||||
//I did it mom, I added ERP content into upstream. Legally!
|
||||
component.DragonStomach = _containerSystem.EnsureContainer<Container>(uid, "dragon_stomach");
|
||||
|
||||
if (component.DevourAction != null)
|
||||
_actionsSystem.AddAction(uid, component.DevourAction, null);
|
||||
|
||||
if (component.SpawnRiftAction != null)
|
||||
_actionsSystem.AddAction(uid, component.SpawnRiftAction, null);
|
||||
|
||||
Roar(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The devour action
|
||||
/// </summary>
|
||||
private void OnDevourAction(EntityUid uid, DragonComponent component, DragonDevourActionEvent args)
|
||||
{
|
||||
if (args.Handled || component.DevourWhitelist?.IsValid(args.Target, EntityManager) != true)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
var target = args.Target;
|
||||
|
||||
// Structure and mob devours handled differently.
|
||||
if (EntityManager.TryGetComponent(target, out MobStateComponent? targetState))
|
||||
{
|
||||
switch (targetState.CurrentState)
|
||||
{
|
||||
case MobState.Critical:
|
||||
case MobState.Dead:
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.DevourTime, new DragonDevourDoAfterEvent(), uid, target: target, used: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
_popupSystem.PopupEntity(Loc.GetString("devour-action-popup-message-fail-target-alive"), uid, uid);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("devour-action-popup-message-structure"), uid, uid);
|
||||
|
||||
if (component.SoundStructureDevour != null)
|
||||
_audioSystem.PlayPvs(component.SoundStructureDevour, uid, component.SoundStructureDevour.Params);
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(uid, component.StructureDevourTime, new DragonDevourDoAfterEvent(), uid, target: target, used: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user