Zombies!!! (#7509)
* wip * heal on bite * more fixes and additions * don't crash * Update medicine.yml * zombie claw item and damage resist * ignoredcomponents.cs * Add zombie claw, fix infection, add immunities * fix * razzle dazzle * yaml fix * Update Content.Server/Disease/DiseaseZombieSystem.cs Co-authored-by: Moony <moonheart08@users.noreply.github.com> * Update Content.Server/Disease/DiseaseZombieSystem.cs Co-authored-by: Moony <moonheart08@users.noreply.github.com> * Update Content.Server/Disease/DiseaseZombieSystem.cs Co-authored-by: Moony <moonheart08@users.noreply.github.com> * Update Content.Server/Disease/DiseaseZombieSystem.cs Co-authored-by: Moony <moonheart08@users.noreply.github.com> * sdasadsadsadasd * Generalize DiseaseProgression.cs * final final final final final final cope seethe * Update medicine.yml * Update Content.Server/Disease/Components/DiseaseZombieComponent.cs Co-authored-by: mirrorcult <lunarautomaton6@gmail.com> * Update BloodstreamSystem.cs * Update Content.Server/Disease/Components/DiseaseZombieComponent.cs Co-authored-by: mirrorcult <lunarautomaton6@gmail.com> * Update Content.Server/Disease/DiseaseZombieSystem.cs Co-authored-by: mirrorcult <lunarautomaton6@gmail.com> * fixing until i die * folder + zombietransfer fix * smol fixe * the smallest of fixes * aaaa * Infection timer buff Co-authored-by: Moony <moonheart08@users.noreply.github.com> Co-authored-by: mirrorcult <lunarautomaton6@gmail.com>
This commit is contained in:
@@ -226,6 +226,14 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
return (component.BloodSolution.CurrentVolume / component.BloodSolution.MaxVolume).Float();
|
||||
}
|
||||
|
||||
public void SetBloodLossThreshold(EntityUid uid, float threshold, BloodstreamComponent? comp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref comp))
|
||||
return;
|
||||
|
||||
comp.BloodlossThreshold = threshold;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to modify the blood level of this entity directly.
|
||||
/// </summary>
|
||||
|
||||
19
Content.Server/Disease/Components/DiseaseBuildupComponent.cs
Normal file
19
Content.Server/Disease/Components/DiseaseBuildupComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Content.Server.Disease.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// The component which records the buildup/progression of an infection
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class DiseaseBuildupComponent : Component
|
||||
{
|
||||
/// This could be served to be generalized to allow for multiple
|
||||
/// diseases to build up at once, but it doesn't matter too much.
|
||||
|
||||
/// <summary>
|
||||
/// The current amount of progression that has built up.
|
||||
/// </summary>
|
||||
[DataField("progression")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Progression = 0.00f;
|
||||
}
|
||||
}
|
||||
@@ -94,8 +94,10 @@ namespace Content.Server.Disease
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var disease in carrierComp.Diseases)
|
||||
for (var i = 0; i < carrierComp.Diseases.Count; i++) //this is a for-loop so that it doesn't break when new diseases are added
|
||||
{
|
||||
var disease = carrierComp.Diseases[i];
|
||||
|
||||
var args = new DiseaseEffectArgs(carrierComp.Owner, disease, EntityManager);
|
||||
disease.Accumulator += frameTime;
|
||||
if (disease.Accumulator >= disease.TickTime)
|
||||
|
||||
46
Content.Server/Disease/Effects/DiseaseProgression.cs
Normal file
46
Content.Server/Disease/Effects/DiseaseProgression.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Content.Server.Disease.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Shared.Disease;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Disease.Effects
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles a disease which incubates over a period of time
|
||||
/// before adding another component to the infected entity
|
||||
/// currently used for zombie virus
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class DiseaseProgression : DiseaseEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// The rate that's increased over time. Defaults to 1% so the probability can be varied in yaml
|
||||
/// </summary>
|
||||
[DataField("rate")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Rate = 0.01f;
|
||||
|
||||
/// <summary>
|
||||
/// The component that is added at the end of build up
|
||||
/// </summary>
|
||||
[DataField("comp")]
|
||||
public string? Comp = null;
|
||||
|
||||
public override void Effect(DiseaseEffectArgs args)
|
||||
{
|
||||
args.EntityManager.EnsureComponent<DiseaseBuildupComponent>(args.DiseasedEntity, out var buildup);
|
||||
if (buildup.Progression < 1) //increases steadily until 100%
|
||||
{
|
||||
buildup.Progression += Rate;
|
||||
}
|
||||
else if (Comp != null)//adds the component for the later stage of the disease.
|
||||
{
|
||||
EntityUid uid = args.DiseasedEntity;
|
||||
var newComponent = (Component) IoCManager.Resolve<IComponentFactory>().GetComponent(Comp);
|
||||
newComponent.Owner = uid;
|
||||
if (!args.EntityManager.HasComponent(uid, newComponent.GetType()))
|
||||
args.EntityManager.AddComponent(uid, newComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Content.Server.Disease.Zombie.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// The component which gives an entity zombie traits.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class DiseaseZombieComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The probability that a given bite will infect a player.
|
||||
/// zombie infection is not based on disease resist items like masks or gloves.
|
||||
/// </summary>
|
||||
[DataField("probability")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Probability = 0.33f;
|
||||
|
||||
/// <summary>
|
||||
/// A multiplier on the movement speed that zombies recieve.
|
||||
/// </summary>
|
||||
[DataField("slowAmount")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float SlowAmount = 0.75f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the zombie needs all the zombie traits initialized upon component init
|
||||
/// useful for entities that already are zombies and do not need the additional traits.
|
||||
/// </summary>
|
||||
[DataField("applyZombieTraits")]
|
||||
public bool ApplyZombieTraits = true;
|
||||
|
||||
/// <summary>
|
||||
/// The color of the zombie's skin
|
||||
/// </summary>
|
||||
[DataField("skinColor")]
|
||||
public readonly Color SkinColor = (0.70f, 0.72f, 0.48f, 1);
|
||||
}
|
||||
}
|
||||
139
Content.Server/Disease/Zombie/DiseaseZombieSystem.cs
Normal file
139
Content.Server/Disease/Zombie/DiseaseZombieSystem.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using Robust.Shared.Containers;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Disease.Components;
|
||||
using Content.Server.Disease.Zombie.Components;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Movement.EntitySystems;
|
||||
using Content.Shared.CharacterAppearance.Components;
|
||||
using Content.Shared.CharacterAppearance.Systems;
|
||||
using Content.Server.Weapons.Melee.ZombieTransfer.Components;
|
||||
|
||||
namespace Content.Server.Disease.Zombie
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles zombie propagation and inherent zombie traits
|
||||
/// </summary>
|
||||
public sealed class DiseaseZombieSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _sharedHands = default!;
|
||||
[Dependency] private readonly SharedHumanoidAppearanceSystem _sharedHumanoidAppearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DiseaseZombieComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<DiseaseZombieComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// I would imagine that if this component got assigned to something other than a mob, it would throw hella errors.
|
||||
/// </remarks>
|
||||
private void OnComponentInit(EntityUid uid, DiseaseZombieComponent component, ComponentInit args)
|
||||
{
|
||||
if (!component.ApplyZombieTraits || !HasComp<MobStateComponent>(uid))
|
||||
return;
|
||||
|
||||
RemComp<DiseaseCarrierComponent>(uid);
|
||||
RemComp<DiseaseBuildupComponent>(uid);
|
||||
RemComp<RespiratorComponent>(uid);
|
||||
RemComp<BarotraumaComponent>(uid);
|
||||
RemComp<HungerComponent>(uid);
|
||||
RemComp<ThirstComponent>(uid);
|
||||
|
||||
EntityManager.EnsureComponent<BloodstreamComponent>(uid, out var bloodstream); //zoms need bloodstream anyway for healing
|
||||
_bloodstream.SetBloodLossThreshold(uid, 0f, bloodstream);
|
||||
_bloodstream.TryModifyBleedAmount(uid, -bloodstream.BleedAmount, bloodstream);
|
||||
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
|
||||
EntityManager.EnsureComponent<ReplacementAccentComponent>(uid).Accent = "zombie";
|
||||
|
||||
if (TryComp<DamageableComponent>(uid, out var comp))
|
||||
{
|
||||
_damageable.SetDamageModifierSetId(uid, "Zombie", comp);
|
||||
_damageable.SetAllDamage(comp, 0);
|
||||
}
|
||||
|
||||
if (TryComp<HumanoidAppearanceComponent>(uid, out var spritecomp))
|
||||
{
|
||||
var oldapp = spritecomp.Appearance;
|
||||
var newapp = oldapp.WithSkinColor(component.SkinColor);
|
||||
_sharedHumanoidAppearance.UpdateAppearance(uid, newapp);
|
||||
|
||||
_sharedHumanoidAppearance.ForceAppearanceUpdate(uid);
|
||||
}
|
||||
|
||||
if (TryComp<HandsComponent>(uid, out var handcomp))
|
||||
{
|
||||
foreach (var hand in handcomp.Hands)
|
||||
{
|
||||
_sharedHands.TrySetActiveHand(uid, hand.Key);
|
||||
_sharedHands.TryDrop(uid);
|
||||
|
||||
var pos = EntityManager.GetComponent<TransformComponent>(uid).Coordinates;
|
||||
var virtualItem = EntityManager.SpawnEntity("ZombieClaw", pos);
|
||||
_sharedHands.DoPickup(uid, hand.Value, virtualItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureComp<ZombieTransferComponent>(uid);
|
||||
}
|
||||
|
||||
if (TryComp<ContainerManagerComponent>(uid, out var cmcomp))
|
||||
{
|
||||
foreach (var container in cmcomp.Containers)
|
||||
{
|
||||
if (container.Value.ID == "gloves")
|
||||
{
|
||||
foreach (var entity in container.Value.ContainedEntities)
|
||||
{
|
||||
container.Value.Remove(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<MindComponent>(uid, out var mindcomp))
|
||||
{
|
||||
if (mindcomp.Mind != null && mindcomp.Mind.TryGetSession(out var session))
|
||||
{
|
||||
var chatMgr = IoCManager.Resolve<IChatManager>();
|
||||
chatMgr.DispatchServerMessage(session, Loc.GetString("zombie-infection-greeting"));
|
||||
}
|
||||
}
|
||||
|
||||
uid.PopupMessageEveryone(Loc.GetString("zombie-transform", ("target", uid)));
|
||||
if (TryComp<MetaDataComponent>(uid, out var metacomp))
|
||||
{
|
||||
metacomp.EntityName = Loc.GetString("zombie-name-prefix", ("target", metacomp.EntityName));
|
||||
|
||||
if (!HasComp<GhostRoleMobSpawnerComponent>(uid)) //this specific component gives build test trouble so pop off, ig
|
||||
{
|
||||
EntityManager.EnsureComponent<GhostTakeoverAvailableComponent>(uid, out var ghostcomp);
|
||||
ghostcomp.RoleName = metacomp.EntityName;
|
||||
ghostcomp.RoleDescription = Loc.GetString("zombie-role-desc");
|
||||
ghostcomp.RoleRules = Loc.GetString("zombie-role-rules");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRefreshMovementSpeedModifiers(EntityUid uid, DiseaseZombieComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
args.ModifySpeed(component.SlowAmount, component.SlowAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Content.Server/StationEvents/Events/ZombieOutbreak.cs
Normal file
54
Content.Server/StationEvents/Events/ZombieOutbreak.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Robust.Shared.Random;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Disease.Zombie.Components;
|
||||
using Content.Shared.MobState.Components;
|
||||
|
||||
namespace Content.Server.StationEvents.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Revives several dead entities as zombies
|
||||
/// </summary>
|
||||
public sealed class ZombieOutbreak : StationEvent
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
|
||||
public override string Name => "ZombieOutbreak";
|
||||
public override float Weight => WeightLow;
|
||||
|
||||
public override string? StartAudio => "/Audio/Announcements/bloblarm.ogg";
|
||||
protected override float EndAfter => 1.0f;
|
||||
|
||||
public override int? MaxOccurrences => 1;
|
||||
|
||||
/// <summary>
|
||||
/// Finds 1-3 random, dead entities accross the station
|
||||
/// and turns them into zombies.
|
||||
/// </summary>
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
List<MobStateComponent> deadList = new();
|
||||
foreach (var mobState in _entityManager.EntityQuery<MobStateComponent>())
|
||||
{
|
||||
if (mobState.IsDead() || mobState.IsCritical())
|
||||
deadList.Add(mobState);
|
||||
}
|
||||
_random.Shuffle(deadList);
|
||||
|
||||
var toInfect = _random.Next(1, 3);
|
||||
|
||||
/// Now we give it to people in the list of dead entities earlier.
|
||||
foreach (var target in deadList)
|
||||
{
|
||||
if (toInfect-- == 0)
|
||||
break;
|
||||
|
||||
_entityManager.EnsureComponent<DiseaseZombieComponent>(target.Owner);
|
||||
}
|
||||
_chatManager.DispatchStationAnnouncement(Loc.GetString("station-event-zombie-outbreak-announcement"),
|
||||
playDefaultSound: false, colorOverride: Color.DarkMagenta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Server.Weapons.Melee.ZombieTransfer.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ZombieTransferComponent : Component
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Linq;
|
||||
using Robust.Shared.Random;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Disease.Components;
|
||||
using Content.Server.Disease.Zombie.Components;
|
||||
using Content.Server.Drone.Components;
|
||||
using Content.Server.Weapon.Melee;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Server.Disease;
|
||||
using Content.Server.Weapons.Melee.ZombieTransfer.Components;
|
||||
|
||||
namespace Content.Server.Weapons.Melee.ZombieTransfer
|
||||
{
|
||||
public sealed class ZombieTransferSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DiseaseSystem _disease = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ZombieTransferComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
}
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, ZombieTransferComponent component, MeleeHitEvent args)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent<DiseaseZombieComponent>(args.User, out var diseaseZombieComp))
|
||||
return;
|
||||
|
||||
if (!args.HitEntities.Any())
|
||||
return;
|
||||
|
||||
foreach (EntityUid entity in args.HitEntities)
|
||||
{
|
||||
if (args.User == entity)
|
||||
continue;
|
||||
|
||||
if (!HasComp<MobStateComponent>(entity))
|
||||
continue;
|
||||
|
||||
if (_robustRandom.Prob(diseaseZombieComp.Probability) && HasComp<DiseaseCarrierComponent>(entity))
|
||||
{
|
||||
_disease.TryAddDisease(entity, "ZombieInfection");
|
||||
}
|
||||
|
||||
EntityManager.EnsureComponent<MobStateComponent>(entity, out var mobState);
|
||||
if ((mobState.IsDead() || mobState.IsCritical()) && !HasComp<DiseaseZombieComponent>(entity)) //dead entities are eautomatically infected. MAYBE: have activated infect ability?
|
||||
{
|
||||
EntityManager.AddComponent<DiseaseZombieComponent>(entity);
|
||||
var dspec = new DamageSpecifier();
|
||||
//these damages match the zombie claw
|
||||
dspec.DamageDict.TryAdd("Slash", -12);
|
||||
dspec.DamageDict.TryAdd("Piercing", -7);
|
||||
args.BonusDamage += dspec;
|
||||
}
|
||||
else if (mobState.IsAlive() && !HasComp<DroneComponent>(entity)) //heals when zombies bite live entities
|
||||
{
|
||||
var healingSolution = new Solution();
|
||||
healingSolution.AddReagent("Bicaridine", 1.00); //if OP, reduce/change chem
|
||||
_bloodstream.TryAddToChemicals(args.User, healingSolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user