/* * 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.block; import nova.core.block.component.BlockProperty; import nova.core.component.SidedComponentProvider; import nova.core.component.misc.FactoryProvider; import nova.core.component.transform.BlockTransform; import nova.core.entity.Entity; import nova.core.event.bus.CancelableEvent; import nova.core.event.bus.Event; import nova.core.item.Item; import nova.core.item.ItemFactory; import nova.core.language.LanguageManager; import nova.core.language.Translatable; import nova.core.util.Direction; import nova.core.util.Identifiable; import nova.core.world.World; import nova.internal.core.Game; import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; import org.apache.commons.math3.util.FastMath; import java.util.Collections; import java.util.Optional; import java.util.Set; /** * The class that represents the instance of a block in the world. * * @author Calclavia */ public class Block extends SidedComponentProvider implements Identifiable, Translatable { /** * Called to get the {@link ItemFactory} that refers to this Block instance. * * @return The {@link ItemFactory} that refers to this Block instance. */ public ItemFactory getItemFactory() { return Game.items().getItemFromBlock(getFactory()); } /** * Called to get the {@link BlockFactory} that refers to this Block class. * * @return The {@link BlockFactory} that refers to this Block class. */ public final BlockFactory getFactory() { return (BlockFactory) components.get(FactoryProvider.class).factory; } @Override public final String getID() { return getFactory().getID(); } @Override public String getUnlocalizedName() { return getFactory().getUnlocalizedName(); } @Override public String getLocalizedName() { return LanguageManager.instance().translate(this.getUnlocalizedName() + ".name", this.getReplacements()); } /** * Gets the block transformation data. * ({@link #world() world} and {@link #position() position}) * * @return The block transformation data */ public final BlockTransform transform() { return components.get(BlockTransform.class); } /** * Gets the world that this block resides in. * * @return The world that this block resides in */ public final World world() { return transform().world(); } /** * Gets the position of this block in the world. * * @return The position of this block */ public final Vector3D position() { return transform().position(); } /** * Get the {@code X} coordinate of this block. * * @return The {@code X} coordinate of this block */ public final long x() { return (long) FastMath.floor(position().getX()); } /** * Get the {@code Y} coordinate of this block. * * @return The {@code Y} coordinate of this block */ public final long y() { return (long) FastMath.floor(position().getY()); } /** * Get the {@code Z} coordinate of this block. * * @return The {@code Z} coordinate of this block */ public final long z() { return (long) FastMath.floor(position().getZ()); } /** * Gets the breaking difficulty for the block. 1 is the standard, regular * block hardness of the game. {@code Double.infinity} is unbreakable. * @return The breaking difficulty. */ public double getHardness() { return components.getOp(BlockProperty.Hardness.class).orElseGet(BlockProperty.Hardness::new).getHardness(); } /** * Gets the explosion resistance for the block. 1 is the standard, regular * resistance of the game. {@code Double.infinity} is unexplodeable. * @return The resistance. */ public double getResistance() { return components.getOp(BlockProperty.Resistance.class).orElseGet(BlockProperty.Resistance::new).getResistance(); } /** * Checks if this block can be replaced. * @return True if this block can be replaced. */ public boolean canReplace() { return false; } /** * Called when an ItemBlock tries to place a block in this position whether to displace the place position or not. * If the ItemBlock does not displace the position, it will replace this block. * TODO move out into BlockProperty * @return True if by right clicking on this block, the placement of the new block should be displaced. */ public boolean shouldDisplacePlacement() { return true; } // Block Events /** * The event that is called when a block next to this one changes (removed, placed, etc...). */ public static class NeighborChangeEvent extends CancelableEvent { public final Optional<Vector3D> neighborPosition; /** * Called when a block next to this one changes (removed, placed, etc...). * @param neighborPosition The position of the block that changed. */ public NeighborChangeEvent(Optional<Vector3D> neighborPosition) { this.neighborPosition = neighborPosition; } } /** * The event that is called when the block is placed. */ public static class PlaceEvent extends Event { /** * The entity that placed the block */ public final Entity placer; /** * Side placed on */ public final Direction side; /** * Where the player clicked on the block for placement. */ public final Vector3D hit; /** * The item used to place this block. */ public final Item item; /** * Called when the block is placed. * * @param placer The entity that placed the block * @param side The side placed on * @param hit Where the entity clicked on the block for placement * @param item The item used to place this block */ public PlaceEvent(Entity placer, Direction side, Vector3D hit, Item item) { this.placer = placer; this.side = side; this.hit = hit; this.item = item; } } /** * The event that is called when the block is about to be removed. */ public static class RemoveEvent extends Event { /** * The entity that is removing the block */ public final Optional<Entity> entity; /** * {@code true} if the block can be removed */ public boolean result = true; /** * Called when the block is about to be removed. * * @param entity The entity that is removing the block */ public RemoveEvent(Optional<Entity> entity) { this.entity = entity; } } /** * The event that is called when the block is right clicked. */ public static class RightClickEvent extends Event { /** * The entity that clicked this object. Most likely a * player. */ public final Entity entity; /** * The side it was clicked. */ public final Direction side; /** * The position it was clicked. */ public final Vector3D position; /** * {@code true} if the right click action does something. */ public boolean result = false; /** * Called when the block is right clicked. * * @param entity The entity that clicked this object * @param side The side it was clicked * @param position The position it was clicked */ public RightClickEvent(Entity entity, Direction side, Vector3D position) { this.entity = entity; this.side = side; this.position = position; } } /** * The event that is called when the block is left clicked. */ public static class LeftClickEvent extends Event { /** * The entity that clicked this object. Most likely a * player. */ public final Entity entity; /** * The side it was clicked. */ public final Direction side; /** * The position it was clicked. */ public final Vector3D position; /** * {@code true} if the right click action does something. */ public boolean result = false; /** * Called when the block is left clicked. * * @param entity The entity that clicked this object * @param side The side it was clicked * @param position The position it was clicked */ public LeftClickEvent(Entity entity, Direction side, Vector3D position) { this.entity = entity; this.side = side; this.position = position; } } /** * The event that is called when the block is broken and it is time to drop it as an item. */ public static class DropEvent extends Event { /** * The set of items to drop. */ public Set<Item> drops; /** * Called when the block is broken and it is time to drop it as an item. * * @param block The block that was broken. */ public DropEvent(Block block) { this.drops = Collections.singleton(block.getItemFactory().build()); } } }