generated from MrSphay/codex-agent-repository-kit
Restore instant crater debris and glow sprites
All checks were successful
Build / build (push) Successful in 9m32s
All checks were successful
Build / build (push) Successful in 9m32s
This commit is contained in:
@@ -1,148 +1,20 @@
|
||||
package com.vinlanx.explosionoverhaul;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Iterator;
|
||||
import java.util.Queue;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class AsyncCraterManager {
|
||||
private static final Queue<CraterJob> JOBS = new ArrayDeque<>();
|
||||
private static final int MAX_SCAN_POSITIONS_PER_TICK = 4096;
|
||||
private static final int MAX_BREAK_EVENTS_PER_TICK = 96;
|
||||
private static final int MAX_DEBRIS_PER_TICK = 4;
|
||||
private static final int MAX_DEBRIS_PER_EXPLOSION = 64;
|
||||
|
||||
public static void submit(ServerLevel level, Vec3 pos, float power) {
|
||||
synchronized (JOBS) {
|
||||
for (CraterJob job : JOBS) {
|
||||
if (job.overlaps(level, pos, power)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
JOBS.add(new CraterJob(level, pos, power));
|
||||
}
|
||||
CraterDeformer.applyInstantCrater(level, pos, power);
|
||||
}
|
||||
|
||||
public static void onServerTick(MinecraftServer server) {
|
||||
int configuredBudget = Math.max(1, (Integer)Config.COMMON.craterApplyBlocksPerTick.get());
|
||||
int budget = Math.max(256, Math.min(configuredBudget, MAX_SCAN_POSITIONS_PER_TICK));
|
||||
synchronized (JOBS) {
|
||||
Iterator<CraterJob> iterator = JOBS.iterator();
|
||||
while (iterator.hasNext() && budget > 0) {
|
||||
CraterJob job = iterator.next();
|
||||
budget = job.apply(budget);
|
||||
if (job.done()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void onLevelUnload(ServerLevel level) {
|
||||
synchronized (JOBS) {
|
||||
JOBS.removeIf(job -> job.level == level);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
synchronized (JOBS) {
|
||||
JOBS.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CraterJob {
|
||||
private final ServerLevel level;
|
||||
private final Vec3 origin;
|
||||
private final float power;
|
||||
private final float radius;
|
||||
private final int minX;
|
||||
private final int maxX;
|
||||
private final int minY;
|
||||
private final int maxY;
|
||||
private final int minZ;
|
||||
private final int maxZ;
|
||||
private final int maxDebris;
|
||||
private int x;
|
||||
private int y;
|
||||
private int z;
|
||||
private int debrisSpawned;
|
||||
|
||||
private CraterJob(ServerLevel level, Vec3 origin, float power) {
|
||||
this.level = level;
|
||||
this.origin = origin;
|
||||
this.power = power;
|
||||
this.radius = CraterDeformer.calculateRadius(power);
|
||||
int scanRadius = CraterDeformer.calculateScanRadius(power);
|
||||
BlockPos center = BlockPos.containing(origin);
|
||||
this.minX = center.getX() - scanRadius;
|
||||
this.maxX = center.getX() + scanRadius;
|
||||
this.minY = Math.max(level.getMinBuildHeight(), center.getY() - scanRadius);
|
||||
this.maxY = Math.min(level.getMaxBuildHeight() - 1, center.getY() + scanRadius);
|
||||
this.minZ = center.getZ() - scanRadius;
|
||||
this.maxZ = center.getZ() + scanRadius;
|
||||
this.x = this.minX;
|
||||
this.y = this.minY;
|
||||
this.z = this.minZ;
|
||||
this.maxDebris = Math.min(MAX_DEBRIS_PER_EXPLOSION, Math.max(4, Math.round(power * 2.5f)));
|
||||
}
|
||||
|
||||
private int apply(int budget) {
|
||||
int breakEvents = 0;
|
||||
int debrisThisTick = 0;
|
||||
int configuredDebris = Math.max(0, (Integer)Config.COMMON.craterMaxFallingBlocksPerTick.get());
|
||||
int debrisBudget = Math.min(configuredDebris, MAX_DEBRIS_PER_TICK);
|
||||
while (!this.done() && budget > 0) {
|
||||
BlockPos pos = new BlockPos(this.x, this.y, this.z);
|
||||
this.advance();
|
||||
--budget;
|
||||
if (!CraterDeformer.shouldDestroyCraterBlock(this.level, pos, this.origin, this.power, this.radius)) {
|
||||
continue;
|
||||
}
|
||||
BlockState state = this.level.getBlockState(pos);
|
||||
if (!state.isAir() && state.getDestroySpeed(this.level, pos) >= 0.0f && !ExplosionOverhaul.isBlockStateBlacklisted(state)) {
|
||||
if (this.debrisSpawned < this.maxDebris && debrisThisTick < debrisBudget && this.level.random.nextFloat() < 0.006f) {
|
||||
CraterDeformer.spawnDebris(this.level, this.origin, this.power, 1);
|
||||
++this.debrisSpawned;
|
||||
++debrisThisTick;
|
||||
}
|
||||
if (breakEvents < MAX_BREAK_EVENTS_PER_TICK && this.level.random.nextFloat() < 0.035f) {
|
||||
this.level.levelEvent(2001, pos, Block.getId(state));
|
||||
++breakEvents;
|
||||
}
|
||||
this.level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
|
||||
}
|
||||
}
|
||||
return budget;
|
||||
}
|
||||
|
||||
private boolean overlaps(ServerLevel level, Vec3 pos, float power) {
|
||||
if (this.level != level || this.done()) {
|
||||
return false;
|
||||
}
|
||||
double threshold = Math.max(3.0, Math.min(this.radius, CraterDeformer.calculateRadius(power)) * 0.45);
|
||||
return this.origin.distanceToSqr(pos) <= threshold * threshold;
|
||||
}
|
||||
|
||||
private void advance() {
|
||||
if (++this.x <= this.maxX) {
|
||||
return;
|
||||
}
|
||||
this.x = this.minX;
|
||||
if (++this.y <= this.maxY) {
|
||||
return;
|
||||
}
|
||||
this.y = this.minY;
|
||||
++this.z;
|
||||
}
|
||||
|
||||
private boolean done() {
|
||||
return this.z > this.maxZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package com.vinlanx.explosionoverhaul;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.item.FallingBlockEntity;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class CraterDeformer {
|
||||
private static final int MAX_BREAK_EVENTS_PER_EXPLOSION = 128;
|
||||
private static final int MAX_DEBRIS_PER_EXPLOSION = 96;
|
||||
|
||||
public static Set<BlockPos> getCraterBlocks(ServerLevel level, Vec3 explosionPos, float power) {
|
||||
Set<BlockPos> blocks = new LinkedHashSet<>();
|
||||
float radius = calculateRadius(power);
|
||||
@@ -64,6 +71,29 @@ public class CraterDeformer {
|
||||
return Math.max(8.0f, power * 6.0f);
|
||||
}
|
||||
|
||||
public static void applyInstantCrater(ServerLevel level, Vec3 explosionPos, float power) {
|
||||
Set<BlockPos> craterBlocks = getCraterBlocks(level, explosionPos, power);
|
||||
int debrisLimit = resolveDebrisLimit(power, defaultDebrisLimit(power));
|
||||
List<DebrisCandidate> debris = sampleDebrisCandidates(level, explosionPos, craterBlocks, debrisLimit);
|
||||
for (DebrisCandidate candidate : debris) {
|
||||
launchDebris(level, explosionPos, power, candidate);
|
||||
}
|
||||
|
||||
int breakEvents = 0;
|
||||
int maxBreakEvents = Math.min(MAX_BREAK_EVENTS_PER_EXPLOSION, Math.max(24, Math.round(power * 5.0f)));
|
||||
for (BlockPos pos : craterBlocks) {
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (state.isAir() || state.getDestroySpeed(level, pos) < 0.0f || ExplosionOverhaul.isBlockStateBlacklisted(state)) {
|
||||
continue;
|
||||
}
|
||||
if (breakEvents < maxBreakEvents && level.random.nextFloat() < 0.035f) {
|
||||
level.levelEvent(2001, pos, Block.getId(state));
|
||||
++breakEvents;
|
||||
}
|
||||
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyLargeExplosionLogic(ServerLevel level, Vec3 explosionPos, float power) {
|
||||
spawnDebris(level, explosionPos, power, Math.min(72, Math.round(power * 3.0f)));
|
||||
}
|
||||
@@ -73,29 +103,97 @@ public class CraterDeformer {
|
||||
}
|
||||
|
||||
public static void spawnDebris(ServerLevel level, Vec3 explosionPos, float power, int maxDebris) {
|
||||
if (!((Boolean)Config.COMMON.enableFallingBlocks.get()).booleanValue() || maxDebris <= 0) {
|
||||
int debrisLimit = resolveDebrisLimit(power, maxDebris);
|
||||
if (debrisLimit <= 0) {
|
||||
return;
|
||||
}
|
||||
float radius = calculateRadius(power);
|
||||
BlockPos origin = BlockPos.containing(explosionPos);
|
||||
int spawned = 0;
|
||||
int attempts = maxDebris * 4;
|
||||
for (int i = 0; i < attempts && spawned < maxDebris; ++i) {
|
||||
int attempts = debrisLimit * 5;
|
||||
for (int i = 0; i < attempts && spawned < debrisLimit; ++i) {
|
||||
double angle = level.random.nextDouble() * Math.PI * 2.0;
|
||||
double distance = radius * (0.25 + level.random.nextDouble() * 0.7);
|
||||
BlockPos pos = origin.offset(Mth.floor(Math.cos(angle) * distance), level.random.nextInt(5) - 1, Mth.floor(Math.sin(angle) * distance));
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (state.isAir() || state.getDestroySpeed(level, pos) < 0.0f || ExplosionOverhaul.isBlockStateBlacklisted(state)) {
|
||||
if (!canLaunchDebris(level, pos, state)) {
|
||||
continue;
|
||||
}
|
||||
FallingBlockEntity falling = FallingBlockEntity.fall(level, pos, state);
|
||||
Vec3 motion = new Vec3(pos.getX() + 0.5 - explosionPos.x(), 0.3 + level.random.nextDouble() * 0.9, pos.getZ() + 0.5 - explosionPos.z()).normalize().scale(0.18 + Math.min(1.4, power * 0.035));
|
||||
falling.setDeltaMovement(motion);
|
||||
falling.time = 1;
|
||||
launchDebris(level, explosionPos, power, new DebrisCandidate(pos.immutable(), state));
|
||||
++spawned;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DebrisCandidate> sampleDebrisCandidates(ServerLevel level, Vec3 explosionPos, Set<BlockPos> craterBlocks, int maxDebris) {
|
||||
List<DebrisCandidate> candidates = new ArrayList<>(Math.max(0, maxDebris));
|
||||
if (maxDebris <= 0) {
|
||||
return candidates;
|
||||
}
|
||||
int seen = 0;
|
||||
for (BlockPos pos : craterBlocks) {
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (!canLaunchDebris(level, pos, state)) {
|
||||
continue;
|
||||
}
|
||||
double distance = Math.sqrt(pos.distToCenterSqr(explosionPos));
|
||||
if (distance < 1.5 || level.random.nextFloat() < 0.22f) {
|
||||
continue;
|
||||
}
|
||||
DebrisCandidate candidate = new DebrisCandidate(pos.immutable(), state);
|
||||
++seen;
|
||||
if (candidates.size() < maxDebris) {
|
||||
candidates.add(candidate);
|
||||
} else {
|
||||
int replacement = level.random.nextInt(seen);
|
||||
if (replacement < maxDebris) {
|
||||
candidates.set(replacement, candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private static boolean canLaunchDebris(ServerLevel level, BlockPos pos, BlockState state) {
|
||||
return !state.isAir()
|
||||
&& state.getDestroySpeed(level, pos) >= 0.0f
|
||||
&& state.getFluidState().isEmpty()
|
||||
&& !ExplosionOverhaul.isBlockStateBlacklisted(state);
|
||||
}
|
||||
|
||||
private static void launchDebris(ServerLevel level, Vec3 explosionPos, float power, DebrisCandidate candidate) {
|
||||
BlockState state = level.getBlockState(candidate.pos());
|
||||
if (state.isAir() || !state.is(candidate.state().getBlock())) {
|
||||
return;
|
||||
}
|
||||
FallingBlockEntity falling = FallingBlockEntity.fall(level, candidate.pos(), candidate.state());
|
||||
Vec3 offset = Vec3.atCenterOf(candidate.pos()).subtract(explosionPos);
|
||||
Vec3 horizontal = new Vec3(offset.x, 0.0, offset.z);
|
||||
if (horizontal.lengthSqr() < 0.0001) {
|
||||
double angle = level.random.nextDouble() * Math.PI * 2.0;
|
||||
horizontal = new Vec3(Math.cos(angle), 0.0, Math.sin(angle));
|
||||
}
|
||||
double horizontalSpeed = 0.22 + Math.min(1.45, power * 0.045) + level.random.nextDouble() * 0.45;
|
||||
double verticalSpeed = 0.32 + Math.min(0.75, power * 0.02) + level.random.nextDouble() * 0.65;
|
||||
falling.setDeltaMovement(horizontal.normalize().scale(horizontalSpeed).add(0.0, verticalSpeed, 0.0));
|
||||
falling.time = 1;
|
||||
}
|
||||
|
||||
private static int defaultDebrisLimit(float power) {
|
||||
return power >= 12.0f ? Math.min(72, Math.round(power * 3.0f)) : Math.min(24, Math.round(power * 3.0f));
|
||||
}
|
||||
|
||||
private static int resolveDebrisLimit(float power, int requestedLimit) {
|
||||
if (!((Boolean)Config.COMMON.enableFallingBlocks.get()).booleanValue() || requestedLimit <= 0) {
|
||||
return 0;
|
||||
}
|
||||
int configuredLimit = Math.max(0, (Integer)Config.COMMON.craterMaxFallingBlocksPerTick.get());
|
||||
if (configuredLimit <= 0) {
|
||||
return 0;
|
||||
}
|
||||
int visibleLimit = Math.max(requestedLimit, Math.round(power * 4.0f));
|
||||
return Math.min(MAX_DEBRIS_PER_EXPLOSION, Math.max(configuredLimit, visibleLimit));
|
||||
}
|
||||
|
||||
private static double roughness(BlockPos pos, Vec3 origin, float power) {
|
||||
long seed = pos.asLong() ^ Double.doubleToLongBits(origin.x()) ^ (Double.doubleToLongBits(origin.z()) << 1) ^ (long)(power * 1000.0f);
|
||||
seed ^= seed >>> 33;
|
||||
@@ -105,4 +203,7 @@ public class CraterDeformer {
|
||||
seed ^= seed >>> 33;
|
||||
return (seed & 0xFFFF) / 65535.0;
|
||||
}
|
||||
|
||||
private record DebrisCandidate(BlockPos pos, BlockState state) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,13 @@ public class CustomGlowParticle extends TextureSheetParticle {
|
||||
Config.Client.ParticleRenderMode mode = Config.CLIENT.particleRenderMode.get();
|
||||
float configScale = ((Double)Config.CLIENT.particleSizeScale.get()).floatValue();
|
||||
float powerScale = Mth.clamp(options.getPower() / 4.0f, 0.8f, 4.0f);
|
||||
this.baseSize = options.getScale() * configScale * powerScale * (0.85f + this.random.nextFloat() * 0.35f);
|
||||
float requestedSize = options.getScale() * configScale * powerScale * (0.85f + this.random.nextFloat() * 0.35f);
|
||||
float maxSize = switch (mode) {
|
||||
case REALISTIC -> 5.0f;
|
||||
case REALISTIC_2 -> 5.6f;
|
||||
case VANILA -> 3.8f;
|
||||
};
|
||||
this.baseSize = Mth.clamp(requestedSize, 0.2f, maxSize);
|
||||
this.lifetime = switch (mode) {
|
||||
case REALISTIC -> 34 + this.random.nextInt(15);
|
||||
case REALISTIC_2 -> 44 + this.random.nextInt(22);
|
||||
@@ -49,7 +55,7 @@ public class CustomGlowParticle extends TextureSheetParticle {
|
||||
}
|
||||
float progress = this.age / (float)this.lifetime;
|
||||
this.alpha = Mth.clamp(1.0f - progress, 0.0f, 1.0f) * 0.92f;
|
||||
this.quadSize = this.baseSize * (0.8f + progress * 1.8f);
|
||||
this.quadSize = this.baseSize * (0.85f + progress * 1.15f);
|
||||
}
|
||||
|
||||
public boolean shouldCull() {
|
||||
|
||||
@@ -18,7 +18,8 @@ public class SmokeParticle extends TextureSheetParticle {
|
||||
super(level, x, y, z, xSpeed, ySpeed, zSpeed);
|
||||
this.sprites = sprites;
|
||||
this.lifetime = Math.max(6, options.getLifetime());
|
||||
this.startSize = options.getScale() * (options.isHeavy() ? 1.45f : 1.0f);
|
||||
float requestedSize = options.getScale() * (options.isHeavy() ? 1.25f : 1.0f);
|
||||
this.startSize = Mth.clamp(requestedSize, 0.12f, options.isHeavy() ? 8.0f : 5.5f);
|
||||
this.startAlpha = options.getAlpha();
|
||||
this.quadSize = this.startSize;
|
||||
this.rCol = options.getRed();
|
||||
@@ -51,7 +52,7 @@ public class SmokeParticle extends TextureSheetParticle {
|
||||
}
|
||||
float progress = this.age / (float)this.lifetime;
|
||||
this.setSpriteFromAge(this.sprites);
|
||||
this.quadSize = this.startSize * (1.0f + progress * 1.6f);
|
||||
this.quadSize = Math.min(this.startSize + 5.5f, this.startSize * (1.0f + progress * 1.25f));
|
||||
this.alpha = this.startAlpha * Mth.clamp(1.0f - progress, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"textures": [
|
||||
"explosionoverhaul:plasma",
|
||||
"explosionoverhaul:plasma_e"
|
||||
"explosionoverhaul:soft_glow",
|
||||
"explosionoverhaul:soft_glow_e"
|
||||
]
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Reference in New Issue
Block a user