From a82b412ef25d6557347e90474e565e95f88bfe79 Mon Sep 17 00:00:00 2001 From: Spatison <137375981+Spatison@users.noreply.github.com> Date: Mon, 4 Aug 2025 02:31:17 +1000 Subject: [PATCH] Xenomorphs: part 2 (#744) * rebalance * oh * looks better * Finally --- Content.Client/StatusIcon/StatusIconSystem.cs | 3 +- .../_White/Inventory/WhiteInventorySystem.cs | 5 + .../{MindSlave => }/MindSlaveIconsSystem.cs | 10 +- .../Overlays/XenomorphInfectionIconSystem.cs | 32 +++++ .../Infection/XenomorphInfectionComponent.cs | 6 + .../_White/Actions/ActionsSystem.cs | 37 +++-- .../Effects/XenomorphInfection.cs | 31 ++++ .../GameTicking/Rules/XenomorphsRuleSystem.cs | 10 ++ .../_White/Headcrab/HeadcrabComponent.cs | 3 - .../_White/Headcrab/HeadcrabSystem.cs | 42 ------ .../Components/EffectsOnEquipComponent.cs | 17 +++ .../Inventory/WhiteInventorySystem.Equip.cs | 26 ++++ .../_White/Inventory/WhiteInventorySystem.cs | 13 ++ .../Xenomorphs/Egg/XenomorphEggSystem.cs | 133 ++++++++++++++++++ .../Evolution/XenomorphEvolutionComponent.cs | 2 +- .../Evolution/XenomorphEvolutionSystem.cs | 2 +- .../Infection/XenomorphInfectionComponent.cs | 37 +++++ .../Infection/XenomorphInfectionSystem.cs | 107 ++++++++++++++ .../Xenomorphs/Larva/XenomorphLarvaSystem.cs | 79 +++++++++++ .../Body/Systems/SharedBodySystem.Organs.cs | 1 + .../StatusIcon/StatusIconPrototype.cs | 8 ++ .../_White/Actions/Events/SpawnActionEvent.cs | 7 + .../Implants/MindSlave/MindSlaveComponent.cs | 17 +-- .../Components/BaseEquipOnComponent.cs | 22 +++ .../Components/EquipOnCollideComponent.cs | 4 + .../Components/EquipOnMeleeHitComponent.cs | 4 + .../Components/EquipOnPickUpComponent.cs | 4 + .../Components/EquipOnThrownHitComponent.cs | 4 + .../SharedWhiteInventorySystem.Equip.cs | 75 ++++++++++ .../Inventory/SharedWhiteInventorySystem.cs | 11 ++ .../_White/StatusIcon/StatusIconPrototype.cs | 18 +++ .../Xenomorphs/Egg/XenomorphEggComponent.cs | 65 +++++++++ Content.Shared/_White/Xenomorphs/Event.cs | 4 +- .../SharedXenomorphInfectionComponent.cs | 21 +++ .../Infection/XenomorphInfectedComponent.cs | 19 +++ .../Larva/XenomorphLarvaComponent.cs | 11 ++ .../Larva/XenomorphLarvaVictimComponent.cs | 13 ++ .../Locale/en-US/_white/xenomorphs/egg.ftl | 1 + .../en-US/_white/xenomorphs/infection.ftl | 4 + .../Locale/en-US/_white/xenomorphs/larva.ftl | 2 + .../_white/prototypes/actions/xenomorph.ftl | 5 +- .../body/organs/animal/xenomorph.ftl | 47 +++++++ .../prototypes/body/organs/infection.ftl | 2 + .../entities/mobs/xenomorphs/facehugger.ftl | 2 + .../structures/furniture/xenomorph_egg.ftl | 2 + .../prototypes/entities/surgery/surgeries.ftl | 7 + .../Locale/ru-RU/_white/xenomorphs/egg.ftl | 1 + .../ru-RU/_white/xenomorphs/infection.ftl | 4 + .../Locale/ru-RU/_white/xenomorphs/larva.ftl | 2 + .../Prototypes/_White/Actions/xenomorph.yml | 38 +++-- .../_White/Body/Organs/Animal/xenomorph.yml | 8 +- .../_White/Body/Organs/infection.yml | 109 ++++++++++++++ .../_White/Damage/modifier_sets.yml | 12 +- .../_White/Entities/Mobs/NPCs/headcrab.yml | 14 +- .../_White/Entities/Mobs/Xenomorphs/base.yml | 27 ++-- .../_White/Entities/Mobs/Xenomorphs/drone.yml | 6 +- .../Entities/Mobs/Xenomorphs/facehugger.yml | 115 +++++++++++++++ .../Entities/Mobs/Xenomorphs/hunter.yml | 6 +- .../_White/Entities/Mobs/Xenomorphs/larva.yml | 5 +- .../_White/Entities/Mobs/Xenomorphs/maid.yml | 1 - .../Entities/Mobs/Xenomorphs/praetorian.yml | 16 +-- .../_White/Entities/Mobs/Xenomorphs/queen.yml | 17 +-- .../Entities/Mobs/Xenomorphs/sentinel.yml | 16 +-- .../Weapons/Guns/Projectiles/projectiles.yml | 9 ++ .../Structures/Furniture/resin_nest.yml | 1 + .../Structures/Furniture/xenomorph_egg.yml | 70 +++++++++ .../_White/Entities/Surgery/surgeries.yml | 56 ++++++++ .../_White/StatusIcon/infection.yml | 92 ++++++++++++ .../Interface/Actions/xenomorph.rsi/egg.png | Bin 0 -> 397 bytes .../Interface/Actions/xenomorph.rsi/meta.json | 3 + .../Misc/infection_icons.rsi/meta.json | 42 ++++++ .../Misc/infection_icons.rsi/xenomorph_0.png | Bin 0 -> 131 bytes .../Misc/infection_icons.rsi/xenomorph_1.png | Bin 0 -> 131 bytes .../Misc/infection_icons.rsi/xenomorph_2.png | Bin 0 -> 177 bytes .../Misc/infection_icons.rsi/xenomorph_3.png | Bin 0 -> 177 bytes .../Misc/infection_icons.rsi/xenomorph_4.png | Bin 0 -> 233 bytes .../Misc/infection_icons.rsi/xenomorph_5.png | Bin 0 -> 233 bytes .../Misc/infection_icons.rsi/xenomorph_6.png | Bin 0 -> 577 bytes .../facehugger.rsi/equipped-MASK.png | Bin 0 -> 713 bytes .../Xenomorphs/facehugger.rsi/facehugger.png | Bin 0 -> 1847 bytes .../facehugger.rsi/facehugger_dead.png | Bin 0 -> 711 bytes .../Xenomorphs/facehugger.rsi/inhand-left.png | Bin 0 -> 602 bytes .../facehugger.rsi/inhand-right.png | Bin 0 -> 598 bytes .../Xenomorphs/facehugger.rsi/meta.json | 56 ++++++++ .../Species/Xenomorph/organs.rsi/embryon.png | Bin 0 -> 526 bytes .../Species/Xenomorph/organs.rsi/meta.json | 3 + .../Furniture/xenomorph_egg.rsi/egg_burst.png | Bin 0 -> 795 bytes .../xenomorph_egg.rsi/egg_bursting.png | Bin 0 -> 3086 bytes .../xenomorph_egg.rsi/egg_growing.png | Bin 0 -> 580 bytes .../Furniture/xenomorph_egg.rsi/egg_grown.png | Bin 0 -> 853 bytes .../Furniture/xenomorph_egg.rsi/meta.json | 42 ++++++ 91 files changed, 1610 insertions(+), 136 deletions(-) create mode 100644 Content.Client/_White/Inventory/WhiteInventorySystem.cs rename Content.Client/_White/Overlays/{MindSlave => }/MindSlaveIconsSystem.cs (78%) create mode 100644 Content.Client/_White/Overlays/XenomorphInfectionIconSystem.cs create mode 100644 Content.Client/_White/Xenomorphs/Infection/XenomorphInfectionComponent.cs create mode 100644 Content.Server/_White/EntityEffects/Effects/XenomorphInfection.cs create mode 100644 Content.Server/_White/Inventory/Components/EffectsOnEquipComponent.cs create mode 100644 Content.Server/_White/Inventory/WhiteInventorySystem.Equip.cs create mode 100644 Content.Server/_White/Inventory/WhiteInventorySystem.cs create mode 100644 Content.Server/_White/Xenomorphs/Egg/XenomorphEggSystem.cs create mode 100644 Content.Server/_White/Xenomorphs/Infection/XenomorphInfectionComponent.cs create mode 100644 Content.Server/_White/Xenomorphs/Infection/XenomorphInfectionSystem.cs create mode 100644 Content.Server/_White/Xenomorphs/Larva/XenomorphLarvaSystem.cs create mode 100644 Content.Shared/_White/Inventory/Components/BaseEquipOnComponent.cs create mode 100644 Content.Shared/_White/Inventory/Components/EquipOnCollideComponent.cs create mode 100644 Content.Shared/_White/Inventory/Components/EquipOnMeleeHitComponent.cs create mode 100644 Content.Shared/_White/Inventory/Components/EquipOnPickUpComponent.cs create mode 100644 Content.Shared/_White/Inventory/Components/EquipOnThrownHitComponent.cs create mode 100644 Content.Shared/_White/Inventory/SharedWhiteInventorySystem.Equip.cs create mode 100644 Content.Shared/_White/Inventory/SharedWhiteInventorySystem.cs create mode 100644 Content.Shared/_White/StatusIcon/StatusIconPrototype.cs create mode 100644 Content.Shared/_White/Xenomorphs/Egg/XenomorphEggComponent.cs create mode 100644 Content.Shared/_White/Xenomorphs/Infection/SharedXenomorphInfectionComponent.cs create mode 100644 Content.Shared/_White/Xenomorphs/Infection/XenomorphInfectedComponent.cs create mode 100644 Content.Shared/_White/Xenomorphs/Larva/XenomorphLarvaComponent.cs create mode 100644 Content.Shared/_White/Xenomorphs/Larva/XenomorphLarvaVictimComponent.cs create mode 100644 Resources/Locale/en-US/_white/xenomorphs/egg.ftl create mode 100644 Resources/Locale/en-US/_white/xenomorphs/infection.ftl create mode 100644 Resources/Locale/en-US/_white/xenomorphs/larva.ftl create mode 100644 Resources/Locale/ru-RU/_white/prototypes/body/organs/animal/xenomorph.ftl create mode 100644 Resources/Locale/ru-RU/_white/prototypes/body/organs/infection.ftl create mode 100644 Resources/Locale/ru-RU/_white/prototypes/entities/mobs/xenomorphs/facehugger.ftl create mode 100644 Resources/Locale/ru-RU/_white/prototypes/entities/structures/furniture/xenomorph_egg.ftl create mode 100644 Resources/Locale/ru-RU/_white/prototypes/entities/surgery/surgeries.ftl create mode 100644 Resources/Locale/ru-RU/_white/xenomorphs/egg.ftl create mode 100644 Resources/Locale/ru-RU/_white/xenomorphs/infection.ftl create mode 100644 Resources/Locale/ru-RU/_white/xenomorphs/larva.ftl create mode 100644 Resources/Prototypes/_White/Body/Organs/infection.yml create mode 100644 Resources/Prototypes/_White/Entities/Mobs/Xenomorphs/facehugger.yml create mode 100644 Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml create mode 100644 Resources/Prototypes/_White/Entities/Structures/Furniture/xenomorph_egg.yml create mode 100644 Resources/Prototypes/_White/Entities/Surgery/surgeries.yml create mode 100644 Resources/Prototypes/_White/StatusIcon/infection.yml create mode 100644 Resources/Textures/_White/Interface/Actions/xenomorph.rsi/egg.png create mode 100644 Resources/Textures/_White/Interface/Misc/infection_icons.rsi/meta.json create mode 100644 Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_0.png create mode 100644 Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_1.png create mode 100644 Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_2.png create mode 100644 Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_3.png create mode 100644 Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_4.png create mode 100644 Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_5.png create mode 100644 Resources/Textures/_White/Interface/Misc/infection_icons.rsi/xenomorph_6.png create mode 100644 Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/equipped-MASK.png create mode 100644 Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/facehugger.png create mode 100644 Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/facehugger_dead.png create mode 100644 Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/inhand-left.png create mode 100644 Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/inhand-right.png create mode 100644 Resources/Textures/_White/Mobs/Aliens/Xenomorphs/facehugger.rsi/meta.json create mode 100644 Resources/Textures/_White/Mobs/Species/Xenomorph/organs.rsi/embryon.png create mode 100644 Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_burst.png create mode 100644 Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_bursting.png create mode 100644 Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_growing.png create mode 100644 Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/egg_grown.png create mode 100644 Resources/Textures/_White/Structures/Furniture/xenomorph_egg.rsi/meta.json 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 0000000000000000000000000000000000000000..989b65872594d4ca53cb8d30824c1ccda19f2a47 GIT binary patch literal 397 zcmV;80doF{P)*kyvmaxNcfUjAS zHDt)$LC2XzO3(?@VRnj?ayCuBoF~9)dLH_WNDe&W0sxOoz>Cy2146O_b3cL>yZpI{ zpzkztC^31Deb1hwXUGfUvyP}~$N4SrpeMrSh%WUY7Cdi1goVf@BAu0T0u-->r5IzR z6gf(*ryMBQL>yu+iVL;&OQ3mEb4a*KLu6h0NrIxCK?m1%)TY0>N!NMkHWDjN70r#@ rR@(r)knW<*wZ445EPQnMI(Zl0g=G(u)~St900000NkvXXu0mjfqj;bh literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b413a93f9395c3b81c54d519d52413b75e95c5ca GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|Y&~5ZLnNjq zCn$)%nJe<6{{4~z{|)SfcQGx$Q>sz3qbd6K?El6FKwxBKbZV7UqEsz3qbd6K?El6FKwxBKbZV7UqEeIr8IG>=6~3!|g4f5zH->T5;TawkEPM(RnFS;{*0@TTy?d|o zXJO^EXOX31-V0jI8+me`|7NPNJ=c9qQ}e9xhC7yD4?EcNFt8-L2)JE|T(i}2!6D0a zmt7vpw11anct81Q_S3UHM_Ddx<&`_I-77WWuLaYEu$NMs?pVH^FWzL3)V(g(LQR!T g;X+wfCBp}1tyxa4vVVdCfX-#`boFyt=akR{0GP5?+5i9m literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..003df3639265310484bb0196d2c77fb8090c8f61 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|7JIrlhDc2H z?bj7*N|0!oKm9}C*>eIr8IG>=6~3!|g4f5zH->T5;TawkEPM(RnFS;{*0@TTy?d|o zXJO^EXOX31-V0jI8+me`|7NPNJ=c9qQ}e9xhC7yD4?EcNFt8-L2)JE|T(i}2!6D0a zmt7vpw11anct81Q_S3UHM_Ddx<&`_I-77WWuLaYEu$NMs?pVH^FWzL3)V(g(LQR!T g;X+wfCBp}1tyxa4vVVdCfX-#`boFyt=akR{0GP5?+5i9m literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..74bd8f832173174d33ad818d3d2119bcda646868 GIT binary patch literal 577 zcmV-H0>1r;P)Px$`$KR6TDRK@fd|qFdbgT1%Ao2WZlh#&U}2LQ8%Eu0ofNbWU~r1Zc6V zKpNmW0R#X@VrC3)eth&rd&5WZe?$mU0AM}& z?3?@h^(O|_iU7bgifRcIAMT%o0Aph?f4D;@>0+C2%F9IokTNrd0HF8vOn z@Jmrh@BYajP+MgwLCS;&);dWSVKalGs3q6#CnR(-&-Jx2C5Hf?mt(H7lue%1-;AqI z^Qgb&sRU$iEdU^2j(uUJ003fR@cYx))8OAgg#FP1+kAs{mSX?;0Xm@3h@-L7>Px7$=10>DtDq~r|B0wNqXI+;J* zL1S86mD5SOWxUDv3A~#b!<#I{ymDv~rD&a{c%O9fCy0lWoSk4`ZLN#`XaU{o5Zip? zi*NG{dc#KvcjQepb*-CR({!bnX%ro{v^w)&1xTW~I4AAIXZnH*E;x^WW(wsgbKn?w P00000NkvXXu0mjfv!n+2 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b9cb635ca757ee075283f4cc84f145e4b95f888b GIT binary patch literal 713 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..07e4240f1ec7b907d6b7527d030108aef155615c GIT binary patch literal 711 zcmV;&0yzDNP)+@+ZlHjRt4Gj z^38nTeDCvn|Lm*HIA&`AGyoa^4S`y=h#| z-FHlox&u&LgtuS&dn3~&*}-uVew%G z8v@X2rI@69qYSjC0IqH=J)?7pn)YP!O(4$nO9tYEBLTFyL@ZDQk#jm{grC<}ajE4D zX1;D>c(5Dk!BlY0`=d|c5Zv_c@h7cKfGFVcSnfN5HsE1T@*tv+1qVL`KAclTf1A1cg{?i1c8P_@Ao z3!qcz|MqR{e6qc0*?`Oc)=|h6Lj%YCiH6Fkn>Z1`UdnRBt*4&Yrx2N{+ zb8Zkq-$xY-0hr>9z^p6H1qsT-!KBADYP19Y7U&TicP=1$YSs1tMuS>i!Q&EzpMoP^%p~z%I-sT+r2qlEwf4002ovPDHLkV1iY;NpS!G literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a9a5abba0a5c8a2d9851221016e1de323d6b8163 GIT binary patch literal 602 zcmV-g0;TPx%6iGxuRCt{2n!QRZK@`XTE4G$bn+T%CK7fduLNK81yn)RHUtk*>K|)g72cnfv zU}F&>NQ%f!aq9&mf~nG4C|DdDk1VSz`!So{kpHg<*-XxykC}6pIRg+v2qA=!e2fQ8 zDO-oq>9mZ{5e2Z&PVKZzp~f%U7U&yK+?`b%>~8}Aw)W1PJo19k6yrft^2#X%-*{GX zf^WR)Z-Kg7le$~8qUc=jc^k_AR84F;IEUG-a>-eJUzmj_egbfTUt)*)iOq?`M(DA`&icZT&uicQ7wLj8n8S#y0JrU;K zTXnZ4(d4IzPk;MqOcHteQl)G7(?`|P@eFIS5zI>DwXlJ^N`M-Mlz-+cac)9Bxg6No;GAjk5vctwlTkH!% oB@8cP@^Cp^xTq6C2qD?|0J?G&*eDUVhyVZp07*qoM6N<$f(Zf{EdT%j literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bf322080f86879a6f0bd64f727560d50039f116d GIT binary patch literal 598 zcmV-c0;&CpP)Px%5J^NqRCt{2n!iuNKorNnH9E37)x^lq#gzyH1Bo;Lfs2W8a$z@w*v*7V2NQJi zFJN(i0mA@EL!2+M!~~|U4v;vUgC1Vs3KeMS(f3&hZPWMT-o1P2djLTY1VIo4u^6{q z;hWZBBe?Aff4C_o(|8?8Y^2Hrjo4kk8^VjtRu9baP_1{3btxA%Zo9(oZ(mk>L8+M4 zHjF~iJ!hFbXPH_$W#ycpOHxrS(xMYgxvxvbELZJ$@$wEE#yTI!`3nFpuj{`0?o3}0 zMykB_T)T+9@c@v{myOr68jg%lFvpvl6J{I$z&GQk^X1qZFF5}g7=zk7tjs=uzl9Bi z-@`gqgF~vR%RdlyADpTj?w2SFvpv#uQJhf*w7Ha zs)QIv2LM3BF7Q52P5#RT+`n~}Lq@0bzhM`6shFJ`~0jr^EWv)B4kHa zi_AOsqjrLjC+d->!-J*eE9V1LFvml~E+_>>-6$3Enynu89^b%5$Ve8be3*ndK)9vMHC&>4rVtP>`6`Hu?Peyh^ kTpSS>JqUsz2tq@?0bj2rC3UOg8vpY3N0?_AWm^irqjXR5X~R2ZK)k{;4JrkH|KZ0+#JG)2*-?* zqgGD=Qh*fTKLDI2Z8F9p*Hz9Vr`Ou^LI?H@x11nPH!X?-i3^rQ z7<548oQ8vDtTtCF`#`#B`4GS2z;5wesT|ULyuYHD&mdPS!`MKOj1zkSg2fm13dMW| z)pkEpI~+895ZRD&U}3LNt2eN)C%Ed4@mQ@!cGl{S$>>y2%x5qg^-yj12|%I?i2y3+ z6m{WRvst9RTPQHyv=Z;iK>z@@itDC@GfiVJjswSaLt0Hyqzg*}0KoO&`3qpXfTk$o ziZjP`;kfR>5C>1f$#N+QkUL*DY!Fdw0-JjvQs`CA>EY&_RLI7=xxLG#mA>uvc>3%Qz4f!U55#$hXOz?~KgziAa7F z?*IUR|IGvNyVgw$-9mxg&gVWrQhW;_01`U9`>K8f@E39PACeRx1xNut0iC5M3vq8e QD*ylh07*qoM6N<$g78i1iU0rr literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..12543880fb131c1fa2605195c2f63634bba46013 GIT binary patch literal 795 zcmV+$1LXXPP)PrfefY8f_YhRf#>RiF)_o!LvvI zG;ew{@!-LuiJEAWCe~F*X|WOrg>+e9+1k=%04o!&c$Q&apQYzQqcQ%Zh8k+SNKatk}er3dj0l; z@?0puv!{#tJS-!B1;Bm4w}zP29?gI} z$2uU`9QW710WbvLlEvgQurC_2eVU<7vpg~OM1b{tPK;)8XGBm=I~%8;A$9EnRqGv^ z*A~2<+nK%zaqz{fceH+MndE;qSp+7(dD(~f^|duh#LtsyZYd!`0U*wYznEK5oI*%t z`TT8awGL=*?qcZKF#rf-gn0k>gq&`dR#&f)rfFi`RKP%pt$%VX&K(^cQll|!#xnyz zoT(f8`;YjJxUqIAjPgI2TJ4b9mE(UQC#TL z=l3FBxxC{6YP)pWv_1fCkHxR%rZ0Z)?n4jt{ak#fWV1Ve>p zV7pWpX%N2@fyRNL^v$oSEEa^2&&z*Iad{&tEchKz@;CWes>D?B_^FQooJCyqZPI+w Zp5HH%=g|y4a`^xN002ovPDHLkV1gl3Y;gbp literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..cb764baa2d66a8ac1a9475f97b66bdf2ff3d7b10 GIT binary patch literal 3086 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0a170d6def0b14bf6ebfbcec33b983189e47cc36 GIT binary patch literal 580 zcmV-K0=xZ*P)YK&x<*%YH_SRxSG} zEnBtdH?)YBg%E)uq>@EpbWsup8*TCy(|n(tX^hGXGy8|fN!uVT0btc3F?zG~-gInsC7|=l(G(3)*!K0h%p}6)fPQp(DU^aOfM`4m7ikrigEEVZrO;fOyK(SDuc6w^ofVc+M?*@;@ zOLL4dNZ;1y5K>e^09d`>KMTcT{ehv;#Ka^4mCLW-85l6lQBS!{=K#=rVPVNM;1xeS z+`*uCh|IB%Ymy|T^QPC(6aWuMB+hlWhrzHclNk!lpVhqCuU4xx_WQbxE9f_7 z^B^Lj1PG+4f#m9;7GbbbZWv1`FgoIcTrLNZs0NrA?&}BunSaFC0E3D|?+FMWp=B>c z&*9qw_AR01&xpwnXF*=jdS=17f|rOAGhzI$<*r8i3Q@wt8X= zaXAA#9vdH{u5}n1A~XQRmx}ZB{^J)<%%1_Czde(p`hGK%-%tP?y|K0_#%9}6W&rcB zL}K7JOcp``;NElDG_94kDScBXvt@ar967`>&+Dj&Fc#?%!a27LApj+DUOzCq{|10x z{+>B}rwk#tvSrdq0TFZ!fdCL6Wpcz9wF`X-<@Eb4DokVmh^xv^9!G%)1gJGkQ33MH z=W_F)q_Mv|-T#Qo0Wbtd$znrQaM~9SvVE2z{5J$yAOOC8F#ZU!pqzF#PCrB9TTrQW zXiA%QdUi)y3FiXf;G4JaXz9VMSJx;nSp+86yxfQQ!r~&uV-sW;D@urw01)THUyYUX zIE9ePip7W2Y#z|$^koB%HDO_TclzF3D})in$OU=f=jQms~L zO4lf>-x-Lxj%0%n!^vapV~A`vM?bd)kGE3URzfH@ zEo!yg|Bk&q%IEXq+`w!~r84JVf!Jo-Ted