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:
parent
3d3065aa62
commit
4ea5854430
8 changed files with 587 additions and 41 deletions
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
184
src/main/java/cr0s/warpdrive/data/EntityFXRegistry.java
Normal file
184
src/main/java/cr0s/warpdrive/data/EntityFXRegistry.java
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
20
src/main/java/cr0s/warpdrive/render/AbstractEntityFX.java
Normal file
20
src/main/java/cr0s/warpdrive/render/AbstractEntityFX.java
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
222
src/main/java/cr0s/warpdrive/render/EntityFXEnergizing.java
Normal file
222
src/main/java/cr0s/warpdrive/render/EntityFXEnergizing.java
Normal 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"));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue