Add pixie

This commit is contained in:
Cheeserolls 2015-04-28 14:21:27 +01:00
parent 1d727ba085
commit 268b90ba12
9 changed files with 495 additions and 2 deletions

View File

@ -0,0 +1,357 @@
/*******************************************************************************
* Copyright 2015, the Biomes O' Plenty Team
*
* This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License.
*
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/.
******************************************************************************/
package biomesoplenty.common.entities;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import biomesoplenty.api.item.BOPItems;
import net.minecraft.entity.EntityFlying;
import net.minecraft.entity.ai.EntityAIBase;
import net.minecraft.entity.ai.EntityMoveHelper;
import net.minecraft.entity.monster.IMob;
import net.minecraft.item.Item;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.MathHelper;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.World;
public class EntityPixie extends EntityFlying implements IMob {
public EntityPixie(World worldIn) {
super(worldIn);
this.setSize(0.7F, 0.7F);
this.moveHelper = new EntityPixie.PixieMoveHelper();
this.tasks.addTask(3, new EntityPixie.AIPixieRandomFly());
}
@Override
protected void applyEntityAttributes()
{
super.applyEntityAttributes();
// TODO: get right value here this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(10.0D);
}
@Override
protected Item getDropItem()
{
return BOPItems.pixie_dust;
}
@Override
public void onLivingUpdate()
{
super.onLivingUpdate();
if (this.worldObj.isRemote)
{
for (int i = 0; i < 7; i++)
{
if (this.rand.nextInt(2)==0)
{
// TODO: add pixie particle BiomesOPlenty.proxy.spawnParticle("pixietrail", this.posX + (this.rand.nextDouble()) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - (double)this.yOffset, this.posZ + (this.rand.nextDouble()) * (double)this.width);
this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - 0.25D, 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, new int[0]);
}
}
}
}
// Checks to make sure the light is not too bright where the mob is spawning
// This is same code as for EntitySkeleton
protected boolean isValidLightLevel()
{
BlockPos blockpos = new BlockPos(this.posX, this.getEntityBoundingBox().minY, this.posZ);
if (this.worldObj.getLightFor(EnumSkyBlock.SKY, blockpos) > this.rand.nextInt(32))
{
// TODO: not sure what's going on here...
return false;
}
else
{
int light = this.worldObj.getLightFromNeighbors(blockpos);
// if it's thundering, force getSkylightSubtracted to 10 before calculating getLightFromNeighbors, then restore it
if (this.worldObj.isThundering())
{
int oldSkyLightSubtracted = this.worldObj.getSkylightSubtracted();
this.worldObj.setSkylightSubtracted(10);
light = this.worldObj.getLightFromNeighbors(blockpos);
this.worldObj.setSkylightSubtracted(oldSkyLightSubtracted);
}
return light <= this.rand.nextInt(8);
}
}
@Override
public boolean getCanSpawnHere()
{
return this.isValidLightLevel() && super.getCanSpawnHere();
}
@Override
protected String getLivingSound()
{
return "biomesoplenty:mob.pixie.say";
}
@Override
protected String getHurtSound()
{
return "biomesoplenty:mob.pixie.hurt";
}
@Override
protected String getDeathSound()
{
return "biomesoplenty:mob.pixie.hurt";
}
// TODO - move PixieMoveTargetPos and AIPixieRandomFly outside and implement in a more generic way, to be reused for pixie and wasp
// Helper class representing a point in space that the pixie is targeting for some reason
class PixieMoveTargetPos
{
private EntityPixie pixie = EntityPixie.this;
public double posX;
public double posY;
public double posZ;
public double distX;
public double distY;
public double distZ;
public double dist;
public double aimX;
public double aimY;
public double aimZ;
public PixieMoveTargetPos()
{
this(0, 0, 0);
}
public PixieMoveTargetPos(double posX, double posY, double posZ)
{
this.setTarget(posX, posY, posZ);
}
public void setTarget(double posX, double posY, double posZ)
{
this.posX = posX;
this.posY = posY;
this.posZ = posZ;
this.refresh();
}
public void refresh()
{
this.distX = this.posX - this.pixie.posX;
this.distY = this.posY - this.pixie.posY;
this.distZ = this.posZ - this.pixie.posZ;
this.dist = (double)MathHelper.sqrt_double(this.distX * this.distX + this.distY * this.distY + this.distZ * this.distZ);
// (aimX,aimY,aimZ) is a unit vector in the direction we want to go
if (this.dist == 0.0D)
{
this.aimX = 0.0D;
this.aimY = 0.0D;
this.aimZ = 0.0D;
}
else
{
this.aimX = this.distX / this.dist;
this.aimY = this.distY / this.dist;
this.aimZ = this.distZ / this.dist;
}
}
public boolean isBoxBlocked(AxisAlignedBB box)
{
return !this.pixie.worldObj.getCollidingBoundingBoxes(this.pixie, box).isEmpty();
}
// check nothing will collide with the pixie in the direction of aim, for howFar units (or until the destination - whichever is closer)
public boolean isPathClear(double howFar)
{
howFar = Math.min(howFar, this.dist);
AxisAlignedBB box = this.pixie.getEntityBoundingBox();
for (double i = 0.5D; i < howFar; ++i)
{
// check there's nothing in the way
if (this.isBoxBlocked(box.offset(this.aimX * i, this.aimY * i, this.aimZ * i)))
{
return false;
}
}
if (this.isBoxBlocked(box.offset(this.aimX * howFar, this.aimY * howFar, this.aimZ * howFar)))
{
return false;
}
return true;
}
}
class PixieMoveHelper extends EntityMoveHelper
{
// EntityMoveHelper has the boolean 'update' which is set to true when the target is changed, and set to false when a bearing is set
// So it means 'the target has changed but we're not yet heading for it'
// We'll re-use it here with a slightly different interpretation
// Here it will mean 'has a target and not yet arrived'
private EntityPixie pixie = EntityPixie.this;
private int courseChangeCooldown = 0;
private double closeEnough = 0.3D;
private PixieMoveTargetPos targetPos = new PixieMoveTargetPos();
public PixieMoveHelper()
{
super(EntityPixie.this);
}
@Override
public void setMoveTo(double x, double y, double z, double speedIn)
{
super.setMoveTo(x,y,z,speedIn);
this.targetPos.setTarget(x, y, z);
}
@Override
public void onUpdateMoveHelper()
{
// if we have arrived at the previous target, or we have no target to aim for, do nothing
if (!this.update) {return;}
if (this.courseChangeCooldown-- > 0) {
// limit the rate at which we change course
return;
}
this.courseChangeCooldown += this.pixie.getRNG().nextInt(2) + 2;
// update the target position
this.targetPos.refresh();
// accelerate the pixie towards the target
double acceleration = 0.1D;
this.pixie.motionX += this.targetPos.aimX * acceleration;
this.pixie.motionY += this.targetPos.aimY * acceleration;
this.pixie.motionZ += this.targetPos.aimZ * acceleration;
// rotate to point at target
this.pixie.renderYawOffset = this.pixie.rotationYaw = -((float)Math.atan2(this.targetPos.distX, this.targetPos.distZ)) * 180.0F / (float)Math.PI;
// abandon this movement if we have reached the target or there is no longer a clear path to the target
if (!this.targetPos.isPathClear(5.0D))
{
//System.out.println("Abandoning move target - way is blocked" );
this.update = false;
} else if (this.targetPos.dist < this.closeEnough) {
//System.out.println("Arrived (close enough) dist:"+this.targetPos.dist);
this.update = false;
}
}
}
// AI class for implementing the random flying behaviour
class AIPixieRandomFly extends EntityAIBase
{
private EntityPixie pixie = EntityPixie.this;
private PixieMoveTargetPos targetPos = new PixieMoveTargetPos();
public AIPixieRandomFly()
{
this.setMutexBits(1);
}
// should we choose a new random destination for the pixie to fly to?
// yes, if the pixie doesn't already have a destination
@Override
public boolean shouldExecute()
{
return !this.pixie.getMoveHelper().isUpdating();
}
@Override
public boolean continueExecuting() {return false;}
// choose a a new random destination for the pixie to fly to
@Override
public void startExecuting()
{
Random rand = this.pixie.getRNG();
// pick a random nearby point and see if we can fly to it
if (this.tryGoingRandomDirection(rand, 6.0D)) {return;}
// pick a random closer point to fly to instead
if (this.tryGoingRandomDirection(rand, 2.0D)) {return;}
// try going straight along axes (try all 6 directions in random order)
List<EnumFacing> directions = Arrays.asList(EnumFacing.values());
Collections.shuffle(directions);
for (EnumFacing facing : directions)
{
if (this.tryGoingAlongAxis(rand, facing, 1.0D)) {return;}
}
}
// note y direction has a slight downward bias to stop them flying too high
public boolean tryGoingRandomDirection(Random rand, double maxDistance)
{
double dirX = ((rand.nextDouble() * 2.0D - 1.0D) * maxDistance);
double dirY = ((rand.nextDouble() * 2.0D - 1.1D) * maxDistance);
double dirZ = ((rand.nextDouble() * 2.0D - 1.0D) * maxDistance);
return this.tryGoing(dirX, dirY, dirZ);
}
public boolean tryGoingAlongAxis(Random rand, EnumFacing facing, double maxDistance)
{
double dirX = 0.0D;
double dirY = 0.0D;
double dirZ = 0.0D;
switch (facing.getAxis())
{
case X:
dirX = rand.nextDouble() * facing.getAxisDirection().getOffset() * maxDistance;
break;
case Y:
dirY = rand.nextDouble() * facing.getAxisDirection().getOffset() * maxDistance;
break;
case Z: default:
dirZ = rand.nextDouble() * facing.getAxisDirection().getOffset() * maxDistance;
break;
}
return this.tryGoing(dirX, dirY, dirZ);
}
public boolean tryGoing(double dirX, double dirY, double dirZ)
{
//System.out.println("("+dirX+","+dirY+","+dirZ+")");
this.targetPos.setTarget(this.pixie.posX + dirX, this.pixie.posY + dirY, this.pixie.posZ + dirZ);
//System.out.println("Testing random move target distance:"+this.targetPos.dist+" direction:("+this.targetPos.aimX+","+this.targetPos.aimY+","+this.targetPos.aimZ+")");
boolean result = this.targetPos.isPathClear(5.0F);
if (result)
{
this.pixie.getMoveHelper().setMoveTo(this.targetPos.posX, this.targetPos.posY, this.targetPos.posZ, 1.0D);
}
return result;
}
}
}

