]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Xenoborgs part 7 (#40042)
authorSamuka <47865393+Samuka-C@users.noreply.github.com>
Fri, 14 Nov 2025 22:10:09 +0000 (19:10 -0300)
committerGitHub <noreply@github.com>
Fri, 14 Nov 2025 22:10:09 +0000 (22:10 +0000)
* add mothership module

* option for mothership to open self UI

* fix mothership module

* remove mothership body

* swapp real hands for modules

* action sprite for mothership module

* removed hands from mothership core sprite

* xenoborgs now drop a pinpointer to the mothership core once destroyed

* add pinpointer to space movment module

* add base for XenoborgRule

* add xenoborg antag option

* something was needed

* something else was needed

* add ghost role spawn points

* change name in antag selection to Xenoborg Core

* add random spawnPoint markers that don't do anything

* add spawn points to mothership

* update spawn points

* add xenoborgs rule

* add xenoborgs rule to rotation

* add xenoborgs preset

* update preset with secret version
also added xenoborg mode description

* modify Antag Selection system to allow for custom entities via the AntagSelectionDefinition

* fix ghostroles spawners

* fix rule with new entityPrototype

* add spawnpoints to the mothership

* whitelist system to spawnpoints

* updated xenoborg components

* added xenoborg component to xenoborgs

* updated spawnpoints of xenoborgs in the mothership

* add new tags for xenoborg and mothership core

* add new tags for xenoborgs and mothership core

* update ghostrole spawners for xenoborgs

* message for when you get the xenoborg role

* explode all xenoborgs when mothership core dies

* for real now. explode all xenoborgs when mothership core is destroyed

* round end summary for xenoborgs

* temporary

* add guidebook entry for xenoborgs

* instructions on how to borg players

* removed lock from xenoborg control computer

* announcement when all xenoborgs die

* announcement when mothership core is destroyed

* typos

* fix error

* improve xenoborg mind role

* move sounds to xenoborg and mothership component

* play sounds when turned into xenoborg

* change sprites of mothership core actions

* minor fix

* add custom xenoborg start sound

* carps now attack xenoborgs

* added guide link to xenoborgs

* add guidebook link to xenoborgs

* added guidebook link to mothership core

* add link to source of the sound

* fixed minor issue

* has to be 1

* typo

* add light layer to mothership core sprite

* fixed antag selection system

* update guideboook

* update the guidebook again

* alphabet

* documentation

* simplify documentation

Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* make the briefing yml instead of code

* fix bug with sprites

* fix formating

* remove stuff from AntagSelection

* add stuff to AntagSpawner

* fix game rule

* removed secret xenoborg preset

* support for more than one entity for each antag role

* fix gamerule

* fixes

* no xeonborgs

* add xenoborgs to all at once

* engi xenoborg included

* more comments

* more recognizable

* more xenoborgs when more players

* removed unused stuff

* correct access

* removed unnecessary stuff

* use GetAliveHumans and make comments better

* Make the system more robust

* use a constant

* remove overload from the Destroy method

* has to be public to be used in the xenoborg system

* fix the mindrole methods

* not sure, but I was asked to do it

* use a constant for the color

* forgot to make it static

* removed param comments

* removed useless parameters

* fixed stuff

* added event listeneers to xenoborg mind change

* only 4

* unfuck the antagSpawner

* comment

* unfix stuff

* commentary

* removed xenoborg stuff from siliconLawSystem

* move some stuff to the component

* removed space

* removed uncessary stuff

* no need to crate a var

* move stuff from mothership comp to xenoborg comp

* removed XenoborgCoreRoleComponent

* comment on the AntagSelectLocationEvent

* added back empty line

* comment

* make the summary better

* make AntagRoleToPrototypes summary better

* adding useless stuff back cause it was there before

* hascomp instead of trycomp

* again

* LocId instead of string

* make a new logic for the whitelist of the spawnpoint

* added ghostrole tags back

* use hascomp instead of trycomp

* removed whitelist from SpawnPointComponent

* not needed anymore

* no longer subverted

* fixed names

* make it better

* add not

* i'm dumb

* briefing is now handled by the xenoborg system

* call evac if there is too many xenoborgs

* update submodule

* fix division

* Add AutoGenerateComponentPause and AutoPausedField to XenoborgsRuleComponent

* add lines between stuff

* Make the Blocking system more robust

* Make mothership inherit from BaseMob

* remove this stuff cause is bad

* Revert "Make the Blocking system more robust"

This reverts commit 099babfe1daef00e6073e04108920327416e4ca4.

* Mothership core snaps to grid

* stop mothership core from moving

* mothership core is static again

* make guidebook entry on how to xenoborg crew better

* Make mothership core damageable

* If xenoborgs need it, so do zombies i guess

* Start the NextRoundEndCheck

* follow private static readonly naming rule

* Samething

* Fix announcments

* Make it a datafield, no?

* Revert "Make it a datafield, no?"

This reverts commit 62f6255ccccdd583d7f833ae4dbcd09a670f721a.

* remove stuff

* doesn't need to move

* is kinda of a structure

* so it doesn't pry floors as soon as it spawns

* powercell hand to mothership core module

* label for new hand

* core_e -> core-e

* mothership core can pilot the shuttle again

* fix duplicated tag + description to xenoborg tags

* scout xenoborg can now move in space without the jetpack so it can better use the sword module

* improve basic xenoborg module

* remove changes from zombie rule comp

* swap AllEntityQuery for EntityQueryEnumerator

* new line at the end

* change to 15 seconds

* make MothershipCoreDeathAnnouncmentSent into a datafield

---------

Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Co-authored-by: beck-thompson <beck314159@hotmail.com>
53 files changed:
Content.Server/Antag/AntagMultipleRoleSpawnerSystem.cs [new file with mode: 0644]
Content.Server/Antag/AntagSelectionSystem.cs
Content.Server/Antag/Components/AntagMultipleRoleSpawnerComponent.cs [new file with mode: 0644]
Content.Server/GameTicking/Rules/Components/XenoborgsRuleComponent.cs [new file with mode: 0644]
Content.Server/GameTicking/Rules/RuleGridsSystem.cs
Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs [new file with mode: 0644]
Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs
Content.Server/Spawners/Components/GridSpawnPointWhitelistComponent.cs [new file with mode: 0644]
Content.Server/Xenoborgs/XenoborgSystem.cs [new file with mode: 0644]
Content.Shared/Roles/Components/XenoborgRoleComponent.cs [new file with mode: 0644]
Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs
Content.Shared/Silicons/Borgs/SharedBorgSystem.cs
Content.Shared/Xenoborgs/Components/MothershipCoreComponent.cs [new file with mode: 0644]
Content.Shared/Xenoborgs/Components/XenoborgComponent.cs
Resources/Audio/Ambience/Antag/attributions.yml
Resources/Audio/Ambience/Antag/xenoborg_start.ogg [new file with mode: 0644]
Resources/Locale/en-US/game-ticking/game-presets/preset-xenoborgs.ftl [new file with mode: 0644]
Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl
Resources/Locale/en-US/guidebook/guides.ftl
Resources/Locale/en-US/mind/role-types.ftl
Resources/Locale/en-US/prototypes/roles/antags.ftl
Resources/Locale/en-US/robotics/borg_modules.ftl
Resources/Locale/en-US/station-laws/laws.ftl
Resources/Locale/en-US/xenoborgs/xenoborgs.ftl [new file with mode: 0644]
Resources/Maps/Shuttles/mothership.yml
Resources/Prototypes/Entities/Clothing/Back/specific.yml
Resources/Prototypes/Entities/Markers/Spawners/Conditional/xenoborgs.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml
Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
Resources/Prototypes/Entities/Mobs/Cyborgs/xenoborgs.yml
Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml
Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml
Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml
Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
Resources/Prototypes/GameRules/roundstart.yml
Resources/Prototypes/GameRules/subgamemodes.yml
Resources/Prototypes/Guidebook/antagonist.yml
Resources/Prototypes/Roles/Antags/xenoborgs.yml [new file with mode: 0644]
Resources/Prototypes/Roles/MindRoles/mind_roles.yml
Resources/Prototypes/ai_factions.yml
Resources/Prototypes/game_presets.yml
Resources/Prototypes/tags.yml
Resources/ServerInfo/Guidebook/Antagonist/Xenoborgs.xml [new file with mode: 0644]
Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json
Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-camera-computer.png [new file with mode: 0644]
Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-control-computer.png [new file with mode: 0644]
Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-module-module.png [new file with mode: 0644]
Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-active.png
Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-e.png [new file with mode: 0644]
Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-idle.png
Resources/Textures/Mobs/Silicon/mothership_core.rsi/meta.json
Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/xenoborg.png [new file with mode: 0644]

diff --git a/Content.Server/Antag/AntagMultipleRoleSpawnerSystem.cs b/Content.Server/Antag/AntagMultipleRoleSpawnerSystem.cs
new file mode 100644 (file)
index 0000000..d59fbc8
--- /dev/null
@@ -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<AntagMultipleRoleSpawnerComponent, AntagSelectEntityEvent>(OnSelectEntity);
+
+        _sawmill = _log.GetSawmill("antag_multiple_spawner");
+    }
+
+    private void OnSelectEntity(Entity<AntagMultipleRoleSpawnerComponent> 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));
+    }
+}
index 2d484a2aa9566471800e4accbe7d3ab6974cb094..15ba636a93a8fe0afd4c8f4e3d62a52340937a42 100644 (file)
@@ -271,7 +271,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
         bool midround = false)
     {
         var playerPool = GetPlayerPool(ent, pool, def);
-        var existingAntagCount = ent.Comp.PreSelectedSessions.TryGetValue(def, out var existingAntags) ?  existingAntags.Count : 0;
+        var existingAntagCount = ent.Comp.PreSelectedSessions.TryGetValue(def, out var existingAntags) ? existingAntags.Count : 0;
         var count = GetTargetAntagCount(ent, GetTotalPlayerCount(pool), def) - existingAntagCount;
 
         // if there is both a spawner and players getting picked, let it fall back to a spawner.
@@ -396,7 +396,8 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
 
         if (!antagEnt.HasValue)
         {
-            var getEntEv = new AntagSelectEntityEvent(session, ent);
+            var getEntEv = new AntagSelectEntityEvent(session, ent, def.PrefRoles);
+
             RaiseLocalEvent(ent, ref getEntEv, true);
             antagEnt = getEntEv.Entity;
         }
@@ -404,7 +405,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
         if (antagEnt is not { } player)
         {
             Log.Error($"Attempted to make {session} antagonist in gamerule {ToPrettyString(ent)} but there was no valid entity for player.");
-            _adminLogger.Add(LogType.AntagSelection,$"Attempted to make {session} antagonist in gamerule {ToPrettyString(ent)} but there was no valid entity for player.");
+            _adminLogger.Add(LogType.AntagSelection, $"Attempted to make {session} antagonist in gamerule {ToPrettyString(ent)} but there was no valid entity for player.");
             if (session != null && ent.Comp.RemoveUponFailedSpawn)
             {
                 ent.Comp.AssignedSessions.Remove(session);
@@ -419,7 +420,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
         // Therefore any component subscribing to this has to make sure both subscriptions return the same value
         // or the ghost role raffle location preview will be wrong.
 
-        var getPosEv = new AntagSelectLocationEvent(session, ent);
+        var getPosEv = new AntagSelectLocationEvent(session, ent, player);
         RaiseLocalEvent(ent, ref getPosEv, true);
         if (getPosEv.Handled)
         {
@@ -435,7 +436,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
             if (!TryComp<GhostRoleAntagSpawnerComponent>(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<AntagSelection
         switch (def.MultiAntagSetting)
         {
             case AntagAcceptability.None:
-            {
-                if (_role.MindIsAntagonist(mind))
-                    return false;
-                if (GetPreSelectedAntagSessions(def).Contains(session)) // Used for rules where the antag has been selected, but not started yet
-                    return false;
-                break;
-            }
+                {
+                    if (_role.MindIsAntagonist(mind))
+                        return false;
+                    if (GetPreSelectedAntagSessions(def).Contains(session)) // Used for rules where the antag has been selected, but not started yet
+                        return false;
+                    break;
+                }
             case AntagAcceptability.NotExclusive:
-            {
-                if (_role.MindIsExclusiveAntagonist(mind))
-                    return false;
-                if (GetPreSelectedExclusiveAntagSessions(def).Contains(session))
-                    return false;
-                break;
-            }
+                {
+                    if (_role.MindIsExclusiveAntagonist(mind))
+                        return false;
+                    if (GetPreSelectedExclusiveAntagSessions(def).Contains(session))
+                        return false;
+                    break;
+                }
         }
 
         // todo: expand this to allow for more fine antag-selection logic for game rules.
@@ -607,10 +608,13 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
 /// Only raised if the selected player's current entity is invalid.
 /// </summary>
 [ByRefEvent]
-public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity<AntagSelectionComponent> GameRule)
+public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity<AntagSelectionComponent> GameRule, List<ProtoId<AntagPrototype>> AntagRoles)
 {
     public readonly ICommonSession? Session = Session;
 
+    /// list of antag role prototypes associated with a entity. used by the <see cref="AntagMultipleRoleSpawnerComponent"/>
+    public readonly List<ProtoId<AntagPrototype>> AntagRoles = AntagRoles;
+
     public bool Handled => Entity != null;
 
     public EntityUid? Entity;
@@ -620,12 +624,15 @@ public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity<Anta
 /// Event raised on a game rule entity to determine the location for the antagonist.
 /// </summary>
 [ByRefEvent]
-public record struct AntagSelectLocationEvent(ICommonSession? Session, Entity<AntagSelectionComponent> GameRule)
+public record struct AntagSelectLocationEvent(ICommonSession? Session, Entity<AntagSelectionComponent> GameRule, EntityUid Entity)
 {
     public readonly ICommonSession? Session = Session;
 
     public bool Handled => Coordinates.Any();
 
+    // the entity of the antagonist
+    public EntityUid Entity = Entity;
+
     public List<MapCoordinates> Coordinates = new();
 }
 
diff --git a/Content.Server/Antag/Components/AntagMultipleRoleSpawnerComponent.cs b/Content.Server/Antag/Components/AntagMultipleRoleSpawnerComponent.cs
new file mode 100644 (file)
index 0000000..5a9103a
--- /dev/null
@@ -0,0 +1,23 @@
+using Content.Shared.Roles;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Antag.Components;
+
+/// <summary>
+/// Selects and spawns one prototype from a list for each antag prototype selected by the <see cref="AntagSelectionSystem"/>
+/// </summary>
+[RegisterComponent]
+public sealed partial class AntagMultipleRoleSpawnerComponent : Component
+{
+    /// <summary>
+    ///     antag prototype -> list of possible entities to spawn for that antag prototype. Will choose from the list randomly once with replacement unless <see cref="PickAndTake"/> is set to true
+    /// </summary>
+    [DataField]
+    public Dictionary<ProtoId<AntagPrototype>, List<EntProtoId>> AntagRoleToPrototypes;
+
+    /// <summary>
+    ///     Should you remove ent prototypes from the list after spawning one.
+    /// </summary>
+    [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 (file)
index 0000000..7428902
--- /dev/null
@@ -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
+{
+    /// <summary>
+    /// When the round will next check for round end.
+    /// </summary>
+    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+    [AutoPausedField]
+    public TimeSpan? NextRoundEndCheck;
+
+    /// <summary>
+    /// The amount of time between each check for the end of the round.
+    /// </summary>
+    [DataField]
+    public TimeSpan EndCheckDelay = TimeSpan.FromSeconds(15);
+
+    /// <summary>
+    /// After this amount of the crew become xenoborgs, the shuttle will be automatically called.
+    /// </summary>
+    [DataField]
+    public float XenoborgShuttleCallPercentage = 0.7f;
+
+    /// <summary>
+    /// If the announcment of the death of the mothership core was sent
+    /// </summary>
+    [DataField]
+    public bool MothershipCoreDeathAnnouncmentSent = false;
+}
index 9eae9e3c95f7fa0c952bbb5e665ffbb40caf5eae..ef1823390964273e8551786ff997fe6891b34008 100644 (file)
@@ -65,6 +65,12 @@ public sealed class RuleGridsSystem : GameRuleSystem<RuleGridsComponent>
             if (_whitelist.IsWhitelistFail(ent.Comp.SpawnerWhitelist, uid))
                 continue;
 
+            if (TryComp<GridSpawnPointWhitelistComponent>(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 (file)
index 0000000..8d13578
--- /dev/null
@@ -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<XenoborgsRuleComponent>
+{
+    [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<XenoborgsRuleComponent> 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<XenoborgsRuleComponent> 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;
+    }
+
+    /// <summary>
+    /// Get the number of xenoborgs
+    /// </summary>
+    /// <param name="playerControlled">if it should only include xenoborgs with a mind</param>
+    /// <param name="alive">if it should only include xenoborgs that are alive</param>
+    /// <returns>the number of xenoborgs</returns>
+    private int GetNumberXenoborgs(bool playerControlled = true, bool alive = true)
+    {
+        var numberXenoborgs = 0;
+
+        var query = EntityQueryEnumerator<XenoborgComponent>();
+        while (query.MoveNext(out var xenoborg, out _))
+        {
+            if (HasComp<MothershipCoreComponent>(xenoborg))
+                continue;
+
+            if (playerControlled && !_mindSystem.TryGetMind(xenoborg, out _, out _))
+                continue;
+
+            if (alive && !_mobState.IsAlive(xenoborg))
+                continue;
+
+            numberXenoborgs++;
+        }
+
+        return numberXenoborgs;
+    }
+
+    /// <summary>
+    /// Gets the number of xenoborg cores
+    /// </summary>
+    /// <returns>the number of xenoborg cores</returns>
+    private int GetNumberMothershipCores()
+    {
+        var numberMothershipCores = 0;
+
+        var mothershipCoreQuery = EntityQueryEnumerator<MothershipCoreComponent>();
+        while (mothershipCoreQuery.MoveNext(out _, out _))
+        {
+            numberMothershipCores++;
+        }
+
+        return numberMothershipCores;
+    }
+}
index fd3b910753d40f3b7774363768e41ba80b9cbb74..8dd074e6a01a591b2413b531b520a28e036dae54 100644 (file)
@@ -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<BorgTransponderComponent, BorgChassisComponent?> ent)
@@ -118,8 +118,15 @@ public sealed partial class BorgSystem
         ent.Comp1.NextDisable = _timing.CurTime + ent.Comp1.DisableDelay;
     }
 
-    private void Destroy(Entity<BorgTransponderComponent> ent)
+    /// <summary>
+    /// Makes a borg with <see cref="BorgTransponderComponent"/> explode
+    /// </summary>
+    /// <param name="ent">the entity of the borg</param>
+    public void Destroy(Entity<BorgTransponderComponent?> 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 (file)
index 0000000..de9f71f
--- /dev/null
@@ -0,0 +1,22 @@
+using Content.Shared.Whitelist;
+
+namespace Content.Server.Spawners.Components;
+
+/// <summary>
+/// Defines whitelist and blacklist for entities that can spawn at a spawnpoint when they are spawned via the <see cref="RuleGridsSystem"/>
+/// </summary>
+[RegisterComponent]
+public sealed partial class GridSpawnPointWhitelistComponent : Component
+{
+    /// <summary>
+    /// Whitelist of entities that can be spawned at this SpawnPoint
+    /// </summary>
+    [DataField]
+    public EntityWhitelist? Whitelist;
+
+    /// <summary>
+    /// Whitelist of entities that can't be spawned at this SpawnPoint
+    /// </summary>
+    [DataField]
+    public EntityWhitelist? Blacklist;
+}
diff --git a/Content.Server/Xenoborgs/XenoborgSystem.cs b/Content.Server/Xenoborgs/XenoborgSystem.cs
new file mode 100644 (file)
index 0000000..8a046ce
--- /dev/null
@@ -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<XenoborgComponent, DestructionEventArgs>(OnXenoborgDestroyed);
+        SubscribeLocalEvent<MothershipCoreComponent, DestructionEventArgs>(OnCoreDestroyed);
+
+        SubscribeLocalEvent<XenoborgComponent, MindAddedMessage>(OnXenoborgMindAdded);
+        SubscribeLocalEvent<XenoborgComponent, MindRemovedMessage>(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<XenoborgComponent>(); // paused xenoborgs still count
+        while (xenoborgQuery.MoveNext(out var xenoborg, out _))
+        {
+            if (xenoborg != uid)
+                return;
+        }
+
+        var mothershipCoreQuery = AllEntityQuery<MothershipCoreComponent>(); // paused mothership cores still count
+        var mothershipCoreAlive = mothershipCoreQuery.MoveNext(out _, out _);
+
+        var xenoborgsRuleQuery = EntityQueryEnumerator<XenoborgsRuleComponent>();
+        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<MothershipCoreComponent>(); // 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<XenoborgsRuleComponent>();
+        if (xenoborgsRuleQuery.MoveNext(out var xenoborgsRuleEnt, out var xenoborgsRuleComp))
+            _xenoborgsRule.SendMothershipDeathAnnouncement((xenoborgsRuleEnt, xenoborgsRuleComp));
+
+        // explode all xenoborgs
+        var xenoborgQuery = AllEntityQuery<XenoborgComponent, BorgTransponderComponent>(); // paused xenoborgs still explode
+        while (xenoborgQuery.MoveNext(out var xenoborgEnt, out _, out _))
+        {
+            if (HasComp<MothershipCoreComponent>(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<ActorComponent>(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 (file)
index 0000000..eeebad0
--- /dev/null
@@ -0,0 +1,9 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Roles.Components;
+
+/// <summary>
+/// Added to mind role entities to tag that they are a xenoborg.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class XenoborgRoleComponent : Component;
index c2bf2b2801b1567ed51c88e42b1415091c5c5b3c..f562ddefdd0741ba9058ad2f8130dff36e366344 100644 (file)
@@ -78,6 +78,12 @@ public sealed partial class BorgChassisComponent : Component
 
     [DataField]
     public ProtoId<AlertPrototype> NoBatteryAlert = "BorgBatteryNone";
+
+    /// <summary>
+    /// If the entity can open own UI.
+    /// </summary>
+    [DataField]
+    public bool CanOpenSelfUi;
 }
 
 [Serializable, NetSerializable]
index 827bb351b0784c9b529fe0d9abef6a5faee730e9..baf604de0c10a86ef6508f866601f3ceaa97776c 100644 (file)
@@ -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 (file)
index 0000000..4835e20
--- /dev/null
@@ -0,0 +1,7 @@
+namespace Content.Shared.Xenoborgs.Components;
+
+/// <summary>
+/// Defines what is a xenoborg core for the intentions of the xenoborg rule. if all xenoborg cores are destroyed. all xenoborgs will self-destruct.
+/// </summary>
+[RegisterComponent]
+public sealed partial class MothershipCoreComponent : Component;
index aee8298bc2200e85c4899c04dd10318c2fa6d50f..db4ca49c5ba0846f4e661c2bf94799700621a68d 100644 (file)
@@ -1,7 +1,32 @@
+using Content.Shared.Roles.Components;
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+
 namespace Content.Shared.Xenoborgs.Components;
 
 /// <summary>
-/// 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
 /// </summary>
 [RegisterComponent]
-public sealed partial class XenoborgMothershipComponent : Component;
+public sealed partial class XenoborgComponent : Component
+{
+    /// <summary>
+    /// The mindrole associated with the xenoborg
+    /// </summary>
+    [DataField]
+    public EntProtoId<MindRoleComponent> MindRole = "MindRoleXenoborg";
+
+    /// <summary>
+    /// The text that is sent when you become a xenoborg
+    /// </summary>
+    [DataField]
+    public LocId BriefingText = "xenoborgs-welcome";
+
+    /// <summary>
+    /// Briefing sound when you become a xenoborg
+    /// </summary>
+    [DataField]
+    public SoundSpecifier BriefingSound = new SoundPathSpecifier("/Audio/Ambience/Antag/xenoborg_start.ogg");
+}
index 5418d2a2040db1881f5e1b0184b5ec120998c67c..c9a9b1304d0aad5d864506292891d8a4dc33f565 100644 (file)
@@ -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 (file)
index 0000000..97fe49d
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 (file)
index 0000000..9ded853
--- /dev/null
@@ -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])
+
index 6c4ca0c4f4a0ed47e1ef0816435bf3730f5a7c72..30d97a28891e3e71d4930a87c5a9b75af30a5435 100644 (file)
@@ -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.
 
index e7558d1e97be12ffeaf0e5c32926ad5dd42a2d65..0212b3ad9b963b98af3a993bc9a0993d055624f2 100644 (file)
@@ -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
index 4614d20a47779620570560acf6a4044a45e6d090..0aed624b23164185f55e83425041791192c8c27f 100644 (file)
@@ -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
index 24dd8a0febb4e8fd75d36c3be7c2d9cd7b7c8194..3fa969d17057dd4703d5a0989971b0f1f74e757c 100644 (file)
@@ -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.
index ba5ee602a57882e01c299b7e3d3b467d9e27b672..83d97e7429425013c856c93dc44bac81d35a804e 100644 (file)
@@ -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
index 0883a7bff65efd4719a73c39ceffc26d0d86781b..f9d89e0d72364dd3b22c104ec69492295bf2595f 100644 (file)
@@ -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 (file)
index 0000000..3c51dd4
--- /dev/null
@@ -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.
index b958c47401229531b5bb3facdd30c7a78b987c3d..41ca3d89d8f87d2525b373ae7d64dba669ca6bba 100644 (file)
@@ -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
index 54b8f677b8ee0678fc60468d2fc9f6d615e68047..488c02e608798c3febdbcaf500a10d80b0442054 100644 (file)
         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 (file)
index 0000000..a825173
--- /dev/null
@@ -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
index d3f2e172ec56878c25d0f7b546812a2d9e4a49e3..e620f376c5da7320586949c7c65a3a733c0495bb 100644 (file)
     - 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
index dd6f5a570bd151141c498b4b9d71931bf41eaf17..ccd9b9aa4d137ab249f543da145034000d43b44b 100644 (file)
     stunTime: 5
   - type: SiliconLawProvider
     laws: XenoborgLawset # custom laws here
-    subverted: true
   - type: IntrinsicRadioTransmitter # can only use binary and xenoborg channel
     channels:
     - Xenoborg
       - !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
     guides:
     - Cyborgs
     - Robotics
-    # TODO: add Xenoborg guide (part 7 spoilers)
+    - Xenoborgs
   - type: Access
     enabled: false
     tags:
   - type: InteractionPopup
     interactSuccessSound:
       path: /Audio/Ambience/Objects/periodic_beep.ogg
+  - type: Xenoborg
index 556b6ac4c4f73f19689b47f236f18a1ac4b155fb..80f1cb8368d403c3b5bebf690f20f26c076caa7b 100644 (file)
       - !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" ]
   - 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
index fc15fe0c0b55f1fade6d2731c342056ef3963010..0ac7872b6751c863d3d7baa00a9d735516eda573 100644 (file)
@@ -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
         type: LatheBoundUserInterface
       enum.ResearchClientUiKey.Key:
         type: ResearchClientBoundUserInterface
-  - type: Transform
-    anchored: true
   - type: Pullable
   - type: StaticPrice
     price: 800
     - 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
   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
   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
index c85358663e1e9f679b42a0f57ff2033acac7ce59..e6ab450c93069415b36f6eee4bc735d154c7536e 100644 (file)
   - type: Icon
     state: pinpointer-station
   - type: Pinpointer
-    component: XenoborgMothership
+    component: MothershipCore
     targetName: the Mothership
index 9cc47d5b10fcd4a84ad558278828f96c61a03ea6..862bc86339dc219d4704834dd077396b0bde69ff 100644 (file)
     - 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 ]
     - 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
     hands:
     - item: HandheldGPSBasic
     - item: HandHeldMassScannerBorg
+    - item: PinpointerMothership
     - item: HandheldStationMapUnpowered
     - item: WeaponGrapplingGun
     - item: JetpackXenoborg
index e16380b2ef7ca5331f890eb93370ea7d39526867..50154a837bd4a9168f8a30fc9396265898fea21e 100644 (file)
     unlockOnClick: false
 
 - type: entity
-  parent: ComputerRoboticsControl
+  parent: BaseComputer
   id: ComputerXenoborgsControl
   name: xenoborgs control console
   description: Used to remotely monitor all xenoborgs.
       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
index 715a1f98c986f4611ce2a95e21caf9fea2a00d75..ad41899befbb661673324ef5bb13a344a7abd83b 100644 (file)
@@ -24,6 +24,8 @@
       prob: 0.5
     - id: SubWizard
       prob: 0.05
+    - id: Xenoborgs
+      prob: 0.05
 
 - type: entity
   parent: BaseGameRule
     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
index 0dbef6e06be7ccd9f27e52d53cd458d7d5b06399..008f500c5ef2858394737f0a50342453c05994f5 100644 (file)
         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
index 4548bb292b0388f3d32e72b739a4a67feb69fb02..66cb65218a9f368e7a20a54e360ed2d0cf0b4e5a 100644 (file)
@@ -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 (file)
index 0000000..4e1989b
--- /dev/null
@@ -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 ]
index d56e798fdabc6ef0dac9497f3e6ab461adf0a561..311c4cfcaa50f03a46df20cd84d10e999b74a875 100644 (file)
     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
index b78143b143dedbe6a839e6f2f69e6afe33eda5b8..0d1e0a6bc02e3da27a23054780148b0d6ed687b7 100644 (file)
@@ -9,6 +9,7 @@
   - Revolutionary
   - AllHostile
   - Wizard
+  - Xenoborg
 
 - type: npcFaction
   id: NanoTrasen
index f7190a77c17b3745a67336c36c9808fc6f82249a..9db2e74a354a65b2d1fba9c784a73e5ef6b0bdc9 100644 (file)
@@ -40,6 +40,7 @@
     - Revolutionary
     - Zombie
     - Wizard
+    - Xenoborgs
     - KesslerSyndromeScheduler
     - RampingStationEventScheduler
     - SpaceTrafficControlEventScheduler
@@ -61,6 +62,7 @@
     - Revolutionary
     - Zombie
     - Wizard
+    - Xenoborgs
     - BasicStationEventScheduler
     - KesslerSyndromeScheduler
     - MeteorSwarmMildScheduler
   - 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:
index 8a466cc8c8a4af3b5745fa284788b0f744242514..3b3d11e612e1728ca34b849f71ea0890671fdfed 100644 (file)
   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
 
 ## X ##
 
 - type: Tag
-  id: XenoborgModuleEngi # Cyborg module category
+  id: XenoborgGhostrole # spawn whitelist : SpawnPointGhostRoleXenoborg
+
+- type: Tag
+  id: XenoborgModuleEngi # Cyborg module category for evil engineer xenoborg
 
 - type: Tag
-  id: XenoborgModuleGeneric # Cyborg module category
+  id: XenoborgModuleGeneric # Cyborg module category for evil xenoborg
 
 - type: Tag
-  id: XenoborgModuleHeavy # Cyborg module category
+  id: XenoborgModuleHeavy # Cyborg module category for evil heavy xenoborg
 
 - type: Tag
-  id: XenoborgModuleScout # Cyborg module category
+  id: XenoborgModuleScout # Cyborg module category for evil scout xenoborg
 
 - type: Tag
-  id: XenoborgModuleStealth # Cyborg module category
+  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 (file)
index 0000000..2c69825
--- /dev/null
@@ -0,0 +1,167 @@
+<Document>
+  # Xenoborgs
+
+  <Box>
+    [color=#2288ff][italic]RESISTANCE IS FUTILE[/italic][/color]
+  </Box>
+  <Box>
+    [color=#2288ff][italic]YOU WILL BE ASSIMILATED[/italic][/color]
+  </Box>
+  <Box>
+    <GuideEntityEmbed Entity="MMIFilled" Caption=""/>
+  </Box>
+
+  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
+  <Box>
+    <GuideEntityEmbed Entity="MothershipCore" Caption=""/>
+  </Box>
+  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:
+  <Box>
+    <GuideEntityEmbed Entity="MachineArtifactCrusher" Caption="body crusher"/>
+  </Box>
+  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]
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleBasic" Caption="basic xenoborg module"/>
+    <GuideEntityEmbed Entity="XenoborgModuleTool" Caption="tool xenoborg module"/>
+  </Box>
+
+  ### The Engineer Xenoborg
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgEngi" Caption=""/>
+  </Box>
+  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]
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleAccessBreaker" Caption="access breaker xenoborg module"/>
+    <GuideEntityEmbed Entity="XenoborgModuleFireExtinguisher" Caption="fire extinguisher xenoborg module"/>
+  </Box>
+
+  [bold]The Engineer Xenoborg also starts with some engineering modules, such as:[/bold]
+  <Box>
+    <GuideEntityEmbed Entity="BorgModuleAdvancedTool" Caption="advanced tool cyborg module"/>
+    <GuideEntityEmbed Entity="BorgModuleConstruction" Caption="construction cyborg module"/>
+  </Box>
+  <Box>
+    <GuideEntityEmbed Entity="BorgModuleRCD" Caption="engineering cyborg module"/>
+    <GuideEntityEmbed Entity="BorgModuleCable" Caption="cable cyborg module"/>
+  </Box>
+
+  ### The Heavy Xenoborg
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgHeavy" Caption=""/>
+  </Box>
+  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]
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleLaser" Caption="laser xenoborg module"/>
+    <GuideEntityEmbed Entity="XenoborgModuleJammer" Caption="jammer xenoborg module"/>
+  </Box>
+
+  [bold]Upgrade exclusive modules:[/bold]
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleHeavyLaser" Caption="heavy laser xenoborg module"/>
+  </Box>
+
+  ### The Scout Xenoborg
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgScout" Caption=""/>
+  </Box>
+  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]
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleSword" Caption="sword xenoborg module"/>
+    <GuideEntityEmbed Entity="XenoborgModuleSpaceMovement" Caption="space movement xenoborg module"/>
+  </Box>
+
+  [bold]Upgrade exclusive modules:[/bold]
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleEnergySword" Caption="energy sword xenoborg module"/>
+  </Box>
+
+  ### The Stealth Xenoborg
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgStealth" Caption=""/>
+  </Box>
+  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]
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleHypo" Caption="nocturine hypo xenoborg module"/>
+    <GuideEntityEmbed Entity="XenoborgModuleChameleonProjector" Caption="chameleon projector xenoborg module"/>
+  </Box>
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleCloakDevice" Caption="cloaking device xenoborg module"/>
+  </Box>
+
+  [bold]Upgrade exclusive modules:[/bold]
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgModuleSuperCloakDevice" Caption="cloaking device xenoborg module"/>
+  </Box>
+
+  ## 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.
+
+  <Box>
+    <GuideEntityEmbed Entity="XenoborgCircuitBoard" Caption=""/>
+  </Box>
+
+  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.
+
+</Document>
index fc70118da6d9fdb6b7e006c2278d7d13b69fb1ef..6072a9a2f9dd16f1fd50619e38922aad2995eac5 100644 (file)
         {
             "name":"xenoborg-basic-module"
         },
+        {
+            "name":"xenoborg-camera-computer"
+        },
+        {
+            "name":"xenoborg-control-computer"
+        },
         {
             "name":"xenoborg-extinguisher-module"
         },
         {
             "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 (file)
index 0000000..2578beb
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 (file)
index 0000000..65ebc2f
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 (file)
index 0000000..144c5bc
Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-module-module.png differ
index 1b64de18a2745b6b0841c86fcccd02afa0c2b920..ccaf6842d0ec8b04f85b06e2ed8d98e841539dd6 100644 (file)
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 (file)
index 0000000..2cfb8be
Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/mothership_core.rsi/core-e.png differ
index 5cafcef584ab894928f383d1dbf8fc45a8289b3c..b674a008d6775dac313af6a11f4e6e4943d58d1b 100644 (file)
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
index 0f8bb695308f68d0c39ffc8e14833aba843aa32f..3f2b3fac24e5fbe777327b1f4e1540b675b01ec1 100644 (file)
       "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 (file)
index 0000000..45f98a2
--- /dev/null
@@ -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 (file)
index 0000000..1a1e082
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/silicon_storage_cube.rsi/xenoborg.png differ