implement sanity gameplay effects and improve insanity mechanics

This commit is contained in:
2026-05-20 00:36:16 +02:00
parent a7d8930bff
commit 16980bc3b4
4 changed files with 87 additions and 8 deletions
@@ -2,8 +2,7 @@ package net.fretux.mindmotion;
import com.mojang.logging.LogUtils; import com.mojang.logging.LogUtils;
import net.fretux.mindmotion.command.SanityCommand; import net.fretux.mindmotion.command.SanityCommand;
import net.fretux.mindmotion.event.CombatEvents; import net.fretux.mindmotion.event.CommonEvents;
import net.fretux.mindmotion.event.PlayerTickHandler;
import net.fretux.mindmotion.network.ModMessages; import net.fretux.mindmotion.network.ModMessages;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
@@ -28,8 +27,7 @@ public class AscendMindMotion {
net.fretux.mindmotion.client.ClientBootstrap.init(modEventBus) net.fretux.mindmotion.client.ClientBootstrap.init(modEventBus)
); );
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(PlayerTickHandler.class); MinecraftForge.EVENT_BUS.register(new CommonEvents());
MinecraftForge.EVENT_BUS.register(CombatEvents.class);
MinecraftForge.EVENT_BUS.register(SanityCommand.class); MinecraftForge.EVENT_BUS.register(SanityCommand.class);
LOGGER.info("Ascend: Mind and Motion initialized!"); LOGGER.info("Ascend: Mind and Motion initialized!");
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigMM.CLIENT_SPEC); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigMM.CLIENT_SPEC);
@@ -83,6 +83,7 @@ public final class ConfigMM {
public final ForgeConfigSpec.DoubleValue BASE_SANITY_REGEN_LIGHT; public final ForgeConfigSpec.DoubleValue BASE_SANITY_REGEN_LIGHT;
public final ForgeConfigSpec.DoubleValue BASE_SANITY_DECAY_DARK; public final ForgeConfigSpec.DoubleValue BASE_SANITY_DECAY_DARK;
public final ForgeConfigSpec.BooleanValue ENABLE_LOW_SANITY_SOUNDS; public final ForgeConfigSpec.BooleanValue ENABLE_LOW_SANITY_SOUNDS;
public final ForgeConfigSpec.BooleanValue ENABLE_SANITY_GAMEPLAY_EFFECTS;
public final ForgeConfigSpec.DoubleValue SCRATCHING_CHANCE; public final ForgeConfigSpec.DoubleValue SCRATCHING_CHANCE;
public final ForgeConfigSpec.DoubleValue PANIC_CHANCE; public final ForgeConfigSpec.DoubleValue PANIC_CHANCE;
public final ForgeConfigSpec.IntValue TEMPO_DECAY_DELAY; public final ForgeConfigSpec.IntValue TEMPO_DECAY_DELAY;
@@ -104,6 +105,10 @@ public final class ConfigMM {
.comment("Enable creepy noises when sanity is low.") .comment("Enable creepy noises when sanity is low.")
.define("enableLowSanitySounds", true); .define("enableLowSanitySounds", true);
ENABLE_SANITY_GAMEPLAY_EFFECTS = b
.comment("Enable gameplay penalties from low sanity and high insanity, beyond shaders and sounds.")
.define("enableSanityGameplayEffects", true);
b.pop().push("insanity"); b.pop().push("insanity");
SCRATCHING_CHANCE = b SCRATCHING_CHANCE = b
@@ -10,9 +10,9 @@ public class CommonEvents {
@SubscribeEvent @SubscribeEvent
public void onPlayerHurt(LivingHurtEvent event) { public void onPlayerHurt(LivingHurtEvent event) {
if (!(event.getEntity() instanceof net.minecraft.world.entity.player.Player player)) return; if (!(event.getEntity() instanceof net.minecraft.world.entity.player.Player player)) return;
if (player.level().isClientSide) return;
player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(sanity -> sanity.reduceSanity(1)); player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(sanity -> sanity.reduceSanity(1));
player.getCapability(PlayerCapabilityProvider.TEMPO).ifPresent(tempo -> tempo.addTempo(4));
} }
@SubscribeEvent @SubscribeEvent
@@ -20,7 +20,17 @@ public class CommonEvents {
event.getOriginal().reviveCaps(); event.getOriginal().reviveCaps();
event.getEntity().getCapability(PlayerCapabilityProvider.SANITY).ifPresent(newCap -> event.getEntity().getCapability(PlayerCapabilityProvider.SANITY).ifPresent(newCap ->
event.getOriginal().getCapability(PlayerCapabilityProvider.SANITY) event.getOriginal().getCapability(PlayerCapabilityProvider.SANITY)
.ifPresent(oldCap -> newCap.setSanity(oldCap.getSanity())) .ifPresent(oldCap -> {
newCap.setSanity(oldCap.getSanity());
newCap.setInsanity(oldCap.getInsanity());
})
);
event.getEntity().getCapability(PlayerCapabilityProvider.TEMPO).ifPresent(newCap ->
event.getOriginal().getCapability(PlayerCapabilityProvider.TEMPO)
.ifPresent(oldCap -> {
newCap.setTempo(oldCap.getTempo());
newCap.setVentCooldown(oldCap.getVentCooldown());
})
); );
event.getOriginal().invalidateCaps(); event.getOriginal().invalidateCaps();
} }
@@ -9,6 +9,7 @@ import net.minecraft.tags.BiomeTags;
import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects; import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
@@ -189,7 +190,11 @@ public class PlayerTickHandler {
insanity = sanity.getInsanity(); insanity = sanity.getInsanity();
} }
} }
float percent = (value / sanity.getMaxSanity()) * 100f; float percent = (sanity.getSanity() / sanity.getMaxSanity()) * 100f;
float insanityPercent = (insanity / sanity.getMaxSanity()) * 100f;
if (ConfigMM.COMMON.ENABLE_SANITY_GAMEPLAY_EFFECTS.get()) {
applySanityGameplayPressure(player, percent, insanityPercent);
}
if (percent <= 70 && Math.random() < 0.03) { if (percent <= 70 && Math.random() < 0.03) {
applyShiver(player); applyShiver(player);
} }
@@ -208,7 +213,6 @@ public class PlayerTickHandler {
if (percent <= 50 && Math.random() < ConfigMM.COMMON.PANIC_CHANCE.get()) { if (percent <= 50 && Math.random() < ConfigMM.COMMON.PANIC_CHANCE.get()) {
applyPanic(player); applyPanic(player);
} }
float insanityPercent = (insanity / sanity.getMaxSanity()) * 100f;
if (insanityPercent > 0) { if (insanityPercent > 0) {
if (insanityPercent > 60 && Math.random() < ConfigMM.COMMON.PANIC_CHANCE.get()) { if (insanityPercent > 60 && Math.random() < ConfigMM.COMMON.PANIC_CHANCE.get()) {
applyPanic(player); applyPanic(player);
@@ -240,6 +244,68 @@ public class PlayerTickHandler {
}); });
} }
private static void applySanityGameplayPressure(Player player, float sanityPercent, float insanityPercent) {
float lowSanity = clamp01((55f - sanityPercent) / 55f);
float highInsanity = clamp01((insanityPercent - 35f) / 65f);
float pressure = Math.max(lowSanity, highInsanity);
if (pressure <= 0f) return;
if (!player.getAbilities().instabuild) {
player.causeFoodExhaustion(0.08f + pressure * 0.18f);
}
if (sanityPercent <= 35f) {
int amplifier = sanityPercent <= 15f ? 1 : 0;
player.addEffect(new MobEffectInstance(MobEffects.DIG_SLOWDOWN, 60, amplifier, true, true));
}
if (player.isUsingItem() && (sanityPercent <= 25f || insanityPercent >= 70f)) {
float interruptChance = 0.06f + pressure * 0.12f;
if (player.getRandom().nextFloat() < interruptChance) {
player.stopUsingItem();
}
}
if ((sanityPercent <= 15f || insanityPercent >= 80f) && player.getRandom().nextFloat() < 0.08f + pressure * 0.10f) {
applyStumble(player, pressure);
}
if (insanityPercent >= 45f) {
int tempoLoss = Math.max(1, Math.round(highInsanity * 4f));
player.getCapability(PlayerCapabilityProvider.TEMPO).ifPresent(tempo -> tempo.reduceTempo(tempoLoss));
}
if (insanityPercent >= 60f && player.getRandom().nextFloat() < 0.18f + highInsanity * 0.20f) {
alertNearbyMonsters(player, 10.0 + highInsanity * 10.0);
}
}
private static void applyStumble(Player player, float pressure) {
double strength = 0.08 + pressure * 0.16;
double x = (player.getRandom().nextDouble() - 0.5) * strength;
double z = (player.getRandom().nextDouble() - 0.5) * strength;
player.setDeltaMovement(player.getDeltaMovement().add(x, 0.0, z));
player.hurtMarked = true;
if (player.isSprinting() && player.getRandom().nextFloat() < 0.5f) {
player.setSprinting(false);
}
}
private static void alertNearbyMonsters(Player player, double radius) {
player.level().getEntitiesOfClass(Monster.class, player.getBoundingBox().inflate(radius),
monster -> monster.isAlive() && monster.hasLineOfSight(player)
).forEach(monster -> {
if (monster.getTarget() == null || monster.getRandom().nextFloat() < 0.35f) {
monster.setTarget(player);
}
monster.getNavigation().moveTo(player, 1.1);
});
}
private static float clamp01(float value) {
return Math.max(0f, Math.min(1f, value));
}
private static void handleTempoTick(Player player, int currentTick) { private static void handleTempoTick(Player player, int currentTick) {
player.getCapability(PlayerCapabilityProvider.TEMPO).ifPresent(tempo -> { player.getCapability(PlayerCapabilityProvider.TEMPO).ifPresent(tempo -> {
int cd = tempo.getVentCooldown(); int cd = tempo.getVentCooldown();