/*
* Copyright 2013 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.core.logic.door;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.utilities.Assets;
import org.terasology.audio.AudioManager;
import org.terasology.audio.StaticSound;
import org.terasology.audio.events.PlaySoundEvent;
import org.terasology.entitySystem.entity.EntityManager;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.entitySystem.event.ReceiveEvent;
import org.terasology.entitySystem.metadata.EntitySystemLibrary;
import org.terasology.entitySystem.systems.BaseComponentSystem;
import org.terasology.entitySystem.systems.RegisterSystem;
import org.terasology.logic.common.ActivateEvent;
import org.terasology.logic.inventory.InventoryManager;
import org.terasology.logic.inventory.ItemComponent;
import org.terasology.logic.location.LocationComponent;
import org.terasology.math.Region3i;
import org.terasology.math.Side;
import org.terasology.math.geom.Vector3f;
import org.terasology.math.geom.Vector3i;
import org.terasology.registry.In;
import org.terasology.rendering.logic.MeshComponent;
import org.terasology.world.BlockEntityRegistry;
import org.terasology.world.WorldProvider;
import org.terasology.world.block.Block;
import org.terasology.world.block.BlockComponent;
import org.terasology.world.block.entity.placement.PlaceBlocks;
import org.terasology.world.block.regions.BlockRegionComponent;
import java.util.HashMap;
import java.util.Map;
/**
*/
@RegisterSystem
public class DoorSystem extends BaseComponentSystem {
private static final Logger logger = LoggerFactory.getLogger(DoorSystem.class);
@In
private WorldProvider worldProvider;
@In
private BlockEntityRegistry blockEntityRegistry;
@In
private EntityManager entityManager;
@In
private AudioManager audioManager;
@In
private InventoryManager inventoryManager;
@In
private EntitySystemLibrary entitySystemLibrary;
@ReceiveEvent(components = {DoorComponent.class, ItemComponent.class})
public void placeDoor(ActivateEvent event, EntityRef entity) {
DoorComponent door = entity.getComponent(DoorComponent.class);
BlockComponent targetBlockComp = event.getTarget().getComponent(BlockComponent.class);
if (targetBlockComp == null) {
event.consume();
return;
}
Vector3f horizDir = new Vector3f(event.getDirection());
horizDir.y = 0;
Side facingDir = Side.inDirection(horizDir);
if (!facingDir.isHorizontal()) {
event.consume();
return;
}
Vector3f offset = new Vector3f(event.getHitPosition());
offset.sub(targetBlockComp.getPosition().toVector3f());
Side offsetDir = Side.inDirection(offset);
Vector3i primePos = new Vector3i(targetBlockComp.getPosition());
primePos.add(offsetDir.getVector3i());
Block primeBlock = worldProvider.getBlock(primePos);
if (!primeBlock.isReplacementAllowed()) {
event.consume();
return;
}
Block belowBlock = worldProvider.getBlock(primePos.x, primePos.y - 1, primePos.z);
Block aboveBlock = worldProvider.getBlock(primePos.x, primePos.y + 1, primePos.z);
// Determine top and bottom blocks
Vector3i bottomBlockPos;
Vector3i topBlockPos;
if (belowBlock.isReplacementAllowed()) {
bottomBlockPos = new Vector3i(primePos.x, primePos.y - 1, primePos.z);
topBlockPos = primePos;
} else if (aboveBlock.isReplacementAllowed()) {
bottomBlockPos = primePos;
topBlockPos = new Vector3i(primePos.x, primePos.y + 1, primePos.z);
} else {
event.consume();
return;
}
Side attachSide = determineAttachSide(facingDir, offsetDir, bottomBlockPos, topBlockPos);
if (attachSide == null) {
event.consume();
return;
}
Side closedSide = facingDir.reverse();
if (closedSide == attachSide || closedSide.reverse() == attachSide) {
closedSide = attachSide.yawClockwise(1);
}
Block newBottomBlock = door.bottomBlockFamily.getBlockForPlacement(worldProvider, blockEntityRegistry, bottomBlockPos, closedSide, Side.TOP);
Block newTopBlock = door.topBlockFamily.getBlockForPlacement(worldProvider, blockEntityRegistry, bottomBlockPos, closedSide, Side.TOP);
Map<Vector3i, Block> blockMap = new HashMap<>();
blockMap.put(bottomBlockPos, newBottomBlock);
blockMap.put(topBlockPos, newTopBlock);
PlaceBlocks blockEvent = new PlaceBlocks(blockMap, event.getInstigator());
worldProvider.getWorldEntity().send(blockEvent);
if (!blockEvent.isConsumed()) {
EntityRef newDoor = entityManager.create("DoorRegion");
entity.removeComponent(MeshComponent.class);
newDoor.addComponent(new BlockRegionComponent(Region3i.createBounded(bottomBlockPos, topBlockPos)));
Vector3f doorCenter = bottomBlockPos.toVector3f();
doorCenter.y += 0.5f;
newDoor.addComponent(new LocationComponent(doorCenter));
DoorComponent newDoorComp = newDoor.getComponent(DoorComponent.class);
newDoorComp.closedSide = closedSide;
newDoorComp.openSide = attachSide.reverse();
newDoorComp.isOpen = false;
newDoor.saveComponent(newDoorComp);
newDoor.send(new PlaySoundEvent(Assets.getSound("engine:PlaceBlock").get(), 0.5f));
logger.info("Closed Side: {}", newDoorComp.closedSide);
logger.info("Open Side: {}", newDoorComp.openSide);
}
}
private Side determineAttachSide(Side facingDir, Side offsetDir, Vector3i bottomBlockPos, Vector3i topBlockPos) {
Side attachSide = null;
if (offsetDir.isHorizontal()) {
if (canAttachTo(topBlockPos, offsetDir.reverse()) && canAttachTo(bottomBlockPos, offsetDir.reverse())) {
attachSide = offsetDir.reverse();
}
}
if (attachSide == null) {
Side clockwise = facingDir.yawClockwise(1);
if (canAttachTo(topBlockPos, clockwise) && canAttachTo(bottomBlockPos, clockwise)) {
attachSide = clockwise;
}
}
if (attachSide == null) {
Side anticlockwise = facingDir.yawClockwise(-1);
if (canAttachTo(topBlockPos, anticlockwise) && canAttachTo(bottomBlockPos, anticlockwise)) {
attachSide = anticlockwise;
}
}
return attachSide;
}
private boolean canAttachTo(Vector3i doorPos, Side side) {
Vector3i adjacentBlockPos = new Vector3i(doorPos);
adjacentBlockPos.add(side.getVector3i());
Block adjacentBlock = worldProvider.getBlock(adjacentBlockPos);
return adjacentBlock.isAttachmentAllowed();
}
@ReceiveEvent(components = {DoorComponent.class, BlockRegionComponent.class, LocationComponent.class})
public void onFrob(ActivateEvent event, EntityRef entity) {
DoorComponent door = entity.getComponent(DoorComponent.class);
Side newSide = (door.isOpen) ? door.closedSide : door.openSide;
BlockRegionComponent regionComp = entity.getComponent(BlockRegionComponent.class);
Block bottomBlock = door.bottomBlockFamily.getBlockForPlacement(worldProvider, blockEntityRegistry, regionComp.region.min(), newSide, Side.TOP);
worldProvider.setBlock(regionComp.region.min(), bottomBlock);
Block topBlock = door.topBlockFamily.getBlockForPlacement(worldProvider, blockEntityRegistry, regionComp.region.max(), newSide, Side.TOP);
worldProvider.setBlock(regionComp.region.max(), topBlock);
StaticSound sound = (door.isOpen) ? door.closeSound : door.openSound;
if (sound != null) {
entity.send(new PlaySoundEvent(sound, 1f));
}
door.isOpen = !door.isOpen;
entity.saveComponent(door);
}
}