213 lines
11 KiB
Java
213 lines
11 KiB
Java
package com.atsuishio.superbwarfare.tools;
|
|
|
|
import com.atsuishio.superbwarfare.config.server.ExplosionConfig;
|
|
import com.atsuishio.superbwarfare.network.message.receive.ShakeClientMessage;
|
|
import com.google.common.collect.Sets;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.particles.ParticleOptions;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.item.PrimedTnt;
|
|
import net.minecraft.world.entity.monster.Monster;
|
|
import net.minecraft.world.level.Explosion;
|
|
import net.minecraft.world.level.ExplosionDamageCalculator;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.level.material.Fluids;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.neoforged.neoforge.event.EventHooks;
|
|
|
|
import javax.annotation.Nullable;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
|
|
public class CustomExplosion extends Explosion {
|
|
|
|
private final Level level;
|
|
private final double x;
|
|
private final double y;
|
|
private final double z;
|
|
@Nullable
|
|
private final Entity source;
|
|
private final float radius;
|
|
private final DamageSource damageSource;
|
|
private final ExplosionDamageCalculator damageCalculator;
|
|
private final float damage;
|
|
private int fireTime;
|
|
private float damageMultiplier;
|
|
private boolean ignoreFluids;
|
|
|
|
public CustomExplosion(Level pLevel, @Nullable Entity pSource, @Nullable DamageSource source,
|
|
@Nullable ExplosionDamageCalculator pDamageCalculator,
|
|
float damage, double pToBlowX, double pToBlowY, double pToBlowZ, float pRadius,
|
|
BlockInteraction pBlockInteraction, ParticleOptions smallParticle, ParticleOptions bigParticle, Holder<SoundEvent> sound, boolean ignoreFluids) {
|
|
super(pLevel, pSource, source, null, pToBlowX, pToBlowY, pToBlowZ, pRadius, false, pBlockInteraction, smallParticle, bigParticle, sound);
|
|
|
|
this.level = pLevel;
|
|
this.source = pSource;
|
|
this.radius = pRadius;
|
|
this.damageSource = source == null ? pLevel.damageSources().explosion(this) : source;
|
|
this.damageCalculator = pDamageCalculator == null ? new ExplosionDamageCalculator() : pDamageCalculator;
|
|
this.x = pToBlowX;
|
|
this.y = pToBlowY;
|
|
this.z = pToBlowZ;
|
|
this.damage = damage;
|
|
this.ignoreFluids = ignoreFluids;
|
|
}
|
|
|
|
public CustomExplosion(Level pLevel, @Nullable Entity pSource, @Nullable DamageSource source,
|
|
@Nullable ExplosionDamageCalculator pDamageCalculator,
|
|
float damage, double pToBlowX, double pToBlowY, double pToBlowZ, float pRadius,
|
|
BlockInteraction pBlockInteraction, boolean ignoreFluids) {
|
|
this(pLevel, pSource, source, pDamageCalculator, damage, pToBlowX, pToBlowY, pToBlowZ, pRadius, pBlockInteraction, ParticleTypes.EXPLOSION, ParticleTypes.EXPLOSION_EMITTER, SoundEvents.GENERIC_EXPLODE, ignoreFluids);
|
|
}
|
|
|
|
public CustomExplosion(Level pLevel, @Nullable Entity pSource, float damage, double pToBlowX, double pToBlowY, double pToBlowZ, float pRadius, BlockInteraction pBlockInteraction) {
|
|
this(pLevel, pSource, null, null, damage, pToBlowX, pToBlowY, pToBlowZ, pRadius, pBlockInteraction, false);
|
|
}
|
|
|
|
public CustomExplosion(Level pLevel, @Nullable Entity pSource, @Nullable DamageSource source, float damage, double pToBlowX, double pToBlowY, double pToBlowZ, float pRadius, Explosion.BlockInteraction pBlockInteraction, boolean vanillaExplode) {
|
|
this(pLevel, pSource, source, damage, pToBlowX, pToBlowY, pToBlowZ, pRadius, pBlockInteraction, false, false);
|
|
}
|
|
|
|
public CustomExplosion(Level pLevel, @Nullable Entity pSource, @Nullable DamageSource source, float damage, double pToBlowX, double pToBlowY, double pToBlowZ, float pRadius, Explosion.BlockInteraction pBlockInteraction, boolean vanillaExplode, boolean ignoreFluids) {
|
|
this(pLevel, pSource, source, null, damage, pToBlowX, pToBlowY, pToBlowZ, pRadius, pBlockInteraction, ignoreFluids);
|
|
|
|
if (pLevel instanceof ServerLevel && vanillaExplode) {
|
|
pLevel.explode(source == null ? null : source.getEntity(), pToBlowX, pToBlowY, pToBlowZ, 0.4f * pRadius, ExplosionConfig.EXPLOSION_DESTROY.get() ? Level.ExplosionInteraction.BLOCK : Level.ExplosionInteraction.NONE);
|
|
}
|
|
|
|
ShakeClientMessage.sendToNearbyPlayers(level, pToBlowX, pToBlowY, pToBlowZ, 4 * radius, 20 + 0.02 * damage, 3 * pRadius, 50 + 0.05 * damage);
|
|
}
|
|
|
|
public CustomExplosion(Level pLevel, @Nullable Entity pSource, @Nullable DamageSource source, float damage, double pToBlowX, double pToBlowY, double pToBlowZ, float pRadius, Explosion.BlockInteraction pBlockInteraction) {
|
|
this(pLevel, pSource, source, null, damage, pToBlowX, pToBlowY, pToBlowZ, pRadius, pBlockInteraction, false);
|
|
|
|
ShakeClientMessage.sendToNearbyPlayers(level, pToBlowX, pToBlowY, pToBlowZ, 4 * radius, 5 + 0.02 * damage, 0.75 * pRadius, 4 + 0.02 * damage);
|
|
}
|
|
|
|
public CustomExplosion(Level pLevel, @Nullable Entity pSource, @Nullable DamageSource source, float damage, double pToBlowX, double pToBlowY, double pToBlowZ, float pRadius) {
|
|
this(pLevel, pSource, source, null, damage, pToBlowX, pToBlowY, pToBlowZ, pRadius, BlockInteraction.KEEP, false);
|
|
|
|
ShakeClientMessage.sendToNearbyPlayers(level, pToBlowX, pToBlowY, pToBlowZ, radius, 20 + 0.02 * damage, pRadius, 10 + 0.03 * damage);
|
|
}
|
|
|
|
public CustomExplosion setFireTime(int fireTime) {
|
|
this.fireTime = fireTime;
|
|
return this;
|
|
}
|
|
|
|
public CustomExplosion setDamageMultiplier(float damageMultiplier) {
|
|
this.damageMultiplier = damageMultiplier;
|
|
return this;
|
|
}
|
|
|
|
public CustomExplosion bulletExplode() {
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public void explode() {
|
|
this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z));
|
|
Set<BlockPos> set = Sets.newHashSet();
|
|
|
|
for (int j = 0; j < 16; ++j) {
|
|
for (int k = 0; k < 16; ++k) {
|
|
for (int l = 0; l < 16; ++l) {
|
|
if (j == 0 || j == 15 || k == 0 || k == 15 || l == 0 || l == 15) {
|
|
double d0 = (float) j / 15.0F * 2.0F - 1.0F;
|
|
double d1 = (float) k / 15.0F * 2.0F - 1.0F;
|
|
double d2 = (float) l / 15.0F * 2.0F - 1.0F;
|
|
double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
|
|
d0 /= d3;
|
|
d1 /= d3;
|
|
d2 /= d3;
|
|
float f = this.radius * (0.2F + this.level.random.nextFloat() * 0.15F);
|
|
double d4 = this.x;
|
|
double d6 = this.y;
|
|
double d8 = this.z;
|
|
|
|
for (; f > 0.0F; f -= 0.22500001F) {
|
|
BlockPos blockpos = BlockPos.containing(d4, d6, d8);
|
|
BlockState blockstate = this.level.getBlockState(blockpos);
|
|
FluidState fluidstate = this.level.getFluidState(blockpos);
|
|
if (!this.level.isInWorldBounds(blockpos)) {
|
|
break;
|
|
}
|
|
|
|
Optional<Float> optional;
|
|
if (this.ignoreFluids) {
|
|
optional = this.damageCalculator.getBlockExplosionResistance(this, this.level, blockpos, blockstate, Fluids.EMPTY.defaultFluidState());
|
|
} else {
|
|
optional = this.damageCalculator.getBlockExplosionResistance(this, this.level, blockpos, blockstate, fluidstate);
|
|
}
|
|
if (optional.isPresent()) {
|
|
f -= (optional.get() + 1F) * 0.3F;
|
|
}
|
|
|
|
if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockpos, blockstate, f)) {
|
|
set.add(blockpos);
|
|
}
|
|
|
|
d4 += d0 * (double) 0.3F;
|
|
d6 += d1 * (double) 0.3F;
|
|
d8 += d2 * (double) 0.3F;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.getToBlow().addAll(set);
|
|
|
|
float diameter = this.radius * 2.0F;
|
|
int x0 = Mth.floor(this.x - (double) diameter - 1.0D);
|
|
int x1 = Mth.floor(this.x + (double) diameter + 1.0D);
|
|
int y0 = Mth.floor(this.y - (double) diameter - 1.0D);
|
|
int y1 = Mth.floor(this.y + (double) diameter + 1.0D);
|
|
int z0 = Mth.floor(this.z - (double) diameter - 1.0D);
|
|
int z1 = Mth.floor(this.z + (double) diameter + 1.0D);
|
|
List<Entity> list = this.level.getEntities(this.source, new AABB(x0, y0, z0, x1, y1, z1));
|
|
EventHooks.onExplosionDetonate(this.level, this, list, diameter);
|
|
Vec3 position = new Vec3(this.x, this.y, this.z);
|
|
|
|
for (Entity entity : list) {
|
|
if (!entity.ignoreExplosion(this)) {
|
|
double distanceRate = Math.sqrt(entity.distanceToSqr(position)) / (double) diameter;
|
|
if (distanceRate <= 1.0D) {
|
|
double xDistance = entity.getX() - this.x;
|
|
double yDistance = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.y;
|
|
double zDistance = entity.getZ() - this.z;
|
|
double distance = Math.sqrt(xDistance * xDistance + yDistance * yDistance + zDistance * zDistance);
|
|
|
|
if (distance != 0.0D) {
|
|
double seenPercent = Mth.clamp(getSeenPercent(position, entity), 0.01 * ExplosionConfig.EXPLOSION_PENETRATION_RATIO.get(), Double.POSITIVE_INFINITY);
|
|
double damagePercent = (1.0D - distanceRate) * seenPercent;
|
|
double damageFinal = (damagePercent * damagePercent + damagePercent) / 2.0D * damage;
|
|
|
|
if (entity instanceof Monster monster) {
|
|
monster.hurt(this.damageSource, (float) damageFinal * (1 + 0.2f * this.damageMultiplier));
|
|
} else {
|
|
entity.hurt(this.damageSource, (float) damageFinal);
|
|
}
|
|
entity.invulnerableTime = 1;
|
|
|
|
if (fireTime > 0) {
|
|
entity.setRemainingFireTicks(fireTime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|