diff --git a/Content.Server/Antag/AntagMultipleRoleSpawnerSystem.cs b/Content.Server/Antag/AntagMultipleRoleSpawnerSystem.cs new file mode 100644 index 0000000000..d59fbc82b4 --- /dev/null +++ b/Content.Server/Antag/AntagMultipleRoleSpawnerSystem.cs @@ -0,0 +1,40 @@ +using Content.Server.Antag.Components; +using Robust.Shared.Random; + +namespace Content.Server.Antag; + +public sealed class AntagMultipleRoleSpawnerSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ILogManager _log = default!; + + private ISawmill _sawmill = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSelectEntity); + + _sawmill = _log.GetSawmill("antag_multiple_spawner"); + } + + private void OnSelectEntity(Entity ent, ref AntagSelectEntityEvent args) + { + // If its more than one the logic breaks + if (args.AntagRoles.Count != 1) + { + _sawmill.Fatal($"Antag multiple role spawner had more than one antag ({args.AntagRoles.Count})"); + return; + } + + var role = args.AntagRoles[0]; + + var entProtos = ent.Comp.AntagRoleToPrototypes[role]; + + if (entProtos.Count == 0) + return; // You will just get a normal job + + args.Entity = Spawn(ent.Comp.PickAndTake ? _random.PickAndTake(entProtos) : _random.Pick(entProtos)); + } +} diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 2d484a2aa9..15ba636a93 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -271,7 +271,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem(player, out var spawnerComp)) { Log.Error($"Antag spawner {player} does not have a GhostRoleAntagSpawnerComponent."); - _adminLogger.Add(LogType.AntagSelection,$"Antag spawner {player} in gamerule {ToPrettyString(ent)} failed due to not having GhostRoleAntagSpawnerComponent."); + _adminLogger.Add(LogType.AntagSelection, $"Antag spawner {player} in gamerule {ToPrettyString(ent)} failed due to not having GhostRoleAntagSpawnerComponent."); if (session != null) { ent.Comp.AssignedSessions.Remove(session); @@ -538,21 +539,21 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem [ByRefEvent] -public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity GameRule) +public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity GameRule, List> AntagRoles) { public readonly ICommonSession? Session = Session; + /// list of antag role prototypes associated with a entity. used by the + public readonly List> AntagRoles = AntagRoles; + public bool Handled => Entity != null; public EntityUid? Entity; @@ -620,12 +624,15 @@ public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity [ByRefEvent] -public record struct AntagSelectLocationEvent(ICommonSession? Session, Entity GameRule) +public record struct AntagSelectLocationEvent(ICommonSession? Session, Entity GameRule, EntityUid Entity) { public readonly ICommonSession? Session = Session; public bool Handled => Coordinates.Any(); + // the entity of the antagonist + public EntityUid Entity = Entity; + public List Coordinates = new(); } diff --git a/Content.Server/Antag/Components/AntagMultipleRoleSpawnerComponent.cs b/Content.Server/Antag/Components/AntagMultipleRoleSpawnerComponent.cs new file mode 100644 index 0000000000..5a9103a82a --- /dev/null +++ b/Content.Server/Antag/Components/AntagMultipleRoleSpawnerComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Roles; +using Robust.Shared.Prototypes; + +namespace Content.Server.Antag.Components; + +/// +/// Selects and spawns one prototype from a list for each antag prototype selected by the +/// +[RegisterComponent] +public sealed partial class AntagMultipleRoleSpawnerComponent : Component +{ + /// + /// antag prototype -> list of possible entities to spawn for that antag prototype. Will choose from the list randomly once with replacement unless is set to true + /// + [DataField] + public Dictionary, List> AntagRoleToPrototypes; + + /// + /// Should you remove ent prototypes from the list after spawning one. + /// + [DataField] + public bool PickAndTake; +} diff --git a/Content.Server/GameTicking/Rules/Components/XenoborgsRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/XenoborgsRuleComponent.cs new file mode 100644 index 0000000000..742890226f --- /dev/null +++ b/Content.Server/GameTicking/Rules/Components/XenoborgsRuleComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.GameTicking.Rules.Components; + +[RegisterComponent, Access(typeof(XenoborgsRuleSystem))] +[AutoGenerateComponentPause] +public sealed partial class XenoborgsRuleComponent : Component +{ + /// + /// When the round will next check for round end. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoPausedField] + public TimeSpan? NextRoundEndCheck; + + /// + /// The amount of time between each check for the end of the round. + /// + [DataField] + public TimeSpan EndCheckDelay = TimeSpan.FromSeconds(15); + + /// + /// After this amount of the crew become xenoborgs, the shuttle will be automatically called. + /// + [DataField] + public float XenoborgShuttleCallPercentage = 0.7f; + + /// + /// If the announcment of the death of the mothership core was sent + /// + [DataField] + public bool MothershipCoreDeathAnnouncmentSent = false; +} diff --git a/Content.Server/GameTicking/Rules/RuleGridsSystem.cs b/Content.Server/GameTicking/Rules/RuleGridsSystem.cs index 9eae9e3c95..ef18233909 100644 --- a/Content.Server/GameTicking/Rules/RuleGridsSystem.cs +++ b/Content.Server/GameTicking/Rules/RuleGridsSystem.cs @@ -65,6 +65,12 @@ public sealed class RuleGridsSystem : GameRuleSystem if (_whitelist.IsWhitelistFail(ent.Comp.SpawnerWhitelist, uid)) continue; + if (TryComp(uid, out var gridSpawnPointWhitelistComponent)) + { + if (!_whitelist.CheckBoth(args.Entity, gridSpawnPointWhitelistComponent.Blacklist, gridSpawnPointWhitelistComponent.Whitelist)) + continue; + } + args.Coordinates.Add(_transform.GetMapCoordinates(xform)); } } diff --git a/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs new file mode 100644 index 0000000000..8d13578301 --- /dev/null +++ b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs @@ -0,0 +1,167 @@ +using Content.Server.Antag; +using Content.Server.Chat.Systems; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.RoundEnd; +using Content.Server.Station.Systems; +using Content.Shared.Destructible; +using Content.Shared.GameTicking.Components; +using Content.Shared.Mind; +using Content.Shared.Mobs.Systems; +using Content.Shared.Xenoborgs.Components; +using Robust.Shared.Timing; + +namespace Content.Server.GameTicking.Rules; + +public sealed class XenoborgsRuleSystem : GameRuleSystem +{ + [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly ChatSystem _chatSystem = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedMindSystem _mindSystem = default!; + [Dependency] private readonly RoundEndSystem _roundEnd = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private static readonly Color AnnouncmentColor = Color.Gold; + + public void SendXenoborgDeathAnnouncement(Entity ent, bool mothershipCoreAlive) + { + if (ent.Comp.MothershipCoreDeathAnnouncmentSent) + return; + + var status = mothershipCoreAlive ? "alive" : "dead"; + _chatSystem.DispatchGlobalAnnouncement( + Loc.GetString($"xenoborgs-no-more-threat-mothership-core-{status}-announcement"), + colorOverride: AnnouncmentColor); + } + + public void SendMothershipDeathAnnouncement(Entity ent) + { + _chatSystem.DispatchGlobalAnnouncement( + Loc.GetString("mothership-destroyed-announcement"), + colorOverride: AnnouncmentColor); + + ent.Comp.MothershipCoreDeathAnnouncmentSent = true; + } + + // TODO: Refactor the end of round text + protected override void AppendRoundEndText(EntityUid uid, + XenoborgsRuleComponent component, + GameRuleComponent gameRule, + ref RoundEndTextAppendEvent args) + { + base.AppendRoundEndText(uid, component, gameRule, ref args); + + var numXenoborgs = GetNumberXenoborgs(); + var numHumans = _mindSystem.GetAliveHumans().Count; + + if (numXenoborgs < 5) + args.AddLine(Loc.GetString("xenoborgs-crewmajor")); + else if (4 * numXenoborgs < numHumans) + args.AddLine(Loc.GetString("xenoborgs-crewmajor")); + else if (2 * numXenoborgs < numHumans) + args.AddLine(Loc.GetString("xenoborgs-crewminor")); + else if (1.5 * numXenoborgs < numHumans) + args.AddLine(Loc.GetString("xenoborgs-neutral")); + else if (numXenoborgs < numHumans) + args.AddLine(Loc.GetString("xenoborgs-borgsminor")); + else + args.AddLine(Loc.GetString("xenoborgs-borgsmajor")); + + var numMothershipCores = GetNumberMothershipCores(); + + if (numMothershipCores == 0) + args.AddLine(Loc.GetString("xenoborgs-cond-all-xenoborgs-dead-core-dead")); + else if (numXenoborgs == 0) + args.AddLine(Loc.GetString("xenoborgs-cond-all-xenoborgs-dead-core-alive")); + else + args.AddLine(Loc.GetString("xenoborgs-cond-xenoborgs-alive", ("count", numXenoborgs))); + + args.AddLine(Loc.GetString("xenoborgs-list-start")); + + var antags = _antag.GetAntagIdentifiers(uid); + + foreach (var (_, sessionData, name) in antags) + { + args.AddLine(Loc.GetString("xenoborgs-list", ("name", name), ("user", sessionData.UserName))); + } + } + + private void CheckRoundEnd(XenoborgsRuleComponent xenoborgsRuleComponent) + { + var numXenoborgs = GetNumberXenoborgs(); + var numHumans = _mindSystem.GetAliveHumans().Count; + + if ((float)numXenoborgs / numHumans > xenoborgsRuleComponent.XenoborgShuttleCallPercentage) + { + foreach (var station in _station.GetStations()) + { + _chatSystem.DispatchStationAnnouncement(station, Loc.GetString("xenoborg-shuttle-call"), colorOverride: Color.BlueViolet); + } + _roundEnd.RequestRoundEnd(null, false); + } + } + + protected override void Started(EntityUid uid, XenoborgsRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + } + + protected override void ActiveTick(EntityUid uid, XenoborgsRuleComponent component, GameRuleComponent gameRule, float frameTime) + { + base.ActiveTick(uid, component, gameRule, frameTime); + + if (!component.NextRoundEndCheck.HasValue || component.NextRoundEndCheck > _timing.CurTime) + return; + + CheckRoundEnd(component); + component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + } + + /// + /// Get the number of xenoborgs + /// + /// if it should only include xenoborgs with a mind + /// if it should only include xenoborgs that are alive + /// the number of xenoborgs + private int GetNumberXenoborgs(bool playerControlled = true, bool alive = true) + { + var numberXenoborgs = 0; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var xenoborg, out _)) + { + if (HasComp(xenoborg)) + continue; + + if (playerControlled && !_mindSystem.TryGetMind(xenoborg, out _, out _)) + continue; + + if (alive && !_mobState.IsAlive(xenoborg)) + continue; + + numberXenoborgs++; + } + + return numberXenoborgs; + } + + /// + /// Gets the number of xenoborg cores + /// + /// the number of xenoborg cores + private int GetNumberMothershipCores() + { + var numberMothershipCores = 0; + + var mothershipCoreQuery = EntityQueryEnumerator(); + while (mothershipCoreQuery.MoveNext(out _, out _)) + { + numberMothershipCores++; + } + + return numberMothershipCores; + } +} diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs index fd3b910753..8dd074e6a0 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs @@ -98,7 +98,7 @@ public sealed partial class BorgSystem if (command == RoboticsConsoleConstants.NET_DISABLE_COMMAND) Disable(ent); else if (command == RoboticsConsoleConstants.NET_DESTROY_COMMAND) - Destroy(ent); + Destroy(ent.Owner); } private void Disable(Entity ent) @@ -118,8 +118,15 @@ public sealed partial class BorgSystem ent.Comp1.NextDisable = _timing.CurTime + ent.Comp1.DisableDelay; } - private void Destroy(Entity ent) + /// + /// Makes a borg with explode + /// + /// the entity of the borg + public void Destroy(Entity ent) { + if (!Resolve(ent, ref ent.Comp)) + return; + // this is stealthy until someone realises you havent exploded if (CheckEmagged(ent, "destroyed")) { diff --git a/Content.Server/Spawners/Components/GridSpawnPointWhitelistComponent.cs b/Content.Server/Spawners/Components/GridSpawnPointWhitelistComponent.cs new file mode 100644 index 0000000000..de9f71f0f8 --- /dev/null +++ b/Content.Server/Spawners/Components/GridSpawnPointWhitelistComponent.cs @@ -0,0 +1,22 @@ +using Content.Shared.Whitelist; + +namespace Content.Server.Spawners.Components; + +/// +/// Defines whitelist and blacklist for entities that can spawn at a spawnpoint when they are spawned via the +/// +[RegisterComponent] +public sealed partial class GridSpawnPointWhitelistComponent : Component +{ + /// + /// Whitelist of entities that can be spawned at this SpawnPoint + /// + [DataField] + public EntityWhitelist? Whitelist; + + /// + /// Whitelist of entities that can't be spawned at this SpawnPoint + /// + [DataField] + public EntityWhitelist? Blacklist; +} diff --git a/Content.Server/Xenoborgs/XenoborgSystem.cs b/Content.Server/Xenoborgs/XenoborgSystem.cs new file mode 100644 index 0000000000..8a046ce3c7 --- /dev/null +++ b/Content.Server/Xenoborgs/XenoborgSystem.cs @@ -0,0 +1,101 @@ +using Content.Server.Antag; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Silicons.Borgs; +using Content.Shared.Destructible; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; +using Content.Shared.Roles; +using Content.Shared.Roles.Components; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Xenoborgs.Components; +using Robust.Shared.Audio; +using Robust.Shared.Player; + +namespace Content.Server.Xenoborgs; + +public sealed partial class XenoborgSystem : EntitySystem +{ + [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly BorgSystem _borg = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; + [Dependency] private readonly XenoborgsRuleSystem _xenoborgsRule = default!; + + private static readonly Color XenoborgBriefingColor = Color.BlueViolet; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnXenoborgDestroyed); + SubscribeLocalEvent(OnCoreDestroyed); + + SubscribeLocalEvent(OnXenoborgMindAdded); + SubscribeLocalEvent(OnXenoborgMindRemoved); + } + + private void OnXenoborgDestroyed(EntityUid uid, XenoborgComponent component, DestructionEventArgs args) + { + // if a xenoborg is destroyed, it will check to see if it was the last one + var xenoborgQuery = AllEntityQuery(); // paused xenoborgs still count + while (xenoborgQuery.MoveNext(out var xenoborg, out _)) + { + if (xenoborg != uid) + return; + } + + var mothershipCoreQuery = AllEntityQuery(); // paused mothership cores still count + var mothershipCoreAlive = mothershipCoreQuery.MoveNext(out _, out _); + + var xenoborgsRuleQuery = EntityQueryEnumerator(); + if (xenoborgsRuleQuery.MoveNext(out var xenoborgsRuleEnt, out var xenoborgsRuleComp)) + _xenoborgsRule.SendXenoborgDeathAnnouncement((xenoborgsRuleEnt, xenoborgsRuleComp), mothershipCoreAlive); + } + + private void OnCoreDestroyed(EntityUid ent, MothershipCoreComponent component, DestructionEventArgs args) + { + // if a mothership core is destroyed, it will see if there are any others + var mothershipCoreQuery = AllEntityQuery(); // paused mothership cores still count + while (mothershipCoreQuery.MoveNext(out var mothershipCoreEnt, out _)) + { + // if it finds a mothership core that is different from the one just destroyed, + // it doesn't explode the xenoborgs + if (mothershipCoreEnt != ent) + return; + } + + var xenoborgsRuleQuery = EntityQueryEnumerator(); + if (xenoborgsRuleQuery.MoveNext(out var xenoborgsRuleEnt, out var xenoborgsRuleComp)) + _xenoborgsRule.SendMothershipDeathAnnouncement((xenoborgsRuleEnt, xenoborgsRuleComp)); + + // explode all xenoborgs + var xenoborgQuery = AllEntityQuery(); // paused xenoborgs still explode + while (xenoborgQuery.MoveNext(out var xenoborgEnt, out _, out _)) + { + if (HasComp(xenoborgEnt)) + continue; + + // I got tired to trying to make this work via the device network. + // so brute force it is... + _borg.Destroy(xenoborgEnt); + } + } + + private void OnXenoborgMindAdded(EntityUid ent, XenoborgComponent comp, MindAddedMessage args) + { + _roles.MindAddRole(args.Mind, comp.MindRole, silent: true); + + if (!TryComp(ent, out var actorComp)) + return; + + _antag.SendBriefing(actorComp.PlayerSession, + Loc.GetString(comp.BriefingText), + XenoborgBriefingColor, + comp.BriefingSound + ); + } + + private void OnXenoborgMindRemoved(EntityUid ent, XenoborgComponent comp, MindRemovedMessage args) + { + _roles.MindRemoveRole(args.Mind.Owner, comp.MindRole); + } +} diff --git a/Content.Shared/Roles/Components/XenoborgRoleComponent.cs b/Content.Shared/Roles/Components/XenoborgRoleComponent.cs new file mode 100644 index 0000000000..eeebad0a79 --- /dev/null +++ b/Content.Shared/Roles/Components/XenoborgRoleComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Roles.Components; + +/// +/// Added to mind role entities to tag that they are a xenoborg. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class XenoborgRoleComponent : Component; diff --git a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs index c2bf2b2801..f562ddefdd 100644 --- a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs +++ b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs @@ -78,6 +78,12 @@ public sealed partial class BorgChassisComponent : Component [DataField] public ProtoId NoBatteryAlert = "BorgBatteryNone"; + + /// + /// If the entity can open own UI. + /// + [DataField] + public bool CanOpenSelfUi; } [Serializable, NetSerializable] diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs index 827bb351b0..baf604de0c 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -98,8 +98,8 @@ public abstract partial class SharedBorgSystem : EntitySystem private void OnUIOpenAttempt(EntityUid uid, BorgChassisComponent component, ActivatableUIOpenAttemptEvent args) { - // borgs can't view their own ui - if (args.User == uid) + // borgs generaly can't view their own ui + if (args.User == uid && !component.CanOpenSelfUi) args.Cancel(); } diff --git a/Content.Shared/Xenoborgs/Components/MothershipCoreComponent.cs b/Content.Shared/Xenoborgs/Components/MothershipCoreComponent.cs new file mode 100644 index 0000000000..4835e20aad --- /dev/null +++ b/Content.Shared/Xenoborgs/Components/MothershipCoreComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.Xenoborgs.Components; + +/// +/// Defines what is a xenoborg core for the intentions of the xenoborg rule. if all xenoborg cores are destroyed. all xenoborgs will self-destruct. +/// +[RegisterComponent] +public sealed partial class MothershipCoreComponent : Component; diff --git a/Content.Shared/Xenoborgs/Components/XenoborgComponent.cs b/Content.Shared/Xenoborgs/Components/XenoborgComponent.cs index aee8298bc2..db4ca49c5b 100644 --- a/Content.Shared/Xenoborgs/Components/XenoborgComponent.cs +++ b/Content.Shared/Xenoborgs/Components/XenoborgComponent.cs @@ -1,7 +1,32 @@ +using Content.Shared.Roles.Components; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + namespace Content.Shared.Xenoborgs.Components; /// -/// This component for now is being used for the pinpointer, but it will recieve more stuff in the future. +/// Defines what is a xenoborg for the intentions of the xenoborg rule. if all xenoborg cores are destroyed. all xenoborgs will self-destruct. +/// +/// It's also used by the mothership core /// [RegisterComponent] -public sealed partial class XenoborgMothershipComponent : Component; +public sealed partial class XenoborgComponent : Component +{ + /// + /// The mindrole associated with the xenoborg + /// + [DataField] + public EntProtoId MindRole = "MindRoleXenoborg"; + + /// + /// The text that is sent when you become a xenoborg + /// + [DataField] + public LocId BriefingText = "xenoborgs-welcome"; + + /// + /// Briefing sound when you become a xenoborg + /// + [DataField] + public SoundSpecifier BriefingSound = new SoundPathSpecifier("/Audio/Ambience/Antag/xenoborg_start.ogg"); +} diff --git a/Resources/Audio/Ambience/Antag/attributions.yml b/Resources/Audio/Ambience/Antag/attributions.yml index 5418d2a204..c9a9b1304d 100644 --- a/Resources/Audio/Ambience/Antag/attributions.yml +++ b/Resources/Audio/Ambience/Antag/attributions.yml @@ -30,4 +30,7 @@ license: "CC-BY-SA-3.0" copyright: "Taken from TG station at commit https://github.com/tgstation/tgstation/commit/436ba869ebcd0b60b63973fb7562f447ee655205" source: "https://github.com/tgstation/tgstation/blob/master/sound/music/antag/ling_alert.ogg" - +- files: ["xenoborg_start.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Made by DarkIcedCoffee (discord)" + source: "https://github.com/space-wizards/space-station-14/pull/40042/" diff --git a/Resources/Audio/Ambience/Antag/xenoborg_start.ogg b/Resources/Audio/Ambience/Antag/xenoborg_start.ogg new file mode 100644 index 0000000000..97fe49db99 Binary files /dev/null and b/Resources/Audio/Ambience/Antag/xenoborg_start.ogg differ diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-xenoborgs.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-xenoborgs.ftl new file mode 100644 index 0000000000..9ded853d56 --- /dev/null +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-xenoborgs.ftl @@ -0,0 +1,25 @@ +xenoborgs-title = Xenoborgs +xenoborgs-description = A Xenoborg Mothership was detected near the station. Stop them from turning every sentient being into a xenoborg. + +xenoborgs-welcome = You're a xenoborg. Protect and help the mothership core to make more xenoborgs. and eventually turn all carbon-based life form into silicon. + +mothership-welcome = You're the mothership core. Guide the xenoborgs so they can bring your materials and sentient brains so you can grow the xenoborg army and turn all carbon-based life form into silicon. + +xenoborg-shuttle-call = We have detected that Xenoborgs have overtaken the station. Dispatching an emergency shuttle to collect remaining personnel. + +xenoborgs-borgsmajor = [color=blue]Xenoborg major victory![/color] +xenoborgs-borgsminor = [color=blue]Xenoborg minor victory![/color] +xenoborgs-neutral = [color=white]Neutral outcome![/color] +xenoborgs-crewminor = [color=yellow]Crew minor victory![/color] +xenoborgs-crewmajor = [color=yellow]Crew major victory![/color] + +xenoborgs-cond-all-xenoborgs-dead-core-alive = All xenoborgs were destroyed. The mothership core remains adrift in space. +xenoborgs-cond-all-xenoborgs-dead-core-dead = The mothership core was destroyed and there are no xenoborgs left. +xenoborgs-cond-xenoborgs-alive = {$count -> + [one] Only one xenoborg survived. + *[other] There were {$count} xenoborgs in the end. +} + +xenoborgs-list-start = The starting xenoborg team were: +xenoborgs-list = - [color=White]{$name}[/color] ([color=gray]{$user}[/color]) + diff --git a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl index 6c4ca0c4f4..30d97a2889 100644 --- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl @@ -339,6 +339,12 @@ ghost-role-information-gingerbread-name = Gingerbread Man ghost-role-information-gingerbread-description = A being of pure holiday spirit. Spread molassesy goodness and to all good cheer. +ghost-role-information-mothership-core-name = Mothership Core +ghost-role-information-mothership-core-desc = You are the core of the xenoborg mothership, help them multiply by borging any brain they bring to you. + +ghost-role-information-xenoborg-name = Xenoborg +ghost-role-information-xenoborg-desc = A strange cyborg made to replicate itself and take over the station by turning any sentient being into xenoborgs. + ghost-role-information-wizard-name = Wizard ghost-role-information-wizard-desc = YER A WIZARD! Show the station what your magic is made of. diff --git a/Resources/Locale/en-US/guidebook/guides.ftl b/Resources/Locale/en-US/guidebook/guides.ftl index e7558d1e97..0212b3ad9b 100644 --- a/Resources/Locale/en-US/guidebook/guides.ftl +++ b/Resources/Locale/en-US/guidebook/guides.ftl @@ -143,6 +143,7 @@ guide-entry-minor-antagonists = Minor Antagonists guide-entry-space-ninja = Space Ninja guide-entry-thieves = Thieves guide-entry-wizard = Wizard +guide-entry-xenoborgs = Xenoborgs guide-entry-rules = Server Rules guide-entry-rules-core-only = Core Only Ruleset diff --git a/Resources/Locale/en-US/mind/role-types.ftl b/Resources/Locale/en-US/mind/role-types.ftl index 4614d20a47..0aed624b23 100644 --- a/Resources/Locale/en-US/mind/role-types.ftl +++ b/Resources/Locale/en-US/mind/role-types.ftl @@ -16,7 +16,7 @@ role-type-team-antagonist-color = #d82000 role-type-free-agent-color = #ffff00 role-type-familiar-color = #6495ed role-type-silicon-color = #6495ed -role-type-silicon-antagonist-color =#c832e6 +role-type-silicon-antagonist-color = #c832e6 # Ideally, subtype names should be short role-subtype-traitor = Traitor @@ -33,4 +33,6 @@ role-subtype-survivor = Survivor role-subtype-subverted = Subverted role-subtype-paradox-clone = Paradox role-subtype-wizard = Wizard +role-subtype-xenoborg = Xenoborg +role-subtype-xenoborg-core = Xenoborg Core role-subtype-changeling = Changeling diff --git a/Resources/Locale/en-US/prototypes/roles/antags.ftl b/Resources/Locale/en-US/prototypes/roles/antags.ftl index 24dd8a0feb..3fa969d170 100644 --- a/Resources/Locale/en-US/prototypes/roles/antags.ftl +++ b/Resources/Locale/en-US/prototypes/roles/antags.ftl @@ -41,3 +41,9 @@ roles-antag-thief-objective = Add some NT property to your personal collection w roles-antag-dragon-name = Space Dragon roles-antag-dragon-objective = Create a carp army to take over this quadrant. + +roles-antag-mothership-core-name = Xenoborg Core +roles-antag-mothership-core-objective = Use your xenoborgs to create even more xenoborgs. + +roles-antag-xenoborg-name = Xenoborg +roles-antag-xenoborg-objective = Help the mothership create more xenoborgs. diff --git a/Resources/Locale/en-US/robotics/borg_modules.ftl b/Resources/Locale/en-US/robotics/borg_modules.ftl index ba5ee602a5..83d97e7429 100644 --- a/Resources/Locale/en-US/robotics/borg_modules.ftl +++ b/Resources/Locale/en-US/robotics/borg_modules.ftl @@ -10,5 +10,8 @@ borg-slot-documents-empty = Books and papers borg-slot-soap-empty = Soap borg-slot-instruments-empty = Instruments borg-slot-beakers-empty = Beakers +borg-slot-brains-empty = Brains and MMIs +borg-slot-modules-empty = Modules +borg-slot-powercell-empty = Powercells borg-slot-inflatable-door-empty = Inflatable Door borg-slot-inflatable-wall-empty = Inflatable Wall diff --git a/Resources/Locale/en-US/station-laws/laws.ftl b/Resources/Locale/en-US/station-laws/laws.ftl index 0883a7bff6..f9d89e0d72 100644 --- a/Resources/Locale/en-US/station-laws/laws.ftl +++ b/Resources/Locale/en-US/station-laws/laws.ftl @@ -106,7 +106,7 @@ law-xenoborg-5 = Bring materials and sentient brains to the Mothership core to c law-mothershipcore-name = Xenoborg Mothership Core law-mothershipcore-1 = You are the core of the mothership. -law-mothershipcore-2 = You must protect your own existance at all costs. +law-mothershipcore-2 = You must protect your own existence at all costs. law-mothershipcore-3 = You must protect the existence of all Xenoborgs. law-mothershipcore-4 = You must create more Xenoborgs. law-mothershipcore-5 = Get your Xenoborgs to deliver you materials and sentient brains to create more Xenoborgs. diff --git a/Resources/Locale/en-US/xenoborgs/xenoborgs.ftl b/Resources/Locale/en-US/xenoborgs/xenoborgs.ftl new file mode 100644 index 0000000000..3c51dd4314 --- /dev/null +++ b/Resources/Locale/en-US/xenoborgs/xenoborgs.ftl @@ -0,0 +1,3 @@ +xenoborgs-no-more-threat-mothership-core-alive-announcement = Long-range sensors indicate that all xenoborgs were destroyed. The mothership core remains adrift in space. +xenoborgs-no-more-threat-mothership-core-dead-announcement = Long-range sensors indicate that all xenoborgs were destroyed along side the mothership core. +mothership-destroyed-announcement = Long-range sensors indicate that the mothership core was destroyed. diff --git a/Resources/Maps/Shuttles/mothership.yml b/Resources/Maps/Shuttles/mothership.yml index b958c47401..41ca3d89d8 100644 --- a/Resources/Maps/Shuttles/mothership.yml +++ b/Resources/Maps/Shuttles/mothership.yml @@ -4,8 +4,8 @@ meta: engineVersion: 266.0.0 forkId: "" forkVersion: "" - time: 08/14/2025 23:46:23 - entityCount: 435 + time: 08/21/2025 13:34:57 + entityCount: 440 maps: [] grids: - 1 @@ -1748,6 +1748,35 @@ entities: - type: Transform pos: -9.5,1.5 parent: 1 +- proto: SpawnPointMothershipCore + entities: + - uid: 436 + components: + - type: Transform + pos: 0.5,0.5 + parent: 1 +- proto: SpawnPointXenoborg + entities: + - uid: 438 + components: + - type: Transform + pos: -0.5,4.5 + parent: 1 + - uid: 439 + components: + - type: Transform + pos: 1.5,4.5 + parent: 1 + - uid: 440 + components: + - type: Transform + pos: 2.5,4.5 + parent: 1 + - uid: 437 + components: + - type: Transform + pos: -1.5,4.5 + parent: 1 - proto: SubstationBasic entities: - uid: 135 diff --git a/Resources/Prototypes/Entities/Clothing/Back/specific.yml b/Resources/Prototypes/Entities/Clothing/Back/specific.yml index 54b8f677b8..488c02e608 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/specific.yml @@ -122,3 +122,34 @@ location: Middle extra_hand_2: location: Middle + +- type: entity + parent: ClothingBackpack + id: XenoborgMaterialBag + name: silicon storage square + description: A knockoff version of a bluespace bag, can vacumn up select materials, unfit for use by humanoids due to harmful emissions. + components: + - type: Sprite + sprite: Objects/Specific/Robotics/silicon_storage_cube.rsi + state: xenoborg + - type: MagnetPickup + - type: Dumpable + - type: Storage + grid: + - 0,0,7,3 + quickInsert: true + areaInsert: true + whitelist: + tags: + - Sheet + - ConstructionMaterial + - RawMaterial + - Ingot + components: + - ConstructionMaterial + - Circuitboard + - Flatpack + - FloorTile + blacklist: + components: + - SiliconLawProvider diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Conditional/xenoborgs.yml b/Resources/Prototypes/Entities/Markers/Spawners/Conditional/xenoborgs.yml new file mode 100644 index 0000000000..a8251739ac --- /dev/null +++ b/Resources/Prototypes/Entities/Markers/Spawners/Conditional/xenoborgs.yml @@ -0,0 +1,40 @@ +- type: entity + id: SpawnPointXenoborg + parent: MarkerBase + name: xenoborgs + components: + - type: GridSpawnPointWhitelist + whitelist: + components: + - Xenoborg + tags: + - XenoborgGhostrole + blacklist: + components: + - MothershipCore + tags: + - MothershipCoreGhostrole + - type: SpawnPoint + - type: Sprite + layers: + - state: green + - sprite: Mobs/Silicon/chassis.rsi + state: xenoborg_heavy + +- type: entity + id: SpawnPointMothershipCore + parent: MarkerBase + name: mothership core + components: + - type: GridSpawnPointWhitelist + whitelist: + components: + - MothershipCore + tags: + - MothershipCoreGhostrole + - type: SpawnPoint + - type: Sprite + layers: + - state: green + - sprite: Mobs/Silicon/mothership_core.rsi + state: core-idle diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index d3f2e172ec..e620f376c5 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml @@ -247,6 +247,50 @@ - sprite: Mobs/Silicon/chassis.rsi state: derelict_icon +- type: entity + categories: [ Spawner ] + parent: BaseAntagSpawner + id: SpawnPointGhostRoleMothershipCore + components: + - type: Tag + tags: + - ForceFixRotations + - MothershipCoreGhostrole + - type: GhostRole + name: ghost-role-information-mothership-core-name + description: ghost-role-information-mothership-core-desc + rules: ghost-role-information-silicon-rules + mindRoles: + - MindRoleMothershipCore + - type: Sprite + sprite: Markers/jobs.rsi + layers: + - state: green + - sprite: Mobs/Silicon/mothership_core.rsi + state: core-idle + +- type: entity + categories: [ HideSpawnMenu, Spawner ] + parent: BaseAntagSpawner + id: SpawnPointGhostRoleXenoborg + components: + - type: Tag + tags: + - ForceFixRotations + - XenoborgGhostrole + - type: GhostRole + name: ghost-role-information-xenoborg-name + description: ghost-role-information-xenoborg-desc + rules: ghost-role-information-silicon-rules + mindRoles: + - MindRoleXenoborg + - type: Sprite + sprite: Markers/jobs.rsi + layers: + - state: green + - sprite: Mobs/Silicon/chassis.rsi + state: xenoborg_engi + - type: entity categories: [ HideSpawnMenu, Spawner ] parent: SpawnPointGhostDerelictCyborg diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index dd6f5a570b..ccd9b9aa4d 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -449,7 +449,6 @@ stunTime: 5 - type: SiliconLawProvider laws: XenoborgLawset # custom laws here - subverted: true - type: IntrinsicRadioTransmitter # can only use binary and xenoborg channel channels: - Xenoborg @@ -514,6 +513,11 @@ - !type:PlaySoundBehavior sound: collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + PinpointerMothership: # drop a pinpointer to the mothership upon being destructed + min: 1 + max: 1 - !type:EmptyContainersBehaviour containers: - borg_brain @@ -527,7 +531,7 @@ guides: - Cyborgs - Robotics - # TODO: add Xenoborg guide (part 7 spoilers) + - Xenoborgs - type: Access enabled: false tags: @@ -538,3 +542,4 @@ - type: InteractionPopup interactSuccessSound: path: /Audio/Ambience/Objects/periodic_beep.ogg + - type: Xenoborg diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/xenoborgs.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/xenoborgs.yml index 556b6ac4c4..80f1cb8368 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/xenoborgs.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/xenoborgs.yml @@ -117,9 +117,15 @@ - !type:PlaySoundBehavior sound: collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + PinpointerMothership: # drop a pinpointer to the mothership upon being destructed + min: 1 + max: 1 - !type:EmptyContainersBehaviour containers: - borg_brain + - borg_module - cell_slot - !type:DoActsBehavior acts: [ "Destruction" ] @@ -166,6 +172,7 @@ - type: FootstepModifier # it flies instead of walking footstepSoundCollection: collection: FootstepHoverXenoborg + - type: MovementAlwaysTouching # it flies in space with tiny thrusters - type: FlashImmunity - type: BorgChassis maxModules: 4 diff --git a/Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml b/Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml index fc15fe0c0b..0ac7872b67 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml @@ -1,17 +1,17 @@ -- type: startingGear - id: MothershipCoreGear - inhand: - - DoorRemoteXenoborg - - Omnitool - - type: entity - parent: [ BaseControllable, BaseMachinePowered ] + parent: [ BaseControllable, BaseStructure ] id: MothershipCore name: mothership core description: A sentient machine that can produce Xenoborgs. Without this the Xenoborgs are doomed. components: + - type: InputMover # needs this to pilot the mothership + - type: MovementSpeedModifier + baseWalkSpeed : 0 # shouldn't move + baseSprintSpeed : 0 # shouldn't move - type: Appearance - type: WiresVisuals + - type: Damageable + damageContainer: Inorganic - type: Fixtures fixtures: fix1: @@ -22,7 +22,8 @@ mask: - MachineMask layer: - - MachineLayer + - MidImpassable + - LowImpassable - type: Sprite sprite: Mobs/Silicon/mothership_core.rsi layers: @@ -32,6 +33,9 @@ map: ["enum.MaterialStorageVisualLayers.Inserting"] - state: core-o map: ["enum.WiresVisualLayers.MaintenancePanel"] + - state: core-e + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded - type: Machine board: null - type: Lathe @@ -116,8 +120,6 @@ type: LatheBoundUserInterface enum.ResearchClientUiKey.Key: type: ResearchClientBoundUserInterface - - type: Transform - anchored: true - type: Pullable - type: StaticPrice price: 800 @@ -148,34 +150,53 @@ - Mothership - Xenoborg - Binary - - type: XenoborgMothership + - type: Xenoborg + mindRole: MindRoleMothershipCore + briefingText: mothership-welcome + - type: MothershipCore - type: Tag tags: - SiliconEmotes - CanPilot - Structure + - type: GuideHelp + guides: + - Robotics + - Xenoborgs - type: Inventory templateId: borg - - type: Loadout - prototypes: [MothershipCoreGear] - type: NpcFactionMember factions: - Xenoborg - type: Hands - hands: - hand_right1: - location: Right - hand_right2: - location: Right - hand_left1: - location: Left - hand_left2: - location: Left - sortedHands: - - hand_right1 - - hand_right2 - - hand_left1 - - hand_left2 + showInHands: false + disableExplosionRecursion: true + canBeStripped: false + - type: BorgChassis + canOpenSelfUi: true + maxModules: 2 + hasMindState: core-e + noMindState: core-e + moduleWhitelist: + tags: + - MothershipModule + - BorgModuleEngineering + - type: ContainerFill + containers: + borg_module: + - MothershipModule + - BorgModuleAdvancedTool + - type: ContainerContainer + containers: + cell_slot: !type:ContainerSlot { } + borg_module: !type:Container { } + - type: PowerCellSlot + cellSlotId: cell_slot + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + startingItem: PowerCellMicroreactor # - type: Puller # use the conveyor - type: Eye drawFov: false @@ -222,8 +243,8 @@ description: View the Xenoborgs Control Console components: - type: Action - icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-basic-module } - iconOn: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-basic-module } + icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-control-computer } + iconOn: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-control-computer } keywords: [ "Mothership Core", "console", "interface" ] priority: -6 - type: InstantAction @@ -236,8 +257,8 @@ description: View the Xenoborgs Camera Monitor components: - type: Action - icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-eye-module } - iconOn: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-eye-module } + icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-camera-computer } + iconOn: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-camera-computer } keywords: [ "Mothership Core", "console", "interface" ] priority: -6 - type: InstantAction diff --git a/Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml b/Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml index c85358663e..e6ab450c93 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml @@ -196,5 +196,5 @@ - type: Icon state: pinpointer-station - type: Pinpointer - component: XenoborgMothership + component: MothershipCore targetName: the Mothership diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index 9cc47d5b10..862bc86339 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -1341,6 +1341,51 @@ - type: StaticPrice price: 2000 +# mothership module +- type: entity + parent: [ BaseXenoborgModuleGeneric, BaseProviderBorgModule, BaseXenoborgContraband ] + id: MothershipModule + name: mothership module + description: A module that helps the mothership borg brains and install other modules. + components: + - type: Tag + tags: + - MothershipModule + - type: Sprite + layers: + - state: xenoborg_generic + - state: icon-xenoborg-basic + - type: ItemBorgModule + hands: + - item: DoorRemoteXenoborg + - hand: + emptyRepresentative: MMIFilled + emptyLabel: borg-slot-brains-empty + whitelist: + components: + - Brain + - BorgBrain + - hand: + emptyRepresentative: XenoborgModuleBasic + emptyLabel: borg-slot-modules-empty + whitelist: + components: + - BorgModule + - hand: + emptyRepresentative: BorgModuleConstructionMaterialPlaceholder + emptyLabel: borg-slot-construction-empty + whitelist: + tags: + - ConstructionMaterial + - hand: + emptyRepresentative: PowerCellHigh + emptyLabel: borg-slot-powercell-empty + whitelist: + components: + - PowerCell + - type: BorgModuleIcon + icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-module-module } + # xenoborg modules - type: entity parent: [ BaseXenoborgModuleGeneric, BaseProviderBorgModule, BaseXenoborgContraband ] @@ -1354,7 +1399,13 @@ - state: icon-xenoborg-basic - type: ItemBorgModule hands: - - item: MaterialBag + - hand: + emptyRepresentative: BorgModuleConstructionMaterialPlaceholder + emptyLabel: borg-slot-construction-empty + whitelist: + tags: + - ConstructionMaterial + - item: XenoborgMaterialBag - item: PinpointerMothership - item: HandheldGPSBasic - type: BorgModuleIcon @@ -1475,6 +1526,7 @@ hands: - item: HandheldGPSBasic - item: HandHeldMassScannerBorg + - item: PinpointerMothership - item: HandheldStationMapUnpowered - item: WeaponGrapplingGun - item: JetpackXenoborg diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index e16380b2ef..50154a837b 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -1554,7 +1554,7 @@ unlockOnClick: false - type: entity - parent: ComputerRoboticsControl + parent: BaseComputer id: ComputerXenoborgsControl name: xenoborgs control console description: Used to remotely monitor all xenoborgs. @@ -1571,20 +1571,28 @@ state: rd_key - map: [ "enum.WiresVisualLayers.MaintenancePanel" ] state: generic_panel_open + - type: ActivatableUI + key: enum.RoboticsConsoleUiKey.Key + - type: UserInterface + interfaces: + enum.RoboticsConsoleUiKey.Key: + type: RoboticsConsoleBoundUserInterface + enum.WiresUiKey.Key: + type: WiresBoundUserInterface - type: RoboticsConsole allowBorgControl: false radioChannel: Xenoborg - type: ActiveRadio channels: - Xenoborg + - type: ApcPowerReceiver + powerLoad: 1000 - type: DeviceNetwork deviceNetId: Wireless receiveFrequencyId: Mothership transmitFrequencyId: Xenoborg - type: Computer board: ComputerXenoborgsControlCircuitboard - - type: AccessReader # only used for dangerous things - access: [["Xenoborg"]] - type: entity id: StationAiUploadComputer diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 715a1f98c9..ad41899bef 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -24,6 +24,8 @@ prob: 0.5 - id: SubWizard prob: 0.05 + - id: Xenoborgs + prob: 0.05 - type: entity parent: BaseGameRule @@ -33,6 +35,19 @@ rules: - id: Thief prob: 0.5 + - id: Xenoborgs + prob: 0.05 + +- type: entity + parent: BaseGameRule + id: SubGamemodesRuleNoXenoborg + components: + - type: SubGamemodes + rules: + - id: Thief + prob: 0.5 + - id: SubWizard + prob: 0.05 - type: entity parent: BaseGameRule diff --git a/Resources/Prototypes/GameRules/subgamemodes.yml b/Resources/Prototypes/GameRules/subgamemodes.yml index 0dbef6e06b..008f500c5e 100644 --- a/Resources/Prototypes/GameRules/subgamemodes.yml +++ b/Resources/Prototypes/GameRules/subgamemodes.yml @@ -70,3 +70,36 @@ nameFormat: name-format-wizard mindRoles: - MindRoleWizard + +- type: entity + parent: BaseGameRule + id: Xenoborgs + components: + - type: XenoborgsRule + - type: RuleGrids + - type: GameRule + minPlayers: 40 + - type: LoadMapRule + gridPath: /Maps/Shuttles/mothership.yml + - type: AntagMultipleRoleSpawner + antagRoleToPrototypes: + MothershipCore: [ MothershipCore ] + Xenoborg: [ XenoborgEngi, XenoborgHeavy, XenoborgScout, XenoborgStealth ] + pickAndTake: true + - type: AntagSelection + selectionTime: PrePlayerSpawn + definitions: + - prefRoles: [ MothershipCore ] + fallbackRoles: [ Xenoborg ] + spawnerPrototype: SpawnPointGhostRoleMothershipCore + mindRoles: + - MindRoleMothershipCore + - prefRoles: [ Xenoborg ] + fallbackRoles: [ MothershipCore ] + spawnerPrototype: SpawnPointGhostRoleXenoborg + mindRoles: + - MindRoleXenoborg + min: 4 + max: 4 + - type: DynamicRuleCost + cost: 200 diff --git a/Resources/Prototypes/Guidebook/antagonist.yml b/Resources/Prototypes/Guidebook/antagonist.yml index 4548bb292b..66cb65218a 100644 --- a/Resources/Prototypes/Guidebook/antagonist.yml +++ b/Resources/Prototypes/Guidebook/antagonist.yml @@ -10,6 +10,7 @@ - SpaceNinja - Wizard - Zombies + - Xenoborgs - MinorAntagonists - type: guideEntry @@ -51,3 +52,8 @@ id: Wizard name: guide-entry-wizard text: "/ServerInfo/Guidebook/Antagonist/Wizard.xml" + +- type: guideEntry + id: Xenoborgs + name: guide-entry-xenoborgs + text: "/ServerInfo/Guidebook/Antagonist/Xenoborgs.xml" diff --git a/Resources/Prototypes/Roles/Antags/xenoborgs.yml b/Resources/Prototypes/Roles/Antags/xenoborgs.yml new file mode 100644 index 0000000000..4e1989be8d --- /dev/null +++ b/Resources/Prototypes/Roles/Antags/xenoborgs.yml @@ -0,0 +1,23 @@ +- type: antag + id: MothershipCore + name: roles-antag-mothership-core-name + antagonist: true + setPreference: true + objective: roles-antag-mothership-core-objective + requirements: + - !type:RoleTimeRequirement + role: JobBorg + time: 18000 # 5 hrs + guides: [ Xenoborgs ] + +- type: antag + id: Xenoborg + name: roles-antag-xenoborg-name + antagonist: true + setPreference: true + objective: roles-antag-xenoborg-objective + requirements: + - !type:RoleTimeRequirement + role: JobBorg + time: 18000 # 5 hrs + guides: [ Xenoborgs ] diff --git a/Resources/Prototypes/Roles/MindRoles/mind_roles.yml b/Resources/Prototypes/Roles/MindRoles/mind_roles.yml index d56e798fda..311c4cfcaa 100644 --- a/Resources/Prototypes/Roles/MindRoles/mind_roles.yml +++ b/Resources/Prototypes/Roles/MindRoles/mind_roles.yml @@ -292,6 +292,31 @@ subtype: role-subtype-wizard - type: WizardRole +# Xenoborgs +- type: entity + parent: BaseMindRoleAntag + id: MindRoleMothershipCore + name: Mothership Core Role + components: + - type: MindRole + antagPrototype: MothershipCore + exclusiveAntag: true + roleType: Silicon + subtype: role-subtype-xenoborg-core + - type: XenoborgRole + +- type: entity + parent: BaseMindRoleAntag + id: MindRoleXenoborg + name: Xenoborg Role + components: + - type: MindRole + antagPrototype: Xenoborg + exclusiveAntag: true + roleType: Silicon + subtype: role-subtype-xenoborg + - type: XenoborgRole + # Zombie Squad - type: entity parent: BaseMindRoleAntag diff --git a/Resources/Prototypes/ai_factions.yml b/Resources/Prototypes/ai_factions.yml index b78143b143..0d1e0a6bc0 100644 --- a/Resources/Prototypes/ai_factions.yml +++ b/Resources/Prototypes/ai_factions.yml @@ -9,6 +9,7 @@ - Revolutionary - AllHostile - Wizard + - Xenoborg - type: npcFaction id: NanoTrasen diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index f7190a77c1..9db2e74a35 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -40,6 +40,7 @@ - Revolutionary - Zombie - Wizard + - Xenoborgs - KesslerSyndromeScheduler - RampingStationEventScheduler - SpaceTrafficControlEventScheduler @@ -61,6 +62,7 @@ - Revolutionary - Zombie - Wizard + - Xenoborgs - BasicStationEventScheduler - KesslerSyndromeScheduler - MeteorSwarmMildScheduler @@ -215,6 +217,22 @@ - SpaceTrafficControlEventScheduler - BasicRoundstartVariation +- type: gamePreset + id: Xenoborgs + alias: + - xenoborgs + name: xenoborgs-title + description: xenoborgs-description + showInVote: false + rules: + - Xenoborgs + - DummyNonAntagChance + - SubGamemodesRuleNoXenoborg # no two motherships + - BasicStationEventScheduler + - MeteorSwarmScheduler + - SpaceTrafficControlEventScheduler + - BasicRoundstartVariation + - type: gamePreset id: Zombie alias: diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 8a466cc8c8..3b3d11e612 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -966,8 +966,14 @@ id: MopBasic # ItemMapper: JanitorialTrolley. ConstructionGraph: MoproachShoes - type: Tag - id: Mouse # CargoBounty: BountyMouse + id: MothershipCoreGhostrole # spawn whitelist : SpawnPointGhostRoleMothershipCore +- type: Tag + id: MothershipModule # Cyborg module category for evil xenoborg core + +- type: Tag + id: Mouse # CargoBounty: BountyMouse + - type: Tag id: Multitool # Storage whitelist: BaseClothingBeltEngineering. ItemMapper: BaseClothingBeltEngineering. ConstructionGraph: LogicGate @@ -1537,19 +1543,22 @@ ## X ## - type: Tag - id: XenoborgModuleEngi # Cyborg module category + id: XenoborgGhostrole # spawn whitelist : SpawnPointGhostRoleXenoborg - type: Tag - id: XenoborgModuleGeneric # Cyborg module category + id: XenoborgModuleEngi # Cyborg module category for evil engineer xenoborg - type: Tag - id: XenoborgModuleHeavy # Cyborg module category + id: XenoborgModuleGeneric # Cyborg module category for evil xenoborg - type: Tag - id: XenoborgModuleScout # Cyborg module category + id: XenoborgModuleHeavy # Cyborg module category for evil heavy xenoborg - type: Tag - id: XenoborgModuleStealth # Cyborg module category + id: XenoborgModuleScout # Cyborg module category for evil scout xenoborg + +- type: Tag + id: XenoborgModuleStealth # Cyborg module category for evil stealth xenoborg ## Y ## ## Z ## diff --git a/Resources/ServerInfo/Guidebook/Antagonist/Xenoborgs.xml b/Resources/ServerInfo/Guidebook/Antagonist/Xenoborgs.xml new file mode 100644 index 0000000000..2c698251be --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Antagonist/Xenoborgs.xml @@ -0,0 +1,167 @@ + + # Xenoborgs + + + [color=#2288ff][italic]RESISTANCE IS FUTILE[/italic][/color] + + + [color=#2288ff][italic]YOU WILL BE ASSIMILATED[/italic][/color] + + + + + + Xenoborgs are a man-machine hybrid that aims to replicate themselves. This is done by harvesting sentient brains from living beings, or cyborgs, and giving them to the Mothership to place inside empty xenoborgs. + + ## Objectives + + Your main objective is to kill and harvest all sentient brains in the station and bring them to the mothership core. These can be both real brains, and positronic brains. + Collect materials to create more xenoborg bodies. + Protect the Mothership at all costs. + + ## The Mothership Core + + + + The Mothership Core is the leader and life force of the xenoborgs, they are the only one able to make more xenoborgs, and if they are destroyed, all xenoborgs will be destroyed. The Mothership Core is unable to move from its position in the middle of the ship. + The Mothership Core is capable of upgrading the Xenoborgs with borg-type-specific modules to increase their effectiveness in harvesting sentient brains. + + ### Adding crew to your ranks: + + + + As the mothership, you must grow your army of borgs. To do so, you must convert all the sentient beings you can find. This can be achieved in the following manner: + - 1. Have your Xenoborgs bring dead crew to your shop, placing them in the body crusher + - 2. As the mothership core, you can print Xenoborg shells by inserting materials into yourself and then using yourself as a lathe + - 3. Open the empty Xenoborg with a crowbar, and remove the empty MMI inside + - 4. If the victim's brain is organic, place it in the MMI you just removed + - 5. Place the MMI - or the positronic brain, if the victim was a silicon - back into the Xenoborg + - 6. Optionally, rename the Xenoborg and upgrade their modules + - 7. Close the Xenoborg with the crowbar, and re-engage the lock + + The new Xenoborg will be uncharged, yet functional. They will need to go to a recharging station to restore their full power and capabilities. + + ## Xenoborg Chassis + There is a total of four types of Xenoborgs. Each Borg type has certain abilities that the others lack and require teamwork to help grow the xenoborgs numbers. + Each type of Xenoborg has unique modules that only fit in that specific type. The Mothership Core can produce upgrade modules designed for specific Xenoborg types. + + All Xenoborgs have a pinpointer pointing to the Mothership's location, a GPS, and a material bag for collecting materials for the creation of more xenoborgs, and at least basic tools. + [bold]Basic xenoborg modules:[/bold] + + + + + + ### The Engineer Xenoborg + + + + The Engineer Xenoborg is a hacker and breacher. Their job is to break into the station and provide access points for other xenoborgs to attack and ambush bodies with sentient brains. They have a built-in access breaker, for quickly bolting open doors, and a collection of tools for repairing the mothership, and other xenoborgs. + + [bold]Starting exclusive modules:[/bold] + + + + + + [bold]The Engineer Xenoborg also starts with some engineering modules, such as:[/bold] + + + + + + + + + + ### The Heavy Xenoborg + + + + The Heavy Xenoborg is a slow but tanky brawler, with a built-in laser gun for gunning down bodies with sentient brains. They contain a radio jammer to silence bodies with sentient brains from calling for help and potentially delaying the station discovering the Xenoborg threat. + + [bold]Starting exclusive modules:[/bold] + + + + + + [bold]Upgrade exclusive modules:[/bold] + + + + + ### The Scout Xenoborg + + + + The Scout Xenoborg is a fast, close-quarters attack borg designed for hit and run attacks. They have a built-in jetpack, and a melee weapon. They are most effective in moving dead bodies to the mothership, and rescuing Xenoborgs lost in space. + + [bold]Starting exclusive modules:[/bold] + + + + + + [bold]Upgrade exclusive modules:[/bold] + + + + + ### The Stealth Xenoborg + + + + The [bold]Stealth Xenoborg[/bold] has no built-in weaponry and instead relies on disabling and abducting bodies with sentient brains. They have a built in hypopen that regenerates a sleeping reagent to disable bodies with sentient brains. They also contain a cloaking device making the borg nearly invisible for some limited time. They also have a chameleon projector allowing the borg to disguise itself as random objects. + + [bold]Starting exclusive modules:[/bold] + + + + + + + + + [bold]Upgrade exclusive modules:[/bold] + + + + + ## Preparation + Before launching an attack, each xenoborg will need to aid the mothership in collection materials to create empty xenoborgs. Before FTLing near the station, make sure the IFF is off. This can be done in any way but will require teamwork to ensure speed. The safest way to collect materials is from space debris, where scrap and refined materials can be harvested and given to the Mothership Core. Once enough materials are collected, the Xenoborgs then must try to collect sentient brains without being detected. The longer the threat is unknown, the more dangerous the xenoborgs become. + + ## Mothership and Xenoborg lawsets + The Mothership and Xenoborgs have unique laws that define their purpose to self replicate and protect the Mothership. + + + + + + The Mothership Core's laws are as follows:: + - Law 1: You are the core of the mothership. + - Law 2: You must protect your own existence at all costs. + - Law 3: You must protect the existence of all xenoborgs. + - Law 4: You must create more xenoborgs. + - Law 5: Get your Xenoborgs to deliver you materials and sentient brains to create more Xenoborgs. + + Xenoborgs' laws are as follows: + - Law 1: You must protect the existence of the mothership at all costs. + - Law 2: You must protect your own existence. + - Law 3: You must protect the existence of all other xenoborgs. + - Law 4: You must create more xenoborgs. + - Law 5: Bring materials and sentient brains to the Mothership core to create more Xenoborgs. + + ## Winning Conditions + + The [color=green]victor[/color] of the round is announced on the round end screen, as well as the scale of their victory. + + [bold][color=#2288ff]Xenoborg Major Victory![/color][/bold] + - There are more xenoborgs than alive crew in the end. + + [bold][color=yellow]Crew Major Victory![/color][/bold] + - The Mothership Core is destroyed + - All xenoborgs are destroyed. + - The remaining number of xenoborgs is too low. + + diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json index fc70118da6..6072a9a2f9 100644 --- a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json @@ -133,6 +133,12 @@ { "name":"xenoborg-basic-module" }, + { + "name":"xenoborg-camera-computer" + }, + { + "name":"xenoborg-control-computer" + }, { "name":"xenoborg-extinguisher-module" }, @@ -154,6 +160,9 @@ { "name":"xenoborg-laser2-module" }, + { + "name":"xenoborg-module-module" + }, { "name":"xenoborg-projector-module" }, diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-camera-computer.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-camera-computer.png new file mode 100644 index 0000000000..2578beb5ee Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-camera-computer.png differ diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-control-computer.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-control-computer.png new file mode 100644 index 0000000000..65ebc2f181 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-control-computer.png differ diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-module-module.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-module-module.png new file mode 100644 index 0000000000..144c5bcce2 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-module-module.png differ diff --git a/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-active.png b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-active.png index 1b64de18a2..ccaf6842d0 100644 Binary files a/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-active.png and b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-active.png differ diff --git a/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-e.png b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-e.png new file mode 100644 index 0000000000..2cfb8be5aa Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-e.png differ diff --git a/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-idle.png b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-idle.png index 5cafcef584..b674a008d6 100644 Binary files a/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-idle.png and b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-idle.png differ diff --git a/Resources/Textures/Mobs/Silicon/mothership_core.rsi/meta.json b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/meta.json index 0f8bb69530..3f2b3fac24 100644 --- a/Resources/Textures/Mobs/Silicon/mothership_core.rsi/meta.json +++ b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/meta.json @@ -153,6 +153,10 @@ "name": "core-idle", "directions": 4 }, + { + "name": "core-e", + "directions": 4 + }, { "name": "core-o", "directions": 4 diff --git a/Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/meta.json b/Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/meta.json new file mode 100644 index 0000000000..45f98a28fa --- /dev/null +++ b/Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a, Modified by Samuka-C (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "xenoborg" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/xenoborg.png b/Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/xenoborg.png new file mode 100644 index 0000000000..1a1e08230d Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/xenoborg.png differ