Add disintegrate animation, plus misc tweaks to disintegration
@@ -692,6 +692,7 @@ public class ClientProxy extends CommonProxy {
|
|||||||
LayerTiledOverlay.initialiseLayers(LayerStone::new);
|
LayerTiledOverlay.initialiseLayers(LayerStone::new);
|
||||||
LayerTiledOverlay.initialiseLayers(LayerFrost::new);
|
LayerTiledOverlay.initialiseLayers(LayerFrost::new);
|
||||||
LayerTiledOverlay.initialiseLayers(LayerSummonAnimation::new);
|
LayerTiledOverlay.initialiseLayers(LayerSummonAnimation::new);
|
||||||
|
LayerTiledOverlay.initialiseLayers(LayerDisintegrateAnimation::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package electroblob.wizardry.client.renderer;
|
||||||
|
|
||||||
|
import electroblob.wizardry.Wizardry;
|
||||||
|
import electroblob.wizardry.spell.Disintegration;
|
||||||
|
import net.minecraft.client.renderer.GlStateManager;
|
||||||
|
import net.minecraft.client.renderer.OpenGlHelper;
|
||||||
|
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||||
|
import net.minecraft.entity.EntityLivingBase;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import net.minecraftforge.client.event.RenderLivingEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||||
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layer used to render the appear/disappear animation for summoned creatures.
|
||||||
|
*
|
||||||
|
* @author Electroblob
|
||||||
|
* @since Wizardry 4.3
|
||||||
|
*/
|
||||||
|
@EventBusSubscriber
|
||||||
|
public class LayerDisintegrateAnimation<T extends EntityLivingBase> extends LayerTiledOverlay<T> {
|
||||||
|
|
||||||
|
private static final int ANIMATION_TICKS = 19; // One less than the max death time
|
||||||
|
|
||||||
|
private static final ResourceLocation[] TEXTURES = new ResourceLocation[ANIMATION_TICKS];
|
||||||
|
|
||||||
|
static {
|
||||||
|
for(int i=0; i<ANIMATION_TICKS; i++){
|
||||||
|
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/disintegrate_overlay/disintegrate_overlay_" + i + ".png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayerDisintegrateAnimation(RenderLivingBase<?> renderer){
|
||||||
|
super(renderer, 32, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRender(T entity, float partialTicks){
|
||||||
|
return entity.getEntityData().hasKey(Disintegration.NBT_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getTexture(T entity, float partialTicks){
|
||||||
|
return TEXTURES[MathHelper.clamp(entity.ticksExisted - entity.getEntityData().getInteger(Disintegration.NBT_KEY), 0, ANIMATION_TICKS - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doRenderLayer(T entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||||
|
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||||
|
GlStateManager.disableLighting();
|
||||||
|
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||||
|
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
|
||||||
|
GlStateManager.enableLighting();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onRenderLivingEvent(RenderLivingEvent.Pre<EntityLivingBase> event){
|
||||||
|
if(event.getEntity().getEntityData().hasKey(Disintegration.NBT_KEY)){
|
||||||
|
event.getEntity().deathTime = 0;
|
||||||
|
event.getEntity().setInvisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
|
||||||
|
if(event.getEntity().getEntityData().hasKey(Disintegration.NBT_KEY)) event.getEntity().setInvisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -39,9 +39,9 @@ public class EntityEmber extends EntityMagicProjectile {
|
|||||||
@Override
|
@Override
|
||||||
protected void onImpact(RayTraceResult result){
|
protected void onImpact(RayTraceResult result){
|
||||||
|
|
||||||
if(result.entityHit != null){
|
// if(result.entityHit != null){
|
||||||
result.entityHit.setFire(Spells.disintegration.getProperty(Spell.BURN_DURATION).intValue());
|
// result.entityHit.setFire(Spells.disintegration.getProperty(Spell.BURN_DURATION).intValue());
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(result.typeOfHit == RayTraceResult.Type.BLOCK){
|
if(result.typeOfHit == RayTraceResult.Type.BLOCK){
|
||||||
this.inGround = true;
|
this.inGround = true;
|
||||||
@@ -60,7 +60,7 @@ public class EntityEmber extends EntityMagicProjectile {
|
|||||||
|
|
||||||
super.applyEntityCollision(entity);
|
super.applyEntityCollision(entity);
|
||||||
|
|
||||||
if(entity instanceof EntityLivingBase){
|
if(entity instanceof EntityLivingBase && ((EntityLivingBase)entity).getHealth() > 0){
|
||||||
entity.setFire(Spells.disintegration.getProperty(Spell.BURN_DURATION).intValue());
|
entity.setFire(Spells.disintegration.getProperty(Spell.BURN_DURATION).intValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,6 +77,7 @@ public class EntityEmber extends EntityMagicProjectile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
world.getEntitiesInAABBexcluding(thrower, this.getEntityBoundingBox(), e -> e instanceof EntityLivingBase)
|
world.getEntitiesInAABBexcluding(thrower, this.getEntityBoundingBox(), e -> e instanceof EntityLivingBase)
|
||||||
|
.stream().filter(e -> !(e instanceof EntityLivingBase) || ((EntityLivingBase)e).getHealth() > 0)
|
||||||
.forEach(e -> e.setFire(Spells.disintegration.getProperty(Spell.BURN_DURATION).intValue()));
|
.forEach(e -> e.setFire(Spells.disintegration.getProperty(Spell.BURN_DURATION).intValue()));
|
||||||
|
|
||||||
// Copied from ParticleLava
|
// Copied from ParticleLava
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ public class Disintegration extends SpellRay {
|
|||||||
public static final String EMBER_COUNT = "ember_count";
|
public static final String EMBER_COUNT = "ember_count";
|
||||||
public static final String EMBER_LIFETIME = "ember_lifetime";
|
public static final String EMBER_LIFETIME = "ember_lifetime";
|
||||||
|
|
||||||
|
public static final String NBT_KEY = "disintegrating";
|
||||||
|
|
||||||
public Disintegration(){
|
public Disintegration(){
|
||||||
super("disintegration", false, SpellActions.POINT);
|
super("disintegration", false, SpellActions.POINT);
|
||||||
addProperties(DAMAGE, BURN_DURATION, EMBER_LIFETIME, EMBER_COUNT);
|
addProperties(DAMAGE, BURN_DURATION, EMBER_LIFETIME, EMBER_COUNT);
|
||||||
@@ -43,7 +45,7 @@ public class Disintegration extends SpellRay {
|
|||||||
MagicDamage.causeDirectMagicDamage(caster, DamageType.FIRE),
|
MagicDamage.causeDirectMagicDamage(caster, DamageType.FIRE),
|
||||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||||
|
|
||||||
if(!world.isRemote && target instanceof EntityLivingBase && ((EntityLivingBase)target).getHealth() <= 0){
|
if(target instanceof EntityLivingBase && ((EntityLivingBase)target).getHealth() <= 0){
|
||||||
spawnEmbers(world, caster, target, getProperty(EMBER_COUNT).intValue());
|
spawnEmbers(world, caster, target, getProperty(EMBER_COUNT).intValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,17 +55,28 @@ public class Disintegration extends SpellRay {
|
|||||||
|
|
||||||
public static void spawnEmbers(World world, EntityLivingBase caster, Entity target, int count){
|
public static void spawnEmbers(World world, EntityLivingBase caster, Entity target, int count){
|
||||||
|
|
||||||
for(int i = 0; i < count; i++){
|
target.extinguish();
|
||||||
EntityEmber ember = new EntityEmber(world, caster);
|
|
||||||
double x = (world.rand.nextDouble() - 0.5) * target.width;
|
if(world.isRemote){ // FIXME: Various syncing issues here!
|
||||||
double y = world.rand.nextDouble() * target.height;
|
// Set NBT client-side, it's only for rendering
|
||||||
double z = (world.rand.nextDouble() - 0.5) * target.width;
|
// Normally dying entities are ignored but we're fiddling with them here so double-check
|
||||||
ember.setPosition(target.posX + x, target.posY + y, target.posZ + z);
|
if(!target.getEntityData().hasKey(NBT_KEY)){
|
||||||
float speed = 0.2f;
|
target.getEntityData().setInteger(NBT_KEY, target.ticksExisted);
|
||||||
ember.motionX = x * speed;
|
}
|
||||||
ember.motionY = y * 0.5f * speed;
|
}else{
|
||||||
ember.motionZ = z * speed;
|
for(int i = 0; i < count; i++){
|
||||||
world.spawnEntity(ember);
|
EntityEmber ember = new EntityEmber(world, caster);
|
||||||
|
double x = (world.rand.nextDouble() - 0.5) * target.width;
|
||||||
|
double y = world.rand.nextDouble() * target.height;
|
||||||
|
double z = (world.rand.nextDouble() - 0.5) * target.width;
|
||||||
|
ember.setPosition(target.posX + x, target.posY + y, target.posZ + z);
|
||||||
|
ember.ticksExisted = world.rand.nextInt(20);
|
||||||
|
float speed = 0.2f;
|
||||||
|
ember.motionX = x * speed;
|
||||||
|
ember.motionY = y * 0.5f * speed;
|
||||||
|
ember.motionZ = z * speed;
|
||||||
|
world.spawnEntity(ember);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,15 +80,17 @@ public final class RayTracer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method for use with {@link RayTracer#rayTrace(World, Vec3d, Vec3d, float, boolean, boolean, boolean, Class, Predicate)}
|
* Helper method for use with {@link RayTracer#rayTrace(World, Vec3d, Vec3d, float, boolean, boolean, boolean, Class, Predicate)}
|
||||||
* which returns a {@link Predicate} that returns true for the given entity, plus any entities that have zero health
|
* which returns a {@link Predicate} that returns true for the given entity, plus any entities that are in the
|
||||||
* or less (i.e. are in the process of dying). This is a commonly used filter in spells.
|
* process of dying. This is a commonly used filter in spells.
|
||||||
*
|
*
|
||||||
* @param entity The entity that the returned predicate should return true for.
|
* @param entity The entity that the returned predicate should return true for.
|
||||||
* @return A {@link Predicate} that returns true for the given entity and any entities that are in the process of
|
* @return A {@link Predicate} that returns true for the given entity and any entities that are in the process of
|
||||||
* dying, false for all other entities.
|
* dying, false for all other entities.
|
||||||
*/
|
*/
|
||||||
public static Predicate<Entity> ignoreEntityFilter(Entity entity){
|
public static Predicate<Entity> ignoreEntityFilter(Entity entity){
|
||||||
return e -> e == entity || (e instanceof EntityLivingBase && ((EntityLivingBase)e).getHealth() <= 0);
|
// Use deathTime > 0 so we still hit stuff that has *just* died, otherwise SpellRay#onEntityHit doesn't get
|
||||||
|
// called on the client side when the entity is killed
|
||||||
|
return e -> e == entity || (e instanceof EntityLivingBase && ((EntityLivingBase)e).deathTime > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.4 KiB |