Real rename

This commit is contained in:
joachimschmidt557 2019-02-12 19:22:33 +01:00
parent aa3dd87076
commit cafb36cb26
68 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,128 @@
package game.map;
import game.Player;
import java.awt.*;
/**
* Diese Klasse representiert eine Burg.
* Jede Burg hat Koordinaten auf der Karte und einen Namen.
* Falls die Burg einen Besitzer hat, hat sie auch eine Anzahl von zugewiesenen Truppen.
* Die Burg kann weiterhin Teil eines Königreichs sein.
*/
public class Castle {
private int troopCount;
private Player owner;
private Kingdom kingdom;
private Point location;
private String name;
/**
* Eine neue Burg erstellen
* @param location die Koordinaten der Burg
* @param name der Name der Burg
*/
public Castle(Point location, String name) {
this.location = location;
this.troopCount = 0;
this.owner = null;
this.kingdom = null;
this.name = name;
}
public Player getOwner() {
return this.owner;
}
public Kingdom getKingdom() {
return this.kingdom;
}
public int getTroopCount() {
return this.troopCount;
}
/**
* Truppen von dieser Burg zur angegebenen Burg bewegen.
* Dies funktioniert nur, wenn die Besitzer übereinstimmen und bei der aktuellen Burg mindestens eine Truppe übrig bleibt
* @param target
* @param troops
*/
public void moveTroops(Castle target, int troops) {
// Troops can only be moved to own regions
if(target.owner != this.owner)
return;
// At least one unit must remain in the source region
if(this.troopCount - troops < 1)
return;
this.troopCount -= troops;
target.troopCount += troops;
}
public Point getLocationOnMap() {
return this.location;
}
/**
* Berechnet die eukldische Distanz zu dem angegebenen Punkt
* @param dest die Zielkoordinate
* @return die euklidische Distanz
*/
public double distance(Point dest) {
return Math.sqrt(Math.pow(this.location.x - dest.x, 2) + Math.pow(this.location.y - dest.y, 2));
}
/**
* Berechnet die eukldische Distanz zu der angegebenen Burg
* @param next die Zielburg
* @return die euklidische Distanz
* @see #distance(Point)
*/
public double distance(Castle next) {
Point otherLocation = next.getLocationOnMap();
return this.distance(otherLocation);
}
public void setOwner(Player player) {
this.owner = player;
}
public void addTroops(int i) {
if(i <= 0)
return;
this.troopCount += i;
}
public String getName() {
return this.name;
}
public void removeTroops(int i) {
this.troopCount = Math.max(0, this.troopCount - i);
if(this.troopCount == 0)
this.owner = null;
}
/**
* Gibt den Burg-Typen zurück. Falls die Burg einem Königreich angehört, wird der Typ des Königreichs zurückgegeben, ansonsten 0
* @return der Burg-Typ für die Anzeige
*/
public int getType() {
return this.kingdom == null ? 0 : this.kingdom.getType();
}
/**
* Die Burg einem Königreich zuordnen
* @param kingdom Ein Königreich oder null
*/
public void setKingdom(Kingdom kingdom) {
this.kingdom = kingdom;
if(kingdom != null)
kingdom.addCastle(this);
}
}

View file

@ -0,0 +1,40 @@
package game.map;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
/**
* Diese Klasse teilt Burgen in Königreiche auf
*/
public class Clustering {
private Random random;
private final List<Castle> allCastles;
private final int kingdomCount;
/**
* Ein neues Clustering-Objekt erzeugen.
* @param castles Die Liste von Burgen, die aufgeteilt werden sollen
* @param kingdomCount Die Anzahl von Königreichen die generiert werden sollen
*/
public Clustering(List<Castle> castles, int kingdomCount) {
if (kingdomCount < 2)
throw new IllegalArgumentException("Ungültige Anzahl an Königreichen");
this.random = new Random();
this.kingdomCount = kingdomCount;
this.allCastles = Collections.unmodifiableList(castles);
}
/**
* Gibt eine Liste von Königreichen zurück.
* Jedes Königreich sollte dabei einen Index im Bereich 0-5 bekommen, damit die Burg richtig angezeigt werden kann.
* Siehe auch {@link Kingdom#getType()}
*/
public List<Kingdom> getPointsClusters() {
// TODO Clustering#getPointsClusters()
return new ArrayList<>();
}
}

