diff --git a/Content.Server/_CP14/BiomeSpawner/Components/CP14BiomeSpawnerComponent.cs b/Content.Server/_CP14/BiomeSpawner/Components/CP14BiomeSpawnerComponent.cs index ccdf909849..1672f0b658 100644 --- a/Content.Server/_CP14/BiomeSpawner/Components/CP14BiomeSpawnerComponent.cs +++ b/Content.Server/_CP14/BiomeSpawner/Components/CP14BiomeSpawnerComponent.cs @@ -7,6 +7,7 @@ using Content.Server._CP14.BiomeSpawner.EntitySystems; using Content.Shared.Parallax.Biomes; +using Content.Shared.Whitelist; using Robust.Shared.Prototypes; namespace Content.Server._CP14.BiomeSpawner.Components; @@ -19,4 +20,10 @@ public sealed partial class CP14BiomeSpawnerComponent : Component { [DataField] public ProtoId Biome = "Grasslands"; + + /// + /// entities that we don't remove. + /// + [DataField(required: true)] + public EntityWhitelist DeleteBlacklist = new(); } diff --git a/Content.Server/_CP14/BiomeSpawner/EntitySystems/CP14BiomeSpawnerSystem.cs b/Content.Server/_CP14/BiomeSpawner/EntitySystems/CP14BiomeSpawnerSystem.cs index 90a6d9159f..2551b6ba1e 100644 --- a/Content.Server/_CP14/BiomeSpawner/EntitySystems/CP14BiomeSpawnerSystem.cs +++ b/Content.Server/_CP14/BiomeSpawner/EntitySystems/CP14BiomeSpawnerSystem.cs @@ -10,6 +10,7 @@ using Content.Server._CP14.BiomeSpawner.Components; using Content.Server._CP14.RoundSeed; using Content.Server.Decals; using Content.Server.Parallax; +using Content.Shared.Whitelist; using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Map.Components; @@ -26,6 +27,7 @@ public sealed class CP14BiomeSpawnerSystem : EntitySystem [Dependency] private readonly DecalSystem _decals = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly CP14RoundSeedSystem _roundSeed = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; public override void Initialize() { @@ -79,11 +81,12 @@ public sealed class CP14BiomeSpawnerSystem : EntitySystem } // Remove entities - var oldEntities = _lookup.GetEntitiesInRange(spawnerTransform.Coordinates, 0.48f); + var oldEntities = _lookup.GetEntitiesInRange(spawnerTransform.Coordinates, 0.48f, LookupFlags.Uncontained); // TODO: Replace this shit with GetEntitiesInBox2 foreach (var entToRemove in oldEntities.Concat(new[] { ent.Owner })) // Do not remove self { - QueueDel(entToRemove); + if (!_whitelist.IsValid(ent.Comp.DeleteBlacklist, entToRemove)) + QueueDel(entToRemove); } if (_biome.TryGetEntity(vec, biome.Layers, tile.Value, seed, map, out var entityProto)) diff --git a/Content.Server/_CP14/StationDungeonMap/CP14StationAdditionalMapComponent.cs b/Content.Server/_CP14/StationDungeonMap/CP14StationAdditionalMapComponent.cs deleted file mode 100644 index e78352b023..0000000000 --- a/Content.Server/_CP14/StationDungeonMap/CP14StationAdditionalMapComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Utility; - -namespace Content.Server._CP14.StationDungeonMap; - -/// -/// Loads additional maps from the list at the start of the round. -/// -[RegisterComponent, Access(typeof(CP14StationAdditionalMapSystem))] -public sealed partial class CP14StationAdditionalMapComponent : Component -{ - /// - /// A map paths to load on a new map. - /// - [DataField] - public List MapPaths = new(); -} diff --git a/Content.Server/_CP14/StationDungeonMap/CP14StationAdditionalMapSystem.cs b/Content.Server/_CP14/StationDungeonMap/CP14StationAdditionalMapSystem.cs deleted file mode 100644 index b81cb4bf1d..0000000000 --- a/Content.Server/_CP14/StationDungeonMap/CP14StationAdditionalMapSystem.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Content.Server.Parallax; -using Content.Server.Station.Components; -using Content.Server.Station.Events; -using Content.Server.Station.Systems; -using Content.Shared.Teleportation.Systems; -using Robust.Server.GameObjects; -using Robust.Server.Maps; -using Robust.Shared.Prototypes; - -namespace Content.Server._CP14.StationDungeonMap; - -public sealed partial class CP14StationAdditionalMapSystem : EntitySystem -{ - [Dependency] private readonly BiomeSystem _biome = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly MapSystem _map = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly LinkedEntitySystem _linkedEntity = default!; - [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly MapLoaderSystem _mapLoader = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnStationPostInit); - } - - private void OnStationPostInit(Entity addMap, ref StationPostInitEvent args) - { - if (!TryComp(addMap, out StationDataComponent? dataComp)) - return; - - foreach (var path in addMap.Comp.MapPaths) - { - var mapUid = _map.CreateMap(out var mapId); - Log.Info($"Created map {mapId} for StationAdditionalMap system"); - var options = new MapLoadOptions { LoadMap = true }; - if (!_mapLoader.TryLoad(mapId, path.ToString(), out var roots, options)) - { - Log.Error($"Failed to load map from {path}!"); - Del(mapUid); - return; - } - } - } -} diff --git a/Content.Server/_CP14/StationDungeonMap/Components/CP14StationZLevelsComponent.cs b/Content.Server/_CP14/StationDungeonMap/Components/CP14StationZLevelsComponent.cs new file mode 100644 index 0000000000..de408cd316 --- /dev/null +++ b/Content.Server/_CP14/StationDungeonMap/Components/CP14StationZLevelsComponent.cs @@ -0,0 +1,28 @@ +using Content.Server._CP14.StationDungeonMap.EntitySystems; +using Robust.Shared.Map; +using Robust.Shared.Utility; + +namespace Content.Server._CP14.StationDungeonMap.Components; + +/// +/// Initializes the z-level system by creating a series of linked maps +/// +[RegisterComponent, Access(typeof(CP14StationZLevelsSystem))] +public sealed partial class CP14StationZLevelsComponent : Component +{ + [DataField(required: true)] + public int DefaultMapLevel = 0; + + [DataField(required: true)] + public Dictionary Levels = new(); + + public bool Initialized = false; + + public Dictionary LevelEntities = new(); +} + +[DataRecord, Serializable] +public sealed class CP14ZLevelEntry +{ + public ResPath? Path { get; set; } = null; +} diff --git a/Content.Server/_CP14/StationDungeonMap/Components/CP14ZLevelAutoPortalComponent.cs b/Content.Server/_CP14/StationDungeonMap/Components/CP14ZLevelAutoPortalComponent.cs new file mode 100644 index 0000000000..6d6469d628 --- /dev/null +++ b/Content.Server/_CP14/StationDungeonMap/Components/CP14ZLevelAutoPortalComponent.cs @@ -0,0 +1,23 @@ +using Content.Server._CP14.StationDungeonMap.EntitySystems; +using Robust.Shared.Prototypes; + +namespace Content.Server._CP14.StationDungeonMap.Components; + +/// +/// automatically creates a linked portal at a different relative z-level, and then the component is removed +/// +[RegisterComponent, Access(typeof(CP14StationZLevelsSystem))] +public sealed partial class CP14ZLevelAutoPortalComponent : Component +{ + /// + /// relative neighboring layer. Ideally, -1 is the neighboring bottom layer, +1 is the neighboring top layer + /// + [DataField(required: true)] + public int ZLevelOffset = 0; + + /// + /// prototype of the portal being created on the other side + /// + [DataField(required: true)] + public EntProtoId OtherSideProto = default!; +} diff --git a/Content.Server/_CP14/StationDungeonMap/EntitySystems/CP14StationZLevelsSystem.cs b/Content.Server/_CP14/StationDungeonMap/EntitySystems/CP14StationZLevelsSystem.cs new file mode 100644 index 0000000000..ae7c1ae11b --- /dev/null +++ b/Content.Server/_CP14/StationDungeonMap/EntitySystems/CP14StationZLevelsSystem.cs @@ -0,0 +1,147 @@ +using Content.Server._CP14.StationDungeonMap.Components; +using Content.Server.GameTicking.Events; +using Content.Server.Station.Components; +using Content.Server.Station.Events; +using Content.Server.Station.Systems; +using Content.Shared.Maps; +using Content.Shared.Station.Components; +using Content.Shared.Teleportation.Systems; +using Robust.Server.GameObjects; +using Robust.Server.Maps; +using Robust.Shared.Map; +using Robust.Shared.Random; + +namespace Content.Server._CP14.StationDungeonMap.EntitySystems; + +public sealed partial class CP14StationZLevelsSystem : EntitySystem +{ + [Dependency] private readonly MapSystem _map = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly MapLoaderSystem _mapLoader = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly SharedMapSystem _maps = default!; + [Dependency] private readonly LinkedEntitySystem _linkedEntity = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPortalMapInit); + SubscribeLocalEvent(OnRoundStart); + SubscribeLocalEvent(OnStationPostInit); + } + + private void OnRoundStart(RoundStartingEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var portal)) + { + InitPortal((uid, portal)); + } + } + + private void OnStationPostInit(Entity ent, ref StationPostInitEvent args) + { + if (ent.Comp.Initialized) + return; + + if (!TryComp(ent, out StationDataComponent? dataComp)) + { + Log.Error($"Failed to init CP14StationZLevelsSystem: no StationData"); + return; + } + + var defaultMap = _station.GetLargestGrid(dataComp); + + if (defaultMap is null) + { + Log.Error($"Failed to init CP14StationZLevelsSystem: defaultMap is null"); + return; + } + + ent.Comp.LevelEntities.Add(Transform(defaultMap.Value).MapID, ent.Comp.DefaultMapLevel); + + ent.Comp.Initialized = true; + + foreach (var (map, level) in ent.Comp.Levels) + { + if (ent.Comp.LevelEntities.ContainsValue(map)) + { + Log.Error($"Key duplication for CP14StationZLevelsSystem at level {map}!"); + continue; + } + + var path = level.Path.ToString(); + if (path is null) + { + Log.Error($"path {path} for CP14StationZLevelsSystem at level {map} don't exist!"); + continue; + } + + var mapUid = _map.CreateMap(out var mapId); + var member = EnsureComp(mapUid); + member.Station = ent; + + Log.Info($"Created map {mapId} for CP14StationZLevelsSystem at level {map}"); + var options = new MapLoadOptions { LoadMap = true }; + + if (!_mapLoader.TryLoad(mapId, path, out var grids, options)) + { + Log.Error($"Failed to load map for CP14StationZLevelsSystem at level {map}!"); + Del(mapUid); + continue; + } + ent.Comp.LevelEntities.Add(mapId, map); + } + } + + private void OnPortalMapInit(Entity autoPortal, ref MapInitEvent args) + { + InitPortal(autoPortal); + } + + private void InitPortal(Entity autoPortal) + { + var mapId = Transform(autoPortal).MapUid; + if (mapId is null) + return; + + var offsetMap = GetMapOffset(mapId.Value, autoPortal.Comp.ZLevelOffset); + + if (offsetMap is null) + return; + + var currentWorldPos = _transform.GetWorldPosition(autoPortal); + var targetMapPos = new MapCoordinates(currentWorldPos, offsetMap.Value); + + var otherSidePortal = Spawn(autoPortal.Comp.OtherSideProto, targetMapPos); + + if (_linkedEntity.TryLink(autoPortal, otherSidePortal, true)) + RemComp(autoPortal); + } + + public MapId? GetMapOffset(EntityUid mapUid, int offset) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var zLevel, out _)) + { + if (!zLevel.LevelEntities.TryGetValue(Transform(mapUid).MapID, out var currentLevel)) + continue; + + var targetLevel = currentLevel + offset; + + if (!zLevel.LevelEntities.ContainsValue(targetLevel)) + continue; + + foreach (var (key, value) in zLevel.LevelEntities) + { + if (value == targetLevel && _map.MapExists(key)) + return key; + } + } + return null; + } +} diff --git a/Resources/Maps/_CP14/battle_royale.yml b/Resources/Maps/_CP14/battle_royale.yml index 733b8c771a..acd301415b 100644 --- a/Resources/Maps/_CP14/battle_royale.yml +++ b/Resources/Maps/_CP14/battle_royale.yml @@ -25,7 +25,6 @@ entities: name: Map Entity - type: Transform - type: Map - mapPaused: True - type: PhysicsMap - type: GridTree - type: MovedGrids diff --git a/Resources/Prototypes/_CP14/Entities/Markers/Spawners/Random/Biome/biomeSpawner.yml b/Resources/Prototypes/_CP14/Entities/Markers/Spawners/Random/Biome/biomeSpawner.yml index 8da10ccc21..2ee335a5fe 100644 --- a/Resources/Prototypes/_CP14/Entities/Markers/Spawners/Random/Biome/biomeSpawner.yml +++ b/Resources/Prototypes/_CP14/Entities/Markers/Spawners/Random/Biome/biomeSpawner.yml @@ -14,6 +14,11 @@ drawdepth: BelowFloor sprite: _CP14/Markers/biome.rsi - type: CP14BiomeSpawner + deleteBlacklist: + components: + - Portal + - MindContainer + - CP14WorldBounding - type: PlacementReplacement key: CP14BiomeSpawner diff --git a/Resources/Prototypes/_CP14/Entities/Structures/dungeon_entrance.yml b/Resources/Prototypes/_CP14/Entities/Structures/dungeon_entrance.yml index 2520a3f6cb..0dbe4e1c25 100644 --- a/Resources/Prototypes/_CP14/Entities/Structures/dungeon_entrance.yml +++ b/Resources/Prototypes/_CP14/Entities/Structures/dungeon_entrance.yml @@ -1,6 +1,7 @@ - type: entity id: CP14DungeonEntrance name: dungeon entrance + noSpawn: true description: The dark depths of the underworld are calling you. placement: mode: SnapgridCenter @@ -31,12 +32,12 @@ - type: Portal canTeleportToOtherMaps: true randomTeleport: false - - type: CP14AutoLink - type: entity parent: CP14DungeonEntrance id: CP14DungeonExit name: dungeon exit + noSpawn: true description: A way out of the dark underworld into the overworld. components: - type: Sprite @@ -50,4 +51,19 @@ radius: 3 energy: 1 netsync: false - - type: CP14AutoLink \ No newline at end of file + +- type: entity + parent: CP14DungeonEntrance + id: CP14DungeonEntranceAutoLink + components: + - type: CP14ZLevelAutoPortal + zLevelOffset: -1 # Go into deep + otherSideProto: CP14DungeonExit + +- type: entity + parent: CP14DungeonExit + id: CP14DungeonExitAutoLink + components: + - type: CP14ZLevelAutoPortal + zLevelOffset: 1 # Go onto surface + otherSideProto: CP14DungeonEntrance \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Maps/arenas.yml b/Resources/Prototypes/_CP14/Maps/arenas.yml index dc34466994..116e4c069e 100644 --- a/Resources/Prototypes/_CP14/Maps/arenas.yml +++ b/Resources/Prototypes/_CP14/Maps/arenas.yml @@ -24,9 +24,11 @@ AlchemyTest: stationProto: StandardStationArena components: - - type: CP14StationAdditionalMap - mapPaths: - - /Maps/_CP14/alchemy_test_layer2.yml + - type: CP14StationZLevels + defaultMapLevel: 0 + levels: + 1: + path: /Maps/_CP14/alchemy_test_layer2.yml - type: StationNameSetup mapNameTemplate: "Alchemy test" - type: StationJobs @@ -78,4 +80,11 @@ CP14GuardCommander: [1, 1] CP14HouseKeeper: [1, 1] - type: CP14StationExpeditionTarget - shuttlePath: /Maps/_CP14/Shuttles/test-ship.yml \ No newline at end of file + shuttlePath: /Maps/_CP14/Shuttles/test-ship.yml + - type: CP14StationZLevels + defaultMapLevel: 0 + levels: + -1: + path: /Maps/_CP14/alchemy_test_layer2.yml + -2: + path: /Maps/_CP14/battle_royale.yml \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Maps/debug.yml b/Resources/Prototypes/_CP14/Maps/debug.yml index 84ad547b9d..4dbf565f1e 100644 --- a/Resources/Prototypes/_CP14/Maps/debug.yml +++ b/Resources/Prototypes/_CP14/Maps/debug.yml @@ -27,3 +27,8 @@ - type: StationJobs availableJobs: CP14Adventurer: [ -1, -1 ] + - type: CP14StationZLevels + defaultMapLevel: 0 + levels: + -1: + path: /Maps/_CP14/alchemy_test_layer2.yml diff --git a/Resources/Prototypes/_CP14/Procedural/biome_markers.yml b/Resources/Prototypes/_CP14/Procedural/biome_markers.yml new file mode 100644 index 0000000000..a79d5413c3 --- /dev/null +++ b/Resources/Prototypes/_CP14/Procedural/biome_markers.yml @@ -0,0 +1,9 @@ + +- type: biomeMarkerLayer + id: CP14OreGold + entityMask: + CP14WallStone: CP14PlantPumpkinDeath + maxCount: 30 + minGroupSize: 10 + maxGroupSize: 15 + radius: 4 \ No newline at end of file diff --git a/Resources/migration.yml b/Resources/migration.yml index c076bf9791..9eb573946c 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -37,6 +37,10 @@ CP14WallStoneSilverOre: CP14WallStoneGoldOre CP14PloughedGround: CP14SeedbedWooden CP14SeedbedDefault: CP14SeedbedWooden +# 2024-08-24 +CP14DungeonEntrance: CP14DungeonEntranceAutoLink +CP14DungeonExit: CP14DungeonExitAutoLink + # <---> CrystallPunk migration zone end