diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 0ce0099f..7e9545dd 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -20,6 +20,7 @@ import electroblob.wizardry.client.model.ModelSageArmour; import electroblob.wizardry.client.model.ModelWizardArmour; import electroblob.wizardry.client.particle.*; import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFactory; +import electroblob.wizardry.client.renderer.RenderSpectralGolem; import electroblob.wizardry.client.renderer.entity.*; import electroblob.wizardry.client.renderer.entity.layers.*; import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect; @@ -771,6 +772,7 @@ public class ClientProxy extends CommonProxy { RenderingRegistry.registerEntityRenderingHandler(EntityIceGiant.class, RenderIceGiant::new); RenderingRegistry.registerEntityRenderingHandler(EntityPhoenix.class, RenderPhoenix::new); + RenderingRegistry.registerEntityRenderingHandler(EntitySpectralGolem.class, RenderSpectralGolem::new); // Projectiles RenderingRegistry.registerEntityRenderingHandler(EntityMagicMissile.class, manager -> new RenderMagicArrow(manager, diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderSpectralGolem.java b/src/main/java/electroblob/wizardry/client/renderer/RenderSpectralGolem.java new file mode 100644 index 00000000..fabb514a --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderSpectralGolem.java @@ -0,0 +1,52 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.Wizardry; +import net.minecraft.client.model.ModelIronGolem; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.entity.monster.EntityIronGolem; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import org.lwjgl.opengl.GL11; + +@SideOnly(Side.CLIENT) +public class RenderSpectralGolem extends RenderLiving +{ + private static final ResourceLocation SPECTRAL_GOLEM_TEXTURES = new ResourceLocation(Wizardry.MODID, "textures/entity/spectral_golem.png"); + + public RenderSpectralGolem(RenderManager renderManagerIn) + { + super(renderManagerIn, new ModelIronGolem(), 0.5F); + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(EntityIronGolem entity) + { + return SPECTRAL_GOLEM_TEXTURES; + } + + @Override + protected void preRenderCallback(EntityIronGolem entity, float partialTickTime){ + super.preRenderCallback(entity, partialTickTime); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.color(1, 1, 1, 0.7f); + } + + protected void applyRotations(EntityIronGolem entityLiving, float ageInTicks, float rotationYaw, float partialTicks) + { + super.applyRotations(entityLiving, ageInTicks, rotationYaw, partialTicks); + + if ((double)entityLiving.limbSwingAmount >= 0.01D) + { + float f = 13.0F; + float f1 = entityLiving.limbSwing - entityLiving.limbSwingAmount * (1.0F - partialTicks) + 6.0F; + float f2 = (Math.abs(f1 % 13.0F - 6.5F) - 3.25F) / 3.25F; + GlStateManager.rotate(6.5F * f2, 0.0F, 0.0F, 1.0F); + } + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySpectralGolem.java b/src/main/java/electroblob/wizardry/entity/living/EntitySpectralGolem.java new file mode 100644 index 00000000..0176e1ad --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySpectralGolem.java @@ -0,0 +1,192 @@ +package electroblob.wizardry.entity.living; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.util.ParticleBuilder; +import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIAttackMelee; +import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookAtVillager; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAIMoveThroughVillage; +import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; +import net.minecraft.entity.ai.EntityAIMoveTowardsTarget; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; +import net.minecraft.entity.ai.EntityAIWanderAvoidWater; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.monster.EntityIronGolem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +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.EnumDifficulty; +import net.minecraft.world.World; + +import java.util.UUID; + +public class EntitySpectralGolem extends EntityIronGolem implements ISummonedCreature { + + private static final DataParameter SPAWN_PARTICLES = EntityDataManager.createKey(EntitySpectralGolem.class, DataSerializers.BOOLEAN); + + // Field implementations + private int lifetime = -1; + private UUID casterUUID; + + // Setter + getter implementations + @Override public int getLifetime(){ return lifetime; } + @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } + @Override public UUID getOwnerId(){ return casterUUID; } + @Override public void setOwnerId(UUID uuid){ this.casterUUID = uuid; } + + /** Creates a new golem minion in the given world. */ + public EntitySpectralGolem(World world){ + super(world); + this.experienceValue = 0; + } + + @Override + protected void entityInit(){ + super.entityInit(); + this.dataManager.register(SPAWN_PARTICLES, true); + } + + @Override + protected void initEntityAI() { + this.tasks.addTask(1, new EntityAIAttackMelee(this, 1.0D, true)); + this.tasks.addTask(2, new EntityAIMoveTowardsTarget(this, 0.9D, 32.0F)); + this.tasks.addTask(3, new EntityAIMoveThroughVillage(this, 0.6D, true)); + this.tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D)); + this.tasks.addTask(5, new EntityAILookAtVillager(this)); + this.tasks.addTask(6, new EntityAIWanderAvoidWater(this, 0.6D)); + this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F)); + this.tasks.addTask(8, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<>(this, EntityLivingBase.class, + 0, false, true, this.getTargetSelector())); + } + + @Override protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty){} // They don't have equipment! + + // Implementations + + @Override + public void setRevengeTarget(EntityLivingBase entity){ + if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + } + + @Override + public void onUpdate(){ + super.onUpdate(); + this.updateDelegate(); + + // Adds a dust particle effect + if(this.world.isRemote){ + double x = this.posX - this.width / 2 + this.rand.nextFloat() * width; + double y = this.posY + this.height * this.rand.nextFloat() + 0.2f; + double z = this.posZ - this.width / 2 + this.rand.nextFloat() * width; + ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(x, y, z).clr(0.7f, 0.9f, 1).shaded(true).spawn(world); + } + + } + + @Override + public void onSpawn(){ + if(this.dataManager.get(SPAWN_PARTICLES)) 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() - 0.5f, + this.posY + this.rand.nextFloat() * 2, this.posZ + this.rand.nextFloat() - 0.5f, 0, 0, 0); + } + } + } + + @Override + public boolean hasParticleEffect(){ + return true; + } + + @Override + public boolean hasAnimation(){ + return this.dataManager.get(SPAWN_PARTICLES) || this.ticksExisted > 20; + } + + public void hideParticles(){ + this.dataManager.set(SPAWN_PARTICLES, false); + } + + @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.readNBTDelegate(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 getCaster() == null && getOwnerId() == null; + } + + @Override + public boolean getCanSpawnHere(){ + return this.world.getDifficulty() != EnumDifficulty.PEACEFUL; + } + + @Override + public boolean canAttackClass(Class 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(), + 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.summonedCreatureNames && getCaster() != null; + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java index 2e7a544b..69043db9 100644 --- a/src/main/java/electroblob/wizardry/registry/Spells.java +++ b/src/main/java/electroblob/wizardry/registry/Spells.java @@ -378,7 +378,8 @@ public final class Spells { registry.register(new SpellMinion<>("silverfish_swarm", EntitySilverfishMinion::new).soundValues(1, 1.1f, 0.1f)); registry.register(new SpellConstructRanged<>("black_hole", EntityBlackHole::new, false).addProperties(Spell.EFFECT_RADIUS).soundValues(2, 0.7f, 0)); registry.register(new Shockwave()); - registry.register(new SummonIronGolem()); + registry.register(new SpellMinion<>("summon_iron_golem", EntitySpectralGolem::new).soundValues(1, 1.1f, 0.1f)); + registry.register(new ArrowRain()); registry.register(new SpellBuff("diamondflesh", 0.1f, 0.7f, 1, () -> WizardryPotions.diamondflesh).soundValues(0.7f, 1.2f, 0.4f)); registry.register(new SpellBuff("font_of_vitality", 1, 0.8f, 0.3f, () -> MobEffects.ABSORPTION, () -> MobEffects.REGENERATION).soundValues(0.7f, 1.2f, 0.4f)); diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java index 25e7d9e6..eac1b5ea 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java @@ -88,6 +88,7 @@ public class WizardryEntities { registry.register(createEntry(EntitySpiritHorse.class, "spirit_horse", TrackingType.LIVING).egg(0x5464c6, 0xbcc2e8).build()); registry.register(createEntry(EntityPhoenix.class, "phoenix", TrackingType.LIVING).egg(0xff4900, 0xfde535).build()); registry.register(createEntry(EntityIceGiant.class, "ice_giant", TrackingType.LIVING).egg(0x5bacd9, 0xeffaff).build()); + registry.register(createEntry(EntitySpectralGolem.class, "spectral_golem", TrackingType.LIVING).egg(0x5bacd9, 0xeffaff).build()); registry.register(createEntry(EntityMagicSlime.class, "magic_slime", TrackingType.LIVING).build()); registry.register(createEntry(EntityDecoy.class, "decoy", TrackingType.LIVING).build()); diff --git a/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java b/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java deleted file mode 100644 index a93c57b8..00000000 --- a/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java +++ /dev/null @@ -1,51 +0,0 @@ -package electroblob.wizardry.spell; - -import electroblob.wizardry.item.SpellActions; -import electroblob.wizardry.util.BlockUtils; -import electroblob.wizardry.util.ParticleBuilder; -import electroblob.wizardry.util.ParticleBuilder.Type; -import electroblob.wizardry.util.SpellModifiers; -import net.minecraft.entity.monster.EntityIronGolem; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; - -public class SummonIronGolem extends Spell { - - public SummonIronGolem(){ - super("summon_iron_golem", SpellActions.SUMMON, false); - addProperties(SpellMinion.SUMMON_RADIUS); - soundValues(1, 1.1f, 0.2f); - } - - @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ - - BlockPos pos = BlockUtils.findNearbyFloorSpace(caster, getProperty(SpellMinion.SUMMON_RADIUS).intValue(), - getProperty(SpellMinion.SUMMON_RADIUS).intValue()); - - if(pos == null) return false; - - if(!world.isRemote){ - - EntityIronGolem golem = new EntityIronGolem(world); - golem.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); - golem.setPlayerCreated(true); - world.spawnEntity(golem); - - }else{ - - for(int i=0; i<10; i++){ - double x = pos.getX() + world.rand.nextDouble() * 2 - 1; - double y = pos.getY() + 0.5 + world.rand.nextDouble(); - double z = pos.getZ() + world.rand.nextDouble() * 2 - 1; - ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).clr(0.6f, 0.6f, 1).spawn(world); - } - } - - this.playSound(world, caster, ticksInUse, -1, modifiers); - return true; - } - -} diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 6e8c8eeb..d356dd20 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -733,6 +733,7 @@ entity.ebwizardry\:evil_wizard.name=Wizard entity.ebwizardry\:decoy.name=Decoy entity.ebwizardry\:vex_minion.name=Vex entity.ebwizardry\:remnant.name=Remnant +entity.ebwizardry\:spectral_golem.name=Spectral Golem entity.ebwizardry\:magic_missile.name=Magic entity.ebwizardry\:arc.name=Magic @@ -1077,7 +1078,7 @@ spell.ebwizardry\:stormcloud=Stormcloud spell.ebwizardry\:summon_blaze=Summon Blaze spell.ebwizardry\:summon_ice_giant=Summon Ice Giant spell.ebwizardry\:summon_ice_wraith=Summon Ice Wraith -spell.ebwizardry\:summon_iron_golem=Summon Iron Golem +spell.ebwizardry\:summon_iron_golem=Summon Spectral Golem spell.ebwizardry\:summon_lightning_wraith=Summon Lightning Wraith spell.ebwizardry\:summon_phoenix=Summon Phoenix spell.ebwizardry\:summon_shadow_wraith=Summon Shadow Wraith diff --git a/src/main/resources/assets/ebwizardry/spells/summon_iron_golem.json b/src/main/resources/assets/ebwizardry/spells/summon_iron_golem.json index fca50afc..06645e1a 100644 --- a/src/main/resources/assets/ebwizardry/spells/summon_iron_golem.json +++ b/src/main/resources/assets/ebwizardry/spells/summon_iron_golem.json @@ -17,6 +17,8 @@ "chargeup": 20, "cooldown": 400, "base_properties": { + "minion_lifetime": 600, + "minion_count": 1, "summon_radius": 2 } } \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/entity/spectral_golem.png b/src/main/resources/assets/ebwizardry/textures/entity/spectral_golem.png new file mode 100644 index 00000000..5a1ea9d3 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/entity/spectral_golem.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/spells/summon_iron_golem.png b/src/main/resources/assets/ebwizardry/textures/spells/summon_iron_golem.png index bcebc77d..ada63583 100644 Binary files a/src/main/resources/assets/ebwizardry/textures/spells/summon_iron_golem.png and b/src/main/resources/assets/ebwizardry/textures/spells/summon_iron_golem.png differ