Add remnants

This commit is contained in:
Electroblob77
2020-05-21 18:40:01 +01:00
parent 30f2a99a2c
commit 1275f1d31f
21 changed files with 665 additions and 1 deletions
@@ -0,0 +1,332 @@
package electroblob.wizardry.entity.living;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockReceptacle;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.ParticleBuilder;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.*;
import net.minecraft.entity.monster.EntityMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class EntityRemnant extends EntityMob {
/** Data parameter for the remnant's element. */
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityRemnant.class, DataSerializers.VARINT);
/** Data parameter that tracks whether the remnant is currently attacking (charging). */
private static final DataParameter<Boolean> ATTACKING = EntityDataManager.createKey(EntityRemnant.class, DataSerializers.BOOLEAN);
private ResourceLocation lootTable;
@Nullable
private BlockPos boundOrigin;
public EntityRemnant(World world){
super(world);
this.setSize(0.8f, 0.8f);
this.isImmuneToFire = true;
this.moveHelper = new EntityRemnant.AIMoveControl(this);
this.experienceValue = 8;
}
@Override
protected void entityInit(){
super.entityInit();
this.dataManager.register(ELEMENT, 1); // Default to fire
this.dataManager.register(ATTACKING, false); // Default to fire
}
@Override
protected void initEntityAI(){
super.initEntityAI();
this.tasks.addTask(0, new EntityAISwimming(this));
this.tasks.addTask(4, new EntityRemnant.AIChargeAttack());
this.tasks.addTask(8, new EntityRemnant.AIMoveRandom());
this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 3.0F, 1.0F));
this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true, EntityRemnant.class));
this.targetTasks.addTask(3, new EntityAINearestAttackableTarget<>(this, EntityPlayer.class, true));
}
@Override
protected void applyEntityAttributes(){
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(16);
this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(4);
}
@Nullable
@Override
public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata){
this.setElement(Element.values()[1 + rand.nextInt(Element.values().length - 1)]); // Exclude MAGIC
this.setBoundOrigin(new BlockPos(this));
return super.onInitialSpawn(difficulty, livingdata);
}
public Element getElement(){
return Element.values()[this.dataManager.get(ELEMENT)];
}
public void setElement(Element element){
this.dataManager.set(ELEMENT, element.ordinal());
this.lootTable = new ResourceLocation(Wizardry.MODID, "entities/remnant/" + element.getName());
}
public boolean isAttacking(){
return this.dataManager.get(ATTACKING);
}
public void setAttacking(boolean attacking){
this.dataManager.set(ATTACKING, attacking);
}
@Nullable
public BlockPos getBoundOrigin(){
return this.boundOrigin;
}
public void setBoundOrigin(@Nullable BlockPos boundOriginIn){
this.boundOrigin = boundOriginIn;
}
@Override
protected boolean isValidLightLevel(){
return true;
}
@Override
public void onUpdate(){
// Use the same trick as EntityVex to fly through stuff
this.noClip = true;
super.onUpdate();
this.noClip = false;
this.setNoGravity(true);
if(world.isRemote){
Vec3d centre = this.getPositionVector().add(0, height/2, 0);
int[] colours = BlockReceptacle.PARTICLE_COLOURS.get(this.getElement());
if(rand.nextInt(10) == 0){
ParticleBuilder.create(ParticleBuilder.Type.FLASH).entity(this).pos(0, height/2, 0).scale(width).time(48).clr(colours[0]).spawn(world);
}
double r = width/3;
double x = r * (rand.nextDouble() * 2 - 1);
double y = r * (rand.nextDouble() * 2 - 1);
double z = r * (rand.nextDouble() * 2 - 1);
if(this.deathTime > 0){
// Spew out particles on death
for(int i = 0; i < 8; i++){
ParticleBuilder.create(ParticleBuilder.Type.DUST, rand, centre.x + x, centre.y + y, centre.z + z, 0.1, true)
.time(12).clr(colours[1]).fade(colours[2]).spawn(world);
}
}else{
ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(centre.x + x, centre.y + y, centre.z + z)
.vel(x * -0.03, 0.02, z * -0.03).time(24 + rand.nextInt(8)).clr(colours[1]).fade(colours[2]).spawn(world);
}
}
}
@Override
protected SoundEvent getAmbientSound(){
return WizardrySounds.ENTITY_REMNANT_AMBIENT;
}
@Override
protected SoundEvent getDeathSound(){
return WizardrySounds.ENTITY_REMNANT_DEATH;
}
@Override
protected SoundEvent getHurtSound(DamageSource source){
return WizardrySounds.ENTITY_REMNANT_HURT;
}
@Nullable
@Override
protected ResourceLocation getLootTable(){
return lootTable;
}
@Override
public void readEntityFromNBT(NBTTagCompound nbt){
super.readEntityFromNBT(nbt);
this.setElement(Element.values()[nbt.getInteger("Element")]);
if(nbt.hasKey("BoundOrigin")) boundOrigin = NBTUtil.getPosFromTag(nbt.getCompoundTag("BoundOrigin"));
}
@Override
public void writeEntityToNBT(NBTTagCompound nbt){
super.writeEntityToNBT(nbt);
nbt.setInteger("Element", this.getElement().ordinal());
if(boundOrigin != null) nbt.setTag("BoundOrigin", NBTUtil.createPosTag(boundOrigin));
}
// AI classes (copied from EntityVex)
class AIChargeAttack extends EntityAIBase {
public AIChargeAttack(){
this.setMutexBits(1);
}
@Override
public boolean shouldExecute(){
if(EntityRemnant.this.getAttackTarget() != null && !EntityRemnant.this.getMoveHelper().isUpdating() && EntityRemnant.this.rand.nextInt(7) == 0){
return EntityRemnant.this.getDistanceSq(EntityRemnant.this.getAttackTarget()) > 4.0D;
}else{
return false;
}
}
@Override
public boolean shouldContinueExecuting(){
return EntityRemnant.this.getMoveHelper().isUpdating() && EntityRemnant.this.isAttacking() && EntityRemnant.this.getAttackTarget() != null && EntityRemnant.this.getAttackTarget().isEntityAlive();
}
@Override
public void startExecuting(){
EntityLivingBase entitylivingbase = EntityRemnant.this.getAttackTarget();
Vec3d vec3d = entitylivingbase.getPositionEyes(1.0F);
EntityRemnant.this.moveHelper.setMoveTo(vec3d.x, vec3d.y, vec3d.z, 1.0D);
EntityRemnant.this.setAttacking(true);
// EntityRemnant.this.playSound(SoundEvents.ENTITY_VEX_CHARGE, 1.0F, 1.0F);
}
@Override
public void resetTask(){
EntityRemnant.this.setAttacking(false);
}
@Override
public void updateTask(){
EntityLivingBase entitylivingbase = EntityRemnant.this.getAttackTarget();
if(EntityRemnant.this.getEntityBoundingBox().intersects(entitylivingbase.getEntityBoundingBox())){
EntityRemnant.this.attackEntityAsMob(entitylivingbase);
EntityRemnant.this.setAttacking(false);
}else{
double d0 = EntityRemnant.this.getDistanceSq(entitylivingbase);
if(d0 < 9.0D){
Vec3d vec3d = entitylivingbase.getPositionEyes(1.0F);
EntityRemnant.this.moveHelper.setMoveTo(vec3d.x, vec3d.y, vec3d.z, 1.0D);
}
}
}
}
class AIMoveControl extends EntityMoveHelper {
public AIMoveControl(EntityRemnant host){
super(host);
}
@Override
public void onUpdateMoveHelper(){
if(this.action == EntityMoveHelper.Action.MOVE_TO){
double d0 = this.posX - EntityRemnant.this.posX;
double d1 = this.posY - EntityRemnant.this.posY;
double d2 = this.posZ - EntityRemnant.this.posZ;
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
d3 = MathHelper.sqrt(d3);
if(d3 < EntityRemnant.this.getEntityBoundingBox().getAverageEdgeLength()){
this.action = EntityMoveHelper.Action.WAIT;
EntityRemnant.this.motionX *= 0.5D;
EntityRemnant.this.motionY *= 0.5D;
EntityRemnant.this.motionZ *= 0.5D;
}else{
EntityRemnant.this.motionX += d0 / d3 * 0.05D * this.speed;
EntityRemnant.this.motionY += d1 / d3 * 0.05D * this.speed;
EntityRemnant.this.motionZ += d2 / d3 * 0.05D * this.speed;
if(EntityRemnant.this.getAttackTarget() == null){
EntityRemnant.this.rotationYaw = -((float)MathHelper.atan2(EntityRemnant.this.motionX, EntityRemnant.this.motionZ)) * (180F / (float)Math.PI);
}else{
double d4 = EntityRemnant.this.getAttackTarget().posX - EntityRemnant.this.posX;
double d5 = EntityRemnant.this.getAttackTarget().posZ - EntityRemnant.this.posZ;
EntityRemnant.this.rotationYaw = -((float)MathHelper.atan2(d4, d5)) * (180F / (float)Math.PI);
}
EntityRemnant.this.renderYawOffset = EntityRemnant.this.rotationYaw;
}
}
}
}
class AIMoveRandom extends EntityAIBase {
public AIMoveRandom(){
this.setMutexBits(1);
}
@Override
public boolean shouldExecute(){
return !EntityRemnant.this.getMoveHelper().isUpdating() && EntityRemnant.this.rand.nextInt(7) == 0;
}
@Override
public boolean shouldContinueExecuting(){
return false;
}
@Override
public void updateTask(){
BlockPos blockpos = EntityRemnant.this.getBoundOrigin();
if(blockpos == null){
blockpos = new BlockPos(EntityRemnant.this);
}
for(int i = 0; i < 3; ++i){
BlockPos blockpos1 = blockpos.add(EntityRemnant.this.rand.nextInt(15) - 7, EntityRemnant.this.rand.nextInt(11) - 5, EntityRemnant.this.rand.nextInt(15) - 7);
if(EntityRemnant.this.world.isAirBlock(blockpos1)){
EntityRemnant.this.moveHelper.setMoveTo((double)blockpos1.getX() + 0.5D, (double)blockpos1.getY() + 0.5D, (double)blockpos1.getZ() + 0.5D, 0.25D);
if(EntityRemnant.this.getAttackTarget() == null){
EntityRemnant.this.getLookHelper().setLookPosition((double)blockpos1.getX() + 0.5D, (double)blockpos1.getY() + 0.5D, (double)blockpos1.getZ() + 0.5D, 180.0F, 20.0F);
}
break;
}
}
}
}
}