View File

@ -0,0 +1,66 @@
package biomesoplenty.common.entities;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.util.MathHelper;
public class ModelPixie extends ModelBase
{
//fields
ModelRenderer Body;
ModelRenderer LeftWing;
ModelRenderer RightWing;
public ModelPixie()
{
textureWidth = 64;
textureHeight = 32;
Body = new ModelRenderer(this, 0, 0);
Body.addBox(0F, 0F, 0F, 4, 4, 4);
Body.setRotationPoint(-2F, 16F, -2F);
Body.setTextureSize(64, 32);
Body.mirror = true;
setRotation(Body, 0F, 0F, 0F);
LeftWing = new ModelRenderer(this, 32, 0);
LeftWing.addBox(0F, 0F, -1F, 0, 4, 7);
LeftWing.setRotationPoint(2F, 15F, 2F);
LeftWing.setTextureSize(64, 32);
LeftWing.mirror = true;
setRotation(LeftWing, 0F, 0F, 0F);
RightWing = new ModelRenderer(this, 50, 0);
RightWing.addBox(0F, 0F, -1F, 0, 4, 7);
RightWing.setRotationPoint(-2F, 15F, 2F);
RightWing.setTextureSize(64, 32);
RightWing.mirror = true;
setRotation(RightWing, 0F, 0F, 0F);
}
@Override
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5)
{
super.render(entity, f, f1, f2, f3, f4, f5);
setRotationAngles(f, f1, f2, f3, f4, f5, entity);
Body.render(f5);
LeftWing.render(f5);
RightWing.render(f5);
}
private void setRotation(ModelRenderer model, float x, float y, float z)
{
model.rotateAngleX = x;
model.rotateAngleY = y;
model.rotateAngleZ = z;
}
@Override
public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity)
{
super.setRotationAngles(f, f1, f2, f3, f4, f5, entity);
RightWing.rotateAngleY = -(MathHelper.cos(f2 * 1.7F) * (float)Math.PI * 0.5F);
LeftWing.rotateAngleY = MathHelper.cos(f2 * 1.7F) * (float)Math.PI * 0.5F;
}
}

