public static MapData Create(int seed, TextureQueue textureQueue) { GC.KeepAlive(textureQueue); // SHUT UP ABOUT UNUSED PARAMETER!! var random = new Random(seed); CellBoard geometryBoard = GenerateGeometryBoard(random); (ConnectedArea playableSpace, Size boardSize) = geometryBoard.TrimToLargestDeadConnectedArea(); var internalDistances = playableSpace.DetermineInteriorEdgeDistance(Neighborhood.Moore); var sideDefCache = new ModelSequence <SideDef, SideDef>(s => s); // Seems silly, but what should be abstracted about it? var vertexCache = new ModelSequence <LatticePoint, Vertex>(ConvertToVertex); var sectorCache = new ModelSequence <SectorDescription, Sector>(ConvertToSector); var lineCache = new ModelSequence <LineDescription, LineDef>(ld => ConvertToLineDef(ld, sectorCache, sideDefCache)); var edges = GetEdges(boardSize, internalDistances); DrawEdges(edges, vertexCache, lineCache); var playerLogicalSpot = FindPlayerSpot(geometryBoard); return(new MapData( NameSpace: "Doom", LineDefs: lineCache.GetDefinitions().ToImmutableArray(), SideDefs: sideDefCache.GetDefinitions().ToImmutableArray(), Vertices: vertexCache.GetDefinitions().ToImmutableArray(), Sectors: sectorCache.GetDefinitions().ToImmutableArray(), Things: ImmutableArray.Create( Actor.Player1Start.MakeThing( x: playerLogicalSpot.X * LogicalUnitSize + LogicalUnitSize / 2, y: playerLogicalSpot.Y * LogicalUnitSize + LogicalUnitSize / 2, angle: 90)))); }
static IEnumerable <ILump> CreateWadContents( IEnumerable <Func <TextureQueue, MapData> > mapCreators) { var textureQueue = new TextureQueue(); var maps = mapCreators.Select(creator => creator(textureQueue)).ToList(); var textureLumps = new List <ILump>(); if (textureQueue.RenderQueue.Any()) { textureLumps.Add(new Marker("P_START")); textureLumps.AddRange(textureQueue.RenderQueue.Select(r => DataLump.ReadFromStream(r.Item2.Name, r.Item1.RenderTo))); textureLumps.Add(new Marker("P_END")); } var lumps = new List <ILump> { DataLump.ReadFromStream("TEXTURES", stream => TexturesWriter.Write(textureQueue.Definitions, stream)), }; lumps.AddRangeAndContinue(textureLumps) .AddRangeAndContinue(maps.SelectMany((map, index) => new ILump[] { new Marker($"MAP{index + 1:00}"), new UwmfLump("TEXTMAP", map), new Marker("ENDMAP") })); return(lumps); }
public static MapData CreateMapAndTextures(TextureQueue textureQueue) { var mapSize = new Size( HorizontalBuffer + TotalNumLevels + HorizontalBuffer, Height); var(planeMap, tiles) = CreateGeometry(mapSize, textureQueue); return(new MapData ( NameSpace: "Wolf3D", TileSize: 64, Name: "Textures Demo", Width: mapSize.Width, Height: mapSize.Height, Tiles: tiles, Sectors: ImmutableArray.Create( new Sector( TextureCeiling: "#C0C0C0", TextureFloor: "#A0A0A0" )), Zones: ImmutableArray.Create(new Zone()), Planes: ImmutableArray.Create(new Plane(Depth: 64)), PlaneMaps: ImmutableArray.Create(planeMap), Things: ImmutableArray.Create(new Thing( Type: Actor.Player1Start.ClassName, X: 1.5, Y: 1.5, Z: 0, Angle: 0, Skill1: true, Skill2: true, Skill3: true, Skill4: true )), Triggers: ImmutableArray <Trigger> .Empty )); }
public static MapData Create(int seed, string texturePrefix, TextureQueue textureQueue) { var random = new Random(seed); var caveBoard = new CellBoard(new Size(128, 128)) .Fill(random, probabilityAlive: 0.5) .MakeBorderAlive(thickness: 3) .GenerateStandardCave(); var(caveArea, size) = ConnectedAreaAnalyzer .FindForegroundAreas(caveBoard.Dimensions, p => caveBoard[p] == CellType.Dead) .OrderByDescending(a => a.Area) .First() .TrimExcess(border: 1); var interior = caveArea.DetermineInteriorEdgeDistance(Neighborhood.VonNeumann); var alternateFloor = new CellBoard(new Size(size.Width + 1, size.Height + 1)) .Fill(random, probabilityAlive: 0.5) .RunGenerations(6); var alternateCeiling = new CellBoard(new Size(size.Width + 1, size.Height + 1)) .Fill(random, probabilityAlive: 0.5) .RunGenerations(6); var lightRange = new LightRange(DarkLevels: 15, LightLevels: 5); var lights = CaveThingPlacement.RandomlyPlaceLights( interior.Where(pair => pair.Value == 2).Select(pair => pair.Key).ToList(), random, lightRange, percentAreaToCover: 0.05, varyHeight: true) .ToArray(); var(floorLighting, ceilingLighting) = LightTracer.Trace(size, p => !caveArea.Contains(p), lightRange, lights); var(planeMap, sectors, tiles) = CreateGeometry( size, caveArea, alternateFloor, alternateCeiling, floorLighting, ceilingLighting, textureQueue, texturePrefix); var playerPosition = caveArea.First(); var things = lights.Select(light => new Thing( Type: light.Height == LightHeight.Ceiling ? "CeilingCrystal" : "FloorCrystal", X: light.Center.X + 0.5, Y: light.Center.Y + 0.5, Z: 0, Angle: 0, Ambush: false, Skill1: true, Skill2: true, Skill3: true, Skill4: true)).ToList(); things.Add(new Thing( Type: Actor.Player1Start.ClassName, X: playerPosition.X + 0.5, Y: playerPosition.Y + 0.5, Z: 0, Angle: 0, Ambush: false, Skill1: true, Skill2: true, Skill3: true, Skill4: true)); return(new MapData( NameSpace: "Wolf3D", TileSize: 64, Name: "Procedural Cave", Width: size.Width, Height: size.Height, Tiles: tiles, Sectors: sectors, Zones: ImmutableArray.Create(new Zone()), Planes: ImmutableArray.Create(new Plane(Depth: 64)), PlaneMaps: ImmutableArray.Create(planeMap), Things: things.ToImmutableArray(), Triggers: ImmutableArray <Trigger> .Empty)); }
static (ImmutableArray <MapSquare>, ImmutableArray <Sector>, ImmutableArray <Tile>) CreateGeometry( Size size, ConnectedArea cave, CellBoard alternateFloor, CellBoard alternateCeiling, LightMap floorLight, LightMap ceilingLight, TextureQueue textureQueue, string texturePrefix) { var planeMap = new Canvas(size); double minLight = 0.2; double darkStep = (1 - minLight) / ceilingLight.Range.DarkLevels; double maxLight = 1.1; double lightStep = (maxLight - 1) / ceilingLight.Range.LightLevels; double GetAlpha(int light) => light switch { 0 => 0, int pos when pos > 0 => pos * lightStep, int neg when neg < 0 => - neg * darkStep, _ => throw new InvalidOperationException("Impossible") }; string GetTextureName(Corners corners, int light, bool isEastWest = false) { string name = $"t{(int)corners:D2}{light:+#;-#;#}{(isEastWest ? "dark" : "")}"; var patches = ImmutableArray.CreateBuilder <Patch>(); if (light > 0) { patches.Add( new Patch( $"{texturePrefix}{(int)corners:D2}", 0, 0, Blend: new ColorBlend("FFFFFF", GetAlpha(light)))); } else if (light < 0) { patches.Add( new Patch( $"{texturePrefix}{(int)corners:D2}", 0, 0, Blend: new ColorBlend("000000", GetAlpha(light)))); } else { patches.Add( new Patch( $"{texturePrefix}{(int)corners:D2}", 0, 0)); } if (isEastWest) { patches.Add( new Patch( $"{texturePrefix}{(int)corners:D2}", 0, 0, Blend: new ColorBlend("000000"), Style: RenderStyle.Translucent, Alpha: 0.075)); } textureQueue.Add(new CompositeTexture(name, 256, 256, patches.ToImmutable(), XScale: 4, YScale: 4)); return(name); } Corners GetCorners(CellBoard board, Position pos) => Corner.CreateFromUpperLeft( upperLeft: pos, on: p => board[p] == CellType.Dead); Corners GetSideCorners(Position left, Position right) => Corner.Create( topLeft: alternateCeiling[left] == CellType.Dead, topRight: alternateCeiling[right] == CellType.Dead, bottomLeft: alternateFloor[left] == CellType.Dead, bottomRight: alternateFloor[right] == CellType.Dead ); int GetSideLight(Position p) => (ceilingLight[p] + floorLight[p]) / 2; var sectorSequence = new ModelSequence <SectorDescription, Sector>(description => new Sector( TextureCeiling: GetTextureName(description.Ceiling, description.CeilingLight), TextureFloor: GetTextureName(description.Floor, description.FloorLight))); var tileSequence = new ModelSequence <TileDescription, Tile>(description => new Tile( TextureEast: GetTextureName(description.EastCorners, description.EastLight, isEastWest: true), TextureNorth: GetTextureName(description.NorthCorners, description.NorthLight), TextureWest: GetTextureName(description.WestCorners, description.WestLight, isEastWest: true), TextureSouth: GetTextureName(description.SouthCorners, description.SouthLight), TextureOverhead: GetTextureName(description.FloorCorners, 0))); for (int y = 0; y < size.Height; y++) { for (int x = 0; x < size.Width; x++) { var pos = new Position(x, y); int tileId = -1; if (!cave.Contains(pos)) { tileId = tileSequence.GetIndex(new TileDescription( NorthCorners: GetSideCorners(left: pos + Offset.Right, right: pos), EastCorners: GetSideCorners(left: pos + Offset.DownAndRight, right: pos + Offset.Right), SouthCorners: GetSideCorners(left: pos + Offset.Down, right: pos + Offset.DownAndRight), WestCorners: GetSideCorners(left: pos, right: pos + Offset.Down), FloorCorners: GetCorners(alternateFloor, pos), NorthLight: GetSideLight(pos + Offset.Up), EastLight: GetSideLight(pos + Offset.Right), SouthLight: GetSideLight(pos + Offset.Down), WestLight: GetSideLight(pos + Offset.Left))); } int sectorId = sectorSequence.GetIndex(new SectorDescription( Floor: GetCorners(alternateFloor, pos), Ceiling: GetCorners(alternateCeiling, pos), FloorLight: floorLight[pos], CeilingLight: ceilingLight[pos])); planeMap.Set(x, y, tile: tileId, sector: sectorId, zone: 0); } } return( planeMap.ToPlaneMap(), sectorSequence.GetDefinitions().ToImmutableArray(), tileSequence.GetDefinitions().ToImmutableArray()); }
// TODO: Add arguments for which types of things to include public static MapData CreateMapAndTextures(TextureQueue textureQueue) { var things = GenerateThings().ToImmutableArray(); int width = things.Max(t => (int)(t.X + 0.5)) + HorizontalBuffer; int height = things.Max(t => (int)(t.Y + 0.5)) + VerticalBuffer; return(new MapData( NameSpace: "Wolf3D", TileSize: 64, Name: "Thing Demo", Width: width, Height: height, Tiles: ImmutableArray.Create( new Tile( TextureNorth: "GSTONEA1", TextureSouth: "GSTONEA1", TextureEast: "GSTONEA2", TextureWest: "GSTONEA2" ), new Tile( TextureNorth: "SLOT1_2", TextureSouth: "SLOT1_2", TextureWest: "DOOR1_2", TextureEast: "DOOR1_2", OffsetVertical: true, OffsetHorizontal: false, BlockingNorth: true, BlockingSouth: true, BlockingWest: true, BlockingEast: true )), Sectors: ImmutableArray.Create( new Sector( TextureCeiling: "#C0C0C0", TextureFloor: "#A0A0A0" ), new Sector( TextureCeiling: "#00FF00", TextureFloor: "#00FF00", Comment: "Good thing" ), new Sector( TextureCeiling: "#FF0000", TextureFloor: "#FF0000", Comment: "Invalid thing" )), Zones: ImmutableArray.Create(new Zone()), Planes: ImmutableArray.Create(new Plane(Depth: 64)), PlaneMaps: ImmutableArray.Create(CreateGeometry(width: width, height: height, things).ToImmutableArray()), Things: things.Add( new Thing( Type: Actor.Player1Start.ClassName, X: 1.5, Y: 1.5, Z: 0, Angle: 0, Ambush: true, Skill1: true, Skill2: true, Skill3: true, Skill4: true)), Triggers: ImmutableArray.Create( new Trigger( X: 2, Y: 1, Z: 0, ActivateNorth: true, ActivateSouth: true, ActivateWest: true, ActivateEast: true, Action: "Door_Open", Arg0: 1, Arg1: 16, Arg2: 300, Arg3: 0, Arg4: 0, PlayerUse: true, MonsterUse: true, PlayerCross: false, Repeatable: true, Secret: false )) )); }
private static (ImmutableArray <MapSquare>, ImmutableArray <Tile>) CreateGeometry(Size size, TextureQueue textureQueue) { var baseTile = DefaultTile.GrayStone1; var nsTexture = baseTile.TextureNorth.Name; var ewTexture = baseTile.TextureEast.Name; var tiles = new List <Tile> { baseTile }; var board = new Canvas(size) .FillRectangle(size.ToRectangle(), tile: -1) .OutlineRectangle(size.ToRectangle(), tile: 0); var middleX = HorizontalBuffer + NumGradients; var middleY = Height / 2; board.Set(middleX, middleY, tile: 1); foreach (var darkLevel in Enumerable.Range(1, NumGradients)) { board.Set(middleX - darkLevel, middleY, tile: tiles.Count); var newNS = nsTexture + "Dark" + darkLevel; var newEW = ewTexture + "Dark" + darkLevel; tiles.Add(baseTile with { TextureNorth = newNS, TextureSouth = newNS, TextureEast = newEW, TextureWest = newEW, }); textureQueue.Add(new CompositeTexture(newNS, 64, 64, ImmutableArray.Create( new Patch(nsTexture, 0, 0), new Patch(nsTexture, 0, 0, Blend: new ColorBlend("000000", Alpha: 0.1 * darkLevel))))); textureQueue.Add(new CompositeTexture(newEW, 64, 64, ImmutableArray.Create( new Patch(ewTexture, 0, 0), new Patch(ewTexture, 0, 0, Blend: new ColorBlend("000000", Alpha: 0.1 * darkLevel))))); } foreach (var lightLevel in Enumerable.Range(1, NumGradients)) { board.Set(middleX + lightLevel, middleY, tile: tiles.Count); var newNS = nsTexture + "Light" + lightLevel; var newEW = ewTexture + "Light" + lightLevel; tiles.Add(baseTile with { TextureNorth = newNS, TextureSouth = newNS, TextureEast = newEW, TextureWest = newEW, }); textureQueue.Add(new CompositeTexture(newNS, 64, 64, ImmutableArray.Create( new Patch(nsTexture, 0, 0), new Patch(nsTexture, 0, 0, Blend: new ColorBlend("FFFFFF", Alpha: 0.1 * lightLevel))))); textureQueue.Add(new CompositeTexture(newEW, 64, 64, ImmutableArray.Create( new Patch(ewTexture, 0, 0), new Patch(ewTexture, 0, 0, Blend: new ColorBlend("FFFFFF", Alpha: 0.1 * lightLevel))))); } return(board.ToPlaneMap(), tiles.ToImmutableArray()); }
public void CaveMap() { var textureQueue = new TextureQueue(); textureQueue.Add( new CompositeTexture("CRSFA0", Width: 35, Height: 37, Namespace: TextureNamespace.Sprite, XScale: 2, YScale: 2, Offset: new TextureOffset(17, 32), Patches: ImmutableArray.Create(new Patch("CRYSTAL", 0, 0))), new CompositeTexture("CRSCA0", Width: 35, Height: 37, Namespace: TextureNamespace.Sprite, XScale: 2, YScale: 2, Offset: new TextureOffset(17, 133), Patches: ImmutableArray.Create(new Patch("CRYSTAL", 0, 0, FlipY: true)))); var map = WolfCaveMapGenerator.Create(seed: 13, texturePrefix: "TILE", textureQueue: textureQueue); Load(new List <ILump> { new Marker("P_START") } .AddRangeAndContinue( Enumerable.Range(0, 16) .Select(i => new DataLump( $"TILE{i:d2}", (byte[])(Resource.ResourceManager.GetObject($"tile{i:00}", CultureInfo.InvariantCulture) ?? throw new ArgumentException("Somehow the name was wrong"))))) .AddRangeAndContinue(textureQueue.RenderQueue.Select(r => DataLump.ReadFromStream(r.Item2.Name, r.Item1.RenderTo))) .AddRangeAndContinue(new ILump[] { new Marker("P_END"), new Marker("S_START"), new DataLump("CRYSTAL", Resource.crystal), new Marker("S_END"), DataLump.ReadFromStream("TEXTURES", stream => TexturesWriter.Write(textureQueue.Definitions, stream)), new DataLump("DECORATE", @"actor CeilingCrystal { states { Spawn: CRSC A -1 stop } } actor FloorCrystal { states { Spawn: CRSF A -1 stop } }"), new Marker("MAP01"), new UwmfLump("TEXTMAP", map), new Marker("ENDMAP") })); }