/* * 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.client.gui.controller.game; import de.lessvoid.nifty.elements.Element; import illarion.client.graphics.AnimationUtility; import illarion.client.gui.MiniMapGui.Pointer; import illarion.client.resources.MiscImageFactory; import illarion.common.types.ServerCoordinate; import illarion.common.util.FastMath; import org.illarion.engine.graphic.Color; import org.illarion.engine.graphic.Graphics; import org.illarion.engine.graphic.Sprite; import org.illarion.engine.nifty.IgeRenderImage; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * This is the implementation of the pointers. This class is the image that is displayed on the GUI in order to * show the arrow on the mini map. It takes care for rendering it with the proper rotation applied. */ final class MiniMapArrowPointer implements IgeRenderImage, Pointer { /** * The sprite that contains the arrow. This is displayed in case the point is outside the area of the mini map. */ @Nonnull private final Sprite arrowSprite; /** * The sprite that contains the point. This is displayed in case the target location is on the area of the * mini map. */ @Nonnull private final Sprite pointSprite; /** * The location the arrow is supposed to point to. */ @Nullable private ServerCoordinate targetLocation; /** * The Nifty-GUI element this pointer is assigned to. */ @Nonnull private final Element parentElement; /** * The current angle of the arrow in (1/10)° */ private int currentAngle; /** * The target angle of the arrow in (1/10)° */ private int targetAngle; /** * The current delta value of the X coordinates. */ private int currentDeltaX; /** * The current delta value of the Y coordinates. */ private int currentDeltaY; /** * Change color depending on value */ private boolean isCurrentQuest; /** * Create a new arrow pointer. */ MiniMapArrowPointer(@Nonnull Element parentElement) { arrowSprite = MiscImageFactory.getInstance().getTemplate(MiscImageFactory.MINI_MAP_ARROW).getSprite(); pointSprite = MiscImageFactory.getInstance().getTemplate(MiscImageFactory.MINI_MAP_POINT).getSprite(); this.parentElement = parentElement; } @Override public void setCurrentQuest(boolean currentQuest) { isCurrentQuest = currentQuest; } @Override public int getWidth() { return arrowSprite.getWidth(); } @Override public int getHeight() { return arrowSprite.getHeight(); } @Override public void dispose() { // nothing to do } private float getAngle() { double angle = Math.toDegrees(Math.atan2(currentDeltaY, currentDeltaX)); return (float) angle + 45.f; } @Override public void renderImage( @Nonnull Graphics g, int x, int y, int width, int height, @Nonnull Color color, float imageScale) { renderImage(g, x, y, width, height, 0, 0, arrowSprite.getWidth(), arrowSprite.getHeight(), color, imageScale, arrowSprite.getWidth() / 2, arrowSprite.getHeight() / 2); } @Override public void renderImage( @Nonnull Graphics g, int x, int y, int w, int h, int srcX, int srcY, int srcW, int srcH, @Nonnull Color color, float scale, int centerX, int centerY) { int scaledWidth = Math.round(w * scale); int scaledHeight = Math.round(h * scale); int fixedX = x + Math.round((w - scaledWidth) * ((float) centerX / (float) w)); int fixedY = y + Math.round((h - scaledHeight) * ((float) centerY / (float) h)); Color pointerColor = POINTER_COLOR; if (isCurrentQuest) { pointerColor = ACTIVEPOINTER_COLOR; } if (isOnMapArea()) { int offsetX = (FastMath.sqrt(FastMath.sqr(currentDeltaX) / 2) * -FastMath.sign(currentDeltaX)) + (FastMath.sqrt(FastMath.sqr(currentDeltaY) / 2) * -FastMath.sign(currentDeltaY)); int offsetY = (FastMath.sqrt(FastMath.sqr(currentDeltaX) / 2) * FastMath.sign(currentDeltaX)) + (FastMath.sqrt(FastMath.sqr(currentDeltaY) / 2) * -FastMath.sign(currentDeltaY)); g.drawTexture(pointSprite.getFrame(0), fixedX - offsetX, fixedY - offsetY, scaledWidth, scaledHeight, srcX, srcY, srcW, srcH, centerX - fixedX, centerY - fixedY, 0.f, pointerColor); } else { float angle = (float) currentAngle / 10.f; int spriteOffsetX = arrowSprite.getOffsetX(); int spriteOffsetY = arrowSprite.getOffsetY(); float radianAngle = FastMath.toRadians(angle); float sinAngle = FastMath.sin(radianAngle); float cosAngle = FastMath.cos(radianAngle); int rotationOffsetX = Math.round((spriteOffsetX * cosAngle) + (spriteOffsetY * sinAngle)); int rotationOffsetY = Math.round((spriteOffsetX * sinAngle) + (spriteOffsetY * cosAngle)); g.drawTexture(arrowSprite.getFrame(0), fixedX + rotationOffsetX, fixedY - rotationOffsetY, scaledWidth, scaledHeight, srcX, srcY, srcW, srcH, centerX - fixedX, centerY - fixedY, angle, pointerColor); } } /** * This functions returns if this pointer is on the mini map right now or outside of it. * * @return {@code true} in case the pointer is on the map */ private boolean isOnMapArea() { return FastMath.sqrt(FastMath.sqr(currentDeltaX) + FastMath.sqr(currentDeltaY)) < 71; } /** * Update the angle of arrow. * * @param delta the time since the last update * @param playerLocation the current location of the player */ void update(int delta, @Nonnull ServerCoordinate playerLocation) { if (targetLocation == null) { throw new IllegalStateException("This pointer has no set target and is not valid to be updated."); } int dX = targetLocation.getX() - playerLocation.getX(); int dY = targetLocation.getY() - playerLocation.getY(); if ((currentDeltaX != dX) || (currentDeltaY != dY)) { currentDeltaX = dX; currentDeltaY = dY; targetAngle = ((Math.round(getAngle() * 10.f) % 3600) + 3600) % 3600; } animateAngle(delta); } /** * Perform the animation of this arrow. * * @param delta the time since the last update */ private void animateAngle(int delta) { if (targetAngle == currentAngle) { return; } int angleDiff = targetAngle - currentAngle; if (Math.abs(angleDiff) <= 1800) { currentAngle += AnimationUtility.approach(0, angleDiff, -1800, 1800, delta); } else if (angleDiff > 0) { currentAngle += AnimationUtility.approach(0, angleDiff - 3600, -3600, 0, delta); } else { currentAngle += AnimationUtility.approach(0, angleDiff + 3600, 0, 3600, delta); } currentAngle = ((currentAngle % 3600) + 3600) % 3600; } @Override public void setTarget(@Nonnull ServerCoordinate coordinate) { targetLocation = coordinate; } /** * Get the parent Nifty-GUI element. * * @return the parent element */ @Nonnull public Element getParentElement() { return parentElement; } }