From 80616c2f7789a5779ed59bb15e80ae9687abf9da Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 5 Mar 2018 03:26:23 +0100 Subject: [PATCH] Added transporter FX in source & destination areas (wip) --- .../block/movement/TileEntityTransporter.java | 10 + .../network/MessageTransporterEffect.java | 234 ++++++++++++++++++ .../cr0s/warpdrive/network/PacketHandler.java | 28 +++ .../cr0s/warpdrive/render/EntityFXDot.java | 113 +++++++++ 4 files changed, 385 insertions(+) create mode 100644 src/main/java/cr0s/warpdrive/network/MessageTransporterEffect.java create mode 100644 src/main/java/cr0s/warpdrive/render/EntityFXDot.java diff --git a/src/main/java/cr0s/warpdrive/block/movement/TileEntityTransporter.java b/src/main/java/cr0s/warpdrive/block/movement/TileEntityTransporter.java index 4e129918..760c21b3 100644 --- a/src/main/java/cr0s/warpdrive/block/movement/TileEntityTransporter.java +++ b/src/main/java/cr0s/warpdrive/block/movement/TileEntityTransporter.java @@ -263,6 +263,16 @@ public class TileEntityTransporter extends TileEntityAbstractEnergy implements I transporterState = EnumTransporterState.DISABLED; break; } + + // client effects + if ( lockStrengthActual > 0.0F + || tickEnergizing > 0 + || tickCooldown > 0 ) { + final Entity entity = weakEntity == null ? null : weakEntity.get(); + PacketHandler.sendTransporterEffectPacket(worldObj, vSource_absolute, vDestination_absolute, lockStrengthActual, + entity, v3EntityPosition, + tickEnergizing, tickCooldown, 64); + } } @Override diff --git a/src/main/java/cr0s/warpdrive/network/MessageTransporterEffect.java b/src/main/java/cr0s/warpdrive/network/MessageTransporterEffect.java new file mode 100644 index 00000000..2e940865 --- /dev/null +++ b/src/main/java/cr0s/warpdrive/network/MessageTransporterEffect.java @@ -0,0 +1,234 @@ +package cr0s.warpdrive.network; + +import cr0s.warpdrive.Commons; +import cr0s.warpdrive.WarpDrive; +import cr0s.warpdrive.config.WarpDriveConfig; +import cr0s.warpdrive.data.Vector3; +import cr0s.warpdrive.data.VectorI; +import cr0s.warpdrive.render.EntityFXBeam; +import cr0s.warpdrive.render.EntityFXDot; +import io.netty.buffer.ByteBuf; + +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.MathHelper; +import net.minecraft.world.World; + +import cpw.mods.fml.client.FMLClientHandler; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraftforge.common.util.ForgeDirection; + +public class MessageTransporterEffect implements IMessage, IMessageHandler { + + private final int ENTITY_ID_NONE = 0; + + private VectorI vSource; + private VectorI vDestination; + private double lockStrength; + private int idEntity; + private Vector3 v3EntityPosition; + private int tickEnergizing; + private int tickCooldown; + + public MessageTransporterEffect() { + // required on receiving side + } + + public MessageTransporterEffect(final VectorI vSource, final VectorI vDestination, final double lockStrength, + final Entity entity, final Vector3 v3EntityPosition, + final int tickEnergizing, final int tickCooldown) { + this.vSource = vSource; + this.vDestination = vDestination; + this.lockStrength = lockStrength; + this.idEntity = entity == null ? ENTITY_ID_NONE : entity.getEntityId(); + this.v3EntityPosition = v3EntityPosition; + this.tickEnergizing = tickEnergizing; + this.tickCooldown = tickCooldown; + } + + @Override + public void fromBytes(final ByteBuf buffer) { + int x = buffer.readInt(); + int y = buffer.readInt(); + int z = buffer.readInt(); + vSource = new VectorI(x, y, z); + + x = buffer.readInt(); + y = buffer.readInt(); + z = buffer.readInt(); + vDestination = new VectorI(x, y, z); + + lockStrength = buffer.readFloat(); + + idEntity = buffer.readInt(); + + final double xEntity = buffer.readDouble(); + final double yEntity = buffer.readDouble(); + final double zEntity = buffer.readDouble(); + v3EntityPosition = new Vector3(xEntity, yEntity, zEntity); + + tickEnergizing = buffer.readShort(); + tickCooldown = buffer.readShort(); + } + + @Override + public void toBytes(ByteBuf buffer) { + buffer.writeInt(vSource.x); + buffer.writeInt(vSource.y); + buffer.writeInt(vSource.z); + buffer.writeInt(vDestination.x); + buffer.writeInt(vDestination.y); + buffer.writeInt(vDestination.z); + buffer.writeFloat((float) lockStrength); + buffer.writeInt(idEntity); + buffer.writeDouble(v3EntityPosition == null ? 0.0D : v3EntityPosition.x); + buffer.writeDouble(v3EntityPosition == null ? -1000.0D : v3EntityPosition.y); + buffer.writeDouble(v3EntityPosition == null ? 0.0D : v3EntityPosition.z); + buffer.writeShort(tickEnergizing); + buffer.writeShort(tickCooldown); + } + + @SideOnly(Side.CLIENT) + private void handle(final World world) { + // adjust render distance + final int maxRenderDistance = Minecraft.getMinecraft().gameSettings.renderDistanceChunks * 16; + final int maxRenderDistance_squared = maxRenderDistance * maxRenderDistance; + + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + + // handle source + if (vSource.distance2To(player) <= maxRenderDistance_squared) { + handleAtSource(world); + } + + // handle target + if (vDestination.distance2To(player) <= maxRenderDistance_squared) { + handleAtDestination(world); + } + } + + private void handleAtSource(final World world) { + // add flying particles in area of effect + spawnParticlesInArea(world, vSource, false, + 0.4F, 0.7F, 0.9F, + 0.10F, 0.15F, 0.10F); + + // get actual entity + final Entity entity = idEntity == ENTITY_ID_NONE ? null : world.getEntityByID(idEntity); + + // energizing + // @TODO cylinder fade in + shower + if (entity != null) { + final Vector3 v3Position = new Vector3(entity); + final Vector3 v3Target = v3Position.clone().translate(ForgeDirection.UP, entity.height); + EntityFX effect = new EntityFXBeam(world, v3Position, v3Target, + 0.6F + 0.1F * world.rand.nextFloat(), + 0.6F + 0.15F * world.rand.nextFloat(), + 0.8F + 0.10F * world.rand.nextFloat(), + 20, 0); + FMLClientHandler.instance().getClient().effectRenderer.addEffect(effect); + } + + // cooldown + // @TODO cylinder fade out + } + + private void handleAtDestination(final World world) { + // add flying particles in area of effect + spawnParticlesInArea(world, vDestination, true, + 0.4F, 0.9F, 0.7F, + 0.10F, 0.10F, 0.15F); + + // energizing + // @TODO cylinder fade in + shower + + // cooldown + // @TODO cylinder fade out + } + + private void spawnParticlesInArea(final World world, final VectorI vCenter, final boolean isFalling, + final float redBase, final float greenBase, final float blueBase, + final float redFactor, final float greenFactor, final float blueFactor) { + + // compute area of effect + final AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox( + vCenter.x - WarpDriveConfig.TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS, + vCenter.y - 1.0D, + vCenter.z - WarpDriveConfig.TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS, + vCenter.x + WarpDriveConfig.TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS + 1.0D, + vCenter.y + 2.0D, + vCenter.z + WarpDriveConfig.TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS + 1.0D); + + final Vector3 v3Motion = new Vector3(0.0D, 0.0D, 0.0D); + final Vector3 v3Acceleration = new Vector3(0.0D, isFalling ? -0.001D : 0.001D, 0.0D); + + // adjust quantity to lockStrength + final int quantityInArea = (int) Commons.clamp(1, 5, Math.round(Commons.interpolate(0.2D, 1.0D, 0.8D, 5.0D, lockStrength))); + for (int count = 0; count < quantityInArea; count++) { + // start preferably from top or bottom side + double y; + if (isFalling) { + y = aabb.maxY - Math.pow(world.rand.nextDouble(), 3.0D) * (aabb.maxY - aabb.minY); + } else { + y = aabb.minY + Math.pow(world.rand.nextDouble(), 3.0D) * (aabb.maxY - aabb.minY); + } + final Vector3 v3Position = new Vector3( + aabb.minX + world.rand.nextDouble() * (aabb.maxX - aabb.minX), + y, + aabb.minZ + world.rand.nextDouble() * (aabb.maxZ - aabb.minZ)); + + // adjust to block presence + if ( ( isFalling && MathHelper.floor_double(y) == MathHelper.floor_double(aabb.maxY)) + || (!isFalling && MathHelper.floor_double(y) == MathHelper.floor_double(aabb.minY)) ) { + final VectorI vPosition = new VectorI(MathHelper.floor_double(v3Position.x), + MathHelper.floor_double(v3Position.y), + MathHelper.floor_double(v3Position.z)); + final Block block = vPosition.getBlock(world); + if ( !block.isAir(world, vPosition.x, vPosition.y, vPosition.z) + && block.isOpaqueCube() ) { + y += isFalling ? -1.0D : 1.0D; + v3Position.y = y; + } + } + + // add particle + final EntityFX effect = new EntityFXDot(world, v3Position, + v3Motion, v3Acceleration, 0.98D, + redBase + redFactor * world.rand.nextFloat(), + greenBase+ greenFactor * world.rand.nextFloat(), + blueBase + blueFactor * world.rand.nextFloat(), + 1.0F, + 30); + FMLClientHandler.instance().getClient().effectRenderer.addEffect(effect); + } + } + + @Override + @SideOnly(Side.CLIENT) + public IMessage onMessage(MessageTransporterEffect messageSpawnParticle, MessageContext context) { + // skip in case player just logged in + if (Minecraft.getMinecraft().theWorld == null) { + WarpDrive.logger.error("WorldObj is null, ignoring particle packet"); + return null; + } + + if (WarpDriveConfig.LOGGING_EFFECTS) { + WarpDrive.logger.info("Received transporter effect from %s to %s with %.3f lockStrength towards entity with id %d at %s, energizing in %d ticks, cooldown for %d ticks", + messageSpawnParticle.vSource, messageSpawnParticle.vDestination, messageSpawnParticle.lockStrength, + messageSpawnParticle.idEntity, messageSpawnParticle.v3EntityPosition, + messageSpawnParticle.tickEnergizing, messageSpawnParticle.tickCooldown); + } + + messageSpawnParticle.handle(Minecraft.getMinecraft().theWorld); + + return null; // no response + } +} diff --git a/src/main/java/cr0s/warpdrive/network/PacketHandler.java b/src/main/java/cr0s/warpdrive/network/PacketHandler.java index bb9570e0..165906ac 100644 --- a/src/main/java/cr0s/warpdrive/network/PacketHandler.java +++ b/src/main/java/cr0s/warpdrive/network/PacketHandler.java @@ -5,6 +5,7 @@ import cr0s.warpdrive.config.WarpDriveConfig; import cr0s.warpdrive.data.CelestialObject; import cr0s.warpdrive.data.CloakedArea; import cr0s.warpdrive.data.Vector3; +import cr0s.warpdrive.data.VectorI; import java.lang.reflect.Method; import java.util.List; @@ -34,6 +35,7 @@ public class PacketHandler { simpleNetworkManager.registerMessage(MessageCloak.class , MessageCloak.class , 3, Side.CLIENT); simpleNetworkManager.registerMessage(MessageSpawnParticle.class , MessageSpawnParticle.class , 4, Side.CLIENT); simpleNetworkManager.registerMessage(MessageVideoChannel.class , MessageVideoChannel.class , 5, Side.CLIENT); + simpleNetworkManager.registerMessage(MessageTransporterEffect.class , MessageTransporterEffect.class , 6, Side.CLIENT); simpleNetworkManager.registerMessage(MessageTargeting.class , MessageTargeting.class , 100, Side.SERVER); simpleNetworkManager.registerMessage(MessageClientValidation.class , MessageClientValidation.class , 101, Side.SERVER); @@ -112,6 +114,32 @@ public class PacketHandler { } } + // Transporter effect sent to client side + public static void sendTransporterEffectPacket(final World world, final VectorI vSource, final VectorI vDestination, final double lockStrength, + final Entity entity, final Vector3 v3EntityPosition, + final int tickEnergizing, final int tickCooldown, final int radius) { + assert(!world.isRemote); + + final MessageTransporterEffect messageTransporterEffect = new MessageTransporterEffect(vSource, vDestination, lockStrength, + entity, v3EntityPosition, + tickEnergizing, tickCooldown); + + // check both ends to send packet + final List playerEntityList = MinecraftServer.getServer().getConfigurationManager().playerEntityList; + final int dimensionId = world.provider.dimensionId; + final int radius_square = radius * radius; + for (int index = 0; index < playerEntityList.size(); index++) { + final EntityPlayerMP entityPlayerMP = playerEntityList.get(index); + + if (entityPlayerMP.dimension == dimensionId) { + if ( vSource.distance2To(entityPlayerMP) < radius_square + || vDestination.distance2To(entityPlayerMP) < radius_square ) { + simpleNetworkManager.sendTo(messageTransporterEffect, entityPlayerMP); + } + } + } + } + // Monitor/Laser/Camera updating its video channel to client side public static void sendVideoChannelPacket(final int dimensionId, final int xCoord, final int yCoord, final int zCoord, final int videoChannel) { MessageVideoChannel messageVideoChannel = new MessageVideoChannel(xCoord, yCoord, zCoord, videoChannel); diff --git a/src/main/java/cr0s/warpdrive/render/EntityFXDot.java b/src/main/java/cr0s/warpdrive/render/EntityFXDot.java new file mode 100644 index 00000000..dfded6e7 --- /dev/null +++ b/src/main/java/cr0s/warpdrive/render/EntityFXDot.java @@ -0,0 +1,113 @@ +package cr0s.warpdrive.render; + +import cr0s.warpdrive.data.Vector3; + +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class EntityFXDot extends EntityFX { + + private Vector3 v3Acceleration; + private double friction; + private int layer = 0; // 0 = particles, 1 = blocks, 2 = items + + public EntityFXDot(final World world, final Vector3 v3Position, + final Vector3 v3Motion, final Vector3 v3Acceleration, final double friction, + final float red, final float green, final float blue, final float alpha, + final int age) { + super(world, v3Position.x, v3Position.y, v3Position.z, 0.0D, 0.0D, 0.0D); + this.setRBGColorF(red, green, blue); + this.setAlphaF(alpha); + this.setSize(0.02F, 0.02F); + this.noClip = true; + this.motionX = v3Motion.x; + this.motionY = v3Motion.y; + this.motionZ = v3Motion.z; + this.v3Acceleration = v3Acceleration; + this.friction = friction; + this.particleMaxAge = age; + + // defaults to vanilla water drip + this.setParticleTextureIndex(113); + + // refresh bounding box + setPosition(v3Position.x, v3Position.y, v3Position.z); + } + + public void setParticleFromBlockIcon(final IIcon icon) { + layer = 1; + setParticleIcon(icon); + } + + public void setParticleFromItemIcon(final IIcon icon) { + layer = 2; + setParticleIcon(icon); + } + + @Override + public void onUpdate() { + prevPosX = posX; + prevPosY = posY; + prevPosZ = posZ; + + if (particleAge++ >= particleMaxAge) { + setDead(); + } + + moveEntity(motionX, motionY, motionZ); + motionX = (motionX + v3Acceleration.x) * friction; + motionY = (motionY + v3Acceleration.y) * friction; + motionZ = (motionZ + v3Acceleration.z) * friction; + } + + @Override + public int getFXLayer() { + return layer; + } + + @Override + public void renderParticle(final Tessellator tessellator, final float partialTick, + final float cosYaw, final float cosPitch, final float sinYaw, final float sinSinPitch, final float cosSinPitch) { + double minU = particleTextureIndexX / 16.0F; + double maxU = minU + 0.0624375F; + double minV = particleTextureIndexY / 16.0F; + double maxV = minV + 0.0624375F; + final float scale = 0.1F * particleScale; + + if (particleIcon != null) { + minU = particleIcon.getMinU(); + maxU = particleIcon.getMaxU(); + minV = particleIcon.getMinV(); + maxV = particleIcon.getMaxV(); + } + + final double x = prevPosX + (posX - prevPosX) * partialTick - interpPosX; + final double y = prevPosY + (posY - prevPosY) * partialTick - interpPosY; + final double z = prevPosZ + (posZ - prevPosZ) * partialTick - interpPosZ; + + // alpha increase during first tick and decays during last 2 ticks + float alpha = particleAlpha; + final int ageLeft = particleMaxAge - particleAge; + if (particleAge < 1) { + alpha = particleAlpha * partialTick; + } else if (ageLeft < 2) { + if (ageLeft < 1) { + alpha = particleAlpha * (0.5F - partialTick / 2.0F); + } else { + alpha = particleAlpha * (1.0F - partialTick / 2.0F); + } + } + + tessellator.setColorRGBA_F(particleRed, particleGreen, particleBlue, alpha); + tessellator.addVertexWithUV(x - cosYaw * scale - sinSinPitch * scale, y - cosPitch * scale, z - sinYaw * scale - cosSinPitch * scale, maxU, maxV); + tessellator.addVertexWithUV(x - cosYaw * scale + sinSinPitch * scale, y + cosPitch * scale, z - sinYaw * scale + cosSinPitch * scale, maxU, minV); + tessellator.addVertexWithUV(x + cosYaw * scale + sinSinPitch * scale, y + cosPitch * scale, z + sinYaw * scale + cosSinPitch * scale, minU, minV); + tessellator.addVertexWithUV(x + cosYaw * scale - sinSinPitch * scale, y - cosPitch * scale, z + sinYaw * scale - cosSinPitch * scale, minU, maxV); + } +} \ No newline at end of file