Advertisement
Guest User

Untitled

a guest
Mar 19th, 2019
54
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 12.63 KB | None | 0 0
  1. package game.map;
  2.  
  3. import base.*;
  4. import game.GameConstants;
  5. import gui.Resources;
  6.  
  7. import java.awt.*;
  8. import java.awt.image.BufferedImage;
  9. import java.util.ArrayList;
  10. import java.util.Collection;
  11. import java.util.Iterator;
  12. import java.util.List;
  13. import java.util.ListIterator;
  14.  
  15. import com.sun.javafx.geom.Line2D;
  16.  
  17. /**
  18.  * Diese Klasse representiert das Spielfeld. Sie beinhaltet das Hintergrundbild, welches mit Perlin noise erzeugt wurde,
  19.  * eine Liste mit Königreichen und alle Burgen und deren Verbindungen als Graphen.
  20.  *
  21.  * Die Karte wird in mehreren Schritten generiert, siehe dazu {@link #generateRandomMap(int, int, int, int, int)}
  22.  */
  23. public class GameMap {
  24.  
  25.     private BufferedImage backgroundImage;
  26.     private Graph<Castle> castleGraph;
  27.     private List<Kingdom> kingdoms;
  28.  
  29.     // Map Generation
  30.     private double[][] noiseValues;
  31.     private int width, height, scale;
  32.  
  33.     /**
  34.      * Erzeugt eine neue leere Karte. Der Konstruktor sollte niemals direkt aufgerufen werden.
  35.      * Um eine neue Karte zu erstellen, muss {@link #generateRandomMap(int, int, int, int, int)} verwendet werden
  36.      * @param width die Breite der Karte
  37.      * @param height die Höhe der Karte
  38.      * @param scale der Skalierungsfaktor
  39.      */
  40.     private GameMap(int width, int height, int scale) {
  41.         this.castleGraph = new Graph<>();
  42.         this.width = width;
  43.         this.height = height;
  44.         this.scale = scale;
  45.     }
  46.  
  47.     /**
  48.      * Wandelt einen Noise-Wert in eine Farbe um. Die Methode kann nach belieben angepasst werden
  49.      * @param value der Perlin-Noise-Wert
  50.      * @return die resultierende Farbe
  51.      */
  52.     private Color doubleToColor(double value) {
  53.         if (value <= 0.40)
  54.             return GameConstants.COLOR_WATER;
  55.         else if (value <= 0.5)
  56.             return GameConstants.COLOR_SAND;
  57.         else if (value <= 0.7)
  58.             return GameConstants.COLOR_GRASS;
  59.         else if (value <= 0.8)
  60.             return GameConstants.COLOR_STONE;
  61.         else
  62.             return GameConstants.COLOR_SNOW;
  63.     }
  64.  
  65.     /**
  66.      * Hier wird das Hintergrund-Bild mittels Perlin-Noise erzeugt.
  67.      * Siehe auch: {@link PerlinNoise}
  68.      */
  69.     private void generateBackground() {
  70.         PerlinNoise perlinNoise = new PerlinNoise(width, height, scale);
  71.         Dimension realSize = perlinNoise.getRealSize();
  72.  
  73.         noiseValues = new double[realSize.width][realSize.height];
  74.         backgroundImage = new BufferedImage(realSize.width, realSize.height, BufferedImage.TYPE_INT_RGB);
  75.         for (int x = 0; x < realSize.width; x++) {
  76.             for (int y = 0; y < realSize.height; y++) {
  77.                 double noiseValue = perlinNoise.getNoise(x, y);
  78.                 noiseValues[x][y] = noiseValue;
  79.                 backgroundImage.setRGB(x, y, doubleToColor(noiseValue).getRGB());
  80.             }
  81.         }
  82.     }
  83.  
  84.     /**
  85.      * Hier werden die Burgen erzeugt.
  86.      * Dabei wir die Karte in Felder unterteilt, sodass auf jedes Fals maximal eine Burg kommt.
  87.      * Sollte auf einem Feld keine Position für eine Burg existieren (z.B. aufgrund von Wasser oder angrenzenden Burgen), wird dieses übersprungen.
  88.      * Dadurch kann es vorkommen, dass nicht alle Burgen generiert werden
  89.      * @param castleCount die maximale Anzahl der zu generierenden Burgen
  90.      */
  91.     private void generateCastles(int castleCount) {
  92.         double square = Math.ceil(Math.sqrt(castleCount));
  93.         double length = width + height;
  94.  
  95.         int tilesX = (int) Math.max(1, (width / length + 0.5) * square) + 5;
  96.         int tilesY = (int) Math.max(1, (height / length + 0.5) * square) + 5;
  97.         int tileW = (width * scale / tilesX);
  98.         int tileH = (height * scale / tilesY);
  99.  
  100.         if (tilesX * tilesY < castleCount) {
  101.             throw new IllegalArgumentException(String.format("CALCULATION Error: tilesX=%d * tilesY=%d < castles=%d", tilesX, tilesY, castleCount));
  102.         }
  103.  
  104.         // Add possible tiles
  105.         List<Point> possibleFields = new ArrayList<>(tilesX * tilesY);
  106.         for (int x = 0; x < tilesX - 1; x++) {
  107.             for (int y = 0; y < tilesY - 1; y++) {
  108.                 possibleFields.add(new Point(x, y));
  109.             }
  110.         }
  111.  
  112.         // Generate castles
  113.         List<String> possibleNames = generateCastleNames();
  114.         int castlesGenerated = 0;
  115.         while (possibleFields.size() > 0 && castlesGenerated < castleCount) {
  116.             Point randomField = possibleFields.remove((int) (Math.random() * possibleFields.size()));
  117.             int x0 = (int) ((randomField.x + 0.5) * tileW);
  118.             int y0 = (int) ((randomField.y + 0.5) * tileH);
  119.  
  120.             for (int x = (int) (0.5 * tileW); x >= 0; x--) {
  121.                 boolean positionFound = false;
  122.                 for (int y = (int) (0.5 * tileH); y >= 0; y--) {
  123.                     int x_mid = (int) (x0 + x + 0.5 * tileW);
  124.                     int y_mid = (int) (y0 + y + 0.5 * tileH);
  125.                     if (noiseValues[x_mid][y_mid] >= 0.6) {
  126.                         String name = possibleNames.isEmpty() ? "Burg " + (castlesGenerated + 1) :
  127.                             possibleNames.get((int) (Math.random() * possibleNames.size()));
  128.                         Castle newCastle = new Castle(new Point(x0 + x, y0 + y), name);
  129.                         boolean doesIntersect = false;
  130.  
  131.                         for (Castle r : castleGraph.getAllValues()) {
  132.                             if (r.distance(newCastle) < Math.max(tileW, tileH)) {
  133.                                 doesIntersect = true;
  134.                                 break;
  135.                             }
  136.                         }
  137.  
  138.                         if (!doesIntersect) {
  139.                             possibleNames.remove(name);
  140.                             castleGraph.addNode(newCastle);
  141.                             castlesGenerated++;
  142.                             positionFound = true;
  143.                             break;
  144.                         }
  145.                     }
  146.                 }
  147.  
  148.                 if (positionFound)
  149.                     break;
  150.             }
  151.         }
  152.     }
  153.  
  154.     /**
  155.      * Hier werden die Kanten erzeugt
  156.      */
  157.     private void generateEdges() {
  158.         List<Node<Castle>> nodes = castleGraph.getNodes();
  159.         int index;
  160.         while(!castleGraph.allNodesConnected()) {
  161.             for(int i = 0; i < nodes.size(); i++) {
  162.                 index = getIndexOfClosestNode(nodes.get(i));
  163.                 if(index != -1)
  164.                     castleGraph.addEdge(nodes.get(i), nodes.get(index));
  165.                
  166.             }
  167.         }
  168.     }
  169.    
  170.     private int getIndexOfClosestNode(Node<Castle> nodeA) {
  171.         List<Node<Castle>> nodes = new ArrayList<>();
  172.         nodes.addAll(castleGraph.getNodes());
  173.        
  174.         nodes.remove(nodeA);
  175.         removeNodesWithExistingEdgesFromList(nodeA, nodes);
  176.        
  177.         if(nodes.isEmpty())
  178.             return -1;
  179.        
  180.         int tempIndex= 0;
  181.         while(edgeIsStriking(nodeA, nodes.get(tempIndex))) {
  182.             tempIndex++;
  183.            
  184.            
  185.             if(tempIndex + 1 == nodes.size())
  186.                 return -1;
  187.         }  
  188.        
  189.        
  190.         Node<Castle> closestNode = nodes.get(tempIndex);
  191.         double smallestDistance = nodeA.getValue().distance(closestNode.getValue());
  192.        
  193.         for(int i = 1; i < nodes.size(); i++) {
  194.             double tempDistance = nodeA.getValue().distance(nodes.get(i).getValue());
  195.             if(!edgeIsStriking(nodeA, nodes.get(i)) && (tempDistance < smallestDistance)) {
  196.                 closestNode = nodes.get(i);
  197.                 smallestDistance = tempDistance;
  198.             }
  199.         }
  200.        
  201.         return castleGraph.getNodes().indexOf(closestNode);
  202.     }
  203.    
  204.     /**
  205.      *
  206.      * @param nodeA
  207.      * @param nodeB
  208.      * @return true if there is one or more edges in castleGraph.getEdges() that is intersecting the line between nodeA and nodeB, else false
  209.      */
  210.     private boolean edgeIsStriking(Node<Castle> nodeA, Node<Castle> nodeB) {
  211.         float x1 = (float) nodeA.getValue().getLocationOnMap().getX();
  212.         float y1 = (float) nodeA.getValue().getLocationOnMap().getY();
  213.         float x2 = (float) nodeB.getValue().getLocationOnMap().getX();
  214.         float y2 = (float) nodeB.getValue().getLocationOnMap().getY();
  215.         Line2D a = new Line2D(x1, y1, x2, y2);
  216.         for (int i = 0; i < castleGraph.getEdges().size(); i++) {
  217.             float x3 = (float) castleGraph.getEdges().get(i).getNodeA().getValue().getLocationOnMap().getX();
  218.             float y3 = (float) castleGraph.getEdges().get(i).getNodeA().getValue().getLocationOnMap().getY();
  219.             float x4 = (float) castleGraph.getEdges().get(i).getNodeB().getValue().getLocationOnMap().getX();
  220.             float y4 = (float) castleGraph.getEdges().get(i).getNodeB().getValue().getLocationOnMap().getY();
  221.             Line2D b = new Line2D(x3, y3, x4, y4);
  222.             if (a.intersectsLine(b) && (!a.intersectsLine(x3, y3, x3, y3) && !a.intersectsLine(x4, y4, x4, y4))) {
  223.                 return true;
  224.             }
  225.         }
  226.         return false;
  227.     }
  228.    
  229.    
  230.     private void removeNodesWithExistingEdgesFromList(Node<Castle> nodeA, List<Node<Castle>> list) {
  231.        
  232.         for(int i = 0; i < castleGraph.getNodes().size(); i++) {
  233.             if(castleGraph.edgeExists(nodeA, castleGraph.getNodes().get(i)))
  234.                 list.remove(castleGraph.getNodes().get(i));
  235.         }
  236.     }
  237.  
  238.     /**
  239.      * Hier werden die Burgen in Königreiche unterteilt. Dazu wird der {@link Clustering} Algorithmus aufgerufen.
  240.      * @param kingdomCount die Anzahl der zu generierenden Königreiche
  241.      */
  242.     private void generateKingdoms(int kingdomCount) {
  243.         if(kingdomCount > 0 && kingdomCount < castleGraph.getAllValues().size()) {
  244.             Clustering clustering = new Clustering(castleGraph.getAllValues(), kingdomCount);
  245.             kingdoms = clustering.getPointsClusters();
  246.         } else {
  247.             kingdoms = new ArrayList<>();
  248.         }
  249.     }
  250.  
  251.     /**
  252.      * Eine neue Spielfeldkarte generieren.
  253.      * Dazu werden folgende Schritte abgearbeitet:
  254.      *   1. Das Hintergrundbild generieren
  255.      *   2. Burgen generieren
  256.      *   3. Kanten hinzufügen
  257.      *   4. Burgen in Köngireiche unterteilen
  258.      * @param width die Breite des Spielfelds
  259.      * @param height die Höhe des Spielfelds
  260.      * @param scale die Skalierung
  261.      * @param castleCount die maximale Anzahl an Burgen
  262.      * @param kingdomCount die Anzahl der Königreiche
  263.      * @return eine neue GameMap-Instanz
  264.      */
  265.     public static GameMap generateRandomMap(int width, int height, int scale, int castleCount, int kingdomCount) {
  266.  
  267.         width = Math.max(width, 15);
  268.         height = Math.max(height, 10);
  269.  
  270.         if (scale <= 0 || castleCount <= 0)
  271.             throw new IllegalArgumentException();
  272.  
  273.         System.out.println(String.format("Generating new map, castles=%d, width=%d, height=%d, kingdoms=%d", castleCount, width, height, kingdomCount));
  274.         GameMap gameMap = new GameMap(width, height, scale);
  275.         gameMap.generateBackground();
  276.         gameMap.generateCastles(castleCount);
  277.         gameMap.generateEdges();
  278.         gameMap.generateKingdoms(kingdomCount);
  279.  
  280.         if(!gameMap.getGraph().allNodesConnected()) {
  281.             System.out.println("Fehler bei der Verifikation: Es sind nicht alle Knoten miteinander verbunden!");
  282.             return null;
  283.         }
  284.  
  285.         return gameMap;
  286.     }
  287.    
  288.  
  289.     /**
  290.      * Generiert eine Liste von Zufallsnamen für Burgen. Dabei wird ein Prefix (Schloss, Burg oder Festung) an einen
  291.      * vorhandenen Namen aus den Resourcen angefügt. Siehe auch: {@link Resources#getcastleNames()}
  292.      * @return eine Liste mit Zufallsnamen
  293.      */
  294.     private List<String> generateCastleNames() {
  295.         String[] prefixes = {"Schloss", "Burg", "Festung"};
  296.         List<String> names = Resources.getInstance().getCastleNames();
  297.         List<String> nameList = new ArrayList<>(names.size());
  298.  
  299.         for (String name : names) {
  300.             String prefix = prefixes[(int) (Math.random() * prefixes.length)];
  301.             nameList.add(prefix + " " + name);
  302.         }
  303.  
  304.         return nameList;
  305.     }
  306.  
  307.     public int getWidth() {
  308.         return this.backgroundImage.getWidth();
  309.     }
  310.  
  311.     public int getHeight() {
  312.         return this.backgroundImage.getHeight();
  313.     }
  314.  
  315.     public BufferedImage getBackgroundImage() {
  316.         return this.backgroundImage;
  317.     }
  318.  
  319.     public Dimension getSize() {
  320.         return new Dimension(this.getWidth(), this.getHeight());
  321.     }
  322.  
  323.     public List<Castle> getCastles() {
  324.         return castleGraph.getAllValues();
  325.     }
  326.  
  327.     public Graph<Castle> getGraph() {
  328.         return this.castleGraph;
  329.     }
  330.  
  331.     public List<Edge<Castle>> getEdges() {
  332.         return this.castleGraph.getEdges();
  333.     }
  334.  
  335.     public List<Kingdom> getKingdoms() {
  336.         return this.kingdoms;
  337.     }
  338. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement