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
                   ));
        }
Beispiel #4
0
    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));
    }
Beispiel #5
0
    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());
    }
Beispiel #6
0
        // 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")
            }));
        }