From 33c5f79ed7ae786df2c95e5cabe0c6ff46f9d2c6 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Tue, 23 Jun 2020 22:09:06 +0100 Subject: [PATCH] Add archivist's eyeglass and spectral tether, plus some improvements to teleportation --- .../block/BlockTransportationStone.java | 5 +- .../wizardry/client/ClientProxy.java | 26 +++--- .../wizardry/client/model/WizardryModels.java | 2 + .../wizardry/loot/RandomSpell.java | 10 ++- .../wizardry/packet/PacketTransportation.java | 21 +++-- .../wizardry/registry/WizardryItems.java | 4 + .../electroblob/wizardry/spell/Blink.java | 51 +++++------ .../electroblob/wizardry/spell/PhaseStep.java | 83 +++++++---------- .../wizardry/spell/Transportation.java | 20 ++++- .../wizardry/util/EntityUtils.java | 85 ++++++++++++++++++ .../assets/ebwizardry/lang/en_gb.lang | 4 + .../assets/ebwizardry/lang/en_us.lang | 4 + .../models/item/charm_mount_teleporting.json | 6 ++ .../models/item/charm_spell_discovery.json | 6 ++ .../items/charm_mount_teleporting.png | Bin 0 -> 6418 bytes .../textures/items/charm_spell_discovery.png | Bin 0 -> 1765 bytes 16 files changed, 224 insertions(+), 103 deletions(-) create mode 100644 src/main/resources/assets/ebwizardry/models/item/charm_mount_teleporting.json create mode 100644 src/main/resources/assets/ebwizardry/models/item/charm_spell_discovery.json create mode 100644 src/main/resources/assets/ebwizardry/textures/items/charm_mount_teleporting.png create mode 100644 src/main/resources/assets/ebwizardry/textures/items/charm_spell_discovery.png diff --git a/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java b/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java index cb9d7c88..f72ea195 100644 --- a/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java +++ b/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java @@ -174,10 +174,11 @@ public class BlockTransportationStone extends Block { return false; } - /** Returns whether the specified location is surrounded by a complete cicle of 8 transportation stones. */ + /** Returns whether the specified location is surrounded by a complete circle of 8 transportation stones. */ public static boolean testForCircle(World world, BlockPos pos){ - if(world.getBlockState(pos).getMaterial().blocksMovement()) return false; + if(world.getBlockState(pos).getMaterial().blocksMovement() || world.getBlockState(pos.up()).getMaterial() + .blocksMovement()) return false; for(int x = -1; x <= 1; x++){ for(int z = -1; z <= 1; z++){ diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 748631c1..67f7d84c 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -549,36 +549,38 @@ public class ClientProxy extends CommonProxy { public void handleTransportationPacket(PacketTransportation.Message message){ World world = Minecraft.getMinecraft().world; - Entity caster = world.getEntityByID(message.casterID); + BlockPos pos = message.destination; - if(caster == null) return; // Shouldn't happen + Entity entity = world.getEntityByID(message.dismountEntityID); + if(message.dismountEntityID != -1 && entity != null) entity.dismountRidingEntity(); // Moved from when the packet is sent to when it is received; fixes the sound not playing in first person. - caster.playSound(WizardrySounds.SPELL_TRANSPORTATION_TRAVEL, 1, 1); + // Changed to a position to avoid syncing issues + world.playSound(pos.getX(), pos.getY(), pos.getZ(), WizardrySounds.SPELL_TRANSPORTATION_TRAVEL, WizardrySounds.SPELLS, 1, 1, false); for(int i = 0; i < 20; i++){ double radius = 1; float angle = world.rand.nextFloat() * (float)Math.PI * 2; - double x = caster.posX + radius * MathHelper.cos(angle); - double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2; - double z = caster.posZ + radius * MathHelper.sin(angle); + double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle); + double y = pos.getY() + world.rand.nextDouble() * 2; + double z = pos.getZ() + 0.5 + radius * MathHelper.sin(angle); ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.02, 0).clr(0.6f, 1, 0.6f) .time(80 + world.rand.nextInt(10)).spawn(world); } for(int i = 0; i < 20; i++){ double radius = 1; float angle = world.rand.nextFloat() * (float)Math.PI * 2; - double x = caster.posX + radius * MathHelper.cos(angle); - double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2; - double z = caster.posZ + radius * MathHelper.sin(angle); + double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle); + double y = pos.getY() + world.rand.nextDouble() * 2; + double z = pos.getZ() + 0.5 + radius * MathHelper.sin(angle); world.spawnParticle(EnumParticleTypes.VILLAGER_HAPPY, x, y, z, 0, 0.02, 0); } for(int i = 0; i < 20; i++){ double radius = 1; float angle = world.rand.nextFloat() * (float)Math.PI * 2; - double x = caster.posX + radius * MathHelper.cos(angle); - double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2; - double z = caster.posZ + radius * MathHelper.sin(angle); + double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle); + double y = pos.getY() + world.rand.nextDouble() * 2; + double z = pos.getZ() + 0.5 + radius * MathHelper.sin(angle); world.spawnParticle(EnumParticleTypes.ENCHANTMENT_TABLE, x, y, z, 0, 0.02, 0); } } diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java index cb776c61..d0519405 100644 --- a/src/main/java/electroblob/wizardry/client/model/WizardryModels.java +++ b/src/main/java/electroblob/wizardry/client/model/WizardryModels.java @@ -282,6 +282,7 @@ public final class WizardryModels { registerItemModel(WizardryItems.charm_haggler); registerItemModel(WizardryItems.charm_experience_tome); registerItemModel(WizardryItems.charm_move_speed); + registerItemModel(WizardryItems.charm_spell_discovery); registerItemModel(WizardryItems.charm_auto_smelt); registerItemModel(WizardryItems.charm_lava_walking); registerItemModel(WizardryItems.charm_storm); @@ -295,6 +296,7 @@ public final class WizardryModels { registerItemModel(WizardryItems.charm_light); registerItemModel(WizardryItems.charm_transportation); registerItemModel(WizardryItems.charm_black_hole); + registerItemModel(WizardryItems.charm_mount_teleporting); registerItemModel(WizardryItems.charm_feeding); } diff --git a/src/main/java/electroblob/wizardry/loot/RandomSpell.java b/src/main/java/electroblob/wizardry/loot/RandomSpell.java index 9db7972c..9e1819e5 100644 --- a/src/main/java/electroblob/wizardry/loot/RandomSpell.java +++ b/src/main/java/electroblob/wizardry/loot/RandomSpell.java @@ -5,9 +5,11 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Element; import electroblob.wizardry.constants.Tier; import electroblob.wizardry.data.WizardData; +import electroblob.wizardry.item.ItemArtefact; import electroblob.wizardry.item.ItemScroll; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.util.SpellProperties; import net.minecraft.entity.player.EntityPlayer; @@ -150,8 +152,12 @@ public class RandomSpell extends LootFunction { possibleSpells.removeIf(s -> s.getElement() != element); if(possibleSpells.isEmpty()) return Spells.none; // If it fails anywhere, it'll most likely be here + float bias = undiscoveredBias; + // Archivist's eyeglass increases undiscovered bias by 0.4 up to a maximum of 0.9 + if(ItemArtefact.isArtefactActive(player, WizardryItems.charm_spell_discovery)) bias = Math.min(bias + 0.4f, 0.9f); + // Remove either the undiscovered spells or the discovered ones, depending on the bias - if(undiscoveredBias > 0 && player != null){ + if(bias > 0 && player != null){ WizardData data = WizardData.get(player); @@ -159,7 +165,7 @@ public class RandomSpell extends LootFunction { // If none have been discovered or they've all been discovered, don't bother! if(discoveredCount > 0 && discoveredCount < possibleSpells.size()){ // Kinda unintuitive but it's very neat! - boolean keepDiscovered = random.nextFloat() < 0.5f + 0.5f * undiscoveredBias; + boolean keepDiscovered = random.nextFloat() < 0.5f + 0.5f * bias; possibleSpells.removeIf(s -> keepDiscovered != data.hasSpellBeenDiscovered(s)); } } diff --git a/src/main/java/electroblob/wizardry/packet/PacketTransportation.java b/src/main/java/electroblob/wizardry/packet/PacketTransportation.java index afb07f7d..1824de64 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketTransportation.java +++ b/src/main/java/electroblob/wizardry/packet/PacketTransportation.java @@ -3,10 +3,14 @@ package electroblob.wizardry.packet; import electroblob.wizardry.Wizardry; import electroblob.wizardry.packet.PacketTransportation.Message; import io.netty.buffer.ByteBuf; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; +import javax.annotation.Nullable; + /** * [Server -> Client] This packet is sent when a player is teleported due to the transportation spell to spawn * the particles. @@ -26,26 +30,31 @@ public class PacketTransportation implements IMessageHandler } public static class Message implements IMessage { - /** EntityID of the caster */ - public int casterID; + + /** The destination that was teleported to */ + public BlockPos destination; + public int dismountEntityID; // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) public Message(){ } - public Message(int casterID){ - this.casterID = casterID; + public Message(BlockPos destination, @Nullable Entity toDismount){ + this.destination = destination; + this.dismountEntityID = toDismount == null ? -1 : toDismount.getEntityId(); } @Override public void fromBytes(ByteBuf buf){ // The order is important - this.casterID = buf.readInt(); + this.destination = BlockPos.fromLong(buf.readLong()); + this.dismountEntityID = buf.readInt(); } @Override public void toBytes(ByteBuf buf){ - buf.writeInt(casterID); + buf.writeLong(destination.toLong()); + buf.writeInt(dismountEntityID); } } } diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java index a490b27c..a37e48d8 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryItems.java @@ -266,6 +266,7 @@ public final class WizardryItems { public static final Item charm_haggler = placeholder(); public static final Item charm_experience_tome = placeholder(); public static final Item charm_move_speed = placeholder(); + public static final Item charm_spell_discovery = placeholder(); public static final Item charm_auto_smelt = placeholder(); public static final Item charm_lava_walking = placeholder(); public static final Item charm_storm = placeholder(); @@ -279,6 +280,7 @@ public final class WizardryItems { public static final Item charm_light = placeholder(); public static final Item charm_transportation = placeholder(); public static final Item charm_black_hole = placeholder(); + public static final Item charm_mount_teleporting = placeholder(); public static final Item charm_feeding = placeholder(); private static final Map, Item> WAND_MAP = new HashMap<>(); @@ -627,6 +629,7 @@ public final class WizardryItems { registerItem(registry, "charm_haggler", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_experience_tome", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_move_speed", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); + registerItem(registry, "charm_spell_discovery", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_auto_smelt", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_lava_walking", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_storm", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); @@ -640,6 +643,7 @@ public final class WizardryItems { registerItem(registry, "charm_light", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_transportation", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_black_hole", new ItemArtefact(EnumRarity.EPIC, ItemArtefact.Type.CHARM)); + registerItem(registry, "charm_mount_teleporting", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.CHARM)); registerItem(registry, "charm_feeding", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.CHARM)); } diff --git a/src/main/java/electroblob/wizardry/spell/Blink.java b/src/main/java/electroblob/wizardry/spell/Blink.java index 01c63c46..e45cb59d 100644 --- a/src/main/java/electroblob/wizardry/spell/Blink.java +++ b/src/main/java/electroblob/wizardry/spell/Blink.java @@ -1,13 +1,14 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.item.ItemArtefact; import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.BlockUtils; -import electroblob.wizardry.util.RayTracer; -import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.*; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.item.EntityBoat; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -15,6 +16,7 @@ import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; public class Blink extends Spell { @@ -27,8 +29,12 @@ public class Blink extends Spell { @Override public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ - RayTraceResult rayTrace = RayTracer.standardBlockRayTrace(world, caster, - getProperty(RANGE).doubleValue() * modifiers.get(WizardryItems.range_upgrade), false); + boolean teleportMount = caster.isRiding() && ItemArtefact.isArtefactActive(caster, WizardryItems.charm_mount_teleporting); + boolean hitLiquids = teleportMount && caster.getRidingEntity() instanceof EntityBoat; // Boats teleport to the surface + + double range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade); + + RayTraceResult rayTrace = RayTracer.standardBlockRayTrace(world, caster, range, hitLiquids, !hitLiquids,false); // It's worth noting that on the client side, the cast() method only gets called if the server side // cast method succeeded, so you need not check any conditions for spawning particles. @@ -48,32 +54,21 @@ public class Blink extends Spell { if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ - BlockPos pos = rayTrace.getBlockPos(); + BlockPos pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); + Entity toTeleport = teleportMount ? caster.getRidingEntity() : caster; - // Leave space for the player's head - if(rayTrace.sideHit == EnumFacing.DOWN) pos = pos.down(); + Vec3d vec = EntityUtils.findSpaceForTeleport(toTeleport, GeometryUtils.getFaceCentre(pos, EnumFacing.DOWN), teleportMount); - // This means stuff like snow layers is ignored, meaning when on snow-covered ground the player does - // not teleport 1 block above the ground. - if(rayTrace.sideHit == EnumFacing.UP && !world.getBlockState(pos).getMaterial().blocksMovement()){ - pos = pos.down(); + if(vec != null){ + // Plays before and after so it is heard from both positions + this.playSound(world, caster, ticksInUse, -1, modifiers); + + if(!teleportMount && caster.isRiding()) caster.dismountRidingEntity(); + if(!world.isRemote) toTeleport.setPositionAndUpdate(vec.x, vec.y, vec.z); + + this.playSound(world, caster, ticksInUse, -1, modifiers); + return true; } - - pos = pos.offset(rayTrace.sideHit); - - // Prevents the player from teleporting into blocks and suffocating. - if(world.getBlockState(pos).getMaterial().blocksMovement() - || world.getBlockState(pos.up()).getMaterial().blocksMovement()){ - return false; - } - - // Plays before and after so it is heard from both positions - this.playSound(world, caster, ticksInUse, -1, modifiers); - - if(!world.isRemote) caster.setPositionAndUpdate(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); - - this.playSound(world, caster, ticksInUse, -1, modifiers); - return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/PhaseStep.java b/src/main/java/electroblob/wizardry/spell/PhaseStep.java index 30f4b40a..9c521bc4 100644 --- a/src/main/java/electroblob/wizardry/spell/PhaseStep.java +++ b/src/main/java/electroblob/wizardry/spell/PhaseStep.java @@ -2,11 +2,12 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.item.ItemArtefact; import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.BlockUtils; -import electroblob.wizardry.util.RayTracer; -import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.util.*; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityBoat; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; @@ -28,8 +29,12 @@ public class PhaseStep extends Spell { @Override public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + boolean teleportMount = caster.isRiding() && ItemArtefact.isArtefactActive(caster, WizardryItems.charm_mount_teleporting); + boolean hitLiquids = teleportMount && caster.getRidingEntity() instanceof EntityBoat; // Boats teleport to the surface + double range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade); - RayTraceResult rayTrace = RayTracer.standardBlockRayTrace(world, caster, range, false); + + RayTraceResult rayTrace = RayTracer.standardBlockRayTrace(world, caster, range, hitLiquids, !hitLiquids, false); // This is here because the conditions are false on the client for whatever reason. (see the Javadoc for cast() // for an explanation) @@ -46,6 +51,8 @@ public class PhaseStep extends Spell { Wizardry.proxy.playBlinkEffect(caster); } + Entity toTeleport = teleportMount ? caster.getRidingEntity() : caster; + if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ BlockPos pos = rayTrace.getBlockPos(); @@ -68,66 +75,40 @@ public class PhaseStep extends Spell { && !Wizardry.settings.teleportThroughUnbreakableBlocks) break; // Don't return false yet, there are other possible outcomes below now - if(!world.getBlockState(pos1).getMaterial().blocksMovement() - && !world.getBlockState(pos1.up()).getMaterial().blocksMovement()){ - - // Plays before and after so it is heard from both positions - this.playSound(world, caster, ticksInUse, -1, modifiers); - - if(!world.isRemote){ - caster.setPositionAndUpdate(pos1.getX() + 0.5, pos1.getY() + 0.5, pos1.getZ() + 0.5); - } - - this.playSound(world, caster, ticksInUse, -1, modifiers); - return true; - } + Vec3d vec = GeometryUtils.getFaceCentre(pos1, EnumFacing.DOWN); + if(attemptTeleport(world, toTeleport, vec, teleportMount, caster, ticksInUse, modifiers)) return true; } // If no suitable position was found on the other side of the wall, works like blink instead - - // Leave space for the player's head - if(rayTrace.sideHit == EnumFacing.DOWN) pos = pos.down(); - - // This means stuff like snow layers is ignored, meaning when on snow-covered ground the player does - // not teleport 1 block above the ground. - if(rayTrace.sideHit == EnumFacing.UP && !world.getBlockState(pos).getMaterial().blocksMovement()){ - pos = pos.down(); - } - pos = pos.offset(rayTrace.sideHit); - // Prevents the player from teleporting into blocks and suffocating - if(world.getBlockState(pos).getMaterial().blocksMovement() - || world.getBlockState(pos.up()).getMaterial().blocksMovement()){ - return false; - } + Vec3d vec = GeometryUtils.getFaceCentre(pos, EnumFacing.DOWN); + if(attemptTeleport(world, toTeleport, vec, teleportMount, caster, ticksInUse, modifiers)) return true; + }else{ // The ray trace missed + Vec3d vec = caster.getPositionVector().add(caster.getLookVec().scale(range)); + if(attemptTeleport(world, toTeleport, vec, teleportMount, caster, ticksInUse, modifiers)) return true; + } + + return false; + } + + protected boolean attemptTeleport(World world, Entity toTeleport, Vec3d destination, boolean teleportMount, EntityPlayer caster, int ticksInUse, SpellModifiers modifiers){ + + destination = EntityUtils.findSpaceForTeleport(toTeleport, destination, teleportMount); + + if(destination != null){ // Plays before and after so it is heard from both positions this.playSound(world, caster, ticksInUse, -1, modifiers); - if(!world.isRemote) caster.setPositionAndUpdate(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + if(!teleportMount && caster.isRiding()) caster.dismountRidingEntity(); + if(!world.isRemote) toTeleport.setPositionAndUpdate(destination.x, destination.y, destination.z); this.playSound(world, caster, ticksInUse, -1, modifiers); - caster.swingArm(hand); - return true; - - }else{ // The ray trace missed - - Vec3d destination = caster.getPositionVector().add(caster.getLookVec().scale(range)); - BlockPos pos = new BlockPos(destination); - - // Prevents the player from teleporting into blocks and suffocating. - if(world.getBlockState(pos).getMaterial().blocksMovement() - || world.getBlockState(pos.up()).getMaterial().blocksMovement()){ - return false; - } - - if(!world.isRemote) caster.setPositionAndUpdate(destination.x, destination.y, destination.z); - - this.playSound(world, caster, ticksInUse, -1, modifiers); - caster.swingArm(hand); return true; } + + return false; } } diff --git a/src/main/java/electroblob/wizardry/spell/Transportation.java b/src/main/java/electroblob/wizardry/spell/Transportation.java index c5f57c9c..19a810f8 100644 --- a/src/main/java/electroblob/wizardry/spell/Transportation.java +++ b/src/main/java/electroblob/wizardry/spell/Transportation.java @@ -9,7 +9,11 @@ import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.packet.PacketTransportation; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.*; +import electroblob.wizardry.util.GeometryUtils; +import electroblob.wizardry.util.Location; +import electroblob.wizardry.util.NBTExtras; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; import net.minecraft.nbt.NBTTagList; @@ -177,9 +181,21 @@ public class Transportation extends Spell { Location destination = locations.get(locations.size() - 1); if(countdown == 1 && destination.dimension == player.dimension){ + + Entity mount = player.getRidingEntity(); + if(mount != null) player.dismountRidingEntity(); + player.setPositionAndUpdate(destination.pos.getX() + 0.5, destination.pos.getY(), destination.pos.getZ() + 0.5); + + boolean teleportMount = mount != null && ItemArtefact.isArtefactActive(player, WizardryItems.charm_mount_teleporting); + + if(teleportMount){ + mount.setPositionAndUpdate(destination.pos.getX() + 0.5, destination.pos.getY(), destination.pos.getZ() + 0.5); + player.startRiding(mount); + } + player.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 50, 0)); - IMessage msg = new PacketTransportation.Message(player.getEntityId()); + IMessage msg = new PacketTransportation.Message(destination.pos, teleportMount ? null : player); WizardryPacketHandler.net.sendToDimension(msg, player.world.provider.getDimension()); } diff --git a/src/main/java/electroblob/wizardry/util/EntityUtils.java b/src/main/java/electroblob/wizardry/util/EntityUtils.java index bf827af2..dbbf488d 100644 --- a/src/main/java/electroblob/wizardry/util/EntityUtils.java +++ b/src/main/java/electroblob/wizardry/util/EntityUtils.java @@ -1,5 +1,6 @@ package electroblob.wizardry.util; +import com.google.common.collect.Streams; import electroblob.wizardry.Wizardry; import electroblob.wizardry.data.WizardData; import electroblob.wizardry.entity.living.ISpellCaster; @@ -17,17 +18,24 @@ import net.minecraft.item.ItemStack; import net.minecraft.network.datasync.DataParameter; import net.minecraft.server.MinecraftServer; import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.EnumDifficulty; import net.minecraft.world.World; import net.minecraftforge.event.ForgeEventFactory; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; import javax.annotation.Nullable; +import java.util.Comparator; import java.util.List; import java.util.UUID; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Contains useful static methods for retrieving and interacting with players, mobs and other entities. These methods @@ -182,6 +190,83 @@ public final class EntityUtils { target.knockBack(attacker, 0.4f, dx, dz); } + /** + * Finds the nearest space to the specified position that the given entity can teleport to without being inside one + * or more solid blocks. The search volume is twice the size of the entity's bounding box (meaning that when + * teleported to the returned position, the original destination remains within the entity's bounding box). + * @param entity The entity being teleported + * @param destination The target position to search around + * @param accountForPassengers True to take passengers into account when searching for a space, false to ignore them + * @return The resulting position, or null if no space was found. + */ + public static Vec3d findSpaceForTeleport(Entity entity, Vec3d destination, boolean accountForPassengers){ + + World world = entity.world; + AxisAlignedBB box = entity.getEntityBoundingBox(); + + if(accountForPassengers){ + for(Entity passenger : entity.getPassengers()){ + box = box.union(passenger.getEntityBoundingBox()); + } + } + + box = box.offset(destination.subtract(entity.posX, box.minY, entity.posZ)); + + // All the parameters of this method are INCLUSIVE, so even the max coordinates should be rounded down + Iterable cuboid = BlockPos.getAllInBox(MathHelper.floor(box.minX), MathHelper.floor(box.minY), + MathHelper.floor(box.minZ), MathHelper.floor(box.maxX), MathHelper.floor(box.maxY), MathHelper.floor(box.maxZ)); + + if(Streams.stream(cuboid).noneMatch(b -> world.collidesWithAnyBlock(new AxisAlignedBB(b)))){ + // Nothing in the way + return destination; + + }else{ + // Nearby position search + double dx = box.maxX - box.minX; + double dy = box.maxY - box.minY; + double dz = box.maxZ - box.minZ; + + // Minimum space required is (nx + px) blocks * (ny + py) blocks * (nz + pz) blocks + int nx = MathHelper.ceil(dx) / 2; + int px = MathHelper.ceil(dx) - nx; + int ny = MathHelper.ceil(dy) / 2; + int py = MathHelper.ceil(dy) - ny; + int nz = MathHelper.ceil(dz) / 2; + int pz = MathHelper.ceil(dz) - nz; + + // Check all the blocks in and around the bounding box... + List nearby = Streams.stream(BlockPos.getAllInBox(MathHelper.floor(box.minX) - 1, + MathHelper.floor(box.minY) - 1, MathHelper.floor(box.minZ) - 1, + MathHelper.floor(box.maxX) + 1, MathHelper.floor(box.maxY) + 1, + MathHelper.floor(box.maxZ) + 1)).collect(Collectors.toList()); + + // ... but only return positions actually inside the box + List possiblePositions = Streams.stream(cuboid).collect(Collectors.toList()); + + // Rather than iterate over each position and check if the box fits, find all solid blocks and cut out all + // positions whose corresponding box would include them - this is waaay more efficient! + while(!nearby.isEmpty()){ + + BlockPos pos = nearby.remove(0); + + if(world.collidesWithAnyBlock(new AxisAlignedBB(pos))){ + Predicate nearSolidBlock = b -> b.getX() >= pos.getX() - nx && b.getX() <= pos.getX() + px + && b.getY() >= pos.getY() - ny && b.getY() <= pos.getY() + py + && b.getZ() >= pos.getZ() - nz && b.getZ() <= pos.getZ() + pz; + nearby.removeIf(nearSolidBlock); + possiblePositions.removeIf(nearSolidBlock); + } + } + + if(possiblePositions.isEmpty()) return null; // No space nearby + + BlockPos nearest = possiblePositions.stream().min(Comparator.comparingDouble(b -> destination.squareDistanceTo( + b.getX() + 0.5, b.getY() + 0.5, b.getZ() + 0.5))).get(); // The list can't be empty + + return GeometryUtils.getFaceCentre(nearest, EnumFacing.DOWN); + } + } + // Damage // =============================================================================================================== diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 325c1eb2..8fc1175e 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -370,6 +370,8 @@ item.ebwizardry\:charm_experience_tome.name=Tome of the Diligent item.ebwizardry\:charm_experience_tome.desc=Increases the rate at which wands gain progression by 50%% item.ebwizardry\:charm_move_speed.name=Icarus Medallion item.ebwizardry\:charm_move_speed.desc=Move at near-full speed when casting spells +item.ebwizardry\:charm_spell_discovery.name=Archivist's Eyeglass +item.ebwizardry\:charm_spell_discovery.desc=Spell books found in chests are much more likely to be ones you have not yet discovered item.ebwizardry\:charm_auto_smelt.name=Metallurgist's Mark item.ebwizardry\:charm_auto_smelt.desc=Pocket furnace triggers automatically when bound to a wand on your hotbar item.ebwizardry\:charm_lava_walking.name=Nether Ice Core @@ -396,6 +398,8 @@ item.ebwizardry\:charm_transportation.name=Ancient Compass item.ebwizardry\:charm_transportation.desc=Up to four stone circles may be remembered and selected from when using transportation item.ebwizardry\:charm_black_hole.name=Void Opal item.ebwizardry\:charm_black_hole.desc=Black hole sucks in and destroys blocks +item.ebwizardry\:charm_mount_teleporting.name=Spectral Tether +item.ebwizardry\:charm_mount_teleporting.desc=Teleporting while riding an animal or vehicle brings it with you item.ebwizardry\:charm_feeding.name=Bottomless Provisions item.ebwizardry\:charm_feeding.desc=Replenish hunger and satiety trigger automatically when bound to a wand on your hotbar diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 4156c4bc..7b9df5c4 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -370,6 +370,8 @@ item.ebwizardry\:charm_experience_tome.name=Tome of the Diligent item.ebwizardry\:charm_experience_tome.desc=Increases the rate at which wands gain progression by 50%% item.ebwizardry\:charm_move_speed.name=Icarus Medallion item.ebwizardry\:charm_move_speed.desc=Move at near-full speed when casting spells +item.ebwizardry\:charm_spell_discovery.name=Archivist's Eyeglass +item.ebwizardry\:charm_spell_discovery.desc=Spell books found in chests are much more likely to be ones you have not yet discovered item.ebwizardry\:charm_auto_smelt.name=Metallurgist's Mark item.ebwizardry\:charm_auto_smelt.desc=Pocket furnace triggers automatically when bound to a wand on your hotbar item.ebwizardry\:charm_lava_walking.name=Nether Ice Core @@ -396,6 +398,8 @@ item.ebwizardry\:charm_transportation.name=Ancient Compass item.ebwizardry\:charm_transportation.desc=Up to four stone circles may be remembered and selected from when using transportation item.ebwizardry\:charm_black_hole.name=Void Opal item.ebwizardry\:charm_black_hole.desc=Black hole sucks in and destroys blocks +item.ebwizardry\:charm_mount_teleporting.name=Spectral Tether +item.ebwizardry\:charm_mount_teleporting.desc=Teleporting while riding an animal or vehicle brings it with you item.ebwizardry\:charm_feeding.name=Bottomless Provisions item.ebwizardry\:charm_feeding.desc=Replenish hunger and satiety trigger automatically when bound to a wand on your hotbar diff --git a/src/main/resources/assets/ebwizardry/models/item/charm_mount_teleporting.json b/src/main/resources/assets/ebwizardry/models/item/charm_mount_teleporting.json new file mode 100644 index 00000000..b0d8b187 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/charm_mount_teleporting.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/charm_mount_teleporting" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/models/item/charm_spell_discovery.json b/src/main/resources/assets/ebwizardry/models/item/charm_spell_discovery.json new file mode 100644 index 00000000..061c845f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/models/item/charm_spell_discovery.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "ebwizardry:items/charm_spell_discovery" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/items/charm_mount_teleporting.png b/src/main/resources/assets/ebwizardry/textures/items/charm_mount_teleporting.png new file mode 100644 index 0000000000000000000000000000000000000000..4ca520fc6e455ca4da86e65b58789ed4ba00a5aa GIT binary patch literal 6418 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 literal 0 HcmV?d00001
72n%YE_v^Y7{F*2~8q3pP9~@VIPp z$nyFu%N-xB7&%UVfAuKAMy|b>>En6kh6Pg&Szh-%v5DusZRNb~yLs2Mlnxqjz20>! z>b8{F#=YO?WVRIRX)I-EGIZ;>r@m<2>vQgtg1S6DKL6Qy{5SiSYY&?gf*2l2wN5em z@JD6Kwe6e?U5j=Y_<7y@cJKRxb^7<-f8SH_b&@s9^2?nno>s?t-4>sJ{#hZ%or literal 0 HcmV?d00001