6
StarHorizon_Public/Content.Server/Anomaly/AnomalySystem.Generator.cs
2025-11-05 11:11:22 +03:00

224 lines
9.0 KiB
C#

using Content.Server.Anomaly.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Station.Components;
using Content.Shared.Anomaly;
using Content.Shared.CCVar;
using Content.Shared.Materials;
using Content.Shared.Radio;
using Robust.Shared.Audio;
using Content.Shared.Physics;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Content.Shared.Power;
using Content.Server.Chat.Systems; // Frontier
namespace Content.Server.Anomaly;
/// <summary>
/// This handles anomalous vessel as well as
/// the calculations for how many points they
/// should produce.
/// </summary>
public sealed partial class AnomalySystem
{
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly ChatSystem _chat = default!; // Frontier
private void InitializeGenerator()
{
SubscribeLocalEvent<AnomalyGeneratorComponent, BoundUIOpenedEvent>(OnGeneratorBUIOpened);
SubscribeLocalEvent<AnomalyGeneratorComponent, MaterialAmountChangedEvent>(OnGeneratorMaterialAmountChanged);
SubscribeLocalEvent<AnomalyGeneratorComponent, AnomalyGeneratorGenerateButtonPressedEvent>(OnGenerateButtonPressed);
SubscribeLocalEvent<AnomalyGeneratorComponent, PowerChangedEvent>(OnGeneratorPowerChanged);
SubscribeLocalEvent<GeneratingAnomalyGeneratorComponent, ComponentStartup>(OnGeneratingStartup);
}
private void OnGeneratorPowerChanged(EntityUid uid, AnomalyGeneratorComponent component, ref PowerChangedEvent args)
{
_ambient.SetAmbience(uid, args.Powered);
}
private void OnGeneratorBUIOpened(EntityUid uid, AnomalyGeneratorComponent component, BoundUIOpenedEvent args)
{
UpdateGeneratorUi(uid, component);
}
private void OnGeneratorMaterialAmountChanged(EntityUid uid, AnomalyGeneratorComponent component, ref MaterialAmountChangedEvent args)
{
UpdateGeneratorUi(uid, component);
}
private void OnGenerateButtonPressed(EntityUid uid, AnomalyGeneratorComponent component, AnomalyGeneratorGenerateButtonPressedEvent args)
{
TryGeneratorCreateAnomaly(uid, component);
}
public void UpdateGeneratorUi(EntityUid uid, AnomalyGeneratorComponent component)
{
var materialAmount = _material.GetMaterialAmount(uid, component.RequiredMaterial);
var state = new AnomalyGeneratorUserInterfaceState(component.CooldownEndTime, materialAmount, component.MaterialPerAnomaly);
_ui.SetUiState(uid, AnomalyGeneratorUiKey.Key, state);
}
public void TryGeneratorCreateAnomaly(EntityUid uid, AnomalyGeneratorComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
if (!this.IsPowered(uid, EntityManager))
return;
if (Timing.CurTime < component.CooldownEndTime)
return;
if (!_material.TryChangeMaterialAmount(uid, component.RequiredMaterial, -component.MaterialPerAnomaly))
return;
var generating = EnsureComp<GeneratingAnomalyGeneratorComponent>(uid);
generating.EndTime = Timing.CurTime + component.GenerationLength;
generating.AudioStream = Audio.PlayPvs(component.GeneratingSound, uid, AudioParams.Default.WithLoop(true))?.Entity;
component.CooldownEndTime = Timing.CurTime + component.CooldownLength;
UpdateGeneratorUi(uid, component);
}
public void SpawnOnRandomGridLocation(EntityUid grid, string toSpawn, Entity<AnomalyGeneratorComponent>? generator = null) // Frontier: add generator
{
if (!TryComp<MapGridComponent>(grid, out var gridComp))
return;
var xform = Transform(grid);
var targetCoords = xform.Coordinates;
var gridBounds = gridComp.LocalAABB.Scale(_configuration.GetCVar(CCVars.AnomalyGenerationGridBoundsScale));
bool validTarget = false; // Frontier
for (var i = 0; i < 20; i++) // Frontier: 25<20
{
var randomX = Random.Next((int) gridBounds.Left, (int) gridBounds.Right);
var randomY = Random.Next((int) gridBounds.Bottom, (int) gridBounds.Top);
var tile = new Vector2i(randomX, randomY);
// no air-blocked areas.
if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile) ||
_atmosphere.IsTileAirBlocked(grid, tile, mapGridComp: gridComp))
{
continue;
}
// don't spawn inside of solid objects
var physQuery = GetEntityQuery<PhysicsComponent>();
var valid = true;
// TODO: This should be using static lookup.
foreach (var ent in _mapSystem.GetAnchoredEntities(grid, gridComp, tile))
{
if (!physQuery.TryGetComponent(ent, out var body))
continue;
if (body.BodyType != BodyType.Static ||
!body.Hard ||
(body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
continue;
valid = false;
break;
}
if (!valid)
continue;
var pos = _mapSystem.GridTileToLocal(grid, gridComp, tile);
var mapPos = _transform.ToMapCoordinates(pos);
// don't spawn in AntiAnomalyZones
var antiAnomalyZonesQueue = AllEntityQuery<AntiAnomalyZoneComponent, TransformComponent>();
while (antiAnomalyZonesQueue.MoveNext(out _, out var zone, out var antiXform))
{
if (antiXform.MapID != mapPos.MapId)
continue;
var antiCoordinates = _transform.GetWorldPosition(antiXform);
var delta = antiCoordinates - mapPos.Position;
if (delta.LengthSquared() < zone.ZoneRadius * zone.ZoneRadius)
{
valid = false;
break;
}
}
if (!valid)
continue;
targetCoords = pos;
validTarget = true; // Frontier
break;
}
// Frontier: one final test - if the spawn point is within an anti-anomaly zone, just don't generate it.
if (!validTarget) // Frontier
{
var mapPos = _transform.ToMapCoordinates(targetCoords);
var antiAnomalyZonesQueue = AllEntityQuery<AntiAnomalyZoneComponent, TransformComponent>();
while (antiAnomalyZonesQueue.MoveNext(out _, out var zone, out var antiXform))
{
if (antiXform.MapID != mapPos.MapId)
continue;
var antiCoordinates = _transform.GetWorldPosition(antiXform);
var delta = antiCoordinates - mapPos.Position;
if (delta.LengthSquared() < zone.ZoneRadius * zone.ZoneRadius)
{
if (generator is { } genEnt
&& TryComp(genEnt, out TransformComponent? generatorXform))
{
_stack.Spawn(genEnt.Comp.RefundAmount, genEnt.Comp.RefundStackType, generatorXform.Coordinates);
genEnt.Comp.CooldownEndTime = TimeSpan.Zero;
UpdateGeneratorUi(genEnt, genEnt.Comp);
_chat.TrySendInGameICMessage(genEnt, Loc.GetString("anomaly-generator-refund-message"), InGameICChatType.Speak, hideChat: true);
}
return;
}
}
}
// End Frontier: one final test - if the spawn point is within an anti-anomaly zone, just don't generate it.
Spawn(toSpawn, targetCoords);
}
private void OnGeneratingStartup(EntityUid uid, GeneratingAnomalyGeneratorComponent component, ComponentStartup args)
{
Appearance.SetData(uid, AnomalyGeneratorVisuals.Generating, true);
}
private void OnGeneratingFinished(EntityUid uid, AnomalyGeneratorComponent component)
{
var xform = Transform(uid);
if (xform.GridUid == null)
return;
SpawnOnRandomGridLocation(xform.GridUid.Value, component.SpawnerPrototype, (uid, component)); // Frontier: add (uid, component)
RemComp<GeneratingAnomalyGeneratorComponent>(uid);
Appearance.SetData(uid, AnomalyGeneratorVisuals.Generating, false);
Audio.PlayPvs(component.GeneratingFinishedSound, uid);
// var message = Loc.GetString("anomaly-generator-announcement"); // Frontier: quiet generators
// _radio.SendRadioMessage(uid, message, _prototype.Index<RadioChannelPrototype>(component.ScienceChannel), uid); // Frontier
}
private void UpdateGenerator()
{
var query = EntityQueryEnumerator<GeneratingAnomalyGeneratorComponent, AnomalyGeneratorComponent>();
while (query.MoveNext(out var ent, out var active, out var gen))
{
if (Timing.CurTime < active.EndTime)
continue;
active.AudioStream = _audio.Stop(active.AudioStream);
OnGeneratingFinished(ent, gen);
}
}
}