/* * 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.mapedit.data; import illarion.common.types.ServerCoordinate; import illarion.mapedit.data.MapTile.MapTileFactory; import illarion.mapedit.events.HistoryPasteCutEvent; import illarion.mapedit.history.CopyPasteAction; import illarion.mapedit.history.GroupAction; import illarion.mapedit.history.ItemPlacedAction; import org.bushe.swing.event.EventBus; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.nio.file.Path; import java.util.List; import java.util.Set; /** * This class represents a whole map, including name, path, dimensions, and data. * * @author Tim */ public class Map implements Iterable<MapTile> { /** * The map name */ private final String name; /** * The path to save this map */ private final Path path; /** * The width of the map */ private final int width; /** * The height of the map */ private final int height; /** * The x coordinate of the origin of the map */ private final int x; /** * The y coordinate */ private final int y; /** * The map level */ private final int z; /** * The tiles. */ @Nonnull private final MapTile[] mapTileData; private int activeX = Integer.MIN_VALUE; private int activeY = Integer.MIN_VALUE; private boolean isFillDragging; private int positionX; private int positionY; private boolean visible; private int fillStartX; private int fillStartY; private int fillX; private int fillY; @Nonnull private final SelectionManager selectionManager; /** * Creates a new map * * @param name the map name * @param path the map path * @param w the with of the map * @param h the height of the map * @param x the x coordinate of the origin of the map * @param y the y coordinate of the origin of the map * @param z the map level (= z coordinate) */ public Map(String name, Path path, int w, int h, int x, int y, int z) { this.name = name; this.path = path; width = w; height = h; this.x = x; this.y = y; this.z = z; mapTileData = new MapTile[w * h]; visible = true; selectionManager = new SelectionManager(); } @Nullable public MapTile getActiveTile() { return getTileAt(activeX, activeY); } @Nullable public List<MapItem> getItemsOnActiveTile() { List<MapItem> items = null; MapTile tile = getTileAt(activeX, activeY); if (tile != null) { items = tile.getMapItems(); } return items; } @Nonnull public Set<MapPosition> getSelectedTiles() { return selectionManager.getSelection(); } public boolean isActiveTile(int x, int y) { return (activeX == x) && (activeY == y); } public boolean isFillDragging() { return isFillDragging; } public boolean isPositionAtTile(int x, int y) { return (positionX == x) && (positionY == y); } @Nullable public ItemPlacedAction removeItemOnActiveTile(int index) { ItemPlacedAction action = null; MapTile tile = getTileAt(activeX, activeY); if (tile != null) { action = new ItemPlacedAction(activeX, activeY, tile.getMapItemAt(index), null, this); tile.removeMapItem(index); } return action; } public void replaceItemOnActiveTile(int index, int newIndex) { MapTile tile = getTileAt(activeX, activeY); if (tile != null) { List<MapItem> items = tile.getMapItems(); if (items != null) { MapItem item = items.get(index); items.set(index, items.get(newIndex)); items.set(newIndex, item); } } } public void setActiveTile(int x, int y) { activeX = x; activeY = y; } public void setFillingArea(int x, int y, int startX, int startY) { fillX = x; fillY = y; fillStartX = startX; fillStartY = startY; isFillDragging = true; } public void setMapPosition(int mapX, int mapY) { positionX = mapX - x; positionY = mapY - y; } /** * Sets a tile at a specified position. * * @param mapTile the tile to add. */ public void setTileAt(int x, int y, @Nonnull MapTile mapTile) { setTileAtIndex(mapToIndex(x, y), mapTile); } /** * Sets a tile at a specified position. * * @param index the index where the new tile is set * @param mapTile the tile to add. */ private void setTileAtIndex(int index, @Nonnull MapTile mapTile) { mapTileData[index] = mapTile; } /** * Get a tile located at a specific internal index value. */ MapTile getTileAtIndex(int index) { return mapTileData[index]; } /** * Convert map coordinates to the internal index value. * * @param x the x coordinate * @param y the y coordinate * @return the internal index value * @throws IllegalArgumentException in case x or y is out of range */ private int mapToIndex(int x, int y) { if (x < 0 || x >= getWidth()) { throw new IllegalArgumentException("X is out of range. 0 <= " + x + " < " + getWidth()); } if (y < 0 || y >= getHeight()) { throw new IllegalArgumentException("Y is out of range. 0 <= " + y + " < " + getHeight()); } return (y * width) + x; } int indexToMapX(int index) { if (index < 0 || index >= mapTileData.length) { throw new IllegalArgumentException("Index is out of range. 0 <= " + index + " < " + mapTileData.length); } return index % width; } int indexToMapY(int index) { if (index < 0 || index >= mapTileData.length) { throw new IllegalArgumentException("Index is out of range. 0 <= " + index + " < " + mapTileData.length); } return index / width; } public void setTileAt(@Nonnull ServerCoordinate loc, @Nonnull MapTile mapTile) { setTileAt(loc.getX(), loc.getY(), mapTile); } /** * Adds an item to a specified position. * * @param x the x coordinate relative to the origin * @param y the y coordinate relative to the origin * @param mapItem the item <- u don't sayy ;) */ public void addItemAt(int x, int y, MapItem mapItem) { mapTileData[mapToIndex(x, y)].addMapItem(mapItem); } public void setVisible(boolean visible) { this.visible = visible; } public boolean isVisible() { return visible; } /** * Sets a warp point at to specified tile * * @param x the x coordinate of the warp point * @param y the y coordinate of the warp point * @param warpPoint the warp point <- u don't sayy ;) */ public void setWarpAt(int x, int y, MapWarpPoint warpPoint) { mapTileData[mapToIndex(x, y)].setMapWarpPoint(warpPoint); } /** * Return a tile at a specified position. * * @param x the x coordinate * @param y the y coordinate * @return the tile */ @Nullable public MapTile getTileAt(int x, int y) { if (!contains(x, y)) { return null; } int i = mapToIndex(x, y); if (mapTileData[i] != null) { return mapTileData[i]; } MapTile tile = MapTileFactory.createNew(0, 0, 0, 0); setTileAtIndex(i, tile); return tile; } @Nullable public MapTile getTileAt(@Nonnull ServerCoordinate loc) { return getTileAt(loc.getX(), loc.getY()); } /** * @return the map width */ public int getWidth() { return width; } /** * @return the map height */ public int getHeight() { return height; } /** * @return the x coordinate of the origin in global coordinates */ public int getX() { return x; } /** * @return the y coordinate of the origin in gobal coordinates */ public int getY() { return y; } /** * @return the map level */ public int getZ() { return z; } /** * @return the path to save the map */ public Path getPath() { return path; } /** * @return the map name */ public String getName() { return name; } public int getFillStartX() { return fillStartX; } public int getFillStartY() { return fillStartY; } public int getFillX() { return fillX; } public int getFillY() { return fillY; } public void setFillDragging(boolean dragging) { isFillDragging = dragging; } /** * Checks if the map contains the following coordinates * * @param x the x coordinate * @param y the y coordinate * @return {@code true} if the map contains x and y */ public boolean contains(int x, int y) { return (x >= 0) && (y >= 0) && (x < getWidth()) && (y < getHeight()); } @Override public boolean equals(@Nullable Object obj) { if (super.equals(obj)) { return true; } if (obj instanceof Map) { Map otherMap = (Map) obj; return name.equals(otherMap.name) && path.equals(otherMap.path); } return false; } @Override public int hashCode() { return name.hashCode(); } /** * Checks if the is selected * * @param x the x coordinate * @param y the y coordinate * @return {@code true} if x and y is a selected tile */ public boolean isSelected(int x, int y) { return selectionManager.isSelected(x, y); } /** * Set the selected state of a tile on the map * * @param x the x coordinate * @param y the y coordinate * @param selected the selected state the tile should have */ public void setSelected(int x, int y, boolean selected) { if (selected) { selectionManager.select(x, y); } else { selectionManager.deselect(x, y); } } /** * Copies the selected tiles * * @return a MapSelection with the selected tiles */ public MapSelection copySelectedTiles() { return selectionManager.copy(this); } /** * Cuts the selected tiles * * @return a MapSelection with the selected tiles */ public MapSelection cutSelectedTiles() { return selectionManager.cut(this); } @Nonnull @Override public String toString() { return name; } /** * Pastes the tiles from the MapSelection * * @param startX starting x coordinate * @param startY starting y coordinate * @param mapSelection tiles to paste */ public void pasteTiles(int startX, int startY, @Nonnull MapSelection mapSelection) { GroupAction action = new GroupAction(); for (MapPosition position : mapSelection.getSelectedPositions()) { int newX = startX + (position.getX() - mapSelection.getOffsetX()); int newY = startY + (position.getY() - mapSelection.getOffsetY()); if (contains(newX, newY)) { MapTile oldTile = getTileAt(newX, newY); MapTile newTile = MapTileFactory.copy(mapSelection.getMapTileAt(position)); action.addAction(new CopyPasteAction(newX, newY, oldTile, newTile, this)); setTileAt(newX, newY, newTile); } } if (!action.isEmpty()) { EventBus.publish(new HistoryPasteCutEvent(action)); } } @Override public MapIterator iterator() { return new MapIterator(this, mapTileData.length); } }