Seats, part II
- Any living entity can now use seats - Fix client sync issues with seats - Fixed contraptions double-reversing roll and pitch values when communicating to the collision engine - Seats now transfer their passengers to a contraption when moved and back when disassembled - Attempted further refinements to the collision response of horizontally rotated contraptions - Set up a hook to inject custom interaction between players and contraption mounted blocks on right-click - Seats can now by mounted by players while assembled to a contraption - Minor refactors to the contraption class
89
src/main/java/ContraptionInteractionHandler.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
import org.apache.commons.lang3.mutable.MutableObject;
|
||||
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionInteractionPacket;
|
||||
import com.simibubi.create.foundation.networking.AllPackets;
|
||||
import com.simibubi.create.foundation.utility.RaycastHelper;
|
||||
import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.player.ClientPlayerEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.shapes.VoxelShape;
|
||||
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.client.event.InputEvent.ClickInputEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||
|
||||
@EventBusSubscriber
|
||||
public class ContraptionInteractionHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void rightClickingOnContraptionsGetsHandledLocally(ClickInputEvent event) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
ClientPlayerEntity player = mc.player;
|
||||
if (player == null)
|
||||
return;
|
||||
if (mc.world == null)
|
||||
return;
|
||||
if (!event.isUseItem())
|
||||
return;
|
||||
Vec3d origin = RaycastHelper.getTraceOrigin(player);
|
||||
|
||||
double reach = mc.playerController.getBlockReachDistance();
|
||||
if (mc.objectMouseOver != null && mc.objectMouseOver.getHitVec() != null)
|
||||
reach = Math.min(mc.objectMouseOver.getHitVec().distanceTo(origin), reach);
|
||||
|
||||
Vec3d target = RaycastHelper.getTraceTarget(player, reach, origin);
|
||||
for (ContraptionEntity contraptionEntity : mc.world.getEntitiesWithinAABB(ContraptionEntity.class,
|
||||
new AxisAlignedBB(origin, target))) {
|
||||
|
||||
Vec3d localOrigin = contraptionEntity.toLocalVector(origin);
|
||||
Vec3d localTarget = contraptionEntity.toLocalVector(target);
|
||||
Contraption contraption = contraptionEntity.getContraption();
|
||||
|
||||
MutableObject<BlockRayTraceResult> mutableResult = new MutableObject<>();
|
||||
PredicateTraceResult predicateResult = RaycastHelper.rayTraceUntil(localOrigin, localTarget, p -> {
|
||||
BlockInfo blockInfo = contraption.blocks.get(p);
|
||||
if (blockInfo == null)
|
||||
return false;
|
||||
BlockState state = blockInfo.state;
|
||||
VoxelShape raytraceShape = state.getShape(Minecraft.getInstance().world, BlockPos.ZERO.down());
|
||||
if (raytraceShape.isEmpty())
|
||||
return false;
|
||||
BlockRayTraceResult rayTrace = raytraceShape.rayTrace(localOrigin, localTarget, p);
|
||||
if (rayTrace != null) {
|
||||
mutableResult.setValue(rayTrace);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (predicateResult == null || predicateResult.missed())
|
||||
return;
|
||||
|
||||
BlockRayTraceResult rayTraceResult = mutableResult.getValue();
|
||||
Hand hand = event.getHand();
|
||||
Direction face = rayTraceResult.getFace();
|
||||
BlockPos pos = rayTraceResult.getPos();
|
||||
|
||||
if (!contraptionEntity.handlePlayerInteraction(player, pos, face, hand))
|
||||
return;
|
||||
AllPackets.channel.sendToServer(new ContraptionInteractionPacket(contraptionEntity, hand,
|
||||
pos, face));
|
||||
event.setCanceled(true);
|
||||
event.setSwingHand(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@ public class AllEntityTypes {
|
|||
public static final RegistryEntry<EntityType<SuperGlueEntity>> SUPER_GLUE =
|
||||
register("super_glue", SuperGlueEntity::new, EntityClassification.MISC, 10, Integer.MAX_VALUE, false, SuperGlueEntity::build);
|
||||
public static final RegistryEntry<EntityType<SeatEntity>> SEAT =
|
||||
register("seat", SeatEntity::new, EntityClassification.MISC, 0, Integer.MAX_VALUE, false, SeatEntity::build);
|
||||
register("seat", SeatEntity::new, EntityClassification.MISC, 5, Integer.MAX_VALUE, false, SeatEntity::build);
|
||||
|
||||
private static <T extends Entity> RegistryEntry<EntityType<T>> register(String name, IFactory<T> factory,
|
||||
EntityClassification group, int range, int updateFrequency, boolean sendVelocity,
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
package com.simibubi.create.content.contraptions.components.actors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.simibubi.create.AllShapes;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.IPortableBlock;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.MobEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.pathfinding.PathNodeType;
|
||||
import net.minecraft.util.ActionResultType;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.math.shapes.ISelectionContext;
|
||||
|
@ -19,8 +27,9 @@ import net.minecraft.util.math.shapes.VoxelShape;
|
|||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class SeatBlock extends Block {
|
||||
public class SeatBlock extends Block implements IPortableBlock {
|
||||
|
||||
public static MovementBehaviour MOVEMENT = new SeatMovementBehaviour();
|
||||
private boolean inCreativeTab;
|
||||
|
||||
public SeatBlock(Properties p_i48440_1_, boolean inCreativeTab) {
|
||||
|
@ -41,8 +50,21 @@ public class SeatBlock extends Block {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onLanded(IBlockReader p_176216_1_, Entity p_176216_2_) {
|
||||
Blocks.PINK_BED.onLanded(p_176216_1_, p_176216_2_);
|
||||
public void onLanded(IBlockReader reader, Entity entity) {
|
||||
BlockPos pos = entity.getPosition();
|
||||
if (entity instanceof PlayerEntity || !(entity instanceof LivingEntity) || isSeatOccupied(entity.world, pos)) {
|
||||
Blocks.PINK_BED.onLanded(reader, entity);
|
||||
return;
|
||||
}
|
||||
if (reader.getBlockState(pos)
|
||||
.getBlock() != this)
|
||||
return;
|
||||
sitDown(entity.world, pos, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathNodeType getAiPathNodeType(BlockState state, IBlockReader world, BlockPos pos, MobEntity entity) {
|
||||
return PathNodeType.RAIL;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,17 +74,46 @@ public class SeatBlock extends Block {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ActionResultType onUse(BlockState p_225533_1_, World world, BlockPos pos, PlayerEntity player, Hand p_225533_5_, BlockRayTraceResult p_225533_6_) {
|
||||
if (SeatEntity.TAKEN.containsKey(pos))
|
||||
return ActionResultType.FAIL;
|
||||
public ActionResultType onUse(BlockState p_225533_1_, World world, BlockPos pos, PlayerEntity player,
|
||||
Hand p_225533_5_, BlockRayTraceResult p_225533_6_) {
|
||||
if (player.isSneaking())
|
||||
return ActionResultType.PASS;
|
||||
|
||||
List<SeatEntity> seats = world.getEntitiesWithinAABB(SeatEntity.class, new AxisAlignedBB(pos));
|
||||
if (!seats.isEmpty()) {
|
||||
SeatEntity seatEntity = seats.get(0);
|
||||
List<Entity> passengers = seatEntity.getPassengers();
|
||||
if (!passengers.isEmpty() && passengers.get(0) instanceof PlayerEntity)
|
||||
return ActionResultType.PASS;
|
||||
if (!world.isRemote) {
|
||||
seatEntity.removePassengers();
|
||||
player.startRiding(seatEntity);
|
||||
}
|
||||
return ActionResultType.SUCCESS;
|
||||
}
|
||||
|
||||
if (world.isRemote)
|
||||
return ActionResultType.SUCCESS;
|
||||
|
||||
SeatEntity seat = new SeatEntity(world, pos);
|
||||
world.addEntity(seat);
|
||||
player.startRiding(seat);
|
||||
|
||||
sitDown(world, pos, player);
|
||||
return ActionResultType.SUCCESS;
|
||||
}
|
||||
|
||||
public static boolean isSeatOccupied(World world, BlockPos pos) {
|
||||
return !world.getEntitiesWithinAABB(SeatEntity.class, new AxisAlignedBB(pos))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
public static void sitDown(World world, BlockPos pos, Entity entity) {
|
||||
if (world.isRemote)
|
||||
return;
|
||||
SeatEntity seat = new SeatEntity(world, pos);
|
||||
seat.setPos(pos.getX() + .5f, pos.getY(), pos.getZ() + .5f);
|
||||
world.addEntity(seat);
|
||||
entity.startRiding(seat, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MovementBehaviour getMovementBehaviour() {
|
||||
return MOVEMENT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.simibubi.create.content.contraptions.components.actors;
|
||||
|
||||
import com.simibubi.create.AllEntityTypes;
|
||||
|
||||
import net.minecraft.client.renderer.culling.ClippingHelperImpl;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererManager;
|
||||
|
@ -8,18 +9,16 @@ import net.minecraft.entity.Entity;
|
|||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.network.IPacket;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
|
||||
import net.minecraftforge.fml.network.NetworkHooks;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SeatEntity extends Entity {
|
||||
|
||||
public static final Map<BlockPos, SeatEntity> TAKEN = new HashMap<>();
|
||||
public class SeatEntity extends Entity implements IEntityAdditionalSpawnData {
|
||||
|
||||
public SeatEntity(EntityType<?> p_i48580_1_, World p_i48580_2_) {
|
||||
super(p_i48580_1_, p_i48580_2_);
|
||||
|
@ -27,28 +26,39 @@ public class SeatEntity extends Entity {
|
|||
|
||||
public SeatEntity(World world, BlockPos pos) {
|
||||
this(AllEntityTypes.SEAT.get(), world);
|
||||
this.setPos(pos.getX() + 0.5, pos.getY() + 0.30, pos.getZ() + 0.5);
|
||||
noClip = true;
|
||||
TAKEN.put(pos, this);
|
||||
|
||||
}
|
||||
|
||||
public static EntityType.Builder<?> build(EntityType.Builder<?> builder) {
|
||||
@SuppressWarnings("unchecked")
|
||||
EntityType.Builder<SeatEntity> entityBuilder = (EntityType.Builder<SeatEntity>) builder;
|
||||
return entityBuilder.size(0, 0);
|
||||
return entityBuilder.size(0.25f, 0.35f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBB getBoundingBox() {
|
||||
return super.getBoundingBox();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPos(double x, double y, double z) {
|
||||
super.setPos(x, y, z);
|
||||
AxisAlignedBB bb = getBoundingBox();
|
||||
Vec3d diff = new Vec3d(x, y, z).subtract(bb.getCenter());
|
||||
setBoundingBox(bb.offset(diff));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMotion(Vec3d p_213317_1_) {}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (world.isRemote)
|
||||
return;
|
||||
|
||||
BlockPos blockPos = new BlockPos(getX(), getY(), getZ());
|
||||
if (isBeingRidden() && world.getBlockState(blockPos).getBlock() instanceof SeatBlock)
|
||||
boolean blockPresent = world.getBlockState(getPosition())
|
||||
.getBlock() instanceof SeatBlock;
|
||||
if (isBeingRidden() && blockPresent)
|
||||
return;
|
||||
|
||||
TAKEN.remove(blockPos);
|
||||
this.remove();
|
||||
}
|
||||
|
||||
|
@ -61,7 +71,7 @@ public class SeatEntity extends Entity {
|
|||
protected void removePassenger(Entity entity) {
|
||||
super.removePassenger(entity);
|
||||
Vec3d pos = entity.getPositionVec();
|
||||
entity.setPosition(pos.x, pos.y + 0.7, pos.z);
|
||||
entity.setPosition(pos.x, pos.y + 0.85f, pos.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,7 +95,8 @@ public class SeatEntity extends Entity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(SeatEntity p_225626_1_, ClippingHelperImpl p_225626_2_, double p_225626_3_, double p_225626_5_, double p_225626_7_) {
|
||||
public boolean shouldRender(SeatEntity p_225626_1_, ClippingHelperImpl p_225626_2_, double p_225626_3_,
|
||||
double p_225626_5_, double p_225626_7_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -94,4 +105,10 @@ public class SeatEntity extends Entity {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSpawnData(PacketBuffer buffer) {}
|
||||
|
||||
@Override
|
||||
public void readSpawnData(PacketBuffer additionalData) {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package com.simibubi.create.content.contraptions.components.actors;
|
||||
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
|
||||
|
||||
public class SeatMovementBehaviour extends MovementBehaviour {
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,7 +1,29 @@
|
|||
package com.simibubi.create.content.contraptions.components.structureMovement;
|
||||
|
||||
import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isExtensionPole;
|
||||
import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isPistonHead;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.simibubi.create.AllBlocks;
|
||||
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
|
||||
import com.simibubi.create.content.contraptions.components.actors.SeatBlock;
|
||||
import com.simibubi.create.content.contraptions.components.actors.SeatEntity;
|
||||
import com.simibubi.create.content.contraptions.components.saw.SawBlock;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.AbstractChassisBlock;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.ChassisTileEntity;
|
||||
|
@ -22,9 +44,19 @@ import com.simibubi.create.foundation.utility.Iterate;
|
|||
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||
import com.simibubi.create.foundation.utility.VecHelper;
|
||||
import com.simibubi.create.foundation.utility.worldWrappers.WrappedWorld;
|
||||
import net.minecraft.block.*;
|
||||
|
||||
import net.minecraft.block.AbstractButtonBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.ChestBlock;
|
||||
import net.minecraft.block.DoorBlock;
|
||||
import net.minecraft.block.IWaterLoggable;
|
||||
import net.minecraft.block.PressurePlateBlock;
|
||||
import net.minecraft.block.SlimeBlock;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.RenderTypeLookup;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.fluid.IFluidState;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
|
@ -45,19 +77,9 @@ import net.minecraft.world.IWorld;
|
|||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
|
||||
import net.minecraftforge.common.util.Constants.BlockFlags;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
||||
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isExtensionPole;
|
||||
import static com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.isPistonHead;
|
||||
|
||||
public abstract class Contraption {
|
||||
|
||||
|
@ -71,6 +93,10 @@ public abstract class Contraption {
|
|||
public AxisAlignedBB bounds;
|
||||
public boolean stalled;
|
||||
|
||||
protected List<BlockPos> seats;
|
||||
protected Map<UUID, Integer> seatMapping;
|
||||
protected Map<BlockPos, Entity> initialPassengers;
|
||||
|
||||
protected Set<BlockPos> cachedColliders;
|
||||
protected Direction cachedColliderDirection;
|
||||
protected BlockPos anchor;
|
||||
|
@ -81,6 +107,9 @@ public abstract class Contraption {
|
|||
blocks = new HashMap<>();
|
||||
storage = new HashMap<>();
|
||||
actors = new ArrayList<>();
|
||||
seats = new ArrayList<>();
|
||||
seatMapping = new HashMap<>();
|
||||
initialPassengers = new HashMap<>();
|
||||
superglue = new HashSet<>();
|
||||
renderOrder = new ArrayList<>();
|
||||
customRenderTEs = new ArrayList<>();
|
||||
|
@ -144,6 +173,7 @@ public abstract class Contraption {
|
|||
}
|
||||
|
||||
public boolean searchMovedStructure(World world, BlockPos pos, @Nullable Direction forcedDirection) {
|
||||
initialPassengers.clear();
|
||||
List<BlockPos> frontier = new ArrayList<>();
|
||||
Set<BlockPos> visited = new HashSet<>();
|
||||
anchor = pos;
|
||||
|
@ -172,6 +202,20 @@ public abstract class Contraption {
|
|||
inventory = new CombinedInvWrapper(Arrays.copyOf(list.toArray(), list.size(), IItemHandlerModifiable[].class));
|
||||
}
|
||||
|
||||
public void mountPassengers(ContraptionEntity contraptionEntity) {
|
||||
if (contraptionEntity.world.isRemote)
|
||||
return;
|
||||
for (BlockPos seatPos : seats) {
|
||||
Entity passenger = initialPassengers.get(seatPos);
|
||||
if (passenger == null)
|
||||
continue;
|
||||
int seatIndex = seats.indexOf(seatPos);
|
||||
if (seatIndex == -1)
|
||||
continue;
|
||||
contraptionEntity.addSittingPassenger(passenger, seatIndex);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean addToInitialFrontier(World world, BlockPos pos, Direction forcedDirection,
|
||||
List<BlockPos> frontier) {
|
||||
return true;
|
||||
|
@ -205,6 +249,19 @@ public abstract class Contraption {
|
|||
frontier.add(prevPos);
|
||||
}
|
||||
|
||||
// Seats transfer their passenger to the contraption
|
||||
if (state.getBlock() instanceof SeatBlock) {
|
||||
BlockPos local = toLocalPos(pos);
|
||||
seats.add(local);
|
||||
List<SeatEntity> seatsEntities = world.getEntitiesWithinAABB(SeatEntity.class, new AxisAlignedBB(pos));
|
||||
if (!seatsEntities.isEmpty()) {
|
||||
SeatEntity seat = seatsEntities.get(0);
|
||||
List<Entity> passengers = seat.getPassengers();
|
||||
if (!passengers.isEmpty())
|
||||
initialPassengers.put(local, passengers.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
// Pulleys drag their rope and their attached structure
|
||||
if (state.getBlock() instanceof PulleyBlock) {
|
||||
int limit = AllConfigs.SERVER.kinetics.maxRopeLength.get();
|
||||
|
@ -356,11 +413,14 @@ public abstract class Contraption {
|
|||
public void addGlue(SuperGlueEntity entity) {
|
||||
BlockPos pos = entity.getHangingPosition();
|
||||
Direction direction = entity.getFacingDirection();
|
||||
BlockPos localPos = pos.subtract(anchor);
|
||||
this.superglue.add(Pair.of(localPos, direction));
|
||||
this.superglue.add(Pair.of(toLocalPos(pos), direction));
|
||||
glueToRemove.add(entity);
|
||||
}
|
||||
|
||||
public BlockPos toLocalPos(BlockPos globalPos) {
|
||||
return globalPos.subtract(anchor);
|
||||
}
|
||||
|
||||
public void add(BlockPos pos, Pair<BlockInfo, TileEntity> pair) {
|
||||
BlockInfo captured = pair.getKey();
|
||||
BlockPos localPos = pos.subtract(anchor);
|
||||
|
@ -374,7 +434,7 @@ public abstract class Contraption {
|
|||
if (te != null && MountedStorage.canUseAsStorage(te))
|
||||
storage.put(localPos, new MountedStorage(te));
|
||||
if (captured.state.getBlock() instanceof IPortableBlock)
|
||||
getActors().add(MutablePair.of(blockInfo, null));
|
||||
actors.add(MutablePair.of(blockInfo, null));
|
||||
}
|
||||
|
||||
public void readNBT(World world, CompoundNBT nbt) {
|
||||
|
@ -456,6 +516,12 @@ public abstract class Contraption {
|
|||
if (nbt.contains("BoundsFront"))
|
||||
bounds = NBTHelper.readAABB(nbt.getList("BoundsFront", 5));
|
||||
|
||||
seats.clear();
|
||||
NBTHelper.iterateCompoundList(nbt.getList("Seats", NBT.TAG_COMPOUND), c -> seats.add(NBTUtil.readBlockPos(c)));
|
||||
seatMapping.clear();
|
||||
NBTHelper.iterateCompoundList(nbt.getList("Passengers", NBT.TAG_COMPOUND),
|
||||
c -> seatMapping.put(NBTUtil.readUniqueId(c.getCompound("Id")), c.getInt("Seat")));
|
||||
|
||||
stalled = nbt.getBoolean("Stalled");
|
||||
anchor = NBTUtil.readBlockPos(nbt.getCompound("Anchor"));
|
||||
}
|
||||
|
@ -502,6 +568,14 @@ public abstract class Contraption {
|
|||
storageNBT.add(c);
|
||||
}
|
||||
|
||||
nbt.put("Seats", NBTHelper.writeCompoundList(seats, NBTUtil::writeBlockPos));
|
||||
nbt.put("Passengers", NBTHelper.writeCompoundList(seatMapping.entrySet(), e -> {
|
||||
CompoundNBT tag = new CompoundNBT();
|
||||
tag.put("Id", NBTUtil.writeUniqueId(e.getKey()));
|
||||
tag.putInt("Seat", e.getValue());
|
||||
return tag;
|
||||
}));
|
||||
|
||||
nbt.put("Blocks", blocksNBT);
|
||||
nbt.put("Actors", actorsNBT);
|
||||
nbt.put("Superglue", superglueNBT);
|
||||
|
@ -517,11 +591,15 @@ public abstract class Contraption {
|
|||
return nbt;
|
||||
}
|
||||
|
||||
public void removeBlocksFromWorld(IWorld world, BlockPos offset) {
|
||||
removeBlocksFromWorld(world, offset, (pos, state) -> false);
|
||||
protected boolean customBlockPlacement(IWorld world, BlockPos pos, BlockState state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeBlocksFromWorld(IWorld world, BlockPos offset, BiPredicate<BlockPos, BlockState> customRemoval) {
|
||||
protected boolean customBlockRemoval(IWorld world, BlockPos pos, BlockState state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeBlocksFromWorld(IWorld world, BlockPos offset) {
|
||||
storage.values()
|
||||
.forEach(MountedStorage::empty);
|
||||
glueToRemove.forEach(SuperGlueEntity::remove);
|
||||
|
@ -535,7 +613,7 @@ public abstract class Contraption {
|
|||
|
||||
BlockPos add = block.pos.add(anchor)
|
||||
.add(offset);
|
||||
if (customRemoval.test(add, block.state))
|
||||
if (customBlockRemoval(world, add, block.state))
|
||||
continue;
|
||||
BlockState oldState = world.getBlockState(add);
|
||||
Block blockIn = oldState.getBlock();
|
||||
|
@ -546,7 +624,9 @@ public abstract class Contraption {
|
|||
int flags = 67;
|
||||
if (blockIn instanceof DoorBlock)
|
||||
flags = flags | 32 | 16;
|
||||
if (blockIn instanceof IWaterLoggable && oldState.has(BlockStateProperties.WATERLOGGED) && oldState.get(BlockStateProperties.WATERLOGGED).booleanValue()) {
|
||||
if (blockIn instanceof IWaterLoggable && oldState.has(BlockStateProperties.WATERLOGGED)
|
||||
&& oldState.get(BlockStateProperties.WATERLOGGED)
|
||||
.booleanValue()) {
|
||||
world.setBlockState(add, Blocks.WATER.getDefaultState(), flags);
|
||||
continue;
|
||||
}
|
||||
|
@ -555,12 +635,7 @@ public abstract class Contraption {
|
|||
}
|
||||
}
|
||||
|
||||
public void addBlocksToWorld(World world, BlockPos offset, Vec3d rotation) {
|
||||
addBlocksToWorld(world, offset, rotation, (pos, state) -> false);
|
||||
}
|
||||
|
||||
public void addBlocksToWorld(World world, BlockPos offset, Vec3d rotation,
|
||||
BiPredicate<BlockPos, BlockState> customPlacement) {
|
||||
public void addBlocksToWorld(World world, BlockPos offset, Vec3d rotation, List<Entity> seatedEntities) {
|
||||
stop(world);
|
||||
StructureTransform transform = new StructureTransform(offset, rotation);
|
||||
|
||||
|
@ -570,10 +645,9 @@ public abstract class Contraption {
|
|||
continue;
|
||||
|
||||
BlockPos targetPos = transform.apply(block.pos);
|
||||
|
||||
BlockState state = transform.apply(block.state);
|
||||
|
||||
if (customPlacement.test(targetPos, state))
|
||||
if (customBlockPlacement(world, targetPos, state))
|
||||
continue;
|
||||
|
||||
if (nonBrittles)
|
||||
|
@ -597,7 +671,8 @@ public abstract class Contraption {
|
|||
}
|
||||
if (state.getBlock() instanceof IWaterLoggable && state.has(BlockStateProperties.WATERLOGGED)) {
|
||||
IFluidState ifluidstate = world.getFluidState(targetPos);
|
||||
state = state.with(BlockStateProperties.WATERLOGGED, Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER));
|
||||
state = state.with(BlockStateProperties.WATERLOGGED,
|
||||
Boolean.valueOf(ifluidstate.getFluid() == Fluids.WATER));
|
||||
}
|
||||
|
||||
world.destroyBlock(targetPos, true);
|
||||
|
@ -642,7 +717,20 @@ public abstract class Contraption {
|
|||
if (!world.isRemote)
|
||||
world.addEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
for (Entity seatedEntity : seatedEntities) {
|
||||
if (seatMapping.isEmpty())
|
||||
continue;
|
||||
Integer seatIndex = seatMapping.get(seatedEntity.getUniqueID());
|
||||
BlockPos seatPos = seats.get(seatIndex);
|
||||
seatPos = transform.apply(seatPos);
|
||||
if (!(world.getBlockState(seatPos)
|
||||
.getBlock() instanceof SeatBlock))
|
||||
continue;
|
||||
if (SeatBlock.isSeatOccupied(world, seatPos))
|
||||
continue;
|
||||
SeatBlock.sitDown(world, seatPos, seatedEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -707,6 +795,15 @@ public abstract class Contraption {
|
|||
bounds = new AxisAlignedBB(min, max);
|
||||
}
|
||||
|
||||
public BlockPos getSeat(UUID entityId) {
|
||||
if (!seatMapping.containsKey(entityId))
|
||||
return null;
|
||||
int seatIndex = seatMapping.get(entityId);
|
||||
if (seatIndex >= seats.size())
|
||||
return null;
|
||||
return seats.get(seatIndex);
|
||||
}
|
||||
|
||||
protected abstract AllContraptionTypes getType();
|
||||
|
||||
}
|
|
@ -24,6 +24,7 @@ import com.simibubi.create.foundation.collision.ContinuousOBBCollider.Continuous
|
|||
import com.simibubi.create.foundation.collision.Matrix3d;
|
||||
import com.simibubi.create.foundation.collision.OrientedBB;
|
||||
import com.simibubi.create.foundation.utility.AngleHelper;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
import com.simibubi.create.foundation.utility.VecHelper;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
|
@ -32,9 +33,11 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Direction.Axis;
|
||||
|
@ -51,10 +54,12 @@ import net.minecraft.world.World;
|
|||
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.event.TickEvent.ClientTickEvent;
|
||||
import net.minecraftforge.event.TickEvent.Phase;
|
||||
import net.minecraftforge.event.TickEvent.WorldTickEvent;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||
|
@ -66,7 +71,7 @@ public class ContraptionCollider {
|
|||
new DamageSource("create.contraption_suffocate").setDamageBypassesArmor();
|
||||
public static boolean wasClientPlayerGrounded;
|
||||
public static Cache<World, List<WeakReference<ContraptionEntity>>> activeContraptions = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(40, SECONDS)
|
||||
.expireAfterAccess(400, SECONDS)
|
||||
.build();
|
||||
|
||||
@SubscribeEvent
|
||||
|
@ -102,6 +107,22 @@ public class ContraptionCollider {
|
|||
runCollisions(world);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void entitiesWhoJustDismountedGetSentToTheRightLocation(LivingUpdateEvent event) {
|
||||
LivingEntity entityLiving = event.getEntityLiving();
|
||||
if (entityLiving == null)
|
||||
return;
|
||||
if (entityLiving.world.isRemote)
|
||||
return;
|
||||
CompoundNBT data = entityLiving.getPersistentData();
|
||||
if (!data.contains("ContraptionDismountLocation"))
|
||||
return;
|
||||
Vec3d position = VecHelper.readNBT(data.getList("ContraptionDismountLocation", NBT.TAG_DOUBLE));
|
||||
if (entityLiving.getRidingEntity() == null)
|
||||
entityLiving.setPositionAndUpdate(position.x, position.y, position.z);
|
||||
data.remove("ContraptionDismountLocation");
|
||||
}
|
||||
|
||||
private static void runCollisions(World world) {
|
||||
List<WeakReference<ContraptionEntity>> list = activeContraptions.getIfPresent(world);
|
||||
if (list == null)
|
||||
|
@ -131,9 +152,9 @@ public class ContraptionCollider {
|
|||
return;
|
||||
|
||||
Vec3d centerOfBlock = VecHelper.getCenterOf(BlockPos.ZERO);
|
||||
double conRotX = contraptionRotation.z;
|
||||
double conRotX = contraptionRotation.x;
|
||||
double conRotY = contraptionRotation.y;
|
||||
double conRotZ = contraptionRotation.x;
|
||||
double conRotZ = contraptionRotation.z;
|
||||
Vec3d conMotion = contraptionPosition.subtract(contraptionEntity.getPrevPositionVec());
|
||||
Vec3d conAngularMotion = contraptionRotation.subtract(contraptionEntity.getPrevRotationVec());
|
||||
Vec3d contraptionCentreOffset = contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0);
|
||||
|
@ -180,6 +201,7 @@ public class ContraptionCollider {
|
|||
// Prepare entity bounds
|
||||
OrientedBB obb = new OrientedBB(localBB);
|
||||
obb.setRotation(rotation);
|
||||
motion = motion.subtract(conMotion);
|
||||
motion = rotation.transform(motion);
|
||||
|
||||
// Vec3d visualizerOrigin = new Vec3d(10, 64, 0);
|
||||
|
@ -198,40 +220,47 @@ public class ContraptionCollider {
|
|||
.forEach(shape -> shape.toBoundingBoxList()
|
||||
.forEach(bbs::add));
|
||||
|
||||
boolean doHorizontalPass = conRotX == 0 && conRotZ == 0;
|
||||
for (boolean horizontalPass : Iterate.trueAndFalse) {
|
||||
|
||||
for (AxisAlignedBB bb : bbs) {
|
||||
Vec3d currentResponse = collisionResponse.getValue();
|
||||
obb.setCenter(obbCenter.add(currentResponse));
|
||||
ContinuousSeparationManifold intersect = obb.intersect(bb, allowedMotion.getValue());
|
||||
// OutlineParams params = CreateClient.outliner.showAABB(bb, bb.offset(visualizerOrigin))
|
||||
// .withFaceTexture(AllSpecialTextures.HIGHLIGHT_CHECKERED);
|
||||
// params.colored(0xffffff);
|
||||
|
||||
if (intersect == null)
|
||||
continue;
|
||||
if (surfaceCollision.isFalse())
|
||||
if ((!horizontalPass || !doHorizontalPass) && surfaceCollision.isFalse())
|
||||
surfaceCollision.setValue(intersect.isSurfaceCollision());
|
||||
|
||||
double timeOfImpact = intersect.getTimeOfImpact();
|
||||
if (timeOfImpact > 0 && timeOfImpact < 1) {
|
||||
futureCollision.setTrue();
|
||||
// Vec3d prev = allowedMotion.getValue();
|
||||
allowedMotion.setValue(intersect.getAllowedMotion(allowedMotion.getValue()));
|
||||
// Debug.debugChat("Allowed Motion FROM " + prev.toString());
|
||||
// Debug.debugChat("Allowed Motion TO " + allowedMotion.getValue()
|
||||
// .toString());
|
||||
// params.colored(0x4499ff);
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec3d separation = intersect.asSeparationVec(entity.stepHeight);
|
||||
if (separation != null && !separation.equals(Vec3d.ZERO)) {
|
||||
if (separation != null && !separation.equals(Vec3d.ZERO))
|
||||
collisionResponse.setValue(currentResponse.add(separation));
|
||||
// Debug.debugChat("Collision " + currentResponse.add(separation)
|
||||
// .toString());
|
||||
// params.colored(0xff9944);
|
||||
}
|
||||
}
|
||||
|
||||
// Debug.debugChat("----");
|
||||
if (!horizontalPass || !doHorizontalPass)
|
||||
break;
|
||||
|
||||
boolean noVerticalMotionResponse = allowedMotion.getValue().y == motion.y;
|
||||
boolean noVerticalCollision = collisionResponse.getValue().y == 0;
|
||||
if (noVerticalCollision && noVerticalMotionResponse)
|
||||
break;
|
||||
|
||||
// Re-run collisions with horizontal offset
|
||||
collisionResponse.setValue(collisionResponse.getValue()
|
||||
.mul(1, 0, 1));
|
||||
allowedMotion.setValue(allowedMotion.getValue()
|
||||
.mul(1, 0, 1)
|
||||
.add(0, motion.y, 0));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Resolve collision
|
||||
Vec3d entityMotion = entity.getMotion();
|
||||
|
@ -240,7 +269,8 @@ public class ContraptionCollider {
|
|||
boolean hardCollision = !totalResponse.equals(Vec3d.ZERO);
|
||||
|
||||
rotation.transpose();
|
||||
motionResponse = rotation.transform(motionResponse);
|
||||
motionResponse = rotation.transform(motionResponse)
|
||||
.add(conMotion);
|
||||
totalResponse = rotation.transform(totalResponse);
|
||||
rotation.transpose();
|
||||
|
||||
|
@ -262,7 +292,7 @@ public class ContraptionCollider {
|
|||
Vec3d contactPoint = entityPosition.subtract(contraptionCentreOffset)
|
||||
.subtract(contraptionPosition);
|
||||
contactPoint =
|
||||
VecHelper.rotate(contactPoint, conAngularMotion.z, conAngularMotion.y, conAngularMotion.x);
|
||||
VecHelper.rotate(contactPoint, conAngularMotion.x, conAngularMotion.y, conAngularMotion.z);
|
||||
contactPoint = contactPoint.add(contraptionPosition)
|
||||
.add(contraptionCentreOffset)
|
||||
.add(conMotion);
|
||||
|
|
|
@ -5,12 +5,15 @@ import static com.simibubi.create.foundation.utility.AngleHelper.getShortestAngl
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.simibubi.create.AllEntityTypes;
|
||||
import com.simibubi.create.content.contraptions.components.actors.SeatEntity;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.BearingContraption;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode;
|
||||
|
@ -45,6 +48,7 @@ import net.minecraft.tags.BlockTags;
|
|||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.ReuseableStream;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
@ -70,7 +74,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
protected BlockPos controllerPos;
|
||||
protected Vec3d motionBeforeStall;
|
||||
protected boolean stationary;
|
||||
|
||||
protected boolean initialized;
|
||||
final List<Entity> collidingEntities = new ArrayList<>();
|
||||
|
||||
private static final Ingredient FUEL_ITEMS = Ingredient.fromItems(Items.COAL, Items.CHARCOAL);
|
||||
|
@ -98,11 +102,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
|
||||
public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle) {
|
||||
ContraptionEntity entity = new ContraptionEntity(AllEntityTypes.CONTRAPTION.get(), world);
|
||||
entity.contraption = contraption;
|
||||
entity.contraptionCreated(contraption);
|
||||
entity.initialAngle = initialAngle;
|
||||
entity.forceYaw(initialAngle);
|
||||
if (contraption != null)
|
||||
contraption.gatherStoredItems();
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
@ -116,12 +118,25 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
|
||||
public static ContraptionEntity createStationary(World world, Contraption contraption) {
|
||||
ContraptionEntity entity = new ContraptionEntity(AllEntityTypes.STATIONARY_CONTRAPTION.get(), world);
|
||||
entity.contraption = contraption;
|
||||
if (contraption != null)
|
||||
contraption.gatherStoredItems();
|
||||
entity.contraptionCreated(contraption);
|
||||
return entity;
|
||||
}
|
||||
|
||||
protected void contraptionCreated(Contraption contraption) {
|
||||
this.contraption = contraption;
|
||||
if (contraption == null)
|
||||
return;
|
||||
if (world.isRemote)
|
||||
return;
|
||||
contraption.gatherStoredItems();
|
||||
}
|
||||
|
||||
protected void contraptionInitialize() {
|
||||
if (!world.isRemote)
|
||||
contraption.mountPassengers(this);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public <T extends TileEntity & IControlContraption> ContraptionEntity controlledBy(T controller) {
|
||||
this.controllerPos = controller.getPos();
|
||||
return this;
|
||||
|
@ -142,6 +157,110 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addPassenger(Entity passenger) {
|
||||
super.addPassenger(passenger);
|
||||
}
|
||||
|
||||
public void addSittingPassenger(Entity passenger, int seatIndex) {
|
||||
passenger.startRiding(this, true);
|
||||
if (world.isRemote)
|
||||
return;
|
||||
contraption.seatMapping.put(passenger.getUniqueID(), seatIndex);
|
||||
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> this),
|
||||
new ContraptionSeatMappingPacket(getEntityId(), contraption.seatMapping));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removePassenger(Entity passenger) {
|
||||
Vec3d transformedVector = getPassengerPosition(passenger);
|
||||
super.removePassenger(passenger);
|
||||
if (world.isRemote)
|
||||
return;
|
||||
if (transformedVector != null)
|
||||
passenger.getPersistentData()
|
||||
.put("ContraptionDismountLocation", VecHelper.writeNBT(transformedVector));
|
||||
contraption.seatMapping.remove(passenger.getUniqueID());
|
||||
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> this),
|
||||
new ContraptionSeatMappingPacket(getEntityId(), contraption.seatMapping));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePassengerPosition(Entity passenger, IMoveCallback callback) {
|
||||
if (!isPassenger(passenger))
|
||||
return;
|
||||
Vec3d transformedVector = getPassengerPosition(passenger);
|
||||
if (transformedVector == null)
|
||||
return;
|
||||
callback.accept(passenger, transformedVector.x, transformedVector.y, transformedVector.z);
|
||||
}
|
||||
|
||||
protected Vec3d getPassengerPosition(Entity passenger) {
|
||||
AxisAlignedBB bb = passenger.getBoundingBox();
|
||||
double ySize = bb.getYSize();
|
||||
BlockPos seat = contraption.getSeat(passenger.getUniqueID());
|
||||
if (seat == null)
|
||||
return null;
|
||||
Vec3d transformedVector = toGlobalVector(new Vec3d(seat).add(.5, passenger.getYOffset() + ySize - .15f, .5))
|
||||
.add(VecHelper.getCenterOf(BlockPos.ZERO))
|
||||
.subtract(0.5, ySize, 0.5);
|
||||
return transformedVector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canFitPassenger(Entity p_184219_1_) {
|
||||
return getPassengers().size() < contraption.seats.size();
|
||||
}
|
||||
|
||||
public boolean handlePlayerInteraction(PlayerEntity player, BlockPos localPos, Direction side,
|
||||
Hand interactionHand) {
|
||||
int indexOfSeat = contraption.seats.indexOf(localPos);
|
||||
if (indexOfSeat == -1)
|
||||
return false;
|
||||
|
||||
// Eject potential existing passenger
|
||||
for (Entry<UUID, Integer> entry : contraption.seatMapping.entrySet()) {
|
||||
if (entry.getValue() != indexOfSeat)
|
||||
continue;
|
||||
for (Entity entity : getPassengers()) {
|
||||
if (!entry.getKey().equals(entity.getUniqueID()))
|
||||
continue;
|
||||
if (entity instanceof PlayerEntity)
|
||||
return false;
|
||||
if (!world.isRemote) {
|
||||
Vec3d transformedVector = getPassengerPosition(entity);
|
||||
entity.stopRiding();
|
||||
if (transformedVector != null)
|
||||
entity.setPositionAndUpdate(transformedVector.x, transformedVector.y, transformedVector.z);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (world.isRemote)
|
||||
return true;
|
||||
addSittingPassenger(player, indexOfSeat);
|
||||
return true;
|
||||
}
|
||||
|
||||
public Vec3d toGlobalVector(Vec3d localVec) {
|
||||
Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
|
||||
localVec = localVec.subtract(rotationOffset);
|
||||
localVec = VecHelper.rotate(localVec, getRotationVec());
|
||||
localVec = localVec.add(rotationOffset)
|
||||
.add(getAnchorVec());
|
||||
return localVec;
|
||||
}
|
||||
|
||||
public Vec3d toLocalVector(Vec3d globalVec) {
|
||||
Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
|
||||
globalVec = globalVec.subtract(getAnchorVec())
|
||||
.subtract(rotationOffset);
|
||||
globalVec = VecHelper.rotate(globalVec, getRotationVec().scale(-1));
|
||||
globalVec = globalVec.add(rotationOffset);
|
||||
return globalVec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (contraption == null) {
|
||||
|
@ -149,6 +268,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
return;
|
||||
}
|
||||
|
||||
if (!initialized)
|
||||
contraptionInitialize();
|
||||
|
||||
checkController();
|
||||
|
||||
Entity mountedEntity = getRidingEntity();
|
||||
|
@ -173,10 +295,6 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
super.tick();
|
||||
}
|
||||
|
||||
public void collisionTick() {
|
||||
// ContraptionCollider.collideEntities(this);
|
||||
}
|
||||
|
||||
public void tickAsPassenger(Entity e) {
|
||||
boolean rotationLock = false;
|
||||
boolean pauseWhileRotating = false;
|
||||
|
@ -266,10 +384,8 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
}
|
||||
|
||||
public void tickActors(Vec3d movementVector) {
|
||||
float anglePitch = getPitch(1);
|
||||
float angleYaw = getYaw(1);
|
||||
float angleRoll = getRoll(1);
|
||||
Vec3d rotationVec = new Vec3d(angleRoll, angleYaw, anglePitch);
|
||||
Vec3d rotationVec = getRotationVec();
|
||||
Vec3d reversedRotationVec = rotationVec.scale(-1);
|
||||
Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
|
||||
boolean stalledPreviously = contraption.stalled;
|
||||
|
||||
|
@ -283,7 +399,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
|
||||
Vec3d actorPosition = new Vec3d(blockInfo.pos);
|
||||
actorPosition = actorPosition.add(actor.getActiveAreaOffset(context));
|
||||
actorPosition = VecHelper.rotate(actorPosition, angleRoll, angleYaw, anglePitch);
|
||||
actorPosition = VecHelper.rotate(actorPosition, rotationVec);
|
||||
actorPosition = actorPosition.add(rotationOffset)
|
||||
.add(getAnchorVec());
|
||||
|
||||
|
@ -295,7 +411,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
if (previousPosition != null) {
|
||||
context.motion = actorPosition.subtract(previousPosition);
|
||||
Vec3d relativeMotion = context.motion;
|
||||
relativeMotion = VecHelper.rotate(relativeMotion, -angleRoll, -angleYaw, -anglePitch);
|
||||
relativeMotion = VecHelper.rotate(relativeMotion, reversedRotationVec);
|
||||
context.relativeMotion = relativeMotion;
|
||||
newPosVisited = !new BlockPos(previousPosition).equals(gridPosition)
|
||||
|| context.relativeMotion.length() > 0 && context.firstMovement;
|
||||
|
@ -429,6 +545,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
|
||||
@Override
|
||||
protected void readAdditional(CompoundNBT compound) {
|
||||
initialized = compound.getBoolean("Initialized");
|
||||
contraption = Contraption.fromNBT(world, compound.getCompound("Contraption"));
|
||||
initialAngle = compound.getFloat("InitialAngle");
|
||||
forceYaw(compound.contains("ForcedYaw") ? compound.getFloat("ForcedYaw") : initialAngle);
|
||||
|
@ -479,6 +596,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
|
||||
compound.putFloat("InitialAngle", initialAngle);
|
||||
compound.putBoolean("Stalled", isStalled());
|
||||
compound.putBoolean("Initialized", initialized);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -499,15 +617,15 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
}
|
||||
|
||||
public void disassemble() {
|
||||
if (!isAlive()) {
|
||||
if (!isAlive())
|
||||
return;
|
||||
}
|
||||
if (getContraption() != null) {
|
||||
remove();
|
||||
BlockPos offset = new BlockPos(getAnchorVec().add(.5, .5, .5));
|
||||
Vec3d rotation = new Vec3d(getRoll(1), getYaw(1), getPitch(1));
|
||||
getContraption().addBlocksToWorld(world, offset, rotation);
|
||||
preventMovedEntitiesFromGettingStuck();
|
||||
Vec3d rotation = getRotationVec();
|
||||
setBoundingBox(new AxisAlignedBB(0, 300, 0, 0, 300, 0));
|
||||
contraption.addBlocksToWorld(world, offset, rotation, getPassengers());
|
||||
// preventMovedEntitiesFromGettingStuck();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -633,11 +751,11 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
}
|
||||
|
||||
public Vec3d getRotationVec() {
|
||||
return new Vec3d(getPitch(1), getYaw(1), getRoll(1));
|
||||
return new Vec3d(getRoll(1), getYaw(1), getPitch(1));
|
||||
}
|
||||
|
||||
public Vec3d getPrevRotationVec() {
|
||||
return new Vec3d(getPitch(0), getYaw(0), getRoll(0));
|
||||
return new Vec3d(getRoll(0), getYaw(0), getPitch(0));
|
||||
}
|
||||
|
||||
public Vec3d getPrevPositionVec() {
|
||||
|
@ -653,8 +771,12 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
|
|||
return false;
|
||||
if (e instanceof SuperGlueEntity)
|
||||
return false;
|
||||
if (e instanceof SeatEntity)
|
||||
return false;
|
||||
if (e instanceof IProjectile)
|
||||
return false;
|
||||
if (e.getRidingEntity() != null)
|
||||
return false;
|
||||
|
||||
Entity riding = this.getRidingEntity();
|
||||
while (riding != null) {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package com.simibubi.create.content.contraptions.components.structureMovement;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.fml.network.NetworkEvent.Context;
|
||||
|
||||
public class ContraptionInteractionPacket extends SimplePacketBase {
|
||||
|
||||
private Hand interactionHand;
|
||||
private int target;
|
||||
private BlockPos localPos;
|
||||
private Direction face;
|
||||
|
||||
public ContraptionInteractionPacket(ContraptionEntity target, Hand hand, BlockPos localPos, Direction side) {
|
||||
this.interactionHand = hand;
|
||||
this.localPos = localPos;
|
||||
this.target = target.getEntityId();
|
||||
this.face = side;
|
||||
}
|
||||
|
||||
public ContraptionInteractionPacket(PacketBuffer buffer) {
|
||||
target = buffer.readInt();
|
||||
int handId = buffer.readInt();
|
||||
interactionHand = handId == -1 ? null : Hand.values()[handId];
|
||||
localPos = buffer.readBlockPos();
|
||||
face = Direction.byIndex(buffer.readShort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketBuffer buffer) {
|
||||
buffer.writeInt(target);
|
||||
buffer.writeInt(interactionHand == null ? -1 : interactionHand.ordinal());
|
||||
buffer.writeBlockPos(localPos);
|
||||
buffer.writeShort(face.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Supplier<Context> context) {
|
||||
context.get()
|
||||
.enqueueWork(() -> {
|
||||
ServerPlayerEntity sender = context.get()
|
||||
.getSender();
|
||||
if (sender == null)
|
||||
return;
|
||||
Entity entityByID = sender.getServerWorld()
|
||||
.getEntityByID(target);
|
||||
if (!(entityByID instanceof ContraptionEntity))
|
||||
return;
|
||||
ContraptionEntity contraptionEntity = (ContraptionEntity) entityByID;
|
||||
if (contraptionEntity.handlePlayerInteraction(sender, localPos, face, interactionHand))
|
||||
sender.swingHand(interactionHand, true);
|
||||
});
|
||||
context.get()
|
||||
.setPacketHandled(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.simibubi.create.content.contraptions.components.structureMovement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.fml.network.NetworkEvent.Context;
|
||||
|
||||
public class ContraptionSeatMappingPacket extends SimplePacketBase {
|
||||
|
||||
private Map<UUID, Integer> mapping;
|
||||
private int entityID;
|
||||
|
||||
public ContraptionSeatMappingPacket(int entityID, Map<UUID, Integer> mapping) {
|
||||
this.entityID = entityID;
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
public ContraptionSeatMappingPacket(PacketBuffer buffer) {
|
||||
entityID = buffer.readInt();
|
||||
mapping = new HashMap<>();
|
||||
short size = buffer.readShort();
|
||||
for (int i = 0; i < size; i++)
|
||||
mapping.put(buffer.readUniqueId(), (int) buffer.readShort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketBuffer buffer) {
|
||||
buffer.writeInt(entityID);
|
||||
buffer.writeShort(mapping.size());
|
||||
mapping.forEach((k, v) -> {
|
||||
buffer.writeUniqueId(k);
|
||||
buffer.writeShort(v);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Supplier<Context> context) {
|
||||
context.get()
|
||||
.enqueueWork(() -> {
|
||||
Entity entityByID = Minecraft.getInstance().world
|
||||
.getEntityByID(entityID);
|
||||
if (!(entityByID instanceof ContraptionEntity))
|
||||
return;
|
||||
ContraptionEntity contraptionEntity = (ContraptionEntity) entityByID;
|
||||
contraptionEntity.contraption.seatMapping = mapping;
|
||||
});
|
||||
context.get()
|
||||
.setPacketHandled(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -47,10 +47,6 @@ public class ClockworkBearingTileEntity extends KineticTileEntity implements IBe
|
|||
|
||||
if (running && Contraption.isFrozen())
|
||||
disassemble();
|
||||
if (hourHand != null)
|
||||
hourHand.collisionTick();
|
||||
if (minuteHand != null)
|
||||
minuteHand.collisionTick();
|
||||
|
||||
if (!world.isRemote && assembleNextTick) {
|
||||
assembleNextTick = false;
|
||||
|
|
|
@ -208,8 +208,6 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp
|
|||
|
||||
if (world.isRemote)
|
||||
clientAngleDiff /= 2;
|
||||
if (movedContraption != null)
|
||||
movedContraption.collisionTick();
|
||||
if (running && Contraption.isFrozen())
|
||||
disassemble();
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import net.minecraft.tileentity.TileEntity;
|
|||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Direction.Axis;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
|
||||
|
@ -91,13 +90,13 @@ public class MountedContraption extends Contraption {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void removeBlocksFromWorld(IWorld world, BlockPos offset) {
|
||||
super.removeBlocksFromWorld(world, offset, (pos, state) -> pos.equals(anchor));
|
||||
protected boolean customBlockPlacement(IWorld world, BlockPos pos, BlockState state) {
|
||||
return AllBlocks.MINECART_ANCHOR.has(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBlocksToWorld(World world, BlockPos offset, Vec3d rotation) {
|
||||
super.addBlocksToWorld(world, offset, rotation, (pos, state) -> AllBlocks.MINECART_ANCHOR.has(state));
|
||||
protected boolean customBlockRemoval(IWorld world, BlockPos pos, BlockState state) {
|
||||
return pos.equals(anchor);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@ public abstract class LinearActuatorTileEntity extends KineticTileEntity impleme
|
|||
super.tick();
|
||||
|
||||
if (movedContraption != null) {
|
||||
movedContraption.collisionTick();
|
||||
if (!movedContraption.isAlive())
|
||||
movedContraption = null;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||
import com.simibubi.create.content.contraptions.components.structureMovement.AllContraptionTypes;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementTraits;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.PistonState;
|
||||
import com.simibubi.create.foundation.config.AllConfigs;
|
||||
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||
|
@ -32,7 +31,6 @@ import net.minecraft.tileentity.TileEntity;
|
|||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
|
||||
|
@ -174,18 +172,13 @@ public class PistonContraption extends Contraption {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addGlue(SuperGlueEntity entity) {
|
||||
BlockPos pos = entity.getHangingPosition();
|
||||
Direction direction = entity.getFacingDirection();
|
||||
BlockPos localPos = pos.subtract(anchor)
|
||||
public BlockPos toLocalPos(BlockPos globalPos) {
|
||||
return globalPos.subtract(anchor)
|
||||
.offset(orientation, -initialExtensionProgress);
|
||||
this.superglue.add(Pair.of(localPos, direction));
|
||||
glueToRemove.add(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBlocksToWorld(World world, BlockPos offset, Vec3d rotation) {
|
||||
super.addBlocksToWorld(world, offset, rotation, (pos, state) -> {
|
||||
protected boolean customBlockPlacement(IWorld world, BlockPos pos, BlockState state) {
|
||||
BlockPos pistonPos = anchor.offset(orientation, -1);
|
||||
BlockState pistonState = world.getBlockState(pistonPos);
|
||||
TileEntity te = world.getTileEntity(pistonPos);
|
||||
|
@ -198,12 +191,10 @@ public class PistonContraption extends Contraption {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBlocksFromWorld(IWorld world, BlockPos offset) {
|
||||
super.removeBlocksFromWorld(world, offset, (pos, state) -> {
|
||||
protected boolean customBlockRemoval(IWorld world, BlockPos pos, BlockState state) {
|
||||
BlockPos pistonPos = anchor.offset(orientation, -1);
|
||||
BlockState blockState = world.getBlockState(pos);
|
||||
if (pos.equals(pistonPos) && isPiston(blockState)) {
|
||||
|
@ -211,7 +202,6 @@ public class PistonContraption extends Contraption {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,7 +6,6 @@ import com.simibubi.create.CreateClient;
|
|||
import com.simibubi.create.foundation.collision.ContinuousOBBCollider.ContinuousSeparationManifold;
|
||||
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
|
||||
import com.simibubi.create.foundation.utility.AngleHelper;
|
||||
import com.simibubi.create.foundation.utility.Debug;
|
||||
import com.simibubi.create.foundation.utility.MatrixStacker;
|
||||
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
|
||||
|
||||
|
@ -31,7 +30,6 @@ public class CollisionDebugger {
|
|||
angle += delta;
|
||||
angle = (int) angle;
|
||||
OBB.setRotation(new Matrix3d().asZRotation(AngleHelper.rad(angle)));
|
||||
Debug.debugMessage("Angle: " + angle);
|
||||
}
|
||||
|
||||
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
|
||||
|
|
|
@ -6,6 +6,8 @@ import java.util.function.Supplier;
|
|||
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.CancelPlayerFallPacket;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionInteractionPacket;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionSeatMappingPacket;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionStallPacket;
|
||||
import com.simibubi.create.content.contraptions.components.structureMovement.glue.GlueEffectPacket;
|
||||
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.ConfigureSequencedGearshiftPacket;
|
||||
|
@ -45,6 +47,7 @@ public enum AllPackets {
|
|||
CONFIGURE_SCROLLABLE(ScrollValueUpdatePacket.class, ScrollValueUpdatePacket::new),
|
||||
CANCEL_FALL(CancelPlayerFallPacket.class, CancelPlayerFallPacket::new),
|
||||
EXTENDO_INTERACT(ExtendoGripInteractionPacket.class, ExtendoGripInteractionPacket::new),
|
||||
CONTRAPTION_INTERACT(ContraptionInteractionPacket.class, ContraptionInteractionPacket::new),
|
||||
PLACE_ARM(ArmPlacementPacket.class, ArmPlacementPacket::new),
|
||||
|
||||
// Server to Client
|
||||
|
@ -54,6 +57,7 @@ public enum AllPackets {
|
|||
CONFIGURE_CONFIG(ConfigureConfigPacket.class, ConfigureConfigPacket::new),
|
||||
CONTRAPTION_STALL(ContraptionStallPacket.class, ContraptionStallPacket::new),
|
||||
GLUE_EFFECT(GlueEffectPacket.class, GlueEffectPacket::new),
|
||||
CONTRAPTION_SEAT_MAPPING(ContraptionSeatMappingPacket.class, ContraptionSeatMappingPacket::new),
|
||||
|
||||
;
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ import net.minecraft.util.math.Vec3i;
|
|||
|
||||
public class VecHelper {
|
||||
|
||||
public static Vec3d rotate(Vec3d vec, Vec3d rotationVec) {
|
||||
return rotate(vec, rotationVec.x, rotationVec.y, rotationVec.z);
|
||||
}
|
||||
|
||||
public static Vec3d rotate(Vec3d vec, double xRot, double yRot, double zRot) {
|
||||
return rotate(rotate(rotate(vec, xRot, Axis.X), yRot, Axis.Y), zRot, Axis.Z);
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 389 B After Width: | Height: | Size: 392 B |
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 389 B |
Before Width: | Height: | Size: 389 B After Width: | Height: | Size: 401 B |
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 390 B |
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 398 B |
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 412 B |
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 398 B |
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 408 B |
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 413 B |
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 399 B |
Before Width: | Height: | Size: 400 B After Width: | Height: | Size: 416 B |
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 404 B |