Added transporter energizing particle effect

Added transporter room containment particles
Fixed brigthness in containment particles
Integrated transporter on dedicated server
This commit is contained in:
Unknown 2018-04-21 18:19:03 +02:00 committed by unknown
parent 3d3065aa62
commit 4ea5854430
8 changed files with 587 additions and 41 deletions

View file

@ -12,6 +12,9 @@ import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import java.util.ArrayList;
import java.util.Collection;
public class BlockTransporterScanner extends BlockAbstractBase {
@SideOnly(Side.CLIENT)
@ -69,7 +72,9 @@ public class BlockTransporterScanner extends BlockAbstractBase {
return iconBuffer[metadata == 0 ? 2 : 3];
}
public boolean isValid(final World worldObj, final VectorI vScanner) {
// return null or empty collection if it's invalid
public Collection<VectorI> getValidContainment(final World worldObj, final VectorI vScanner) {
final ArrayList<VectorI> vContainments = new ArrayList<>(8);
boolean isScannerPosition = true;
for (int x = vScanner.x - 1; x <= vScanner.x + 1; x++) {
for (int z = vScanner.z - 1; z <= vScanner.z + 1; z++) {
@ -77,19 +82,24 @@ public class BlockTransporterScanner extends BlockAbstractBase {
final Block blockBase = worldObj.getBlock(x, vScanner.y, z);
if ( !(blockBase instanceof BlockTransporterContainment)
&& (!isScannerPosition || !(blockBase instanceof BlockTransporterScanner)) ) {
return false;
return null;
}
isScannerPosition = !isScannerPosition;
// check 2 above blocks are air
if (!worldObj.isAirBlock(x, vScanner.y + 1, z)) {
return false;
return null;
}
if (!worldObj.isAirBlock(x, vScanner.y + 2, z)) {
return false;
return null;
}
// save containment position
if (blockBase instanceof BlockTransporterContainment) {
vContainments.add(new VectorI(x, vScanner.y, z));
}
}
}
return true;
return vContainments;
}
}

View file

@ -32,6 +32,7 @@ import li.cil.oc.api.machine.Callback;
import li.cil.oc.api.machine.Context;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -42,7 +43,6 @@ import java.util.Map.Entry;
import java.util.UUID;
import net.minecraft.block.Block;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLivingBase;
@ -53,6 +53,9 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntity;
@ -82,6 +85,7 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
private EnumTransporterState transporterState = EnumTransporterState.DISABLED;
// computed properties
private ArrayList<VectorI> vLocalContainments = null;
private AxisAlignedBB aabbLocalScanners = null;
private boolean isBlockUpdated = false;
private int tickUpdateRegistry = 0;
@ -558,6 +562,7 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
final int zMax = zCoord + WarpDriveConfig.TRANSPORTER_SCANNER_GRAB_XZ_BLOCKS;
final ArrayList<VectorI> vScanners = new ArrayList<>(16);
final HashSet<VectorI> vContainments = new HashSet<>(64);
for (int x = xMin; x <= xMax; x++) {
for (int y = yMin; y <= yMax; y++) {
if (y < 0 || y > 254) {
@ -570,29 +575,31 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
// only accept valid ones, spawn particles on others
final VectorI vScanner = new VectorI(x, y, z);
final boolean isValid = ((BlockTransporterScanner) block).isValid(worldObj, vScanner);
if (isValid) {
vScanners.add(vScanner);
worldObj.setBlockMetadataWithNotify(x, y, z, 1, 2);
} else {
final Collection<VectorI> vValidContainments = ((BlockTransporterScanner) block).getValidContainment(worldObj, vScanner);
if (vValidContainments == null || vValidContainments.isEmpty()) {
worldObj.setBlockMetadataWithNotify(x, y, z, 0, 2);
PacketHandler.sendSpawnParticlePacket(worldObj, "jammed", (byte) 5, new Vector3(vScanner.x + 0.5D, vScanner.y + 1.5D, vScanner.z + 0.5D),
new Vector3(0.0D, 0.0D, 0.0D),
1.0F, 1.0F, 1.0F,
1.0F, 1.0F, 1.0F,
32);
} else {
vScanners.add(vScanner);
vContainments.addAll(vValidContainments);
worldObj.setBlockMetadataWithNotify(x, y, z, 1, 2);
}
}
}
}
}
setLocalScanners(vScanners);
setLocalScanners(vScanners, vContainments);
}
private void setLocalScanners(final ArrayList<VectorI> vScanners) {
private void setLocalScanners(final ArrayList<VectorI> vScanners, final Collection<VectorI> vContainments) {
// no scanner defined => force null
if (vScanners == null || vScanners.isEmpty()) {
vLocalScanners = null;
vLocalContainments = null;
aabbLocalScanners = null;
return;
}
@ -611,11 +618,17 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
// save values
vLocalScanners = vScanners;
vLocalContainments = new ArrayList<>(vContainments);
aabbLocalScanners = AxisAlignedBB.getBoundingBox(
vMin.x, vMin.y, vMin.z,
vMax.x + 1.0D, vMax.y + 1.0D, vMax.z + 1.0D);
}
@SuppressWarnings("unchecked")
public Collection<VectorI> getContainments() {
return vLocalContainments;
}
private static class FocusValues {
ArrayList<VectorI> vScanners;
int countRangeUpgrades;
@ -1207,10 +1220,7 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
final Entity entity = (Entity) object;
// skip particle effects
if (entity instanceof EntityFX) {
continue;
}
// (particle effects are client side only, no need to filter them out)
// skip blacklisted ids
final String entityId = EntityList.getEntityString(entity);
@ -1262,10 +1272,7 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
final Entity entity = (Entity) object;
// skip particle effects
if (entity instanceof EntityFX) {
continue;
}
// (particle effects are client side only, no need to filter them out)
// skip blacklisted ids
final String entityId = EntityList.getEntityString(entity);
@ -1304,13 +1311,21 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
tagCompound.setLong("uuidLeast", uuid.getLeastSignificantBits());
}
final NBTTagList tagListScanners = new NBTTagList();
if (vLocalScanners != null) {
for (VectorI vScanner : vLocalScanners) {
if ( vLocalScanners != null
&& vLocalContainments != null ) {
final NBTTagList tagListScanners = new NBTTagList();
for (final VectorI vScanner : vLocalScanners) {
final NBTTagCompound tagCompoundScanner = vScanner.writeToNBT(new NBTTagCompound());
tagListScanners.appendTag(tagCompoundScanner);
}
tagCompound.setTag("scanners", tagListScanners);
final NBTTagList tagListContainments = new NBTTagList();
for (final VectorI vContainment : vLocalContainments) {
final NBTTagCompound tagCompoundContainment = vContainment.writeToNBT(new NBTTagCompound());
tagListContainments.appendTag(tagCompoundContainment);
}
tagCompound.setTag("containments", tagListContainments);
}
tagCompound.setInteger(IBeamFrequency.BEAM_FREQUENCY_TAG, beamFrequency);
@ -1346,14 +1361,22 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
uuid = UUID.randomUUID();
}
if (tagCompound.hasKey("scanners", Constants.NBT.TAG_LIST)) {
if ( tagCompound.hasKey("scanners", Constants.NBT.TAG_LIST)
&& tagCompound.hasKey("containments", Constants.NBT.TAG_LIST)) {
final NBTTagList tagListScanners = (NBTTagList) tagCompound.getTag("scanners");
final ArrayList<VectorI> vScanners = new ArrayList<>(tagListScanners.tagCount());
for (int indexScanner = 0; indexScanner < tagListScanners.tagCount(); indexScanner++) {
final VectorI vScanner = VectorI.createFromNBT(tagListScanners.getCompoundTagAt(indexScanner));
vScanners.add(vScanner);
}
setLocalScanners(vScanners);
final NBTTagList tagListContainments = (NBTTagList) tagCompound.getTag("containments");
final ArrayList<VectorI> vContainments = new ArrayList<>(tagListContainments.tagCount());
for (int indexContainment = 0; indexContainment < tagListContainments.tagCount(); indexContainment++) {
final VectorI vContainment = VectorI.createFromNBT(tagListContainments.getCompoundTagAt(indexContainment));
vContainments.add(vContainment);
}
setLocalScanners(vScanners, vContainments);
}
beamFrequency = tagCompound.getInteger(IBeamFrequency.BEAM_FREQUENCY_TAG);
@ -1390,6 +1413,20 @@ public class TileEntityTransporterCore extends TileEntityAbstractEnergy implemen
}
}
@Override
public Packet getDescriptionPacket() {
final NBTTagCompound tagCompound = new NBTTagCompound();
writeToNBT(tagCompound);
return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, -1, tagCompound);
}
@Override
public void onDataPacket(final NetworkManager networkManager, final S35PacketUpdateTileEntity packet) {
final NBTTagCompound tagCompound = packet.func_148857_g();
readFromNBT(tagCompound);
}
// Common OC/CC methods
@Override
public String[] transporterName(final Object[] arguments) {

View file

@ -203,6 +203,7 @@ public class WarpDriveConfig {
public static boolean LOGGING_RENDERING = false;
public static boolean LOGGING_CHUNK_HANDLER = false;
public static boolean LOGGING_CHUNK_LOADING = true;
public static boolean LOGGING_ENTITY_FX = false;
public static boolean LOGGING_CLIENT_SYNCHRONIZATION = false;
// Starmap
@ -687,6 +688,7 @@ public class WarpDriveConfig {
LOGGING_RENDERING = config.get("logging", "enable_rendering_logs", LOGGING_RENDERING, "Detailed rendering logs to help debug the mod.").getBoolean(false);
LOGGING_CHUNK_HANDLER = config.get("logging", "enable_chunk_handler_logs", LOGGING_CHUNK_HANDLER, "Detailed chunk data logs to help debug the mod.").getBoolean(false);
LOGGING_CHUNK_LOADING = config.get("logging", "enable_chunk_loading_logs", LOGGING_CHUNK_LOADING, "Chunk loading logs, enable it to report chunk loaders updates").getBoolean(false);
LOGGING_ENTITY_FX = config.get("logging", "enable_entity_fx_logs", LOGGING_ENTITY_FX, "EntityFX logs, enable it to dump entityFX registry updates").getBoolean(false);
// Starmap registry
STARMAP_REGISTRY_UPDATE_INTERVAL_SECONDS = Commons.clamp(0, 300,

View file

@ -0,0 +1,184 @@
package cr0s.warpdrive.data;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.render.AbstractEntityFX;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Registry of all active entity FX on this client
*
* @author LemADEC
*/
public class EntityFXRegistry {
private static final HashMap<Integer, CopyOnWriteArraySet<WeakReference<AbstractEntityFX>>> REGISTRY = new HashMap<>();
private static int countAdd = 0;
private static int countRemove = 0;
private static int countRead = 0;
public EntityFXRegistry() {
}
private static int computeHashcode(final AbstractEntityFX entityFX) {
return computeHashcode(entityFX.worldObj.provider.dimensionId,
MathHelper.floor_double(entityFX.posX),
MathHelper.floor_double(entityFX.posY),
MathHelper.floor_double(entityFX.posZ));
}
private static int computeHashcode(final World world, final Vector3 v3Position) {
return computeHashcode(world.provider.dimensionId,
MathHelper.floor_double(v3Position.x),
MathHelper.floor_double(v3Position.y),
MathHelper.floor_double(v3Position.z));
}
private static int computeHashcode(final int dimensionId, final int x, final int y, final int z) {
return (dimensionId << 24) ^ ((x & 0xFFFF) << 8) ^ (y << 16) ^ (z & 0xFFFF);
}
private static void logStats(final int trigger) {
if ((trigger & 0x3FF) != 0) {
return;
}
int sizeTotal = 0;
int sizeClusterMax = 0;
for (final CopyOnWriteArraySet<WeakReference<AbstractEntityFX>> items : REGISTRY.values()) {
final int size = items.size();
sizeTotal += size;
sizeClusterMax = Math.max(sizeClusterMax, size);
}
WarpDrive.logger.info(String.format("AbstractEntityFX REGISTRY stats: read %d add %d remove %d => %.3f read, currently holding %d items %d hashes %d maxCluster",
countRead, countAdd, countRemove,
((float) countRead) / (countRemove + countRead + countAdd),
sizeTotal, REGISTRY.size(), sizeClusterMax));
}
public static AbstractEntityFX get(final World world, final Vector3 v3Position, final double rangeMax) {
countRead++;
if (WarpDriveConfig.LOGGING_ENTITY_FX) {
logStats(countRead);
}
// get by hashcode
final Integer hashcode = computeHashcode(world, v3Position);
final CopyOnWriteArraySet<WeakReference<AbstractEntityFX>> setRegistryItems = REGISTRY.get(hashcode);
if (setRegistryItems == null) {
return null;
}
final double rangeMaxSquare = rangeMax * rangeMax;
// get the exact match
for (final WeakReference<AbstractEntityFX> weakEntityFX : setRegistryItems) {
if (weakEntityFX == null) {
countRemove++;
setRegistryItems.remove(null);
continue;
}
final AbstractEntityFX entityFX = weakEntityFX.get();
if ( entityFX == null
|| entityFX.isDead ) {
countRemove++;
setRegistryItems.remove(weakEntityFX);
continue;
}
final double rangeSquared = v3Position.distanceTo_square(entityFX);
if (rangeSquared < rangeMaxSquare) {
return entityFX;
}
}
return null;
}
public static boolean add(final AbstractEntityFX entityFX) {
countRead++;
if (WarpDriveConfig.LOGGING_ENTITY_FX) {
logStats(countRead);
}
// get by hashcode
final Integer hashcode = computeHashcode(entityFX);
CopyOnWriteArraySet<WeakReference<AbstractEntityFX>> setRegistryItems = REGISTRY.get(hashcode);
if (setRegistryItems == null) {
setRegistryItems = new CopyOnWriteArraySet<>();
REGISTRY.put(hashcode, setRegistryItems);
} else {
// get the exact match
final Vector3 v3Position = new Vector3(entityFX);
for (final WeakReference<AbstractEntityFX> weakEntityFX : setRegistryItems) {
if (weakEntityFX == null) {
countRemove++;
setRegistryItems.remove(null);
continue;
}
final AbstractEntityFX entityFX_existing = weakEntityFX.get();
if ( entityFX_existing == null
|| entityFX_existing.isDead ) {
countRemove++;
setRegistryItems.remove(weakEntityFX);
continue;
}
if (entityFX.getEntityId() == entityFX_existing.getEntityId()) {
if (WarpDriveConfig.LOGGING_ENTITY_FX) {
printRegistry("already registered");
}
return false;
}
if (v3Position.distanceTo_square(entityFX) < 0.01D) {
if (WarpDriveConfig.LOGGING_ENTITY_FX) {
printRegistry("existing entity at location");
}
return false;
}
}
}
// not found => add
countAdd++;
setRegistryItems.add(new WeakReference<>(entityFX));
if (WarpDriveConfig.LOGGING_ENTITY_FX) {
printRegistry("added");
}
return true;
}
private static void printRegistry(final String trigger) {
WarpDrive.logger.info("AbstractEntityFX REGISTRY (" + REGISTRY.size() + " entries after " + trigger + "):");
for (final Entry<Integer, CopyOnWriteArraySet<WeakReference<AbstractEntityFX>>> entryRegistryItems : REGISTRY.entrySet()) {
StringBuilder message = new StringBuilder();
final Iterator<WeakReference<AbstractEntityFX>> iterator = entryRegistryItems.getValue().iterator();
while (iterator.hasNext()) {
final WeakReference<AbstractEntityFX> weakEntityFX = iterator.next();
if (weakEntityFX == null) {
countRemove++;
iterator.remove();
continue;
}
final AbstractEntityFX entityFX = weakEntityFX.get();
if (entityFX == null) {
countRemove++;
iterator.remove();
continue;
}
message.append(String.format("\n- %s",
entityFX));
}
WarpDrive.logger.info(String.format("- %d entries with hashcode 0x%8X: %s",
entryRegistryItems.getValue().size(),
entryRegistryItems.getKey(),
message.toString()));
}
}
}

View file

@ -2,13 +2,16 @@ package cr0s.warpdrive.network;
import cr0s.warpdrive.Commons;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.block.movement.TileEntityTransporterCore;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.data.EntityFXRegistry;
import cr0s.warpdrive.data.GlobalPosition;
import cr0s.warpdrive.data.MovingEntity;
import cr0s.warpdrive.data.Vector3;
import cr0s.warpdrive.data.VectorI;
import cr0s.warpdrive.render.EntityFXBeam;
import cr0s.warpdrive.render.AbstractEntityFX;
import cr0s.warpdrive.render.EntityFXDot;
import cr0s.warpdrive.render.EntityFXEnergizing;
import io.netty.buffer.ByteBuf;
import net.minecraft.block.Block;
@ -16,6 +19,7 @@ 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.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
@ -143,6 +147,10 @@ public class MessageTransporterEffect implements IMessage, IMessageHandler<Messa
spawnParticlesInArea(world, globalPosition.getVectorI(), false,
0.4F, 0.7F, 0.9F,
0.10F, 0.15F, 0.10F);
} else {
spawnParticlesInTransporterRoom(world, globalPosition.getVectorI(), false,
0.4F, 0.7F, 0.9F,
0.10F, 0.15F, 0.10F);
}
// get actual entity
@ -150,23 +158,29 @@ public class MessageTransporterEffect implements IMessage, IMessageHandler<Messa
final Entity entity = world.getEntityByID(idEntities.get(indexEntity));
// energizing
// @TODO cylinder fade in + shower
if (entity != null) {
final Vector3 v3Position = v3EntityPositions.get(indexEntity).translate(ForgeDirection.DOWN, entity.getEyeHeight());
final Vector3 v3Target = v3Position.clone().translate(ForgeDirection.UP, entity.height);
final 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);
// check existing particle at position
final Vector3 v3Position = v3EntityPositions.get(indexEntity).clone().translate(ForgeDirection.DOWN, entity.getEyeHeight());
AbstractEntityFX effect = EntityFXRegistry.get(world, v3Position, 0.5D);
if (effect == null) {
// compute height with a margin
final Vector3 v3Target = v3Position.clone().translate(ForgeDirection.UP, entity.height + 0.5F);
// add particle to world
effect = new EntityFXEnergizing(world, v3Position, v3Target,
0.35F + 0.05F * world.rand.nextFloat(),
0.50F + 0.15F * world.rand.nextFloat(),
0.85F + 0.10F * world.rand.nextFloat(),
20, entity.width);
FMLClientHandler.instance().getClient().effectRenderer.addEffect(effect);
EntityFXRegistry.add(effect);
} else {
effect.refresh();
}
}
}
// cooldown
// @TODO cylinder fade out
}
@SideOnly(Side.CLIENT)
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) {
@ -182,6 +196,7 @@ public class MessageTransporterEffect implements IMessage, IMessageHandler<Messa
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);
final double yRange = aabb.maxY - aabb.minY;
// 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)));
@ -189,9 +204,9 @@ public class MessageTransporterEffect implements IMessage, IMessageHandler<Messa
// 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);
y = aabb.maxY - Math.pow(world.rand.nextDouble(), 3.0D) * yRange;
} else {
y = aabb.minY + Math.pow(world.rand.nextDouble(), 3.0D) * (aabb.maxY - aabb.minY);
y = aabb.minY + Math.pow(world.rand.nextDouble(), 3.0D) * yRange;
}
final Vector3 v3Position = new Vector3(
aabb.minX + world.rand.nextDouble() * (aabb.maxX - aabb.minX),
@ -224,6 +239,57 @@ public class MessageTransporterEffect implements IMessage, IMessageHandler<Messa
}
}
@SideOnly(Side.CLIENT)
private void spawnParticlesInTransporterRoom(final World world, final VectorI vTransporter, final boolean isFalling,
final float redBase, final float greenBase, final float blueBase,
final float redFactor, final float greenFactor, final float blueFactor) {
final TileEntity tileEntity = vTransporter.getTileEntity(world);
if (!(tileEntity instanceof TileEntityTransporterCore)) {
WarpDrive.logger.error(String.format("Missing transporter core at %s: %s",
vTransporter, tileEntity));
return;
}
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);
final double yRange = 2.5D;
final Collection<VectorI> vContainments = ((TileEntityTransporterCore) tileEntity).getContainments();
if (vContainments == null) {
WarpDrive.logger.error(String.format("No containments blocks identified for transporter core at %s",
vTransporter));
return;
}
for (final VectorI vContainment : vContainments) {
if (world.rand.nextFloat() < 0.85F) {
continue;
}
// start preferably from top or bottom side
double y;
if (isFalling) {
y = vContainment.y + 0.5D - Math.pow(world.rand.nextDouble(), 3.0D) * yRange;
} else {
y = vContainment.y + 0.5D + Math.pow(world.rand.nextDouble(), 3.0D) * yRange;
}
final Vector3 v3Position = new Vector3(
vContainment.x + world.rand.nextDouble(),
y,
vContainment.z + world.rand.nextDouble());
// add particle
final EntityFX effect = new EntityFXDot(world, v3Position,
v3Motion, v3Acceleration, 0.98D,
30);
effect.setRBGColorF(redBase + redFactor * world.rand.nextFloat(),
greenBase + greenFactor * world.rand.nextFloat(),
blueBase + blueFactor * world.rand.nextFloat() );
effect.setAlphaF(1.0F);
FMLClientHandler.instance().getClient().effectRenderer.addEffect(effect);
}
}
@Override
@SideOnly(Side.CLIENT)
public IMessage onMessage(MessageTransporterEffect messageSpawnParticle, MessageContext context) {

View file

@ -0,0 +1,20 @@
package cr0s.warpdrive.render;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.world.World;
@SideOnly(Side.CLIENT)
public abstract class AbstractEntityFX extends EntityFX {
public AbstractEntityFX(final World world, final double x, final double y, final double z,
final double xSpeed, final double ySpeed, final double zSpeed) {
super(world, x, y, z, xSpeed, ySpeed, zSpeed);
}
// extend current life
public void refresh() {
particleMaxAge = Math.max(particleMaxAge, particleAge + 20);
}
}

View file

@ -68,6 +68,11 @@ public class EntityFXDot extends EntityFX {
return layer;
}
@Override
public int getBrightnessForRender(float p_70070_1_) {
return 0xF00000;
}
@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) {

View file

@ -0,0 +1,222 @@
package cr0s.warpdrive.render;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import cr0s.warpdrive.data.Vector3;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import org.lwjgl.opengl.GL11;
@SideOnly(Side.CLIENT)
public class EntityFXEnergizing extends AbstractEntityFX {
private static final ResourceLocation TEXTURE = new ResourceLocation("warpdrive", "textures/particle/energy_grey.png");
private double radius;
private double length;
private final int countSteps;
private float rotYaw;
private float rotPitch;
private float prevYaw;
private float prevPitch;
public EntityFXEnergizing(final World world, final Vector3 position, final Vector3 target,
final float red, final float green, final float blue,
final int age, final float radius) {
super(world, position.x, position.y, position.z, 0.0D, 0.0D, 0.0D);
setRBGColorF(red, green, blue);
setSize(0.02F, 0.02F);
noClip = true;
motionX = 0.0D;
motionY = 0.0D;
motionZ = 0.0D;
this.radius = radius;
final float xd = (float) (posX - target.x);
final float yd = (float) (posY - target.y);
final float zd = (float) (posZ - target.z);
length = new Vector3(this).distanceTo(target);
final double lengthXZ = MathHelper.sqrt_double(xd * xd + zd * zd);
rotYaw = (float) (Math.atan2(xd, zd) * 180.0D / Math.PI);
rotPitch = (float) (Math.atan2(yd, lengthXZ) * 180.0D / Math.PI);
prevYaw = rotYaw;
prevPitch = rotPitch;
particleMaxAge = age;
// kill the particle if it's too far away
// reduce cylinder resolution when fancy graphic are disabled
final EntityLivingBase entityRender = Minecraft.getMinecraft().renderViewEntity;
int visibleDistance = 300;
if (!Minecraft.getMinecraft().gameSettings.fancyGraphics) {
visibleDistance = 100;
countSteps = 1;
} else {
countSteps = 6;
}
if (entityRender.getDistance(posX, posY, posZ) > visibleDistance) {
particleMaxAge = 0;
}
}
@Override
public void onUpdate() {
prevPosX = posX;
prevPosY = posY;
prevPosZ = posZ;
prevYaw = rotYaw;
prevPitch = rotPitch;
if (particleAge++ >= particleMaxAge) {
setDead();
}
}
@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) {
tessellator.draw();
GL11.glPushMatrix();
final double factorFadeIn = Math.min((particleAge + partialTick) / 20.0F, 1.0F);
// alpha starts at 50%, vanishing to 10% during last ticks
float alpha = 0.5F;
if (particleMaxAge - particleAge <= 4) {
alpha = 0.5F - (4 - (particleMaxAge - particleAge)) * 0.1F;
} else {
// add random flickering
final double timeAlpha = ((getEntityId() ^ 0x47C8) & 0xFFFF) + particleAge + partialTick + 0.0167D;
alpha += Math.pow(Math.sin(timeAlpha * 0.37D) + Math.sin(0.178D + timeAlpha * 0.17D), 2.0D) * 0.05D;
}
// texture clock is offset to de-synchronize particles
final double timeTexture =(getEntityId() & 0xFFFF) + particleAge + partialTick;
// repeated a pixel column, changing periodically, to animate the texture
final double uOffset = ((int) Math.floor(timeTexture * 0.5D) % 16) / 16.0D;
// add vertical noise
final double vOffset = Math.pow(Math.sin(timeTexture * 0.20D), 2.0D) * 0.005D;
// bind our texture, repeating on both axis
FMLClientHandler.instance().getClient().renderEngine.bindTexture(TEXTURE);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
// rendering on both sides
GL11.glDisable(GL11.GL_CULL_FACE);
// alpha transparency, don't update depth mask
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
GL11.glDepthMask(false);
// animated translation
final float xx = (float)(prevPosX + (posX - prevPosX) * partialTick - interpPosX);
final float yy = (float)(prevPosY + (posY - prevPosY) * partialTick - interpPosY);
final float zz = (float)(prevPosZ + (posZ - prevPosZ) * partialTick - interpPosZ);
GL11.glTranslated(xx, yy, zz);
// animated rotation
final float rotYaw = prevYaw + (this.rotYaw - prevYaw) * partialTick;
final float rotPitch = prevPitch + (this.rotPitch - prevPitch) * partialTick;
final float rotSpin = 0.0F;
GL11.glRotatef(90.0F, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(180.0F + rotYaw, 0.0F, 0.0F, -1.0F);
GL11.glRotatef(rotPitch, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(rotSpin, 0.0F, 1.0F, 0.0F);
// actual parameters
final double radius = this.radius * factorFadeIn;
final double yMin = length * (0.5D - factorFadeIn / 2.0D);
final double yMax = length * (0.5D + factorFadeIn / 2.0D);
final double uMin = uOffset;
final double uMax = uMin + 1.0D / 32.0D;
final double vMin = -1.0D + vOffset;
final double vMax = vMin + length * factorFadeIn;
// start drawing
tessellator.startDrawingQuads();
tessellator.setBrightness(200);
tessellator.setColorRGBA_F(particleRed, particleGreen, particleBlue, alpha);
// loop covering 45 deg, using symmetry to cover a full circle
final double angleMax = Math.PI / 4.0D;
final double angleStep = angleMax / countSteps;
double angle = 0.0D;
double cosPrev = radius * Math.cos(angle);
double sinPrev = radius * Math.sin(angle);
for (int indexStep = 1; indexStep <= countSteps; indexStep++) {
angle += angleStep;
final double cosNext = radius * Math.cos(angle);
final double sinNext = radius * Math.sin(angle);
// cos sin
tessellator.addVertexWithUV( cosPrev, yMax, sinPrev, uMax, vMax);
tessellator.addVertexWithUV( cosPrev, yMin, sinPrev, uMax, vMin);
tessellator.addVertexWithUV( cosNext, yMin, sinNext, uMin, vMin);
tessellator.addVertexWithUV( cosNext, yMax, sinNext, uMin, vMax);
tessellator.addVertexWithUV(-cosPrev, yMax, sinPrev, uMax, vMax);
tessellator.addVertexWithUV(-cosPrev, yMin, sinPrev, uMax, vMin);
tessellator.addVertexWithUV(-cosNext, yMin, sinNext, uMin, vMin);
tessellator.addVertexWithUV(-cosNext, yMax, sinNext, uMin, vMax);
tessellator.addVertexWithUV( cosPrev, yMax, -sinPrev, uMax, vMax);
tessellator.addVertexWithUV( cosPrev, yMin, -sinPrev, uMax, vMin);
tessellator.addVertexWithUV( cosNext, yMin, -sinNext, uMin, vMin);
tessellator.addVertexWithUV( cosNext, yMax, -sinNext, uMin, vMax);
tessellator.addVertexWithUV(-cosPrev, yMax, -sinPrev, uMax, vMax);
tessellator.addVertexWithUV(-cosPrev, yMin, -sinPrev, uMax, vMin);
tessellator.addVertexWithUV(-cosNext, yMin, -sinNext, uMin, vMin);
tessellator.addVertexWithUV(-cosNext, yMax, -sinNext, uMin, vMax);
// sin cos
tessellator.addVertexWithUV( sinPrev, yMax, cosPrev, uMax, vMax);
tessellator.addVertexWithUV( sinPrev, yMin, cosPrev, uMax, vMin);
tessellator.addVertexWithUV( sinNext, yMin, cosNext, uMin, vMin);
tessellator.addVertexWithUV( sinNext, yMax, cosNext, uMin, vMax);
tessellator.addVertexWithUV(-sinPrev, yMax, cosPrev, uMax, vMax);
tessellator.addVertexWithUV(-sinPrev, yMin, cosPrev, uMax, vMin);
tessellator.addVertexWithUV(-sinNext, yMin, cosNext, uMin, vMin);
tessellator.addVertexWithUV(-sinNext, yMax, cosNext, uMin, vMax);
tessellator.addVertexWithUV( sinPrev, yMax, -cosPrev, uMax, vMax);
tessellator.addVertexWithUV( sinPrev, yMin, -cosPrev, uMax, vMin);
tessellator.addVertexWithUV( sinNext, yMin, -cosNext, uMin, vMin);
tessellator.addVertexWithUV( sinNext, yMax, -cosNext, uMin, vMax);
tessellator.addVertexWithUV(-sinPrev, yMax, -cosPrev, uMax, vMax);
tessellator.addVertexWithUV(-sinPrev, yMin, -cosPrev, uMax, vMin);
tessellator.addVertexWithUV(-sinNext, yMin, -cosNext, uMin, vMin);
tessellator.addVertexWithUV(-sinNext, yMax, -cosNext, uMin, vMax);
cosPrev = cosNext;
sinPrev = sinNext;
}
// draw
tessellator.draw();
// restore OpenGL state
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glDepthMask(true);
GL11.glDisable(GL11.GL_BLEND);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glPopMatrix();
tessellator.startDrawingQuads();
FMLClientHandler.instance().getClient().renderEngine.bindTexture(new ResourceLocation("textures/particle/particles.png"));
}
}