View file

@ -0,0 +1,256 @@
package game.map;
import base.*;
import game.GameConstants;
import gui.Resources;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
/**
* Diese Klasse representiert das Spielfeld. Sie beinhaltet das Hintergrundbild, welches mit Perlin noise erzeugt wurde,
* eine Liste mit Königreichen und alle Burgen und deren Verbindungen als Graphen.
*
* Die Karte wird in mehreren Schritten generiert, siehe dazu {@link #generateRandomMap(int, int, int, int, int)}
*/
public class GameMap {
private BufferedImage backgroundImage;
private Graph<Castle> castleGraph;
private List<Kingdom> kingdoms;
// Map Generation
private double[][] noiseValues;
private int width, height, scale;
/**
* Erzeugt eine neue leere Karte. Der Konstruktor sollte niemals direkt aufgerufen werden.
* Um eine neue Karte zu erstellen, muss {@link #generateRandomMap(int, int, int, int, int)} verwendet werden
* @param width die Breite der Karte
* @param height die Höhe der Karte
* @param scale der Skalierungsfaktor
*/
private GameMap(int width, int height, int scale) {
this.castleGraph = new Graph<>();
this.width = width;
this.height = height;
this.scale = scale;
}
/**
* Wandelt einen Noise-Wert in eine Farbe um. Die Methode kann nach belieben angepasst werden
* @param value der Perlin-Noise-Wert
* @return die resultierende Farbe
*/
private Color doubleToColor(double value) {
if (value <= 0.40)
return GameConstants.COLOR_WATER;
else if (value <= 0.5)
return GameConstants.COLOR_SAND;
else if (value <= 0.7)
return GameConstants.COLOR_GRASS;
else if (value <= 0.8)
return GameConstants.COLOR_STONE;
else
return GameConstants.COLOR_SNOW;
}
/**
* Hier wird das Hintergrund-Bild mittels Perlin-Noise erzeugt.
* Siehe auch: {@link PerlinNoise}
*/
private void generateBackground() {
PerlinNoise perlinNoise = new PerlinNoise(width, height, scale);
Dimension realSize = perlinNoise.getRealSize();
noiseValues = new double[realSize.width][realSize.height];
backgroundImage = new BufferedImage(realSize.width, realSize.height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < realSize.width; x++) {
for (int y = 0; y < realSize.height; y++) {
double noiseValue = perlinNoise.getNoise(x, y);
noiseValues[x][y] = noiseValue;
backgroundImage.setRGB(x, y, doubleToColor(noiseValue).getRGB());
}
}
}
/**
* Hier werden die Burgen erzeugt.
* Dabei wir die Karte in Felder unterteilt, sodass auf jedes Fals maximal eine Burg kommt.
* Sollte auf einem Feld keine Position für eine Burg existieren (z.B. aufgrund von Wasser oder angrenzenden Burgen), wird dieses übersprungen.
* Dadurch kann es vorkommen, dass nicht alle Burgen generiert werden
* @param castleCount die maximale Anzahl der zu generierenden Burgen
*/
private void generateCastles(int castleCount) {
double square = Math.ceil(Math.sqrt(castleCount));
double length = width + height;
int tilesX = (int) Math.max(1, (width / length + 0.5) * square) + 5;
int tilesY = (int) Math.max(1, (height / length + 0.5) * square) + 5;
int tileW = (width * scale / tilesX);
int tileH = (height * scale / tilesY);
if (tilesX * tilesY < castleCount) {
throw new IllegalArgumentException(String.format("CALCULATION Error: tilesX=%d * tilesY=%d < castles=%d", tilesX, tilesY, castleCount));
}
// Add possible tiles
List<Point> possibleFields = new ArrayList<>(tilesX * tilesY);
for (int x = 0; x < tilesX - 1; x++) {
for (int y = 0; y < tilesY - 1; y++) {
possibleFields.add(new Point(x, y));
}
}
// Generate castles
List<String> possibleNames = generateCastleNames();
int castlesGenerated = 0;
while (possibleFields.size() > 0 && castlesGenerated < castleCount) {
Point randomField = possibleFields.remove((int) (Math.random() * possibleFields.size()));
int x0 = (int) ((randomField.x + 0.5) * tileW);
int y0 = (int) ((randomField.y + 0.5) * tileH);
for (int x = (int) (0.5 * tileW); x >= 0; x--) {
boolean positionFound = false;
for (int y = (int) (0.5 * tileH); y >= 0; y--) {
int x_mid = (int) (x0 + x + 0.5 * tileW);
int y_mid = (int) (y0 + y + 0.5 * tileH);
if (noiseValues[x_mid][y_mid] >= 0.6) {
String name = possibleNames.isEmpty() ? "Burg " + (castlesGenerated + 1) :
possibleNames.get((int) (Math.random() * possibleNames.size()));
Castle newCastle = new Castle(new Point(x0 + x, y0 + y), name);
boolean doesIntersect = false;
for (Castle r : castleGraph.getAllValues()) {
if (r.distance(newCastle) < Math.max(tileW, tileH)) {
doesIntersect = true;
break;
}
}
if (!doesIntersect) {
possibleNames.remove(name);
castleGraph.addNode(newCastle);
castlesGenerated++;
positionFound = true;
break;
}
}
}
if (positionFound)
break;
}
}
}
/**
* Hier werden die Kanten erzeugt. Dazu werden zunächst alle Burgen durch eine Linie verbunden und anschließend
* jede Burg mit allen anderen in einem bestimmten Radius nochmals verbunden
*/
private void generateEdges() {
// TODO: GameMap#generateEdges()
}
/**
* Hier werden die Burgen in Königreiche unterteilt. Dazu wird der {@link Clustering} Algorithmus aufgerufen.
* @param kingdomCount die Anzahl der zu generierenden Königreiche
*/
private void generateKingdoms(int kingdomCount) {
if(kingdomCount > 0 && kingdomCount < castleGraph.getAllValues().size()) {
Clustering clustering = new Clustering(castleGraph.getAllValues(), kingdomCount);
kingdoms = clustering.getPointsClusters();
} else {
kingdoms = new ArrayList<>();
}
}
/**
* Eine neue Spielfeldkarte generieren.
* Dazu werden folgende Schritte abgearbeitet:
* 1. Das Hintergrundbild generieren
* 2. Burgen generieren
* 3. Kanten hinzufügen
* 4. Burgen in Köngireiche unterteilen
* @param width die Breite des Spielfelds
* @param height die Höhe des Spielfelds
* @param scale die Skalierung
* @param castleCount die maximale Anzahl an Burgen
* @param kingdomCount die Anzahl der Königreiche
* @return eine neue GameMap-Instanz
*/
public static GameMap generateRandomMap(int width, int height, int scale, int castleCount, int kingdomCount) {
width = Math.max(width, 15);
height = Math.max(height, 10);
if (scale <= 0 || castleCount <= 0)
throw new IllegalArgumentException();
System.out.println(String.format("Generating new map, castles=%d, width=%d, height=%d, kingdoms=%d", castleCount, width, height, kingdomCount));
GameMap gameMap = new GameMap(width, height, scale);
gameMap.generateBackground();
gameMap.generateCastles(castleCount);
gameMap.generateEdges();
gameMap.generateKingdoms(kingdomCount);
if(!gameMap.getGraph().allNodesConnected()) {
System.out.println("Fehler bei der Verifikation: Es sind nicht alle Knoten miteinander verbunden!");
return null;
}
return gameMap;
}
/**
* Generiert eine Liste von Zufallsnamen für Burgen. Dabei wird ein Prefix (Schloss, Burg oder Festung) an einen
* vorhandenen Namen aus den Resourcen angefügt. Siehe auch: {@link Resources#getcastleNames()}
* @return eine Liste mit Zufallsnamen
*/
private List<String> generateCastleNames() {
String[] prefixes = {"Schloss", "Burg", "Festung"};
List<String> names = Resources.getInstance().getCastleNames();
List<String> nameList = new ArrayList<>(names.size());
for (String name : names) {
String prefix = prefixes[(int) (Math.random() * prefixes.length)];
nameList.add(prefix + " " + name);
}
return nameList;
}
public int getWidth() {
return this.backgroundImage.getWidth();
}
public int getHeight() {
return this.backgroundImage.getHeight();
}
public BufferedImage getBackgroundImage() {
return this.backgroundImage;
}
public Dimension getSize() {
return new Dimension(this.getWidth(), this.getHeight());
}
public List<Castle> getCastles() {
return castleGraph.getAllValues();
}
public Graph<Castle> getGraph() {
return this.castleGraph;
}
public List<Edge<Castle>> getEdges() {
return this.castleGraph.getEdges();
}
public List<Kingdom> getKingdoms() {
return this.kingdoms;
}
}

