/*
* CraftBook Copyright (C) 2010-2017 sk89q <http://www.sk89q.com>
* CraftBook Copyright (C) 2011-2017 me4502 <http://www.me4502.com>
* CraftBook Copyright (C) Contributors
*
* This program 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.
*
* This program 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 this program. If not,
* see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.craftbook.sponge.util;
import com.sk89q.craftbook.core.util.RegexUtil;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.block.BlockState;
import org.spongepowered.api.block.BlockType;
import org.spongepowered.api.block.BlockTypes;
import org.spongepowered.api.block.tileentity.Sign;
import org.spongepowered.api.data.key.Keys;
import org.spongepowered.api.data.property.block.GroundLuminanceProperty;
import org.spongepowered.api.data.property.block.IndirectlyPoweredProperty;
import org.spongepowered.api.data.property.block.PoweredProperty;
import org.spongepowered.api.data.property.block.SkyLuminanceProperty;
import org.spongepowered.api.util.Direction;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
public final class BlockUtil {
/**
* Gets the relative direction of 'other' from 'base'.
*
* @param base The location of base.
* @param other The location of other.
* @return The relative direction
*/
public static Direction getFacing(Location base, Location other) {
for (Direction dir : Direction.values()) {
if (base.getRelative(dir).getPosition().equals(other.getPosition()))
return dir;
}
return null;
}
public static Optional<Integer> getDirectBlockPowerLevel(Location<?> ... blocks) {
int power = -1;
for(Location<?> block : blocks) {
Optional<Integer> optional = getDirectBlockPowerLevel(block);
if(optional.isPresent()) {
if (power < optional.get()) {
power = optional.get();
}
}
}
if (power == -1) {
return Optional.empty();
} else {
return Optional.of(power);
}
}
public static Optional<Integer> getDirectBlockPowerLevel(Location<?> block) {
if (block.get(Keys.POWER).isPresent()) {
return block.get(Keys.POWER);
} else if (block.get(Keys.POWERED).isPresent()) {
return block.get(Keys.POWERED).map((powered -> powered ? 15 : 0));
} else if (block.getBlock().getType() == BlockTypes.POWERED_REPEATER || block.getBlock().getType() == BlockTypes.UNPOWERED_REPEATER) {
return Optional.of(block.getBlock().getType() == BlockTypes.POWERED_REPEATER ? 15 : 0);
}
return Optional.empty();
}
public static Optional<Integer> getBlockPowerLevel(Location<?> block) {
if (getDirectBlockPowerLevel(block).isPresent()) {
return getDirectBlockPowerLevel(block);
} else if (block.getProperty(PoweredProperty.class).isPresent()) {
return Optional.of(block.getProperty(PoweredProperty.class).get().getValue() ? 15 : 0);
}
return Optional.empty();
}
public static Optional<Integer> getBlockIndirectPowerLevel(Location<?> block) {
if (getBlockPowerLevel(block).isPresent()) {
return getBlockPowerLevel(block);
} else if (block.getProperty(IndirectlyPoweredProperty.class).isPresent()) {
return Optional.of(block.getProperty(IndirectlyPoweredProperty.class).get().getValue() ? 15 : 0);
}
return Optional.empty();
}
/**
* Gets the combined light level of this block.
*
* <p>
* Note: This includes both sky and block light levels.
* </p>
*
* @param block The block.
* @return The light level
*/
public static int getLightLevel(Location<?> block) {
Optional<GroundLuminanceProperty> groundLuminanceProperty = block.getProperty(GroundLuminanceProperty.class);
Optional<SkyLuminanceProperty> skyLuminanceProperty = block.getProperty(SkyLuminanceProperty.class);
if(groundLuminanceProperty.isPresent() && skyLuminanceProperty.isPresent())
return (groundLuminanceProperty.get().getValue().intValue() + skyLuminanceProperty.get().getValue().intValue()) / 2;
else if(groundLuminanceProperty.isPresent())
return groundLuminanceProperty.get().getValue().intValue();
else if(skyLuminanceProperty.isPresent())
return skyLuminanceProperty.get().getValue().intValue();
return 0;
}
public static BlockState getBlockStateFromString(String rule) {
BlockType blockType;
Map<String, String> traitSpecifics = new HashMap<>();
if(rule.contains("[") && rule.endsWith("]")) {
String subRule = rule.substring(rule.indexOf('['), rule.length()-2);
String[] parts = RegexUtil.COMMA_PATTERN.split(subRule);
blockType = Sponge.getGame().getRegistry().getType(BlockType.class, rule.substring(0, rule.indexOf('['))).orElse(null);
for(String part : parts) {
String[] keyValue = RegexUtil.EQUALS_PATTERN.split(part);
traitSpecifics.put(keyValue[0].toLowerCase(), keyValue[1]);
}
} else {
blockType = Sponge.getGame().getRegistry().getType(BlockType.class, rule).orElse(null);
}
if(blockType == null) {
return null;
}
BlockState state = blockType.getDefaultState();
for(Map.Entry<String, String> entry : traitSpecifics.entrySet()) {
state.getTrait(entry.getKey()).ifPresent((trait) -> state.withTrait(trait, entry.getValue()));
}
return state;
}
private static Direction[] directFaces = null;
/**
* Get faces that are directly touching the block.
*
* @return Faces that are directly touching the block.
*/
public static Direction[] getDirectFaces() {
if(directFaces == null)
directFaces = new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST};
return directFaces;
}
public static List<Location<World>> getAdjacentExcept(Location<World> location, Direction ... directions) {
List<Location<World>> locations = new ArrayList<>();
for(Direction direction : getDirectFaces()) {
boolean passes = true;
for(Direction direction1 : directions) {
if(direction1 == direction) {
passes = false;
break;
}
}
if (passes) {
locations.add(location.getRelative(direction));
}
}
return locations;
}
/**
* Gets whether or not the specified {@link BlockState} passes the {@link BlockFilter}s.
*
* @param filters The filters
* @param state The state to test
* @return If it passes
*/
public static boolean doesStatePassFilters(Collection<BlockFilter> filters, BlockState state) {
for(BlockFilter filter : filters)
for(BlockState blockState : filter.getApplicableBlockStates())
if(blockState.equals(state))
return true;
return false;
}
/**
* Gets the length of a line of blocks, with a maximum length.
*
* @param startBlock The starting location
* @param testState The block that the line is made of
* @param direction The direction of the line from the starting block
* @param maximum The maximum length
* @return The found length
*/
public static int getLength(Location startBlock, BlockState testState, Direction direction, int maximum) {
int length = 0;
while(length < maximum) {
if(startBlock.getBlock().equals(testState)) {
length ++;
startBlock = startBlock.getRelative(direction);
} else {
break;
}
}
return length;
}
public static int getMinimumLength(Location firstBlock, Location secondBlock, BlockState testState, Direction direction, int maximum) {
return Math.min(getLength(firstBlock, testState, direction, maximum), getLength(secondBlock, testState, direction, maximum));
}
public static Location<World> getNextMatchingSign(Location<World> block, Direction back, int maximumLength, Predicate<Sign> predicate) {
for (int i = 0; i < maximumLength; i++) {
block = block.getRelative(back);
if (SignUtil.isSign(block)) {
Sign sign = (Sign) block.getTileEntity().get();
if (predicate.test(sign)) {
return block;
}
}
}
return null;
}
}