if (component.Seed.Sentient)
{
- var comp = EnsureComp<GhostTakeoverAvailableComponent>(uid);
- comp.RoleName = MetaData(uid).EntityName;
- comp.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", comp.RoleName));
+ var ghostRole = EnsureComp<GhostRoleComponent>(uid);
+ EnsureComp<GhostTakeoverAvailableComponent>(uid);
+ ghostRole.RoleName = MetaData(uid).EntityName;
+ ghostRole.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", ghostRole.RoleName));
}
if (component.UpdateSpriteAfterUpdate)
+using Content.Server.Ghost.Roles.Components;
using Content.Server.Mind.Components;
using Content.Server.Speech.Components;
-
using Content.Shared.Chemistry.Reagent;
-using Content.Server.Ghost.Roles.Components;
namespace Content.Server.Chemistry.ReagentEffects;
}
// No idea what anything past this point does
- if (entityManager.TryGetComponent(uid, out GhostTakeoverAvailableComponent? takeOver))
+ if (entityManager.TryGetComponent(uid, out GhostRoleComponent? ghostRole) ||
+ entityManager.TryGetComponent(uid, out GhostTakeoverAvailableComponent? takeOver))
{
return;
}
- takeOver = entityManager.AddComponent<GhostTakeoverAvailableComponent>(uid);
+ ghostRole = entityManager.AddComponent<GhostRoleComponent>(uid);
+ entityManager.AddComponent<GhostTakeoverAvailableComponent>(uid);
var entityData = entityManager.GetComponent<MetaDataComponent>(uid);
- takeOver.RoleName = entityData.EntityName;
- takeOver.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description");
+ ghostRole.RoleName = entityData.EntityName;
+ ghostRole.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description");
}
}
using Content.Server.Station.Systems;
using Content.Server.Traitor;
using Content.Shared.Dataset;
+using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Nuke;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
-using Content.Shared.Humanoid.Prototypes;
namespace Content.Server.GameTicking.Rules;
else if (addSpawnPoints)
{
var spawnPoint = EntityManager.SpawnEntity(_nukeopsRuleConfig.GhostSpawnPointProto, _random.Pick(spawns));
- var spawner = EnsureComp<GhostRoleMobSpawnerComponent>(spawnPoint);
- spawner.RoleName = Loc.GetString(nukeOpsAntag.Name);
- spawner.RoleDescription = Loc.GetString(nukeOpsAntag.Objective);
+ var ghostRole = EnsureComp<GhostRoleComponent>(spawnPoint);
+ EnsureComp<GhostRoleMobSpawnerComponent>(spawnPoint);
+ ghostRole.RoleName = Loc.GetString(nukeOpsAntag.Name);
+ ghostRole.RoleDescription = Loc.GetString(nukeOpsAntag.Objective);
var nukeOpSpawner = EnsureComp<NukeOperativeSpawnerComponent>(spawnPoint);
nukeOpSpawner.OperativeName = spawnDetails.Name;
using Content.Server.Mind.Commands;
-using Robust.Server.Player;
namespace Content.Server.Ghost.Roles.Components
{
+ [RegisterComponent]
[Access(typeof(GhostRoleSystem))]
- public abstract class GhostRoleComponent : Component
+ public sealed class GhostRoleComponent : Component
{
[DataField("name")] public string _roleName = "Unknown";
/// Whether the <see cref="MakeSentientCommand"/> should run on the mob.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField("makeSentient")]
- protected bool MakeSentient = true;
+ public bool MakeSentient = true;
/// <summary>
/// The probability that this ghost role will be available after init.
[ViewVariables(VVAccess.ReadWrite)]
[DataField("reregister")]
public bool ReregisterOnGhost { get; set; } = true;
-
- public abstract bool Take(IPlayerSession session);
}
}
-using Content.Server.Mind.Commands;
-using Content.Server.Mind.Components;
-using JetBrains.Annotations;
-using Robust.Server.Player;
-using Robust.Shared.Prototypes;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Content.Server.Ghost.Roles.Events;
namespace Content.Server.Ghost.Roles.Components
{
/// <summary>
/// Allows a ghost to take this role, spawning a new entity.
/// </summary>
- [RegisterComponent, ComponentReference(typeof(GhostRoleComponent))]
- public sealed class GhostRoleMobSpawnerComponent : GhostRoleComponent
+ [RegisterComponent]
+ [Access(typeof(GhostRoleSystem))]
+ public sealed class GhostRoleMobSpawnerComponent : Component
{
- [Dependency] private readonly IEntityManager _entMan = default!;
-
[ViewVariables(VVAccess.ReadWrite)] [DataField("deleteOnSpawn")]
- private bool _deleteOnSpawn = true;
+ public bool DeleteOnSpawn = true;
[ViewVariables(VVAccess.ReadWrite)] [DataField("availableTakeovers")]
- private int _availableTakeovers = 1;
+ public int AvailableTakeovers = 1;
[ViewVariables]
- private int _currentTakeovers = 0;
+ public int CurrentTakeovers = 0;
- [CanBeNull]
[ViewVariables(VVAccess.ReadWrite)]
[DataField("prototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? Prototype { get; private set; }
-
- public override bool Take(IPlayerSession session)
- {
- if (Taken)
- return false;
-
- if (string.IsNullOrEmpty(Prototype))
- throw new NullReferenceException("Prototype string cannot be null or empty!");
-
- var mob = _entMan.SpawnEntity(Prototype, _entMan.GetComponent<TransformComponent>(Owner).Coordinates);
- var xform = _entMan.GetComponent<TransformComponent>(mob);
- xform.AttachToGridOrMap();
-
- var spawnedEvent = new GhostRoleSpawnerUsedEvent(Owner, mob);
- _entMan.EventBus.RaiseLocalEvent(mob, spawnedEvent, false);
-
- if (MakeSentient)
- MakeSentientCommand.MakeSentient(mob, _entMan, AllowMovement, AllowSpeech);
-
- mob.EnsureComponent<MindComponent>();
-
- var ghostRoleSystem = EntitySystem.Get<GhostRoleSystem>();
- ghostRoleSystem.GhostRoleInternalCreateMindAndTransfer(session, Owner, mob, this);
-
- if (++_currentTakeovers < _availableTakeovers)
- return true;
-
- Taken = true;
-
- if (_deleteOnSpawn)
- _entMan.QueueDeleteEntity(Owner);
-
- return true;
- }
}
}
-using Content.Server.Mind.Commands;
-using Content.Server.Mind.Components;
-using Robust.Server.Player;
-
namespace Content.Server.Ghost.Roles.Components
{
/// <summary>
/// Allows a ghost to take over the Owner entity.
/// </summary>
- [RegisterComponent, ComponentReference(typeof(GhostRoleComponent))]
- public sealed class GhostTakeoverAvailableComponent : GhostRoleComponent
+ [RegisterComponent]
+ [Access(typeof(GhostRoleSystem))]
+ public sealed class GhostTakeoverAvailableComponent : Component
{
- public override bool Take(IPlayerSession session)
- {
- if (Taken)
- return false;
-
- Taken = true;
-
- var mind = Owner.EnsureComponent<MindComponent>();
-
- if (mind.HasMind)
- return false;
-
- if (MakeSentient)
- MakeSentientCommand.MakeSentient(Owner, IoCManager.Resolve<IEntityManager>(), AllowMovement, AllowSpeech);
-
- var ghostRoleSystem = EntitySystem.Get<GhostRoleSystem>();
- ghostRoleSystem.GhostRoleInternalCreateMindAndTransfer(session, Owner, Owner, this);
-
- ghostRoleSystem.UnregisterGhostRole(this);
-
- return true;
- }
}
}
--- /dev/null
+using Robust.Server.Player;
+
+namespace Content.Server.Ghost.Roles.Components;
+
+[ByRefEvent]
+public record struct TakeGhostRoleEvent(IPlayerSession Player)
+{
+ public bool TookRole { get; set; }
+}
using Content.Server.EUI;
using Content.Server.Ghost.Components;
using Content.Server.Ghost.Roles.Components;
+using Content.Server.Ghost.Roles.Events;
using Content.Server.Ghost.Roles.UI;
+using Content.Server.Mind.Commands;
using Content.Server.Mind.Components;
using Content.Server.Players;
using Content.Shared.Administration;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly FollowerSystem _followerSystem = default!;
+ [Dependency] private readonly TransformSystem _transform = default!;
private uint _nextRoleIdentifier;
private bool _needsUpdateGhostRoleCount = true;
SubscribeLocalEvent<GhostTakeoverAvailableComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<GhostRoleComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<GhostRoleComponent, ComponentShutdown>(OnShutdown);
+ SubscribeLocalEvent<GhostRoleMobSpawnerComponent, TakeGhostRoleEvent>(OnSpawnerTakeRole);
+ SubscribeLocalEvent<GhostTakeoverAvailableComponent, TakeGhostRoleEvent>(OnTakeoverTakeRole);
_playerManager.PlayerStatusChanged += PlayerStatusChanged;
}
- private void OnMobStateChanged(EntityUid uid, GhostRoleComponent component, MobStateChangedEvent args)
+ private void OnMobStateChanged(EntityUid uid, GhostTakeoverAvailableComponent component, MobStateChangedEvent args)
{
+ if (!TryComp(uid, out GhostRoleComponent? ghostRole))
+ return;
+
switch (args.NewMobState)
{
case MobState.Alive:
{
- if (!component.Taken)
- RegisterGhostRole(component);
+ if (!ghostRole.Taken)
+ RegisterGhostRole(ghostRole);
break;
}
case MobState.Critical:
case MobState.Dead:
- UnregisterGhostRole(component);
+ UnregisterGhostRole(ghostRole);
break;
}
}
public void Takeover(IPlayerSession player, uint identifier)
{
if (!_ghostRoles.TryGetValue(identifier, out var role)) return;
- if (!role.Take(player)) return;
+
+ var ev = new TakeGhostRoleEvent(player);
+ RaiseLocalEvent(role.Owner, ref ev);
+
+ if (!ev.TookRole) return;
if (player.AttachedEntity != null)
_adminLogger.Add(LogType.GhostRoleTaken, LogImpact.Low, $"{player:player} took the {role.RoleName:roleName} ghost role {ToPrettyString(player.AttachedEntity.Value):entity}");
private void OnMindAdded(EntityUid uid, GhostTakeoverAvailableComponent component, MindAddedMessage args)
{
- component.Taken = true;
- UnregisterGhostRole(component);
+ if (!TryComp(uid, out GhostRoleComponent? ghostRole))
+ return;
+
+ ghostRole.Taken = true;
+ UnregisterGhostRole(ghostRole);
}
- private void OnMindRemoved(EntityUid uid, GhostRoleComponent component, MindRemovedMessage args)
+ private void OnMindRemoved(EntityUid uid, GhostTakeoverAvailableComponent component, MindRemovedMessage args)
{
+ if (!TryComp(uid, out GhostRoleComponent? ghostRole))
+ return;
+
// Avoid re-registering it for duplicate entries and potential exceptions.
- if (!component.ReregisterOnGhost || component.LifeStage > ComponentLifeStage.Running)
+ if (!ghostRole.ReregisterOnGhost || component.LifeStage > ComponentLifeStage.Running)
return;
- component.Taken = false;
- RegisterGhostRole(component);
+ ghostRole.Taken = false;
+ RegisterGhostRole(ghostRole);
}
public void Reset(RoundRestartCleanupEvent ev)
{
UnregisterGhostRole(role);
}
+
+ private void OnSpawnerTakeRole(EntityUid uid, GhostRoleMobSpawnerComponent component, ref TakeGhostRoleEvent args)
+ {
+ if (!TryComp(uid, out GhostRoleComponent? ghostRole) ||
+ ghostRole.Taken)
+ {
+ args.TookRole = false;
+ return;
+ }
+
+ if (string.IsNullOrEmpty(component.Prototype))
+ throw new NullReferenceException("Prototype string cannot be null or empty!");
+
+ var mob = Spawn(component.Prototype, Transform(uid).Coordinates);
+ _transform.AttachToGridOrMap(mob);
+
+ var spawnedEvent = new GhostRoleSpawnerUsedEvent(uid, mob);
+ RaiseLocalEvent(mob, spawnedEvent);
+
+ if (ghostRole.MakeSentient)
+ MakeSentientCommand.MakeSentient(mob, EntityManager, ghostRole.AllowMovement, ghostRole.AllowSpeech);
+
+ mob.EnsureComponent<MindComponent>();
+
+ GhostRoleInternalCreateMindAndTransfer(args.Player, uid, mob, ghostRole);
+
+ if (++component.CurrentTakeovers < component.AvailableTakeovers)
+ {
+ args.TookRole = true;
+ return;
+ }
+
+ ghostRole.Taken = true;
+
+ if (component.DeleteOnSpawn)
+ QueueDel(uid);
+
+ args.TookRole = true;
+ }
+
+ private void OnTakeoverTakeRole(EntityUid uid, GhostTakeoverAvailableComponent component, ref TakeGhostRoleEvent args)
+ {
+ if (!TryComp(uid, out GhostRoleComponent? ghostRole) ||
+ ghostRole.Taken)
+ {
+ args.TookRole = false;
+ return;
+ }
+
+ ghostRole.Taken = true;
+
+ var mind = EnsureComp<MindComponent>(uid);
+
+ if (mind.HasMind)
+ {
+ args.TookRole = false;
+ return;
+ }
+
+ if (ghostRole.MakeSentient)
+ MakeSentientCommand.MakeSentient(uid, EntityManager, ghostRole.AllowMovement, ghostRole.AllowSpeech);
+
+ GhostRoleInternalCreateMindAndTransfer(args.Player, uid, uid, ghostRole);
+ UnregisterGhostRole(ghostRole);
+
+ args.TookRole = true;
+ }
}
[AnyCommand]
var description = args[2];
var rules = args.Length >= 4 ? args[3] : Loc.GetString("ghost-role-component-default-rules");
+ if (entityManager.TryGetComponent(uid, out GhostRoleComponent? ghostRole))
+ {
+ shell.WriteLine($"Entity {metaData.EntityName} with id {uid} already has a {nameof(GhostRoleComponent)}");
+ return;
+ }
+
if (entityManager.TryGetComponent(uid, out GhostTakeoverAvailableComponent? takeOver))
{
shell.WriteLine($"Entity {metaData.EntityName} with id {uid} already has a {nameof(GhostTakeoverAvailableComponent)}");
return;
}
- takeOver = entityManager.AddComponent<GhostTakeoverAvailableComponent>(uid);
- takeOver.RoleName = name;
- takeOver.RoleDescription = description;
- takeOver.RoleRules = rules;
+ ghostRole = entityManager.AddComponent<GhostRoleComponent>(uid);
+ entityManager.AddComponent<GhostTakeoverAvailableComponent>(uid);
+ ghostRole.RoleName = name;
+ ghostRole.RoleDescription = description;
+ ghostRole.RoleRules = rules;
shell.WriteLine($"Made entity {metaData.EntityName} a ghost role.");
}
-using Content.Shared.Examine;
-using Content.Shared.PAI;
-using Content.Shared.Verbs;
-using Content.Server.Popups;
-using Content.Server.Instruments;
using Content.Server.Ghost.Roles.Components;
+using Content.Server.Instruments;
using Content.Server.Mind.Components;
-using Robust.Server.GameObjects;
-using Robust.Shared.Player;
+using Content.Server.Popups;
+using Content.Shared.Examine;
using Content.Shared.Interaction.Events;
+using Content.Shared.PAI;
using Content.Shared.Popups;
+using Content.Shared.Verbs;
+using Robust.Server.GameObjects;
namespace Content.Server.PAI
{
EntityManager.GetComponent<MetaDataComponent>(component.Owner).EntityName = val;
- var ghostFinder = EntityManager.EnsureComponent<GhostTakeoverAvailableComponent>(uid);
+ var ghostRole = AddComp<GhostRoleComponent>(uid);
+ EnsureComp<GhostTakeoverAvailableComponent>(uid);
- ghostFinder.RoleName = Loc.GetString("pai-system-role-name");
- ghostFinder.RoleDescription = Loc.GetString("pai-system-role-description");
+ ghostRole.RoleName = Loc.GetString("pai-system-role-name");
+ ghostRole.RoleDescription = Loc.GetString("pai-system-role-description");
_popupSystem.PopupEntity(Loc.GetString("pai-system-searching"), uid, args.User);
UpdatePAIAppearance(uid, PAIStatus.Searching);
using System.Linq;
-using Content.Server.Chat;
using Content.Server.Chat.Systems;
using Content.Server.Ghost.Roles.Components;
-using Content.Server.Mind.Commands;
using Content.Server.Station.Systems;
using Content.Server.StationEvents.Components;
-using Robust.Shared.Random;
namespace Content.Server.StationEvents.Events;
break;
EntityManager.RemoveComponent<SentienceTargetComponent>(target.Owner);
- var comp = EntityManager.AddComponent<GhostTakeoverAvailableComponent>(target.Owner);
- comp.RoleName = EntityManager.GetComponent<MetaDataComponent>(target.Owner).EntityName;
- comp.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", comp.RoleName));
+ var ghostRole = AddComp<GhostRoleComponent>(target.Owner);
+ AddComp<GhostTakeoverAvailableComponent>(target.Owner);
+ ghostRole.RoleName = EntityManager.GetComponent<MetaDataComponent>(target.Owner).EntityName;
+ ghostRole.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", ghostRole.RoleName));
groups.Add(Loc.GetString(target.FlavorKind));
}
if (!HasComp<GhostRoleMobSpawnerComponent>(target) && !mindcomp.HasMind) //this specific component gives build test trouble so pop off, ig
{
//yet more hardcoding. Visit zombie.ftl for more information.
- EntityManager.EnsureComponent<GhostTakeoverAvailableComponent>(target, out var ghostcomp);
- ghostcomp.RoleName = Loc.GetString("zombie-generic");
- ghostcomp.RoleDescription = Loc.GetString("zombie-role-desc");
- ghostcomp.RoleRules = Loc.GetString("zombie-role-rules");
+ var ghostRole = EnsureComp<GhostRoleComponent>(target);
+ EnsureComp<GhostTakeoverAvailableComponent>(target);
+ ghostRole.RoleName = Loc.GetString("zombie-generic");
+ ghostRole.RoleDescription = Loc.GetString("zombie-role-desc");
+ ghostRole.RoleRules = Loc.GetString("zombie-role-rules");
}
//Goes through every hand, drops the items in it, then removes the hand
suffix: rat king
parent: MarkerBase
components:
- - type: GhostRoleMobSpawner
+ - type: GhostRole
prototype: MobRatKing
name: Rat King
description: You are the Rat King, scavenge food in order to produce rat minions to do your bidding.
suffix: Remilia
parent: MarkerBase
components:
- - type: GhostRoleMobSpawner
- prototype: MobBatRemilia
+ - type: GhostRole
name: Remilia, the chaplain's familiar
description: Obey your master. Eat fruit.
rules: You are an intelligent fruit bat. Follow the chaplain around. Don't cause any trouble unless the chaplain tells you to.
+ - type: GhostRoleMobSpawner
+ prototype: MobBatRemilia
- type: Sprite
sprite: Markers/jobs.rsi
layers:
suffix: cerberus
parent: MarkerBase
components:
- - type: GhostRoleMobSpawner
- prototype: MobCorgiCerberus
+ - type: GhostRole
name: Cerberus, Evil Familiar
description: Obey your master. Spread chaos.
rules: You are an intelligent, demonic dog. Try to help the chaplain and any of his flock. As an antagonist, you're otherwise unrestrained.
+ - type: GhostRoleMobSpawner
+ prototype: MobCorgiCerberus
- type: Sprite
sprite: Markers/jobs.rsi
layers:
suffix: nukeops
parent: MarkerBase
components:
+ - type: GhostRole
+ rules: You are a syndicate operative tasked with the destruction of the station. As an antagonist, do whatever is required to complete this task.
- type: GhostRoleMobSpawner
prototype: MobHumanNukeOp
- rules: You are a syndicate operative tasked with the destruction of the station. As an antagonist, do whatever is required to complete this task.
- type: NukeOperativeSpawner
- type: Sprite
sprite: Markers/jobs.rsi
- state: green
- sprite: Structures/Wallmounts/signs.rsi
state: radiation
-
-
id: MobMouse
description: Squeak!
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
allowSpeech: true
allowMovement: true
name: ghost-role-information-mouse-name
description: ghost-role-information-mouse-description
+ - type: GhostTakeoverAvailable
- type: Speech
speechSounds: Squeak
- type: Sprite
- type: MobMover
- type: HTN
rootTask: SimpleHostileCompound
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
name: ghost-role-information-giant-spider-name
description: ghost-role-information-giant-spider-description
+ - type: GhostTakeoverAvailable
- type: entity
name: possum
id: MobHamster
description: A cute, fluffy, robust hamster.
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
allowSpeech: true
allowMovement: true
name: ghost-role-information-hamster-name
description: ghost-role-information-hamster-description
+ - type: GhostTakeoverAvailable
- type: Speech
speechSounds: Squeak
- type: Sprite
radius: 1.2
energy: 2
color: "#4faffb"
- - type: GhostTakeoverAvailable
+ - type: GhostRole
prob: 0.25
name: space bear
description: |
You're a bear! Do bear things.
+ - type: GhostTakeoverAvailable
- type: entity
id: MobBearSpaceSalvage
parent: MobBearSpace
suffix: "Salvage Ruleset"
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
prob: 0.25
name: space bear on salvage wreck
description: |
Defend the loot inside the salvage wreck!
+ - type: GhostTakeoverAvailable
- type: SalvageMobRestrictions
parent: MobCarp
suffix: "Salvage Ruleset"
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
prob: 0.33
name: space carp on salvage wreck
allowMovement: true
allowSpeech: true
description: |
Defend the loot inside the salvage wreck!
+ - type: GhostTakeoverAvailable
- type: SalvageMobRestrictions
- type: entity
suffix: DragonBrood
parent: BaseMobCarp
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: Sentient Carp
description: Help the dragon flood the station with carps!
+ - type: GhostTakeoverAvailable
- type: HTN
rootTask: DragonCarpCompound
id: MobHamsterHamlet
description: A grumpy, cute and fluffy hamster.
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
allowSpeech: true
allowMovement: true
name: ghost-role-information-hamlet-name
description: ghost-role-information-hamlet-description
+ - type: GhostTakeoverAvailable
- type: InteractionPopup
successChance: 1
- type: Butcherable
Base: dead
Dead:
Base: dead
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
name: Rat King
description: You are the Rat King, scavenge food in order to produce rat minions to do your bidding.
rules: You are an antagonist, scavenge, attack, and grow your hoard!
+ - type: GhostTakeoverAvailable
- type: Tag
tags:
- CannotSuicide
Female: Mouse
Unsexed: Mouse
wilhelmProbability: 0.001
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
name: Rat Servant
description: You are a Rat Servant. You must follow your king's orders.
rules: You are an antagonist, scavenge, attack, and serve your king!
+ - type: GhostTakeoverAvailable
- type: Tag
tags:
- CannotSuicide
- type: Alerts
- type: NameIdentifier
group: GenericNumber
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
name: Revenant
description: You are a Revenant. Use your powers to harvest souls and unleash chaos upon the crew. Unlock new abilities with the essence you harvest.
rules: You are an antagonist, harvest, defile, and drive the crew insane.
+ - type: GhostTakeoverAvailable
- type: Revenant
- type: PointLight
color: MediumPurple
- type: Construction
graph: HonkBot
node: bot
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
name: honkbot
description: An artificial being of pure evil.
+ - type: GhostTakeoverAvailable
- type: InteractionPopup
interactSuccessString: petting-success-honkbot
interactFailureString: petting-failure-honkbot
Quantity: 5
- type: MeleeChemicalInjector
solution: melee
- - type: GhostTakeoverAvailable
+ - type: GhostRole
prob: 0.33
makeSentient: true
name: space tick
description: |
Wreak havoc on the station!
+ - type: GhostTakeoverAvailable
- type: ReplacementAccent
accent: genericAggressive
parent: MobTick
suffix: "Salvage Ruleset"
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: space tick on salvage wreck
description: |
Defend the loot inside the salvage wreck!
+ - type: GhostTakeoverAvailable
- type: SalvageMobRestrictions
spawned:
- id: FoodMeatXeno
amount: 5
- - type: GhostTakeoverAvailable
+ - type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: xeno
description: You are a xeno, co-operate with your hive to kill all crewmembers!
rules: You are an antagonist, smack, slash, and wack!
+ - type: GhostTakeoverAvailable
- type: TypingIndicator
proto: alien
- type: Temperature
suffix: ""
description: A flying leviathan, loosely related to space carps.
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: Space dragon
description: Call in 3 carp rifts and take over this quadrant! You have only 5 minutes in between each rift before you will disappear.
+ - type: GhostTakeoverAvailable
- type: HTN
rootTask: XenoCompound
- type: Faction
id: MobBatRemilia
description: The chaplain's familiar. Likes fruit.
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
allowMovement: true
allowSpeech: true
name: Remilia, the chaplain's familiar
description: Obey your master. Eat fruit.
rules: You are an intelligent fruit bat. Follow the chaplain around. Don't cause any trouble unless the chaplain tells you to.
+ - type: GhostTakeoverAvailable
- type: Grammar
attributes:
gender: female
id: MobCorgiCerberus
description: This pupper is not wholesome.
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
allowMovement: true
allowSpeech: true
name: Cerberus, Evil Familiar
description: Obey your master. Spread chaos.
rules: You are an intelligent, demonic dog. Try to help the chaplain and any of his flock. As an antagonist, you're otherwise unrestrained.
+ - type: GhostTakeoverAvailable
- type: MeleeWeapon
hidden: true
angle: 0
save: false
components:
- type: LagCompensation
- - type: GhostTakeoverAvailable
+ - type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: Guardian
description: Listen to your owner. Don't tank damage. Punch people hard.
+ - type: GhostTakeoverAvailable
- type: Input
context: "human"
- type: MobMover
parent: MobGuardianBase
description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself.
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: Holoparasite
description: Listen to your owner. Don't tank damage. Punch people hard.
+ - type: GhostTakeoverAvailable
- type: NameIdentifier
group: Holoparasite
- type: TypingIndicator
id: MobIfritGuardian
description: A corrupted jinn, ripped from fitra to serve the wizard's petty needs. It stands wicked, tuning into it's owner's life to sustain itself.
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: Ifrit
description: Listen to your owner. Don't tank damage. Punch people hard.
+ - type: GhostTakeoverAvailable
- type: RandomSprite
available:
- enum.DamageStateVisualLayers.BaseUnshaded:
id: ERTLeader
randomizeName: false
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Leader
description: Lead a team of specialists to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [ ERTLeaderGear ]
- type: RandomMetadata
id: ERTLeaderEVA
parent: ERTLeader
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Leader
description: Lead a team of specialists to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [ ERTLeaderGearEVA ]
id: ERTJanitor
parent: ERTLeader
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Janitor
description: Assist with custodial efforts to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: RandomMetadata
nameSegments:
- NamesFirstMilitary
id: ERTJanitorEVA
parent: ERTJanitor
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Janitor
description: Assist with custodial efforts to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [ ERTJanitorGearEVA ]
id: ERTEngineer
parent: ERTLeader
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Engineer
description: Assist with engineering efforts to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: RandomMetadata
nameSegments:
- NamesFirstMilitary
id: ERTEngineerEVA
parent: ERTEngineer
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Engineer
description: Assist with engineering efforts to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [ ERTEngineerGearEVA ]
id: ERTSecurity
parent: ERTLeader
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Security
description: Assist with security efforts to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: RandomMetadata
nameSegments:
- NamesFirstMilitary
id: ERTSecurityEVA
parent: ERTSecurity
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Security
description: Assist with security efforts to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [ ERTSecurityGearEVA ]
id: ERTMedical
parent: ERTLeader
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Medical
description: Assist with medicaling efforts to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: RandomMetadata
nameSegments:
- NamesFirstMilitary
id: ERTMedicalEVA
parent: ERTMedical
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: ERT Medical
description: Assist with medicaling efforts to resolve the stations issues.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [ ERTMedicalGearEVA ]
components:
- type: Loadout
prototypes: [CBURNGear]
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: CBURN Agent
description: A highly trained CentCom agent, capable of dealing with various threats.
+ - type: GhostTakeoverAvailable
- type: RandomMetadata
nameSegments:
- NamesFirstMilitary
- type: randomHumanoidSettings
id: CentcomOfficial
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: CentCom official
description: Inspect the station, jot down performance reviews for heads of staff, bug the Captain.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [ CentcomGear ]
id: Cluwne
randomizeName: false
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: Cluwne
description: Become a pitiful cluwne, your only goal in life is to find a sweet release from your suffering (usually by being beaten to death). A cluwne is not an antagonist but may defend itself. Crewmembers may murder cluwnes freely.
+ - type: GhostTakeoverAvailable
- type: Cluwne
interfaces:
- key: enum.StrippingUiKey.Key
type: StrippableBoundUserInterface
- #- type: GhostTakeoverAvailable
+ #- type: GhostRole
# makeSentient: true
# name: Maintenance Drone
# description: Maintain the station. Ignore other beings except drones.
# 1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.
# 2. You may not harm any being, regardless of intent or circumstance.
# 3. Your goals are to build, maintain, repair, improve, and power to the best of your abilities, You must never actively work against these goals.
+ #- type: GhostTakeoverAvailable
- type: MovementSpeedModifier
baseWalkSpeed : 5
baseSprintSpeed : 5
interfaces:
- key: enum.StrippingUiKey.Key
type: StrippableBoundUserInterface
- - type: GhostTakeoverAvailable
+ - type: GhostRole
makeSentient: true
name: Onestar Mecha
description: You are an experimental mecha created by who-knows-what, all you know is that you have weapons and you detect fleshy moving targets nearby...
rules: Use your weapons to cause havoc. You are an antagonist.
+ - type: GhostTakeoverAvailable
- type: MovementSpeedModifier
baseWalkSpeed : 3
baseSprintSpeed : 2
parent: MobSkeletonPerson
id: MobSkeletonPirate
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: Skeleton Pirate
description: Cause chaos and loot the station for treasure.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [PirateGear]
- type: RandomHumanoidAppearance
parent: MobSkeletonPerson
id: MobSkeletonBiker
components:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
name: Skeleton Biker
description: Ride around on your sweet ride.
+ - type: GhostTakeoverAvailable
- type: Loadout
prototypes: [SkeletonBiker]
- type: RandomHumanoidAppearance
layers:
- state: old-radio
netsync: false
- - type: GhostRoleMobSpawner
- prototype: MobHumanSyndicateAgent
+ - type: GhostRole
name: Syndicate Agent
description: Someone needs reinforcements. You, the first person the syndicate could find, will help them.
rules: Normal syndicate antagonist rules apply. Work with whoever called you in, and don't harm them.
+ - type: GhostRoleMobSpawner
+ prototype: MobHumanSyndicateAgent
- type: EmitSoundOnUse
sound: /Audio/Misc/emergency_meeting.ogg
- type: ItemCooldown
targetDepth: 3
effectHint: artifact-effect-hint-sentience
permanentComponents:
- - type: GhostTakeoverAvailable
+ - type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
description: |
Enact your eldritch whims.
Forcibly activate your nodes for good or for evil.
+ - type: GhostTakeoverAvailable
- type: MovementSpeedModifier
baseWalkSpeed: 0.25
baseSprintSpeed: 0.5