/* * 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.world.interactive; import illarion.client.graphics.Item; import illarion.client.world.Char; import illarion.client.world.MapTile; import illarion.client.world.World; import illarion.client.world.items.ContainerSlot; import illarion.client.world.items.InventorySlot; import illarion.client.world.items.ItemContainer; import illarion.common.types.ItemCount; import org.jetbrains.annotations.Contract; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; /** * Main purpose of this class is to interconnect the GUI environment and the map environment to exchange information * between both. * * @author Martin Karing <nitram@illarion.org> */ @NotThreadSafe public final class InteractionManager { /** * The logger instance of this class. */ @Nonnull private static final Logger LOGGER = LoggerFactory.getLogger(InteractionManager.class); /** * The object that is currently dragged around. */ @Nullable private Draggable draggedObject; /** * This value is set {@code true} in case there is currently a dragging operation in progress. */ private boolean dragging; /** * This is the task that has to be executed once the dragging operation is done. */ @Nullable private Runnable endOfDragAction; /** * The maximal amount of movable objects in the current move operation. */ @Nullable private ItemCount amount; /** * Get the amount of items currently moved around. * * @return the amount of items moved around or {@code null} in case there is currently no dragging operation */ @Nullable @Contract(pure = true) public ItemCount getMovedAmount() { return amount; } /** * Drop a object to a container slot. * * @param container the ID of the container the object is dropped in * @param slot the slot inside the container the object is dropped in * @param count the amount of objects to be dropped at the container */ public void dropAtContainer(int container, int slot, @Nonnull ItemCount count) { if (draggedObject == null) { LOGGER.warn("Dropping to container called without an active dragging operation."); cancelDragging(); }else { // If cancelDragging() didn't stop the sequence, don't do anything anyway ItemContainer itemContainer = World.getPlayer().getContainer(container); if (itemContainer == null) { // If the container is not valid LOGGER.error("Container an item was dropped at was not found."); }else { try { InteractiveContainerSlot targetSlot = itemContainer.getSlot(slot).getInteractive(); draggedObject.dragTo(targetSlot, count); // Enter the contents into the slot } catch (@Nonnull IndexOutOfBoundsException ex) { LOGGER.error("Tried to drop an item at a container slot that does not exist.", ex); } finally { cancelDragging(); // Always clean up the action }// End finally }// End else } // End else } /** * Drop a object at a slot in the inventory. * * @param slot the inventory slot * @param count the amount of items to be dropped in the inventory */ public void dropAtInventory(int slot, @Nonnull ItemCount count) { if (draggedObject == null) { LOGGER.warn("Dropping to inventory called without an active dragging operation."); cancelDragging(); } try { InteractiveInventorySlot targetSlot = World.getPlayer().getInventory().getItem(slot).getInteractive(); draggedObject.dragTo(targetSlot, count); } catch (@Nonnull IndexOutOfBoundsException ex) { LOGGER.error("Tried to drop an item at an inventory slot that does not exist.", ex); } finally { cancelDragging(); } } /** * Drop the currently dragged object on the map. * * @param x the x coordinate on the screen to drop the object to * @param y the y coordinate on the screen to drop the object to * @param count the amount of objects to be dropped at the map */ public void dropAtMap(int x, int y, @Nonnull ItemCount count) { if (draggedObject == null) { LOGGER.warn("Dropping to map called without an active dragging operation."); cancelDragging(); } try { @Nullable InteractiveMapTile possibleTile = World.getMap().getInteractive() .getInteractiveTileOnScreenLoc(x, y); @Nullable Char characterOnScreen = World.getPeople().getCharOnScreenLoc(x, y); @Nullable InteractiveMapTile targetTile; if (characterOnScreen != null) { InteractiveChar iChar = characterOnScreen.getInteractive(); if ((possibleTile != null) && (possibleTile.getElevationDisplayLevel() < iChar.getDisplayLevel())) { targetTile = possibleTile; } else { targetTile = iChar.getInteractiveTile(); } } else { targetTile = possibleTile; } if (targetTile == null) { return; } draggedObject.dragTo(targetTile, count); } finally { cancelDragging(); } } /** * Cancel the current dragging operation. */ public void cancelDragging() { if (endOfDragAction != null) { endOfDragAction.run(); endOfDragAction = null; } draggedObject = null; dragging = false; amount = null; } /** * Check if there is currently a active dragging operation. * * @return {@code true} in case there is a current dragging operation */ public boolean isDragging() { return dragging; } /** * Start dragging around a item from a container slot. * * @param container the ID of the container * @param slot the slot in the container * @param endOfDragOp the operation to be performed at the end of the dragging operation */ public void notifyDraggingContainer(int container, int slot, @Nullable Runnable endOfDragOp) { if (!dragging) { ItemContainer itemContainer = World.getPlayer().getContainer(container); if (itemContainer == null) { LOGGER.error("Start dragging notification about a container that does not exist?!"); return; } try { ContainerSlot conSlot = itemContainer.getSlot(slot); InteractiveContainerSlot sourceSlot = conSlot.getInteractive(); startDragging(sourceSlot); endOfDragAction = endOfDragOp; amount = conSlot.getCount(); return; } catch (@Nonnull IndexOutOfBoundsException ex) { LOGGER.error("Tried to start dragging from a container slot that does not exist?!", ex); } } if (endOfDragOp != null) { endOfDragOp.run(); } } /** * Start a new dragging operation. * * @param draggable the draggable that is dragged around */ private void startDragging(Draggable draggable) { draggedObject = draggable; dragging = true; } /** * Start dragging around a item from a inventory slot. * * @param slot the slot in the inventory * @param endOfDragOp the operation to be performed at the end of the dragging operation */ public void notifyDraggingInventory(int slot, @Nullable Runnable endOfDragOp) { if (!dragging) { try { InventorySlot invSlot = World.getPlayer().getInventory().getItem(slot); InteractiveInventorySlot sourceSlot = invSlot.getInteractive(); if (sourceSlot.isValidItem()) { startDragging(sourceSlot); endOfDragAction = endOfDragOp; amount = invSlot.getCount(); return; } } catch (@Nonnull IndexOutOfBoundsException ex) { LOGGER.error("Tried to start dragging from an inventory slot that does not exist?!", ex); } } if (endOfDragOp != null) { endOfDragOp.run(); } } /** * Start dragging around a item from the map. * * @param targetTile the tile on the map that is dragged around * @param endOfDragOp the operation to be performed at the end of the dragging operation */ public void notifyDraggingMap(@Nonnull MapTile targetTile, @Nullable Runnable endOfDragOp) { if (!dragging) { InteractiveMapTile interactiveMapTile = targetTile.getInteractive(); Item draggedItem = targetTile.getTopItem(); if (draggedItem == null) { LOGGER.error("Tried to start dragging on a tile without an item to drag."); } else if (interactiveMapTile.canDrag()) { startDragging(interactiveMapTile); endOfDragAction = endOfDragOp; amount = draggedItem.getCount(); return; } else { LOGGER.error("Tried to start dragging on a tile without a draggable item."); } } if (endOfDragOp != null) { endOfDragOp.run(); } } }