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:
@@ -3,6 +3,7 @@ package electroblob.wizardry.entity.construct;
|
||||
import net.minecraft.entity.projectile.EntityTippedArrow;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityArrowRain extends EntityMagicConstruct {
|
||||
@@ -20,9 +21,9 @@ public class EntityArrowRain extends EntityMagicConstruct {
|
||||
if(!this.world.isRemote){
|
||||
EntityTippedArrow arrow = new EntityTippedArrow(world, this.posX + rand.nextDouble() * 6 - 3,
|
||||
this.posY + rand.nextDouble() * 4 - 2, this.posZ + rand.nextDouble() * 6 - 3);
|
||||
arrow.motionX = Math.cos(Math.toRadians(this.rotationYaw + 90));
|
||||
arrow.motionX = MathHelper.cos((float)Math.toRadians(this.rotationYaw + 90));
|
||||
arrow.motionY = -0.6;
|
||||
arrow.motionZ = Math.sin(Math.toRadians(this.rotationYaw + 90));
|
||||
arrow.motionZ = MathHelper.sin((float)Math.toRadians(this.rotationYaw + 90));
|
||||
arrow.shootingEntity = this.getCaster();
|
||||
arrow.setDamage(7.0d * damageMultiplier);
|
||||
arrow.setPotionEffect(new ItemStack(Items.ARROW));
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
@@ -16,7 +18,11 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityBlackHole extends EntityMagicConstruct {
|
||||
|
||||
private static final double SUCTION_STRENGTH = 0.075;
|
||||
|
||||
public int[] randomiser;
|
||||
public int[] randomiser2;
|
||||
@@ -69,42 +75,49 @@ public class EntityBlackHole extends EntityMagicConstruct {
|
||||
}
|
||||
|
||||
if(this.lifetime - this.ticksExisted == 75){
|
||||
this.playSound(SoundEvents.BLOCK_PORTAL_TRIGGER, 1.5f, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_BLACK_HOLE_VANISH, 1.5f, 1.0f);
|
||||
}else if(this.ticksExisted % 80 == 1 && this.ticksExisted + 80 < this.lifetime){
|
||||
this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.5f, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_BLACK_HOLE_AMBIENT, 1.5f, 1.0f);
|
||||
}
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(6.0d, this.posX, this.posY,
|
||||
this.posZ, this.world);
|
||||
|
||||
if(!this.world.isRemote){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(6.0d, this.posX, this.posY,
|
||||
this.posZ, this.world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
|
||||
if(this.isValidTarget(target)){
|
||||
|
||||
// Sucks the target in
|
||||
if(this.posX > target.posX && target.motionX < 1){
|
||||
target.motionX += 0.1;
|
||||
}else if(this.posX < target.posX && target.motionX > -1){
|
||||
target.motionX -= 0.1;
|
||||
}
|
||||
// If the target can't be moved, it isn't sucked in but is still damaged if it gets too close
|
||||
if(!(target instanceof EntityPlayer && ((getCaster() instanceof EntityPlayer && !Wizardry.settings.playersMoveEachOther)
|
||||
|| ItemArtefact.isArtefactActive((EntityPlayer)target, WizardryItems.amulet_anchoring)))){
|
||||
|
||||
if(this.posY > target.posY && target.motionY < 1){
|
||||
target.motionY += 0.1;
|
||||
}else if(this.posY < target.posY && target.motionY > -1){
|
||||
target.motionY -= 0.1;
|
||||
}
|
||||
WizardryUtilities.undoGravity(target);
|
||||
|
||||
if(this.posZ > target.posZ && target.motionZ < 1){
|
||||
target.motionZ += 0.1;
|
||||
}else if(this.posZ < target.posZ && target.motionZ > -1){
|
||||
target.motionZ -= 0.1;
|
||||
}
|
||||
// Sucks the target in
|
||||
if(this.posX > target.posX && target.motionX < 1){
|
||||
target.motionX += SUCTION_STRENGTH;
|
||||
}else if(this.posX < target.posX && target.motionX > -1){
|
||||
target.motionX -= SUCTION_STRENGTH;
|
||||
}
|
||||
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
if(this.posY > target.posY && target.motionY < 1){
|
||||
target.motionY += SUCTION_STRENGTH;
|
||||
}else if(this.posY < target.posY && target.motionY > -1){
|
||||
target.motionY -= SUCTION_STRENGTH;
|
||||
}
|
||||
|
||||
if(this.posZ > target.posZ && target.motionZ < 1){
|
||||
target.motionZ += SUCTION_STRENGTH;
|
||||
}else if(this.posZ < target.posZ && target.motionZ > -1){
|
||||
target.motionZ -= SUCTION_STRENGTH;
|
||||
}
|
||||
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
}
|
||||
}
|
||||
|
||||
if(this.getDistance(target) <= 2){
|
||||
@@ -128,4 +141,9 @@ public class EntityBlackHole extends EntityMagicConstruct {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderInPass(int pass){
|
||||
return pass == 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -14,6 +14,8 @@ import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityBlizzard extends EntityMagicConstruct {
|
||||
|
||||
public EntityBlizzard(World world){
|
||||
@@ -25,14 +27,18 @@ public class EntityBlizzard extends EntityMagicConstruct {
|
||||
public void onUpdate(){
|
||||
|
||||
if(this.ticksExisted % 120 == 1){
|
||||
this.playSound(WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_BLIZZARD_AMBIENT, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
// This is a good example of why you might define a spell base property without necessarily using it in the
|
||||
// spell - in fact, blizzard doesn't even have a spell class (yet)
|
||||
double radius = Spells.blizzard.getProperty(Spell.EFFECT_RADIUS).doubleValue();
|
||||
|
||||
if(!this.world.isRemote){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(3.0d, this.posX, this.posY,
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(radius, this.posX, this.posY,
|
||||
this.posZ, this.world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
@@ -59,7 +65,7 @@ public class EntityBlizzard extends EntityMagicConstruct {
|
||||
for(int i=1; i<12; i++){
|
||||
double speed = (rand.nextBoolean() ? 1 : -1) * 0.1 + 0.05 * rand.nextDouble();
|
||||
ParticleBuilder.create(Type.SNOW).pos(this.posX, this.posY + rand.nextDouble() * 3, this.posZ).vel(0, 0, 0)
|
||||
.time(100).scale(2).spin(rand.nextDouble() * 2.5 + 0.5, speed).spawn(world);
|
||||
.time(100).scale(2).spin(rand.nextDouble() * (radius - 0.5) + 0.5, speed).spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Entrapment;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
@@ -17,6 +17,8 @@ import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class EntityBubble extends EntityMagicConstruct {
|
||||
|
||||
@@ -46,7 +48,7 @@ public class EntityBubble extends EntityMagicConstruct {
|
||||
if((this.rider == null || this.rider.get() == null)
|
||||
&& WizardryUtilities.getRider(this) instanceof EntityLivingBase
|
||||
&& !WizardryUtilities.getRider(this).isDead){
|
||||
this.rider = new WeakReference<EntityLivingBase>((EntityLivingBase)WizardryUtilities.getRider(this));
|
||||
this.rider = new WeakReference<>((EntityLivingBase)WizardryUtilities.getRider(this));
|
||||
}
|
||||
|
||||
// Prevents dismounting
|
||||
@@ -62,7 +64,8 @@ public class EntityBubble extends EntityMagicConstruct {
|
||||
|
||||
if(isDarkOrb){
|
||||
|
||||
if(WizardryUtilities.getRider(this) != null && this.ticksExisted % 30 == 0){
|
||||
if(WizardryUtilities.getRider(this) != null
|
||||
&& this.ticksExisted % Spells.entrapment.getProperty(Entrapment.DAMAGE_INTERVAL).intValue() == 0){
|
||||
if(this.getCaster() != null){
|
||||
WizardryUtilities.getRider(this).attackEntityFrom(
|
||||
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC),
|
||||
@@ -81,16 +84,16 @@ public class EntityBubble extends EntityMagicConstruct {
|
||||
(this.rand.nextDouble() - 0.5D) * 2.0D);
|
||||
}
|
||||
if(lifetime - this.ticksExisted == 75){
|
||||
this.playSound(SoundEvents.BLOCK_PORTAL_TRIGGER, 1.5f, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_ENTRAPMENT_VANISH, 1.5f, 1.0f);
|
||||
}else if(this.ticksExisted % 100 == 1 && this.ticksExisted < 150){
|
||||
this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.5f, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_ENTRAPMENT_AMBIENT, 1.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble bursts if the entity is hurt (see event handler) or killed, or if the bubble has existed for more than
|
||||
// 10 seconds.
|
||||
if(WizardryUtilities.getRider(this) == null && this.ticksExisted > 1){
|
||||
if(!this.isDarkOrb) this.playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f);
|
||||
if(!this.isDarkOrb) this.playSound(WizardrySounds.ENTITY_BUBBLE_POP, 1.5f, 1.0f);
|
||||
this.setDead();
|
||||
}
|
||||
}
|
||||
@@ -100,7 +103,7 @@ public class EntityBubble extends EntityMagicConstruct {
|
||||
if(WizardryUtilities.getRider(this) != null){
|
||||
((EntityLivingBase)WizardryUtilities.getRider(this)).dismountEntity(this);
|
||||
}
|
||||
if(!this.isDarkOrb) this.playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f);
|
||||
if(!this.isDarkOrb) this.playSound(WizardrySounds.ENTITY_BUBBLE_POP, 1.5f, 1.0f);
|
||||
super.despawn();
|
||||
}
|
||||
|
||||
@@ -133,7 +136,7 @@ public class EntityBubble extends EntityMagicConstruct {
|
||||
// Bursts bubble when the creature inside takes damage
|
||||
if(event.getEntityLiving().getRidingEntity() instanceof EntityBubble
|
||||
&& !((EntityBubble)event.getEntityLiving().getRidingEntity()).isDarkOrb){
|
||||
event.getEntityLiving().getRidingEntity().playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f);
|
||||
event.getEntityLiving().getRidingEntity().playSound(WizardrySounds.ENTITY_BUBBLE_POP, 1.5f, 1.0f);
|
||||
event.getEntityLiving().getRidingEntity().setDead();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityCombustionRune extends EntityMagicConstruct {
|
||||
|
||||
public EntityCombustionRune(World world){
|
||||
super(world);
|
||||
this.height = 0.2f;
|
||||
this.width = 2.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
if(!this.world.isRemote){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(width/2, posX, posY, posZ, world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
|
||||
if(this.isValidTarget(target)){
|
||||
|
||||
float strength = Spells.combustion_rune.getProperty(Spell.BLAST_RADIUS).floatValue();
|
||||
|
||||
world.newExplosion(this.getCaster(), this.posX, this.posY, this.posZ, strength, true, true);
|
||||
|
||||
// The trap is destroyed once triggered.
|
||||
this.setDead();
|
||||
}
|
||||
}
|
||||
}else if(this.rand.nextInt(15) == 0){
|
||||
double radius = 0.5 + rand.nextDouble() * 0.3;
|
||||
float angle = rand.nextFloat() * (float)Math.PI * 2;
|
||||
world.spawnParticle(EnumParticleTypes.FLAME, this.posX + radius * MathHelper.cos(angle), this.posY + 0.1,
|
||||
this.posZ + radius * MathHelper.sin(angle), 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void entityInit(){}
|
||||
|
||||
@Override
|
||||
public boolean canRenderOnFire(){
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +1,20 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityDecay extends EntityMagicConstruct {
|
||||
|
||||
public int textureIndex = 0;
|
||||
@@ -29,7 +32,7 @@ public class EntityDecay extends EntityMagicConstruct {
|
||||
super.onUpdate();
|
||||
|
||||
if(this.rand.nextInt(700) == 0 && this.ticksExisted + 100 < lifetime)
|
||||
this.playSound(SoundEvents.BLOCK_LAVA_AMBIENT, 0.2F + rand.nextFloat() * 0.2F,
|
||||
this.playSound(WizardrySounds.ENTITY_DECAY_AMBIENT, 0.2F + rand.nextFloat() * 0.2F,
|
||||
0.6F + rand.nextFloat() * 0.15F);
|
||||
|
||||
if(!this.world.isRemote){
|
||||
@@ -41,18 +44,19 @@ public class EntityDecay extends EntityMagicConstruct {
|
||||
// damaged each tick.
|
||||
// In this case, we do want particles to be shown.
|
||||
if(!target.isPotionActive(WizardryPotions.decay))
|
||||
target.addPotionEffect(new PotionEffect(WizardryPotions.decay, lifetime, 0));
|
||||
target.addPotionEffect(new PotionEffect(WizardryPotions.decay,
|
||||
Spells.decay.getProperty(Spell.EFFECT_DURATION).intValue(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
}else if(this.rand.nextInt(15) == 0){
|
||||
|
||||
double radius = rand.nextDouble() * 0.8;
|
||||
double angle = rand.nextDouble() * Math.PI * 2;
|
||||
float angle = rand.nextFloat() * (float)Math.PI * 2;
|
||||
float brightness = rand.nextFloat() * 0.4f;
|
||||
|
||||
ParticleBuilder.create(Type.DARK_MAGIC)
|
||||
.pos(this.posX + radius * Math.cos(angle), this.posY, this.posZ + radius * Math.sin(angle))
|
||||
.pos(this.posX + radius * MathHelper.cos(angle), this.posY, this.posZ + radius * MathHelper.sin(angle))
|
||||
.clr(brightness, 0, brightness + 0.1f)
|
||||
.spawn(world);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Earthquake;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
@@ -13,8 +13,11 @@ import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityEarthquake extends EntityMagicConstruct {
|
||||
|
||||
public EntityEarthquake(World world){
|
||||
@@ -27,21 +30,20 @@ public class EntityEarthquake extends EntityMagicConstruct {
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
double speed = Spells.earthquake.getProperty(Earthquake.SPREAD_SPEED).doubleValue();
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
double speed = 0.4;
|
||||
|
||||
// The further the earthquake is going to spread, the finer the angle increments.
|
||||
for(double angle = 0; angle < 2 * Math.PI; angle += Math.PI / (lifetime * 1.5)){
|
||||
for(float angle = 0; angle < 2 * Math.PI; angle += Math.PI / (lifetime * 1.5)){
|
||||
|
||||
// Calculates coordinates for the block to be moved. The radius increases with time. The +1.5 is to
|
||||
// leave
|
||||
// blocks in the centre untouched.
|
||||
int x = this.posX < 0 ? (int)(this.posX + ((this.ticksExisted * speed) + 1.5) * Math.sin(angle) - 1)
|
||||
: (int)(this.posX + ((this.ticksExisted * speed) + 1.5) * Math.sin(angle));
|
||||
// leave blocks in the centre untouched.
|
||||
int x = this.posX < 0 ? (int)(this.posX + ((this.ticksExisted * speed) + 1.5) * MathHelper.sin(angle) - 1)
|
||||
: (int)(this.posX + ((this.ticksExisted * speed) + 1.5) * MathHelper.sin(angle));
|
||||
int y = (int)(this.posY - 0.5);
|
||||
int z = this.posZ < 0 ? (int)(this.posZ + ((this.ticksExisted * speed) + 1.5) * Math.cos(angle) - 1)
|
||||
: (int)(this.posZ + ((this.ticksExisted * speed) + 1.5) * Math.cos(angle));
|
||||
int z = this.posZ < 0 ? (int)(this.posZ + ((this.ticksExisted * speed) + 1.5) * MathHelper.cos(angle) - 1)
|
||||
: (int)(this.posZ + ((this.ticksExisted * speed) + 1.5) * MathHelper.cos(angle));
|
||||
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
|
||||
@@ -58,50 +60,53 @@ public class EntityEarthquake extends EntityMagicConstruct {
|
||||
}
|
||||
}
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities
|
||||
.getEntitiesWithinRadius((this.ticksExisted * speed) + 1.5, this.posX, this.posY, this.posZ, world);
|
||||
}
|
||||
|
||||
// In this particular instance, the caster is completely unaffected because they will always be in the
|
||||
// centre.
|
||||
targets.remove(this.getCaster());
|
||||
List<EntityLivingBase> targets = WizardryUtilities
|
||||
.getEntitiesWithinRadius((this.ticksExisted * speed) + 1.5, this.posX, this.posY, this.posZ, world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
// In this particular instance, the caster is completely unaffected because they will always be in the
|
||||
// centre.
|
||||
targets.remove(this.getCaster());
|
||||
|
||||
// Searches in a 1 wide ring.
|
||||
if(this.getDistance(target) > (this.ticksExisted * speed) + 0.5 && target.posY < this.posY + 1
|
||||
&& target.posY > this.posY - 1){
|
||||
for(EntityLivingBase target : targets){
|
||||
|
||||
// Knockback must be removed in this instance, or the target will fall into the floor.
|
||||
double motionX = target.motionX;
|
||||
double motionZ = target.motionZ;
|
||||
// Searches in a 1 wide ring.
|
||||
if(this.getDistance(target) > (this.ticksExisted * speed) + 0.5 && target.posY < this.posY + 1
|
||||
&& target.posY > this.posY - 1){
|
||||
|
||||
if(this.isValidTarget(target)){
|
||||
target.attackEntityFrom(
|
||||
MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.BLAST),
|
||||
10 * this.damageMultiplier);
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 400, 1));
|
||||
}
|
||||
// Knockback must be removed in this instance, or the target will fall into the floor.
|
||||
double motionX = target.motionX;
|
||||
double motionZ = target.motionZ;
|
||||
|
||||
// All targets are thrown, even those immune to the damage, so they don't fall into the ground.
|
||||
target.motionX = motionX;
|
||||
target.motionY = 0.8; // Throws target into the air.
|
||||
target.motionZ = motionZ;
|
||||
if(this.isValidTarget(target)){
|
||||
target.attackEntityFrom(
|
||||
MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.BLAST),
|
||||
10 * this.damageMultiplier);
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 400, 1));
|
||||
}
|
||||
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
}
|
||||
// All targets are thrown, even those immune to the damage, so they don't fall into the ground.
|
||||
target.motionX = motionX;
|
||||
target.motionY = 0.8; // Throws target into the air.
|
||||
target.motionZ = motionZ;
|
||||
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// Constant 15 blocks for now
|
||||
List<EntityPlayer> targets = WizardryUtilities.getEntitiesWithinRadius(15, posX, posY, posZ, world, EntityPlayer.class);
|
||||
}
|
||||
|
||||
float magnitude = 6f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime;
|
||||
if(!world.isRemote){
|
||||
// Constant 15 blocks for now
|
||||
List<EntityPlayer> targets2 = WizardryUtilities.getEntitiesWithinRadius(15, posX, posY, posZ, world, EntityPlayer.class);
|
||||
|
||||
float magnitude = 10f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime;
|
||||
|
||||
// Makes the screen shake
|
||||
for(EntityPlayer target : targets){
|
||||
target.rotationPitch = this.ticksExisted % 2 == 0 ? magnitude : -magnitude;
|
||||
for(EntityPlayer target : targets2){
|
||||
target.rotationPitch += this.ticksExisted % 2 == 0 ? magnitude : -magnitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityFireRing extends EntityMagicConstruct {
|
||||
|
||||
// TODO: Implement blast modifiers
|
||||
|
||||
public EntityFireRing(World world){
|
||||
super(world);
|
||||
this.height = 1.0f;
|
||||
@@ -21,7 +25,7 @@ public class EntityFireRing extends EntityMagicConstruct {
|
||||
public void onUpdate(){
|
||||
|
||||
if(this.ticksExisted % 40 == 1){
|
||||
this.playSound(SoundEvents.BLOCK_FIRE_AMBIENT, 4.0f, 0.7f);
|
||||
this.playSound(WizardrySounds.ENTITY_FIRE_RING_AMBIENT, 4.0f, 0.7f);
|
||||
}
|
||||
|
||||
super.onUpdate();
|
||||
@@ -41,14 +45,15 @@ public class EntityFireRing extends EntityMagicConstruct {
|
||||
|
||||
if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)){
|
||||
|
||||
target.setFire(10);
|
||||
target.setFire(Spells.ring_of_fire.getProperty(Spell.BURN_DURATION).intValue());
|
||||
|
||||
float damage = Spells.ring_of_fire.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier;
|
||||
|
||||
if(this.getCaster() != null){
|
||||
target.attackEntityFrom(
|
||||
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FIRE),
|
||||
1 * damageMultiplier);
|
||||
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(),
|
||||
DamageType.FIRE), damage);
|
||||
}else{
|
||||
target.attackEntityFrom(DamageSource.MAGIC, 1 * damageMultiplier);
|
||||
target.attackEntityFrom(DamageSource.MAGIC, damage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityFireSigil extends EntityMagicConstruct {
|
||||
|
||||
public EntityFireSigil(World world){
|
||||
@@ -38,16 +41,18 @@ public class EntityFireSigil extends EntityMagicConstruct {
|
||||
|
||||
target.attackEntityFrom(this.getCaster() != null
|
||||
? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FIRE)
|
||||
: DamageSource.MAGIC, 6);
|
||||
: DamageSource.MAGIC, Spells.fire_sigil.getProperty(Spell.DAMAGE).floatValue()
|
||||
* damageMultiplier);
|
||||
|
||||
// Removes knockback
|
||||
target.motionX = velX;
|
||||
target.motionY = velY;
|
||||
target.motionZ = velZ;
|
||||
|
||||
if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire(10);
|
||||
if(!MagicDamage.isEntityImmune(DamageType.FIRE, target))
|
||||
target.setFire(Spells.fire_sigil.getProperty(Spell.BURN_DURATION).intValue());
|
||||
|
||||
this.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
this.playSound(WizardrySounds.ENTITY_FIRE_SIGIL_TRIGGER, 1, 1);
|
||||
|
||||
// The trap is destroyed once triggered.
|
||||
this.setDead();
|
||||
@@ -55,9 +60,9 @@ public class EntityFireSigil extends EntityMagicConstruct {
|
||||
}
|
||||
}else if(this.rand.nextInt(15) == 0){
|
||||
double radius = 0.5 + rand.nextDouble() * 0.3;
|
||||
double angle = rand.nextDouble() * Math.PI * 2;
|
||||
world.spawnParticle(EnumParticleTypes.FLAME, this.posX + radius * Math.cos(angle), this.posY + 0.1,
|
||||
this.posZ + radius * Math.sin(angle), 0, 0, 0);
|
||||
float angle = rand.nextFloat() * (float)Math.PI * 2;;
|
||||
world.spawnParticle(EnumParticleTypes.FLAME, this.posX + radius * MathHelper.cos(angle), this.posY + 0.1,
|
||||
this.posZ + radius * MathHelper.sin(angle), 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +1,89 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.ICustomHitbox;
|
||||
import electroblob.wizardry.entity.projectile.EntityMagicArrow;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityXPOrb;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.entity.projectile.EntityThrowable;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.event.world.ExplosionEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
public class EntityForcefield extends EntityMagicConstruct {
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class EntityForcefield extends EntityMagicConstruct implements ICustomHitbox {
|
||||
|
||||
/** Extra radius to search around the forcefield for incoming entities. Any entities with a velocity greater than
|
||||
* this could potentially penetrate the forcefield. */
|
||||
private static final double SEARCH_BORDER_SIZE = 4;
|
||||
|
||||
private static final float BOUNCINESS = 0.2f;
|
||||
|
||||
private float radius;
|
||||
|
||||
public EntityForcefield(World world){
|
||||
super(world);
|
||||
this.height = 6;
|
||||
this.width = 6;
|
||||
setRadius(3); // Shouldn't be needed but it's a good failsafe
|
||||
this.ignoreFrustumCheck = true;
|
||||
this.noClip = true;
|
||||
}
|
||||
|
||||
public void setRadius(float radius){
|
||||
this.radius = radius;
|
||||
this.height = 2 * radius;
|
||||
this.width = 2 * radius;
|
||||
// y-3 because it needs to be centred on the given position
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(posX - 3, posY - 3, posZ - 3, posX + 3, posY + 3, posZ + 3));
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(posX - radius, posY - radius, posZ - radius,
|
||||
posX + radius, posY + radius, posZ + radius));
|
||||
}
|
||||
|
||||
public float getRadius(){
|
||||
return radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCollidedWith(){
|
||||
return !this.isDead;
|
||||
return false;//!this.isDead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBB getCollisionBox(Entity entity){
|
||||
return entity.getEntityBoundingBox();
|
||||
return null;//entity.getEntityBoundingBox();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AxisAlignedBB getCollisionBoundingBox(){
|
||||
return super.getCollisionBoundingBox();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderInPass(int pass){
|
||||
return pass == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,48 +91,182 @@ public class EntityForcefield extends EntityMagicConstruct {
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
if(!this.world.isRemote){
|
||||
// TESTME: This used to say posY+3, but I'm pretty sure that's wrong because of how the bounding box was set...
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(3.5, posX, posY, posZ, world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
if(this.isValidTarget(target)){
|
||||
double multiplier = (3.5 - target.getDistance(this.posX, this.posY, this.posZ)) * 0.1;
|
||||
target.addVelocity((target.posX - this.posX) * multiplier,
|
||||
(target.posY - this.posY) * multiplier, (target.posZ - this.posZ) * multiplier);
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
// New forcefield repulsion system:
|
||||
// Searches for all entities near the forcefield and determines where they will be next tick.
|
||||
// If they will be inside the forcefield next tick, sets their position and velocity such that they appear to
|
||||
// bounce off the forcefield and creates impact particle effects and sounds where they hit it
|
||||
|
||||
List<Entity> targets = WizardryUtilities.getEntitiesWithinRadius(radius + SEARCH_BORDER_SIZE, posX, posY, posZ, world, Entity.class);
|
||||
|
||||
targets.remove(this);
|
||||
targets.removeIf(t -> t instanceof EntityXPOrb); // Gets annoying since they're attracted to the player
|
||||
|
||||
// Ring of the defender allows players to shoot through their own forcefields
|
||||
if(getCaster() instanceof EntityPlayer && ItemArtefact.isArtefactActive((EntityPlayer)getCaster(),
|
||||
WizardryItems.ring_defender)){
|
||||
targets.removeIf(t -> t instanceof EntityMagicArrow && !this.isValidTarget(((EntityMagicArrow)t).getCaster())
|
||||
|| t instanceof EntityThrowable && !this.isValidTarget(((EntityThrowable)t).getThrower())
|
||||
|| t instanceof EntityArrow && !this.isValidTarget(((EntityArrow)t).shootingEntity));
|
||||
}
|
||||
|
||||
for(Entity target : targets){
|
||||
|
||||
if(this.isValidTarget(target)){
|
||||
|
||||
Vec3d currentPos = Arrays.stream(WizardryUtilities.getVertices(target.getEntityBoundingBox()))
|
||||
.min(Comparator.comparingDouble(v -> v.distanceTo(this.getPositionVector())))
|
||||
.orElse(target.getPositionVector()); // This will never happen, it's just here to make the compiler happy
|
||||
|
||||
double currentDistance = target.getDistance(this);
|
||||
|
||||
// Estimate the target's position next tick
|
||||
// We have to assume the same vertex is closest or the velocity will be wrong
|
||||
Vec3d nextTickPos = currentPos.add(target.motionX, target.motionY, target.motionZ);
|
||||
double nextTickDistance = nextTickPos.distanceTo(this.getPositionVector());
|
||||
|
||||
boolean flag;
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
// Non-allied living entities shouldn't be inside at all
|
||||
flag = nextTickDistance <= radius;
|
||||
}else{
|
||||
// Non-living entities will bounce off if they hit the forcefield within the next tick...
|
||||
flag = (currentDistance > radius && nextTickDistance <= radius) // ...from the outside...
|
||||
|| (currentDistance < radius && nextTickDistance >= radius); // ...or from the inside
|
||||
}
|
||||
|
||||
if(flag){
|
||||
|
||||
// Ring of interdiction
|
||||
if(getCaster() instanceof EntityPlayer && ItemArtefact.isArtefactActive((EntityPlayer)getCaster(),
|
||||
WizardryItems.ring_interdiction) && WizardryUtilities.isLiving(target)){
|
||||
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(),
|
||||
MagicDamage.DamageType.MAGIC), 1);
|
||||
}
|
||||
|
||||
Vec3d targetRelativePos = currentPos.subtract(this.getPositionVector());
|
||||
|
||||
double nudgeVelocity = this.contains(target) ? -0.1 : 0.1;
|
||||
if(WizardryUtilities.isLiving(target)) nudgeVelocity = 0.25;
|
||||
Vec3d extraVelocity = targetRelativePos.normalize().scale(nudgeVelocity);
|
||||
|
||||
// ...make it bounce off!
|
||||
target.motionX = target.motionX * -BOUNCINESS + extraVelocity.x;
|
||||
target.motionY = target.motionY * -BOUNCINESS + extraVelocity.y;
|
||||
target.motionZ = target.motionZ * -BOUNCINESS + extraVelocity.z;
|
||||
|
||||
// Prevents the forcefield bouncing things into the floor
|
||||
if(target.onGround && target.motionY < 0) target.motionY = 0.1;
|
||||
|
||||
// How far the target needs to move towards the centre (negative means away from the centre)
|
||||
double distanceTowardsCentre = -(targetRelativePos.length() - radius) - (radius - nextTickDistance);
|
||||
Vec3d targetNewPos = target.getPositionVector().add(targetRelativePos.normalize().scale(distanceTowardsCentre));
|
||||
target.setPosition(targetNewPos.x, targetNewPos.y, targetNewPos.z);
|
||||
|
||||
world.playSound(target.posX, target.posY, target.posZ, WizardrySounds.ENTITY_FORCEFIELD_DEFLECT,
|
||||
WizardrySounds.SPELLS, 0.3f, 1.3f, false);
|
||||
|
||||
if(!world.isRemote){
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
Vec3d relativeImpactPos = targetRelativePos.normalize().scale(radius);
|
||||
|
||||
float yaw = (float)Math.atan2(relativeImpactPos.x, -relativeImpactPos.z);
|
||||
float pitch = (float)Math.asin(relativeImpactPos.y/ radius);
|
||||
|
||||
ParticleBuilder.create(Type.FLASH).pos(this.getPositionVector().add(relativeImpactPos))
|
||||
.time(6).face((float)(yaw * 180/Math.PI), (float)(pitch * 180/Math.PI))
|
||||
.clr(0.9f, 0.95f, 1).spawn(world);
|
||||
|
||||
for(int i = 0; i < 12; i++){
|
||||
|
||||
float yaw1 = yaw + 0.3f * (rand.nextFloat() - 0.5f) - (float)Math.PI/2;
|
||||
float pitch1 = pitch + 0.3f * (rand.nextFloat() - 0.5f);
|
||||
|
||||
float brightness = rand.nextFloat();
|
||||
|
||||
double r = radius + 0.05;
|
||||
double x = this.posX + r * MathHelper.cos(yaw1) * MathHelper.cos(pitch1);
|
||||
double y = this.posY + r * MathHelper.sin(pitch1);
|
||||
double z = this.posZ + r * MathHelper.sin(yaw1) * MathHelper.cos(pitch1);
|
||||
|
||||
ParticleBuilder.create(Type.DUST).pos(x, y, z).time(6 + rand.nextInt(6))
|
||||
.face((float)(yaw1 * 180/Math.PI) + 90, (float)(pitch1 * 180/Math.PI)).scale(1.5f)
|
||||
.clr(0.7f + 0.3f * brightness, 0.85f + 0.15f * brightness, 1).spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
for(int i = 1; i < 40; i++){
|
||||
|
||||
// Generates a spherical pattern of particles
|
||||
float brightness = 0.5f + (rand.nextFloat() * 0.5f);
|
||||
double radius = 3;
|
||||
double yaw = rand.nextDouble() * Math.PI * 2;
|
||||
double pitch = (rand.nextDouble() - 0.5) * Math.PI;
|
||||
|
||||
ParticleBuilder.create(Type.DUST)
|
||||
.pos(this.posX + radius * Math.cos(yaw) * Math.cos(pitch), this.posY + 3 + radius * Math.sin(pitch),
|
||||
this.posZ + radius * Math.sin(yaw) * Math.cos(pitch))
|
||||
.time(48 + this.rand.nextInt(12))
|
||||
.clr(brightness, brightness, 1.0f).spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean attackEntityFrom(DamageSource source, float damage){
|
||||
public boolean contains(Vec3d vec){
|
||||
return vec.distanceTo(this.getPositionVector()) < radius; // The surface counts as outside
|
||||
}
|
||||
|
||||
if(source != null && source.getImmediateSource() != null){
|
||||
// Now works for any source of damage.
|
||||
source.getImmediateSource().playSound(WizardrySounds.SPELL_DEFLECTION, 0.3f, 1.3f);
|
||||
/** Returns true if the given bounding box is completely inside this forcefield (the surface counts as outside). */
|
||||
public boolean contains(AxisAlignedBB box){
|
||||
return Arrays.stream(WizardryUtilities.getVertices(box)).allMatch(this::contains);
|
||||
}
|
||||
|
||||
/** Returns true if the given entity is completely inside this forcefield (the surface counts as outside). */
|
||||
public boolean contains(Entity entity){
|
||||
return contains(entity.getEntityBoundingBox());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d calculateIntercept(Vec3d origin, Vec3d endpoint, float fuzziness){
|
||||
|
||||
// We want the intercept between the line and a sphere
|
||||
// First we need to find the point where the line is closest to the centre
|
||||
// Then we can use a bit of geometry to find the intercept
|
||||
|
||||
// Find the closest point to the centre
|
||||
// http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
|
||||
Vec3d line = endpoint.subtract(origin);
|
||||
double t = -origin.subtract(this.getPositionVector()).dotProduct(line) / line.lengthSquared();
|
||||
Vec3d closestPoint = origin.add(line.scale(t));
|
||||
// Now calculate the distance from that point to the centre (squared because that's all we need)
|
||||
double dsquared = closestPoint.squareDistanceTo(this.getPositionVector());
|
||||
double rsquared = Math.pow(radius + fuzziness, 2);
|
||||
// If the minimum distance is outside the radius (plus fuzziness) then there is no intercept
|
||||
if(dsquared > rsquared) return null;
|
||||
// Now do pythagoras to find the other side of the triangle, which is the distance along the line from
|
||||
// the closest point to the edge of the sphere, and go that far back towards the origin - and that's it!
|
||||
return closestPoint.subtract(line.normalize().scale(MathHelper.sqrt(rsquared - dsquared)));
|
||||
}
|
||||
|
||||
// Need to sync the caster because we're now dealing with client-side motion
|
||||
|
||||
@Override
|
||||
public void writeSpawnData(ByteBuf data){
|
||||
super.writeSpawnData(data);
|
||||
data.writeFloat(getRadius());
|
||||
if(getCaster() != null) data.writeInt(getCaster().getEntityId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSpawnData(ByteBuf data){
|
||||
|
||||
super.readSpawnData(data);
|
||||
|
||||
setRadius(data.readFloat());
|
||||
|
||||
if(!data.isReadable()) return;
|
||||
|
||||
Entity entity = world.getEntityByID(data.readInt());
|
||||
|
||||
if(entity instanceof EntityLivingBase){
|
||||
setCaster((EntityLivingBase)entity);
|
||||
}else{
|
||||
Wizardry.logger.warn("Forcefield caster with ID in spawn data not found");
|
||||
}
|
||||
super.attackEntityFrom(source, damage);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,4 +274,91 @@ public class EntityForcefield extends EntityMagicConstruct {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevents any kind of interactions or attacks through the forcefield
|
||||
// We may as well include projectile damage for this, then it will act as a failsafe
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
|
||||
if(event.getSource().getTrueSource() instanceof EntityPlayer && event.getSource().isProjectile()
|
||||
&& ItemArtefact.isArtefactActive((EntityPlayer)event.getSource().getTrueSource(), WizardryItems.ring_defender)){
|
||||
return; // Players wearing a ring of the defender can shoot stuff as normal, so don't cancel the event
|
||||
}
|
||||
|
||||
if(!event.getSource().isUnblockable() && event.getSource().getTrueSource() != null && event.getEntityLiving() != null
|
||||
&& !(event.getSource().getImmediateSource() instanceof EntityForcefield)){ // If the damage was from a forcefield that's ok
|
||||
// This condition will be false if both entities are outside a forcefield or both are in the same one
|
||||
if(getSurroundingForcefield(event.getEntityLiving()) != getSurroundingForcefield(event.getSource().getTrueSource())){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EntityForcefield getSurroundingForcefield(World world, Vec3d vec){
|
||||
|
||||
double searchRadius = 20;
|
||||
|
||||
List<EntityForcefield> forcefields = WizardryUtilities.getEntitiesWithinRadius(searchRadius, vec.x,
|
||||
vec.y, vec.z, world, EntityForcefield.class);
|
||||
|
||||
forcefields.removeIf(f -> !f.contains(vec));
|
||||
// There should only be one left at this point since we now have anti-overlap, but commands might bypass that
|
||||
return forcefields.stream().min(Comparator.comparingDouble(f -> vec.squareDistanceTo(f.getPositionVector())))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EntityForcefield getSurroundingForcefield(World world, AxisAlignedBB box, Vec3d vec){
|
||||
|
||||
double searchRadius = 20;
|
||||
|
||||
List<EntityForcefield> forcefields = WizardryUtilities.getEntitiesWithinRadius(searchRadius, vec.x,
|
||||
vec.y, vec.z, world, EntityForcefield.class);
|
||||
|
||||
forcefields.removeIf(f -> !f.contains(box));
|
||||
// There should only be one left at this point since we now have anti-overlap, but commands might bypass that
|
||||
return forcefields.stream().min(Comparator.comparingDouble(f -> vec.squareDistanceTo(f.getPositionVector())))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EntityForcefield getSurroundingForcefield(Entity entity){
|
||||
return getSurroundingForcefield(entity.world, entity.getEntityBoundingBox(), entity.getPositionVector());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerInteractEvent(PlayerInteractEvent event){
|
||||
|
||||
if(!event.isCancelable()) return; // We don't care about clicking empty space
|
||||
|
||||
// For some reason block bounding boxes are relative whereas entity bounding boxes are absolute
|
||||
AxisAlignedBB box = event.getWorld().getBlockState(event.getPos()).getBoundingBox(event.getWorld(), event.getPos())
|
||||
.offset(event.getPos().getX(), event.getPos().getY(), event.getPos().getZ());
|
||||
|
||||
if(event instanceof PlayerInteractEvent.EntityInteract){
|
||||
box = ((PlayerInteractEvent.EntityInteract)event).getTarget().getEntityBoundingBox();
|
||||
}else if(event instanceof PlayerInteractEvent.EntityInteractSpecific){
|
||||
box = ((PlayerInteractEvent.EntityInteractSpecific)event).getTarget().getEntityBoundingBox();
|
||||
}
|
||||
|
||||
// If the player is trying to interact across a forcefield boundary, cancel the event
|
||||
// The most pragmatic solution here is to use the centres - it's not perfect, but it's simple!
|
||||
if(getSurroundingForcefield(event.getWorld(), WizardryUtilities.getCentre(box))
|
||||
!= getSurroundingForcefield(event.getWorld(), event.getEntityPlayer().getPositionVector())){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onExplosionEvent(ExplosionEvent event){
|
||||
|
||||
EntityForcefield forcefield = getSurroundingForcefield(event.getWorld(), event.getExplosion().getPosition());
|
||||
// Not a particularly efficient way of doing it but explosions are laggy anyway, and the code is neat :P
|
||||
event.getExplosion().getAffectedBlockPositions().removeIf(p -> getSurroundingForcefield(event.getWorld(),
|
||||
new Vec3d(p).add(0.5, 0.5, 0.5)) != forcefield);
|
||||
|
||||
event.getExplosion().getPlayerKnockbackMap().keySet().removeIf(p -> getSurroundingForcefield(p) != forcefield);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -12,8 +12,11 @@ import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityFrostSigil extends EntityMagicConstruct {
|
||||
|
||||
public EntityFrostSigil(World world){
|
||||
@@ -38,12 +41,15 @@ public class EntityFrostSigil extends EntityMagicConstruct {
|
||||
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target, this.getCaster() != null
|
||||
? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FROST)
|
||||
: DamageSource.MAGIC, 8);
|
||||
: DamageSource.MAGIC, Spells.frost_sigil.getProperty(Spell.DAMAGE).floatValue()
|
||||
* damageMultiplier);
|
||||
|
||||
if(!MagicDamage.isEntityImmune(DamageType.FROST, target))
|
||||
target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 1));
|
||||
target.addPotionEffect(new PotionEffect(WizardryPotions.frost,
|
||||
Spells.frost_sigil.getProperty(Spell.EFFECT_DURATION).intValue(),
|
||||
Spells.frost_sigil.getProperty(Spell.EFFECT_STRENGTH).intValue()));
|
||||
|
||||
this.playSound(WizardrySounds.SPELL_FREEZE, 1.0f, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_FROST_SIGIL_TRIGGER, 1.0f, 1.0f);
|
||||
|
||||
// The trap is destroyed once triggered.
|
||||
this.setDead();
|
||||
@@ -51,9 +57,9 @@ public class EntityFrostSigil extends EntityMagicConstruct {
|
||||
}
|
||||
}else if(this.rand.nextInt(15) == 0){
|
||||
double radius = 0.5 + rand.nextDouble() * 0.3;
|
||||
double angle = rand.nextDouble() * Math.PI * 2;
|
||||
float angle = rand.nextFloat() * (float)Math.PI * 2;;
|
||||
ParticleBuilder.create(Type.SNOW)
|
||||
.pos(this.posX + radius * Math.cos(angle), this.posY + 0.1, this.posZ + radius * Math.sin(angle))
|
||||
.pos(this.posX + radius * MathHelper.cos(angle), this.posY + 0.1, this.posZ + radius * MathHelper.sin(angle))
|
||||
.vel(0, 0, 0) // Required since default for snow is not stationary
|
||||
.spawn(world);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import electroblob.wizardry.entity.projectile.EntityIceShard;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityHailstorm extends EntityMagicConstruct {
|
||||
@@ -20,9 +21,9 @@ public class EntityHailstorm extends EntityMagicConstruct {
|
||||
EntityIceShard iceshard = new EntityIceShard(world);
|
||||
iceshard.setPosition(this.posX + rand.nextDouble() * 6 - 3, this.posY + rand.nextDouble() * 4 - 2,
|
||||
this.posZ + rand.nextDouble() * 6 - 3);
|
||||
iceshard.motionX = Math.cos(Math.toRadians(this.rotationYaw + 90));
|
||||
iceshard.motionX = MathHelper.cos((float)Math.toRadians(this.rotationYaw + 90));
|
||||
iceshard.motionY = -0.6;
|
||||
iceshard.motionZ = Math.sin(Math.toRadians(this.rotationYaw + 90));
|
||||
iceshard.motionZ = MathHelper.sin((float)Math.toRadians(this.rotationYaw + 90));
|
||||
iceshard.setCaster(this.getCaster());
|
||||
iceshard.damageMultiplier = this.damageMultiplier;
|
||||
this.world.spawnEntity(iceshard);
|
||||
|
||||
@@ -1,32 +1,44 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.item.ItemLightningHammer;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.LightningHammer;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.entity.effect.EntityLightningBolt;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityHammer extends EntityMagicConstruct {
|
||||
|
||||
/** How long the hammer has been falling for. */
|
||||
public int fallTime;
|
||||
|
||||
public boolean spin = false;
|
||||
|
||||
public EntityHammer(World world){
|
||||
super(world);
|
||||
this.setSize(1.0f, 1.9F);
|
||||
@@ -51,15 +63,20 @@ public class EntityHammer extends EntityMagicConstruct {
|
||||
return this.getEntityBoundingBox();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyEntityCollision(Entity entity){
|
||||
super.applyEntityCollision(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
if(this.ticksExisted % 20 == 1 && !this.onGround && world.isRemote){
|
||||
// Though this sound does repeat, it stops when it hits the ground.
|
||||
Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_LIGHTNING, 3.0f, 1.0f, false);
|
||||
}
|
||||
// if(this.ticksExisted % 20 == 1 && !this.onGround && world.isRemote){
|
||||
// // Though this sound does repeat, it stops when it hits the ground.
|
||||
// Wizardry.proxy.playMovingSound(this, WizardrySounds.ENTITY_HAMMER_FALLING, WizardrySounds.SPELLS, 3.0f, 1.0f, false);
|
||||
// }
|
||||
|
||||
if(this.world.isRemote && this.ticksExisted % 3 == 0){
|
||||
ParticleBuilder.create(Type.SPARK)
|
||||
@@ -67,65 +84,81 @@ public class EntityHammer extends EntityMagicConstruct {
|
||||
.spawn(world);
|
||||
}
|
||||
|
||||
if(!this.world.isRemote){
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
++this.fallTime;
|
||||
this.motionY -= 0.03999999910593033D;
|
||||
this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ);
|
||||
this.motionX *= 0.9800000190734863D;
|
||||
this.motionY *= 0.9800000190734863D;
|
||||
this.motionZ *= 0.9800000190734863D;
|
||||
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
++this.fallTime;
|
||||
this.motionY -= 0.03999999910593033D;
|
||||
this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ);
|
||||
this.motionX *= 0.9800000190734863D;
|
||||
this.motionY *= 0.9800000190734863D;
|
||||
this.motionZ *= 0.9800000190734863D;
|
||||
if(this.onGround){
|
||||
|
||||
if(this.onGround){
|
||||
this.motionX *= 0.699999988079071D;
|
||||
this.motionZ *= 0.699999988079071D;
|
||||
this.motionY *= -0.5D;
|
||||
|
||||
this.motionX *= 0.699999988079071D;
|
||||
this.motionZ *= 0.699999988079071D;
|
||||
this.motionY *= -0.5D;
|
||||
this.rotationPitch = 0;
|
||||
this.spin = false;
|
||||
|
||||
if(this.ticksExisted % 40 == 0){
|
||||
if(this.ticksExisted % Spells.lightning_hammer.getProperty(LightningHammer.ATTACK_INTERVAL).floatValue() == 0){
|
||||
|
||||
double seekerRange = 10.0d;
|
||||
double seekerRange = Spells.lightning_hammer.getProperty(Spell.EFFECT_RADIUS).doubleValue();
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX,
|
||||
this.posY + 1, this.posZ, world);
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX,
|
||||
this.posY + 1, this.posZ, world);
|
||||
|
||||
// For this spell there is no limit to the amount of secondary targets!
|
||||
for(EntityLivingBase target : targets){
|
||||
int maxTargets = Spells.lightning_hammer.getProperty(LightningHammer.SECONDARY_MAX_TARGETS).intValue();
|
||||
while(targets.size() > maxTargets) targets.remove(targets.size() - 1);
|
||||
|
||||
if(this.isValidTarget(target)){
|
||||
for(EntityLivingBase target : targets){
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
ParticleBuilder.create(Type.LIGHTNING).pos(posX, posY + height - 0.1, posZ) .target(target).spawn(world);
|
||||
|
||||
ParticleBuilder.spawnShockParticles(world, target.posX,
|
||||
target.getEntityBoundingBox().minY + target.height, target.posZ);
|
||||
}
|
||||
if(WizardryUtilities.isLiving(target) && this.isValidTarget(target)){
|
||||
|
||||
target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
|
||||
if(world.isRemote){
|
||||
|
||||
if(this.getCaster() != null){
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target,
|
||||
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK),
|
||||
6 * damageMultiplier);
|
||||
WizardryUtilities.applyStandardKnockback(this, target);
|
||||
}else{
|
||||
target.attackEntityFrom(DamageSource.MAGIC, 6 * damageMultiplier);
|
||||
}
|
||||
ParticleBuilder.create(Type.LIGHTNING).pos(posX, posY + height - 0.1, posZ) .target(target).spawn(world);
|
||||
|
||||
ParticleBuilder.spawnShockParticles(world, target.posX,
|
||||
target.getEntityBoundingBox().minY + target.height, target.posZ);
|
||||
}
|
||||
|
||||
target.playSound(WizardrySounds.ENTITY_HAMMER_ATTACK, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
|
||||
|
||||
float damage = Spells.lightning_hammer.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier;
|
||||
|
||||
if(this.getCaster() != null){
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(
|
||||
this, getCaster(), DamageType.SHOCK), damage);
|
||||
WizardryUtilities.applyStandardKnockback(this, target);
|
||||
}else{
|
||||
target.attackEntityFrom(DamageSource.MAGIC, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
if(spin) this.setRotation(this.rotationYaw, this.rotationPitch + 15);
|
||||
|
||||
List<Entity> collided = world.getEntitiesInAABBexcluding(this, this.getCollisionBoundingBox(), e -> e instanceof EntityLivingBase);
|
||||
|
||||
float damage = Spells.lightning_hammer.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
|
||||
|
||||
for(Entity entity : collided){
|
||||
entity.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK), damage);
|
||||
//if(entity instanceof EntityLivingBase) ((EntityLivingBase)entity).knockBack(this, 2, -this.motionX, -this.motionZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void despawn(){
|
||||
|
||||
this.playSound(SoundEvents.ENTITY_GENERIC_EXPLODE, 1.0F, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_HAMMER_EXPLODE, 1.0F, 1.0f);
|
||||
|
||||
if(this.world.isRemote){
|
||||
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
|
||||
@@ -156,6 +189,30 @@ public class EntityHammer extends EntityMagicConstruct {
|
||||
false);
|
||||
world.addWeatherEffect(entitylightning);
|
||||
}
|
||||
|
||||
this.playSound(WizardrySounds.ENTITY_HAMMER_LAND, 1.0F, 0.6f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processInitialInteract(EntityPlayer player, EnumHand hand){
|
||||
|
||||
if(player == this.getCaster() && ItemArtefact.isArtefactActive(player, WizardryItems.ring_hammer)
|
||||
&& player.getHeldItemMainhand().isEmpty() && ticksExisted > 10){
|
||||
|
||||
this.setDead();
|
||||
|
||||
ItemStack hammer = new ItemStack(WizardryItems.lightning_hammer);
|
||||
if(!hammer.hasTagCompound()) hammer.setTagCompound(new NBTTagCompound());
|
||||
hammer.getTagCompound().setInteger(ItemLightningHammer.DURATION_NBT_KEY, lifetime);
|
||||
hammer.setItemDamage(ticksExisted);
|
||||
hammer.getTagCompound().setFloat(ItemLightningHammer.DAMAGE_MULTIPLIER_NBT_KEY, damageMultiplier);
|
||||
|
||||
player.setHeldItem(EnumHand.MAIN_HAND, hammer);
|
||||
return true;
|
||||
|
||||
}else{
|
||||
return super.processInitialInteract(player, hand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,12 +220,39 @@ public class EntityHammer extends EntityMagicConstruct {
|
||||
public void writeEntityToNBT(NBTTagCompound nbttagcompound){
|
||||
super.writeEntityToNBT(nbttagcompound);
|
||||
nbttagcompound.setByte("Time", (byte)this.fallTime);
|
||||
nbttagcompound.setBoolean("Spin", spin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readEntityFromNBT(NBTTagCompound nbttagcompound){
|
||||
super.readEntityFromNBT(nbttagcompound);
|
||||
this.fallTime = nbttagcompound.getByte("Time") & 255;
|
||||
this.spin = nbttagcompound.getBoolean("Spin");
|
||||
}
|
||||
|
||||
// Need to sync the caster so they don't have particles spawned at them
|
||||
|
||||
@Override
|
||||
public void writeSpawnData(ByteBuf data){
|
||||
super.writeSpawnData(data);
|
||||
data.writeBoolean(spin);
|
||||
if(getCaster() != null) data.writeInt(getCaster().getEntityId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSpawnData(ByteBuf data){
|
||||
super.readSpawnData(data);
|
||||
spin = data.readBoolean();
|
||||
|
||||
if(!data.isReadable()) return;
|
||||
|
||||
Entity entity = world.getEntityByID(data.readInt());
|
||||
|
||||
if(entity instanceof EntityLivingBase){
|
||||
setCaster((EntityLivingBase)entity);
|
||||
}else{
|
||||
Wizardry.logger.warn("Lightning hammer caster with ID in spawn data not found");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -10,10 +10,15 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityHealAura extends EntityMagicConstruct {
|
||||
|
||||
// TODO: Implement blast modifiers
|
||||
|
||||
public EntityHealAura(World world){
|
||||
super(world);
|
||||
this.height = 1.0f;
|
||||
@@ -24,7 +29,7 @@ public class EntityHealAura extends EntityMagicConstruct {
|
||||
public void onUpdate(){
|
||||
|
||||
if(this.ticksExisted % 25 == 1){
|
||||
this.playSound(WizardrySounds.SPELL_LOOP_SPARKLE, 0.1f, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_HEAL_AURA_AMBIENT, 0.1f, 1.0f);
|
||||
}
|
||||
|
||||
super.onUpdate();
|
||||
@@ -46,9 +51,9 @@ public class EntityHealAura extends EntityMagicConstruct {
|
||||
if(this.getCaster() != null){
|
||||
target.attackEntityFrom(
|
||||
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.RADIANT),
|
||||
1 * damageMultiplier);
|
||||
Spells.healing_aura.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier);
|
||||
}else{
|
||||
target.attackEntityFrom(DamageSource.MAGIC, 1 * damageMultiplier);
|
||||
target.attackEntityFrom(DamageSource.MAGIC, Spells.healing_aura.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier);
|
||||
}
|
||||
|
||||
// Removes knockback
|
||||
@@ -58,16 +63,16 @@ public class EntityHealAura extends EntityMagicConstruct {
|
||||
}
|
||||
|
||||
}else if(target.getHealth() < target.getMaxHealth() && this.ticksExisted % 5 == 0){
|
||||
target.heal(1 * damageMultiplier);
|
||||
target.heal(Spells.healing_aura.getProperty(Spell.HEALTH).floatValue() * damageMultiplier);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
for(int i=1; i<3; i++){
|
||||
float brightness = 0.5f + (rand.nextFloat() * 0.5f);
|
||||
double radius = rand.nextDouble() * 2.0;
|
||||
double angle = rand.nextDouble() * Math.PI * 2;
|
||||
float angle = rand.nextFloat() * (float)Math.PI * 2;;
|
||||
ParticleBuilder.create(Type.SPARKLE)
|
||||
.pos(this.posX + radius * Math.cos(angle), this.posY, this.posZ + radius * Math.sin(angle))
|
||||
.pos(this.posX + radius * MathHelper.cos(angle), this.posY, this.posZ + radius * MathHelper.sin(angle))
|
||||
.vel(0, 0.05, 0)
|
||||
.time(48 + this.rand.nextInt(12))
|
||||
.clr(1.0f, 1.0f, brightness)
|
||||
|
||||
@@ -1,35 +1,62 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class EntityIceSpike extends EntityMagicConstruct {
|
||||
|
||||
private EnumFacing facing;
|
||||
|
||||
public EntityIceSpike(World world){
|
||||
super(world);
|
||||
this.setSize(0.5f, 1.0f);
|
||||
}
|
||||
|
||||
public void setFacing(EnumFacing facing){
|
||||
this.facing = facing;
|
||||
this.setRotation(-facing.getHorizontalAngle(), WizardryUtilities.getPitch(facing));
|
||||
float yaw = (-facing.getHorizontalAngle()) * (float)Math.PI/180;
|
||||
float pitch = (WizardryUtilities.getPitch(facing) - 90) * (float)Math.PI/180;
|
||||
Vec3d min = new Vec3d(-width/2, 0, -width/2).rotatePitch(pitch).rotateYaw(yaw);
|
||||
Vec3d max = new Vec3d(width/2, height, width/2).rotatePitch(pitch).rotateYaw(yaw);
|
||||
this.setEntityBoundingBox(new AxisAlignedBB(this.getPositionVector().add(min), this.getPositionVector().add(max)));
|
||||
}
|
||||
|
||||
public EnumFacing getFacing(){
|
||||
return facing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
|
||||
double extensionSpeed = 0;
|
||||
|
||||
if(lifetime - this.ticksExisted < 15){
|
||||
this.motionY = -0.01 * (this.ticksExisted - (lifetime - 15));
|
||||
extensionSpeed = -0.01 * (this.ticksExisted - (lifetime - 15));
|
||||
}else if(lifetime - this.ticksExisted < 25){
|
||||
this.motionY = 0;
|
||||
extensionSpeed = 0;
|
||||
}else if(lifetime - this.ticksExisted < 28){
|
||||
this.motionY = 0.25;
|
||||
extensionSpeed = 0.25;
|
||||
}
|
||||
|
||||
this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ);
|
||||
if(facing != null){ // Will probably be null on the client side, but should never be on the server side
|
||||
this.move(MoverType.SELF, this.facing.getXOffset() * extensionSpeed, this.facing.getYOffset() * extensionSpeed,
|
||||
this.facing.getZOffset() * extensionSpeed);
|
||||
}
|
||||
|
||||
if(lifetime - this.ticksExisted == 30) this.playSound(WizardrySounds.SPELL_ICE, 1, 2);
|
||||
if(lifetime - this.ticksExisted == 30) this.playSound(WizardrySounds.ENTITY_ICE_SPIKE_EXTEND, 1, 2);
|
||||
|
||||
if(!this.world.isRemote){
|
||||
for(Object entity : this.world.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox())){
|
||||
@@ -37,8 +64,10 @@ public class EntityIceSpike extends EntityMagicConstruct {
|
||||
// Potion effect only gets added if the damage succeeded.
|
||||
if(((EntityLivingBase)entity).attackEntityFrom(
|
||||
MagicDamage.causeDirectMagicDamage(this.getCaster(), DamageType.FROST),
|
||||
5 * this.damageMultiplier))
|
||||
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0));
|
||||
Spells.ice_spikes.getProperty(Spell.DAMAGE).floatValue() * this.damageMultiplier))
|
||||
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost,
|
||||
Spells.ice_spikes.getProperty(Spell.EFFECT_DURATION).intValue(),
|
||||
Spells.ice_spikes.getProperty(Spell.EFFECT_STRENGTH).intValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,4 +75,8 @@ public class EntityIceSpike extends EntityMagicConstruct {
|
||||
super.onUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightnessForRender(){
|
||||
return 15728880;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -10,10 +10,15 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityLightningSigil extends EntityMagicConstruct {
|
||||
|
||||
public static final String SECONDARY_MAX_TARGETS = "secondary_max_targets";
|
||||
|
||||
public EntityLightningSigil(World world){
|
||||
super(world);
|
||||
this.height = 0.2f;
|
||||
@@ -42,22 +47,24 @@ public class EntityLightningSigil extends EntityMagicConstruct {
|
||||
|
||||
// Only works if target is actually damaged to account for hurtResistantTime
|
||||
if(target.attackEntityFrom(getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, getCaster(),
|
||||
DamageType.SHOCK) : DamageSource.MAGIC, 6)){
|
||||
DamageType.SHOCK) : DamageSource.MAGIC, Spells.lightning_sigil.getProperty(Spell.DIRECT_DAMAGE)
|
||||
.floatValue() * damageMultiplier)){
|
||||
|
||||
// Removes knockback
|
||||
target.motionX = velX;
|
||||
target.motionY = velY;
|
||||
target.motionZ = velZ;
|
||||
|
||||
this.playSound(WizardrySounds.SPELL_SPARK, 1.0f, 1.0f);
|
||||
this.playSound(WizardrySounds.ENTITY_LIGHTNING_SIGIL_TRIGGER, 1.0f, 1.0f);
|
||||
|
||||
// Secondary chaining effect
|
||||
double seekerRange = 5.0d;
|
||||
double seekerRange = Spells.lightning_sigil.getProperty(Spell.EFFECT_RADIUS).doubleValue();
|
||||
|
||||
List<EntityLivingBase> secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange,
|
||||
target.posX, target.posY + target.height / 2, target.posZ, world);
|
||||
|
||||
for(int j = 0; j < Math.min(secondaryTargets.size(), 3); j++){
|
||||
for(int j = 0; j < Math.min(secondaryTargets.size(),
|
||||
Spells.lightning_sigil.getProperty(SECONDARY_MAX_TARGETS).floatValue()); j++){
|
||||
|
||||
EntityLivingBase secondaryTarget = secondaryTargets.get(j);
|
||||
|
||||
@@ -73,11 +80,12 @@ public class EntityLightningSigil extends EntityMagicConstruct {
|
||||
secondaryTarget.posZ);
|
||||
}
|
||||
|
||||
secondaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F,
|
||||
secondaryTarget.playSound(WizardrySounds.ENTITY_LIGHTNING_SIGIL_TRIGGER, 1.0F,
|
||||
world.rand.nextFloat() * 0.4F + 1.5F);
|
||||
|
||||
secondaryTarget.attackEntityFrom(
|
||||
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK), 4);
|
||||
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK),
|
||||
Spells.lightning_sigil.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -89,9 +97,9 @@ public class EntityLightningSigil extends EntityMagicConstruct {
|
||||
|
||||
if(this.world.isRemote && this.rand.nextInt(15) == 0){
|
||||
double radius = 0.5 + rand.nextDouble() * 0.3;
|
||||
double angle = rand.nextDouble() * Math.PI * 2;
|
||||
float angle = rand.nextFloat() * (float)Math.PI * 2;;
|
||||
ParticleBuilder.create(Type.SPARK)
|
||||
.pos(this.posX + radius * Math.cos(angle), this.posY + 0.1, this.posZ + radius * Math.sin(angle))
|
||||
.pos(this.posX + radius * MathHelper.cos(angle), this.posY + 0.1, this.posZ + radius * MathHelper.sin(angle))
|
||||
.spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.UUID;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IEntityOwnable;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class is for all inanimate magical constructs which are not projectiles. It was made from scratch to provide a
|
||||
* unifying superclass for black hole, blizzard, tornado and a few others which all share some characteristics. The
|
||||
* EntityPlayer instance of the caster, the lifetime and the damage multiplier are stored and synced here.
|
||||
* <p>
|
||||
* caster UUID, lifetime and damage multiplier are stored here, and lifetime is also synced here.
|
||||
* <p></p>
|
||||
* When extending this class, override both constructors. Generally speaking, subclasses of this class are areas of
|
||||
* effect which deal damage or apply effects over time.
|
||||
*
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
public abstract class EntityMagicConstruct extends Entity implements IEntityAdditionalSpawnData {
|
||||
public abstract class EntityMagicConstruct extends Entity implements IEntityOwnable, IEntityAdditionalSpawnData {
|
||||
|
||||
/** The entity that created this construct */
|
||||
private WeakReference<EntityLivingBase> caster;
|
||||
|
||||
/**
|
||||
* The UUID of the caster. Note that this is only for loading purposes; during normal updates the actual entity
|
||||
* instance is stored (so that getEntityByUUID is not called constantly), so this will not always be synced (this is
|
||||
* why it is private).
|
||||
*/
|
||||
/** The UUID of the caster. As of Wizardry 4.2, this <b>is</b> synced, and rather than storing the caster
|
||||
* instance via a weak reference, it is fetched from the UUID each time it is needed in
|
||||
* {@link EntityMagicConstruct#getCaster()}. */
|
||||
private UUID casterUUID;
|
||||
|
||||
/**
|
||||
@@ -62,13 +62,6 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi
|
||||
|
||||
public void onUpdate(){
|
||||
|
||||
if(this.getCaster() == null && this.casterUUID != null){
|
||||
Entity entity = WizardryUtilities.getEntityByUUID(world, casterUUID);
|
||||
if(entity instanceof EntityLivingBase){
|
||||
this.caster = new WeakReference<EntityLivingBase>((EntityLivingBase)entity);
|
||||
}
|
||||
}
|
||||
|
||||
if(this.ticksExisted > lifetime && lifetime != -1){
|
||||
this.despawn();
|
||||
}
|
||||
@@ -118,25 +111,51 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi
|
||||
lifetime = data.readInt();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UUID getOwnerId(){
|
||||
return casterUUID;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Entity getOwner(){
|
||||
return getCaster(); // Delegate to getCaster
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the EntityLivingBase that created this construct, or null if it no longer exists. Cases where the entity
|
||||
* may no longer exist are: entity died or was deleted, mob despawned, player logged out, entity teleported to
|
||||
* another dimension, or this construct simply had no caster in the first place.
|
||||
*/
|
||||
public EntityLivingBase getCaster(){
|
||||
return caster == null ? null : caster.get();
|
||||
@Nullable
|
||||
public EntityLivingBase getCaster(){ // Kept despite the above method because it returns an EntityLivingBase
|
||||
|
||||
Entity entity = WizardryUtilities.getEntityByUUID(world, getOwnerId());
|
||||
|
||||
if(entity != null && !(entity instanceof EntityLivingBase)){ // Should never happen
|
||||
Wizardry.logger.warn("{} has a non-living owner!", this);
|
||||
entity = null;
|
||||
}
|
||||
|
||||
return (EntityLivingBase)entity;
|
||||
}
|
||||
|
||||
public void setCaster(EntityLivingBase caster){
|
||||
this.caster = new WeakReference<EntityLivingBase>(caster);
|
||||
public void setCaster(@Nullable EntityLivingBase caster){
|
||||
this.casterUUID = caster == null ? null : caster.getUniqueID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for {@link WizardryUtilities#isValidTarget(Entity, Entity)}, with the owner of this construct as the
|
||||
* Shorthand for {@link AllyDesignationSystem#isValidTarget(Entity, Entity)}, with the owner of this construct as the
|
||||
* attacker. Also allows subclasses to override it if they wish to do so.
|
||||
*/
|
||||
public boolean isValidTarget(Entity target){
|
||||
return WizardryUtilities.isValidTarget(this.getCaster(), target);
|
||||
return AllyDesignationSystem.isValidTarget(this.getCaster(), target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoundCategory getSoundCategory(){
|
||||
return WizardrySounds.SPELLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.spell.Tornado;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -15,15 +17,18 @@ import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.MoverType;
|
||||
import net.minecraft.entity.passive.EntityPig;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityTornado extends EntityMagicConstruct {
|
||||
|
||||
private double velX, velZ;
|
||||
@@ -45,63 +50,69 @@ public class EntityTornado extends EntityMagicConstruct {
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
double radius = Spells.tornado.getProperty(Spell.EFFECT_RADIUS).doubleValue();
|
||||
|
||||
if(this.ticksExisted % 120 == 1 && world.isRemote){
|
||||
// Repeat is false so that the sound fades out when the tornado does rather than stopping suddenly
|
||||
Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f, false);
|
||||
Wizardry.proxy.playMovingSound(this, WizardrySounds.ENTITY_TORNADO_AMBIENT, WizardrySounds.SPELLS, 1.0f, 1.0f, false);
|
||||
}
|
||||
|
||||
this.move(MoverType.SELF, velX, motionY, velZ);
|
||||
|
||||
BlockPos pos = new BlockPos(this);
|
||||
int y = WizardryUtilities.getNearestFloorLevelC(world, pos.up(3), 5);
|
||||
pos = new BlockPos(pos.getX(), y, pos.getZ());
|
||||
Integer y = WizardryUtilities.getNearestSurface(world, pos.up(3), EnumFacing.UP, 5, true, WizardryUtilities.SurfaceCriteria.NOT_AIR_TO_AIR);
|
||||
|
||||
if(this.world.getBlockState(pos).getMaterial() == Material.LAVA){
|
||||
// Fire tornado!
|
||||
this.setFire(5);
|
||||
if(y != null){
|
||||
|
||||
pos = new BlockPos(pos.getX(), y, pos.getZ());
|
||||
|
||||
if(this.world.getBlockState(pos).getMaterial() == Material.LAVA){
|
||||
// Fire tornado!
|
||||
this.setFire(5);
|
||||
}
|
||||
}
|
||||
|
||||
if(!this.world.isRemote){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(4.0d, this.posX, this.posY,
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(radius, this.posX, this.posY,
|
||||
this.posZ, this.world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
|
||||
if(target instanceof EntityPlayer && ((getCaster() instanceof EntityPlayer && !Wizardry.settings.playersMoveEachOther)
|
||||
|| ItemArtefact.isArtefactActive((EntityPlayer)target, WizardryItems.amulet_anchoring))){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this.isValidTarget(target)){
|
||||
|
||||
double velY = target.motionY;
|
||||
|
||||
double dx = this.posX - target.posX > 0 ? 0.5 - (this.posX - target.posX) / 8
|
||||
: -0.5 - (this.posX - target.posX) / 8;
|
||||
double dz = this.posZ - target.posZ > 0 ? 0.5 - (this.posZ - target.posZ) / 8
|
||||
: -0.5 - (this.posZ - target.posZ) / 8;
|
||||
// TODO: This doesn't seem right...
|
||||
double dx = (this.posX - target.posX > 0 ? 0.5 : -0.5) - (this.posX - target.posX) * 0.125;
|
||||
double dz = (this.posZ - target.posZ > 0 ? 0.5 : -0.5) - (this.posZ - target.posZ) * 0.125;
|
||||
|
||||
if(this.isBurning()){
|
||||
target.setFire(4);
|
||||
target.setFire(4); // Just a fun Easter egg so no properties here!
|
||||
}
|
||||
|
||||
float damage = Spells.tornado.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier;
|
||||
|
||||
if(this.getCaster() != null){
|
||||
target.attackEntityFrom(
|
||||
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC),
|
||||
1 * damageMultiplier);
|
||||
target.attackEntityFrom( MagicDamage.causeIndirectMagicDamage(this, getCaster(),
|
||||
DamageType.MAGIC), damage);
|
||||
}else{
|
||||
target.attackEntityFrom(DamageSource.MAGIC, 1 * damageMultiplier);
|
||||
target.attackEntityFrom(DamageSource.MAGIC, damage);
|
||||
}
|
||||
|
||||
target.motionX = dx;
|
||||
target.motionY = velY + 0.2;
|
||||
target.motionY = velY + Spells.tornado.getProperty(Tornado.UPWARD_ACCELERATION).floatValue();
|
||||
target.motionZ = dz;
|
||||
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
}
|
||||
|
||||
// The 'Not Again...' achievement
|
||||
if(target instanceof EntityPig && WizardryUtilities.getRider(target) instanceof EntityPlayer){
|
||||
WizardryAdvancementTriggers.pig_tornado.triggerFor((EntityPlayer)WizardryUtilities.getRider(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
@@ -114,38 +125,43 @@ public class EntityTornado extends EntityMagicConstruct {
|
||||
|
||||
BlockPos pos1 = new BlockPos(blockX, this.posY + 3, blockZ);
|
||||
|
||||
int blockY = WizardryUtilities.getNearestFloorLevelC(world, pos1, 5) - 1;
|
||||
Integer blockY = WizardryUtilities.getNearestSurface(world, pos1, EnumFacing.UP, 5, true, WizardryUtilities.SurfaceCriteria.NOT_AIR_TO_AIR);
|
||||
|
||||
pos1 = new BlockPos(pos1.getX(), blockY, pos1.getZ());
|
||||
if(blockY != null){
|
||||
|
||||
IBlockState block = this.world.getBlockState(pos1);
|
||||
blockY--;
|
||||
|
||||
// If the block it found was air or something it can't pick up, it makes a best guess based on the
|
||||
// biome.
|
||||
if(!canTornadoPickUpBitsOf(block)){
|
||||
block = world.getBiome(pos1).topBlock;
|
||||
}
|
||||
pos1 = new BlockPos(pos1.getX(), blockY, pos1.getZ());
|
||||
|
||||
Wizardry.proxy.spawnTornadoParticle(world, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ,
|
||||
yPos / 3 + 0.5d, 100, block, pos1);
|
||||
Wizardry.proxy.spawnTornadoParticle(world, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ,
|
||||
yPos / 3 + 0.5d, 100, block, pos1);
|
||||
|
||||
// Sometimes spawns leaf particles if the block is leaves, or snow particles if the block is snow
|
||||
if(this.rand.nextInt(3) == 0){
|
||||
IBlockState block = this.world.getBlockState(pos1);
|
||||
|
||||
Type type = null;
|
||||
|
||||
if(block.getMaterial() == Material.LEAVES) type = Type.LEAF;
|
||||
if(block.getMaterial() == Material.SNOW || block.getMaterial() == Material.CRAFTED_SNOW) type = Type.SNOW;
|
||||
|
||||
if(type != null){
|
||||
double yPos1 = rand.nextDouble() * 8;
|
||||
ParticleBuilder.create(type)
|
||||
.pos(this.posX + (rand.nextDouble() * 2 - 1) * (yPos1 / 3 + 0.5d), this.posY + yPos1,
|
||||
this.posZ + (rand.nextDouble() * 2 - 1) * (yPos1 / 3 + 0.5d))
|
||||
.time(40 + rand.nextInt(10))
|
||||
.spawn(world);
|
||||
// If the block it found was air or something it can't pick up, it makes a best guess based on the biome
|
||||
if(!canTornadoPickUpBitsOf(block)){
|
||||
block = world.getBiome(pos1).topBlock;
|
||||
}
|
||||
|
||||
Wizardry.proxy.spawnTornadoParticle(world, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ,
|
||||
yPos / 3 + 0.5d, 100, block, pos1);
|
||||
Wizardry.proxy.spawnTornadoParticle(world, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ,
|
||||
yPos / 3 + 0.5d, 100, block, pos1);
|
||||
|
||||
// Sometimes spawns leaf particles if the block is leaves, or snow particles if the block is snow
|
||||
if(this.rand.nextInt(3) == 0){
|
||||
|
||||
ResourceLocation type = null;
|
||||
|
||||
if(block.getMaterial() == Material.LEAVES) type = Type.LEAF;
|
||||
if(block.getMaterial() == Material.SNOW || block.getMaterial() == Material.CRAFTED_SNOW)
|
||||
type = Type.SNOW;
|
||||
|
||||
if(type != null){
|
||||
double yPos1 = rand.nextDouble() * 8;
|
||||
ParticleBuilder.create(type)
|
||||
.pos(this.posX + (rand.nextDouble() * 2 - 1) * (yPos1 / 3 + 0.5d), this.posY + yPos1,
|
||||
this.posZ + (rand.nextDouble() * 2 - 1) * (yPos1 / 3 + 0.5d))
|
||||
.time(40 + rand.nextInt(10))
|
||||
.spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user