using System.Linq;
using Content.Server.Gateway.Components;
using Content.Server.Procedural;
using Content.Shared.CCVar;
using Content.Shared.Dataset;
using Content.Shared.Maps;
using Content.Shared.Procedural;
using Content.Shared.Procedural.Components;
using Content.Shared.Salvage;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Gateway.Systems;
///
/// Generates gateway destinations regularly and indefinitely that can be chosen from.
///
public sealed class GatewayGeneratorSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
[Dependency] private readonly BiomeSystem _biome = default!;
[Dependency] private readonly DungeonSystem _dungeon = default!;
[Dependency] private readonly GatewaySystem _gateway = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly SharedSalvageSystem _salvage = default!;
[Dependency] private readonly TileSystem _tile = default!;
[ValidatePrototypeId]
private const string PlanetNames = "NamesBorer";
// TODO:
// Fix shader some more
// Show these in UI
// Use regular mobs for thingo.
// Use salvage mission params
// Add the funny song
// Put salvage params in the UI
// Re-use salvage config stuff for the RNG
// Have it in the UI like expeditions.
// Also add weather coz it's funny.
// Add songs (incl. the downloaded one) to the ambient music playlist for planet probably.
// Copy most of salvage mission spawner
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnGeneratorMapInit);
SubscribeLocalEvent(OnGeneratorShutdown);
SubscribeLocalEvent(OnGeneratorAttemptOpen);
SubscribeLocalEvent(OnGeneratorOpen);
}
private void OnGeneratorShutdown(EntityUid uid, GatewayGeneratorComponent component, ComponentShutdown args)
{
foreach (var genUid in component.Generated)
{
if (Deleted(genUid))
continue;
QueueDel(genUid);
}
}
private void OnGeneratorMapInit(EntityUid uid, GatewayGeneratorComponent generator, MapInitEvent args)
{
if (!_cfgManager.GetCVar(CCVars.GatewayGeneratorEnabled))
return;
generator.NextUnlock = TimeSpan.FromMinutes(5);
for (var i = 0; i < 3; i++)
{
GenerateDestination(uid, generator);
}
}
private void GenerateDestination(EntityUid uid, GatewayGeneratorComponent? generator = null)
{
if (!Resolve(uid, ref generator))
return;
var tileDef = _tileDefManager["FloorSteel"];
const int MaxOffset = 256;
var tiles = new List<(Vector2i Index, Tile Tile)>();
var seed = _random.Next();
var random = new Random(seed);
var mapUid = _maps.CreateMap();
var gatewayName = _salvage.GetFTLName(_protoManager.Index(PlanetNames), seed);
_metadata.SetEntityName(mapUid, gatewayName);
var origin = new Vector2i(random.Next(-MaxOffset, MaxOffset), random.Next(-MaxOffset, MaxOffset));
var restricted = new RestrictedRangeComponent
{
Origin = origin
};
AddComp(mapUid, restricted);
_biome.EnsurePlanet(mapUid, _protoManager.Index("BiomeContinental"), seed);
var grid = Comp(mapUid);
for (var x = -2; x <= 2; x++)
{
for (var y = -2; y <= 2; y++)
{
tiles.Add((new Vector2i(x, y) + origin, new Tile(tileDef.TileId, variant: _tile.PickVariant((ContentTileDefinition)tileDef, random))));
}
}
// Clear area nearby as a sort of landing pad.
_maps.SetTiles(mapUid, grid, tiles);
_metadata.SetEntityName(mapUid, gatewayName);
var originCoords = new EntityCoordinates(mapUid, origin);
var genDest = AddComp(mapUid);
genDest.Origin = origin;
genDest.Seed = seed;
genDest.Generator = uid;
// Create the gateway.
var gatewayUid = SpawnAtPosition(generator.Proto, originCoords);
var gatewayComp = Comp(gatewayUid);
_gateway.SetDestinationName(gatewayUid, FormattedMessage.FromMarkupOrThrow($"[color=#D381C996]{gatewayName}[/color]"), gatewayComp);
_gateway.SetEnabled(gatewayUid, true, gatewayComp);
generator.Generated.Add(mapUid);
}
private void OnGeneratorAttemptOpen(Entity ent, ref AttemptGatewayOpenEvent args)
{
if (ent.Comp.Loaded || args.Cancelled)
return;
if (!TryComp(ent.Comp.Generator, out GatewayGeneratorComponent? generatorComp))
return;
if (generatorComp.NextUnlock + _metadata.GetPauseTime(ent.Owner) <= _timing.CurTime)
return;
args.Cancelled = true;
}
private void OnGeneratorOpen(Entity ent, ref GatewayOpenEvent args)
{
if (ent.Comp.Loaded)
return;
if (TryComp(ent.Comp.Generator, out GatewayGeneratorComponent? generatorComp))
{
generatorComp.NextUnlock = _timing.CurTime + generatorComp.UnlockCooldown;
_gateway.UpdateAllGateways();
// Generate another destination to keep them going.
GenerateDestination(ent.Comp.Generator);
}
if (!TryComp(args.MapUid, out MapGridComponent? grid))
return;
ent.Comp.Locked = false;
ent.Comp.Loaded = true;
// Do dungeon
var seed = ent.Comp.Seed;
var origin = ent.Comp.Origin;
var random = new Random(seed);
var dungeonDistance = random.Next(3, 6);
var dungeonRotation = _dungeon.GetDungeonRotation(seed);
var dungeonPosition = (origin + dungeonRotation.RotateVec(new Vector2i(0, dungeonDistance))).Floored();
_dungeon.GenerateDungeon(_protoManager.Index("Experiment"), args.MapUid, grid, dungeonPosition, seed);
// TODO: Dungeon mobs + loot.
// Do markers on the map.
if (TryComp(ent.Owner, out BiomeComponent? biomeComp) && generatorComp != null)
{
// - Loot
var lootLayers = generatorComp.LootLayers.ToList();
for (var i = 0; i < generatorComp.LootLayerCount; i++)
{
var layerIdx = random.Next(lootLayers.Count);
var layer = lootLayers[layerIdx];
lootLayers.RemoveSwap(layerIdx);
_biome.AddLayer((ent.Owner, biomeComp), $"{layer.Id}-{i}", layer.Id);
}
// - Mobs
var mobLayers = generatorComp.MobLayers.ToList();
for (var i = 0; i < generatorComp.MobLayerCount; i++)
{
var layerIdx = random.Next(mobLayers.Count);
var layer = mobLayers[layerIdx];
mobLayers.RemoveSwap(layerIdx);
_biome.AddLayer((ent.Owner, biomeComp), $"{layer.Id}-{i}", layer.Id);
}
}
}
}