/* * Decompiled with CFR 0.152. */ package com.vinlanx.explosionoverhaul; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.logging.LogUtils; import com.vinlanx.explosionoverhaul.AmbientExplosionManager; import com.vinlanx.explosionoverhaul.AsyncCraterManager; import com.vinlanx.explosionoverhaul.BlockIndexManager; import com.vinlanx.explosionoverhaul.ClientSoundHandler; import com.vinlanx.explosionoverhaul.Config; import com.vinlanx.explosionoverhaul.CustomGlowParticleOptions; import com.vinlanx.explosionoverhaul.DripstoneEffects; import com.vinlanx.explosionoverhaul.GlassBreakingEffects; import com.vinlanx.explosionoverhaul.ModBlockEntities; import com.vinlanx.explosionoverhaul.ModBlocks; import com.vinlanx.explosionoverhaul.ModCreativeTabs; import com.vinlanx.explosionoverhaul.ModItems; import com.vinlanx.explosionoverhaul.ModParticles; import com.vinlanx.explosionoverhaul.ModSounds; import com.vinlanx.explosionoverhaul.PacketHandler; import com.vinlanx.explosionoverhaul.PlasmaParticleOptions; import com.vinlanx.explosionoverhaul.RedstoneLampEffects; import com.vinlanx.explosionoverhaul.ScanInfoHUD; import com.vinlanx.explosionoverhaul.ScanKeyHandler; import com.vinlanx.explosionoverhaul.ScanLoadInfoHUD; import com.vinlanx.explosionoverhaul.ScanLoadPromptHUD; import com.vinlanx.explosionoverhaul.ScanProgressHUD; import com.vinlanx.explosionoverhaul.ScanPromptHUD; import com.vinlanx.explosionoverhaul.ServerExplosionHandler; import com.vinlanx.explosionoverhaul.VinlanxTheLightRenderer; import com.vinlanx.explosionoverhaul.client.Blur; import com.vinlanx.explosionoverhaul.client.ClientEffects; import com.vinlanx.explosionoverhaul.client.ConcussionAudioEffect; import com.vinlanx.explosionoverhaul.client.CustomGlowParticleProvider; import com.vinlanx.explosionoverhaul.client.ExplosionTextureManager; import com.vinlanx.explosionoverhaul.client.ExplosionWindController; import com.vinlanx.explosionoverhaul.client.FirstTimeSetupHandler; import com.vinlanx.explosionoverhaul.client.IntroMusicTickHandler; import com.vinlanx.explosionoverhaul.client.LineSparkParticleProvider; import com.vinlanx.explosionoverhaul.client.ModConfigScreen; import com.vinlanx.explosionoverhaul.client.ModKeyMappings; import com.vinlanx.explosionoverhaul.client.PlasmaParticle; import com.vinlanx.explosionoverhaul.client.SmokeParticle; import com.vinlanx.explosionoverhaul.client.SoundPhysicsCompatibility; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.Reader; import java.nio.file.CopyOption; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileAttribute; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.particles.BlockParticleOption; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleType; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundSoundPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.ConfigScreenHandler; import net.minecraftforge.client.event.ClientPlayerNetworkEvent; import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.client.event.RegisterClientReloadListenersEvent; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.client.event.RegisterParticleProvidersEvent; import net.minecraftforge.client.event.RegisterShadersEvent; import net.minecraftforge.client.event.RenderGuiOverlayEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.level.LevelEvent; import net.minecraftforge.event.server.ServerStoppedEvent; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.config.IConfigSpec; import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.server.ServerLifecycleHooks; import org.slf4j.Logger; import software.bernie.geckolib.GeckoLib; @Mod(value="explosionoverhaul") public class ExplosionOverhaul { public static final String MODID = "explosionoverhaul"; public static final Logger LOGGER = LogUtils.getLogger(); private static final Map SOURCE_MODES = new HashMap(); private static final List EXPLOSION_BLACKLIST = new ArrayList(); private static final List DEFAULT_BLACKLIST = List.of("ancient_elements:block_of_raw_infernal_ore", "ancient_elements:celestium_ore", "ancient_elements:deepslate_frost_ore", "ancient_elements:deepslate_jungle_steel_ore", "ancient_elements:deepslate_lead_ore", "ancient_elements:deepslate_steel_ore", "ancient_elements:deepslate_tin_ore", "ancient_elements:deepslate_titanium_ore", "ancient_elements:ender_steel_ore", "ancient_elements:endrium_ore", "ancient_elements:frost_ore", "ancient_elements:infernal_ore", "ancient_elements:jungle_steel_ore", "ancient_elements:lead_ore", "ancient_elements:meteorite_ore", "ancient_elements:nether_steel_ore", "ancient_elements:palladium_ore", "ancient_elements:spectrillium_ore", "ancient_elements:steel_ore", "ancient_elements:tin_ore", "ancient_elements:titanium_ore", "ancient_elements:void_steel_ore", "mofus_better_end_:void_portal_block", "mofus_better_end_:void_portal_igniter", "mofus_better_end_:star_portal"); private static final Path CONFIG_DIR = Paths.get("config", "explosionoverhaul"); private static final Path OLD_COMMON = Paths.get("config", "explosionoverhaul-common.toml"); private static final Path OLD_CLIENT = Paths.get("config", "explosionoverhaul-client.toml"); private static final Path NEW_COMMON = CONFIG_DIR.resolve("explosionoverhaul-common.toml"); private static final Path NEW_CLIENT = CONFIG_DIR.resolve("explosionoverhaul-client.toml"); private static final Path BLACKLIST_JSON = CONFIG_DIR.resolve("DestroyingBlacklist.json"); private static final Path SOURCE_MODES_JSON = CONFIG_DIR.resolve("ExplosionSourceBlacklist.json"); private static final List delayedSounds = Collections.synchronizedList(new ArrayList()); private static final List delayedParticles = Collections.synchronizedList(new ArrayList()); @SubscribeEvent public static void onServerTick(TickEvent.LevelTickEvent event) { } private static void ensureConfigDirectory() { try { Files.createDirectories(CONFIG_DIR, new FileAttribute[0]); ExplosionOverhaul.writeConfigReadme(); } catch (Exception e) { LOGGER.warn("Failed to create config/explosionoverhaul directory", (Throwable)e); } } private static void writeConfigReadme() { Path readmePath = CONFIG_DIR.resolve("README.md"); if (Files.exists(readmePath, new LinkOption[0])) { return; } try { String content = "# Explosion Overhaul Config Helpers\n\n## Blacklist Files (.json)\n\n### DestroyingBlacklist.json\n- **Format**: `[\"namespace:block_id\", ...]`\n- **Effect**: Blocks listed here are immune to explosion craters.\n\n### GlassBlacklist.json\n- **Format**: `[\"namespace:block_id\", ...]`\n- **Effect**: Blocks listed here won't be shattered by the glass-breaking system.\n\n### ExplosionSourceBlacklist.json\n- **Format**: `{\"namespace:entity_id\": \"MODE\"}`\n- **Modes**:\n - `DEFAULT`: Standard mod behavior (crater + concussion + sounds).\n - `VANILLA`: Reverts to vanilla explosion logic for this source.\n - `NO_DESTRUCTION`: Concussions and sounds only (no crater or glass breaking).\n - `NO_DESTRUCTION_GLASSWORKS`: Like NO_DESTRUCTION, but glass still shatters.\n\n**Note**: Invalid JSON (extra commas, comments) will cause the mod to use defaults. Restart the game/server for changes to take effect.\n\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ"; Files.writeString(readmePath, (CharSequence)content, new OpenOption[0]); } catch (Exception e) { LOGGER.warn("Failed to write config README.md", (Throwable)e); } } private static void migrateOldTomlIfPresent() { try { byte[] oldBytes; if (Files.exists(OLD_COMMON, new LinkOption[0])) { ExplosionOverhaul.ensureConfigDirectory(); if (!Files.exists(NEW_COMMON, new LinkOption[0])) { Files.move(OLD_COMMON, NEW_COMMON, new CopyOption[0]); } else { oldBytes = Files.readAllBytes(OLD_COMMON); Files.write(NEW_COMMON, oldBytes, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); Files.delete(OLD_COMMON); } } if (Files.exists(OLD_CLIENT, new LinkOption[0])) { ExplosionOverhaul.ensureConfigDirectory(); if (!Files.exists(NEW_CLIENT, new LinkOption[0])) { Files.move(OLD_CLIENT, NEW_CLIENT, new CopyOption[0]); } else { oldBytes = Files.readAllBytes(OLD_CLIENT); Files.write(NEW_CLIENT, oldBytes, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); Files.delete(OLD_CLIENT); } } } catch (Exception e) { LOGGER.warn("Failed to migrate old toml configs", (Throwable)e); } } private static void loadBlacklistFromJson() { ExplosionOverhaul.ensureConfigDirectory(); Gson gson = new GsonBuilder().setPrettyPrinting().create(); try { if (!Files.exists(BLACKLIST_JSON, new LinkOption[0])) { try (BufferedWriter w = Files.newBufferedWriter(BLACKLIST_JSON, new OpenOption[0]);){ gson.toJson(DEFAULT_BLACKLIST, (Appendable)w); } EXPLOSION_BLACKLIST.clear(); EXPLOSION_BLACKLIST.addAll(DEFAULT_BLACKLIST); return; } try (BufferedReader r = Files.newBufferedReader(BLACKLIST_JSON);){ List list = (List)gson.fromJson((Reader)r, new TypeToken>(){}.getType()); if (list != null) { EXPLOSION_BLACKLIST.clear(); EXPLOSION_BLACKLIST.addAll(list); } } } catch (Exception e) { LOGGER.warn("Failed to load DestroyingBlacklist.json, falling back to defaults", (Throwable)e); EXPLOSION_BLACKLIST.clear(); EXPLOSION_BLACKLIST.addAll(DEFAULT_BLACKLIST); } } private static void loadSourceModesFromJson() { ExplosionOverhaul.ensureConfigDirectory(); Gson gson = new GsonBuilder().setPrettyPrinting().create(); try { if (!Files.exists(SOURCE_MODES_JSON, new LinkOption[0])) { ExplosionOverhaul.ensureConfigDirectory(); SOURCE_MODES.clear(); try (BufferedWriter w = Files.newBufferedWriter(SOURCE_MODES_JSON, new OpenOption[0]);){ gson.toJson(SOURCE_MODES, (Appendable)w); } return; } try (BufferedReader r = Files.newBufferedReader(SOURCE_MODES_JSON);){ Map map = (Map)gson.fromJson((Reader)r, new TypeToken>(){}.getType()); if (map != null) { SOURCE_MODES.clear(); SOURCE_MODES.putAll(map); } } } catch (Exception e) { LOGGER.warn("Failed to load ExplosionSourceBlacklist.json", (Throwable)e); } } public static ExplosionSourceMode getSourceMode(String id) { return SOURCE_MODES.getOrDefault(id, ExplosionSourceMode.DEFAULT); } public static Map getSourceModes() { return new HashMap(SOURCE_MODES); } public static void setSourceModes(Map modes) { SOURCE_MODES.clear(); SOURCE_MODES.putAll(modes); Gson gson = new GsonBuilder().setPrettyPrinting().create(); try (BufferedWriter w = Files.newBufferedWriter(SOURCE_MODES_JSON, new OpenOption[0]);){ gson.toJson(SOURCE_MODES, (Appendable)w); } catch (Exception e) { LOGGER.warn("Failed to save ExplosionSourceBlacklist.json", (Throwable)e); } } public static List getExplosionBlacklistList() { return new ArrayList(EXPLOSION_BLACKLIST); } public static List getDefaultExplosionBlacklist() { return new ArrayList(DEFAULT_BLACKLIST); } public static void setExplosionBlacklistFromList(List list) { EXPLOSION_BLACKLIST.clear(); for (String s : list) { if (s == null || s.isBlank()) continue; EXPLOSION_BLACKLIST.add(s.trim()); } Gson gson = new GsonBuilder().setPrettyPrinting().create(); try (BufferedWriter w = Files.newBufferedWriter(BLACKLIST_JSON, new OpenOption[0]);){ gson.toJson(ExplosionOverhaul.getExplosionBlacklistList(), (Appendable)w); } catch (Exception e) { LOGGER.warn("Failed to save DestroyingBlacklist.json", (Throwable)e); } } public static boolean isBlockBlacklisted(Block block) { ResourceLocation blockId = ForgeRegistries.BLOCKS.getKey((Object)block); if (blockId != null) { String blockName = blockId.toString(); return EXPLOSION_BLACKLIST.contains(blockName); } return false; } public static boolean isBlockStateBlacklisted(BlockState state) { ResourceLocation blockId = ForgeRegistries.BLOCKS.getKey((Object)state.m_60734_()); if (blockId != null) { String blockName = blockId.toString(); return EXPLOSION_BLACKLIST.contains(blockName); } return false; } public ExplosionOverhaul() { IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); GeckoLib.initialize(); ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, (IConfigSpec)Config.COMMON_SPEC, "explosionoverhaul/explosionoverhaul-common.toml"); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, (IConfigSpec)Config.CLIENT_SPEC, "explosionoverhaul/explosionoverhaul-client.toml"); ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class, () -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> ModConfigScreen.create(parent))); ModSounds.register(modEventBus); ModParticles.register(modEventBus); ModBlocks.register(modEventBus); ModItems.register(modEventBus); ModBlockEntities.register(modEventBus); ModCreativeTabs.register(modEventBus); MinecraftForge.EVENT_BUS.register(ExplosionOverhaul.class); MinecraftForge.EVENT_BUS.register((Object)new ServerExplosionHandler()); MinecraftForge.EVENT_BUS.register(BlockIndexManager.class); modEventBus.addListener(this::commonSetup); DistExecutor.unsafeRunWhenOn((Dist)Dist.CLIENT, () -> () -> { SoundPhysicsCompatibility.init(); modEventBus.addListener(ClientSetup::init); modEventBus.addListener(ClientSetup::registerParticleFactories); modEventBus.addListener(ClientSetup::registerBlockEntityRenderers); modEventBus.addListener(ClientSetup::registerShaders); modEventBus.addListener(ClientSetup::registerReloadListeners); modEventBus.addListener(ClientSetup::onRegisterKeyMappings); MinecraftForge.EVENT_BUS.register(ClientSetup.class); MinecraftForge.EVENT_BUS.register((Object)new ClientSoundHandler()); MinecraftForge.EVENT_BUS.register(ScanProgressHUD.class); MinecraftForge.EVENT_BUS.register(ScanPromptHUD.class); MinecraftForge.EVENT_BUS.register(ScanInfoHUD.class); MinecraftForge.EVENT_BUS.register(ScanLoadPromptHUD.class); MinecraftForge.EVENT_BUS.register(ScanLoadInfoHUD.class); MinecraftForge.EVENT_BUS.register((Object)new ScanKeyHandler()); MinecraftForge.EVENT_BUS.register(FirstTimeSetupHandler.class); MinecraftForge.EVENT_BUS.register(IntroMusicTickHandler.class); MinecraftForge.EVENT_BUS.register(ConcussionAudioEffect.class); }); } private void commonSetup(FMLCommonSetupEvent event) { ExplosionOverhaul.migrateOldTomlIfPresent(); ExplosionOverhaul.loadBlacklistFromJson(); ExplosionOverhaul.loadSourceModesFromJson(); event.enqueueWork(PacketHandler::register); } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.END) { RedstoneLampEffects.onServerTick(); if (event.getServer() != null) { for (ServerLevel level : event.getServer().m_129785_()) { DripstoneEffects.onServerTick(level); } } GlassBreakingEffects.onServerTick(); AmbientExplosionManager.onServerTick(event.getServer().m_129783_()); if (ServerLifecycleHooks.getCurrentServer() != null) { AsyncCraterManager.onServerTick(ServerLifecycleHooks.getCurrentServer()); } delayedSounds.removeIf(delayedSound -> { --delayedSound.ticksRemaining; if (delayedSound.ticksRemaining <= 0L) { delayedSound.play(); return true; } return false; }); delayedParticles.removeIf(delayedParticle -> { --delayedParticle.ticksRemaining; if (delayedParticle.ticksRemaining <= 0L) { delayedParticle.spawn(); if (delayedParticle.durationTicks > 1L) { --delayedParticle.durationTicks; delayedParticle.ticksRemaining = 1L; return false; } return true; } return false; }); } } @SubscribeEvent public static void onLevelUnload(LevelEvent.Unload event) { LevelAccessor levelAccessor = event.getLevel(); if (levelAccessor instanceof ServerLevel) { ServerLevel level = (ServerLevel)levelAccessor; AsyncCraterManager.onLevelUnload(level); } } @SubscribeEvent public static void onServerStopped(ServerStoppedEvent event) { AsyncCraterManager.shutdown(); } @SubscribeEvent public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) { if (event.getEntity() instanceof ServerPlayer) { AmbientExplosionManager.onPlayerLoggedIn((ServerPlayer)event.getEntity()); } } @SubscribeEvent public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) { if (event.getEntity() instanceof ServerPlayer) { AmbientExplosionManager.onPlayerLoggedOut((ServerPlayer)event.getEntity()); } } public static void addDelayedSound(ServerPlayer player, SoundEvent sound, SoundSource source, float x, float y, float z, float volume, float pitch, long seed, long delayTicks) { if (player.f_8906_ == null) { return; } if (delayTicks <= 0L) { player.f_8906_.m_9829_((Packet)new ClientboundSoundPacket((Holder)ForgeRegistries.SOUND_EVENTS.getHolder((Object)sound).orElseThrow(), source, (double)x, (double)y, (double)z, volume, pitch, seed)); } else { delayedSounds.add(new DelayedSound(player, sound, source, x, y, z, volume, pitch, seed, delayTicks)); } } public static void addDelayedParticle(ServerLevel level, BlockParticleOption particleOption, double x, double y, double z, int count, double dx, double dy, double dz, double speed, long delayTicks, long durationTicks) { if (delayTicks <= 0L && durationTicks <= 1L) { level.m_8767_((ParticleOptions)particleOption, x, y, z, count, dx, dy, dz, speed); } else { delayedParticles.add(new DelayedParticle(level, particleOption, x, y, z, count, dx, dy, dz, speed, delayTicks, Math.max(1L, durationTicks))); } } public static enum ExplosionSourceMode { DEFAULT, VANILLA, NO_DESTRUCTION, NO_DESTRUCTION_GLASSWORKS; } public static class DelayedSound { public ServerPlayer player; public SoundEvent sound; public SoundSource source; public float x; public float y; public float z; public float volume; public float pitch; public long seed; public long ticksRemaining; public DelayedSound(ServerPlayer player, SoundEvent sound, SoundSource source, float x, float y, float z, float volume, float pitch, long seed, long ticksRemaining) { this.player = player; this.sound = sound; this.source = source; this.x = x; this.y = y; this.z = z; this.volume = volume; this.pitch = pitch; this.seed = seed; this.ticksRemaining = ticksRemaining; } public void play() { if (this.player.f_8906_ == null) { return; } this.player.f_8906_.m_9829_((Packet)new ClientboundSoundPacket((Holder)ForgeRegistries.SOUND_EVENTS.getHolder((Object)this.sound).orElseThrow(), this.source, (double)this.x, (double)this.y, (double)this.z, this.volume, this.pitch, this.seed)); } } public static class DelayedParticle { public ServerLevel level; public BlockParticleOption particleOption; public double x; public double y; public double z; public int count; public double dx; public double dy; public double dz; public double speed; public long ticksRemaining; public long durationTicks; public DelayedParticle(ServerLevel level, BlockParticleOption particleOption, double x, double y, double z, int count, double dx, double dy, double dz, double speed, long ticksRemaining, long durationTicks) { this.level = level; this.particleOption = particleOption; this.x = x; this.y = y; this.z = z; this.count = count; this.dx = dx; this.dy = dy; this.dz = dz; this.speed = speed; this.ticksRemaining = ticksRemaining; this.durationTicks = durationTicks; } public void spawn() { int particlesPerSpawn = Math.max(1, this.count / (int)Math.max(1L, this.durationTicks)); this.level.m_8767_((ParticleOptions)this.particleOption, this.x, this.y, this.z, particlesPerSpawn, this.dx, this.dy, this.dz, this.speed); } } public static class ClientSetup { private static boolean hasPreWarmed = false; public static void init(FMLClientSetupEvent event) { ExplosionTextureManager.getInstance().reload(); } public static void registerReloadListeners(RegisterClientReloadListenersEvent event) { event.registerReloadListener((PreparableReloadListener)new TexturePreloader()); } @SubscribeEvent public static void registerShaders(RegisterShadersEvent event) { try { event.registerShader(new ShaderInstance(event.getResourceProvider(), new ResourceLocation(ExplosionOverhaul.MODID, "blur"), DefaultVertexFormat.f_85817_), Blur::setShader); } catch (IOException e) { LOGGER.warn("Failed to load core shaders", (Throwable)e); } } @SubscribeEvent public static void registerParticleFactories(RegisterParticleProvidersEvent event) { event.registerSpriteSet((ParticleType)ModParticles.CUSTOM_GLOW.get(), CustomGlowParticleProvider::new); event.registerSpriteSet((ParticleType)ModParticles.PLASMA.get(), PlasmaParticle.Provider::new); event.registerSpriteSet((ParticleType)ModParticles.CUSTOM_SMOKE.get(), SmokeParticle.Provider::new); event.registerSpriteSet((ParticleType)ModParticles.LINE_SPARK.get(), LineSparkParticleProvider::new); LOGGER.info("Registered custom particle providers."); } @SubscribeEvent public static void onRegisterKeyMappings(RegisterKeyMappingsEvent event) { ModKeyMappings.register(event); } @SubscribeEvent public static void registerBlockEntityRenderers(EntityRenderersEvent.RegisterRenderers event) { event.registerBlockEntityRenderer((BlockEntityType)ModBlockEntities.VINLANX_THE_LIGHT.get(), VinlanxTheLightRenderer::new); } @SubscribeEvent public static void onClientTick(TickEvent.ClientTickEvent event) { if (event.phase == TickEvent.Phase.END) { ClientEffects.onClientTick(); if (!hasPreWarmed && Minecraft.m_91087_().f_91073_ != null && Minecraft.m_91087_().f_91074_ != null) { LOGGER.info("Pre-warming explosion particle render pipeline..."); ClientLevel level = Minecraft.m_91087_().f_91073_; double x = Minecraft.m_91087_().f_91074_.m_20185_(); double y = -200.0; double z = Minecraft.m_91087_().f_91074_.m_20189_(); level.m_7106_((ParticleOptions)new PlasmaParticleOptions(1.0f), x, y, z, 0.0, 0.0, 0.0); LOGGER.info("Pre-warming 'glow' animation..."); level.m_7106_((ParticleOptions)new CustomGlowParticleOptions(0, 1.0f, 0.01f, 0, (float)y, 4.0f, 0.0f), x, y, z, 0.0, 0.0, 0.0); LOGGER.info("Pre-warming 'glow_2' animation..."); level.m_7106_((ParticleOptions)new CustomGlowParticleOptions(0, 1.0f, 0.01f, 1, (float)y, 4.0f, 0.0f), x, y, z, 0.0, 0.0, 0.0); LOGGER.info("Pre-warming 'sglow' animation..."); level.m_7106_((ParticleOptions)new CustomGlowParticleOptions(0, 1.0f, 0.01f, 2, (float)y, 4.0f, 0.0f), x, y, z, 0.0, 0.0, 0.0); hasPreWarmed = true; LOGGER.info("Explosion particle pipeline pre-warmed successfully."); } } } @SubscribeEvent public static void onPlayerLogout(ClientPlayerNetworkEvent.LoggingOut event) { LOGGER.info("Player logged out, resetting particle pre-warmer and scan HUDs."); hasPreWarmed = false; ExplosionWindController.reset(); ScanProgressHUD.reset(); ScanPromptHUD.setVisible(false); ScanLoadPromptHUD.setVisible(false); ScanInfoHUD.setVisible(false); ScanLoadInfoHUD.setVisible(false); } @SubscribeEvent public static void onRenderGuiOverlay(RenderGuiOverlayEvent.Post event) { if (event.getOverlay().id().m_135815_().equals("all")) { Blur.render(Blur.RenderStage.HUD); } if (event.getOverlay().id().m_135815_().equals("crosshair")) { ClientEffects.renderFlash(event); ConcussionAudioEffect.renderHeartbeatHUD(event.getGuiGraphics()); } } public static class TexturePreloader implements PreparableReloadListener { public CompletableFuture m_5540_(PreparableReloadListener.PreparationBarrier pPreparationBarrier, ResourceManager pResourceManager, ProfilerFiller pPreparationsProfiler, ProfilerFiller pReloadProfiler, Executor pBackgroundExecutor, Executor pGameExecutor) { CompletableFuture prepareFuture = CompletableFuture.supplyAsync(() -> { LOGGER.info("Scanning for all mod textures to preload..."); return pResourceManager.m_214159_("textures", path -> path.m_135827_().equals(ExplosionOverhaul.MODID) && path.m_135815_().endsWith(".png")).keySet().stream().toList(); }, pBackgroundExecutor); return prepareFuture.thenCompose(locationsToLoad -> pPreparationBarrier.m_6769_(locationsToLoad).thenRunAsync(() -> { if (locationsToLoad.isEmpty()) { LOGGER.warn("Did not find any textures to preload for mod '{}'.", (Object)ExplosionOverhaul.MODID); return; } LOGGER.info("Preloading {} textures from '{}' to GPU...", (Object)locationsToLoad.size(), (Object)ExplosionOverhaul.MODID); TextureManager textureManager = Minecraft.m_91087_().m_91097_(); for (ResourceLocation location : locationsToLoad) { textureManager.m_118506_(location); } ExplosionTextureManager.getInstance().reload(); LOGGER.info("Finished preloading all textures for the mod."); }, pGameExecutor)); } } } public static class CaveEffects { private static final int PLAYER_EFFECT_RADIUS = 10; public static void spawnFallingBlocksAndDust(ServerLevel level, Vec3 explosionPos, ServerPlayer player, float power, long initialDelayTicks) { RandomSource random = level.m_213780_(); long maxEffectOverallDurationTicks = (2 + random.m_188503_(5)) * 20; int totalEffectSpawns = 15 + (int)(power / 5.0f * 3.0f); totalEffectSpawns = Math.min(totalEffectSpawns, 60); for (int i = 0; i < totalEffectSpawns; ++i) { long individualDelay = initialDelayTicks + (long)random.m_188503_((int)maxEffectOverallDurationTicks / 2); long randomDurationTicks = 10 + random.m_188503_(40); CaveEffects.spawnEffectAtPlayer(level, player, individualDelay, random, power, randomDurationTicks); } } private static void spawnEffectAtPlayer(ServerLevel level, ServerPlayer player, long delayTicks, RandomSource random, float power, long durationTicks) { int xOffset = random.m_188503_(21) - 10; int zOffset = random.m_188503_(21) - 10; int ySearchStart = (int)player.m_20186_() + 3 + random.m_188503_(4); BlockPos playerBlockPos = player.m_20183_(); BlockPos checkPosBase = new BlockPos(playerBlockPos.m_123341_() + xOffset, ySearchStart, playerBlockPos.m_123343_() + zOffset); BlockPos effectPos = null; for (int i = 0; i < 8; ++i) { BlockState aboveState; BlockState currentState; BlockPos currentCheck = checkPosBase.m_6625_(i); if (!level.m_46749_(currentCheck) || (double)currentCheck.m_123342_() <= player.m_20186_() - 1.0 || !(currentState = level.m_8055_(currentCheck)).m_280296_() || currentState.m_60795_() || !(aboveState = level.m_8055_(currentCheck.m_7494_())).m_60795_() && !level.m_8055_(currentCheck.m_7495_()).m_60795_()) continue; effectPos = currentCheck; break; } if (effectPos != null && effectPos.m_123342_() > level.m_141937_()) { boolean isSpecialBlock; BlockState blockState = level.m_8055_(effectPos); if (blockState.m_60795_()) { return; } ResourceLocation registryName = ForgeRegistries.BLOCKS.getKey((Object)blockState.m_60734_()); String name = registryName != null ? registryName.m_135815_() : ""; boolean bl = isSpecialBlock = name.contains("bedrock") || name.contains("end_portal") || name.contains("end_portal_frame") || name.contains("command_block") || name.contains("barrier"); if (isSpecialBlock) { return; } int particleCount = 8 + (int)((double)(power / 10.0f) * 1.5); particleCount = Math.min(particleCount, 25); ExplosionOverhaul.addDelayedParticle(level, new BlockParticleOption(ParticleTypes.f_123814_, blockState), (double)effectPos.m_123341_() + 0.5, (double)effectPos.m_123342_() + 0.2, (double)effectPos.m_123343_() + 0.5, particleCount, 0.3, 0.3, 0.3, 0.02, delayTicks, durationTicks); if (random.m_188500_() < 0.3 + (double)power * 0.02) { ExplosionOverhaul.addDelayedSound(player, (SoundEvent)ModSounds.DUST_SOUND.get(), SoundSource.BLOCKS, (float)effectPos.m_123341_() + 0.5f, (float)effectPos.m_123342_() + 0.5f, (float)effectPos.m_123343_() + 0.5f, 0.6f + random.m_188501_() * 0.4f, 0.85f + random.m_188501_() * 0.3f, player.m_217043_().m_188505_(), delayTicks + (long)random.m_188503_(10)); } if (random.m_188500_() < 0.03 + (double)power * 0.005 && power > 10.0f) { ExplosionOverhaul.addDelayedSound(player, (SoundEvent)ModSounds.FALLING_STONES_SOUND.get(), SoundSource.BLOCKS, (float)effectPos.m_123341_() + 0.5f, (float)effectPos.m_123342_() + 0.5f, (float)effectPos.m_123343_() + 0.5f, 0.8f + random.m_188501_() * 0.2f, 0.9f + random.m_188501_() * 0.2f, player.m_217043_().m_188505_(), delayTicks + (long)random.m_188503_(20)); } } } } }