Initial 1.11.2 update

This commit is contained in:
Electroblob
2018-02-07 21:15:50 +00:00
parent 67f6be0671
commit 3df5597dcc
368 changed files with 17234 additions and 15082 deletions
@@ -7,19 +7,19 @@ import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
public class EntityArc extends Entity implements IEntityAdditionalSpawnData {
public int textureIndex = 0;
public double x1, y1, z1, x2, y2, z2;
// The number of ticks the arc lasts for before disappearing
public int lifetime = 3;
public double offsetX, offsetZ;
public EntityArc(World par1World) {
public EntityArc(World par1World){
super(par1World);
textureIndex = this.rand.nextInt(16);
this.ignoreFrustumCheck = true;
}
public void setEndpointCoords(double x1, double y1, double z1, double x2, double y2, double z2){
this.x1 = x1;
this.y1 = y1;
@@ -27,7 +27,7 @@ public class EntityArc extends Entity implements IEntityAdditionalSpawnData {
this.x2 = x2;
this.y2 = y2;
this.z2 = z2;
this.setPosition(x2, y2, z2);
this.setPosition(x2, y2, z2);
}
@Override
@@ -36,38 +36,38 @@ public class EntityArc extends Entity implements IEntityAdditionalSpawnData {
this.setDead();
}
}
protected void entityInit()
{
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {
protected void entityInit(){
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {
// Nothing needed here; arc is merely a graphic effect that only exists for a few ticks; as such there is no need to save it.
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
}
@Override
public boolean isInRangeToRenderDist(double distance) {
protected void writeEntityToNBT(NBTTagCompound nbttagcompound){
// Nothing needed here; arc is merely a graphic effect that only exists for a few ticks; as such there is no
// need to save it.
}
@Override
public boolean isInRangeToRenderDist(double distance){
return true;
}
@Override
public void writeSpawnData(ByteBuf data) {
public void writeSpawnData(ByteBuf data){
data.writeDouble(this.x1);
data.writeDouble(this.y1);
data.writeDouble(this.z1);
data.writeDouble(this.y1);
data.writeDouble(this.z1);
}
@Override
public void readSpawnData(ByteBuf data) {
public void readSpawnData(ByteBuf data){
this.x1 = data.readDouble();
this.y1 = data.readDouble();
this.z1 = data.readDouble();
}
}
@@ -5,6 +5,7 @@ import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.item.EntityFallingBlock;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
@@ -14,99 +15,106 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
public class EntityMeteor extends EntityFallingBlock {
/** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */
/**
* The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in
* EntityMagicProjectile.
*/
public float blastMultiplier;
public EntityMeteor(World world){
super(world);
// Superconstructor doesn't call this.
this.setSize(0.98F, 0.98F);
}
public EntityMeteor(World world){
super(world);
// Superconstructor doesn't call this.
this.setSize(0.98F, 0.98F);
}
public EntityMeteor(World world, double x, double y, double z, float blastMultiplier){
super(world, x, y, z, WizardryBlocks.meteor.getDefaultState());
this.motionY = -1.0D;
this.setFire(200);
this.blastMultiplier = blastMultiplier;
}
@Override
public double getYOffset() {
return this.height / 2.0F;
}
public EntityMeteor(World world, double x, double y, double z, float blastMultiplier){
super(world, x, y, z, WizardryBlocks.meteor.getDefaultState());
this.motionY = -1.0D;
this.setFire(200);
this.blastMultiplier = blastMultiplier;
}
@Override
public void onUpdate(){
if(this.ticksExisted % 16 == 1 && worldObj.isRemote){
Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_FIRE, 3.0f, 1.0f, false);
@Override
public double getYOffset(){
return this.height / 2.0F;
}
@Override
public void onUpdate(){
if(this.ticksExisted % 16 == 1 && world.isRemote){
Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_FIRE, 3.0f, 1.0f, false);
}
// You'd think the best way to do this would be to call super and do all the exploding stuff in fall() instead.
// However, for some reason, fallTile is null on the client side, causing an NPE in super.onUpdate()
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
++this.fallTime;
this.motionY -= 0.1d; //0.03999999910593033D;
this.moveEntity(this.motionX, this.motionY, this.motionZ);
this.motionX *= 0.9800000190734863D;
this.motionY *= 0.9800000190734863D;
this.motionZ *= 0.9800000190734863D;
if(!this.worldObj.isRemote){
if(this.onGround){
this.motionX *= 0.699999988079071D;
this.motionZ *= 0.699999988079071D;
this.motionY *= -0.5D;
this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, 2.0f*blastMultiplier, true);
for(int i1=-3; i1<4; i1++){
for(int j1=-3; j1<4; j1++){
int y = WizardryUtilities.getNearestFloorLevelB(this.worldObj, new BlockPos(this.posX + i1, this.posY, this.posZ + j1), 7);
//System.out.println(y);
// You'd think the best way to do this would be to call super and do all the exploding stuff in fall() instead.
// However, for some reason, fallTile is null on the client side, causing an NPE in super.onUpdate()
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
++this.fallTime;
this.motionY -= 0.1d; // 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.world.isRemote){
if(this.onGround){
this.motionX *= 0.699999988079071D;
this.motionZ *= 0.699999988079071D;
this.motionY *= -0.5D;
this.world.createExplosion(this, this.posX, this.posY, this.posZ, 2.0f * blastMultiplier, true);
for(int i1 = -3; i1 < 4; i1++){
for(int j1 = -3; j1 < 4; j1++){
int y = WizardryUtilities.getNearestFloorLevelB(this.world,
new BlockPos(this.posX + i1, this.posY, this.posZ + j1), 7);
// System.out.println(y);
double dist = this.getDistance((int)this.posX + i1, y, (int)this.posZ + j1);
// Randomised with weighting so that the nearer the block the more likely it is to be set on fire.
if(y != -1 && rand.nextInt((int)dist*2 + 1) < 3 && dist < 4){
this.worldObj.setBlockState(new BlockPos(this.posX + i1, y, this.posZ + j1), Blocks.FIRE.getDefaultState());
// Randomised with weighting so that the nearer the block the more likely it is to be set on
// fire.
if(y != -1 && rand.nextInt((int)dist * 2 + 1) < 3 && dist < 4){
this.world.setBlockState(new BlockPos(this.posX + i1, y, this.posZ + j1),
Blocks.FIRE.getDefaultState());
}
}
}
this.setDead();
}
}
}
this.setDead();
}
}
}
@Override
public void fall(float distance, float damageMultiplier){
// Don't need to do anything here, the meteor should have already exploded.
}
@SideOnly(Side.CLIENT)
@Override
public boolean canRenderOnFire(){
return true;
}
@Override
public void fall(float distance, float damageMultiplier){
// Don't need to do anything here, the meteor should have already exploded.
}
@Override
public IBlockState getBlock(){
return WizardryBlocks.meteor.getDefaultState(); // For some reason the superclass version returns null on the client
}
@SideOnly(Side.CLIENT)
@Override
public int getBrightnessForRender(float partialTicks){
return 15728880;
}
@SideOnly(Side.CLIENT)
@Override
public boolean canRenderOnFire(){
return true;
}
@Override
public IBlockState getBlock(){
return WizardryBlocks.meteor.getDefaultState(); // For some reason the superclass version returns null on the
// client
}
@SideOnly(Side.CLIENT)
@Override
public int getBrightnessForRender(float partialTicks){
return 15728880;
}
@Override
public float getBrightness(float partialTicks){
return 1.0F;
}
@Override
public float getBrightness(float partialTicks){
return 1.0F;
}
@Override
public boolean isInRangeToRenderDist(double distance){
return true;
@@ -114,8 +122,8 @@ public class EntityMeteor extends EntityFallingBlock {
@Override
public void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
blastMultiplier = nbttagcompound.getFloat("blastMultiplier");
super.readEntityFromNBT(nbttagcompound);
blastMultiplier = nbttagcompound.getFloat("blastMultiplier");
}
@Override
@@ -123,5 +131,5 @@ public class EntityMeteor extends EntityFallingBlock {
super.writeEntityToNBT(nbttagcompound);
nbttagcompound.setFloat("blastMultiplier", blastMultiplier);
}
}
@@ -14,9 +14,9 @@ import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.world.World;
public class EntityShield extends Entity {
public WeakReference<EntityPlayer> player;
public EntityShield(World world){
super(world);
this.noClip = true;
@@ -24,70 +24,73 @@ public class EntityShield extends Entity {
this.height = 1.4f;
}
public EntityShield(World par1World, EntityPlayer player) {
public EntityShield(World par1World, EntityPlayer player){
super(par1World);
this.width = 1.2f;
this.height = 1.4f;
this.player = new WeakReference<EntityPlayer>(player);
this.noClip = true;
this.setPositionAndRotation(player.posX + player.getLookVec().xCoord, player.posY + 1 + player.getLookVec().yCoord, player.posZ + player.getLookVec().zCoord, player.rotationYawHead, player.rotationPitch);
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.6f, this.posY - 0.7f, this.posZ - 0.6f, this.posX + 0.6f, this.posY + 0.7f, this.posZ + 0.6f));
this.setPositionAndRotation(player.posX + player.getLookVec().xCoord,
player.posY + 1 + player.getLookVec().yCoord, player.posZ + player.getLookVec().zCoord,
player.rotationYawHead, player.rotationPitch);
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.6f, this.posY - 0.7f, this.posZ - 0.6f,
this.posX + 0.6f, this.posY + 0.7f, this.posZ + 0.6f));
}
@Override
public void onUpdate(){
//System.out.println("Shield exists, ID: " + this.getUniqueID().toString());
// System.out.println("Shield exists, ID: " + this.getUniqueID().toString());
EntityPlayer entityplayer = player != null ? player.get() : null;
if(entityplayer != null){
this.setPositionAndRotation(entityplayer.posX + entityplayer.getLookVec().xCoord*0.3, entityplayer.posY + 1 + entityplayer.getLookVec().yCoord*0.3, entityplayer.posZ + entityplayer.getLookVec().zCoord*0.3, entityplayer.rotationYawHead, entityplayer.rotationPitch);
if(!entityplayer.isHandActive() || entityplayer.getHeldItem(entityplayer.getActiveHand()) == null || !(entityplayer.getHeldItem(entityplayer.getActiveHand()).getItem() instanceof ItemWand)){
this.setPositionAndRotation(entityplayer.posX + entityplayer.getLookVec().xCoord * 0.3,
entityplayer.posY + 1 + entityplayer.getLookVec().yCoord * 0.3,
entityplayer.posZ + entityplayer.getLookVec().zCoord * 0.3, entityplayer.rotationYawHead,
entityplayer.rotationPitch);
if(!entityplayer.isHandActive() || entityplayer.getHeldItem(entityplayer.getActiveHand()) == null
|| !(entityplayer.getHeldItem(entityplayer.getActiveHand()).getItem() instanceof ItemWand)){
WizardData.get(entityplayer).shield = null;
this.setDead();
}
}else if(!worldObj.isRemote){
}else if(!world.isRemote){
this.setDead();
}
}
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9)
{
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
public boolean attackEntityFrom(DamageSource par1DamageSource, float par2)
{
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9){
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
public boolean attackEntityFrom(DamageSource par1DamageSource, float par2){
if(par1DamageSource != null && par1DamageSource.getSourceOfDamage() instanceof IProjectile){
par1DamageSource.getSourceOfDamage().playSound(WizardrySounds.SPELL_DEFLECTION, 0.3f, 1.3f);
}
super.attackEntityFrom(par1DamageSource, par2);
return false;
}
public boolean canBeCollidedWith()
{
return !this.isDead;
}
public AxisAlignedBB getCollisionBox(Entity par1Entity)
{
return par1Entity.getEntityBoundingBox();
}
@Override
protected void entityInit() {
}
public boolean canBeCollidedWith(){
return !this.isDead;
}
public AxisAlignedBB getCollisionBox(Entity par1Entity){
return par1Entity.getEntityBoundingBox();
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {
protected void entityInit(){
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound){
}
@@ -7,32 +7,34 @@ import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
public class EntityArrowRain extends EntityMagicConstruct {
public EntityArrowRain(World par1World) {
public EntityArrowRain(World par1World){
super(par1World);
this.height = 3.0f;
this.width = 5.0f;
}
public EntityArrowRain(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) {
public EntityArrowRain(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 3.0f;
this.width = 5.0f;
}
public void onUpdate(){
super.onUpdate();
if(!this.worldObj.isRemote){
EntityTippedArrow arrow = new EntityTippedArrow(worldObj, this.posX + rand.nextDouble()*6 - 3, this.posY + rand.nextDouble()*4 - 2, this.posZ + rand.nextDouble()*6 - 3);
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.motionY = -0.6;
arrow.motionZ = Math.sin(Math.toRadians(this.rotationYaw + 90));
arrow.shootingEntity = this.getCaster();
arrow.setDamage(7.0d*damageMultiplier);
arrow.setDamage(7.0d * damageMultiplier);
arrow.setPotionEffect(new ItemStack(Items.ARROW));
this.worldObj.spawnEntityInWorld(arrow);
this.world.spawnEntity(arrow);
}
}
@@ -25,38 +25,39 @@ public class EntityBlackHole extends EntityMagicConstruct {
this.width = 6.0f;
this.height = 3.0f;
randomiser = new int[30];
for(int i=0; i<randomiser.length; i++){
for(int i = 0; i < randomiser.length; i++){
randomiser[i] = this.rand.nextInt(10);
}
randomiser2 = new int[30];
for(int i=0; i<randomiser2.length; i++){
for(int i = 0; i < randomiser2.length; i++){
randomiser2[i] = this.rand.nextInt(10);
}
}
public EntityBlackHole(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) {
public EntityBlackHole(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.width = 6.0f;
this.height = 3.0f;
randomiser = new int[30];
for(int i=0; i<randomiser.length; i++){
for(int i = 0; i < randomiser.length; i++){
randomiser[i] = this.rand.nextInt(10);
}
randomiser2 = new int[30];
for(int i=0; i<randomiser2.length; i++){
for(int i = 0; i < randomiser2.length; i++){
randomiser2[i] = this.rand.nextInt(10);
}
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
randomiser = nbttagcompound.getIntArray("randomiser");
randomiser2 = nbttagcompound.getIntArray("randomiser2");
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {
protected void writeEntityToNBT(NBTTagCompound nbttagcompound){
super.writeEntityToNBT(nbttagcompound);
nbttagcompound.setIntArray("randomiser", randomiser);
nbttagcompound.setIntArray("randomiser2", randomiser2);
@@ -66,13 +67,18 @@ public class EntityBlackHole extends EntityMagicConstruct {
super.onUpdate();
//System.out.println("Client side: " + this.worldObj.isRemote + ", Caster: " + this.caster);
// System.out.println("Client side: " + this.world.isRemote + ", Caster: " + this.caster);
// Particle effect. Finishes 40 ticks before the end so the particles disappear at the same time.
if(this.ticksExisted + 40 < this.lifetime){
for (int i=0; i<5; i++){
//this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - 0.75D, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D);
this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX, this.posY, this.posZ, (this.rand.nextDouble() - 0.5D) * 4.0D, (this.rand.nextDouble() - 0.5D) * 4.0D - 1, (this.rand.nextDouble() - 0.5D) * 4.0D);
for(int i = 0; i < 5; i++){
// this.world.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) *
// (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - 0.75D, this.posZ +
// (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D,
// -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D);
this.world.spawnParticle(EnumParticleTypes.PORTAL, this.posX, this.posY, this.posZ,
(this.rand.nextDouble() - 0.5D) * 4.0D, (this.rand.nextDouble() - 0.5D) * 4.0D - 1,
(this.rand.nextDouble() - 0.5D) * 4.0D);
}
}
@@ -82,9 +88,10 @@ public class EntityBlackHole extends EntityMagicConstruct {
this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.5f, 1.0f);
}
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(6.0d, this.posX, this.posY, this.posZ, this.worldObj);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(6.0d, this.posX, this.posY,
this.posZ, this.world);
if(!this.worldObj.isRemote){
if(!this.world.isRemote){
for(EntityLivingBase target : targets){
@@ -92,21 +99,21 @@ public class EntityBlackHole extends EntityMagicConstruct {
// Sucks the target in
if(this.posX > target.posX && target.motionX < 1){
target.motionX+=0.1;
target.motionX += 0.1;
}else if(this.posX < target.posX && target.motionX > -1){
target.motionX-=0.1;
target.motionX -= 0.1;
}
if(this.posY > target.posY && target.motionY < 1){
target.motionY+=0.1;
target.motionY += 0.1;
}else if(this.posY < target.posY && target.motionY > -1){
target.motionY-=0.1;
target.motionY -= 0.1;
}
if(this.posZ > target.posZ && target.motionZ < 1){
target.motionZ+=0.1;
target.motionZ += 0.1;
}else if(this.posZ < target.posZ && target.motionZ > -1){
target.motionZ-=0.1;
target.motionZ -= 0.1;
}
// Player motion is handled on that player's client so needs packets
@@ -117,9 +124,11 @@ public class EntityBlackHole extends EntityMagicConstruct {
if(this.getDistanceToEntity(target) <= 2){
// Damages the target if it is close enough
if(this.getCaster() != null){
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 2*damageMultiplier);
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC),
2 * damageMultiplier);
}else{
target.attackEntityFrom(DamageSource.magic, 2*damageMultiplier);
target.attackEntityFrom(DamageSource.MAGIC, 2 * damageMultiplier);
}
}
}
@@ -130,8 +139,7 @@ public class EntityBlackHole extends EntityMagicConstruct {
/**
* Checks using a Vec3dd to determine if this entity is within range of that vector to be rendered. Args: Vec3dD
*/
public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d)
{
public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d){
return true;
}
@@ -16,13 +16,14 @@ import net.minecraft.world.World;
public class EntityBlizzard extends EntityMagicConstruct {
public EntityBlizzard(World par1World) {
public EntityBlizzard(World par1World){
super(par1World);
this.height = 1.0f;
this.width = 1.0f;
}
public EntityBlizzard(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) {
public EntityBlizzard(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 1.0f;
this.width = 1.0f;
@@ -36,21 +37,25 @@ public class EntityBlizzard extends EntityMagicConstruct {
super.onUpdate();
if(!this.worldObj.isRemote){
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(3.0d, this.posX, this.posY, this.posZ, this.worldObj);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(3.0d, this.posX, this.posY,
this.posZ, this.world);
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
if(this.getCaster() != null){
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FROST), 1*damageMultiplier);
WizardryUtilities.attackEntityWithoutKnockback(target,
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FROST),
1 * damageMultiplier);
}else{
WizardryUtilities.attackEntityWithoutKnockback(target, DamageSource.magic, 1*damageMultiplier);
WizardryUtilities.attackEntityWithoutKnockback(target, DamageSource.MAGIC,
1 * damageMultiplier);
}
}
// All entities are slowed, even the caster (except those immune to frost effects)
if(!MagicDamage.isEntityImmune(DamageType.FROST, target))
target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 20, 0));
@@ -58,10 +63,14 @@ public class EntityBlizzard extends EntityMagicConstruct {
}else{
// For some reason this number of particles now causes the game to lag significantly, despite it being fine
// in 1.7.10. I thought particles were supposed to be LESS laggy now...
for(int i=1; i<6; i++){
float brightness = 0.5f + (rand.nextFloat()/2);
Wizardry.proxy.spawnParticle(WizardryParticleType.BLIZZARD, worldObj, this.posX, this.posY + rand.nextDouble()*3, this.posZ, 0, 0, 0, 100, brightness, brightness + 0.1f, 1.0f, false, rand.nextDouble() * 2.5d + 0.5d);
Wizardry.proxy.spawnParticle(WizardryParticleType.BLIZZARD, worldObj, this.posX, this.posY + rand.nextDouble()*3, this.posZ, 0, 0, 0, 100, 1.0f, 1.0f, 1.0f, false, rand.nextDouble() * 2.5d + 0.5d);
for(int i = 1; i < 6; i++){
float brightness = 0.5f + (rand.nextFloat() / 2);
Wizardry.proxy.spawnParticle(WizardryParticleType.BLIZZARD, world, this.posX,
this.posY + rand.nextDouble() * 3, this.posZ, 0, 0, 0, 100, brightness, brightness + 0.1f, 1.0f,
false, rand.nextDouble() * 2.5d + 0.5d);
Wizardry.proxy.spawnParticle(WizardryParticleType.BLIZZARD, world, this.posX,
this.posY + rand.nextDouble() * 3, this.posZ, 0, 0, 0, 100, 1.0f, 1.0f, 1.0f, false,
rand.nextDouble() * 2.5d + 0.5d);
}
}
}
@@ -7,6 +7,7 @@ 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;
@@ -18,79 +19,89 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@Mod.EventBusSubscriber
public class EntityBubble extends EntityMagicConstruct {
public boolean isDarkOrb;
private WeakReference<EntityLivingBase> rider;
public EntityBubble(World world){
super(world);
}
public EntityBubble(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, boolean isDarkOrb, float damageMultiplier) {
public EntityBubble(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
boolean isDarkOrb, float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
//this.setSize(0.1f, 0.1f);
// this.setSize(0.1f, 0.1f);
this.isDarkOrb = isDarkOrb;
}
@Override
public double getMountedYOffset()
{
return 0.1;
}
public double getMountedYOffset(){
return 0.1;
}
@Override
public boolean shouldRiderSit(){
return false;
}
public void onUpdate(){
super.onUpdate();
// Synchronises the rider field
if((this.rider == null || this.rider.get() == null) && WizardryUtilities.getRider(this) instanceof EntityLivingBase
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>((EntityLivingBase)WizardryUtilities.getRider(this));
}
// Prevents dismounting
if(WizardryUtilities.getRider(this) == null && this.rider != null && this.rider.get() != null && !this.rider.get().isDead){
if(WizardryUtilities.getRider(this) == null && this.rider != null && this.rider.get() != null
&& !this.rider.get().isDead){
this.rider.get().startRiding(this);
}
// Stops the bubble bursting instantly.
if(this.ticksExisted < 1 && !isDarkOrb) ((EntityLivingBase)WizardryUtilities.getRider(this)).hurtTime = 0;
this.moveEntity(0, 0.03, 0);
this.move(MoverType.SELF, 0, 0.03, 0);
if(isDarkOrb){
if(WizardryUtilities.getRider(this) != null && this.ticksExisted % 30 == 0){
if(this.getCaster() != null){
WizardryUtilities.getRider(this).attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 1*damageMultiplier);
WizardryUtilities.getRider(this).attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC),
1 * damageMultiplier);
}else{
WizardryUtilities.getRider(this).attackEntityFrom(DamageSource.magic, 1*damageMultiplier);
WizardryUtilities.getRider(this).attackEntityFrom(DamageSource.MAGIC, 1 * damageMultiplier);
}
}
for(int i=0; i<5; i++){
this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height + 0.5d, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D);
}
for(int i = 0; i < 5; i++){
this.world.spawnParticle(EnumParticleTypes.PORTAL,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height + 0.5d,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width,
(this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(),
(this.rand.nextDouble() - 0.5D) * 2.0D);
}
if(lifetime - this.ticksExisted == 75){
this.playSound(SoundEvents.BLOCK_PORTAL_TRIGGER, 1.5f, 1.0f);
}else if(this.ticksExisted % 100 == 1 && this.ticksExisted < 150){
this.playSound(SoundEvents.BLOCK_PORTAL_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.
// 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);
this.setDead();
}
}
@Override
public void despawn(){
if(WizardryUtilities.getRider(this) != null){
@@ -101,34 +112,34 @@ public class EntityBubble extends EntityMagicConstruct {
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
isDarkOrb = nbttagcompound.getBoolean("isDarkOrb");
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {
protected void writeEntityToNBT(NBTTagCompound nbttagcompound){
super.writeEntityToNBT(nbttagcompound);
nbttagcompound.setBoolean("isDarkOrb", isDarkOrb);
}
@Override
public void writeSpawnData(ByteBuf data) {
public void writeSpawnData(ByteBuf data){
super.writeSpawnData(data);
data.writeBoolean(this.isDarkOrb);
}
@Override
public void readSpawnData(ByteBuf data) {
public void readSpawnData(ByteBuf data){
super.readSpawnData(data);
this.isDarkOrb = data.readBoolean();
}
@SubscribeEvent
public static void onLivingAttackEvent(LivingAttackEvent event){
// Bursts bubble when the creature inside takes damage
if(event.getEntityLiving().getRidingEntity() instanceof EntityBubble &&
!((EntityBubble)event.getEntityLiving().getRidingEntity()).isDarkOrb){
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().setDead();
}
@@ -14,18 +14,18 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class EntityDecay extends EntityMagicConstruct {
public int textureIndex = 0;
public static final int LIFETIME = 400;
public EntityDecay(World par1World) {
public EntityDecay(World par1World){
super(par1World);
textureIndex = this.rand.nextInt(10);
this.height = 0.2f;
this.width = 2.0f;
}
public EntityDecay(World par1World, double x, double y, double z, EntityLivingBase caster) {
public EntityDecay(World par1World, double x, double y, double z, EntityLivingBase caster){
super(par1World, x, y, z, caster, LIFETIME, 1);
textureIndex = this.rand.nextInt(10);
this.height = 0.2f;
@@ -34,45 +34,51 @@ public class EntityDecay extends EntityMagicConstruct {
@Override
public void onUpdate(){
super.onUpdate();
if(this.rand.nextInt(700) == 0 && this.ticksExisted+100 < LIFETIME) this.playSound(SoundEvents.BLOCK_LAVA_AMBIENT, 0.2F + rand.nextFloat() * 0.2F, 0.6F + rand.nextFloat() * 0.15F);
if(!this.worldObj.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj);
if(this.rand.nextInt(700) == 0 && this.ticksExisted + 100 < LIFETIME)
this.playSound(SoundEvents.BLOCK_LAVA_AMBIENT, 0.2F + rand.nextFloat() * 0.2F,
0.6F + rand.nextFloat() * 0.15F);
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY,
this.posZ, this.world);
for(EntityLivingBase target : targets){
if(target != this.getCaster()){
// If this check wasn't here the potion would be reapplied every tick and hence the entity would be damaged each tick.
// If this check wasn't here the potion would be reapplied every tick and hence the entity would be
// 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));
if(!target.isPotionActive(WizardryPotions.decay))
target.addPotionEffect(new PotionEffect(WizardryPotions.decay, LIFETIME, 0));
}
}
}else if(this.rand.nextInt(15) == 0){
double radius = rand.nextDouble()*0.8;
double angle = rand.nextDouble()*Math.PI*2;
float brightness = rand.nextFloat()*0.4f;
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + radius*Math.cos(angle), this.posY, this.posZ + radius*Math.sin(angle), 0, 0, 0, 0, brightness, 0, brightness+0.1f);
double radius = rand.nextDouble() * 0.8;
double angle = rand.nextDouble() * Math.PI * 2;
float brightness = rand.nextFloat() * 0.4f;
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, this.posX + radius * Math.cos(angle),
this.posY, this.posZ + radius * Math.sin(angle), 0, 0, 0, 0, brightness, 0, brightness + 0.1f);
}
}
protected void entityInit(){}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {
protected void entityInit(){
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound){
}
/**
* Checks using a Vec3dd to determine if this entity is within range of that vector to be rendered. Args: Vec3dD
*/
public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d)
{
return true;
}
* Checks using a Vec3dd to determine if this entity is within range of that vector to be rendered. Args: Vec3dD
*/
public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d){
return true;
}
}
@@ -23,7 +23,7 @@ public class EntityEarthquake extends EntityMagicConstruct {
}
public EntityEarthquake(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier) {
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 1.0f;
this.width = 1.0f;
@@ -33,48 +33,58 @@ public class EntityEarthquake extends EntityMagicConstruct {
super.onUpdate();
if(!worldObj.isRemote){
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(double 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
// 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));
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));
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) * Math.cos(angle) - 1)
: (int)(this.posZ + ((this.ticksExisted * speed) + 1.5) * Math.cos(angle));
BlockPos pos = new BlockPos(x, y, z);
if(!WizardryUtilities.isBlockUnbreakable(worldObj, pos) && !worldObj.isAirBlock(pos) && worldObj.isBlockNormalCube(pos, false)
if(!WizardryUtilities.isBlockUnbreakable(world, pos) && !world.isAirBlock(pos)
&& world.isBlockNormalCube(pos, false)
// Checks that the block above is not solid, since this causes the falling sand to vanish.
&& !worldObj.isBlockNormalCube(pos.up(), false)){
&& !world.isBlockNormalCube(pos.up(), false)){
// Falling blocks do the setting block to air themselves.
EntityFallingBlock fallingblock = new EntityFallingBlock(worldObj, x+0.5, y+0.5, z+0.5, worldObj.getBlockState(new BlockPos(x, y, z)));
EntityFallingBlock fallingblock = new EntityFallingBlock(world, x + 0.5, y + 0.5, z + 0.5,
world.getBlockState(new BlockPos(x, y, z)));
fallingblock.motionY = 0.3;
worldObj.spawnEntityInWorld(fallingblock);
world.spawnEntity(fallingblock);
}
}
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius((this.ticksExisted*speed)+1.5, this.posX, this.posY, this.posZ, worldObj);
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.
// In this particular instance, the caster is completely unaffected because they will always be in the
// centre.
targets.remove(this.getCaster());
for(EntityLivingBase target : targets){
// Searches in a 1 wide ring.
if(this.getDistanceToEntity(target) > (this.ticksExisted*speed)+0.5 && target.posY < this.posY + 1 && target.posY > this.posY - 1){
if(this.getDistanceToEntity(target) > (this.ticksExisted * speed) + 0.5 && target.posY < this.posY + 1
&& target.posY > this.posY - 1){
// Knockback must be removed in this instance, or the target will fall into the floor.
double motionX = target.motionX;
double motionZ = target.motionZ;
if(this.isValidTarget(target)){
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.BLAST), 10*this.damageMultiplier);
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.BLAST),
10 * this.damageMultiplier);
target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 400, 1));
}
@@ -89,18 +99,19 @@ public class EntityEarthquake extends EntityMagicConstruct {
}
}
}
// TODO: Uncomment once 2.1.0 is released
// }else{
//
// // Constant 15 blocks for now
// List<EntityPlayer> targets = WizardryUtilities.getEntitiesWithinRadius(15, this.posX, this.posY, this.posZ, worldObj, EntityPlayer.class);
//
// float magnitude = 6f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime;
//
// // Makes the screen shake
// for(EntityLivingBase target : targets){
// target.setAngles(0, this.ticksExisted % 4 < 2 ? magnitude : -magnitude);
// }
// TODO: Uncomment once 2.1.0 is released
// }else{
//
// // Constant 15 blocks for now
// List<EntityPlayer> targets = WizardryUtilities.getEntitiesWithinRadius(15, this.posX, this.posY,
// this.posZ, world, EntityPlayer.class);
//
// float magnitude = 6f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime;
//
// // Makes the screen shake
// for(EntityLivingBase target : targets){
// target.setAngles(0, this.ticksExisted % 4 < 2 ? magnitude : -magnitude);
// }
}
}
@@ -11,50 +11,54 @@ import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
public class EntityFireRing extends EntityMagicConstruct {
public EntityFireRing(World par1World) {
public EntityFireRing(World par1World){
super(par1World);
this.height = 1.0f;
this.width = 5.0f;
}
public EntityFireRing(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) {
public EntityFireRing(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 1.0f;
this.width = 5.0f;
}
public void onUpdate(){
if(this.ticksExisted % 40 == 1){
this.playSound(SoundEvents.BLOCK_FIRE_AMBIENT, 4.0f, 0.7f);
}
super.onUpdate();
if(!this.worldObj.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(2.5d, this.posX, this.posY, this.posZ, this.worldObj);
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(2.5d, this.posX, this.posY,
this.posZ, this.world);
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
double velX = target.motionX;
double velY = target.motionY;
double velZ = target.motionZ;
if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)){
target.setFire(10);
if(this.getCaster() != null){
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FIRE), 1*damageMultiplier);
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FIRE),
1 * damageMultiplier);
}else{
target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier);
target.attackEntityFrom(DamageSource.MAGIC, 1 * damageMultiplier);
}
}
// Removes knockback
target.motionX = velX;
target.motionY = velY;
@@ -13,13 +13,14 @@ import net.minecraft.world.World;
public class EntityFireSigil extends EntityMagicConstruct {
public EntityFireSigil(World par1World) {
public EntityFireSigil(World par1World){
super(par1World);
this.height = 0.2f;
this.width = 2.0f;
}
public EntityFireSigil(World par1World, double x, double y, double z, EntityLivingBase caster, float damageMultiplier) {
public EntityFireSigil(World par1World, double x, double y, double z, EntityLivingBase caster,
float damageMultiplier){
super(par1World, x, y, z, caster, -1, damageMultiplier);
this.height = 0.2f;
this.width = 2.0f;
@@ -27,8 +28,7 @@ public class EntityFireSigil extends EntityMagicConstruct {
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9)
{
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9){
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
@@ -37,9 +37,10 @@ public class EntityFireSigil extends EntityMagicConstruct {
super.onUpdate();
if(!this.worldObj.isRemote){
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY,
this.posZ, this.world);
for(EntityLivingBase target : targets){
@@ -48,8 +49,10 @@ public class EntityFireSigil extends EntityMagicConstruct {
double velX = target.motionX;
double velY = target.motionY;
double velZ = target.motionZ;
target.attackEntityFrom(this.getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FIRE) : DamageSource.magic, 6);
target.attackEntityFrom(this.getCaster() != null
? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FIRE)
: DamageSource.MAGIC, 6);
// Removes knockback
target.motionX = velX;
@@ -59,28 +62,28 @@ public class EntityFireSigil extends EntityMagicConstruct {
if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire(10);
this.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
// The trap is destroyed once triggered.
this.setDead();
}
}
}else if(this.rand.nextInt(15) == 0){
double radius = 0.5 + rand.nextDouble()*0.3;
double angle = rand.nextDouble()*Math.PI*2;
worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 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);
}
}
@Override
protected void entityInit() {
protected void entityInit(){
}
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire()
{
public boolean canRenderOnFire(){
return false;
}
@@ -16,42 +16,44 @@ import net.minecraft.world.World;
public class EntityForcefield extends EntityMagicConstruct {
public EntityForcefield(World world) {
public EntityForcefield(World world){
super(world);
this.height = 6;
this.width = 6;
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3, this.posY + 3, this.posZ + 3));
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3,
this.posY + 3, this.posZ + 3));
}
public EntityForcefield(World world, double x, double y, double z, EntityLivingBase caster, int lifetime) {
public EntityForcefield(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
// y-3 because it needs to be centred on the given position
// Damage multiplier is 1 because forcefields do no damage!
super(world, x, y-3, z, caster, lifetime, 1.0f);
super(world, x, y - 3, z, caster, lifetime, 1.0f);
this.height = 6;
this.width = 6;
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3, this.posY + 3, this.posZ + 3));
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3,
this.posY + 3, this.posZ + 3));
}
public boolean canBeCollidedWith()
{
return !this.isDead;
}
public AxisAlignedBB getCollisionBox(Entity par1Entity)
{
return par1Entity.getEntityBoundingBox();
}
public boolean canBeCollidedWith(){
return !this.isDead;
}
public AxisAlignedBB getCollisionBox(Entity par1Entity){
return par1Entity.getEntityBoundingBox();
}
public void onUpdate(){
super.onUpdate();
if(!this.worldObj.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(3.5, this.posX, this.posY + 3, this.posZ, this.worldObj);
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(3.5, this.posX, this.posY + 3,
this.posZ, this.world);
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
double multiplier = (3.5 - target.getDistance(this.posX, this.posY+3, this.posZ))*0.1;
target.addVelocity((target.posX - this.posX)*multiplier, (target.posY - (this.posY + 3))*multiplier, (target.posZ - this.posZ)*multiplier);
double multiplier = (3.5 - target.getDistance(this.posX, this.posY + 3, this.posZ)) * 0.1;
target.addVelocity((target.posX - this.posX) * multiplier,
(target.posY - (this.posY + 3)) * 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));
@@ -59,33 +61,35 @@ public class EntityForcefield extends EntityMagicConstruct {
}
}
}else{
for(int i=1; i<40; i++){
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;
// Generates a spherical pattern of particles
Wizardry.proxy.spawnParticle(WizardryParticleType.BRIGHT_DUST, worldObj, 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), 0, 0, 0, 48 + this.rand.nextInt(12), brightness, brightness, 1.0f);
for(int i = 1; i < 40; i++){
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;
// Generates a spherical pattern of particles
Wizardry.proxy.spawnParticle(WizardryParticleType.BRIGHT_DUST, world,
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), 0, 0, 0, 48 + this.rand.nextInt(12),
brightness, brightness, 1.0f);
}
}
}
public boolean attackEntityFrom(DamageSource source, float par2){
if(source != null && source.getSourceOfDamage() != null){
// Now works for any source of damage.
source.getSourceOfDamage().playSound(WizardrySounds.SPELL_DEFLECTION, 0.3f, 1.3f);
}
super.attackEntityFrom(source, par2);
return false;
}
}
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire()
{
return false;
}
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire(){
return false;
}
}
@@ -15,77 +15,80 @@ import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
public class EntityFrostSigil extends EntityMagicConstruct {
public EntityFrostSigil(World par1World) {
public EntityFrostSigil(World par1World){
super(par1World);
this.height = 0.2f;
this.width = 2.0f;
}
public EntityFrostSigil(World par1World, double x, double y, double z, EntityLivingBase caster, float damageMultiplier) {
public EntityFrostSigil(World par1World, double x, double y, double z, EntityLivingBase caster,
float damageMultiplier){
super(par1World, x, y, z, caster, -1, damageMultiplier);
this.height = 0.2f;
this.width = 2.0f;
}
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9)
{
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9){
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
public void onUpdate(){
super.onUpdate();
if(!this.worldObj.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj);
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY,
this.posZ, this.world);
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
double velX = target.motionX;
double velY = target.motionY;
double velZ = target.motionZ;
target.attackEntityFrom(this.getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FROST) : DamageSource.magic, 8);
target.attackEntityFrom(this.getCaster() != null
? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FROST)
: DamageSource.MAGIC, 8);
// Removes knockback
target.motionX = velX;
target.motionY = velY;
target.motionZ = velZ;
if(!MagicDamage.isEntityImmune(DamageType.FROST, target))
target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 1));
this.playSound(WizardrySounds.SPELL_FREEZE, 1.0f, 1.0f);
// The trap is destroyed once triggered.
this.setDead();
}
}
}else if(this.rand.nextInt(15) == 0){
double radius = 0.5 + rand.nextDouble()*0.3;
double angle = rand.nextDouble()*Math.PI*2;
Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, worldObj, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0, 40 + rand.nextInt(10));
double radius = 0.5 + rand.nextDouble() * 0.3;
double angle = rand.nextDouble() * Math.PI * 2;
Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, this.posX + radius * Math.cos(angle),
this.posY + 0.1, this.posZ + radius * Math.sin(angle), 0, 0, 0, 40 + rand.nextInt(10));
}
}
@Override
protected void entityInit() {
protected void entityInit(){
}
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire()
{
return false;
}
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire(){
return false;
}
}
@@ -5,32 +5,34 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.world.World;
public class EntityHailstorm extends EntityMagicConstruct {
public EntityHailstorm(World par1World) {
public EntityHailstorm(World par1World){
super(par1World);
this.height = 3.0f;
this.width = 5.0f;
}
public EntityHailstorm(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) {
public EntityHailstorm(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 3.0f;
this.width = 5.0f;
}
public void onUpdate(){
super.onUpdate();
if(!this.worldObj.isRemote){
//System.out.println(this.rotationYaw);
EntityIceShard iceshard = new EntityIceShard(worldObj, this.posX + rand.nextDouble()*6 - 3, this.posY + rand.nextDouble()*4 - 2, this.posZ + rand.nextDouble()*6 - 3);
if(!this.world.isRemote){
// System.out.println(this.rotationYaw);
EntityIceShard iceshard = new EntityIceShard(world, 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.motionY = -0.6;
iceshard.motionZ = Math.sin(Math.toRadians(this.rotationYaw + 90));
iceshard.setShootingEntity(this.getCaster());
iceshard.damageMultiplier = this.damageMultiplier;
this.worldObj.spawnEntityInWorld(iceshard);
this.world.spawnEntity(iceshard);
}
}
@@ -12,6 +12,7 @@ import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.effect.EntityLightningBolt;
import net.minecraft.init.SoundEvents;
import net.minecraft.nbt.NBTTagCompound;
@@ -23,161 +24,171 @@ import net.minecraft.world.World;
public class EntityHammer extends EntityMagicConstruct {
/** How long the hammer has been falling for. */
public int fallTime;
/** How long the hammer has been falling for. */
public int fallTime;
public EntityHammer(World par1World){
super(par1World);
this.setSize(1.0f, 1.9F);
this.noClip = false;
}
public EntityHammer(World par1World){
super(par1World);
this.setSize(1.0f, 1.9F);
this.noClip = false;
}
public EntityHammer(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.setSize(1.0f, 1.9F);
this.motionX = 0.0D;
this.motionY = 0.0D;
this.motionZ = 0.0D;
this.noClip = false;
}
@Override
public boolean isBurning(){
return false;
}
public EntityHammer(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.setSize(1.0f, 1.9F);
this.motionX = 0.0D;
this.motionY = 0.0D;
this.motionZ = 0.0D;
this.noClip = false;
}
@Override
public boolean canBeCollidedWith(){
return true;
}
@Override
public AxisAlignedBB getCollisionBoundingBox(){
return this.getEntityBoundingBox();
}
@Override
public boolean isBurning(){
return false;
}
@Override
public void onUpdate(){
super.onUpdate();
if(this.ticksExisted % 20 == 1 && !this.onGround && worldObj.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);
@Override
public boolean canBeCollidedWith(){
return true;
}
@Override
public AxisAlignedBB getCollisionBoundingBox(){
return this.getEntityBoundingBox();
}
@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.worldObj.isRemote && this.ticksExisted % 3 == 0){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + 2*rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0, 0, 3);
}
if(!this.worldObj.isRemote){
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
++this.fallTime;
this.motionY -= 0.03999999910593033D;
this.moveEntity(this.motionX, this.motionY, this.motionZ);
this.motionX *= 0.9800000190734863D;
this.motionY *= 0.9800000190734863D;
this.motionZ *= 0.9800000190734863D;
if(this.onGround){
this.motionX *= 0.699999988079071D;
this.motionZ *= 0.699999988079071D;
this.motionY *= -0.5D;
if(this.ticksExisted % 40 == 0){
double seekerRange = 10.0d;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX, this.posY+1, this.posZ, worldObj);
if(this.world.isRemote && this.ticksExisted % 3 == 0){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, this.posX - 0.5d + rand.nextDouble(),
this.posY + 2 * rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0, 0, 3);
}
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;
if(this.onGround){
this.motionX *= 0.699999988079071D;
this.motionZ *= 0.699999988079071D;
this.motionY *= -0.5D;
if(this.ticksExisted % 40 == 0){
double seekerRange = 10.0d;
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){
if(this.isValidTarget(target)){
if(!worldObj.isRemote){
EntityArc arc = new EntityArc(worldObj);
arc.setEndpointCoords(this.posX, this.posY + this.height - 0.1, this.posZ,
target.posX, target.posY + target.height/2, target.posZ);
worldObj.spawnEntityInWorld(arc);
if(!world.isRemote){
EntityArc arc = new EntityArc(world);
arc.setEndpointCoords(this.posX, this.posY + this.height - 0.1, this.posZ, target.posX,
target.posY + target.height / 2, target.posZ);
world.spawnEntity(arc);
}else{
for(int j=0;j<8;j++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, target.posX + worldObj.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + worldObj.rand.nextFloat()*2 - 1, target.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0, 3);
worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + rand.nextFloat(), target.getEntityBoundingBox().minY + target.height/2 + rand.nextFloat(), target.posZ + rand.nextFloat(), 0, 0, 0);
}
for(int j = 0; j < 8; j++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world,
target.posX + world.rand.nextFloat() - 0.5,
target.getEntityBoundingBox().minY + target.height / 2
+ world.rand.nextFloat() * 2 - 1,
target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3);
world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + rand.nextFloat(),
target.getEntityBoundingBox().minY + target.height / 2 + rand.nextFloat(),
target.posZ + rand.nextFloat(), 0, 0, 0);
}
}
target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
if(this.getCaster() != null){
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK), 6*damageMultiplier);
WizardryUtilities.attackEntityWithoutKnockback(target,
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK),
6 * damageMultiplier);
WizardryUtilities.applyStandardKnockback(this, target);
}else{
target.attackEntityFrom(DamageSource.magic, 6*damageMultiplier);
target.attackEntityFrom(DamageSource.MAGIC, 6 * damageMultiplier);
}
}
}
}
}
}
}
@Override
public void despawn(){
this.playSound(SoundEvents.ENTITY_GENERIC_EXPLODE, 1.0F, 1.0f);
if(this.worldObj.isRemote){
this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
}
}
}
@Override
public void despawn(){
this.playSound(SoundEvents.ENTITY_GENERIC_EXPLODE, 1.0F, 1.0f);
if(this.world.isRemote){
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
super.despawn();
}
@Override
public void fall(float distance, float damageMultiplier) {
if(worldObj.isRemote){
for(int i=0; i<40; i++){
double particleX = this.posX - 1.0d + 2*rand.nextDouble();
double particleZ = this.posZ - 1.0d + 2*rand.nextDouble();
// Roundabout way of getting a block instance for the block the hammer is standing on (if any).
IBlockState block = worldObj.getBlockState(new BlockPos(this.posX, this.posY-2, this.posZ));
}
@Override
public void fall(float distance, float damageMultiplier){
if(world.isRemote){
for(int i = 0; i < 40; i++){
double particleX = this.posX - 1.0d + 2 * rand.nextDouble();
double particleZ = this.posZ - 1.0d + 2 * rand.nextDouble();
// Roundabout way of getting a block instance for the block the hammer is standing on (if any).
IBlockState block = world.getBlockState(new BlockPos(this.posX, this.posY - 2, this.posZ));
if(block != null){
worldObj.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, this.posY, particleZ,
world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, this.posY, particleZ,
particleX - this.posX, 0, particleZ - this.posZ, Block.getStateId(block));
}
}
}else{
}
}else{
// Just to check the hammer has actually fallen from the sky, rather than the block under it being broken.
if(this.fallDistance > 10){
EntityLightningBolt entitylightning = new EntityLightningBolt(worldObj, this.posX, this.posY, this.posZ, false);
worldObj.addWeatherEffect(entitylightning);
}
}
}
if(this.fallDistance > 10){
EntityLightningBolt entitylightning = new EntityLightningBolt(world, this.posX, this.posY, this.posZ,
false);
world.addWeatherEffect(entitylightning);
}
}
}
@Override
public void writeEntityToNBT(NBTTagCompound nbttagcompound)
{
super.writeEntityToNBT(nbttagcompound);
nbttagcompound.setByte("Time", (byte)this.fallTime);
}
@Override
public void writeEntityToNBT(NBTTagCompound nbttagcompound){
super.writeEntityToNBT(nbttagcompound);
nbttagcompound.setByte("Time", (byte)this.fallTime);
}
@Override
public void readEntityFromNBT(NBTTagCompound nbttagcompound)
{
super.readEntityFromNBT(nbttagcompound);
this.fallTime = nbttagcompound.getByte("Time") & 255;
}
@Override
public void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
this.fallTime = nbttagcompound.getByte("Time") & 255;
}
@Override
public boolean isInRangeToRenderDist(double distance) {
return true;
}
@Override
public boolean isInRangeToRenderDist(double distance){
return true;
}
}
@@ -14,13 +14,14 @@ import net.minecraft.world.World;
public class EntityHealAura extends EntityMagicConstruct {
public EntityHealAura(World world) {
public EntityHealAura(World world){
super(world);
this.height = 1.0f;
this.width = 5.0f;
}
public EntityHealAura(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) {
public EntityHealAura(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 1.0f;
this.width = 5.0f;
@@ -34,9 +35,10 @@ public class EntityHealAura extends EntityMagicConstruct {
super.onUpdate();
if(!this.worldObj.isRemote){
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(2.5d, this.posX, this.posY, this.posZ, this.worldObj);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(2.5d, this.posX, this.posY,
this.posZ, this.world);
for(EntityLivingBase target : targets){
@@ -49,9 +51,11 @@ public class EntityHealAura extends EntityMagicConstruct {
double velZ = target.motionZ;
if(this.getCaster() != null){
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.RADIANT), 1*damageMultiplier);
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.RADIANT),
1 * damageMultiplier);
}else{
target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier);
target.attackEntityFrom(DamageSource.MAGIC, 1 * damageMultiplier);
}
// Removes knockback
@@ -61,15 +65,17 @@ public class EntityHealAura extends EntityMagicConstruct {
}
}else if(target.getHealth() < target.getMaxHealth() && this.ticksExisted % 5 == 0){
target.heal(1*damageMultiplier);
target.heal(1 * 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;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + radius*Math.cos(angle), this.posY, this.posZ + radius*Math.sin(angle), 0, 0.05f, 0, 48 + this.rand.nextInt(12), 1.0f, 1.0f, brightness);
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;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, this.posX + radius * Math.cos(angle),
this.posY, this.posZ + radius * Math.sin(angle), 0, 0.05f, 0, 48 + this.rand.nextInt(12), 1.0f,
1.0f, brightness);
}
}
}
@@ -77,8 +83,7 @@ public class EntityHealAura extends EntityMagicConstruct {
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire()
{
public boolean canRenderOnFire(){
return false;
}
@@ -5,45 +5,49 @@ import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.MagicDamage.DamageType;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.MoverType;
import net.minecraft.potion.PotionEffect;
import net.minecraft.world.World;
public class EntityIceSpike extends EntityMagicConstruct {
public EntityIceSpike(World world) {
public EntityIceSpike(World world){
super(world);
this.setSize(0.5f, 1.0f);
}
public EntityIceSpike(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier){
public EntityIceSpike(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.setSize(0.5f, 1.0f);
}
public void onUpdate(){
if(lifetime - this.ticksExisted < 15){
this.motionY = -0.01*(this.ticksExisted-(lifetime-15));
this.motionY = -0.01 * (this.ticksExisted - (lifetime - 15));
}else if(lifetime - this.ticksExisted < 25){
this.motionY = 0;
}else if(lifetime - this.ticksExisted < 28){
this.motionY = 0.25;
}
this.moveEntity(this.motionX, this.motionY, this.motionZ);
this.move(MoverType.SELF, this.motionX, this.motionY, this.motionZ);
if(lifetime - this.ticksExisted == 30) this.playSound(WizardrySounds.SPELL_ICE, 1, 2);
if(!this.worldObj.isRemote){
for(Object entity : this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox())){
if(!this.world.isRemote){
for(Object entity : this.world.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox())){
if(entity instanceof EntityLivingBase && this.isValidTarget((EntityLivingBase)entity)){
// Potion effect only gets added if the damage succeeded.
if(((EntityLivingBase)entity).attackEntityFrom(MagicDamage.causeDirectMagicDamage(this.getCaster(), DamageType.FROST), 5*this.damageMultiplier))
if(((EntityLivingBase)entity).attackEntityFrom(
MagicDamage.causeDirectMagicDamage(this.getCaster(), DamageType.FROST),
5 * this.damageMultiplier))
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0));
}
}
}
super.onUpdate();
}
@@ -5,12 +5,13 @@ import net.minecraft.world.World;
public class EntityLightningPulse extends EntityMagicConstruct {
public EntityLightningPulse(World world) {
public EntityLightningPulse(World world){
super(world);
this.setSize(6, 0.2f);
}
public EntityLightningPulse(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) {
public EntityLightningPulse(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.setSize(6, 0.2f);
}
@@ -18,8 +19,7 @@ public class EntityLightningPulse extends EntityMagicConstruct {
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire()
{
public boolean canRenderOnFire(){
return false;
}
@@ -15,111 +15,127 @@ import net.minecraft.util.EnumParticleTypes;
import net.minecraft.world.World;
public class EntityLightningSigil extends EntityMagicConstruct {
public EntityLightningSigil(World par1World) {
public EntityLightningSigil(World par1World){
super(par1World);
this.height = 0.2f;
this.width = 2.0f;
}
public EntityLightningSigil(World par1World, double x, double y, double z, EntityLivingBase caster, float damageMultiplier) {
public EntityLightningSigil(World par1World, double x, double y, double z, EntityLivingBase caster,
float damageMultiplier){
super(par1World, x, y, z, caster, -1, damageMultiplier);
this.height = 0.2f;
this.width = 2.0f;
}
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9)
{
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9){
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
public void onUpdate(){
super.onUpdate();
if(this.ticksExisted > 600 && this.getCaster() == null && !this.worldObj.isRemote){
if(this.ticksExisted > 600 && this.getCaster() == null && !this.world.isRemote){
this.setDead();
}
//if(!this.worldObj.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj);
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
double velX = target.motionX;
double velY = target.motionY;
double velZ = target.motionZ;
// 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)){
// Removes knockback
target.motionX = velX;
target.motionY = velY;
target.motionZ = velZ;
this.playSound(WizardrySounds.SPELL_SPARK, 1.0f, 1.0f);
// Secondary chaining effect
double seekerRange = 5.0d;
List<EntityLivingBase> secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, target.posX, target.posY + target.height/2, target.posZ, worldObj);
// if(!this.world.isRemote){
for(int j=0;j<Math.min(secondaryTargets.size(), 3);j++){
EntityLivingBase secondaryTarget = secondaryTargets.get(j);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY,
this.posZ, this.world);
if(secondaryTarget != target && this.isValidTarget(secondaryTarget)){
if(!worldObj.isRemote){
EntityArc arc = new EntityArc(worldObj);
arc.setEndpointCoords(target.posX, target.posY + target.height/2, target.posZ,
secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height/2, secondaryTarget.posZ);
worldObj.spawnEntityInWorld(arc);
}else{
for(int k=0;k<8;k++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, secondaryTarget.posX + worldObj.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + worldObj.rand.nextFloat()*2 - 1, secondaryTarget.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0, 3);
worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, secondaryTarget.posX + worldObj.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + worldObj.rand.nextFloat()*2 - 1, secondaryTarget.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0);
}
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
double velX = target.motionX;
double velY = target.motionY;
double velZ = target.motionZ;
// 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)){
// Removes knockback
target.motionX = velX;
target.motionY = velY;
target.motionZ = velZ;
this.playSound(WizardrySounds.SPELL_SPARK, 1.0f, 1.0f);
// Secondary chaining effect
double seekerRange = 5.0d;
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++){
EntityLivingBase secondaryTarget = secondaryTargets.get(j);
if(secondaryTarget != target && this.isValidTarget(secondaryTarget)){
if(!world.isRemote){
EntityArc arc = new EntityArc(world);
arc.setEndpointCoords(target.posX, target.posY + target.height / 2, target.posZ,
secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height / 2,
secondaryTarget.posZ);
world.spawnEntity(arc);
}else{
for(int k = 0; k < 8; k++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world,
secondaryTarget.posX + world.rand.nextFloat() - 0.5,
secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height / 2
+ world.rand.nextFloat() * 2 - 1,
secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3);
world.spawnParticle(EnumParticleTypes.SMOKE_LARGE,
secondaryTarget.posX + world.rand.nextFloat() - 0.5,
secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height / 2
+ world.rand.nextFloat() * 2 - 1,
secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0);
}
secondaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F, worldObj.rand.nextFloat() * 0.4F + 1.5F);
secondaryTarget.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK), 4);
}
secondaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F,
world.rand.nextFloat() * 0.4F + 1.5F);
secondaryTarget.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK), 4);
}
// The trap is destroyed once triggered.
this.setDead();
}
// The trap is destroyed once triggered.
this.setDead();
}
}
//}
if(this.worldObj.isRemote && this.rand.nextInt(15) == 0){
double radius = 0.5 + rand.nextDouble()*0.3;
double angle = rand.nextDouble()*Math.PI*2;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0, 3);
}
// }
if(this.world.isRemote && this.rand.nextInt(15) == 0){
double radius = 0.5 + rand.nextDouble() * 0.3;
double angle = rand.nextDouble() * Math.PI * 2;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, this.posX + radius * Math.cos(angle),
this.posY + 0.1, this.posZ + radius * Math.sin(angle), 0, 0, 0, 3);
}
}
@Override
protected void entityInit() {
protected void entityInit(){
}
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire()
{
return false;
}
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire(){
return false;
}
}
@@ -12,40 +12,45 @@ import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
/**
* 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.
* 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>
* 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.
* 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 {
/** 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. 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).
*/
private UUID casterUUID;
/** The time in ticks this magical construct lasts for; defaults to 600 (30 seconds). If this is -1 the construct
* doesn't despawn. */
/**
* The time in ticks this magical construct lasts for; defaults to 600 (30 seconds). If this is -1 the construct
* doesn't despawn.
*/
public int lifetime = 600;
/** The damage multiplier for this construct, determined by the wand with which it was cast. */
public float damageMultiplier = 1.0f;
public EntityMagicConstruct(World par1World) {
public EntityMagicConstruct(World par1World){
super(par1World);
this.height = 1.0f;
this.width = 1.0f;
this.noClip = true;
}
public EntityMagicConstruct(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) {
public EntityMagicConstruct(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world);
this.height = 1.0f;
this.width = 1.0f;
@@ -55,64 +60,64 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi
this.lifetime = lifetime;
this.damageMultiplier = damageMultiplier;
}
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
// it to stick in blocks.
@Override
public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, int posRotationIncrements, boolean teleport)
{
this.setPosition(x, y, z);
this.setRotation(yaw, pitch);
}
public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch,
int posRotationIncrements, boolean teleport){
this.setPosition(x, y, z);
this.setRotation(yaw, pitch);
}
public void onUpdate(){
if(this.getCaster() == null && this.casterUUID != null){
Entity entity = WizardryUtilities.getEntityByUUID(worldObj, casterUUID);
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();
}
super.onUpdate();
}
/**
* Defaults to just setDead() in EntityMagicConstruct, but is provided to allow subclasses to override this
* e.g. bubble uses it to dismount the entity inside it and play the 'pop' sound before calling super(). You
* should always call super() when overriding this method, in case it changes. There is no need, therefore, to
* call setDead() when overriding.
* Defaults to just setDead() in EntityMagicConstruct, but is provided to allow subclasses to override this e.g.
* bubble uses it to dismount the entity inside it and play the 'pop' sound before calling super(). You should
* always call super() when overriding this method, in case it changes. There is no need, therefore, to call
* setDead() when overriding.
*/
public void despawn(){
this.setDead();
}
@Override
protected void entityInit() {
protected void entityInit(){
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
casterUUID = nbttagcompound.getUniqueId("casterUUID");
lifetime = nbttagcompound.getInteger("lifetime");
damageMultiplier = nbttagcompound.getFloat("damageMultiplier");
lifetime = nbttagcompound.getInteger("lifetime");
damageMultiplier = nbttagcompound.getFloat("damageMultiplier");
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound){
if(this.getCaster() != null){
nbttagcompound.setUniqueId("casterUUID", this.getCaster().getUniqueID());
}
nbttagcompound.setUniqueId("casterUUID", this.getCaster().getUniqueID());
}
nbttagcompound.setInteger("lifetime", lifetime);
nbttagcompound.setFloat("damageMultiplier", damageMultiplier);
}
@Override
public void writeSpawnData(ByteBuf data){
data.writeInt(lifetime);
@@ -124,14 +129,14 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi
}
/**
* 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.
* 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() {
public EntityLivingBase getCaster(){
return caster == null ? null : caster.get();
}
/**
* Shorthand for {@link WizardryUtilities#isValidTarget(Entity, Entity)}, with the owner of this construct as the
* attacker. Also allows subclasses to override it if they wish to do so.
@@ -139,15 +144,14 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi
public boolean isValidTarget(Entity target){
return WizardryUtilities.isValidTarget(this.getCaster(), target);
}
@Override
public boolean canRenderOnFire()
{
return false;
}
public boolean canRenderOnFire(){
return false;
}
@Override
public boolean isPushedByWater() {
public boolean isPushedByWater(){
return false;
}
}
@@ -13,6 +13,7 @@ import io.netty.buffer.ByteBuf;
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;
@@ -26,14 +27,15 @@ public class EntityTornado extends EntityMagicConstruct {
private double velX, velZ;
public EntityTornado(World world) {
public EntityTornado(World world){
super(world);
this.height = 8.0f;
this.width = 5.0f;
this.isImmuneToFire = false;
}
public EntityTornado(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, double velX, double velZ, float damageMultiplier) {
public EntityTornado(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, double velX,
double velZ, float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 8.0f;
this.width = 5.0f;
@@ -46,25 +48,26 @@ public class EntityTornado extends EntityMagicConstruct {
super.onUpdate();
if(this.ticksExisted % 120 == 1 && worldObj.isRemote){
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);
}
this.moveEntity(velX, motionY, velZ);
this.move(MoverType.SELF, velX, motionY, velZ);
BlockPos pos = new BlockPos(this);
int y = WizardryUtilities.getNearestFloorLevelC(worldObj, pos.up(3), 5);
int y = WizardryUtilities.getNearestFloorLevelC(world, pos.up(3), 5);
pos = new BlockPos(pos.getX(), y, pos.getZ());
if(this.worldObj.getBlockState(pos).getMaterial() == Material.LAVA){
if(this.world.getBlockState(pos).getMaterial() == Material.LAVA){
// Fire tornado!
this.setFire(5);
}
if(!this.worldObj.isRemote){
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(4.0d, this.posX, this.posY, this.posZ, this.worldObj);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(4.0d, this.posX, this.posY,
this.posZ, this.world);
for(EntityLivingBase target : targets){
@@ -72,23 +75,27 @@ public class EntityTornado extends EntityMagicConstruct {
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;
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;
if(this.isBurning()){
target.setFire(4);
}
if(this.getCaster() != null){
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 1*damageMultiplier);
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC),
1 * damageMultiplier);
}else{
target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier);
target.attackEntityFrom(DamageSource.MAGIC, 1 * damageMultiplier);
}
target.motionX = dx;
target.motionY = velY+0.2;
target.motionY = velY + 0.2;
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));
@@ -101,81 +108,85 @@ public class EntityTornado extends EntityMagicConstruct {
}
}
}else{
for(int i=1; i<10; i++){
double yPos = rand.nextDouble()*8;
for(int i = 1; i < 10; i++){
double yPos = rand.nextDouble() * 8;
int blockX = (int)this.posX - 2 + this.rand.nextInt(4);
int blockZ = (int)this.posZ - 2 + this.rand.nextInt(4);
BlockPos pos1 = new BlockPos(blockX, this.posY+3, blockZ);
int blockY = WizardryUtilities.getNearestFloorLevelC(worldObj, pos1, 5) - 1;
BlockPos pos1 = new BlockPos(blockX, this.posY + 3, blockZ);
int blockY = WizardryUtilities.getNearestFloorLevelC(world, pos1, 5) - 1;
pos1 = new BlockPos(pos1.getX(), blockY, pos1.getZ());
IBlockState block = this.worldObj.getBlockState(pos1);
// If the block it found was air or something it can't pick up, it makes a best guess based on the biome.
IBlockState block = this.world.getBlockState(pos1);
// 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 = worldObj.getBiome(pos1).topBlock;
block = world.getBiome(pos1).topBlock;
}
Wizardry.proxy.spawnTornadoParticle(worldObj, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ, yPos/3 + 0.5d, 100, block, pos1);
Wizardry.proxy.spawnTornadoParticle(worldObj, 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);
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
if(block.getMaterial() == Material.LEAVES && this.rand.nextInt(3) == 0){
double yPos1 = rand.nextDouble()*8;
Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, worldObj, this.posX + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), this.posY + yPos1, this.posZ + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), 0, -0.05, 0, 40 + rand.nextInt(10));
double yPos1 = rand.nextDouble() * 8;
Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, world,
this.posX + (rand.nextDouble() * 2 - 1) * (yPos1 / 3 + 0.5d), this.posY + yPos1,
this.posZ + (rand.nextDouble() * 2 - 1) * (yPos1 / 3 + 0.5d), 0, -0.05, 0,
40 + rand.nextInt(10));
}
// Sometimes spawns snow particles if the block is snow
if(block.getMaterial() == Material.SNOW || block.getMaterial() == Material.CRAFTED_SNOW && this.rand.nextInt(3) == 0){
double yPos1 = rand.nextDouble()*8;
Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, worldObj, this.posX + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), this.posY + yPos1, this.posZ + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), 0, -0.02, 0, 40 + rand.nextInt(10));
if(block.getMaterial() == Material.SNOW
|| block.getMaterial() == Material.CRAFTED_SNOW && this.rand.nextInt(3) == 0){
double yPos1 = rand.nextDouble() * 8;
Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world,
this.posX + (rand.nextDouble() * 2 - 1) * (yPos1 / 3 + 0.5d), this.posY + yPos1,
this.posZ + (rand.nextDouble() * 2 - 1) * (yPos1 / 3 + 0.5d), 0, -0.02, 0,
40 + rand.nextInt(10));
}
}
}
}
private static boolean canTornadoPickUpBitsOf(IBlockState block){
Material material = block.getMaterial();
return material == Material.CRAFTED_SNOW
|| material == Material.GROUND
|| material == Material.GRASS
|| material == Material.LAVA
|| material == Material.SAND
|| material == Material.SNOW
|| material == Material.WATER
|| material == Material.PLANTS
|| material == Material.LEAVES
return material == Material.CRAFTED_SNOW || material == Material.GROUND || material == Material.GRASS
|| material == Material.LAVA || material == Material.SAND || material == Material.SNOW
|| material == Material.WATER || material == Material.PLANTS || material == Material.LEAVES
|| material == Material.VINE;
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
velX = nbttagcompound.getDouble("velX");
velZ = nbttagcompound.getDouble("velZ");
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {
protected void writeEntityToNBT(NBTTagCompound nbttagcompound){
super.writeEntityToNBT(nbttagcompound);
nbttagcompound.setDouble("velX", velX);
nbttagcompound.setDouble("velZ", velZ);
}
@Override
public void writeSpawnData(ByteBuf data) {
public void writeSpawnData(ByteBuf data){
super.writeSpawnData(data);
data.writeDouble(velX);
data.writeDouble(velZ);
}
@Override
public void readSpawnData(ByteBuf data) {
public void readSpawnData(ByteBuf data){
super.readSpawnData(data);
this.velX = data.readDouble();
this.velZ = data.readDouble();
@@ -18,9 +18,11 @@ import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.network.NetworkRegistry.TargetPoint;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
/** Entity AI class for use by instances of {@link ISpellCaster}. This deals with pathing, the spell casting itself
* and the attack cooldown. Also provides an automatic implementation of continuous spell casting using the methods
* specified in {@code ISpellCaster}; all the entity class needs to do is implement those methods. */
/**
* Entity AI class for use by instances of {@link ISpellCaster}. This deals with pathing, the spell casting itself and
* the attack cooldown. Also provides an automatic implementation of continuous spell casting using the methods
* specified in {@code ISpellCaster}; all the entity class needs to do is implement those methods.
*/
public class EntityAIAttackSpell extends EntityAIBase {
/** The entity the AI instance has been applied to. */
@@ -29,14 +31,20 @@ public class EntityAIAttackSpell extends EntityAIBase {
private final ISpellCaster caster;
/** The tagret to be attacked. */
private EntityLivingBase target;
/** Decremented each tick while greater than 0. When a spell is cast, this is set to that spell's cooldown plus
* the base cooldown. */
/**
* Decremented each tick while greater than 0. When a spell is cast, this is set to that spell's cooldown plus the
* base cooldown.
*/
private int cooldown;
/** The number of ticks between the entity finding a new target and when it first starts attacking, and also the
* amount that is added to the spell's cooldown between casting spells. */
/**
* The number of ticks between the entity finding a new target and when it first starts attacking, and also the
* amount that is added to the spell's cooldown between casting spells.
*/
private final int baseCooldown;
/** Decremented each tick while greater than 0. When a continuous spell is first cast, this is set to
* the value of {@link EntityAIAttackSpell#continuousSpellDuration}. */
/**
* Decremented each tick while greater than 0. When a continuous spell is first cast, this is set to the value of
* {@link EntityAIAttackSpell#continuousSpellDuration}.
*/
// I think that in this case this is only necessary on the server side. If any inconsistent behaviour
// occurs, look into syncing this as well.
private int continuousSpellTimer;
@@ -49,19 +57,22 @@ public class EntityAIAttackSpell extends EntityAIBase {
/**
* Creates a new spell attack AI with the given parameters.
*
* @param attacker The entity that that uses this AI.
* @param speed The speed that the entity should move when attacking. Only used when passed into the navigator.
* @param maxDistance The maximum distance the entity should be from its target.
* @param baseCooldown The number of ticks between the entity finding a new target and when it first starts attacking,
* and also the amount that is added to the cooldown of the spell that has just been cast.
* @param baseCooldown The number of ticks between the entity finding a new target and when it first starts
* attacking, and also the amount that is added to the cooldown of the spell that has just been cast.
* @param continuousSpellDuration The number of ticks that continuous spells will be cast for before cooling down.
*/
public EntityAIAttackSpell(ISpellCaster attacker, double speed, float maxDistance, int baseCooldown, int continuousSpellDuration){
public EntityAIAttackSpell(ISpellCaster attacker, double speed, float maxDistance, int baseCooldown,
int continuousSpellDuration){
this.cooldown = -1;
if(!(attacker instanceof EntityLiving)){
throw new IllegalArgumentException("Tried to create an EntityAICastSpell for an entity that isn't an EntityLiving");
throw new IllegalArgumentException(
"Tried to create an EntityAICastSpell for an entity that isn't an EntityLiving");
}else{
this.caster = attacker;
this.attacker = (EntityLiving)attacker;
@@ -102,8 +113,9 @@ public class EntityAIAttackSpell extends EntityAIBase {
private void setContinuousSpellAndNotify(Spell spell, SpellModifiers modifiers){
caster.setContinuousSpell(spell);
WizardryPacketHandler.net.sendToAllAround(new PacketNPCCastSpell.Message(attacker.getEntityId(),
target == null ? -1 : target.getEntityId(), EnumHand.MAIN_HAND, spell.id(), modifiers),
WizardryPacketHandler.net.sendToAllAround(
new PacketNPCCastSpell.Message(attacker.getEntityId(), target == null ? -1 : target.getEntityId(),
EnumHand.MAIN_HAND, spell.id(), modifiers),
// Particles are usually only visible from 16 blocks away, so 128 is more than far enough.
new TargetPoint(attacker.dimension, attacker.posX, attacker.posY, attacker.posZ, 128));
}
@@ -113,7 +125,8 @@ public class EntityAIAttackSpell extends EntityAIBase {
// Only executed server side.
double distanceSq = this.attacker.getDistanceSq(this.target.posX, this.target.getEntityBoundingBox().minY, this.target.posZ);
double distanceSq = this.attacker.getDistanceSq(this.target.posX, this.target.getEntityBoundingBox().minY,
this.target.posZ);
boolean targetIsVisible = this.attacker.getEntitySenses().canSee(this.target);
if(targetIsVisible){
@@ -133,24 +146,25 @@ public class EntityAIAttackSpell extends EntityAIBase {
if(this.continuousSpellTimer > 0){
this.continuousSpellTimer--;
// If the target goes out of range or out of sight...
if(distanceSq > (double)this.maxAttackDistance || !targetIsVisible
// ...or the spell is cancelled via events...
|| MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(attacker, caster.getContinuousSpell(),
caster.getModifiers(), Source.NPC, this.continuousSpellDuration - this.continuousSpellTimer))
// ...or the spell is cancelled via events...
|| MinecraftForge.EVENT_BUS
.post(new SpellCastEvent.Tick(attacker, caster.getContinuousSpell(), caster.getModifiers(),
Source.NPC, this.continuousSpellDuration - this.continuousSpellTimer))
// ...or the spell no longer succeeds...
|| !caster.getContinuousSpell().cast(attacker.worldObj, attacker, EnumHand.MAIN_HAND,
|| !caster.getContinuousSpell().cast(attacker.world, attacker, EnumHand.MAIN_HAND,
this.continuousSpellDuration - this.continuousSpellTimer, target, caster.getModifiers())
// ...or the time has elapsed...
|| this.continuousSpellTimer == 0){
// ...reset the continuous spell timer and start the cooldown.
this.continuousSpellTimer = 0;
setContinuousSpellAndNotify(Spells.none, new SpellModifiers());
this.cooldown = this.baseCooldown;
return;
}else if(this.continuousSpellDuration - this.continuousSpellTimer == 1){
// On the first tick, if the spell did succeed, fire SpellCastEvent.Post.
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(attacker, caster.getContinuousSpell(),
@@ -170,7 +184,7 @@ public class EntityAIAttackSpell extends EntityAIBase {
if(spells.size() > 0){
if(!attacker.worldObj.isRemote){
if(!attacker.world.isRemote){
// New way of choosing a spell; keeps trying until one works or all have been tried
@@ -178,7 +192,7 @@ public class EntityAIAttackSpell extends EntityAIBase {
while(!spells.isEmpty()){
spell = spells.get(attacker.worldObj.rand.nextInt(spells.size()));
spell = spells.get(attacker.world.rand.nextInt(spells.size()));
SpellModifiers modifiers = caster.getModifiers();
@@ -198,16 +212,16 @@ public class EntityAIAttackSpell extends EntityAIBase {
this.cooldown = this.baseCooldown;
}
}
/** Attempts to cast the given spell (including event firing) and returns true if it succeeded. */
private boolean attemptCastSpell(Spell spell, SpellModifiers modifiers){
// If anything stops the spell working at this point, nothing else happens.
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(attacker, spell, modifiers, Source.NPC))){
return false;
}
if(spell.cast(attacker.worldObj, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){
if(spell.cast(attacker.world, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){
if(spell.isContinuous){
// -1 because the spell has been cast once already!
@@ -215,24 +229,24 @@ public class EntityAIAttackSpell extends EntityAIBase {
setContinuousSpellAndNotify(spell, modifiers);
}else{
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(attacker, spell, modifiers, Source.NPC));
// For now, the cooldown is just added to the constant base cooldown. I think this
// is a reasonable way of doing things; it's certainly better than before.
this.cooldown = this.baseCooldown + spell.cooldown;
if(spell.doesSpellRequirePacket()){
// Sends a packet to all players in dimension to tell them to spawn particles.
IMessage msg = new PacketNPCCastSpell.Message(attacker.getEntityId(),
target.getEntityId(), EnumHand.MAIN_HAND, spell.id(), modifiers);
WizardryPacketHandler.net.sendToDimension(msg, attacker.worldObj.provider.getDimension());
IMessage msg = new PacketNPCCastSpell.Message(attacker.getEntityId(), target.getEntityId(),
EnumHand.MAIN_HAND, spell.id(), modifiers);
WizardryPacketHandler.net.sendToDimension(msg, attacker.world.provider.getDimension());
}
}
return true;
}
return false;
}
}
@@ -5,7 +5,7 @@ import net.minecraft.entity.ai.EntityAIBase;
public class EntityAISelectSpell extends EntityAIBase {
// TODO: Write this class
@Override
public boolean shouldExecute(){
return false;
@@ -10,7 +10,6 @@ import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.monster.EntityBlaze;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes;
@@ -27,18 +26,41 @@ public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public WeakReference<EntityLivingBase> getCasterReference(){ return casterReference; }
@Override public void setCasterReference(WeakReference<EntityLivingBase> reference){ casterReference = reference; }
@Override public UUID getCasterUUID() { return casterUUID; }
@Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; }
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
/**
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't
* matter because the client side entity immediately gets the lifetime value copied over to it by this class
* anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's
* no need to do anything inside it other than call super().
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't matter
* because the client side entity immediately gets the lifetime value copied over to it by this class anyway. When
* extending this class, you must override this constructor or Minecraft won't like it, but there's no need to do
* anything inside it other than call super().
*/
public EntityBlazeMinion(World world){
super(world);
@@ -46,8 +68,8 @@ public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature
}
/**
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when
* extending this class (be sure to call super()) so that AI and other things can be added.
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when extending
* this class (be sure to call super()) so that AI and other things can be added.
*/
public EntityBlazeMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world);
@@ -56,21 +78,20 @@ public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature
this.experienceValue = 0;
this.lifetime = lifetime;
}
// EntityBlaze overrides
// This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its
// targeting system with one that targets hostile mobs and takes the ADS into account.
@Override
protected void initEntityAI()
{
protected void initEntityAI(){
super.initEntityAI();
this.targetTasks.taskEntries.clear();
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
// Implementations
@Override
@@ -93,27 +114,30 @@ public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature
public void onDespawn(){
this.spawnParticleEffect();
}
/** Normally this would be private, but since this class has subclasses with different spawn/despawn particle
* effects, it makes sense to have them override this rather than both onSpawn() and onDespawn(). */
/**
* Normally this would be private, but since this class has subclasses with different spawn/despawn particle
* effects, it makes sense to have them override this rather than both onSpawn() and onDespawn().
*/
protected void spawnParticleEffect(){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
this.world.spawnParticle(EnumParticleTypes.FLAME, this.posX + this.rand.nextFloat(),
this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
}
}
}
}
@Override
public boolean hasParticleEffect() {
public boolean hasParticleEffect(){
return false;
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements.
return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack);
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
@@ -130,12 +154,31 @@ public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
// This vanilla method has nothing to do with the custom despawn() method.
@Override protected boolean canDespawn(){ return false; }
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
@@ -145,7 +188,7 @@ public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -17,61 +17,61 @@ import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.World;
public class EntityDecoy extends EntitySummonedCreature {
public EntityDecoy(World world){
super(world);
}
public EntityDecoy(World world, double x, double y, double z, EntityLivingBase caster, int lifetime) {
public EntityDecoy(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world, x, y, z, caster, lifetime);
this.setAlwaysRenderNameTag(caster instanceof EntityPlayer);
this.setAlwaysRenderNameTag(caster instanceof EntityPlayer);
}
@Override
protected void initEntityAI() {
protected void initEntityAI(){
// Decoys just wander around aimlessly, watching anything living.
this.tasks.addTask(0, new EntityAISwimming(this));
this.tasks.addTask(1, new EntityAIWander(this, 1.0D));
this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityLivingBase.class, 6.0F));
this.tasks.addTask(3, new EntityAILookIdle(this));
this.tasks.addTask(1, new EntityAIWander(this, 1.0D));
this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityLivingBase.class, 6.0F));
this.tasks.addTask(3, new EntityAILookIdle(this));
}
@Override
public void onDespawn(){
super.onDespawn();
for(int i=0; i<20; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX + (this.rand.nextDouble()-0.5)*this.width,
this.getEntityBoundingBox().minY + this.rand.nextDouble()*this.height, this.posZ + (this.rand.nextDouble()-0.5)*this.width,
0, 0, 0, 40, 0.2f, 1.0f, 0.8f);
for(int i = 0; i < 20; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world,
this.posX + (this.rand.nextDouble() - 0.5) * this.width,
this.getEntityBoundingBox().minY + this.rand.nextDouble() * this.height,
this.posZ + (this.rand.nextDouble() - 0.5) * this.width, 0, 0, 0, 40, 0.2f, 1.0f, 0.8f);
}
}
@Override
public boolean isEntityInvulnerable(DamageSource source){
return true;
}
@Override
public boolean isSneaking(){
return false;
}
@Override
public void onUpdate() {
public void onUpdate(){
super.onUpdate();
if(this.getCaster() == null || this.getCaster().isDead){
this.setDead();
this.onDespawn();
}
}
@Override
protected void applyEntityAttributes()
{
protected void applyEntityAttributes(){
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
}
@Override
public ITextComponent getDisplayName(){
if(getCaster() instanceof EntityPlayer){
@@ -87,7 +87,7 @@ public class EntityDecoy extends EntitySummonedCreature {
}
@Override
public boolean hasRangedAttack() {
public boolean hasRangedAttack(){
return false;
}
@@ -101,7 +101,8 @@ public class EntityDecoy extends EntitySummonedCreature {
public void readSpawnData(ByteBuf data){
super.readSpawnData(data);
if(!data.isReadable()) return;
this.setCasterReference(new WeakReference<EntityLivingBase>((EntityLivingBase)this.worldObj.getEntityByID(data.readInt())));
this.setCasterReference(
new WeakReference<EntityLivingBase>((EntityLivingBase)this.world.getEntityByID(data.readInt())));
}
}
@@ -66,9 +66,11 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
protected Predicate<Entity> targetSelector;
/** Data parameter for the cooldown time for wizards healing themselves. */
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.VARINT);
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityEvilWizard.class,
DataSerializers.VARINT);
/** Data parameter for the wizard's element. */
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.VARINT);
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityEvilWizard.class,
DataSerializers.VARINT);
/** The resource location for the evil wizard's loot table. */
private static final ResourceLocation LOOT_TABLE = new ResourceLocation(Wizardry.MODID, "entities/evil_wizard");
@@ -109,14 +111,18 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
public boolean apply(Entity entity){
// If the target is valid and not invisible...
if(entity != null && !entity.isInvisible() && WizardryUtilities.isValidTarget(EntityEvilWizard.this, entity)){
if(entity != null && !entity.isInvisible()
&& WizardryUtilities.isValidTarget(EntityEvilWizard.this, entity)){
//... and is a player, a summoned creature, another (non-evil) wizard ...
if(entity instanceof EntityPlayer || (entity instanceof ISummonedCreature || entity instanceof EntityWizard
// ... or in the whitelist ...
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT)))
// ... and isn't in the blacklist ...
&& !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){
// ... and is a player, a summoned creature, another (non-evil) wizard ...
if(entity instanceof EntityPlayer
|| (entity instanceof ISummonedCreature || entity instanceof EntityWizard
// ... or in the whitelist ...
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist)
.contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT)))
// ... and isn't in the blacklist ...
&& !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist)
.contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){
// ... it can be attacked.
return true;
}
@@ -125,15 +131,14 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
return false;
}
};
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
this.targetTasks.addTask(0, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.targetSelector));
}
@Override
protected void applyEntityAttributes()
{
protected void applyEntityAttributes(){
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5D);
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30);
@@ -186,7 +191,8 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
// affected with arcane jammer and healCooldown == 0, whilst the wizard didn't actually heal or play the sound,
// the particles still spawned, and since healCooldown wasn't reset they spawned every tick until the arcane
// jammer wore off.
if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0 && !this.isPotionActive(WizardryPotions.arcane_jammer)){
if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0
&& !this.isPotionActive(WizardryPotions.arcane_jammer)){
// Healer wizards use greater heal.
this.heal(this.getElement() == Element.HEALING ? 8 : 4);
@@ -196,13 +202,15 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
}else if(healCooldown == -1 && this.deathTime == 0){
// Heal particles
if(worldObj.isRemote){
for(int i=0; i<10; i++){
double d0 = (double)((float)this.posX + rand.nextFloat()*2 - 1.0F);
// Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F.
if(world.isRemote){
for(int i = 0; i < 10; i++){
double d0 = (double)((float)this.posX + rand.nextFloat() * 2 - 1.0F);
// Apparently the client side spawns the particles 1 block higher than it should... hence the -
// 0.5F.
double d1 = (double)((float)this.posY - 0.5F + rand.nextFloat());
double d2 = (double)((float)this.posZ + rand.nextFloat()*2 - 1.0F);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, d0, d1, d2, 0, 0.1F, 0, 48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f);
double d2 = (double)((float)this.posZ + rand.nextFloat() * 2 - 1.0F);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0,
48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f);
}
}else{
if(this.getHealth() < 10){
@@ -221,15 +229,19 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack){
protected boolean processInteract(EntityPlayer player, EnumHand hand){
ItemStack stack = player.getHeldItem(hand);
// Debugging
//player.addChatComponentMessage(new TextComponentTranslation("wizard.debug", Spell.get(spells[1]).getDisplayName(), Spell.get(spells[2]).getDisplayName(), Spell.get(spells[3]).getDisplayName()));
// player.addChatComponentMessage(new TextComponentTranslation("wizard.debug",
// Spell.get(spells[1]).getDisplayName(), Spell.get(spells[2]).getDisplayName(),
// Spell.get(spells[3]).getDisplayName()));
// When right-clicked with a spell book in creative, sets one of the spells to that spell
if(player.capabilities.isCreativeMode && stack != null && stack.getItem() instanceof ItemSpellBook){
if(player.capabilities.isCreativeMode && stack.getItem() instanceof ItemSpellBook){
if(this.spells.size() >= 4 && Spell.get(stack.getItemDamage()).canBeCastByNPCs()){
this.spells.set(rand.nextInt(3)+1, Spell.get(stack.getItemDamage()));
this.spells.set(rand.nextInt(3) + 1, Spell.get(stack.getItemDamage()));
return true;
}
}
@@ -251,7 +263,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
super.readEntityFromNBT(nbt);
this.setElement(Element.values()[nbt.getInteger("element")]);
this.textureIndex = nbt.getInteger("skin");
this.spells = (List<Spell>) WizardryUtilities.NBTToList(nbt.getTagList("spells", NBT.TAG_INT),
this.spells = (List<Spell>)WizardryUtilities.NBTToList(nbt.getTagList("spells", NBT.TAG_INT),
(NBTTagInt tag) -> Spell.get(tag.getInt()));
this.hasTower = nbt.getBoolean("hasTower");
}
@@ -261,26 +273,24 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
// Evil wizards can only despawn if they don't have a tower (i.e. if they spawned naturally at night)
return !this.hasTower;
}
@Override
protected float getSoundPitch(){
return (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 0.6F;
}
@Override
protected SoundEvent getAmbientSound() {
protected SoundEvent getAmbientSound(){
return SoundEvents.ENTITY_WITCH_AMBIENT;
}
@Override
protected SoundEvent getHurtSound()
{
protected SoundEvent getHurtSound(){
return SoundEvents.ENTITY_WITCH_HURT;
}
@Override
protected SoundEvent getDeathSound()
{
protected SoundEvent getDeathSound(){
return SoundEvents.ENTITY_WITCH_DEATH;
}
@@ -292,15 +302,17 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
// Drops 3-5 crystals without looting bonuses
int j = 3 + this.rand.nextInt(3) + this.rand.nextInt(1 + lootingLevel);
for(int k=0; k<j; k++){
for(int k = 0; k < j; k++){
this.dropItem(WizardryItems.magic_crystal, 1);
}
// Evil wizards occasionally drop one of their spells as a spell book, but not magic missile. This isn't in
// the dropRareDrop method because that would be just as rare as normal mobs; instead this is half as rare.
if(this.spells.size() > 0 && rand.nextInt(100) - lootingLevel < 5) this.entityDropItem(new ItemStack(WizardryItems.spell_book, 1, this.spells.get(1 + rand.nextInt(this.spells.size() - 1)).id()), 0);
if(this.spells.size() > 0 && rand.nextInt(100) - lootingLevel < 5)
this.entityDropItem(new ItemStack(WizardryItems.spell_book, 1,
this.spells.get(1 + rand.nextInt(this.spells.size() - 1)).id()), 0);
}
@Override
protected ResourceLocation getLootTable(){
return LOOT_TABLE;
@@ -336,11 +348,12 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
}
// Default chance is 0.085f, for reference.
for(EntityEquipmentSlot slot : EntityEquipmentSlot.values()) this.setDropChance(slot, 0.0f);
for(EntityEquipmentSlot slot : EntityEquipmentSlot.values())
this.setDropChance(slot, 0.0f);
// All wizards know magic missile, even if it is disabled.
spells.add(Spells.magic_missile);
Tier maxTier = EntityWizard.populateSpells(spells, element, 3, rand);
// Now done after the spells so it can take the tier into account. For evil wizards this is slightly different;
@@ -19,7 +19,6 @@ import net.minecraft.entity.monster.EntityIronGolem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumHand;
@@ -37,58 +36,91 @@ public class EntityIceGiant extends EntityIronGolem implements ISummonedCreature
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public WeakReference<EntityLivingBase> getCasterReference(){ return casterReference; }
@Override public void setCasterReference(WeakReference<EntityLivingBase> reference){ casterReference = reference; }
@Override public UUID getCasterUUID() { return casterUUID; }
@Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; }
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
/**
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't
* matter because the client side entity immediately gets the lifetime value copied over to it by this class
* anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's
* no need to do anything inside it other than call super().
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't matter
* because the client side entity immediately gets the lifetime value copied over to it by this class anyway. When
* extending this class, you must override this constructor or Minecraft won't like it, but there's no need to do
* anything inside it other than call super().
*/
public EntityIceGiant(World world){
super(world);
this.setSize(1.4F, 2.9F);
this.setSize(1.4F, 2.9F);
this.experienceValue = 0;
}
/**
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when
* extending this class (be sure to call super()) so that AI and other things can be added.
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when extending
* this class (be sure to call super()) so that AI and other things can be added.
*/
public EntityIceGiant(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world);
this.setSize(1.4F, 2.9F);
this.setSize(1.4F, 2.9F);
this.setPosition(x, y, z);
this.casterReference = new WeakReference<EntityLivingBase>(caster);
this.experienceValue = 0;
this.lifetime = lifetime;
}
@Override
protected void initEntityAI(){
this.getNavigator().getNodeProcessor().setCanSwim(false);
this.tasks.addTask(1, new EntityAIAttackMelee(this, 1.0D, true));
this.tasks.addTask(2, new EntityAIMoveTowardsTarget(this, 0.9D, 32.0F));
//this.tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D));
//this.tasks.addTask(5, new EntityAIWander(this, 0.6D));
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
this.tasks.addTask(7, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
this.tasks.addTask(1, new EntityAIAttackMelee(this, 1.0D, true));
this.tasks.addTask(2, new EntityAIMoveTowardsTarget(this, 0.9D, 32.0F));
// this.tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D));
// this.tasks.addTask(5, new EntityAIWander(this, 0.6D));
this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
this.tasks.addTask(7, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
// EntityIronGolem overrides
@Override protected void updateAITasks(){} // Disables home-checking
@Override public Village getVillage(){ return null; }
@Override public int getHoldRoseTick(){ return 0; }
@Override
protected void updateAITasks(){
} // Disables home-checking
@Override
public Village getVillage(){
return null;
}
@Override
public int getHoldRoseTick(){
return 0;
}
// Implementations
@Override
@@ -103,53 +135,59 @@ public class EntityIceGiant extends EntityIronGolem implements ISummonedCreature
}
@Override
public void onSpawn(){}
public void onSpawn(){
}
@Override
public void onDespawn(){
this.playSound(WizardrySounds.SPELL_FREEZE, 1.0f, 1.0f);
if(this.worldObj.isRemote){
for(int i=0; i<30; i++){
float brightness = 0.5f + (rand.nextFloat()/2);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, this.worldObj, this.posX-1 + rand.nextDouble()*2, this.posY+ rand.nextDouble()*3, this.posZ-1 + rand.nextDouble()*2, 0, -0.02, 0, 12 + rand.nextInt(8), brightness, brightness + 0.1f, 1.0f);
}
}
if(this.world.isRemote){
for(int i = 0; i < 30; i++){
float brightness = 0.5f + (rand.nextFloat() / 2);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, this.world,
this.posX - 1 + rand.nextDouble() * 2, this.posY + rand.nextDouble() * 3,
this.posZ - 1 + rand.nextDouble() * 2, 0, -0.02, 0, 12 + rand.nextInt(8), brightness,
brightness + 0.1f, 1.0f);
}
}
}
@Override
public void onLivingUpdate(){
super.onLivingUpdate();
if(this.world.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, this.world, this.posX - 1 + rand.nextDouble() * 2,
this.posY + rand.nextDouble() * 3, this.posZ - 1 + rand.nextDouble() * 2, 0, -0.02, 0,
40 + rand.nextInt(10));
}
}
@Override
public void onLivingUpdate(){
super.onLivingUpdate();
if(this.worldObj.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, this.worldObj, this.posX-1 + rand.nextDouble()*2, this.posY+ rand.nextDouble()*3, this.posZ-1 + rand.nextDouble()*2, 0, -0.02, 0, 40 + rand.nextInt(10));
}
}
@Override
public void onSuccessfulAttack(EntityLivingBase target){
target.motionY += 0.2;
target.motionX += this.getLookVec().xCoord*0.2;
target.motionZ += this.getLookVec().xCoord*0.2;
target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 300, 0));
this.applyEnchantments(this, target);
this.playSound(SoundEvents.ENTITY_IRONGOLEM_ATTACK, 1.0F, 1.0F);
target.motionY += 0.2;
target.motionX += this.getLookVec().xCoord * 0.2;
target.motionZ += this.getLookVec().xCoord * 0.2;
target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 300, 0));
this.applyEnchantments(this, target);
this.playSound(SoundEvents.ENTITY_IRONGOLEM_ATTACK, 1.0F, 1.0F);
}
@Override
public boolean hasParticleEffect() {
public boolean hasParticleEffect(){
return false;
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack);
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
@@ -166,13 +204,36 @@ public class EntityIceGiant extends EntityIronGolem implements ISummonedCreature
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override public boolean canPickUpLoot(){ return false; }
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
@Override
public boolean canPickUpLoot(){
return false;
}
// This vanilla method has nothing to do with the custom onDespawn() method.
@Override protected boolean canDespawn(){ return false; }
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
@@ -183,7 +244,7 @@ public class EntityIceGiant extends EntityIronGolem implements ISummonedCreature
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -33,264 +33,243 @@ public class EntityIceWraith extends EntityBlazeMinion {
super(world, x, y, z, caster, lifetime);
this.isImmuneToFire = false;
}
@Override
protected void initEntityAI(){
super.initEntityAI();
this.tasks.taskEntries.clear();
this.tasks.addTask(4, new AIIceShardAttack(this));
this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D));
this.tasks.addTask(7, new EntityAIWander(this, 1.0D));
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
this.tasks.addTask(8, new EntityAILookIdle(this));
this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D));
this.tasks.addTask(7, new EntityAIWander(this, 1.0D));
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
this.tasks.addTask(8, new EntityAILookIdle(this));
}
@Override
protected void spawnParticleEffect() {
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
float brightness = 0.5f + (rand.nextFloat()/2);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.1f, 1.0f);
protected void spawnParticleEffect(){
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
float brightness = 0.5f + (rand.nextFloat() / 2);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, this.posX - 0.5d + rand.nextDouble(),
this.posY + this.height / 2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0,
0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.1f, 1.0f);
}
}
}
@Override
public void onLivingUpdate(){
if(!this.onGround && this.motionY < 0.0D){
this.motionY *= 0.6D;
}
if(this.rand.nextInt(24) == 0){
this.playSound(WizardrySounds.SPELL_LOOP_WIND, 0.3F + this.rand.nextFloat()/4, this.rand.nextFloat() * 0.7F + 1.4F);
this.motionY *= 0.6D;
}
if(this.worldObj.isRemote){
if(this.rand.nextInt(24) == 0){
this.playSound(WizardrySounds.SPELL_LOOP_WIND, 0.3F + this.rand.nextFloat() / 4,
this.rand.nextFloat() * 0.7F + 1.4F);
}
if(this.world.isRemote){
for(int i = 0; i < 2; ++i){
this.worldObj.spawnParticle(EnumParticleTypes.CLOUD, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D);
this.world.spawnParticle(EnumParticleTypes.CLOUD,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D);
}
}
// Replaces super call.
this.livingBaseUpdate();
}
/** Copied from {@link EntityLivingBase#onLivingUpdate()}. The only change is removal of the updateElytra() call
* since that's irrelevant here. In actual fact, neither EntityMob nor EntityLiving has any code in its version
* of this method that is of use. This isn't exactly ideal, but it's the lesser of two evils since the
* alternative is copying the entire EntityBlaze class and its renderer. All to remove one particle effect... */
/**
* Copied from {@link EntityLivingBase#onLivingUpdate()}. The only change is removal of the updateElytra() call
* since that's irrelevant here. In actual fact, neither EntityMob nor EntityLiving has any code in its version of
* this method that is of use. This isn't exactly ideal, but it's the lesser of two evils since the alternative is
* copying the entire EntityBlaze class and its renderer. All to remove one particle effect...
*/
// ... demonstrating why critical methods should delegate any non-critical functionality (like particles) to
// separate protected methods.
private void livingBaseUpdate(){
if (this.jumpTicks > 0)
{
--this.jumpTicks;
}
if (this.newPosRotationIncrements > 0 && !this.canPassengerSteer())
{
double d0 = this.posX + (this.interpTargetX - this.posX) / (double)this.newPosRotationIncrements;
double d1 = this.posY + (this.interpTargetY - this.posY) / (double)this.newPosRotationIncrements;
double d2 = this.posZ + (this.interpTargetZ - this.posZ) / (double)this.newPosRotationIncrements;
double d3 = MathHelper.wrapDegrees(this.interpTargetYaw - (double)this.rotationYaw);
this.rotationYaw = (float)((double)this.rotationYaw + d3 / (double)this.newPosRotationIncrements);
this.rotationPitch = (float)((double)this.rotationPitch + (this.interpTargetPitch - (double)this.rotationPitch) / (double)this.newPosRotationIncrements);
--this.newPosRotationIncrements;
this.setPosition(d0, d1, d2);
this.setRotation(this.rotationYaw, this.rotationPitch);
}
else if (!this.isServerWorld())
{
this.motionX *= 0.98D;
this.motionY *= 0.98D;
this.motionZ *= 0.98D;
}
if(this.jumpTicks > 0){
--this.jumpTicks;
}
if (Math.abs(this.motionX) < 0.003D)
{
this.motionX = 0.0D;
}
if(this.newPosRotationIncrements > 0 && !this.canPassengerSteer()){
double d0 = this.posX + (this.interpTargetX - this.posX) / (double)this.newPosRotationIncrements;
double d1 = this.posY + (this.interpTargetY - this.posY) / (double)this.newPosRotationIncrements;
double d2 = this.posZ + (this.interpTargetZ - this.posZ) / (double)this.newPosRotationIncrements;
double d3 = MathHelper.wrapDegrees(this.interpTargetYaw - (double)this.rotationYaw);
this.rotationYaw = (float)((double)this.rotationYaw + d3 / (double)this.newPosRotationIncrements);
this.rotationPitch = (float)((double)this.rotationPitch
+ (this.interpTargetPitch - (double)this.rotationPitch) / (double)this.newPosRotationIncrements);
--this.newPosRotationIncrements;
this.setPosition(d0, d1, d2);
this.setRotation(this.rotationYaw, this.rotationPitch);
}else if(!this.isServerWorld()){
this.motionX *= 0.98D;
this.motionY *= 0.98D;
this.motionZ *= 0.98D;
}
if (Math.abs(this.motionY) < 0.003D)
{
this.motionY = 0.0D;
}
if(Math.abs(this.motionX) < 0.003D){
this.motionX = 0.0D;
}
if (Math.abs(this.motionZ) < 0.003D)
{
this.motionZ = 0.0D;
}
if(Math.abs(this.motionY) < 0.003D){
this.motionY = 0.0D;
}
this.worldObj.theProfiler.startSection("ai");
if(Math.abs(this.motionZ) < 0.003D){
this.motionZ = 0.0D;
}
if (this.isMovementBlocked())
{
this.isJumping = false;
this.moveStrafing = 0.0F;
this.moveForward = 0.0F;
this.randomYawVelocity = 0.0F;
}
else if (this.isServerWorld())
{
this.worldObj.theProfiler.startSection("newAi");
this.updateEntityActionState();
this.worldObj.theProfiler.endSection();
}
this.world.theProfiler.startSection("ai");
this.worldObj.theProfiler.endSection();
this.worldObj.theProfiler.startSection("jump");
if(this.isMovementBlocked()){
this.isJumping = false;
this.moveStrafing = 0.0F;
this.moveForward = 0.0F;
this.randomYawVelocity = 0.0F;
}else if(this.isServerWorld()){
this.world.theProfiler.startSection("newAi");
this.updateEntityActionState();
this.world.theProfiler.endSection();
}
if (this.isJumping)
{
if (this.isInWater())
{
this.handleJumpWater();
}
else if (this.isInLava())
{
this.handleJumpLava();
}
else if (this.onGround && this.jumpTicks == 0)
{
this.jump();
this.jumpTicks = 10;
}
}
else
{
this.jumpTicks = 0;
}
this.world.theProfiler.endSection();
this.world.theProfiler.startSection("jump");
this.worldObj.theProfiler.endSection();
this.worldObj.theProfiler.startSection("travel");
this.moveStrafing *= 0.98F;
this.moveForward *= 0.98F;
this.randomYawVelocity *= 0.9F;
this.moveEntityWithHeading(this.moveStrafing, this.moveForward);
this.worldObj.theProfiler.endSection();
this.worldObj.theProfiler.startSection("push");
this.collideWithNearbyEntities();
this.worldObj.theProfiler.endSection();
if(this.isJumping){
if(this.isInWater()){
this.handleJumpWater();
}else if(this.isInLava()){
this.handleJumpLava();
}else if(this.onGround && this.jumpTicks == 0){
this.jump();
this.jumpTicks = 10;
}
}else{
this.jumpTicks = 0;
}
this.world.theProfiler.endSection();
this.world.theProfiler.startSection("travel");
this.moveStrafing *= 0.98F;
this.moveForward *= 0.98F;
this.randomYawVelocity *= 0.9F;
this.moveEntityWithHeading(this.moveStrafing, this.moveForward);
this.world.theProfiler.endSection();
this.world.theProfiler.startSection("push");
this.collideWithNearbyEntities();
this.world.theProfiler.endSection();
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount){
// Removes the damage from being wet that applies to blazes by checking if the mob is actually drowning.
if(source == DamageSource.drown && (this.getAir() > 0 || this.isPotionActive(MobEffects.WATER_BREATHING))){
if(source == DamageSource.DROWN && (this.getAir() > 0 || this.isPotionActive(MobEffects.WATER_BREATHING))){
// In this case, the ice wraith is not actually drowning, so cancel the damage.
return false;
}else{
return super.attackEntityFrom(source, amount);
}
}
@Override
public boolean isBurning(){
// Uses the datawatcher on both sides because fire is private to Entity (and I'm not using reflection here).
// TESTME: This should work, but there may be some issues with updating, so if it doesn't work, copy the
// TESTME: This should work, but there may be some issues with updating, so if it doesn't work, copy the
// version from Entity and use reflection to access the fire field.
return this.getFlag(0);
}
}
/** Copied straight from EntityBlaze.AIFireballAttack, with the only changes being replacement of fireball
* spawning with a one-liner call to WizardryRegistry.iceShard.cast(...) and the removal of redundant local variables. */
/**
* Copied straight from EntityBlaze.AIFireballAttack, with the only changes being replacement of fireball spawning
* with a one-liner call to WizardryRegistry.iceShard.cast(...) and the removal of redundant local variables.
*/
static class AIIceShardAttack extends EntityAIBase {
private final EntityBlaze blaze;
private int attackStep;
private int attackTime;
public AIIceShardAttack(EntityBlaze blazeIn)
{
this.blaze = blazeIn;
this.setMutexBits(3);
}
private final EntityBlaze blaze;
private int attackStep;
private int attackTime;
/**
* Returns whether the EntityAIBase should begin execution.
*/
public boolean shouldExecute()
{
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
return entitylivingbase != null && entitylivingbase.isEntityAlive();
}
public AIIceShardAttack(EntityBlaze blazeIn){
this.blaze = blazeIn;
this.setMutexBits(3);
}
/**
* Execute a one shot task or start executing a continuous task
*/
public void startExecuting()
{
this.attackStep = 0;
}
/**
* Returns whether the EntityAIBase should begin execution.
*/
public boolean shouldExecute(){
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
return entitylivingbase != null && entitylivingbase.isEntityAlive();
}
/**
* Resets the task
*/
public void resetTask()
{
// This might be called setOnFire, but what it really controls is whether the wraith is in attack mode.
this.blaze.setOnFire(false);
}
/**
* Execute a one shot task or start executing a continuous task
*/
public void startExecuting(){
this.attackStep = 0;
}
/**
* Updates the task
*/
public void updateTask()
{
--this.attackTime;
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
double d0 = this.blaze.getDistanceSqToEntity(entitylivingbase);
/**
* Resets the task
*/
public void resetTask(){
// This might be called setOnFire, but what it really controls is whether the wraith is in attack mode.
this.blaze.setOnFire(false);
}
if (d0 < 4.0D)
{
if (this.attackTime <= 0)
{
this.attackTime = 20;
this.blaze.attackEntityAsMob(entitylivingbase);
}
/**
* Updates the task
*/
public void updateTask(){
--this.attackTime;
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
double d0 = this.blaze.getDistanceSqToEntity(entitylivingbase);
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D);
}
else if (d0 < 256.0D)
{
if (this.attackTime <= 0)
{
++this.attackStep;
if(d0 < 4.0D){
if(this.attackTime <= 0){
this.attackTime = 20;
this.blaze.attackEntityAsMob(entitylivingbase);
}
if (this.attackStep == 1)
{
this.attackTime = 60;
this.blaze.setOnFire(true);
}
else if (this.attackStep <= 4)
{
this.attackTime = 6;
}
else
{
this.attackTime = 100;
this.attackStep = 0;
this.blaze.setOnFire(false);
}
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY,
entitylivingbase.posZ, 1.0D);
}else if(d0 < 256.0D){
if(this.attackTime <= 0){
++this.attackStep;
if(this.attackStep > 1){
// Proof, if it were at all needed, of the elegance and versatility of the spell system.
Spells.ice_shard.cast(this.blaze.worldObj, this.blaze, EnumHand.MAIN_HAND, 0, entitylivingbase, new SpellModifiers());
// TODO: Decide if an event should be fired here. I'm guessing no.
}
}
if(this.attackStep == 1){
this.attackTime = 60;
this.blaze.setOnFire(true);
}else if(this.attackStep <= 4){
this.attackTime = 6;
}else{
this.attackTime = 100;
this.attackStep = 0;
this.blaze.setOnFire(false);
}
this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F);
}
else
{
this.blaze.getNavigator().clearPathEntity();
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D);
}
if(this.attackStep > 1){
// Proof, if it were at all needed, of the elegance and versatility of the spell system.
Spells.ice_shard.cast(this.blaze.world, this.blaze, EnumHand.MAIN_HAND, 0, entitylivingbase,
new SpellModifiers());
// TODO: Decide if an event should be fired here. I'm guessing no.
}
}
super.updateTask();
}
}
this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F);
}else{
this.blaze.getNavigator().clearPathEntity();
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY,
entitylivingbase.posZ, 1.0D);
}
super.updateTask();
}
}
}
@@ -27,24 +27,26 @@ public class EntityLightningWraith extends EntityBlazeMinion {
super(world, x, y, z, caster, lifetime);
this.isImmuneToFire = false;
}
@Override
protected void initEntityAI(){
super.initEntityAI();
this.tasks.taskEntries.clear();
this.tasks.addTask(4, new AILightningAttack(this));
this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D));
this.tasks.addTask(7, new EntityAIWander(this, 1.0D));
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
this.tasks.addTask(8, new EntityAILookIdle(this));
this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D));
this.tasks.addTask(7, new EntityAIWander(this, 1.0D));
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
this.tasks.addTask(8, new EntityAILookIdle(this));
}
@Override
protected void spawnParticleEffect(){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
float brightness = 0.3f + (rand.nextFloat()/2);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.2f, 1.0f);
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
float brightness = 0.3f + (rand.nextFloat() / 2);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, this.posX - 0.5d + rand.nextDouble(),
this.posY + this.height / 2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0,
0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.2f, 1.0f);
}
}
}
@@ -53,128 +55,119 @@ public class EntityLightningWraith extends EntityBlazeMinion {
public void onLivingUpdate(){
// Fortunately, lightning wraiths don't replace any of blazes' particle effects or the fire sound, they only
// add the sparks, so it's fine to call super here.
if(worldObj.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 3);
if(world.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 3);
}
super.onLivingUpdate();
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount){
// Removes the damage from being wet that applies to blazes by checking if the mob is actually drowning.
if(source == DamageSource.drown && (this.getAir() > 0 || this.isPotionActive(MobEffects.WATER_BREATHING))){
if(source == DamageSource.DROWN && (this.getAir() > 0 || this.isPotionActive(MobEffects.WATER_BREATHING))){
// In this case, the lightning wraith is not actually drowning, so cancel the damage.
return false;
}else{
return super.attackEntityFrom(source, amount);
}
}
@Override
public boolean isBurning(){
// Uses the datawatcher on both sides because fire is private to Entity (and I'm not using reflection here).
// TESTME: This should work, but there may be some issues with updating, so if it doesn't work, copy the
// TESTME: This should work, but there may be some issues with updating, so if it doesn't work, copy the
// version from Entity and use reflection to access the fire field.
return this.getFlag(0);
}
/** Copied straight from EntityBlaze.AIFireballAttack, with the only changes being replacement of fireball
* spawning with a one-liner call to WizardryRegistry.arc.cast(...) and the removal of redundant local variables. */
}
/**
* Copied straight from EntityBlaze.AIFireballAttack, with the only changes being replacement of fireball spawning
* with a one-liner call to WizardryRegistry.arc.cast(...) and the removal of redundant local variables.
*/
static class AILightningAttack extends EntityAIBase {
private final EntityBlaze blaze;
private int attackStep;
private int attackTime;
public AILightningAttack(EntityBlaze blazeIn)
{
this.blaze = blazeIn;
this.setMutexBits(3);
}
private final EntityBlaze blaze;
private int attackStep;
private int attackTime;
/**
* Returns whether the EntityAIBase should begin execution.
*/
public boolean shouldExecute()
{
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
return entitylivingbase != null && entitylivingbase.isEntityAlive();
}
public AILightningAttack(EntityBlaze blazeIn){
this.blaze = blazeIn;
this.setMutexBits(3);
}
/**
* Execute a one shot task or start executing a continuous task
*/
public void startExecuting()
{
this.attackStep = 0;
}
/**
* Returns whether the EntityAIBase should begin execution.
*/
public boolean shouldExecute(){
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
return entitylivingbase != null && entitylivingbase.isEntityAlive();
}
/**
* Resets the task
*/
public void resetTask()
{
// This might be called setOnFire, but what it really controls is whether the wraith is in attack mode.
this.blaze.setOnFire(false);
}
/**
* Execute a one shot task or start executing a continuous task
*/
public void startExecuting(){
this.attackStep = 0;
}
/**
* Updates the task
*/
public void updateTask()
{
--this.attackTime;
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
double d0 = this.blaze.getDistanceSqToEntity(entitylivingbase);
/**
* Resets the task
*/
public void resetTask(){
// This might be called setOnFire, but what it really controls is whether the wraith is in attack mode.
this.blaze.setOnFire(false);
}
if (d0 < 4.0D)
{
if (this.attackTime <= 0)
{
this.attackTime = 20;
this.blaze.attackEntityAsMob(entitylivingbase);
}
/**
* Updates the task
*/
public void updateTask(){
--this.attackTime;
EntityLivingBase entitylivingbase = this.blaze.getAttackTarget();
double d0 = this.blaze.getDistanceSqToEntity(entitylivingbase);
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D);
}
else if (d0 < 256.0D)
{
if (this.attackTime <= 0)
{
++this.attackStep;
if(d0 < 4.0D){
if(this.attackTime <= 0){
this.attackTime = 20;
this.blaze.attackEntityAsMob(entitylivingbase);
}
if (this.attackStep == 1)
{
this.attackTime = 60;
this.blaze.setOnFire(true);
}
else if (this.attackStep <= 4)
{
this.attackTime = 6;
}
else
{
this.attackTime = 100;
this.attackStep = 0;
this.blaze.setOnFire(false);
}
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY,
entitylivingbase.posZ, 1.0D);
}else if(d0 < 256.0D){
if(this.attackTime <= 0){
++this.attackStep;
if(this.attackStep > 1){
// Proof, if it were at all needed, of the elegance and versatility of the spell system.
Spells.arc.cast(this.blaze.worldObj, this.blaze, EnumHand.MAIN_HAND, 0, entitylivingbase, new SpellModifiers());
// TODO: Decide if an event should be fired here. I'm guessing no.
}
}
if(this.attackStep == 1){
this.attackTime = 60;
this.blaze.setOnFire(true);
}else if(this.attackStep <= 4){
this.attackTime = 6;
}else{
this.attackTime = 100;
this.attackStep = 0;
this.blaze.setOnFire(false);
}
this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F);
}
else
{
this.blaze.getNavigator().clearPathEntity();
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D);
}
if(this.attackStep > 1){
// Proof, if it were at all needed, of the elegance and versatility of the spell system.
Spells.arc.cast(this.blaze.world, this.blaze, EnumHand.MAIN_HAND, 0, entitylivingbase,
new SpellModifiers());
// TODO: Decide if an event should be fired here. I'm guessing no.
}
}
super.updateTask();
}
}
this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F);
}else{
this.blaze.getNavigator().clearPathEntity();
this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY,
entitylivingbase.posZ, 1.0D);
}
super.updateTask();
}
}
}
@@ -12,7 +12,6 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.DamageSource;
@@ -32,21 +31,46 @@ public class EntityMagicSlime extends EntitySlime implements ISummonedCreature {
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public WeakReference<EntityLivingBase> getCasterReference(){ return casterReference; }
@Override public void setCasterReference(WeakReference<EntityLivingBase> reference){ casterReference = reference; }
@Override public UUID getCasterUUID() { return casterUUID; }
@Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; }
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
public EntityMagicSlime(World world){
super(world);
this.setSlimeSize(2); // Needs to be called before setting the experience value to 0
// TESTME: Should this be true or false? Has something to do with health.
this.setSlimeSize(2, false); // Needs to be called before setting the experience value to 0
this.experienceValue = 0;
}
/**
* Creates a new magic slime with the given caster and lifetime, riding the given target.
*
* @param world The world that the slime is in.
* @param caster The entity that created the slime.
* @param target The slime's victim. The slime will automatically start riding this entity.
@@ -57,49 +81,52 @@ public class EntityMagicSlime extends EntitySlime implements ISummonedCreature {
this.setPosition(target.posX, target.posY, target.posZ);
this.startRiding(target);
this.casterReference = new WeakReference<EntityLivingBase>(caster);
this.setSlimeSize(2); // Needs to be called before setting the experience value to 0
this.setSlimeSize(2, false); // Needs to be called before setting the experience value to 0
this.experienceValue = 0;
this.lifetime = lifetime;
}
// EntitySlime overrides
@Override
protected void initEntityAI(){} // Has no AI!
protected void initEntityAI(){
} // Has no AI!
@Override
protected void dealDamage(EntityLivingBase entity){} // Handles damage itself
protected void dealDamage(EntityLivingBase entity){
} // Handles damage itself
@Override
public void setDead(){
// Restores behaviour from Entity, replacing slime splitting behaviour.
this.isDead = true;
// Makes sure that the undoing in onUpdate won't undo this. For some reason, EntitySlime sets isDead directly
// to do the peaceful despawning, which seems odd but is actually rather handy!
this.setHealth(0);
// Bursting effect
for(int i=0; i<30; i++){
this.isDead = true;
// Makes sure that the undoing in onUpdate won't undo this. For some reason, EntitySlime sets isDead directly
// to do the peaceful despawning, which seems odd but is actually rather handy!
this.setHealth(0);
// Bursting effect
for(int i = 0; i < 30; i++){
double x = this.posX - 0.5 + rand.nextDouble();
double y = this.posY - 0.5 + rand.nextDouble();
double z = this.posZ - 0.5 + rand.nextDouble();
this.worldObj.spawnParticle(EnumParticleTypes.SLIME, x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2);
this.world.spawnParticle(EnumParticleTypes.SLIME, x, y, z, (x - this.posX) * 2, (y - this.posY) * 2,
(z - this.posZ) * 2);
}
this.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 2.5f, 0.6f);
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST_FAR, 1.0f, 0.5f);
}
}
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata){
// Removes size randomisation
IEntityLivingData data = super.onInitialSpawn(difficulty, livingdata);
this.setSlimeSize(2);
return data;
}
this.setSlimeSize(2, false);
return data;
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount){
// Immune to suffocation
return source == DamageSource.inWall ? false : super.attackEntityFrom(source, amount);
return source == DamageSource.IN_WALL ? false : super.attackEntityFrom(source, amount);
}
// Implementations
@@ -111,45 +138,49 @@ public class EntityMagicSlime extends EntitySlime implements ISummonedCreature {
@Override
public void onUpdate(){
super.onUpdate();
// Undoes the despawning on peaceful behaviour. I don't think there's anything in super.onUpdate that sets
// isDead other than that, but it's better to do a quick sanity check just to be sure.
if(this.isDead && worldObj.getDifficulty() == EnumDifficulty.PEACEFUL && this.getHealth() > 0) this.isDead = false;
if(this.isDead && world.getDifficulty() == EnumDifficulty.PEACEFUL && this.getHealth() > 0) this.isDead = false;
// Bursts instantly rather than doing the falling over animation.
if(this.getHealth() <= 0) this.setDead();
this.updateDelegate();
// Damages and slows the slime's victim or makes the slime explode if the victim is dead.
if(this.getRidingEntity() != null && this.getRidingEntity() instanceof EntityLivingBase && ((EntityLivingBase)this.getRidingEntity()).getHealth() > 0){
if(this.ticksExisted % 16 == 1){
this.getRidingEntity().attackEntityFrom(DamageSource.magic, 1);
((EntityLivingBase)this.getRidingEntity()).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 20, 2));
this.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 1.0f, 1.0f);
this.squishAmount = 0.5F;
}
}else{
this.setDead();
}
if(this.getRidingEntity() != null && this.getRidingEntity() instanceof EntityLivingBase
&& ((EntityLivingBase)this.getRidingEntity()).getHealth() > 0){
if(this.ticksExisted % 16 == 1){
this.getRidingEntity().attackEntityFrom(DamageSource.MAGIC, 1);
((EntityLivingBase)this.getRidingEntity())
.addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 20, 2));
this.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 1.0f, 1.0f);
this.squishAmount = 0.5F;
}
}else{
this.setDead();
}
}
@Override
public void onSpawn(){}
public void onSpawn(){
}
@Override
public void onDespawn(){}
public void onDespawn(){
}
@Override
public boolean hasParticleEffect() {
public boolean hasParticleEffect(){
return false;
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack);
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
@@ -166,12 +197,35 @@ public class EntityMagicSlime extends EntitySlime implements ISummonedCreature {
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override public boolean canPickUpLoot(){ return false; }
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
@Override
public boolean canPickUpLoot(){
return false;
}
// This vanilla method has nothing to do with the custom onDespawn() method.
@Override protected boolean canDespawn(){ return false; }
@Override
protected boolean canDespawn(){
return false;
}
}
@@ -22,42 +22,42 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaster {
private double AISpeed = 0.5;
private double AISpeed = 0.5;
// Can attack for 7 seconds, then must cool down for 3.
private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 60, 140);
private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 60, 140);
private Spell continuousSpell;
private static final List<Spell> attack = Collections.singletonList(Spells.flame_ray);
public EntityPhoenix(World world){
super(world);
}
public EntityPhoenix(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world, x, y, z, caster, lifetime);
this.isImmuneToFire = true;
this.height = 2.0f;
super(world);
}
public EntityPhoenix(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world, x, y, z, caster, lifetime);
this.isImmuneToFire = true;
this.height = 2.0f;
// For some reason this can't be in initEntityAI
this.tasks.addTask(1, this.spellAttackAI);
}
@Override
protected void initEntityAI(){
this.tasks.addTask(1, this.spellAttackAI);
}
this.tasks.addTask(0, new EntityAIWatchClosest(this, EntityLivingBase.class, 0));
//this.tasks.addTask(2, new EntityAIWander(this, AISpeed));
this.tasks.addTask(3, new EntityAILookIdle(this));
//this.targetTasks.addTask(0, new EntityAIMoveTowardsTarget(this, 1, 10));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
@Override
protected void initEntityAI(){
this.tasks.addTask(0, new EntityAIWatchClosest(this, EntityLivingBase.class, 0));
// this.tasks.addTask(2, new EntityAIWander(this, AISpeed));
this.tasks.addTask(3, new EntityAILookIdle(this));
// this.targetTasks.addTask(0, new EntityAIMoveTowardsTarget(this, 1, 10));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
this.setAIMoveSpeed((float)AISpeed);
}
this.setAIMoveSpeed((float)AISpeed);
}
@Override
public List<Spell> getSpells(){
return attack;
@@ -77,113 +77,118 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
public void setContinuousSpell(Spell spell){
continuousSpell = spell;
}
@Override
public boolean hasRangedAttack() {
@Override
public boolean hasRangedAttack(){
return true;
}
@Override
// Makes the flames come from the phoenix's head rather than its body
public float getEyeHeight(){
return 2.1f;
}
@Override
protected void applyEntityAttributes(){
super.applyEntityAttributes();
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(30.0D);
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D);
}
@Override
protected SoundEvent getAmbientSound(){
return SoundEvents.ENTITY_BLAZE_AMBIENT;
}
@Override
protected SoundEvent getHurtSound(){
return SoundEvents.ENTITY_BLAZE_HURT;
}
@Override
protected SoundEvent getDeathSound(){
return SoundEvents.ENTITY_BLAZE_DEATH;
}
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float par1){
return 15728880;
}
@Override
public float getBrightness(float par1){
return 1.0F;
}
@Override
public void onSpawn(){
this.spawnParticleEffect();
}
@Override
public void onDespawn(){
this.spawnParticleEffect();
// Makes the flames come from the phoenix's head rather than its body
public float getEyeHeight(){
return 2.1f;
}
private void spawnParticleEffect(){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
}
}
}
@Override
public void onLivingUpdate(){
// Makes the phoenix hover.
int floorLevel = WizardryUtilities.getNearestFloorLevel(worldObj, new BlockPos(this), 4);
if(this.posY - floorLevel > 3){
this.motionY = -0.1;
}else if(this.posY - floorLevel < 2){
this.motionY = 0.1;
}else{
this.motionY = 0.0;
}
protected void applyEntityAttributes(){
super.applyEntityAttributes();
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(30.0D);
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D);
}
// Living sound
if(this.rand.nextInt(24) == 0){
this.playSound(SoundEvents.BLOCK_FIRE_AMBIENT, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F);
}
@Override
protected SoundEvent getAmbientSound(){
return SoundEvents.ENTITY_BLAZE_AMBIENT;
}
// Flapping sound effect
@Override
protected SoundEvent getHurtSound(){
return SoundEvents.ENTITY_BLAZE_HURT;
}
@Override
protected SoundEvent getDeathSound(){
return SoundEvents.ENTITY_BLAZE_DEATH;
}
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float par1){
return 15728880;
}
@Override
public float getBrightness(float par1){
return 1.0F;
}
@Override
public void onSpawn(){
this.spawnParticleEffect();
}
@Override
public void onDespawn(){
this.spawnParticleEffect();
}
private void spawnParticleEffect(){
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
this.world.spawnParticle(EnumParticleTypes.FLAME, this.posX + this.rand.nextFloat(),
this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
}
}
}
@Override
public void onLivingUpdate(){
// Makes the phoenix hover.
int floorLevel = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(this), 4);
if(this.posY - floorLevel > 3){
this.motionY = -0.1;
}else if(this.posY - floorLevel < 2){
this.motionY = 0.1;
}else{
this.motionY = 0.0;
}
// Living sound
if(this.rand.nextInt(24) == 0){
this.playSound(SoundEvents.BLOCK_FIRE_AMBIENT, 1.0F + this.rand.nextFloat(),
this.rand.nextFloat() * 0.7F + 0.3F);
}
// Flapping sound effect
if(this.ticksExisted % 22 == 0){
this.playSound(SoundEvents.ENTITY_ENDERDRAGON_FLAP, 1.0F, 1.0f);
}
for(int i=0; i<2; i++){
this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.height/2 + this.rand.nextDouble() * (double)this.height/2, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, -0.1D, 0.0D);
}
// Adding this allows the phoenix to attack despite being in the air. However, for some strange reason
// it will only attack when within about 3 blocks of the ground. Any higher and it just sits there, not even
// attempting to find targets.
this.onGround = true;
for(int i = 0; i < 2; i++){
this.world.spawnParticle(EnumParticleTypes.FLAME,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.height / 2 + this.rand.nextDouble() * (double)this.height / 2,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, -0.1D, 0.0D);
}
super.onLivingUpdate();
}
@Override
public void fall(float distance, float damageMultiplier){} // Immune to fall damage
// Adding this allows the phoenix to attack despite being in the air. However, for some strange reason
// it will only attack when within about 3 blocks of the ground. Any higher and it just sits there, not even
// attempting to find targets.
this.onGround = true;
super.onLivingUpdate();
}
@Override
public boolean isBurning()
{
return false;
}
public void fall(float distance, float damageMultiplier){
} // Immune to fall damage
@Override
public boolean isBurning(){
return false;
}
}
@@ -25,13 +25,13 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
public class EntityShadowWraith extends EntitySummonedCreature implements ISpellCaster {
// TODO: This currently doesn't fly like it used to. Should it, or does it not matter?
private double AISpeed = 1.0;
private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 30, 0);
private double AISpeed = 1.0;
private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 30, 0);
private static final List<Spell> attack = Collections.singletonList(Spells.darkness_orb);
public EntityShadowWraith(World world){
@@ -41,24 +41,24 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
public EntityShadowWraith(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world, x, y, z, caster, lifetime);
// For some reason this can't be in initEntityAI
this.tasks.addTask(0, this.spellAttackAI);
this.tasks.addTask(0, this.spellAttackAI);
}
@Override
protected void initEntityAI(){
this.tasks.addTask(1, new EntityAIAttackMelee(this, AISpeed, false));
this.tasks.addTask(1, new EntityAIAttackMelee(this, AISpeed, false));
this.tasks.addTask(2, new EntityAIWander(this, AISpeed));
this.tasks.addTask(3, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
this.setAIMoveSpeed((float)AISpeed);
this.setAIMoveSpeed((float)AISpeed);
}
@Override
public boolean hasRangedAttack() {
public boolean hasRangedAttack(){
return true;
}
@@ -85,32 +85,32 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
@Override
protected void applyEntityAttributes(){
super.applyEntityAttributes();
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(AISpeed);
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D);
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(16.0D);
}
@Override
public boolean isPotionApplicable(PotionEffect potion){
return potion.getPotion() == MobEffects.WITHER ? false : super.isPotionApplicable(potion);
}
@Override
protected SoundEvent getAmbientSound(){
return SoundEvents.ENTITY_BLAZE_AMBIENT;
}
@Override
protected SoundEvent getAmbientSound(){
return SoundEvents.ENTITY_BLAZE_AMBIENT;
}
@Override
protected SoundEvent getHurtSound(){
return SoundEvents.ENTITY_BLAZE_HURT;
}
@Override
protected SoundEvent getHurtSound(){
return SoundEvents.ENTITY_BLAZE_HURT;
}
@Override
protected SoundEvent getDeathSound(){
return SoundEvents.ENTITY_BLAZE_DEATH;
}
@Override
protected SoundEvent getDeathSound(){
return SoundEvents.ENTITY_BLAZE_DEATH;
}
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float partialTicks){
@@ -124,10 +124,12 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
@Override
public void onSpawn(){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
float brightness = rand.nextFloat()*0.4f;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness);
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
float brightness = rand.nextFloat() * 0.4f;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, this.posX - 0.5d + rand.nextDouble(),
this.posY + this.height / 2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0,
0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness);
}
}
}
@@ -136,7 +138,8 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
public void onLivingUpdate(){
if(this.rand.nextInt(24) == 0){
this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F);
this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.0F + this.rand.nextFloat(),
this.rand.nextFloat() * 0.7F + 0.3F);
}
// Slow fall
@@ -144,22 +147,36 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
this.motionY *= 0.6D;
}
if(worldObj.isRemote){
for(int i=0; i<2; i++){
worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0);
worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0);
float brightness = rand.nextFloat()*0.2f;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f);
if(world.isRemote){
for(int i = 0; i < 2; i++){
world.spawnParticle(EnumParticleTypes.PORTAL,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0);
world.spawnParticle(EnumParticleTypes.SMOKE_LARGE,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0);
float brightness = rand.nextFloat() * 0.2f;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0.05f, 0,
20 + rand.nextInt(10), brightness, 0.0f, brightness);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f,
0.0f, 0.0f);
}
}
super.onLivingUpdate();
}
@Override
public void fall(float distance, float damageMultiplier){
// Immune to fall damage.
}
}
@@ -14,7 +14,6 @@ import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.monster.EntitySilverfish;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
@@ -30,18 +29,41 @@ public class EntitySilverfishMinion extends EntitySilverfish implements ISummone
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public WeakReference<EntityLivingBase> getCasterReference(){ return casterReference; }
@Override public void setCasterReference(WeakReference<EntityLivingBase> reference){ casterReference = reference; }
@Override public UUID getCasterUUID() { return casterUUID; }
@Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; }
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
/**
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't
* matter because the client side entity immediately gets the lifetime value copied over to it by this class
* anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's
* no need to do anything inside it other than call super().
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't matter
* because the client side entity immediately gets the lifetime value copied over to it by this class anyway. When
* extending this class, you must override this constructor or Minecraft won't like it, but there's no need to do
* anything inside it other than call super().
*/
public EntitySilverfishMinion(World world){
super(world);
@@ -49,8 +71,8 @@ public class EntitySilverfishMinion extends EntitySilverfish implements ISummone
}
/**
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when
* extending this class (be sure to call super()) so that AI and other things can be added.
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when extending
* this class (be sure to call super()) so that AI and other things can be added.
*/
public EntitySilverfishMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world);
@@ -62,8 +84,7 @@ public class EntitySilverfishMinion extends EntitySilverfish implements ISummone
// EntitySilverfish overrides
@Override
protected void initEntityAI()
{
protected void initEntityAI(){
// Super not called because we don't want AISummonSilverfish or AIHideInStone
this.tasks.addTask(1, new EntityAISwimming(this));
this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, false));
@@ -71,7 +92,7 @@ public class EntitySilverfishMinion extends EntitySilverfish implements ISummone
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
// Implementations
@Override
@@ -96,13 +117,15 @@ public class EntitySilverfishMinion extends EntitySilverfish implements ISummone
}
private void spawnParticleEffect(){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + this.rand.nextFloat(), this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.3f, 0.3f);
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, this.posX + this.rand.nextFloat(),
this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.3f,
0.3f, 0.3f);
}
}
}
@Override
public void onSuccessfulAttack(EntityLivingBase target){
if(!target.isEntityAlive()){
@@ -111,30 +134,31 @@ public class EntitySilverfishMinion extends EntitySilverfish implements ISummone
}
@Override
public void onKillEntity(EntityLivingBase victim) {
public void onKillEntity(EntityLivingBase victim){
// If the silverfish has a summoner, this is actually called from Wizardry's event handler rather than by
// Minecraft itself, because the damagesource being changed causes it not to get called.
if(!this.worldObj.isRemote){
if(!this.world.isRemote){
// Summons 1-4 more silverfish
int alliesToSummon = rand.nextInt(4) + 1;
for(int i=0; i<alliesToSummon; i++){
EntitySilverfishMinion silverfish = new EntitySilverfishMinion(this.worldObj, victim.posX, victim.posY, victim.posZ, this.getCaster(), this.lifetime);
this.worldObj.spawnEntityInWorld(silverfish);
for(int i = 0; i < alliesToSummon; i++){
EntitySilverfishMinion silverfish = new EntitySilverfishMinion(this.world, victim.posX, victim.posY,
victim.posZ, this.getCaster(), this.lifetime);
this.world.spawnEntity(silverfish);
}
}
}
@Override
public boolean hasParticleEffect() {
public boolean hasParticleEffect(){
return true;
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements.
return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack);
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
@@ -151,13 +175,36 @@ public class EntitySilverfishMinion extends EntitySilverfish implements ISummone
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override public boolean canPickUpLoot(){ return false; }
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
@Override
public boolean canPickUpLoot(){
return false;
}
// This vanilla method has nothing to do with the custom despawn() method.
@Override protected boolean canDespawn(){ return false; }
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
@@ -168,7 +215,7 @@ public class EntitySilverfishMinion extends EntitySilverfish implements ISummone
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -14,16 +14,13 @@ import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.monster.EntitySkeleton;
import net.minecraft.entity.monster.SkeletonType;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.init.MobEffects;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.ResourceLocation;
@@ -40,18 +37,41 @@ public class EntitySkeletonMinion extends EntitySkeleton implements ISummonedCre
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public WeakReference<EntityLivingBase> getCasterReference(){ return casterReference; }
@Override public void setCasterReference(WeakReference<EntityLivingBase> reference){ casterReference = reference; }
@Override public UUID getCasterUUID() { return casterUUID; }
@Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; }
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
/**
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't
* matter because the client side entity immediately gets the lifetime value copied over to it by this class
* anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's
* no need to do anything inside it other than call super().
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't matter
* because the client side entity immediately gets the lifetime value copied over to it by this class anyway. When
* extending this class, you must override this constructor or Minecraft won't like it, but there's no need to do
* anything inside it other than call super().
*/
public EntitySkeletonMinion(World world){
super(world);
@@ -59,8 +79,8 @@ public class EntitySkeletonMinion extends EntitySkeleton implements ISummonedCre
}
/**
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when
* extending this class (be sure to call super()) so that AI and other things can be added.
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when extending
* this class (be sure to call super()) so that AI and other things can be added.
*/
public EntitySkeletonMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world);
@@ -69,57 +89,52 @@ public class EntitySkeletonMinion extends EntitySkeleton implements ISummonedCre
this.experienceValue = 0;
this.lifetime = lifetime;
}
// EntitySkeleton overrides
// This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its
// targeting system with one that targets hostile mobs and takes the ADS into account.
@Override
protected void initEntityAI()
{
protected void initEntityAI(){
super.initEntityAI();
this.targetTasks.taskEntries.clear();
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
// Shouldn't have randomised armour, but does still need a bow!
@Override
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty) {
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW));
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty){
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW));
}
// Where the skeleton minion is summoned does not affect its type.
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata)
{
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata){
// Can't call super, so the code from the next level up (EntityLiving) had to be copied as well.
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1));
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE)
.applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1));
if (this.rand.nextFloat() < 0.05F)
{
this.setLeftHanded(true);
}
else
{
this.setLeftHanded(false);
}
if(this.rand.nextFloat() < 0.05F){
this.setLeftHanded(true);
}else{
this.setLeftHanded(false);
}
// Halloween pumpkin heads! Why not?
if (this.getItemStackFromSlot(EntityEquipmentSlot.HEAD) == null)
{
Calendar calendar = this.worldObj.getCurrentDate();
// Halloween pumpkin heads! Why not?
if(this.getItemStackFromSlot(EntityEquipmentSlot.HEAD) == null){
Calendar calendar = this.world.getCurrentDate();
if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.rand.nextFloat() < 0.25F)
{
this.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(this.rand.nextFloat() < 0.1F ? Blocks.LIT_PUMPKIN : Blocks.PUMPKIN));
this.inventoryArmorDropChances[EntityEquipmentSlot.HEAD.getIndex()] = 0.0F;
}
}
if(calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.rand.nextFloat() < 0.25F){
this.setItemStackToSlot(EntityEquipmentSlot.HEAD,
new ItemStack(this.rand.nextFloat() < 0.1F ? Blocks.LIT_PUMPKIN : Blocks.PUMPKIN));
this.inventoryArmorDropChances[EntityEquipmentSlot.HEAD.getIndex()] = 0.0F;
}
}
return livingdata;
}
return livingdata;
}
// Implementations
@@ -143,32 +158,26 @@ public class EntitySkeletonMinion extends EntitySkeleton implements ISummonedCre
public void onDespawn(){
this.spawnParticleEffect();
}
private void spawnParticleEffect(){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
this.world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(),
this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
}
}
}
@Override
public boolean hasParticleEffect() {
public boolean hasParticleEffect(){
return true;
}
@Override
public void onSuccessfulAttack(EntityLivingBase target) {
if(this.getSkeletonType() == SkeletonType.WITHER){
target.addPotionEffect(new PotionEffect(MobEffects.WITHER, 200));
}
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements.
return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack);
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
@@ -185,13 +194,36 @@ public class EntitySkeletonMinion extends EntitySkeleton implements ISummonedCre
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override public boolean canPickUpLoot(){ return false; }
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
@Override
public boolean canPickUpLoot(){
return false;
}
// This vanilla method has nothing to do with the custom despawn() method.
@Override protected boolean canDespawn(){ return false; }
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
@@ -201,7 +233,7 @@ public class EntitySkeletonMinion extends EntitySkeleton implements ISummonedCre
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -16,7 +16,6 @@ import net.minecraft.entity.monster.EntityCaveSpider;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.MobEffects;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumHand;
@@ -35,18 +34,41 @@ public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCre
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public WeakReference<EntityLivingBase> getCasterReference(){ return casterReference; }
@Override public void setCasterReference(WeakReference<EntityLivingBase> reference){ casterReference = reference; }
@Override public UUID getCasterUUID() { return casterUUID; }
@Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; }
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
/**
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't
* matter because the client side entity immediately gets the lifetime value copied over to it by this class
* anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's
* no need to do anything inside it other than call super().
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't matter
* because the client side entity immediately gets the lifetime value copied over to it by this class anyway. When
* extending this class, you must override this constructor or Minecraft won't like it, but there's no need to do
* anything inside it other than call super().
*/
public EntitySpiderMinion(World world){
super(world);
@@ -54,8 +76,8 @@ public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCre
}
/**
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when
* extending this class (be sure to call super()) so that AI and other things can be added.
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when extending
* this class (be sure to call super()) so that AI and other things can be added.
*/
public EntitySpiderMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world);
@@ -66,12 +88,11 @@ public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCre
}
// EntitySpider overrides
// This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its
// targeting system with one that targets hostile mobs and takes the ADS into account.
@Override
protected void initEntityAI()
{
protected void initEntityAI(){
super.initEntityAI();
this.targetTasks.taskEntries.clear();
// Spiders use a custom AI type specific to spiders which I can't access, but it's just an extension of
@@ -81,25 +102,23 @@ public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCre
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
// No spider jockeys!
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata) {
// Can't call super, so the code from the next level up (EntityLiving) had to be copied as well.
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1));
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata){
if (this.rand.nextFloat() < 0.05F)
{
this.setLeftHanded(true);
}
else
{
this.setLeftHanded(false);
}
// Don't need anything from EntitySpider, since neither spider jockeys nor group data is relevant.
return livingdata;
// Can't call super, so the code from the next level up (EntityLiving) had to be copied as well.
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE)
.applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1));
if(this.rand.nextFloat() < 0.05F){
this.setLeftHanded(true);
}else{
this.setLeftHanded(false);
}
// Don't need anything from EntitySpider, since neither spider jockeys nor group data is relevant.
return livingdata;
}
// Implementations
@@ -124,41 +143,43 @@ public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCre
public void onDespawn(){
this.spawnParticleEffect();
}
private void spawnParticleEffect(){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + this.rand.nextFloat(), this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.2f, 0.0f);
}
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, this.posX + this.rand.nextFloat(),
this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.1f,
0.2f, 0.0f);
}
}
}
@Override
public boolean hasParticleEffect() {
public boolean hasParticleEffect(){
return true;
}
@Override
public void onSuccessfulAttack(EntityLivingBase target){
int seconds = 0;
if(this.worldObj.getDifficulty() == EnumDifficulty.NORMAL){
seconds = 7;
}else if(this.worldObj.getDifficulty() == EnumDifficulty.HARD){
seconds = 15;
}
if(this.world.getDifficulty() == EnumDifficulty.NORMAL){
seconds = 7;
}else if(this.world.getDifficulty() == EnumDifficulty.HARD){
seconds = 15;
}
if(seconds > 0){
target.addPotionEffect(new PotionEffect(MobEffects.POISON, seconds * 20, 0));
}
if(seconds > 0){
target.addPotionEffect(new PotionEffect(MobEffects.POISON, seconds * 20, 0));
}
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements.
return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack);
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
@@ -175,13 +196,36 @@ public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCre
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override public boolean canPickUpLoot(){ return false; }
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
@Override
public boolean canPickUpLoot(){
return false;
}
// This vanilla method has nothing to do with the custom despawn() method.
@Override protected boolean canDespawn(){ return false; }
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
@@ -192,7 +236,7 @@ public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCre
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -23,78 +23,74 @@ import net.minecraft.util.text.translation.I18n;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
/** Does not implement ISummonedCreature because it has different despawning rules and because EntityHorse already
* has an owner system. */
/**
* Does not implement ISummonedCreature because it has different despawning rules and because EntityHorse already has an
* owner system.
*/
@SuppressWarnings("deprecation") // It's what Entity does, so...
public class EntitySpiritHorse extends EntityHorse {
private int idleTimer = 0;
public EntitySpiritHorse(World par1World)
{
public EntitySpiritHorse(World par1World){
super(par1World);
}
@Override
public String getName()
{
if (this.hasCustomName())
{
public String getName(){
if(this.hasCustomName()){
return this.getCustomNameTag();
}
else
{
}else{
return I18n.translateToLocal("entity.wizardry.Spirit Horse.name");
}
}
@Override
public boolean isChested()
{
return false;
}
@Override
public int getTotalArmorValue()
{
public int getTotalArmorValue(){
return 0;
}
@Override
protected int getExperiencePoints(EntityPlayer p_70693_1_){
return 0;
}
protected int getExperiencePoints(EntityPlayer p_70693_1_){
return 0;
}
@Override
protected Item getDropItem(){
return null;
}
return null;
}
@Override
protected void dropFewItems(boolean par1, int par2){}
protected void dropFewItems(boolean par1, int par2){
}
@Override
protected void applyEntityAttributes()
{
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(24.0D);
}
@Override
public void openGUI(EntityPlayer p_110199_1_){}
protected void applyEntityAttributes(){
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(24.0D);
}
@Override
public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack){
ItemStack itemstack = player.inventory.getCurrentItem();
// Allows the owner (but not other players) to dispel the spirit horse using a wand (shift-clicking, because clicking mounts the horse in this case).
if(itemstack != null && itemstack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){
public void openGUI(EntityPlayer p_110199_1_){
}
@Override
public boolean processInteract(EntityPlayer player, EnumHand hand){
ItemStack itemstack = player.getHeldItem(hand);
// Allows the owner (but not other players) to dispel the spirit horse using a wand (shift-clicking, because
// clicking mounts the horse in this case).
if(itemstack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){
// Prevents accidental double clicking.
if(this.ticksExisted > 20){
for(int i=0;i<15;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f);
for(int i = 0; i < 15; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world,
this.posX - this.width / 2 + this.rand.nextFloat() * width,
this.posY + this.height * this.rand.nextFloat() + 0.2f,
this.posZ - this.width / 2 + this.rand.nextFloat() * width, 0, 0, 0,
48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f);
}
this.setDead();
if(WizardData.get(player) != null){
@@ -106,15 +102,15 @@ public class EntitySpiritHorse extends EntityHorse {
}
return false;
}
return super.processInteract(player, hand, itemstack);
return super.processInteract(player, hand);
}
@Override
public void onDeath(DamageSource par1DamageSource){
super.onDeath(par1DamageSource);
// Allows player to summon another spirit horse once this one has died.
if(this.getOwner() instanceof EntityPlayer && WizardData.get((EntityPlayer)this.getOwner()) != null){
WizardData.get((EntityPlayer)this.getOwner()).hasSpiritHorse = false;
@@ -123,10 +119,10 @@ public class EntitySpiritHorse extends EntityHorse {
// I wrote this one!
private EntityLivingBase getOwner(){
// I think the DataManager stores any objects, so it now stores the UUID instead of its string representation.
Entity owner = WizardryUtilities.getEntityByUUID(worldObj, this.getOwnerUniqueId());
Entity owner = WizardryUtilities.getEntityByUUID(world, this.getOwnerUniqueId());
if(owner instanceof EntityLivingBase){
return (EntityLivingBase)owner;
}else{
@@ -136,12 +132,15 @@ public class EntitySpiritHorse extends EntityHorse {
@Override
public void onUpdate(){
super.onUpdate();
// Adds a dust particle effect
if(this.worldObj.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f);
if(this.world.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world,
this.posX - this.width / 2 + this.rand.nextFloat() * width,
this.posY + this.height * this.rand.nextFloat() + 0.2f,
this.posZ - this.width / 2 + this.rand.nextFloat() * width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f);
}
// Spirit horse disappears a short time after being dismounted.
@@ -152,9 +151,13 @@ public class EntitySpiritHorse extends EntityHorse {
}
if(this.idleTimer > 200){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f);
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world,
this.posX - this.width / 2 + this.rand.nextFloat() * width,
this.posY + this.height * this.rand.nextFloat() + 0.2f,
this.posZ - this.width / 2 + this.rand.nextFloat() * width, 0, 0, 0,
48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f);
}
}
this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F);
@@ -170,24 +173,28 @@ public class EntitySpiritHorse extends EntityHorse {
public boolean canMateWith(EntityAnimal par1EntityAnimal){
return false;
}
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data){
// Adds Particles on spawn. Due to client/server differences this cannot be done in the item.
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f);
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world,
this.posX - this.width / 2 + this.rand.nextFloat() * width,
this.posY + this.height * this.rand.nextFloat() + 0.2f,
this.posZ - this.width / 2 + this.rand.nextFloat() * width, 0, 0, 0, 48 + this.rand.nextInt(12),
0.8f, 0.8f, 1.0f);
}
}
return super.onInitialSpawn(difficulty, data);
}
@Override
public ITextComponent getDisplayName(){
if(getOwner() != null){
return new TextComponentTranslation(ISummonedCreature.NAMEPLATE_TRANSLATION_KEY, getOwner().getName(),
return new TextComponentTranslation(ISummonedCreature.NAMEPLATE_TRANSLATION_KEY, getOwner().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -29,119 +29,133 @@ import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
/** Does not implement ISummonedCreature because it has different despawning rules and because EntityWolf already
* has an owner system. */
/**
* Does not implement ISummonedCreature because it has different despawning rules and because EntityWolf already has an
* owner system.
*/
public class EntitySpiritWolf extends EntityWolf {
public EntitySpiritWolf(World par1World){
super(par1World);
this.experienceValue = 0;
}
@Override
protected void initEntityAI(){
this.aiSit = new EntityAISit(this);
this.tasks.addTask(1, new EntityAISwimming(this));
this.tasks.addTask(2, this.aiSit);
this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4F));
this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, true));
this.tasks.addTask(5, new EntityAIFollowOwner(this, 1.0D, 10.0F, 2.0F));
this.tasks.addTask(7, new EntityAIWander(this, 1.0D));
this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
this.tasks.addTask(9, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIOwnerHurtByTarget(this));
this.targetTasks.addTask(2, new EntityAIOwnerHurtTarget(this));
this.targetTasks.addTask(3, new EntityAIHurtByTarget(this, true, new Class[0]));
}
public EntitySpiritWolf(World par1World){
@Override
public void onDeath(DamageSource source){
// Allows player to summon another spirit wolf once this one has died.
// NOTE: This has been known to work incorrectly.
super(par1World);
this.experienceValue = 0;
}
@Override
protected void initEntityAI(){
this.aiSit = new EntityAISit(this);
this.tasks.addTask(1, new EntityAISwimming(this));
this.tasks.addTask(2, this.aiSit);
this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4F));
this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, true));
this.tasks.addTask(5, new EntityAIFollowOwner(this, 1.0D, 10.0F, 2.0F));
this.tasks.addTask(7, new EntityAIWander(this, 1.0D));
this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
this.tasks.addTask(9, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIOwnerHurtByTarget(this));
this.targetTasks.addTask(2, new EntityAIOwnerHurtTarget(this));
this.targetTasks.addTask(3, new EntityAIHurtByTarget(this, true, new Class[0]));
}
@Override
public void onDeath(DamageSource source){
// Allows player to summon another spirit wolf once this one has died.
// NOTE: This has been known to work incorrectly.
if(this.getOwner() instanceof EntityPlayer && WizardData.get((EntityPlayer)this.getOwner()) != null){
WizardData.get((EntityPlayer)this.getOwner()).hasSpiritWolf = false;
}
super.onDeath(source);
}
@Override
protected int getExperiencePoints(EntityPlayer p_70693_1_){
return 0;
}
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata) {
// Adds Particles on spawn. Due to client/server differences this cannot be done in the item.
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f);
}
}
super.onDeath(source);
}
@Override
protected int getExperiencePoints(EntityPlayer p_70693_1_){
return 0;
}
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata){
// Adds Particles on spawn. Due to client/server differences this cannot be done
// in the item.
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world,
this.posX - this.width / 2 + this.rand.nextFloat() * width,
this.posY + this.height * this.rand.nextFloat() + 0.2f,
this.posZ - this.width / 2 + this.rand.nextFloat() * width, 0, 0, 0, 48 + this.rand.nextInt(12),
0.8f, 0.8f, 1.0f);
}
}
return livingdata;
}
@Override
public void onUpdate(){
super.onUpdate();
// Adds a dust particle effect
if(this.worldObj.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f);
}
}
@Override
public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
}
if (this.isTamed())
{
if (stack != null){
// Allows the owner (but not other players) to dispel the spirit wolf using a wand.
if(stack != null && stack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){
// Prevents accidental double clicking.
if(this.ticksExisted > 20){
for(int i=0;i<10;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f);
}
this.setDead();
if(WizardData.get(player) != null){
WizardData.get(player).hasSpiritWolf = false;
}
this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F);
// This is necessary to prevent the wand's spell being cast when performing this action.
return true;
}
}
}
}
@Override
public void onUpdate(){
return super.processInteract(player, hand, stack);
}
super.onUpdate();
@Override
public EntityWolf createChild(EntityAgeable par1EntityAgeable)
{
return null;
}
// Adds a dust particle effect
if(this.world.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world,
this.posX - this.width / 2 + this.rand.nextFloat() * width,
this.posY + this.height * this.rand.nextFloat() + 0.2f,
this.posZ - this.width / 2 + this.rand.nextFloat() * width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f);
}
}
@Override
protected Item getDropItem()
{
return null;
}
@Override
@Override
public boolean processInteract(EntityPlayer player, EnumHand hand){
ItemStack stack = player.getHeldItem(hand);
if(this.isTamed()){
// Allows the owner (but not other players) to dispel the spirit wolf using a
// wand.
if(stack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){
// Prevents accidental double clicking.
if(this.ticksExisted > 20){
for(int i = 0; i < 10; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world,
this.posX - this.width / 2 + this.rand.nextFloat() * width,
this.posY + this.height * this.rand.nextFloat() + 0.2f,
this.posZ - this.width / 2 + this.rand.nextFloat() * width, 0, 0, 0,
48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f);
}
this.setDead();
if(WizardData.get(player) != null){
WizardData.get(player).hasSpiritWolf = false;
}
this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F);
// This is necessary to prevent the wand's spell being cast when performing this
// action.
return true;
}
}
}
return super.processInteract(player, hand);
}
@Override
public EntityWolf createChild(EntityAgeable par1EntityAgeable){
return null;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
public ITextComponent getDisplayName(){
if(getOwner() != null){
return new TextComponentTranslation(ISummonedCreature.NAMEPLATE_TRANSLATION_KEY, getOwner().getName(),
return new TextComponentTranslation(ISummonedCreature.NAMEPLATE_TRANSLATION_KEY, getOwner().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -150,7 +164,8 @@ public class EntitySpiritWolf extends EntityWolf {
@Override
public boolean hasCustomName(){
// If this returns true, the renderer will show the nameplate when looking directly at the entity
// If this returns true, the renderer will show the nameplate when looking
// directly at the entity
return Wizardry.settings.showSummonedCreatureNames && getOwner() != null;
}
@@ -25,11 +25,11 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
public class EntityStormElemental extends EntitySummonedCreature implements ISpellCaster {
private double AISpeed = 1.0;
private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 30, 0);
private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 30, 0);
private static final List<Spell> attack = Collections.singletonList(Spells.lightning_disc);
public EntityStormElemental(World world){
@@ -39,24 +39,24 @@ public class EntityStormElemental extends EntitySummonedCreature implements ISpe
public EntityStormElemental(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world, x, y, z, caster, lifetime);
// For some reason this can't be in initEntityAI
this.tasks.addTask(0, this.spellAttackAI);
this.tasks.addTask(0, this.spellAttackAI);
}
@Override
protected void initEntityAI(){
this.tasks.addTask(1, new EntityAIAttackMelee(this, AISpeed, false));
this.tasks.addTask(1, new EntityAIAttackMelee(this, AISpeed, false));
this.tasks.addTask(2, new EntityAIWander(this, AISpeed));
this.tasks.addTask(3, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
this.setAIMoveSpeed((float)AISpeed);
this.setAIMoveSpeed((float)AISpeed);
}
@Override
public boolean hasRangedAttack() {
public boolean hasRangedAttack(){
return true;
}
@@ -83,27 +83,27 @@ public class EntityStormElemental extends EntitySummonedCreature implements ISpe
@Override
protected void applyEntityAttributes(){
super.applyEntityAttributes();
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D);
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(AISpeed);
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D);
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(16.0D);
}
@Override
protected SoundEvent getAmbientSound(){
return SoundEvents.ENTITY_BLAZE_AMBIENT;
}
@Override
protected SoundEvent getAmbientSound(){
return SoundEvents.ENTITY_BLAZE_AMBIENT;
}
@Override
protected SoundEvent getHurtSound(){
return SoundEvents.ENTITY_BLAZE_HURT;
}
@Override
protected SoundEvent getHurtSound(){
return SoundEvents.ENTITY_BLAZE_HURT;
}
@Override
protected SoundEvent getDeathSound(){
return SoundEvents.ENTITY_BLAZE_DEATH;
}
@Override
protected SoundEvent getDeathSound(){
return SoundEvents.ENTITY_BLAZE_DEATH;
}
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float partialTicks){
@@ -122,8 +122,9 @@ public class EntityStormElemental extends EntitySummonedCreature implements ISpe
this.playSound(WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f);
}
if (this.rand.nextInt(24) == 0){
this.playSound(SoundEvents.ENTITY_BLAZE_BURN, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F);
if(this.rand.nextInt(24) == 0){
this.playSound(SoundEvents.ENTITY_BLAZE_BURN, 1.0F + this.rand.nextFloat(),
this.rand.nextFloat() * 0.7F + 0.3F);
}
// Slow fall
@@ -131,28 +132,35 @@ public class EntityStormElemental extends EntitySummonedCreature implements ISpe
this.motionY *= 0.6D;
}
if(worldObj.isRemote){
for(int i=0; i<2; ++i){
worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0, 0, 0);
if(world.isRemote){
for(int i = 0; i < 2; ++i){
world.spawnParticle(EnumParticleTypes.SMOKE_LARGE,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0, 0, 0);
}
for(int i=0; i<10; i++){
float brightness = rand.nextFloat()*0.2f;
for(int i = 0; i < 10; i++){
float brightness = rand.nextFloat() * 0.2f;
double dy = this.rand.nextDouble() * (double)this.height;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE_ROTATING, worldObj, this.posX, this.posY + dy, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0, brightness, brightness, false, 0.2f + 0.5f*dy);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE_ROTATING, world, this.posX, this.posY + dy,
this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0, brightness, brightness, false, 0.2f + 0.5f * dy);
}
}
super.onLivingUpdate();
}
@Override
public void fall(float distance, float damageMultiplier){
// Immune to fall damage.
}
@Override
public void onStruckByLightning(EntityLightningBolt lightning){
// Immune to lightning.
@@ -9,7 +9,6 @@ import net.minecraft.entity.EntityFlying;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
@@ -17,12 +16,16 @@ import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
/** Abstract base implementation of {@link ISummonedCreature} which is the superclass to all custom summoned entities
* (i.e. entities that don't extend vanilla/mod creatures). Also serves as an example of how to correctly implement
* the above interface, and includes some non-critical method overrides which should be used for best results (xp, drops,
* and such like). <i>Not to be confused with the old version of EntitySummonedCreature; that system has been replaced.</i>
/**
* Abstract base implementation of {@link ISummonedCreature} which is the superclass to all custom summoned entities
* (i.e. entities that don't extend vanilla/mod creatures). Also serves as an example of how to correctly implement the
* above interface, and includes some non-critical method overrides which should be used for best results (xp, drops,
* and such like). <i>Not to be confused with the old version of EntitySummonedCreature; that system has been
* replaced.</i>
*
* @since Wizardry 1.2
* @author Electroblob */
* @author Electroblob
*/
public abstract class EntitySummonedCreature extends EntityCreature implements ISummonedCreature {
// Field implementations
@@ -31,18 +34,41 @@ public abstract class EntitySummonedCreature extends EntityCreature implements I
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public WeakReference<EntityLivingBase> getCasterReference(){ return casterReference; }
@Override public void setCasterReference(WeakReference<EntityLivingBase> reference){ casterReference = reference; }
@Override public UUID getCasterUUID() { return casterUUID; }
@Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; }
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
/**
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't
* matter because the client side entity immediately gets the lifetime value copied over to it by this class
* anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's
* no need to do anything inside it other than call super().
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't matter
* because the client side entity immediately gets the lifetime value copied over to it by this class anyway. When
* extending this class, you must override this constructor or Minecraft won't like it, but there's no need to do
* anything inside it other than call super().
*/
public EntitySummonedCreature(World world){
super(world);
@@ -50,8 +76,8 @@ public abstract class EntitySummonedCreature extends EntityCreature implements I
}
/**
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when
* extending this class (be sure to call super()) so that AI and other things can be added.
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when extending
* this class (be sure to call super()) so that AI and other things can be added.
*/
public EntitySummonedCreature(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world);
@@ -60,7 +86,7 @@ public abstract class EntitySummonedCreature extends EntityCreature implements I
this.experienceValue = 0;
this.lifetime = lifetime;
}
// Implementations
@Override
@@ -75,21 +101,23 @@ public abstract class EntitySummonedCreature extends EntityCreature implements I
}
@Override
public void onSpawn(){}
public void onSpawn(){
}
@Override
public void onDespawn(){}
public void onDespawn(){
}
@Override
public boolean hasParticleEffect() {
public boolean hasParticleEffect(){
return false;
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack);
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
@@ -106,13 +134,36 @@ public abstract class EntitySummonedCreature extends EntityCreature implements I
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override public boolean canPickUpLoot(){ return false; }
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
@Override
public boolean canPickUpLoot(){
return false;
}
// This vanilla method has nothing to do with the custom onDespawn() method.
@Override protected boolean canDespawn(){ return false; }
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
@@ -124,7 +175,7 @@ public abstract class EntitySummonedCreature extends EntityCreature implements I
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -0,0 +1,255 @@
package electroblob.wizardry.entity.living;
import java.lang.ref.WeakReference;
import java.util.Calendar;
import java.util.UUID;
import javax.annotation.Nullable;
import electroblob.wizardry.Wizardry;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.monster.EntityWitherSkeleton;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.init.MobEffects;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
public class EntityWitherSkeletonMinion extends EntityWitherSkeleton implements ISummonedCreature {
// Field implementations
private int lifetime = 600;
private WeakReference<EntityLivingBase> casterReference;
private UUID casterUUID;
// Setter + getter implementations
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
/**
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't matter
* because the client side entity immediately gets the lifetime value copied over to it by this class anyway. When
* extending this class, you must override this constructor or Minecraft won't like it, but there's no need to do
* anything inside it other than call super().
*/
public EntityWitherSkeletonMinion(World world){
super(world);
this.experienceValue = 0;
}
/**
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when extending
* this class (be sure to call super()) so that AI and other things can be added.
*/
public EntityWitherSkeletonMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world);
this.setPosition(x, y, z);
this.casterReference = new WeakReference<EntityLivingBase>(caster);
this.experienceValue = 0;
this.lifetime = lifetime;
}
// EntitySkeleton overrides
// This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its
// targeting system with one that targets hostile mobs and takes the ADS into account.
@Override
protected void initEntityAI(){
super.initEntityAI();
this.targetTasks.taskEntries.clear();
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
// Shouldn't have randomised armour, but does still need a bow!
@Override
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty){
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW));
}
// Where the skeleton minion is summoned does not affect its type.
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata){
// Can't call super, so the code from the next level up (EntityLiving) had to be copied as well.
this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE)
.applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1));
if(this.rand.nextFloat() < 0.05F){
this.setLeftHanded(true);
}else{
this.setLeftHanded(false);
}
// Halloween pumpkin heads! Why not?
if(this.getItemStackFromSlot(EntityEquipmentSlot.HEAD) == null){
Calendar calendar = this.world.getCurrentDate();
if(calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.rand.nextFloat() < 0.25F){
this.setItemStackToSlot(EntityEquipmentSlot.HEAD,
new ItemStack(this.rand.nextFloat() < 0.1F ? Blocks.LIT_PUMPKIN : Blocks.PUMPKIN));
this.inventoryArmorDropChances[EntityEquipmentSlot.HEAD.getIndex()] = 0.0F;
}
}
return livingdata;
}
// Implementations
@Override
public void setRevengeTarget(EntityLivingBase entity){
if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity);
}
@Override
public void onUpdate(){
super.onUpdate();
this.updateDelegate();
}
@Override
public void onSpawn(){
this.spawnParticleEffect();
}
@Override
public void onDespawn(){
this.spawnParticleEffect();
}
private void spawnParticleEffect(){
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
this.world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(),
this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
}
}
}
@Override
public boolean hasParticleEffect(){
return true;
}
@Override
public void onSuccessfulAttack(EntityLivingBase target){
target.addPotionEffect(new PotionEffect(MobEffects.WITHER, 200));
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
public void writeEntityToNBT(NBTTagCompound nbttagcompound){
super.writeEntityToNBT(nbttagcompound);
this.writeNBTDelegate(nbttagcompound);
}
@Override
public void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
this.writeNBTDelegate(nbttagcompound);
}
// Recommended overrides
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
@Override
public boolean canPickUpLoot(){
return false;
}
// This vanilla method has nothing to do with the custom despawn() method.
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
return true;
}
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
}
}
@Override
public boolean hasCustomName(){
// If this returns true, the renderer will show the nameplate when looking directly at the entity
return Wizardry.settings.showSummonedCreatureNames && getCaster() != null;
}
}
@@ -69,8 +69,8 @@ import net.minecraft.village.MerchantRecipe;
import net.minecraft.village.MerchantRecipeList;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -83,14 +83,12 @@ import net.minecraftforge.oredict.OreDictionary;
@Mod.EventBusSubscriber
public class EntityWizard extends EntityVillager implements ISpellCaster, IEntityAdditionalSpawnData {
/*
* After much debugging, the error in the compiled mod (outside of eclipse) was traced back to this class,
/* After much debugging, the error in the compiled mod (outside of eclipse) was traced back to this class,
* specifically the methods copied in from EntityVillager when I changed this class to extend it. This figures,
* since I had 1.2.1 working just fine before I did that, and it was the only thing I changed. Apparently,
* methods and fields with obfuscated names like func_129090_a can cause problems when compiled. One of the
* ones here was renamed and the other deleted since it was never called. Watch out for this in future (unless,
* of course, they are overriding something, in which case it should be fine).
*/
* since I had 1.2.1 working just fine before I did that, and it was the only thing I changed. Apparently, methods
* and fields with obfuscated names like func_129090_a can cause problems when compiled. One of the ones here was
* renamed and the other deleted since it was never called. Watch out for this in future (unless, of course, they
* are overriding something, in which case it should be fine). */
// Extending EntityVillager turned out to be a pretty neat thing to do, since now zombies will attack wizards
@@ -109,11 +107,13 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
private boolean updateRecipes;
/** Data parameter for the cooldown time for wizards healing themselves. */
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT);
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class,
DataSerializers.VARINT);
/** Data parameter for the wizard's element. */
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT);
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityWizard.class,
DataSerializers.VARINT);
// Field implementations
// Field implementations
private List<Spell> spells = new ArrayList<Spell>(4);
private Spell continuousSpell;
@@ -126,14 +126,14 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
// For some reason this can't be in initEntityAI
this.tasks.addTask(3, this.spellCastingAI);
}
@Override
protected void entityInit(){
super.entityInit();
this.dataManager.register(HEAL_COOLDOWN, -1);
this.dataManager.register(ELEMENT, 0);
}
@Override
protected void initEntityAI(){
@@ -153,14 +153,17 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
public boolean apply(Entity entity){
// If the target is valid and not invisible...
if(entity != null && !entity.isInvisible() && WizardryUtilities.isValidTarget(EntityWizard.this, entity)){
if(entity != null && !entity.isInvisible()
&& WizardryUtilities.isValidTarget(EntityWizard.this, entity)){
//... and is a mob, a summoned creature ...
// ... and is a mob, a summoned creature ...
if((entity instanceof IMob || entity instanceof ISummonedCreature
// ... or in the whitelist ...
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT)))
// ... or in the whitelist ...
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist)
.contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT)))
// ... and isn't in the blacklist ...
&& !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){
&& !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist)
.contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){
// ... it can be attacked.
return true;
}
@@ -172,7 +175,8 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
// By default, wizards don't attack players unless the player has attacked them.
this.targetTasks.addTask(0, new EntityAINearestAttackableTarget<EntityLiving>(this, EntityLiving.class, 0, false, true, this.targetSelector));
this.targetTasks.addTask(0, new EntityAINearestAttackableTarget<EntityLiving>(this, EntityLiving.class, 0,
false, true, this.targetSelector));
}
@Override
@@ -180,11 +184,11 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5);
}
private int getHealCooldown(){
return this.dataManager.get(HEAL_COOLDOWN);
}
private void setHealCooldown(int cooldown){
this.dataManager.set(HEAL_COOLDOWN, cooldown);
}
@@ -206,12 +210,12 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
public SpellModifiers getModifiers(){
return new SpellModifiers();
}
@Override
public void setContinuousSpell(Spell spell){
this.continuousSpell = spell;
}
@Override
public Spell getContinuousSpell(){
return this.continuousSpell;
@@ -229,23 +233,26 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
// affected with arcane jammer and healCooldown == 0, whilst the wizard didn't actually heal or play the sound,
// the particles still spawned, and since healCooldown wasn't reset they spawned every tick until the arcane
// jammer wore off.
if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0 && !this.isPotionActive(WizardryPotions.arcane_jammer)){
if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0
&& !this.isPotionActive(WizardryPotions.arcane_jammer)){
// Healer wizards use greater heal.
this.heal(this.getElement() == Element.HEALING ? 8 : 4);
this.setHealCooldown(-1);
// deathTime == 0 checks the wizard isn't currently dying
// deathTime == 0 checks the wizard isn't currently dying
}else if(healCooldown == -1 && this.deathTime == 0){
// Heal particles
if(worldObj.isRemote){
for(int i=0; i<10; i++){
double d0 = (double)((float)this.posX + rand.nextFloat()*2 - 1.0F);
// Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F.
if(world.isRemote){
for(int i = 0; i < 10; i++){
double d0 = (double)((float)this.posX + rand.nextFloat() * 2 - 1.0F);
// Apparently the client side spawns the particles 1 block higher than it should... hence the -
// 0.5F.
double d1 = (double)((float)this.posY - 0.5F + rand.nextFloat());
double d2 = (double)((float)this.posZ + rand.nextFloat()*2 - 1.0F);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, d0, d1, d2, 0, 0.1F, 0, 48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f);
double d2 = (double)((float)this.posZ + rand.nextFloat() * 2 - 1.0F);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0,
48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f);
}
}else{
if(this.getHealth() < 10){
@@ -258,9 +265,9 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F);
}
}
if(healCooldown > 0){
this.setHealCooldown(healCooldown-1);
this.setHealCooldown(healCooldown - 1);
}
}
@@ -299,77 +306,79 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
}
@Override
public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
public boolean processInteract(EntityPlayer player, EnumHand hand){
ItemStack stack = player.getHeldItem(hand);
// Debugging
//player.addChatComponentMessage(new TextComponentTranslation("wizard.debug", Spell.get(spells[1]).getDisplayName(), Spell.get(spells[2]).getDisplayName(), Spell.get(spells[3]).getDisplayName()));
// player.addChatComponentMessage(new TextComponentTranslation("wizard.debug",
// Spell.get(spells[1]).getDisplayName(), Spell.get(spells[2]).getDisplayName(),
// Spell.get(spells[3]).getDisplayName()));
// When right-clicked with a spell book in creative, sets one of the spells to that spell
if(player.capabilities.isCreativeMode && stack != null && stack.getItem() instanceof ItemSpellBook){
if(player.capabilities.isCreativeMode && stack.getItem() instanceof ItemSpellBook){
if(this.spells.size() >= 4 && Spell.get(stack.getItemDamage()).canBeCastByNPCs()){
this.spells.set(rand.nextInt(3)+1, Spell.get(stack.getItemDamage()));
this.spells.set(rand.nextInt(3) + 1, Spell.get(stack.getItemDamage()));
return true;
}
}
// Won't trade with a player that has attacked them.
if (this.isEntityAlive() && !this.isTrading() && !this.isChild() && !player.isSneaking() && this.getAttackTarget() != player)
{
if (!this.worldObj.isRemote)
{
if(this.isEntityAlive() && !this.isTrading() && !this.isChild() && !player.isSneaking()
&& this.getAttackTarget() != player){
if(!this.world.isRemote){
this.setCustomer(player);
player.displayVillagerTradeGui(this);
//player.displayGUIMerchant(this, this.getElement().getWizardName());
// player.displayGUIMerchant(this, this.getElement().getWizardName());
}
return true;
}
else
{
}else{
return false;
}
}
@Override
public ITextComponent getDisplayName() {
public ITextComponent getDisplayName(){
return this.getElement().getWizardName();
}
@Override
public void writeEntityToNBT(NBTTagCompound nbt){
super.writeEntityToNBT(nbt);
if (this.trades != null){
nbt.setTag("trades", this.trades.getRecipiesAsTags());
}
if(this.trades != null){
nbt.setTag("trades", this.trades.getRecipiesAsTags());
}
nbt.setInteger("element", this.getElement().ordinal());
nbt.setInteger("skin", this.textureIndex);
nbt.setTag("spells", WizardryUtilities.listToNBT(spells, spell -> new NBTTagInt(spell.id())));
if(this.towerBlocks != null && this.towerBlocks.size() > 0){
nbt.setTag("towerBlocks", WizardryUtilities.listToNBT(this.towerBlocks, pos -> new NBTTagLong(pos.toLong())));
nbt.setTag("towerBlocks",
WizardryUtilities.listToNBT(this.towerBlocks, pos -> new NBTTagLong(pos.toLong())));
}
}
@Override
public void readEntityFromNBT(NBTTagCompound nbt){
super.readEntityFromNBT(nbt);
if(nbt.hasKey("trades")){
NBTTagCompound nbttagcompound1 = nbt.getCompoundTag("trades");
this.trades = new MerchantRecipeList(nbttagcompound1);
}
NBTTagCompound nbttagcompound1 = nbt.getCompoundTag("trades");
this.trades = new MerchantRecipeList(nbttagcompound1);
}
this.setElement(Element.values()[nbt.getInteger("element")]);
this.textureIndex = nbt.getInteger("skin");
this.spells = (List<Spell>) WizardryUtilities.NBTToList(nbt.getTagList("spells", NBT.TAG_INT),
this.spells = (List<Spell>)WizardryUtilities.NBTToList(nbt.getTagList("spells", NBT.TAG_INT),
(NBTTagInt tag) -> Spell.get(tag.getInt()));
this.towerBlocks = new HashSet<BlockPos>(WizardryUtilities.NBTToList(nbt.getTagList("towerBlocks",
NBT.TAG_LONG), (NBTTagLong tag) -> BlockPos.fromLong(tag.getLong())));
this.towerBlocks = new HashSet<BlockPos>(WizardryUtilities.NBTToList(
nbt.getTagList("towerBlocks", NBT.TAG_LONG), (NBTTagLong tag) -> BlockPos.fromLong(tag.getLong())));
}
@Override
@@ -378,8 +387,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
}
@Override
public boolean isTrading()
{
public boolean isTrading(){
return this.getCustomer() != null;
}
@@ -391,8 +399,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
this.playSound(SoundEvents.ENTITY_VILLAGER_YES, this.getSoundVolume(), this.getSoundPitch());
// Achievements
if (this.getCustomer() != null)
{
if(this.getCustomer() != null){
this.getCustomer().addStat(WizardryAchievements.wizard_trade);
if(merchantrecipe.getItemToSell().getItem() instanceof ItemSpellBook
@@ -406,12 +413,9 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
this.timeUntilReset = 40;
this.updateRecipes = true;
if (this.getCustomer() != null)
{
if(this.getCustomer() != null){
this.getCustomer().getName();
}
else
{
}else{
}
}
}
@@ -447,9 +451,9 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
MerchantRecipeList merchantrecipelist;
merchantrecipelist = new MerchantRecipeList();
for(int i=0; i<numberOfItemsToAdd; i++){
for(int i = 0; i < numberOfItemsToAdd; i++){
ItemStack itemToSell = null;
ItemStack itemToSell = ItemStack.EMPTY;
boolean itemAlreadySold = true;
@@ -461,20 +465,12 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
/* New way of getting random item, by giving a chance to increase the tier which depends on how much the
* player has already traded with the wizard. The more the player has traded with the wizard, the more
* likely they are to get items of a higher tier. The -4 is to ignore the original 4 trades.
* For reference, the chances are as follows:
* Trades done Basic Apprentice Advanced Master
* 0 50% 25% 18% 8%
* 1 46% 25% 20% 9%
* 2 42% 24% 22% 12%
* 3 38% 24% 24% 14%
* 4 34% 22% 26% 17%
* 5 30% 21% 28% 21%
* 6 26% 19% 30% 24%
* 7 22% 17% 32% 28%
* 8 18% 15% 34% 33% */
* likely they are to get items of a higher tier. The -4 is to ignore the original 4 trades. For
* reference, the chances are as follows: Trades done Basic Apprentice Advanced Master 0 50% 25% 18% 8%
* 1 46% 25% 20% 9% 2 42% 24% 22% 12% 3 38% 24% 24% 14% 4 34% 22% 26% 17% 5 30% 21% 28% 21% 6 26% 19%
* 30% 24% 7 22% 17% 32% 28% 8 18% 15% 34% 33% */
double tierIncreaseChance = 0.5 + 0.04*(Math.max(this.trades.size()-4, 0));
double tierIncreaseChance = 0.5 + 0.04 * (Math.max(this.trades.size() - 4, 0));
tier = Tier.BASIC;
@@ -482,7 +478,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
tier = Tier.APPRENTICE;
if(rand.nextDouble() < tierIncreaseChance){
tier = Tier.ADVANCED;
if(rand.nextDouble() < tierIncreaseChance*0.6){
if(rand.nextDouble() < tierIncreaseChance * 0.6){
tier = Tier.MASTER;
}
}
@@ -491,46 +487,49 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
itemToSell = this.getRandomItemOfTier(tier);
for(Object recipe : merchantrecipelist){
if(ItemStack.areItemStacksEqual(((MerchantRecipe)recipe).getItemToSell(), itemToSell)) itemAlreadySold = true;
if(ItemStack.areItemStacksEqual(((MerchantRecipe)recipe).getItemToSell(), itemToSell))
itemAlreadySold = true;
}
if(this.trades != null){
for(Object recipe : this.trades){
if(ItemStack.areItemStacksEqual(((MerchantRecipe)recipe).getItemToSell(), itemToSell)) itemAlreadySold = true;
if(ItemStack.areItemStacksEqual(((MerchantRecipe)recipe).getItemToSell(), itemToSell))
itemAlreadySold = true;
}
}
}
// Don't know how it can ever be null here, but saves it crashing.
if(itemToSell == null) return;
// Don't know how it can ever be empty here, but it's a failsafe.
if(itemToSell.isEmpty()) return;
merchantrecipelist.add(new MerchantRecipe(this.getRandomPrice(tier), new ItemStack(WizardryItems.magic_crystal, tier.ordinal()*3 + 1 + rand.nextInt(4)), itemToSell));
merchantrecipelist.add(new MerchantRecipe(this.getRandomPrice(tier),
new ItemStack(WizardryItems.magic_crystal, tier.ordinal() * 3 + 1 + rand.nextInt(4)), itemToSell));
}
Collections.shuffle(merchantrecipelist);
if (this.trades == null)
{
if(this.trades == null){
this.trades = new MerchantRecipeList();
}
for (int j1 = 0; j1 < merchantrecipelist.size(); ++j1)
{
for(int j1 = 0; j1 < merchantrecipelist.size(); ++j1){
this.trades.add(merchantrecipelist.get(j1));
}
}
private ItemStack getRandomPrice(Tier tier) {
ItemStack itemstack = null;
// TODO: Switch all of this over to some kind of loot pool system?
private ItemStack getRandomPrice(Tier tier){
ItemStack itemstack = ItemStack.EMPTY;
switch(this.rand.nextInt(3)){
case 0:
itemstack = new ItemStack(Items.GOLD_INGOT, (tier.ordinal()+1)*8-1 + rand.nextInt(6));
itemstack = new ItemStack(Items.GOLD_INGOT, (tier.ordinal() + 1) * 8 - 1 + rand.nextInt(6));
break;
case 1:
itemstack = new ItemStack(Items.DIAMOND, (tier.ordinal()+1)*4-2 + rand.nextInt(3));
itemstack = new ItemStack(Items.DIAMOND, (tier.ordinal() + 1) * 4 - 2 + rand.nextInt(3));
break;
case 2:
itemstack = new ItemStack(Items.EMERALD, (tier.ordinal()+1)*6-1 + rand.nextInt(3));
itemstack = new ItemStack(Items.EMERALD, (tier.ordinal() + 1) * 6 - 1 + rand.nextInt(3));
break;
}
return itemstack;
@@ -552,17 +551,21 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
randomiser = rand.nextInt(5);
if(randomiser < 4 && !spells.isEmpty()){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){
// This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id());
// This means it is more likely for spell books sold to be of the same element as the wizard if the
// wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1,
specialismSpells.get(rand.nextInt(specialismSpells.size())).id());
}else{
return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id());
}
}else{
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
// This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element.
// This means it is more likely for wands sold to be of the same element as the wizard if the wizard
// has an element.
return new ItemStack(WizardryUtilities.getWand(tier, this.getElement()));
}else{
return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)]));
return new ItemStack(
WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)]));
}
}
@@ -570,27 +573,34 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
randomiser = rand.nextInt(Wizardry.settings.discoveryMode ? 12 : 10);
if(randomiser < 5 && !spells.isEmpty()){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){
// This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id());
// This means it is more likely for spell books sold to be of the same element as the wizard if the
// wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1,
specialismSpells.get(rand.nextInt(specialismSpells.size())).id());
}else{
return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id());
}
}else if(randomiser < 6){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
// This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element.
// This means it is more likely for wands sold to be of the same element as the wizard if the wizard
// has an element.
return new ItemStack(WizardryUtilities.getWand(tier, this.getElement()));
}else{
return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)]));
return new ItemStack(
WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)]));
}
}else if(randomiser < 8){
return new ItemStack(WizardryItems.arcane_tome, 1, 1);
}else if(randomiser < 10){
EntityEquipmentSlot slot = WizardryUtilities.ARMOUR_SLOTS[rand.nextInt(WizardryUtilities.ARMOUR_SLOTS.length)];
EntityEquipmentSlot slot = WizardryUtilities.ARMOUR_SLOTS[rand
.nextInt(WizardryUtilities.ARMOUR_SLOTS.length)];
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
// This means it is more likely for armour sold to be of the same element as the wizard if the wizard has an element.
// This means it is more likely for armour sold to be of the same element as the wizard if the
// wizard has an element.
return new ItemStack(WizardryUtilities.getArmour(this.getElement(), slot));
}else{
return new ItemStack(WizardryUtilities.getArmour(Element.values()[rand.nextInt(Element.values().length)], slot));
return new ItemStack(
WizardryUtilities.getArmour(Element.values()[rand.nextInt(Element.values().length)], slot));
}
}else{
// Don't need to check for discovery mode here since it is done above
@@ -601,17 +611,21 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
randomiser = rand.nextInt(12);
if(randomiser < 5 && !spells.isEmpty()){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){
// This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id());
// This means it is more likely for spell books sold to be of the same element as the wizard if the
// wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1,
specialismSpells.get(rand.nextInt(specialismSpells.size())).id());
}else{
return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id());
}
}else if(randomiser < 6){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
// This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element.
// This means it is more likely for wands sold to be of the same element as the wizard if the wizard
// has an element.
return new ItemStack(WizardryUtilities.getWand(tier, this.getElement()));
}else{
return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)]));
return new ItemStack(
WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)]));
}
}else if(randomiser < 8){
return new ItemStack(WizardryItems.arcane_tome, 1, 2);
@@ -627,7 +641,8 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
if(randomiser < 5 && this.getElement() != Element.MAGIC && !specialismSpells.isEmpty()){
// Master spells can only be sold by a specialist in that element.
return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id());
return new ItemStack(WizardryItems.spell_book, 1,
specialismSpells.get(rand.nextInt(specialismSpells.size())).id());
}else if(randomiser < 6){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
@@ -643,9 +658,9 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
return new ItemStack(Blocks.STONE);
}
@Override
public void setProfession(VillagerProfession prof) {
public void setProfession(VillagerProfession prof){
// Disables Forge's stuff.
}
@@ -670,22 +685,25 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
}
// Default chance is 0.085f, for reference.
for(EntityEquipmentSlot slot : EntityEquipmentSlot.values()) this.setDropChance(slot, 0.0f);
for(EntityEquipmentSlot slot : EntityEquipmentSlot.values())
this.setDropChance(slot, 0.0f);
// All wizards know magic missile, even if it is disabled.
spells.add(Spells.magic_missile);
Tier maxTier = populateSpells(spells, element, 3, rand);
// Now done after the spells so it can take the tier into account.
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(WizardryUtilities.getWand(maxTier, element)));
this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND,
new ItemStack(WizardryUtilities.getWand(maxTier, element)));
return livingdata;
}
/**
* Adds n random spells to the given list. The spells will be of the given element if possible. Extracted as a
* separate function since it was the same in both EntityWizard and EntityEvilWizard.
*
* @param spells The spell list to be populated.
* @param e The element that the spells should belong to, or {@link Element#MAGIC} for a random element each time.
* @param n The number of spells to add.
@@ -696,10 +714,10 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
// This is the tier of the highest tier spell added.
Tier maxTier = Tier.BASIC;
List<Spell> npcSpells = Spell.getSpells(Spell.npcSpells);
for(int i=0; i<3; i++){
for(int i = 0; i < 3; i++){
Tier tier;
// If the wizard has no element, it picks a random one each time.
@@ -739,7 +757,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
if(!list.isEmpty()) spells.add(list.get(random.nextInt(list.size())));
}
return maxTier;
}
@@ -763,9 +781,10 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
return super.attackEntityFrom(source, damage);
}
/**
/**
* Sets the list of blocks that are part of this wizard's tower. If a player breaks any of these blocks, the wizard
* will get angry and attack them.
*
* @param blocks A Set of BlockPos objects representing the blocks in the tower.
*/
public void setTowerBlocks(Set<BlockPos> blocks){
@@ -774,6 +793,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
/**
* Tests whether the block at the given coordinates is part of this wizard's tower.
*
* @param x
* @param y
* @param z
@@ -784,7 +804,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
// Uses .equals() rather than == so this will work fine.
return this.towerBlocks.contains(pos);
}
@SubscribeEvent
public static void onBlockBreakEvent(BlockEvent.BreakEvent event){
// Makes wizards angry if a player breaks a block in their tower
@@ -806,22 +826,46 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit
// EntityVillager overrides (that don't add features)
@Override public boolean isMating(){ return false; }
@Override public void setMating(boolean p_70947_1_){}
@Override public void setPlaying(boolean p_70939_1_){}
@Override public boolean isPlaying(){ return false; }
@Override public void setLookingForHome(){}
@Override
public boolean isMating(){
return false;
}
@Override
public void setMating(boolean p_70947_1_){
}
@Override
public void setPlaying(boolean p_70939_1_){
}
@Override
public boolean isPlaying(){
return false;
}
@Override
public void setLookingForHome(){
}
// Doesn't say it, but this is in fact nullable.
@Override public EntityVillager createChild(EntityAgeable par1EntityAgeable){ return null; }
@Override
public EntityVillager createChild(EntityAgeable par1EntityAgeable){
return null;
}
@SideOnly(Side.CLIENT)
@Override public void setRecipes(MerchantRecipeList par1MerchantRecipeList){}
@Override
public void setRecipes(MerchantRecipeList par1MerchantRecipeList){
}
@Override
public void onStruckByLightning(EntityLightningBolt lightningBolt){
// Restores the normal behaviour, replacing EntityVillager's witch conversion.
this.attackEntityFrom(DamageSource.lightningBolt, 5.0F);
this.attackEntityFrom(DamageSource.LIGHTNING_BOLT, 5.0F);
// Entity's version does something strange with the private fire variable, but since I don't have access this
// will probably be fine.
this.setFire(8);
this.setFire(8);
}
}
@@ -10,10 +10,8 @@ import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAIMoveThroughVillage;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.monster.ZombieType;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes;
@@ -31,27 +29,50 @@ public class EntityZombieMinion extends EntityZombie implements ISummonedCreatur
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public WeakReference<EntityLivingBase> getCasterReference(){ return casterReference; }
@Override public void setCasterReference(WeakReference<EntityLivingBase> reference){ casterReference = reference; }
@Override public UUID getCasterUUID() { return casterUUID; }
@Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; }
@Override
public int getLifetime(){
return lifetime;
}
@Override
public void setLifetime(int lifetime){
this.lifetime = lifetime;
}
@Override
public WeakReference<EntityLivingBase> getCasterReference(){
return casterReference;
}
@Override
public void setCasterReference(WeakReference<EntityLivingBase> reference){
casterReference = reference;
}
@Override
public UUID getCasterUUID(){
return casterUUID;
}
@Override
public void setCasterUUID(UUID uuid){
this.casterUUID = uuid;
}
/**
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't
* matter because the client side entity immediately gets the lifetime value copied over to it by this class
* anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's
* no need to do anything inside it other than call super().
* Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't matter
* because the client side entity immediately gets the lifetime value copied over to it by this class anyway. When
* extending this class, you must override this constructor or Minecraft won't like it, but there's no need to do
* anything inside it other than call super().
*/
public EntityZombieMinion(World world){
super(world);
this.experienceValue = 0;
}
/**
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when
* extending this class (be sure to call super()) so that AI and other things can be added.
* Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when extending
* this class (be sure to call super()) so that AI and other things can be added.
*/
public EntityZombieMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
super(world);
@@ -60,31 +81,38 @@ public class EntityZombieMinion extends EntityZombie implements ISummonedCreatur
this.experienceValue = 0;
this.lifetime = lifetime;
}
// EntityZombie overrides (EntityZombie is a long class so there are lots of these)
@Override
protected void applyEntityAI()
{
this.tasks.addTask(6, new EntityAIMoveThroughVillage(this, 1.0D, false));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
@Override public boolean isChild(){ return false; }
@Override public void setChild(boolean childZombie){}
@Override public ZombieType getZombieType(){ return ZombieType.NORMAL; }
@Override public boolean isVillager(){ return false; }
@Override public net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession getVillagerTypeForge(){ return null; }
@Override protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty){} // They don't have equipment!
@Override public void onKillEntity(EntityLivingBase entityLivingIn){} // Turns villagers to zombies in EntityZombie
@Override protected void startConversion(int ticks){}
@Override public boolean isConverting(){ return false; }
@Override protected void convertToVillager(){}
@Override protected int getConversionTimeBoost(){ return 0; }
@Override public void setChildSize(boolean isChild){}
protected void applyEntityAI(){
this.tasks.addTask(6, new EntityAIMoveThroughVillage(this, 1.0D, false));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<EntityLivingBase>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
@Override
public boolean isChild(){
return false;
}
@Override
public void setChild(boolean childZombie){
} // Can't be a child
@Override
protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty){
} // They don't have equipment!
@Override
public void onKillEntity(EntityLivingBase entityLivingIn){
} // Turns villagers to zombies in EntityZombie
@Override
public void setChildSize(boolean isChild){
}
// Implementations
@Override
@@ -107,25 +135,26 @@ public class EntityZombieMinion extends EntityZombie implements ISummonedCreatur
public void onDespawn(){
this.spawnParticleEffect();
}
private void spawnParticleEffect(){
if(this.worldObj.isRemote){
for(int i=0;i<15;i++){
this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
this.world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(),
this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0);
}
}
}
@Override
public boolean hasParticleEffect() {
public boolean hasParticleEffect(){
return true;
}
@Override
public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) {
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements.
return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack);
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
@@ -142,24 +171,47 @@ public class EntityZombieMinion extends EntityZombie implements ISummonedCreatur
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override public boolean canPickUpLoot(){ return false; }
@Override
protected int getExperiencePoints(EntityPlayer player){
return 0;
}
@Override
protected boolean canDropLoot(){
return false;
}
@Override
protected Item getDropItem(){
return null;
}
@Override
protected ResourceLocation getLootTable(){
return null;
}
@Override
public boolean canPickUpLoot(){
return false;
}
// This vanilla method has nothing to do with the custom despawn() method.
@Override protected boolean canDespawn(){ return false; }
@Override
protected boolean canDespawn(){
return false;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
// Returns true unless the given entity type is a flying entity.
return !EntityFlying.class.isAssignableFrom(entityType);
}
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
@@ -4,17 +4,23 @@ import java.util.List;
import electroblob.wizardry.spell.Spell;
/** [NYI] Interface for entities that can select between spells based on their current circumstances, to be used in
* conjunction with {@link EntityAISelectSpell}. */
/**
* [NYI] Interface for entities that can select between spells based on their current circumstances, to be used in
* conjunction with {@link EntityAISelectSpell}.
*/
public interface IIntelligentSpellCaster extends ISpellCaster {
/** Called from {@link EntityAISelectSpell} to set the spells that the entity can use in its current situation.
/**
* Called from {@link EntityAISelectSpell} to set the spells that the entity can use in its current situation.
* Implementors should assign the given list to some internal field and retrieve it when {@link #getSpells()} is
* called. */
* called.
*/
public void setCurrentSpells(List<Spell> spells);
/** Returns a list of spells that this entity 'knows'. The entity's current spell will be selected from this list
/**
* Returns a list of spells that this entity 'knows'. The entity's current spell will be selected from this list
* based on the current situation: whether it is attacking, its health, etc. Most likely, you will want to return a
* constant list, but it may change for some reason - perhaps if the entity 'learns' a new spell. */
* constant list, but it may change for some reason - perhaps if the entity 'learns' a new spell.
*/
public List<Spell> getKnownSpells();
}
@@ -8,10 +8,11 @@ import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.SpellModifiers;
/** Interface for entities that can cast spells. Mainly intended for use by wizard-type entities, but can be
* implemented by any subclass of EntityLiving. Designed to be as flexible as possible - ranging from the simplest
* use of giving an entity a specific spell as an attack, to a complex AI which selects different spell types
* depending on the situation. The only restriction is that the spells must be castable by NPCs.
/**
* Interface for entities that can cast spells. Mainly intended for use by wizard-type entities, but can be implemented
* by any subclass of EntityLiving. Designed to be as flexible as possible - ranging from the simplest use of giving an
* entity a specific spell as an attack, to a complex AI which selects different spell types depending on the situation.
* The only restriction is that the spells must be castable by NPCs.
* <p>
* This is intended for entities that use {@link EntityAIAttackSpell}. If so, all the spell casting code (including
* packets) is handled by that class, and all the implementor needs to do is decide which spell(s) to select.
@@ -20,42 +21,48 @@ import electroblob.wizardry.util.SpellModifiers;
* needs to do is store the actual fields involved.
*/
/* Perhaps this should be a capability? Though I can't help thinking they're mainly for attaching data to vanilla
* classes, rather than custom ones. For now, the main purpose of this is to centralise code within wizardry itself,
* and I may as well make it an API feature - but I'm not writing a capability unless someone sees a reason to attach
* it to a class they don't own, and I don't see that happening anytime soon. */
* classes, rather than custom ones. For now, the main purpose of this is to centralise code within wizardry itself, and
* I may as well make it an API feature - but I'm not writing a capability unless someone sees a reason to attach it to
* a class they don't own, and I don't see that happening anytime soon. */
// For even more fun, combine this with an ISummonedCreature and have skeletons that can cast spells!
public interface ISpellCaster {
/**
* Called each time the entity attacks to get the spells that can be cast. For simple implementations, just
* return a list of size one containing the spell that the entity uses as an attack, perhaps performing some
* simple logic within this method. For more intelligent implementations based on spell types, consider using
* Called each time the entity attacks to get the spells that can be cast. For simple implementations, just return a
* list of size one containing the spell that the entity uses as an attack, perhaps performing some simple logic
* within this method. For more intelligent implementations based on spell types, consider using
* {@link IIntelligentSpellCaster} instead.
*
* @return A list of {@link Spell} instances. A random spell from this list will be cast when the entity attacks.
* The list will not be modified by the AI class and can therefore be an immutable list. The spells in the list
* <b>must</b> be castable by NPCs (i.e. {@link Spell#canBeCastByNPCs()} returns true).
* The list will not be modified by the AI class and can therefore be an immutable list. The spells in the
* list <b>must</b> be castable by NPCs (i.e. {@link Spell#canBeCastByNPCs()} returns true).
*/
@Nonnull
public List<Spell> getSpells();
/**
* Called each time the entity attacks to get the modifiers to apply to the spell.
* @return A {@link SpellModifiers} object representing the modifiers to apply to the spell. If no modifiers are required,
* pass in an empty {@code SpellModifiers} object.
*
* @return A {@link SpellModifiers} object representing the modifiers to apply to the spell. If no modifiers are
* required, pass in an empty {@code SpellModifiers} object.
*/
@Nonnull
public SpellModifiers getModifiers();
/** Returns the continuous spell that is currently being cast, or the None spell if there is none. Implementors should simply
* store this as a private field and return it here. Will be synced by the AI class, but whether it is saved to NBT
* is up to you. If the implementing class does not deal with continuous spells, just return {@link Spells#none}. If the
* implementing class only ever uses one continuous spell, do <b>not</b> just return that spell; the field must still
* be stored. */
/**
* Returns the continuous spell that is currently being cast, or the None spell if there is none. Implementors
* should simply store this as a private field and return it here. Will be synced by the AI class, but whether it is
* saved to NBT is up to you. If the implementing class does not deal with continuous spells, just return
* {@link Spells#none}. If the implementing class only ever uses one continuous spell, do <b>not</b> just return
* that spell; the field must still be stored.
*/
@Nonnull
public Spell getContinuousSpell();
/** Sets the continuous spell that is currently being cast, or the None spell if there is none. Implementors should simply
* store this as a private field and assign it here. Will be synced by the AI class, but whether it is saved to NBT
* is up to you. If the implementing class does not deal with continuous spells, leave this method blank. */
/**
* Sets the continuous spell that is currently being cast, or the None spell if there is none. Implementors should
* simply store this as a private field and assign it here. Will be synced by the AI class, but whether it is saved
* to NBT is up to you. If the implementing class does not deal with continuous spells, leave this method blank.
*/
public void setContinuousSpell(Spell spell);
}
@@ -11,7 +11,6 @@ import com.google.common.base.Predicate;
import electroblob.wizardry.WizardData;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryEventHandler;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.util.IElementalDamage;
import electroblob.wizardry.util.IndirectMinionDamage;
@@ -36,23 +35,27 @@ import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
/** Interface for all summoned creatures. The code for summoned creatures has been overhauled in Wizardry 2.1, and this
* interface allows summoned creatures to extend vanilla (or indeed modded) entity classes, so <code>EntitySummonedZombie</code>
* now extends <code>EntityZombie</code>, for example. This change has two major benefits:
/**
* Interface for all summoned creatures. The code for summoned creatures has been overhauled in Wizardry 2.1, and this
* interface allows summoned creatures to extend vanilla (or indeed modded) entity classes, so
* <code>EntitySummonedZombie</code> now extends <code>EntityZombie</code>, for example. This change has two major
* benefits:
* <p>
* - There is no longer any need for separate render classes, because summoned creatures are now instances of vanilla
* types. <i>You don't even need to assign a render class</i> because the supertype should already be assigned the correct one.<br>
* types. <i>You don't even need to assign a render class</i> because the supertype should already be assigned the
* correct one.<br>
* - Summoned creature classes are now much more robust when it comes to changes between Minecraft versions, since none
* of the vanilla code needs to be copied.
* <p>
* <b>Summoned creatures that do not emulate vanilla entities do not directly implement this interface</b>. Instead, they
* should extend the abstract base implementation, {@link EntitySummonedCreature}.
* <b>Summoned creatures that do not emulate vanilla entities do not directly implement this interface</b>. Instead,
* they should extend the abstract base implementation, {@link EntitySummonedCreature}.
* <p>
* All damage dealt by ISummonedCreature instances is redirected via {@link WizardryEventHandler#onLivingAttackEvent(
* net.minecraftforge.event.entity.living.LivingAttackEvent) WizardryEventHandler.onLivingAttackEvent(LivingAttackEvent)}
* and replaced by an instance of {@link electroblob.wizardry.util.IElementalDamage IElementalDamage} with the summoner
* of that creature as the source rather than the creature itself. This means that kills by summoned creatures register
* as player kills, dropping xp and rare loot.
* All damage dealt by ISummonedCreature instances is redirected via
* {@link ISummonedCreature#onLivingAttackEvent(net.minecraftforge.event.entity.living.LivingAttackEvent)
* ISummonedCreature.onLivingAttackEvent(LivingAttackEvent)} and replaced by an instance of
* {@link electroblob.wizardry.util.IElementalDamage IElementalDamage} with the summoner of that creature as the source
* rather than the creature itself. This means that kills by summoned creatures register as kills for their owner,
* dropping xp and rare loot if that owner is a player.
* <p>
* Though this system is a lot better than the previous system, <i>it is not a perfect solution</i>. The old
* EntitySummonedCreature class overrode some methods from Entity in order to add shared functionality, but this cannot
@@ -61,92 +64,102 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
* <b>Implementing classes must override the corresponding methods from Entity, and within them, call the appropriate
* delegate method in this interface.</b> It is impossible to enforce this condition, but the summoned creature will not
* work properly unless it is adhered to. <i>The position of the delegate method call is unimportant, but by convention
* it is usually at the start of the calling method, which avoids it being unintentionally skipped by a return statement.</i>
* it is usually at the start of the calling method, which avoids it being unintentionally skipped by a return
* statement (except for methods where the result of the delegate method should itself be returned).</i>
* <p>
* It is recommended that when implementing this interface, you begin by copying {@link EntitySummonedCreature} to ensure
* all the relevant methods are duplicated. You can then change the superclass, override any additional methods and add
* functionality to any that are already overridden. You will always want to override the AI methods at the very least.
* It is recommended that when implementing this interface, you begin by copying {@link EntitySummonedCreature} to
* ensure all the relevant methods are duplicated. You can then change the superclass, override any additional methods
* and add functionality to any that are already overridden. You will always want to override the AI methods at the very
* least.
* <p>
* Due to the limitations of interfaces, some methods that really ought to be protected are public. These are clearly
* marked as 'Internal, DO NOT CALL'. <b>Don't call them, only implement them.</b>
*
* @since Wizardry 2.1
* @author Electroblob */
/*
* Quite honestly, this is not what default methods are really for. However, this is modding, and in modding some
* sacrifices have to be made when it comes to Java style - because adding on to a pre-existing program is not a
* good way of doing this sort of thing anyway, but we have no choice about that!
* @author Electroblob
*/
/* Quite honestly, this is not what default methods are really for. However, this is modding, and in modding some
* sacrifices have to be made when it comes to Java style - because adding on to a pre-existing program is not a good
* way of doing this sort of thing anyway, but we have no choice about that! */
@Mod.EventBusSubscriber
public interface ISummonedCreature extends IEntityAdditionalSpawnData {
// Remember that ALL fields are static and final in interfaces, even if they don't explicitly state that.
String NAMEPLATE_TRANSLATION_KEY = "entity.wizardry.summonedcreature.nameplate";
// Setters and getters. The subclass fields that these access should be private.
/** Sets the lifetime of the summoned creature in ticks. */
void setLifetime(int ticks);
/** Returns the lifetime of the summoned creature in ticks. Allows primarily for duration multiplier support, but
/**
* Returns the lifetime of the summoned creature in ticks. Allows primarily for duration multiplier support, but
* also for example the skeleton legion spell which lasts for 60 seconds instead of the usual 30. Syncing and saving
* is done automatically. As of Wizardry 2.1, despawning is handled in ISummonedCreature; see
* {@link ISummonedCreature#onDespawn()} for details. */
* {@link ISummonedCreature#onDespawn()} for details.
*/
int getLifetime();
/** Sets the WeakReference object which refers to the owner of this summoned creature. Internal, don't call
* unless you know what you are doing. */
/**
* Sets the WeakReference object which refers to the owner of this summoned creature. Internal, don't call unless
* you know what you are doing.
*/
void setCasterReference(WeakReference<EntityLivingBase> reference);
/** Returns a WeakReference object which refers to the owner of this summoned creature. Subclasses should store this
* as a private field. This may be null; as such it is preferable to use {@link ISummonedCreature#getCaster()} to get
* the caster object itself. */
/**
* Returns a WeakReference object which refers to the owner of this summoned creature. Subclasses should store this
* as a private field. This may be null; as such it is preferable to use {@link ISummonedCreature#getCaster()} to
* get the caster object itself.
*/
@Nullable
WeakReference<EntityLivingBase> getCasterReference();
/** Internal, DO NOT CALL. */
void setCasterUUID(UUID uuid);
/** Internal, DO NOT CALL. This is for loading purposes only and is not usually synchronised. */
UUID getCasterUUID();
/** Returns the EntityLivingBase that summoned this creature, 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 creature simply had no caster in the first place. <i>This is the correct method
* to use to get the owner of this summoned creature. */
/**
* Returns the EntityLivingBase that summoned this creature, 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 creature simply had no caster in the first place. <i>This is the correct method to use
* to get the owner of this summoned creature.
*/
@Nullable
default EntityLivingBase getCaster(){
return getCasterReference() == null ? null : getCasterReference().get();
}
// Miscellaneous
/**
* Called by the server when constructing the spawn packet.
* Data should be added to the provided stream.
* <b>Implementors must call super when overriding.</b>
*
* @param buffer The packet data stream
*/
* Called by the server when constructing the spawn packet. Data should be added to the provided stream.
* <b>Implementors must call super when overriding.</b>
*
* @param buffer The packet data stream
*/
@Override
default void writeSpawnData(ByteBuf buffer) {
default void writeSpawnData(ByteBuf buffer){
buffer.writeInt(getCaster() != null ? getCaster().getEntityId() : -1);
buffer.writeInt(getLifetime());
}
/**
* Called by the client when it receives a Entity spawn packet.
* Data should be read out of the stream in the same way as it was written.
* <b>Implementors must call super when overriding.</b>
*
* @param additionalData The packet data stream
*/
* Called by the client when it receives a Entity spawn packet. Data should be read out of the stream in the same
* way as it was written. <b>Implementors must call super when overriding.</b>
*
* @param additionalData The packet data stream
*/
@Override
default void readSpawnData(ByteBuf buffer) {
default void readSpawnData(ByteBuf buffer){
int id = buffer.readInt();
// We're on the client side here, so we can safely use Minecraft.getMinecraft().theWorld via proxies.
if(id > -1) setCasterReference(new WeakReference<EntityLivingBase>((EntityLivingBase)Wizardry.proxy.getTheWorld().getEntityByID(id)));
// We're on the client side here, so we can safely use Minecraft.getMinecraft().world via proxies.
if(id > -1) setCasterReference(
new WeakReference<EntityLivingBase>((EntityLivingBase)Wizardry.proxy.getTheWorld().getEntityByID(id)));
setLifetime(buffer.readInt());
}
/**
* Shorthand for {@link WizardryUtilities#isValidTarget(Entity, Entity)}, with the owner of this creature as the
* attacker. Also allows implementors to override it if they wish to do so.
@@ -154,11 +167,13 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
default boolean isValidTarget(Entity target){
return WizardryUtilities.isValidTarget(this.getCaster(), target);
}
/** Returns a entity selector to be passed into AI methods. Normally, this should not be overridden, but it is
* possible for implementors to override this in order to do something special when selecting a target. */
/**
* Returns a entity selector to be passed into AI methods. Normally, this should not be overridden, but it is
* possible for implementors to override this in order to do something special when selecting a target.
*/
default Predicate<Entity> getTargetSelector(){
return new Predicate<Entity>(){
public boolean apply(Entity entity){
@@ -166,15 +181,19 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
// If the target is valid and not invisible...
if(!entity.isInvisible() && isValidTarget(entity)){
//... and is a player, they can be attacked, since players can't be in the whitelist or the blacklist.
// ... and is a player, they can be attacked, since players can't be in the whitelist or the
// blacklist.
if(entity instanceof EntityPlayer) return true;
//... and is a mob, a summoned creature, a wizard ...
if((entity instanceof IMob || entity instanceof ISummonedCreature || (entity instanceof EntityWizard && !(getCaster() instanceof EntityWizard))
// ... or in the whitelist ...
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT)))
// ... and is a mob, a summoned creature, a wizard ...
if((entity instanceof IMob || entity instanceof ISummonedCreature
|| (entity instanceof EntityWizard && !(getCaster() instanceof EntityWizard))
// ... or in the whitelist ...
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist)
.contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT)))
// ... and isn't in the blacklist ...
&& !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){
&& !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist)
.contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){
// ... it can be attacked.
return true;
}
@@ -184,10 +203,10 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
}
};
}
/**
* Called when this creature has existed for 1 tick, effectively when it has just been spawned. Normally used
* to add particles, sounds, etc.
* Called when this creature has existed for 1 tick, effectively when it has just been spawned. Normally used to add
* particles, sounds, etc.
*/
void onSpawn();
@@ -195,22 +214,27 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
* Called when this summoned creature vanishes. Normally used to add particles, sounds, etc.
*/
void onDespawn();
/** Whether this creature should spawn a subtle black swirl particle effect while alive. */
boolean hasParticleEffect();
/** Called from the event handler after the damage change is applied. Does nothing by default, but can be overridden
* to do something when a successful attack is made. This was added because the event-based damage source system
* can cause parts of attackEntityAsMob not to fire, since attackEntityFrom is intercepted and canceled.
/**
* Called from the event handler after the damage change is applied. Does nothing by default, but can be overridden
* to do something when a successful attack is made. This was added because the event-based damage source system can
* cause parts of attackEntityAsMob not to fire, since attackEntityFrom is intercepted and canceled.
* <p>
* Usage examples: {@link EntitySliverfishMinion} uses this to summon more silverfish if the target is killed,
* {@link EntitySkeletonMinion} and {@link EntitySpiderMinion} use this to add potion effects to the target. */
default void onSuccessfulAttack(EntityLivingBase target){};
* {@link EntitySkeletonMinion} and {@link EntitySpiderMinion} use this to add potion effects to the target.
*/
default void onSuccessfulAttack(EntityLivingBase target){
};
// Delegates
/** Implementors should call this from writeEntityToNBT. Can be overridden as long as super is called, but there's
* very little point in doing that since anything extra could just be added to writeEntityToNBT anyway. */
/**
* Implementors should call this from writeEntityToNBT. Can be overridden as long as super is called, but there's
* very little point in doing that since anything extra could just be added to writeEntityToNBT anyway.
*/
default void writeNBTDelegate(NBTTagCompound tagcompound){
if(this.getCaster() != null){
tagcompound.setUniqueId("casterUUID", this.getCaster().getUniqueID());
@@ -218,30 +242,37 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
tagcompound.setInteger("lifetime", getLifetime());
}
/** Implementors should call this from readEntityFromNBT. Can be overridden as long as super is called, but there's
* very little point in doing that since anything extra could just be added to readEntityFromNBT anyway. */
/**
* Implementors should call this from readEntityFromNBT. Can be overridden as long as super is called, but there's
* very little point in doing that since anything extra could just be added to readEntityFromNBT anyway.
*/
default void readNBTDelegate(NBTTagCompound tagcompound){
this.setCasterUUID(tagcompound.getUniqueId("casterUUID"));
this.setLifetime(tagcompound.getInteger("lifetime"));
}
/** Implementors should call this from setRevengeTarget, and call super.setRevengeTarget if and only if this method
* returns <b>true</b>. */
/**
* Implementors should call this from setRevengeTarget, and call super.setRevengeTarget if and only if this method
* returns <b>true</b>.
*/
default boolean shouldRevengeTarget(EntityLivingBase entity){
// Allows the config to prevent minions from revenge-targeting their owners.
return entity != this.getCaster() || Wizardry.settings.minionRevengeTargeting;
}
/** Implementors should call this from onUpdate. Can be overridden as long as super is called, but there's
* very little point in doing that since anything extra could just be added to onUpdate anyway. */
/**
* Implementors should call this from onUpdate. Can be overridden as long as super is called, but there's very
* little point in doing that since anything extra could just be added to onUpdate anyway.
*/
default void updateDelegate(){
if(!(this instanceof Entity)) throw new ClassCastException("Implementations of ISummonedCreature must extend Entity!");
if(!(this instanceof Entity))
throw new ClassCastException("Implementations of ISummonedCreature must extend Entity!");
Entity thisEntity = ((Entity)this);
if(this.getCaster() == null && this.getCasterUUID() != null){
Entity entity = WizardryUtilities.getEntityByUUID(thisEntity.worldObj, getCasterUUID());
Entity entity = WizardryUtilities.getEntityByUUID(thisEntity.world, getCasterUUID());
if(entity instanceof EntityLivingBase){
this.setCasterReference(new WeakReference<EntityLivingBase>((EntityLivingBase)entity));
}
@@ -255,22 +286,28 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
this.onDespawn();
thisEntity.setDead();
}
if(this.hasParticleEffect() && thisEntity.worldObj.isRemote && thisEntity.worldObj.rand.nextInt(8) == 0)
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, thisEntity.worldObj, thisEntity.posX, thisEntity.posY + thisEntity.worldObj.rand.nextDouble()*1.5, thisEntity.posZ, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f);
if(this.hasParticleEffect() && thisEntity.world.isRemote && thisEntity.world.rand.nextInt(8) == 0)
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, thisEntity.world, thisEntity.posX,
thisEntity.posY + thisEntity.world.rand.nextDouble() * 1.5, thisEntity.posZ, 0.0d, 0.0d, 0.0d, 0,
0.1f, 0.0f, 0.0f);
}
/** Implementors should call this from processInteract, and call super.processInteract if and only if this method
* returns <b>false</b>. */
default boolean interactDelegate(EntityPlayer player, EnumHand hand, ItemStack stack) {
/**
* Implementors should call this from processInteract, and call super.processInteract if and only if this method
* returns <b>false</b>.
*/
default boolean interactDelegate(EntityPlayer player, EnumHand hand){
ItemStack stack = player.getHeldItem(hand);
WizardData properties = WizardData.get(player);
// Selects one of the player's minions.
if(player.isSneaking() && stack != null && stack.getItem() instanceof ItemWand){
if(!player.worldObj.isRemote && properties != null && this.getCaster() == player){
if(player.isSneaking() && stack.getItem() instanceof ItemWand){
if(!player.world.isRemote && properties != null && this.getCaster() == player){
if(properties.selectedMinion != null && properties.selectedMinion.get() == this){
// Deselects the selected minion if right-clicked again
properties.selectedMinion = null;
@@ -282,12 +319,12 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
}
return true;
}
return false;
}
// Damage system
@SubscribeEvent
public static void onLivingAttackEvent(LivingAttackEvent event){
@@ -302,16 +339,22 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
event.setCanceled(true);
DamageSource newSource = event.getSource();
// Copies over the original DamageType if appropriate.
DamageType type = event.getSource() instanceof IElementalDamage ? ((IElementalDamage)event.getSource()).getType() : DamageType.MAGIC;
DamageType type = event.getSource() instanceof IElementalDamage
? ((IElementalDamage)event.getSource()).getType()
: DamageType.MAGIC;
// Copies over the original isRetaliatory flag if appropriate.
boolean isRetaliatory = event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory();
boolean isRetaliatory = event.getSource() instanceof IElementalDamage
&& ((IElementalDamage)event.getSource()).isRetaliatory();
// All summoned creatures are classified as magic, so it makes sense to do it this way.
if(event.getSource() instanceof EntityDamageSourceIndirect){
newSource = new IndirectMinionDamage(event.getSource().damageType, event.getSource().getSourceOfDamage(), event.getSource().getEntity(), summoner, type, isRetaliatory);
newSource = new IndirectMinionDamage(event.getSource().damageType,
event.getSource().getSourceOfDamage(), event.getSource().getEntity(), summoner, type,
isRetaliatory);
}else if(event.getSource() instanceof EntityDamageSource){
// Name is copied over so it uses the appropriate vanilla death message
newSource = new MinionDamage(event.getSource().damageType, event.getSource().getEntity(), summoner, type, isRetaliatory);
newSource = new MinionDamage(event.getSource().damageType, event.getSource().getEntity(), summoner,
type, isRetaliatory);
}
// Copy over any relevant 'attributes' the original DamageSource might have had.
@@ -325,7 +368,7 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
WizardryUtilities.applyStandardKnockback(event.getSource().getEntity(), event.getEntityLiving());
((ISummonedCreature)event.getSource().getEntity()).onSuccessfulAttack(event.getEntityLiving());
}
}
}
}
@@ -9,6 +9,7 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
/**
* Same as {@link EntityMagicProjectile}, but with an additional blast multiplier field which is synced and saved to
* allow for the spread of particles to be changed depending on the blast area.
*
* @author Electroblob
* @since Wizardry 1.2
*/
@@ -16,38 +17,38 @@ public abstract class EntityBomb extends EntityMagicProjectile implements IEntit
/** The entity blast multiplier. This is now synced and saved centrally from {@link EntityBomb}. */
public float blastMultiplier = 1.0f;
public EntityBomb(World world) {
public EntityBomb(World world){
super(world);
}
public EntityBomb(World world, EntityLivingBase thrower) {
public EntityBomb(World world, EntityLivingBase thrower){
super(world, thrower);
}
public EntityBomb(World world, EntityLivingBase thrower, float damageMultiplier, float blastMultiplier) {
public EntityBomb(World world, EntityLivingBase thrower, float damageMultiplier, float blastMultiplier){
super(world, thrower, damageMultiplier);
this.blastMultiplier = blastMultiplier;
this.blastMultiplier = blastMultiplier;
}
public EntityBomb(World par1World, double par2, double par4, double par6) {
public EntityBomb(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
@Override
public void writeSpawnData(ByteBuf buffer) {
public void writeSpawnData(ByteBuf buffer){
buffer.writeFloat(blastMultiplier);
}
@Override
public void readSpawnData(ByteBuf buffer) {
public void readSpawnData(ByteBuf buffer){
blastMultiplier = buffer.readFloat();
}
@Override
public void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
blastMultiplier = nbttagcompound.getFloat("blastMultiplier");
super.readEntityFromNBT(nbttagcompound);
blastMultiplier = nbttagcompound.getFloat("blastMultiplier");
}
@Override
@@ -12,77 +12,80 @@ import net.minecraft.potion.PotionEffect;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntityDarknessOrb extends EntityMagicProjectile
{
public EntityDarknessOrb(World par1World)
{
super(par1World);
}
public class EntityDarknessOrb extends EntityMagicProjectile {
public EntityDarknessOrb(World par1World){
super(par1World);
}
public EntityDarknessOrb(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntityDarknessOrb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier);
}
public EntityDarknessOrb(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
public EntityDarknessOrb(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
@Override
protected float getSpeed(){
return 0.5F;
}
public EntityDarknessOrb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier);
}
@Override
protected void onImpact(RayTraceResult RayTraceResult)
{
Entity target = RayTraceResult.entityHit;
if (target != null && !MagicDamage.isEntityImmune(DamageType.WITHER, target))
{
float damage = 8 * damageMultiplier;
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.WITHER).setProjectile(), damage);
if(target instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.WITHER, target)) ((EntityLivingBase)target).addPotionEffect(new PotionEffect(MobEffects.WITHER, 150, 1));
public EntityDarknessOrb(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
this.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
@Override
protected float getSpeed(){
return 0.5F;
}
this.setDead();
}
public void onUpdate(){
super.onUpdate();
if(worldObj.isRemote){
float brightness = rand.nextFloat()*0.2f;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f);
}
if(this.ticksExisted > 150){
this.setDead();
}
// Cancels out the slowdown effect in EntityThrowable
this.motionX /= 0.99;
this.motionY /= 0.99;
this.motionZ /= 0.99;
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
protected float getGravityVelocity()
{
return 0.0F;
}
@Override
protected void onImpact(RayTraceResult RayTraceResult){
Entity target = RayTraceResult.entityHit;
if(target != null && !MagicDamage.isEntityImmune(DamageType.WITHER, target)){
float damage = 8 * damageMultiplier;
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.WITHER).setProjectile(),
damage);
if(target instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.WITHER, target))
((EntityLivingBase)target).addPotionEffect(new PotionEffect(MobEffects.WITHER, 150, 1));
this.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
this.setDead();
}
public void onUpdate(){
super.onUpdate();
if(world.isRemote){
float brightness = rand.nextFloat() * 0.2f;
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 20 + rand.nextInt(10),
brightness, 0.0f, brightness);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world,
this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width,
this.posY + this.rand.nextDouble() * (double)this.height,
this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f,
0.0f);
}
if(this.ticksExisted > 150){
this.setDead();
}
// Cancels out the slowdown effect in EntityThrowable
this.motionX /= 0.99;
this.motionY /= 0.99;
this.motionZ /= 0.99;
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
protected float getGravityVelocity(){
return 0.0F;
}
}
@@ -10,86 +10,90 @@ import net.minecraft.init.SoundEvents;
import net.minecraft.potion.PotionEffect;
import net.minecraft.world.World;
public class EntityDart extends EntityMagicArrow
{
public class EntityDart extends EntityMagicArrow {
/** Basic shell constructor. Should only be used by the client. */
public EntityDart(World world) {
public EntityDart(World world){
super(world);
}
/** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this
* constructor and then call setVelocity() as that method is, bizarrely, client-side only. */
public EntityDart(World world, double x, double y, double z)
{
super(world, x, y, z);
}
/** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered
* slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on
* normal and 2 on hard difficulty. */
public EntityDart(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier)
{
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this constructor
* and then call setVelocity() as that method is, bizarrely, client-side only.
*/
public EntityDart(World world, double x, double y, double z){
super(world, x, y, z);
}
/** Creates a projectile pointing in the direction the caster is looking, with the given speed.
* USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */
public EntityDart(World world, EntityLivingBase caster, float speed, float damageMultiplier)
{
super(world, caster, speed, damageMultiplier);
}
/**
* Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be
* altered slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on
* easy, 6 on normal and 2 on hard difficulty.
*/
public EntityDart(World world, EntityLivingBase caster, Entity target, float speed, float aimingError,
float damageMultiplier){
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile pointing in the direction the caster is looking, with the given speed. USE THIS CONSTRUCTOR
* FOR NORMAL SPELLS.
*/
public EntityDart(World world, EntityLivingBase caster, float speed, float damageMultiplier){
super(world, caster, speed, damageMultiplier);
}
@Override
public void onEntityHit(EntityLivingBase entityHit){
//Adds a weakness effect to the target.
entityHit.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 200, 1, false, false));
// Adds a weakness effect to the target.
entityHit.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 200, 1, false, false));
this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
@Override
public void onBlockHit(){
this.playSound(SoundEvents.ENTITY_ARROW_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
@Override
public void tickInAir(){
if(this.worldObj.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, worldObj, this.posX, this.posY, this.posZ, 0, -0.03, 0, 10 + rand.nextInt(5));
}
}
if(this.world.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, world, this.posX, this.posY, this.posZ, 0, -0.03, 0,
10 + rand.nextInt(5));
}
}
// Replicates the original behaviour of staying stuck in block for a few seconds before disappearing.
@Override
public void tickInGround(){
if(this.ticksInGround > 60){
this.setDead();
}
}
}
@Override
public double getDamage(){
return 4.0d;
}
public double getDamage(){
return 4.0d;
}
@Override
public DamageType getDamageType(){
return DamageType.MAGIC;
}
@Override
public boolean doGravity(){
return true;
}
@Override
public boolean doDeceleration(){
return true;
}
@Override
protected void entityInit() {
public boolean doGravity(){
return true;
}
@Override
public boolean doDeceleration(){
return true;
}
@Override
protected void entityInit(){
}
}
@@ -9,85 +9,82 @@ import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntityFirebolt extends EntityMagicProjectile
{
public EntityFirebolt(World par1World)
{
super(par1World);
}
public class EntityFirebolt extends EntityMagicProjectile {
public EntityFirebolt(World par1World){
super(par1World);
}
public EntityFirebolt(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntityFirebolt(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier);
}
public EntityFirebolt(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
public EntityFirebolt(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
public EntityFirebolt(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier);
}
@Override
protected void onImpact(RayTraceResult rayTrace)
{
Entity entityHit = rayTrace.entityHit;
if (entityHit != null)
{
float damage = 5 * damageMultiplier;
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), damage);
if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) entityHit.setFire(5);
}
public EntityFirebolt(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
this.playSound(SoundEvents.BLOCK_LAVA_POP, 2, 0.8f + rand.nextFloat()*0.3f);
@Override
protected void onImpact(RayTraceResult rayTrace){
Entity entityHit = rayTrace.entityHit;
// Particle effect
if(worldObj.isRemote){
for(int i=0;i<8;i++){
worldObj.spawnParticle(EnumParticleTypes.LAVA, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0);
if(entityHit != null){
float damage = 5 * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(),
damage);
if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) entityHit.setFire(5);
}
this.playSound(SoundEvents.BLOCK_LAVA_POP, 2, 0.8f + rand.nextFloat() * 0.3f);
// Particle effect
if(world.isRemote){
for(int i = 0; i < 8; i++){
world.spawnParticle(EnumParticleTypes.LAVA, this.posX + rand.nextFloat() - 0.5,
this.posY + this.height / 2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0,
0);
}
}
}
this.setDead();
}
@Override
public void onUpdate(){
super.onUpdate();
if(worldObj.isRemote){
for(int i=0; i<4; i++){
worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0);
}
}
if(this.ticksExisted > 8){
this.setDead();
}
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
@Override
protected float getGravityVelocity()
{
return 0.0F;
}
/**
* Return whether this entity should be rendered as on fire.
*/
@Override
public boolean canRenderOnFire()
{
return false;
}
this.setDead();
}
@Override
public void onUpdate(){
super.onUpdate();
if(world.isRemote){
for(int i = 0; i < 4; i++){
world.spawnParticle(EnumParticleTypes.FLAME, this.posX + rand.nextFloat() * 0.2 - 0.1,
this.posY + this.height / 2 + rand.nextFloat() * 0.2 - 0.1,
this.posZ + rand.nextFloat() * 0.2 - 0.1, 0, 0, 0);
}
}
if(this.ticksExisted > 8){
this.setDead();
}
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
@Override
protected float getGravityVelocity(){
return 0.0F;
}
/**
* Return whether this entity should be rendered as on fire.
*/
@Override
public boolean canRenderOnFire(){
return false;
}
}
@@ -15,73 +15,84 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntityFirebomb extends EntityBomb {
public EntityFirebomb(World par1World)
{
super(par1World);
}
public EntityFirebomb(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntityFirebomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
public EntityFirebomb(World par1World){
super(par1World);
}
public EntityFirebomb(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
public EntityFirebomb(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult)
{
Entity entityHit = par1RayTraceResult.entityHit;
if (entityHit != null)
{
// This is if the firebomb gets a direct hit
float damage = 5 * damageMultiplier;
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), damage);
if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) entityHit.setFire(10);
}
public EntityFirebomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier,
float blastMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
// Particle effect
if(worldObj.isRemote){
this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
for(int i=0;i<60*blastMultiplier;i++){
//this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0);
Wizardry.proxy.spawnParticle(WizardryParticleType.MAGIC_FIRE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0, 15 + rand.nextInt(5), 2 + rand.nextFloat(), 0, 0);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 1.0f, 0.2f + rand.nextFloat()*0.4f, 0.0f);
public EntityFirebomb(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult){
Entity entityHit = par1RayTraceResult.entityHit;
if(entityHit != null){
// This is if the firebomb gets a direct hit
float damage = 5 * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(),
damage);
if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) entityHit.setFire(10);
}
// Particle effect
if(world.isRemote){
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
for(int i = 0; i < 60 * blastMultiplier; i++){
// this.world.spawnParticle(EnumParticleTypes.FLAME, this.posX + (this.rand.nextDouble()*4 -
// 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ +
// (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0);
Wizardry.proxy.spawnParticle(WizardryParticleType.MAGIC_FIRE, world,
this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0, 0, 0, 15 + rand.nextInt(5),
2 + rand.nextFloat(), 0, 0);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world,
this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 1.0f,
0.2f + rand.nextFloat() * 0.4f, 0.0f);
}
}
}
if(!this.world.isRemote){
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
double range = 3.0d * blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY,
this.posZ, this.world);
if(!this.worldObj.isRemote){
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
double range = 3.0d*blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY, this.posZ, this.worldObj);
for(EntityLivingBase target : targets){
if(target != entityHit && target != this.getThrower() && !MagicDamage.isEntityImmune(DamageType.FIRE, target)){
if(target != entityHit && target != this.getThrower()
&& !MagicDamage.isEntityImmune(DamageType.FIRE, target)){
// Splash damage does not count as projectile damage
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE), 4.0f * damageMultiplier);
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE),
4.0f * damageMultiplier);
target.setFire(7);
}
}
this.setDead();
}
}
this.setDead();
}
}
}
@@ -7,79 +7,83 @@ import net.minecraft.init.SoundEvents;
import net.minecraft.world.World;
public class EntityForceArrow extends EntityMagicArrow {
/** Basic shell constructor. Should only be used by the client. */
public EntityForceArrow(World world) {
/** Basic shell constructor. Should only be used by the client. */
public EntityForceArrow(World world){
super(world);
}
/** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this
* constructor and then call setVelocity() as that method is, bizarrely, client-side only. */
public EntityForceArrow(World world, double x, double y, double z)
{
super(world, x, y, z);
}
/** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered
* slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on
* normal and 2 on hard difficulty. */
public EntityForceArrow(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier)
{
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this constructor
* and then call setVelocity() as that method is, bizarrely, client-side only.
*/
public EntityForceArrow(World world, double x, double y, double z){
super(world, x, y, z);
}
/** Creates a projectile pointing in the direction the caster is looking, with the given speed.
* USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */
public EntityForceArrow(World world, EntityLivingBase caster, float speed, float damageMultiplier)
{
super(world, caster, speed, damageMultiplier);
}
/**
* Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be
* altered slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on
* easy, 6 on normal and 2 on hard difficulty.
*/
public EntityForceArrow(World world, EntityLivingBase caster, Entity target, float speed, float aimingError,
float damageMultiplier){
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile pointing in the direction the caster is looking, with the given speed. USE THIS CONSTRUCTOR
* FOR NORMAL SPELLS.
*/
public EntityForceArrow(World world, EntityLivingBase caster, float speed, float damageMultiplier){
super(world, caster, speed, damageMultiplier);
}
@Override
public void onEntityHit(EntityLivingBase entityHit){
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.0F, 1.0F);
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.0F, 1.0F);
}
@Override
public void tickInGround(){
this.setDead();
this.setDead();
}
@Override
public void onBlockHit(){
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.0F, 1.0F);
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.0F, 1.0F);
}
@Override
public void tickInAir(){
if (this.ticksExisted > 20){
this.setDead();
}
}
if(this.ticksExisted > 20){
this.setDead();
}
}
@Override
public double getDamage(){
return 7.0d;
}
public double getDamage(){
return 7.0d;
}
@Override
public DamageType getDamageType(){
return DamageType.FORCE;
}
@Override
public boolean doGravity(){
return false;
}
@Override
public boolean doDeceleration(){
return false;
}
@Override
protected void entityInit() {
public boolean doGravity(){
return false;
}
@Override
public boolean doDeceleration(){
return false;
}
@Override
protected void entityInit(){
}
}
@@ -15,92 +15,95 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntityForceOrb extends EntityMagicProjectile {
/** The entity blast multiplier. In this particular case, it doesn't need syncing, so this class doesn't extend
* EntityBlastProjectile. */
/**
* The entity blast multiplier. In this particular case, it doesn't need syncing, so this class doesn't extend
* EntityBlastProjectile.
*/
public float blastMultiplier;
public EntityForceOrb(World par1World)
{
super(par1World);
}
public EntityForceOrb(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntityForceOrb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier);
this.blastMultiplier = blastMultiplier;
}
public EntityForceOrb(World par1World){
super(par1World);
}
public EntityForceOrb(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
public EntityForceOrb(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult){
if (par1RayTraceResult.entityHit != null)
{
// This is if the force orb gets a direct hit
this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
// Particle effect
if(this.worldObj.isRemote){
for(int j=0; j<20; j++){
float brightness = 0.5f + (rand.nextFloat()/2);
double x = this.posX - 0.25d + (rand.nextDouble()/2);
double y = this.posY - 0.25d + (rand.nextDouble()/2);
double z = this.posZ - 0.25d + (rand.nextDouble()/2);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2, 6, brightness, 1.0f, brightness + 0.2f);
}
this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
public EntityForceOrb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier,
float blastMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier);
this.blastMultiplier = blastMultiplier;
}
public EntityForceOrb(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult){
if(par1RayTraceResult.entityHit != null){
// This is if the force orb gets a direct hit
this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
// Particle effect
if(this.world.isRemote){
for(int j = 0; j < 20; j++){
float brightness = 0.5f + (rand.nextFloat() / 2);
double x = this.posX - 0.25d + (rand.nextDouble() / 2);
double y = this.posY - 0.25d + (rand.nextDouble() / 2);
double z = this.posZ - 0.25d + (rand.nextDouble() / 2);
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x, y, z, (x - this.posX) * 2,
(y - this.posY) * 2, (z - this.posZ) * 2, 6, brightness, 1.0f, brightness + 0.2f);
}
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
if(!this.world.isRemote){
// 2 gives a cool flanging effect!
float pitch = this.rand.nextFloat() * 0.2F + 0.3F;
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.5F, pitch);
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.5F, pitch - 0.01f);
double blastRadius = 4.0d * blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(blastRadius, this.posX,
this.posY, this.posZ, this.world);
if(!this.worldObj.isRemote){
// 2 gives a cool flanging effect!
float pitch = this.rand.nextFloat() * 0.2F + 0.3F;
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.5F, pitch);
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.5F, pitch - 0.01f);
double blastRadius = 4.0d*blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(blastRadius, this.posX, this.posY, this.posZ, this.worldObj);
for(EntityLivingBase target : targets){
if(target != this.getThrower()){
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;
float damage = 4*damageMultiplier;
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.BLAST), damage);
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;
float damage = 4 * damageMultiplier;
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.BLAST), damage);
target.motionX = dx;
target.motionY = velY + 0.4;
target.motionZ = dz;
}
}
this.setDead();
}
}
this.setDead();
}
}
@Override
public void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
blastMultiplier = nbttagcompound.getFloat("blastMultiplier");
super.readEntityFromNBT(nbttagcompound);
blastMultiplier = nbttagcompound.getFloat("blastMultiplier");
}
@Override
@@ -20,116 +20,122 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntityIceCharge extends EntityBomb {
public EntityIceCharge(World par1World)
{
super(par1World);
}
public EntityIceCharge(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntityIceCharge(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
public EntityIceCharge(World par1World){
super(par1World);
}
public EntityIceCharge(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
public EntityIceCharge(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult)
{
Entity entityHit = par1RayTraceResult.entityHit;
if (entityHit != null)
{
// This is if the ice charge gets a direct hit
float damage = 4 * damageMultiplier;
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FROST).setProjectile(), damage);
if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.FROST, entityHit))
((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(WizardryPotions.frost, 120, 1));
}
public EntityIceCharge(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier,
float blastMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
// Particle effect
if(worldObj.isRemote){
this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
for(int i=0;i<30*blastMultiplier;i++){
float brightness = 0.4f + rand.nextFloat()*0.5f;
Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 35);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness, brightness+0.1f, 1.0f);
public EntityIceCharge(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult){
Entity entityHit = par1RayTraceResult.entityHit;
if(entityHit != null){
// This is if the ice charge gets a direct hit
float damage = 4 * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FROST).setProjectile(),
damage);
if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.FROST, entityHit))
((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(WizardryPotions.frost, 120, 1));
}
// Particle effect
if(world.isRemote){
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
for(int i = 0; i < 30 * blastMultiplier; i++){
float brightness = 0.4f + rand.nextFloat() * 0.5f;
Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, world,
this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0.0d, 0.0d, 0.0d, 35);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world,
this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness,
brightness + 0.1f, 1.0f);
}
}
}
if(!this.world.isRemote){
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5f, rand.nextFloat() * 0.4f + 0.6f);
this.playSound(WizardrySounds.SPELL_ICE, 1.2f, rand.nextFloat() * 0.4f + 1.2f);
double radius = 3.0d * blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(radius, this.posX, this.posY,
this.posZ, this.world);
if(!this.worldObj.isRemote){
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5f, rand.nextFloat() * 0.4f + 0.6f);
this.playSound(WizardrySounds.SPELL_ICE, 1.2f, rand.nextFloat() * 0.4f + 1.2f);
double radius = 3.0d*blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(radius, this.posX, this.posY, this.posZ, this.worldObj);
// Slows targets
for(EntityLivingBase target : targets){
if(target != entityHit && target != this.getThrower()){
if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0));
if(!MagicDamage.isEntityImmune(DamageType.FROST, target))
target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0));
}
}
// Places snow and ice on ground.
for(int i=-1; i<2; i++){
for(int j=-1; j<2; j++){
// Places snow and ice on ground.
for(int i = -1; i < 2; i++){
for(int j = -1; j < 2; j++){
BlockPos pos = new BlockPos(this.posX + i, this.posY, this.posZ + j);
int y = WizardryUtilities.getNearestFloorLevelB(worldObj, pos, 7);
int y = WizardryUtilities.getNearestFloorLevelB(world, pos, 7);
pos = new BlockPos(pos.getX(), y, pos.getZ());
double dist = this.getDistance(pos.getX(), pos.getY(), pos.getZ());
// Randomised with weighting so that the nearer the block the more likely it is to be snowed.
if(y != -1 && rand.nextInt((int)dist*2 + 1) < 1 && dist < 2){
if(worldObj.getBlockState(pos.down()).getBlock() == Blocks.WATER){
worldObj.setBlockState(pos.down(), Blocks.ICE.getDefaultState());
if(y != -1 && rand.nextInt((int)dist * 2 + 1) < 1 && dist < 2){
if(world.getBlockState(pos.down()).getBlock() == Blocks.WATER){
world.setBlockState(pos.down(), Blocks.ICE.getDefaultState());
}else{
// Don't need to check whether the block at pos can be replaced since getNearestFloorLevelB
// only ever returns floors with air above them.
worldObj.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState());
world.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState());
}
}
}
}
// Releases shards
for(int i=0; i<10; i++){
double dx = rand.nextDouble()-0.5;
double dy = rand.nextDouble()-0.5;
double dz = rand.nextDouble()-0.5;
EntityIceShard iceshard = new EntityIceShard(worldObj, this.posX + dx, this.posY + dy, this.posZ + dz);
// Releases shards
for(int i = 0; i < 10; i++){
double dx = rand.nextDouble() - 0.5;
double dy = rand.nextDouble() - 0.5;
double dz = rand.nextDouble() - 0.5;
EntityIceShard iceshard = new EntityIceShard(world, this.posX + dx, this.posY + dy, this.posZ + dz);
iceshard.motionX = dx;
iceshard.motionY = dy;
iceshard.motionZ = dz;
iceshard.setShootingEntity(this.getThrower());
iceshard.damageMultiplier = this.damageMultiplier;
worldObj.spawnEntityInWorld(iceshard);
world.spawnEntity(iceshard);
}
this.setDead();
}
}
@Override
public boolean canRenderOnFire() {
this.setDead();
}
}
@Override
public boolean canRenderOnFire(){
return false;
}
}
@@ -12,106 +12,112 @@ import net.minecraft.potion.PotionEffect;
import net.minecraft.world.World;
public class EntityIceLance extends EntityMagicArrow {
/** Basic shell constructor. Should only be used by the client. */
public EntityIceLance(World world) {
/** Basic shell constructor. Should only be used by the client. */
public EntityIceLance(World world){
super(world);
this.setKnockbackStrength(1);
}
/** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this
* constructor and then call setVelocity() as that method is, bizarrely, client-side only. */
public EntityIceLance(World world, double x, double y, double z)
{
super(world, x, y, z);
this.setKnockbackStrength(1);
}
/** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered
* slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on
* normal and 2 on hard difficulty. */
public EntityIceLance(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier)
{
super(world, caster, target, speed, aimingError, damageMultiplier);
/**
* Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this constructor
* and then call setVelocity() as that method is, bizarrely, client-side only.
*/
public EntityIceLance(World world, double x, double y, double z){
super(world, x, y, z);
this.setKnockbackStrength(1);
}
}
/** Creates a projectile pointing in the direction the caster is looking, with the given speed.
* USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */
public EntityIceLance(World world, EntityLivingBase caster, float speed, float damageMultiplier)
{
super(world, caster, speed, damageMultiplier);
/**
* Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be
* altered slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on
* easy, 6 on normal and 2 on hard difficulty.
*/
public EntityIceLance(World world, EntityLivingBase caster, Entity target, float speed, float aimingError,
float damageMultiplier){
super(world, caster, target, speed, aimingError, damageMultiplier);
this.setKnockbackStrength(1);
}
}
/**
* Creates a projectile pointing in the direction the caster is looking, with the given speed. USE THIS CONSTRUCTOR
* FOR NORMAL SPELLS.
*/
public EntityIceLance(World world, EntityLivingBase caster, float speed, float damageMultiplier){
super(world, caster, speed, damageMultiplier);
this.setKnockbackStrength(1);
}
@Override
public void onEntityHit(EntityLivingBase entityHit){
// Adds a freeze effect to the target.
if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) entityHit.addPotionEffect(new PotionEffect(WizardryPotions.frost, 300, 0));
// Adds a freeze effect to the target.
if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit))
entityHit.addPotionEffect(new PotionEffect(WizardryPotions.frost, 300, 0));
this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
@Override
public void tickInAir(){
}
}
@Override
public void onBlockHit(){
// Adds a particle effect when the ice lance hits a block.
if(this.worldObj.isRemote){
for(int j=0; j<10; j++){
double x = this.posX - 0.25d + (rand.nextDouble()/2);
double y = this.posY - 0.25d + (rand.nextDouble()/2);
double z = this.posZ - 0.25d + (rand.nextDouble()/2);
Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, x, y, z, x - this.posX, y - this.posY, z - this.posZ, 20 + rand.nextInt(10));
}
}
// Parameters for sound: sound event name, volume, pitch.
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.0F, rand.nextFloat() * 0.4F + 1.2F);
if(this.world.isRemote){
for(int j = 0; j < 10; j++){
double x = this.posX - 0.25d + (rand.nextDouble() / 2);
double y = this.posY - 0.25d + (rand.nextDouble() / 2);
double z = this.posZ - 0.25d + (rand.nextDouble() / 2);
Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, world, x, y, z, x - this.posX, y - this.posY,
z - this.posZ, 20 + rand.nextInt(10));
}
}
// Parameters for sound: sound event name, volume, pitch.
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.0F, rand.nextFloat() * 0.4F + 1.2F);
}
@Override
public void tickInGround(){
this.setDead();
this.setDead();
}
@Override
public double getDamage(){
return 10.0d;
}
public double getDamage(){
return 10.0d;
}
@Override
public DamageType getDamageType(){
return DamageType.FROST;
}
@Override
public boolean doGravity(){
return true;
}
@Override
public boolean doDeceleration(){
return true;
}
@Override
public boolean doOverpenetration(){
return true;
}
@Override
protected void entityInit() {
public boolean doGravity(){
return true;
}
@Override
public boolean canRenderOnFire() {
public boolean doDeceleration(){
return true;
}
@Override
public boolean doOverpenetration(){
return true;
}
@Override
protected void entityInit(){
}
@Override
public boolean canRenderOnFire(){
return false;
}
}
@@ -12,92 +12,98 @@ import net.minecraft.potion.PotionEffect;
import net.minecraft.world.World;
public class EntityIceShard extends EntityMagicArrow {
/** Basic shell constructor. Should only be used by the client. */
public EntityIceShard(World world) {
/** Basic shell constructor. Should only be used by the client. */
public EntityIceShard(World world){
super(world);
}
/** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this
* constructor and then call setVelocity() as that method is, bizarrely, client-side only. */
public EntityIceShard(World world, double x, double y, double z)
{
super(world, x, y, z);
}
/** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered
* slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on
* normal and 2 on hard difficulty. */
public EntityIceShard(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier)
{
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this constructor
* and then call setVelocity() as that method is, bizarrely, client-side only.
*/
public EntityIceShard(World world, double x, double y, double z){
super(world, x, y, z);
}
/** Creates a projectile pointing in the direction the caster is looking, with the given speed.
* USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */
public EntityIceShard(World world, EntityLivingBase caster, float speed, float damageMultiplier)
{
super(world, caster, speed, damageMultiplier);
}
/**
* Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be
* altered slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on
* easy, 6 on normal and 2 on hard difficulty.
*/
public EntityIceShard(World world, EntityLivingBase caster, Entity target, float speed, float aimingError,
float damageMultiplier){
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile pointing in the direction the caster is looking, with the given speed. USE THIS CONSTRUCTOR
* FOR NORMAL SPELLS.
*/
public EntityIceShard(World world, EntityLivingBase caster, float speed, float damageMultiplier){
super(world, caster, speed, damageMultiplier);
}
@Override
public void onEntityHit(EntityLivingBase entityHit){
// Adds a freeze effect to the target.
if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) entityHit.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 0));
// Adds a freeze effect to the target.
if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit))
entityHit.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 0));
this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
@Override
public void tickInAir(){
}
}
@Override
public void onBlockHit(){
// Adds a particle effect when the ice shard hits a block.
if(this.worldObj.isRemote){
for(int j=0; j<10; j++){
double x = this.posX - 0.25d + (rand.nextDouble()/2);
double y = this.posY - 0.25d + (rand.nextDouble()/2);
double z = this.posZ - 0.25d + (rand.nextDouble()/2);
Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, x, y, z, x - this.posX, y - this.posY, z - this.posZ, 20 + rand.nextInt(10));
}
}
// Parameters for sound: sound event name, volume, pitch.
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.0F, rand.nextFloat() * 0.4F + 1.2F);
if(this.world.isRemote){
for(int j = 0; j < 10; j++){
double x = this.posX - 0.25d + (rand.nextDouble() / 2);
double y = this.posY - 0.25d + (rand.nextDouble() / 2);
double z = this.posZ - 0.25d + (rand.nextDouble() / 2);
Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, world, x, y, z, x - this.posX, y - this.posY,
z - this.posZ, 20 + rand.nextInt(10));
}
}
// Parameters for sound: sound event name, volume, pitch.
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.0F, rand.nextFloat() * 0.4F + 1.2F);
}
@Override
public double getDamage(){
return 6.0d;
}
public double getDamage(){
return 6.0d;
}
@Override
public DamageType getDamageType(){
return DamageType.FROST;
}
@Override
public boolean doGravity(){
return true;
}
@Override
public boolean doDeceleration(){
return true;
}
@Override
protected void entityInit() {
public boolean doGravity(){
return true;
}
@Override
public boolean canRenderOnFire() {
public boolean doDeceleration(){
return true;
}
@Override
protected void entityInit(){
}
@Override
public boolean canRenderOnFire(){
return false;
}
}
@@ -10,86 +10,91 @@ import net.minecraft.world.World;
public class EntityLightningArrow extends EntityMagicArrow {
/** Basic shell constructor. Should only be used by the client. */
public EntityLightningArrow(World world) {
/** Basic shell constructor. Should only be used by the client. */
public EntityLightningArrow(World world){
super(world);
}
/** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this
* constructor and then call setVelocity() as that method is, bizarrely, client-side only. */
public EntityLightningArrow(World world, double x, double y, double z)
{
super(world, x, y, z);
}
/** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered
* slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on
* normal and 2 on hard difficulty. */
public EntityLightningArrow(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier)
{
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this constructor
* and then call setVelocity() as that method is, bizarrely, client-side only.
*/
public EntityLightningArrow(World world, double x, double y, double z){
super(world, x, y, z);
}
/** Creates a projectile pointing in the direction the caster is looking, with the given speed.
* USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */
public EntityLightningArrow(World world, EntityLivingBase caster, float speed, float damageMultiplier)
{
super(world, caster, speed, damageMultiplier);
}
/**
* Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be
* altered slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on
* easy, 6 on normal and 2 on hard difficulty.
*/
public EntityLightningArrow(World world, EntityLivingBase caster, Entity target, float speed, float aimingError,
float damageMultiplier){
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile pointing in the direction the caster is looking, with the given speed. USE THIS CONSTRUCTOR
* FOR NORMAL SPELLS.
*/
public EntityLightningArrow(World world, EntityLivingBase caster, float speed, float damageMultiplier){
super(world, caster, speed, damageMultiplier);
}
@Override
public void onEntityHit(EntityLivingBase entityHit){
if(worldObj.isRemote){
for(int j=0;j<8;j++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3);
if(world.isRemote){
for(int j = 0; j < 8; j++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, this.posX + rand.nextFloat() - 0.5,
this.posY + this.height / 2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0,
0, 3);
}
}
/* Pretty sure this needn't be here, probably missed it when I implemented the damage type system.
if(entityHit instanceof EntityCreeper && !((EntityCreeper)entityHit).getPowered()){
entityHit.getDataWatcher().updateObject(17, Byte.valueOf((byte)1));
if(this.getShootingEntity() instanceof EntityPlayer) ((EntityPlayer)this.getShootingEntity()).addStat(Wizardry.chargeCreeper);
}
*/
this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.0F);
/* Pretty sure this needn't be here, probably missed it when I implemented the damage type system. if(entityHit
* instanceof EntityCreeper && !((EntityCreeper)entityHit).getPowered()){
* entityHit.getDataWatcher().updateObject(17, Byte.valueOf((byte)1)); if(this.getShootingEntity() instanceof
* EntityPlayer) ((EntityPlayer)this.getShootingEntity()).addStat(Wizardry.chargeCreeper); } */
this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.0F);
}
@Override
public void tickInAir(){
if (this.ticksExisted > 20){
this.setDead();
}
if(this.ticksExisted > 20){
this.setDead();
}
if(world.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, this.posX, this.posY, this.posZ, 0, 0, 0,
3);
}
}
if(worldObj.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 3);
}
}
@Override
public double getDamage(){
return 7.0d;
}
public double getDamage(){
return 7.0d;
}
@Override
public DamageType getDamageType(){
return DamageType.SHOCK;
}
@Override
public boolean doGravity(){
return false;
}
@Override
public boolean doDeceleration(){
return false;
}
@Override
protected void entityInit() {
public boolean doGravity(){
return false;
}
@Override
public boolean doDeceleration(){
return false;
}
@Override
protected void entityInit(){
}
}
@@ -13,71 +13,67 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntityLightningDisc extends EntityMagicProjectile
{
public EntityLightningDisc(World par1World)
{
super(par1World);
}
public class EntityLightningDisc extends EntityMagicProjectile {
public EntityLightningDisc(World par1World){
super(par1World);
}
public EntityLightningDisc(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntityLightningDisc(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier);
}
public EntityLightningDisc(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
public EntityLightningDisc(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
this.width = 2.0f;
this.height = 0.5f;
}
@Override
protected float getSpeed(){
return 1.2f;
}
public EntityLightningDisc(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier);
}
public EntityLightningDisc(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
this.width = 2.0f;
this.height = 0.5f;
}
@Override
protected void onImpact(RayTraceResult mop)
{
Entity entityHit = mop.entityHit;
if (entityHit != null)
{
float damage = 12 * damageMultiplier;
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), damage);
}
@Override
protected float getSpeed(){
return 1.2f;
}
this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
@Override
protected void onImpact(RayTraceResult mop){
Entity entityHit = mop.entityHit;
if(mop.typeOfHit == RayTraceResult.Type.BLOCK) this.setDead();
}
if(entityHit != null){
float damage = 12 * damageMultiplier;
@Override
public void onUpdate(){
super.onUpdate();
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK),
damage);
}
// Particle effect
if(worldObj.isRemote){
for(int i=0;i<8;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat()*2 - 1, this.posY, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3);
//worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0);
this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
if(mop.typeOfHit == RayTraceResult.Type.BLOCK) this.setDead();
}
@Override
public void onUpdate(){
super.onUpdate();
// Particle effect
if(world.isRemote){
for(int i = 0; i < 8; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, this.posX + rand.nextFloat() * 2 - 1,
this.posY, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3);
// world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + rand.nextFloat() - 0.5, this.posY +
// this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0);
}
}
if(!this.isCollided && !worldObj.isRemote){
double seekingRange = 5.0d;
List<EntityLivingBase> entities = WizardryUtilities.getEntitiesWithinRadius(seekingRange, this.posX, this.posY, this.posZ, this.worldObj);
}
if(!this.isCollided && !world.isRemote){
double seekingRange = 5.0d;
List<EntityLivingBase> entities = WizardryUtilities.getEntitiesWithinRadius(seekingRange, this.posX,
this.posY, this.posZ, this.world);
Entity target = null;
for(Entity possibleTarget : entities){
@@ -89,33 +85,31 @@ public class EntityLightningDisc extends EntityMagicProjectile
}
}
}
if(target != null && Math.abs(this.motionX) < 1 && Math.abs(this.motionY) < 1 && Math.abs(this.motionZ) < 1){
this.addVelocity((target.posX - this.posX)/30, (target.posY + target.height/2 - this.posY)/30, (target.posZ - this.posZ)/30);
if(target != null && Math.abs(this.motionX) < 1 && Math.abs(this.motionY) < 1
&& Math.abs(this.motionZ) < 1){
this.addVelocity((target.posX - this.posX) / 30, (target.posY + target.height / 2 - this.posY) / 30,
(target.posZ - this.posZ) / 30);
}
}
if(this.ticksExisted > 50){
this.setDead();
}
// Cancels out the slowdown effect in EntityThrowable
this.motionX /= 0.99;
this.motionY /= 0.99;
this.motionZ /= 0.99;
}
}
@Override
protected float getGravityVelocity()
{
return 0.0F;
}
if(this.ticksExisted > 50){
this.setDead();
}
@Override
public boolean canRenderOnFire()
{
return false;
}
// Cancels out the slowdown effect in EntityThrowable
this.motionX /= 0.99;
this.motionY /= 0.99;
this.motionZ /= 0.99;
}
@Override
protected float getGravityVelocity(){
return 0.0F;
}
@Override
public boolean canRenderOnFire(){
return false;
}
}
File diff suppressed because it is too large Load Diff
@@ -8,75 +8,82 @@ import net.minecraft.init.SoundEvents;
import net.minecraft.world.World;
public class EntityMagicMissile extends EntityMagicArrow {
/** Basic shell constructor. Should only be used by the client. */
public EntityMagicMissile(World world) {
/** Basic shell constructor. Should only be used by the client. */
public EntityMagicMissile(World world){
super(world);
}
/** Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this
* constructor and then call setVelocity() as that method is, bizarrely, client-side only. */
public EntityMagicMissile(World world, double x, double y, double z)
{
super(world, x, y, z);
}
/** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered
* slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on easy, 6 on
* normal and 2 on hard difficulty. */
public EntityMagicMissile(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier)
{
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile at position xyz in world, with no motion. Do not create a projectile with this constructor
* and then call setVelocity() as that method is, bizarrely, client-side only.
*/
public EntityMagicMissile(World world, double x, double y, double z){
super(world, x, y, z);
}
/** Creates a projectile pointing in the direction the caster is looking, with the given speed.
* USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */
public EntityMagicMissile(World world, EntityLivingBase caster, float speed, float damageMultiplier)
{
super(world, caster, speed, damageMultiplier);
}
/**
* Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be
* altered slightly by a random amount determined by the last parameter. For reference, skeletons set this to 10 on
* easy, 6 on normal and 2 on hard difficulty.
*/
public EntityMagicMissile(World world, EntityLivingBase caster, Entity target, float speed, float aimingError,
float damageMultiplier){
super(world, caster, target, speed, aimingError, damageMultiplier);
}
/**
* Creates a projectile pointing in the direction the caster is looking, with the given speed. USE THIS CONSTRUCTOR
* FOR NORMAL SPELLS.
*/
public EntityMagicMissile(World world, EntityLivingBase caster, float speed, float damageMultiplier){
super(world, caster, speed, damageMultiplier);
}
@Override
public void onEntityHit(EntityLivingBase entityHit){
this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
@Override
public void tickInAir(){
if (this.ticksExisted > 20){
this.setDead();
}
if(this.worldObj.isRemote){
if(this.ticksExisted % 2 == 1){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2));
}
else{
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2));
}
}
}
@Override
public double getDamage(){
return 4.0d;
}
@Override
public boolean doGravity(){
return false;
}
@Override
public boolean doDeceleration(){
return false;
}
if(this.ticksExisted > 20){
this.setDead();
}
if(this.world.isRemote){
if(this.ticksExisted % 2 == 1){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, this.posX, this.posY, this.posZ, 0, 0,
0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat() / 2), 0.5f + (rand.nextFloat() / 2),
0.5f + (rand.nextFloat() / 2));
}else{
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, this.posX, this.posY, this.posZ, 0, 0,
0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat() / 2), 0.5f + (rand.nextFloat() / 2),
0.5f + (rand.nextFloat() / 2));
}
}
}
@Override
protected void entityInit() {
public double getDamage(){
return 4.0d;
}
@Override
public boolean doGravity(){
return false;
}
@Override
public boolean doDeceleration(){
return false;
}
@Override
protected void entityInit(){
}
}
@@ -7,15 +7,18 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
/** This class is a generic superclass for all <b>non-directed</b> projectiles, namely: darkness orb, firebolt, firebomb,
* force orb, ice charge, lightning disc, poison bomb, spark, spark bomb and thunderbolt. Directed (arrow-like) projectiles
* should instead extend {@link EntityMagicArrow}.
/**
* This class is a generic superclass for all <b>non-directed</b> projectiles, namely: darkness orb, firebolt, firebomb,
* force orb, ice charge, lightning disc, poison bomb, spark, spark bomb and thunderbolt. Directed (arrow-like)
* projectiles should instead extend {@link EntityMagicArrow}.
* <p>
* This class purely handles saving of the damage multiplier; EntityThrowable is pretty well suited to my purposes
* as it is. Range is done via the velocity when the constructor is called. Caster is already handled by EntityThrowable.getThrower().
* This class purely handles saving of the damage multiplier; EntityThrowable is pretty well suited to my purposes as it
* is. Range is done via the velocity when the constructor is called. Caster is already handled by
* EntityThrowable.getThrower().
*
* Note that this class does not implement {@link IEntityAdditionalSpawnData}; subclasses that need to transfer extra
* data to the client should implement that interface themselves. See {@link EntityBomb} for an example.
*
* Note that this class does not implement {@link IEntityAdditionalSpawnData}; subclasses that need to transfer extra data
* to the client should implement that interface themselves. See {@link EntityBomb} for an example.
* @since Wizardry 1.0
* @author Electroblob
* @see EntityBomb
@@ -23,51 +26,48 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
public abstract class EntityMagicProjectile extends EntityThrowable {
public float damageMultiplier = 1.0f;
public EntityMagicProjectile(World world)
{
super(world);
}
public EntityMagicProjectile(World world, EntityLivingBase thrower)
{
super(world, thrower);
}
public EntityMagicProjectile(World world, EntityLivingBase thrower, float damageMultiplier)
{
super(world, thrower);
// This is the standard set of parameters for this method, used by snowballs and ender pearls amongst others.
this.setHeadingFromThrower(thrower, thrower.rotationPitch, thrower.rotationYaw, 0.0f, this.getSpeed(), 1.0f);
this.damageMultiplier = damageMultiplier;
}
public EntityMagicProjectile(World world){
super(world);
}
public EntityMagicProjectile(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
/** This got removed at some point since 1.7.10, but I liked it so I thought I'd add it back in again. */
protected float getSpeed(){
return 1.5f;
}
/** Sets this projectile's velocity as a normalised vector towards the target. */
public void directTowards(Entity target, float velocity){
public EntityMagicProjectile(World world, EntityLivingBase thrower){
super(world, thrower);
}
double dx = target.posX - this.posX;
double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F));
double dz = target.posZ - this.posZ;
this.motionX = dx/this.getDistanceToEntity(target) * velocity;
this.motionY = dy/this.getDistanceToEntity(target) * velocity;
this.motionZ = dz/this.getDistanceToEntity(target) * velocity;
}
@Override
public EntityMagicProjectile(World world, EntityLivingBase thrower, float damageMultiplier){
super(world, thrower);
// This is the standard set of parameters for this method, used by snowballs and ender pearls amongst others.
this.setHeadingFromThrower(thrower, thrower.rotationPitch, thrower.rotationYaw, 0.0f, this.getSpeed(), 1.0f);
this.damageMultiplier = damageMultiplier;
}
public EntityMagicProjectile(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
/** This got removed at some point since 1.7.10, but I liked it so I thought I'd add it back in again. */
protected float getSpeed(){
return 1.5f;
}
/** Sets this projectile's velocity as a normalised vector towards the target. */
public void directTowards(Entity target, float velocity){
double dx = target.posX - this.posX;
double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F)
- (this.posY + (double)(this.height / 2.0F));
double dz = target.posZ - this.posZ;
this.motionX = dx / this.getDistanceToEntity(target) * velocity;
this.motionY = dy / this.getDistanceToEntity(target) * velocity;
this.motionZ = dz / this.getDistanceToEntity(target) * velocity;
}
@Override
public void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
damageMultiplier = nbttagcompound.getFloat("damageMultiplier");
super.readEntityFromNBT(nbttagcompound);
damageMultiplier = nbttagcompound.getFloat("damageMultiplier");
}
@Override
@@ -75,5 +75,5 @@ public abstract class EntityMagicProjectile extends EntityThrowable {
super.writeEntityToNBT(nbttagcompound);
nbttagcompound.setFloat("damageMultiplier", damageMultiplier);
}
}
@@ -17,69 +17,80 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntityPoisonBomb extends EntityBomb {
public EntityPoisonBomb(World par1World)
{
super(par1World);
}
public EntityPoisonBomb(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntityPoisonBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
public EntityPoisonBomb(World par1World){
super(par1World);
}
public EntityPoisonBomb(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
public EntityPoisonBomb(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
@Override
protected void onImpact(RayTraceResult par1RayTraceResult)
{
Entity entityHit = par1RayTraceResult.entityHit;
if (entityHit != null)
{
// This is if the poison bomb gets a direct hit
float damage = 5 * damageMultiplier;
public EntityPoisonBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier,
float blastMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(), damage);
if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit)) ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON, 120, 1));
}
public EntityPoisonBomb(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
// Particle effect
if(worldObj.isRemote){
for(int i=0;i<60*blastMultiplier;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 35, 0.2f + rand.nextFloat()*0.3f, 0.6f, 0.0f);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 0.2f + rand.nextFloat()*0.2f, 0.8f, 0.0f);
@Override
protected void onImpact(RayTraceResult par1RayTraceResult){
Entity entityHit = par1RayTraceResult.entityHit;
if(entityHit != null){
// This is if the poison bomb gets a direct hit
float damage = 5 * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(),
damage);
if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit))
((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON, 120, 1));
}
// Particle effect
if(world.isRemote){
for(int i = 0; i < 60 * blastMultiplier; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world,
this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0.0d, 0.0d, 0.0d, 35,
0.2f + rand.nextFloat() * 0.3f, 0.6f, 0.0f);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world,
this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0.0d, 0.0d, 0.0d, 0,
0.2f + rand.nextFloat() * 0.2f, 0.8f, 0.0f);
}
// Spawning this after the other particles fixes the rendering colour bug. It's a bit of a cheat, but it works pretty well.
this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
// Spawning this after the other particles fixes the rendering colour bug. It's a bit of a cheat, but it
// works pretty well.
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
if(!this.worldObj.isRemote){
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.2F, 1.0f);
if(!this.world.isRemote){
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.2F, 1.0f);
double range = 3.0d * blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY,
this.posZ, this.world);
double range = 3.0d*blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY, this.posZ, this.worldObj);
for(EntityLivingBase target : targets){
if(target != entityHit && target != this.getThrower() && !MagicDamage.isEntityImmune(DamageType.POISON, target)){
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON), 4.0f * damageMultiplier);
target.addPotionEffect(new PotionEffect(MobEffects.POISON, 100, 1));
if(target != entityHit && target != this.getThrower()
&& !MagicDamage.isEntityImmune(DamageType.POISON, target)){
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON),
4.0f * damageMultiplier);
target.addPotionEffect(new PotionEffect(MobEffects.POISON, 100, 1));
}
}
this.setDead();
}
}
this.setDead();
}
}
}
@@ -17,49 +17,54 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntitySmokeBomb extends EntityBomb {
public EntitySmokeBomb(World par1World)
{
super(par1World);
}
public EntitySmokeBomb(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntitySmokeBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
public EntitySmokeBomb(World par1World){
super(par1World);
}
public EntitySmokeBomb(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
public EntitySmokeBomb(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
@Override
protected void onImpact(RayTraceResult par1RayTraceResult){
public EntitySmokeBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier,
float blastMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
// Particle effect
if(worldObj.isRemote){
this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
for(int i=0;i<60*blastMultiplier;i++){
this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0);
public EntitySmokeBomb(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
@Override
protected void onImpact(RayTraceResult par1RayTraceResult){
// Particle effect
if(world.isRemote){
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
for(int i = 0; i < 60 * blastMultiplier; i++){
this.world.spawnParticle(EnumParticleTypes.SMOKE_LARGE,
this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0, 0, 0);
float brightness = rand.nextFloat() * 0.3f;
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness, brightness, brightness);
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world,
this.posX + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posY + (this.rand.nextDouble() * 4 - 2) * blastMultiplier,
this.posZ + (this.rand.nextDouble() * 4 - 2) * blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness,
brightness, brightness);
}
}
}
if(!this.worldObj.isRemote){
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.2F, 1.0f);
if(!this.world.isRemote){
this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.2F, 1.0f);
double range = 3.0d * blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY,
this.posZ, this.world);
double range = 3.0d * blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(range, this.posX, this.posY, this.posZ, this.worldObj);
for(EntityLivingBase target : targets){
if(target != this.getThrower()){
// Gives the target blindness if it is a player, mind trick otherwise (since this has the desired
@@ -69,13 +74,13 @@ public class EntitySmokeBomb extends EntityBomb {
}else if(target instanceof EntityLiving){
// New AI
((EntityLiving)target).setAttackTarget(null);
target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, 120, 0));
}
}
}
this.setDead();
}
}
this.setDead();
}
}
}
@@ -14,68 +14,66 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntitySpark extends EntityMagicProjectile {
public EntitySpark(World par1World)
{
super(par1World);
}
public EntitySpark(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntitySpark(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier);
}
public EntitySpark(World par1World){
super(par1World);
}
public EntitySpark(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
/** This is the speed */
protected float getSpeed()
{
return 0.5F;
}
public EntitySpark(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult)
{
Entity entityHit = par1RayTraceResult.entityHit;
if (entityHit != null){
float damage = 6 * damageMultiplier;
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), damage);
}
public EntitySpark(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier);
}
this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
public EntitySpark(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
// Particle effect
if(worldObj.isRemote){
for(int i=0;i<8;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3);
/** This is the speed */
protected float getSpeed(){
return 0.5F;
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult){
Entity entityHit = par1RayTraceResult.entityHit;
if(entityHit != null){
float damage = 6 * damageMultiplier;
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK),
damage);
}
this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
// Particle effect
if(world.isRemote){
for(int i = 0; i < 8; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, this.posX + rand.nextFloat() - 0.5,
this.posY + this.height / 2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0,
0, 3);
}
}
}
this.setDead();
}
public void onUpdate(){
super.onUpdate();
if(!this.isCollided && !worldObj.isRemote){
double seekingRange = 5.0d;
List<EntityLivingBase> entities = WizardryUtilities.getEntitiesWithinRadius(seekingRange, this.posX, this.posY, this.posZ, this.worldObj);
this.setDead();
}
public void onUpdate(){
super.onUpdate();
if(!this.isCollided && !world.isRemote){
double seekingRange = 5.0d;
List<EntityLivingBase> entities = WizardryUtilities.getEntitiesWithinRadius(seekingRange, this.posX,
this.posY, this.posZ, this.world);
Entity target = null;
for(Entity possibleTarget : entities){
@@ -87,30 +85,30 @@ public class EntitySpark extends EntityMagicProjectile {
}
}
}
if(target != null && Math.abs(this.motionX) < 1 && Math.abs(this.motionY) < 1 && Math.abs(this.motionZ) < 1){
this.addVelocity((target.posX - this.posX)/30, (target.posY + target.height/2 - this.posY)/30, (target.posZ - this.posZ)/30);
if(target != null && Math.abs(this.motionX) < 1 && Math.abs(this.motionY) < 1
&& Math.abs(this.motionZ) < 1){
this.addVelocity((target.posX - this.posX) / 30, (target.posY + target.height / 2 - this.posY) / 30,
(target.posZ - this.posZ) / 30);
}
}
if(this.ticksExisted > 100){
this.setDead();
}
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
protected float getGravityVelocity()
{
return 0.0F;
}
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire()
{
return false;
}
}
if(this.ticksExisted > 100){
this.setDead();
}
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
protected float getGravityVelocity(){
return 0.0F;
}
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire(){
return false;
}
}
@@ -19,96 +19,107 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntitySparkBomb extends EntityBomb {
/** For client use, because thrower field is not visible. */
/** For client use, because thrower field is not visible. */
private int casterID;
public EntitySparkBomb(World par1World)
{
super(par1World);
}
public EntitySparkBomb(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntitySparkBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
public EntitySparkBomb(World par1World){
super(par1World);
}
public EntitySparkBomb(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
public EntitySparkBomb(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult)
{
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST_FAR, 0.5f, 0.5f);
public EntitySparkBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier,
float blastMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier);
}
Entity entityHit = par1RayTraceResult.entityHit;
if (entityHit != null)
{
// This is if the spark bomb gets a direct hit
float damage = 6 * damageMultiplier;
this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
public EntitySparkBomb(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(), damage);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult){
this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST_FAR, 0.5f, 0.5f);
// Particle effect
if(worldObj.isRemote){
for(int i=0;i<8;i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3);
worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0);
Entity entityHit = par1RayTraceResult.entityHit;
if(entityHit != null){
// This is if the spark bomb gets a direct hit
float damage = 6 * damageMultiplier;
this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(),
damage);
}
// Particle effect
if(world.isRemote){
for(int i = 0; i < 8; i++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, this.posX + rand.nextFloat() - 0.5,
this.posY + this.height / 2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0,
0, 3);
world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + rand.nextFloat() - 0.5,
this.posY + this.height / 2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0,
0);
}
}
double seekerRange = 5.0d * blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX, this.posY, this.posZ, this.worldObj);
for(int i=0; i<Math.min(targets.size(), 4); i++){
boolean flag = targets.get(i) != entityHit && targets.get(i) != this.getThrower() && !(targets.get(i) instanceof EntityPlayer && ((EntityPlayer)targets.get(i)).capabilities.isCreativeMode);
}
double seekerRange = 5.0d * blastMultiplier;
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX, this.posY,
this.posZ, this.world);
for(int i = 0; i < Math.min(targets.size(), 4); i++){
boolean flag = targets.get(i) != entityHit && targets.get(i) != this.getThrower()
&& !(targets.get(i) instanceof EntityPlayer
&& ((EntityPlayer)targets.get(i)).capabilities.isCreativeMode);
// Detects (client side) if target is the thrower, to stop particles being spawned around them.
if(flag && worldObj.isRemote && targets.get(i).getEntityId() == this.casterID) flag = false;
if(flag && world.isRemote && targets.get(i).getEntityId() == this.casterID) flag = false;
if(flag){
EntityLivingBase target = targets.get(i);
if(!this.worldObj.isRemote){
EntityArc arc = new EntityArc(this.worldObj);
arc.setEndpointCoords(this.posX, this.posY, this.posZ,
target.posX, target.posY + target.height/2, target.posZ);
this.worldObj.spawnEntityInWorld(arc);
if(!this.world.isRemote){
EntityArc arc = new EntityArc(this.world);
arc.setEndpointCoords(this.posX, this.posY, this.posZ, target.posX, target.posY + target.height / 2,
target.posZ);
this.world.spawnEntity(arc);
target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), 5.0f * damageMultiplier);
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK),
5.0f * damageMultiplier);
}else{
// Particle effect
for(int j=0;j<8;j++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, target.posX + rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height*rand.nextFloat(), target.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3);
worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height*rand.nextFloat(), target.posZ + rand.nextFloat() - 0.5, 0, 0, 0);
for(int j = 0; j < 8; j++){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world,
target.posX + rand.nextFloat() - 0.5,
target.getEntityBoundingBox().minY + target.height * rand.nextFloat(),
target.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3);
world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + rand.nextFloat() - 0.5,
target.getEntityBoundingBox().minY + target.height * rand.nextFloat(),
target.posZ + rand.nextFloat() - 0.5, 0, 0, 0);
}
}
}
}
this.setDead();
}
this.setDead();
}
@Override
public void writeSpawnData(ByteBuf data){
@@ -12,89 +12,87 @@ import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
public class EntityThunderbolt extends EntityMagicProjectile {
public EntityThunderbolt(World par1World)
{
super(par1World);
}
public EntityThunderbolt(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntityThunderbolt(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier)
{
super(par1World, par2EntityLivingBase, damageMultiplier);
}
public EntityThunderbolt(World par1World){
super(par1World);
}
public EntityThunderbolt(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
/** This is the speed */
protected float getSpeed()
{
return 2.5F;
}
public EntityThunderbolt(World par1World, EntityLivingBase par2EntityLivingBase){
super(par1World, par2EntityLivingBase);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult)
{
Entity entityHit = par1RayTraceResult.entityHit;
if(entityHit != null){
float damage = 3 * damageMultiplier;
entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(), damage);
// Knockback
entityHit.addVelocity(this.motionX*0.2, this.motionY*0.2, this.motionZ*0.2);
}
public EntityThunderbolt(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier){
super(par1World, par2EntityLivingBase, damageMultiplier);
}
this.playSound(SoundEvents.ENTITY_FIREWORK_LARGE_BLAST, 1.4F, 0.5f + this.rand.nextFloat() * 0.1F);
public EntityThunderbolt(World par1World, double par2, double par4, double par6){
super(par1World, par2, par4, par6);
}
// Particle effect
if(worldObj.isRemote){
worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
/** This is the speed */
protected float getSpeed(){
return 2.5F;
}
this.setDead();
}
public void onUpdate(){
super.onUpdate();
if(worldObj.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0, 3);
for(int i=0; i<4; i++){
worldObj.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0);
}
}
if(this.ticksExisted > 8){
this.setDead();
}
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
protected float getGravityVelocity()
{
return 0.0F;
}
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire()
{
return false;
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(RayTraceResult par1RayTraceResult){
Entity entityHit = par1RayTraceResult.entityHit;
if(entityHit != null){
float damage = 3 * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(),
damage);
// Knockback
entityHit.addVelocity(this.motionX * 0.2, this.motionY * 0.2, this.motionZ * 0.2);
}
this.playSound(SoundEvents.ENTITY_FIREWORK_LARGE_BLAST, 1.4F, 0.5f + this.rand.nextFloat() * 0.1F);
// Particle effect
if(world.isRemote){
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
this.setDead();
}
public void onUpdate(){
super.onUpdate();
if(world.isRemote){
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, this.posX + rand.nextFloat() * 0.2 - 0.1,
this.posY + this.height / 2 + rand.nextFloat() * 0.2 - 0.1,
this.posZ + rand.nextFloat() * 0.2 - 0.1, 0, 0, 0, 3);
for(int i = 0; i < 4; i++){
world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, this.posX + rand.nextFloat() * 0.2 - 0.1,
this.posY + this.height / 2 + rand.nextFloat() * 0.2 - 0.1,
this.posZ + rand.nextFloat() * 0.2 - 0.1, 0, 0, 0);
}
}
if(this.ticksExisted > 8){
this.setDead();
}
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
protected float getGravityVelocity(){
return 0.0F;
}
/**
* Return whether this entity should be rendered as on fire.
*/
public boolean canRenderOnFire(){
return false;
}
}