diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/WildBackport.java b/common/src/main/java/com/cursedcauldron/wildbackport/WildBackport.java index c06cf21..2dca602 100644 --- a/common/src/main/java/com/cursedcauldron/wildbackport/WildBackport.java +++ b/common/src/main/java/com/cursedcauldron/wildbackport/WildBackport.java @@ -8,6 +8,7 @@ import com.cursedcauldron.wildbackport.common.registry.WBBlocks; import com.cursedcauldron.wildbackport.common.registry.WBEnchantments; import com.cursedcauldron.wildbackport.common.registry.WBGameEvents; import com.cursedcauldron.wildbackport.common.registry.WBItems; +import com.cursedcauldron.wildbackport.common.registry.WBMobEffects; import com.cursedcauldron.wildbackport.common.registry.WBPositionSources; import com.cursedcauldron.wildbackport.common.registry.worldgen.WBFeatures; import com.cursedcauldron.wildbackport.common.registry.worldgen.RootPlacerType; @@ -43,6 +44,7 @@ public class WildBackport { WBFeatures.FEATURES.register(); WBItems.ITEMS.register(); WBMemoryModules.MEMORIES.register(); + WBMobEffects.EFFECTS.register(); WBParticleTypes.PARTICLES.register(); WBPositionSources.SOURCES.register(); RootPlacerType.PLACERS.register(); diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/blocks/DrippingFluid.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/blocks/DrippingFluid.java index db962b9..220902e 100644 --- a/common/src/main/java/com/cursedcauldron/wildbackport/common/blocks/DrippingFluid.java +++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/blocks/DrippingFluid.java @@ -1,6 +1,7 @@ package com.cursedcauldron.wildbackport.common.blocks; import com.cursedcauldron.wildbackport.common.registry.WBBlocks; +import com.cursedcauldron.wildbackport.common.tag.WBBlockTags; import com.cursedcauldron.wildbackport.core.mixin.access.PointedDripstoneBlockAccessor; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; @@ -18,7 +19,7 @@ public record DrippingFluid(BlockPos pos, Fluid fluid, BlockState sourceState) { Optional fluidAbove = getFluidAboveStalactite(world, blockPos, state); if (fluidAbove.isPresent()) { Fluid fluid = fluidAbove.get().fluid; - if (fluidAbove.get().sourceState().is(WBBlocks.MUD.get()) && fluid == Fluids.WATER) { + if (fluidAbove.get().sourceState().is(WBBlockTags.MUD) && fluid == Fluids.WATER) { world.setBlockAndUpdate(fluidAbove.get().pos, Blocks.CLAY.defaultBlockState()); world.levelEvent(1504, blockPos, 0); ci.cancel(); @@ -30,7 +31,7 @@ public record DrippingFluid(BlockPos pos, Fluid fluid, BlockState sourceState) { return !PointedDripstoneBlockAccessor.callIsStalactite(state) ? Optional.empty() : PointedDripstoneBlockAccessor.callFindRootBlock(level, pos, state, 11).map(blockPos -> { BlockPos position = blockPos.above(); BlockState sourceState = level.getBlockState(position); - Fluid fluid = sourceState.is(WBBlocks.MUD.get()) && !level.dimensionType().ultraWarm() ? Fluids.WATER : level.getFluidState(position).getType(); + Fluid fluid = sourceState.is(WBBlockTags.MUD) && !level.dimensionType().ultraWarm() ? Fluids.WATER : level.getFluidState(position).getType(); return new DrippingFluid(position, fluid, sourceState); }); } diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/effects/EffectFactor.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/effects/EffectFactor.java new file mode 100644 index 0000000..da5b1fa --- /dev/null +++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/effects/EffectFactor.java @@ -0,0 +1,40 @@ +package com.cursedcauldron.wildbackport.common.effects; + +import net.minecraft.network.protocol.Packet; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; + +import java.util.Optional; +import java.util.function.Supplier; + +public interface EffectFactor { + static EffectFactor of(MobEffect effect) { + return (EffectFactor)effect; + } + + MobEffect setFactorCalculationData(Supplier data); + + Supplier getFactorCalculationData(); + + static Optional create(MobEffect effect) { + return Optional.ofNullable(of(effect).getFactorCalculationData().get()); + } + + interface Instance { + static Instance of(MobEffectInstance instance) { + return (Instance)instance; + } + + void setFactorCalculationData(Optional data); + + Optional getFactorCalculationData(); + } + + interface Network { + static Network of(Packet packet) { + return (Network)packet; + } + + FactorCalculationData getFactorCalculationData(); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/effects/FactorCalculationData.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/effects/FactorCalculationData.java new file mode 100644 index 0000000..04794ac --- /dev/null +++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/effects/FactorCalculationData.java @@ -0,0 +1,70 @@ +package com.cursedcauldron.wildbackport.common.effects; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.ExtraCodecs; +import net.minecraft.util.Mth; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.LivingEntity; + +public class FactorCalculationData { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> { + return instance.group(ExtraCodecs.NON_NEGATIVE_INT.fieldOf("padding_duration").forGetter(data -> { + return data.paddingDuration; + }), Codec.FLOAT.fieldOf("factor_start").orElse(0.0F).forGetter(data -> { + return data.factorStart; + }), Codec.FLOAT.fieldOf("factor_target").orElse(1.0F).forGetter(data -> { + return data.factorTarget; + }), Codec.FLOAT.fieldOf("factor_current").orElse(0.0F).forGetter(data -> { + return data.factorCurrent; + }), ExtraCodecs.NON_NEGATIVE_INT.fieldOf("effect_changed_timestamp").orElse(0).forGetter(data -> { + return data.effectChangedTimestamp; + }), Codec.FLOAT.fieldOf("factor_previous_frame").orElse(0.0F).forGetter(data -> { + return data.factorPreviousFrame; + }), Codec.BOOL.fieldOf("had_effect_last_tick").orElse(false).forGetter(data -> { + return data.hadEffectLastTick; + })).apply(instance, FactorCalculationData::new); + }); + private final int paddingDuration; + private float factorStart; + private float factorTarget; + private float factorCurrent; + public int effectChangedTimestamp; + private float factorPreviousFrame; + private boolean hadEffectLastTick; + + public FactorCalculationData(int paddingDuration, float factorStart, float factorTarget, float factorCurrent, int effectChangedTimestamp, float factorPreviousFrame, boolean hadEffectLastTick) { + this.paddingDuration = paddingDuration; + this.factorStart = factorStart; + this.factorTarget = factorTarget; + this.factorCurrent = factorCurrent; + this.effectChangedTimestamp = effectChangedTimestamp; + this.factorPreviousFrame = factorPreviousFrame; + this.hadEffectLastTick = hadEffectLastTick; + } + + public FactorCalculationData(int paddingDuration) { + this(paddingDuration, 0.0F, 1.0F, 0.0F, 0, 0.0F, false); + } + + public void update(MobEffectInstance instance) { + this.factorPreviousFrame = this.factorCurrent; + boolean inRange = instance.getDuration() > this.paddingDuration; + + if (this.hadEffectLastTick != inRange) { + this.hadEffectLastTick = inRange; + this.effectChangedTimestamp = instance.getDuration(); + this.factorStart = this.factorCurrent; + this.factorTarget = inRange ? 1.0F : 0.0F; + } + + float delta = Mth.clamp(((float)this.effectChangedTimestamp - (float)instance.getDuration()) / (float)this.paddingDuration, 0.0F, 1.0F); + this.factorCurrent = Mth.lerp(delta, this.factorCurrent, this.factorTarget); + } + + public float lerp(LivingEntity entity, float factor) { + if (entity.isRemoved()) this.factorPreviousFrame = this.factorCurrent; + + return Mth.lerp(factor, this.factorPreviousFrame, this.factorCurrent); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/entities/Warden.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/entities/Warden.java index 1978e6f..94518f1 100644 --- a/common/src/main/java/com/cursedcauldron/wildbackport/common/entities/Warden.java +++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/entities/Warden.java @@ -3,13 +3,14 @@ package com.cursedcauldron.wildbackport.common.entities; import com.cursedcauldron.wildbackport.WildBackport; import com.cursedcauldron.wildbackport.client.animation.api.AnimationState; import com.cursedcauldron.wildbackport.client.registry.WBSoundEvents; +import com.cursedcauldron.wildbackport.common.entities.access.api.Poses; import com.cursedcauldron.wildbackport.common.entities.brain.WardenBrain; import com.cursedcauldron.wildbackport.common.entities.brain.warden.SonicBoom; -import com.cursedcauldron.wildbackport.common.entities.access.api.Poses; import com.cursedcauldron.wildbackport.common.entities.warden.Angriness; import com.cursedcauldron.wildbackport.common.entities.warden.MobPositionSource; import com.cursedcauldron.wildbackport.common.entities.warden.VibrationListenerSource; import com.cursedcauldron.wildbackport.common.entities.warden.WardenAngerManager; +import com.cursedcauldron.wildbackport.common.registry.WBMobEffects; import com.cursedcauldron.wildbackport.common.registry.entity.WBEntities; import com.cursedcauldron.wildbackport.common.registry.entity.WBMemoryModules; import com.cursedcauldron.wildbackport.common.tag.WBGameEventTags; @@ -35,7 +36,6 @@ import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.IndirectEntityDamageSource; import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntitySelector; @@ -68,6 +68,7 @@ import java.util.Random; //<> +//TODO: fix warden not detecting vibrations by players public class Warden extends Monster implements VibrationListenerSource.VibrationConfig { private static final EntityDataAccessor ANGER = SynchedEntityData.defineId(Warden.class, EntityDataSerializers.INT); private int tendrilPitchEnd; @@ -266,7 +267,7 @@ public class Warden extends Monster implements VibrationListenerSource.Vibration } private int getHeartRate() { - return 40 - Mth.floor(Mth.clamp((float)this.getAnger() / (float) Angriness.ANGRY.getThreshold(), 0.0F, 1.0F) * 30.0F); + return 40 - Mth.floor(Mth.clamp((float)this.getAnger() / (float)Angriness.ANGRY.getThreshold(), 0.0F, 1.0F) * 30.0F); } public float getTendrilPitch(float tickDelta) { @@ -353,7 +354,7 @@ public class Warden extends Monster implements VibrationListenerSource.Vibration } public static void addDarknessEffectToClosePlayers(ServerLevel level, Vec3 pos, @Nullable Entity entity, int range) { - MobEffectInstance instance = new MobEffectInstance(MobEffects.BLINDNESS, 260, 0, false, false); + MobEffectInstance instance = new MobEffectInstance(WBMobEffects.DARKNESS.get(), 260, 0, false, false); MobUtils.addEffectToPlayersWithinDistance(level, entity, pos, range, instance, 200); } @@ -408,7 +409,7 @@ public class Warden extends Monster implements VibrationListenerSource.Vibration } } - public Optional getEntityAngryAt() { + public Optional getPrimeSuspect() { return this.getAngriness().isAngry() ? this.angerManager.getPrimeSuspect() : Optional.empty(); } @@ -423,7 +424,7 @@ public class Warden extends Monster implements VibrationListenerSource.Vibration } @Nullable @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor accessor, DifficultyInstance difficulty, MobSpawnType spawn, @Nullable SpawnGroupData groupData, @Nullable CompoundTag tag) { + public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawn, @Nullable SpawnGroupData groupData, @Nullable CompoundTag tag) { this.getBrain().setMemoryWithExpiry(WBMemoryModules.DIG_COOLDOWN.get(), Unit.INSTANCE, 1200L); if (spawn == MobSpawnType.TRIGGERED) { this.setPose(Poses.EMERGING.get()); @@ -431,7 +432,7 @@ public class Warden extends Monster implements VibrationListenerSource.Vibration this.playSound(WBSoundEvents.WARDEN_AGITATED, 5.0F, 1.0F); } - return super.finalizeSpawn(accessor, difficulty, spawn, groupData, tag); + return super.finalizeSpawn(level, difficulty, spawn, groupData, tag); } @Override @@ -452,8 +453,8 @@ public class Warden extends Monster implements VibrationListenerSource.Vibration public void updateAttackTarget(LivingEntity entity) { this.getBrain().eraseMemory(WBMemoryModules.ROAR_TARGET.get()); - entity.getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, entity); - entity.getBrain().eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE); + this.getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, entity); + this.getBrain().eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE); SonicBoom.setCooldown(this, 200); } diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/entities/brain/WardenBrain.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/entities/brain/WardenBrain.java index 38fbee3..5c78088 100644 --- a/common/src/main/java/com/cursedcauldron/wildbackport/common/entities/brain/WardenBrain.java +++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/entities/brain/WardenBrain.java @@ -95,15 +95,15 @@ public class WardenBrain { } private static void initIdleActivity(Brain brain) { - brain.addActivity(Activity.IDLE, 10, ImmutableList.of(new SetRoarTarget<>(Warden::getEntityAngryAt), new TryToSniff(), new RunOne<>(ImmutableMap.of(WBMemoryModules.IS_SNIFFING.get(), MemoryStatus.VALUE_ABSENT), ImmutableList.of(Pair.of(new RandomStroll(0.5F), 2), Pair.of(new DoNothing(30, 60), 1))))); + brain.addActivity(Activity.IDLE, 10, ImmutableList.of(new SetRoarTarget<>(Warden::getPrimeSuspect), new TryToSniff(), new RunOne<>(ImmutableMap.of(WBMemoryModules.IS_SNIFFING.get(), MemoryStatus.VALUE_ABSENT), ImmutableList.of(Pair.of(new RandomStroll(0.5F), 2), Pair.of(new DoNothing(30, 60), 1))))); } private static void initInvestigateActivity(Brain brain) { - brain.addActivityAndRemoveMemoryWhenStopped(WBActivities.INVESTIGATE.get(), 5, ImmutableList.of(new SetRoarTarget<>(Warden::getEntityAngryAt), new GoToTargetLocation<>(WBMemoryModules.DISTURBANCE_LOCATION.get(), 2, 0.7F)), WBMemoryModules.DISTURBANCE_LOCATION.get()); + brain.addActivityAndRemoveMemoryWhenStopped(WBActivities.INVESTIGATE.get(), 5, ImmutableList.of(new SetRoarTarget<>(Warden::getPrimeSuspect), new GoToTargetLocation<>(WBMemoryModules.DISTURBANCE_LOCATION.get(), 2, 0.7F)), WBMemoryModules.DISTURBANCE_LOCATION.get()); } private static void initSniffingActivity(Brain brain) { - brain.addActivityAndRemoveMemoryWhenStopped(WBActivities.SNIFF.get(), 5, ImmutableList.of(new SetRoarTarget<>(Warden::getEntityAngryAt), new Sniffing<>(SNIFFING_DURATION)), WBMemoryModules.IS_SNIFFING.get()); + brain.addActivityAndRemoveMemoryWhenStopped(WBActivities.SNIFF.get(), 5, ImmutableList.of(new SetRoarTarget<>(Warden::getPrimeSuspect), new Sniffing<>(SNIFFING_DURATION)), WBMemoryModules.IS_SNIFFING.get()); } private static void initRoarActivity(Brain brain) { @@ -132,7 +132,7 @@ public class WardenBrain { } public static void setDisturbanceLocation(Warden warden, BlockPos pos) { - if (warden.level.getWorldBorder().isWithinBounds(pos) && warden.getEntityAngryAt().isEmpty() && warden.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).isEmpty()) { + if (warden.level.getWorldBorder().isWithinBounds(pos) && warden.getPrimeSuspect().isEmpty() && warden.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).isEmpty()) { setDigCooldown(warden); warden.getBrain().setMemoryWithExpiry(WBMemoryModules.SNIFF_COOLDOWN.get(), Unit.INSTANCE, 100L); warden.getBrain().setMemoryWithExpiry(MemoryModuleType.LOOK_TARGET, new BlockPosTracker(pos), 100L); diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/WBMobEffects.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/WBMobEffects.java new file mode 100644 index 0000000..5c154de --- /dev/null +++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/WBMobEffects.java @@ -0,0 +1,18 @@ +package com.cursedcauldron.wildbackport.common.registry; + +import com.cursedcauldron.wildbackport.WildBackport; +import com.cursedcauldron.wildbackport.common.effects.EffectFactor; +import com.cursedcauldron.wildbackport.common.effects.FactorCalculationData; +import com.cursedcauldron.wildbackport.core.api.CoreRegistry; +import com.cursedcauldron.wildbackport.core.mixin.access.MobEffectAccessor; +import net.minecraft.core.Registry; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; + +import java.util.function.Supplier; + +public class WBMobEffects { + public static final CoreRegistry EFFECTS = CoreRegistry.create(Registry.MOB_EFFECT, WildBackport.MOD_ID); + + public static final Supplier DARKNESS = EFFECTS.register("darkness", () -> EffectFactor.of(MobEffectAccessor.createMobEffect(MobEffectCategory.HARMFUL, 2696993)).setFactorCalculationData(() -> new FactorCalculationData(22))); +} \ No newline at end of file diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/tag/WBBlockTags.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/tag/WBBlockTags.java index cab3b5c..4aac2f2 100644 --- a/common/src/main/java/com/cursedcauldron/wildbackport/common/tag/WBBlockTags.java +++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/tag/WBBlockTags.java @@ -20,4 +20,7 @@ public class WBBlockTags { // Deep Dark public static final TagKey SCULK_REPLACEABLE = TAGS.create("sculk_replaceable"); public static final TagKey SCULK_REPLACEABLE_WORLD_GEN = TAGS.create("sculk_replaceable_world_gen"); + + // Compatibility + public static final TagKey MUD = TAGS.create("mud"); } \ No newline at end of file diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/MobEffectAccessor.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/MobEffectAccessor.java new file mode 100644 index 0000000..f9e8c6d --- /dev/null +++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/MobEffectAccessor.java @@ -0,0 +1,14 @@ +package com.cursedcauldron.wildbackport.core.mixin.access; + +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(MobEffect.class) +public interface MobEffectAccessor { + @Invoker("") + static MobEffect createMobEffect(MobEffectCategory mobEffectCategory, int i) { + throw new UnsupportedOperationException(); + } +} diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/client/FogRendererMixin.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/client/FogRendererMixin.java new file mode 100644 index 0000000..6d0d13d --- /dev/null +++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/client/FogRendererMixin.java @@ -0,0 +1,82 @@ +package com.cursedcauldron.wildbackport.core.mixin.client; + +import com.cursedcauldron.wildbackport.common.effects.EffectFactor; +import com.cursedcauldron.wildbackport.common.registry.WBMobEffects; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.Camera; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.FogRenderer; +import net.minecraft.util.Mth; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.material.FogType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(FogRenderer.class) +public class FogRendererMixin { + @Shadow private static float fogRed; + @Shadow private static float fogGreen; + @Shadow private static float fogBlue; + private static float fogPartialTicks; + + @Inject(method = "setupColor", at = @At("TAIL")) + private static void applyDarknessColor(Camera camera, float partialTicks, ClientLevel level, int viewDistance, float skyDarkness, CallbackInfo ci) { + FogType type = camera.getFluidInCamera(); + fogPartialTicks = partialTicks; + + double colorModifier = (camera.getPosition().y - (double)level.getMinBuildHeight()) * level.getLevelData().getClearColorScale(); + if (camera.getEntity() instanceof LivingEntity living && living.hasEffect(WBMobEffects.DARKNESS.get())) { + MobEffectInstance effect = living.getEffect(WBMobEffects.DARKNESS.get()); + + if (effect != null) { + EffectFactor.Instance instance = EffectFactor.Instance.of(effect); + colorModifier = instance.getFactorCalculationData().isPresent() ? 1.0F - instance.getFactorCalculationData().get().lerp(living, partialTicks) : 0.0D; + } + } + + if (colorModifier < 1.0D && type != FogType.LAVA) { + if (colorModifier < 0.0D) { + colorModifier = 0.0D; + } + + colorModifier *= colorModifier; + fogRed = (float)((double)fogRed * colorModifier); + fogGreen = (float)((double)fogGreen * colorModifier); + fogBlue = (float)((double)fogBlue * colorModifier); + } + + if (skyDarkness > 0.0F) { + fogRed = fogRed * (1.0F - skyDarkness) + fogRed * 0.7F * skyDarkness; + fogGreen = fogGreen * (1.0F - skyDarkness) + fogGreen * 0.6F * skyDarkness; + fogBlue = fogBlue * (1.0F - skyDarkness) + fogBlue * 0.6F * skyDarkness; + } + + RenderSystem.clearColor(fogRed, fogGreen, fogBlue, 0.0F); + } + + @Inject(method = "setupFog", at = @At("TAIL"), remap = false) + private static void applyDarknessFog(Camera camera, FogRenderer.FogMode mode, float viewDistance, boolean thickFog, CallbackInfo ci) { + FogType fogtype = camera.getFluidInCamera(); + + if (fogtype != FogType.WATER) { + if (camera.getEntity() instanceof LivingEntity living && living.hasEffect(WBMobEffects.DARKNESS.get())) { + MobEffectInstance effect = living.getEffect(WBMobEffects.DARKNESS.get()); + + if (effect != null) { + EffectFactor.Instance instance = EffectFactor.Instance.of(effect); + if (instance.getFactorCalculationData().isPresent()) { + float modifier = Mth.lerp(instance.getFactorCalculationData().get().lerp(living, fogPartialTicks), viewDistance, 15.0F); + float start = mode == FogRenderer.FogMode.FOG_SKY ? 0.0F : modifier * 0.75F; + + RenderSystem.setShaderFogStart(start); + RenderSystem.setShaderFogEnd(modifier); + } + } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/client/LightTextureMixin.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/client/LightTextureMixin.java new file mode 100644 index 0000000..b17fe6a --- /dev/null +++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/client/LightTextureMixin.java @@ -0,0 +1,133 @@ +package com.cursedcauldron.wildbackport.core.mixin.client; + +import com.cursedcauldron.wildbackport.common.effects.EffectFactor; +import com.cursedcauldron.wildbackport.common.registry.WBMobEffects; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.math.Vector3f; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.util.Mth; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(LightTexture.class) +public abstract class LightTextureMixin { + @Shadow @Final private Minecraft minecraft; + @Shadow private boolean updateLightTexture; + @Shadow private float blockLightRedFlicker; + @Shadow protected abstract float getBrightness(Level level, int i); + @Shadow @Final private DynamicTexture lightTexture; + @Shadow @Final private NativeImage lightPixels; + @Shadow protected abstract float notGamma(float f); + @Shadow @Final private GameRenderer renderer; + + private LocalPlayer getPlayer() { + assert this.minecraft.player != null; + return this.minecraft.player; + } + + private float getDarknessFactor(float delta) { + MobEffectInstance instance = this.getPlayer().getEffect(WBMobEffects.DARKNESS.get()); + if (this.getPlayer().hasEffect(WBMobEffects.DARKNESS.get()) && instance != null && EffectFactor.Instance.of(instance).getFactorCalculationData().isPresent()) { + return EffectFactor.Instance.of(instance).getFactorCalculationData().get().lerp(this.getPlayer(), delta); + } else { + return 0.0F; + } + } + + private float getDarkness(LivingEntity entity, float factor, float delta) { + return Math.max(0.0F, Mth.cos(((float)entity.tickCount - delta) * (float) Math.PI * 0.025F) * 0.45F * factor); + } + + //TODO simplify + @Inject(method = "updateLightTexture", at = @At("HEAD")) + private void updateLight(float delta, CallbackInfo ci) { + if (this.updateLightTexture) { + this.updateLightTexture = false; + ClientLevel level = this.minecraft.level; + if (level != null) { + float skyDarken = level.getSkyDarken(1.0F); + float skyFlashTime = level.getSkyFlashTime() > 0 ? 1.0F : skyDarken * 0.95F + 0.05F; + float darknessFactor = this.getDarknessFactor(delta); + float darkness = this.getDarkness(this.getPlayer(), darknessFactor, delta); + float waterVision = this.getPlayer().getWaterVision(); + float visionScale = this.getPlayer().hasEffect(MobEffects.NIGHT_VISION) ? GameRenderer.getNightVisionScale(this.getPlayer(), delta) : (waterVision > 0.0F && this.getPlayer().hasEffect(MobEffects.CONDUIT_POWER) ? waterVision : 0.0F); + Vector3f vec3f = new Vector3f(skyDarken, skyDarken, 1.0F); + vec3f.lerp(new Vector3f(1.0F, 1.0F, 1.0F), 0.35F); + float blockLightFlicker = this.blockLightRedFlicker + 1.5F; + Vector3f vec3f2 = new Vector3f(); + + for (int skyLight = 0; skyLight < 16; ++skyLight) { + for (int blockLight = 0; blockLight < 16; ++blockLight) { + float skyBrightness = this.getBrightness(level, skyLight) * skyFlashTime; + float blockBrightness = this.getBrightness(level, blockLight) * blockLightFlicker; + float yLight = blockBrightness * ((blockBrightness * 0.6F + 0.4F) * 0.6F + 0.4F); + float xzLight = blockBrightness * (blockBrightness * blockBrightness * 0.6F + 0.4F); + vec3f2.set(blockBrightness, yLight, xzLight); + boolean forceLightmap = level.effects().forceBrightLightmap(); + + if (forceLightmap) { + vec3f2.lerp(new Vector3f(0.99F, 1.12F, 1.0F), 0.25F); + vec3f2.clamp(0.0F, 1.0F); + } else { + Vector3f vec3f3 = vec3f.copy(); + vec3f3.mul(skyBrightness); + vec3f2.add(vec3f3); + vec3f2.lerp(new Vector3f(0.75F, 0.75F, 0.75F), 0.04F); + if (this.renderer.getDarkenWorldAmount(delta) > 0.0f) { + float darkenWorldAmount = this.renderer.getDarkenWorldAmount(delta); + Vector3f vec3f4 = vec3f2.copy(); + vec3f4.mul(0.7F, 0.6F, 0.6F); + vec3f2.lerp(vec3f4, darkenWorldAmount); + } + } + + if (visionScale > 0.0F) { + float modifier = Math.max(vec3f2.x(), Math.max(vec3f2.y(), vec3f2.z())); + if (modifier < 1.0F) { + float scale = 1.0F / modifier; + Vector3f vec3f3 = vec3f2.copy(); + vec3f3.mul(scale); + vec3f2.lerp(vec3f3, visionScale); + } + } + + if (!forceLightmap) { + if (darkness > 0.0F) { + vec3f2.add(-darkness, -darkness, -darkness); + } + + vec3f2.clamp(0.0F, 1.0F); + } + + float gamma = (float)this.minecraft.options.gamma; + Vector3f vec3f3 = vec3f2.copy(); + vec3f3.map(this::notGamma); + vec3f2.lerp(vec3f3, Math.max(0.0F, gamma - darknessFactor)); + vec3f2.lerp(new Vector3f(0.75F, 0.75F, 0.75F), 0.04F); + vec3f2.clamp(0.0F, 1.0F); + vec3f2.mul(255.0F); + int x = (int)vec3f2.x(); + int y = (int)vec3f2.y(); + int z = (int)vec3f2.z(); + this.lightPixels.setPixelRGBA(blockLight, skyLight, -16777216 | z << 16 | y << 8 | x); + } + } + + this.lightTexture.upload(); + } + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/common/MobEffectMixin.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/common/MobEffectMixin.java new file mode 100644 index 0000000..8abd367 --- /dev/null +++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/common/MobEffectMixin.java @@ -0,0 +1,90 @@ +package com.cursedcauldron.wildbackport.core.mixin.common; + +import com.cursedcauldron.wildbackport.WildBackport; +import com.cursedcauldron.wildbackport.common.effects.EffectFactor; +import com.cursedcauldron.wildbackport.common.effects.FactorCalculationData; +import com.mojang.serialization.Dynamic; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Optional; +import java.util.function.Supplier; + +@Mixin(MobEffect.class) +public class MobEffectMixin implements EffectFactor { + private Supplier factorCalculationData = () -> null; + + @Override + public MobEffect setFactorCalculationData(Supplier data) { + this.factorCalculationData = data; + return MobEffect.class.cast(this); + } + + @Override + public Supplier getFactorCalculationData() { + return this.factorCalculationData; + } + + @Mixin(MobEffectInstance.class) + public static class MobEffectInstanceMixin implements EffectFactor.Instance { + @Shadow @Final private MobEffect effect; + @Shadow private int duration; + private Optional factorCalculationData; + + @Inject(method = "(Lnet/minecraft/world/effect/MobEffect;IIZZZLnet/minecraft/world/effect/MobEffectInstance;)V", at = @At("TAIL")) + private void wb$create(MobEffect effect, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon, MobEffectInstance hiddenEffect, CallbackInfo ci) { + this.setFactorCalculationData(EffectFactor.create(effect)); + } + + @Inject(method = "(Lnet/minecraft/world/effect/MobEffectInstance;)V", at = @At("TAIL")) + private void wb$create(MobEffectInstance instance, CallbackInfo ci) { + this.setFactorCalculationData(EffectFactor.create(this.effect)); + } + + @Override + public void setFactorCalculationData(Optional data) { + this.factorCalculationData = data; + } + + @Override + public Optional getFactorCalculationData() { + return this.factorCalculationData; + } + + @Inject(method = "update", at = @At("HEAD"), cancellable = true) + private void wb$update(MobEffectInstance instance, CallbackInfoReturnable cir) { + int i = instance.getDuration(); +// int i = this.duration; + if (i != this.duration) { + this.factorCalculationData.ifPresent(data -> data.effectChangedTimestamp += this.duration - i); + cir.setReturnValue(true); + } + } + + @Inject(method = "tick", at = @At("HEAD")) + private void wb$tick(LivingEntity entity, Runnable runnable, CallbackInfoReturnable cir) { + this.factorCalculationData.ifPresent(data -> data.update(MobEffectInstance.class.cast(this))); + } + + @Inject(method = "writeDetailsTo", at = @At("TAIL")) + private void wb$write(CompoundTag tag, CallbackInfo ci) { + this.factorCalculationData.flatMap(instance -> FactorCalculationData.CODEC.encodeStart(NbtOps.INSTANCE, instance).resultOrPartial(WildBackport.LOGGER::error)).ifPresent(data -> tag.put("FactorCalculationData", data)); + } + + @Inject(method = "loadSpecifiedEffect", at = @At("TAIL")) + private static void wb$load(MobEffect effect, CompoundTag tag, CallbackInfoReturnable cir) { + Optional data = tag.contains("FactorCalculationData", 10) ? FactorCalculationData.CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, tag.getCompound("FactorCalculationData"))).resultOrPartial(WildBackport.LOGGER::error) : Optional.empty(); + EffectFactor.Instance.of(cir.getReturnValue()).setFactorCalculationData(data); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/network/ClientPacketListenerMixin.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/network/ClientPacketListenerMixin.java index 4d06c04..0e7a510 100644 --- a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/network/ClientPacketListenerMixin.java +++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/network/ClientPacketListenerMixin.java @@ -1,8 +1,44 @@ package com.cursedcauldron.wildbackport.core.mixin.network; +import com.cursedcauldron.wildbackport.common.effects.EffectFactor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.PacketUtils; +import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Optional; @Mixin(ClientPacketListener.class) public class ClientPacketListenerMixin { + @Shadow private ClientLevel level; + @Shadow @Final private Minecraft minecraft; + + //TODO simplify + @Inject(method = "handleUpdateMobEffect", at = @At("HEAD"), cancellable = true) + private void wb$updateEffect(ClientboundUpdateMobEffectPacket packet, CallbackInfo ci) { + PacketUtils.ensureRunningOnSameThread(packet, ClientPacketListener.class.cast(this), this.minecraft); + Entity entity = this.level.getEntity(packet.getEntityId()); + if (entity instanceof LivingEntity living) { + MobEffect effect = MobEffect.byId(packet.getEffectId() & 0xFF); + if (effect != null) { + MobEffectInstance instance = new MobEffectInstance(effect, packet.getEffectDurationTicks(), packet.getEffectAmplifier(), packet.isEffectAmbient(), packet.isEffectVisible(), packet.effectShowsIcon()); + instance.setNoCounter(packet.isSuperLongDuration()); + EffectFactor.Instance.of(instance).setFactorCalculationData(Optional.ofNullable(EffectFactor.Network.of(packet).getFactorCalculationData())); + living.forceAddEffect(instance, null); + } + } + + ci.cancel(); + } } \ No newline at end of file diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/network/ClientboundUpdateMobEffectPacketMixin.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/network/ClientboundUpdateMobEffectPacketMixin.java new file mode 100644 index 0000000..df30bfc --- /dev/null +++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/network/ClientboundUpdateMobEffectPacketMixin.java @@ -0,0 +1,53 @@ +package com.cursedcauldron.wildbackport.core.mixin.network; + +import com.cursedcauldron.wildbackport.common.effects.EffectFactor; +import com.cursedcauldron.wildbackport.common.effects.FactorCalculationData; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket; +import net.minecraft.world.effect.MobEffectInstance; +import org.jetbrains.annotations.Nullable; +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; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +@Mixin(ClientboundUpdateMobEffectPacket.class) +public class ClientboundUpdateMobEffectPacketMixin implements EffectFactor.Network { + private FactorCalculationData factorCalculationData; + + @Inject(method = "(ILnet/minecraft/world/effect/MobEffectInstance;)V", at = @At("TAIL")) + private void create(int id, MobEffectInstance instance, CallbackInfo ci) { + this.factorCalculationData = EffectFactor.Instance.of(instance).getFactorCalculationData().orElse(null); + } + + @Inject(method = "(Lnet/minecraft/network/FriendlyByteBuf;)V", at = @At("TAIL")) + private void create(FriendlyByteBuf buf, CallbackInfo ci) { + this.factorCalculationData = this.readNullable(buf, buffer -> buffer.readWithCodec(FactorCalculationData.CODEC)); + } + + @Inject(method = "write", at = @At("TAIL")) + private void writeData(FriendlyByteBuf buf, CallbackInfo ci) { + this.writeNullable(buf, this.factorCalculationData, (buffer, data) -> buffer.writeWithCodec(FactorCalculationData.CODEC, data)); + } + + @Override + public FactorCalculationData getFactorCalculationData() { + return this.factorCalculationData; + } + + private T readNullable(FriendlyByteBuf buf, Function consumer) { + return buf.readBoolean() ? consumer.apply(buf) : null; + } + + private void writeNullable(FriendlyByteBuf buf, @Nullable T entry, BiConsumer consumer) { + if (entry != null) { + buf.writeBoolean(true); + consumer.accept(buf, entry); + } else { + buf.writeBoolean(false); + } + } +} \ No newline at end of file diff --git a/common/src/main/resources/data/wildbackport/tags/blocks/mud.json b/common/src/main/resources/data/wildbackport/tags/blocks/mud.json new file mode 100644 index 0000000..0bdc8fc --- /dev/null +++ b/common/src/main/resources/data/wildbackport/tags/blocks/mud.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "wildbackport:mud" + ] +} \ No newline at end of file diff --git a/common/src/main/resources/wildbackport-common.mixins.json b/common/src/main/resources/wildbackport-common.mixins.json index 57ca190..00f5a83 100644 --- a/common/src/main/resources/wildbackport-common.mixins.json +++ b/common/src/main/resources/wildbackport-common.mixins.json @@ -11,6 +11,7 @@ "access.DoorBlockAccessor", "access.FireBlockAccessor", "access.MemoryModuleTypeAccessor", + "access.MobEffectAccessor", "access.ModelPartAccessor", "access.OverworldBiomesAccessor", "access.PointedDripstoneBlockAccessor", @@ -32,6 +33,8 @@ "common.BlockEntityTypeMixin", "common.FlyNodeEvaluatorMixin", "common.LivingEntityMixin", + "common.MobEffectMixin", + "common.MobEffectMixin$MobEffectInstanceMixin", "common.NoteBlockMixin", "common.PathfinderMobMixin", "common.PlayerMixin", @@ -40,9 +43,12 @@ "common.SculkSensorBlockMixin", "extension.BoatTypeMixin", "extension.PoseMixin", + "network.ClientboundUpdateMobEffectPacketMixin", "network.ServerGamePacketListenerImplMixin" ], "client": [ + "client.FogRendererMixin", + "client.LightTextureMixin", "client.LocalPlayerMixin", "client.ModelPartMixin", "client.PartDefinitionMixin",