Procedural + z-levels refactor (#216)

* create biome tile spawner

* spawner now spawn decals and entities

* fancy spawners

* remove old decals

* randomize seed

* fix bugs

* update alchemy test map to new system

* update z-level system

* autolink tweak

* add documentation

* Update GravityComponent.cs

* Update GravityComponent.cs
This commit is contained in:
Ed
2024-06-05 19:59:39 +03:00
committed by GitHub
parent 454930bb85
commit b803496694
21 changed files with 65825 additions and 246 deletions

View File

@@ -0,0 +1,15 @@
using Content.Server._CP14.MeleeWeapon;
using Content.Shared.Parallax.Biomes;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.BiomeSpawner;
/// <summary>
/// fills the tile in which it is located with the contents of the biome. Includes: tile, decals and entities
/// </summary>
[RegisterComponent, Access(typeof(CP14BiomeSpawnerSystem))]
public sealed partial class CP14BiomeSpawnerComponent : Component
{
[DataField]
public ProtoId<BiomeTemplatePrototype> Biome = "Grasslands";
}

View File

@@ -0,0 +1,88 @@
using System.Numerics;
using Content.Server.Decals;
using Content.Server.GameTicking;
using Content.Server.Parallax;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server._CP14.BiomeSpawner;
public sealed class CP14BiomeSpawnerSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly BiomeSystem _biome = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly DecalSystem _decals = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private int _seed = 27;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RoundStartAttemptEvent>(OnRoundStartAttempt);
SubscribeLocalEvent<CP14BiomeSpawnerComponent, MapInitEvent>(OnMapInit);
}
private void OnRoundStartAttempt(RoundStartAttemptEvent ev)
{
_seed = _random.Next(100000);
}
private void OnMapInit(Entity<CP14BiomeSpawnerComponent> spawner, ref MapInitEvent args)
{
SpawnBiome(spawner);
QueueDel(spawner);
}
private void SpawnBiome(Entity<CP14BiomeSpawnerComponent> spawner)
{
var biome = _proto.Index(spawner.Comp.Biome);
var parent = _transform.GetParent(spawner);
if (parent == null)
return;
var gridUid = parent.Owner;
if (!TryComp<MapGridComponent>(gridUid, out var map))
return;
var v2i = _transform.GetGridOrMapTilePosition(spawner);
if (!_biome.TryGetTile(v2i, biome.Layers, _seed, map, out var tile))
return;
// Set new tile
_maps.SetTile(gridUid, map, v2i, tile.Value);
// Remove old decals
var oldDecals = _decals.GetDecalsInRange(gridUid, v2i + new Vector2(0.5f, 0.5f));
foreach (var (id, _) in oldDecals)
{
_decals.RemoveDecal(gridUid, id);
}
//Add decals
if (_biome.TryGetDecals(v2i, biome.Layers, _seed, map, out var decals))
{
foreach (var decal in decals)
{
_decals.TryAddDecal(decal.ID, new EntityCoordinates(gridUid, decal.Position), out _);
}
}
//TODO maybe need remove anchored entities here
//Add entities
if (_biome.TryGetEntity(v2i, biome.Layers, tile.Value, _seed, map, out var entityProto))
{
var ent = _entManager.SpawnEntity(entityProto,
new EntityCoordinates(gridUid, v2i + map.TileSizeHalfVector));
}
}
}

View File

@@ -1,13 +0,0 @@
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.DungeonPortal;
/// <summary>
/// Automatically creates a linked portal of a certain prototype on the opposite linked world, if it exists.
/// </summary>
[RegisterComponent, Access(typeof(CP14AutoDungeonPortalSystem))]
public sealed partial class CP14AutoDungeonPortalComponent : Component
{
[DataField(required: true)]
public EntProtoId OtherSidePortal;
}

View File

