/* * This file is part of LanternServer, licensed under the MIT License (MIT). * * Copyright (c) LanternPowered <https://www.lanternpowered.org> * Copyright (c) SpongePowered <https://www.spongepowered.org> * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.lanternpowered.server.block.behavior.vanilla; import com.flowpowered.math.imaginary.Quaterniond; import com.flowpowered.math.vector.Vector3d; import com.flowpowered.math.vector.Vector3i; import org.lanternpowered.server.behavior.Behavior; import org.lanternpowered.server.behavior.BehaviorContext; import org.lanternpowered.server.behavior.BehaviorResult; import org.lanternpowered.server.behavior.Parameters; import org.lanternpowered.server.behavior.pipeline.BehaviorPipeline; import org.lanternpowered.server.block.BlockSnapshotBuilder; import org.lanternpowered.server.block.behavior.simple.BlockSnapshotProviderPlaceBehavior; import org.lanternpowered.server.block.behavior.types.BreakBlockBehavior; import org.lanternpowered.server.block.behavior.types.PlaceBlockBehavior; import org.lanternpowered.server.data.key.LanternKeys; import org.lanternpowered.server.data.type.LanternDoorHalf; import org.lanternpowered.server.util.Quaternions; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.data.key.Keys; import org.spongepowered.api.data.property.block.ReplaceableProperty; import org.spongepowered.api.data.property.block.SolidCubeProperty; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.living.Living; import org.spongepowered.api.util.Direction; import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; import java.util.Optional; public class DoorBehavior implements PlaceBlockBehavior, BreakBlockBehavior { private static final Quaterniond LEFT_ANGLE = Quaternions.fromAxesAnglesDeg(new Vector3d(0, -90, 0)); @Override public BehaviorResult tryPlace(BehaviorPipeline<Behavior> pipeline, BehaviorContext context) { final Location<World> location = context.tryGet(Parameters.BLOCK_LOCATION); final Direction face = context.tryGet(Parameters.INTERACTION_FACE); // Door can only be placed by clicking in the floor if (face != Direction.DOWN) { return BehaviorResult.PASS; } final Location<World> down = location.getBlockRelative(Direction.DOWN); final SolidCubeProperty solidProp = down.getProperty(SolidCubeProperty.class).get(); // The door must be placed on a solid block //noinspection ConstantConditions if (!solidProp.getValue()) { return BehaviorResult.PASS; } final Location<World> up = location.getBlockRelative(Direction.UP); final ReplaceableProperty replaceableProp = up.getProperty(ReplaceableProperty.class).get(); //noinspection ConstantConditions if (!replaceableProp.getValue()) { return BehaviorResult.PASS; } final BlockSnapshot snapshot = context.getCause().get(BlockSnapshotProviderPlaceBehavior.BLOCK_SNAPSHOT, BlockSnapshot.class) .orElseThrow(() -> new IllegalStateException("The BlockSnapshotRetrieveBehavior BlockSnapshot isn't present.")); final BlockSnapshotBuilder builder = BlockSnapshotBuilder.create().from(snapshot); context.populateBlockSnapshot(builder, BehaviorContext.PopulationFlags.CREATOR_AND_NOTIFIER); Direction facing = Direction.NORTH; Vector3i left = Vector3i.UNIT_X; final Optional<Entity> optSource = context.getCause().first(Entity.class); if (optSource.isPresent()) { final Entity source = optSource.get(); final Vector3d rotVector; if (source instanceof Living) { rotVector = ((Living) source).getHeadRotation(); } else { rotVector = optSource.get().getRotation(); } // Calculate the direction the entity is looking final Vector3d dir = Quaternions.fromAxesAnglesDeg(rotVector.mul(-1)).rotate(Vector3d.FORWARD); facing = Direction.getClosestHorizontal(dir, Direction.Division.CARDINAL); left = LEFT_ANGLE.rotate(facing.asOffset()).toInt(); facing = facing.getOpposite(); } builder.add(Keys.DIRECTION, facing); // TODO: Hinges context.addBlockChange(builder.location(location).build()); context.addBlockChange(builder.add(LanternKeys.DOOR_HALF, LanternDoorHalf.UPPER).location(up).build()); return BehaviorResult.SUCCESS; } @Override public BehaviorResult tryBreak(BehaviorPipeline<Behavior> pipeline, BehaviorContext context) { final Location<World> location = context.get(Parameters.BLOCK_LOCATION).get(); final BlockState baseState = location.getBlock(); final LanternDoorHalf half = baseState.get(LanternKeys.DOOR_HALF).get(); final BlockSnapshotBuilder builder = BlockSnapshotBuilder.create(); builder.blockState(BlockTypes.AIR.getDefaultState()); context.populateBlockSnapshot(builder, BehaviorContext.PopulationFlags.CREATOR_AND_NOTIFIER); builder.location(location); context.addBlockChange(builder.build()); final Direction dir = half == LanternDoorHalf.LOWER ? Direction.UP : Direction.DOWN; final LanternDoorHalf other = half == LanternDoorHalf.LOWER ? LanternDoorHalf.UPPER : LanternDoorHalf.LOWER; final Location<World> loc = location.getBlockRelative(dir); BlockState otherState = loc.getBlock(); if (otherState.get(LanternKeys.DOOR_HALF).orElse(null) == other && otherState.with(LanternKeys.DOOR_HALF, half).orElse(null) == baseState) { builder.location(loc); context.addBlockChange(builder.build()); return BehaviorResult.CONTINUE; } return BehaviorResult.CONTINUE; } }