From 505be6029f4359cb809edec6fad7373cb2c0b492 Mon Sep 17 00:00:00 2001 From: doctor4t Date: Fri, 3 Jan 2025 16:20:57 +0100 Subject: [PATCH] Add firefly dynamic lights --- .../ladysnake/effective/core/Effective.java | 11 +- .../core/index/EffectiveParticles.java | 2 + .../core/mixin/ClientWorldMixin.java | 40 +- .../mixin/lights/ParticleManagerMixin.java | 18 + .../core/particle/FireflyParticle.java | 372 +++++++++++------- .../effective/textures/particle/firefly.png | Bin 196 -> 357 bytes .../resources/effective_core.mixins.json | 1 + 7 files changed, 268 insertions(+), 176 deletions(-) create mode 100644 src/client/java/org/ladysnake/effective/core/mixin/lights/ParticleManagerMixin.java diff --git a/src/client/java/org/ladysnake/effective/core/Effective.java b/src/client/java/org/ladysnake/effective/core/Effective.java index 9c255893..e54b3483 100644 --- a/src/client/java/org/ladysnake/effective/core/Effective.java +++ b/src/client/java/org/ladysnake/effective/core/Effective.java @@ -1,5 +1,6 @@ package org.ladysnake.effective.core; +import foundry.veil.api.client.render.light.Light; import foundry.veil.platform.VeilEventPlatform; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; @@ -15,7 +16,6 @@ import net.minecraft.particle.SimpleParticleType; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; -import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvents; import net.minecraft.util.Identifier; import net.minecraft.util.math.MathHelper; @@ -24,7 +24,6 @@ import net.minecraft.world.World; import org.ladysnake.effective.core.gui.ParryScreen; import org.ladysnake.effective.core.index.EffectiveParticles; -import org.ladysnake.effective.core.particle.ChorusPetalParticle; import org.ladysnake.effective.core.particle.EyesParticle; import org.ladysnake.effective.core.particle.WillOWispParticle; import org.ladysnake.effective.core.render.entity.model.SplashBottomModel; @@ -42,6 +41,9 @@ import org.ladysnake.satin.api.managed.ShaderEffectManager; import org.ladysnake.satin.api.managed.uniform.Uniform1f; +import java.util.ArrayList; +import java.util.HashMap; + public class Effective implements ClientModInitializer { public static final String MODID = "effective"; @@ -60,6 +62,9 @@ public class Effective implements ClientModInitializer { // freeze frames for feedbacking public static int freezeFrames = -1; + // particle lights cache + public static final ArrayList PARTICLE_LIGHTS = new ArrayList<>(); + // particle types public static SimpleParticleType EYES; public static SimpleParticleType WILL_O_WISP; @@ -236,7 +241,7 @@ public void onInitializeClient() { } }); - + // Inject into the Vanilla particle shader to lower the transparency discard threshold VeilEventPlatform.INSTANCE.onVeilAddShaderProcessors((provider, registry) -> { registry.addPreprocessor(new EffectiveTransparencyFixPreProcessor(), false); }); diff --git a/src/client/java/org/ladysnake/effective/core/index/EffectiveParticles.java b/src/client/java/org/ladysnake/effective/core/index/EffectiveParticles.java index c2989fe6..7f32c6a8 100644 --- a/src/client/java/org/ladysnake/effective/core/index/EffectiveParticles.java +++ b/src/client/java/org/ladysnake/effective/core/index/EffectiveParticles.java @@ -30,6 +30,7 @@ public interface EffectiveParticles { SimpleParticleType GLOW_CASCADE = create("glow_cascade", FabricParticleTypes.simple(true)); SimpleParticleType MIST = create("mist", FabricParticleTypes.simple(true)); SimpleParticleType CHORUS_PETAL = create("chorus_petal", FabricParticleTypes.simple(true)); + SimpleParticleType FIREFLY = create("firefly", FabricParticleTypes.simple(true)); static void initialize() { PARTICLES.keySet().forEach(particle -> Registry.register(Registries.PARTICLE_TYPE, PARTICLES.get(particle), particle)); @@ -55,5 +56,6 @@ private static void registerFactories() { ParticleFactoryRegistry.getInstance().register(GLOW_CASCADE, GlowCascadeParticle.Factory::new); ParticleFactoryRegistry.getInstance().register(MIST, MistParticle.Factory::new); ParticleFactoryRegistry.getInstance().register(CHORUS_PETAL, ChorusPetalParticle.Factory::new); + ParticleFactoryRegistry.getInstance().register(FIREFLY, FireflyParticle.Factory::new); } } diff --git a/src/client/java/org/ladysnake/effective/core/mixin/ClientWorldMixin.java b/src/client/java/org/ladysnake/effective/core/mixin/ClientWorldMixin.java index 4e64624d..240c5f54 100644 --- a/src/client/java/org/ladysnake/effective/core/mixin/ClientWorldMixin.java +++ b/src/client/java/org/ladysnake/effective/core/mixin/ClientWorldMixin.java @@ -19,6 +19,10 @@ import org.jetbrains.annotations.Nullable; import org.ladysnake.effective.core.Effective; import org.ladysnake.effective.core.EffectiveConfig; +import org.ladysnake.effective.core.index.EffectiveParticles; +import org.ladysnake.effective.core.particle.FireflyParticle; +import org.ladysnake.effective.core.settings.SpawnSettings; +import org.ladysnake.effective.core.settings.data.FireflySpawnSetting; import org.ladysnake.effective.core.utils.EffectiveUtils; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -76,27 +80,21 @@ protected ClientWorldMixin(MutableWorldProperties properties, RegistryKey // FIREFLIES if (EffectiveConfig.fireflyDensity > 0) { -// FireflySpawnSetting fireflySpawnSetting = SpawnSettings.FIREFLIES.get(biome.getKey().get()); -// if (fireflySpawnSetting != null) { -// if (random.nextFloat() * 250f <= fireflySpawnSetting.spawnChance() * EffectiveConfig.fireflyDensity && pos.getY() > this.getSeaLevel()) { -// for (int y = this.getSeaLevel(); y <= this.getSeaLevel() * 2; y++) { -// pos.setY(y); -// pos2.setY(y-1); -// boolean canSpawnFirefly = FireflyParticle.canFlyThroughBlock(this, pos, this.getBlockState(pos)) && !FireflyParticle.canFlyThroughBlock(this, pos2, this.getBlockState(pos2)); -// -// if (canSpawnFirefly) { -// WorldParticleBuilder.create(Effective.FIREFLY) -// .enableForcedSpawn() -// .setColorData(ColorParticleData.create(fireflySpawnSetting.color(), fireflySpawnSetting.color()).build()) -// .setScaleData(GenericParticleData.create(0.05f + random.nextFloat() * 0.10f).build()) -// .setLifetime(ThreadLocalRandom.current().nextInt(40, 120)) -// .setRenderType(LodestoneWorldParticleRenderType.ADDITIVE) -// .spawn(this, pos.getX() + random.nextFloat(), pos.getY() + random.nextFloat() * 5f, pos.getZ() + random.nextFloat()); -// break; -// } -// } -// } -// } + FireflySpawnSetting fireflySpawnSetting = SpawnSettings.FIREFLIES.get(biome.getKey().get()); + if (fireflySpawnSetting != null) { + if (random.nextFloat() * 250f <= fireflySpawnSetting.spawnChance() * EffectiveConfig.fireflyDensity && pos.getY() > this.getSeaLevel()) { + for (int y = this.getSeaLevel(); y <= this.getSeaLevel() * 2; y++) { + pos.setY(y); + pos2.setY(y - 1); + boolean canSpawnFirefly = FireflyParticle.canFlyThroughBlock(this, pos, this.getBlockState(pos)) && !FireflyParticle.canFlyThroughBlock(this, pos2, this.getBlockState(pos2)); + + if (canSpawnFirefly) { + this.addParticle(EffectiveParticles.FIREFLY, pos.getX() + random.nextFloat(), pos.getY() + random.nextFloat() * 5f, pos.getZ() + random.nextFloat(), 0, 0, 0); + break; + } + } + } + } } pos = blockPos.add(MathHelper.floor(EffectiveUtils.getRandomFloatOrNegative(this.random) * 50), MathHelper.floor(EffectiveUtils.getRandomFloatOrNegative(this.random) * 25), MathHelper.floor(EffectiveUtils.getRandomFloatOrNegative(this.random) * 50)).mutableCopy(); diff --git a/src/client/java/org/ladysnake/effective/core/mixin/lights/ParticleManagerMixin.java b/src/client/java/org/ladysnake/effective/core/mixin/lights/ParticleManagerMixin.java new file mode 100644 index 00000000..848ea254 --- /dev/null +++ b/src/client/java/org/ladysnake/effective/core/mixin/lights/ParticleManagerMixin.java @@ -0,0 +1,18 @@ +package org.ladysnake.effective.core.mixin.lights; + +import foundry.veil.api.client.render.VeilRenderSystem; +import net.minecraft.client.particle.ParticleManager; +import org.ladysnake.effective.core.Effective; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ParticleManager.class) +public class ParticleManagerMixin { + @Inject(method = "clearParticles", at = @At("HEAD")) + private void effective$clearLightsOnClearParticles(CallbackInfo ci) { + Effective.PARTICLE_LIGHTS.forEach(light -> VeilRenderSystem.renderer().getLightRenderer().removeLight(light)); + Effective.PARTICLE_LIGHTS.clear(); + } +} diff --git a/src/client/java/org/ladysnake/effective/core/particle/FireflyParticle.java b/src/client/java/org/ladysnake/effective/core/particle/FireflyParticle.java index 66b18fa8..73d6c1df 100644 --- a/src/client/java/org/ladysnake/effective/core/particle/FireflyParticle.java +++ b/src/client/java/org/ladysnake/effective/core/particle/FireflyParticle.java @@ -1,152 +1,220 @@ -//package org.ladysnake.effective.core.particle; -// -//import net.fabricmc.fabric.impl.client.particle.FabricSpriteProviderImpl; -//import net.minecraft.block.BlockState; -//import net.minecraft.client.world.ClientWorld; -//import net.minecraft.util.math.BlockPos; -//import net.minecraft.util.math.Vec3d; -//import net.minecraft.world.LightType; -//import net.minecraft.world.World; -//import org.ladysnake.effective.cosmetics.EffectiveCosmetics; -//import team.lodestar.lodestone.systems.particle.world.LodestoneWorldParticle; -//import team.lodestar.lodestone.systems.particle.world.options.WorldParticleOptions; -// -//import java.util.HashMap; -//import java.util.concurrent.ThreadLocalRandom; -// -//public class FireflyParticle extends LodestoneWorldParticle { -// protected static final float BLINK_STEP = 0.05f; -// protected float nextAlphaGoal = 0f; -// protected double xTarget; -// protected double yTarget; -// protected double zTarget; -// protected int targetChangeCooldown = 0; -// protected int maxHeight; -// private BlockPos lightTarget; -// -// public FireflyParticle(ClientWorld world, WorldParticleOptions data, FabricSpriteProviderImpl spriteSet, double x, double y, double z, double xd, double yd, double zd) { -// super(world, data, spriteSet, x, y, z, xd, yd, zd); -// -// this.maxAge = ThreadLocalRandom.current().nextInt(400, 1201); // live between 20 seconds and one minute -// this.maxHeight = 4; -// this.alpha = 0f; -// this.collidesWithWorld = false; -// } -// -// public static boolean canFlyThroughBlock(World world, BlockPos blockPos, BlockState blockState) { -// return !blockState.shouldSuffocate(world, blockPos) && blockState.getFluidState().isEmpty(); -// } -// -// public void tick() { -// this.prevPosX = this.x; -// this.prevPosY = this.y; -// this.prevPosZ = this.z; -// -// // fade and die on daytime or if old enough unless fireflies can spawn any time of day -// if ((!world.getDimension().hasFixedTime() && !EffectiveCosmetics.isNightTime(world)) || this.age++ >= this.maxAge) { -// nextAlphaGoal = 0; -// if (this.alpha <= 0.01f) { -// this.markDead(); -// } -// } -// -// // blinking -// if (this.alpha > nextAlphaGoal - BLINK_STEP && this.alpha < nextAlphaGoal + BLINK_STEP) { -// nextAlphaGoal = random.nextFloat(); -// } else { -// if (nextAlphaGoal > this.alpha) { -// this.alpha = Math.min(this.alpha + BLINK_STEP, 1f); -// } else if (nextAlphaGoal < this.alpha) { -// this.alpha = Math.max(this.alpha - BLINK_STEP, 0f); -// } -// } -// -//// this.targetChangeCooldown -= (new Vec3d(x, y, z).squaredDistanceTo(prevPosX, prevPosY, prevPosZ) < 0.0125) ? 10 : 1; -// -// if (random.nextInt(20) == 0 || (xTarget == 0 && yTarget == 0 && zTarget == 0) || new Vec3d(x, y, z).squaredDistanceTo(xTarget, yTarget, zTarget) < 9) { -// selectBlockTarget(); -// } -// -// Vec3d targetVector = new Vec3d(this.xTarget - this.x, this.yTarget - this.y, this.zTarget - this.z); -// double length = targetVector.length(); -// targetVector = targetVector.multiply(0.1 / length); -// -// BlockPos blockPos = BlockPos.ofFloored(this.x, this.y - 0.1, this.z); -// if (!canFlyThroughBlock(this.world, blockPos, this.world.getBlockState(blockPos))) { -// velocityX = (0.9) * velocityX + (0.1) * targetVector.x; -// velocityY = 0.05; -// velocityZ = (0.9) * velocityZ + (0.1) * targetVector.z; -// } else { -// velocityX = (0.9) * velocityX + (0.1) * targetVector.x; -// velocityY = (0.9) * velocityY + (0.1) * targetVector.y; -// velocityZ = (0.9) * velocityZ + (0.1) * targetVector.z; -// } -// if (!BlockPos.ofFloored(x, y, z).equals(this.getTargetPosition())) { -// this.move(velocityX, velocityY, velocityZ); -// } -// } -// -// private void selectBlockTarget() { -// if (this.lightTarget == null) { -// // Behaviour -// double groundLevel = 0; -// for (int i = 0; i < 20; i++) { -// BlockPos checkedPos = BlockPos.ofFloored(this.x, this.y - i, this.z); -// BlockState checkedBlock = this.world.getBlockState(checkedPos); -// if (canFlyThroughBlock(this.world, checkedPos, checkedBlock)) { -// groundLevel = this.y - i; -// } -// if (groundLevel != 0) break; -// } -// -// this.xTarget = this.x + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10; -// this.yTarget = Math.min(Math.max(this.y + EffectiveUtils.getRandomFloatOrNegative(this.random) * 2, groundLevel), groundLevel + maxHeight); -// this.zTarget = this.z + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10; -// -// BlockPos targetPos = BlockPos.ofFloored(this.xTarget, this.yTarget, this.zTarget); -// if (!canFlyThroughBlock(this.world, targetPos, this.world.getBlockState(targetPos))) { -// this.yTarget += 1; -// } -// -// this.lightTarget = getMostLitBlockAround(); -// } else { -// this.xTarget = this.lightTarget.getX() + EffectiveUtils.getRandomFloatOrNegative(this.random); -// this.yTarget = this.lightTarget.getY() + EffectiveUtils.getRandomFloatOrNegative(this.random); -// this.zTarget = this.lightTarget.getZ() + EffectiveUtils.getRandomFloatOrNegative(this.random); -// -// if (this.world.getLightLevel(LightType.BLOCK, BlockPos.ofFloored(x, y, z)) > 0 && !this.world.isDay()) { -// this.lightTarget = getMostLitBlockAround(); -// } else { -// this.lightTarget = null; -// } -// } -// -//// targetChangeCooldown = random.nextInt() % 100; -// } -// -// public BlockPos getTargetPosition() { -// return BlockPos.ofFloored(this.xTarget, this.yTarget + 0.5, this.zTarget); -// } -// -// private BlockPos getMostLitBlockAround() { -// HashMap randBlocks = new HashMap<>(); -// -// // get blocks adjacent to the fly -// for (int x = -1; x <= 1; x++) { -// for (int y = -1; y <= 1; y++) { -// for (int z = -1; z <= 1; z++) { -// BlockPos bp = BlockPos.ofFloored(this.x + x, this.y + y, this.z + z); -// randBlocks.put(bp, this.world.getLightLevel(LightType.BLOCK, bp)); -// } -// } -// } -// -// // get other random blocks to find a different light source -// for (int i = 0; i < 15; i++) { -// BlockPos randBP = BlockPos.ofFloored(this.x + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10, this.y + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10, this.z + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10); -// randBlocks.put(randBP, this.world.getLightLevel(LightType.BLOCK, randBP)); -// } -// -// return randBlocks.entrySet().stream().max((entry1, entry2) -> entry1.getValue() > entry2.getValue() ? 1 : -1).get().getKey(); -// } -//} +package org.ladysnake.effective.core.particle; + +import foundry.veil.api.client.render.VeilRenderSystem; +import foundry.veil.api.client.render.light.PointLight; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.block.BlockState; +import net.minecraft.client.particle.*; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.particle.SimpleParticleType; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.LightType; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; +import org.ladysnake.effective.core.Effective; +import org.ladysnake.effective.core.settings.SpawnSettings; +import org.ladysnake.effective.core.settings.data.FireflySpawnSetting; +import org.ladysnake.effective.core.utils.EffectiveUtils; +import org.ladysnake.effective.cosmetics.EffectiveCosmetics; + +import java.awt.*; +import java.util.HashMap; +import java.util.concurrent.ThreadLocalRandom; + +public class FireflyParticle extends SpriteBillboardParticle { + protected static final float BLINK_STEP = 0.05f; + protected float nextAlphaGoal = 0f; + protected double xTarget; + protected double yTarget; + protected double zTarget; + protected int maxHeight; + private BlockPos lightTarget; + + PointLight light; + + public FireflyParticle(ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ, SpriteProvider spriteProvider) { + super(world, x, y, z, velocityX, velocityY, velocityZ); + + this.setSprite(spriteProvider); + + this.maxAge = ThreadLocalRandom.current().nextInt(400, 1200); // live between 20 seconds and one minute + this.maxHeight = 4; + this.alpha = 0f; + this.collidesWithWorld = false; + this.scale = 0.02f + random.nextFloat() * 0.02f; + + Color color = Color.GREEN; + RegistryEntry biome = world.getBiome(BlockPos.ofFloored(x, y, z)); + if (biome.getKey().isPresent()) { + FireflySpawnSetting fireflySpawnSetting = SpawnSettings.FIREFLIES.get(biome.getKey().get()); + if (fireflySpawnSetting != null) { + color = fireflySpawnSetting.color(); + } + } + this.red = color.getRed() / 255f; + this.green = color.getGreen() / 255f; + this.blue = color.getBlue() / 255f; + + this.light = new PointLight(); + this.light.setBrightness(0f); + this.light.setColor(color.getRGB()); + this.light.setRadius(25f * this.scale); + this.light.setPosition(x, y, z); + Effective.PARTICLE_LIGHTS.add(this.light); + VeilRenderSystem.renderer().getLightRenderer().addLight(light); + } + + @Override + protected int getBrightness(float tint) { + return LightmapTextureManager.MAX_LIGHT_COORDINATE; + } + + @Override + public void markDead() { + super.markDead(); + VeilRenderSystem.renderer().getLightRenderer().removeLight(this.light); + } + + public static boolean canFlyThroughBlock(World world, BlockPos blockPos, BlockState blockState) { + return !blockState.shouldSuffocate(world, blockPos) && blockState.getFluidState().isEmpty(); + } + + public void tick() { + this.prevPosX = this.x; + this.prevPosY = this.y; + this.prevPosZ = this.z; + + this.light.setPosition(this.x, this.y, this.z); + + // fade and die on daytime or if old enough unless fireflies can spawn any time of day + if ((!world.getDimension().hasFixedTime() && !EffectiveCosmetics.isNightTime(world)) || this.age++ >= this.maxAge) { + nextAlphaGoal = 0; + if (this.alpha <= 0.01f) { + this.markDead(); + } + } + + // blinking + if (this.alpha > nextAlphaGoal - BLINK_STEP && this.alpha < nextAlphaGoal + BLINK_STEP) { + nextAlphaGoal = random.nextFloat(); + } else { + if (nextAlphaGoal > this.alpha) { + this.alpha = Math.min(this.alpha + BLINK_STEP, 1f); + } else if (nextAlphaGoal < this.alpha) { + this.alpha = Math.max(this.alpha - BLINK_STEP, 0f); + } + } + this.light.setBrightness(this.alpha * 4f); + +// this.targetChangeCooldown -= (new Vec3d(x, y, z).squaredDistanceTo(prevPosX, prevPosY, prevPosZ) < 0.0125) ? 10 : 1; + + if (random.nextInt(20) == 0 || (xTarget == 0 && yTarget == 0 && zTarget == 0) || new Vec3d(x, y, z).squaredDistanceTo(xTarget, yTarget, zTarget) < 9) { + selectBlockTarget(); + } + + Vec3d targetVector = new Vec3d(this.xTarget - this.x, this.yTarget - this.y, this.zTarget - this.z); + double length = targetVector.length(); + targetVector = targetVector.multiply(0.1 / length); + + BlockPos blockPos = BlockPos.ofFloored(this.x, this.y - 0.1, this.z); + if (!canFlyThroughBlock(this.world, blockPos, this.world.getBlockState(blockPos))) { + velocityX = (0.9) * velocityX + (0.1) * targetVector.x; + velocityY = 0.05; + velocityZ = (0.9) * velocityZ + (0.1) * targetVector.z; + } else { + velocityX = (0.9) * velocityX + (0.1) * targetVector.x; + velocityY = (0.9) * velocityY + (0.1) * targetVector.y; + velocityZ = (0.9) * velocityZ + (0.1) * targetVector.z; + } + if (!BlockPos.ofFloored(x, y, z).equals(this.getTargetPosition())) { + this.move(velocityX, velocityY, velocityZ); + } + } + + @Override + public ParticleTextureSheet getType() { + return ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT; + } + + private void selectBlockTarget() { + if (this.lightTarget == null) { + // Behaviour + double groundLevel = 0; + for (int i = 0; i < 20; i++) { + BlockPos checkedPos = BlockPos.ofFloored(this.x, this.y - i, this.z); + BlockState checkedBlock = this.world.getBlockState(checkedPos); + if (canFlyThroughBlock(this.world, checkedPos, checkedBlock)) { + groundLevel = this.y - i; + } + if (groundLevel != 0) break; + } + + this.xTarget = this.x + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10; + this.yTarget = Math.min(Math.max(this.y + EffectiveUtils.getRandomFloatOrNegative(this.random) * 2, groundLevel), groundLevel + maxHeight); + this.zTarget = this.z + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10; + + BlockPos targetPos = BlockPos.ofFloored(this.xTarget, this.yTarget, this.zTarget); + if (!canFlyThroughBlock(this.world, targetPos, this.world.getBlockState(targetPos))) { + this.yTarget += 1; + } + + this.lightTarget = getMostLitBlockAround(); + } else { + this.xTarget = this.lightTarget.getX() + EffectiveUtils.getRandomFloatOrNegative(this.random); + this.yTarget = this.lightTarget.getY() + EffectiveUtils.getRandomFloatOrNegative(this.random); + this.zTarget = this.lightTarget.getZ() + EffectiveUtils.getRandomFloatOrNegative(this.random); + + if (this.world.getLightLevel(LightType.BLOCK, BlockPos.ofFloored(x, y, z)) > 0 && !this.world.isDay()) { + this.lightTarget = getMostLitBlockAround(); + } else { + this.lightTarget = null; + } + } + +// targetChangeCooldown = random.nextInt() % 100; + } + + public BlockPos getTargetPosition() { + return BlockPos.ofFloored(this.xTarget, this.yTarget + 0.5, this.zTarget); + } + + private BlockPos getMostLitBlockAround() { + HashMap randBlocks = new HashMap<>(); + + // get blocks adjacent to the fly + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + BlockPos bp = BlockPos.ofFloored(this.x + x, this.y + y, this.z + z); + randBlocks.put(bp, this.world.getLightLevel(LightType.BLOCK, bp)); + } + } + } + + // get other random blocks to find a different light source + for (int i = 0; i < 15; i++) { + BlockPos randBP = BlockPos.ofFloored(this.x + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10, this.y + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10, this.z + EffectiveUtils.getRandomFloatOrNegative(this.random) * 10); + randBlocks.put(randBP, this.world.getLightLevel(LightType.BLOCK, randBP)); + } + + return randBlocks.entrySet().stream().max((entry1, entry2) -> entry1.getValue() > entry2.getValue() ? 1 : -1).get().getKey(); + } + + @Environment(EnvType.CLIENT) + public static class Factory implements ParticleFactory { + private final SpriteProvider spriteProvider; + + public Factory(SpriteProvider spriteProvider) { + this.spriteProvider = spriteProvider; + } + + @Override + public Particle createParticle(SimpleParticleType parameters, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { + return new FireflyParticle(world, x, y, z, velocityX, velocityY, velocityZ, this.spriteProvider); + } + } +} diff --git a/src/client/resources/assets/effective/textures/particle/firefly.png b/src/client/resources/assets/effective/textures/particle/firefly.png index ac26aa1d906db114d89c85a30ffdde923bf4bd6d..d9417e8adf61fc22048a1c59bf2738acc205912c 100644 GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScvUi-X*q7}lMWc?smOq&xaLGB9lH z=l+w(3gmMZctipf@$Cm;Mhn(p6`-I*iEBhjaDG}zd16s2LqK9?UWuNcYlwoWo}r%6 z?>)@hfNHj-MtG)qdTKFn06DA-QjDw&j6jwb5KBYZAlGOxGK0lI?lEL!Vh{k*Q9zv8 z&H@(C0KxWadFqVTGflLuk7sn8Z@Z>-L|Nn32mv<0hW@cvaao{-nN|m(= Oq}J2b&t;ucLK6V4D?q*g delta 165 zcmaFLbc9i{Gr-TCmrII^fq{Y7)59eQNOOQN2OE$)!oRP3qM}Ci0R{%X1B_YMIyr%Y zk)AG&AsWH0{oY&+20SjZNA>UhFOhSeqP1<`M7=v1>~g-dI6XraP6;}D+3~SVY=KAD zsiT5b71B&^%y?LY&s=CKJ;XJ8_ubT(7^A-V_0n~rGJ$cIZhoKHWq#_0>cf&QV@04P N44$rjF6*2UngHn@ILrV5 diff --git a/src/client/resources/effective_core.mixins.json b/src/client/resources/effective_core.mixins.json index 419da810..593966fb 100644 --- a/src/client/resources/effective_core.mixins.json +++ b/src/client/resources/effective_core.mixins.json @@ -29,6 +29,7 @@ "glowsquids.HypnotizingGlowSquidsAdder", "glowsquids.JebGlowSquidRainbowRenderer", "glowsquids.RainbowShaderRenderLayerSwapper", + "lights.ParticleManagerMixin", "screenshake.EnderDragonRoarScreenshakeAdder", "screenshake.RavagerRoarScreenshakeAdder", "screenshake.SonicBoomScreenshakeAdder",