View File

@ -0,0 +1,54 @@
package biomesoplenty.common.entities;
import net.minecraft.client.renderer.entity.RenderLiving;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
@SideOnly(Side.CLIENT)
public class RenderPixie extends RenderLiving
{
private static final ResourceLocation pixieTextureLocation = new ResourceLocation("biomesoplenty:textures/entity/pixie.png");
public RenderPixie(RenderManager renderManager)
{
super(renderManager, new ModelPixie(), 0.25F);
this.shadowSize = 0.0F;
}
@Override
protected ResourceLocation getEntityTexture(Entity entity)
{
return pixieTextureLocation;
}
// TODO: Not sure about all this - Adubbz check please
// Looks like the idea is to set some rendering functions, then call super.doRender, then go back to normal
// LayerEndermanEyes have the same kind of approach I think
@Override
public void doRender(Entity entity, double x, double y, double z, float entityYaw, float partialTicks)
{
GlStateManager.enableBlend();
GlStateManager.disableAlpha();
GlStateManager.blendFunc(1, 1);
GlStateManager.disableLighting();
char c0 = 61680;
int i = c0 % 65536;
int j = c0 / 65536;
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)i / 1.0F, (float)j / 1.0F);
GlStateManager.enableLighting();
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
super.doRender(entity, x, y, z, entityYaw, partialTicks);
GlStateManager.disableBlend();
GlStateManager.enableAlpha();
}
}

