diff --git a/src/main/java/org/terasology/minecarts/blocks/RailBlockFamily.java b/src/main/java/org/terasology/minecarts/blocks/RailBlockFamily.java index 58cfd74..f79c3f6 100644 --- a/src/main/java/org/terasology/minecarts/blocks/RailBlockFamily.java +++ b/src/main/java/org/terasology/minecarts/blocks/RailBlockFamily.java @@ -15,7 +15,6 @@ */ package org.terasology.minecarts.blocks; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; import gnu.trove.map.TByteObjectMap; import gnu.trove.map.hash.TByteObjectHashMap; @@ -35,15 +34,11 @@ import org.terasology.world.block.family.BlockSections; import org.terasology.world.block.family.MultiConnectFamily; import org.terasology.world.block.family.RegisterBlockFamily; -import org.terasology.world.block.family.UpdatesWithNeighboursFamily; import org.terasology.world.block.loader.BlockFamilyDefinition; import org.terasology.world.block.shapes.BlockShape; import javax.print.attribute.standard.Sides; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; +import java.util.*; @RegisterBlockFamily("rails") @BlockSections({"no_connections", "one_connection", "one_connection_slope", "line_connection", "2d_corner", "2d_t", "cross"}) @@ -69,7 +64,7 @@ public RailBlockFamily(BlockFamilyDefinition definition, BlockBuilderHelper bloc this.registerBlock(blockUri,definition,blockBuilder,NO_CONNECTIONS, (byte) 0, Rotation.horizontalRotations()); this.registerBlock(blockUri,definition,blockBuilder,ONE_CONNECTION, SideBitFlag.getSides(Side.RIGHT), Rotation.horizontalRotations()); - this.registerBlock(blockUri,definition,blockBuilder,ONE_CONNECTIONS_SLOPE, SideBitFlag.getSides(Side.BACK, Side.TOP), Rotation.horizontalRotations()); + this.registerBlock(blockUri,definition,blockBuilder,ONE_CONNECTIONS_SLOPE, SideBitFlag.getSides(Side.FRONT, Side.TOP), Rotation.horizontalRotations()); this.registerBlock(blockUri,definition,blockBuilder,TWO_CONNECTIONS_LINE, SideBitFlag.getSides(Side.LEFT, Side.RIGHT), Rotation.horizontalRotations()); this.registerBlock(blockUri,definition,blockBuilder,TWO_CONNECTIONS_CORNER, SideBitFlag.getSides(Side.LEFT, Side.FRONT), Rotation.horizontalRotations()); this.registerBlock(blockUri,definition,blockBuilder,THREE_CONNECTIONS_T, SideBitFlag.getSides(Side.LEFT, Side.RIGHT, Side.FRONT), Rotation.horizontalRotations()); @@ -94,94 +89,48 @@ public Set registerBlock(BlockUri root, BlockFamilyDefinition definition, @Override public Block getBlockForPlacement(Vector3i location, Side attachmentSide, Side direction) { - return blocks.get(getByteConnections(location)); - } - - private byte getByteConnections(Vector3i location) { byte connections = 0; - byte fullConnectedEdges = 0; - int countConnetions = 0; - boolean hasTopBlock = false; - ArrayList skipSides = new ArrayList(); - Vector3i upLocation = new Vector3i(location); - upLocation.y += 1; - Block block = worldProvider.getBlock(upLocation); - - if (block.getURI() != BlockManager.AIR_ID && !block.isPenetrable() && block.isLiquid()) { - hasTopBlock = true; - } - - for (Side connectSide : Side.values()) { - if (connectionCondition(location, connectSide)) { - if (isFullyConnected(location, connectSide, worldProvider, blockEntityRegistry)) - fullConnectedEdges += SideBitFlag.getSide(connectSide); - else - connections += SideBitFlag.getSide(connectSide); - - } else if (hasTopBlock) { - block = worldProvider.getBlock(location); - - if (block.getURI() != BlockManager.AIR_ID && !block.isPenetrable() && block.isLiquid()) { - skipSides.add(connectSide); - } + for (Side connectSide : SideBitFlag.getSides(getConnectionSides())) { + if (this.connectionCondition(location, connectSide) && !isFullyConnected(location,connectSide)) { + connections |= SideBitFlag.getSide(connectSide); } } - if (connections == 0) - connections = fullConnectedEdges; - countConnetions = SideBitFlag.getSides(connections).size(); - - upLocation.y -= 2; - for (Side connectSide : Side.values()) { - if (connectionCondition(upLocation, connectSide)) { - connections += SideBitFlag.getSide(connectSide); + for (Side connectSide : SideBitFlag.getSides(getConnectionSides())) { + if (this.connectionCondition(new Vector3i(location).add(Vector3i.down()), connectSide)) { + connections |= SideBitFlag.getSide(connectSide); } } - upLocation.y += 2; - switch (countConnetions) { - case 0: - for (Side connectSide : Side.values()) { - if (skipSides.contains(connectSide)) { - continue; - } - if (connectionCondition(upLocation, connectSide)) { - connections = 0; - connections += SideBitFlag.getSide(connectSide); - connections += SideBitFlag.getSide(Side.TOP); - break; - } - } - break; - case 1: - EnumSet sides = SideBitFlag.getSides(connections); - Side connectSide = (Side) sides.toArray()[0]; - connectSide = connectSide.reverse(); - if (skipSides.contains(connectSide)) { - break; - } - if (connectionCondition(upLocation,connectSide)) { - connections = 0; - connections += SideBitFlag.getSide(connectSide); - connections += SideBitFlag.getSide(Side.TOP); - break; + for (Side connectSide : SideBitFlag.getSides(getConnectionSides())) { + if (this.connectionCondition(new Vector3i(location).add(Vector3i.up()), connectSide)) { + connections |= SideBitFlag.getSide(Side.TOP); + if(SideBitFlag.getSides(connections).size() == 1){ + connections |= SideBitFlag.getSide(connectSide.reverse()); } break; + } } - return connections; + return blocks.get(connections); } + + @Override + public Block getBlockForNeighborUpdate(Vector3i location, Block oldBlock) { + return oldBlock; + } + + + /** * a fully connected tile has more then 1 connected edge and is not attached to the reference tile * * @param location * @param connectSide - * @param worldProvider - * @param blockEntityRegistry * @return */ - private boolean isFullyConnected(Vector3i location, Side connectSide, WorldProvider worldProvider, BlockEntityRegistry blockEntityRegistry) { + private boolean isFullyConnected(Vector3i location, Side connectSide) { if (connectionCondition(location, connectSide)) { Vector3i neighborLocation = new Vector3i(location); neighborLocation.add(connectSide.getVector3i()); diff --git a/src/main/java/org/terasology/minecarts/blocks/RailsBlockFamilyUpdateSystem.java b/src/main/java/org/terasology/minecarts/blocks/RailsBlockFamilyUpdateSystem.java new file mode 100644 index 0000000..c8f0c7d --- /dev/null +++ b/src/main/java/org/terasology/minecarts/blocks/RailsBlockFamilyUpdateSystem.java @@ -0,0 +1,179 @@ +/* + * Copyright 2015 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.minecarts.blocks; + +import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.entitySystem.entity.EntityRef; +import org.terasology.entitySystem.event.EventPriority; +import org.terasology.entitySystem.event.ReceiveEvent; +import org.terasology.entitySystem.systems.BaseComponentSystem; +import org.terasology.entitySystem.systems.RegisterMode; +import org.terasology.entitySystem.systems.RegisterSystem; +import org.terasology.entitySystem.systems.UpdateSubscriberSystem; +import org.terasology.logic.common.ActivateEvent; +import org.terasology.logic.health.DoDamageEvent; +import org.terasology.logic.health.DoDestroyEvent; +import org.terasology.logic.health.EngineDamageTypes; +import org.terasology.logic.inventory.ItemComponent; +import org.terasology.math.Side; +import org.terasology.math.SideBitFlag; +import org.terasology.math.geom.Vector3i; +import org.terasology.registry.In; +import org.terasology.world.BlockEntityRegistry; +import org.terasology.world.OnChangedBlock; +import org.terasology.world.WorldProvider; +import org.terasology.world.block.Block; +import org.terasology.world.block.BlockComponent; +import org.terasology.world.block.BlockManager; +import org.terasology.world.block.entity.neighbourUpdate.LargeBlockUpdateFinished; +import org.terasology.world.block.entity.neighbourUpdate.LargeBlockUpdateStarting; +import org.terasology.world.block.items.BlockItemComponent; +import org.terasology.world.block.items.OnBlockItemPlaced; + +import java.util.Set; + +@RegisterSystem(RegisterMode.AUTHORITY) +public class RailsBlockFamilyUpdateSystem extends BaseComponentSystem implements UpdateSubscriberSystem { + private static final Logger logger = LoggerFactory.getLogger(RailsBlockFamilyUpdateSystem.class); + + @In + private WorldProvider worldProvider; + @In + private BlockEntityRegistry blockEntityRegistry; + @In + private BlockManager blockManager; + + private int largeBlockUpdateCount; + private Set blocksUpdatedInLargeBlockUpdate = Sets.newHashSet(); + private int[] checkOnHeight = {-1, 0, 1}; + + @ReceiveEvent + public void largeBlockUpdateStarting(LargeBlockUpdateStarting event, EntityRef entity) { + largeBlockUpdateCount++; + } + + @ReceiveEvent + public void largeBlockUpdateFinished(LargeBlockUpdateFinished event, EntityRef entity) { + largeBlockUpdateCount--; + if (largeBlockUpdateCount < 0) { + largeBlockUpdateCount = 0; + throw new IllegalStateException("LargeBlockUpdateFinished invoked too many times"); + } + + if (largeBlockUpdateCount == 0) { + notifyNeighboursOfChangedBlocks(); + } + } + + @ReceiveEvent() + public void doDestroy(DoDestroyEvent event, EntityRef entity, BlockComponent blockComponent) { + Vector3i upBlock = new Vector3i(blockComponent.getPosition()); + upBlock.y += 1; + Block block = worldProvider.getBlock(upBlock); + + if (block.getBlockFamily() instanceof RailBlockFamily) { + blockEntityRegistry.getEntityAt(upBlock).send(new DoDamageEvent(1000, EngineDamageTypes.DIRECT.get())); + } + } + + //prevents rails from being stacked on top of each other. + @ReceiveEvent(components = {BlockItemComponent.class, ItemComponent.class}, priority = EventPriority.PRIORITY_HIGH) + public void onBlockActivated(ActivateEvent event, EntityRef item) { + BlockComponent blockComponent = event.getTarget().getComponent(BlockComponent.class); + if (blockComponent == null) + return; + + Vector3i targetBlock = blockComponent.getPosition(); + Block centerBlock = worldProvider.getBlock(targetBlock.x, targetBlock.y, targetBlock.z); + + if (centerBlock.getBlockFamily() instanceof RailBlockFamily) { + event.consume(); + } + } + + @ReceiveEvent(components = {BlockItemComponent.class, ItemComponent.class}) + public void onPlaceBlock(OnBlockItemPlaced event, EntityRef entity) { + BlockComponent blockComponent = event.getPlacedBlock().getComponent(BlockComponent.class); + if (blockComponent == null) + return; + + Vector3i targetBlock = blockComponent.getPosition(); + Block centerBlock = worldProvider.getBlock(targetBlock.x, targetBlock.y, targetBlock.z); + + if (centerBlock.getBlockFamily() instanceof RailBlockFamily) { + processUpdateForBlockLocation(targetBlock); + } + + } + + private void notifyNeighboursOfChangedBlocks() { + // Invoke the updates in another large block change for this class only + largeBlockUpdateCount++; + while (!blocksUpdatedInLargeBlockUpdate.isEmpty()) { + Set blocksToUpdate = blocksUpdatedInLargeBlockUpdate; + + // Setup new collection for blocks changed in this pass + blocksUpdatedInLargeBlockUpdate = Sets.newHashSet(); + + for (Vector3i blockLocation : blocksToUpdate) { + processUpdateForBlockLocation(blockLocation); + } + } + largeBlockUpdateCount--; + } + + @ReceiveEvent(components = {BlockComponent.class}) + public void blockUpdate(OnChangedBlock event, EntityRef blockEntity) { + if (largeBlockUpdateCount > 0) { + blocksUpdatedInLargeBlockUpdate.add(event.getBlockPosition()); + } else { + Vector3i blockLocation = event.getBlockPosition(); + processUpdateForBlockLocation(blockLocation); + } + } + + private void processUpdateForBlockLocation(Vector3i blockLocation) { + for (int height : checkOnHeight) { + for (Side side : Side.horizontalSides()) { + Vector3i neighborLocation = new Vector3i(blockLocation); + neighborLocation.add(side.getVector3i()); + neighborLocation.y += height; + Block neighborBlock = worldProvider.getBlock(neighborLocation); + EntityRef blockEntity = blockEntityRegistry.getBlockEntityAt(neighborLocation); + if (blockEntity.hasComponent(RailComponent.class)) { + RailBlockFamily railsFamily = (RailBlockFamily) neighborBlock.getBlockFamily(); + Block neighborBlockAfterUpdate = railsFamily.getBlockForPlacement(neighborLocation, null,null); + if (neighborBlock != neighborBlockAfterUpdate && neighborBlockAfterUpdate != null) { + byte connections = Byte.parseByte(neighborBlock.getURI().getIdentifier().toString()); + //only add segment with two connections + if (SideBitFlag.getSides(connections).size() <= 1) + worldProvider.setBlock(neighborLocation, neighborBlockAfterUpdate); + } + } + } + } + } + + @Override + public void update(float delta) { + if (largeBlockUpdateCount > 0) { + logger.error("Unmatched LargeBlockUpdateStarted - LargeBlockUpdateFinished not invoked enough times"); + } + largeBlockUpdateCount = 0; + } +} \ No newline at end of file