@@ -1,78 +0,0 @@
using Content.Shared.Maps;
using Content.Shared.Teleportation.Components;
using Content.Shared.Teleportation.Systems;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Random;
namespace Content.Server._CP14.DungeonPortal;
public sealed partial class CP14AutoDungeonPortalSystem : EntitySystem
{
[Dependency] private readonly MapSystem _map = default!;
[Dependency] private readonly LinkedEntitySystem _linkedEntity = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
[Dependency] private readonly TileSystem _tile = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14AutoDungeonPortalComponent, MapInitEvent>(OnMapInit);
}
private void OnMapInit(Entity<CP14AutoDungeonPortalComponent> autoPortal, ref MapInitEvent args)
{
if (!TryComp<PortalComponent>(autoPortal, out var portalComp))
return;
var mapId = Transform(autoPortal).MapID;
_map.TryGetMap(mapId, out var mapUid);
if (mapUid == null)
return;
if (!TryComp<LinkedEntityComponent>(mapUid, out var link))
return;
if (link.LinkedEntities.Count > 1) //Bruh, we don't want more than 1 linked maps for now
return;
var targetMapUid = _random.Pick(link.LinkedEntities);
var targetMapId = Transform(targetMapUid).MapID;
var currentWorldPos = _transform.GetWorldPosition(autoPortal);
var targetMapPos = new MapCoordinates(currentWorldPos, targetMapId);
var otherSidePortal = Spawn(autoPortal.Comp.OtherSidePortal, targetMapPos);
if (_linkedEntity.TryLink(autoPortal, otherSidePortal, true))
RemComp<CP14AutoDungeonPortalComponent>(autoPortal);
ClearOtherSide(otherSidePortal, targetMapUid);
}
private void ClearOtherSide(EntityUid otherSidePortal, EntityUid targetMapUid)
{
var tiles = new List<(Vector2i Index, Tile Tile)>();
var originF = _transform.GetWorldPosition(otherSidePortal);
var origin = new Vector2i((int) originF.X, (int) originF.Y);
var tileDef = _tileDefManager["CP14FloorStonebricks"]; //TODO: Remove hardcode
var seed = _random.Next();
var random = new Random(seed);
var grid = Comp<MapGridComponent>(targetMapUid);
for (var x = -2; x <= 2; x++) //TODO: Remove hardcode
{
for (var y = -2; y <= 2; y++)
{
tiles.Add((new Vector2i(x, y) + origin, new Tile(tileDef.TileId, variant: _tile.PickVariant((ContentTileDefinition) tileDef, random))));
}
}
_maps.SetTiles(targetMapUid, grid, tiles);
}
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.Map;
namespace Content.Server._CP14.PortalAutoLink;
/// <summary>
/// allows you to automatically link entities to each other, through key matching searches
/// </summary>
[RegisterComponent, Access(typeof(CP14AutoLinkSystem))]
public sealed partial class CP14AutoLinkComponent : Component
{
/// <summary>
/// a key that is used to search for another autolinked entity installed in the worlds
/// </summary>
[DataField]
public string? AutoLinkKey = "Hello";
}

View File

@@ -0,0 +1,44 @@
using Content.Shared.Interaction;
using Content.Shared.Teleportation.Systems;
namespace Content.Server._CP14.PortalAutoLink;
public sealed partial class CP14AutoLinkSystem : EntitySystem
{
[Dependency] private readonly LinkedEntitySystem _link = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14AutoLinkComponent, MapInitEvent>(OnMapInit);
}
private void OnMapInit(Entity<CP14AutoLinkComponent> autolink, ref MapInitEvent args)
{
TryAutoLink(autolink, out var otherLink);
}
public bool TryAutoLink(Entity<CP14AutoLinkComponent> autolink, out EntityUid? linkedEnt)
{
linkedEnt = null;
var query = EntityQueryEnumerator<CP14AutoLinkComponent>();
while (query.MoveNext(out var uid, out var otherAutolink))
{
if (autolink.Comp == otherAutolink)
continue;
if (autolink.Comp.AutoLinkKey == otherAutolink.AutoLinkKey)
{
if (_link.TryLink(autolink, uid, false))
{
RemComp<CP14AutoLinkComponent>(uid);
RemComp<CP14AutoLinkComponent>(autolink);
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.Utility;
namespace Content.Server._CP14.StationDungeonMap;
/// <summary>
/// Initializes a procedurally generated world with points of interest
/// </summary>
[RegisterComponent, Access(typeof(CP14StationAdditionalMapSystem))]
public sealed partial class CP14StationAdditionalMapComponent : Component
{
/// <summary>
/// A map paths to load on a new map.
/// </summary>
[DataField]
public List<ResPath> MapPaths = new();
}

View File

@@ -0,0 +1,46 @@
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<CP14StationAdditionalMapComponent, StationPostInitEvent>(OnStationPostInit);
}
private void OnStationPostInit(Entity<CP14StationAdditionalMapComponent> 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;
}
}
}
}

View File

@@ -1,27 +0,0 @@
using Content.Shared.Parallax.Biomes;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.StationDungeonMap;
/// <summary>
/// Initializes a procedurally generated world with points of interest
/// </summary>
[RegisterComponent, Access(typeof(CP14StationDungeonMapSystem))]
public sealed partial class CP14StationDungeonMapComponent : Component
{
[DataField(required: true)]
public ProtoId<BiomeTemplatePrototype> Biome = "Caves";
// If null, its random
[DataField]
public int? Seed = null;
[DataField]
public Color MapLightColor = Color.Black;
[DataField]
public string MapName = "Dungeon map";
[DataField(serverOnly: true)]
public ComponentRegistry Components = new();
}

View File

