--- /dev/null
+using Content.Server.Chat.Systems;
+using Content.Server.NPC;
+using Content.Server.NPC.Systems;
+using Content.Server.Pinpointer;
+using Content.Shared.Damage;
+using Content.Shared.Dragon;
+using Content.Shared.Examine;
+using Content.Shared.Sprite;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+using Robust.Shared.Player;
+using Robust.Shared.Serialization.Manager;
+using System.Numerics;
+
+namespace Content.Server.Dragon;
+
+/// <summary>
+/// Handles events for rift entities and rift updating.
+/// </summary>
+public sealed class DragonRiftSystem : EntitySystem
+{
+ [Dependency] private readonly ChatSystem _chat = default!;
+ [Dependency] private readonly DragonSystem _dragon = default!;
+ [Dependency] private readonly ISerializationManager _serManager = default!;
+ [Dependency] private readonly NavMapSystem _navMap = default!;
+ [Dependency] private readonly NPCSystem _npc = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<DragonRiftComponent, ExaminedEvent>(OnExamined);
+ SubscribeLocalEvent<DragonRiftComponent, AnchorStateChangedEvent>(OnAnchorChange);
+ SubscribeLocalEvent<DragonRiftComponent, ComponentShutdown>(OnShutdown);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator<DragonRiftComponent, TransformComponent>();
+ while (query.MoveNext(out var uid, out var comp, out var xform))
+ {
+ if (comp.State != DragonRiftState.Finished && comp.Accumulator >= comp.MaxAccumulator)
+ {
+ // TODO: When we get autocall you can buff if the rift finishes / 3 rifts are up
+ // for now they just keep 3 rifts up.
+
+ if (comp.Dragon != null)
+ _dragon.RiftCharged(comp.Dragon.Value);
+
+ comp.Accumulator = comp.MaxAccumulator;
+ RemComp<DamageableComponent>(uid);
+ comp.State = DragonRiftState.Finished;
+ Dirty(uid, 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 = xform.LocalPosition;
+ _chat.DispatchGlobalAnnouncement(Loc.GetString("carp-rift-warning", ("location", location)), playSound: false, colorOverride: Color.Red);
+ _audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
+ _navMap.SetBeaconEnabled(uid, true);
+ }
+
+ if (comp.SpawnAccumulator > comp.SpawnCooldown)
+ {
+ comp.SpawnAccumulator -= comp.SpawnCooldown;
+ var ent = Spawn(comp.SpawnPrototype, xform.Coordinates);
+
+ // Update their look to match the leader.
+ if (TryComp<RandomSpriteComponent>(comp.Dragon, out var randomSprite))
+ {
+ var spawnedSprite = EnsureComp<RandomSpriteComponent>(ent);
+ _serManager.CopyTo(randomSprite, ref spawnedSprite, notNullableOverride: true);
+ Dirty(ent, spawnedSprite);
+ }
+
+ if (comp.Dragon != null)
+ _npc.SetBlackboard(ent, NPCBlackboard.FollowTarget, new EntityCoordinates(comp.Dragon.Value, Vector2.Zero));
+ }
+ }
+ }
+
+ private void OnExamined(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)
+ {
+ QueueDel(uid);
+ }
+ }
+
+ private void OnShutdown(EntityUid uid, DragonRiftComponent comp, ComponentShutdown args)
+ {
+ if (!TryComp<DragonComponent>(comp.Dragon, out var dragon) || dragon.Weakened)
+ return;
+
+ _dragon.RiftDestroyed(comp.Dragon.Value, dragon);
+ }
+}
-using System.Numerics;
-using Content.Server.Chat.Systems;
-using Content.Server.GameTicking;
-using Content.Server.NPC;
-using Content.Server.NPC.Systems;
+using Content.Server.GenericAntag;
+using Content.Server.Objectives.Components;
+using Content.Server.Objectives.Systems;
using Content.Server.Popups;
+using Content.Server.Roles;
using Content.Shared.Actions;
-using Content.Shared.Damage;
using Content.Shared.Dragon;
-using Content.Shared.Examine;
using Content.Shared.Maps;
+using Content.Shared.Mind;
+using Content.Shared.Mind.Components;
using Content.Shared.Mobs;
using Content.Shared.Movement.Systems;
-using Content.Shared.Sprite;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Player;
-using Robust.Shared.Serialization.Manager;
namespace Content.Server.Dragon;
public sealed partial class DragonSystem : EntitySystem
{
+ [Dependency] private readonly CarpRiftsConditionSystem _carpRifts = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly ISerializationManager _serManager = default!;
[Dependency] private readonly ITileDefinitionManager _tileDef = default!;
- [Dependency] private readonly ChatSystem _chat = default!;
- [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
- [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
- [Dependency] private readonly NPCSystem _npc = default!;
+ [Dependency] private readonly PopupSystem _popup = default!;
+ [Dependency] private readonly RoleSystem _role = default!;
+ [Dependency] private readonly SharedActionsSystem _actions = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+ private EntityQuery<CarpRiftsConditionComponent> _objQuery;
/// <summary>
/// Minimum distance between 2 rifts allowed.
{
base.Initialize();
+ _objQuery = GetEntityQuery<CarpRiftsConditionComponent>();
+
SubscribeLocalEvent<DragonComponent, MapInitEvent>(OnInit);
SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
- SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
+ SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnSpawnRift);
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);
+ SubscribeLocalEvent<DragonComponent, GenericAntagCreatedEvent>(OnCreated);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
- foreach (var comp in EntityQuery<DragonComponent>())
+ var query = EntityQueryEnumerator<DragonComponent>();
+ while (query.MoveNext(out var uid, out var comp))
{
if (comp.WeakenedAccumulator > 0f)
{
if (comp.WeakenedAccumulator < 0f)
{
comp.WeakenedAccumulator = 0f;
- _movement.RefreshMovementSpeedModifiers(comp.Owner);
+ _movement.RefreshMovementSpeedModifiers(uid);
}
}
// At max rifts
if (comp.Rifts.Count >= RiftsAllowed)
- {
continue;
- }
// If there's an active rift don't accumulate.
if (comp.Rifts.Count > 0)
// Delete it, naughty dragon!
if (comp.RiftAccumulator >= comp.RiftMaxAccumulator)
{
- Roar(comp);
- QueueDel(comp.Owner);
- }
- }
-
- foreach (var comp in EntityQuery<DragonRiftComponent>())
- {
- if (comp.State != DragonRiftState.Finished && comp.Accumulator >= comp.MaxAccumulator)
- {
- // 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).Coordinates);
-
- // Update their look to match the leader.
- if (TryComp<RandomSpriteComponent>(comp.Dragon, out var randomSprite))
- {
- var spawnedSprite = EnsureComp<RandomSpriteComponent>(ent);
- _serManager.CopyTo(randomSprite, ref spawnedSprite, notNullableOverride: true);
- Dirty(ent, spawnedSprite);
- }
-
- if (comp.Dragon != null)
- _npc.SetBlackboard(ent, NPCBlackboard.FollowTarget, new EntityCoordinates(comp.Dragon.Value, Vector2.Zero));
+ Roar(uid, comp);
+ QueueDel(uid);
}
}
}
- #region Rift
-
- private void OnRiftExamined(EntityUid uid, DragonRiftComponent component, ExaminedEvent args)
+ private void OnInit(EntityUid uid, DragonComponent component, MapInitEvent args)
{
- args.PushMarkup(Loc.GetString("carp-rift-examine", ("percentage", MathF.Round(component.Accumulator / component.MaxAccumulator * 100))));
+ Roar(uid, component);
+ _actions.AddAction(uid, ref component.SpawnRiftActionEntity, component.SpawnRiftAction);
}
- private void OnAnchorChange(EntityUid uid, DragonRiftComponent component, ref AnchorStateChangedEvent args)
- {
- if (!args.Anchored && component.State == DragonRiftState.Charging)
- {
- 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)
- {
- 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);
- }
- }
-
- private void OnRiftGetState(EntityUid uid, DragonRiftComponent component, ref ComponentGetState args)
+ private void OnShutdown(EntityUid uid, DragonComponent component, ComponentShutdown args)
{
- args.State = new DragonRiftComponentState()
- {
- State = component.State
- };
+ DeleteRifts(uid, false, component);
}
- private void OnDragonMove(EntityUid uid, DragonComponent component, RefreshMovementSpeedModifiersEvent args)
+ private void OnSpawnRift(EntityUid uid, DragonComponent component, DragonSpawnRiftActionEvent 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);
+ _popup.PopupEntity(Loc.GetString("carp-rift-weakened"), uid, uid);
return;
}
if (component.Rifts.Count >= RiftsAllowed)
{
- _popupSystem.PopupEntity(Loc.GetString("carp-rift-max"), uid, uid);
+ _popup.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);
+ _popup.PopupEntity(Loc.GetString("carp-rift-duplicate"), uid, uid);
return;
}
// Have to be on a grid fam
if (!_mapManager.TryGetGrid(xform.GridUid, out var grid))
{
- _popupSystem.PopupEntity(Loc.GetString("carp-rift-anchor"), uid, uid);
+ _popup.PopupEntity(Loc.GetString("carp-rift-anchor"), uid, uid);
return;
}
+ // cant stack rifts near eachother
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);
+ _popup.PopupEntity(Loc.GetString("carp-rift-proximity", ("proximity", RiftRange)), uid, uid);
return;
}
}
+ // cant put a rift on solars
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);
+ _popup.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)
+ // TODO: just make this a move speed modifier component???
+ private void OnDragonMove(EntityUid uid, DragonComponent component, RefreshMovementSpeedModifiersEvent args)
{
- foreach (var rift in component.Rifts)
+ if (component.Weakened)
{
- QueueDel(rift);
+ args.ModifySpeed(0.5f, 0.5f);
}
}
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)
+ // Deletes all rifts after dying
+ if (args.NewMobState != MobState.Dead)
+ return;
+
+ if (component.SoundDeath != null)
+ _audio.PlayPvs(component.SoundDeath, uid);
+
+ // objective is explicitly not reset so that it will show how many you got before dying in round end text
+ DeleteRifts(uid, false, component);
+ }
+
+ private void OnCreated(EntityUid uid, DragonComponent comp, ref GenericAntagCreatedEvent args)
+ {
+ var mindId = args.MindId;
+ var mind = args.Mind;
+
+ _role.MindAddRole(mindId, new DragonRoleComponent(), mind);
+ _role.MindAddRole(mindId, new RoleBriefingComponent()
+ {
+ Briefing = Loc.GetString("dragon-role-briefing")
+ }, mind);
+ }
+
+ private void Roar(EntityUid uid, DragonComponent comp)
+ {
+ if (comp.SoundRoar != null)
+ _audio.Play(comp.SoundRoar, Filter.Pvs(uid, 4f, EntityManager), uid, true);
+ }
+
+ /// <summary>
+ /// Delete all rifts this dragon made.
+ /// </summary>
+ /// <param name="uid">Entity id of the dragon</param>
+ /// <param name="resetRole">If true, the role's rift count will be reset too</param>
+ /// <param name="comp">The dragon component</param>
+ public void DeleteRifts(EntityUid uid, bool resetRole, DragonComponent? comp = null)
+ {
+ if (!Resolve(uid, ref comp))
+ return;
+
+ foreach (var rift in comp.Rifts)
{
- if (component.SoundDeath != null)
- _audioSystem.PlayPvs(component.SoundDeath, uid, component.SoundDeath.Params);
+ QueueDel(rift);
+ }
+
+ comp.Rifts.Clear();
- foreach (var rift in component.Rifts)
+ // stop here if not trying to reset the objective's rift count
+ if (!resetRole || !TryComp<MindContainerComponent>(uid, out var mindContainer) || !mindContainer.HasMind)
+ return;
+
+ var mind = Comp<MindComponent>(mindContainer.Mind.Value);
+ foreach (var objId in mind.AllObjectives)
+ {
+ if (_objQuery.TryGetComponent(objId, out var obj))
{
- QueueDel(rift);
+ _carpRifts.ResetRifts(objId, obj);
+ break;
}
-
- component.Rifts.Clear();
}
}
- private void Roar(DragonComponent component)
+ /// <summary>
+ /// Increment the dragon role's charged rift count.
+ /// </summary>
+ public void RiftCharged(EntityUid uid, DragonComponent? comp = null)
{
- if (component.SoundRoar != null)
- _audioSystem.Play(component.SoundRoar, Filter.Pvs(component.Owner, 4f, EntityManager), component.Owner, true, component.SoundRoar.Params);
+ if (!Resolve(uid, ref comp))
+ return;
+
+ if (!TryComp<MindContainerComponent>(uid, out var mindContainer) || !mindContainer.HasMind)
+ return;
+
+ var mind = Comp<MindComponent>(mindContainer.Mind.Value);
+ foreach (var objId in mind.AllObjectives)
+ {
+ if (_objQuery.TryGetComponent(objId, out var obj))
+ {
+ _carpRifts.RiftCharged(objId, obj);
+ break;
+ }
+ }
}
- private void OnInit(EntityUid uid, DragonComponent component, MapInitEvent args)
+ /// <summary>
+ /// Do everything that needs to happen when a rift gets destroyed by the crew.
+ /// </summary>
+ public void RiftDestroyed(EntityUid uid, DragonComponent? comp = null)
{
- Roar(component);
- _actionsSystem.AddAction(uid, ref component.SpawnRiftActionEntity, component.SpawnRiftAction);
+ if (!Resolve(uid, ref comp))
+ return;
+
+ // do reset the rift count since crew destroyed the rift, not deleted by the dragon dying.
+ DeleteRifts(uid, true, comp);
+
+ // We can't predict the rift being destroyed anyway so no point adding weakened to shared.
+ comp.WeakenedAccumulator = comp.WeakenedDuration;
+ _movement.RefreshMovementSpeedModifiers(uid);
+ _popup.PopupEntity(Loc.GetString("carp-rift-destroyed"), uid, uid);
}
}
-