dragon minor refactor and stuff (#18149)

Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas
2023-07-25 00:32:12 +00:00
committed by GitHub
parent 0f163beb3c
commit 88eaf7a772
8 changed files with 305 additions and 297 deletions

View File

@@ -27,43 +27,25 @@ public sealed partial class DragonSystem
return finished; return finished;
} }
protected override void Started(EntityUid uid, DragonRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
var eligible = EntityQuery<StationEventEligibleComponent>().Select(x => x.Owner).ToList();
if (!eligible.Any())
return;
var station = _random.Pick(eligible);
if (_station.GetLargestGrid(EntityManager.GetComponent<StationDataComponent>(station)) is not { } grid)
return;
Spawn("MobDragon", Transform(grid).MapPosition);
}
private void OnRiftRoundEnd(RoundEndTextAppendEvent args) private void OnRiftRoundEnd(RoundEndTextAppendEvent args)
{ {
var dragons = EntityQuery<DragonComponent>(true).ToList(); if (EntityQuery<DragonComponent>().Count() == 0)
if (dragons.Count == 0)
return; return;
args.AddLine(Loc.GetString("dragon-round-end-summary")); args.AddLine(Loc.GetString("dragon-round-end-summary"));
foreach (var dragon in EntityQuery<DragonComponent>(true)) var query = EntityQueryEnumerator<DragonComponent>();
while (query.MoveNext(out var uid, out var dragon))
{ {
var met = RiftsMet(dragon); var met = RiftsMet(dragon);
if (TryComp<ActorComponent>(dragon.Owner, out var actor)) if (TryComp<ActorComponent>(uid, out var actor))
{ {
args.AddLine(Loc.GetString("dragon-round-end-dragon-player", ("name", dragon.Owner), ("count", met), ("player", actor.PlayerSession))); args.AddLine(Loc.GetString("dragon-round-end-dragon-player", ("name", uid), ("count", met), ("player", actor.PlayerSession)));
} }
else else
{ {
args.AddLine(Loc.GetString("dragon-round-end-dragon", ("name", dragon.Owner), ("count", met))); args.AddLine(Loc.GetString("dragon-round-end-dragon", ("name", uid), ("count", met)));
} }
} }
} }

View File

