From d835480ac5220f51347ef5e7c1fc9989a64c3eed Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 17 Feb 2018 17:06:14 +0100 Subject: [PATCH] Added transporter (redesign with fresh reimplementation) wip --- .../block/movement/BlockTransporter.java | 9 +- .../block/movement/TileEntityTransporter.java | 1219 +++++++++++------ .../cr0s/warpdrive/config/Dictionary.java | 68 +- .../warpdrive/config/WarpDriveConfig.java | 46 +- .../warpdrive/data/EnumTransporterState.java | 45 + .../java/cr0s/warpdrive/data/VectorI.java | 25 +- 6 files changed, 919 insertions(+), 493 deletions(-) create mode 100644 src/main/java/cr0s/warpdrive/data/EnumTransporterState.java diff --git a/src/main/java/cr0s/warpdrive/block/movement/BlockTransporter.java b/src/main/java/cr0s/warpdrive/block/movement/BlockTransporter.java index 26084dd3..a9eb471e 100644 --- a/src/main/java/cr0s/warpdrive/block/movement/BlockTransporter.java +++ b/src/main/java/cr0s/warpdrive/block/movement/BlockTransporter.java @@ -48,12 +48,9 @@ public class BlockTransporter extends BlockAbstractContainer { return iconBuffer[2]; } - - /** - * Called upon block activation (right click on the block.) - */ @Override - public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ) { + public boolean onBlockActivated(final World world, final int x, final int y, final int z, final EntityPlayer entityPlayer, + final int side, final float hitX, final float hitY, final float hitZ) { if (world.isRemote) { return false; } @@ -66,6 +63,6 @@ public class BlockTransporter extends BlockAbstractContainer { } } - return false; + return super.onBlockActivated(world, x, y, z, entityPlayer, side, hitX, hitY, hitZ); } } \ No newline at end of file diff --git a/src/main/java/cr0s/warpdrive/block/movement/TileEntityTransporter.java b/src/main/java/cr0s/warpdrive/block/movement/TileEntityTransporter.java index d84467a2..4e129918 100644 --- a/src/main/java/cr0s/warpdrive/block/movement/TileEntityTransporter.java +++ b/src/main/java/cr0s/warpdrive/block/movement/TileEntityTransporter.java @@ -2,43 +2,72 @@ package cr0s.warpdrive.block.movement; import cr0s.warpdrive.Commons; import cr0s.warpdrive.WarpDrive; +import cr0s.warpdrive.api.IBeamFrequency; import cr0s.warpdrive.block.TileEntityAbstractEnergy; +import cr0s.warpdrive.block.forcefield.BlockForceField; +import cr0s.warpdrive.block.forcefield.TileEntityForceField; +import cr0s.warpdrive.block.passive.BlockTransportBeacon; +import cr0s.warpdrive.config.Dictionary; import cr0s.warpdrive.config.WarpDriveConfig; -import cr0s.warpdrive.data.UpgradeType; +import cr0s.warpdrive.data.EnumComponentType; +import cr0s.warpdrive.data.EnumTransporterState; +import cr0s.warpdrive.data.ForceFieldSetup; import cr0s.warpdrive.data.Vector3; -import cr0s.warpdrive.item.ItemUpgrade; +import cr0s.warpdrive.data.VectorI; +import cr0s.warpdrive.network.PacketHandler; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.peripheral.IComputerAccess; import li.cil.oc.api.machine.Arguments; import li.cil.oc.api.machine.Callback; import li.cil.oc.api.machine.Context; -import java.util.ArrayList; +import java.lang.ref.WeakReference; import java.util.List; +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; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.EnumFacing; import net.minecraft.util.StatCollector; import cpw.mods.fml.common.Optional; import net.minecraftforge.common.util.ForgeDirection; -public class TileEntityTransporter extends TileEntityAbstractEnergy { - private double scanRange = 2; - - private int scanDist = 4; - - private double beaconEffect = 0; - private double powerBoost = 1; - private double baseLockStrength = -1; - private double lockStrengthMul = 1; - private boolean isLocked = false; - - private static final Vector3 centreOnMe = new Vector3(0.5D, 1.0D, 0.5D); - private Vector3 sourceVec = new Vector3(); - private Vector3 destVec = new Vector3(); +public class TileEntityTransporter extends TileEntityAbstractEnergy implements IBeamFrequency { + + // persistent properties + private int beamFrequency = -1; + private boolean isConnected = false; + private boolean isEnabled = true; + private boolean isLockRequested = false; + private boolean isEnergizeRequested = false; + private VectorI vSource_relative = new VectorI(); + private VectorI vDestination_relative = new VectorI(); + private double energyFactor = 1.0D; + private double lockStrengthActual = 0.0D; + private int tickCooldown = 0; + private EnumTransporterState transporterState = EnumTransporterState.DISABLED; + + // computed properties + private int energyCostForTransfer = 0; + private double lockStrengthOptimal = -1.0D; + private double lockStrengthSpeed = 0.0D; + private boolean isJammed = false; + private VectorI vSource_absolute = new VectorI(); + private VectorI vDestination_absolute = new VectorI(); + private WeakReference weakEntity = null; + private Vector3 v3EntityPosition = null; + private int tickEnergizing = 0; public TileEntityTransporter() { super(); @@ -48,483 +77,801 @@ public class TileEntityTransporter extends TileEntityAbstractEnergy { peripheralName = "warpdriveTransporter"; addMethods(new String[] { - "source", - "dest", - "lock", - "release", - "lockStrength", - "energize", - "powerBoost", - "getEnergyRequired", - "upgrades" + "beamFrequency", + "enable", + "source", + "destination", + "lock", + "energyFactor", + "getLockStrength", + "getEnergyRequired", + "energize", + "upgrades" }); - setUpgradeMaxCount(ItemUpgrade.getItemStack(UpgradeType.Energy), 2); - setUpgradeMaxCount(ItemUpgrade.getItemStack(UpgradeType.Power), 4); - setUpgradeMaxCount(ItemUpgrade.getItemStack(UpgradeType.Range), 4); + setUpgradeMaxCount(EnumComponentType.ENDER_CRYSTAL, WarpDriveConfig.TRANSPORTER_RANGE_UPGRADE_MAX_QUANTITY); + setUpgradeMaxCount(EnumComponentType.CAPACITIVE_CRYSTAL, WarpDriveConfig.TRANSPORTER_ENERGY_STORED_UPGRADE_MAX_QUANTITY); + setUpgradeMaxCount(EnumComponentType.EMERALD_CRYSTAL, WarpDriveConfig.TRANSPORTER_LOCKING_UPGRADE_MAX_QUANTITY); } - + + @Override + protected void onFirstUpdateTick() { + super.onFirstUpdateTick(); + updateParameters(); + } + @Override public void updateEntity() { super.updateEntity(); - - if (isLocked) { - if (lockStrengthMul > 0.8) { - lockStrengthMul *= 0.995; - } else { - lockStrengthMul *= 0.98; + + if (worldObj.isRemote) { + return; + } + + // Frequency is not set + final boolean new_isConnected = beamFrequency > 0 && beamFrequency <= IBeamFrequency.BEAM_FREQUENCY_MAX; + if (isConnected != new_isConnected) { + isConnected = new_isConnected; + markDirty(); + } + + // global override + boolean isActive = isEnabled; + if (!isEnabled) { + transporterState = EnumTransporterState.DISABLED; + } else { + // energy consumption + final int energyRequired = getEnergyRequired(transporterState); + if (energyRequired > 0) { + final boolean isPowered = energy_consume(energyRequired, false); + if (!isPowered) { + transporterState = EnumTransporterState.IDLE; + isActive = false; + } } + + // apply cooldown when enabled + if (tickCooldown > 0) { + tickCooldown--; + } else { + tickCooldown = 0; + } + + // lock strength always decays + lockStrengthActual *= WarpDriveConfig.TRANSPORTER_LOCKING_STRENGTH_FACTOR_PER_TICK; + + // lock strength is capped at optimal, increasing when powered + // a slight overshoot is added to force convergence + final double overshoot = 0.01D; + if (isActive) { + lockStrengthActual = Math.min(lockStrengthOptimal, + lockStrengthActual + lockStrengthSpeed * (lockStrengthOptimal - lockStrengthActual + overshoot)); + } else { + lockStrengthActual = Math.max(0.0D, lockStrengthActual); + } + } + + // state feedback + if (isActive && isJammed) { + PacketHandler.sendSpawnParticlePacket(worldObj, "jammed", (byte) 5, new Vector3(this).translate(0.5F), + new Vector3(0.0D, 0.0D, 0.0D), + 1.0F, 1.0F, 1.0F, + 1.0F, 1.0F, 1.0F, + 32); + } + + switch (transporterState) { + case DISABLED: + isLockRequested = false; + isEnergizeRequested = false; + lockStrengthActual = 0.0D; + if (isActive) { + transporterState = EnumTransporterState.IDLE; + } + break; + + case IDLE: + if (isLockRequested) { + // initial validation before starting acquisition + updateParameters(); + + if (!isJammed) { + transporterState = EnumTransporterState.ACQUIRING; + } else { + tickCooldown = WarpDriveConfig.TRANSPORTER_JAMMED_COOLDOWN_TICKS; + } + } + break; + + case ACQUIRING: + if (!isLockRequested) { + transporterState = EnumTransporterState.IDLE; + + } else if (isEnergizeRequested) { + // final validation in case environment has changed + updateParameters(); + + if (isJammed) { + tickCooldown += WarpDriveConfig.TRANSPORTER_JAMMED_COOLDOWN_TICKS; + transporterState = EnumTransporterState.IDLE; + } else { + // reset entity to grab + weakEntity = null; + v3EntityPosition = null; + + // consume energy + final int energyRequired = getEnergyRequired(transporterState); + if (energy_consume(energyRequired, false)) { + tickEnergizing = WarpDriveConfig.TRANSPORTER_TRANSFER_WARMUP_TICKS; + transporterState = EnumTransporterState.ENERGIZING; + } + } + } + break; + + case ENERGIZING: + // get entity + updateEntityToTransfer(); + if (weakEntity == null) { + transporterState = EnumTransporterState.ACQUIRING; + isEnergizeRequested = false; + tickCooldown = WarpDriveConfig.TRANSPORTER_TRANSFER_COOLDOWN_TICKS; + lockStrengthActual = Math.max(0.0D, lockStrengthActual - WarpDriveConfig.TRANSPORTER_TRANSFER_LOCKING_LOST); + break; + } + final Entity entity = weakEntity.get(); + if (entity == null) { + // bad state + WarpDrive.logger.info(String.format("%s Entity went missing, retrying next tick...", + this)); + break; + } + + // warm-up + if (tickEnergizing > 0) { + tickEnergizing--; + break; + } + + // check lock strength + if ( lockStrengthActual < 1.0D + && worldObj.rand.nextDouble() > lockStrengthActual ) { + if (WarpDriveConfig.LOGGING_TRANSPORTER) { + WarpDrive.logger.info(String.format("%s Insufficient lock strength %.3f", this, lockStrengthActual)); + } + applyTeleportationDamages(false, entity, lockStrengthActual); + tickCooldown = WarpDriveConfig.TRANSPORTER_TRANSFER_COOLDOWN_TICKS; + break; + } + + // teleport + final Vector3 v3Target = new Vector3( + vDestination_absolute.x + entity.posX - vSource_absolute.x, + vDestination_absolute.y + entity.posY - vSource_absolute.y, + vDestination_absolute.z + entity.posZ - vSource_absolute.z); + if (WarpDriveConfig.LOGGING_TRANSPORTER) { + WarpDrive.logger.info(String.format("%s Teleporting entity %s to %s", + this, entity, v3Target)); + } + Commons.moveEntity(entity, worldObj, v3Target); + applyTeleportationDamages(false, entity, lockStrengthActual); + tickCooldown = WarpDriveConfig.TRANSPORTER_TRANSFER_COOLDOWN_TICKS; + lockStrengthActual = Math.max(0.0D, lockStrengthActual - WarpDriveConfig.TRANSPORTER_TRANSFER_LOCKING_LOST); + break; + + default: + transporterState = EnumTransporterState.DISABLED; + break; } } @Override - public String getStatus() { - return super.getStatus() - + "\n" + StatCollector.translateToLocalFormatted("warpdrive.transporter.status", - sourceVec.x, sourceVec.y, sourceVec.z, - destVec.x, destVec.y, destVec.z); + public String getStatusHeader() { + return super.getStatusHeader() + + "\n" + StatCollector.translateToLocalFormatted("warpdrive.transporter.status", + vSource_absolute.x, vSource_absolute.y, vSource_absolute.z, + vDestination_absolute.x, vDestination_absolute.y, vDestination_absolute.z); } + @Override + public int getBeamFrequency() { + return beamFrequency; + } + @Override + public void setBeamFrequency(final int beamFrequency) { + this.beamFrequency = beamFrequency; + } + private class FocusValues { + public int countRangeUpgrades; + public double strength; + public double speed; + } + + private void updateParameters() { + isJammed = false; + + // check minimum range + if (vSource_relative.subtract(vDestination_relative).getMagnitudeSquared() < 2) { + isJammed = true; + return; + } + + // compute absolute coordinates + vSource_absolute = new VectorI(this).translate(EnumFacing.UP).translate(vSource_relative); + vDestination_absolute = new VectorI(this).translate(EnumFacing.UP).translate(vDestination_relative); + + // compute range as max distance between transporter, source and destination + final int rangeSource2 = vSource_relative.getMagnitudeSquared(); + final int rangeDestination2 = vDestination_relative.getMagnitudeSquared(); + final int rangeDelta2 = vSource_relative.subtract(vDestination_relative).getMagnitudeSquared(); + final int rangeActual = (int) Math.ceil(Math.sqrt(Math.max(rangeSource2, Math.max(rangeDestination2, rangeDelta2)))); + + // compute energy cost from range + energyCostForTransfer = (int) Math.ceil(Math.max(0, Commons.interpolate( + 0, + WarpDriveConfig.TRANSPORTER_TRANSFER_ENERGY_AT_MIN_RANGE, + WarpDriveConfig.TRANSPORTER_RANGE_BASE_BLOCKS, + WarpDriveConfig.TRANSPORTER_TRANSFER_ENERGY_AT_MAX_RANGE, + rangeActual))); + + // compute focalization bonuses + final FocusValues focusValuesSource = getFocusValueAtCoordinates(vSource_absolute); + final FocusValues focusValuesDestination = getFocusValueAtCoordinates(vDestination_absolute); + final double focusBoost = energyFactor + * WarpDriveConfig.TRANSPORTER_LOCKING_STRENGTH_BONUS_AT_MAX_ENERGY_FACTOR + / WarpDriveConfig.TRANSPORTER_TRANSFER_ENERGY_FACTOR_MAX; + lockStrengthOptimal = (focusValuesSource.strength + focusValuesDestination.strength) / 2.0D + focusBoost; + lockStrengthSpeed = (focusValuesSource.speed + focusValuesDestination.speed) / 2.0D + / WarpDriveConfig.TRANSPORTER_LOCKING_SPEED_OPTIMAL_TICKS; + + final int rangeMax = WarpDriveConfig.TRANSPORTER_RANGE_BASE_BLOCKS + + WarpDriveConfig.TRANSPORTER_RANGE_UPGRADE_BLOCKS * focusValuesSource.countRangeUpgrades + + WarpDriveConfig.TRANSPORTER_RANGE_UPGRADE_BLOCKS * focusValuesDestination.countRangeUpgrades; + + if (WarpDriveConfig.LOGGING_TRANSPORTER) { + WarpDrive.logger.info(String.format("Transporter parameters at (%d %d %d) are range (actual %d max %d) lockStrength (optimal %.3f speed %.3f)", + xCoord, yCoord, zCoord, + rangeActual, rangeMax, + lockStrengthOptimal, lockStrengthSpeed)); + } + + if (rangeActual > rangeMax) { + isJammed = true; + return; + } + + isJammed |= isJammedTrajectory(vSource_absolute); + isJammed |= isJammedTrajectory(vDestination_absolute); + } + + private FocusValues getFocusValueAtCoordinates(final VectorI vAbsolute) { + // scan the area + int countBeacons = 0; + int countTransporters = 0; + int sumRangeUpgrades = 0; + int sumFocusUpgrades = 0; + + final int xMin = vAbsolute.x - WarpDriveConfig.TRANSPORTER_FOCUS_SEARCH_RADIUS_BLOCKS; + final int xMax = vAbsolute.x + WarpDriveConfig.TRANSPORTER_FOCUS_SEARCH_RADIUS_BLOCKS; + final int yMin = vAbsolute.y - WarpDriveConfig.TRANSPORTER_FOCUS_SEARCH_RADIUS_BLOCKS; + final int yMax = vAbsolute.y + WarpDriveConfig.TRANSPORTER_FOCUS_SEARCH_RADIUS_BLOCKS; + final int zMin = vAbsolute.z - WarpDriveConfig.TRANSPORTER_FOCUS_SEARCH_RADIUS_BLOCKS; + final int zMax = vAbsolute.z + WarpDriveConfig.TRANSPORTER_FOCUS_SEARCH_RADIUS_BLOCKS; + + for (int x = xMin; x <= xMax; x++) { + for (int y = yMin; y <= yMax; y++) { + if (y < 0 || y > 254) { + continue; + } + + for (int z = zMin; z <= zMax; z++) { + final Block block = worldObj.getBlock(x, y, z); + if (block instanceof BlockTransportBeacon) { + // count active beacons + final boolean isActive = worldObj.getBlockMetadata(x, y, z) == 0; + if (isActive) { + countBeacons++; + } + + } else if (block instanceof BlockTransporter) { + // count active transporters + final TileEntity tileEntity = worldObj.getTileEntity(x, y, z); + if (tileEntity instanceof TileEntityTransporter) { + countTransporters++; + // remember upgrades + sumRangeUpgrades += ((TileEntityTransporter) tileEntity).getUpgradeCount(EnumComponentType.ENDER_CRYSTAL); + sumFocusUpgrades += ((TileEntityTransporter) tileEntity).getUpgradeCount(EnumComponentType.EMERALD_CRYSTAL); + } + } + } + } + } + + // compute values + final FocusValues result = new FocusValues(); + if (countTransporters > 0) { + final int countFocusUpgrades = sumFocusUpgrades / countTransporters; + result.countRangeUpgrades = sumRangeUpgrades / countTransporters; + result.speed = WarpDriveConfig.TRANSPORTER_LOCKING_SPEED_AT_TRANSPORTER + countFocusUpgrades * WarpDriveConfig.TRANSPORTER_LOCKING_STRENGTH_UPGRADE; + result.strength = WarpDriveConfig.TRANSPORTER_LOCKING_STRENGTH_AT_TRANSPORTER + countFocusUpgrades * WarpDriveConfig.TRANSPORTER_LOCKING_SPEED_UPGRADE; + } else if (countBeacons > 0) { + result.countRangeUpgrades = 0; + result.speed = WarpDriveConfig.TRANSPORTER_LOCKING_SPEED_AT_FOCUS; + result.strength = WarpDriveConfig.TRANSPORTER_LOCKING_STRENGTH_AT_FOCUS; + } else { + result.countRangeUpgrades = 0; + result.speed = WarpDriveConfig.TRANSPORTER_LOCKING_SPEED_IN_WILDERNESS; + result.strength = WarpDriveConfig.TRANSPORTER_LOCKING_STRENGTH_IN_WILDERNESS; + } + + if (WarpDriveConfig.LOGGING_TRANSPORTER) { + WarpDrive.logger.info(String.format("Transporter getFocusValueAtCoordinates %s gives range %d speed %.3f strength %.3f", + vAbsolute, result.countRangeUpgrades, result.speed, result.strength)); + } + + return result; + } + + private boolean isJammedTrajectory(final VectorI vAbsolute) { + final VectorI vPath = vAbsolute.clone().translateBack(new VectorI(this)); + final int length = (int) Math.ceil(3 * Math.sqrt(vPath.getMagnitudeSquared())); + final Vector3 v3Delta = new Vector3(vPath.x / (double) length, vPath.y / (double) length, vPath.z / (double) length); + + // scan along given trajectory + final Vector3 v3Current = new Vector3(this).translate(0.5D); + final VectorI vCurrent = new VectorI(this); + final VectorI vPrevious = vCurrent.clone(); + for (int step = 0; step < length; step++) { + v3Current.translate(v3Delta); + vCurrent.x = (int) Math.round(v3Current.x); + vCurrent.y = (int) Math.round(v3Current.y); + vCurrent.z = (int) Math.round(v3Current.z); + // skip repeating coordinates + if (vCurrent.equals(vPrevious)) { + continue; + } + if (isJammedCoordinate(vCurrent)) return true; + + // remember this coordinates + vPrevious.x = vCurrent.x; + vPrevious.y = vCurrent.y; + vPrevious.z = vCurrent.z; + } + return false; + } + private boolean isJammedCoordinate(final VectorI vCurrent) { + // check block blacklist for blinking + final Block block = vCurrent.getBlock(worldObj); + if (Dictionary.BLOCKS_NOBLINK.contains(block)) { + return true; + } + + // allow passing through force fields with same beam frequency + if (block instanceof BlockForceField) { + final TileEntity tileEntity = vCurrent.getTileEntity(worldObj); + if (tileEntity instanceof TileEntityForceField) { + final ForceFieldSetup forceFieldSetup = ((TileEntityForceField) tileEntity).getForceFieldSetup(); + if (forceFieldSetup == null) { + // projector not loaded yet, consider it jammed by default + WarpDrive.logger.warn(String.format("%s projector not loaded at %s", this, tileEntity)); + return true; + } + if (forceFieldSetup.beamFrequency != beamFrequency) { + // jammed by invalid beam frequency + if (WarpDriveConfig.LOGGING_TRANSPORTER) { + WarpDrive.logger.info(String.format("%s signal jammed by %s", this, tileEntity)); + } + return true; + } + } + } + return false; + } + + private int getEnergyRequired(final EnumTransporterState transporterState) { + switch (transporterState) { + case DISABLED: + return 0; + + case IDLE: + return 0; + + case ACQUIRING: + return (int) Math.ceil(WarpDriveConfig.TRANSPORTER_ACQUIRING_ENERGY_FACTOR * energyCostForTransfer * energyFactor); + + case ENERGIZING: + return (int) Math.ceil(energyCostForTransfer * energyFactor); + + default: + return 0; + } + } + + private void applyTeleportationDamages(final boolean isPreTeleportation, final Entity entity, final double strength) { + // skip invulnerable + if ( entity.isDead + || entity.isEntityInvulnerable() ) { + return; + } + + // add bonus if transport was successful + final double strengthToUse = isPreTeleportation ? strength : Math.random() * WarpDriveConfig.TRANSPORTER_TRANSFER_SUCCESS_LOCK_BONUS + strength; + + final double strengthSafe = 0.95D; + final double strengthMaxDamage = 0.65D; + final double strengthNoDamage = 0.10D; + if (strengthToUse > strengthSafe) { + return; + } + final double damageNormalized = (strengthSafe - strengthToUse) / (strengthSafe - strengthMaxDamage); + final double damageMax = isPreTeleportation ? WarpDriveConfig.TRANSPORTER_TRANSFER_FAILURE_MAX_DAMAGE : WarpDriveConfig.TRANSPORTER_TRANSFER_SUCCESS_MAX_DAMAGE ; + // final double damageAmount = Commons.clamp(1.0D, 1000.0D, Math.pow(10.0D, 10.0D * damageNormalized)); + final double damageAmount = Commons.clamp(1.0D, 1000.0D, damageMax * damageNormalized); + + if (WarpDriveConfig.LOGGING_TRANSPORTER) { + WarpDrive.logger.info(String.format("%s Applying teleportation damages %s transport with %.3f strength, %.2f damage towards %s", + this, + isPreTeleportation ? "pre" : "post", + strengthToUse, + damageAmount, + entity)); + } + + if (entity instanceof EntityLivingBase) { + entity.attackEntityFrom(WarpDrive.damageTeleportation, (float) damageAmount); + final boolean isCreative = (entity instanceof EntityPlayer) && ((EntityPlayer) entity).capabilities.isCreativeMode; + if (!isCreative) { + if (isPreTeleportation) { + // add 1s nausea + ((EntityLivingBase) entity).addPotionEffect(new PotionEffect(Potion.confusion.id, 20, 0, true)); + } else { + // add 5s poison + ((EntityLivingBase) entity).addPotionEffect(new PotionEffect(Potion.poison.id, 5 * 20, 0, true)); + } + } + } else if (strengthToUse > strengthNoDamage) { + // add lava blade at location + final VectorI vPosition = new VectorI(entity); + if (worldObj.isAirBlock(vPosition.x, vPosition.y, vPosition.z)) { + worldObj.setBlock(vPosition.x, vPosition.y, vPosition.z, Blocks.flowing_lava, 6, 2); + } + } + } + + private void updateEntityToTransfer() { + // validate existing entity + if (weakEntity != null) { + final Entity entity = weakEntity.get(); + if ( entity == null + || entity.isDead ) { + // no longer valid => search a new one, no energy lost + weakEntity = null; + v3EntityPosition = null; + + } else { + final double tolerance2 = WarpDriveConfig.TRANSPORTER_ENTITY_MOVEMENT_TOLERANCE_BLOCKS + * WarpDriveConfig.TRANSPORTER_ENTITY_MOVEMENT_TOLERANCE_BLOCKS; + final double distance2 = v3EntityPosition.distanceTo_square(entity); + if (distance2 > tolerance2) { + // entity moved too much => damage existing one, grab another, lose energy + final double strength = Math.sqrt(distance2) / Math.sqrt(tolerance2) / 2.0D; + applyTeleportationDamages(true, entity, strength); + weakEntity = null; + v3EntityPosition = null; + transporterState = EnumTransporterState.ACQUIRING; + } + } + } + + // grab another entity on first tick or if bad things happened + if (weakEntity == null) { + final Entity entityClosest = getClosestEntityAtSource(); + if (entityClosest == null) { + return; + } + weakEntity = new WeakReference<>(entityClosest); + v3EntityPosition = new Vector3(entityClosest); + } + } + + private Entity getClosestEntityAtSource() { + final AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox( + vSource_absolute.x - WarpDriveConfig.TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS, + vSource_absolute.y - 1.0D, + vSource_absolute.z - WarpDriveConfig.TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS, + vSource_absolute.x + WarpDriveConfig.TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS + 1.0D, + vSource_absolute.y + 2.0D, + vSource_absolute.z + WarpDriveConfig.TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS + 1.0D); + + Entity entityClosest = null; + double rangeClosest2 = Integer.MAX_VALUE; + + final List entities = worldObj.getEntitiesWithinAABBExcludingEntity(null, aabb); + for (final Object object : entities) { + if (!(object instanceof Entity)) { + continue; + } + + final Entity entity = (Entity) object; + + // skip particle effects + if (entity instanceof EntityFX) { + continue; + } + + // skip blacklisted ids + final String entityId = EntityList.getEntityString(entity); + if (Dictionary.ENTITIES_LEFTBEHIND.contains(entityId)) { + if (WarpDriveConfig.LOGGING_TRANSPORTER) { + WarpDrive.logger.info(String.format("Entity is not valid for transportation (id %s) %s", + entityId, entity)); + } + continue; + } + + // keep closest one + final double range2 = vSource_absolute.distance2To(entity); + if (range2 < rangeClosest2) { + rangeClosest2 = range2; + entityClosest = (Entity) object; + } + } + + return entityClosest; + } + + @Override + public int energy_getMaxStorage() { + final int energyUpgrades = getUpgradeCount(EnumComponentType.CAPACITIVE_CRYSTAL); + return WarpDriveConfig.TRANSPORTER_MAX_ENERGY_STORED + energyUpgrades * WarpDriveConfig.TRANSPORTER_ENERGY_STORED_UPGRADE_BONUS; + } + + @Override + public boolean energy_canInput(final ForgeDirection from) { + return from != ForgeDirection.UP; + } + + @Override + public void writeToNBT(final NBTTagCompound tagCompound) { + super.writeToNBT(tagCompound); + + tagCompound.setInteger(IBeamFrequency.BEAM_FREQUENCY_TAG, beamFrequency); + tagCompound.setBoolean("isConnected", isConnected); + tagCompound.setBoolean("isEnabled", isEnabled); + tagCompound.setBoolean("isLockRequested", isLockRequested); + tagCompound.setBoolean("isEnergizeRequested", isEnergizeRequested); + + tagCompound.setTag("source", vSource_relative.writeToNBT(new NBTTagCompound())); + tagCompound.setTag("destination", vDestination_relative.writeToNBT(new NBTTagCompound())); + tagCompound.setDouble("energyFactor", energyFactor); + tagCompound.setDouble("lockStrengthActual", lockStrengthActual); + tagCompound.setInteger("tickCooldown", tickCooldown); + + tagCompound.setString("state", transporterState.toString()); + } + + @Override + public void readFromNBT(final NBTTagCompound tagCompound) { + super.readFromNBT(tagCompound); + + beamFrequency = tagCompound.getInteger(IBeamFrequency.BEAM_FREQUENCY_TAG); + isConnected = tagCompound.getBoolean("isConnected"); + isEnabled = tagCompound.getBoolean("isEnabled"); + isLockRequested = tagCompound.getBoolean("isLockRequested"); + isEnergizeRequested = tagCompound.getBoolean("isEnergizeRequested"); + + final NBTBase tagSource = tagCompound.getTag("source"); + if (tagSource instanceof NBTTagCompound) { + vSource_relative.readFromNBT((NBTTagCompound) tagSource); + } + + final NBTBase tagDestination = tagCompound.getTag("destination"); + if (tagDestination instanceof NBTTagCompound) { + vDestination_relative.readFromNBT((NBTTagCompound) tagDestination); + } + + energyFactor = tagCompound.getDouble("energyFactor"); + lockStrengthActual = tagCompound.getDouble("lockStrengthActual"); + tickCooldown = tagCompound.getInteger("tickCooldown"); + + try { + transporterState = EnumTransporterState.valueOf(tagCompound.getString("state")); + } catch (IllegalArgumentException exception) { + transporterState = EnumTransporterState.DISABLED; + } + } + + // Common OC/CC methods + public Boolean[] enable(Object[] arguments) { + if (arguments.length == 1) { + isEnabled = Commons.toBool(arguments[0]); + markDirty(); + } + return new Boolean[] { isEnabled }; + } + + public Integer[] source(final Object[] arguments) { + final VectorI vNew = getVectorI(vSource_relative, arguments); + if (!vNew.equals(vSource_relative)) { + isLockRequested = false; + isEnergizeRequested = false; + vSource_relative = vNew; + } + return new Integer[] { vSource_relative.x, vSource_relative.y, vSource_relative.z }; + } + + public Integer[] destination(final Object[] arguments) { + final VectorI vNew = getVectorI(vDestination_relative, arguments); + if (!vNew.equals(vDestination_relative)) { + isLockRequested = false; + isEnergizeRequested = false; + vDestination_relative = vNew; + } + return new Integer[] { vDestination_relative.x, vDestination_relative.y, vDestination_relative.z }; + } + + private VectorI getVectorI(final VectorI vDefault, final Object[] arguments) { + final VectorI vResult = vDefault.clone(); + + try { + if (arguments.length == 3) { + vResult.x = Commons.toInt(arguments[0]); + vResult.y = Commons.toInt(arguments[1]); + vResult.z = Commons.toInt(arguments[2]); + } else if (arguments.length == 1) { + vResult.x = 0; + vResult.y = 0; + vResult.z = 0; + } + } catch (NumberFormatException e) { + // ignore + } + return vResult; + } + + private Boolean[] lock(final Object[] arguments) { + if (arguments.length == 1) { + isLockRequested = Commons.toBool(arguments[0]); + markDirty(); + } + return new Boolean[] { isLockRequested }; + } + + private Double[] energyFactor(final Object[] arguments) { + try { + if (arguments.length >= 1) { + energyFactor = Commons.clamp(1, WarpDriveConfig.TRANSPORTER_TRANSFER_ENERGY_FACTOR_MAX, Commons.toDouble(arguments[0])); + } + } catch (NumberFormatException exception) { + // ignore + } + + return new Double[] { energyFactor }; + } + + private Double[] getLockStrength() { + return new Double[] { lockStrengthActual }; + } + + private Integer[] getEnergyRequired() { + return new Integer[] { getEnergyRequired(EnumTransporterState.ENERGIZING) }; + } + + private Boolean[] energize(final Object[] arguments) { + if (arguments.length == 1) { + isEnergizeRequested = Commons.toBool(arguments[0]); + markDirty(); + } + return new Boolean[] { isEnergizeRequested }; + } + + // OpenComputers callback methods + @Callback + @Optional.Method(modid = "OpenComputers") + public Object[] beamFrequency(Context context, Arguments arguments) { + if (arguments.count() == 1) { + setBeamFrequency(arguments.checkInteger(0)); + } + return new Integer[] { beamFrequency }; + } + + @Callback + @Optional.Method(modid = "OpenComputers") + public Object[] enable(Context context, Arguments arguments) { + return enable(argumentsOCtoCC(arguments)); + } - // OpenComputer callback methods - // ------------------------------------------------------------------------------------------ @Callback @Optional.Method(modid = "OpenComputers") public Object[] source(Context context, Arguments arguments) { - return setVec3(true, argumentsOCtoCC(arguments)); + return source(argumentsOCtoCC(arguments)); } @Callback @Optional.Method(modid = "OpenComputers") - public Object[] dest(Context context, Arguments arguments) { - return setVec3(false, argumentsOCtoCC(arguments)); + public Object[] destination(Context context, Arguments arguments) { + return destination(argumentsOCtoCC(arguments)); } @Callback @Optional.Method(modid = "OpenComputers") public Object[] lock(Context context, Arguments arguments) { - return new Object[] { - lock(sourceVec, destVec) - }; + return lock(argumentsOCtoCC(arguments)); } @Callback @Optional.Method(modid = "OpenComputers") - public Object[] release(Context context, Arguments arguments) { - unlock(); - return new Object[] { - null - }; + public Object[] energyFactor(Context context, Arguments arguments) { + return energyFactor(argumentsOCtoCC(arguments)); } @Callback @Optional.Method(modid = "OpenComputers") - public Object[] lockStrength(Context context, Arguments arguments) { - return new Object[] { - getLockStrength() - }; - } - - @Callback - @Optional.Method(modid = "OpenComputers") - public Object[] energize(Context context, Arguments arguments) { - return new Object[] { - energize() - }; - } - - @Callback - @Optional.Method(modid = "OpenComputers") - public Object[] powerBoost(Context context, Arguments arguments) { - return new Object[] { - powerBoost(argumentsOCtoCC(arguments)) - }; + public Object[] getLockStrength(Context context, Arguments arguments) { + return getLockStrength(); } @Callback @Optional.Method(modid = "OpenComputers") public Object[] getEnergyRequired(Context context, Arguments arguments) { - return new Object[] { - getEnergyRequired() - }; + return getEnergyRequired(); } @Callback @Optional.Method(modid = "OpenComputers") - public Object[] help(Context context, Arguments arguments) { - return new Object[] { - helpStr(argumentsOCtoCC(arguments)) - }; + public Object[] energize(Context context, Arguments arguments) { + return energize(argumentsOCtoCC(arguments)); } - - - // ComputerCraft IPeripheral methods implementation - private static String helpStr(Object[] function) { - if (function != null && function.length > 0) { - String methodName = function[0].toString().toLowerCase(); - switch (methodName) { - case "source": - if (WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS) { - return "source(x,y,z): sets the coordinates (relative to the transporter) to teleport from\ndest(): returns the relative x,y,z coordinates of the source"; - } else { - return "source(x,y,z): sets the absolute coordinates to teleport from\ndest(): returns the x,y,z coordinates of the source"; - } - case "dest": - if (WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS) { - return "dest(x,y,z): sets the coordinates (relative to the transporter) to teleport to\ndest(): returns the relative x,y,z coordinates of the destination"; - } else { - return "dest(x,y,z): sets the absolute coordinates to teleport to\ndest(): returns the x,y,z coordinates of the destination"; - } - case "lock": - return "lock(): locks the source and dest coordinates in and returns the lock strength (float)"; - case "release": - return "release(): releases the current lock"; - case "lockstrength": - return "lockStrength(): returns the current lock strength (float)"; - case "energize": - return "energize(): attempts to teleport all entities at source to dest. Returns the number of entities transported (-1 indicates a problem)."; - case "powerboost": - return "powerBoost(boostAmount): sets the level of power to use (1 being default), returns the level of power\npowerBoost(): returns the level of power"; - case "getEnergyRequired": - return "getEnergyRequired(): returns the amount of energy it will take for a single entity to transport with the current settings"; - } - } - return null; - } - - private Object[] setVec3(boolean src, Object... arguments) { - Vector3 vec = src ? sourceVec : destVec; - - if (vec == null) { - Vector3 sV = WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS ? new Vector3(this) : new Vector3(0, 0, 0); - if (src) - sourceVec = sV; - else - destVec = sV; - vec = src ? sourceVec : destVec; - } - - try { - if (arguments.length >= 3) { - unlock(); - vec.x = Commons.toDouble(arguments[0]); - vec.y = Commons.toDouble(arguments[1]); - vec.z = Commons.toDouble(arguments[2]); - } else if (arguments.length == 1) { - unlock(); - if (WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS) { - vec.x = centreOnMe.x; - vec.y = centreOnMe.y; - vec.z = centreOnMe.z; - } else { - vec.x = xCoord + centreOnMe.x; - vec.y = yCoord + centreOnMe.y; - vec.z = zCoord + centreOnMe.z; - } - } - } catch (NumberFormatException e) { - return setVec3(src, "this"); - } - return new Object[] { vec.x, vec.y, vec.z }; - } - + // ComputerCraft IPeripheral methods @Override @Optional.Method(modid = "ComputerCraft") public Object[] callMethod(IComputerAccess computer, ILuaContext context, int method, Object[] arguments) { final String methodName = getMethodName(method); switch (methodName) { - case "source": - return setVec3(true, arguments); - - case "dest": - return setVec3(false, arguments); - - case "lock": - return new Object[]{lock(sourceVec, destVec)}; - - case "release": - unlock(); - return null; - - case "lockStrength": - return new Object[]{getLockStrength()}; - - case "energize": - return new Object[]{energize()}; - - case "powerBoost": - return new Object[]{powerBoost(arguments)}; - - case "getEnergyRequired": - return new Object[]{getEnergyRequired()}; - - case "help": - return new Object[]{helpStr(arguments)}; + case "beamFrequency": + if (arguments.length == 1) { + setBeamFrequency(Commons.toInt(arguments[0])); + } + return new Integer[] { beamFrequency }; + + case "enable": + return enable(arguments); + + case "source": + return source(arguments); + + case "destination": + return destination(arguments); + + case "lock": + return lock(arguments); + + case "energyFactor": + return energyFactor(arguments); + + case "getLockStrength": + return getLockStrength(); + + case "getEnergyRequired": + return getEnergyRequired(); + + case "energize": + return energize(arguments); } return super.callMethod(computer, context, method, arguments); } - - private Integer getEnergyRequired() { - if (sourceVec != null && destVec != null) { - return (int) Math.ceil(Math.pow(3, powerBoost - 1) * WarpDriveConfig.TRANSPORTER_ENERGY_PER_BLOCK * sourceVec.distanceTo(destVec)); - } - return null; - } - private double powerBoost(Object[] arguments) { - try { - if (arguments.length >= 1) { - powerBoost = Commons.clamp(1, WarpDriveConfig.TRANSPORTER_MAX_BOOST_MUL, Commons.toDouble(arguments[0])); - } - } catch (NumberFormatException e) { - powerBoost = 1; - } - - return powerBoost; - } - - - private int energize() { - if (isLocked) { - int count = 0; - double ls = getLockStrength(); - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + " lock strength " + getLockStrength()); - } - final ArrayList entitiesToTransport = findEntities(sourceVec, ls); - final Integer energyRequired = getEnergyRequired(); - if (energyRequired == null) { - return -1; - } - final Vector3 modDest = destVec.clone().translate(centreOnMe); - for (final Entity ent : entitiesToTransport) { - if (energy_consume(energyRequired, false)) { - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + " Transporting entity " + ent.getEntityId()); - } - inflictNegativeEffect(ent, ls); - transportEnt(ent, modDest); - count++; - } else { - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + " Insufficient energy to transport entity " + ent.getEntityId()); - } - break; - } - } - return count; - } - return -1; - } - - private void transportEnt(Entity ent, Vector3 dest) { - if (ent instanceof EntityLivingBase) { - EntityLivingBase livingEnt = (EntityLivingBase) ent; - if (WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS) { - livingEnt.setPositionAndUpdate(xCoord + dest.x, yCoord + dest.y, zCoord + dest.z); - } else { - livingEnt.setPositionAndUpdate(dest.x, dest.y, dest.z); - } - } else { - if (WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS) { - ent.setPosition(xCoord + dest.x, yCoord + dest.y, zCoord + dest.z); - } else { - ent.setPosition(dest.x, dest.y, dest.z); - } - } - } - - private void inflictNegativeEffect(Entity entity, double lockStrength) { - double value = Math.random() + lockStrength; - - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + " Inflicting negative effect " + value); - } - - if (value < 0.1) { - entity.attackEntityFrom(WarpDrive.damageTeleportation, 1000); - } - - if (value < 0.2) { - entity.attackEntityFrom(WarpDrive.damageTeleportation, 10); - } - - if (value < 0.5) { - entity.attackEntityFrom(WarpDrive.damageTeleportation, 1); - } - } - - private double beaconScan(int xV, int yV, int zV) { - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + "BeaconScan:" + xV + "," + yV + "," + zV); - } - double beacon = 0; - int beaconCount = 0; - int xL = xV - scanDist; - int xU = xV + scanDist; - int yL = yV - scanDist; - int yU = yV + scanDist; - int zL = zV - scanDist; - int zU = zV + scanDist; - for (int x = xL; x <= xU; x++) { - for (int y = yL; y <= yU; y++) { - if (y < 0 || y > 254) { - continue; - } - - for (int z = zL; z <= zU; z++) { - if (!worldObj.getBlock(x, y, z).isAssociatedBlock(WarpDrive.blockTransportBeacon)) { - continue; - } - double dist = 1 + Math.abs(x - xV) + Math.abs(y - yV) + Math.abs(z - zV); - beaconCount++; - if (worldObj.getBlockMetadata(x, y, z) == 0) { - beacon += 1 / dist; - } else { - beacon -= 1 / dist; - } - } - } - } - if (beaconCount > 0) { - beacon /= Math.sqrt(beaconCount); - } - return beacon; - } - - private double beaconScan(Vector3 s, Vector3 d) { - s = absoluteVector(s); - d = absoluteVector(d); - return beaconScan(Commons.toInt(s.x), Commons.toInt(s.y), Commons.toInt(s.z)) + beaconScan(Commons.toInt(d.x), Commons.toInt(d.y), Commons.toInt(d.z)); - } - - private Vector3 absoluteVector(Vector3 a) { - if (WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS) - return a.clone().translate(new Vector3(this)); - else - return a; - } - - private double calculatePower(Vector3 d) { - Vector3 myCoords; - if (WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS) - myCoords = centreOnMe; - else - myCoords = new Vector3(this).translate(centreOnMe); - return calculatePower(myCoords, d); - } - - private static double calculatePower(Vector3 s, Vector3 d) { - double dist = s.distanceTo(d); - return Commons.clamp(0, 1, Math.pow(Math.E, -dist / 300)); - } - - private static double min(double... ds) { - double curMin = Double.MAX_VALUE; - for (double d : ds) - curMin = Math.min(curMin, d); - return curMin; - } - - private double getLockStrength() { - if (isLocked) { - int rangeUgrades = getUpgradeCount(ItemUpgrade.getItemStack(UpgradeType.Range)); - double upgradeBoost = Math.pow(1.2, rangeUgrades); - return Commons.clamp(0, 1, baseLockStrength * lockStrengthMul * Math.pow(2, powerBoost - 1) * upgradeBoost * (1 + beaconEffect)); - } - return -1; - } - - private void unlock() { - isLocked = false; - baseLockStrength = 0; - } - - private double lock(Vector3 source, Vector3 dest) { - if (source != null && dest != null) { - double basePower = min(calculatePower(source), calculatePower(dest), calculatePower(source, dest)); - beaconEffect = beaconScan(source, dest); - baseLockStrength = basePower; - lockStrengthMul = 1; - isLocked = true; - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + " Beacon effect " + beaconEffect + " Lock strength " + baseLockStrength + "," + getLockStrength()); - } - return getLockStrength(); - } else { - unlock(); - return 0; - } - } - - private AxisAlignedBB getAABB() { - Vector3 tS = new Vector3(this); - Vector3 bS = new Vector3(this); - Vector3 scanPos = new Vector3(scanRange / 2, 2, scanRange / 2); - Vector3 scanNeg = new Vector3(-scanRange / 2, -1, -scanRange / 2); - if (WarpDriveConfig.TRANSPORTER_USE_RELATIVE_COORDS) { - tS.translate(sourceVec).translate(scanPos); - bS.translate(sourceVec).translate(scanNeg); - } else { - tS = sourceVec.clone().translate(scanPos); - bS = sourceVec.clone().translate(scanNeg); - } - return AxisAlignedBB.getBoundingBox(bS.x, bS.y, bS.z, tS.x, tS.y, tS.z); - } - - private ArrayList findEntities(Vector3 source, double lockStrength) { - AxisAlignedBB bb = getAABB(); - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + " Transporter:" + bb); - } - List data = worldObj.getEntitiesWithinAABBExcludingEntity(null, bb); - ArrayList output = new ArrayList<>(data.size()); - for (Object entity : data) { - if (lockStrength >= 1 || worldObj.rand.nextDouble() < lockStrength) {// If weak lock, don't transport - if (entity instanceof Entity) { - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + " Entity '" + entity + "' found and added"); - } - output.add((Entity) entity); - } - } else { - if (WarpDriveConfig.LOGGING_TRANSPORTER) { - WarpDrive.logger.info(this + " Entity '" + entity + "' discarded"); - } - } - } - return output; - } - @Override - public int energy_getMaxStorage() { - int energyUpgrades = getUpgradeCount(ItemUpgrade.getItemStack(UpgradeType.Energy)); - int max = (int) Math.floor(WarpDriveConfig.TRANSPORTER_MAX_ENERGY_STORED * Math.pow(1.2, energyUpgrades)); - return max; - } - - @Override - public boolean energy_canInput(ForgeDirection from) { - if (from == ForgeDirection.UP) { - return false; - } - return true; - } - - @Override - public void writeToNBT(NBTTagCompound tag) { - super.writeToNBT(tag); - tag.setDouble("powerBoost", powerBoost); - } - - @Override - public void readFromNBT(NBTTagCompound tag) { - super.readFromNBT(tag); - powerBoost = tag.getDouble("powerBoost"); + public String toString() { + return String.format("%s Beam \'%d\' @ %s (%d %d %d)", + getClass().getSimpleName(), + beamFrequency, + worldObj == null ? "~NULL~" : worldObj.provider.getDimensionName(), + xCoord, yCoord, zCoord); } } diff --git a/src/main/java/cr0s/warpdrive/config/Dictionary.java b/src/main/java/cr0s/warpdrive/config/Dictionary.java index 71ac9861..41de0d0c 100644 --- a/src/main/java/cr0s/warpdrive/config/Dictionary.java +++ b/src/main/java/cr0s/warpdrive/config/Dictionary.java @@ -48,6 +48,7 @@ public class Dictionary { public static HashSet BLOCKS_STOPMINING = null; public static HashMap BLOCKS_PLACE = null; public static HashSet BLOCKS_NOCAMOUFLAGE = null; + public static HashSet BLOCKS_NOBLINK = null; // Entities dictionary public static HashSet ENTITIES_ANCHOR = null; @@ -80,13 +81,14 @@ public class Dictionary { + "- Expandable: this block will be squished/ignored in case of collision.\n" + "- Mining: this block is mineable (default: all 'ore' blocks from the ore dictionary).\n" + "- SkipMining: this block is ignored from mining (default: bedrock).\n" - + "- StopMining: this block will prevent mining through it (default: forcefields).\n" + + "- StopMining: this block will prevent mining through it (default: command/creative, bedrock, force fields).\n" + "- PlaceEarliest: this block will be removed last and placed first (default: ship hull and projectors).\n" + "- PlaceEarlier: this block will be placed fairly soon (default: forcefield blocks).\n" + "- PlaceNormal: this block will be removed and placed with non-tile entities.\n" + "- PlaceLater: this block will be placed fairly late (default: IC2 Reactor core).\n" + "- PlaceLatest: this block will be removed first and placed last (default: IC2 Reactor chamber).\n" - + "- NoCamouflage: this block isn't valid for camouflage."); + + "- NoCamouflage: this block isn't valid for camouflage.\n" + + "- NoBlink: this block will prevent teleportation through it (default: bedrock, force fields)"); ConfigCategory categoryBlockTags = config.getCategory("block_tags"); String[] taggedBlocksName = categoryBlockTags.getValues().keySet().toArray(new String[0]); @@ -103,17 +105,17 @@ public class Dictionary { config.get("block_tags", "TConstruct:slime.leaves" , "Leaf").getString(); // anchors - config.get("block_tags", "minecraft:bedrock" , "Anchor SkipMining").getString(); + config.get("block_tags", "minecraft:bedrock" , "Anchor SkipMining NoBlink").getString(); config.get("block_tags", "minecraft:command_block" , "Anchor StopMining").getString(); config.get("block_tags", "minecraft:end_portal_frame" , "Anchor StopMining").getString(); config.get("block_tags", "minecraft:end_portal" , "Anchor StopMining").getString(); - config.get("block_tags", "Artifacts:invisible_bedrock" , "Anchor StopMining").getString(); + config.get("block_tags", "Artifacts:invisible_bedrock" , "Anchor StopMining NoBlink").getString(); config.get("block_tags", "Artifacts:anti_anti_builder_stone" , "Anchor StopMining").getString(); config.get("block_tags", "Artifacts:anti_builder" , "Anchor StopMining").getString(); config.get("block_tags", "ComputerCraft:command_computer" , "Anchor SkipMining").getString(); config.get("block_tags", "IC2:blockPersonal" , "Anchor SkipMining").getString(); config.get("block_tags", "malisisdoors:rustyHatch" , "Anchor").getString(); - config.get("block_tags", "WarpDrive:blockBedrockGlass" , "Anchor SkipMining").getString(); + config.get("block_tags", "WarpDrive:blockBedrockGlass" , "Anchor SkipMining NoBlink").getString(); // placement priorities config.get("block_tags", "minecraft:lever" , "PlaceLatest").getString(); @@ -130,8 +132,8 @@ public class Dictionary { config.get("block_tags", "IC2:blockAlloy" , "PlaceEarliest StopMining").getString(); config.get("block_tags", "IC2:blockAlloyGlass" , "PlaceEarliest StopMining").getString(); config.get("block_tags", "minecraft:obsidian" , "PlaceEarliest Mining").getString(); - config.get("block_tags", "AdvancedRepulsionSystems:field" , "PlaceEarlier StopMining").getString(); - // config.get("block_tags", "MFFS:field" , "PlaceEarlier StopMining" ).getString(); + config.get("block_tags", "AdvancedRepulsionSystems:field" , "PlaceEarlier StopMining NoBlink").getString(); + // config.get("block_tags", "MFFS:field" , "PlaceEarlier StopMining NoBlink").getString(); config.get("block_tags", "IC2:blockGenerator" , "PlaceLater").getString(); config.get("block_tags", "IC2:blockReactorChamber" , "PlaceLatest").getString(); config.get("block_tags", "ImmersiveEngineering:metalDevice" , "PlaceLatest").getString(); // FIXME: need to fine tune at metadata level @@ -143,19 +145,19 @@ public class Dictionary { config.get("block_tags", "CarpentersBlocks:blockCarpentersLever" , "PlaceLatest").getString(); config.get("block_tags", "CarpentersBlocks:blockCarpentersPressurePlate" , "PlaceLatest").getString(); config.get("block_tags", "CarpentersBlocks:blockCarpentersTorch" , "PlaceLatest").getString(); - // config.get("block_tags", "SGCraft:stargateBase" , "PlaceEarliest").getString(); - // config.get("block_tags", "SGCraft:stargateBase" , "PlaceEarliest").getString(); - // config.get("block_tags", "SGCraft:stargateRing" , "PlaceEarlier").getString(); - // config.get("block_tags", "SGCraft:stargateController" , "PlaceLatest").getString(); + // config.get("block_tags", "SGCraft:stargateBase" , "PlaceEarliest").getString(); + // config.get("block_tags", "SGCraft:stargateBase" , "PlaceEarliest").getString(); + // config.get("block_tags", "SGCraft:stargateRing" , "PlaceEarlier").getString(); + // config.get("block_tags", "SGCraft:stargateController" , "PlaceLatest").getString(); config.get("block_tags", "OpenComputers:case1" , "PlaceLatest").getString(); config.get("block_tags", "OpenComputers:case2" , "PlaceLatest").getString(); config.get("block_tags", "OpenComputers:case3" , "PlaceLatest").getString(); config.get("block_tags", "OpenComputers:caseCreative" , "PlaceLatest").getString(); config.get("block_tags", "OpenComputers:keyboard" , "PlaceLatest").getString(); config.get("block_tags", "PneumaticCraft:pressureChamberValve" , "PlaceEarlier").getString(); - config.get("block_tags", "StargateTech2:block.shieldEmitter" , "PlaceLater StopMining").getString(); - config.get("block_tags", "StargateTech2:block.shieldController" , "PlaceNormal StopMining").getString(); - config.get("block_tags", "StargateTech2:block.shield" , "PlaceNormal StopMining").getString(); + config.get("block_tags", "StargateTech2:block.shieldEmitter" , "PlaceLater StopMining NoBlink").getString(); + config.get("block_tags", "StargateTech2:block.shieldController" , "PlaceNormal StopMining NoBlink").getString(); + config.get("block_tags", "StargateTech2:block.shield" , "PlaceNormal StopMining NoBlink").getString(); config.get("block_tags", "StargateTech2:block.busAdapter" , "PlaceLatest StopMining").getString(); config.get("block_tags", "StargateTech2:block.busCable" , "PlaceNormal StopMining").getString(); @@ -176,26 +178,26 @@ public class Dictionary { config.get("block_tags", "minecraft:redstone_block" , "Mining").getString(); // mining an 'end' moon - config.get("block_tags", "WarpDrive:blockIridium" , "Mining").getString(); // stronger than obsidian but can still be mined (see ender moon) + config.get("block_tags", "WarpDrive:blockIridium" , "Mining").getString(); // stronger than obsidian but can still be mined (see ender moon) // force field camouflage blacklisting - config.get("block_tags", "deepresonance:energyCollectorBlock" , "NoCamouflage").getString(); - config.get("block_tags", "deepresonance:resonatingCrystalBlock" , "NoCamouflage").getString(); - config.get("block_tags", "evilcraft:bloodInfuser" , "NoCamouflage").getString(); - config.get("block_tags", "evilcraft:darkOre" , "NoCamouflage").getString(); - config.get("block_tags", "evilcraft:sanguinaryEnvironmentalAccumulator", "NoCamouflage").getString(); - config.get("block_tags", "evilcraft:spiritReanimator" , "NoCamouflage").getString(); - config.get("block_tags", "openmodularturrets:baseTierWood" , "NoCamouflage").getString(); - config.get("block_tags", "openmodularturrets:baseTierOneBlock" , "NoCamouflage").getString(); - config.get("block_tags", "openmodularturrets:baseTierTwoBlock" , "NoCamouflage").getString(); - config.get("block_tags", "openmodularturrets:baseTierThreeBlock" , "NoCamouflage").getString(); - config.get("block_tags", "openmodularturrets:baseTierFourBlock" , "NoCamouflage").getString(); - config.get("block_tags", "Thaumcraft:blockCustomPlant" , "NoCamouflage").getString(); - config.get("block_tags", "ThermalExpansion:Cache" , "NoCamouflage").getString(); - config.get("block_tags", "ThermalExpansion:Device" , "NoCamouflage").getString(); - config.get("block_tags", "ThermalExpansion:Machine" , "NoCamouflage").getString(); - config.get("block_tags", "ThermalExpansion:Sponge" , "NoCamouflage").getString(); - config.get("block_tags", "witchery:leechchest" , "NoCamouflage").getString(); + config.get("block_tags", "deepresonance:energyCollectorBlock" , "NoCamouflage").getString(); + config.get("block_tags", "deepresonance:resonatingCrystalBlock" , "NoCamouflage").getString(); + config.get("block_tags", "evilcraft:bloodInfuser" , "NoCamouflage").getString(); + config.get("block_tags", "evilcraft:darkOre" , "NoCamouflage").getString(); + config.get("block_tags", "evilcraft:sanguinaryEnvironmentalAccumulator" , "NoCamouflage").getString(); + config.get("block_tags", "evilcraft:spiritReanimator" , "NoCamouflage").getString(); + config.get("block_tags", "openmodularturrets:baseTierWood" , "NoCamouflage").getString(); + config.get("block_tags", "openmodularturrets:baseTierOneBlock" , "NoCamouflage").getString(); + config.get("block_tags", "openmodularturrets:baseTierTwoBlock" , "NoCamouflage").getString(); + config.get("block_tags", "openmodularturrets:baseTierThreeBlock" , "NoCamouflage").getString(); + config.get("block_tags", "openmodularturrets:baseTierFourBlock" , "NoCamouflage").getString(); + config.get("block_tags", "Thaumcraft:blockCustomPlant" , "NoCamouflage").getString(); + config.get("block_tags", "ThermalExpansion:Cache" , "NoCamouflage").getString(); + config.get("block_tags", "ThermalExpansion:Device" , "NoCamouflage").getString(); + config.get("block_tags", "ThermalExpansion:Machine" , "NoCamouflage").getString(); + config.get("block_tags", "ThermalExpansion:Sponge" , "NoCamouflage").getString(); + config.get("block_tags", "witchery:leechchest" , "NoCamouflage").getString(); taggedBlocksName = categoryBlockTags.getValues().keySet().toArray(new String[0]); } @@ -361,6 +363,7 @@ public class Dictionary { BLOCKS_STOPMINING = new HashSet<>(taggedBlocks.size()); BLOCKS_PLACE = new HashMap<>(taggedBlocks.size()); BLOCKS_NOCAMOUFLAGE = new HashSet<>(taggedBlocks.size()); + BLOCKS_NOBLINK = new HashSet<>(taggedBlocks.size()); for (final Entry taggedBlock : taggedBlocks.entrySet()) { final Block block = Block.getBlockFromName(taggedBlock.getKey()); if (block == null) { @@ -385,6 +388,7 @@ public class Dictionary { case "PlaceLater" : BLOCKS_PLACE.put(block, 3); break; case "PlaceLatest" : BLOCKS_PLACE.put(block, 4); break; case "NoCamouflage" : BLOCKS_NOCAMOUFLAGE.add(block); break; + case "NoBlink" : BLOCKS_NOBLINK.add(block); break; default: WarpDrive.logger.error("Unsupported tag '" + tag + "' for block " + block); break; diff --git a/src/main/java/cr0s/warpdrive/config/WarpDriveConfig.java b/src/main/java/cr0s/warpdrive/config/WarpDriveConfig.java index 9d91a252..7e80688a 100644 --- a/src/main/java/cr0s/warpdrive/config/WarpDriveConfig.java +++ b/src/main/java/cr0s/warpdrive/config/WarpDriveConfig.java @@ -356,10 +356,39 @@ public class WarpDriveConfig { public static int IC2_REACTOR_COOLING_INTERVAL_TICKS = 10; // Transporter - public static int TRANSPORTER_MAX_ENERGY_STORED = 1000000; - public static boolean TRANSPORTER_USE_RELATIVE_COORDS = true; - public static double TRANSPORTER_ENERGY_PER_BLOCK = 100.0; - public static double TRANSPORTER_MAX_BOOST_MUL = 4.0; + public static int TRANSPORTER_MAX_ENERGY_STORED = 2000000; + public static int TRANSPORTER_ENERGY_STORED_UPGRADE_BONUS = TRANSPORTER_MAX_ENERGY_STORED / 2; + public static int TRANSPORTER_ENERGY_STORED_UPGRADE_MAX_QUANTITY = 8; + public static int TRANSPORTER_RANGE_BASE_BLOCKS = 128; + public static int TRANSPORTER_RANGE_UPGRADE_BLOCKS = 16; + public static int TRANSPORTER_RANGE_UPGRADE_MAX_QUANTITY = 8; + public static double TRANSPORTER_ACQUIRING_ENERGY_FACTOR = 0.02D; + public static int TRANSPORTER_JAMMED_COOLDOWN_TICKS = 2 * 20; + public static int TRANSPORTER_TRANSFER_ENERGY_AT_MIN_RANGE = 100000; + public static int TRANSPORTER_TRANSFER_ENERGY_AT_MAX_RANGE = 1000000; + public static double TRANSPORTER_TRANSFER_ENERGY_FACTOR_MAX = 10.0D; + public static int TRANSPORTER_TRANSFER_FAILURE_MAX_DAMAGE = 5; + public static double TRANSPORTER_TRANSFER_SUCCESS_LOCK_BONUS = 0.20D; + public static int TRANSPORTER_TRANSFER_SUCCESS_MAX_DAMAGE = 100; + public static double TRANSPORTER_TRANSFER_LOCKING_LOST = 0.5D; + public static int TRANSPORTER_TRANSFER_WARMUP_TICKS = 3 * 20; + public static int TRANSPORTER_TRANSFER_RELEASE_TICKS = 2 * 20; + public static int TRANSPORTER_TRANSFER_COOLDOWN_TICKS = 10 * 20; + public static double TRANSPORTER_ENTITY_MOVEMENT_TOLERANCE_BLOCKS = 1.0D; + public static int TRANSPORTER_ENTITY_GRAB_RADIUS_BLOCKS = 2; + public static int TRANSPORTER_FOCUS_SEARCH_RADIUS_BLOCKS = 2; + public static double TRANSPORTER_LOCKING_STRENGTH_FACTOR_PER_TICK = 0.999D; // natural decay + public static double TRANSPORTER_LOCKING_STRENGTH_IN_WILDERNESS = 0.25D; + public static double TRANSPORTER_LOCKING_STRENGTH_AT_FOCUS = 0.5D; + public static double TRANSPORTER_LOCKING_STRENGTH_AT_TRANSPORTER = 1.0D; + public static double TRANSPORTER_LOCKING_STRENGTH_BONUS_AT_MAX_ENERGY_FACTOR = 0.5D; + public static double TRANSPORTER_LOCKING_STRENGTH_UPGRADE = 0.15D; + public static double TRANSPORTER_LOCKING_SPEED_IN_WILDERNESS = 0.25D; + public static double TRANSPORTER_LOCKING_SPEED_AT_FOCUS = 0.75D; + public static double TRANSPORTER_LOCKING_SPEED_AT_TRANSPORTER = 1.0D; + public static double TRANSPORTER_LOCKING_SPEED_UPGRADE = 0.25D; + public static int TRANSPORTER_LOCKING_SPEED_OPTIMAL_TICKS = 5 * 20; + public static int TRANSPORTER_LOCKING_UPGRADE_MAX_QUANTITY = 2; // Enantiomorphic power reactor public static int ENAN_REACTOR_MAX_ENERGY_STORED = 100000000; @@ -942,11 +971,10 @@ public class WarpDriveConfig { // Transporter TRANSPORTER_MAX_ENERGY_STORED = Commons.clamp(1, Integer.MAX_VALUE, config.get("transporter", "max_energy_stored", TRANSPORTER_MAX_ENERGY_STORED, "Maximum energy stored").getInt()); - TRANSPORTER_USE_RELATIVE_COORDS = config.get("transporter", "use_relative_coords", TRANSPORTER_USE_RELATIVE_COORDS, "Should transporter use relative coordinates?").getBoolean(true); - TRANSPORTER_ENERGY_PER_BLOCK = Commons.clamp(1.0D, TRANSPORTER_MAX_ENERGY_STORED / 10.0D, - config.get("transporter", "energy_per_block", TRANSPORTER_ENERGY_PER_BLOCK, "Energy cost per block distance").getDouble(100.0D)); - TRANSPORTER_MAX_BOOST_MUL = Commons.clamp(1.0D, 1000.0D, - config.get("transporter", "max_boost", TRANSPORTER_MAX_BOOST_MUL, "Maximum energy boost allowed").getDouble(4.0)); +// TRANSPORTER_ENERGY_PER_BLOCK = Commons.clamp(1.0D, TRANSPORTER_MAX_ENERGY_STORED / 10.0D, +// config.get("transporter", "energy_per_block", TRANSPORTER_ENERGY_PER_BLOCK, "Energy cost per block distance").getDouble(100.0D)); +// TRANSPORTER_MAX_BOOST_MUL = Commons.clamp(1.0D, 1000.0D, +// config.get("transporter", "max_boost", TRANSPORTER_MAX_BOOST_MUL, "Maximum energy boost allowed").getDouble(4.0)); // Enantiomorphic reactor ENAN_REACTOR_MAX_ENERGY_STORED = Commons.clamp(1, Integer.MAX_VALUE, diff --git a/src/main/java/cr0s/warpdrive/data/EnumTransporterState.java b/src/main/java/cr0s/warpdrive/data/EnumTransporterState.java new file mode 100644 index 00000000..58f771b6 --- /dev/null +++ b/src/main/java/cr0s/warpdrive/data/EnumTransporterState.java @@ -0,0 +1,45 @@ +package cr0s.warpdrive.data; + +import cr0s.warpdrive.api.IStringSerializable; + +import javax.annotation.Nonnull; +import java.util.HashMap; + +public enum EnumTransporterState implements IStringSerializable { + + DISABLED (0, "disabled"), // disabled + IDLE (1, "idle"), // enabling, waiting for lock + ACQUIRING (2, "acquiring"), // acquiring lock + ENERGIZING (3, "energizing"); // transferring entities + + private final int metadata; + private final String unlocalizedName; + + // cached values + public static final int length; + private static final HashMap ID_MAP = new HashMap<>(); + + static { + length = EnumTransporterState.values().length; + for (final EnumTransporterState transporterState : values()) { + ID_MAP.put(transporterState.ordinal(), transporterState); + } + } + + EnumTransporterState(final int metadata, final String unlocalizedName) { + this.metadata = metadata; + this.unlocalizedName = unlocalizedName; + } + + public int getMetadata() { + return metadata; + } + + public static EnumTransporterState get(final int id) { + return ID_MAP.get(id); + } + + @Nonnull + @Override + public String getName() { return unlocalizedName; } +} diff --git a/src/main/java/cr0s/warpdrive/data/VectorI.java b/src/main/java/cr0s/warpdrive/data/VectorI.java index 5c33eb65..0a660ffb 100644 --- a/src/main/java/cr0s/warpdrive/data/VectorI.java +++ b/src/main/java/cr0s/warpdrive/data/VectorI.java @@ -8,13 +8,11 @@ import net.minecraft.entity.Entity; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.EnumFacing; import net.minecraft.util.MovingObjectPosition; -import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraft.world.WorldServer; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.gen.ChunkProviderServer; import net.minecraftforge.common.util.ForgeDirection; @@ -209,6 +207,13 @@ public class VectorI implements Cloneable { return this; } + public VectorI translate(final EnumFacing side) { + x += side.getFrontOffsetX(); + y += side.getFrontOffsetY(); + z += side.getFrontOffsetZ(); + return this; + } + // return a new vector adding both parts public static VectorI add(final VectorI vector1, final VectorI vector2) { return new VectorI(vector1.x + vector2.x, vector1.y + vector2.y, vector1.z + vector2.z); @@ -279,13 +284,13 @@ public class VectorI implements Cloneable { return vector; } - public void readFromNBT(NBTTagCompound nbtTagCompound) { + public void readFromNBT(final NBTTagCompound nbtTagCompound) { x = nbtTagCompound.getInteger("x"); y = nbtTagCompound.getInteger("y"); z = nbtTagCompound.getInteger("z"); } - public NBTTagCompound writeToNBT(NBTTagCompound nbtTagCompound) { + public NBTTagCompound writeToNBT(final NBTTagCompound nbtTagCompound) { nbtTagCompound.setInteger("x", x); nbtTagCompound.setInteger("y", y); nbtTagCompound.setInteger("z", z); @@ -308,11 +313,11 @@ public class VectorI implements Cloneable { return (newX * newX + newY * newY + newZ * newZ); } - public int distance2To(final Entity entity) { - int newX = (int) (Math.round(entity.posX)) - x; - int newY = (int) (Math.round(entity.posY)) - y; - int newZ = (int) (Math.round(entity.posZ)) - z; - return (newX * newX + newY * newY + newZ * newZ); + public double distance2To(final Entity entity) { + final double newX = entity.posX - x; + final double newY = entity.posY - y; + final double newZ = entity.posZ - z; + return newX * newX + newY * newY + newZ * newZ; } public int distance2To(final TileEntity tileEntity) {