diff --git a/src/main/java/com/vinlanx/explosionoverhaul/DripstoneEffects.java b/src/main/java/com/vinlanx/explosionoverhaul/DripstoneEffects.java index 7272eac..67d3a03 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/DripstoneEffects.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/DripstoneEffects.java @@ -3,15 +3,37 @@ package com.vinlanx.explosionoverhaul; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; public class DripstoneEffects { public static void handleDripstoneFall(ServerLevel level, BlockPos center, int explosionPower, RandomSource random) { + if (!((Boolean)Config.COMMON.enableDripstoneFalling.get()).booleanValue()) { + return; + } + int radius = Math.min((Integer)Config.COMMON.dripstoneFallingSearchRadius.get(), Math.max(8, explosionPower * 4)); + int fallPercent = getFallPercent(explosionPower); + int changed = 0; + for (BlockPos pos : BlockPos.betweenClosed(center.offset(-radius, -radius, -radius), center.offset(radius, radius, radius))) { + if (changed >= 400) { + break; + } + BlockState state = level.getBlockState(pos); + if (!state.is(Blocks.POINTED_DRIPSTONE) || random.nextInt(100) >= fallPercent) { + continue; + } + BlockPos immutable = pos.immutable(); + level.levelEvent(2001, immutable, Block.getId(state)); + level.setBlock(immutable, Blocks.AIR.defaultBlockState(), 3); + ++changed; + } } public static void onServerTick(ServerLevel level) { } public static int getFallPercent(int explosionPower) { - return 0; + return Math.max(10, Math.min(90, explosionPower * 7)); } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/ExplosionClusterHandler.java b/src/main/java/com/vinlanx/explosionoverhaul/ExplosionClusterHandler.java index fb11921..a61ff52 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/ExplosionClusterHandler.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/ExplosionClusterHandler.java @@ -1,10 +1,29 @@ package com.vinlanx.explosionoverhaul; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.item.PrimedTnt; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; public class ExplosionClusterHandler { public static float calculateClusteredPower(Level level, Vec3 pos, float initialPower) { - return initialPower; + if (!((Boolean)Config.COMMON.enableExplosionClustering.get()).booleanValue()) { + return initialPower; + } + int nearbyExplosives = 0; + BlockPos origin = BlockPos.containing(pos); + for (BlockPos blockPos : BlockPos.betweenClosed(origin.offset(-3, -2, -3), origin.offset(3, 2, 3))) { + if (level.getBlockState(blockPos).is(Blocks.TNT)) { + ++nearbyExplosives; + } + } + nearbyExplosives += level.getEntitiesOfClass(PrimedTnt.class, new AABB(pos.x - 4.0, pos.y - 4.0, pos.z - 4.0, pos.x + 4.0, pos.y + 4.0, pos.z + 4.0)).size(); + if (nearbyExplosives <= 1) { + return initialPower; + } + float clusteredPower = initialPower + (nearbyExplosives - 1) * 2.75f; + return Math.min(clusteredPower, ((Integer)Config.COMMON.maxClusterPower.get()).floatValue()); } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/GlassBreakingEffects.java b/src/main/java/com/vinlanx/explosionoverhaul/GlassBreakingEffects.java index 9b55fc3..30c586c 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/GlassBreakingEffects.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/GlassBreakingEffects.java @@ -1,37 +1,83 @@ package com.vinlanx.explosionoverhaul; import java.util.Collections; +import java.util.HashSet; import java.util.Random; import java.util.Set; import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; 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 GlassBreakingEffects { public static void trigger(ServerLevel level, Vec3 explosionPos, float power) { + if (!((Boolean)Config.COMMON.enableGlassBreaking.get()).booleanValue()) { + return; + } + Random random = new Random(level.getSeed() ^ BlockPos.containing(explosionPos).asLong()); + double radius = calculateRadius(power); + BlockPos origin = BlockPos.containing(explosionPos); + int r = (int)Math.ceil(radius); + int broken = 0; + int maxBreak = Math.max(24, Math.min(700, (Integer)Config.COMMON.glassBlocksPerCycle.get() * 8)); + for (BlockPos pos : BlockPos.betweenClosed(origin.offset(-r, -r, -r), origin.offset(r, r, r))) { + if (broken >= maxBreak) { + break; + } + BlockState state = level.getBlockState(pos); + if (!isGlass(state) || !shouldBreak(level, explosionPos, pos, power, random, radius)) { + continue; + } + BlockPos immutable = pos.immutable(); + level.levelEvent(2001, immutable, Block.getId(state)); + level.setBlock(immutable, Blocks.AIR.defaultBlockState(), 3); + ++broken; + } } public static void onServerTick() { } public static double calculateRadius(float power) { - return Math.max(1.0, power); + return Math.max(5.0, Math.min(72.0, power * 7.0 * calculateRadiusMultiplier(power))); } public static float calculateRadiusMultiplier(float power) { - return 1.0f; + return power >= 16.0f ? 1.35f : 1.0f; } public static boolean isGlass(BlockState state) { - return false; + ResourceLocation id = BuiltInRegistries.BLOCK.getKey(state.getBlock()); + return id != null && id.getPath().contains("glass"); } public static Set findConnectedGlass(ServerLevel level, BlockPos pos, Set processed) { - return Collections.emptySet(); + if (processed.contains(pos) || !isGlass(level.getBlockState(pos))) { + return Collections.emptySet(); + } + Set connected = new HashSet<>(); + connected.add(pos); + processed.add(pos); + for (BlockPos neighbor : new BlockPos[] { pos.north(), pos.south(), pos.east(), pos.west(), pos.above(), pos.below() }) { + if (connected.size() > 64) { + break; + } + connected.addAll(findConnectedGlass(level, neighbor, processed)); + } + return connected; } public static boolean shouldBreak(ServerLevel level, Vec3 explosionPos, BlockPos pos, float power, Random random, double radius) { - return false; + double distance = Math.sqrt(pos.distToCenterSqr(explosionPos)); + if (distance > radius) { + return false; + } + double chance = 1.0 - distance / radius; + chance = Math.max(0.12, chance * 1.35); + return random.nextDouble() < chance; } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/RedstoneLampEffects.java b/src/main/java/com/vinlanx/explosionoverhaul/RedstoneLampEffects.java index 588b91d..878d089 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/RedstoneLampEffects.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/RedstoneLampEffects.java @@ -1,10 +1,43 @@ package com.vinlanx.explosionoverhaul; +import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.RedstoneLampBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; public class RedstoneLampEffects { public static void triggerLampFlicker(ServerLevel level, ServerPlayer player, float power, long delayTicks, double distance) { + triggerNearbyLamps(level, player.position(), power); + } + + public static void triggerNearbyLamps(ServerLevel level, Vec3 explosionPos, float power) { + if (!((Boolean)Config.COMMON.enableLampFlicker.get()).booleanValue()) { + return; + } + int radius = Math.min((Integer)Config.COMMON.lampFlickerSearchRadius.get(), Math.max(8, Math.round(power * 6.0f))); + BlockPos origin = BlockPos.containing(explosionPos); + int changed = 0; + for (BlockPos pos : BlockPos.betweenClosed(origin.offset(-radius, -radius, -radius), origin.offset(radius, radius, radius))) { + if (changed >= 96) { + break; + } + BlockState state = level.getBlockState(pos); + if (!state.is(Blocks.REDSTONE_LAMP) || !state.hasProperty(RedstoneLampBlock.LIT)) { + continue; + } + BlockPos immutable = pos.immutable(); + boolean lit = state.getValue(RedstoneLampBlock.LIT); + level.setBlock(immutable, state.setValue(RedstoneLampBlock.LIT, !lit), 3); + level.playSound(null, immutable, ModSounds.LAMP_FLICKER_SPARK_1.get(), SoundSource.BLOCKS, 0.25f, 0.8f + level.random.nextFloat() * 0.4f); + if (level.hasNeighborSignal(immutable)) { + level.setBlock(immutable, state.setValue(RedstoneLampBlock.LIT, true), 3); + } + ++changed; + } } public static void onServerTick() { diff --git a/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java b/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java index 6654177..6b7ff12 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java @@ -24,8 +24,12 @@ public class ServerExplosionHandler { power = Math.max(1.0f, explosionPower.getPower()); } Vec3 center = explosion instanceof ExplosionAccessor accessor ? accessor.explosionoverhaul$getCenter() : Vec3.ZERO; + power = ExplosionClusterHandler.calculateClusteredPower(level, center, power); int radius = Math.max(5, Math.min(18, Math.round(power * 2.0f * ((Double)Config.COMMON.craterSizeMultiplier.get()).floatValue()))); ExplosionOverhaul.LOGGER.info("Explosion Overhaul handling explosion at {} with power {} and radius {}", center, power, radius); + GlassBreakingEffects.trigger(level, center, power); + DripstoneEffects.handleDripstoneFall(level, BlockPos.containing(center), Math.round(power), level.random); + RedstoneLampEffects.triggerNearbyLamps(level, center, power); Set existing = new HashSet<>(affectedBlocks); BlockPos origin = BlockPos.containing(center); double radiusSq = radius * radius; diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/FirstTimeScreen.java b/src/main/java/com/vinlanx/explosionoverhaul/client/FirstTimeScreen.java index fcd1190..28caed0 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/FirstTimeScreen.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/FirstTimeScreen.java @@ -7,17 +7,17 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; public class FirstTimeScreen extends Screen { - private static final int FRAME_WIDTH = 256; - private static final int FRAME_HEIGHT = 144; + private static final int FRAME_WIDTH = 768; + private static final int FRAME_HEIGHT = 432; private static final int COLUMNS = 10; private static final int ROWS = 10; private static final int FRAME_COUNT = COLUMNS * ROWS; private static final int BORDER = 0xFFFF7A70; private static final int BORDER_HOVER = 0xFFFF9B91; private static final Choice[] CHOICES = new Choice[] { - new Choice("gui_screen_1.png", 12, "REALISTIC", Config.Client.ParticleRenderMode.REALISTIC), - new Choice("gui_screen_2.png", 28, "VANILLA-LIKE", Config.Client.ParticleRenderMode.VANILA), - new Choice("gui_screen_3.png", 44, "REALISTIC 2", Config.Client.ParticleRenderMode.REALISTIC_2) + new Choice("gui_screen_1.png", 7, 18, "REALISTIC", Config.Client.ParticleRenderMode.REALISTIC), + new Choice("gui_screen_2.png", 7, 18, "VANILLA-LIKE", Config.Client.ParticleRenderMode.VANILA), + new Choice("gui_screen_3.png", 20, 34, "REALISTIC 2", Config.Client.ParticleRenderMode.REALISTIC_2) }; private final Rect[] cardRects = new Rect[] { Rect.empty(), Rect.empty(), Rect.empty() }; @@ -31,20 +31,20 @@ public class FirstTimeScreen extends Screen { graphics.fill(0, 0, this.width, this.height, 0xFF050506); this.drawAmbient(graphics); - int top = Math.max(28, this.height / 9); + int top = Math.max(24, this.height / 12); graphics.drawCenteredString(this.font, Component.literal("Choose Your Explosion Style."), this.width / 2, top, 0xFFFF9900); graphics.drawCenteredString(this.font, Component.literal("You can change this anytime in the config"), this.width / 2, top + 25, 0xFFE5E5E5); graphics.drawCenteredString(this.font, Component.literal("The game may differ from the animations because compression has altered them."), this.width / 2, top + 48, 0xFF9C9C9C); - int availableCardH = Math.max(78, (this.height - top - 188) / 2); - int cardW = Math.min(360, Math.max(150, (this.width - 170) / 2)); + int availableCardH = Math.max(76, (this.height - top - 188) / 2); + int cardW = Math.min(320, Math.max(150, (this.width - 190) / 2)); cardW = Math.min(cardW, availableCardH * FRAME_WIDTH / FRAME_HEIGHT); int cardH = cardW * FRAME_HEIGHT / FRAME_WIDTH; int gap = Math.max(34, this.width / 28); int firstX = this.width / 2 - cardW - gap / 2; int secondX = this.width / 2 + gap / 2; - int firstRowY = top + 92; - int secondRowY = firstRowY + cardH + 60; + int firstRowY = top + 88; + int secondRowY = firstRowY + cardH + 58; this.cardRects[0] = new Rect(firstX, firstRowY, cardW, cardH + 58); this.cardRects[1] = new Rect(secondX, firstRowY, cardW, cardH + 58); @@ -83,7 +83,7 @@ public class FirstTimeScreen extends Screen { int imageW = rect.w; int imageH = rect.w * FRAME_HEIGHT / FRAME_WIDTH; graphics.fill(imageX - 6, imageY - 6, imageX + imageW + 6, imageY + imageH + 6, hovered ? BORDER_HOVER : BORDER); - this.drawSheetFrame(graphics, choice.texture, (this.currentFrame(65L) + choice.frameOffset) % FRAME_COUNT, imageX, imageY, imageW, imageH); + this.drawSheetFrame(graphics, choice.texture, choice.currentFrame(65L), imageX, imageY, imageW, imageH); graphics.drawCenteredString(this.font, Component.literal(choice.label), imageX + imageW / 2, imageY + imageH + 28, 0xFFFFFFFF); } @@ -100,10 +100,6 @@ public class FirstTimeScreen extends Screen { graphics.pose().popPose(); } - private int currentFrame(long frameTimeMillis) { - return (int)((System.currentTimeMillis() / frameTimeMillis) % FRAME_COUNT); - } - private void drawAmbient(GuiGraphics graphics) { for (int i = 0; i < 48; ++i) { int x = Math.floorMod(i * 173 + 19, Math.max(1, this.width)); @@ -120,16 +116,23 @@ public class FirstTimeScreen extends Screen { private static final class Choice { private final ResourceLocation texture; - private final int frameOffset; + private final int firstFrame; + private final int lastFrameExclusive; private final String label; private final Config.Client.ParticleRenderMode mode; - private Choice(String fileName, int frameOffset, String label, Config.Client.ParticleRenderMode mode) { + private Choice(String fileName, int firstFrame, int lastFrameExclusive, String label, Config.Client.ParticleRenderMode mode) { this.texture = ResourceLocation.fromNamespaceAndPath("explosionoverhaul", "intro_gui/preview/" + fileName); - this.frameOffset = frameOffset; + this.firstFrame = firstFrame; + this.lastFrameExclusive = lastFrameExclusive; this.label = label; this.mode = mode; } + + private int currentFrame(long frameTimeMillis) { + int span = Math.max(1, this.lastFrameExclusive - this.firstFrame); + return this.firstFrame + (int)((System.currentTimeMillis() / frameTimeMillis) % span); + } } private static final class Rect { diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/GuideSlidesScreen.java b/src/main/java/com/vinlanx/explosionoverhaul/client/GuideSlidesScreen.java index cee5f2f..e52ea1b 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/GuideSlidesScreen.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/GuideSlidesScreen.java @@ -8,10 +8,10 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.util.FormattedCharSequence; public class GuideSlidesScreen extends Screen { - private static final int FRAME_WIDTH = 256; - private static final int FRAME_HEIGHT = 144; - private static final int COLUMNS = 14; - private static final int ROWS = 14; + private static final int FRAME_WIDTH = 768; + private static final int FRAME_HEIGHT = 432; + private static final int COLUMNS = 10; + private static final int ROWS = 18; private static final int FRAME_COUNT = COLUMNS * ROWS; private static final int ACCENT = 0xFFFF9F3A; private static final int RED = 0xFF561818; @@ -38,10 +38,10 @@ public class GuideSlidesScreen extends Screen { graphics.fill(0, 0, this.width, this.height, 0xFF070708); this.drawAmbient(graphics); - int imageW = Math.min(this.width - 220, 1080); + int imageW = Math.min(this.width - 260, 980); int imageH = imageW * FRAME_HEIGHT / FRAME_WIDTH; int imageY = Math.max(28, this.height / 9); - int maxImageH = Math.max(120, this.height - imageY - 220); + int maxImageH = Math.max(110, this.height - imageY - 265); if (imageH > maxImageH) { imageH = Math.max(120, maxImageH); imageW = imageH * FRAME_WIDTH / FRAME_HEIGHT; @@ -53,9 +53,9 @@ public class GuideSlidesScreen extends Screen { Slide slide = SLIDES[this.slideIndex]; this.drawSheetFrame(graphics, slide.texture, this.currentFrame(slide.frameCount, 55L), imageX, imageY, imageW, imageH); - int textWidth = Math.min(this.width - 240, 1040); - int textY = imageY + imageH + 44; - this.drawCenteredWrappedText(graphics, slide.text, this.width / 2, textY, textWidth, 0xFFFFFFFF); + int textWidth = Math.min(this.width - 300, 920); + int textY = imageY + imageH + 42; + int textBottom = this.drawCenteredWrappedText(graphics, slide.text, this.width / 2, textY, textWidth, 0xFFFFFFFF); int arrowSize = 74; int arrowY = imageY + imageH / 2 - arrowSize / 2; @@ -70,7 +70,8 @@ public class GuideSlidesScreen extends Screen { int skipW = 142; int skipH = 38; - this.skipButton = new Rect((this.width - skipW) / 2, this.height - 58, skipW, skipH); + int skipY = Math.min(this.height - skipH - 12, textBottom + 18); + this.skipButton = new Rect((this.width - skipW) / 2, skipY, skipW, skipH); this.drawFlatButton(graphics, this.skipButton, "Skip Guide", this.skipButton.contains(mouseX, mouseY)); } @@ -117,13 +118,14 @@ public class GuideSlidesScreen extends Screen { return (int)((System.currentTimeMillis() / frameTimeMillis) % maxFrame); } - private void drawCenteredWrappedText(GuiGraphics graphics, String text, int centerX, int y, int maxWidth, int color) { + private int drawCenteredWrappedText(GuiGraphics graphics, String text, int centerX, int y, int maxWidth, int color) { List lines = this.font.split(Component.literal(text), maxWidth); int lineY = y; for (FormattedCharSequence line : lines) { graphics.drawCenteredString(this.font, line, centerX, lineY, color); lineY += this.font.lineHeight + 5; } + return lineY; } private void drawArrow(GuiGraphics graphics, Rect rect, String text, boolean hovered) { diff --git a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/cave_slide.png b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/cave_slide.png index f3af90c..a37d8f6 100644 Binary files a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/cave_slide.png and b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/cave_slide.png differ diff --git a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/claster_slide.png b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/claster_slide.png index 2e5f0a3..38f6e92 100644 Binary files a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/claster_slide.png and b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/claster_slide.png differ diff --git a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/glass_slide.png b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/glass_slide.png index 63bb9aa..ce747bc 100644 Binary files a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/glass_slide.png and b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/glass_slide.png differ diff --git a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_1.png b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_1.png index 87d7e69..c3a3d4b 100644 Binary files a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_1.png and b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_1.png differ diff --git a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_2.png b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_2.png index 5a82383..a93e0ca 100644 Binary files a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_2.png and b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_2.png differ diff --git a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_3.png b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_3.png index ddb2446..73cec63 100644 Binary files a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_3.png and b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/gui_screen_3.png differ diff --git a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/lamp_slide.png b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/lamp_slide.png index 3ac769c..2317b0d 100644 Binary files a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/lamp_slide.png and b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/lamp_slide.png differ diff --git a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/speed_of_sound_slide.png b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/speed_of_sound_slide.png index 9012390..a300135 100644 Binary files a/src/main/resources/assets/explosionoverhaul/intro_gui/preview/speed_of_sound_slide.png and b/src/main/resources/assets/explosionoverhaul/intro_gui/preview/speed_of_sound_slide.png differ