package vazkii.botania.common.network; import java.awt.Color; import io.netty.buffer.ByteBuf; import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.item.EnumDyeColor; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; import vazkii.botania.common.Botania; import vazkii.botania.common.block.tile.TileTerraPlate; import vazkii.botania.common.core.handler.ConfigHandler; import vazkii.botania.common.core.helper.MathHelper; import vazkii.botania.common.core.helper.Vector3; import vazkii.botania.common.entity.EntityDoppleganger; public class PacketBotaniaEffect implements IMessage { private EffectType type; private double x; private double y; private double z; private int[] args; public PacketBotaniaEffect() {} public PacketBotaniaEffect(EffectType type, double x, double y, double z, int... args) { this.type = type; this.x = x; this.y = y; this.z = z; this.args = args; } @Override public void fromBytes(ByteBuf buf) { type = EffectType.values()[buf.readShort()]; x = buf.readDouble(); y = buf.readDouble(); z = buf.readDouble(); args = new int[type.argCount]; for (int i = 0; i < args.length; i++) { args[i] = ByteBufUtils.readVarInt(buf, 5); } } @Override public void toBytes(ByteBuf buf) { buf.writeShort(type.ordinal()); buf.writeDouble(x); buf.writeDouble(y); buf.writeDouble(z); for (int i = 0; i < type.argCount; i++) { ByteBufUtils.writeVarInt(buf, args[i], 5); } } public static class Handler implements IMessageHandler<PacketBotaniaEffect, IMessage> { @Override public IMessage onMessage(final PacketBotaniaEffect message, final MessageContext ctx) { Minecraft.getMinecraft().addScheduledTask(new Runnable() { // Use anon - lambda causes classloading issues @Override public void run() { Minecraft mc = Minecraft.getMinecraft(); World world = mc.world; switch (message.type) { case POOL_CRAFT: { for(int i = 0; i < 25; i++) { float red = (float) Math.random(); float green = (float) Math.random(); float blue = (float) Math.random(); Botania.proxy.sparkleFX(message.x + 0.5 + Math.random() * 0.4 - 0.2, message.y + 0.75, message.z + 0.5 + Math.random() * 0.4 - 0.2, red, green, blue, (float) Math.random(), 10); } break; } case POOL_CHARGE: { if(ConfigHandler.chargingAnimationEnabled) { boolean outputting = message.args[0] == 1; BlockPos pos = new BlockPos(message.x, message.y, message.z); Vector3 itemVec = Vector3.fromBlockPos(pos).add(0.5, 0.5 + Math.random() * 0.3, 0.5); Vector3 tileVec = Vector3.fromBlockPos(pos).add(0.2 + Math.random() * 0.6, 0, 0.2 + Math.random() * 0.6); Botania.proxy.lightningFX(outputting ? tileVec : itemVec, outputting ? itemVec : tileVec, 80, world.rand.nextLong(), 0x4400799c, 0x4400C6FF); } break; } case PAINT_LENS: { EnumDyeColor placeColor = EnumDyeColor.byMetadata(message.args[0]); int hex = placeColor.getMapColor().colorValue; int r = (hex & 0xFF0000) >> 16; int g = (hex & 0xFF00) >> 8; int b = hex & 0xFF; for(int i = 0; i < 10; i++) { BlockPos pos = new BlockPos(message.x, message.y, message.z).offset(EnumFacing.VALUES[world.rand.nextInt(6)]); Botania.proxy.sparkleFX( pos.getX() + (float) Math.random(), pos.getY() + (float) Math.random(), pos.getZ() + (float) Math.random(), r / 255F, g / 255F, b / 255F, 0.6F + (float) Math.random() * 0.5F, 5); } break; } case ARENA_INDICATOR: { for(int i = 0; i < 360; i += 8) { float r = 1F; float g = 0F; float b = 1F; float rad = i * (float) Math.PI / 180F; double x = message.x + 0.5 - Math.cos(rad) * EntityDoppleganger.ARENA_RANGE; double y = message.y + 0.5; double z = message.z + 0.5 - Math.sin(rad) * EntityDoppleganger.ARENA_RANGE; Botania.proxy.sparkleFX(x, y, z, r, g, b, 5F, 120); } break; } case ITEM_SMOKE: { Entity item = world.getEntityByID(message.args[0]); int p = message.args[1]; for(int i = 0; i < p; i++) { double m = 0.01; double d0 = item.world.rand.nextGaussian() * m; double d1 = item.world.rand.nextGaussian() * m; double d2 = item.world.rand.nextGaussian() * m; double d3 = 10.0D; item.world.spawnParticle(EnumParticleTypes.EXPLOSION_NORMAL, message.x + item.world.rand.nextFloat() * item.width * 2.0F - item.width - d0 * d3, message.y + item.world.rand.nextFloat() * item.height - d1 * d3, message.z + item.world.rand.nextFloat() * item.width * 2.0F - item.width - d2 * d3, d0, d1, d2); } break; } case SPARK_NET_INDICATOR: { Entity e1 = world.getEntityByID(message.args[0]); Entity e2 = world.getEntityByID(message.args[1]); if(e1 == null || e2 == null) return; Vector3 orig = new Vector3(e1.posX , e1.posY + 0.25, e1.posZ); Vector3 end = new Vector3(e2.posX, e2.posY + 0.25, e2.posZ); Vector3 diff = end.subtract(orig); Vector3 movement = diff.normalize().multiply(0.1); int iters = (int) (diff.mag() / movement.mag()); float huePer = 1F / iters; float hueSum = (float) Math.random(); Vector3 currentPos = orig; for(int i = 0; i < iters; i++) { float hue = i * huePer + hueSum; Color color = Color.getHSBColor(hue, 1F, 1F); float r = Math.min(1F, color.getRed() / 255F + 0.4F); float g = Math.min(1F, color.getGreen() / 255F + 0.4F); float b = Math.min(1F, color.getBlue() / 255F + 0.4F); Botania.proxy.setSparkleFXNoClip(true); Botania.proxy.sparkleFX(currentPos.x, currentPos.y, currentPos.z, r, g, b, 1F, 12); Botania.proxy.setSparkleFXNoClip(false); currentPos = currentPos.add(movement); } break; } case SPARK_MANA_FLOW: { Entity e1 = world.getEntityByID(message.args[0]); Entity e2 = world.getEntityByID(message.args[1]); if(e1 == null || e2 == null) return; double rc = 0.45; Vector3 thisVec = Vector3.fromEntityCenter(e1).add((Math.random() - 0.5) * rc, (Math.random() - 0.5) * rc, (Math.random() - 0.5) * rc); Vector3 receiverVec = Vector3.fromEntityCenter(e2).add((Math.random() - 0.5) * rc, (Math.random() - 0.5) * rc, (Math.random() - 0.5) * rc); Vector3 motion = receiverVec.subtract(thisVec).multiply(0.04F); float r = 0.4F + 0.3F * (float) Math.random(); float g = 0.4F + 0.3F * (float) Math.random(); float b = 0.4F + 0.3F * (float) Math.random(); float size = 0.125F + 0.125F * (float) Math.random(); Botania.proxy.wispFX(thisVec.x, thisVec.y, thisVec.z, r, g, b, size, (float) motion.x, (float) motion.y, (float) motion.z); break; } case ENCHANTER_CRAFT: { for(int i = 0; i < 25; i++) { float red = (float) Math.random(); float green = (float) Math.random(); float blue = (float) Math.random(); Botania.proxy.sparkleFX( message.x + Math.random() * 0.4 - 0.2, message.y, message.z + Math.random() * 0.4 - 0.2, red, green, blue, (float) Math.random(), 10); } break; } case ENCHANTER_DESTROY: { for(int i = 0; i < 50; i++) { float red = (float) Math.random(); float green = (float) Math.random(); float blue = (float) Math.random(); Botania.proxy.wispFX(message.x, message.y, message.z, red, green, blue, (float) Math.random() * 0.15F + 0.15F, (float) (Math.random() - 0.5F) * 0.25F, (float) (Math.random() - 0.5F) * 0.25F, (float) (Math.random() - 0.5F) * 0.25F); } break; } case ENTROPINNYUM: { for(int i = 0; i < 50; i++) Botania.proxy.sparkleFX(message.x + Math.random() * 4 - 2, message.y + Math.random() * 4 - 2, message.z + Math.random() * 4 - 2, 1F, (float) Math.random() * 0.25F, (float) Math.random() * 0.25F, (float) (Math.random() * 0.65F + 1.25F), 12); world.spawnParticle(EnumParticleTypes.EXPLOSION_HUGE, message.x, message.y, message.z, 1D, 0D, 0D); break; } case BLACK_LOTUS_DISSOLVE: { for(int i = 0; i < 50; i++) { float r = (float) Math.random() * 0.35F; float g = 0F; float b = (float) Math.random() * 0.35F; float s = 0.45F * (float) Math.random() * 0.25F; float m = 0.045F; float mx = ((float) Math.random() - 0.5F) * m; float my = (float) Math.random() * m; float mz = ((float) Math.random() - 0.5F) * m; Botania.proxy.wispFX(message.x, message.y, message.z, r, g, b, s, mx, my, mz); } break; } case BREWERY_FINISH: { for(int i = 0; i < 25; i++) { Color c = new Color(message.args[0]); float r = c.getRed() / 255F; float g = c.getGreen() / 255F; float b = c.getBlue() / 255F; Botania.proxy.sparkleFX(message.x + 0.5 + Math.random() * 0.4 - 0.2, message.y + 1, message.z + 0.5 + Math.random() * 0.4 - 0.2, r, g, b, (float) Math.random() * 2F + 0.5F, 10); for(int j = 0; j < 2; j++) Botania.proxy.wispFX(message.x + 0.7 - Math.random() * 0.4, message.y + 0.9 - Math.random() * 0.2, message.z + 0.7 - Math.random() * 0.4, 0.2F, 0.2F, 0.2F, 0.1F + (float) Math.random() * 0.2F, 0.05F - (float) Math.random() * 0.1F, 0.05F + (float) Math.random() * 0.03F, 0.05F - (float) Math.random() * 0.1F); } } case TERRA_PLATE: { TileEntity te = world.getTileEntity(new BlockPos(message.x, message.y, message.z)); if(te instanceof TileTerraPlate) { int ticks = (int) (100.0 * ((double) ((TileTerraPlate) te).getCurrentMana() / (double) TileTerraPlate.MAX_MANA)); int totalSpiritCount = 3; double tickIncrement = 360D / totalSpiritCount; int speed = 5; double wticks = ticks * speed - tickIncrement; double r = Math.sin((ticks - 100) / 10D) * 2; double g = Math.sin(wticks * Math.PI / 180 * 0.55); for(int i = 0; i < totalSpiritCount; i++) { double x = message.x + Math.sin(wticks * Math.PI / 180) * r + 0.5; double y = message.y + 0.25 + Math.abs(r) * 0.7; double z = message.z + Math.cos(wticks * Math.PI / 180) * r + 0.5; wticks += tickIncrement; float[] colorsfx = new float[] { 0F, (float) ticks / (float) 100, 1F - (float) ticks / (float) 100 }; Botania.proxy.wispFX(x, y, z, colorsfx[0], colorsfx[1], colorsfx[2], 0.85F, (float)g * 0.05F, 0.25F); Botania.proxy.wispFX(x, y, z, colorsfx[0], colorsfx[1], colorsfx[2], (float) Math.random() * 0.1F + 0.1F, (float) (Math.random() - 0.5) * 0.05F, (float) (Math.random() - 0.5) * 0.05F, (float) (Math.random() - 0.5) * 0.05F, 0.9F); if(ticks == 100) for(int j = 0; j < 15; j++) Botania.proxy.wispFX(message.x + 0.5, message.y + 0.5, message.z + 0.5, colorsfx[0], colorsfx[1], colorsfx[2], (float) Math.random() * 0.15F + 0.15F, (float) (Math.random() - 0.5F) * 0.125F, (float) (Math.random() - 0.5F) * 0.125F, (float) (Math.random() - 0.5F) * 0.125F); } } } case APOTHECARY_CRAFT: { for(int i = 0; i < 25; i++) { float red = (float) Math.random(); float green = (float) Math.random(); float blue = (float) Math.random(); Botania.proxy.sparkleFX(message.x + 0.5 + Math.random() * 0.4 - 0.2, message.y + 1, message.z + 0.5 + Math.random() * 0.4 - 0.2, red, green, blue, (float) Math.random(), 10); } break; } case RUNE_CRAFT: { for(int i = 0; i < 25; i++) { float red = (float) Math.random(); float green = (float) Math.random(); float blue = (float) Math.random(); Botania.proxy.sparkleFX(message.x + 0.5 + Math.random() * 0.4 - 0.2, message.y + 1, message.z + 0.5 + Math.random() * 0.4 - 0.2, red, green, blue, (float) Math.random(), 10); } } } } }); return null; } } public enum EffectType { POOL_CRAFT(0), POOL_CHARGE(1), // Arg: 1 if outputting, 0 if inputting PAINT_LENS(1), // Arg: EnumDyeColor ARENA_INDICATOR(0), ITEM_SMOKE(2), // Arg: Entity ID, number of particles SPARK_NET_INDICATOR(2), // Arg: Entity ID from, Entity ID towards SPARK_MANA_FLOW(2), // Arg: Entity ID from, Entity ID towards ENCHANTER_CRAFT(0), ENCHANTER_DESTROY(0), ENTROPINNYUM(0), BLACK_LOTUS_DISSOLVE(0), BREWERY_FINISH(1), // Arg: RGB TERRA_PLATE(0), APOTHECARY_CRAFT(0), RUNE_CRAFT(0); private final int argCount; EffectType(int argCount) { this.argCount = argCount; } } }