@@ -1,304 +1,302 @@
using System.Numerics;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Popups;
using Content.Shared.Actions;
using Content.Shared.Chemistry.Components;
using Robust.Shared.Containers;
using Robust.Shared.Player;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.NPC; using Content.Server.NPC;
using Content.Server.NPC.Systems;
using Content.Server.Popups;
using Content.Server.Station.Systems;
using Content.Shared.Actions;
using Content.Shared.Chemistry.Components;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.Dragon; using Content.Shared.Dragon;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Humanoid;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Player;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Random; using Robust.Shared.Random;
using Content.Server.NPC.Systems; using System.Numerics;
using Content.Server.Station.Systems;
using Content.Shared.DoAfter;
using Content.Shared.Humanoid;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
namespace Content.Server.Dragon namespace Content.Server.Dragon;
public sealed partial class DragonSystem : EntitySystem
{ {
public sealed partial class DragonSystem : GameRuleSystem<DragonRuleComponent> [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ITileDefinitionManager _tileDef = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly NPCSystem _npc = default!;
/// <summary>
/// Minimum distance between 2 rifts allowed.
/// </summary>
private const int RiftRange = 15;
/// <summary>
/// Radius of tiles
/// </summary>
private const int RiftTileRadius = 2;
private const int RiftsAllowed = 3;
public override void Initialize()
{ {
[Dependency] private readonly IMapManager _mapManager = default!; base.Initialize();
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ITileDefinitionManager _tileDef = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly NPCSystem _npc = default!;
/// <summary> SubscribeLocalEvent<DragonComponent, ComponentStartup>(OnStartup);
/// Minimum distance between 2 rifts allowed. SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
/// </summary> SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
private const int RiftRange = 15; SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
/// <summary> SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged);
/// Radius of tiles
/// </summary>
private const int RiftTileRadius = 2;
private const int RiftsAllowed = 3; SubscribeLocalEvent<DragonRiftComponent, ComponentShutdown>(OnRiftShutdown);
SubscribeLocalEvent<DragonRiftComponent, ComponentGetState>(OnRiftGetState);
SubscribeLocalEvent<DragonRiftComponent, AnchorStateChangedEvent>(OnAnchorChange);
SubscribeLocalEvent<DragonRiftComponent, ExaminedEvent>(OnRiftExamined);
public override void Initialize() SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRiftRoundEnd);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var comp in EntityQuery<DragonComponent>())
{ {
base.Initialize(); if (comp.WeakenedAccumulator > 0f)
SubscribeLocalEvent<DragonComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<DragonRiftComponent, ComponentShutdown>(OnRiftShutdown);
SubscribeLocalEvent<DragonRiftComponent, ComponentGetState>(OnRiftGetState);
SubscribeLocalEvent<DragonRiftComponent, AnchorStateChangedEvent>(OnAnchorChange);
SubscribeLocalEvent<DragonRiftComponent, ExaminedEvent>(OnRiftExamined);
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRiftRoundEnd);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var comp in EntityQuery<DragonComponent>())
{ {
if (comp.WeakenedAccumulator > 0f) comp.WeakenedAccumulator -= frameTime;
{
comp.WeakenedAccumulator -= frameTime;
// No longer weakened. // No longer weakened.
if (comp.WeakenedAccumulator < 0f) if (comp.WeakenedAccumulator < 0f)
{ {
comp.WeakenedAccumulator = 0f; comp.WeakenedAccumulator = 0f;
_movement.RefreshMovementSpeedModifiers(comp.Owner); _movement.RefreshMovementSpeedModifiers(comp.Owner);
}
} }
}
// At max rifts // At max rifts
if (comp.Rifts.Count >= RiftsAllowed) if (comp.Rifts.Count >= RiftsAllowed)
{
continue;
}
// If there's an active rift don't accumulate.
if (comp.Rifts.Count > 0)
{
var lastRift = comp.Rifts[^1];
if (TryComp<DragonRiftComponent>(lastRift, out var rift) && rift.State != DragonRiftState.Finished)
{ {
comp.RiftAccumulator = 0f;
continue; continue;
} }
// If there's an active rift don't accumulate.
if (comp.Rifts.Count > 0)
{
var lastRift = comp.Rifts[^1];
if (TryComp<DragonRiftComponent>(lastRift, out var rift) && rift.State != DragonRiftState.Finished)
{
comp.RiftAccumulator = 0f;
continue;
}
}
comp.RiftAccumulator += frameTime;
// Delete it, naughty dragon!
if (comp.RiftAccumulator >= comp.RiftMaxAccumulator)
{
Roar(comp);
QueueDel(comp.Owner);
}
} }
foreach (var comp in EntityQuery<DragonRiftComponent>()) comp.RiftAccumulator += frameTime;
// Delete it, naughty dragon!
if (comp.RiftAccumulator >= comp.RiftMaxAccumulator)
{ {
if (comp.State != DragonRiftState.Finished && comp.Accumulator >= comp.MaxAccumulator) Roar(comp);
{ QueueDel(comp.Owner);
// TODO: When we get autocall you can buff if the rift finishes / 3 rifts are up
// for now they just keep 3 rifts up.
comp.Accumulator = comp.MaxAccumulator;
RemComp<DamageableComponent>(comp.Owner);
comp.State = DragonRiftState.Finished;
Dirty(comp);
}
else if (comp.State != DragonRiftState.Finished)
{
comp.Accumulator += frameTime;
}
comp.SpawnAccumulator += frameTime;
if (comp.State < DragonRiftState.AlmostFinished && comp.Accumulator > comp.MaxAccumulator / 2f)
{
comp.State = DragonRiftState.AlmostFinished;
Dirty(comp);
var location = Transform(comp.Owner).LocalPosition;
_chat.DispatchGlobalAnnouncement(Loc.GetString("carp-rift-warning", ("location", location)), playSound: false, colorOverride: Color.Red);
_audioSystem.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
}
if (comp.SpawnAccumulator > comp.SpawnCooldown)
{
comp.SpawnAccumulator -= comp.SpawnCooldown;
var ent = Spawn(comp.SpawnPrototype, Transform(comp.Owner).MapPosition);
_npc.SetBlackboard(ent, NPCBlackboard.FollowTarget, new EntityCoordinates(comp.Owner, Vector2.Zero));
}
} }
} }
#region Rift foreach (var comp in EntityQuery<DragonRiftComponent>())
private void OnRiftExamined(EntityUid uid, DragonRiftComponent component, ExaminedEvent args)
{ {
args.PushMarkup(Loc.GetString("carp-rift-examine", ("percentage", MathF.Round(component.Accumulator / component.MaxAccumulator * 100)))); if (comp.State != DragonRiftState.Finished && comp.Accumulator >= comp.MaxAccumulator)
}
private void OnAnchorChange(EntityUid uid, DragonRiftComponent component, ref AnchorStateChangedEvent args)
{
if (!args.Anchored && component.State == DragonRiftState.Charging)
{ {
QueueDel(uid); // TODO: When we get autocall you can buff if the rift finishes / 3 rifts are up
// for now they just keep 3 rifts up.
comp.Accumulator = comp.MaxAccumulator;
RemComp<DamageableComponent>(comp.Owner);
comp.State = DragonRiftState.Finished;
Dirty(comp);
}
else if (comp.State != DragonRiftState.Finished)
{
comp.Accumulator += frameTime;
}
comp.SpawnAccumulator += frameTime;
if (comp.State < DragonRiftState.AlmostFinished && comp.Accumulator > comp.MaxAccumulator / 2f)
{
comp.State = DragonRiftState.AlmostFinished;
Dirty(comp);
var location = Transform(comp.Owner).LocalPosition;
_chat.DispatchGlobalAnnouncement(Loc.GetString("carp-rift-warning", ("location", location)), playSound: false, colorOverride: Color.Red);
_audioSystem.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
}
if (comp.SpawnAccumulator > comp.SpawnCooldown)
{
comp.SpawnAccumulator -= comp.SpawnCooldown;
var ent = Spawn(comp.SpawnPrototype, Transform(comp.Owner).MapPosition);
_npc.SetBlackboard(ent, NPCBlackboard.FollowTarget, new EntityCoordinates(comp.Owner, Vector2.Zero));
} }
} }
}
private void OnRiftShutdown(EntityUid uid, DragonRiftComponent component, ComponentShutdown args) #region Rift
private void OnRiftExamined(EntityUid uid, DragonRiftComponent component, ExaminedEvent args)
{
args.PushMarkup(Loc.GetString("carp-rift-examine", ("percentage", MathF.Round(component.Accumulator / component.MaxAccumulator * 100))));
}
private void OnAnchorChange(EntityUid uid, DragonRiftComponent component, ref AnchorStateChangedEvent args)
{
if (!args.Anchored && component.State == DragonRiftState.Charging)
{ {
if (TryComp<DragonComponent>(component.Dragon, out var dragon) && !dragon.Weakened) QueueDel(uid);
}
}
private void OnRiftShutdown(EntityUid uid, DragonRiftComponent component, ComponentShutdown args)
{
if (TryComp<DragonComponent>(component.Dragon, out var dragon) && !dragon.Weakened)
{
foreach (var rift in dragon.Rifts)
{ {
foreach (var rift in dragon.Rifts) QueueDel(rift);
{
QueueDel(rift);
}
dragon.Rifts.Clear();
// We can't predict the rift being destroyed anyway so no point adding weakened to shared.
dragon.WeakenedAccumulator = dragon.WeakenedDuration;
_movement.RefreshMovementSpeedModifiers(component.Dragon.Value);
_popupSystem.PopupEntity(Loc.GetString("carp-rift-destroyed"), component.Dragon.Value, component.Dragon.Value);
} }
dragon.Rifts.Clear();
// We can't predict the rift being destroyed anyway so no point adding weakened to shared.
dragon.WeakenedAccumulator = dragon.WeakenedDuration;
_movement.RefreshMovementSpeedModifiers(component.Dragon.Value);
_popupSystem.PopupEntity(Loc.GetString("carp-rift-destroyed"), component.Dragon.Value, component.Dragon.Value);
}
}
private void OnRiftGetState(EntityUid uid, DragonRiftComponent component, ref ComponentGetState args)
{
args.State = new DragonRiftComponentState()
{
State = component.State
};
}
private void OnDragonMove(EntityUid uid, DragonComponent component, RefreshMovementSpeedModifiersEvent args)
{
if (component.Weakened)
{
args.ModifySpeed(0.5f, 0.5f);
}
}
private void OnDragonRift(EntityUid uid, DragonComponent component, DragonSpawnRiftActionEvent args)
{
if (component.Weakened)
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-weakened"), uid, uid);
return;
} }
private void OnRiftGetState(EntityUid uid, DragonRiftComponent component, ref ComponentGetState args) if (component.Rifts.Count >= RiftsAllowed)
{ {
args.State = new DragonRiftComponentState() _popupSystem.PopupEntity(Loc.GetString("carp-rift-max"), uid, uid);
{ return;
State = component.State
};
} }
private void OnDragonMove(EntityUid uid, DragonComponent component, RefreshMovementSpeedModifiersEvent args) if (component.Rifts.Count > 0 && TryComp<DragonRiftComponent>(component.Rifts[^1], out var rift) && rift.State != DragonRiftState.Finished)
{ {
if (component.Weakened) _popupSystem.PopupEntity(Loc.GetString("carp-rift-duplicate"), uid, uid);
{ return;
args.ModifySpeed(0.5f, 0.5f);
}
} }
private void OnDragonRift(EntityUid uid, DragonComponent component, DragonSpawnRiftActionEvent args) var xform = Transform(uid);
// Have to be on a grid fam
if (!_mapManager.TryGetGrid(xform.GridUid, out var grid))
{ {
if (component.Weakened) _popupSystem.PopupEntity(Loc.GetString("carp-rift-anchor"), uid, uid);
return;
}
foreach (var (_, riftXform) in EntityQuery<DragonRiftComponent, TransformComponent>(true))
{
if (riftXform.Coordinates.InRange(EntityManager, xform.Coordinates, RiftRange))
{ {
_popupSystem.PopupEntity(Loc.GetString("carp-rift-weakened"), uid, uid); _popupSystem.PopupEntity(Loc.GetString("carp-rift-proximity", ("proximity", RiftRange)), uid, uid);
return; return;
} }
if (component.Rifts.Count >= RiftsAllowed)
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-max"), uid, uid);
return;
}
if (component.Rifts.Count > 0 && TryComp<DragonRiftComponent>(component.Rifts[^1], out var rift) && rift.State != DragonRiftState.Finished)
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-duplicate"), uid, uid);
return;
}
var xform = Transform(uid);
// Have to be on a grid fam
if (!_mapManager.TryGetGrid(xform.GridUid, out var grid))
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-anchor"), uid, uid);
return;
}
foreach (var (_, riftXform) in EntityQuery<DragonRiftComponent, TransformComponent>(true))
{
if (riftXform.Coordinates.InRange(EntityManager, xform.Coordinates, RiftRange))
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-proximity", ("proximity", RiftRange)), uid, uid);
return;
}
}
foreach (var tile in grid.GetTilesIntersecting(new Circle(xform.WorldPosition, RiftTileRadius), false))
{
if (!tile.IsSpace(_tileDef))
continue;
_popupSystem.PopupEntity(Loc.GetString("carp-rift-space-proximity", ("proximity", RiftTileRadius)), uid, uid);
return;
}
var carpUid = Spawn(component.RiftPrototype, xform.MapPosition);
component.Rifts.Add(carpUid);
Comp<DragonRiftComponent>(carpUid).Dragon = uid;
_audioSystem.PlayPvs("/Audio/Weapons/Guns/Gunshots/rocket_launcher.ogg", carpUid);
} }
#endregion foreach (var tile in grid.GetTilesIntersecting(new Circle(xform.WorldPosition, RiftTileRadius), false))
private void OnShutdown(EntityUid uid, DragonComponent component, ComponentShutdown args)
{ {
if (!tile.IsSpace(_tileDef))
continue;
_popupSystem.PopupEntity(Loc.GetString("carp-rift-space-proximity", ("proximity", RiftTileRadius)), uid, uid);
return;
}
var carpUid = Spawn(component.RiftPrototype, xform.MapPosition);
component.Rifts.Add(carpUid);
Comp<DragonRiftComponent>(carpUid).Dragon = uid;
_audioSystem.PlayPvs("/Audio/Weapons/Guns/Gunshots/rocket_launcher.ogg", carpUid);
}
#endregion
private void OnShutdown(EntityUid uid, DragonComponent component, ComponentShutdown args)
{
foreach (var rift in component.Rifts)
{
QueueDel(rift);
}
}
private void OnMobStateChanged(EntityUid uid, DragonComponent component, MobStateChangedEvent args)
{
//Empties the stomach upon death
//TODO: Do this when the dragon gets butchered instead
if (args.NewMobState == MobState.Dead)
{
if (component.SoundDeath != null)
_audioSystem.PlayPvs(component.SoundDeath, uid, component.SoundDeath.Params);
foreach (var rift in component.Rifts) foreach (var rift in component.Rifts)
{ {
QueueDel(rift); QueueDel(rift);
} }
}
private void OnMobStateChanged(EntityUid uid, DragonComponent component, MobStateChangedEvent args) component.Rifts.Clear();
{
//Empties the stomach upon death
//TODO: Do this when the dragon gets butchered instead
if (args.NewMobState == MobState.Dead)
{
if (component.SoundDeath != null)
_audioSystem.PlayPvs(component.SoundDeath, uid, component.SoundDeath.Params);
foreach (var rift in component.Rifts)
{
QueueDel(rift);
}
component.Rifts.Clear();
}
}
private void Roar(DragonComponent component)
{
if (component.SoundRoar != null)
_audioSystem.Play(component.SoundRoar, Filter.Pvs(component.Owner, 4f, EntityManager), component.Owner, true, component.SoundRoar.Params);
}
private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args)
{
if (component.SpawnRiftAction != null)
_actionsSystem.AddAction(uid, component.SpawnRiftAction, null);
Roar(component);
} }
} }
private void Roar(DragonComponent component)
{
if (component.SoundRoar != null)
_audioSystem.Play(component.SoundRoar, Filter.Pvs(component.Owner, 4f, EntityManager), component.Owner, true, component.SoundRoar.Params);
}
private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args)
{
if (component.SpawnRiftAction != null)
_actionsSystem.AddAction(uid, component.SpawnRiftAction, null);
Roar(component);
}
} }

