Files
Wizardry/src/main/java/electroblob/wizardry/entity/construct/EntityBoulder.java
T
Electroblob77 2f91573284 Block protection compat overhaul
- Unifies all block protection checks into helper methods in BlockUtils which trigger all the necessary forge events and vanilla methods
- Implements these checks in all the appropriate places and removes calls to EntityUtils#canDamageBlocks(...) where they are no longer needed
- Changes the mine spell to respect the config settings in line with the rest of the spells
- Fixes earthquake not respecting the settings at all
- Adds a separate config option for dispenser block damage and updates the description for the existing settings to reflect the changes
2020-09-23 00:57:55 +01:00

257 lines
8.6 KiB
Java

package electroblob.wizardry.entity.construct;
import com.google.common.collect.Lists;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Boulder;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.BlockUtils;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.MagicDamage.DamageType;
import io.netty.buffer.ByteBuf;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import java.util.List;
public class EntityBoulder extends EntityScaledConstruct {
private double velX, velZ;
private int hitsRemaining;
private boolean soundStarted = false;
public EntityBoulder(World world){
super(world);
setSize(2.375f, 2.375f);
this.noClip = false;
this.setNoGravity(false);
this.stepHeight = 1;
hitsRemaining = 5;
}
public void setHorizontalVelocity(double velX, double velZ){
this.velX = velX;
this.velZ = velZ;
}
@Override
public void onUpdate(){
if(world.isRemote && !soundStarted && onGround){
soundStarted = true;
Wizardry.proxy.playMovingSound(this, WizardrySounds.ENTITY_BOULDER_ROLL, WizardrySounds.SPELLS, 1, 1, true);
}
super.onUpdate();
this.motionY -= 0.03999999910593033D; // Gravity
this.move(MoverType.SELF, velX, motionY, velZ);
// Entity damage
List<EntityLivingBase> collided = world.getEntitiesWithinAABB(EntityLivingBase.class, this.getEntityBoundingBox());
float damage = Spells.boulder.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier;
float knockback = Spells.boulder.getProperty(Boulder.KNOCKBACK_STRENGTH).floatValue();
for(EntityLivingBase entity : collided){
if(!isValidTarget(entity)) break;
boolean crushBonus = entity.posY < this.posY
&& entity.getEntityBoundingBox().minX > this.getEntityBoundingBox().minX
&& entity.getEntityBoundingBox().maxX < this.getEntityBoundingBox().maxX
&& entity.getEntityBoundingBox().minZ > this.getEntityBoundingBox().minZ
&& entity.getEntityBoundingBox().maxZ < this.getEntityBoundingBox().maxZ;
if(EntityUtils.attackEntityWithoutKnockback(entity, MagicDamage.causeIndirectMagicDamage(this,
getCaster(), DamageType.MAGIC), crushBonus ? damage * 1.5f : damage) && !crushBonus){
// Only knock back if not crushing
EntityUtils.applyStandardKnockback(this, entity, knockback);
entity.motionX += this.motionX;
entity.motionZ += this.motionZ;
}
entity.playSound(WizardrySounds.ENTITY_BOULDER_HIT, 1, 1);
}
// Wall smashing
if(EntityUtils.canDamageBlocks(getCaster(), world) && collidedHorizontally){
AxisAlignedBB box = getEntityBoundingBox().offset(velX, 0, velZ);
List<BlockPos> cuboid = Lists.newArrayList(BlockPos.getAllInBox(MathHelper.floor(box.minX), MathHelper.floor(box.minY),
MathHelper.floor(box.minZ), MathHelper.floor(box.maxX), MathHelper.floor(box.maxY), MathHelper.floor(box.maxZ)));
smashBlocks(cuboid, true);
}
// Trailing particles
for(int i = 0; i < 10; i++){
double particleX = this.posX + width * 0.7 * (rand.nextDouble() - 0.5);
double particleZ = this.posZ + width * 0.7 * (rand.nextDouble() - 0.5);
IBlockState block = world.getBlockState(new BlockPos(this).down());
if(block.getBlock() != Blocks.AIR){
world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, this.posY, particleZ,
0, 0.2, 0, Block.getStateId(block));
}
}
}
/**
* Attempts to smash the given list of blocks. If the boulder has exhausted its hits, or if any of the blocks are
* too hard to be smashed and breakIfTooHard is true, the boulder is destroyed.
* @param blocks A list of block positions at which any solid blocks that are weak enough will be destroyed
* @param breakIfTooHard True if the boulder should break if any of the blocks are too hard, false if it should stop
* on those blocks
* @return True if something was destroyed (either the boulder, one or more blocks, or both), false otherwise
*/
private boolean smashBlocks(List<BlockPos> blocks, boolean breakIfTooHard){
if(blocks.removeIf(p -> world.getBlockState(p).getBlock().getExplosionResistance(world, p, this, null) > 3
|| (!world.isRemote && !BlockUtils.canBreakBlock(getCaster(), world, p)))){
// If any of the blocks were not breakable, the boulder is smashed
if(breakIfTooHard){
this.despawn();
}else{
return false;
}
}else{
if(!world.isRemote){
blocks.forEach(p -> world.destroyBlock(p, false));
if(--hitsRemaining <= 0) this.despawn();
}else{
world.playSound(posX, posY, posZ, WizardrySounds.ENTITY_BOULDER_BREAK_BLOCK, SoundCategory.BLOCKS, 1, 1, false);
}
}
shakeNearbyPlayers();
return true;
}
private void shakeNearbyPlayers(){
EntityUtils.getEntitiesWithinRadius(10, posX, posY, posZ, world, EntityPlayer.class)
.forEach(p -> Wizardry.proxy.shakeScreen(p, 8));
}
@Override
public void despawn(){
if(world.isRemote){
for(int i = 0; i < 200; i++){
double x = posX + (rand.nextDouble() - 0.5) * width;
double y = posY + rand.nextDouble() * height;
double z = posZ + (rand.nextDouble() - 0.5) * width;
world.spawnParticle(EnumParticleTypes.BLOCK_DUST, x, y, z, (x - posX) * 0.1,
(y - posY + height / 2) * 0.1, (z - posZ) * 0.1, Block.getStateId(Blocks.DIRT.getDefaultState()));
}
world.playSound(posX, posY, posZ, WizardrySounds.ENTITY_BOULDER_BREAK_BLOCK, SoundCategory.BLOCKS, 1, 1, false);
}
super.despawn();
}
@Override
public void move(MoverType type, double x, double y, double z){
super.move(type, x, y, z);
this.rotationPitch += Math.toDegrees(MathHelper.sqrt(x*x + y*y + z*z) / (width/2)); // That's how we roll
}
@Override
public void fall(float distance, float damageMultiplier){
super.fall(distance, damageMultiplier);
// Floor smashing
if(EntityUtils.canDamageBlocks(getCaster(), world) && distance > 3){
AxisAlignedBB box = getEntityBoundingBox().offset(velX, motionY, velZ);
List<BlockPos> cuboid = Lists.newArrayList(BlockPos.getAllInBox(MathHelper.floor(box.minX), MathHelper.floor(box.minY),
MathHelper.floor(box.minZ), MathHelper.floor(box.maxX), MathHelper.floor(box.maxY), MathHelper.floor(box.maxZ)));
if(smashBlocks(cuboid, distance > 8)) return;
hitsRemaining--;
}
if(world.isRemote){
// Landing particles
for(int i = 0; i < 40; i++){
double particleX = this.posX - 1.5 + 3 * rand.nextDouble();
double particleZ = this.posZ - 1.5 + 3 * rand.nextDouble();
// Roundabout way of getting a block instance for the block the boulder is standing on (if any).
IBlockState block = world.getBlockState(new BlockPos(this.posX, this.posY - 2, this.posZ));
if(block.getBlock() != Blocks.AIR){
world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, this.posY, particleZ,
particleX - this.posX, 0, particleZ - this.posZ, Block.getStateId(block));
}
}
// Other landing effects
if(distance > 1.2){
world.playSound(posX, posY, posZ, WizardrySounds.ENTITY_BOULDER_LAND, SoundCategory.BLOCKS, Math.min(2, distance / 4), 1, false);
shakeNearbyPlayers();
// EntityUtils.getEntitiesWithinRadius(Math.min(12, distance * 2), posX, posY, posZ, world, EntityPlayer.class)
// .forEach(p -> Wizardry.proxy.shakeScreen(p, Math.min(12, distance * 2)));
}
}
}
@Override
public boolean canBeCollidedWith(){
return true;
}
@Override
public AxisAlignedBB getCollisionBoundingBox(){
return this.getEntityBoundingBox();
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbt){
super.readEntityFromNBT(nbt);
velX = nbt.getDouble("velX");
velZ = nbt.getDouble("velZ");
hitsRemaining = nbt.getInteger("hitsRemaining");
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt){
super.writeEntityToNBT(nbt);
nbt.setDouble("velX", velX);
nbt.setDouble("velZ", velZ);
nbt.setInteger("hitsRemaining", hitsRemaining);
}
@Override
public void writeSpawnData(ByteBuf data){
super.writeSpawnData(data);
data.writeDouble(velX);
data.writeDouble(velZ);
}
@Override
public void readSpawnData(ByteBuf data){
super.readSpawnData(data);
this.velX = data.readDouble();
this.velZ = data.readDouble();
}
}