├── wang1.png
├── wang2.png
├── TerrainGenDemo
├── bin
│ └── data
│ │ ├── glob.png
│ │ ├── pcb.png
│ │ ├── wang.png
│ │ ├── ground.png
│ │ ├── patch.png
│ │ ├── seasand.png
│ │ └── terrain.png
├── packages.config
├── App.config
├── OpenTK.dll.config
├── TerrainGenDemo.csproj
└── Program.cs
├── WangTiles
├── bin
│ └── data
│ │ ├── tileset0.png
│ │ ├── tileset1.png
│ │ ├── tileset10.png
│ │ ├── tileset11.png
│ │ ├── tileset12.png
│ │ ├── tileset13.png
│ │ ├── tileset14.png
│ │ ├── tileset15.png
│ │ ├── tileset16.png
│ │ ├── tileset17.png
│ │ ├── tileset18.png
│ │ ├── tileset19.png
│ │ ├── tileset2.png
│ │ ├── tileset20.png
│ │ ├── tileset21.png
│ │ ├── tileset22.png
│ │ ├── tileset3.png
│ │ ├── tileset4.png
│ │ ├── tileset5.png
│ │ ├── tileset6.png
│ │ ├── tileset7.png
│ │ ├── tileset8.png
│ │ ├── tileset9.png
│ │ ├── tileset0_16.png
│ │ └── tileset0_32.png
├── packages.config
├── App.config
├── Properties
│ └── AssemblyInfo.cs
├── OpenTK.dll.config
├── WangTilesDemo.csproj
└── Program.cs
├── DungeonGenDemo
├── bin
│ └── data
│ │ ├── tileset0.png
│ │ ├── tileset0_16.png
│ │ └── tileset0_32.png
├── packages.config
├── App.config
├── OpenTK.dll.config
├── DungeonGenDemo.csproj
└── Program.cs
├── WangTileLib
├── packages.config
├── Properties
│ └── AssemblyInfo.cs
├── OpenTK.dll.config
├── WangTileLib.csproj
├── Tileset.cs
├── MathUtility.cs
├── WangUtils.cs
└── DungeonPlanner.cs
├── .gitignore
├── LICENSE
├── README.md
└── WangTiles.sln
/wang1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/wang1.png
--------------------------------------------------------------------------------
/wang2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/wang2.png
--------------------------------------------------------------------------------
/TerrainGenDemo/bin/data/glob.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/TerrainGenDemo/bin/data/glob.png
--------------------------------------------------------------------------------
/TerrainGenDemo/bin/data/pcb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/TerrainGenDemo/bin/data/pcb.png
--------------------------------------------------------------------------------
/TerrainGenDemo/bin/data/wang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/TerrainGenDemo/bin/data/wang.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset0.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset1.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset10.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset11.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset12.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset13.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset14.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset15.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset16.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset17.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset18.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset19.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset2.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset20.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset21.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset22.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset3.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset4.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset5.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset6.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset7.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset8.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset9.png
--------------------------------------------------------------------------------
/TerrainGenDemo/bin/data/ground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/TerrainGenDemo/bin/data/ground.png
--------------------------------------------------------------------------------
/TerrainGenDemo/bin/data/patch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/TerrainGenDemo/bin/data/patch.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset0_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset0_16.png
--------------------------------------------------------------------------------
/WangTiles/bin/data/tileset0_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/WangTiles/bin/data/tileset0_32.png
--------------------------------------------------------------------------------
/DungeonGenDemo/bin/data/tileset0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/DungeonGenDemo/bin/data/tileset0.png
--------------------------------------------------------------------------------
/TerrainGenDemo/bin/data/seasand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/TerrainGenDemo/bin/data/seasand.png
--------------------------------------------------------------------------------
/TerrainGenDemo/bin/data/terrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/TerrainGenDemo/bin/data/terrain.png
--------------------------------------------------------------------------------
/DungeonGenDemo/bin/data/tileset0_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/DungeonGenDemo/bin/data/tileset0_16.png
--------------------------------------------------------------------------------
/DungeonGenDemo/bin/data/tileset0_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Relfos/WangTiles/master/DungeonGenDemo/bin/data/tileset0_32.png
--------------------------------------------------------------------------------
/WangTileLib/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/WangTiles/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/DungeonGenDemo/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TerrainGenDemo/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/WangTiles/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/DungeonGenDemo/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/TerrainGenDemo/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | WangTiles/bin/Debug/
2 | WangTiles/obj/
3 | packages/
4 | .vs/
5 | DungeonGenDemo/bin/Debug/
6 | TerrainGenDemo/bin/Debug/
7 | WangTileLib/bin/
8 | WangTileLib/obj/
9 | TerrainGenDemo/obj/
10 | DungeonGenDemo/obj/
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Wang Tiles
2 | ============
3 |
4 | Implements a random map generator using Wang Tiles, in C#.
5 |
6 | * Author: [Sérgio Flores](https://github.com/relfos)
7 | * License: [MIT](https://opensource.org/licenses/MIT)
8 | * [Reporting Issues](https://github.com/relfos/WangTiles/issues)
9 | * Support can be obtained via [Email](mailto:sergio.flores@lunarlabs.pt)
10 | * If you require some specific feature please contact for a quote.
11 |
12 | ============
13 | Wang Tiles are a very simple but useful concept that can be used to generate an infinite set of connecting tiles.
14 | This small project was implemented based on the information found [here](http://s358455341.websitehome.co.uk/stagecast/wang/intro.html).
15 |
16 |
17 | Note that is just a small proof of concept done in 1 hour, using C# and OpenTK for rendering the output (you can easily swap it out for any other rendering system, the algoritm only gives you a list of tile IDs).
18 |
19 |
20 | When running the sample, you can change the tileset using the number keys.
21 |
22 | A sample output, using a simple tileset with roads.
23 |
24 | 
25 |
26 | ============
27 | Same map, using a different tileset that shows tile IDs.
28 | 
29 |
--------------------------------------------------------------------------------
/WangTiles/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("WangTiles")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("WangTiles")]
13 | [assembly: AssemblyCopyright("Copyright © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("729fb5b7-138f-48df-9bde-7fa3e72bc373")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/WangTileLib/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("WangTileLib")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("WangTileLib")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("f179fa9f-2e56-429e-9f1a-aba91fa6c9ce")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/WangTileLib/OpenTK.dll.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/WangTiles/OpenTK.dll.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/DungeonGenDemo/OpenTK.dll.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/TerrainGenDemo/OpenTK.dll.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/WangTiles.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WangTilesDemo", "WangTiles\WangTilesDemo.csproj", "{729FB5B7-138F-48DF-9BDE-7FA3E72BC373}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WangTileLib", "WangTileLib\WangTileLib.csproj", "{F179FA9F-2E56-429E-9F1A-ABA91FA6C9CE}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DungeonGenDemo", "DungeonGenDemo\DungeonGenDemo.csproj", "{98659412-205D-456C-A8EB-D4E3608B8DEB}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerrainGenDemo", "TerrainGenDemo\TerrainGenDemo.csproj", "{90B6F5E9-CF85-45E4-B8C6-D05313A4309A}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {729FB5B7-138F-48DF-9BDE-7FA3E72BC373}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {729FB5B7-138F-48DF-9BDE-7FA3E72BC373}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {729FB5B7-138F-48DF-9BDE-7FA3E72BC373}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {729FB5B7-138F-48DF-9BDE-7FA3E72BC373}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {F179FA9F-2E56-429E-9F1A-ABA91FA6C9CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {F179FA9F-2E56-429E-9F1A-ABA91FA6C9CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {F179FA9F-2E56-429E-9F1A-ABA91FA6C9CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {F179FA9F-2E56-429E-9F1A-ABA91FA6C9CE}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {98659412-205D-456C-A8EB-D4E3608B8DEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {98659412-205D-456C-A8EB-D4E3608B8DEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {98659412-205D-456C-A8EB-D4E3608B8DEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {98659412-205D-456C-A8EB-D4E3608B8DEB}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {90B6F5E9-CF85-45E4-B8C6-D05313A4309A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {90B6F5E9-CF85-45E4-B8C6-D05313A4309A}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {90B6F5E9-CF85-45E4-B8C6-D05313A4309A}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {90B6F5E9-CF85-45E4-B8C6-D05313A4309A}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | EndGlobal
41 |
--------------------------------------------------------------------------------
/DungeonGenDemo/DungeonGenDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {98659412-205D-456C-A8EB-D4E3608B8DEB}
8 | Exe
9 | Properties
10 | DungeonGenDemo
11 | DungeonGenDemo
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | ..\packages\OpenTK.2.0.0\lib\net20\OpenTK.dll
46 | True
47 |
48 |
49 |
50 |
51 |
52 |
53 | {f179fa9f-2e56-429e-9f1a-aba91fa6c9ce}
54 | WangTileLib
55 |
56 |
57 |
58 |
65 |
--------------------------------------------------------------------------------
/TerrainGenDemo/TerrainGenDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {90B6F5E9-CF85-45E4-B8C6-D05313A4309A}
8 | Exe
9 | Properties
10 | TerrainGenDemo
11 | TerrainGenDemo
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | ..\packages\OpenTK.2.0.0\lib\net20\OpenTK.dll
46 | True
47 |
48 |
49 |
50 |
51 |
52 |
53 | {f179fa9f-2e56-429e-9f1a-aba91fa6c9ce}
54 | WangTileLib
55 |
56 |
57 |
58 |
65 |
--------------------------------------------------------------------------------
/WangTileLib/WangTileLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {F179FA9F-2E56-429E-9F1A-ABA91FA6C9CE}
8 | Library
9 | Properties
10 | WangTileLib
11 | WangTileLib
12 | v4.5.2
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\packages\OpenTK.2.0.0\lib\net20\OpenTK.dll
35 | True
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
67 |
--------------------------------------------------------------------------------
/WangTiles/WangTilesDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {729FB5B7-138F-48DF-9BDE-7FA3E72BC373}
8 | Exe
9 | Properties
10 | WangTiles
11 | WangTiles
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\OpenTK.2.0.0\lib\net20\OpenTK.dll
38 | True
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | {f179fa9f-2e56-429e-9f1a-aba91fa6c9ce}
62 | WangTileLib
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/WangTileLib/Tileset.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Drawing;
3 | using WangTiles.Core;
4 |
5 | namespace WangTiles.Utils
6 | {
7 | public class Tileset
8 | {
9 | private Dictionary> tiles = new Dictionary>();
10 | private int _tileSize;
11 | public int TileSize { get { return _tileSize; } }
12 |
13 | public Tileset(string fileName)
14 | {
15 | var source = new Bitmap(fileName);
16 |
17 | this._tileSize = source.Width / 16;
18 |
19 | for (int i = 0; i < 16; i++)
20 | {
21 | List variations = new List();
22 |
23 | int maxVariations = source.Height / _tileSize;
24 | for (int j = 0; j < maxVariations; j++)
25 | {
26 | Bitmap tile = new Bitmap(_tileSize, _tileSize);
27 | bool isEmpty = true;
28 | for (int y = 0; y < _tileSize; y++)
29 | {
30 | for (int x = 0; x < _tileSize; x++)
31 | {
32 | var color = source.GetPixel(x + i * _tileSize, y + j * _tileSize);
33 | if (color.A <= 0)
34 | {
35 | continue;
36 | }
37 |
38 | isEmpty = false;
39 | tile.SetPixel(x, y, color);
40 | }
41 | }
42 |
43 | if (isEmpty)
44 | {
45 | break;
46 | }
47 |
48 | variations.Add(tile);
49 | }
50 |
51 | tiles[i] = variations;
52 | }
53 | }
54 |
55 |
56 | ///
57 | /// Draws a tile in the texture buffer at specified position
58 | ///
59 | private void DrawTile(byte[] buffer, int bufferWidth, int bufferHeight, int targetX, int targetY, int tileID, int variation, Color borderColor, int drawScale)
60 | {
61 | var variations = tiles[tileID];
62 | Bitmap tile = variations[variation % variations.Count];
63 |
64 | for (int y = 0; y < _tileSize; y++)
65 | {
66 | for (int x = 0; x < _tileSize; x++)
67 | {
68 | var c = tile.GetPixel(x, y);
69 |
70 | if (borderColor.A > 0 && (x == 0 || y == 0 || x == _tileSize - 1 || y == _tileSize - 1))
71 | {
72 | c = borderColor;
73 | }
74 |
75 | for (int iy = 0; iy < drawScale; iy++)
76 | {
77 | for (int ix = 0; ix < drawScale; ix++)
78 | {
79 | int tx = targetX + x * drawScale + ix;
80 | int ty = targetY + y * drawScale + iy;
81 | if (tx >= bufferWidth || tx < 0) { continue; }
82 | if (ty >= bufferHeight || ty < 0) { continue; }
83 |
84 | int destOfs = (tx + bufferWidth * ty) * 4;
85 | buffer[destOfs + 0] = c.R;
86 | buffer[destOfs + 1] = c.G;
87 | buffer[destOfs + 2] = c.B;
88 | buffer[destOfs + 3] = c.A;
89 | }
90 | }
91 | }
92 | }
93 | }
94 |
95 | public void RedrawWithTileset(byte[] buffer, int bufferWidth, int bufferHeight, WangMap map, bool drawBorders, int drawScale)
96 | {
97 | for (int j = 0; j < map.Height; j++)
98 | {
99 | for (int i = 0; i < map.Width; i++)
100 | {
101 | var tile = map.GetTileAt(i, j);
102 | if (tile.tileID < 0) { continue; }
103 |
104 | int variation = tile.variationID;
105 |
106 | DrawTile(buffer, bufferWidth, bufferHeight, i * _tileSize * drawScale, j * _tileSize * drawScale, tile.tileID, variation, drawBorders ? WangArea.GetColor(tile.areaID) : Color.FromArgb(0), drawScale);
107 | }
108 | }
109 | }
110 |
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/TerrainGenDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using OpenTK.Graphics.OpenGL;
4 | using OpenTK.Input;
5 | using System.Drawing;
6 | using System.Net;
7 | using WangTiles.Utils;
8 | using WangTiles.Core;
9 |
10 | namespace WangTiles
11 | {
12 | public class LayoutGenExample
13 | {
14 | #region TEXTURE_BUFFER_UTILS
15 | private static int bufferWidth = 600;
16 | private static int bufferHeight = 400;
17 | private static byte[] buffer = new byte[bufferWidth * bufferHeight * 4];
18 |
19 | static void UpdateBuffer(byte[] buffer, int width, int height, int texID)
20 | {
21 | GL.BindTexture(TextureTarget.Texture2D, texID);
22 | GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, buffer);
23 | }
24 |
25 | public static void DrawBuffer(OpenTK.GameWindow game, int textureID)
26 | {
27 | float u1 = 0;
28 | float u2 = 1;
29 | float v1 = 0;
30 | float v2 = 1;
31 |
32 | float w = 1;
33 | float h = 1;
34 |
35 |
36 | float px = 0;
37 | float py = 0;
38 |
39 | GL.Enable(EnableCap.Texture2D);
40 | GL.BindTexture(TextureTarget.Texture2D, textureID);
41 |
42 | GL.Begin(PrimitiveType.Quads);
43 |
44 | GL.Color3(Color.White);
45 | GL.TexCoord2(u1, v1);
46 | GL.Vertex2(px + 0, py + h);
47 |
48 | GL.TexCoord2(u1, v2);
49 | GL.Vertex2(px + 0, py + 0);
50 |
51 | GL.TexCoord2(u2, v2);
52 | GL.Vertex2(px + w, py + 0);
53 |
54 | GL.TexCoord2(u2, v1);
55 | GL.Vertex2(px + w, py + h);
56 |
57 | GL.End();
58 |
59 | GL.Disable(EnableCap.Texture2D);
60 | }
61 | #endregion
62 |
63 |
64 |
65 | private static void DownloadTileset(string name)
66 | {
67 | using (var client = new WebClient())
68 | {
69 | Bitmap target = new Bitmap(512, 32);
70 | for (int i = 0; i < 16; i++)
71 | {
72 | var url = "http://cr31.co.uk/stagecast/art/corn/" + name + "/" + i + ".gif";
73 | var tempName = "temp" + i + ".gif";
74 | client.DownloadFile(url, tempName);
75 | Bitmap temp = new Bitmap(tempName);
76 | Graphics g = Graphics.FromImage(target);
77 | g.DrawImage(temp, i * 32, 0);
78 | }
79 | target.Save(@"..\data\"+name+".png");
80 | }
81 | }
82 |
83 |
84 | public static void Main(string[] args)
85 | {
86 | //DownloadTileset("glob");
87 |
88 | bool drawBorders = false;
89 | int drawScale = 1;
90 |
91 | var tileset = new Tileset("../data/seasand.png");
92 |
93 | #region WANG_GENERATION
94 | var map = new WangCornerMap(18, 12, 1424);
95 |
96 | // not everything needs to be random, we can pre-fill parts of the map with our own data
97 | for (int j = 2; j <= 6; j++)
98 | {
99 | for (int i = 2; i <= 6; i++)
100 | {
101 | map.SetTileIDAt(i, j, 0);
102 | }
103 | }
104 |
105 | for (int j = 8; j <= 11; j++)
106 | {
107 | for (int i = 8; i <= 14; i++)
108 | {
109 | map.SetTileIDAt(i, j, 15);
110 | }
111 | }
112 |
113 | // now fill the missing tiles randomly
114 | map.Generate();
115 | map.Invert();
116 | #endregion
117 |
118 | // now render the map to a pixel array
119 | tileset.RedrawWithTileset(buffer, bufferWidth, bufferHeight, map, drawBorders, drawScale);
120 |
121 | int bufferTexID = 0;
122 |
123 | int selX = -1;
124 | int selY = -1;
125 |
126 | using (var game = new OpenTK.GameWindow(bufferWidth, bufferHeight, OpenTK.Graphics.GraphicsMode.Default, "Wang Tiles"))
127 | {
128 | game.Load += (sender, e) =>
129 | {
130 | // setup settings, load textures, sounds
131 | game.VSync = OpenTK.VSyncMode.Off;
132 |
133 | // generate a texture and copy the pixel array there
134 | bufferTexID = GL.GenTexture();
135 | UpdateBuffer(buffer, bufferWidth, bufferHeight, bufferTexID);
136 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
137 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
138 | };
139 |
140 | game.Resize += (sender, e) =>
141 | {
142 | GL.Viewport(0, 0, game.Width, game.Height);
143 | };
144 |
145 |
146 | game.Mouse.Move += (object sender, MouseMoveEventArgs mouseEvent) =>
147 | {
148 | var mousePos = mouseEvent.Position;
149 | //mousePos = game.PointToClient(mousePos);
150 |
151 | //Console.WriteLine(mousePos.X + " " + mousePos.Y);
152 |
153 | selX = (mousePos.X) / (drawScale * tileset.TileSize);
154 | selY = (mousePos.Y) / (drawScale * tileset.TileSize);
155 |
156 | if (selX >= map.Width) { selX = -1; }
157 | if (selY >= map.Height) { selY = -1; }
158 | };
159 |
160 | game.Mouse.ButtonDown += (object sender, MouseButtonEventArgs buttonEvent) => {
161 | };
162 |
163 | game.UpdateFrame += (sender, e) =>
164 | {
165 | if (game.Keyboard[Key.Escape])
166 | {
167 | game.Exit();
168 | Environment.Exit(0);
169 | }
170 |
171 | };
172 |
173 |
174 | game.RenderFrame += (sender, e) =>
175 | {
176 | GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
177 |
178 | GL.MatrixMode(MatrixMode.Projection);
179 | GL.LoadIdentity();
180 | GL.Ortho(0.0, 1.0, 0.0, 1.0, 0.0, 4.0);
181 |
182 |
183 | // draw the texture to the screen, stretched to fill the whole window
184 | DrawBuffer(game, bufferTexID);
185 |
186 |
187 | game.SwapBuffers();
188 | };
189 |
190 | game.Run();
191 | }
192 | }
193 |
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/WangTiles/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using OpenTK.Graphics.OpenGL;
4 | using OpenTK.Input;
5 | using System.Drawing;
6 | using System.Net;
7 | using WangTiles.Utils;
8 | using WangTiles.Core;
9 |
10 | namespace WangTiles
11 | {
12 | public class LayoutGenExample
13 | {
14 | #region TEXTURE_BUFFER_UTILS
15 | private static int bufferWidth = 600;
16 | private static int bufferHeight = 400;
17 | private static byte[] buffer = new byte[bufferWidth * bufferHeight * 4];
18 |
19 | static void UpdateBuffer(byte[] buffer, int width, int height, int texID)
20 | {
21 | GL.BindTexture(TextureTarget.Texture2D, texID);
22 | GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, buffer);
23 | }
24 |
25 | public static void DrawBuffer(OpenTK.GameWindow game, int textureID)
26 | {
27 | float u1 = 0;
28 | float u2 = 1;
29 | float v1 = 0;
30 | float v2 = 1;
31 |
32 | float w = 1;
33 | float h = 1;
34 |
35 |
36 | float px = 0;
37 | float py = 0;
38 |
39 | GL.Enable(EnableCap.Texture2D);
40 | GL.BindTexture(TextureTarget.Texture2D, textureID);
41 |
42 | GL.Begin(PrimitiveType.Quads);
43 |
44 | GL.Color3(Color.White);
45 | GL.TexCoord2(u1, v1);
46 | GL.Vertex2(px + 0, py + h);
47 |
48 | GL.TexCoord2(u1, v2);
49 | GL.Vertex2(px + 0, py + 0);
50 |
51 | GL.TexCoord2(u2, v2);
52 | GL.Vertex2(px + w, py + 0);
53 |
54 | GL.TexCoord2(u2, v1);
55 | GL.Vertex2(px + w, py + h);
56 |
57 | GL.End();
58 |
59 | GL.Disable(EnableCap.Texture2D);
60 | }
61 | #endregion
62 |
63 |
64 |
65 | private static void DownloadTileset(string name)
66 | {
67 | using (var client = new WebClient())
68 | {
69 | Bitmap target = new Bitmap(512, 32);
70 | for (int i = 0; i < 16; i++)
71 | {
72 | var url = "http://s358455341.websitehome.co.uk/stagecast/art/edge/" + name + "/" + i + ".gif";
73 | var tempName = "temp" + i + ".gif";
74 | client.DownloadFile(url, tempName);
75 | Bitmap temp = new Bitmap(tempName);
76 | Graphics g = Graphics.FromImage(target);
77 | g.DrawImage(temp, i * 32, 0);
78 | }
79 | target.Save("tileset.png");
80 | }
81 | }
82 |
83 |
84 | public static void Main(string[] args)
85 | {
86 | //DownloadTileset("walkway");
87 |
88 | bool drawBorders = false;
89 | int drawScale = 4;
90 |
91 | var tilesets = new List();
92 | // load tilesets
93 | for (int i=0; i<=9; i++)
94 | {
95 | var tileset = new Tileset("../data/tileset"+i+".png");
96 | tilesets.Add(tileset);
97 | }
98 |
99 |
100 | int exitX = 1;
101 | int exitY = -1;
102 |
103 | #region WANG_GENERATION
104 | var map = new WangEdgeMap(14, 9, 3424);
105 | map.AddExit(exitX, exitY);
106 | map.Generate();
107 | map.FixConnectivity();
108 | #endregion
109 |
110 |
111 | // now render the map to a pixel array
112 | int currentTileset = 0;
113 | tilesets[currentTileset].RedrawWithTileset(buffer, bufferWidth, bufferHeight, map, drawBorders, drawScale);
114 |
115 |
116 | int bufferTexID = 0;
117 |
118 | int selX = -1;
119 | int selY = -1;
120 |
121 | using (var game = new OpenTK.GameWindow(bufferWidth, bufferHeight, OpenTK.Graphics.GraphicsMode.Default, "Wang Tiles"))
122 | {
123 | game.Load += (sender, e) =>
124 | {
125 | // setup settings, load textures, sounds
126 | game.VSync = OpenTK.VSyncMode.Off;
127 |
128 | // generate a texture and copy the pixel array there
129 | bufferTexID = GL.GenTexture();
130 | UpdateBuffer(buffer, bufferWidth, bufferHeight, bufferTexID);
131 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
132 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
133 | };
134 |
135 | game.Resize += (sender, e) =>
136 | {
137 | GL.Viewport(0, 0, game.Width, game.Height);
138 | };
139 |
140 |
141 | game.Mouse.Move += (object sender, MouseMoveEventArgs mouseEvent) =>
142 | {
143 | var mousePos = mouseEvent.Position;
144 | //mousePos = game.PointToClient(mousePos);
145 |
146 | //Console.WriteLine(mousePos.X + " " + mousePos.Y);
147 |
148 | selX = (mousePos.X) / (drawScale * tilesets[currentTileset].TileSize);
149 | selY = (mousePos.Y) / (drawScale * tilesets[currentTileset].TileSize);
150 |
151 | if (selX >= map.Width) { selX = -1; }
152 | if (selY >= map.Height) { selY = -1; }
153 | };
154 |
155 | game.Mouse.ButtonDown += (object sender, MouseButtonEventArgs buttonEvent) => {
156 | };
157 |
158 | game.UpdateFrame += (sender, e) =>
159 | {
160 | if (game.Keyboard[Key.Escape])
161 | {
162 | game.Exit();
163 | Environment.Exit(0);
164 | }
165 |
166 | int oldTileset = currentTileset;
167 | if (game.Keyboard[Key.Number1])
168 | {
169 | currentTileset = 0;
170 | }
171 | if (game.Keyboard[Key.Number2])
172 | {
173 | currentTileset = 1;
174 | }
175 | if (game.Keyboard[Key.Number3])
176 | {
177 | currentTileset = 2;
178 | }
179 | if (game.Keyboard[Key.Number4])
180 | {
181 | currentTileset = 3;
182 | }
183 | if (game.Keyboard[Key.Number5])
184 | {
185 | currentTileset = 4;
186 | }
187 | if (game.Keyboard[Key.Number6])
188 | {
189 | currentTileset = 5;
190 | }
191 |
192 | if (game.Keyboard[Key.Number7])
193 | {
194 | currentTileset = 6;
195 | }
196 |
197 | if (game.Keyboard[Key.Number8])
198 | {
199 | currentTileset = 7;
200 | }
201 |
202 | if (game.Keyboard[Key.Number9])
203 | {
204 | currentTileset = 8;
205 | }
206 |
207 | if (oldTileset != currentTileset)
208 | {
209 | tilesets[currentTileset].RedrawWithTileset(buffer, bufferWidth, bufferHeight, map, drawBorders, drawScale);
210 | UpdateBuffer(buffer, bufferWidth, bufferHeight, bufferTexID);
211 | }
212 | };
213 |
214 |
215 | game.RenderFrame += (sender, e) =>
216 | {
217 | GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
218 |
219 | GL.MatrixMode(MatrixMode.Projection);
220 | GL.LoadIdentity();
221 | GL.Ortho(0.0, 1.0, 0.0, 1.0, 0.0, 4.0);
222 |
223 |
224 | // draw the texture to the screen, stretched to fill the whole window
225 | DrawBuffer(game, bufferTexID);
226 |
227 |
228 | game.SwapBuffers();
229 | };
230 |
231 | game.Run();
232 | }
233 | }
234 |
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/DungeonGenDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using OpenTK.Graphics.OpenGL;
4 | using OpenTK.Input;
5 | using System.Drawing;
6 | using System.Net;
7 | using WangTiles.Utils;
8 | using WangTiles.DungeonPlanner;
9 | using WangTiles.Core;
10 |
11 | namespace DungeonDemo
12 | {
13 | public class DungeonExample
14 | {
15 | #region TEXTURE_BUFFER_UTILS
16 | private static int bufferWidth = 600;
17 | private static int bufferHeight = 400;
18 | private static byte[] buffer = new byte[bufferWidth * bufferHeight * 4];
19 |
20 | static void UpdateBuffer(byte[] buffer, int width, int height, int texID)
21 | {
22 | GL.BindTexture(TextureTarget.Texture2D, texID);
23 | GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, buffer);
24 | }
25 |
26 | public static void DrawBuffer(OpenTK.GameWindow game, int textureID)
27 | {
28 | float u1 = 0;
29 | float u2 = 1;
30 | float v1 = 0;
31 | float v2 = 1;
32 |
33 | float w = 1;
34 | float h = 1;
35 |
36 |
37 | float px = 0;
38 | float py = 0;
39 |
40 | GL.Enable(EnableCap.Texture2D);
41 | GL.BindTexture(TextureTarget.Texture2D, textureID);
42 |
43 | GL.Begin(PrimitiveType.Quads);
44 |
45 | GL.Color3(Color.White);
46 | GL.TexCoord2(u1, v1);
47 | GL.Vertex2(px + 0, py + h);
48 |
49 | GL.TexCoord2(u1, v2);
50 | GL.Vertex2(px + 0, py + 0);
51 |
52 | GL.TexCoord2(u2, v2);
53 | GL.Vertex2(px + w, py + 0);
54 |
55 | GL.TexCoord2(u2, v1);
56 | GL.Vertex2(px + w, py + h);
57 |
58 | GL.End();
59 |
60 | GL.Disable(EnableCap.Texture2D);
61 | }
62 | #endregion
63 |
64 | public static void Main(string[] args)
65 | {
66 | //DownloadTileset("walkway");
67 |
68 | bool drawBorders = false;
69 | int drawScale = 4;
70 |
71 | var tileset = new Tileset("../data/tileset0.png");
72 |
73 | int exitX = 1;
74 | int exitY = -1;
75 |
76 | #region WANG_GENERATION
77 | var map = new WangEdgeMap(14, 9, 3424);
78 | map.AddExit(exitX, exitY);
79 | map.Generate();
80 | map.FixConnectivity();
81 | #endregion
82 |
83 | #region DUNGEON_PLANNING
84 | LayoutPlanner planner = new LayoutPlanner(4343);
85 | for (int j = 0; j < map.Height; j++)
86 | {
87 | for (int i = 0; i < map.Width; i++)
88 | {
89 | var tile = map.GetTileAt(i, j);
90 | int tileID = tile.tileID;
91 | if (tileID <= 0)
92 | {
93 | continue;
94 | }
95 |
96 | bool north, south, east, west;
97 | WangEdgeUtils.GetConnectionsForTile(tileID, out north, out east, out south, out west);
98 |
99 | if (north) { planner.AddConnection(new LayoutCoord(i, j), WangEdgeDirection.North); }
100 | if (south) { planner.AddConnection(new LayoutCoord(i, j), WangEdgeDirection.South); }
101 | if (east) { planner.AddConnection(new LayoutCoord(i, j), WangEdgeDirection.East); }
102 | if (west) { planner.AddConnection(new LayoutCoord(i, j), WangEdgeDirection.West); }
103 |
104 | var room = planner.FindRoomAt(new LayoutCoord(i, j));
105 | room.tileID = tileID;
106 | room.variationID = tile.variationID;
107 | }
108 | }
109 | planner.entrance = planner.FindRoomAt(new LayoutCoord(exitX, exitY));
110 | Console.WriteLine("Selected entrance: " + planner.entrance);
111 |
112 | var goal = planner.FindGoal();
113 |
114 | goal = goal.previous;
115 | while (goal.GetShape() == LayoutRoom.RoomShape.Corridor)
116 | {
117 | goal = goal.previous;
118 | }
119 |
120 | planner.SetGoal(goal);
121 | Console.WriteLine("Selected goal: " + goal);
122 |
123 | List keys = new List();
124 | keys.Add(new LayoutKey("Copper", 0));
125 | keys.Add(new LayoutKey("Bronze", 1));
126 | keys.Add(new LayoutKey("Silver", 2));
127 | keys.Add(new LayoutKey("Gold", 3));
128 |
129 | planner.GenerateProgression();
130 | planner.GenerateRoomTypes();
131 | planner.GenerateLocks(keys);
132 | #endregion
133 |
134 |
135 | // now render the map to a pixel array
136 | tileset.RedrawWithTileset(buffer, bufferWidth, bufferHeight, map, drawBorders, drawScale);
137 |
138 |
139 | int bufferTexID = 0;
140 |
141 | int selX = -1;
142 | int selY = -1;
143 |
144 | using (var game = new OpenTK.GameWindow(bufferWidth, bufferHeight, OpenTK.Graphics.GraphicsMode.Default, "Wang Tiles"))
145 | {
146 | game.Load += (sender, e) =>
147 | {
148 | // setup settings, load textures, sounds
149 | game.VSync = OpenTK.VSyncMode.Off;
150 |
151 | // generate a texture and copy the pixel array there
152 | bufferTexID = GL.GenTexture();
153 | UpdateBuffer(buffer, bufferWidth, bufferHeight, bufferTexID);
154 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
155 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
156 | };
157 |
158 | game.Resize += (sender, e) =>
159 | {
160 | GL.Viewport(0, 0, game.Width, game.Height);
161 | };
162 |
163 |
164 | game.Mouse.Move += (object sender, MouseMoveEventArgs mouseEvent) =>
165 | {
166 | var mousePos = mouseEvent.Position;
167 | //mousePos = game.PointToClient(mousePos);
168 |
169 | //Console.WriteLine(mousePos.X + " " + mousePos.Y);
170 |
171 | selX = (mousePos.X) / (drawScale * tileset.TileSize);
172 | selY = (mousePos.Y) / (drawScale * tileset.TileSize);
173 |
174 | if (selX >= map.Width) { selX = -1; }
175 | if (selY >= map.Height) { selY = -1; }
176 | };
177 |
178 | game.Mouse.ButtonDown += (object sender, MouseButtonEventArgs buttonEvent) => {
179 | };
180 |
181 | game.UpdateFrame += (sender, e) =>
182 | {
183 | if (game.Keyboard[Key.Escape])
184 | {
185 | game.Exit();
186 | Environment.Exit(0);
187 | }
188 |
189 | };
190 |
191 |
192 | game.RenderFrame += (sender, e) =>
193 | {
194 | GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
195 |
196 | GL.MatrixMode(MatrixMode.Projection);
197 | GL.LoadIdentity();
198 | GL.Ortho(0.0, 1.0, 0.0, 1.0, 0.0, 4.0);
199 |
200 |
201 | // draw the texture to the screen, stretched to fill the whole window
202 | DrawBuffer(game, bufferTexID);
203 |
204 |
205 | if (selX >= 0 && selY >= 0)
206 | {
207 | var selRoom = planner.FindRoomAt(new LayoutCoord(selX, selY), false);
208 | if (selRoom != null)
209 | {
210 | int percent = ((int)(selRoom.intensity * 100));
211 | string s = selRoom.order + "# " + selRoom.ToString();
212 | string s2 = selRoom.GetShape() + "/" + selRoom.category + "(" + selRoom.distanceFromMainPath + ") " + percent + "%";
213 |
214 | if (selRoom.contains != null)
215 | {
216 | s += "(Contains " + selRoom.contains + ")";
217 | }
218 |
219 | if (selRoom.locked != null)
220 | {
221 | s += "(Requires " + selRoom.locked + ")";
222 | }
223 |
224 | if (selRoom.isLoop)
225 | {
226 | s += "(Loop)";
227 | }
228 |
229 | FontUtils.DrawText(game, s, 4, 20, 0.6f, Color.White);
230 | FontUtils.DrawText(game, s2, 4, 0, 0.6f, Color.White);
231 | }
232 | }
233 |
234 | game.SwapBuffers();
235 | };
236 |
237 | game.Run();
238 | }
239 | }
240 |
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/WangTileLib/MathUtility.cs:
--------------------------------------------------------------------------------
1 | #if UNITY_5
2 | #define UNITY
3 | #endif
4 |
5 | #if UNITY
6 | using System;
7 | using UnityEngine;
8 | #else
9 | using System;
10 | #endif
11 |
12 | namespace WangTiles.Utils
13 | {
14 | public static class MathUtils
15 | {
16 | #if UNITY
17 | public const float PI = Mathf.PI;
18 | public const float Deg2Rad = Mathf.Deg2Rad;
19 | #else
20 | public const float PI = 3.14159265359f;
21 | public const float Deg2Rad = PI / 180.0f;
22 | #endif
23 |
24 | #region CORE
25 | public static int Sign(float x)
26 | {
27 | if (x == 0)
28 | {
29 | return 0;
30 | }
31 |
32 | return x < 0 ? -1 : 1;
33 | }
34 |
35 | public static float Frac(float x)
36 | {
37 | #if UNITY
38 | return x - Mathf.Floor(x);
39 | #else
40 | return x - (int)(x);
41 | #endif
42 | }
43 |
44 | public static float Round(float x)
45 | {
46 | #if UNITY
47 | return Mathf.Round(x);
48 | #else
49 | return (float)Math.Round(x);
50 | #endif
51 | }
52 |
53 |
54 | #if UNITY
55 | public static Vector4 Frac(Vector4 v)
56 | {
57 | return new Vector4(Frac(v.x), Frac(v.y), Frac(v.z), Frac(v.w));
58 | }
59 | #endif
60 |
61 | public static float Sqr(float x)
62 | {
63 | return x * x;
64 | }
65 |
66 | public static float Sqrt(float x)
67 | {
68 | #if UNITY
69 | return Mathf.Sqrt(x);
70 | #else
71 | return (float)Math.Sqrt(x);
72 | #endif
73 | }
74 |
75 | public static float Pow(float f, float p)
76 | {
77 | #if UNITY
78 | return Mathf.Pow(f, p);
79 | #else
80 | return (float)Math.Pow(f, p);
81 | #endif
82 | }
83 |
84 | public static float Log(float x, float power)
85 | {
86 | #if UNITY
87 | return Mathf.Log(x, power);
88 | #else
89 | return (float)Math.Log(x, power);
90 | #endif
91 | }
92 |
93 | public static float Log(float x)
94 | {
95 | #if UNITY
96 | return Mathf.Log(x);
97 | #else
98 | return (float)Math.Log(x);
99 | #endif
100 | }
101 |
102 | public static float Abs(float x)
103 | {
104 | #if UNITY
105 | return Mathf.Abs(x);
106 | #else
107 | return (float)Math.Abs(x);
108 | #endif
109 | }
110 |
111 | public static int Abs(int x)
112 | {
113 | #if UNITY
114 | return Mathf.Abs(x);
115 | #else
116 | return Math.Abs(x);
117 | #endif
118 | }
119 |
120 | public static float Floor(float x)
121 | {
122 | #if UNITY
123 | return Mathf.Floor(x);
124 | #else
125 | return (float)Math.Floor(x);
126 | #endif
127 | }
128 |
129 |
130 | public static float Sin(float x)
131 | {
132 | #if UNITY
133 | return Mathf.Sin(x);
134 | #else
135 | return (float)Math.Sin(x);
136 | #endif
137 | }
138 |
139 | public static float Cos(float x)
140 | {
141 | #if UNITY
142 | return Mathf.Cos(x);
143 | #else
144 | return (float)Math.Cos(x);
145 | #endif
146 | }
147 |
148 | public static float Asin(float x)
149 | {
150 | #if UNITY
151 | return Mathf.Asin(x);
152 | #else
153 | return (float)Math.Asin(x);
154 | #endif
155 | }
156 |
157 | public static float Acos(float x)
158 | {
159 | #if UNITY
160 | return Mathf.Acos(x);
161 | #else
162 | return (float)Math.Acos(x);
163 | #endif
164 | }
165 |
166 |
167 | public static float Min(float a, float b)
168 | {
169 | #if UNITY
170 | return Mathf.Min(a, b);
171 | #else
172 | return (float)Math.Min(a, b);
173 | #endif
174 | }
175 |
176 | public static float Max(float a, float b)
177 | {
178 | #if UNITY
179 | return Mathf.Max(a, b);
180 | #else
181 | return (float)Math.Max(a, b);
182 | #endif
183 | }
184 |
185 | public static int Min(int a, int b)
186 | {
187 | #if UNITY
188 | return Mathf.Min(a, b);
189 | #else
190 | return Math.Min(a, b);
191 | #endif
192 | }
193 |
194 | public static int Max(int a, int b)
195 | {
196 | #if UNITY
197 | return Mathf.Max(a, b);
198 | #else
199 | return Math.Max(a, b);
200 | #endif
201 | }
202 |
203 | public static void GetDirectionForAngle(float angle, float speed, out float dx, out float dy)
204 | {
205 | dx = Cos(angle) * speed;
206 | dy = Sin(angle) * speed;
207 | }
208 |
209 | #endregion
210 |
211 | #region DISTANCES
212 | public static float Distance(float x1, float y1, float x2, float y2)
213 | {
214 | float dx = x1 - x2;
215 | float dy = y1 - y2;
216 | dx *= dx;
217 | dy *= dy;
218 | return Sqrt(dx + dy);
219 | }
220 |
221 | #if UNITY
222 | public static float Distance(Vector2 A, Vector2 B)
223 | {
224 | return Distance(A.x, A.y, B.x, B.y);
225 | }
226 | #endif
227 |
228 | public static float DotProduct(float x1, float y1, float x2, float y2)
229 | {
230 | return x1 * x2 + y1 * y2;
231 | }
232 |
233 | public static float Angle(float x1, float y1, float x2, float y2)
234 | {
235 | return Acos(DotProduct(x1, y1, x2, y2));
236 | }
237 |
238 | #endregion
239 |
240 | #region CURVES
241 | public static float SmoothCurveWithOffset(float delta, float offset)
242 | {
243 | if (delta < offset)
244 | {
245 | delta = (delta / offset);
246 | return Abs(Sin(delta * PI * 0.5f));
247 | }
248 | else
249 | {
250 | delta = delta - offset;
251 | delta = (delta / (1.0f - offset));
252 | return Abs(Cos(delta * PI * 0.5f));
253 | }
254 | }
255 |
256 | public static float SmoothCurve(float delta)
257 | {
258 | return Abs(Sin(delta * PI));
259 | }
260 |
261 | public static float CubicInterpolate(float y0, float y1, float y2, float y3, float mu)
262 | {
263 | float mu2 = (mu * mu);
264 | float a0 = y3 - y2 - y0 + y1;
265 | float a1 = y0 - y1 - a0;
266 | float a2 = y2 - y0;
267 | float a3 = y1;
268 | return (a0 * mu * mu2) + (a1 * mu2) + (a2 * mu) + a3;
269 | }
270 |
271 | public static float CatmullRomInterpolate(float y0, float y1, float y2, float y3, float mu)
272 | {
273 | float mu2 = (mu * mu);
274 | float a0 = (-0.5f * y0) + (1.5f * y1) - (1.5f * y2) + (0.5f * y3);
275 | float a1 = y0 - (2.5f * y1) + (2.0f * y2) - (0.5f * y3);
276 | float a2 = (-0.5f * y0) + (0.5f * y2);
277 | float a3 = y1;
278 | return (a0 * mu * mu2) + (a1 * mu2) + (a2 * mu) + a3;
279 | }
280 |
281 | public static float HermiteInterpolate(float pA, float pB, float vA, float vB, float u)
282 | {
283 | float u2 = (u * u);
284 | float u3 = u2 * u;
285 | float B0 = 2.0f * u3 - 3.0f * u2 + 1.0f;
286 | float B1 = -2.0f * u3 + 3.0f * u2;
287 | float B2 = u3 - 2.0f * u2 + u;
288 | float B3 = u3 - u;
289 | return (B0 * pA + B1 * pB + B2 * vA + B3 * vB);
290 |
291 | }
292 |
293 | public static float QuadraticBezierCurve(float y0, float y1, float y2, float mu)
294 | {
295 |
296 | return Sqr(1 - mu) * y0 + 2 * (1 - mu) * y1 + Sqr(mu) * y2;
297 | }
298 |
299 | public static float CubicBezierCurve(float y0, float y1, float y2, float y3, float mu)
300 | {
301 | return (1 - mu) * Sqr(1 - mu) * y0 + 3 * Sqr(1 - mu) * y1 + 3 * (1 - mu) * Sqr(mu) * y2 + Sqr(mu) * mu * y3; ;
302 | }
303 | #endregion
304 |
305 | #region LERPING
306 | public static float Lerp(float min, float max, float delta)
307 | {
308 | #if UNITY
309 | return Mathf.Lerp(min, max, delta);
310 | #else
311 | delta = delta > 1 ? 1 : delta < 0 ? 0 : delta;
312 | return min * delta + max * (1.0f- delta);
313 | #endif
314 | }
315 |
316 | public static float InverseLerp(float min, float max, float value)
317 | {
318 | #if UNITY
319 | return Mathf.InverseLerp(min, max, value);
320 | #else
321 | return (value - min) / (max - min);
322 | #endif
323 | }
324 |
325 |
326 | // Some quadrilateral with position vectors a, b, c, and d.
327 | // a---b
328 | // | |
329 | // c---d
330 |
331 | // u = relative position on the "horizontal" axis between a and b, or c and d.
332 | // v = relative position on the "vertical" axis between a and c, or b and d.
333 |
334 | #if UNITY
335 | public static Vector3 BilinearLerp(Vector3 a, Vector3 b, Vector3 c, Vector3 d, float u, float v)
336 | {
337 | Vector3 ab = Vector3.Lerp(a, b, u); // interpolation of a and b by u
338 | Vector3 cd = Vector3.Lerp(c, d, u); // interpolation of c and d by u
339 | return Vector3.Lerp(ab, cd, v); // interpolation of the interpolation of a and b and c and d by u, by v
340 | }
341 | #endif
342 |
343 | #endregion
344 |
345 |
346 | #region COLOR
347 | #if UNITY
348 | //NOTE: values only valid for 1024 x 32
349 | private static Vector3 coord_scale = new Vector4(0.0302734375f, 0.96875f, 31.0f);
350 | private static Vector4 coord_offset = new Vector4(0.00048828125f, 0.015625f, 0.0f, 0.0f);
351 | private static Vector2 texel_height_X0 = new Vector2(0.03125f, 0.0f);
352 |
353 | private static Color LUTSample(Texture2D LUT, int red0, int green0, int blue, float u, float v)
354 | {
355 | int red1 = red0 < 31 ? red0 + 1 : red0;
356 | int green1 = green0 < 31 ? green0 + 1 : green0;
357 |
358 | Color c00 = LUT.GetPixel(blue * 32 + red0, green0);
359 | Color c10 = LUT.GetPixel(blue * 32 + red1, green0);
360 | Color c11 = LUT.GetPixel(blue * 32 + red1, green1);
361 | Color c01 = LUT.GetPixel(blue * 32 + red0, green1);
362 |
363 | Color ab = Color.Lerp(c00, c10, u); // interpolation of a and b by u
364 | Color cd = Color.Lerp(c01, c11, u); // interpolation of c and d by u
365 | return Color.Lerp(ab, cd, v); // interpolation of the interpolation of a and b and c and d by u, by v
366 | }
367 |
368 | public static Color32 LUTTransform(Color32 color, Texture2D LUT)
369 | {
370 | /*Vector4 coord = new Vector4(color.r * coord_scale.x, color.g * coord_scale.y, color.b * coord_scale.z, 0);
371 | coord += coord_offset;
372 |
373 | Vector4 coord_frac = Frac(coord);
374 | Vector4 coord_floor = coord - coord_frac;
375 |
376 | Vector2 coord_bot = new Vector2(coord.x + coord_floor.z * texel_height_X0.x, coord.y + coord_floor.z * texel_height_X0.y);
377 | Vector2 coord_top = coord_bot + texel_height_X0;
378 |
379 | Color lutcol_bot = LUT.GetPixelBilinear(coord_bot.x, coord_bot.y);
380 | Color lutcol_top = LUT.GetPixelBilinear(coord_top.x, coord_top.y);
381 |
382 | //Color lutcol_bot = LUT.GetPixel((int)(coord_bot.x * LUT.width), (int)(coord_bot.y * LUT.height));
383 | //Color lutcol_top = LUT.GetPixel((int)(coord_top.x * LUT.width), (int)(coord_top.x * LUT.height));
384 |
385 | return Color.Lerp(lutcol_bot, lutcol_top, coord_frac.z);
386 | */
387 |
388 | float div = 1.0f / 8.0f;
389 | float red = (float)color.r * div;
390 | float green = (float)color.g * div;
391 | float blue = (float)color.b * div;
392 |
393 | float u = Frac(red);
394 | float v = Frac(green);
395 | float w = Frac(blue);
396 |
397 | int x = Mathf.FloorToInt(red);
398 | int y = Mathf.FloorToInt(green);
399 | int z0 = Mathf.FloorToInt(blue);
400 | int z1 = z0 < 31 ? z0 + 1 : z0;
401 |
402 | Color A = LUTSample(LUT, x, y, z0, u, v);
403 | Color B = LUTSample(LUT, x, y, z0, u, v);
404 |
405 | return Color.Lerp(A, B, w);
406 | }
407 |
408 | #endif
409 | #endregion
410 |
411 | #region RANDOM
412 | ///
413 | /// Generates normally distributed numbers. Each operation makes two Gaussians for the price of one, and apparently they can be cached or something for better performance, but who cares.
414 | ///
415 | ///
416 | /// Mean of the distribution
417 | /// Standard deviation
418 | ///
419 | public static float RandomGaussian(float mu = 0, float sigma = 1)
420 | {
421 | var u1 = RandomFloat(0, 1);
422 | var u2 = RandomFloat(0, 1);
423 |
424 | var rand_std_normal = Sqrt(-2.0f * Log(u1)) * Sin(2.0f * PI * u2);
425 |
426 | var rand_normal = mu + sigma * rand_std_normal;
427 |
428 | return rand_normal;
429 | }
430 |
431 | public static float RandomAngle(float minDegrees, float maxDegrees, float step = 1.0f)
432 | {
433 | return Deg2Rad * RandomFloat(minDegrees, maxDegrees);
434 | }
435 |
436 | #if !UNITY
437 | private static Random _random = new Random();
438 | #endif
439 |
440 | public static float RandomFloat(float min, float max)
441 | {
442 | #if UNITY
443 | return UnityEngine.Random.Range(min, max);
444 | #else
445 | return min + (float)(_random.NextDouble() * (max - min));
446 | #endif
447 | }
448 |
449 | ///
450 | /// Returns a value between min and max - 1
451 | ///
452 | ///
453 | ///
454 | ///
455 | public static int RandomInt(int min, int max)
456 | {
457 | #if UNITY
458 | return UnityEngine.Random.Range(min, max);
459 | #else
460 | return min + _random.Next(max - min);
461 | #endif
462 | }
463 |
464 | public static T RandomEnum()
465 | {
466 | var v = Enum.GetValues(typeof(T));
467 | return (T)v.GetValue(RandomInt(0, v.Length));
468 | }
469 |
470 | #endregion
471 |
472 | }
473 | }
474 |
--------------------------------------------------------------------------------
/WangTileLib/WangUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 |
5 | //http://s358455341.websitehome.co.uk/stagecast/wang/intro.html
6 | namespace WangTiles.Core
7 | {
8 | public enum WangEdgeDirection
9 | {
10 | North = 0,
11 | East = 1,
12 | South = 2,
13 | West = 3
14 | }
15 |
16 | #region EDGE
17 | public static class WangEdgeUtils
18 | {
19 | ///
20 | /// Returns bools for all 4 directions, if true, the tile has a connection in that direction
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | ///
27 | public static void GetConnectionsForTile(int tileID, out bool north, out bool east, out bool south, out bool west)
28 | {
29 | north = GetConnectionForTile(tileID, WangEdgeDirection.North);
30 | east = GetConnectionForTile(tileID, WangEdgeDirection.East);
31 | south = GetConnectionForTile(tileID, WangEdgeDirection.South);
32 | west = GetConnectionForTile(tileID, WangEdgeDirection.West);
33 | }
34 |
35 | public static bool GetConnectionForTile(int tileID, WangEdgeDirection direction)
36 | {
37 | int mask = 1 << ((int)direction);
38 | return (tileID & mask) != 0;
39 | }
40 |
41 | ///
42 | /// Returns the opposite direction (eg: North -> South)
43 | ///
44 | ///
45 | ///
46 | public static WangEdgeDirection InvertDirection(WangEdgeDirection direction)
47 | {
48 | switch (direction)
49 | {
50 | case WangEdgeDirection.North: return WangEdgeDirection.South;
51 | case WangEdgeDirection.South: return WangEdgeDirection.North;
52 | case WangEdgeDirection.East: return WangEdgeDirection.West;
53 | default: return WangEdgeDirection.East;
54 | }
55 | }
56 |
57 | ///
58 | /// Returns a list of all possible tiles that can connect with a specified set of neighbors
59 | ///
60 | ///
61 | ///
62 | ///
63 | public static List GetPossibleMatches(int left, int right, int up, int down)
64 | {
65 | //ushort matchID = (ushort)(left + (right << 4) + (up << 8) + (down << 12));
66 |
67 | List result = new List();
68 | for (int newID = 0; newID < 16; newID++)
69 | {
70 | if (!MatchTile(left, newID, WangEdgeDirection.East)) { continue; }
71 | if (!MatchTile(right, newID, WangEdgeDirection.West)) { continue; }
72 | if (!MatchTile(up, newID, WangEdgeDirection.South)) { continue; }
73 | if (!MatchTile(down, newID, WangEdgeDirection.North)) { continue; }
74 |
75 | result.Add(newID);
76 | }
77 |
78 | return result;
79 | }
80 |
81 | ///
82 | /// Checks if the two tiles can be placed in an adjancent manner
83 | ///
84 | ///
85 | ///
86 | ///
87 | public static bool MatchTile(int currentID, int newID, WangEdgeDirection newDirection)
88 | {
89 | if (currentID == -1 || newID == -1)
90 | {
91 | return true;
92 | }
93 |
94 | bool currentSide = GetConnectionForTile(currentID, newDirection);
95 | bool newSide = GetConnectionForTile(newID, InvertDirection(newDirection));
96 | return currentSide == newSide;
97 | }
98 |
99 | public static int AddConnection(int tileID, WangEdgeDirection direction)
100 | {
101 | int mask = 1 << ((int)direction);
102 | return tileID | mask;
103 | }
104 | }
105 | #endregion
106 |
107 | public enum WangCornerDirection
108 | {
109 | Northeast = 0,
110 | Southeast = 1,
111 | Southwest = 2,
112 | Northwest = 3
113 | }
114 |
115 | #region CORNER
116 | public static class WangCornerUtils
117 | {
118 | ///
119 | /// Returns bools for all 4 directions, if true, the tile has a connection in that direction
120 | ///
121 | ///
122 | ///
123 | ///
124 | ///
125 | ///
126 | public static void GetConnectionsForTile(int tileID, out bool northeast, out bool northwest, out bool southeast, out bool southwest)
127 | {
128 | northeast = GetConnectionForTile(tileID, WangCornerDirection.Northeast);
129 | northwest = GetConnectionForTile(tileID, WangCornerDirection.Northwest);
130 | southeast = GetConnectionForTile(tileID, WangCornerDirection.Southeast);
131 | southwest = GetConnectionForTile(tileID, WangCornerDirection.Southwest);
132 | }
133 |
134 | public static bool GetConnectionForTile(int tileID, WangCornerDirection direction)
135 | {
136 | int mask = 1 << ((int)direction);
137 | return (tileID & mask) != 0;
138 | }
139 |
140 | ///
141 | /// Returns the opposite direction (eg: North -> South)
142 | ///
143 | ///
144 | ///
145 | public static WangCornerDirection InvertDirection(WangCornerDirection direction)
146 | {
147 | switch (direction)
148 | {
149 | case WangCornerDirection.Northeast: return WangCornerDirection.Southwest;
150 | case WangCornerDirection.Northwest: return WangCornerDirection.Southeast;
151 | case WangCornerDirection.Southeast: return WangCornerDirection.Northwest;
152 | default: return WangCornerDirection.Northeast;
153 | }
154 | }
155 |
156 | ///
157 | /// Returns a list of all possible tiles that can connect with a specified set of neighbors
158 | ///
159 | ///
160 | ///
161 | ///
162 | public static List GetPossibleMatches(int left, int right, int up, int down, int northwest, int northeast, int southwest, int southeast)
163 | {
164 | List result = new List();
165 | for (int newID = 0; newID < 16; newID++)
166 | {
167 | if (!MatchTile(newID, WangCornerDirection.Northeast, northeast, WangCornerDirection.Southwest)) { continue; }
168 | if (!MatchTile(newID, WangCornerDirection.Northwest, northwest, WangCornerDirection.Southeast)) { continue; }
169 |
170 | if (!MatchTile(newID, WangCornerDirection.Southeast, southeast, WangCornerDirection.Northwest)) { continue; }
171 | if (!MatchTile(newID, WangCornerDirection.Southwest, southwest, WangCornerDirection.Northeast)) { continue; }
172 |
173 | if (!MatchTile(newID, WangCornerDirection.Northeast, right, WangCornerDirection.Northwest)) { continue; }
174 | if (!MatchTile(newID, WangCornerDirection.Southeast, right, WangCornerDirection.Southwest)) { continue; }
175 |
176 | if (!MatchTile(newID, WangCornerDirection.Northwest, left, WangCornerDirection.Northeast)) { continue; }
177 | if (!MatchTile(newID, WangCornerDirection.Southwest, left, WangCornerDirection.Southeast)) { continue; }
178 |
179 | if (!MatchTile(newID, WangCornerDirection.Northeast, up, WangCornerDirection.Southeast)) { continue; }
180 | if (!MatchTile(newID, WangCornerDirection.Northwest, up, WangCornerDirection.Southwest)) { continue; }
181 |
182 | if (!MatchTile(newID, WangCornerDirection.Southeast, down, WangCornerDirection.Northeast)) { continue; }
183 | if (!MatchTile(newID, WangCornerDirection.Southwest, down, WangCornerDirection.Northwest)) { continue; }
184 |
185 | result.Add(newID);
186 | }
187 |
188 | return result;
189 | }
190 |
191 | ///
192 | /// Checks if the two tiles can be placed in an adjancent manner
193 | ///
194 | ///
195 | ///
196 | ///
197 | public static bool MatchTile(int currentID, WangCornerDirection currentDirection, int newID, WangCornerDirection newDirection)
198 | {
199 | if (currentID == -1 || newID == -1)
200 | {
201 | return true;
202 | }
203 |
204 | bool currentSide = GetConnectionForTile(currentID, currentDirection);
205 | bool newSide = GetConnectionForTile(newID, newDirection);
206 | return currentSide == newSide;
207 | }
208 |
209 | public static int AddConnection(int tileID, WangEdgeDirection direction)
210 | {
211 | int mask = 1 << ((int)direction);
212 | return tileID | mask;
213 | }
214 | }
215 | #endregion
216 |
217 | public class WangArea
218 | {
219 | public int ID;
220 | public List children = new List();
221 |
222 | public static Color GetColor(WangArea area)
223 | {
224 | if (area == null)
225 | {
226 | return Color.FromArgb(0);
227 | }
228 |
229 | return area.GetColor();
230 | }
231 |
232 | public override string ToString()
233 | {
234 | return "Area "+ID.ToString()+ " ("+ GetColor().ToString() +")";
235 | }
236 |
237 | public Color GetColor()
238 | {
239 | switch (ID)
240 | {
241 | case 0: return Color.Red;
242 | case 1: return Color.Green;
243 | case 2: return Color.Blue;
244 | case 3: return Color.Yellow;
245 | case 4: return Color.Magenta;
246 | case 5: return Color.Cyan;
247 | case 6: return Color.Orange;
248 | case 7: return Color.Purple;
249 | case 8: return Color.Gray;
250 | case 9: return Color.Beige;
251 | case 10: return Color.Chocolate;
252 | default: return Color.Black;
253 | }
254 | }
255 | }
256 |
257 | public struct WangTile
258 | {
259 | public int tileID;
260 | public int variationID;
261 | public WangArea areaID;
262 | }
263 |
264 | public struct WangMapExit
265 | {
266 | public int x;
267 | public int y;
268 | public WangEdgeDirection direction;
269 |
270 | public WangMapExit(int x, int y, WangEdgeDirection dir)
271 | {
272 | this.x = x;
273 | this.y = y;
274 | this.direction = dir;
275 | }
276 | }
277 |
278 | public struct WangEdgeTile
279 | {
280 | public int x1;
281 | public int y1;
282 | public int x2;
283 | public int y2;
284 | public WangEdgeDirection exit;
285 | }
286 |
287 | public abstract class WangMap
288 | {
289 | protected int _width = 16;
290 | public int Width { get { return _width; } }
291 |
292 | protected int _height = 10;
293 | public int Height { get { return _height; } }
294 |
295 | protected WangTile[] tiles;
296 | protected Random rnd;
297 |
298 | public WangMap(int width, int height, int seed = 0)
299 | {
300 | if (seed == 0)
301 | {
302 | seed = Environment.TickCount;
303 | }
304 |
305 | this.rnd = new Random(seed);
306 |
307 | this._width = width;
308 | this._height = height;
309 | tiles = new WangTile[_width * _height];
310 |
311 | // first clear all tiles
312 | for (int j = 0; j < _height; j++)
313 | {
314 | for (int i = 0; i < _width; i++)
315 | {
316 | int ofs = GetTileOffset(i, j);
317 | tiles[ofs].areaID = null;
318 | tiles[ofs].tileID = -1;
319 | }
320 | }
321 | }
322 |
323 | public void SetTileIDAt(int x, int y, int val)
324 | {
325 | int ofs = GetTileOffset(x, y);
326 | tiles[ofs].tileID = val;
327 |
328 | int n = (x * y) + (x | y);
329 | int variation;
330 | switch (val)
331 | {
332 | case 0: variation = 0; break;
333 | case 1: case 2: case 4: case 8: variation = n % 3; break;
334 | case 3: case 6: case 9: case 12: variation = n % 3; break;
335 | case 5: case 10: variation = n % 4; break;
336 | case 15: variation = n % 2; break;
337 |
338 | default: variation = n % 3; break;
339 | }
340 |
341 |
342 | tiles[ofs].variationID = variation;
343 | }
344 |
345 | public abstract WangTile GetTileAt(int x, int y);
346 |
347 | public int GetTileOffset(int x, int y)
348 | {
349 | return x + y * _width;
350 | }
351 |
352 |
353 | public void Invert()
354 | {
355 | for (int j = 0; j < _height; j++)
356 | {
357 | for (int i = 0; i < _width; i++)
358 | {
359 | int current = GetTileAt(i, j).tileID;
360 | if (current < 0) // if tile is not set, skip
361 | {
362 | continue;
363 | }
364 |
365 | SetTileIDAt(i, j, 15 - current);
366 | }
367 | }
368 | }
369 | }
370 |
371 |
372 |
373 | #region EDGE MAP
374 | public class WangEdgeMap : WangMap
375 | {
376 | private bool _wrapX = false;
377 | private bool _wrapY = false;
378 |
379 |
380 | private List exits = new List();
381 |
382 | public WangEdgeMap(int width, int height, int seed = 0, bool wrapX = false, bool wrapY = false) : base (width, height, seed)
383 | {
384 | this._wrapX = wrapX;
385 | this._wrapY = wrapY;
386 | }
387 |
388 | public override WangTile GetTileAt(int x, int y)
389 | {
390 | if (x < 0 || y < 0 || x >= Width || y >= Height)
391 | {
392 | foreach (var exit in exits)
393 | {
394 | if (exit.x == x && exit.y == y)
395 | {
396 | var result = new WangTile();
397 | result.tileID = WangEdgeUtils.AddConnection(0, exit.direction);
398 | result.areaID = null;
399 | return result;
400 | }
401 | }
402 | }
403 |
404 | if (_wrapX && x < 0)
405 | {
406 | x += _width;
407 | }
408 |
409 | if (_wrapX && x >= _width)
410 | {
411 | x -= _width;
412 | }
413 |
414 | if (_wrapY && y < 0)
415 | {
416 | y += _height;
417 | }
418 |
419 | if (_wrapY && y >= _height)
420 | {
421 | y -= _height;
422 | }
423 |
424 | if (x < 0 || y < 0 || x >= _width || y >= _height)
425 | {
426 | var result = new WangTile();
427 | result.tileID = 0;
428 | result.areaID = null;
429 | return result;
430 | }
431 | return tiles[GetTileOffset(x, y)];
432 | }
433 |
434 | public bool AddExit(int x, int y)
435 | {
436 | WangEdgeDirection dir;
437 |
438 | if (y < 0) { dir = WangEdgeDirection.South; }
439 | else
440 | if (y >= Height) { dir = WangEdgeDirection.North; }
441 | else
442 | if (x < 0) { dir = WangEdgeDirection.East; }
443 | else
444 | if (x >= Width) { dir = WangEdgeDirection.West; }
445 | else
446 | {
447 | return false;
448 | }
449 |
450 | exits.Add(new WangMapExit(x, y, dir));
451 | return true;
452 | }
453 |
454 | private static void AddEdgeTile(Dictionary> result, WangArea otherArea, int x1, int y1, int x2, int y2, WangEdgeDirection exitDir)
455 | {
456 | if (!result.ContainsKey(otherArea))
457 | {
458 | result[otherArea] = new List();
459 | }
460 |
461 | WangEdgeTile et = new WangEdgeTile();
462 | et.x1 = x1;
463 | et.y1 = y1;
464 | et.x2 = x2;
465 | et.y2 = y2;
466 | et.exit = exitDir;
467 | result[otherArea].Add(et);
468 | }
469 |
470 | private Dictionary> FindTilesInAreaEdges(WangArea curArea)
471 | {
472 | Dictionary> result = new Dictionary>();
473 | for (int j = 0; j < this.Height; j++)
474 | {
475 | for (int i = 0; i < this.Width; i++)
476 | {
477 | int ofs = GetTileOffset(i, j);
478 | if (tiles[ofs].areaID != curArea) // check if this tile belongs to the area we are testing
479 | {
480 | continue;
481 | }
482 |
483 | var otherArea = GetTileAt(i - 1, j).areaID;
484 | if (i > 0 && otherArea != curArea && otherArea != null)
485 | {
486 | AddEdgeTile(result, otherArea, i, j, i - 1, j, WangEdgeDirection.West);
487 | }
488 |
489 | otherArea = GetTileAt(i, j - 1).areaID;
490 | if (j > 0 && otherArea != curArea && otherArea != null)
491 | {
492 | AddEdgeTile(result, otherArea, i, j, i, j - 1, WangEdgeDirection.North);
493 | }
494 |
495 | otherArea = GetTileAt(i + 1, j).areaID;
496 | if (i < _width - 1 && otherArea != curArea && otherArea != null)
497 | {
498 | AddEdgeTile(result, otherArea, i, j, i + 1, j, WangEdgeDirection.East);
499 | }
500 |
501 | otherArea = GetTileAt(i, j + 1).areaID;
502 | if (j < _height - 1 && otherArea != curArea && otherArea != null)
503 | {
504 | AddEdgeTile(result, otherArea, i, j, i, j + 1, WangEdgeDirection.South);
505 | }
506 | }
507 | }
508 |
509 | return result;
510 | }
511 |
512 |
513 | private void FloodFillArea(int x, int y, WangArea areaID)
514 | {
515 | int ofs = GetTileOffset(x, y);
516 | tiles[ofs].areaID = areaID;
517 |
518 | // flood all adjacent tiles if possible
519 | FloodFillArea(x - 1, y, areaID, WangEdgeDirection.East);
520 | FloodFillArea(x + 1, y, areaID, WangEdgeDirection.West);
521 | FloodFillArea(x, y - 1, areaID, WangEdgeDirection.South);
522 | FloodFillArea(x, y + 1, areaID, WangEdgeDirection.North);
523 | }
524 |
525 | private void FloodFillArea(int x, int y, WangArea areaID, WangEdgeDirection direction)
526 | {
527 | if (x < 0 || y < 0 || x >= _width || y >= _height) // if out of bounds, return
528 | {
529 | return;
530 | }
531 |
532 | int ofs = GetTileOffset(x, y);
533 | if (tiles[ofs].areaID != null) // if already filled then stop
534 | {
535 | return;
536 | }
537 |
538 | if (tiles[ofs].tileID == 0) // empty tiles should not be filled
539 | {
540 | return;
541 | }
542 |
543 | if (WangEdgeUtils.GetConnectionForTile(tiles[ofs].tileID, direction) == false)
544 | {
545 | return;
546 | }
547 |
548 | FloodFillArea(x, y, areaID);
549 | }
550 |
551 | private bool JoinArea(WangArea parent, WangArea area, HashSet areaSet)
552 | {
553 | if (areaSet.Contains(area))
554 | {
555 | return false;
556 | }
557 |
558 | areaSet.Add(area);
559 |
560 | //Console.WriteLine("Testing area: " + GetAreaColor(area));
561 |
562 | var result = FindTilesInAreaEdges(area);
563 | if (result.Count <= 0)
564 | {
565 | return true;
566 | }
567 |
568 | foreach (var temp in result)
569 | {
570 | if (JoinArea(area, temp.Key, areaSet))
571 | {
572 | area.children.Add(temp.Key);
573 |
574 | var tiles = temp.Value;
575 | var tile = tiles[rnd.Next(tiles.Count)];
576 |
577 | //Console.WriteLine("Joined " + GetAreaColor(area) + " to " + GetAreaColor(temp.Key));
578 |
579 | int ofs = GetTileOffset(tile.x1, tile.y1);
580 | SetTileIDAt(tile.x1, tile.y1, WangEdgeUtils.AddConnection(this.tiles[ofs].tileID, tile.exit));
581 |
582 | ofs = GetTileOffset(tile.x2, tile.y2);
583 | SetTileIDAt(tile.x2, tile.y2, WangEdgeUtils.AddConnection(this.tiles[ofs].tileID, WangEdgeUtils.InvertDirection(tile.exit)));
584 | }
585 | }
586 |
587 | return true;
588 | }
589 |
590 | public void FixConnectivity()
591 | {
592 | // optional, detect isolate areas
593 | List areas = new List();
594 | for (int j = 0; j < Height; j++)
595 | {
596 | for (int i = 0; i < _width; i++)
597 | {
598 | int ofs = GetTileOffset(i, j);
599 | if (tiles[ofs].areaID != null) // check if this tile already have an area assigned
600 | {
601 | continue;
602 | }
603 |
604 | if (tiles[ofs].tileID == 0) // empty tiles should not be filled
605 | {
606 | continue;
607 | }
608 |
609 | var area = new WangArea();
610 | area.ID = areas.Count;
611 | areas.Add(area);
612 | FloodFillArea(i, j, area);
613 | }
614 | }
615 |
616 | // join the isolated areas by adding exits to some tiles
617 | HashSet areaSet = new HashSet();
618 | for (int k = 0; k < areas.Count; k++)
619 | {
620 | var area = areas[k];
621 | if (k == 0)
622 | {
623 | JoinArea(null, area, areaSet);
624 | }
625 | else
626 | if (!areaSet.Contains(area))
627 | {
628 | DeleteArea(area);
629 | }
630 | }
631 | }
632 |
633 | private void DeleteArea(WangArea area)
634 | {
635 | for (int j = 0; j < _height; j++)
636 | {
637 | for (int i = 0; i < _width; i++)
638 | {
639 | if (GetTileAt(i, j).areaID == area)
640 | {
641 | SetTileIDAt(i, j, 0);
642 | }
643 | }
644 | }
645 | }
646 |
647 | public void Generate()
648 | {
649 | // first pass places random tiles in a checkerboard pattern
650 | for (int j = 0; j < _height; j++)
651 | {
652 | for (int i = 0; i < _width; i++)
653 | {
654 | if (i % 2 == j % 2)
655 | {
656 | continue;
657 | }
658 |
659 | TryPlacingRandomTile(i, j);
660 | }
661 | }
662 |
663 | // second pass places random tiles in the mising holes, checking for matching edges
664 | for (int j = 0; j < _height; j++)
665 | {
666 | for (int i = 0; i < _width; i++)
667 | {
668 | int current = GetTileAt(i, j).tileID;
669 | if (current >= 0) // if tile already set, skip
670 | {
671 | continue;
672 | }
673 |
674 | //continue;
675 |
676 | TryPlacingRandomTile(i, j);
677 | }
678 | }
679 | }
680 |
681 | protected void TryPlacingRandomTile(int i, int j)
682 | {
683 | int left = GetTileAt(i - 1, j).tileID;
684 | int right = GetTileAt(i + 1, j).tileID;
685 | int up = GetTileAt(i, j - 1).tileID;
686 | int down = GetTileAt(i, j + 1).tileID;
687 |
688 | var matches = WangEdgeUtils.GetPossibleMatches(left, right, up, down);
689 |
690 | if (matches.Count <= 0)
691 | {
692 | return;
693 | }
694 |
695 | int n = matches[rnd.Next(matches.Count)];
696 | SetTileIDAt(i, j, n);
697 | }
698 |
699 | }
700 |
701 | #endregion
702 |
703 | #region CORNER MAP
704 | public class WangCornerMap : WangMap
705 | {
706 | public WangCornerMap(int width, int height, int seed = 0) : base(width, height, seed)
707 | {
708 | }
709 |
710 | public override WangTile GetTileAt(int x, int y)
711 | {
712 | if (x < 0 || y < 0 || x >= _width || y >= _height)
713 | {
714 | var result = new WangTile();
715 | result.tileID = -1;
716 | result.areaID = null;
717 | return result;
718 | }
719 | return tiles[GetTileOffset(x, y)];
720 | }
721 |
722 |
723 | public void Generate()
724 | {
725 | // first pass places random tiles in a checkerboard pattern
726 | int n = 12;
727 | for (int j = 0; j < _height; j++)
728 | {
729 | for (int i = 0; i < _width; i++)
730 | {
731 | TryPlacingRandomTile(i, j);
732 | //n--; if (n == 0) return;
733 | }
734 | }
735 | }
736 |
737 |
738 | protected void TryPlacingRandomTile(int i, int j)
739 | {
740 | int west = GetTileAt(i - 1, j).tileID;
741 | int east = GetTileAt(i + 1, j).tileID;
742 | int north = GetTileAt(i, j - 1).tileID;
743 | int south = GetTileAt(i, j + 1).tileID;
744 |
745 | int northwest = GetTileAt(i - 1, j - 1).tileID;
746 | int northeast = GetTileAt(i + 1, j - 1).tileID;
747 | int southwest = GetTileAt(i - 1, j + 1).tileID;
748 | int southeast = GetTileAt(i + 1, j + 1).tileID;
749 |
750 | var matches = WangCornerUtils.GetPossibleMatches(west, east, north, south, northwest, northeast, southwest, southeast);
751 |
752 | if (matches.Count <= 0)
753 | {
754 | return;
755 | }
756 |
757 | int n = matches[rnd.Next(matches.Count)];
758 | SetTileIDAt(i, j, n);
759 | }
760 | }
761 |
762 | #endregion
763 |
764 | }
765 |
--------------------------------------------------------------------------------
/WangTileLib/DungeonPlanner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | using WangTiles.Core;
5 |
6 | namespace WangTiles.DungeonPlanner
7 | {
8 | public struct LayoutCoord
9 | {
10 | public int X;
11 | public int Y;
12 |
13 | public LayoutCoord(int x, int y)
14 | {
15 | this.X = x;
16 | this.Y = y;
17 | }
18 |
19 | public override int GetHashCode()
20 | {
21 | unchecked // Overflow is fine, just wrap
22 | {
23 | int hash = 17;
24 | // Suitable nullity checks etc, of course :)
25 | hash = hash * 23 + X.GetHashCode();
26 | hash = hash * 23 + Y.GetHashCode();
27 | return hash;
28 | }
29 | }
30 | }
31 |
32 | public class LayoutConnection
33 | {
34 | public LayoutRoom roomA;
35 | public LayoutRoom roomB;
36 | public bool active;
37 | public int weight;
38 | public int pathOrder;
39 |
40 | public LayoutConnection(LayoutRoom roomA, LayoutRoom roomB, Random rnd)
41 | {
42 | active = true;
43 | weight = 1 + rnd.Next(10);
44 | pathOrder = -1;
45 | this.roomA = roomA;
46 | this.roomB = roomB;
47 | }
48 |
49 | public static void QuickSort(List edgesList, int nLeft, int nRight)
50 | {
51 | int i, j, x;
52 | i = nLeft; j = nRight;
53 | x = edgesList[(nLeft + nRight) / 2].weight;
54 |
55 | do
56 | {
57 | while ((edgesList[i].weight < x) && (i < nRight)) i++;
58 | while ((x < edgesList[j].weight) && (j > nLeft)) j--;
59 |
60 | if (i <= j)
61 | {
62 | LayoutConnection y = edgesList[i];
63 | edgesList[i] = edgesList[j];
64 | edgesList[j] = y;
65 | i++; j--;
66 | }
67 | } while (i <= j);
68 |
69 | if (nLeft < j) QuickSort(edgesList, nLeft, j);
70 | if (i < nRight) QuickSort(edgesList, i, nRight);
71 | }
72 | }
73 |
74 | public class LayoutRoom
75 | {
76 | public enum RoomKind
77 | {
78 | Invalid,
79 | Entrance,
80 | Hall,
81 | Puzzle,
82 | Monster,
83 | Farm,
84 | Shrine,
85 | Treasure,
86 | Goal
87 | }
88 |
89 | public enum RoomShape
90 | {
91 | Empty,
92 | Curve,
93 | Corridor,
94 | Room,
95 | Split
96 | }
97 |
98 | public enum RoomCategory
99 | {
100 | Unknown,
101 | Main,
102 | Side,
103 | Distant,
104 | Secret
105 | }
106 |
107 | public string name;
108 | private LayoutRoom root;
109 | public int rank;
110 |
111 | public LayoutCoord coord;
112 |
113 | public List connections = new List();
114 |
115 | public int distance;
116 | public LayoutRoom previous;
117 |
118 | public RoomKind kind;
119 |
120 |
121 | public RoomCategory category;
122 | public bool isLoop;
123 | public int distanceFromMainPath;
124 |
125 | public float intensity;
126 | public bool spike;
127 |
128 | public bool lockable;
129 | public bool important;
130 |
131 | public LayoutKey require;
132 | public LayoutKey contains;
133 | public LayoutKey locked;
134 |
135 | public int order;
136 |
137 | public int tileID;
138 | public int variationID;
139 |
140 | public LayoutRoom parent;
141 | public List children = new List();
142 |
143 | public LayoutRoom(LayoutCoord coord)
144 | {
145 | this.name = coord.X + " : "+ coord.Y;
146 | this.coord = coord;
147 | this.rank = 0;
148 | this.root = this;
149 |
150 | this.kind = RoomKind.Hall;
151 | this.intensity = -1;
152 | this.lockable = true;
153 | }
154 |
155 | public override string ToString()
156 | {
157 | return this.kind + " ("+ name+")";
158 | }
159 |
160 | public bool isDeadEnd()
161 | {
162 | switch (tileID)
163 | {
164 | case 1: case 2: case 4: case 8: return true;
165 | default: return false;
166 | }
167 | }
168 |
169 |
170 | public RoomShape GetShape()
171 | {
172 | if (tileID>0 && variationID == 1)
173 | {
174 | return RoomShape.Room;
175 | }
176 |
177 |
178 | switch (tileID)
179 | {
180 | case 1: case 2: case 4: case 8: return RoomShape.Room;
181 | case 3: case 6: case 9: case 12: return RoomShape.Curve;
182 | case 5: case 10: return RoomShape.Corridor;
183 | case 7: case 11: case 13: case 14: case 15: return RoomShape.Split;
184 | default: return RoomShape.Empty;
185 | }
186 | }
187 |
188 | internal LayoutRoom GetRoot()
189 | {
190 | if (this.root != this)// am I my own parent ? (am i the root ?)
191 | {
192 | this.root = this.root.GetRoot();// No? then get my parent
193 | }
194 | return this.root;
195 | }
196 |
197 | internal static void Join(LayoutRoom vRoot1, LayoutRoom vRoot2)
198 | {
199 |
200 | if (vRoot2.rank < vRoot1.rank)//is the rank of Root2 less than that of Root1 ?
201 | {
202 | vRoot2.root = vRoot1;//yes! then Root1 is the parent of Root2 (since it has the higher rank)
203 | }
204 | else //rank of Root2 is greater than or equal to that of Root1
205 | {
206 | vRoot1.root = vRoot2;//make Root2 the parent
207 | if (vRoot1.rank == vRoot2.rank)//both ranks are equal ?
208 | {
209 | vRoot1.rank++;//increment one of them, we need to reach a single root for the whole tree
210 | }
211 | }
212 | }
213 |
214 | public LayoutConnection FindConnection(LayoutRoom room)
215 | {
216 | foreach (var conn in connections)
217 | {
218 | if (conn.roomA == room || conn.roomB == room)
219 | {
220 | return conn;
221 | }
222 | }
223 |
224 | return null;
225 | }
226 | }
227 |
228 | public class LayoutKey
229 | {
230 | public string name;
231 | public LayoutRoom sourceRoom;
232 | public LayoutRoom targetRoom;
233 |
234 | public int order;
235 | public int condition;
236 |
237 | public LayoutKey(string name, int order)
238 | {
239 | this.name = name;
240 | this.order = order;
241 | }
242 |
243 | public override string ToString()
244 | {
245 | return this.name;
246 | }
247 | }
248 |
249 |
250 | public class LayoutPlanner
251 | {
252 | private Random randomGenerator;
253 |
254 | public LayoutRoom entrance;
255 | public LayoutRoom goal;
256 |
257 | public List connections = new List();
258 | private Dictionary rooms = new Dictionary();
259 |
260 | public LayoutPlanner(int seed)
261 | {
262 | if (seed == 0)
263 | {
264 | seed = Environment.TickCount;
265 | }
266 | this.randomGenerator = new Random(seed);
267 | }
268 |
269 | #region LAYOUT_SETUP
270 | public LayoutConnection AddConnection(LayoutCoord a, LayoutCoord b)
271 | {
272 | return AddConnection(FindRoomAt(a), FindRoomAt(b));
273 | }
274 |
275 | public LayoutConnection AddConnection(LayoutCoord a, WangEdgeDirection dir)
276 | {
277 | LayoutCoord b;
278 |
279 | switch (dir)
280 | {
281 | case WangEdgeDirection.East: b = new LayoutCoord(a.X + 1, a.Y); break;
282 | case WangEdgeDirection.West: b = new LayoutCoord(a.X - 1, a.Y); break;
283 | case WangEdgeDirection.North: b = new LayoutCoord(a.X, a.Y - 1); break;
284 | case WangEdgeDirection.South: b = new LayoutCoord(a.X, a.Y + 1); break;
285 | default:return null;
286 | }
287 |
288 | return AddConnection(FindRoomAt(a), FindRoomAt(b));
289 | }
290 |
291 | public LayoutConnection AddConnection(LayoutRoom roomA, LayoutRoom roomB)
292 | {
293 | if (roomA == roomB)
294 | {
295 | return null;
296 | }
297 |
298 | LayoutConnection conn = roomA.FindConnection(roomB);
299 | if (conn != null)
300 | {
301 | return conn;
302 | }
303 |
304 | conn = new LayoutConnection(roomA, roomB, this.randomGenerator);
305 | roomA.connections.Add(conn);
306 | roomB.connections.Add(conn);
307 |
308 | this.connections.Add(conn);
309 |
310 | return conn;
311 | }
312 |
313 | public LayoutRoom FindRoomAt(LayoutCoord coord, bool canCreate = true)
314 | {
315 | if (rooms.ContainsKey(coord))
316 | {
317 | return rooms[coord];
318 | }
319 |
320 | if (!canCreate)
321 | {
322 | return null;
323 | }
324 |
325 | var room = new LayoutRoom(coord);
326 | rooms[coord] = room;
327 | return room;
328 | }
329 | #endregion
330 |
331 | public struct RoomScore
332 | {
333 | public int score;
334 | public LayoutRoom room;
335 | public RoomScore(int score, LayoutRoom room)
336 | {
337 | this.score = score;
338 | this.room = room;
339 | }
340 | }
341 |
342 | protected int GetRoomScore(LayoutRoom room)
343 | {
344 | return 10;
345 | }
346 |
347 | public LayoutRoom FindEntrance()
348 | {
349 | entrance = null;
350 |
351 | List temp = new List();
352 | foreach (var room in rooms.Values)
353 | {
354 | int score = GetRoomScore(room);
355 |
356 | if (score < 0)
357 | {
358 | continue;
359 | }
360 |
361 | if (room.isDeadEnd())
362 | {
363 | score *= 2;
364 | }
365 |
366 | temp.Add(new RoomScore(score, room));
367 | }
368 |
369 | if (temp.Count == 0)
370 | {
371 | //LogError("Could not find any room that matches entrance settings");
372 | return null;
373 | }
374 |
375 | temp.Sort(new Comparison((RoomScore x, RoomScore y) =>
376 | {
377 | //if (x.order == y.order) return x.height.CompareTo(y.height); else return x.order.CompareTo(y.order);
378 | return y.score.CompareTo(x.score);
379 | }));
380 |
381 | entrance = temp[randomGenerator.Next((temp.Count < 3 ? temp.Count : 3))].room;
382 |
383 | entrance.kind = LayoutRoom.RoomKind.Entrance;
384 |
385 | return entrance;
386 | }
387 |
388 |
389 | public LayoutRoom FindGoal()
390 | {
391 | if (entrance == null)
392 | {
393 | Console.WriteLine("Entrance is not set...");
394 | return null;
395 | }
396 |
397 | // run dijkstra to find shortest paths
398 | List Q = new List();
399 | foreach (var room in rooms.Values)
400 | {
401 | room.distance = 99999; // Unknown distance from source to v
402 | room.previous = null; // Previous node in optimal path from source
403 | Q.Add(room); // All nodes initially in Q (unvisited nodes)
404 | }
405 |
406 | entrance.distance = 0; // Distance from source to source
407 | while (Q.Count > 0)
408 | {
409 | LayoutRoom best = null;
410 | int min = 9999;
411 | foreach (LayoutRoom other in Q)
412 | {
413 | if (other.distance < min)
414 | {
415 | min = other.distance;
416 | best = other;
417 | }
418 | }
419 |
420 | Q.Remove(best);
421 | foreach (var path in best.connections) // where V is still in Q.
422 | {
423 | LayoutRoom V = path.roomA == best ? path.roomB : path.roomA;
424 | if (!path.active)
425 | {
426 | continue;
427 | }
428 |
429 | if (!Q.Contains(V))
430 | {
431 | continue;
432 | }
433 |
434 | int alt = best.distance + path.weight;
435 | if (alt < V.distance)
436 | {
437 | V.distance = alt;
438 | V.previous = best;
439 | }
440 | }
441 | }
442 |
443 | // find goal room
444 | goal = null;
445 | int maxGoal = 0;
446 | foreach (var room in rooms.Values)
447 | {
448 | if (room == entrance)
449 | {
450 | continue;
451 | }
452 |
453 | if (!room.isDeadEnd())
454 | {
455 | continue;
456 | }
457 |
458 | int dist = room.distance;
459 | if (dist > maxGoal)
460 | {
461 | int score = GetRoomScore(room);
462 | if (score > 0)
463 | {
464 | maxGoal = dist;
465 | goal = room;
466 | }
467 |
468 | }
469 | }
470 |
471 | if (goal == null)
472 | {
473 | //LogError("Could not find a goal...");
474 | return null;
475 | }
476 |
477 | //Debug.LogWarning("Found goal: " + goal.FloorLevel);
478 | return goal;
479 | }
480 |
481 | public void SetGoal(LayoutRoom goal)
482 | {
483 | this.goal = goal;
484 | goal.kind = LayoutRoom.RoomKind.Goal;
485 |
486 | this.GenerateIntensity();
487 | }
488 |
489 | ///
490 | /// Selects a category for each room, based if its a main path, a side path, secret path etc
491 | ///
492 | private void CategorizeRooms()
493 | {
494 | // first set any room in the shortest path from entrance to goal as a 'main' room
495 | var temp = goal;
496 | while (temp != null)
497 | {
498 | temp.category = LayoutRoom.RoomCategory.Main;
499 | temp.distanceFromMainPath = 0;
500 | temp = temp.previous;
501 | }
502 |
503 | foreach (var room in rooms.Values)
504 | {
505 | if (room.category != LayoutRoom.RoomCategory.Main)
506 | {
507 | continue;
508 | }
509 |
510 | foreach (var path in room.connections)
511 | {
512 | var other = path.roomA == room ? path.roomB : path.roomA;
513 |
514 | if (other.category == LayoutRoom.RoomCategory.Unknown)
515 | {
516 | FloodAdjacentsWithCategory(other, 1, room);
517 | }
518 | }
519 | }
520 | }
521 |
522 | private void FloodChildrenWithCategory(LayoutRoom room, int distance)
523 | {
524 | room.category = LayoutRoom.RoomCategory.Distant;
525 | room.distanceFromMainPath = distance;
526 |
527 | foreach (var child in room.children)
528 | {
529 | FloodChildrenWithCategory(child, distance + 1);
530 | }
531 | }
532 |
533 | private void FloodLoopCategory(LayoutRoom source, LayoutRoom dest)
534 | {
535 | if (source.isDeadEnd())
536 | {
537 | return;
538 | }
539 |
540 | source.isLoop = true;
541 |
542 | if (source == dest)
543 | {
544 | return;
545 | }
546 |
547 | foreach (var path in source.connections)
548 | {
549 | var other = path.roomA == source ? path.roomB : path.roomA;
550 |
551 | if (other.order < source.order)
552 | {
553 | continue;
554 | }
555 |
556 | FloodLoopCategory(other, dest);
557 | }
558 | }
559 |
560 | private void FixLoopCategory(LayoutRoom source, LayoutRoom dest, LayoutRoom room)
561 | {
562 | if (source.order > dest.order)
563 | {
564 | var temp = source;
565 | source = dest;
566 | dest = temp;
567 | }
568 |
569 | /*while (room != source)
570 | {
571 |
572 | }*/
573 |
574 | FloodLoopCategory(source, dest);
575 | }
576 |
577 | private void FloodAdjacentsWithCategory(LayoutRoom room, int distance, LayoutRoom parent)
578 | {
579 | if (room.connections.Count>2)
580 | {
581 | FloodChildrenWithCategory(room, distance);
582 | return;
583 | }
584 |
585 | room.category = LayoutRoom.RoomCategory.Side;
586 | room.distanceFromMainPath = distance;
587 |
588 | foreach (var path in room.connections)
589 | {
590 | var other = path.roomA == room ? path.roomB : path.roomA;
591 |
592 | if (other.category == LayoutRoom.RoomCategory.Main && other != parent)
593 | {
594 | FixLoopCategory(other, parent, room);
595 | return;
596 | }
597 |
598 | if (other.category != LayoutRoom.RoomCategory.Unknown)
599 | {
600 | continue;
601 | }
602 |
603 | FloodAdjacentsWithCategory(other, distance + 1, parent);
604 | }
605 | }
606 |
607 | ///
608 | /// Runs Kruskals-Minimum-Spanning-Tree on the dungeon network
609 | ///
610 | ///
611 | private void SolveGraph(List edges)
612 | {
613 | int nTotalCost = 0;
614 | int currentOrder = 0;
615 |
616 | if (edges.Count <= 0)
617 | {
618 | return;
619 | }
620 |
621 | LayoutConnection.QuickSort(edges, 0, edges.Count - 1);
622 | foreach (var ed in edges)
623 | {
624 | if (!ed.active)
625 | {
626 | continue;
627 | }
628 |
629 | LayoutRoom vRoot1, vRoot2;
630 | vRoot1 = ed.roomA.GetRoot();
631 | vRoot2 = ed.roomB.GetRoot();
632 |
633 | if (vRoot1 != vRoot2)
634 | {
635 | nTotalCost += ed.weight;
636 | LayoutRoom.Join(vRoot1, vRoot2);
637 |
638 | ed.pathOrder = currentOrder;
639 | ed.active = true;
640 | currentOrder++;
641 | }
642 | }
643 | }
644 |
645 | public void GenerateProgression()
646 | {
647 | this.SolveGraph(this.connections);
648 |
649 | // generate dungeon semantic tree (which represents the logical progression inside the dungeon)
650 | int currentOrder = 1;
651 | FindChildrenProgression(ref currentOrder, entrance, new List());
652 |
653 | CategorizeRooms();
654 | }
655 |
656 | protected void FindChildrenProgression(ref int currentOrder, LayoutRoom startRoom, List visitedRooms)
657 | {
658 | startRoom.order = currentOrder;
659 | currentOrder++;
660 | visitedRooms.Add(startRoom);
661 |
662 | startRoom.children = new List();
663 | foreach (var path in startRoom.connections)
664 | {
665 | if (path.pathOrder < 0)
666 | {
667 | continue;
668 | }
669 |
670 | var other = path.roomA == startRoom ? path.roomB : path.roomA;
671 | //Console.WriteLine("Found connection to " + other.ToString() + " -> " + other.visited + " order: " + path.pathOrder);
672 |
673 | if (!visitedRooms.Contains(other))
674 | {
675 | other.parent = startRoom;
676 | startRoom.children.Add(other);
677 | FindChildrenProgression(ref currentOrder, other, visitedRooms);
678 | }
679 | }
680 | }
681 |
682 | protected void CalculateIntensity(LayoutRoom room, float value)
683 | {
684 | if (room.intensity >= 0)
685 | {
686 | return;
687 | }
688 |
689 | room.spike = false;
690 |
691 | if (room.kind == LayoutRoom.RoomKind.Goal)
692 | {
693 | value += 1.5f;
694 | room.spike = true;
695 | }
696 | else
697 | {
698 | if (room.contains != null)
699 | {
700 | value += 1.25f;
701 | room.spike = true;
702 | }
703 | else
704 | if (room.important)
705 | {
706 | value += 1;
707 | }
708 | else
709 | {
710 | value += 0.25f;
711 | }
712 |
713 | if (room.kind == LayoutRoom.RoomKind.Treasure)
714 | {
715 | value += 0.5f;
716 | room.spike = true;
717 | }
718 |
719 | if (room.parent != null && room.parent.spike)
720 | {
721 | value -= 2;
722 | }
723 | }
724 |
725 | room.intensity = value;
726 |
727 | foreach (var child in room.children)
728 | {
729 | CalculateIntensity(child, value);
730 | }
731 | }
732 |
733 | protected void GenerateIntensity()
734 | {
735 | if (goal == null)
736 | {
737 | return;
738 | }
739 |
740 | foreach (var room in rooms.Values)
741 | {
742 | room.intensity = -1;
743 | }
744 |
745 | CalculateIntensity(entrance, 0);
746 |
747 | float max = goal.intensity;
748 |
749 | foreach (var room in rooms.Values)
750 | {
751 | if (room.intensity >= max)
752 | {
753 | room.intensity = max;
754 | }
755 | }
756 |
757 | goal.intensity = max;
758 |
759 | // normalize intensities
760 | foreach (var room in rooms.Values)
761 | {
762 | room.intensity /= max;
763 | // Debug.Log(room.Name + " => " + ((int)(room.intensity * 100.0f)).ToString());
764 | }
765 | }
766 |
767 | #region LOCKS_AND_KEYS
768 | /*protected bool CanBeLocked(LayoutRoom room)
769 | {
770 | if (room.parent == null)
771 | {
772 | return false;
773 | }
774 |
775 | if (!room.lockable)
776 | {
777 | return false;
778 | }
779 |
780 | if (room.kind == LayoutRoom.RoomKind.Entrance)
781 | {
782 | return false;
783 | }
784 |
785 | return room.GetShape() != LayoutRoom.RoomShape.DeadEnd;
786 | //return parent.GetBranchesCount() > 1;
787 | }
788 |
789 |
790 | protected LayoutRoom FindLockableRoom(LayoutRoom currentRoom, List testedRooms, float maxIntensity)
791 | {
792 | if (testedRooms.Contains(currentRoom))
793 | {
794 | return null;
795 | }
796 |
797 | testedRooms.Add(currentRoom);
798 |
799 | if (currentRoom.locked == null)
800 | {
801 | if (currentRoom.require != null || (currentRoom.intensity < maxIntensity && CanBeLocked(currentRoom)))
802 | {
803 | return currentRoom;
804 | }
805 |
806 | }
807 |
808 | if (currentRoom.parent == null)
809 | {
810 | return null;
811 | }
812 |
813 | return FindLockableRoom(currentRoom.parent, testedRooms, maxIntensity);
814 | }
815 |
816 | protected void LockRoom(LayoutRoom room, LayoutKey keylock)
817 | {
818 | if (room.locked == null)
819 | {
820 | room.locked = keylock;
821 | Console.WriteLine("Locked " + room.ToString());
822 | }
823 |
824 | foreach (var child in room.children)
825 | {
826 | LockRoom(child, keylock);
827 | }
828 | }
829 |
830 | public bool CanPlaceKey(LayoutRoom room, bool deadEndsonly)
831 | {
832 | if (deadEndsonly && room.GetShape() != LayoutRoom.RoomShape.DeadEnd)
833 | {
834 | return false;
835 | }
836 |
837 | return (room.kind != LayoutRoom.RoomKind.Goal && room.kind != LayoutRoom.RoomKind.Entrance);
838 | }*/
839 |
840 | /*public void PlaceKey(LayoutRoom sourceRoom, LayoutKey key, LayoutRoom targetRoom, List testedRooms, int count, bool deadEndsonly)
841 | {
842 | if (testedRooms.Contains(sourceRoom))
843 | {
844 | return;
845 | }
846 |
847 | if (sourceRoom.locked != null)
848 | {
849 | return;
850 | }
851 |
852 | Console.WriteLine("Testing " + this.ToString());
853 | testedRooms.Add(sourceRoom);
854 |
855 | if (key.room != null)
856 | {
857 | return;
858 | }
859 |
860 |
861 | if (sourceRoom != targetRoom && CanPlaceKey(sourceRoom, deadEndsonly))
862 | {
863 | if (sourceRoom.contains != null)
864 | {
865 | return;
866 | }
867 |
868 | if (count <= 0)
869 | {
870 | Console.WriteLine("Placed " + key.name + " in room " + sourceRoom);
871 |
872 | key.room = sourceRoom;
873 | sourceRoom.contains = key;
874 | return;
875 | }
876 | }
877 |
878 | List rooms = new List();
879 | if (sourceRoom.parent != null && !testedRooms.Contains(sourceRoom.parent))
880 | {
881 | rooms.Add(sourceRoom.parent);
882 | }
883 |
884 |
885 | if (sourceRoom != targetRoom)
886 | {
887 | foreach (var child in sourceRoom.children)
888 | {
889 | if (!testedRooms.Contains(child))
890 | {
891 | rooms.Add(child);
892 | }
893 |
894 | }
895 |
896 | }
897 |
898 | if (rooms.Count <= 0)
899 | {
900 | return;
901 | }
902 |
903 | while (rooms.Count > 0)
904 | {
905 | int n = this.randomGenerator.Next(rooms.Count);
906 | LayoutRoom target = rooms[n];
907 |
908 | PlaceKey(target, key, targetRoom, testedRooms, count - 1, deadEndsonly);
909 | if (key.room != null)
910 | {
911 | return;
912 | }
913 |
914 | rooms.RemoveAt(n);
915 | }
916 | }*/
917 |
918 | protected bool IsRoomImportant(LayoutRoom room)
919 | {
920 | if (room.important)
921 | {
922 | return true;
923 | }
924 |
925 | if (room.contains != null)
926 | {
927 | return true;
928 | }
929 |
930 | foreach (var child in room.children)
931 | {
932 | if (IsRoomImportant(child))
933 | {
934 | return true;
935 | }
936 | }
937 |
938 | return false;
939 | }
940 |
941 | /*private void FindRoomsForKey(LayoutRoom room, List possibleRooms, List visitedRooms)
942 | {
943 | if (visitedRooms.Contains(room))
944 | {
945 | return;
946 | }
947 | visitedRooms.Add(room);
948 |
949 | var shape = room.GetShape();
950 | if (room.category == LayoutRoom.RoomCategory.Distant && (shape == LayoutRoom.RoomShape.DeadEnd || shape == LayoutRoom.RoomShape.Room))
951 | {
952 | possibleRooms.Add(room);
953 | }
954 |
955 | foreach (var child in room.children)
956 | {
957 | FindRoomsForKey(child, possibleRooms, visitedRooms);
958 | }
959 |
960 | if (room.parent != null)
961 | {
962 | FindRoomsForKey(room.parent, possibleRooms, visitedRooms);
963 | }
964 | }*/
965 |
966 | private bool isValidKeyRoom(LayoutRoom room)
967 | {
968 | return room.category == LayoutRoom.RoomCategory.Distant && room.GetShape() == LayoutRoom.RoomShape.Room;
969 | }
970 |
971 | public void GenerateLocks(List keys)
972 | {
973 | if (keys == null || keys.Count <= 0)
974 | {
975 | return;
976 | }
977 |
978 | if (goal == null)
979 | {
980 | Console.WriteLine("Goal is not set...");
981 | return;
982 | }
983 |
984 | keys.Sort((x, y) => x.order.CompareTo(y.order));
985 |
986 | int firstKeyRoom = rooms.Count + 1;
987 | foreach (var room in rooms.Values)
988 | {
989 | if (isValidKeyRoom(room) && room.order < firstKeyRoom)
990 | {
991 | firstKeyRoom = room.order;
992 | }
993 | }
994 |
995 | List possibleLockedRooms = new List();
996 | List possibleKeyRooms = new List();
997 | foreach (var room in rooms.Values)
998 | {
999 | if (room == entrance || room == goal)
1000 | {
1001 | continue;
1002 | }
1003 |
1004 | if (isValidKeyRoom(room))
1005 | {
1006 | possibleKeyRooms.Add(room);
1007 | }
1008 |
1009 | if (room.category == LayoutRoom.RoomCategory.Main && !room.isLoop && room.order > firstKeyRoom)
1010 | {
1011 | possibleLockedRooms.Add(room);
1012 |
1013 | if (room.require != null)
1014 | {
1015 | var key = room.require;
1016 | key.targetRoom = room;
1017 | }
1018 | }
1019 | }
1020 |
1021 | Dictionary> lockableRooms = new Dictionary>();
1022 | int roomSpread = possibleLockedRooms.Count / keys.Count;
1023 |
1024 | possibleLockedRooms.Sort((x, y) => x.order.CompareTo(y.order));
1025 |
1026 | for (int k=0; k=possibleLockedRooms.Count)
1031 | {
1032 | lastRoom = possibleLockedRooms.Count - 1;
1033 | }
1034 |
1035 | var key = keys[k];
1036 | lockableRooms[key] = new List();
1037 | for (int i=startRoom; i<=lastRoom; i++)
1038 | {
1039 | lockableRooms[key].Add(possibleLockedRooms[i]);
1040 | }
1041 | }
1042 |
1043 | foreach (LayoutKey key in keys)
1044 | {
1045 | if (key.targetRoom != null)
1046 | {
1047 | continue;
1048 | }
1049 |
1050 | var possibleTargets = lockableRooms[key];
1051 |
1052 | if (possibleTargets.Count == 0)
1053 | {
1054 | continue;
1055 | }
1056 |
1057 | LayoutRoom targetRoom = possibleTargets[randomGenerator.Next(possibleTargets.Count)];
1058 | key.targetRoom = targetRoom;
1059 | }
1060 |
1061 | List originalKeys = new List();
1062 | foreach (LayoutKey key in keys)
1063 | {
1064 | originalKeys.Add(key);
1065 | }
1066 |
1067 | keys.Sort((x, y) => y.order.CompareTo(x.order));
1068 | for (int k=0; k possibleSources = new List();
1083 | foreach (var room in possibleKeyRooms)
1084 | {
1085 | if (room.order > key.targetRoom.order)
1086 | {
1087 | continue;
1088 | }
1089 |
1090 | if (nextKey!=null && room.order<=nextKey.targetRoom.order)
1091 | {
1092 | continue;
1093 | }
1094 |
1095 | possibleSources.Add(room);
1096 | }
1097 |
1098 | if (possibleSources.Count == 0)
1099 | {
1100 | foreach (var room in rooms.Values)
1101 | {
1102 | if (room.category != LayoutRoom.RoomCategory.Side)
1103 | {
1104 | continue;
1105 | }
1106 |
1107 | if (room.order > key.targetRoom.order)
1108 | {
1109 | continue;
1110 | }
1111 |
1112 | if (nextKey != null && room.order <= nextKey.targetRoom.order)
1113 | {
1114 | continue;
1115 | }
1116 |
1117 | possibleSources.Add(room);
1118 | }
1119 | }
1120 |
1121 | if (possibleSources.Count == 0)
1122 | {
1123 | continue;
1124 | }
1125 |
1126 | var sourceRoom = possibleSources[randomGenerator.Next(possibleSources.Count)];
1127 | key.sourceRoom = sourceRoom;
1128 | sourceRoom.contains = key;
1129 |
1130 | possibleKeyRooms.Remove(sourceRoom);
1131 |
1132 | Console.WriteLine("Placed " + key.name + " in room " + sourceRoom);
1133 | }
1134 |
1135 | foreach (LayoutKey key in originalKeys)
1136 | {
1137 | if (key.sourceRoom == null)
1138 | {
1139 | Console.WriteLine("Key '" + key.name + "' was not placed...");
1140 | }
1141 | }
1142 |
1143 | // after generating the locks, its now possible to understand which rooms are important and which ones are optional
1144 | goal.important = true;
1145 | entrance.important = true;
1146 | foreach (var room in rooms.Values)
1147 | {
1148 | room.important = IsRoomImportant(room);
1149 | }
1150 |
1151 | // generate intensity for rooms based on tension curve
1152 | GenerateIntensity();
1153 | }
1154 |
1155 | /*public void GenerateLocks(List keys)
1156 | {
1157 | if (keys == null || keys.Count <= 0)
1158 | {
1159 | return;
1160 | }
1161 |
1162 | if (goal == null)
1163 | {
1164 | Console.WriteLine("Goal is not set...");
1165 | return;
1166 | }
1167 |
1168 | List openKeys = new List();
1169 | foreach (LayoutKey key in keys)
1170 | {
1171 | openKeys.Add(key);
1172 | }
1173 |
1174 | LayoutRoom currentRoom = goal;
1175 | bool found = true;
1176 | int currentCondition = openKeys.Count;
1177 | float maxLockableIntensity = 1.0f;
1178 | while (openKeys.Count > 0)
1179 | {
1180 |
1181 | found = false;
1182 |
1183 | Console.WriteLine("Trying to find lockable room with max intensity " + (int)(maxLockableIntensity * 100));
1184 |
1185 | LayoutRoom targetRoom = FindLockableRoom(currentRoom, new List(), maxLockableIntensity);
1186 | if (targetRoom != null)
1187 | {
1188 | Console.WriteLine("Found lockable room " + targetRoom.ToString());
1189 | LayoutKey targetKey = null;
1190 |
1191 | if (targetRoom.require != null && openKeys.Contains(targetRoom.require))
1192 | {
1193 | targetKey = targetRoom.require;
1194 | }
1195 | else
1196 | {
1197 | int n = this.randomGenerator.Next(openKeys.Count);
1198 | targetKey = openKeys[n];
1199 | }
1200 |
1201 | int minKeyDist = 1;
1202 | int keyDistance = minKeyDist + this.randomGenerator.Next(6 - minKeyDist);
1203 | //keyDistance = 20;
1204 | while (keyDistance >= minKeyDist)
1205 | {
1206 | Console.WriteLine("Trying to find room for " + targetKey.name + " at distance " + keyDistance);
1207 |
1208 |
1209 | PlaceKey(targetRoom.parent, targetKey, targetRoom, new List(), keyDistance, true);
1210 |
1211 | if (targetKey.room == null)
1212 | {
1213 | PlaceKey(targetRoom.parent, targetKey, targetRoom, new List(), keyDistance, false);
1214 | }
1215 |
1216 | if (targetKey.room != null)
1217 | {
1218 | LockRoom(targetRoom, targetKey);
1219 | targetRoom.require = targetKey;
1220 | targetKey.condition = currentCondition;
1221 | currentCondition--;
1222 |
1223 | maxLockableIntensity = targetRoom.intensity * 0.8f;
1224 | float intensityLimit = 0.4f;
1225 | if (maxLockableIntensity < intensityLimit)
1226 | {
1227 | maxLockableIntensity = intensityLimit;
1228 | }
1229 |
1230 |
1231 | openKeys.Remove(targetKey);
1232 | currentRoom = targetRoom.parent;
1233 |
1234 | found = true;
1235 | break;
1236 | }
1237 | else
1238 | {
1239 | targetRoom.require = null;
1240 | Console.WriteLine("Unable to place key " + targetKey.name);
1241 | }
1242 |
1243 | keyDistance--;
1244 | }
1245 |
1246 | }
1247 | else
1248 | {
1249 | Console.WriteLine("Unable to find lockable room");
1250 | }
1251 |
1252 |
1253 | if (!found)
1254 | {
1255 | break;
1256 | }
1257 | }
1258 |
1259 | foreach (LayoutKey key in openKeys)
1260 | {
1261 | Console.WriteLine("Key " + key.name + " was not placed...");
1262 | }
1263 |
1264 | // after generating the locks, its now possible to understand which rooms are important and which ones are optional
1265 | goal.important = true;
1266 | entrance.important = true;
1267 | foreach (var room in rooms.Values)
1268 | {
1269 | room.important = IsRoomImportant(room);
1270 | }
1271 |
1272 | // convert non-deadend with keys into puzzle rooms
1273 | foreach (var room in rooms.Values)
1274 | {
1275 | if (room.contains != null)
1276 | {
1277 | int n = this.randomGenerator.Next(8);
1278 | if (n > 2)
1279 | {
1280 | room.kind = LayoutRoom.RoomKind.Puzzle;
1281 | }
1282 | else
1283 | {
1284 | room.kind = LayoutRoom.RoomKind.Treasure;
1285 | }
1286 |
1287 | }
1288 | }
1289 |
1290 | // generate intensity for rooms based on tension curve
1291 | GenerateIntensity();
1292 | }*/
1293 |
1294 | protected int GetRoomCondition(LayoutRoom room)
1295 | {
1296 | if (room.locked != null)
1297 | {
1298 | return room.locked.condition;
1299 | }
1300 | return -1;
1301 | }
1302 |
1303 | #endregion
1304 |
1305 | public void GenerateBacktracking(float linearity)
1306 | {
1307 | List temp = new List();
1308 | foreach (var path in connections)
1309 | {
1310 | if (!path.active && path.pathOrder < 0)
1311 | {
1312 | temp.Add(path);
1313 | }
1314 | }
1315 |
1316 | int totalLeft = (int)(temp.Count * (1.0f - linearity));
1317 |
1318 | while (temp.Count > 0 && totalLeft > 0)
1319 | {
1320 | int n = this.randomGenerator.Next(temp.Count);
1321 | LayoutConnection path = temp[n];
1322 | temp.RemoveAt(n);
1323 |
1324 |
1325 | if (GetRoomCondition(path.roomA) == GetRoomCondition(path.roomB))
1326 | {
1327 | path.active = true;
1328 | totalLeft--;
1329 | }
1330 | }
1331 | }
1332 |
1333 | public void GenerateRoomTypes()
1334 | {
1335 | // re-generate intensity
1336 | this.GenerateIntensity();
1337 |
1338 | // generate monster rooms
1339 | foreach (var room in rooms.Values)
1340 | {
1341 | if (room.kind == LayoutRoom.RoomKind.Hall)
1342 | {
1343 | int n = this.randomGenerator.Next(6);
1344 |
1345 | switch (room.GetShape())
1346 | {
1347 | case LayoutRoom.RoomShape.Room:
1348 | {
1349 | switch (n)
1350 | {
1351 | case 0: room.kind = LayoutRoom.RoomKind.Shrine; break;
1352 | case 1: room.kind = LayoutRoom.RoomKind.Farm; break;
1353 | case 2: room.kind = LayoutRoom.RoomKind.Puzzle; break;
1354 | default: room.kind = LayoutRoom.RoomKind.Treasure; break;
1355 | }
1356 | break;
1357 | }
1358 |
1359 | case LayoutRoom.RoomShape.Curve:
1360 | {
1361 | switch (n)
1362 | {
1363 | case 0: room.kind = LayoutRoom.RoomKind.Puzzle; break;
1364 | case 1: room.kind = LayoutRoom.RoomKind.Monster; break;
1365 | }
1366 | break;
1367 | }
1368 |
1369 | case LayoutRoom.RoomShape.Split:
1370 | {
1371 | switch (n)
1372 | {
1373 | case 0: room.kind = LayoutRoom.RoomKind.Puzzle; break;
1374 | case 2: room.kind = LayoutRoom.RoomKind.Monster; break;
1375 | case 3: room.kind = LayoutRoom.RoomKind.Shrine; break;
1376 | case 4: room.kind = LayoutRoom.RoomKind.Farm; break;
1377 | }
1378 | break;
1379 | }
1380 |
1381 | case LayoutRoom.RoomShape.Corridor:
1382 | {
1383 | switch (n)
1384 | {
1385 | case 0: room.kind = LayoutRoom.RoomKind.Monster; break;
1386 | }
1387 | break;
1388 | }
1389 |
1390 | }
1391 |
1392 | }
1393 | }
1394 |
1395 | // re-generate intensity
1396 | this.GenerateIntensity();
1397 | }
1398 |
1399 | }
1400 | }
1401 |
--------------------------------------------------------------------------------