package me.srvstr.world; import java.awt.Point; import java.awt.geom.Point2D; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import javax.swing.JOptionPane; import me.srvstr.bot.Direction; import me.srvstr.bot.Gytebot; import me.srvstr.objects.Coin; import me.srvstr.objects.Wall; import me.srvstr.objects.WorldObject; public class World { public static final int MAP_SIZE = 32; private List<String> rawMapData; private int rawDataWidth; private int coinCount; private String author; private String version; private String name; private String date; private String mapPath; private volatile WorldObject[][] objects; private volatile Gytebot gytebot; private boolean updateCoinBuffer; private int[] coinBufferData; private boolean updateWallBufferData; private float[] wallBufferData; public World() { this.rawMapData = new LinkedList<>(); this.coinBufferData = new int[1024]; this.wallBufferData = new float[1024]; setWorld("/data/standardlevel.map"); } public final void setWorld(String fileName) { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(fileName))); } catch (Exception e1) { try { reader = new BufferedReader(new FileReader(new File(fileName))); } catch (Exception e2) { JOptionPane.showMessageDialog(null, "Unable to open Map: " + fileName, "Error - opening map", 0); } } if (reader != null) { try { this.gytebot = null; this.rawDataWidth = 0; this.rawMapData.clear(); try { reader.lines().forEachOrdered(line -> parseLine(line)); if (this.rawDataWidth > 0 && this.rawMapData.size() > 0) { this.objects = new WorldObject[this.rawDataWidth][this.rawMapData.size()]; createWorldData(); updateTransferBuffers(); this.mapPath = fileName; } else { setWorld(this.mapPath); JOptionPane.showMessageDialog(null, "Unable to open Map: " + fileName, "Error - opening map", 0); } } catch (Error e) { setWorld(this.mapPath); JOptionPane.showMessageDialog(null, "Unable to open Map: " + fileName, "Error - opening map", 0); } finally { reader.close(); } } catch (IOException e) { e.printStackTrace(); } } } public final void setDataString(String data) { this.gytebot = null; this.rawDataWidth = 0; this.rawMapData.clear(); try { Arrays.<String>asList(data.split("(?=\\n)")).forEach(line -> parseLine(line)); if (this.rawDataWidth > 0 && this.rawMapData.size() > 0) { this.objects = new WorldObject[this.rawDataWidth][this.rawMapData.size()]; createWorldData(); updateTransferBuffers(); } else { setWorld(this.mapPath); JOptionPane.showMessageDialog(null, "The map was not properly loaded", "Error - converting map", 0); } } catch (Error e) { setWorld(this.mapPath); JOptionPane.showMessageDialog(null, "Unable to convert Map: " + e.getMessage(), "Error - converting map", 0); } } private void parseLine(String line) throws Error { String trimed = line.trim(); if (trimed.length() != 0) { String[] splitted = trimed.split("[/](?=/)|(?=[@])"); String rawMapLine = new String(); byte b; int i; String[] arrayOfString1; for (i = (arrayOfString1 = splitted).length, b = 0; b < i;) { String content = arrayOfString1[b]; if (content.startsWith("/")) { break; } if (content.startsWith("@")) { String[] tag = content.split("\\s+"); if (tag.length > 2) { throw new AssertionError("To many arguments provided for tag: " + content); } String str; switch ((str = tag[0]).hashCode()) { case -440667701: if (str.equals("@author")) { this.author = tag[1]; break; } case 62181358: if (str.equals("@date")) { this.date = tag[1]; break; } case 62479051: if (str.equals("@name")) { this.name = tag[1]; break; } case 222319768: if (str.equals("@version")) { this.version = tag[1]; break; } default: throw new AssertionError("Unknown tag found: " + tag[0]); } } else { rawMapLine = String.valueOf(rawMapLine) + content; } b++; } if (rawMapLine.length() > 0) { this.rawMapData.add(rawMapLine); } this.rawDataWidth = Math.max(this.rawDataWidth, rawMapLine.length()); if (this.rawDataWidth > 32) { throw new AssertionError("Map size exceeds valid map bounds of 32x32! It is to wide"); } if (this.rawMapData.size() > 32) { throw new AssertionError("Map size exceeds valid map bounds of 32x32! It is to large"); } } } private void createWorldData() { this.coinCount = 0; for (int y = 0; y < this.rawMapData.size(); y++) { for (int x = 0; x < ((String) this.rawMapData.get(y)).length(); x++) { char current = ((String) this.rawMapData.get(y)).charAt(x); if (current == '#') { this.objects[x][y] = (WorldObject) new Wall(new Point2D.Float(x, y)); } else if (current == '€' || current == '$') { this.objects[x][y] = (WorldObject) new Coin(new Point2D.Float(x, y)); this.coinCount++; } else if (current == '^' || current == '>' || current == 'v' || current == 'V' || current == '<') { if (this.gytebot != null) { System.err.println("Conflicting world rule: There is already a gytebot!"); JOptionPane.showMessageDialog(null, "Conflicting world rule: There is already a gytebot", "Error - converting map", 0); } else if (x < ((String) this.rawMapData.get(0)).length() && y < this.rawMapData.size()) { this.gytebot = new Gytebot(new Point2D.Float(x, y), Direction.directionFromCodePoint(current)); } else { this.gytebot = null; JOptionPane.showMessageDialog(null, "Gytebot is placed outside Map", "Error - converting map", 0); } } else if (current != ' ') { System.err.println("Unkown map entity: " + current); } } } } private void updateTransferBuffers() { Arrays.fill(this.coinBufferData, 0); Arrays.fill(this.wallBufferData, 0.0F); for (int y = 0; y < (this.objects[0]).length; y++) { for (int x = 0; x < this.objects.length; x++) { if (this.objects[x][y] instanceof Coin) { this.coinBufferData[x + y * this.objects.length] = 1; } else if (this.objects[x][y] instanceof Wall) { this.wallBufferData[x + y * 32] = 1.0F; } } } this.updateCoinBuffer = true; this.updateWallBufferData = true; } public final void setWall(int x, int y) { setObject(x, y, (WorldObject) new Wall(new Point2D.Float(x, y))); } public final void setCoin(int x, int y) { setObject(x, y, (WorldObject) new Coin(new Point2D.Float(x, y))); } public final void removeObject(int x, int y) { if (this.objects[x][y] != null) { this.objects[x][y] = null; updateTransferBuffers(); } else { JOptionPane.showMessageDialog(null, "Index to remove object from is already empty", "Exception", 0); } } private void setObject(int x, int y, WorldObject object) { if (x >= 0 && x < this.objects.length && y >= 0 && y < (this.objects[0]).length) { this.objects[x][y] = object; updateTransferBuffers(); } else { JOptionPane.showMessageDialog(null, "The requested object placement occured outside of valid bounds!", "Exception", 0); } } public boolean isLocationObstructed(Point point) { if (point.x < 0 || point.y < 0 || point.x >= getWidth() || point.y >= getHeight()) { return true; } if (this.objects[point.x][point.y] instanceof Wall) { return true; } return false; } public boolean isCoinPlacedHere(Point2D.Float location) { if (location.x < 0.0F || location.y < 0.0F || location.x >= getWidth() || location.y >= getHeight()) { return false; } if (this.objects[Math.round(location.x)][Math.round(location.y)] instanceof Coin) { return true; } return false; } public final String toString() { return this.rawMapData.stream().reduce("", (A, B) -> String.valueOf(A) + B + System.lineSeparator()); } public boolean updateWallBufferData() { if (this.updateWallBufferData) { this.updateWallBufferData = false; return true; } return false; } public boolean updateCoinTextureData() { if (this.updateCoinBuffer) { this.updateCoinBuffer = false; return true; } return false; } public final int getCoinCount() { return this.coinCount; } public final String getAuthor() { return this.author; } public final String getVersion() { return this.version; } public final String getName() { return this.name; } public final String getDate() { return this.date; } public final synchronized Gytebot getGytebot() { return this.gytebot; } public final int[] getCoinBufferData() { return this.coinBufferData; } public final int getWidth() { return this.objects.length; } public final int getHeight() { return (this.objects[0]).length; } public int getDrawGytebotAsInt() { return (this.gytebot == null) ? 0 : 1; } public int[] getCoinTextureData() { return this.coinBufferData; } public float[] getWallBufferData() { return this.wallBufferData; } }