package mcjty.rftools.blocks.teleporter; import mcjty.lib.varia.Coordinate; import mcjty.lib.varia.Logging; import mcjty.rftools.Achievements; import mcjty.rftools.RFTools; import mcjty.rftools.dimension.RfToolsDimensionManager; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.DamageSource; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.util.ForgeDirection; public class TeleportationTools { public static final int STATUS_OK = 0; public static final int STATUS_WARN = 1; public static final int STATUS_UNKNOWN = 2; public static void applyEffectForSeverity(EntityPlayer player, int severity, boolean boostNeeded) { switch (severity) { case 1: if (boostNeeded) { player.addPotionEffect(new PotionEffect(Potion.confusion.getId(), 100)); player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 5)); } break; case 2: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 100)); break; case 3: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 100)); player.attackEntityFrom(DamageSource.generic, 0.5f); break; case 4: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 200)); player.attackEntityFrom(DamageSource.generic, 0.5f); break; case 5: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 200)); player.attackEntityFrom(DamageSource.generic, 1.0f); break; case 6: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 300)); player.attackEntityFrom(DamageSource.generic, 1.0f); break; case 7: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 300)); player.addPotionEffect(new PotionEffect(Potion.wither.getId(), 200)); player.attackEntityFrom(DamageSource.generic, 2.0f); break; case 8: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 400)); player.addPotionEffect(new PotionEffect(Potion.wither.getId(), 300)); player.attackEntityFrom(DamageSource.generic, 2.0f); break; case 9: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 400)); player.addPotionEffect(new PotionEffect(Potion.wither.getId(), 400)); player.attackEntityFrom(DamageSource.generic, 3.0f); break; case 10: player.addPotionEffect(new PotionEffect(Potion.harm.getId(), 500)); player.addPotionEffect(new PotionEffect(Potion.wither.getId(), 500)); player.attackEntityFrom(DamageSource.generic, 3.0f); break; } } /** * Calculate the cost of doing a dial between a transmitter and a destination. * @param world * @param c1 the start coordinate * @param teleportDestination * @return */ public static int calculateRFCost(World world, Coordinate c1, TeleportDestination teleportDestination) { if (world.provider.dimensionId != teleportDestination.getDimension()) { return TeleportConfiguration.rfStartTeleportBaseDim; } else { Coordinate c2 = teleportDestination.getCoordinate(); double dist = Vec3.createVectorHelper(c1.getX(), c1.getY(), c1.getZ()).distanceTo(Vec3.createVectorHelper(c2.getX(), c2.getY(), c2.getZ())); int rf = TeleportConfiguration.rfStartTeleportBaseLocal + (int)(TeleportConfiguration.rfStartTeleportDist * dist); if (rf > TeleportConfiguration.rfStartTeleportBaseDim) { rf = TeleportConfiguration.rfStartTeleportBaseDim; } return rf; } } /** * Calculate the time in ticks of doing a dial between a transmitter and a destination. * @param world * @param c1 the start coordinate * @param teleportDestination * @return */ public static int calculateTime(World world, Coordinate c1, TeleportDestination teleportDestination) { if (world.provider.dimensionId != teleportDestination.getDimension()) { return TeleportConfiguration.timeTeleportBaseDim; } else { Coordinate c2 = teleportDestination.getCoordinate(); double dist = Vec3.createVectorHelper(c1.getX(), c1.getY(), c1.getZ()).distanceTo(Vec3.createVectorHelper(c2.getX(), c2.getY(), c2.getZ())); int time = TeleportConfiguration.timeTeleportBaseLocal + (int)(TeleportConfiguration.timeTeleportDist * dist / 1000); if (time > TeleportConfiguration.timeTeleportBaseDim) { time = TeleportConfiguration.timeTeleportBaseDim; } return time; } } // Return true if we needed a boost. public static boolean performTeleport(EntityPlayer player, TeleportDestination dest, int bad, int good, boolean boosted) { Coordinate c = dest.getCoordinate(); Coordinate old = new Coordinate((int)player.posX, (int)player.posY, (int)player.posZ); int oldId = player.worldObj.provider.dimensionId; if (oldId != dest.getDimension()) { TeleportationTools.teleportToDimension(player, dest.getDimension(), c.getX() + 0.5, c.getY() + 1.5, c.getZ() + 0.5); } else { player.setPositionAndUpdate(c.getX()+0.5, c.getY()+1, c.getZ()+0.5); } Logging.message(player, "Whoosh!"); Achievements.trigger(player, Achievements.firstTeleport); boolean boostNeeded = false; int severity = consumeReceiverEnergy(player, dest.getCoordinate(), dest.getDimension()); if (severity > 0 && boosted) { boostNeeded = true; severity = 1; } severity = applyBadEffectIfNeeded(player, severity, bad, good, boostNeeded); if (severity <= 0) { if (TeleportConfiguration.teleportVolume >= 0.01) { ((EntityPlayerMP) player).worldObj.playSoundAtEntity(player, RFTools.MODID + ":teleport_whoosh", TeleportConfiguration.teleportVolume, 1.0f); } } if (TeleportConfiguration.logTeleportUsages) { Logging.log("Teleport: Player " + player.getDisplayName() + " from " + old + " (dim " + oldId + ") to " + dest.getCoordinate() + " (dim " + dest.getDimension() + ") with severity " + severity); } return boostNeeded; } // Server side only public static int dial(World worldObj, DialingDeviceTileEntity dialingDeviceTileEntity, String player, Coordinate transmitter, int transDim, Coordinate coordinate, int dimension, boolean once) { World transWorld = RfToolsDimensionManager.getDimensionManager(worldObj).getWorldForDimension(transDim); if (transWorld == null) { return DialingDeviceTileEntity.DIAL_INVALID_SOURCE_MASK; } MatterTransmitterTileEntity transmitterTileEntity = (MatterTransmitterTileEntity) transWorld.getTileEntity(transmitter.getX(), transmitter.getY(), transmitter.getZ()); if (transmitterTileEntity == null) { return DialingDeviceTileEntity.DIAL_INVALID_TRANSMITTER; } if (player != null && !transmitterTileEntity.checkAccess(player)) { return DialingDeviceTileEntity.DIAL_TRANSMITTER_NOACCESS; } if (coordinate == null) { transmitterTileEntity.setTeleportDestination(null, false); return DialingDeviceTileEntity.DIAL_INTERRUPTED; } TeleportDestination teleportDestination = findDestination(worldObj, coordinate, dimension); if (teleportDestination == null) { return DialingDeviceTileEntity.DIAL_INVALID_DESTINATION_MASK; } Coordinate c = teleportDestination.getCoordinate(); World recWorld = RfToolsDimensionManager.getWorldForDimension(teleportDestination.getDimension()); if (recWorld == null) { recWorld = MinecraftServer.getServer().worldServerForDimension(teleportDestination.getDimension()); if (recWorld == null) { return DialingDeviceTileEntity.DIAL_INVALID_DESTINATION_MASK; } } // Only do this if not an rftools dimension. TileEntity tileEntity = recWorld.getTileEntity(c.getX(), c.getY(), c.getZ()); if (!(tileEntity instanceof MatterReceiverTileEntity)) { return DialingDeviceTileEntity.DIAL_INVALID_DESTINATION_MASK; } MatterReceiverTileEntity matterReceiverTileEntity = (MatterReceiverTileEntity) tileEntity; matterReceiverTileEntity.updateDestination(); // Make sure destination is ok. if (player != null && !matterReceiverTileEntity.checkAccess(player)) { return DialingDeviceTileEntity.DIAL_RECEIVER_NOACCESS; } if (!checkBeam(transmitter, transWorld, 1, 4, 2)) { return DialingDeviceTileEntity.DIAL_TRANSMITTER_BLOCKED_MASK; } if (dialingDeviceTileEntity != null) { int cost = TeleportConfiguration.rfPerDial; cost = (int) (cost * (2.0f - dialingDeviceTileEntity.getInfusedFactor()) / 2.0f); if (dialingDeviceTileEntity.getEnergyStored(ForgeDirection.DOWN) < cost) { return DialingDeviceTileEntity.DIAL_DIALER_POWER_LOW_MASK; } dialingDeviceTileEntity.consumeEnergy(cost); } transmitterTileEntity.setTeleportDestination(teleportDestination, once); return DialingDeviceTileEntity.DIAL_OK; } /** * Consume energy on the receiving side and return a number indicating how good this went. * * @param c * @param dimension * @return 0 in case of success. 10 in case of severe failure */ private static int consumeReceiverEnergy(EntityPlayer player, Coordinate c, int dimension) { World world = DimensionManager.getWorld(dimension); TileEntity te = world.getTileEntity(c.getX(), c.getY(), c.getZ()); if (!(te instanceof MatterReceiverTileEntity)) { Logging.warn(player, "Something went wrong with the destination!"); return 0; } MatterReceiverTileEntity matterReceiverTileEntity = (MatterReceiverTileEntity) te; int rf = TeleportConfiguration.rfPerTeleportReceiver; rf = (int) (rf * (2.0f - matterReceiverTileEntity.getInfusedFactor()) / 2.0f); if (rf <= 0) { return 0; } int extracted = rf; if (rf > matterReceiverTileEntity.getEnergyStored(ForgeDirection.DOWN)) { extracted = matterReceiverTileEntity.getEnergyStored(ForgeDirection.DOWN); } matterReceiverTileEntity.consumeEnergy(rf); int remainingRf = matterReceiverTileEntity.getEnergyStored(ForgeDirection.DOWN); if (remainingRf <= 1) { Logging.warn(player, "The matter receiver has run out of power!"); } else if (remainingRf < (TeleportConfiguration.RECEIVER_MAXENERGY / 10)) { Logging.warn(player, "The matter receiver is getting very low on power!"); } else if (remainingRf < (TeleportConfiguration.RECEIVER_MAXENERGY / 5)) { Logging.warn(player, "The matter receiver is getting low on power!"); } return 10 - (extracted * 10 / rf); } /** * Return a number between 0 and 10 indicating the severity of the teleportation. * @return */ public static int calculateSeverity(int bad, int total) { if (total == 0) { total = 1; } int severity = bad * 10 / total; if (mustInterrupt(bad, total)) { // If an interrupt was done then severity is worse. severity += 2; } if (severity > 10) { severity = 10; } return severity; } public static int applyBadEffectIfNeeded(EntityPlayer player, int severity, int bad, int total, boolean boostNeeded) { severity += calculateSeverity(bad, total); if (severity > 10) { severity = 10; } if (severity <= 0) { return 0; } if (TeleportConfiguration.teleportErrorVolume >= 0.01) { player.worldObj.playSoundAtEntity(player, RFTools.MODID + ":teleport_error", TeleportConfiguration.teleportErrorVolume, 1.0f); } applyEffectForSeverity(player, severity, boostNeeded); return severity; } public static boolean mustInterrupt(int bad, int total) { return bad > (total / 2); } public static void teleportToDimension(EntityPlayer player, int dimension, double x, double y, double z) { int oldDimension = player.worldObj.provider.dimensionId; EntityPlayerMP entityPlayerMP = (EntityPlayerMP) player; WorldServer worldServer = MinecraftServer.getServer().worldServerForDimension(dimension); player.addExperienceLevel(0); MinecraftServer.getServer().getConfigurationManager().transferPlayerToDimension(entityPlayerMP, dimension, new RfToolsTeleporter(worldServer, x, y, z)); if (oldDimension == 1) { // For some reason teleporting out of the end does weird things. player.setPositionAndUpdate(x, y, z); worldServer.spawnEntityInWorld(player); worldServer.updateEntityWithOptionalForce(player, false); } } public static TeleportDestination findDestination(World worldObj, Coordinate coordinate, int dimension) { TeleportDestinations destinations = TeleportDestinations.getDestinations(worldObj); return destinations.getDestination(coordinate, dimension); } // Check if there is room for a beam. public static boolean checkBeam(Coordinate c, World world, int dy1, int dy2, int errory) { for (int dy = dy1 ; dy <= dy2 ; dy++) { Block b = world.getBlock(c.getX(), c.getY()+dy, c.getZ()); if (!b.isAir(world, c.getX(), c.getY()+dy, c.getZ())) { if (dy <= errory) { // Everything below errory must be free. return false; } else { // Everything higher then errory doesn't have to be free. break; } } } return true; } public static boolean checkValidTeleport(EntityPlayer player, int srcId, int dstId) { if (TeleportConfiguration.preventInterdimensionalTeleports) { if (srcId == dstId) { Logging.warn(player, "Teleportation in the same dimension is not allowed!"); return false; } } if (TeleportConfiguration.getBlacklistedTeleportationDestinations().contains(dstId)) { Logging.warn(player, "Teleportation to that dimension is not allowed!"); return false; } if (TeleportConfiguration.getBlacklistedTeleportationSources().contains(srcId)) { Logging.warn(player, "Teleportation from this dimension is not allowed!"); return false; } return true; } }