diff --git a/NailsCmd/NailsCmd.csproj b/NailsCmd/NailsCmd.csproj index 38f9d5b..7bbff75 100644 --- a/NailsCmd/NailsCmd.csproj +++ b/NailsCmd/NailsCmd.csproj @@ -83,9 +83,8 @@ - - Always - Designer + + PreserveNewest diff --git a/NailsCmd/NailsConfig.xml b/NailsCmd/NailsConfig.xml new file mode 100644 index 0000000..dfee5c2 --- /dev/null +++ b/NailsCmd/NailsConfig.xml @@ -0,0 +1,150 @@ + + + + + + + 0 + 0 + 0 + + 3 + + 2 + + 20 + + 20 + + 0.05 + + 0.1 + + 0.05 + + + + East + + 0.005 + + 0 + + 1 + + 0 + + 2 + + true + + 8 + + 1 + + + + + + 0 + 0 + 0 + + 4 + + + + 8 + + true + + + + + dev + + + wall + + dev/wall.vmf + + + + ceiling + + dev/ceiling.vmf + + + + floor + + dev/floor.vmf + + + + + + RandomTest + + + wall + + RandomTest/wall1.vmf + RandomTest/wall2.vmf + RandomTest/wall3.vmf + RandomTest/wall4.vmf + + + + ceiling + + RandomTest/ceiling1.vmf + RandomTest/ceiling2.vmf + RandomTest/ceiling3.vmf + RandomTest/ceiling4.vmf + + + + floor + + RandomTest/floor1.vmf + RandomTest/floor2.vmf + RandomTest/floor3.vmf + RandomTest/floor4.vmf + + + + + + \ No newline at end of file diff --git a/NailsCmd/Program.cs b/NailsCmd/Program.cs index 89bb9d6..cbdc101 100644 --- a/NailsCmd/Program.cs +++ b/NailsCmd/Program.cs @@ -26,7 +26,7 @@ class Program */ class Options { - [Option('f', "file", Required = false, Default = "alife.xml", HelpText = "XML containing agent configuration.")] + [Option('f', "file", Required = false, Default = "NailsConfig.xml", HelpText = "Nails configuration")] public string InputFileName { get; set; } [Option('o', "output", Required = false, Default = "output.vmf", HelpText = "Filename to write out to.")] @@ -66,23 +66,23 @@ private static void RunOptionsAndReturnExitCode(Options opts) { try { - // Read agents from XML file - List agents = AlifeConfigurator.ReadConfiguration(opts.InputFileName); + // Read agents from XML file + NailsConfig config = NailsConfig.ReadConfiguration(opts.InputFileName); // Create a new nails map NailsMap nailsMap = new NailsMap(); AlifeMap alifeMap = new NailsAlifeMap(nailsMap); - alifeMap.Agents = agents; + alifeMap.Agents = config.Agents; // Run the simulation AlifeSimulation.Simulate(ref alifeMap, opts.Lifetime); // Write out to a file - VMFAdapter vmfAdapter = new VMFAdapter(opts.OutputFileName, opts.HorizontalScale, opts.VerticalScale); + VMFAdapter vmfAdapter = new VMFAdapter(filename : opts.OutputFileName, horizontal_scale : opts.HorizontalScale, vertical_scale : opts.VerticalScale, config : config); vmfAdapter.Export(nailsMap); } catch(Exception e) { - log.Error("NailsCmd caught fatal exception: ", e); + log.Fatal("NailsCmd caught fatal exception: ", e); } } diff --git a/NailsCmd/alife.xml b/NailsCmd/alife.xml deleted file mode 100644 index 922ccca..0000000 --- a/NailsCmd/alife.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - 0 - 0 - 0 - 3 - 3 - 32 - 32 - 0.0625 - 0.0625 - 0.0625 - - North - 0.0625 - 0 - 1 - 0 - 1 - true - 8 - - - - 0 - 0 - 0 - 4 - - - - - \ No newline at end of file diff --git a/NailsLib/Adapters/JSONAdapter.cs b/NailsLib/Adapters/JSONAdapter.cs index 35efcca..dd01354 100644 --- a/NailsLib/Adapters/JSONAdapter.cs +++ b/NailsLib/Adapters/JSONAdapter.cs @@ -13,7 +13,7 @@ namespace NailsLib.Adapters * 1upD * */ - public class JSONAdapter + public class JSONAdapter : NailsMapAdapter { private String _filename; diff --git a/NailsLib/Adapters/NailsMapAdapter.cs b/NailsLib/Adapters/NailsMapAdapter.cs new file mode 100644 index 0000000..c6a187f --- /dev/null +++ b/NailsLib/Adapters/NailsMapAdapter.cs @@ -0,0 +1,15 @@ +using NailsLib.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NailsLib.Adapters +{ + interface NailsMapAdapter + { + void Export(NailsMap map); + NailsMap Import(); + } +} diff --git a/NailsLib/Adapters/VMFAdapter.cs b/NailsLib/Adapters/VMFAdapter.cs index 9a9d0f6..45b835c 100644 --- a/NailsLib/Adapters/VMFAdapter.cs +++ b/NailsLib/Adapters/VMFAdapter.cs @@ -15,7 +15,7 @@ namespace NailsLib.Adapters * 1upD * */ - public class VMFAdapter + public class VMFAdapter : NailsMapAdapter { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); @@ -23,15 +23,47 @@ public class VMFAdapter private int _vertical_scale; private string _filename; private VMF _vmf; + private NailsConfig Config; - public VMFAdapter(string filename, int horizontal_scale = 64, int vertical_scale = 64) + public VMFAdapter(string filename, int horizontal_scale = 64, int vertical_scale = 64, NailsConfig config = null) { + this.Config = config; + + if(Config == null) + { + Config = new NailsConfig(); + } + this._filename = filename; this._horizontal_scale = horizontal_scale; this._vertical_scale = vertical_scale; this._vmf = new VMF(); // Blank VMF in case export is called before input } + private instanceData MakeInstance(string aInstanceName, string aStyleName, int aRotation, int aEntID, int aX, int aY, int aZ) + { + try + { + int seed = aEntID + aX + aY + aZ + aRotation; + Random random = new Random(seed); + + List filepaths = this.Config.GetStyle(aStyleName).GetInstancePaths(aInstanceName); + + string filepath = filepaths[random.Next(filepaths.Count)]; + + instanceData instance = new instanceData { filename = "Nails/" + filepath, rotation = aRotation }; + instance.xpos = aX; + instance.ypos = aY; + instance.zpos = aZ; + instance.ent_id = aEntID; + return instance; + } catch(Exception e) + { + log.Error("VMFAdapter caught exception while generating instance data: ", e); + } + return null; + } + /** * Exports the given NailsMap to a VMF file. Uses the file name passed in when this adapter was constructed. * 1upD @@ -57,61 +89,37 @@ public void Export(NailsMap map) if (faces.Contains(NailsCubeFace.Floor)) { - instanceData instance = new instanceData { filename = "Nails/" + cube.StyleName + "/floor.vmf", rotation = 0 }; - instance.xpos = x_pos; - instance.ypos = y_pos; - instance.zpos = z_pos; - instance.ent_id = i++; + instanceData instance = this.MakeInstance("floor", cube.StyleName, 0, i++, x_pos, y_pos, z_pos); insertInstanceIntoVMF(instance, output_vmf); } if (faces.Contains(NailsCubeFace.Front)) { - instanceData instance = new instanceData { filename = "Nails/" + cube.StyleName + "/wall.vmf", rotation = 0 }; - instance.xpos = x_pos; - instance.ypos = y_pos; - instance.zpos = z_pos; - instance.ent_id = i++; + instanceData instance = this.MakeInstance("wall", cube.StyleName, 0, i++, x_pos, y_pos, z_pos); insertInstanceIntoVMF(instance, output_vmf); } if (faces.Contains(NailsCubeFace.Left)) { - instanceData instance = new instanceData { filename = "Nails/" + cube.StyleName + "/wall.vmf", rotation = 90 }; - instance.xpos = x_pos; - instance.ypos = y_pos; - instance.zpos = z_pos; - instance.ent_id = i++; + instanceData instance = this.MakeInstance("wall", cube.StyleName, 90, i++, x_pos, y_pos, z_pos); insertInstanceIntoVMF(instance, output_vmf); } if (faces.Contains(NailsCubeFace.Back)) { - instanceData instance = new instanceData { filename = "Nails/" + cube.StyleName + "/wall.vmf", rotation = 180 }; - instance.xpos = x_pos; - instance.ypos = y_pos; - instance.zpos = z_pos; - instance.ent_id = i++; + instanceData instance = this.MakeInstance("wall", cube.StyleName, 180, i++, x_pos, y_pos, z_pos); insertInstanceIntoVMF(instance, output_vmf); } if (faces.Contains(NailsCubeFace.Right)) { - instanceData instance = new instanceData { filename = "Nails/" + cube.StyleName + "/wall.vmf", rotation = 270 }; - instance.xpos = x_pos; - instance.ypos = y_pos; - instance.zpos = z_pos; - instance.ent_id = i++; + instanceData instance = this.MakeInstance("wall", cube.StyleName, 270, i++, x_pos, y_pos, z_pos); insertInstanceIntoVMF(instance, output_vmf); } if (faces.Contains(NailsCubeFace.Ceiling)) { - instanceData instance = new instanceData { filename = "Nails/" + cube.StyleName + "/ceiling.vmf", rotation = 0 }; - instance.xpos = x_pos; - instance.ypos = y_pos; - instance.zpos = z_pos; - instance.ent_id = i++; + instanceData instance = this.MakeInstance("ceiling", cube.StyleName, 0, i++, x_pos, y_pos, z_pos); insertInstanceIntoVMF(instance, output_vmf); } } diff --git a/NailsLib/Data/NailsConfig.cs b/NailsLib/Data/NailsConfig.cs new file mode 100644 index 0000000..944af10 --- /dev/null +++ b/NailsLib/Data/NailsConfig.cs @@ -0,0 +1,90 @@ +using Alife.Agents; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +namespace NailsLib.Data +{ + [Serializable] + public class NailsConfig : ISerializable + + { + private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public List Agents; + public List Styles; + + public NailsConfig() + { + } + + public NailsStyle GetStyle(string aStyleName) + { + var styles = this.Styles.Where(t => t.Name == aStyleName).ToList(); + if (styles == null || styles.Count < 1) + { + return null; + } + + return styles[0]; + } + + /** + * Static method to read configuration from an XML file and return a NailsConfig object. + * 1upD + */ + public static NailsConfig ReadConfiguration(string filepath) + { + NailsConfig config = null; + try + { + log.Info(string.Format("Loading Nails configuration from file: {0}", filepath)); + XmlSerializer serializer = new XmlSerializer(typeof(NailsConfig)); + + StreamReader reader = new StreamReader(filepath); + config = (NailsConfig)serializer.Deserialize(reader); + reader.Close(); + } + catch (Exception e) + { + log.Error(string.Format("Error reading Nails configuration in file: {0}", filepath), e); + } + + return config; + } + + /** + * Static method to write Nails configuration to a file + * 1upD + */ + public static void WriteConfiguration(string filepath, NailsConfig config) + { + try + { + log.Info(string.Format("Writing Nails configuration to file: {0}", filepath)); + + XmlSerializer serializer = new XmlSerializer(typeof(NailsConfig)); + + StreamWriter writer = new StreamWriter(filepath); + serializer.Serialize(writer, config); + writer.Close(); + } + catch (Exception e) + { + log.Error(string.Format("Error writing Nails configuration to file: {0}", filepath), e); + } + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new NotImplementedException(); + } + } +} diff --git a/NailsLib/Data/NailsInstance.cs b/NailsLib/Data/NailsInstance.cs new file mode 100644 index 0000000..98e375a --- /dev/null +++ b/NailsLib/Data/NailsInstance.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace NailsLib.Data +{ + public class NailsInstance + { + public string Name { get; set; } + public List Filepaths { get; set; } + + } +} \ No newline at end of file diff --git a/NailsLib/Data/NailsStyle.cs b/NailsLib/Data/NailsStyle.cs new file mode 100644 index 0000000..6c7c4c8 --- /dev/null +++ b/NailsLib/Data/NailsStyle.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace NailsLib.Data +{ + public class NailsStyle + { + public string Name; + + public List Instances; + + /** + * + * Returns a list of filepaths for a given instance name. + * If this theme does not have any instances with that name, + * returns null. + * 1upD + * + */ + public List GetInstancePaths(string aInstanceName) + { + // Unreadable linq expression gets the filepath list from an instance by instance name + var instances = Instances.Where(i => i.Name == aInstanceName).Select(i => i.Filepaths).ToList(); + if(instances == null || instances.Count < 1) + { + return null; + } + return instances[0]; + } + } + + +} diff --git a/NailsLib/NailsLib.csproj b/NailsLib/NailsLib.csproj index be4c39f..7fa0f8f 100644 --- a/NailsLib/NailsLib.csproj +++ b/NailsLib/NailsLib.csproj @@ -50,11 +50,15 @@ + + + + diff --git a/README.md b/README.md index 39128c3..97c8090 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,9 @@ The first iteration of Nails is a random map generator that creates layouts and http://www.gridsagegames.com/blog/2014/06/mapgen-tunneling-algorithm/ http://dungeonmaker.sourceforge.net/DM2_Manual/manual3.html -The generator works by simulating "artificial life forms" that carve out layouts by following randomly specified rules, ie tunnelers go straight each step with a certain percent chance of turning 90 degrees. - ## How To Use ## -Usage of NailsCmd.exe: +### Usage of NailsCmd.exe: ### -f, --file XML file containing agent configuration. @@ -18,38 +16,98 @@ Usage of NailsCmd.exe: -t, --lifetime Number of steps in the simulation. -Writing an agent XML file: +### Writing an agent XML file: ### + +The generator works by simulating "artificial life forms" that carve out layouts by following randomly specified rules. The starting parameters for the artificial life simulation are configurable by an XML file, 'alife.xml'. The configuration can have any number of starting agents represented by the tag in the XML. + +Some agents have 'styles' which refer to sets of instances defined in the Nails folder. By default, there is one style called dev, located in Nails/dev. + +Types of agents: +* Tunneler: Tunnelers 'dig', carving out empty tunnels or corridors by applying their given style. By default, they move in a straight line. For every cube the roomer marks as its style, there are set probabilities that it will reproduce a tunneler, produce a roomer, ascend or descend by one cube, or turn 90 degrees in either direction. +* Roomer: Roomers 'explode', creating rooms by marking large numbers of adjacent cubes as their given style in a rectangular pattern. The following XML demonstrates a random map configuration that has one tunneler and one roomer: It will generate a room and then a random path leading North from the room. ```xml + - 0 + + 0 0 0 + 3 + 2 + 20 + 20 - 0.05 - 0.1 - 0.05 - - dev + + 0.05 + + 0.1 + + 0.05 + + + + East + 0.005 + 0 - 1 - 0 - 2 - true + + 1 + + 0 + + 2 + + true + + 0 0 0 + 4 - + + ``` diff --git a/Todo.txt b/Todo.txt new file mode 100644 index 0000000..5d12687 --- /dev/null +++ b/Todo.txt @@ -0,0 +1,5 @@ +* Make themes XML configurable +* Auto-discover themes based on file structure +* New Alife entities + * Convolutionary + * Consolidator \ No newline at end of file