That's one heck of a commit you've got there...
I may have got a bit behind with version control. A lot behind, in fact. Maybe I'll go back and split this sometime - then again, I probably won't. But hey, at least it's here!
This commit is contained in:
@@ -1,63 +1,197 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.block.BlockTransportationStone;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.data.IStoredVariable;
|
||||
import electroblob.wizardry.data.Persistence;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.packet.PacketTransportation;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.Location;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Transportation extends Spell {
|
||||
|
||||
public static final String TELEPORT_COUNTDOWN = "teleport_countdown";
|
||||
|
||||
public static final int MAX_REMEMBERED_LOCATIONS = 4;
|
||||
|
||||
// For some reason 'the diamond' doesn't work if I chain methods onto this. Type inference is weird.
|
||||
public static final IStoredVariable<List<Location>> LOCATIONS_KEY = new IStoredVariable.StoredVariable<List<Location>, NBTTagList>("stoneCirclePos",
|
||||
s -> NBTExtras.listToNBT(s, Location::toNBT), t -> new ArrayList<>(NBTExtras.NBTToList(t, Location::fromNBT)), Persistence.ALWAYS).setSynced();
|
||||
public static final IStoredVariable<Integer> COUNTDOWN_KEY = IStoredVariable.StoredVariable.ofInt("tpCountdown", Persistence.NEVER).withTicker(Transportation::update);
|
||||
|
||||
public Transportation(){
|
||||
super("transportation", Tier.ADVANCED, Element.SORCERY, SpellType.UTILITY, 100, 100, EnumAction.BOW, false);
|
||||
super("transportation", EnumAction.BOW, false);
|
||||
addProperties(TELEPORT_COUNTDOWN);
|
||||
WizardData.registerStoredVariables(LOCATIONS_KEY, COUNTDOWN_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
WizardData properties = WizardData.get(caster);
|
||||
WizardData data = WizardData.get(caster);
|
||||
// Fixes the sound not playing in first person.
|
||||
if(world.isRemote) WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_PORTAL_TRIGGER, 1.0f, 1.0f);
|
||||
if(world.isRemote) this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
// Only works when the caster is in the same dimension.
|
||||
if(properties != null && properties.getTpCountdown() == 0){
|
||||
if(caster.dimension == properties.getStoneCircleDimension()){
|
||||
// Has to be y since x and z could reasonably be -1.
|
||||
if(properties.getStoneCircleLocation() != null){
|
||||
if(BlockTransportationStone.testForCircle(world, properties.getStoneCircleLocation())){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_PORTAL_TRIGGER, 1.0f, 1.0f);
|
||||
caster.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, 150, 0));
|
||||
properties.setTpCountdown(75);
|
||||
return true;
|
||||
}else{
|
||||
if(!world.isRemote)
|
||||
caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".missing"), true);
|
||||
}
|
||||
}else{
|
||||
if(!world.isRemote)
|
||||
caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".undefined"), true);
|
||||
if(data != null){
|
||||
|
||||
Integer countdown = data.getVariable(COUNTDOWN_KEY);
|
||||
|
||||
if(countdown == null || countdown == 0){
|
||||
|
||||
List<Location> locations = data.getVariable(Transportation.LOCATIONS_KEY);
|
||||
|
||||
if(locations == null) data.setVariable(Transportation.LOCATIONS_KEY, locations = new ArrayList<>(Transportation.MAX_REMEMBERED_LOCATIONS));
|
||||
|
||||
if(locations.isEmpty()){
|
||||
if(!world.isRemote) caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".undefined"), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ItemArtefact.isArtefactActive(caster, WizardryItems.charm_transportation)){
|
||||
|
||||
List<Location> locationsInDimension = locations.stream().filter(l -> l.dimension == caster.dimension).collect(Collectors.toList());
|
||||
|
||||
if(locationsInDimension.isEmpty()){
|
||||
if(!world.isRemote) caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".wrongdimension"), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
Location destination = getLocationAimedAt(caster, locationsInDimension, 1);
|
||||
|
||||
if(destination == null) return false; // None of them were aimed at
|
||||
|
||||
if(attemptTravelTo(caster, world, destination.pos, modifiers)){
|
||||
// Move the selected destination to the end of the list, making it the 'most recent' one
|
||||
// This makes my life easier in update() below, and is a kind of useful feature too
|
||||
locations.remove(destination);
|
||||
locations.add(destination);
|
||||
if(!world.isRemote) data.sync();
|
||||
return true;
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
Location destination = locations.get(locations.size() - 1); // The most recent one, or the only one
|
||||
|
||||
if(destination.dimension == caster.dimension){
|
||||
return attemptTravelTo(caster, world, destination.pos, modifiers);
|
||||
}else{
|
||||
if(!world.isRemote) caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".wrongdimension"), true);
|
||||
}
|
||||
|
||||
}
|
||||
}else{
|
||||
if(!world.isRemote)
|
||||
caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".wrongdimension"), true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The following four methods centralise and neaten up the code
|
||||
// Since the deviation angle is also used by the UI renderer, this also ensures they use the same calculation
|
||||
|
||||
/** Returns the location from the given list that the give player is aiming at, or null if they are not aiming
|
||||
* at any of them. */
|
||||
public static Location getLocationAimedAt(EntityPlayer player, List<Location> locations, float partialTicks){
|
||||
return locations.stream()
|
||||
.filter(l -> isLocationAimedAt(player, l.pos, partialTicks))
|
||||
.min(Comparator.comparingDouble(l -> getLookDeviationAngle(player, l.pos, partialTicks)))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static boolean isLocationAimedAt(EntityPlayer player, BlockPos pos, float partialTicks){
|
||||
|
||||
Vec3d origin = player.getPositionEyes(partialTicks);
|
||||
Vec3d centre = WizardryUtilities.getCentre(pos);
|
||||
Vec3d direction = centre.subtract(origin);
|
||||
double distance = direction.length();
|
||||
|
||||
return getLookDeviationAngle(player, pos, partialTicks) < getIconSize(distance);
|
||||
}
|
||||
|
||||
public static double getLookDeviationAngle(EntityPlayer player, BlockPos pos, float partialTicks){
|
||||
|
||||
Vec3d origin = player.getPositionEyes(partialTicks);
|
||||
Vec3d look = player.getLook(partialTicks);
|
||||
Vec3d centre = WizardryUtilities.getCentre(pos);
|
||||
Vec3d direction = centre.subtract(origin);
|
||||
double distance = direction.length();
|
||||
|
||||
return Math.acos(direction.dotProduct(look) / distance); // Angle between a and b = acos((a.b) / (|a|*|b|))
|
||||
}
|
||||
|
||||
public static double getIconSize(double distance){
|
||||
return 0.05 + 2/(distance + 5);
|
||||
}
|
||||
|
||||
private boolean attemptTravelTo(EntityPlayer player, World world, BlockPos destination, SpellModifiers modifiers){
|
||||
|
||||
WizardData data = WizardData.get(player);
|
||||
|
||||
if(BlockTransportationStone.testForCircle(world, destination)){
|
||||
this.playSound(world, player, 0, -1, modifiers);
|
||||
player.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, 150, 0));
|
||||
data.setVariable(COUNTDOWN_KEY, getProperty(TELEPORT_COUNTDOWN).intValue());
|
||||
return true;
|
||||
}else{
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".missing"), true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static int update(EntityPlayer player, Integer countdown){
|
||||
|
||||
if(countdown == null) return 0;
|
||||
|
||||
if(!player.world.isRemote){
|
||||
|
||||
WizardData data = WizardData.get(player);
|
||||
|
||||
List<Location> locations = data.getVariable(Transportation.LOCATIONS_KEY);
|
||||
if(locations == null || locations.isEmpty()) return 0;
|
||||
|
||||
// If the location was selected, either it was already at the end of the list or it was moved there
|
||||
Location destination = locations.get(locations.size() - 1);
|
||||
|
||||
if(countdown == 1 && destination.dimension == player.dimension){
|
||||
player.setPositionAndUpdate(destination.pos.getX() + 0.5, destination.pos.getY(), destination.pos.getZ() + 0.5);
|
||||
player.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 50, 0));
|
||||
IMessage msg = new PacketTransportation.Message(player.getEntityId());
|
||||
WizardryPacketHandler.net.sendToDimension(msg, player.world.provider.getDimension());
|
||||
}
|
||||
|
||||
if(countdown > 0){
|
||||
countdown--;
|
||||
}
|
||||
}
|
||||
|
||||
return countdown;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user