diff --git a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs index 977002168c..7156ab6da4 100644 --- a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs @@ -40,13 +40,13 @@ namespace Content.IntegrationTests.Tests var mapGrid = mapManager.CreateGrid(mapId); var mapGridEnt = mapGrid.GridEntityId; sEntities.GetComponent(mapGridEnt).WorldPosition = new Vector2(10, 10); - mapGrid.SetTile(new Vector2i(0,0), new Tile(1, 512)); + mapGrid.SetTile(new Vector2i(0,0), new Tile(1, (TileRenderFlag)1, 255)); } { var mapGrid = mapManager.CreateGrid(mapId); var mapGridEnt = mapGrid.GridEntityId; sEntities.GetComponent(mapGridEnt).WorldPosition = new Vector2(-8, -8); - mapGrid.SetTile(new Vector2i(0, 0), new Tile(2, 511)); + mapGrid.SetTile(new Vector2i(0, 0), new Tile(2, (TileRenderFlag)1, 254)); } mapLoader.SaveMap(mapId, mapPath); @@ -68,14 +68,14 @@ namespace Content.IntegrationTests.Tests Assert.That(mapGrid.WorldPosition, Is.EqualTo(new Vector2(10, 10))); - Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(1, 512))); + Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(1, (TileRenderFlag)1, 255))); } { if (!mapManager.TryFindGridAt(new MapId(10), new Vector2(-8, -8), out var mapGrid)) Assert.Fail(); Assert.That(mapGrid.WorldPosition, Is.EqualTo(new Vector2(-8, -8))); - Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(2, 511))); + Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(2, (TileRenderFlag)1, 254))); } }); diff --git a/Content.MapRenderer/Painters/TilePainter.cs b/Content.MapRenderer/Painters/TilePainter.cs index e7a35017c0..19acd467fa 100644 --- a/Content.MapRenderer/Painters/TilePainter.cs +++ b/Content.MapRenderer/Painters/TilePainter.cs @@ -44,7 +44,7 @@ namespace Content.MapRenderer.Painters var x = (int) (tile.X + xOffset); var y = (int) (tile.Y + yOffset); var sprite = _sTileDefinitionManager[tile.Tile.TypeId].SpriteName; - var image = images[sprite]; + var image = images[sprite][tile.Tile.Variant]; gridCanvas.Mutate(o => o.DrawImage(image, new Point(x * tileSize, y * tileSize), 1)); @@ -54,7 +54,7 @@ namespace Content.MapRenderer.Painters Console.WriteLine($"{nameof(TilePainter)} painted {i} tiles on grid {grid.Index} in {(int) stopwatch.Elapsed.TotalMilliseconds} ms"); } - private Dictionary GetTileImages( + private Dictionary> GetTileImages( ITileDefinitionManager tileDefinitionManager, IResourceCache resourceCache, int tileSize) @@ -62,11 +62,12 @@ namespace Content.MapRenderer.Painters var stopwatch = new Stopwatch(); stopwatch.Start(); - var images = new Dictionary(); + var images = new Dictionary>(); foreach (var definition in tileDefinitionManager) { var sprite = definition.SpriteName; + images[sprite] = new List(definition.Variants); if (string.IsNullOrEmpty(sprite)) { @@ -74,14 +75,18 @@ namespace Content.MapRenderer.Painters } using var stream = resourceCache.ContentFileRead($"{TilesPath}{sprite}.png"); - Image tileImage = Image.Load(stream); + Image tileSheet = Image.Load(stream); - if (tileImage.Width != tileSize || tileImage.Height != tileSize) + if (tileSheet.Width != tileSize * definition.Variants || tileSheet.Height != tileSize) { throw new NotSupportedException($"Unable to use tiles with a dimension other than {tileSize}x{tileSize}."); } - images[sprite] = tileImage; + for (var i = 0; i < definition.Variants; i++) + { + var tileImage = tileSheet.Clone(o => o.Crop(new Rectangle(tileSize * i, 0, 32, 32))); + images[sprite].Add(tileImage); + } } Console.WriteLine($"Indexed all tile images in {(int) stopwatch.Elapsed.TotalMilliseconds} ms"); diff --git a/Content.Server/Administration/Commands/VariantizeCommand.cs b/Content.Server/Administration/Commands/VariantizeCommand.cs new file mode 100644 index 0000000000..ed96906ed9 --- /dev/null +++ b/Content.Server/Administration/Commands/VariantizeCommand.cs @@ -0,0 +1,49 @@ +using Content.Shared.Administration; +using Content.Shared.Maps; +using Robust.Shared.Console; +using Robust.Shared.Map; +using Robust.Shared.Random; + +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Mapping)] +public sealed class VariantizeCommand : IConsoleCommand +{ + + public string Command => "variantize"; + + public string Description => Loc.GetString("variantize-command-description"); + + public string Help => Loc.GetString("variantize-command-help-text"); + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number")); + return; + } + + var mapManager = IoCManager.Resolve(); + var random = IoCManager.Resolve(); + + if (!int.TryParse(args[0], out var targetId)) + { + shell.WriteError(Loc.GetString("shell-argument-must-be-number")); + return; + } + + var gridId = new GridId(targetId); + if (!mapManager.TryGetGrid(gridId, out var grid)) + { + shell.WriteError(Loc.GetString("shell-invalid-grid-id")); + return; + } + foreach (var tile in grid.GetAllTiles()) + { + var def = tile.GetContentTileDefinition(); + var newTile = new Tile(tile.Tile.TypeId, tile.Tile.Flags, random.Pick(def.PlacementVariants)); + grid.SetTile(tile.GridIndices, newTile); + } + } +} diff --git a/Content.Server/Tiles/FloorTileItemComponent.cs b/Content.Server/Tiles/FloorTileItemComponent.cs index 4370593110..a943b0fba4 100644 --- a/Content.Server/Tiles/FloorTileItemComponent.cs +++ b/Content.Server/Tiles/FloorTileItemComponent.cs @@ -12,6 +12,7 @@ using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Player; +using Robust.Shared.Random; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; @@ -23,6 +24,7 @@ namespace Content.Server.Tiles { [Dependency] private readonly IEntityManager _entMan = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; [DataField("outputs", customTypeSerializer: typeof(PrototypeIdListSerializer))] private List? _outputTiles; @@ -50,7 +52,8 @@ namespace Content.Server.Tiles private void PlaceAt(IMapGrid mapGrid, EntityCoordinates location, ushort tileId, float offset = 0) { - mapGrid.SetTile(location.Offset(new Vector2(offset, offset)), new Tile(tileId)); + var variant = _random.Pick(((ContentTileDefinition) _tileDefinitionManager[tileId]).PlacementVariants); + mapGrid.SetTile(location.Offset(new Vector2(offset, offset)), new Tile(tileId, 0, variant)); SoundSystem.Play(Filter.Pvs(location), _placeTileSound.GetSound(), location, AudioHelpers.WithVariation(0.125f)); } diff --git a/Content.Shared/Maps/ContentTileDefinition.cs b/Content.Shared/Maps/ContentTileDefinition.cs index 517fc6c94a..cb0557aa43 100644 --- a/Content.Shared/Maps/ContentTileDefinition.cs +++ b/Content.Shared/Maps/ContentTileDefinition.cs @@ -40,6 +40,13 @@ namespace Content.Shared.Maps [DataField("friction")] public float Friction { get; set; } + [DataField("variants")] public byte Variants { get; set; } = 1; + + /// + /// This controls what variants the `variantize` command is allowed to use. + /// + [DataField("placementVariants")] public byte[] PlacementVariants { get; set; } = new byte[1] { 0 }; + [DataField("thermalConductivity")] public float ThermalConductivity { get; set; } = 0.05f; // Heat capacity is opt-in, not opt-out. diff --git a/Resources/Locale/en-US/administration/commands/variantize-command.ftl b/Resources/Locale/en-US/administration/commands/variantize-command.ftl new file mode 100644 index 0000000000..6160a86494 --- /dev/null +++ b/Resources/Locale/en-US/administration/commands/variantize-command.ftl @@ -0,0 +1,2 @@ +variantize-command-description = Randomizes all tile variants on a given grid. +variantize-command-help-text = variantize diff --git a/Resources/Locale/en-US/shell.ftl b/Resources/Locale/en-US/shell.ftl index 0c6cbcabf4..b912b9dc1b 100644 --- a/Resources/Locale/en-US/shell.ftl +++ b/Resources/Locale/en-US/shell.ftl @@ -21,6 +21,7 @@ shell-need-between-arguments = Need {$lower} to {$upper} arguments! shell-entity-is-not-mob = Target entity is not a mob! shell-invalid-entity-id = Invalid entity ID. +shell-invalid-grid-id = Invalid grid ID. shell-invalid-entity-uid = {$uid} is not a valid entity uid shell-entity-uid-must-be-number = EntityUid must be a number. shell-could-not-find-entity = Could not find entity {$entity} diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index 0a4c777a64..9841145209 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -2,6 +2,8 @@ id: floor_steel name: steel floor texture: "steel" + variants: 4 + placementVariants: [0, 1, 2, 3] base_turfs: - plating is_subfloor: false @@ -17,6 +19,8 @@ id: floor_wood name: wood texture: "wood" + variants: 4 + placementVariants: [0, 1, 2, 3] base_turfs: - plating is_subfloor: false @@ -32,6 +36,8 @@ id: floor_white name: white floor texture: "white" + variants: 4 + placementVariants: [0, 1, 2, 3] base_turfs: - plating is_subfloor: false @@ -47,6 +53,8 @@ id: floor_dark name: dark floor texture: "dark" + variants: 4 + placementVariants: [ 0, 1, 2, 3 ] base_turfs: - plating is_subfloor: false @@ -228,6 +236,8 @@ id: floor_bar name: bar floor texture: "bar" + variants: 4 + placementVariants: [ 0, 1, 2, 3 ] base_turfs: - plating is_subfloor: false @@ -298,7 +308,7 @@ item_drop: FloorTileItemLaundry thermalConductivity: 0.04 heatCapacity: 10000 - + # Carpets (non smoothing) - type: tile id: FloorArcadeBlue @@ -314,7 +324,7 @@ item_drop: FloorTileItemArcadeBlue thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorArcadeRed name: red arcade floor @@ -329,7 +339,7 @@ item_drop: FloorTileItemArcadeRed thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorEighties name: eighties floor @@ -360,7 +370,7 @@ item_drop: FloorTileItemShuttleWhite thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorShuttleBlue name: blue shuttle floor @@ -375,7 +385,7 @@ item_drop: FloorTileItemShuttleBlue thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorShuttleOrange name: orange shuttle floor @@ -390,7 +400,7 @@ item_drop: FloorTileItemShuttleOrange thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorShuttlePurple name: purple shuttle floor @@ -544,7 +554,7 @@ item_drop: FloorTileItemGrass thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorGrassJungle name: jungle grass floor @@ -591,8 +601,10 @@ - type: tile id: floor_asteroid_coarse_sand0 - name: asteroid coarse sand 0 - texture: Asteroid/asteroid_coarse_sand0 + name: asteroid coarse sand + texture: Asteroid/asteroid_coarse_sand + variants: 3 + placementVariants: [ 0, 1, 2 ] base_turfs: - space is_subfloor: false @@ -605,8 +617,10 @@ - type: tile id: floor_asteroid_coarse_sand1 - name: asteroid coarse sand 1 - texture: Asteroid/asteroid_coarse_sand1 + name: asteroid coarse sand [DO NOT USE] + texture: Asteroid/asteroid_coarse_sand + variants: 3 + placementVariants: [ 0, 1, 2 ] base_turfs: - space is_subfloor: false @@ -619,8 +633,10 @@ - type: tile id: floor_asteroid_coarse_sand2 - name: asteroid coarse sand 2 - texture: Asteroid/asteroid_coarse_sand2 + name: asteroid coarse sand [DO NOT USE] + texture: Asteroid/asteroid_coarse_sand + variants: 3 + placementVariants: [ 0, 1, 2 ] base_turfs: - space is_subfloor: false @@ -644,7 +660,7 @@ friction: 0.30 thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorAsteroidIronsand1 name: asteroid ironsand @@ -658,7 +674,7 @@ friction: 0.30 thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorAsteroidIronsand2 name: asteroid ironsand pebbles @@ -686,7 +702,7 @@ friction: 0.30 thermalConductivity: 0.04 heatCapacity: 10000 - + - type: tile id: FloorAsteroidIronsand4 name: asteroid ironsand rock diff --git a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand.png b/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand.png new file mode 100644 index 0000000000..4e9a96e2c2 Binary files /dev/null and b/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand.png differ diff --git a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand0.png b/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand0.png deleted file mode 100644 index 4710843571..0000000000 Binary files a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand0.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand1.png b/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand1.png deleted file mode 100644 index 8363126546..0000000000 Binary files a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand1.png and /dev/null differ diff --git a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand2.png b/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand2.png deleted file mode 100644 index bcb794d539..0000000000 Binary files a/Resources/Textures/Tiles/Asteroid/asteroid_coarse_sand2.png and /dev/null differ diff --git a/Resources/Textures/Tiles/bar.png b/Resources/Textures/Tiles/bar.png index d5d6f1e1ea..fa2502a875 100644 Binary files a/Resources/Textures/Tiles/bar.png and b/Resources/Textures/Tiles/bar.png differ diff --git a/Resources/Textures/Tiles/dark.png b/Resources/Textures/Tiles/dark.png index 1d44cc4119..1db2454daa 100644 Binary files a/Resources/Textures/Tiles/dark.png and b/Resources/Textures/Tiles/dark.png differ diff --git a/Resources/Textures/Tiles/steel.png b/Resources/Textures/Tiles/steel.png index 33572086b4..e2485a43d9 100644 Binary files a/Resources/Textures/Tiles/steel.png and b/Resources/Textures/Tiles/steel.png differ diff --git a/Resources/Textures/Tiles/white.png b/Resources/Textures/Tiles/white.png index 8645f5e704..d716369869 100644 Binary files a/Resources/Textures/Tiles/white.png and b/Resources/Textures/Tiles/white.png differ diff --git a/Resources/Textures/Tiles/wood.png b/Resources/Textures/Tiles/wood.png index e6be307097..1b4a318903 100644 Binary files a/Resources/Textures/Tiles/wood.png and b/Resources/Textures/Tiles/wood.png differ