using System.Linq; using Content.Server.Gateway.Components; using Content.Server.Parallax; using Content.Server.Procedural; using Content.Shared.CCVar; using Content.Shared.Dataset; using Content.Shared.Maps; using Content.Shared.Parallax.Biomes; using Content.Shared.Procedural; 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("Continental"), 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.AddMarkerLayer(ent.Owner, biomeComp, 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.AddMarkerLayer(ent.Owner, biomeComp, layer.Id); } } } }