/*
* Copyright (c) 2015 NOVA, All rights reserved.
* This library is free software, licensed under GNU Lesser General Public License version 3
*
* This file is part of NOVA.
*
* NOVA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NOVA 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.
*
* You should have received a copy of the GNU General Public License
* along with NOVA. If not, see <http://www.gnu.org/licenses/>.
*/
package nova.core.recipes.crafting;
import nova.core.entity.component.Player;
import nova.core.item.Item;
import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
import java.util.Iterator;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* Represents a crafting grid. Crafting grids contain an item in each slot, each of which can be read and possibly
* modified.
* @author Stan Hebben
*/
public interface CraftingGrid extends Iterable<Item> {
String TOPOLOGY_SQUARE = "nova:square";
String TYPE_CRAFTING = "nova:crafting";
/**
* Represents the player that is currently using this crafting grid.
* @return crafting grid player
*/
Optional<Player> getPlayer();
/**
* Returns the total size of this crafting grid. For a square crafting grid, this is width x height. Note that the
* size can be less than width x height in the case of non-square crafting grid (but never more).
* @return The size
*/
int size();
/**
* Gets the item in a specified slot.
* @param slot slot index
* @return the item in the given slot
* @see #getCrafting(int, int)
*/
Optional<Item> getCrafting(int slot);
/**
* Gets the item at the given (x, y) position.
* Returns an empty optional if there is no the item at that position.
* @param x x position
* @param y y position
* @return the item at the given position
* @see #getCrafting(int)
*/
Optional<Item> getCrafting(int x, int y);
/**
* Modifies the item in the given slot. If the modification is not possible, this method returns false. If modification
* was successful, returns true.
*
* Slots in a crafting grid should also be ordered with the slots at the y=0 first, starting from smallest x to
* largest x and then from smallest y to largest y (natural order). However, not all (x, y) positions need to have
* a corresponding slot.
* @param slot slot to be modified
* @param item the item to be setCrafting
* @return true if modification was successful, false otherwise
* @see #setCrafting(int, int, Optional)
*/
boolean setCrafting(int slot, Optional<Item> item);
/**
* Sets the item at the given (x, y) position.
* @param x x position
* @param y y position
* @param item the item to be setCrafting
* @return true if the modification is successful, false otherwise
* @see #setCrafting(int, Optional)
*/
boolean setCrafting(int x, int y, Optional<Item> item);
/**
* Removes the item in a specified slot.
* @param slot slot index
* @return the previous item in the given slot
* @see #removeCrafting(int, int)
*/
default Optional<Item> removeCrafting(int slot) {
Optional<Item> ret = CraftingGrid.this.getCrafting(slot);
CraftingGrid.this.setCrafting(slot, Optional.empty());
return ret;
}
/**
* Removes the item at the given (x, y) position.
* @param x x position
* @param y y position
* @return the previous item at the position
* @see #removeCrafting(int)
*/
default Optional<Item> removeCrafting(int x, int y) {
Optional<Item> ret = getCrafting(x, y);
setCrafting(x, y, Optional.empty());
return ret;
}
/**
* Gets the width of the crafting grid. For a non-square grid,, this should return the highest acceptable x-value + 1.
* @return crafting grid width
*/
int getWidth();
/**
* Gets the height of the crafting grid. For a non-square grid, this should return the highest acceptable y-value + 1.
* @return crafting grid height
*/
int getHeight();
/**
* Gives back a certain item. In the case of a player's crafting grid,
* this would typically go back to the player's inventory.
* Machines may implement this method differently.
*
* @param item The {@link Item} to give back.
*/
default void giveBack(Item item) {
getPlayer().map(Player::getInventory).ifPresent(inv -> inv.add(item));
}
/**
* Gets the topology of the crafting grid. For a square grid, this should be {@link #TOPOLOGY_SQUARE}. Other
* kinds of grids may return a different value.
* @return crafting grid topology
*/
String getTopology();
/**
* Gets the type of crafting grid. For a crafting recipe, this should return {@link #TYPE_CRAFTING}. Other
machines or crafting tables (with a separate setCrafting of recipes) may return a different value.
* @return crafting grid type
*/
String getType();
/**
* Counts the number of filled the items in this crafting grid.
* @return number of non-empty the items in this crafting grid
*/
default int countFilledStacks() {
int filledStacks = 0;
for (int i = 0; i < size(); i++) {
if (CraftingGrid.this.getCrafting(i).isPresent()) {
filledStacks++;
}
}
return filledStacks;
}
/**
* Gets the first non-empty item in this crafting grid. Returns empty if and only if the crafting grid is completely
* empty.
* @return first non-empty item
*/
default Optional<Item> getFirstNonEmptyItem() {
for (int i = 0; i < size(); i++) {
Optional<Item> item = CraftingGrid.this.getCrafting(i);
if (item.isPresent()) {
return item;
}
}
return Optional.empty();
}
/**
* Finds the position of the first non-empty the item in this crafting grid. Returns empty if and only if the crafting
* grid is completely empty.
* @return first non-empty item position
*/
default Optional<Vector2D> getFirstNonEmptyPosition() {
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
if (getCrafting(x, y).isPresent()) {
return Optional.of(new Vector2D(x, y));
}
}
}
return Optional.empty();
}
@Override
default Iterator<Item> iterator() {
return new CraftingGridIterator(this);
}
@Override
default Spliterator<Item> spliterator() {
return Spliterators.spliterator(iterator(), size(), Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED);
}
/**
* Represents this crafting grid as an {@link Item} {@link Stream}
* @return This crafting grid as an {@link Item} {@link Stream}
*/
default Stream<Item> stream() {
return StreamSupport.stream(spliterator(), false);
}
}