View file

@ -0,0 +1,75 @@
package game.map;
import game.Player;
import java.util.LinkedList;
import java.util.List;
/**
* Diese Klasse representiert ein Königreich. Jedes Königreich hat eine Liste von Burgen sowie einen Index {@link #type} im Bereich von 0-5
*
*/
public class Kingdom {
private List<Castle> castles;
private int type;
/**
* Erstellt ein neues Königreich
* @param type der Typ des Königreichs (im Bereich 0-5)
*/
public Kingdom(int type) {
this.castles = new LinkedList<>();
this.type = type;
}
/**
* Eine Burg zum Königreich hinzufügen
* @param castle die Burg, die hinzugefügt werden soll
*/
public void addCastle(Castle castle) {
this.castles.add(castle);
}
/**
* Gibt den Typen des Königreichs zurück. Dies wird zur korrekten Anzeige benötigt
* @return der Typ des Königreichs.
*/
public int getType() {
return this.type;
}
/**
* Eine Burg aus dem Königreich entfernen
* @param castle die zu entfernende Burg
*/
public void removeCastle(Castle castle) {
this.castles.remove(castle);
}
/**
* Gibt den Spieler zurück, der alle Burgen in dem Köngreich besitzt.
* Sollte es keinen Spieler geben, der alle Burgen besitzt, wird null zurückgegeben.
* @return der Besitzer oder null
*/
public Player getOwner() {
if(castles.isEmpty())
return null;
Player owner = castles.get(0).getOwner();
for(Castle castle : castles) {
if(castle.getOwner() != owner)
return null;
}
return owner;
}
/**
* Gibt alle Burgen zurück, die in diesem Königreich liegen
* @return Liste von Burgen im Königreich
*/
public List<Castle> getCastles() {
return this.castles;
}
}