@@ -1,54 +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.Shared.Prototypes;
namespace Content.Server._CP14.StationDungeonMap;
public sealed partial class CP14StationDungeonMapSystem : 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!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14StationDungeonMapComponent, StationPostInitEvent>(OnStationPostInit);
}
private void OnStationPostInit(Entity<CP14StationDungeonMapComponent> map, ref StationPostInitEvent args)
{
if (!TryComp(map, out StationDataComponent? dataComp))
return;
var mapUid = _map.CreateMap(out var mapId);
_metaData.SetEntityName(mapUid, map.Comp.MapName);
_biome.EnsurePlanet(mapUid, _proto.Index(map.Comp.Biome), map.Comp.Seed, mapLight: map.Comp.MapLightColor);
EntityManager.AddComponents(mapUid, map.Comp.Components);
TryLinkDungeonAndStationMaps(dataComp, mapUid);
}
private bool TryLinkDungeonAndStationMaps(StationDataComponent dataComp, EntityUid mapUid)
{
var station = _station.GetLargestGrid(dataComp);
if (station == null)
return false;
var stationMapId = Transform(station.Value).MapID;
_map.TryGetMap(stationMapId, out var stationMapUid);
if (stationMapUid == null)
return false;
return _linkedEntity.TryLink(mapUid, stationMapUid.Value);
}
}

View File

@@ -118,7 +118,8 @@ public abstract class SharedBiomeSystem : EntitySystem
// Check if the tile is from meta layer, otherwise fall back to default layers.
if (layer is BiomeMetaLayer meta)
{
if (TryGetBiomeTile(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, seed, grid, out tile))
if (TryGetTile(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, seed, grid, out tile))
//CP14 bandage - replaced from TryGetBiomeTile (not working for biomespawner)
{
return true;
}
@@ -183,7 +184,7 @@ public abstract class SharedBiomeSystem : EntitySystem
}
private bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, Tile tileRef, int seed, MapGridComponent grid,
public bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, Tile tileRef, int seed, MapGridComponent grid,
[NotNullWhen(true)] out string? entity)
{
var tileId = TileDefManager[tileRef.TypeId].ID;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -36,8 +36,6 @@
- CP14Adventurer #CrystallPunk Dev replacement
availableJobs:
CP14Adventurer: [ -1, -1 ] #CrystallPunk Dev replacement
- type: CP14StationDungeonMap
biome: CP14CavesGeneric
#- type: gameMap
# id: TestTeg

View File

@@ -0,0 +1,42 @@
- type: entity
id: CP14BaseBiomeSpawner
name: biome spawner
abstract: true
placement:
mode: SnapgridCenter
components:
- type: Transform
anchored: true
- type: Clickable
- type: InteractionOutline
- type: Sprite
snapCardinals: true
drawdepth: BelowFloor
sprite: _CP14/Markers/biome.rsi
- type: CP14BiomeSpawner
- type: PlacementReplacement
key: CP14BiomeSpawner
- type: entity
id: CP14BiomeSpawnerGrassland
parent: CP14BaseBiomeSpawner
suffix: Grassland
components:
- type: Sprite
layers:
- state: grass
- state: frame
- type: CP14BiomeSpawner
biome: CP14GrasslandTestResult
- type: entity
id: CP14BiomeSpawnerCave
parent: CP14BaseBiomeSpawner
suffix: Cave
components:
- type: Sprite
layers:
- state: cave
- state: frame
- type: CP14BiomeSpawner
biome: CP14CavesGeneric

View File

@@ -1,7 +1,6 @@
- type: entity
id: CP14DungeonEntrance
name: dungeon entrance
noSpawn: true
description: The dark depths of the underworld are calling you.
placement:
mode: SnapgridCenter
@@ -32,19 +31,11 @@
- type: Portal
canTeleportToOtherMaps: true
randomTeleport: false
- type: entity
parent: CP14DungeonEntrance
id: CP14DungeonEntranceAutolink
suffix: Autolink Dungeon
components:
- type: CP14AutoDungeonPortal
otherSidePortal: CP14DungeonExit
- type: CP14AutoLink
- type: entity
parent: CP14DungeonEntrance
id: CP14DungeonExit
noSpawn: true
name: dungeon exit
description: A way out of the dark underworld into the overworld.
components:
@@ -59,11 +50,4 @@
radius: 3
energy: 1
netsync: false
- type: entity
parent: CP14DungeonExit
id: CP14DungeonExitAutolink
suffix: Autolink Dungeon
components:
- type: CP14AutoDungeonPortal
otherSidePortal: CP14DungeonEntrance
- type: CP14AutoLink

View File

@@ -27,10 +27,9 @@
- type: StationRandomTransform
enableStationRotation: false
maxStationOffset: null
- type: StationBiome
biome: CP14GrasslandTestResult
- type: CP14StationDungeonMap
biome: CP14CavesGeneric
- type: CP14StationAdditionalMap
mapPaths:
- /Maps/_CP14/alchemy_test_layer2.yml
- type: StationNameSetup
mapNameTemplate: "Alchemy test"
- type: StationJobs

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

View File

@@ -0,0 +1,20 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Created by TheShuEd (Github) for CrystallPunk",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "cave"
},
{
"name": "frame"
},
{
"name": "grass"
}
]
}