/* * This file is part of the Illarion project. * * Copyright © 2015 - Illarion e.V. * * Illarion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Illarion is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ package illarion.common.types; import illarion.common.graphics.Layer; import illarion.common.graphics.MapConstants; import illarion.common.net.NetCommReader; import illarion.common.net.NetCommWriter; import org.jetbrains.annotations.Contract; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import java.io.IOException; /** * This class represents a server coordinate. * * @author Martin Karing <nitram@illarion.org> */ @Immutable @ThreadSafe @SuppressWarnings("InstanceVariableNamingConvention") public final class ServerCoordinate { /** * Modifier used at the calculation of the display coordinates in case its a tile above or below the level 0. */ public static final int DISPLAY_Z_OFFSET_MOD = 6; private final int x; private final int y; private final int z; public ServerCoordinate(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } public ServerCoordinate(@Nonnull ServerCoordinate org, @Nonnull Direction dir) { this(org, dir.getDirectionVectorX(), dir.getDirectionVectorY(), 0); } public ServerCoordinate(@Nonnull ServerCoordinate org, int dX, int dY, int dZ) { this(org.x + dX, org.y + dY, org.z + dZ); } public ServerCoordinate(@Nonnull NetCommReader reader) throws IOException { x = reader.readShort(); y = reader.readShort(); z = reader.readShort(); } @Contract(pure = true) public int getX() { return x; } @Contract(pure = true) public int getY() { return y; } @Contract(pure = true) public int getZ() { return z; } public void encode(@Nonnull NetCommWriter writer) { writer.writeShort((short) x); writer.writeShort((short) y); writer.writeShort((short) z); } @Nullable @Contract(pure = true) public Direction getDirection(@Nonnull ServerCoordinate target) { return getDirection(this, target); } @Nullable @Contract(pure = true) public static Direction getDirection(@Nonnull ServerCoordinate origin, @Nonnull ServerCoordinate target) { int dX = origin.getX() - target.getX(); int dY = origin.getY() - target.getY(); if ((dX == 0) && (dY == 0)) { return null; } double theta = Math.atan2(dY, dX) + Math.PI; double part = Math.PI / 8; if (theta < part) { return Direction.East; } else if (theta < (3 * part)) { return Direction.SouthEast; } else if (theta < (5 * part)) { return Direction.South; } else if (theta < (7 * part)) { return Direction.SouthWest; } else if (theta < (9 * part)) { return Direction.West; } else if (theta < (11 * part)) { return Direction.NorthWest; } else if (theta < (13 * part)) { return Direction.North; } else if (theta < (15 * part)) { return Direction.NorthEast; } else { return Direction.East; } } @Contract(pure = true) public int getStepDistance(@Nonnull ServerCoordinate target) { return getStepDistance(this, target); } @Contract(pure = true) public static int getStepDistance(@Nonnull ServerCoordinate origin, @Nonnull ServerCoordinate target) { int dX = Math.abs(origin.getX() - target.getX()); int dY = Math.abs(origin.getY() - target.getY()); return Math.max(dX, dY); } @Contract(pure = true) public double getDistance(@Nonnull ServerCoordinate target) { return getDistance(this, target); } @Contract(pure = true) public static double getDistance(@Nonnull ServerCoordinate origin, @Nonnull ServerCoordinate target) { int dX = origin.getX() - target.getX(); int dY = origin.getY() - target.getY(); return Math.sqrt((dX * dX) + (dY * dY)); } @Contract(pure = true) public boolean isNeighbour(@Nonnull ServerCoordinate otherCoordinate) { return (z == otherCoordinate.z) && (Math.abs(x - otherCoordinate.x) <= 1) && (Math.abs(y - otherCoordinate.y) <= 1); } @Nonnull @Contract(pure = true) public MapCoordinate toMapCoordinate() { return toMapCoordinate(x, y); } @Nonnull @Contract(pure = true) public static MapCoordinate toMapCoordinate(int x, int y) { return new MapCoordinate(toMapColumn(x, y), toMapRow(x, y)); } @Nonnull @Contract(pure = true) public DisplayCoordinate toDisplayCoordinate(@Nonnull Layer layer) { return toDisplayCoordinate(x, y, z, layer); } @Nonnull @Contract(pure = true) public static DisplayCoordinate toDisplayCoordinate(int x, int y, int z, @Nonnull Layer layer) { return new DisplayCoordinate(toDisplayX(x, y), toDisplayY(x, y, z), toDisplayLayer(x, y, z, layer)); } @Contract(pure = true) public int toDisplayX() { return toDisplayX(x, y); } @Contract(pure = true) public static int toDisplayX(int scX, int scY) { return (scX + scY) * MapConstants.STEP_X; } @Contract(pure = true) public int toDisplayY() { return toDisplayY(x, y, z); } @Contract(pure = true) public static int toDisplayY(int scX, int scY, int scZ) { return -(((scX - scY) * MapConstants.STEP_Y) + (DISPLAY_Z_OFFSET_MOD * scZ * MapConstants.STEP_Y)); } @Contract(pure = true) public int toDisplayLayer(@Nonnull Layer layer) { return toDisplayLayer(x, y, z, layer); } @Contract(pure = true) public static int toDisplayLayer(int scX, int scY, int scZ, @Nonnull Layer layer) { return ((scX - scY - (scZ * DisplayCoordinate.LEVEL_DISTANCE)) * DisplayCoordinate.ROW_DISTANCE) - layer.getLayerOffset(); } @Contract(pure = true) public int toMapColumn() { return toMapColumn(x, y); } @Contract(pure = true) public static int toMapColumn(int scX, int scY) { return scX + scY; } @Contract(pure = true) public int toMapRow() { return toMapRow(x, y); } @Contract(pure = true) public static int toMapRow(int scX, int scY) { return scX - scY; } @Override @Contract(value = "null->false", pure = true) public boolean equals(@Nullable Object obj) { return (obj instanceof ServerCoordinate) && equals((ServerCoordinate) obj); } @Contract(value = "null->false", pure = true) public boolean equals(@Nullable ServerCoordinate serverCoordinate) { return (serverCoordinate != null) && (serverCoordinate.x == x) && (serverCoordinate.y == y) && (serverCoordinate.z == z); } @Override @Contract(pure = true) public int hashCode() { return ((((27 + x) * 31) + y) * 31) + z; } @Override @Nonnull @Contract(pure = true) public String toString() { return "Server Coordinate (" + x + ", " + y + ", " + z + ')'; } }