diff --git a/Content.Client/StatusIcon/StatusIconSystem.cs b/Content.Client/StatusIcon/StatusIconSystem.cs index 63f5776769..0a3f374c4f 100644 --- a/Content.Client/StatusIcon/StatusIconSystem.cs +++ b/Content.Client/StatusIcon/StatusIconSystem.cs @@ -72,8 +72,7 @@ public sealed class StatusIconSystem : SharedStatusIconSystem { var viewer = _playerManager.LocalSession?.AttachedEntity; - // Always show our icons to our entity - if (viewer == ent.Owner) + if (data.VisibleToOwner && viewer == ent.Owner) // WD EDIT: not always show our icons to our entity return true; if (data.VisibleToGhosts && HasComp(viewer)) diff --git a/Content.Client/_White/Inventory/WhiteInventorySystem.cs b/Content.Client/_White/Inventory/WhiteInventorySystem.cs new file mode 100644 index 0000000000..3e64f30920 --- /dev/null +++ b/Content.Client/_White/Inventory/WhiteInventorySystem.cs @@ -0,0 +1,5 @@ +using Content.Shared._White.Inventory; + +namespace Content.Client._White.Inventory; + +public sealed class WhiteInventorySystem : SharedWhiteInventorySystem; diff --git a/Content.Client/_White/Overlays/MindSlave/MindSlaveIconsSystem.cs b/Content.Client/_White/Overlays/MindSlaveIconsSystem.cs similarity index 78% rename from Content.Client/_White/Overlays/MindSlave/MindSlaveIconsSystem.cs rename to Content.Client/_White/Overlays/MindSlaveIconsSystem.cs index 22cbc0c7bd..0c02955f00 100644 --- a/Content.Client/_White/Overlays/MindSlave/MindSlaveIconsSystem.cs +++ b/Content.Client/_White/Overlays/MindSlaveIconsSystem.cs @@ -5,7 +5,7 @@ using Content.Shared.StatusIcon.Components; using Robust.Client.Player; using Robust.Shared.Prototypes; -namespace Content.Client._White.Overlays.MindSlave; +namespace Content.Client._White.Overlays; public sealed class MindSlaveIconsSystem : EntitySystem { @@ -31,18 +31,18 @@ public sealed class MindSlaveIconsSystem : EntitySystem if (TryComp(_player.LocalEntity, out MindSlaveComponent? ownerMindSlave)) { var netUid = GetNetEntity(uid); - if (ownerMindSlave.Master == netUid && _prototype.TryIndex(ownerMindSlave.MasterStatusIcon, out var masterIcon)) + if (ownerMindSlave.Master == netUid && _prototype.TryIndex(ownerMindSlave.MasterStatusIcon, out var masterIcon)) result.Add(masterIcon); - if (ownerMindSlave.Slaves.Contains(netUid) && _prototype.TryIndex(ownerMindSlave.SlaveStatusIcon, out var slaveIcon)) + if (ownerMindSlave.Slaves.Contains(netUid) && _prototype.TryIndex(ownerMindSlave.SlaveStatusIcon, out var slaveIcon)) result.Add(slaveIcon); } else { - if (mindSlave.Slaves.Any() && _prototype.TryIndex(mindSlave.MasterStatusIcon, out var masterIcon)) + if (mindSlave.Slaves.Any() && _prototype.TryIndex(mindSlave.MasterStatusIcon, out var masterIcon)) result.Add(masterIcon); - if (mindSlave.Master.HasValue && _prototype.TryIndex(mindSlave.SlaveStatusIcon, out var slaveIcon)) + if (mindSlave.Master.HasValue && _prototype.TryIndex(mindSlave.SlaveStatusIcon, out var slaveIcon)) result.Add(slaveIcon); } diff --git a/Content.Client/_White/Overlays/XenomorphInfectionIconSystem.cs b/Content.Client/_White/Overlays/XenomorphInfectionIconSystem.cs new file mode 100644 index 0000000000..e1e15c37cd --- /dev/null +++ b/Content.Client/_White/Overlays/XenomorphInfectionIconSystem.cs @@ -0,0 +1,32 @@ +using Content.Shared._White.Xenomorphs.Infection; +using Content.Shared._White.Xenomorphs.Larva; +using Content.Shared.StatusIcon.Components; +using Robust.Shared.Prototypes; + +namespace Content.Client._White.Overlays; + +public sealed class XenomorphInfectionIconSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnXenomorphInfectedGetStatusIconsEvent); + SubscribeLocalEvent(OnXenomorphLarvaVictimGetStatusIconsEvent); + } + + private void OnXenomorphInfectedGetStatusIconsEvent(EntityUid uid, XenomorphInfectedComponent component, ref GetStatusIconsEvent args) + { + if (component.InfectedIcons.TryGetValue(component.GrowthStage, out var infectedIcon) + && _prototype.TryIndex(infectedIcon, out var icon)) + args.StatusIcons.Add(icon); + } + + private void OnXenomorphLarvaVictimGetStatusIconsEvent(EntityUid uid, XenomorphLarvaVictimComponent component, ref GetStatusIconsEvent args) + { + if (component.InfectedIcon.HasValue && _prototype.TryIndex(component.InfectedIcon.Value, out var icon)) + args.StatusIcons.Add(icon); + } +} diff --git a/Content.Client/_White/Xenomorphs/Infection/XenomorphInfectionComponent.cs b/Content.Client/_White/Xenomorphs/Infection/XenomorphInfectionComponent.cs new file mode 100644 index 0000000000..143755f6ad --- /dev/null +++ b/Content.Client/_White/Xenomorphs/Infection/XenomorphInfectionComponent.cs @@ -0,0 +1,6 @@ +using Content.Shared._White.Xenomorphs.Infection; + +namespace Content.Client._White.Xenomorphs.Infection; + +[RegisterComponent] +public sealed partial class XenomorphInfectionComponent : SharedXenomorphInfectionComponent; diff --git a/Content.Server/_White/Actions/ActionsSystem.cs b/Content.Server/_White/Actions/ActionsSystem.cs index 6e136ec6a8..0607882d8a 100644 --- a/Content.Server/_White/Actions/ActionsSystem.cs +++ b/Content.Server/_White/Actions/ActionsSystem.cs @@ -2,7 +2,10 @@ using Content.Server.DoAfter; using Content.Shared._White.Actions.Events; using Content.Shared.Coordinates; using Content.Shared.DoAfter; +using Content.Shared.Maps; +using Content.Shared.Physics; using Robust.Server.Audio; +using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Map; @@ -13,12 +16,15 @@ namespace Content.Server._White.Actions; public sealed class ActionsSystem : EntitySystem { + [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDef = default!; [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly MapSystem _mapSystem = default!; [Dependency] private readonly TransformSystem _transform = default!; - [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -30,7 +36,7 @@ public sealed class ActionsSystem : EntitySystem private void OnSpawnTileEntityAction(SpawnTileEntityActionEvent args) { - if (args.Handled || !CreationTileEntity(args.Performer.ToCoordinates(), args.TileId, args.Entity, args.Audio)) + if (args.Handled || !CreationTileEntity(args.Performer, args.Performer.ToCoordinates(), args.TileId, args.Entity, args.Audio, args.BlockedCollision)) return; args.Handled = true; @@ -43,6 +49,9 @@ public sealed class ActionsSystem : EntitySystem if (args.Length != 0) { + if (CheckTileBlocked(args.Target, args.BlockedCollision)) + return; + var ev = new PlaceTileEntityDoAfterEvent { Target = GetNetCoordinates(args.Target), @@ -64,7 +73,7 @@ public sealed class ActionsSystem : EntitySystem return; } - if (!CreationTileEntity(args.Target, args.TileId, args.Entity, args.Audio)) + if (!CreationTileEntity(args.Performer, args.Target, args.TileId, args.Entity, args.Audio, args.BlockedCollision)) return; args.Handled = true; @@ -72,7 +81,7 @@ public sealed class ActionsSystem : EntitySystem private void OnPlaceTileEntityDoAfter(PlaceTileEntityDoAfterEvent args) { - if (args.Handled || !CreationTileEntity(GetCoordinates(args.Target), args.TileId, args.Entity, args.Audio)) + if (args.Handled || !CreationTileEntity(args.User, GetCoordinates(args.Target), args.TileId, args.Entity, args.Audio, null)) return; args.Handled = true; @@ -80,8 +89,11 @@ public sealed class ActionsSystem : EntitySystem #region Helpers - private bool CreationTileEntity(EntityCoordinates coordinates, string? tileId, EntProtoId? entProtoId, SoundSpecifier? audio) + private bool CreationTileEntity(EntityUid user, EntityCoordinates coordinates, string? tileId, EntProtoId? entProtoId, SoundSpecifier? audio, CollisionGroup? blockedCollision) { + if (_container.IsEntityOrParentInContainer(user)) + return false; + if (tileId != null) { if (_transform.GetGrid(coordinates) is not { } grid || !TryComp(grid, out MapGridComponent? mapGrid)) @@ -93,13 +105,22 @@ public sealed class ActionsSystem : EntitySystem _mapSystem.SetTile(grid, mapGrid, coordinates, tile); } - if (entProtoId != null) - Spawn(entProtoId, coordinates); - _audio.PlayPvs(audio, coordinates); + if (entProtoId == null || CheckTileBlocked(coordinates, blockedCollision)) + return false; + + Spawn(entProtoId, coordinates); + return true; } + private bool CheckTileBlocked(EntityCoordinates coordinates, CollisionGroup? blockedCollision) + { + var tileRef = coordinates.GetTileRef(EntityManager, _mapManager); + + return tileRef.HasValue && blockedCollision.HasValue && _turf.IsTileBlocked(tileRef.Value, blockedCollision.Value); + } + #endregion } diff --git a/Content.Server/_White/EntityEffects/Effects/XenomorphInfection.cs b/Content.Server/_White/EntityEffects/Effects/XenomorphInfection.cs new file mode 100644 index 0000000000..74ac88dd15 --- /dev/null +++ b/Content.Server/_White/EntityEffects/Effects/XenomorphInfection.cs @@ -0,0 +1,31 @@ +using Content.Server.Body.Systems; +using Content.Shared.EntityEffects; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; + +namespace Content.Server._White.EntityEffects.Effects; + +[UsedImplicitly] +public sealed partial class XenomorphInfection : EntityEffect +{ + [DataField] + public string SlotId = "xenomorph_larva"; + + [DataField] + public EntProtoId Prototype = "XenomorphInfection"; + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => null; + + public override void Effect(EntityEffectBaseArgs args) + { + var bodySystem = args.EntityManager.System(); + + if (bodySystem.GetRootPartOrNull(args.TargetEntity) is not {} rootPart) + return; + + var organ = args.EntityManager.Spawn(Prototype); + bodySystem.TryCreateOrganSlot(rootPart.Entity, SlotId, out _, rootPart.BodyPart); + if (!bodySystem.InsertOrgan(rootPart.Entity, organ, SlotId, rootPart.BodyPart)) + args.EntityManager.QueueDeleteEntity(organ); + } +} diff --git a/Content.Server/_White/GameTicking/Rules/XenomorphsRuleSystem.cs b/Content.Server/_White/GameTicking/Rules/XenomorphsRuleSystem.cs index 9b30920a5e..0da43e5b7b 100644 --- a/Content.Server/_White/GameTicking/Rules/XenomorphsRuleSystem.cs +++ b/Content.Server/_White/GameTicking/Rules/XenomorphsRuleSystem.cs @@ -46,6 +46,7 @@ public sealed class XenomorphsRuleSystem : GameRuleSystem(AfterAntagEntitySelected); + SubscribeLocalEvent(OnXenomorphInit); SubscribeLocalEvent(BeforeXenomorphEvolution); SubscribeLocalEvent(AfterXenomorphEvolution); @@ -66,6 +67,15 @@ public sealed class XenomorphsRuleSystem : GameRuleSystem(OnMeleeHit); - SubscribeLocalEvent(OnThrowDoHit, before: new[] {typeof(JumpSystem)}); SubscribeLocalEvent(OnGotEquipped); SubscribeLocalEvent(OnGotUnequipped); SubscribeLocalEvent(OnGotEquippedHand); SubscribeLocalEvent(OnUnequipAttempt); } - private void OnThrowDoHit(EntityUid uid, HeadcrabComponent component, ThrowDoHitEvent args) - { - if (args.Handled) - return; - - TryEquipHeadcrab(uid, args.Target, component); - - args.Handled = true; - } - private void OnGotEquipped(EntityUid uid, HeadcrabComponent component, GotEquippedEvent args) { if (args.Slot != "mask") @@ -119,15 +99,6 @@ public sealed partial class HeadcrabSystem : EntitySystem _npcFaction.RemoveFaction(args.Equipee, "Zombie"); } - private void OnMeleeHit(EntityUid uid, HeadcrabComponent component, MeleeHitEvent args) - { - if (!args.HitEntities.Any() - || _random.Next(1, 101) <= component.ChancePounce) - return; - - TryEquipHeadcrab(uid, args.HitEntities.First(), component); - } - public override void Update(float frameTime) { base.Update(frameTime); @@ -161,17 +132,4 @@ public sealed partial class HeadcrabSystem : EntitySystem ("entity", targetId)), targetId, Filter.PvsExcept(targetId), true); } } - - private bool TryEquipHeadcrab(EntityUid uid, EntityUid target, HeadcrabComponent component) - { - if (_mobState.IsDead(uid) - || !_mobState.IsAlive(target) - || !HasComp(target) - || HasComp(target)) - return false; - - _inventory.TryGetSlotEntity(target, "head", out var headItem); - return !HasComp(headItem) - && !_inventory.TryEquip(target, uid, "mask", true, true); - } } diff --git a/Content.Server/_White/Inventory/Components/EffectsOnEquipComponent.cs b/Content.Server/_White/Inventory/Components/EffectsOnEquipComponent.cs new file mode 100644 index 0000000000..5855ee67cc --- /dev/null +++ b/Content.Server/_White/Inventory/Components/EffectsOnEquipComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.EntityEffects; +using Content.Shared.Whitelist; + +namespace Content.Server._White.Inventory.Components; + +[RegisterComponent] +public sealed partial class EffectsOnEquipComponent : Component +{ + [DataField(required: true)] + public string Slot; + + [DataField] + public List Effects = new (); + + [DataField] + public EntityWhitelist? Blacklist; +} diff --git a/Content.Server/_White/Inventory/WhiteInventorySystem.Equip.cs b/Content.Server/_White/Inventory/WhiteInventorySystem.Equip.cs new file mode 100644 index 0000000000..fbb020da0c --- /dev/null +++ b/Content.Server/_White/Inventory/WhiteInventorySystem.Equip.cs @@ -0,0 +1,26 @@ +using Content.Server._White.Inventory.Components; +using Content.Shared.EntityEffects; +using Content.Shared.Inventory.Events; +using Content.Shared.Whitelist; + +namespace Content.Server._White.Inventory; + +public sealed partial class WhiteInventorySystem +{ + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; + + private void InitializeEquip() + { + SubscribeLocalEvent(OnGotEquipped); + } + + private void OnGotEquipped(EntityUid uid, EffectsOnEquipComponent component, GotEquippedEvent args) + { + if (args.Slot != component.Slot || _entityWhitelist.IsBlacklistPass(component.Blacklist, args.Equipee)) + return; + + var effectsArgs = new EntityEffectBaseArgs(args.Equipee, EntityManager); + foreach (var effect in component.Effects) + effect.Effect(effectsArgs); + } +} diff --git a/Content.Server/_White/Inventory/WhiteInventorySystem.cs b/Content.Server/_White/Inventory/WhiteInventorySystem.cs new file mode 100644 index 0000000000..db8ee93f19 --- /dev/null +++ b/Content.Server/_White/Inventory/WhiteInventorySystem.cs @@ -0,0 +1,13 @@ +using Content.Shared._White.Inventory; + +namespace Content.Server._White.Inventory; + +public sealed partial class WhiteInventorySystem : SharedWhiteInventorySystem +{ + public override void Initialize() + { + base.Initialize(); + + InitializeEquip(); + } +} diff --git a/Content.Server/_White/Xenomorphs/Egg/XenomorphEggSystem.cs b/Content.Server/_White/Xenomorphs/Egg/XenomorphEggSystem.cs new file mode 100644 index 0000000000..e18566c50e --- /dev/null +++ b/Content.Server/_White/Xenomorphs/Egg/XenomorphEggSystem.cs @@ -0,0 +1,133 @@ +using Content.Server._White.Inventory; +using Content.Server.Popups; +using Content.Shared._White.Inventory.Components; +using Content.Shared._White.Xenomorphs.Egg; +using Content.Shared._White.Xenomorphs.Xenomorph; +using Content.Shared.Ghost; +using Content.Shared.Interaction; +using Content.Shared.Inventory; +using Content.Shared.Weapons.Melee.Events; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server._White.Xenomorphs.Egg; + +public sealed class XenomorphEggSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly WhiteInventorySystem _whiteInventory = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnActivateInWorld); + SubscribeLocalEvent(OnAttacked); + } + + private void OnInit(EntityUid uid, XenomorphEggComponent component, ComponentInit args) + { + if (component.Status != XenomorphEggStatus.Growning) + return; + + _appearance.SetData(uid, XenomorphEggKey.Key, XenomorphEggVisualsStatus.Growning); + component.GrownAt = _timing.CurTime + _random.Next(component.MinGrowthTime, component.MaxGrowthTime); + } + + private void OnActivateInWorld(EntityUid uid, XenomorphEggComponent component, ActivateInWorldEvent args) + { + switch (component.Status) + { + case XenomorphEggStatus.Grown: + SetBursting(uid, component); + return; + case XenomorphEggStatus.Burst: + CleanBurstingEgg(uid, args.User, component); + return; + } + } + + private void OnAttacked(EntityUid uid, XenomorphEggComponent component, AttackedEvent args) + { + if (component.Status != XenomorphEggStatus.Burst || !HasComp(args.User)) + return; + + CleanBurstingEgg(uid, args.User, component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var time = _timing.CurTime; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var xenomorphEgg)) + { + switch (xenomorphEgg.Status) + { + case XenomorphEggStatus.Growning when time >= xenomorphEgg.GrownAt: + xenomorphEgg.Status = XenomorphEggStatus.Grown; + _appearance.SetData(uid, XenomorphEggKey.Key, XenomorphEggVisualsStatus.Grown); + return; + case XenomorphEggStatus.Grown when time >= xenomorphEgg.CheckInRangeAt: + xenomorphEgg.CheckInRangeAt = time + xenomorphEgg.CheckInRangeDelay; + + foreach (var entity in _entityLookup.GetEntitiesInRange(Transform(uid).Coordinates, xenomorphEgg.BurstRange)) + { + if (HasComp(entity) || HasComp(entity)) + continue; + + SetBursting(uid, xenomorphEgg); + return; + } + + return; + case XenomorphEggStatus.Bursting when time >= xenomorphEgg.BurstAt: + SetBurst(uid, xenomorphEgg); + return; + } + } + } + + private void CleanBurstingEgg(EntityUid uid, EntityUid user, XenomorphEggComponent component) + { + _popup.PopupEntity(Loc.GetString("xenomorph-egg-clean-butsting-egg"), user, user); + _audio.PlayEntity(component.CleaningSound, user, user); + QueueDel(uid); + } + + private void SetBurst(EntityUid uid, XenomorphEggComponent component) + { + component.Status = XenomorphEggStatus.Burst; + _appearance.SetData(uid, XenomorphEggKey.Key, XenomorphEggVisualsStatus.Burst); + + var coordinates = Transform(uid).Coordinates; + var spawned = Spawn(component.FaceHuggerPrototype, coordinates); + + if (!TryComp(spawned, out var equipOn)) + return; + + foreach (var entity in _entityLookup.GetEntitiesInRange(coordinates, component.BurstRange)) + { + if (_whiteInventory.TryEquip(spawned, entity, equipOn)) + return; + } + } + + private void SetBursting(EntityUid uid, XenomorphEggComponent component) + { + component.Status = XenomorphEggStatus.Bursting; + component.BurstAt = _timing.CurTime + component.BurstingDelay; + _appearance.SetData(uid, XenomorphEggKey.Key, XenomorphEggVisualsStatus.Bursting); + } +} diff --git a/Content.Server/_White/Xenomorphs/Evolution/XenomorphEvolutionComponent.cs b/Content.Server/_White/Xenomorphs/Evolution/XenomorphEvolutionComponent.cs index 67b6f056e6..e90337bbb3 100644 --- a/Content.Server/_White/Xenomorphs/Evolution/XenomorphEvolutionComponent.cs +++ b/Content.Server/_White/Xenomorphs/Evolution/XenomorphEvolutionComponent.cs @@ -21,7 +21,7 @@ public sealed partial class XenomorphEvolutionComponent : Component public FixedPoint2 Max; [DataField] - public FixedPoint2 PointsPerSecond = 0.5; + public FixedPoint2 PointsPerSecond = 2; [DataField] public TimeSpan EvolutionJitterDuration = TimeSpan.FromSeconds(10); diff --git a/Content.Server/_White/Xenomorphs/Evolution/XenomorphEvolutionSystem.cs b/Content.Server/_White/Xenomorphs/Evolution/XenomorphEvolutionSystem.cs index d60725de0b..8201dbed30 100644 --- a/Content.Server/_White/Xenomorphs/Evolution/XenomorphEvolutionSystem.cs +++ b/Content.Server/_White/Xenomorphs/Evolution/XenomorphEvolutionSystem.cs @@ -133,7 +133,7 @@ public sealed class XenomorphEvolutionSystem : EntitySystem { if (component.Points < component.Max) { - _popup.PopupEntity(Loc.GetString("xenomorphs-evolution-not-enough-points", ("seconds", (component.Max - component.Points) * component.PointsPerSecond)), uid, uid); + _popup.PopupEntity(Loc.GetString("xenomorphs-evolution-not-enough-points", ("seconds", (component.Max - component.Points) / component.PointsPerSecond)), uid, uid); return false; } diff --git a/Content.Server/_White/Xenomorphs/Infection/XenomorphInfectionComponent.cs b/Content.Server/_White/Xenomorphs/Infection/XenomorphInfectionComponent.cs new file mode 100644 index 0000000000..a30633b1f8 --- /dev/null +++ b/Content.Server/_White/Xenomorphs/Infection/XenomorphInfectionComponent.cs @@ -0,0 +1,37 @@ +using Content.Shared._White.Xenomorphs.Infection; +using Content.Shared.EntityEffects; +using Robust.Shared.Prototypes; + +namespace Content.Server._White.Xenomorphs.Infection; + +[RegisterComponent] +public sealed partial class XenomorphInfectionComponent : SharedXenomorphInfectionComponent +{ + [DataField] + public int MaxGrowthStage = 1; + + [DataField] + public EntProtoId LarvaPrototype = "MobXenomorphLarva"; + + /// + /// The probability of infection growth per GrowTime. + /// + [DataField] + public float GrowProb = 1f; + + /// + /// The time required for infection to grow. + /// + [DataField] + public TimeSpan GrowTime = TimeSpan.FromSeconds(25); + + [DataField] + public Dictionary> Effects = new (); + + [ViewVariables] + public TimeSpan NextPointsAt; + + [ViewVariables] + public EntityUid? Infected; + +} diff --git a/Content.Server/_White/Xenomorphs/Infection/XenomorphInfectionSystem.cs b/Content.Server/_White/Xenomorphs/Infection/XenomorphInfectionSystem.cs new file mode 100644 index 0000000000..72823d6317 --- /dev/null +++ b/Content.Server/_White/Xenomorphs/Infection/XenomorphInfectionSystem.cs @@ -0,0 +1,107 @@ +using Content.Shared._White.Xenomorphs.Infection; +using Content.Shared._White.Xenomorphs.Larva; +using Content.Shared.Body.Events; +using Content.Shared.EntityEffects; +using Robust.Server.Containers; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server._White.Xenomorphs.Infection; + +public sealed class XenomorphInfectionSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly ContainerSystem _container = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnOrganAddedToBody); + SubscribeLocalEvent(OnOrganRemovedFromBody); + } + + private void OnShutdown(EntityUid uid, XenomorphInfectionComponent component, ComponentShutdown args) + { + if (component.Infected.HasValue) + RemComp(component.Infected.Value); + } + + private void OnOrganAddedToBody(EntityUid uid, XenomorphInfectionComponent component, OrganAddedToBodyEvent args) + { + var xenomorphInfected = EnsureComp(args.Body); + xenomorphInfected.Infection = uid; + xenomorphInfected.InfectedIcons = component.InfectedIcons; + Dirty(args.Body, xenomorphInfected); + + component.Infected = args.Body; + } + + private void OnOrganRemovedFromBody(EntityUid uid, XenomorphInfectionComponent component, OrganRemovedFromBodyEvent args) + { + RemComp(args.OldBody); + component.Infected = null; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var time = _timing.CurTime; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var infection)) + { + if (!infection.Infected.HasValue || infection.GrowthStage >= infection.MaxGrowthStage || time < infection.NextPointsAt) + continue; + + infection.NextPointsAt = time + infection.GrowTime; + + if (!_random.Prob(infection.GrowProb)) + continue; + + infection.GrowthStage++; + if (TryComp(infection.Infected.Value, out var xenomorphInfected)) + { + xenomorphInfected.GrowthStage = infection.GrowthStage; + DirtyField(infection.Infected.Value, xenomorphInfected, nameof(XenomorphInfectedComponent.GrowthStage)); + } + + if (infection.Effects.TryGetValue(infection.GrowthStage, out var effects)) + { + var effectsArgs = new EntityEffectBaseArgs(infection.Infected.Value, EntityManager); + foreach (var effect in effects) + effect.Effect(effectsArgs); + } + + if (infection.GrowthStage < infection.MaxGrowthStage) + continue; + + if (!_container.TryGetContainingContainer((uid, null, null), out var container)) + { + QueueDel(uid); + continue; + } + + var larva = Spawn(infection.LarvaPrototype); + + var larvaComponent = EnsureComp(larva); + larvaComponent.Victim = infection.Infected.Value; + + var larvaVictim = EnsureComp(infection.Infected.Value); + if (infection.InfectedIcons.TryGetValue(infection.GrowthStage, out var infectedIcon)) + { + larvaVictim.InfectedIcon = infectedIcon; + Dirty(infection.Infected.Value, larvaVictim); + } + + _container.Remove(uid, container); + _container.Insert(larva, container); + + QueueDel(uid); + } + } +} diff --git a/Content.Server/_White/Xenomorphs/Larva/XenomorphLarvaSystem.cs b/Content.Server/_White/Xenomorphs/Larva/XenomorphLarvaSystem.cs new file mode 100644 index 0000000000..5dcd8bff68 --- /dev/null +++ b/Content.Server/_White/Xenomorphs/Larva/XenomorphLarvaSystem.cs @@ -0,0 +1,79 @@ +using Content.Server.Body.Systems; +using Content.Server.DoAfter; +using Content.Server.Ghost.Roles.Components; +using Content.Server.Jittering; +using Content.Server.Popups; +using Content.Shared._White.Xenomorphs; +using Content.Shared._White.Xenomorphs.Larva; +using Content.Shared.DoAfter; +using Content.Shared.IdentityManagement; +using Content.Shared.Popups; +using Robust.Server.Containers; +using Robust.Shared.Containers; +using Robust.Shared.Player; + +namespace Content.Server._White.Xenomorphs.Larva; + +public sealed class XenomorphLarvaSystem : EntitySystem +{ + [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly JitteringSystem _jitter = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnGotRemovedFromContainer); + SubscribeLocalEvent(OnTakeGhostRole); + SubscribeLocalEvent(OnLarvaBurstDoAfter); + } + + private void OnShutdown(EntityUid uid, XenomorphLarvaComponent component, ComponentShutdown args) + { + if (component.Victim.HasValue) + RemComp(component.Victim.Value); + } + + private void OnGotRemovedFromContainer(EntityUid uid, XenomorphLarvaComponent component, EntGotRemovedFromContainerMessage args) + { + if (component.Victim.HasValue) + RemComp(component.Victim.Value); + } + + private void OnTakeGhostRole(EntityUid uid, XenomorphLarvaComponent component, TakeGhostRoleEvent args) + { + if (component.Victim is not {} victim) + return; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, uid, component.BurstDelay, new LarvaBurstDoAfterEvent(), uid, target: component.Victim) + { + NeedHand = false, + BreakOnDamage = false, + BreakOnMove = false, + Hidden = true, + CancelDuplicate = true, + BlockDuplicate = true, + DuplicateCondition = DuplicateConditions.SameEvent + }; + + if (!_doAfter.TryStartDoAfter(doAfterEventArgs)) + return; + + _popup.PopupEntity(Loc.GetString("xenomorphs-burst-victim"), victim, victim, PopupType.MediumCaution); + _popup.PopupEntity(Loc.GetString("xenomorphs-burst-other", ("victim", Identity.Entity(victim, EntityManager))), victim, Filter.PvsExcept(victim), true, PopupType.LargeCaution); + + _jitter.DoJitter(victim, component.BurstDelay, true); + } + + private void OnLarvaBurstDoAfter(EntityUid uid, XenomorphLarvaComponent component, LarvaBurstDoAfterEvent args) + { + if (!_container.TryGetContainingContainer((uid, null, null), out var container) + || component.Victim is not { } victim) + return; + + _container.Remove(uid, container); + _body.GibBody(victim); + } +} diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs b/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs index 2c8da0503e..d2ce9ada8b 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs @@ -142,6 +142,7 @@ public partial class SharedBodySystem if (!part.Organs.ContainsKey(slotId) && !part.Organs.TryAdd(slotId, slot.Value)) return false; + Dirty(parent.Value, part); // WD EDIT return true; // Shitmed Change End diff --git a/Content.Shared/StatusIcon/StatusIconPrototype.cs b/Content.Shared/StatusIcon/StatusIconPrototype.cs index a9ec1f7098..0599b83e9f 100644 --- a/Content.Shared/StatusIcon/StatusIconPrototype.cs +++ b/Content.Shared/StatusIcon/StatusIconPrototype.cs @@ -32,6 +32,14 @@ public partial class StatusIconData : IComparable [DataField] public bool VisibleToGhosts = true; + // WD EDIT START + /// + /// Whether or not to hide the icon to owner + /// + [DataField] + public bool VisibleToOwner = true; + // WD EDIT END + /// /// Whether or not to hide the icon when we are inside a container like a locker or a crate. /// diff --git a/Content.Shared/_White/Actions/Events/SpawnActionEvent.cs b/Content.Shared/_White/Actions/Events/SpawnActionEvent.cs index 400de0eaad..11bf83e95c 100644 --- a/Content.Shared/_White/Actions/Events/SpawnActionEvent.cs +++ b/Content.Shared/_White/Actions/Events/SpawnActionEvent.cs @@ -1,5 +1,6 @@ using Content.Shared.Actions; using Content.Shared.DoAfter; +using Content.Shared.Physics; using Robust.Shared.Audio; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -29,6 +30,9 @@ public sealed partial class SpawnTileEntityActionEvent : InstantActionEvent /// [DataField] public SoundSpecifier? Audio; + + [DataField] + public CollisionGroup? BlockedCollision; } /// @@ -54,6 +58,9 @@ public sealed partial class PlaceTileEntityEvent : WorldTargetActionEvent [DataField] public SoundSpecifier? Audio; + [DataField] + public CollisionGroup? BlockedCollision; + /// /// The duration of the action in seconds /// diff --git a/Content.Shared/_White/Implants/MindSlave/MindSlaveComponent.cs b/Content.Shared/_White/Implants/MindSlave/MindSlaveComponent.cs index f5b8d2ead4..0d28f6c439 100644 --- a/Content.Shared/_White/Implants/MindSlave/MindSlaveComponent.cs +++ b/Content.Shared/_White/Implants/MindSlave/MindSlaveComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.StatusIcon; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared._White.Implants.MindSlave; @@ -7,15 +8,15 @@ namespace Content.Shared._White.Implants.MindSlave; [RegisterComponent, AutoGenerateComponentState, NetworkedComponent] public sealed partial class MindSlaveComponent : Component { - [ViewVariables(VVAccess.ReadOnly), AutoNetworkedField] + [DataField] + public ProtoId SlaveStatusIcon = "SlaveMindSlaveFaction"; + + [DataField] + public ProtoId MasterStatusIcon = "MasterMindSlaveFaction"; + + [AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)] public List Slaves = []; - [ViewVariables(VVAccess.ReadOnly), AutoNetworkedField] + [AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)] public NetEntity? Master; - - [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] - public string SlaveStatusIcon = "SlaveMindSlaveFaction"; - - [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] - public string MasterStatusIcon = "MasterMindSlaveFaction"; } diff --git a/Content.Shared/_White/Inventory/Components/BaseEquipOnComponent.cs b/Content.Shared/_White/Inventory/Components/BaseEquipOnComponent.cs new file mode 100644 index 0000000000..df664948e7 --- /dev/null +++ b/Content.Shared/_White/Inventory/Components/BaseEquipOnComponent.cs @@ -0,0 +1,22 @@ +using Content.Shared.Whitelist; + +namespace Content.Shared._White.Inventory.Components; + +public abstract partial class BaseEquipOnComponent : Component +{ + [DataField(required: true)] + public string Slot = "mask"; + + [DataField] + public string BlockingSlot = "head"; + + [DataField] + public float EquipProb = 1f; + + [DataField] + public EntityWhitelist? Blacklist; + + [DataField] + public bool Force = true; + +} diff --git a/Content.Shared/_White/Inventory/Components/EquipOnCollideComponent.cs b/Content.Shared/_White/Inventory/Components/EquipOnCollideComponent.cs new file mode 100644 index 0000000000..297a038fb2 --- /dev/null +++ b/Content.Shared/_White/Inventory/Components/EquipOnCollideComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared._White.Inventory.Components; + +[RegisterComponent] +public sealed partial class EquipOnCollideComponent : BaseEquipOnComponent; diff --git a/Content.Shared/_White/Inventory/Components/EquipOnMeleeHitComponent.cs b/Content.Shared/_White/Inventory/Components/EquipOnMeleeHitComponent.cs new file mode 100644 index 0000000000..844228ad02 --- /dev/null +++ b/Content.Shared/_White/Inventory/Components/EquipOnMeleeHitComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared._White.Inventory.Components; + +[RegisterComponent] +public sealed partial class EquipOnMeleeHitComponent : BaseEquipOnComponent; diff --git a/Content.Shared/_White/Inventory/Components/EquipOnPickUpComponent.cs b/Content.Shared/_White/Inventory/Components/EquipOnPickUpComponent.cs new file mode 100644 index 0000000000..83846390d1 --- /dev/null +++ b/Content.Shared/_White/Inventory/Components/EquipOnPickUpComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared._White.Inventory.Components; + +[RegisterComponent] +public sealed partial class EquipOnPickUpComponent : BaseEquipOnComponent; diff --git a/Content.Shared/_White/Inventory/Components/EquipOnThrownHitComponent.cs b/Content.Shared/_White/Inventory/Components/EquipOnThrownHitComponent.cs new file mode 100644 index 0000000000..9dbf222ca5 --- /dev/null +++ b/Content.Shared/_White/Inventory/Components/EquipOnThrownHitComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared._White.Inventory.Components; + +[RegisterComponent] +public sealed partial class EquipOnThrownHitComponent : BaseEquipOnComponent; diff --git a/Content.Shared/_White/Inventory/SharedWhiteInventorySystem.Equip.cs b/Content.Shared/_White/Inventory/SharedWhiteInventorySystem.Equip.cs new file mode 100644 index 0000000000..c782194aa3 --- /dev/null +++ b/Content.Shared/_White/Inventory/SharedWhiteInventorySystem.Equip.cs @@ -0,0 +1,75 @@ +using Content.Shared._White.Inventory.Components; +using Content.Shared._White.Jump; +using Content.Shared.Hands; +using Content.Shared.Inventory; +using Content.Shared.Mobs.Systems; +using Content.Shared.Nutrition.Components; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Melee.Events; +using Content.Shared.Whitelist; +using Robust.Shared.Physics.Events; +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Shared._White.Inventory; + +public abstract partial class SharedWhiteInventorySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + + private void InitializeEquip() + { + SubscribeLocalEvent(OnCollideEvent); + SubscribeLocalEvent(OnMeleeHit); + SubscribeLocalEvent(OnPickedUp); + SubscribeLocalEvent(OnThrowDoHit, before: new[] {typeof(JumpSystem)}); + } + + private void OnCollideEvent(EntityUid uid, EquipOnCollideComponent component, StartCollideEvent args) + { + TryEquip(uid, args.OtherEntity, component); + } + + private void OnMeleeHit(EntityUid uid, EquipOnMeleeHitComponent component, MeleeHitEvent args) + { + if (args.HitEntities.FirstOrNull() is not {} target) + return; + + TryEquip(uid, target, component); + } + + private void OnPickedUp(EntityUid uid, EquipOnPickUpComponent component, GotEquippedHandEvent args) + { + TryEquip(uid, args.User, component); + } + + private void OnThrowDoHit(EntityUid uid, EquipOnThrownHitComponent component, ThrowDoHitEvent args) + { + if (args.Handled) + return; + + TryEquip(uid, args.Target, component); + args.Handled = true; + } + + public bool TryEquip(EntityUid uid, EntityUid target, BaseEquipOnComponent component) + { + if (_mobState.IsDead(uid) + || !_random.Prob(component.EquipProb) + || _entityWhitelist.IsBlacklistPass(component.Blacklist, target) + || _inventory.TryGetSlotEntity(target, component.BlockingSlot, out var headItem) + && TryComp(headItem, out var ingestionBlocker) + && ingestionBlocker.Enabled) + return false; + + if (component.Force) + _inventory.TryUnequip(target, component.Slot, true); + _inventory.TryEquip(target, uid, component.Slot, true, true); + + return true; + } +} diff --git a/Content.Shared/_White/Inventory/SharedWhiteInventorySystem.cs b/Content.Shared/_White/Inventory/SharedWhiteInventorySystem.cs new file mode 100644 index 0000000000..d47d890c32 --- /dev/null +++ b/Content.Shared/_White/Inventory/SharedWhiteInventorySystem.cs @@ -0,0 +1,11 @@ +namespace Content.Shared._White.Inventory; + +public abstract partial class SharedWhiteInventorySystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + InitializeEquip(); + } +} diff --git a/Content.Shared/_White/StatusIcon/StatusIconPrototype.cs b/Content.Shared/_White/StatusIcon/StatusIconPrototype.cs new file mode 100644 index 0000000000..eb45b0ff78 --- /dev/null +++ b/Content.Shared/_White/StatusIcon/StatusIconPrototype.cs @@ -0,0 +1,18 @@ +using Content.Shared.StatusIcon; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; + +namespace Content.Shared._White.StatusIcon; + +[Prototype] +public sealed partial class InfectionIconPrototype : StatusIconPrototype, IInheritingPrototype +{ + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; } +} diff --git a/Content.Shared/_White/Xenomorphs/Egg/XenomorphEggComponent.cs b/Content.Shared/_White/Xenomorphs/Egg/XenomorphEggComponent.cs new file mode 100644 index 0000000000..8cc6fc8142 --- /dev/null +++ b/Content.Shared/_White/Xenomorphs/Egg/XenomorphEggComponent.cs @@ -0,0 +1,65 @@ +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Xenomorphs.Egg; + +[RegisterComponent] +public sealed partial class XenomorphEggComponent : Component +{ + [DataField] + public EntProtoId FaceHuggerPrototype = "MobXenomorphFaceHugger"; + + [DataField] + public float BurstRange = 1f; + + [DataField] + public SoundSpecifier? CleaningSound = new SoundPathSpecifier("/Audio/Animals/Blob/blobattack.ogg"); + + [DataField] + public TimeSpan CheckInRangeDelay = TimeSpan.FromSeconds(1); + + [DataField] + public TimeSpan BurstingDelay = TimeSpan.FromSeconds(1.5f); + + [DataField] + public TimeSpan MaxGrowthTime = TimeSpan.FromSeconds(150); + + [DataField] + public TimeSpan MinGrowthTime = TimeSpan.FromSeconds(90); + + [ViewVariables] + public TimeSpan BurstAt = TimeSpan.Zero; + + [ViewVariables] + public TimeSpan CheckInRangeAt = TimeSpan.Zero; + + [ViewVariables] + public TimeSpan GrownAt = TimeSpan.Zero; + + [ViewVariables] + public XenomorphEggStatus Status = XenomorphEggStatus.Growning; +} + +public enum XenomorphEggStatus : byte +{ + Burst, + Bursting, + Grown, + Growning, +} + +[Serializable, NetSerializable] +public enum XenomorphEggVisualsStatus : byte +{ + Burst, + Bursting, + Grown, + Growning, +} + +[Serializable, NetSerializable] +public enum XenomorphEggKey +{ + Key +} diff --git a/Content.Shared/_White/Xenomorphs/Event.cs b/Content.Shared/_White/Xenomorphs/Event.cs index a3aa302803..4e2c3fcb81 100644 --- a/Content.Shared/_White/Xenomorphs/Event.cs +++ b/Content.Shared/_White/Xenomorphs/Event.cs @@ -5,7 +5,6 @@ using Content.Shared.FixedPoint; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; - namespace Content.Shared._White.Xenomorphs; [Serializable, NetSerializable] @@ -26,6 +25,9 @@ public sealed partial class XenomorphEvolutionDoAfterEvent : DoAfterEvent public override DoAfterEvent Clone() => this; } +[Serializable, NetSerializable] +public sealed partial class LarvaBurstDoAfterEvent : SimpleDoAfterEvent; + public sealed partial class TransferPlasmaActionEvent : EntityTargetActionEvent { [DataField] diff --git a/Content.Shared/_White/Xenomorphs/Infection/SharedXenomorphInfectionComponent.cs b/Content.Shared/_White/Xenomorphs/Infection/SharedXenomorphInfectionComponent.cs new file mode 100644 index 0000000000..1673108a85 --- /dev/null +++ b/Content.Shared/_White/Xenomorphs/Infection/SharedXenomorphInfectionComponent.cs @@ -0,0 +1,21 @@ +using Content.Shared._White.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._White.Xenomorphs.Infection; + +[NetworkedComponent] +public abstract partial class SharedXenomorphInfectionComponent : Component +{ + /// + /// A set of prototype IDs for status icons representing different growth stages of the infection. + /// + [DataField] + public Dictionary> InfectedIcons = new(); + + /// + /// Current stage of infection development. + /// + [ViewVariables(VVAccess.ReadWrite)] + public int GrowthStage; +} diff --git a/Content.Shared/_White/Xenomorphs/Infection/XenomorphInfectedComponent.cs b/Content.Shared/_White/Xenomorphs/Infection/XenomorphInfectedComponent.cs new file mode 100644 index 0000000000..ab732dd16f --- /dev/null +++ b/Content.Shared/_White/Xenomorphs/Infection/XenomorphInfectedComponent.cs @@ -0,0 +1,19 @@ +using Content.Shared._White.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._White.Xenomorphs.Infection; + +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState(true, fieldDeltas: true)] +public sealed partial class XenomorphInfectedComponent : Component +{ + [AutoNetworkedField, ViewVariables] + public Dictionary> InfectedIcons = new(); + + [ViewVariables] + public EntityUid Infection; + + [AutoNetworkedField, ViewVariables] + public int GrowthStage; +} diff --git a/Content.Shared/_White/Xenomorphs/Larva/XenomorphLarvaComponent.cs b/Content.Shared/_White/Xenomorphs/Larva/XenomorphLarvaComponent.cs new file mode 100644 index 0000000000..7576025c32 --- /dev/null +++ b/Content.Shared/_White/Xenomorphs/Larva/XenomorphLarvaComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared._White.Xenomorphs.Larva; + +[RegisterComponent] +public sealed partial class XenomorphLarvaComponent : Component +{ + [DataField] + public TimeSpan BurstDelay = TimeSpan.FromSeconds(5); + + [ViewVariables] + public EntityUid? Victim; +} diff --git a/Content.Shared/_White/Xenomorphs/Larva/XenomorphLarvaVictimComponent.cs b/Content.Shared/_White/Xenomorphs/Larva/XenomorphLarvaVictimComponent.cs new file mode 100644 index 0000000000..0f208ed054 --- /dev/null +++ b/Content.Shared/_White/Xenomorphs/Larva/XenomorphLarvaVictimComponent.cs @@ -0,0 +1,13 @@ +using Content.Shared._White.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._White.Xenomorphs.Larva; + +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState(true)] +public sealed partial class XenomorphLarvaVictimComponent : Component +{ + [AutoNetworkedField, ViewVariables] + public ProtoId? InfectedIcon; +} diff --git a/Resources/Locale/en-US/_white/xenomorphs/egg.ftl b/Resources/Locale/en-US/_white/xenomorphs/egg.ftl new file mode 100644 index 0000000000..c8e6f9f0ad --- /dev/null +++ b/Resources/Locale/en-US/_white/xenomorphs/egg.ftl @@ -0,0 +1 @@ +xenomorph-egg-clean-butsting-egg = You clear the hatched egg. diff --git a/Resources/Locale/en-US/_white/xenomorphs/infection.ftl b/Resources/Locale/en-US/_white/xenomorphs/infection.ftl new file mode 100644 index 0000000000..946e332498 --- /dev/null +++ b/Resources/Locale/en-US/_white/xenomorphs/infection.ftl @@ -0,0 +1,4 @@ +xenomorph-infection-stage-2 = Your chest hurts a little bit. +xenomorph-infection-stage-3 = Your throat feels sore. +xenomorph-infection-stage-4 = Your chest hurts badly. +xenomorph-infection-stage-6 = You feel something crawling through your insides! diff --git a/Resources/Locale/en-US/_white/xenomorphs/larva.ftl b/Resources/Locale/en-US/_white/xenomorphs/larva.ftl new file mode 100644 index 0000000000..1b153605c0 --- /dev/null +++ b/Resources/Locale/en-US/_white/xenomorphs/larva.ftl @@ -0,0 +1,2 @@ +xenomorphs-burst-victim = Your muscles seize up with cramps. You no longer control your body... It's vomiting something horrific! +xenomorphs-burst-other = { $victim } is in agony! Something is bursting out of their chest! diff --git a/Resources/Locale/ru-RU/_white/prototypes/actions/xenomorph.ftl b/Resources/Locale/ru-RU/_white/prototypes/actions/xenomorph.ftl index a870830f65..02cfb19494 100644 --- a/Resources/Locale/ru-RU/_white/prototypes/actions/xenomorph.ftl +++ b/Resources/Locale/ru-RU/_white/prototypes/actions/xenomorph.ftl @@ -7,7 +7,7 @@ ent-ToggleThermalVisionXenomorph = { ent-ActionCombatModeToggle } ent-ActionEvolution = Эволюционировать .desc = Эволюционирует в высшую касту ксеноморфов. -ent-ActionEvolutionPraetorian = Эволюционировать в Преторианца (500) +ent-ActionEvolutionPraetorian = Эволюционировать в преторианца (500) .desc = Эволюционирует в главного защитника улья. ent-ActionEvolutionQueen = Эволюционировать в королеву (500) @@ -16,6 +16,9 @@ ent-ActionEvolutionQueen = Эволюционировать в королеву ent-ActionSpawnResinWeedNode = Посадить траву (50) .desc = Сажает траву ксеноморфов. +ent-ActionSpawnXenomorphEgg = Отложить яйцо (75) + .desc = Отложить яйцо, чтобы произвести на свет лицехватов, с помощью которых можно оплодотворить добычу. + ent-ActionSpawnWallResin = Выделить смолу (50) .desc = Выделяют вязкую, пластичную смолу. diff --git a/Resources/Locale/ru-RU/_white/prototypes/body/organs/animal/xenomorph.ftl b/Resources/Locale/ru-RU/_white/prototypes/body/organs/animal/xenomorph.ftl new file mode 100644 index 0000000000..dffdf6304b --- /dev/null +++ b/Resources/Locale/ru-RU/_white/prototypes/body/organs/animal/xenomorph.ftl @@ -0,0 +1,47 @@ +ent-BaseXenomorphOrgan = { ent-BaseItem } + .desc = { ent-BaseItem.desc } + +ent-OrganXenomorphBrain = мозг ксеноморфа + .desc = Мы едва ли понимаем, как устроен мозг наземных животных. Кто знает, что мы можем найти в мозге столь высокоразвитого вида? + +ent-OrganXenomorphEyes = глаза ксеноморфа + .desc = Оказалось, они у них всё-таки были! + +ent-OrganXenomorphTongue = язык ксеноморфа + .desc = По мнению ведущих ксенобиологов, эволюционное преимущество наличия второго рта заключается в том, что он «выглядит круто». + +ent-OrganXenomorphStomach = желудок ксеноморфа + .desc = Его стенки способны как значительно расширяться, так и сжиматься с огромной силой. + +ent-OrganXenomorphLiver = печень ксеноморфа + .desc = Печень, принадлежавшая инопланетному убийце, который, кто знает, чем питался. + +ent-OrganXenomorphAcidGland = кислотная железа + .desc = Специализированный резервуар с кислотой. Образующаяся едкая смесь способна плавить большинство известных материалов. + +ent-OrganXenomorphNeurotoxinGland = нейротоксиновая железа + .desc = Специализированный резервуар с нейротоксином. Состоит из множества микроскопических трубок, заполненных прозрачной жидкостью. + +ent-OrganXenomorphEggSack = мешок для яиц + .desc = Предназначен для производства и хранения яиц, содержащих лицехватов. Огромные размеры органа позволяют Королеве ксеноморфов откладывать тысячи яиц в течение короткого периода времени. + +ent-OrganXenomorphResinSpinner = cмоляной прядильщик + .desc = Предназначен для производства и выделения смолоподобного биополимера. + +ent-OrganXenomorphHiveNode = узел улья + .desc = Центральный орган ксеноморфов, обеспечивающий связь с общим разумом. + +ent-OrganXenomorphPlasmaVessel = хранилище плазмы + .desc = Резервуар, содержащий концентрированную и высокоактивную плазму — основной источник энергии и сырья для биологического оружия ксеноморфа. + +ent-OrganXenomorphPlasmaVesselLarge = большое хранилище плазмы + .desc = { ent-OrganXenomorphPlasmaVessel.desc } + +ent-OrganXenomorphPlasmaVesselLargeQueen = { ent-OrganXenomorphPlasmaVesselLarge } + .desc = { ent-OrganXenomorphPlasmaVesselLarge.desc } + +ent-OrganXenomorphPlasmaVesselSmall = маленькое хранилище плазмы + .desc = { ent-OrganXenomorphPlasmaVessel.desc } + +ent-OrganXenomorphPlasmaVesselTiny = крошечное хранилище плазмы + .desc = { ent-OrganXenomorphPlasmaVesselTiny.desc } diff --git a/Resources/Locale/ru-RU/_white/prototypes/body/organs/infection.ftl b/Resources/Locale/ru-RU/_white/prototypes/body/organs/infection.ftl new file mode 100644 index 0000000000..c696db5876 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/prototypes/body/organs/infection.ftl @@ -0,0 +1,2 @@ +ent-XenomorphInfection = эмбрион личинки ксеноморфа + .desc = Паразитическая форма жизни на ранних стадиях развития. Имеет червеобразное тело, недоразвитые конечности и хвостовой придаток. diff --git a/Resources/Locale/ru-RU/_white/prototypes/entities/mobs/xenomorphs/facehugger.ftl b/Resources/Locale/ru-RU/_white/prototypes/entities/mobs/xenomorphs/facehugger.ftl new file mode 100644 index 0000000000..5d8c05b4c6 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/prototypes/entities/mobs/xenomorphs/facehugger.ftl @@ -0,0 +1,2 @@ +ent-MobXenomorphFaceHugger = ксеноморф лицехват + .desc = Извивающийся кошмар. Лицехват цепляется за лицо жертвы, насильно внедряя в неё плод ксеноморфа. diff --git a/Resources/Locale/ru-RU/_white/prototypes/entities/structures/furniture/xenomorph_egg.ftl b/Resources/Locale/ru-RU/_white/prototypes/entities/structures/furniture/xenomorph_egg.ftl new file mode 100644 index 0000000000..6a027a920b --- /dev/null +++ b/Resources/Locale/ru-RU/_white/prototypes/entities/structures/furniture/xenomorph_egg.ftl @@ -0,0 +1,2 @@ +ent-XenomorphEgg = яйцо ксеноморфа + .desc = Большое пятнистое яйцо. diff --git a/Resources/Locale/ru-RU/_white/prototypes/entities/surgery/surgeries.ftl b/Resources/Locale/ru-RU/_white/prototypes/entities/surgery/surgeries.ftl new file mode 100644 index 0000000000..433924f68c --- /dev/null +++ b/Resources/Locale/ru-RU/_white/prototypes/entities/surgery/surgeries.ftl @@ -0,0 +1,7 @@ +ent-SurgeryRemoveLarvaEmbryo = Удалить эмбрион личинки + .desc = { ent-SurgeryBase.desc } + .suffix = { ent-SurgeryBase.suffix } + +ent-SurgeryRemoveLarva = Удалить личинку + .desc = { ent-SurgeryBase.desc } + .suffix = { ent-SurgeryBase.suffix } diff --git a/Resources/Locale/ru-RU/_white/xenomorphs/egg.ftl b/Resources/Locale/ru-RU/_white/xenomorphs/egg.ftl new file mode 100644 index 0000000000..abad45ba41 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/xenomorphs/egg.ftl @@ -0,0 +1 @@ +xenomorph-egg-clean-butsting-egg = Вы очищаете вылупившееся яйцо. diff --git a/Resources/Locale/ru-RU/_white/xenomorphs/infection.ftl b/Resources/Locale/ru-RU/_white/xenomorphs/infection.ftl new file mode 100644 index 0000000000..398af92105 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/xenomorphs/infection.ftl @@ -0,0 +1,4 @@ +xenomorph-infection-stage-2 = Немного болит в груди. +xenomorph-infection-stage-3 = Немного першит в горле. +xenomorph-infection-stage-4 = Сильно болит в груди. +xenomorph-infection-stage-6 = Вы чувствуете, как что-то ползает внутри! diff --git a/Resources/Locale/ru-RU/_white/xenomorphs/larva.ftl b/Resources/Locale/ru-RU/_white/xenomorphs/larva.ftl new file mode 100644 index 0000000000..5c6faf0522 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/xenomorphs/larva.ftl @@ -0,0 +1,2 @@ +xenomorphs-burst-victim = Ваши мышцы сводит судорогой. Вы больше не контролируете своё тело… Оно извергает из себя нечто ужасное! +xenomorphs-burst-other = { $victim } в агонии! Что-то вырывается из его груди! diff --git a/Resources/Prototypes/_White/Actions/xenomorph.yml b/Resources/Prototypes/_White/Actions/xenomorph.yml index 4d05d40f97..d09094f73b 100644 --- a/Resources/Prototypes/_White/Actions/xenomorph.yml +++ b/Resources/Prototypes/_White/Actions/xenomorph.yml @@ -32,7 +32,7 @@ - type: entity id: ActionEvolutionPraetorian parent: ActionEvolution - name: Evolve to Praetorian (500) + name: Evolve to praetorian (500) description: Evolve into the main defender of the hive. components: - type: InstantAction @@ -43,7 +43,7 @@ - type: entity id: ActionEvolutionQueen parent: ActionEvolutionPraetorian - name: Evolve to Queen (500) + name: Evolve to queen (500) description: Produce an internal egg sac capable of spawning children. Only one queen can exist at a time. components: - type: InstantAction @@ -52,7 +52,7 @@ # Structures actions - type: entity id: ActionSpawnResinWeedNode - name: Plant Weeds (50) + name: Plant weeds (50) description: Plants some xenomorph weeds. components: - type: InstantAction @@ -62,12 +62,29 @@ icon: { sprite: _White/Interface/Actions/xenomorph.rsi, state: plant_seed } event: !type:SpawnTileEntityActionEvent entity: ResinWeedNode + blockedCollision: SlipLayer - type: PlasmaCostAction +- type: entity + id: ActionSpawnXenomorphEgg + name: Lay egg (75) + description: Lay an egg to produce facehuggers to impregnate prey with. + components: + - type: InstantAction + checkCanInteract: false + itemIconStyle: BigAction + useDelay: 2 + icon: { sprite: _White/Interface/Actions/xenomorph.rsi, state: egg } + event: !type:SpawnTileEntityActionEvent + entity: XenomorphEgg + blockedCollision: TableMask + - type: PlasmaCostAction + plasmaCost: 75 + # TODO: delete it - type: entity id: ActionSpawnWallResin - name: Secrete Resin (50) + name: Secrete resin (50) description: Secrete tough malleable resin. components: - type: WorldTargetAction @@ -79,12 +96,13 @@ event: !type:PlaceTileEntityEvent entity: WallResin length: 0.5 + blockedCollision: FullTileMask - type: PlasmaCostAction # TODO: delete it - type: entity id: ActionSpawnResinMembrane - name: Secrete Resin (50) + name: Secrete resin (50) description: Secrete tough malleable resin. components: - type: WorldTargetAction @@ -96,12 +114,13 @@ event: !type:PlaceTileEntityEvent entity: ResinMembrane length: 0.5 + blockedCollision: GlassLayer - type: PlasmaCostAction # TODO: delete it - type: entity id: ActionSpawnResinNest - name: Secrete Resin (50) + name: Secrete resin (50) description: Secrete tough malleable resin. components: - type: WorldTargetAction @@ -113,12 +132,13 @@ event: !type:PlaceTileEntityEvent entity: ResinNest length: 0.5 + blockedCollision: TableMask - type: PlasmaCostAction # Tail lash action - type: entity id: ActionTailLash - name: Tail Lash + name: Tail lash description: Hit your opponent with your tail. components: - type: WorldTargetAction @@ -132,7 +152,7 @@ # Acid action - type: entity id: ActionAcid - name: Corrosive Acid (200) + name: Corrosive acid (200) description: Drench an object in acid, destroying it over time. components: - type: EntityTargetAction @@ -160,7 +180,7 @@ # Transfer plasma action - type: entity id: ActionTransferPlasma - name: Transfer Plasma (50) + name: Transfer plasma (50) description: Passes plasma to your friend. components: - type: EntityTargetAction diff --git a/Resources/Prototypes/_White/Body/Organs/Animal/xenomorph.yml b/Resources/Prototypes/_White/Body/Organs/Animal/xenomorph.yml index 5627e0296f..e2e61fca3a 100644 --- a/Resources/Prototypes/_White/Body/Organs/Animal/xenomorph.yml +++ b/Resources/Prototypes/_White/Body/Organs/Animal/xenomorph.yml @@ -92,7 +92,7 @@ - type: XenomorphAcid damagePerSecond: types: - Caustic: 5 + Caustic: 20 - type: entity parent: BaseXenomorphOrgan @@ -108,7 +108,7 @@ - type: RechargeBasicEntityAmmo rechargeCooldown: 0.75 - type: BasicEntityAmmoProvider - proto: BulletAcid + proto: BulletAcidLarge capacity: 1 count: 1 - type: Gun @@ -123,6 +123,7 @@ parent: BaseXenomorphOrgan id: OrganXenomorphEggSack name: egg sack + description: Designed to produce and store eggs containing facehuggers, the organ's enormous size allows the xenomorph queen to lay thousands of eggs in a short period of time. components: - type: Sprite sprite: _White/Mobs/Species/Xenomorph/organs.rsi @@ -135,7 +136,7 @@ parent: BaseXenomorphOrgan id: OrganXenomorphResinSpinner name: resin spinner - description: The central organ of the xenomorphs, providing communication with the common mind. + description: Designed for the production and isolation of resin-like biopolymer. components: - type: Sprite state: resin_spinner @@ -187,7 +188,6 @@ - type: entity parent: OrganXenomorphPlasmaVesselLarge id: OrganXenomorphPlasmaVesselLargeQueen - name: large plasma vessel components: - type: Organ onAdd: diff --git a/Resources/Prototypes/_White/Body/Organs/infection.yml b/Resources/Prototypes/_White/Body/Organs/infection.yml new file mode 100644 index 0000000000..8be99fe57b --- /dev/null +++ b/Resources/Prototypes/_White/Body/Organs/infection.yml @@ -0,0 +1,109 @@ +- type: entity + parent: BaseItem + id: XenomorphInfection + name: xenomorph larva embryo + description: A parasitic life form in its early stages of development. It has a worm-like body shape, underdeveloped limbs, and a tail appendage. + components: + - type: Sprite + sprite: _White/Mobs/Species/Xenomorph/organs.rsi + state: embryon + - type: Organ + slotId: xenomorph_larva + - type: StaticPrice + price: 2000 + - type: XenomorphInfection + maxGrowthStage: 6 + growProb: 0.5 + infectedIcons: + 0: XenomorphInfectionIconStageZero + 1: XenomorphInfectionIconStageOne + 2: XenomorphInfectionIconStageTwo + 3: XenomorphInfectionIconStageThree + 4: XenomorphInfectionIconStageFour + 5: XenomorphInfectionIconStageFive + 6: XenomorphInfectionIconStageSix + effects: + 2: + - !type:PopupMessage + messages: [ "xenomorph-infection-stage-2" ] + visualType: SmallCaution + - !type:Paralyze + paralyzeTime: 1 + - !type:GenericStatusEffect + key: Muted + component: Muted + type: Add + time: 1 + refresh: true + - !type:GenericStatusEffect + key: TemporaryBlindness + component: TemporaryBlindness + type: Add + time: 1 + refresh: true + - !type:Jitter + time: 2 + 3: + - !type:PopupMessage + messages: [ "xenomorph-infection-stage-3" ] + visualType: SmallCaution + - !type:Paralyze + paralyzeTime: 2 + - !type:GenericStatusEffect + key: Muted + component: Muted + type: Add + time: 2 + refresh: true + - !type:GenericStatusEffect + key: TemporaryBlindness + component: TemporaryBlindness + type: Add + time: 2 + refresh: true + - !type:Jitter + time: 4 + 4: + - !type:PopupMessage + messages: [ "xenomorph-infection-stage-4" ] + visualType: SmallCaution + - !type:Paralyze + paralyzeTime: 4 + - !type:GenericStatusEffect + key: Muted + component: Muted + type: Add + time: 4 + refresh: true + - !type:GenericStatusEffect + key: TemporaryBlindness + component: TemporaryBlindness + type: Add + time: 4 + refresh: true + - !type:Jitter + time: 8 + 6: + - !type:PopupMessage + messages: [ "xenomorph-infection-stage-6" ] + visualType: Medium + - !type:Paralyze + paralyzeTime: 8 + - !type:GenericStatusEffect + key: Muted + component: Muted + type: Add + time: 8 + refresh: true + - !type:GenericStatusEffect + key: TemporaryBlindness + component: TemporaryBlindness + type: Add + time: 8 + refresh: true + - !type:HealthChange + damage: + types: + Blunt: 40 + - !type:Jitter + time: 16 diff --git a/Resources/Prototypes/_White/Damage/modifier_sets.yml b/Resources/Prototypes/_White/Damage/modifier_sets.yml index 10b6dec2e4..ea275a1f82 100644 --- a/Resources/Prototypes/_White/Damage/modifier_sets.yml +++ b/Resources/Prototypes/_White/Damage/modifier_sets.yml @@ -11,11 +11,11 @@ - type: damageModifierSet id: Xenomorph coefficients: - Blunt: 0.85 + Blunt: 0.80 Slash: 0.80 - Piercing: 1.2 + Piercing: 0.80 Cold: 0 - Heat: 1.8 + Heat: 1.2 Poison: 0 - type: damageModifierSet @@ -23,9 +23,9 @@ coefficients: Blunt: 1 Slash: 1 - Piercing: 1.4 + Piercing: 1 Cold: 0 - Heat: 2.2 + Heat: 1.5 Poison: 0 - type: damageModifierSet @@ -34,5 +34,5 @@ Blunt: 0.3 Slash: 0.5 Piercing: 0.8 - Heat: 2.0 + Heat: 1.5 Shock: 0 diff --git a/Resources/Prototypes/_White/Entities/Mobs/NPCs/headcrab.yml b/Resources/Prototypes/_White/Entities/Mobs/NPCs/headcrab.yml index 7a223647a3..8532d317f2 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/NPCs/headcrab.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/NPCs/headcrab.yml @@ -29,9 +29,21 @@ thresholds: 0: Alive 50: Dead + - type: EquipOnThrownHit + slot: mask + blockingSlot: head + blacklist: + components: + - Ghost + - type: EquipOnMeleeHit + slot: mask + blockingSlot: head + equipProb: 0.33 + blacklist: + components: + - Ghost - type: Headcrab paralyzeTime: 3 - chancePounce: 33 damage: types: Piercing: 2 diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/base.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/base.yml index 8c45b89f66..0915c94b48 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/base.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/base.yml @@ -1,7 +1,10 @@ - type: entity - parent: SimpleSpaceMobBase + parent: + - BaseSimpleMob + - MobCombat + - MobBloodstream + - MobFlammable id: BaseMobXenomorph - suffix: "" abstract: true components: - type: Xenomorph @@ -40,17 +43,17 @@ - type: MobThresholds thresholds: 0: Alive - 125: Critical - 135: Dead + 185: Critical + 200: Dead stateAlertDict: Alive: XenomorphHealth Critical: XenomorphCrit Dead: XenomorphDead - type: SlowOnDamage speedModifierThresholds: - 70: 0.9 - 85: 0.7 - 100: 0.6 + 105: 0.9 + 125: 0.7 + 150: 0.6 - type: Damageable damageContainer: Biological damageModifierSet: Xenomorph @@ -67,10 +70,6 @@ - MobMask layer: - MobLayer - - type: Tag - tags: - - DoorBumpOpener - - FootstepSound - type: Bloodstream bloodReagent: FluorosulfuricAcid bloodMaxVolume: 250 @@ -88,7 +87,7 @@ animation: WeaponArcBite damage: groups: - Brute: 20 + Brute: 25 - type: TypingIndicator proto: alien - type: Temperature @@ -159,6 +158,10 @@ defaultRotation: 90 horizontalRotation: 90 - type: Dna + - type: Tag + tags: + - DoorBumpOpener + - FootstepSound - type: entity id: BaseMobXenomorphDungeon diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/drone.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/drone.yml index 802e363d39..a7e41f92e3 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/drone.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/drone.yml @@ -71,6 +71,6 @@ baseSprintSpeed: 4 - type: SlowOnDamage speedModifierThresholds: - 70: 0.7 - 85: 0.6 - 100: 0.5 + 105: 0.7 + 125: 0.6 + 150: 0.5 diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/facehugger.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/facehugger.yml new file mode 100644 index 0000000000..1c2f18d245 --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/facehugger.yml @@ -0,0 +1,115 @@ +- type: entity + parent: [BaseMob, ClothingMaskBase, MobDamageable] + id: MobXenomorphFaceHugger + name: xenomorph facehugger + description: A writhing nightmare. The facehugger clings to its victim's face, forcibly inserting a xenomorph fetus. + components: + - type: Sprite + sprite: _White/Mobs/Aliens/Xenomorphs/facehugger.rsi + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: facehugger + - type: Clothing + sprite: _White/Mobs/Aliens/Xenomorphs/facehugger.rsi + slots: [mask] + - type: IngestionBlocker + - type: Blindfold + - type: AddAccentClothing + accent: ReplacementAccent + replacement: mumble + - type: Damageable + damageContainer: Biological + damageModifierSet: Xenomorph + - type: MobThresholds + thresholds: + 0: Alive + 10: Dead + stateAlertDict: + Alive: XenomorphHealth + Critical: XenomorphCrit + Dead: XenomorphDead + - type: DamageStateVisuals + rotate: true + states: + Alive: + Base: facehugger + Dead: + Base: facehugger_dead + - type: MeleeWeapon + hidden: true + canHeavyAttack: false + angle: 0 + damage: + types: + Blunt: 0 + - type: Temperature + heatDamageThreshold: 360 + coldDamageThreshold: -150 + currentTemperature: 310.15 + - type: EquipOnCollide + slot: mask + blockingSlot: head + blacklist: + components: + - XenomorphInfected + - XenomorphLarvaVictim + - Ghost + - type: EquipOnPickUp + slot: mask + blockingSlot: head + blacklist: + components: + - XenomorphInfected + - XenomorphLarvaVictim + - Ghost + - type: EquipOnMeleeHit + slot: mask + blockingSlot: head + blacklist: + components: + - XenomorphInfected + - XenomorphLarvaVictim + - Ghost + - type: EffectsOnEquip + slot: mask + blacklist: + components: + - XenomorphInfected + - XenomorphLarvaVictim + - Ghost + effects: + - !type:XenomorphInfection + - !type:Paralyze + paralyzeTime: 5 + - type: NameIdentifier + group: GenericNumber + - type: NpcFactionMember + factions: + - Xeno + - type: Tag + tags: + - XenomorphItem + - type: Physics + bodyType: Dynamic + - type: CollisionWake + enabled: false + - type: RequireProjectileTarget + active: true + - type: Fixtures + fixtures: + slips: + shape: + !type:PhysShapeCircle + radius: 0.35 + layer: + - SlipLayer + hard: false + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 100 + mask: + - SmallMobMask + layer: + - SmallMobLayer diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/hunter.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/hunter.yml index 9137d2a070..1037b65c81 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/hunter.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/hunter.yml @@ -78,6 +78,6 @@ baseSprintSpeed: 4.8 - type: SlowOnDamage speedModifierThresholds: - 70: 0.7 - 85: 0.6 - 100: 0.5 + 105: 0.7 + 125: 0.6 + 150: 0.5 diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/larva.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/larva.yml index 9e2f3ccac5..de7cbb1854 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/larva.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/larva.yml @@ -20,6 +20,7 @@ state: xenomorph - type: Xenomorph caste: Larva + - type: XenomorphLarva - type: ActionGrant actions: - ActionTransferPlasma @@ -33,7 +34,7 @@ radius: 0.2 density: 100 mask: - - FlyingMobMask + - SmallMobMask layer: - SmallMobLayer - type: Deathgasp @@ -55,6 +56,8 @@ bloodMaxVolume: 50 - type: MobPrice price: 250 + - type: Organ + slotId: xenomorph_larva # - type: GuideHelp # guides: # - XenomorphLarva diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/maid.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/maid.yml index 7ebbff3d6e..bc10f8ce03 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/maid.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/maid.yml @@ -3,7 +3,6 @@ id: MobXenomorphMaid name: lusty xenomorph maid description: Don't ask it to lay eggs in you. - abstract: true components: - type: Sprite sprite: _White/Mobs/Aliens/Xenomorphs/maid.rsi diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/praetorian.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/praetorian.yml index bc33973a50..de6c1ba5ee 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/praetorian.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/praetorian.yml @@ -46,13 +46,13 @@ - type: MobThresholds thresholds: 0: Alive - 240: Critical - 250: Dead + 360: Critical + 375: Dead - type: SlowOnDamage speedModifierThresholds: - 150: 0.9 - 180: 0.7 - 210: 0.6 + 225: 0.9 + 270: 0.7 + 315: 0.6 - type: MeleeWeapon damage: groups: @@ -90,6 +90,6 @@ baseSprintSpeed: 3.8 - type: SlowOnDamage speedModifierThresholds: - 150: 0.7 - 180: 0.6 - 210: 0.5 + 225: 0.7 + 270: 0.6 + 315: 0.5 diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/queen.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/queen.yml index 109a287ff3..d1ca3a5e2c 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/queen.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/queen.yml @@ -28,6 +28,7 @@ - ActionSpawnResinMembrane - ActionSpawnResinNest - ActionSpawnResinWeedNode + - ActionSpawnXenomorphEgg - type: Body prototype: XenomorphQueen - type: TypingIndicator @@ -46,13 +47,13 @@ - type: MobThresholds thresholds: 0: Alive - 490: Critical - 500: Dead + 735: Critical + 750: Dead - type: SlowOnDamage speedModifierThresholds: - 410: 0.9 - 430: 0.7 - 460: 0.6 + 600: 0.9 + 645: 0.7 + 690: 0.6 - type: MeleeWeapon damage: groups: @@ -102,6 +103,6 @@ baseSprintSpeed: 3.8 - type: SlowOnDamage speedModifierThresholds: - 410: 0.7 - 430: 0.6 - 460: 0.5 + 600: 0.7 + 645: 0.6 + 690: 0.5 diff --git a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/sentinel.yml b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/sentinel.yml index bbaf87cded..b02aea8fe8 100644 --- a/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/sentinel.yml +++ b/Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/sentinel.yml @@ -29,13 +29,13 @@ - type: MobThresholds thresholds: 0: Alive - 150: Critical - 160: Dead + 225: Critical + 240: Dead - type: SlowOnDamage speedModifierThresholds: - 100: 0.9 - 115: 0.7 - 130: 0.6 + 150: 0.9 + 170: 0.7 + 195: 0.6 # - type: GuideHelp # guides: # - XenomorphSentinel @@ -65,6 +65,6 @@ baseSprintSpeed: 3.2 - type: SlowOnDamage speedModifierThresholds: - 100: 0.7 - 115: 0.6 - 130: 0.5 + 150: 0.7 + 170: 0.6 + 195: 0.5 diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml new file mode 100644 index 0000000000..99eb7060ab --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -0,0 +1,9 @@ +- type: entity + id: BulletAcidLarge + parent: BulletAcid + categories: [ HideSpawnMenu ] + components: + - type: Projectile + damage: + types: + Caustic: 35 diff --git a/Resources/Prototypes/_White/Entities/Structures/Furniture/resin_nest.yml b/Resources/Prototypes/_White/Entities/Structures/Furniture/resin_nest.yml index 6f683570b1..9044cdb00b 100644 --- a/Resources/Prototypes/_White/Entities/Structures/Furniture/resin_nest.yml +++ b/Resources/Prototypes/_White/Entities/Structures/Furniture/resin_nest.yml @@ -24,6 +24,7 @@ mask: - TableMask layer: + - TableLayer - BulletImpassable - type: RequireProjectileTarget - type: Damageable diff --git a/Resources/Prototypes/_White/Entities/Structures/Furniture/xenomorph_egg.yml b/Resources/Prototypes/_White/Entities/Structures/Furniture/xenomorph_egg.yml new file mode 100644 index 0000000000..70be83c964 --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Structures/Furniture/xenomorph_egg.yml @@ -0,0 +1,70 @@ +- type: entity + parent: BaseStructure + id: XenomorphEgg + name: xenomorph egg + description: A large mottled egg. + components: + - type: Sprite + sprite: _White/Structures/Furniture/xenomorph_egg.rsi + noRot: true + layers: + - state: egg_growing + map: [ "status" ] + - type: Icon + sprite: _White/Structures/Furniture/xenomorph_egg.rsi + state: egg_growing + - type: Appearance + - type: GenericVisualizer + visuals: + enum.XenomorphEggKey.Key: + status: + Burst: { state: egg_burst } + Bursting: { state: egg_bursting } + Grown: { state: egg_grown } + Growning: { state: egg_growing } + - type: Damageable + damageContainer: Biological + damageModifierSet: Resin + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: blood + - trigger: + !type:DamageTypeTrigger + damageType: Heat + damage: 45 + behaviors: + - !type:SpawnEntitiesBehavior + spawnInContainer: true + spawn: + Ash: + min: 0 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: Flammable + fireSpread: false + damage: + types: + Heat: 3 + - type: XenomorphEgg + - type: RequireProjectileTarget + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.45,-0.45,0.45,0.05" + density: 190 + mask: + - TableMask + layer: + - TableLayer + - BulletImpassable diff --git a/Resources/Prototypes/_White/Entities/Surgery/surgeries.yml b/Resources/Prototypes/_White/Entities/Surgery/surgeries.yml new file mode 100644 index 0000000000..f94c05be72 --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Surgery/surgeries.yml @@ -0,0 +1,56 @@ +- type: entity + parent: SurgeryBase + id: SurgeryRemoveLarvaEmbryo + name: Remove Larva Embryo + categories: [ HideSpawnMenu ] + components: + - type: Surgery + requirement: SurgeryOpenRibcage + steps: + - SurgeryStepClampInternalBleeders + - SurgeryStepRemoveOrgan + - type: SurgeryPartCondition + part: Torso + - type: SurgeryOrganCondition + organ: + - type: XenomorphInfection + slotId: xenomorph_larva + +# - type: entity +# parent: SurgeryBase +# id: SurgeryInsertLarvaEmbryo +# name: Insert Larva +# categories: [ HideSpawnMenu ] +# components: +# - type: Surgery +# requirement: SurgeryOpenRibcage +# steps: +# - SurgeryStepSawBones +# - SurgeryStepInsertOrgan +# - SurgeryStepSealOrganWound +# - type: SurgeryPartCondition +# part: Torso +# - type: SurgeryOrganCondition +# organ: +# - type: XenomorphInfection +# inverse: true +# reattaching: true +# slotId: xenomorph_larva + +- type: entity + parent: SurgeryBase + id: SurgeryRemoveLarva + name: Remove Larva + categories: [ HideSpawnMenu ] + components: + - type: Surgery + requirement: SurgeryOpenRibcage + steps: + - SurgeryStepClampInternalBleeders + - SurgeryStepRemoveOrgan + - type: SurgeryPartCondition + part: Torso + - type: SurgeryOrganCondition + organ: + - type: XenomorphLarva + slotId: xenomorph_larva diff --git a/Resources/Prototypes/_White/StatusIcon/infection.yml b/Resources/Prototypes/_White/StatusIcon/infection.yml new file mode 100644 index 0000000000..219790d268 --- /dev/null +++ b/Resources/Prototypes/_White/StatusIcon/infection.yml @@ -0,0 +1,92 @@ +# Xenomorph + +- type: infectionIcon + id: XenomorphInfectionIconStageZero + priority: 0 + locationPreference: Right + isShaded: true + visibleToOwner: true + showTo: + components: + - Xenomorph + icon: + sprite: /Textures/_White/Interface/Misc/infection_icons.rsi + state: xenomorph_0 + +- type: infectionIcon + id: XenomorphInfectionIconStageOne + priority: 0 + locationPreference: Right + isShaded: true + visibleToOwner: true + showTo: + components: + - Xenomorph + icon: + sprite: /Textures/_White/Interface/Misc/infection_icons.rsi + state: xenomorph_1 + +- type: infectionIcon + id: XenomorphInfectionIconStageTwo + priority: 0 + locationPreference: Right + isShaded: true + visibleToOwner: true + showTo: + components: + - Xenomorph + icon: + sprite: /Textures/_White/Interface/Misc/infection_icons.rsi + state: xenomorph_2 + +- type: infectionIcon + id: XenomorphInfectionIconStageThree + priority: 0 + locationPreference: Right + isShaded: true + visibleToOwner: true + showTo: + components: + - Xenomorph + icon: + sprite: /Textures/_White/Interface/Misc/infection_icons.rsi + state: xenomorph_3 + +- type: infectionIcon + id: XenomorphInfectionIconStageFour + priority: 0 + locationPreference: Right + isShaded: true + visibleToOwner: true + showTo: + components: + - Xenomorph + icon: + sprite: /Textures/_White/Interface/Misc/infection_icons.rsi + state: xenomorph_4 + +- type: infectionIcon + id: XenomorphInfectionIconStageFive + priority: 0 + locationPreference: Right + isShaded: true + visibleToOwner: true + showTo: + components: + - Xenomorph + icon: + sprite: /Textures/_White/Interface/Misc/infection_icons.rsi + state: xenomorph_5 + +- type: infectionIcon + id: XenomorphInfectionIconStageSix + priority: 0 + locationPreference: Right + isShaded: true + visibleToOwner: true + showTo: + components: + - Xenomorph + icon: + sprite: /Textures/_White/Interface/Misc/infection_icons.rsi + state: xenomorph_6 diff --git a/Resources/Textures/_White/Interface/Actions/xenomorph.rsi/egg.png b/Resources/Textures/_White/Interface/Actions/xenomorph.rsi/egg.png new file mode 100644 index 0000000000..989b658725 Binary files /dev/null and b/Resources/Textures/_White/Interface/Actions/xenomorph.rsi/egg.png differ diff --git a/Resources/Textures/_White/Interface/Actions/xenomorph.rsi/meta.json b/Resources/Textures/_White/Interface/Actions/xenomorph.rsi/meta.json index 1f7fd4cbac..a38ce8eb91 100644 --- a/Resources/Textures/_White/Interface/Actions/xenomorph.rsi/meta.json +++ b/Resources/Textures/_White/Interface/Actions/xenomorph.rsi/meta.json @@ -37,6 +37,9 @@ { "name": "acid" }, + { + "name": "egg" + }, { "name": "evolve" }, diff --git a/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/meta.json b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/meta.json new file mode 100644 index 0000000000..21c5db6394 --- /dev/null +++ b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/meta.json @@ -0,0 +1,42 @@ + +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from cmss13 at https://github.com/cmss13-devs/cmss13/blob/029cdbdae1a118c40b558a75e8171fbd49d560a6/icons/mob/hud/hud.dmi | Edited by Spatison (Github)", + "size": { + "x": 8, + "y": 8 + }, + "states": [ + { + "name": "xenomorph_0" + }, + { + "name": "xenomorph_1" + }, + { + "name": "xenomorph_2" + }, + { + "name": "xenomorph_3" + }, + { + "name": "xenomorph_4" + }, + { + "name": "xenomorph_5" + }, + { + "name": "xenomorph_6", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + } + ] +} diff --git a/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_0.png b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_0.png new file mode 100644 index 0000000000..b413a93f93 Binary files /dev/null and b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_0.png differ diff --git a/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_1.png b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_1.png new file mode 100644 index 0000000000..b413a93f93 Binary files /dev/null and b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_1.png differ diff --git a/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_2.png b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_2.png new file mode 100644 index 0000000000..2a9b97a785 Binary files /dev/null and b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_2.png differ diff --git a/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_3.png b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_3.png new file mode 100644 index 0000000000..2a9b97a785 Binary files /dev/null and b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_3.png differ diff --git a/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_4.png b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_4.png new file mode 100644 index 0000000000..003df36392 Binary files /dev/null and b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_4.png differ diff --git a/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_5.png b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_5.png new file mode 100644 index 0000000000..003df36392 Binary files /dev/null and b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_5.png differ diff --git a/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_6.png b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_6.png new file mode 100644 index 0000000000..74bd8f8321 Binary files /dev/null and b/Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_6.png differ diff --git a/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/equipped-MASK.png b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/equipped-MASK.png new file mode 100644 index 0000000000..b9cb635ca7 Binary files /dev/null and b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/equipped-MASK.png differ diff --git a/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/facehugger.png b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/facehugger.png new file mode 100644 index 0000000000..361ee97a4f Binary files /dev/null and b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/facehugger.png differ diff --git a/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/facehugger_dead.png b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/facehugger_dead.png new file mode 100644 index 0000000000..07e4240f1e Binary files /dev/null and b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/facehugger_dead.png differ diff --git a/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/inhand-left.png b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/inhand-left.png new file mode 100644 index 0000000000..a9a5abba0a Binary files /dev/null and b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/inhand-left.png differ diff --git a/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/inhand-right.png b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/inhand-right.png new file mode 100644 index 0000000000..bf322080f8 Binary files /dev/null and b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/inhand-right.png differ diff --git a/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/meta.json b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/meta.json new file mode 100644 index 0000000000..9d1edfe080 --- /dev/null +++ b/Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/meta.json @@ -0,0 +1,56 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation: https://github.com/tgstation/tgstation/blob/master/icons/mob/nonhuman-player/alien.dmi | Edited by PuroSlavKing (Github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "facehugger", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "facehugger_dead" + }, + { + "name": "equipped-MASK", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_White/Mobs/Species/Xenomorph/organs.rsi/embryon.png b/Resources/Textures/_White/Mobs/Species/Xenomorph/organs.rsi/embryon.png new file mode 100644 index 0000000000..b12cb7a52b Binary files /dev/null and b/Resources/Textures/_White/Mobs/Species/Xenomorph/organs.rsi/embryon.png differ diff --git a/Resources/Textures/_White/Mobs/Species/Xenomorph/organs.rsi/meta.json b/Resources/Textures/_White/Mobs/Species/Xenomorph/organs.rsi/meta.json index e392dad899..fc0cd994f0 100644 --- a/Resources/Textures/_White/Mobs/Species/Xenomorph/organs.rsi/meta.json +++ b/Resources/Textures/_White/Mobs/Species/Xenomorph/organs.rsi/meta.json @@ -16,6 +16,9 @@ { "name": "egg_sack" }, + { + "name": "embryon" + }, { "name": "eyeball-l" }, diff --git a/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_burst.png b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_burst.png new file mode 100644 index 0000000000..12543880fb Binary files /dev/null and b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_burst.png differ diff --git a/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_bursting.png b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_bursting.png new file mode 100644 index 0000000000..cb764baa2d Binary files /dev/null and b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_bursting.png differ diff --git a/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_growing.png b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_growing.png new file mode 100644 index 0000000000..0a170d6def Binary files /dev/null and b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_growing.png differ diff --git a/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_grown.png b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_grown.png new file mode 100644 index 0000000000..00f63624c7 Binary files /dev/null and b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_grown.png differ diff --git a/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/meta.json b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/meta.json new file mode 100644 index 0000000000..9e530e69de --- /dev/null +++ b/Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/meta.json @@ -0,0 +1,42 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation: https://github.com/tgstation/tgstation/blob/master/icons/mob/nonhuman-player/alien.dmi | Edited by Kai518 (Github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "egg_burst" + }, + { + "name": "egg_bursting", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "egg_growing" + }, + { + "name": "egg_grown" + } + ] +}