Implement fortitude and intelligence scaling
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package net.fretux.ascend.event;
|
||||
|
||||
import net.fretux.ascend.AscendMod;
|
||||
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||
import net.minecraft.world.effect.MobEffectCategory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.entity.living.MobEffectEvent;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = AscendMod.MODID)
|
||||
public class FortitudeDefenseEvents {
|
||||
@SubscribeEvent
|
||||
public static void onHarmfulEffectApplicable(MobEffectEvent.Applicable event) {
|
||||
if (!(event.getEntity() instanceof Player player)) return;
|
||||
if (player.level().isClientSide) return;
|
||||
if (event.getEffectInstance() == null) return;
|
||||
if (event.getEffectInstance().getEffect().getCategory() != MobEffectCategory.HARMFUL) return;
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
int fortitude = stats.getAttributeLevel("fortitude");
|
||||
if (fortitude <= 0) return;
|
||||
double perPoint = 0.004d;
|
||||
double max = 0.5d;
|
||||
double chance = Math.min(fortitude * perPoint, max);
|
||||
if (chance > 0 && player.getRandom().nextDouble() < chance) {
|
||||
event.setResult(Event.Result.DENY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTickFortitudeCleanse(TickEvent.PlayerTickEvent event) {
|
||||
Player player = event.player;
|
||||
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
int fortitude = stats.getAttributeLevel("fortitude");
|
||||
if (fortitude <= 0) return;
|
||||
if (player.tickCount % 40 != 0) return;
|
||||
double perPoint = 0.003d;
|
||||
double max = 0.4d;
|
||||
double chance = Math.min(fortitude * perPoint, max);
|
||||
if (chance <= 0) return;
|
||||
if (player.getRandom().nextDouble() >= chance) return;
|
||||
player.getActiveEffects().stream()
|
||||
.filter(inst -> inst.getEffect().getCategory() == MobEffectCategory.HARMFUL)
|
||||
.findAny()
|
||||
.ifPresent(inst -> player.removeEffect(inst.getEffect()));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.fretux.ascend.event;
|
||||
|
||||
import net.fretux.ascend.AscendMod;
|
||||
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.enchanting.EnchantmentLevelSetEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = AscendMod.MODID)
|
||||
public class IntelligenceEnchantmentEvents {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onEnchantmentLevelSet(EnchantmentLevelSetEvent event) {
|
||||
if (!(event.getLevel() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
Player player = serverLevel.getNearestPlayer(
|
||||
event.getPos().getX() + 0.5,
|
||||
event.getPos().getY() + 0.5,
|
||||
event.getPos().getZ() + 0.5,
|
||||
5.0,
|
||||
false
|
||||
);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
int intelligence = stats.getAttributeLevel("intelligence");
|
||||
if (intelligence <= 0) return;
|
||||
int original = event.getOriginalLevel();
|
||||
if (original <= 0) return;
|
||||
double perPoint = 0.005d;
|
||||
double max = 0.40d;
|
||||
double reduction = Math.min(intelligence * perPoint, max);
|
||||
int newLevel = (int) Math.max(1, Math.floor(original * (1.0d - reduction)));
|
||||
event.setEnchantLevel(newLevel);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package net.fretux.ascend.event;
|
||||
|
||||
import net.fretux.ascend.AscendMod;
|
||||
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||
import net.minecraft.world.effect.MobEffectCategory;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.entity.living.MobEffectEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = AscendMod.MODID)
|
||||
public class IntelligencePotionEvents {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onBeneficialEffectAdded(MobEffectEvent.Added event) {
|
||||
if (!(event.getEntity() instanceof Player player)) return;
|
||||
if (player.level().isClientSide) return;
|
||||
|
||||
MobEffectInstance inst = event.getEffectInstance();
|
||||
if (inst == null) return;
|
||||
if (inst.getEffect().getCategory() != MobEffectCategory.BENEFICIAL) return;
|
||||
|
||||
// Avoid re-scaling already huge/custom durations.
|
||||
if (inst.getDuration() > 12000) return;
|
||||
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
int intel = stats.getAttributeLevel("intelligence");
|
||||
if (intel <= 0) return;
|
||||
|
||||
int baseDuration = inst.getDuration();
|
||||
|
||||
// +0.5% duration per INT, up to +50%
|
||||
double durPerPoint = 0.005d;
|
||||
double durMax = 0.5d;
|
||||
double durBonus = Math.min(intel * durPerPoint, durMax);
|
||||
int newDuration = (int) (baseDuration * (1.0d + durBonus));
|
||||
|
||||
if (newDuration <= baseDuration) return; // nothing to do
|
||||
|
||||
// Replace effect: remove original, add scaled.
|
||||
// This will fire another Added event, but newDuration > 12000 guard prevents loops.
|
||||
player.removeEffect(inst.getEffect());
|
||||
player.addEffect(new MobEffectInstance(
|
||||
inst.getEffect(),
|
||||
newDuration,
|
||||
inst.getAmplifier(),
|
||||
inst.isAmbient(),
|
||||
inst.isVisible(),
|
||||
inst.showIcon()
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,13 @@ public class PlayerStats {
|
||||
attributes.put(attribute, clamped);
|
||||
}
|
||||
|
||||
public int getMaxMana() {
|
||||
int intelligence = getAttributeLevel("intelligence");
|
||||
int base = 50;
|
||||
int perPoint = 3;
|
||||
return base + intelligence * perPoint;
|
||||
}
|
||||
|
||||
public CompoundTag serializeNBT() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putInt("AscendLevel", ascendLevel);
|
||||
|
||||
@@ -14,27 +14,39 @@ public final class StatEffects {
|
||||
UUID.fromString("a7b8b2c3-9b0d-4e1a-8ee3-21f6a7b2c3d4");
|
||||
private static final UUID STRENGTH_KNOCKBACK_UUID =
|
||||
UUID.fromString("b17f0d74-9cd9-4f90-9ec8-6a1046e2f912");
|
||||
|
||||
// ===== Agility UUIDs =====
|
||||
private static final UUID AGILITY_SPEED_UUID =
|
||||
UUID.fromString("c12e9b6f-1d79-4a49-b57f-7f563c10aa11");
|
||||
private static final UUID AGILITY_STEALTH_UUID =
|
||||
UUID.fromString("d4e8a9f2-3b5c-4d0a-9f11-8acb3f0b77e2");
|
||||
|
||||
// ===== Fortitude UUIDs =====
|
||||
private static final UUID FORTITUDE_HEALTH_UUID =
|
||||
UUID.fromString("e2b8ff31-2c3d-4a60-9ce4-12aa4d3f9b77");
|
||||
private static final UUID FORTITUDE_KB_RESIST_UUID =
|
||||
UUID.fromString("f4c1a9d2-6b8e-4f01-8a21-9e7c5b3d12aa");
|
||||
|
||||
private StatEffects() {}
|
||||
|
||||
/** Call on login/clone and after stat changes. */
|
||||
public static void applyAll(Player player) {
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
applyStrength(player, stats.getAttributeLevel("strength"));
|
||||
applyAgility(player, stats.getAttributeLevel("agility"));
|
||||
// later: fortitude, etc.
|
||||
applyFortitude(player, stats.getAttributeLevel("fortitude"));
|
||||
applyIntelligence(player, stats.getAttributeLevel("intelligence"));
|
||||
});
|
||||
}
|
||||
|
||||
// ========== STRENGTH ==========
|
||||
|
||||
public static void applyStrength(Player player, int strengthLevel) {
|
||||
AttributeInstance atk = player.getAttribute(Attributes.ATTACK_DAMAGE);
|
||||
if (atk != null) {
|
||||
atk.removeModifier(STRENGTH_DAMAGE_UUID);
|
||||
if (strengthLevel > 0) {
|
||||
double dmgBonus = strengthLevel * 0.1d;
|
||||
double dmgBonus = strengthLevel * 0.1d; // 0.05 hearts per point
|
||||
atk.addTransientModifier(new AttributeModifier(
|
||||
STRENGTH_DAMAGE_UUID,
|
||||
"Ascend Strength damage bonus",
|
||||
@@ -43,7 +55,7 @@ public final class StatEffects {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AttributeInstance kb = player.getAttribute(Attributes.ATTACK_KNOCKBACK);
|
||||
if (kb != null) {
|
||||
kb.removeModifier(STRENGTH_KNOCKBACK_UUID);
|
||||
@@ -58,13 +70,17 @@ public final class StatEffects {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ========== AGILITY ==========
|
||||
|
||||
public static void applyAgility(Player player, int agilityLevel) {
|
||||
// Movement speed
|
||||
AttributeInstance move = player.getAttribute(Attributes.MOVEMENT_SPEED);
|
||||
if (move != null) {
|
||||
move.removeModifier(AGILITY_SPEED_UUID);
|
||||
if (agilityLevel > 0) {
|
||||
double speedBonus = agilityLevel * 0.0005d;
|
||||
// ~+2.5% at 10, +25% at 100
|
||||
double speedBonus = agilityLevel * 0.00025d;
|
||||
move.addTransientModifier(new AttributeModifier(
|
||||
AGILITY_SPEED_UUID,
|
||||
"Ascend Agility speed bonus",
|
||||
@@ -73,12 +89,14 @@ public final class StatEffects {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Stealth via follow range reduction
|
||||
AttributeInstance follow = player.getAttribute(Attributes.FOLLOW_RANGE);
|
||||
if (follow != null) {
|
||||
follow.removeModifier(AGILITY_STEALTH_UUID);
|
||||
if (agilityLevel > 0) {
|
||||
double perPoint = 0.003d;
|
||||
double maxReduction = 0.5d;
|
||||
double perPoint = 0.003d; // 0.3% per point
|
||||
double maxReduction = 0.5d; // up to -50%
|
||||
double reduction = Math.min(agilityLevel * perPoint, maxReduction);
|
||||
|
||||
if (reduction > 0.0d) {
|
||||
@@ -93,4 +111,62 @@ public final class StatEffects {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== FORTITUDE ==========
|
||||
|
||||
public static void applyFortitude(Player player, int fortitudeLevel) {
|
||||
// Max HP
|
||||
AttributeInstance maxHp = player.getAttribute(Attributes.MAX_HEALTH);
|
||||
if (maxHp != null) {
|
||||
maxHp.removeModifier(FORTITUDE_HEALTH_UUID);
|
||||
if (fortitudeLevel > 0) {
|
||||
// +0.2 HP per point (1 heart per 10 points)
|
||||
double bonusHp = fortitudeLevel * 0.2d;
|
||||
maxHp.addTransientModifier(new AttributeModifier(
|
||||
FORTITUDE_HEALTH_UUID,
|
||||
"Ascend Fortitude health bonus",
|
||||
bonusHp,
|
||||
AttributeModifier.Operation.ADDITION
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
float maxHealthNow = player.getMaxHealth();
|
||||
if (player.getHealth() > maxHealthNow) {
|
||||
player.setHealth(maxHealthNow);
|
||||
}
|
||||
|
||||
// Knockback resistance
|
||||
AttributeInstance kbRes = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
|
||||
if (kbRes != null) {
|
||||
kbRes.removeModifier(FORTITUDE_KB_RESIST_UUID);
|
||||
if (fortitudeLevel > 0) {
|
||||
// 0.3% per point, up to 30% at 100 (with safety cap)
|
||||
double bonus = fortitudeLevel * 0.003d;
|
||||
double safeBonus = Math.min(bonus, 0.8d);
|
||||
if (safeBonus > 0) {
|
||||
kbRes.addTransientModifier(new AttributeModifier(
|
||||
FORTITUDE_KB_RESIST_UUID,
|
||||
"Ascend Fortitude knockback resist",
|
||||
safeBonus,
|
||||
AttributeModifier.Operation.ADDITION
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== INTELLIGENCE ==========
|
||||
|
||||
/**
|
||||
* Intelligence:
|
||||
* - Max mana is exposed via PlayerStats (see below).
|
||||
* - Potions & enchanting are handled via event hooks.
|
||||
*
|
||||
* This exists for future attribute-based hooks if needed.
|
||||
*/
|
||||
public static void applyIntelligence(Player player, int intelligenceLevel) {
|
||||
// No direct vanilla attribute to touch here for mana.
|
||||
// Potion & enchanting effects are implemented in event handlers.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user