/*
* 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.graphics.Tile;
import illarion.client.net.client.*;
import illarion.client.world.MapTile;
import illarion.client.world.World;
import illarion.client.world.items.ContainerSlot;
import illarion.common.types.ItemCount;
import illarion.common.types.ItemId;
import illarion.common.types.ServerCoordinate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* This is the interactive representation of a tile on the map.
*
* @author Martin Karing <nitram@illarion.org>
* @author Vilarion <vilarion@illarion.org>
*/
public class InteractiveMapTile implements Draggable, DropTarget, Usable {
/**
* The ID that is needed to tell the server that the operations refer to a
* tile on the map.
*/
private static final byte REFERENCE_ID = 1;
/**
* The tile this interactive tile refers to.
*/
private final MapTile parentTile;
/**
* Copy constructor.
*
* @param tile the instance that shall be copied
*/
public InteractiveMapTile(@Nonnull InteractiveMapTile tile) {
parentTile = tile.parentTile;
}
/**
* Constructor that allows setting the tile to be used.
*
* @param tile the tile this interactive class refers to
*/
public InteractiveMapTile(MapTile tile) {
parentTile = tile;
}
/**
* Check if it is possible to drag this tile to another location. This
* implies that there is something on this tile that can be dragged.
*
* @return <code>true</code> in case a dragging operation is valid for this
* tile
*/
public boolean canDrag() {
return isInUseRange() && parentTile.canMoveItem();
}
/**
* The logging instance that takes care for the logging output of this class.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(InteractiveMapTile.class);
/**
* Drag something from a map tile to
*/
@Override
public void dragTo(@Nonnull InteractiveChar targetChar, @Nonnull ItemCount count) {
if (!canDrag()) {
LOGGER.error("Finished dragging of tile that can't be dragged.");
return;
}
MapTile tile = World.getMap().getMapAt(targetChar.getLocation());
if (tile == null) {
LOGGER.error("Dragged to a tile that does not exist.");
return;
}
dragTo(tile.getInteractive(), count);
}
@Override
public void dragTo(@Nonnull InteractiveInventorySlot targetSlot, @Nonnull ItemCount count) {
if (!canDrag()) {
LOGGER.error("Finished dragging of tile that can't be dragged.");
return;
}
ItemId topItemId = getTopItemId();
assert topItemId != null;
if (!targetSlot.isAcceptingItem(topItemId)) {
return;
}
World.getNet().sendCommand(new DragMapInvCmd(getLocation(), targetSlot.getSlotId(), count));
}
/**
* Drag this tile to another tile.
*
* @param targetTile the tile to drag this tile to
*/
@Override
public void dragTo(@Nonnull InteractiveMapTile targetTile, @Nonnull ItemCount count) {
if (!canDrag()) {
return;
}
if (targetTile.getLocation().equals(World.getPlayer().getLocation())) {
World.getNet().sendCommand(new PickUpItemCmd(getLocation()));
} else {
World.getNet().sendCommand(new DragMapMapCmd(getLocation(), targetTile.getLocation(), count));
}
}
@Override
public void dragTo(@Nonnull InteractiveContainerSlot targetSlot, @Nonnull ItemCount count) {
if (!canDrag()) {
return;
}
ItemId topItemId = getTopItemId();
assert topItemId != null;
if (!targetSlot.acceptItem(topItemId)) {
return;
}
ContainerSlot slot = targetSlot.getSlot();
World.getNet().sendCommand(new DragMapScCmd(getLocation(), slot.getContainerId(), slot.getLocation(), count));
}
/**
* Perform a use operation on this tile.
*/
@Override
public void use() {
if (!isInUseRange()) {
return;
}
Item topItem = getTopItem();
if ((topItem != null) && topItem.getTemplate().getItemInfo().isContainer()) {
World.getNet().sendCommand(new OpenOnMapCmd(getLocation()));
return;
}
World.getNet().sendCommand(new UseMapCmd(getLocation()));
}
/**
* Request a look at on this tile.
*/
public void lookAt(@Nonnull Item lookAtItem) {
int index = parentTile.getItemIndex(lookAtItem);
if (index == -1) {
return;
}
World.getNet().sendCommand(new LookAtMapItemCmd(getLocation(), index));
}
/**
* Get the location of the tile this interactive tile refers to
*
* @return the location of this tile
*/
@Nonnull
public ServerCoordinate getLocation() {
return parentTile.getCoordinates();
}
/**
* Check if the tile is inside the valid using range of the player character.
*
* @return {@code true} in case the character is allowed to use anything on this tile or the tile itself
*/
@Override
public boolean isInUseRange() {
@Nonnull ServerCoordinate playerLocation = World.getPlayer().getMovementHandler().getServerLocation();
if (playerLocation.getZ() == getLocation().getZ()) {
return playerLocation.getStepDistance(getLocation()) <= getUseRange();
}
return false;
}
@Override
public int getUseRange() {
return 1;
}
/**
* Get the item that is located on top of the tile.
*
* @return the item on top of the tile
*/
@Nullable
public Item getTopItem() {
return parentTile.getTopItem();
}
/**
* Get the ID of the first item on this tile.
*
* @return the item ID
*/
@Nullable
public ItemId getTopItemId() {
Item topItem = getTopItem();
if (topItem == null) {
return null;
}
return topItem.getItemId();
}
/**
* Get the display level. This can be used to determine the order of multiple objects in the render list. In this
* case this function is quite a hack so use it with care. It does only use the order of the tile in case there
* are no items on it. In case there are any items that have a elevation, the function returns the display level
* of the elevating object.
*
* @return the display level
*/
public int getElevationDisplayLevel() {
@Nullable Tile tile = parentTile.getTile();
if (tile == null) {
return Integer.MAX_VALUE;
}
return tile.getOrder();
}
}