/// <summary> /// Generate a dungeon in the given map, from the objects in the lists given in the constructor /// </summary> /// <param name="map">the map to generate the dungeon in</param> /// <param name="entry">the entry (door + position) used to start the dungeon</param> public void GenerateMap(Map map, PatternPosition entry) { List <PatternPosition> availablesExits = new List <PatternPosition>(); List <PatternPosition> usedRooms = new List <PatternPosition>(); List <PatternPosition> rejectedExits = new List <PatternPosition>(); // use given entry as the initial entry/door map.Place(_doors.Find(entry.id), entry.x, entry.y); availablesExits.Add(entry); // also add it to the toremove (it will not be used by another room...) // since it's the first, it will be the "Up" (see ManageEntries) rejectedExits.Add(entry); if (Debug) { CharUtils.saveAsImage($"./assets/map{operationCount++}.png", map.Content); } // While there are not checked exits while (availablesExits.Count > 0) { // Select one door on the map (random) //PatternPosition exit = availablesExits[rnd.Next(0, availablesExits.Count)]; // Select one door on the map (breadth first) PatternPosition exit = availablesExits[0]; // Select one door on the map (deapth first) //PatternPosition exit = availablesExits[availablesExits.Count - 1]; // try to place a room to this exit this.PlaceRoomForDoor(map, exit, availablesExits, usedRooms, rejectedExits); if (Debug) { CharUtils.saveAsImage($"./assets/map{operationCount++}.png", map.Content); } Logger.Pop(); } // generate an entry and exit ManageEntries(map, rejectedExits); // remove unwanted artifacts (not used doors...) Clean(map); // place some new element / remove some based on pattern Decorate(map); Logger.Pop(); }
/// <summary> /// Read a room in the "0" FileFormat /// First line is XSize ///Second Line is YSize ///Third line are operation to generate Mirror rooms (Rotate : X, H Mirror : X, V Mirror : Y) ///Followed by YSize lines of XSize chars /// </summary> /// <param name="reader"></param> public static void Read_FileFormat_1(StreamReader reader, ReplacementRuleList templates) { try { int priority = 1; int xsize = 0; int ysize = 0; int chance = 100; Int32.TryParse(reader.ReadLine(), out priority); Int32.TryParse(reader.ReadLine(), out xsize); Int32.TryParse(reader.ReadLine(), out ysize); string operations = reader.ReadLine(); char[,] initialData = loadData(reader, xsize, ysize); Int32.TryParse(reader.ReadLine(), out chance); char[,] replacementData = loadData(reader, xsize, ysize); // if requested, create mirrored copies List <char[, ]> initialMirrors = CharUtils.CreateMirrors(initialData, operations); List <char[, ]> replacementMirrors = CharUtils.CreateMirrors(replacementData, operations); for (int i = 0; i < initialMirrors.Count; i++) { MapTemplate initial = new MapTemplate(initialMirrors[i]); MapTemplate replacement = new MapTemplate(replacementMirrors[i]); ReplacementRule replace = new ReplacementRule(); replace.InitialContent = initial; replace.ReplacementContent = replacement; replace.Priority = priority; replace.Chance = chance; if (replace.Check()) { templates.Add(replace); } } } catch (Exception e) { Logger.Error(e.Message); Console.WriteLine(e.Message); } }
/// <summary> /// Remove unwanted artifact from the map : door leading to nowhere, cul-de-sac... /// This is done by replacement rules with chance = 100 /// </summary> /// <param name="map"></param> private void Clean(Map map) { Logger.Info($"Cleaning the map from unwanted artifact", Logger.LogAction.PUSH); // change "unknown" to "wall" map.ReplaceAll('?', '#'); bool modified = true; // get all rentries that are always used... List <ReplacementRule> always = _modifications.collection.FindAll(template => (template.Chance == 100)); Logger.Info($"{always.Count} rules found"); always.Sort( delegate(ReplacementRule x, ReplacementRule y) { if (x.Priority == y.Priority) { return(0); } if (x.Priority > y.Priority) { return(1); } else { return(-1); } } ); while (modified) { modified = false; foreach (ReplacementRule rule in always) { modified = modified || map.ReplaceAll(rule.InitialContent, rule.ReplacementContent); } if (Debug) { CharUtils.saveAsImage($"./assets/map{operationCount++}.png", map.Content); } } Logger.Pop(); }
/// <summary> /// Read a pattern in the "0" FileFormat /// First line is XSize ///Second Line is YSize ///Third line are operation to generate Mirror pattern (Rotate : X, H Mirror : X, V Mirror : Y) ///Followed by YSize lines of XSize chars /// </summary> /// <param name="reader"></param> public static void Read_FileFormat_0(StreamReader reader, MapTemplateList templates) { try { int xsize = 0; int ysize = 0; Int32.TryParse(reader.ReadLine(), out xsize); Int32.TryParse(reader.ReadLine(), out ysize); string operations = reader.ReadLine(); char[,] roomData = loadData(reader, xsize, ysize); // if requested, create mirrored copies List <char[, ]> mirrors = CharUtils.CreateMirrors(roomData, operations); int count = 0; int sourceId = 0; MapTemplate tmp; foreach (char[,] copy in mirrors) { if (count == 0) { tmp = new MapTemplate(copy); sourceId = tmp.Id; CharUtils.saveAsImage($"./assets/images/{templates._name}_{tmp.Id}.png", copy); } else { tmp = new MapTemplate(copy, sourceId); } templates.Add(tmp); //CharUtils.saveAsImage($"./assets/images/{templates._name}_{tmp.Id}.png", copy); count++; } } catch (Exception e) { Logger.Error(e.Message); Console.WriteLine(e.Message); } }
/// <summary> /// Check that a room can be placed, comparing the map and the room pattern /// </summary> /// <param name="map">the map where to add the room to</param> /// <param name="room">the room to add</param> /// <param name="xpos">xposition of the left side of the room in the map</param> /// <param name="ypos">yposition of the top of the room in the map</param> /// <param name="usedRooms">List of all already used room, with their position</param> /// <returns>true if the room can be added</returns> private bool CheckRoom(Map map, MapTemplate room, int xpos, int ypos, List <PatternPosition> usedRooms) { char[,] roomContent = room.Content; if ((xpos + room.XSize >= (map.XSize - 1)) || (ypos + room.YSize >= (map.YSize - 1))) { return(false); } if ((xpos <= 0) || (ypos <= 0)) { return(false); } // check that we didn't put the new room exactly at the same place as a previous room with the same Id (ie same look) int count = usedRooms.FindAll(used => (used.x == xpos && used.y == ypos && used.id == room.SourceId)).Count; if (count > 0) { return(false); } // check that the room can be placed here, taking into accountt the joker on both pattern return(CharUtils.FullMatch(map: map.Content, pattern: room.Content, xpos, ypos)); }
static void Main(string[] args) { Logger.initialisation(); // get char groups CharGroupsLoader.loadFromDirectory("./assets/"); // get the rooms & doors template MapTemplateList rooms = MapTemplateLoader.loadFromDirectory("./assets/rooms", "rooms"); // get the rooms & doors template MapTemplateList doors = MapTemplateLoader.loadFromDirectory("./assets/doors", "doors"); // get the cleaning template rules ReplacementRuleList modifications = ReplacementRuleLoader.loadFromDirectory("./assets/modifications", "modifications"); // create the map Map map = new Map(75, 75); // create the generator & call it MapGenerator1 generator = new MapGenerator1(rooms, doors, modifications); generator.GenerateMap(map); CharUtils.saveAsImage("./assets/map.png", map.Content); }
/// <summary> /// replace some part of the map by others, based on pattern matching and randomness /// </summary> /// <param name="map"></param> private void Decorate(Map map) { Logger.Info($"Modifiying the map ", Logger.LogAction.PUSH); // get all rentries that are always used... List <ReplacementRule> sometimes = _modifications.collection.FindAll(template => (template.Chance != 100)); Logger.Info($"{sometimes.Count} rules found"); sometimes.Sort( delegate(ReplacementRule x, ReplacementRule y) { if (x.Priority == y.Priority) { return(0); } if (x.Priority > y.Priority) { return(1); } else { return(-1); } } ); foreach (ReplacementRule rule in sometimes) { Logger.Info($" replacement of {rule.InitialContent.Id} by {rule.ReplacementContent.Id} with {rule.Chance}% chance "); map.ReplaceAll(rule.InitialContent, rule.ReplacementContent, rule.Chance, ""); if (Debug) { CharUtils.saveAsImage($"./assets/map{operationCount++}.png", map.Content); } } Logger.Pop(); }
/// <summary> /// Select two non used doors and set them as exit of the map; /// </summary> /// <param name="map">the map to get the entries on</param> /// <param name="rejectedExits">list of non used doors</param> private void ManageEntries(Map map, List <PatternPosition> rejectedExits) { // choose the exit PatternPosition exitPos = rejectedExits[0]; // get the template MapTemplate door = _doors.Find(exitPos.id); // duplicate it and change it MapTemplate exit = new MapTemplate(door); CharUtils.ReplaceAll(exit.Content, '=', 'U'); // update the map map.Place(exit, exitPos.x, exitPos.y); map.UpExit = exitPos; exitPos = rejectedExits[rejectedExits.Count - 1]; // get the template door = _doors.Find(exitPos.id); // duplicate it and change it exit = new MapTemplate(door); CharUtils.ReplaceAll(exit.Content, '=', 'D'); // update the map map.Place(exit, exitPos.x, exitPos.y); map.DownExit = exitPos; }
/// <summary> /// Replace all occurence of a character on the map by another one /// </summary> /// <param name="source">the character to replace</param> /// <param name="dest">the new character to replace with</param> public bool ReplaceAll(char source, char dest) { return(CharUtils.ReplaceAll(Content, source, dest)); }
/// <summary> /// Search a template in the content of this objetc, and replace it with another one, /// </summary> /// <param name="toFind">the template to find</param> /// <param name="toReplace">the template to replace with</param> /// <returns>true if at least one modification occured</returns> public bool ReplaceAll(MapTemplate toFind, MapTemplate toReplace) { return(CharUtils .Replace(Content, toFind.Content, toReplace.Content, 100, "")); }
/// <summary> /// place a given template on the content at a given position. /// Only filled part of the template characters (!= '?') are used /// </summary> /// <param name="Template">the replacing template</param> /// <param name="xpos">X position</param> /// <param name="ypos">Y position</param> public void Place(MapTemplate template, int xpos, int ypos) { CharUtils.Place(Content, template.Content, xpos, ypos); }
/// <summary> /// check the given template against the content of the object return the list of position where the template matche /// </summary> /// <param name="template">The template to chek </param> /// <param name="operation">operations to perform on the template (RXY) </param> /// <returns>List of position where the template Matches</returns> public List <Position> Matches(MapTemplate template, string operations, bool strict = false) { return(CharUtils.Matches(Content, template.Content, operations, strict: strict)); }
/// <summary> /// check the current template against a given map and return the list of position where both template matche /// </summary> /// <param name="destination">The template to chekc the template against. It should be bigger than the template</param> /// <returns>List of position where the template Matches</returns> public List <Position> Matches(MapTemplate content, bool strict = false) { return(CharUtils .Matches(map: content.Content, template: Content, "", strict: strict)); }
/// <summary> /// check the current template against a given position on the given map and return true if the template matche /// </summary> /// <param name="template">The template to check</param> /// <param name="xpos">X position</param> /// <param name="ypos">Y position</param> /// <returns>true if the template Matches, false otherwise</returns> public bool Matches(MapTemplate template, int xpos, int ypos, bool strict = false) { return(CharUtils.Match(pattern: Content, map: template.Content, xpos: xpos, ypos: ypos, strict: strict)); }