View file

@ -0,0 +1,27 @@
package game.map;
import java.util.Arrays;
import java.util.Vector;
import java.util.stream.Collectors;
public enum MapSize {
SMALL("Klein"),
MEDIUM("Mittel"),
LARGE("Groß");
private String label;
MapSize(String lbl) {
this.label = lbl;
}
@Override
public String toString() {
return this.label;
}
public static Vector<String> getMapSizes() {
return Arrays.stream(values()).map(MapSize::toString).collect(Collectors.toCollection(Vector::new));
}
}

View file

@ -0,0 +1,58 @@
package game.map;
import base.GraphAlgorithm;
import base.Edge;
import base.Graph;
import game.Player;
import game.map.Castle;
import gui.components.MapPanel;
import java.util.List;
public class PathFinding extends GraphAlgorithm<Castle> {
private MapPanel.Action action;
private Player currentPlayer;
public PathFinding(Graph<Castle> graph, Castle sourceCastle, MapPanel.Action action, Player currentPlayer) {
super(graph, graph.getNode(sourceCastle));
this.action = action;
this.currentPlayer = currentPlayer;
}
@Override
protected double getValue(Edge<Castle> edge) {
Castle castleA = edge.getNodeA().getValue();
Castle castleB = edge.getNodeB().getValue();
return castleA.distance(castleB);
}
@Override
protected boolean isPassable(Edge<Castle> edge) {
Castle castleA = edge.getNodeA().getValue();
Castle castleB = edge.getNodeB().getValue();
// One of the regions should belong to the current player
if(castleA.getOwner() != currentPlayer && castleB.getOwner() != currentPlayer)
return false;
if(action == MapPanel.Action.ATTACKING) {
return castleA.getOwner() != null && castleB.getOwner() != null;
} else if(action == MapPanel.Action.MOVING) {
// One of the regions may be empty
if(castleA.getOwner() == null || castleB.getOwner() == null)
return true;
// Else both regions should belong to the current player
return castleA.getOwner() == castleB.getOwner() && castleA.getOwner() == currentPlayer;
} else {
return false;
}
}
public List<Edge<Castle>> getPath(Castle targetCastle) {
return this.getPath(getGraph().getNode(targetCastle));
}
}