From 0bfe52a3ee46eac1c2b0e3c443d13f353bf2c3f9 Mon Sep 17 00:00:00 2001 From: fretuks Date: Sat, 15 Nov 2025 22:02:08 +0100 Subject: [PATCH] Make sanity more intense --- .../fretux/mindmotion/AscendMindMotion.java | 9 ++ .../mindmotion/client/SanityTempoOverlay.java | 6 - .../client/effects/SanityShaderHandler.java | 42 +++++- .../mindmotion/command/SanityCommand.java | 41 ++++++ .../mindmotion/event/PlayerTickHandler.java | 121 +++++++++++++++++- .../mindmotion/shaders/program/desaturate.fsh | 45 +++++-- .../shaders/program/desaturate.json | 24 ++++ .../mindmotion/shaders/program/desaturate.vsh | 17 ++- 8 files changed, 274 insertions(+), 31 deletions(-) create mode 100644 src/main/java/net/fretux/mindmotion/command/SanityCommand.java diff --git a/src/main/java/net/fretux/mindmotion/AscendMindMotion.java b/src/main/java/net/fretux/mindmotion/AscendMindMotion.java index cee01cd..0df212e 100644 --- a/src/main/java/net/fretux/mindmotion/AscendMindMotion.java +++ b/src/main/java/net/fretux/mindmotion/AscendMindMotion.java @@ -1,11 +1,14 @@ package net.fretux.mindmotion; import com.mojang.logging.LogUtils; +import net.fretux.mindmotion.command.SanityCommand; import net.fretux.mindmotion.event.CombatEvents; import net.fretux.mindmotion.event.PlayerTickHandler; import net.fretux.mindmotion.network.ModMessages; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @@ -19,6 +22,7 @@ public class AscendMindMotion { public AscendMindMotion() { IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); modEventBus.addListener(this::commonSetup); + MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(PlayerTickHandler.class); MinecraftForge.EVENT_BUS.register(CombatEvents.class); LOGGER.info("Ascend: Mind and Motion initialized!"); @@ -27,4 +31,9 @@ public class AscendMindMotion { private void commonSetup(final FMLCommonSetupEvent event) { ModMessages.register(); } + + @SubscribeEvent + public static void registerCommands(RegisterCommandsEvent event) { + SanityCommand.register(event.getDispatcher()); + } } \ No newline at end of file diff --git a/src/main/java/net/fretux/mindmotion/client/SanityTempoOverlay.java b/src/main/java/net/fretux/mindmotion/client/SanityTempoOverlay.java index 93cf7c0..0d7e51d 100644 --- a/src/main/java/net/fretux/mindmotion/client/SanityTempoOverlay.java +++ b/src/main/java/net/fretux/mindmotion/client/SanityTempoOverlay.java @@ -41,16 +41,10 @@ public class SanityTempoOverlay { int insanityFill = (int) (BAR_WIDTH * insanityPercent); RenderSystem.enableBlend(); - - // --- TEMPO BAR --- drawRoundedBar(gui, xTempo, y, BAR_WIDTH, BAR_HEIGHT, 0xFF33AFFF, tempoFill); gui.drawString(mc.font, Component.literal("Tempo"), xTempo, y - 10, 0x66CCFF, false); - - // --- SANITY OR INSANITY BAR --- boolean insaneMode = sanity <= 0; - if (!insaneMode) { - // Normal sanity bar drawRoundedBar(gui, xSanity, y, BAR_WIDTH, BAR_HEIGHT, 0xFFFFFFFF, sanityFill); gui.drawString(mc.font, Component.literal("Sanity"), xSanity + BAR_WIDTH - 30, y - 10, 0xFFFFFF, false); } else { diff --git a/src/main/java/net/fretux/mindmotion/client/effects/SanityShaderHandler.java b/src/main/java/net/fretux/mindmotion/client/effects/SanityShaderHandler.java index 82d9d6a..41bc60f 100644 --- a/src/main/java/net/fretux/mindmotion/client/effects/SanityShaderHandler.java +++ b/src/main/java/net/fretux/mindmotion/client/effects/SanityShaderHandler.java @@ -93,15 +93,25 @@ public class SanityShaderHandler { float blur = 0f; float vig = 0f; float fog = 0f; - + float flash = 0f; if (sanityPct < 0.5f) { - blur = (0.5f - sanityPct) * 0.6f; - vig = (0.5f - sanityPct) * 0.4f; - fog = (0.5f - sanityPct) * 0.25f; + blur = (0.5f - sanityPct) * 0.6f; + vig = (0.5f - sanityPct) * 0.4f; + fog = (0.5f - sanityPct) * 0.25f; } - blur += insanityPct * 0.25f; - vig += insanityPct * 0.35f; - fog += insanityPct * 0.35f; + if (insanityPct >= 0.8f) { + float level = (insanityPct - 0.8f) / 0.2f; + flash = 0.5f * level; + if (mc.player.getRandom().nextFloat() < 0.01f) { + flash += 0.4f; + } + flash = Math.min(flash, 1f); + } + var uFlash = effect.getUniform("FlashStrength"); + if (uFlash != null) uFlash.set(flash); + blur += insanityPct * 0.25f; + vig += insanityPct * 0.35f; + fog += insanityPct * 0.35f; blur = Math.min(blur, 1f); vig = Math.min(vig, 1f); fog = Math.min(fog, 1f); @@ -114,6 +124,24 @@ public class SanityShaderHandler { var uFog = effect.getUniform("FogStrength"); if (uFog != null) uFog.set(fog); + float shake = 0f; + float pulse = 0f; + if (sanityPct <= 0.125f) { + float lowSan = (0.125f - sanityPct) / 0.125f; + shake = lowSan * 0.010f; + pulse = lowSan * 0.6f; + } + shake += insanityPct * 0.012f; + pulse += insanityPct * 0.8f; + shake = Math.min(shake, 0.025f); + pulse = Math.min(pulse, 1f); + var uShake = effect.getUniform("ShakeStrength"); + if (uShake != null) uShake.set(shake); + var uPulse = effect.getUniform("PulseStrength"); + if (uPulse != null) uPulse.set(pulse); + float time = (mc.level.getGameTime() % 200000) / 20f; + var uTime = effect.getUniform("Time"); + if (uTime != null) uTime.set(time); } } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/net/fretux/mindmotion/command/SanityCommand.java b/src/main/java/net/fretux/mindmotion/command/SanityCommand.java new file mode 100644 index 0000000..dbdf124 --- /dev/null +++ b/src/main/java/net/fretux/mindmotion/command/SanityCommand.java @@ -0,0 +1,41 @@ +package net.fretux.mindmotion.command; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.FloatArgumentType; +import net.fretux.mindmotion.player.PlayerCapabilityProvider; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; + +public class SanityCommand { + + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("sanity") + .requires(source -> source.hasPermission(2)) // OP only + .then(Commands.literal("set") + .then(Commands.argument("sanity", FloatArgumentType.floatArg(0, 80)) + .then(Commands.argument("insanity", FloatArgumentType.floatArg(0, 80)) + .executes(ctx -> { + ServerPlayer player = ctx.getSource().getPlayer(); + float sanity = FloatArgumentType.getFloat(ctx, "sanity"); + float insanity = FloatArgumentType.getFloat(ctx, "insanity"); + + player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(cap -> { + cap.setSanity(sanity); + cap.setInsanity(insanity); + }); + + ctx.getSource().sendSuccess(() -> + net.minecraft.network.chat.Component.literal( + "Set sanity = " + sanity + ", insanity = " + insanity), + true + ); + + return 1; + }) + ) + ) + ) + ); + } +} diff --git a/src/main/java/net/fretux/mindmotion/event/PlayerTickHandler.java b/src/main/java/net/fretux/mindmotion/event/PlayerTickHandler.java index d31d162..982eca1 100644 --- a/src/main/java/net/fretux/mindmotion/event/PlayerTickHandler.java +++ b/src/main/java/net/fretux/mindmotion/event/PlayerTickHandler.java @@ -4,6 +4,7 @@ import net.fretux.mindmotion.network.ModMessages; import net.fretux.mindmotion.network.SyncStatsS2CPacket; import net.fretux.mindmotion.player.PlayerCapabilityProvider; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.BiomeTags; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.player.Player; @@ -23,6 +24,8 @@ public class PlayerTickHandler { private static final int TEMPO_TICK_INTERVAL = 15; private static final int COMBAT_DECAY_DELAY = 100; private static final Map lastCombatTick = new ConcurrentHashMap<>(); + private static final Map insanityDamageTicks = new ConcurrentHashMap<>(); + private static final Map lowSanitySoundCooldown = new ConcurrentHashMap<>(); @SubscribeEvent public static void onPlayerTick(TickEvent.PlayerTickEvent event) { @@ -58,7 +61,7 @@ public class PlayerTickHandler { float value = sanity.getSanity(); float insanity = sanity.getInsanity(); float delta = 0f; - boolean inDark = player.level().getMaxLocalRawBrightness(player.blockPosition()) < 5; + boolean inDark = player.level().getMaxLocalRawBrightness(player.blockPosition()) < 8; boolean inRain = player.level().isRainingAt(player.blockPosition()); boolean isHungry = player.getFoodData().getFoodLevel() < 6; boolean nearLava = player.level().getBlockState(player.blockPosition().below()).is(Blocks.LAVA); @@ -67,26 +70,85 @@ public class PlayerTickHandler { boolean sleeping = player.isSleeping(); boolean inLight = player.level().getMaxLocalRawBrightness(player.blockPosition()) > 12; boolean nearTorch = player.level().getBlockState(player.blockPosition().above()).is(Blocks.TORCH); - if (inDark) delta -= 2f; + if (inDark) delta -= 0.02f; if (inRain) delta -= 0.04f; if (isHungry) delta -= 0.06f; if (nearLava) delta -= 0.05f; if (underWater) delta -= 0.04f; if (inWater && !underWater) delta -= 0.02f; + int light = player.level().getMaxLocalRawBrightness(player.blockPosition()); + if (light <= 2) delta -= 0.10f; + else if (light <= 5) delta -= 0.05f; + if (player.level().isThundering()) delta -= 0.06f; + if (player.level().getBiome(player.blockPosition()) + .is(BiomeTags.IS_END)) { + delta -= 0.02f; + } + if (player.level().getBiome(player.blockPosition()) + .is(BiomeTags.IS_NETHER)) { + delta -= 0.015f; + } + if (player.level().dimension() == net.minecraft.world.level.Level.NETHER) + delta -= 0.12f; + if (player.level().dimension() == net.minecraft.world.level.Level.END) + delta -= 0.08f; + if (!player.level().getEntities(player, player.getBoundingBox().inflate(30), + e -> e.getType().toString().contains("warden")).isEmpty()) { + delta -= 0.15f; + } + long hostileCount = player.level().getEntities(player, + player.getBoundingBox().inflate(10), + e -> e instanceof net.minecraft.world.entity.Mob && !(e instanceof net.minecraft.world.entity.animal.Animal) + ).size(); + delta -= hostileCount * 0.005f; if (inLight) delta += 0.03f; if (nearTorch) delta += 0.05f; if (sleeping) delta += 0.25f; if (player.getUseItem() != null && player.getUseItem().isEdible()) delta += 0.05f; + if (player.level().getBlockState(player.blockPosition().below()) + .is(net.minecraft.world.level.block.Blocks.CAMPFIRE)) { + delta += 0.08f; + } + if (player.level().getBlockState(player.blockPosition().below()) + .is(Blocks.FURNACE) || (player.level().getBlockState(player.blockPosition().below()) + .is(Blocks.BLAST_FURNACE)) || (player.level().getBlockState(player.blockPosition().below()) + .is(Blocks.SMOKER))) { + delta += 0.04f; + } + long animalCount = player.level().getEntities(player, + player.getBoundingBox().inflate(6), + e -> e instanceof net.minecraft.world.entity.animal.Animal + ).size(); + delta += animalCount * 0.01f; + long villagerCount = player.level().getEntities(player, + player.getBoundingBox().inflate(8), + e -> e instanceof net.minecraft.world.entity.npc.Villager + ).size(); + delta += villagerCount * 0.02f; + if (player.level().getBlockState(player.blockPosition().below()).is(Blocks.BEACON)) + delta += 0.15f; + if (player.getMainHandItem().is(Blocks.POPPY.asItem()) + || player.getMainHandItem().is(Blocks.DANDELION.asItem())) { + delta += 0.04f; + } + if (player.isSleepingLongEnough()) + delta += 0.10f; sanity.setSanity(value + delta); - if (sanity.getSanity() <= 0) { - sanity.setSanity(0); + if (sanity.getSanity() > 0) { + sanity.setInsanity(Math.max(0, insanity - 0.15f)); + insanityDamageTicks.remove(player.getUUID()); + } else { sanity.setInsanity(Math.min(sanity.getMaxSanity(), insanity + 0.3f)); if (sanity.getInsanity() >= sanity.getMaxSanity()) { - player.kill(); + UUID id = player.getUUID(); + int ticks = insanityDamageTicks.getOrDefault(id, 0) + SANITY_TICK_INTERVAL; + insanityDamageTicks.put(id, ticks); + + float damage = 1.0f + (ticks / 100f); + player.hurt(player.damageSources().magic(), damage); + player.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 40, 1)); return; } - } else if (sanity.getInsanity() > 0) { - sanity.setInsanity(Math.max(0, insanity - 0.15f)); } float percent = (value / sanity.getMaxSanity()) * 100f; if (percent <= 70 && Math.random() < 0.03) { @@ -107,6 +169,17 @@ public class PlayerTickHandler { applyScratching(player); } } + if (percent <= 30f) { + UUID id = player.getUUID(); + int cd = lowSanitySoundCooldown.getOrDefault(id, 0) - SANITY_TICK_INTERVAL; + if (cd <= 0) { + playSanitySound(player); + cd = 20 * (6 + player.getRandom().nextInt(15)); + } + lowSanitySoundCooldown.put(id, cd); + } else { + lowSanitySoundCooldown.remove(player.getUUID()); + } }); } @@ -141,4 +214,38 @@ public class PlayerTickHandler { player.hurt(player.damageSources().magic(), 2f); applyShiver(player); } + + private static void playSanitySound(Player player) { + var level = player.level(); + if (level.isClientSide) return; + var sounds = new net.minecraft.sounds.SoundEvent[]{ + net.minecraft.sounds.SoundEvents.ZOMBIE_AMBIENT, + net.minecraft.sounds.SoundEvents.ENDERMAN_STARE, + net.minecraft.sounds.SoundEvents.SPIDER_AMBIENT, + net.minecraft.sounds.SoundEvents.CREEPER_PRIMED, + net.minecraft.sounds.SoundEvents.HUSK_AMBIENT, + net.minecraft.sounds.SoundEvents.WARDEN_ANGRY + }; + net.minecraft.sounds.SoundEvent chosen = + sounds[player.getRandom().nextInt(sounds.length)]; + var holder = net.minecraft.core.registries.BuiltInRegistries.SOUND_EVENT.wrapAsHolder(chosen); + float yaw = player.getYRot(); + double angleRad = Math.toRadians(yaw + 180 + player.getRandom().nextInt(40) - 20); + double dist = 4.0 + player.getRandom().nextDouble() * 3.0; + double dx = player.getX() + Math.cos(angleRad) * dist; + double dz = player.getZ() + Math.sin(angleRad) * dist; + double dy = player.getY(); + if (player instanceof ServerPlayer sp) { + sp.connection.send( + new net.minecraft.network.protocol.game.ClientboundSoundPacket( + holder, + net.minecraft.sounds.SoundSource.AMBIENT, + dx, dy, dz, + 1.0f, + 0.9f + player.getRandom().nextFloat() * 0.2f, + player.getRandom().nextLong() + ) + ); + } + } } \ No newline at end of file diff --git a/src/main/resources/assets/mindmotion/shaders/program/desaturate.fsh b/src/main/resources/assets/mindmotion/shaders/program/desaturate.fsh index 86c3e44..e7cb718 100644 --- a/src/main/resources/assets/mindmotion/shaders/program/desaturate.fsh +++ b/src/main/resources/assets/mindmotion/shaders/program/desaturate.fsh @@ -5,6 +5,9 @@ uniform float Desat; uniform float BlurAmount; uniform float Vignette; uniform float FogStrength; +uniform float PulseStrength; +uniform float Time; +uniform float FlashStrength; in vec2 texCoord; in vec2 oneTexel; @@ -17,29 +20,53 @@ vec4 sampleBlur() { float w = BlurAmount * 0.5; vec4 sum = vec4(0.0); - sum += texture(DiffuseSampler, texCoord + oneTexel * vec2(-1, 0)) * w; - sum += texture(DiffuseSampler, texCoord + oneTexel * vec2( 1, 0)) * w; - sum += texture(DiffuseSampler, texCoord + oneTexel * vec2( 0, -1)) * w; - sum += texture(DiffuseSampler, texCoord + oneTexel * vec2( 0, 1)) * w; + sum += texture(DiffuseSampler, texCoord + oneTexel * vec2(-1, 0)) * w; + sum += texture(DiffuseSampler, texCoord + oneTexel * vec2( 1, 0)) * w; + sum += texture(DiffuseSampler, texCoord + oneTexel * vec2( 0,-1)) * w; + sum += texture(DiffuseSampler, texCoord + oneTexel * vec2( 0, 1)) * w; vec4 center = texture(DiffuseSampler, texCoord) * (1.0 - w * 4.0); - return center + sum; } void main() { vec4 col = texture(DiffuseSampler, texCoord); + + // Blur vec4 blurred = sampleBlur(); col = mix(col, blurred, BlurAmount); + // Grayscale float gray = dot(col.rgb, vec3(0.2126, 0.7152, 0.0722)); col.rgb = mix(col.rgb, vec3(gray), clamp(Desat, 0.0, 1.0)); - col.rgb = mix(col.rgb, vec3(0.75, 0.75, 0.8), FogStrength); + // Fog + col.rgb = mix(col.rgb, vec3(0.75), FogStrength); - float dist = distance(texCoord, vec2(0.5, 0.5)); - float vign = smoothstep(0.3, 0.7, dist); - col.rgb = mix(col.rgb, col.rgb * 0.4, vign * Vignette); + // Stronger base vignette + float dist = distance(texCoord, vec2(0.5)); + float vign = smoothstep(0.15, 0.6, dist); + col.rgb = mix(col.rgb, vec3(0.0), vign * Vignette); + // Pulsating vignette toward black + if (PulseStrength > 0.001) { + float pulse = (sin(Time * 4.0) * 0.5 + 0.5); + pulse *= PulseStrength; + + float pVig = smoothstep(0.2, 0.8, dist); + col.rgb = mix(col.rgb, vec3(0.0), pVig * pulse); + } + if (FlashStrength > 0.001) { + float wave = sin(Time * 12.0); // slower: 12 instead of 40 + float intensity = (abs(wave) * 0.65) * FlashStrength; + // max 25% brightness shift even at 100% insanity + + // shift colors subtly instead of full white/black + vec3 flashColor = wave > 0.0 + ? vec3(0.9, 0.9, 1.0) // pale bluish-white + : vec3(0.05, 0.05, 0.07); // deep blue-black + + col.rgb = mix(col.rgb, flashColor, intensity); + } fragColor = vec4(col.rgb, 1.0); } \ No newline at end of file diff --git a/src/main/resources/assets/mindmotion/shaders/program/desaturate.json b/src/main/resources/assets/mindmotion/shaders/program/desaturate.json index 2a0076a..29550c5 100644 --- a/src/main/resources/assets/mindmotion/shaders/program/desaturate.json +++ b/src/main/resources/assets/mindmotion/shaders/program/desaturate.json @@ -72,6 +72,30 @@ "type": "float", "count": 1, "values": [ 0.0 ] + }, + { + "name": "ShakeStrength", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "PulseStrength", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "Time", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "FlashStrength", + "type": "float", + "count": 1, + "values": [ 0.0 ] } ] } \ No newline at end of file diff --git a/src/main/resources/assets/mindmotion/shaders/program/desaturate.vsh b/src/main/resources/assets/mindmotion/shaders/program/desaturate.vsh index 8ea8d2a..b6345e7 100644 --- a/src/main/resources/assets/mindmotion/shaders/program/desaturate.vsh +++ b/src/main/resources/assets/mindmotion/shaders/program/desaturate.vsh @@ -4,11 +4,24 @@ in vec4 Position; uniform mat4 ProjMat; uniform vec2 OutSize; +uniform float ShakeStrength; +uniform float Time; out vec2 texCoord; void main() { - vec4 outPos = ProjMat * vec4(Position.xy, 0.0, 1.0); + + // screen-shake offset in *pixels* + float px = sin(Time * 25.0 + Position.x * 5.0) * ShakeStrength * 20.0; + float py = cos(Time * 20.0 + Position.y * 7.0) * ShakeStrength * 20.0; + + // convert from pixels → normalized coordinates + vec2 offset = vec2(px / OutSize.x, py / OutSize.y); + + vec2 shaken = Position.xy + offset; + + texCoord = shaken / OutSize; + + vec4 outPos = ProjMat * vec4(shaken, 0.0, 1.0); gl_Position = vec4(outPos.xy, 0.0, 1.0); - texCoord = Position.xy / OutSize; } \ No newline at end of file