View File

@ -8,6 +8,7 @@
package biomesoplenty.common.init;
import biomesoplenty.common.entities.EntityPixie;
import biomesoplenty.common.entities.EntityWasp;
import biomesoplenty.common.entities.projectiles.EntityDart;
import biomesoplenty.core.BiomesOPlenty;
@ -20,10 +21,10 @@ public class ModEntities
{
// TODO: how to set id?
// TODO: why can't we use the summon command on these?
EntityRegistry.registerModEntity(EntityDart.class, "dart", 26, BiomesOPlenty.instance, 80, 3, true);
EntityRegistry.registerModEntity(EntityWasp.class, "wasp", 27, BiomesOPlenty.instance, 80, 3, true);
EntityRegistry.registerModEntity(EntityPixie.class, "pixie", 28, BiomesOPlenty.instance, 80, 3, true);
}
}

View File

@ -22,7 +22,9 @@ import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import biomesoplenty.api.block.IBOPBlock;
import biomesoplenty.common.config.MiscConfigurationHandler;
import biomesoplenty.common.entities.EntityPixie;
import biomesoplenty.common.entities.EntityWasp;
import biomesoplenty.common.entities.RenderPixie;
import biomesoplenty.common.entities.RenderWasp;
import biomesoplenty.common.entities.projectiles.EntityDart;
import biomesoplenty.common.entities.projectiles.RenderDart;
@ -43,6 +45,7 @@ public class ClientProxy extends CommonProxy
//Entity rendering and other stuff will go here in future
RenderingRegistry.registerEntityRenderingHandler(EntityDart.class, new RenderDart(minecraft.getRenderManager()));
RenderingRegistry.registerEntityRenderingHandler(EntityWasp.class, new RenderWasp(minecraft.getRenderManager()));
RenderingRegistry.registerEntityRenderingHandler(EntityPixie.class, new RenderPixie(minecraft.getRenderManager()));
}

View File

@ -1,4 +1,16 @@
{
"mob.pixie.hurt": {
"category": "neutral",
"sounds": [
"mob/pixie/hurt"
]
},
"mob.pixie.say": {
"category": "neutral",
"sounds": [
"mob/pixie/say"
]
},
"mob.wasp.hurt": {
"category": "hostile",
"sounds": [

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B