View File

@@ -0,0 +1,18 @@
using Content.Server.StationEvents.Events;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.StationEvents.Components;
/// <summary>
/// Spawns a single entity at a random tile on a station using TryGetRandomTile.
/// </summary>
[RegisterComponent, Access(typeof(RandomSpawnRule))]
public sealed class RandomSpawnRuleComponent : Component
{
/// <summary>
/// The entity to be spawned.
/// </summary>
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Prototype = string.Empty;
}

View File

@@ -1,10 +0,0 @@
using Content.Server.StationEvents.Events;
namespace Content.Server.StationEvents.Components;
[RegisterComponent, Access(typeof(RevenantSpawnRule))]
public sealed class RevenantSpawnRuleComponent : Component
{
[DataField("revenantPrototype")]
public string RevenantPrototype = "MobRevenant";
}

View File

@@ -0,0 +1,18 @@
using Content.Server.GameTicking.Rules.Components;
using Content.Server.StationEvents.Components;
namespace Content.Server.StationEvents.Events;
public sealed class RandomSpawnRule : StationEventSystem<RandomSpawnRuleComponent>
{
protected override void Started(EntityUid uid, RandomSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, comp, gameRule, args);
if (TryFindRandomTile(out _, out _, out _, out var coords))
{
Sawmill.Info($"Spawning {comp.Prototype} at {coords}");
Spawn(comp.Prototype, coords);
}
}
}

