From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 12 Feb 2023 02:15:09 +0000 (+1100) Subject: Procgen biomes (#13487) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=37f432ca58db3d220b6b0fd46fd7a16f61e97f82;p=space-station-14.git Procgen biomes (#13487) * Planetmap tiles Biomes etc etc * a * oop * Chunk-based rendering * funny * Less allocations * Fix overdraw * Content tile edge support Also updated grass to use it as a POC. * Kindly revert * Update for variant edges * fixes * Use fastnoise * Remove redundant group * a * refactor a fair bit * Prototype data instead * tweaks * a * fix maths * working * a * Slightly better empty support * a * flowers * sounds * lewd * Networking * more fixes * better * colours * Some chunk loading * Proper loading and unloading * Better loading * Fix parallax and movement sounds * Anchoring support + decal setup * Most of the way to load and unload * Decal loading kinda werkin * large trees * started diffing * a * Variant support and deserts * a * snow * agony, even * working again * todo * a * laba tiles * aeiou * a # Conflicts: # Resources/Prototypes/Entities/Tiles/planet.yml # Resources/Prototypes/Tiles/planet.yml # Resources/Textures/Tiles/Planet/Lava/lava.rsi/meta.json * laba * Add lava * Initial ignition * triggers * a * a * y * Add basalt tiles Did some unconventional things for the animation + rocks. * fixies * mergies * promotion * lava biome * Lava planet start * cleanup and more lava * laba * maccas * biome stuf * weh * bongflicts * aeaeae * More fixes * a * these too --- diff --git a/Content.Client/IconSmoothing/IconSmoothComponent.cs b/Content.Client/IconSmoothing/IconSmoothComponent.cs index bc7667d222..75c3637a3a 100644 --- a/Content.Client/IconSmoothing/IconSmoothComponent.cs +++ b/Content.Client/IconSmoothing/IconSmoothComponent.cs @@ -1,4 +1,6 @@ using JetBrains.Annotations; +using Robust.Client.Graphics; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Client.IconSmoothing { diff --git a/Content.Client/Parallax/BiomeSystem.cs b/Content.Client/Parallax/BiomeSystem.cs new file mode 100644 index 0000000000..dc326e1fa7 --- /dev/null +++ b/Content.Client/Parallax/BiomeSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.Parallax.Biomes; + +namespace Content.Client.Parallax; + +public sealed class BiomeSystem : SharedBiomeSystem +{ + +} diff --git a/Content.Client/Parallax/ParallaxOverlay.cs b/Content.Client/Parallax/ParallaxOverlay.cs index 5e63bfca0a..642cdec896 100644 --- a/Content.Client/Parallax/ParallaxOverlay.cs +++ b/Content.Client/Parallax/ParallaxOverlay.cs @@ -1,5 +1,6 @@ using Content.Client.Parallax.Managers; using Content.Shared.CCVar; +using Content.Shared.Parallax.Biomes; using Robust.Client.Graphics; using Robust.Shared.Configuration; using Robust.Shared.Enums; @@ -11,9 +12,11 @@ namespace Content.Client.Parallax; public sealed class ParallaxOverlay : Overlay { + [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IParallaxManager _manager = default!; private readonly ParallaxSystem _parallax; @@ -23,8 +26,15 @@ public sealed class ParallaxOverlay : Overlay { ZIndex = ParallaxSystem.ParallaxZIndex; IoCManager.InjectDependencies(this); - _parallax = IoCManager.Resolve().GetEntitySystem(); + _parallax = _entManager.System(); + } + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + if (args.MapId == MapId.Nullspace || _entManager.HasComponent(_mapManager.GetMapEntityId(args.MapId))) + return false; + return true; } protected override void Draw(in OverlayDrawArgs args) diff --git a/Content.MapRenderer/Painters/TilePainter.cs b/Content.MapRenderer/Painters/TilePainter.cs index 805792b2a3..a19e56def1 100644 --- a/Content.MapRenderer/Painters/TilePainter.cs +++ b/Content.MapRenderer/Painters/TilePainter.cs @@ -15,7 +15,6 @@ namespace Content.MapRenderer.Painters { public sealed class TilePainter { - private const string TilesPath = "/Textures/Tiles/"; public const int TileImageSize = EyeManager.PixelsPerMeter; private readonly ITileDefinitionManager _sTileDefinitionManager; diff --git a/Content.Server/Maps/PlanetCommand.cs b/Content.Server/Maps/PlanetCommand.cs index e248bd55a8..558469c4a2 100644 --- a/Content.Server/Maps/PlanetCommand.cs +++ b/Content.Server/Maps/PlanetCommand.cs @@ -6,11 +6,13 @@ using Content.Shared.Administration; using Content.Shared.Atmos; using Content.Shared.Gravity; using Content.Shared.Movement.Components; -using Content.Shared.Parallax; +using Content.Shared.Parallax.Biomes; using Robust.Shared.Audio; using Robust.Shared.Console; using Robust.Shared.Map; using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Server.Maps; @@ -22,13 +24,15 @@ public sealed class PlanetCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; public string Command => $"planet"; public string Description => Loc.GetString("cmd-planet-desc"); public string Help => Loc.GetString("cmd-planet-help", ("command", Command)); public void Execute(IConsoleShell shell, string argStr, string[] args) { - if (args.Length != 1) + if (args.Length != 2) { shell.WriteError(Loc.GetString($"cmd-planet-args")); return; @@ -48,16 +52,35 @@ public sealed class PlanetCommand : IConsoleCommand return; } + if (!_protoManager.HasIndex(args[1])) + { + shell.WriteError(Loc.GetString("cmd-planet-map-prototype", ("prototype", args[1]))); + return; + } + var mapUid = _mapManager.GetMapEntityId(mapId); MetaDataComponent? metadata = null; - var parallax = _entManager.EnsureComponent(mapUid); - parallax.Parallax = "Grass"; - _entManager.Dirty(parallax, metadata); + var biome = _entManager.EnsureComponent(mapUid); + biome.BiomePrototype = args[1]; + biome.Seed = _random.Next(); + _entManager.Dirty(biome); + var gravity = _entManager.EnsureComponent(mapUid); gravity.Enabled = true; - _entManager.Dirty(gravity); - _entManager.EnsureComponent(mapUid); + _entManager.Dirty(gravity, metadata); + + // Day lighting + // Daylight: #D8B059 + // Midday: #E6CB8B + // Moonlight: #2b3143 + // Lava: #A34931 + + var light = _entManager.EnsureComponent(mapUid); + light.AmbientLightColor = Color.FromHex("#D8B059"); + _entManager.Dirty(light, metadata); + + // Atmos var atmos = _entManager.EnsureComponent(mapUid); atmos.Space = false; @@ -71,24 +94,27 @@ public sealed class PlanetCommand : IConsoleCommand Moles = moles, }; - var footstep = _entManager.EnsureComponent(mapUid); - footstep.Sound = new SoundCollectionSpecifier("FootstepGrass"); - _entManager.Dirty(footstep); - _entManager.EnsureComponent(mapUid); shell.WriteLine(Loc.GetString("cmd-planet-success", ("mapId", mapId))); } public CompletionResult GetCompletion(IConsoleShell shell, string[] args) { - if (args.Length != 1) + if (args.Length == 1) { - return CompletionResult.Empty; + var options = _entManager.EntityQuery(true) + .Select(o => new CompletionOption(o.WorldMap.ToString(), "MapId")); + + return CompletionResult.FromOptions(options); } - var options = _entManager.EntityQuery(true) - .Select(o => new CompletionOption(o.WorldMap.ToString(), "MapId")); + if (args.Length == 2) + { + var options = _protoManager.EnumeratePrototypes() + .Select(o => new CompletionOption(o.ID, "Biome")); + return CompletionResult.FromOptions(options); + } - return CompletionResult.FromOptions(options); + return CompletionResult.Empty; } } diff --git a/Content.Server/Parallax/BiomeSystem.cs b/Content.Server/Parallax/BiomeSystem.cs new file mode 100644 index 0000000000..9f8c9c1c4c --- /dev/null +++ b/Content.Server/Parallax/BiomeSystem.cs @@ -0,0 +1,301 @@ +using Content.Server.Decals; +using Content.Shared.Decals; +using Content.Shared.Parallax.Biomes; +using Robust.Server.Player; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Noise; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Serialization.Manager; + +namespace Content.Server.Parallax; + +public sealed class BiomeSystem : SharedBiomeSystem +{ + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly DecalSystem _decals = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + private readonly HashSet _handledEntities = new(); + private const float LoadRange = ChunkSize * 2f; + private readonly Box2 _loadArea = new(-LoadRange, -LoadRange, LoadRange, LoadRange); + + private readonly Dictionary> _activeChunks = new(); + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnBiomeMapInit); + } + + private void OnBiomeMapInit(EntityUid uid, BiomeComponent component, MapInitEvent args) + { + component.Seed = _random.Next(); + Dirty(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + var biomeQuery = GetEntityQuery(); + var xformQuery = GetEntityQuery(); + var biomes = AllEntityQuery(); + + while (biomes.MoveNext(out var biome)) + { + _activeChunks.Add(biome, new HashSet()); + } + + // Get chunks in range + foreach (var client in Filter.GetAllPlayers(_playerManager)) + { + var pSession = (IPlayerSession) client; + + if (xformQuery.TryGetComponent(pSession.AttachedEntity, out var xform) && + _handledEntities.Add(pSession.AttachedEntity.Value) && + biomeQuery.TryGetComponent(xform.MapUid, out var biome)) + { + AddChunksInRange(biome, _transform.GetWorldPosition(xform, xformQuery)); + } + + foreach (var viewer in pSession.ViewSubscriptions) + { + if (!_handledEntities.Add(viewer) || + !xformQuery.TryGetComponent(viewer, out xform) || + !biomeQuery.TryGetComponent(xform.MapUid, out biome)) + { + continue; + } + + AddChunksInRange(biome, _transform.GetWorldPosition(xform, xformQuery)); + } + } + + var loadBiomes = AllEntityQuery(); + + while (loadBiomes.MoveNext(out var biome, out var grid)) + { + var noise = new FastNoise(biome.Seed); + var gridUid = grid.Owner; + + // Load new chunks + LoadChunks(biome, gridUid, grid, noise); + // Unload old chunks + UnloadChunks(biome, gridUid, grid, noise); + } + + _handledEntities.Clear(); + _activeChunks.Clear(); + } + + private void AddChunksInRange(BiomeComponent biome, Vector2 worldPos) + { + var enumerator = new ChunkIndicesEnumerator(_loadArea.Translated(worldPos), ChunkSize); + + while (enumerator.MoveNext(out var chunkOrigin)) + { + _activeChunks[biome].Add(chunkOrigin.Value); + } + } + + private void LoadChunks(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, FastNoise noise) + { + var active = _activeChunks[component]; + var prototype = ProtoManager.Index(component.BiomePrototype); + List<(Vector2i, Tile)>? tiles = null; + + foreach (var chunk in active) + { + if (!component.LoadedChunks.Add(chunk)) + continue; + + tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize); + // Load NOW! + LoadChunk(component, gridUid, grid, chunk * ChunkSize, noise, prototype, tiles); + } + } + + private void LoadChunk(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, Vector2i chunk, FastNoise noise, BiomePrototype prototype, List<(Vector2i, Tile)> tiles) + { + component.ModifiedTiles.TryGetValue(chunk, out var modified); + modified ??= new HashSet(); + + // Set tiles first + for (var x = 0; x < ChunkSize; x++) + { + for (var y = 0; y < ChunkSize; y++) + { + var indices = new Vector2i(x + chunk.X, y + chunk.Y); + + if (modified.Contains(indices)) + continue; + + // If there's existing data then don't overwrite it. + if (grid.TryGetTileRef(indices, out var tileRef) && !tileRef.Tile.IsEmpty) + continue; + + // Pass in null so we don't try to get the tileref. + if (!TryGetBiomeTile(indices, prototype, noise, null, out var biomeTile) || biomeTile.Value == tileRef.Tile) + continue; + + tiles.Add((indices, biomeTile.Value)); + } + } + + grid.SetTiles(tiles); + tiles.Clear(); + + // Now do entities + var loadedEntities = new List(); + component.LoadedEntities.Add(chunk, loadedEntities); + + for (var x = 0; x < ChunkSize; x++) + { + for (var y = 0; y < ChunkSize; y++) + { + var indices = new Vector2i(x + chunk.X, y + chunk.Y); + + if (modified.Contains(indices)) + continue; + + // Don't mess with anything that's potentially anchored. + var anchored = grid.GetAnchoredEntitiesEnumerator(indices); + + if (anchored.MoveNext(out _) || !TryGetEntity(indices, prototype, noise, grid, out var entPrototype)) + continue; + + // TODO: Fix non-anchored ents spawning. + // Just track loaded chunks for now. + var ent = Spawn(entPrototype, grid.GridTileToLocal(indices)); + loadedEntities.Add(ent); + } + } + + // Decals + var loadedDecals = new Dictionary(); + component.LoadedDecals.Add(chunk, loadedDecals); + + for (var x = 0; x < ChunkSize; x++) + { + for (var y = 0; y < ChunkSize; y++) + { + var indices = new Vector2i(x + chunk.X, y + chunk.Y); + + if (modified.Contains(indices)) + continue; + + // Don't mess with anything that's potentially anchored. + var anchored = grid.GetAnchoredEntitiesEnumerator(indices); + + if (anchored.MoveNext(out _) || !TryGetDecals(indices, prototype, noise, grid, out var decals)) + continue; + + foreach (var decal in decals) + { + if (!_decals.TryAddDecal(decal.ID, new EntityCoordinates(gridUid, decal.Position), out var dec)) + continue; + + loadedDecals.Add(dec, indices); + } + } + } + + if (modified.Count == 0) + { + component.ModifiedTiles.Remove(chunk); + } + else + { + component.ModifiedTiles[chunk] = modified; + } + } + + private void UnloadChunks(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, FastNoise noise) + { + var active = _activeChunks[component]; + List<(Vector2i, Tile)>? tiles = null; + + foreach (var chunk in component.LoadedChunks) + { + if (active.Contains(chunk) || !component.LoadedChunks.Remove(chunk)) + continue; + + // Unload NOW! + tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize); + UnloadChunk(component, gridUid, grid, chunk * ChunkSize, noise, tiles); + } + } + + private void UnloadChunk(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, Vector2i chunk, FastNoise noise, List<(Vector2i, Tile)> tiles) + { + // Reverse order to loading + var prototype = ProtoManager.Index(component.BiomePrototype); + component.ModifiedTiles.TryGetValue(chunk, out var modified); + modified ??= new HashSet(); + + // Delete decals + foreach (var (dec, indices) in component.LoadedDecals[chunk]) + { + // If we couldn't remove it then flag the tile to never be touched. + if (!_decals.RemoveDecal(gridUid, dec)) + { + modified.Add(indices); + } + } + + component.LoadedDecals.Remove(chunk); + + // Delete entities + // This is a TODO + // Ideally any entities that aren't modified just get deleted and re-generated later + // This is because if we want to save the map (e.g. persistent server) it makes the file much smaller + // and also if the map is enormous will make stuff like physics broadphase much faster + // For now we'll just leave them because no entity diffs. + + component.LoadedEntities.Remove(chunk); + + // Unset tiles (if the data is custom) + + for (var x = 0; x < ChunkSize; x++) + { + for (var y = 0; y < ChunkSize; y++) + { + var indices = new Vector2i(x + chunk.X, y + chunk.Y); + + if (modified.Contains(indices)) + continue; + + // Don't mess with anything that's potentially anchored. + var anchored = grid.GetAnchoredEntitiesEnumerator(indices); + + if (anchored.MoveNext(out _)) + { + modified.Add(indices); + continue; + } + + // If it's default data unload the tile. + if (!TryGetBiomeTile(indices, prototype, noise, null, out var biomeTile) || + grid.TryGetTileRef(indices, out var tileRef) && tileRef.Tile != biomeTile.Value) + { + modified.Add(indices); + continue; + } + + tiles.Add((indices, Tile.Empty)); + } + } + + grid.SetTiles(tiles); + tiles.Clear(); + component.LoadedChunks.Remove(chunk); + + if (modified.Count == 0) + { + component.ModifiedTiles.Remove(chunk); + } + } +} diff --git a/Content.Shared/Maps/ContentTileDefinition.cs b/Content.Shared/Maps/ContentTileDefinition.cs index 53909006cc..8676d63e6e 100644 --- a/Content.Shared/Maps/ContentTileDefinition.cs +++ b/Content.Shared/Maps/ContentTileDefinition.cs @@ -31,6 +31,10 @@ namespace Content.Shared.Maps public string Name { get; private set; } = ""; [DataField("sprite")] public ResourcePath? Sprite { get; } + [DataField("cornerSprites")] public List CornerSprites { get; } = new(); + + [DataField("cardinalSprites")] public List CardinalSprites { get; } = new(); + [DataField("isSubfloor")] public bool IsSubFloor { get; private set; } [DataField("baseTurfs")] public List BaseTurfs { get; } = new(); diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 1239e45b33..55829dd8d4 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -17,6 +17,10 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; using System.Diagnostics.CodeAnalysis; using Content.Shared.Mobs.Systems; +using Content.Shared.Mech.Components; +using Content.Shared.Parallax.Biomes; +using Robust.Shared.Map.Components; +using Robust.Shared.Noise; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; @@ -32,6 +36,7 @@ namespace Content.Shared.Movement.Systems [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly SharedBiomeSystem _biome = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; @@ -449,7 +454,7 @@ namespace Content.Shared.Movement.Systems } if (_inventory.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) && - EntityManager.TryGetComponent(shoes, out var modifier)) + TryComp(shoes, out var modifier)) { sound = modifier.Sound; return true; @@ -461,10 +466,10 @@ namespace Content.Shared.Movement.Systems private bool TryGetFootstepSound(TransformComponent xform, bool haveShoes, [NotNullWhen(true)] out SoundSpecifier? sound) { sound = null; + MapGridComponent? grid; - // Fallback to the map - if (xform.MapUid == xform.GridUid || - xform.GridUid == null) + // Fallback to the map? + if (xform.GridUid == null) { if (TryComp(xform.MapUid, out var modifier)) { @@ -475,25 +480,30 @@ namespace Content.Shared.Movement.Systems return false; } - var grid = _mapManager.GetGrid(xform.GridUid.Value); - var tile = grid.GetTileRef(xform.Coordinates); - - if (tile.IsSpace(_tileDefinitionManager)) - return false; + grid = _mapManager.GetGrid(xform.GridUid.Value); + var position = grid.LocalToTile(xform.Coordinates); // If the coordinates have a FootstepModifier component // i.e. component that emit sound on footsteps emit that sound - foreach (var maybeFootstep in grid.GetAnchoredEntities(tile.GridIndices)) + var anchored = grid.GetAnchoredEntitiesEnumerator(position); + + while (anchored.MoveNext(out var maybeFootstep)) { - if (EntityManager.TryGetComponent(maybeFootstep, out FootstepModifierComponent? footstep)) + if (TryComp(maybeFootstep, out var footstep)) { sound = footstep.Sound; return true; } } + if (!grid.TryGetTileRef(position, out var tileRef)) + { + sound = null; + return false; + } + // Walking on a tile. - var def = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId]; + var def = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId]; sound = haveShoes ? def.FootstepSounds : def.BarestepSounds; return sound != null; } diff --git a/Content.Shared/Parallax/Biomes/BiomeComponent.cs b/Content.Shared/Parallax/Biomes/BiomeComponent.cs new file mode 100644 index 0000000000..1331cfa6a7 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/BiomeComponent.cs @@ -0,0 +1,39 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Parallax.Biomes; + +[RegisterComponent, NetworkedComponent] +public sealed class BiomeComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite), DataField("seed")] + public int Seed; + + [ViewVariables(VVAccess.ReadWrite), + DataField("prototype", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string BiomePrototype = "Grasslands"; + + // TODO: Need to flag tiles as not requiring custom data anymore, e.g. if we spawn an ent and don't unspawn it. + + /// + /// If we've already generated a tile and couldn't deload it then we won't ever reload it in future. + /// Stored by [Chunkorigin, Tiles] + /// + [DataField("modifiedTiles")] + public Dictionary> ModifiedTiles = new(); + + /// + /// Decals that have been loaded as a part of this biome. + /// + [DataField("decals")] + public Dictionary> LoadedDecals = new(); + + [DataField("entities")] + public Dictionary> LoadedEntities = new(); + + /// + /// Currently active chunks + /// + [ViewVariables] + public readonly HashSet LoadedChunks = new(); +} diff --git a/Content.Shared/Parallax/Biomes/BiomePrototype.cs b/Content.Shared/Parallax/Biomes/BiomePrototype.cs new file mode 100644 index 0000000000..731efbc1f1 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/BiomePrototype.cs @@ -0,0 +1,123 @@ +using Content.Shared.Decals; +using Content.Shared.Maps; +using Robust.Shared.Noise; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; +using Robust.Shared.Utility; + +namespace Content.Shared.Parallax.Biomes; + +[Prototype("biome")] +public sealed class BiomePrototype : IPrototype +{ + [IdDataField] public string ID { get; } = default!; + + [DataField("layers")] + public List Layers = new(); +} + +[ImplicitDataDefinitionForInheritors] +public interface IBiomeLayer +{ + /// + /// Threshold for this layer to be present. If set to 0 forces it for every tile. + /// + float Threshold { get; } + + /// + /// Offset the seed by the specified amount for this layer. + /// Useful if you have 2 similar layers but don't want them to match exactly. + /// + int SeedOffset { get; } + + /// + /// Frequency for noise: lower values create larger blobs. + /// + float Frequency { get; } +} + +public sealed class BiomeTileLayer : IBiomeLayer +{ + /// + [DataField("threshold")] + public float Threshold { get; } = 0.5f; + + /// + [DataField("seedOffset")] + public int SeedOffset { get; } = 0; + + /// + [DataField("frequency")] + public float Frequency { get; } = 0.1f; + + /// + /// Which tile variants to use for this layer. Uses all of the tile's variants if none specified + /// + [DataField("variants")] + public List? Variants = null; + + [DataField("tile", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string Tile = string.Empty; +} + +/// +/// Handles actual objects such as decals and entities. +/// +public interface IBiomeWorldLayer : IBiomeLayer +{ + /// + /// What tiles we're allowed to spawn on, real or biome. + /// + List AllowedTiles { get; } +} + +public sealed class BiomeDecalLayer : IBiomeWorldLayer +{ + /// + [DataField("allowedTiles", customTypeSerializer:typeof(PrototypeIdListSerializer))] + public List AllowedTiles { get; } = new(); + + /// + /// Divide each tile up by this amount. + /// + [DataField("divisions")] + public float Divisions = 1f; + + /// + [DataField("seedOffset")] + public int SeedOffset { get; } = 0; + + /// + [DataField("frequency")] + public float Frequency { get; } = 0.25f; + + /// + [DataField("threshold")] + public float Threshold { get; } = 0.8f; + + [DataField("decals", required: true, customTypeSerializer:typeof(PrototypeIdListSerializer))] + public List Decals = new(); +} + +public sealed class BiomeEntityLayer : IBiomeWorldLayer +{ + /// + [DataField("allowedTiles", customTypeSerializer:typeof(PrototypeIdListSerializer))] + public List AllowedTiles { get; } = new(); + + /// + [DataField("threshold")] + public float Threshold { get; } = 0.5f; + + /// + [DataField("seedOffset")] + public int SeedOffset { get; } = 0; + + /// + [DataField("frequency")] + public float Frequency { get; } = 0.1f; + + [DataField("entities", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List Entities = new(); +} diff --git a/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs b/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs new file mode 100644 index 0000000000..eefc772c17 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs @@ -0,0 +1,333 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Shared.Decals; +using Content.Shared.Maps; +using Robust.Shared.Console; +using Robust.Shared.GameStates; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Noise; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Parallax.Biomes; + +public abstract class SharedBiomeSystem : EntitySystem +{ + [Dependency] protected readonly IPrototypeManager ProtoManager = default!; + [Dependency] protected readonly ITileDefinitionManager TileDefManager = default!; + + protected const byte ChunkSize = 4; + + // TODO: After I wrote all of this FastNoiseLite got ported so this needs updating for that don't @ me + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnBiomeGetState); + SubscribeLocalEvent(OnBiomeHandleState); + } + + private void OnBiomeHandleState(EntityUid uid, BiomeComponent component, ref ComponentHandleState args) + { + if (args.Current is not BiomeComponentState state) + return; + + component.Seed = state.Seed; + } + + private void OnBiomeGetState(EntityUid uid, BiomeComponent component, ref ComponentGetState args) + { + args.State = new BiomeComponentState(component.Seed, component.BiomePrototype); + } + + protected T Pick(List collection, float value) + { + DebugTools.Assert(value is >= 0f and <= 1f); + + if (collection.Count == 1) + return collection[0]; + + value *= collection.Count; + + foreach (var item in collection) + { + value -= 1f; + + if (value <= 0f) + { + return item; + } + } + + throw new ArgumentOutOfRangeException(); + } + + protected int Pick(int count, float value) + { + DebugTools.Assert(value is >= 0f and <= 1f); + + if (count == 1) + return 0; + + value *= count; + + for (var i = 0; i < count; i++) + { + value -= 1f; + + if (value <= 0f) + { + return i; + } + } + + throw new ArgumentOutOfRangeException(); + } + + public bool TryGetBiomeTile(EntityUid uid, MapGridComponent grid, Vector2i indices, [NotNullWhen(true)] out Tile? tile) + { + if (grid.TryGetTileRef(indices, out var tileRef)) + { + tile = tileRef.Tile; + return true; + } + + if (!TryComp(uid, out var biome)) + { + tile = null; + return false; + } + + return TryGetBiomeTile(indices, ProtoManager.Index(biome.BiomePrototype), + new FastNoise(biome.Seed), grid, out tile); + } + + /// + /// Tries to get the tile, real or otherwise, for the specified indices. + /// + public bool TryGetBiomeTile(Vector2i indices, BiomePrototype prototype, FastNoise seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile) + { + if (grid?.TryGetTileRef(indices, out var tileRef) == true && !tileRef.Tile.IsEmpty) + { + tile = tileRef.Tile; + return true; + } + + var oldFrequency = seed.GetFrequency(); + + for (var i = prototype.Layers.Count - 1; i >= 0; i--) + { + var layer = prototype.Layers[i]; + + if (layer is not BiomeTileLayer tileLayer) + continue; + + seed.SetFrequency(tileLayer.Frequency); + + if (TryGetTile(indices, seed, tileLayer.Threshold, ProtoManager.Index(tileLayer.Tile), tileLayer.Variants, out tile)) + { + seed.SetFrequency(oldFrequency); + return true; + } + } + + seed.SetFrequency(oldFrequency); + tile = null; + return false; + } + + /// + /// Tries to get the relevant entity for this tile. + /// + protected bool TryGetEntity(Vector2i indices, BiomePrototype prototype, FastNoise noise, MapGridComponent grid, + [NotNullWhen(true)] out string? entity) + { + if (!TryGetBiomeTile(indices, prototype, noise, grid, out var tileRef)) + { + entity = null; + return false; + } + + var tileId = TileDefManager[tileRef.Value.TypeId].ID; + var oldFrequency = noise.GetFrequency(); + var seed = noise.GetSeed(); + + for (var i = prototype.Layers.Count - 1; i >= 0; i--) + { + var layer = prototype.Layers[i]; + var offset = 0; + + // Decals might block entity so need to check if there's one in front of us. + switch (layer) + { + case IBiomeWorldLayer worldLayer: + if (!worldLayer.AllowedTiles.Contains(tileId)) + continue; + + offset = worldLayer.SeedOffset; + noise.SetSeed(seed + offset); + noise.SetFrequency(worldLayer.Frequency); + break; + default: + continue; + } + + var value = (noise.GetCellular(indices.X, indices.Y) + 1f) / 2f; + + if (value < layer.Threshold) + { + DebugTools.Assert(value is <= 1f and >= 0f); + continue; + } + + if (layer is not BiomeEntityLayer biomeLayer) + { + entity = null; + noise.SetFrequency(oldFrequency); + noise.SetSeed(seed); + return false; + } + + entity = Pick(biomeLayer.Entities, (noise.GetSimplex(indices.X, indices.Y) + 1f) / 2f); + noise.SetFrequency(oldFrequency); + noise.SetSeed(seed); + return true; + } + + noise.SetFrequency(oldFrequency); + noise.SetSeed(seed); + entity = null; + return false; + } + + /// + /// Tries to get the relevant decals for this tile. + /// + public bool TryGetDecals(Vector2i indices, BiomePrototype prototype, FastNoise noise, MapGridComponent grid, + [NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals) + { + if (!TryGetBiomeTile(indices, prototype, noise, grid, out var tileRef)) + { + decals = null; + return false; + } + + var tileId = TileDefManager[tileRef.Value.TypeId].ID; + var oldFrequency = noise.GetFrequency(); + var seed = noise.GetSeed(); + + for (var i = prototype.Layers.Count - 1; i >= 0; i--) + { + var layer = prototype.Layers[i]; + var offset = 0; + + // Entities might block decal so need to check if there's one in front of us. + switch (layer) + { + case IBiomeWorldLayer worldLayer: + if (!worldLayer.AllowedTiles.Contains(tileId)) + continue; + + offset = worldLayer.SeedOffset; + noise.SetSeed(seed + offset); + noise.SetFrequency(worldLayer.Frequency); + break; + default: + continue; + } + + // Check if the other layer should even render, if not then keep going. + if (layer is not BiomeDecalLayer decalLayer) + { + if ((noise.GetCellular(indices.X, indices.Y) + 1f) / 2f < layer.Threshold) + continue; + + decals = null; + noise.SetFrequency(oldFrequency); + noise.SetSeed(seed); + return false; + } + + decals = new List<(string ID, Vector2 Position)>(); + + for (var x = 0; x < decalLayer.Divisions; x++) + { + for (var y = 0; y < decalLayer.Divisions; y++) + { + var index = new Vector2(indices.X + x * 1f / decalLayer.Divisions, indices.Y + y * 1f / decalLayer.Divisions); + var decalValue = (noise.GetCellular(index.X, index.Y) + 1f) / 2f; + + if (decalValue < decalLayer.Threshold) + continue; + + DebugTools.Assert(decalValue is <= 1f and >= 0f); + decals.Add((Pick(decalLayer.Decals, (noise.GetSimplex(index.X, index.Y) + 1f) / 2f), index)); + } + } + + noise.SetFrequency(oldFrequency); + noise.SetSeed(seed); + + // Check other layers + if (decals.Count == 0) + continue; + + return true; + } + + noise.SetFrequency(oldFrequency); + noise.SetSeed(seed); + decals = null; + return false; + } + + /// + /// Gets the underlying biome tile, ignoring any existing tile that may be there. + /// + public bool TryGetTile(Vector2i indices, FastNoise seed, float threshold, ContentTileDefinition tileDef, List? variants, [NotNullWhen(true)] out Tile? tile) + { + if (threshold > 0f) + { + var found = (seed.GetSimplexFractal(indices.X, indices.Y) + 1f) / 2f; + + if (found < threshold) + { + tile = null; + return false; + } + } + + byte variant = 0; + var variantCount = variants?.Count ?? tileDef.Variants; + + // Pick a variant tile if they're available as well + if (variantCount > 1) + { + var variantValue = (seed.GetSimplex(indices.X * 2f, indices.Y * 2f) + 1f) / 2f; + variant = (byte) Pick(variantCount, variantValue); + + if (variants != null) + { + variant = variants[variant]; + } + } + + tile = new Tile(tileDef.TileId, 0, variant); + return true; + } + + [Serializable, NetSerializable] + private sealed class BiomeComponentState : ComponentState + { + public int Seed; + public string Prototype; + + public BiomeComponentState(int seed, string prototype) + { + Seed = seed; + Prototype = prototype; + } + } +} diff --git a/Resources/Locale/en-US/maps/planet.ftl b/Resources/Locale/en-US/maps/planet.ftl index 0d51694500..75da034fd7 100644 --- a/Resources/Locale/en-US/maps/planet.ftl +++ b/Resources/Locale/en-US/maps/planet.ftl @@ -1,5 +1,5 @@ cmd-planet-desc = Converts the supplied map into a planet with sensible defaults. cmd-planet-help = {$command} . -cmd-planet-args = Require 1 arg only. +cmd-planet-args = Requires 2 args only. cmd-planet-map = Unable to parse {$map} as an existing map. cmd-planet-success = Set map {$mapId} to Planet. NOTE! You will need to load the map (either onto a new map or by restarting the game) for atmospherics to work. diff --git a/Resources/Locale/en-US/tiles/tiles.ftl b/Resources/Locale/en-US/tiles/tiles.ftl index 974db2ab6e..cc64145d3f 100644 --- a/Resources/Locale/en-US/tiles/tiles.ftl +++ b/Resources/Locale/en-US/tiles/tiles.ftl @@ -69,6 +69,7 @@ tiles-green-circuit-floor = green circuit floor tiles-blue-circuit-floor = blue circuit floor tiles-snow = snow tiles-grass-floor = grass floor +tiles-planet-grass-floor = grass floor tiles-jungle-grass-floor = jungle grass floor tiles-dark-grass-floor = dark grass floor tiles-light-grass-floor = light grass floor diff --git a/Resources/Prototypes/Decals/planet.yml b/Resources/Prototypes/Decals/planet.yml new file mode 100644 index 0000000000..91b3c3ccc6 --- /dev/null +++ b/Resources/Prototypes/Decals/planet.yml @@ -0,0 +1,73 @@ +# Flowers +- type: decal + id: FlowersBROne + sprite: + sprite: /Textures/Decals/Flora/flora_flowers.rsi + state: flowersbr1 + +- type: decal + id: FlowersBRTwo + sprite: + sprite: /Textures/Decals/Flora/flora_flowers.rsi + state: flowersbr2 + +- type: decal + id: FlowersBRThree + sprite: + sprite: /Textures/Decals/Flora/flora_flowers.rsi + state: flowersbr3 + +# Grass +- type: decal + id: BushAOne + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: busha1 + +- type: decal + id: BushATwo + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: busha2 + +- type: decal + id: BushAThree + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: busha3 + +- type: decal + id: BushCOne + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: bushc1 + +- type: decal + id: BushCTwo + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: bushc2 + +- type: decal + id: BushCThree + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: bushc3 + +- type: decal + id: BushDOne + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: bushd1 + +- type: decal + id: BushDTwo + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: bushd2 + +- type: decal + id: BushDThree + sprite: + sprite: /Textures/Decals/Flora/flora_bushes.rsi + state: bushd3 diff --git a/Resources/Prototypes/Entities/Tile/basalt.yml b/Resources/Prototypes/Entities/Tiles/basalt.yml similarity index 100% rename from Resources/Prototypes/Entities/Tile/basalt.yml rename to Resources/Prototypes/Entities/Tiles/basalt.yml diff --git a/Resources/Prototypes/Parallaxes/default.yml b/Resources/Prototypes/Parallaxes/default.yml index 26a0fe0b39..4a84b4618b 100644 --- a/Resources/Prototypes/Parallaxes/default.yml +++ b/Resources/Prototypes/Parallaxes/default.yml @@ -1,3 +1,6 @@ +- type: parallax + id: Blank + - type: parallax id: Default layers: diff --git a/Resources/Prototypes/Parallaxes/planet.yml b/Resources/Prototypes/Parallaxes/planet.yml index d18d32b063..3371dfd746 100644 --- a/Resources/Prototypes/Parallaxes/planet.yml +++ b/Resources/Prototypes/Parallaxes/planet.yml @@ -18,7 +18,7 @@ layers: - texture: !type:ImageParallaxTextureSource - path: "/Textures/Tiles/Planet/grass.rsi/grass0.png" + path: "/Textures/Tiles/Planet/Grass/grass.png" slowness: 0 scale: "1, 1" shader: "" diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index a23e9493a4..3aa770d10e 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -1103,26 +1103,12 @@ friction: 0.30 thermalConductivity: 0.04 heatCapacity: 10000 - -- type: tile - id: FloorSnow - name: tiles-snow - sprite: /Textures/Tiles/snow.png - baseTurfs: - - FloorDirt - isSubfloor: true - canCrowbar: false - footstepSounds: - collection: FootstepSnow - friction: 0.20 - itemDrop: FloorTileItemSnow - thermalConductivity: 0.04 - heatCapacity: 10000 weather: true + - type: tile id: FloorGrass - name: tiles-grass-floor + name: tiles-planet-grass-floor sprite: /Textures/Tiles/grass.png baseTurfs: - FloorDirt diff --git a/Resources/Prototypes/Tiles/planet.yml b/Resources/Prototypes/Tiles/planet.yml new file mode 100644 index 0000000000..c67c7994d4 --- /dev/null +++ b/Resources/Prototypes/Tiles/planet.yml @@ -0,0 +1,89 @@ +# Desert +- type: tile + id: FloorDesert + name: tiles-desert-floor + sprite: /Textures/Tiles/Planet/Desert/desert.png + variants: 6 + placementVariants: [0, 1, 2, 3, 4, 5] + isSubfloor: true + canCrowbar: false + footstepSounds: + collection: FootstepAsteroid + friction: 0.30 + thermalConductivity: 0.04 + heatCapacity: 10000 + weather: true + +- type: tile + id: FloorLowDesert + name: tiles-low-desert-floor + sprite: /Textures/Tiles/Planet/Desert/low_desert.png + variants: 6 + placementVariants: [0, 1, 2, 3, 4, 5] + isSubfloor: true + canCrowbar: false + footstepSounds: + collection: FootstepAsteroid + friction: 0.30 + thermalConductivity: 0.04 + heatCapacity: 10000 + weather: true + +# Grass +- type: tile + id: FloorPlanetGrass + name: tiles-grass-planet-floor + sprite: /Textures/Tiles/Planet/Grass/grass.png + variants: 4 + placementVariants: [0, 1, 2, 3] + cornerSprites: + - /Textures/Tiles/Planet/Grass/single_edge.png + cardinalSprites: + - /Textures/Tiles/Planet/Grass/double_edge.png + baseTurfs: + - FloorDirt + isSubfloor: true + canCrowbar: false + footstepSounds: + collection: FootstepGrass + friction: 0.30 + itemDrop: FloorTileItemGrass + thermalConductivity: 0.04 + heatCapacity: 10000 + weather: true + +# Lava +- type: tile + id: FloorBasalt + name: tiles-basalt-floor + sprite: /Textures/Tiles/Planet/basalt.png + isSubfloor: true + canCrowbar: false + footstepSounds: + collection: FootstepAsteroid + friction: 0.30 + thermalConductivity: 0.04 + heatCapacity: 10000 + weather: true + +# Snow +- type: tile + id: FloorSnow + name: tiles-snow-floor + sprite: /Textures/Tiles/Planet/Snow/snow.png + variants: 13 + placementVariants: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + #cornerSprites: + # - /Textures/Tiles/Planet/Snow/single_edge.png + #cardinalSprites: + # - /Textures/Tiles/Planet/Snow/double_edge.png + isSubfloor: true + canCrowbar: false + footstepSounds: + collection: FootstepSnow + friction: 0.20 + thermalConductivity: 0.04 + heatCapacity: 10000 + weather: true + +# Wasteland diff --git a/Resources/Prototypes/biomes.yml b/Resources/Prototypes/biomes.yml new file mode 100644 index 0000000000..59b1c3acae --- /dev/null +++ b/Resources/Prototypes/biomes.yml @@ -0,0 +1,240 @@ +# Desert +# TODO: Water in grasslands +# TODO: Water in desert / grass? +- type: biome + id: LowDesert + layers: + - !type:BiomeEntityLayer + threshold: 0.99 + frequency: 1 + seedOffset: 1 + allowedTiles: + - FloorLowDesert + entities: + - FloraRockSolid01 + - FloraRockSolid02 + - FloraRockSolid03 + - !type:BiomeEntityLayer + threshold: 0.9 + frequency: 0.2 + allowedTiles: + - FloorLowDesert + entities: + - AsteroidRock + - !type:BiomeTileLayer + threshold: 0 + variants: + - 0 + tile: FloorLowDesert + - !type:BiomeTileLayer + threshold: 0.6 + tile: FloorLowDesert + frequency: 0.1 + +# Grass +- type: biome + id: Grasslands + layers: + - !type:BiomeDecalLayer + allowedTiles: + - FloorPlanetGrass + seedOffset: 3 + threshold: 0.98 + divisions: 1 + frequency: 1 + decals: + - FlowersBROne + - FlowersBRTwo + - FlowersBRThree + - !type:BiomeDecalLayer + allowedTiles: + - FloorPlanetGrass + seedOffset: 2 + threshold: 0.95 + divisions: 2 + frequency: 1 + decals: + - BushDOne + - BushDTwo + - BushDThree + - !type:BiomeDecalLayer + allowedTiles: + - FloorPlanetGrass + seedOffset: 1 + threshold: 0.8 + divisions: 1 + frequency: 0.05 + decals: + - BushCOne + - BushCTwo + - BushCThree + - !type:BiomeDecalLayer + allowedTiles: + - FloorPlanetGrass + divisions: 1 + decals: + - BushAOne + - BushATwo + - BushAThree + - !type:BiomeEntityLayer + threshold: 0.9 + frequency: 1 + allowedTiles: + - FloorPlanetGrass + entities: + - FloraTree01 + - FloraTree02 + - FloraTree03 + - FloraTree04 + - FloraTree05 + - FloraTree06 + - FloraTreeLarge01 + - FloraTreeLarge02 + - FloraTreeLarge03 + - FloraTreeLarge04 + - FloraTreeLarge05 + - FloraTreeLarge06 + # Fill remainder with sand. + - !type:BiomeTileLayer + threshold: 0 + tile: FloorAsteroidSand + - !type:BiomeTileLayer + threshold: 0.5 + tile: FloorPlanetGrass + +# Lava +- type: biome + id: Lava + layers: + - !type:BiomeEntityLayer + threshold: 0.9 + frequency: 1 + seedOffset: 3 + allowedTiles: + - FloorBasalt + entities: + - BasaltOne + - BasaltTwo + - BasaltThree + - BasaltFour + - BasaltFive + - !type:BiomeDecalLayer + allowedTiles: + - FloorBasalt + seedOffset: 2 + threshold: 0.9 + divisions: 1 + frequency: 1 + decals: + - Basalt1 + - Basalt2 + - Basalt3 + - Basalt4 + - Basalt5 + - Basalt6 + - Basalt7 + - Basalt8 + - Basalt9 + - !type:BiomeEntityLayer + threshold: 0.99 + frequency: 1 + seedOffset: 1 + allowedTiles: + - FloorBasalt + entities: + - FloraRockSolid01 + - FloraRockSolid02 + - FloraRockSolid03 + - !type:BiomeEntityLayer + threshold: 0.7 + frequency: 0.2 + allowedTiles: + - FloorBasalt + entities: + - FloorLavaEntity + - !type:BiomeTileLayer + threshold: 0 + variants: + - 0 + tile: FloorBasalt + +# Snow +- type: biome + id: Snow + layers: + - !type:BiomeDecalLayer + allowedTiles: + - FloorSnow + seedOffset: 4 + threshold: 0.95 + divisions: 1 + frequency: 1 + decals: + - grasssnowa1 + - grasssnowa2 + - grasssnowa3 + - grasssnowb1 + - grasssnowb2 + - grasssnowb3 + - grasssnowc1 + - grasssnowc2 + - grasssnowc3 + # The main grass texture, this one blends in very well + - !type:BiomeDecalLayer + allowedTiles: + - FloorSnow + divisions: 1 + seedOffset: 3 + threshold: 0.8 + frequency: 0.05 + decals: + - grasssnow + - grasssnow01 + - grasssnow02 + - grasssnow03 + - grasssnow04 + - grasssnow05 + - grasssnow06 + - grasssnow07 + - grasssnow08 + - grasssnow09 + - grasssnow10 + - grasssnow11 + - grasssnow12 + - grasssnow13 + - !type:BiomeDecalLayer + allowedTiles: + - FloorSnow + seedOffset: 2 + threshold: 0.99 + divisions: 1 + frequency: 1 + decals: + - bushsnowa1 + - bushsnowa2 + - bushsnowa3 + - bushsnowb1 + - bushsnowb2 + - bushsnowb3 + - !type:BiomeEntityLayer + seedOffset: 1 + threshold: 0.95 + frequency: 1 + allowedTiles: + - FloorSnow + entities: + - FloraTreeSnow01 + - FloraTreeSnow02 + - FloraTreeSnow03 + - FloraTreeSnow04 + - FloraTreeSnow05 + - FloraTreeSnow06 + - !type:BiomeTileLayer + threshold: 0 + variants: + - 0 + tile: FloorSnow + - !type:BiomeTileLayer + threshold: 0.6 + tile: FloorSnow + frequency: 0.1 diff --git a/Resources/Textures/Tiles/Asteroid/asteroid_dug.png b/Resources/Textures/Tiles/Asteroid/asteroid_dug.png new file mode 100644 index 0000000000..b4e392f082 Binary files /dev/null and b/Resources/Textures/Tiles/Asteroid/asteroid_dug.png differ diff --git a/Resources/Textures/Tiles/Planet/Desert/desert.png b/Resources/Textures/Tiles/Planet/Desert/desert.png new file mode 100644 index 0000000000..274ceca95d Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Desert/desert.png differ diff --git a/Resources/Textures/Tiles/Planet/Desert/desert_dug.png b/Resources/Textures/Tiles/Planet/Desert/desert_dug.png new file mode 100644 index 0000000000..55bf6904c9 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Desert/desert_dug.png differ diff --git a/Resources/Textures/Tiles/Planet/Desert/low_desert.png b/Resources/Textures/Tiles/Planet/Desert/low_desert.png new file mode 100644 index 0000000000..885902d341 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Desert/low_desert.png differ diff --git a/Resources/Textures/Tiles/Planet/Desert/low_desert_dug.png b/Resources/Textures/Tiles/Planet/Desert/low_desert_dug.png new file mode 100644 index 0000000000..e82207e688 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Desert/low_desert_dug.png differ diff --git a/Resources/Textures/Tiles/Planet/Desert/meta.json b/Resources/Textures/Tiles/Planet/Desert/meta.json new file mode 100644 index 0000000000..8ee5d0acdc --- /dev/null +++ b/Resources/Textures/Tiles/Planet/Desert/meta.json @@ -0,0 +1,53 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/discordia-space/CEV-Eris/tree/d1b3041899a42ef1fb59cd7ad4a83a300b35638c", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "desert" + }, + { + "name": "desert_dug" + }, + { + "name": "desert0" + }, + { + "name": "desert1" + }, + { + "name": "desert2" + }, + { + "name": "desert3" + }, + { + "name": "desert4" + }, + { + "name": "lowdesert" + }, + { + "name": "lowdesert_dug" + }, + { + "name": "lowdesert0" + }, + { + "name": "lowdesert1" + }, + { + "name": "lowdesert2" + }, + { + "name": "lowdesert3" + }, + { + "name": "lowdesert4" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Tiles/Planet/Grass/attributions.yml b/Resources/Textures/Tiles/Planet/Grass/attributions.yml new file mode 100644 index 0000000000..3cb803a9ab --- /dev/null +++ b/Resources/Textures/Tiles/Planet/Grass/attributions.yml @@ -0,0 +1,7 @@ +- files: + - grass.png + - double_edge.png + - single_edge.png + license: "CC-BY-SA-3.0" + copyright: "https://github.com/discordia-space/CEV-Eris/commit/026ee3250ac1de938b503e3eb46ad73dd9c3ca82" + source: "https://github.com/discordia-space/CEV-Eris/commit/026ee3250ac1de938b503e3eb46ad73dd9c3ca82" \ No newline at end of file diff --git a/Resources/Textures/Tiles/Planet/Grass/double_edge.png b/Resources/Textures/Tiles/Planet/Grass/double_edge.png new file mode 100644 index 0000000000..b41e55af7c Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Grass/double_edge.png differ diff --git a/Resources/Textures/Tiles/Planet/Grass/grass.png b/Resources/Textures/Tiles/Planet/Grass/grass.png new file mode 100644 index 0000000000..006a76e64e Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Grass/grass.png differ diff --git a/Resources/Textures/Tiles/Planet/Grass/single_edge.png b/Resources/Textures/Tiles/Planet/Grass/single_edge.png new file mode 100644 index 0000000000..437c090770 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Grass/single_edge.png differ diff --git a/Resources/Textures/Tiles/Planet/Grass/triple_edge.png b/Resources/Textures/Tiles/Planet/Grass/triple_edge.png new file mode 100644 index 0000000000..3fd2a39a8f Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Grass/triple_edge.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/edge0.png b/Resources/Textures/Tiles/Planet/Snow/edge0.png new file mode 100644 index 0000000000..a998a72873 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/edge0.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/edge1.png b/Resources/Textures/Tiles/Planet/Snow/edge1.png new file mode 100644 index 0000000000..04f6f7222a Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/edge1.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/edge2.png b/Resources/Textures/Tiles/Planet/Snow/edge2.png new file mode 100644 index 0000000000..579cecc73f Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/edge2.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/gravsnow.png b/Resources/Textures/Tiles/Planet/Snow/gravsnow.png new file mode 100644 index 0000000000..c5d077ef6a Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/gravsnow.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/gravsnow_corner.png b/Resources/Textures/Tiles/Planet/Snow/gravsnow_corner.png new file mode 100644 index 0000000000..d157293bde Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/gravsnow_corner.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/gravsnow_surround.png b/Resources/Textures/Tiles/Planet/Snow/gravsnow_surround.png new file mode 100644 index 0000000000..0745067fe1 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/gravsnow_surround.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/ice.png b/Resources/Textures/Tiles/Planet/Snow/ice.png new file mode 100644 index 0000000000..0f460febe8 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/ice.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/meta.json b/Resources/Textures/Tiles/Planet/Snow/meta.json new file mode 100644 index 0000000000..85b770fa38 --- /dev/null +++ b/Resources/Textures/Tiles/Planet/Snow/meta.json @@ -0,0 +1,101 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "snow" + }, + { + "name": "snow_corner", + "directions": 8 + }, + { + "name": "snow_surround", + "directions": 4 + }, + { + "name": "gravsnow" + }, + { + "name": "gravsnow_corner", + "directions": 8 + }, + { + "name": "gravsnow_surround", + "directions": 4 + }, + { + "name": "plating" + }, + { + "name": "platingdrift", + "directions": 4 + }, + { + "name": "ice" + }, + { + "name": "snowwhite" + }, + { + "name": "snow0" + }, + { + "name": "snow1" + }, + { + "name": "snow2" + }, + { + "name": "snow3" + }, + { + "name": "snow4" + }, + { + "name": "snow5" + }, + { + "name": "snow6" + }, + { + "name": "snow7" + }, + { + "name": "snow8" + }, + { + "name": "snow9" + }, + { + "name": "snow10" + }, + { + "name": "snow11" + }, + { + "name": "snow12" + }, + { + "name": "snowplating" + }, + { + "name": "permafrost" + }, + { + "name": "edge0", + "directions": 4 + }, + { + "name": "edge1", + "directions": 4 + }, + { + "name": "edge2", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Tiles/Planet/Snow/permafrost.png b/Resources/Textures/Tiles/Planet/Snow/permafrost.png new file mode 100644 index 0000000000..cd5849936e Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/permafrost.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/plating.png b/Resources/Textures/Tiles/Planet/Snow/plating.png new file mode 100644 index 0000000000..3713f8be78 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/plating.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/platingdrift.png b/Resources/Textures/Tiles/Planet/Snow/platingdrift.png new file mode 100644 index 0000000000..8b85de8844 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/platingdrift.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/snow.png b/Resources/Textures/Tiles/Planet/Snow/snow.png new file mode 100644 index 0000000000..0078b0dbd7 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/snow.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/snow_corner.png b/Resources/Textures/Tiles/Planet/Snow/snow_corner.png new file mode 100644 index 0000000000..9798cdfb4a Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/snow_corner.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/snow_piss.png b/Resources/Textures/Tiles/Planet/Snow/snow_piss.png new file mode 100644 index 0000000000..7c57e327ca Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/snow_piss.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/snow_surround.png b/Resources/Textures/Tiles/Planet/Snow/snow_surround.png new file mode 100644 index 0000000000..52289ed92f Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/snow_surround.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/snowplating.png b/Resources/Textures/Tiles/Planet/Snow/snowplating.png new file mode 100644 index 0000000000..0dc0e48fed Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/snowplating.png differ diff --git a/Resources/Textures/Tiles/Planet/Snow/snowwhite.png b/Resources/Textures/Tiles/Planet/Snow/snowwhite.png new file mode 100644 index 0000000000..1a44d006f7 Binary files /dev/null and b/Resources/Textures/Tiles/Planet/Snow/snowwhite.png differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/grass0.png b/Resources/Textures/Tiles/Planet/grass.rsi/grass0.png deleted file mode 100644 index 8868223f7f..0000000000 Binary files a/Resources/Textures/Tiles/Planet/grass.rsi/grass0.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/grass1.png b/Resources/Textures/Tiles/Planet/grass.rsi/grass1.png deleted file mode 100644 index 9598fcb9a4..0000000000 Binary files a/Resources/Textures/Tiles/Planet/grass.rsi/grass1.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/grass2.png b/Resources/Textures/Tiles/Planet/grass.rsi/grass2.png deleted file mode 100644 index 1d21d0234f..0000000000 Binary files a/Resources/Textures/Tiles/Planet/grass.rsi/grass2.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/grass3.png b/Resources/Textures/Tiles/Planet/grass.rsi/grass3.png deleted file mode 100644 index deeddb3fef..0000000000 Binary files a/Resources/Textures/Tiles/Planet/grass.rsi/grass3.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/grass_corners.png b/Resources/Textures/Tiles/Planet/grass.rsi/grass_corners.png deleted file mode 100644 index a1f647264c..0000000000 Binary files a/Resources/Textures/Tiles/Planet/grass.rsi/grass_corners.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/grass_edge_corner.png b/Resources/Textures/Tiles/Planet/grass.rsi/grass_edge_corner.png deleted file mode 100644 index 7f6508e0e4..0000000000 Binary files a/Resources/Textures/Tiles/Planet/grass.rsi/grass_edge_corner.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/grass_edges.png b/Resources/Textures/Tiles/Planet/grass.rsi/grass_edges.png deleted file mode 100644 index a11f225e7a..0000000000 Binary files a/Resources/Textures/Tiles/Planet/grass.rsi/grass_edges.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/grass_edges_old.png b/Resources/Textures/Tiles/Planet/grass.rsi/grass_edges_old.png deleted file mode 100644 index af73ea013e..0000000000 Binary files a/Resources/Textures/Tiles/Planet/grass.rsi/grass_edges_old.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Planet/grass.rsi/meta.json b/Resources/Textures/Tiles/Planet/grass.rsi/meta.json deleted file mode 100644 index c238d25175..0000000000 --- a/Resources/Textures/Tiles/Planet/grass.rsi/meta.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/discordia-space/CEV-Eris/commit/026ee3250ac1de938b503e3eb46ad73dd9c3ca82", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "grass0" - }, - { - "name": "grass1" - }, - { - "name": "grass2" - }, - { - "name": "grass3" - }, - { - "name": "grass_edges", - "directions": 8 - }, - { - "name": "grass_edges_old", - "directions": 8 - }, - { - "name": "grass_corners", - "directions": 8 - }, - { - "name": "grass_edge_corner", - "directions": 8 - } - ] -} \ No newline at end of file