Added transporter FX in source & destination areas (wip)
This commit is contained in:
parent
50812ff0d8
commit
80616c2f77
4 changed files with 385 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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<MessageTransporterEffect, IMessage> {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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<EntityPlayerMP> 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);
|
||||
|
|
113
src/main/java/cr0s/warpdrive/render/EntityFXDot.java
Normal file
113
src/main/java/cr0s/warpdrive/render/EntityFXDot.java
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue