Add summoned creature appear/disappear animation
This commit is contained in:
@@ -691,6 +691,7 @@ public class ClientProxy extends CommonProxy {
|
||||
public void initialiseLayers(){
|
||||
LayerTiledOverlay.initialiseLayers(LayerStone::new);
|
||||
LayerTiledOverlay.initialiseLayers(LayerFrost::new);
|
||||
LayerTiledOverlay.initialiseLayers(LayerSummonAnimation::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,23 +3,12 @@ package electroblob.wizardry.client.renderer;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.model.ModelPlayer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||
import net.minecraft.client.renderer.entity.RenderPlayer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Layer used to render the frost texture on creatures with the frostbite effect.
|
||||
|
||||
@@ -2,19 +2,12 @@ package electroblob.wizardry.client.renderer;
|
||||
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Layer used to render the stone texture on a petrified creature.
|
||||
@@ -22,6 +15,8 @@ import org.lwjgl.opengl.GL11;
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.2
|
||||
*/
|
||||
// N.B. The rule here is don't restrict the type any more than necessary, so even though we *know* petrified creatures
|
||||
// are always instances of EntityLiving, we don't need any methods/fields specific to EntityLiving so use ELB instead.
|
||||
public class LayerStone extends LayerTiledOverlay<EntityLivingBase> {
|
||||
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation("textures/blocks/stone.png");
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
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 LayerSummonAnimation<T extends EntityLivingBase> extends LayerTiledOverlay<T> {
|
||||
|
||||
private static final int ANIMATION_TICKS = 19;
|
||||
private static final int HIDE_MODEL_TICKS = 9;
|
||||
|
||||
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/summon_overlay/summon_overlay_" + i + ".png");
|
||||
}
|
||||
}
|
||||
|
||||
public LayerSummonAnimation(RenderLivingBase<?> renderer){
|
||||
super(renderer, 32, 32);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(T entity, float partialTicks){
|
||||
return entity instanceof ISummonedCreature && ((ISummonedCreature)entity).hasAnimation()
|
||||
&& getFrameNumber(entity) < ANIMATION_TICKS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(T entity, float partialTicks){
|
||||
return TEXTURES[getFrameNumber(entity)];
|
||||
}
|
||||
|
||||
private int getFrameNumber(T entity){
|
||||
if(!(entity instanceof ISummonedCreature)) throw new IllegalArgumentException("Entity must be an ISummonedCreature");
|
||||
return Math.min(entity.ticksExisted, Math.max(((ISummonedCreature)entity).getLifetime() - entity.ticksExisted - 1, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRenderLayer(T entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||
|
||||
if(!(entity instanceof ISummonedCreature)) return;
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
int colour = ((ISummonedCreature)entity).getAnimationColour((float)getFrameNumber(entity) / ANIMATION_TICKS);
|
||||
GlStateManager.color((colour >> 16 & 255) / 255f, (colour >> 8 & 255) / 255f, (colour & 255) / 255f);
|
||||
|
||||
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderLivingEvent(RenderLivingEvent.Pre<EntityLivingBase> event){
|
||||
if(shouldHideMainModel(event.getEntity())) event.getEntity().setInvisible(true);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
|
||||
if(shouldHideMainModel(event.getEntity())) event.getEntity().setInvisible(false);
|
||||
}
|
||||
|
||||
private static boolean shouldHideMainModel(EntityLivingBase entity){
|
||||
return entity instanceof ISummonedCreature && ((ISummonedCreature)entity).hasAnimation()
|
||||
&& (entity.ticksExisted < HIDE_MODEL_TICKS
|
||||
|| ((ISummonedCreature)entity).getLifetime() - entity.ticksExisted < HIDE_MODEL_TICKS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,10 +30,21 @@ public abstract class LayerTiledOverlay<T extends EntityLivingBase> implements L
|
||||
|
||||
private final RenderLivingBase<?> renderer;
|
||||
|
||||
public LayerTiledOverlay(RenderLivingBase<?> renderer){
|
||||
private final int textureWidth;
|
||||
private final int textureHeight;
|
||||
|
||||
/** Creates a new {@code LayerTiledOverlay} with the given renderer parameters. */
|
||||
public LayerTiledOverlay(RenderLivingBase<?> renderer, int textureWidth, int textureHeight){
|
||||
this.textureWidth = textureWidth;
|
||||
this.textureHeight = textureHeight;
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
/** Creates a new {@code LayerTiledOverlay} with the given renderer. Texture width and height default to 16. */
|
||||
public LayerTiledOverlay(RenderLivingBase<?> renderer){
|
||||
this(renderer, 16, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this layer should be rendered for the given entity, false if not.
|
||||
* @param entity The entity being rendered
|
||||
@@ -60,26 +71,6 @@ public abstract class LayerTiledOverlay<T extends EntityLivingBase> implements L
|
||||
return renderer.getMainModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the texture to use for the overlay. Defaults to 16.
|
||||
* @param entity The entity being rendered
|
||||
* @param partialTicks The current partial tick time
|
||||
* @return The width of the texture in pixels.
|
||||
*/
|
||||
public int getTextureWidth(T entity, float partialTicks){
|
||||
return 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the texture to use for the overlay. Defaults to 16.
|
||||
* @param entity The entity being rendered
|
||||
* @param partialTicks The current partial tick time
|
||||
* @return The height of the texture in pixels.
|
||||
*/
|
||||
public int getTextureHeight(T entity, float partialTicks){
|
||||
return 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether to render the second layer of mob/player skins (which is not an <i>actual</i> render layer, but
|
||||
* part of the model) as part of this layer. Defaults to false.
|
||||
@@ -138,19 +129,16 @@ public abstract class LayerTiledOverlay<T extends EntityLivingBase> implements L
|
||||
|
||||
ModelBase model = getModel(entity, partialTicks);
|
||||
|
||||
int width = getTextureWidth(entity, partialTicks);
|
||||
int height = getTextureHeight(entity, partialTicks);
|
||||
|
||||
// Calculate the scaling required in each direction
|
||||
double scaleX = 1, scaleY = 1;
|
||||
// It's more logical to use the model's texture size, but some classes don't bother setting it properly
|
||||
// (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead.
|
||||
if(model.boxList != null && model.boxList.get(0) != null){
|
||||
scaleX = (double)model.boxList.get(0).textureWidth / width;
|
||||
scaleY = (double)model.boxList.get(0).textureHeight / height;
|
||||
scaleX = (double)model.boxList.get(0).textureWidth / textureWidth;
|
||||
scaleY = (double)model.boxList.get(0).textureHeight / textureHeight;
|
||||
}else{ // Fallback to model fields; should never be needed
|
||||
scaleX = (double)model.textureWidth / width;
|
||||
scaleY = (double)model.textureHeight / height;
|
||||
scaleX = (double)model.textureWidth / textureWidth;
|
||||
scaleY = (double)model.textureHeight / textureHeight;
|
||||
}
|
||||
|
||||
GlStateManager.scale(scaleX, scaleY, 1);
|
||||
@@ -220,13 +208,14 @@ public abstract class LayerTiledOverlay<T extends EntityLivingBase> implements L
|
||||
* @param <T> The type of entity this layer renderer is applicable for
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends EntityLivingBase> void initialiseLayers(Class<T> entityType, Function<RenderLivingBase<T>, LayerRenderer<T>> layerFactory){
|
||||
public static <T extends EntityLivingBase> void initialiseLayers(Class<T> entityType, Function<RenderLivingBase<T>, LayerRenderer<? extends T>> layerFactory){
|
||||
|
||||
for(Class<? extends Entity> c : Minecraft.getMinecraft().getRenderManager().entityRenderMap.keySet()){
|
||||
if(entityType.isAssignableFrom(c)){
|
||||
Render<T> renderer = Minecraft.getMinecraft().getRenderManager().getEntityClassRenderObject(c);
|
||||
if(renderer instanceof RenderLivingBase){ // Should always be true
|
||||
if(renderer instanceof RenderLivingBase<?>){ // Should always be true
|
||||
// For some reason IntelliJ is quite happy with this cast, even without suppress warnings
|
||||
// Instinct suggests Java shouldn't be happy casting to RenderLivingBase<T> but somehow it works
|
||||
((RenderLivingBase<T>)renderer).addLayer(layerFactory.apply((RenderLivingBase<T>)renderer));
|
||||
}
|
||||
}
|
||||
@@ -235,7 +224,7 @@ public abstract class LayerTiledOverlay<T extends EntityLivingBase> implements L
|
||||
// Players have a separate renderer map
|
||||
if(entityType.isAssignableFrom(EntityPlayer.class)){
|
||||
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
|
||||
renderer.addLayer(layerFactory.apply((RenderLivingBase<T>)renderer));
|
||||
renderer.addLayer(layerFactory.apply((RenderLivingBase<T>)renderer)); // This does need suppress warnings
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,7 +234,7 @@ public abstract class LayerTiledOverlay<T extends EntityLivingBase> implements L
|
||||
* {@link LayerTiledOverlay#initialiseLayers(Class, Function)}.
|
||||
* @param layerFactory A function that creates a layer for a given renderer, usually a constructor reference
|
||||
*/
|
||||
public static void initialiseLayers(Function<RenderLivingBase<EntityLivingBase>, LayerRenderer<EntityLivingBase>> layerFactory){
|
||||
public static void initialiseLayers(Function<RenderLivingBase<EntityLivingBase>, LayerRenderer<? extends EntityLivingBase>> layerFactory){
|
||||
initialiseLayers(EntityLivingBase.class, layerFactory);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package electroblob.wizardry.entity.living;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.ai.EntityAIHurtByTarget;
|
||||
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
|
||||
@@ -105,6 +106,11 @@ public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationColour(float animationProgress){
|
||||
return DrawingUtils.mix(0xffdd4d, 0xff6600, animationProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processInteract(EntityPlayer player, EnumHand hand){
|
||||
// In this case, the delegate method determines whether super is called.
|
||||
|
||||
@@ -54,6 +54,11 @@ public class EntityDecoy extends EntitySummonedCreature {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationColour(float animationProgress){
|
||||
return 0xffc600;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntityInvulnerable(DamageSource source){
|
||||
return true;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package electroblob.wizardry.entity.living;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -96,6 +97,11 @@ public class EntityIceGiant extends EntityIronGolem implements ISummonedCreature
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationColour(float animationProgress){
|
||||
return DrawingUtils.mix(0xffffff, 0x73e1ff, animationProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLivingUpdate(){
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package electroblob.wizardry.entity.living;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -61,6 +62,11 @@ public class EntityIceWraith extends EntityBlazeMinion {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationColour(float animationProgress){
|
||||
return DrawingUtils.mix(0xffffff, 0x73e1ff, animationProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLivingUpdate(){
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package electroblob.wizardry.entity.living;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
@@ -49,6 +50,11 @@ public class EntityLightningWraith extends EntityBlazeMinion {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationColour(float animationProgress){
|
||||
return DrawingUtils.mix(0xffffff, 0x0092ff, animationProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLivingUpdate(){
|
||||
// Fortunately, lightning wraiths don't replace any of blazes' particle effects or the fire sound, they only
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package electroblob.wizardry.entity.living;
|
||||
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
@@ -151,6 +152,11 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationColour(float animationProgress){
|
||||
return DrawingUtils.mix(0xffdd4d, 0xff6600, animationProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLivingUpdate(){
|
||||
|
||||
|
||||
@@ -91,6 +91,11 @@ public class EntityVexMinion extends EntityVex implements ISummonedCreature {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimationColour(float animationProgress){
|
||||
return 0xef829c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasParticleEffect(){
|
||||
return true;
|
||||
|
||||
@@ -94,8 +94,8 @@ public class EntityZombieMinion extends EntityZombie implements ISummonedCreatur
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,11 @@ public class EntityZombieMinion extends EntityZombie implements ISummonedCreatur
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnimation(){
|
||||
return this.dataManager.get(SPAWN_PARTICLES) || this.ticksExisted > 20;
|
||||
}
|
||||
|
||||
public void hideParticles(){
|
||||
this.dataManager.set(SPAWN_PARTICLES, false);
|
||||
}
|
||||
|
||||
@@ -239,6 +239,16 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData, IEntityOw
|
||||
/** Whether this creature should spawn a subtle black swirl particle effect while alive. */
|
||||
boolean hasParticleEffect();
|
||||
|
||||
/** Returns whether this creature has an animation when appearing and disappearing. */
|
||||
default boolean hasAnimation(){
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns the colour of this creature's appear/disappear animation. */
|
||||
default int getAnimationColour(float animationProgress){
|
||||
return 0x000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
Reference in New Issue
Block a user