Implement strength and agility scaling

This commit is contained in:
2025-11-08 14:22:55 +01:00
parent 519f65665b
commit c9da949e45
5 changed files with 164 additions and 13 deletions
@@ -16,6 +16,8 @@ import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.event.entity.living.LivingDamageEvent;
import net.minecraft.world.entity.LivingEntity;
@Mod.EventBusSubscriber(modid = AscendMod.MODID)
public class ActivityEvents {
@@ -54,7 +56,7 @@ public class ActivityEvents {
public static void onPlayerDamaged(LivingHurtEvent event) {
if (event.getEntity() instanceof Player player && !player.level().isClientSide) {
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
int gain = Math.min((int) event.getAmount() * 2, 10);
int gain = Math.min((int) event.getAmount() / 2, 10);
stats.addAscendXP(gain);
PlayerStatsProvider.sync(player);
});
@@ -203,4 +205,22 @@ public class ActivityEvents {
});
}
}
@SubscribeEvent
public static void onPlayerDealsDamage(LivingDamageEvent event) {
if (!(event.getSource().getEntity() instanceof Player player)) return;
if (player.level().isClientSide) return;
LivingEntity target = event.getEntity();
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
int strength = stats.getAttributeLevel("strength");
if (strength <= 0) return;
float bypassFraction = Math.min(strength * 0.001f, 0.30f);
int armor = target.getArmorValue();
if (armor <= 0 || bypassFraction <= 0f) return;
float baseDamage = event.getAmount();
float armorFactor = (float) armor / (armor + 100.0f);
float bonus = baseDamage * armorFactor * bypassFraction;
event.setAmount(baseDamage + bonus);
});
}
}
@@ -2,6 +2,7 @@ package net.fretux.ascend.event;
import net.fretux.ascend.AscendMod;
import net.fretux.ascend.player.PlayerStatsProvider;
import net.fretux.ascend.player.StatEffects;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
@@ -27,15 +28,19 @@ public class PlayerEvents {
public static void onPlayerClone(PlayerEvent.Clone event) {
event.getOriginal().reviveCaps();
event.getOriginal().getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(oldStats ->
event.getEntity().getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(newStats ->
newStats.deserializeNBT(oldStats.serializeNBT())));
event.getEntity().getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(newStats -> {
newStats.deserializeNBT(oldStats.serializeNBT());
StatEffects.applyAll(event.getEntity());
})
);
}
// Optional but very helpful: sync on login so client sees initial 15 points
@SubscribeEvent
public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
if (!event.getEntity().level().isClientSide) {
PlayerStatsProvider.sync(event.getEntity());
StatEffects.applyAll(event.getEntity());
}
}
}
@@ -1,5 +1,6 @@
package net.fretux.ascend.network;
import net.fretux.ascend.player.StatEffects;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
@@ -50,8 +51,29 @@ public class ServerboundSpendPointPacket {
));
}
});
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
boolean success = stats.spendPoints(attribute);
if (success) {
if ("strength".equals(attribute)) {
StatEffects.applyStrength(player, stats.getAttributeLevel("strength"));
}
player.sendSystemMessage(Component.literal("You increased " + attribute + "!"));
CompoundTag data = stats.serializeNBT();
PacketHandler.INSTANCE.send(
PacketDistributor.PLAYER.with(() -> player),
new ClientboundSyncStatsPacket(data)
);
} else {
int level = stats.getAttributeLevel(attribute);
int cost = stats.getCostToUpgrade(attribute);
int have = stats.getUnspentPoints();
player.sendSystemMessage(Component.literal(
"Not enough points (" + have + "/" + cost + ") to increase " + attribute + " (lvl " + level + ")."
));
}
});
});
ctx.get().setPacketHandled(true);
}
}
@@ -12,6 +12,7 @@ public class PlayerStats {
private int ascendLevel = 1;
private int ascendXP = 0;
private int unspentPoints = POINTS_PER_LEVEL;
public static final int MAX_ATTRIBUTE_POINTS = 100;
public PlayerStats() {
for (String attr : new String[]{
"strength", "agility", "fortitude", "intelligence",
@@ -59,12 +60,18 @@ public class PlayerStats {
public void addUnspentPoints(int amount) {
unspentPoints += amount;
}
public boolean spendPoints(String attribute) {
if (unspentPoints <= 0) return false;
if (!attributes.containsKey(attribute)) return false;
attributes.put(attribute, attributes.get(attribute) + 1);
unspentPoints--;
int level = attributes.get(attribute);
if (level >= MAX_ATTRIBUTE_POINTS) {
return false;
}
if (unspentPoints <= 0) {
return false;
}
unspentPoints -= 1;
attributes.put(attribute, level + 1);
return true;
}
@@ -77,7 +84,9 @@ public class PlayerStats {
}
public void setAttributeLevel(String attribute, int level) {
attributes.put(attribute, level);
if (!attributes.containsKey(attribute)) return;
int clamped = Math.max(0, Math.min(level, MAX_ATTRIBUTE_POINTS));
attributes.put(attribute, clamped);
}
public CompoundTag serializeNBT() {
@@ -104,9 +113,8 @@ public class PlayerStats {
if (tag.contains("Attributes")) {
CompoundTag attrTag = tag.getCompound("Attributes");
for (String key : attributes.keySet()) {
if (attrTag.contains(key)) {
attributes.put(key, attrTag.getInt(key));
}
int loaded = attrTag.getInt(key);
attributes.put(key, Math.max(0, Math.min(loaded, MAX_ATTRIBUTE_POINTS)));
}
}
}
@@ -0,0 +1,96 @@
package net.fretux.ascend.player;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import java.util.UUID;
public final class StatEffects {
// ===== Strength UUIDs =====
private static final UUID STRENGTH_DAMAGE_UUID =
UUID.fromString("a7b8b2c3-9b0d-4e1a-8ee3-21f6a7b2c3d4");
private static final UUID STRENGTH_KNOCKBACK_UUID =
UUID.fromString("b17f0d74-9cd9-4f90-9ec8-6a1046e2f912");
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");
private StatEffects() {}
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.
});
}
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;
atk.addTransientModifier(new AttributeModifier(
STRENGTH_DAMAGE_UUID,
"Ascend Strength damage bonus",
dmgBonus,
AttributeModifier.Operation.ADDITION
));
}
}
AttributeInstance kb = player.getAttribute(Attributes.ATTACK_KNOCKBACK);
if (kb != null) {
kb.removeModifier(STRENGTH_KNOCKBACK_UUID);
if (strengthLevel > 0) {
double kbBonus = strengthLevel * 0.03d;
kb.addTransientModifier(new AttributeModifier(
STRENGTH_KNOCKBACK_UUID,
"Ascend Strength knockback bonus",
kbBonus,
AttributeModifier.Operation.ADDITION
));
}
}
}
public static void applyAgility(Player player, int agilityLevel) {
AttributeInstance move = player.getAttribute(Attributes.MOVEMENT_SPEED);
if (move != null) {
move.removeModifier(AGILITY_SPEED_UUID);
if (agilityLevel > 0) {
double speedBonus = agilityLevel * 0.0005d;
move.addTransientModifier(new AttributeModifier(
AGILITY_SPEED_UUID,
"Ascend Agility speed bonus",
speedBonus,
AttributeModifier.Operation.ADDITION
));
}
}
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 reduction = Math.min(agilityLevel * perPoint, maxReduction);
if (reduction > 0.0d) {
double multiplier = 1.0d - reduction;
follow.addTransientModifier(new AttributeModifier(
AGILITY_STEALTH_UUID,
"Ascend Agility stealth bonus",
multiplier - 1.0d,
AttributeModifier.Operation.MULTIPLY_TOTAL
));
}
}
}
}
}