View File

@@ -1,19 +0,0 @@
using Content.Server.GameTicking.Rules.Components;
using Content.Server.StationEvents.Components;
namespace Content.Server.StationEvents.Events;
public sealed class RevenantSpawnRule : StationEventSystem<RevenantSpawnRuleComponent>
{
protected override void Started(EntityUid uid, RevenantSpawnRuleComponent component, GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
if (TryFindRandomTile(out _, out _, out _, out var coords))
{
Sawmill.Info($"Spawning revenant at {coords}");
Spawn(component.RevenantPrototype, coords);
}
}
}

View File

@@ -93,3 +93,22 @@
- state: green - state: green
- sprite: Structures/Wallmounts/signs.rsi - sprite: Structures/Wallmounts/signs.rsi
state: radiation state: radiation
- type: entity
parent: MarkerBase
id: SpawnPointGhostDragon
noSpawn: true
name: ghost role spawn point
suffix: dragon
components:
- type: GhostRole
name: ghost-role-information-space-dragon-name
description: ghost-role-information-space-dragon-description
rules: ghost-role-component-default-rules
- type: GhostRoleMobSpawner
prototype: MobDragon
- type: Sprite
layers:
- state: green
- sprite: Mobs/Aliens/Carps/dragon.rsi
state: alive

View File

@@ -56,8 +56,8 @@
- type: BureaucraticErrorRule - type: BureaucraticErrorRule
- type: entity - type: entity
id: Dragon
parent: BaseGameRule parent: BaseGameRule
id: Dragon
noSpawn: true noSpawn: true
components: components:
- type: StationEvent - type: StationEvent
@@ -65,11 +65,12 @@
duration: 1 duration: 1
earliestStart: 45 earliestStart: 45
minimumPlayers: 20 minimumPlayers: 20
- type: DragonRule - type: RandomSpawnRule
prototype: SpawnPointGhostDragon
- type: entity - type: entity
id: RevenantSpawn
parent: BaseGameRule parent: BaseGameRule
id: RevenantSpawn
noSpawn: true noSpawn: true
components: components:
- type: StationEvent - type: StationEvent
@@ -77,7 +78,8 @@
duration: 1 duration: 1
earliestStart: 45 earliestStart: 45
minimumPlayers: 20 minimumPlayers: 20
- type: RevenantSpawnRule - type: RandomSpawnRule
prototype: MobRevenant
- type: entity - type: entity
id: FalseAlarm id: FalseAlarm