Enhance intelligence, willpower, and charisma bonuses

This commit is contained in:
2026-01-13 19:59:32 +01:00
parent d22629d1d1
commit 4b81455e2b
8 changed files with 265 additions and 16 deletions
@@ -14,6 +14,8 @@ import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.entity.TamableAnimal;
import net.minecraft.world.entity.animal.horse.AbstractHorse;
import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent;
import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent;
@@ -24,8 +26,14 @@ import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.event.level.BlockEvent; import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.event.entity.living.LivingDamageEvent; import net.minecraftforge.event.entity.living.LivingDamageEvent;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.animal.IronGolem;
import net.minecraft.world.entity.animal.SnowGolem;
import net.minecraft.world.entity.npc.GossipType;
import net.minecraftforge.event.entity.living.LivingEvent;
import net.minecraft.world.phys.AABB;
import java.util.Objects; import java.util.Objects;
import java.util.UUID;
@Mod.EventBusSubscriber(modid = AscendMod.MODID) @Mod.EventBusSubscriber(modid = AscendMod.MODID)
public class ActivityEvents { public class ActivityEvents {
@@ -33,6 +41,7 @@ public class ActivityEvents {
private static int agilityTickCounter = 0; private static int agilityTickCounter = 0;
private static int fortitudeTickCounter = 0; private static int fortitudeTickCounter = 0;
private static int willpowerTickCounter = 0; private static int willpowerTickCounter = 0;
private static int charismaTickCounter = 0;
@SubscribeEvent @SubscribeEvent
public static void onMobKilled(LivingDeathEvent event) { public static void onMobKilled(LivingDeathEvent event) {
@@ -41,7 +50,7 @@ public class ActivityEvents {
stats.addAscendXP((int) (AscendConfig.COMMON.xpPerMobKill.get() * AscendConfig.COMMON.xpMultiplier.get())); stats.addAscendXP((int) (AscendConfig.COMMON.xpPerMobKill.get() * AscendConfig.COMMON.xpMultiplier.get()));
LivingEntity mob = event.getEntity(); LivingEntity mob = event.getEntity();
if (mob.getMaxHealth() > 100f || mob.getType().toString().toLowerCase().contains("boss")) { if (mob.getMaxHealth() > 100f || mob.getType().toString().toLowerCase().contains("boss")) {
stats.addKnowledge(2); stats.addKnowledgeScaled(2);
} }
PlayerStatsProvider.sync(player); PlayerStatsProvider.sync(player);
}); });
@@ -134,8 +143,15 @@ public class ActivityEvents {
Player player = event.player; Player player = event.player;
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return; if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
willpowerTickCounter++; willpowerTickCounter++;
if (willpowerTickCounter % 200 != 0) return;
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> { player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
int willpower = stats.getAttributeLevel("willpower");
if (willpower > 0 && willpowerTickCounter % 80 == 0) {
float regen = StatEffects.getWillpowerHealthRegen(willpower);
if (regen > 0.0f && player.getHealth() < player.getMaxHealth()) {
player.heal(regen);
}
}
if (willpowerTickCounter % 200 != 0) return;
boolean hasNegativeEffect = player.getActiveEffects().stream() boolean hasNegativeEffect = player.getActiveEffects().stream()
.anyMatch(e -> e.getEffect().getCategory() == MobEffectCategory.HARMFUL); .anyMatch(e -> e.getEffect().getCategory() == MobEffectCategory.HARMFUL);
boolean lowHealth = player.getHealth() <= player.getMaxHealth() * 0.25f; boolean lowHealth = player.getHealth() <= player.getMaxHealth() * 0.25f;
@@ -151,12 +167,48 @@ public class ActivityEvents {
}); });
} }
@SubscribeEvent
public static void onPlayerTickCharisma(TickEvent.PlayerTickEvent event) {
Player player = event.player;
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
charismaTickCounter++;
if (charismaTickCounter % 40 != 0) return;
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
int charisma = stats.getAttributeLevel("charisma");
if (charisma <= 0) return;
double radius = 16.0d;
AABB range = player.getBoundingBox().inflate(radius);
for (LivingEntity entity : player.level().getEntitiesOfClass(LivingEntity.class, range,
candidate -> isCharismaAlly(player, candidate))) {
StatEffects.applyCharismaAllyBuff(entity, charisma);
}
});
}
@SubscribeEvent
public static void onCharismaAllyTick(LivingEvent.LivingTickEvent event) {
LivingEntity entity = event.getEntity();
if (entity.level().isClientSide) return;
if (!isCharismaAllyCandidate(entity)) return;
if (entity.tickCount % 40 != 0) return;
double radius = 16.0d;
if (hasNearbyCharismaSource(entity, radius)) return;
StatEffects.clearCharismaAllyBuff(entity);
}
@SubscribeEvent @SubscribeEvent
public static void onVillagerTrade(PlayerInteractEvent.EntityInteract event) { public static void onVillagerTrade(PlayerInteractEvent.EntityInteract event) {
if (!(event.getTarget() instanceof Villager)) return; if (!(event.getTarget() instanceof Villager)) return;
Player player = event.getEntity(); Player player = event.getEntity();
if (player.level().isClientSide) return; if (player.level().isClientSide) return;
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> { player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
int charisma = stats.getAttributeLevel("charisma");
if (charisma > 0) {
int reputationBonus = StatEffects.getCharismaVillagerReputationBonus(charisma);
if (reputationBonus > 0 && event.getTarget() instanceof Villager villager) {
villager.getGossips().add(player.getUUID(), GossipType.MINOR_POSITIVE, reputationBonus);
}
}
stats.addAscendXP((int) (AscendConfig.COMMON.xpPerTrade.get() * AscendConfig.COMMON.xpMultiplier.get())); stats.addAscendXP((int) (AscendConfig.COMMON.xpPerTrade.get() * AscendConfig.COMMON.xpMultiplier.get()));
PlayerStatsProvider.sync(player); PlayerStatsProvider.sync(player);
}); });
@@ -271,4 +323,64 @@ public class ActivityEvents {
} }
}); });
} }
private static boolean isCharismaAlly(Player player, LivingEntity entity) {
if (entity instanceof TamableAnimal tame) {
return tame.isTame() && tame.isOwnedBy(player);
}
if (entity instanceof AbstractHorse horse) {
return horse.isTamed() && player.getUUID().equals(horse.getOwnerUUID());
}
if (entity instanceof IronGolem ironGolem) {
return ironGolem.isPlayerCreated();
}
if (entity instanceof SnowGolem snowGolem) {
return snowGolem.isPlayerCreated();
}
return false;
}
private static boolean isCharismaAllyCandidate(LivingEntity entity) {
if (entity instanceof TamableAnimal tame) {
return tame.isTame();
}
if (entity instanceof AbstractHorse horse) {
return horse.isTamed();
}
if (entity instanceof IronGolem ironGolem) {
return ironGolem.isPlayerCreated();
}
if (entity instanceof SnowGolem snowGolem) {
return snowGolem.isPlayerCreated();
}
return false;
}
private static boolean hasNearbyCharismaSource(LivingEntity entity, double radius) {
if (entity instanceof TamableAnimal tame) {
Player owner = tame.getOwner() instanceof Player player ? player : null;
return owner != null && owner.distanceToSqr(entity) <= radius * radius && hasCharisma(owner);
}
if (entity instanceof AbstractHorse horse) {
UUID ownerId = horse.getOwnerUUID();
if (ownerId == null) return false;
Player owner = entity.level().getPlayerByUUID(ownerId);
return owner != null && owner.distanceToSqr(entity) <= radius * radius && hasCharisma(owner);
}
if (entity instanceof IronGolem || entity instanceof SnowGolem) {
AABB range = entity.getBoundingBox().inflate(radius);
for (Player player : entity.level().getEntitiesOfClass(Player.class, range)) {
if (hasCharisma(player)) {
return true;
}
}
}
return false;
}
private static boolean hasCharisma(Player player) {
return player.getCapability(PlayerStatsProvider.PLAYER_STATS)
.map(stats -> stats.getAttributeLevel("charisma") > 0)
.orElse(false);
}
} }
@@ -0,0 +1,31 @@
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.world.entity.player.Player;
import net.minecraftforge.event.AnvilUpdateEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(modid = AscendMod.MODID)
public class IntelligenceAnvilEvents {
@SubscribeEvent
public static void onAnvilUpdate(AnvilUpdateEvent event) {
Player player = event.getPlayer();
if (player == null || player.level().isClientSide) return;
int baseCost = event.getCost();
if (baseCost <= 0) return;
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
int intelligence = stats.getAttributeLevel("intelligence");
if (intelligence <= 0) return;
double reduction = StatEffects.getIntelligenceAnvilCostReduction(intelligence);
if (reduction <= 0.0d) return;
int newCost = (int) Math.max(1, Math.floor(baseCost * (1.0d - reduction)));
if (newCost < baseCost) {
event.setCost(newCost);
}
});
}
}
@@ -26,8 +26,8 @@ public class MoonseyeTome extends Item {
} else { } else {
gained = 50; gained = 50;
} }
stats.addKnowledge(gained); int totalGained = stats.addKnowledgeScaled(gained);
player.sendSystemMessage(net.minecraft.network.chat.Component.translatable("item.ascend.moonseye_tome.used", gained)); player.sendSystemMessage(net.minecraft.network.chat.Component.translatable("item.ascend.moonseye_tome.used", totalGained));
player.playSound(SoundEvents.ENCHANTMENT_TABLE_USE, 1.0f, 1.2f); player.playSound(SoundEvents.ENCHANTMENT_TABLE_USE, 1.0f, 1.2f);
stack.shrink(1); stack.shrink(1);
PlayerStatsProvider.sync(player); PlayerStatsProvider.sync(player);
@@ -55,7 +55,7 @@ public class ServerboundShrineChoicePacket {
player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(sanity -> { player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(sanity -> {
float sanityPercent = sanity.getSanity() / sanity.getMaxSanity(); float sanityPercent = sanity.getSanity() / sanity.getMaxSanity();
if (sanityPercent >= 0.90f) { if (sanityPercent >= 0.90f) {
stats.addKnowledge(5); stats.addKnowledgeScaled(5);
sanity.setSanity(0); sanity.setSanity(0);
sanity.setInsanity(0); sanity.setInsanity(0);
player.sendSystemMessage(Component.literal("YOUR MIND EXPANDS AS IT BREAKS.")); player.sendSystemMessage(Component.literal("YOUR MIND EXPANDS AS IT BREAKS."));
@@ -32,7 +32,7 @@ public class PlayerStats {
ascendXP -= xpNeeded; ascendXP -= xpNeeded;
ascendLevel++; ascendLevel++;
unspentPoints += POINTS_PER_LEVEL; unspentPoints += POINTS_PER_LEVEL;
knowledge += 1; addKnowledgeScaled(1);
} }
if (ascendLevel >= MAX_ASCEND_LEVEL) { if (ascendLevel >= MAX_ASCEND_LEVEL) {
int xpNeeded = getXPToNextAscendLevel(); int xpNeeded = getXPToNextAscendLevel();
@@ -111,6 +111,17 @@ public class PlayerStats {
knowledge = Math.max(0, knowledge + amount); knowledge = Math.max(0, knowledge + amount);
} }
public int addKnowledgeScaled(int amount) {
if (amount <= 0) {
addKnowledge(amount);
return amount;
}
int intelligence = getAttributeLevel("intelligence");
int scaled = StatEffects.getIntelligenceKnowledgeGain(intelligence, amount);
addKnowledge(scaled);
return scaled;
}
public void refundAllPoints() { public void refundAllPoints() {
int totalSpent = 0; int totalSpent = 0;
for (String key : attributes.keySet()) { for (String key : attributes.keySet()) {
@@ -3,6 +3,7 @@ package net.fretux.ascend.player;
import net.fretux.ascend.compat.IronsSpellbooksCompat; import net.fretux.ascend.compat.IronsSpellbooksCompat;
import net.fretux.ascend.config.AscendConfig; import net.fretux.ascend.config.AscendConfig;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.Attributes;
@@ -23,6 +24,10 @@ public final class StatEffects {
UUID.fromString("d3a4b5c6-7d8e-4f90-8a1b-223344556677"); UUID.fromString("d3a4b5c6-7d8e-4f90-8a1b-223344556677");
private static final UUID FORTITUDE_KB_RESIST_UUID = private static final UUID FORTITUDE_KB_RESIST_UUID =
UUID.fromString("e4b5c6d7-8e9f-4012-9abc-334455667788"); UUID.fromString("e4b5c6d7-8e9f-4012-9abc-334455667788");
private static final UUID CHARISMA_ALLY_HEALTH_UUID =
UUID.fromString("f5c6d7e8-9012-4bcd-9ef0-445566778899");
private static final UUID CHARISMA_ALLY_DAMAGE_UUID =
UUID.fromString("0a1b2c3d-4e5f-4012-8a9b-556677889900");
private StatEffects() { private StatEffects() {
} }
@@ -145,6 +150,12 @@ public final class StatEffects {
return (float) (1.0d + bonus); return (float) (1.0d + bonus);
} }
public static float getWillpowerHealthRegen(int willpowerLevel) {
if (willpowerLevel <= 0) return 0.0f;
double bonus = Math.min(willpowerLevel * 0.01d * getScaling(), 2.0d);
return (float) bonus;
}
public static float getPlayerSanityMultiplier(Player player) { public static float getPlayerSanityMultiplier(Player player) {
return player.getCapability(PlayerStatsProvider.PLAYER_STATS) return player.getCapability(PlayerStatsProvider.PLAYER_STATS)
.map(stats -> getWillpowerSanityMultiplier(stats.getAttributeLevel("willpower"))) .map(stats -> getWillpowerSanityMultiplier(stats.getAttributeLevel("willpower")))
@@ -161,6 +172,23 @@ public final class StatEffects {
return Math.max(0, intelligenceLevel); return Math.max(0, intelligenceLevel);
} }
public static double getIntelligenceAnvilCostReduction(int intelligenceLevel) {
if (intelligenceLevel <= 0) return 0.0d;
double perPoint = 0.005d;
double max = 0.40d;
return Math.min(intelligenceLevel * perPoint * getScaling(), max);
}
public static int getIntelligenceKnowledgeGain(int intelligenceLevel, int baseGain) {
if (baseGain <= 0) return baseGain;
if (intelligenceLevel <= 0) return baseGain;
double perPoint = 0.005d;
double max = 0.50d;
double bonus = Math.min(intelligenceLevel * perPoint * getScaling(), max);
int extra = (int) Math.floor(baseGain * bonus);
return baseGain + Math.max(0, extra);
}
public static double getCharismaTradeDiscount(int charismaLevel) { public static double getCharismaTradeDiscount(int charismaLevel) {
if (charismaLevel <= 0) return 0.0d; if (charismaLevel <= 0) return 0.0d;
double perPoint = 0.01d; double perPoint = 0.01d;
@@ -168,6 +196,74 @@ public final class StatEffects {
return Math.min(charismaLevel * perPoint * getScaling(), max); return Math.min(charismaLevel * perPoint * getScaling(), max);
} }
public static int getCharismaVillagerReputationBonus(int charismaLevel) {
if (charismaLevel <= 0) return 0;
double perPoint = 0.1d;
double max = 10.0d;
return (int) Math.floor(Math.min(charismaLevel * perPoint * getScaling(), max));
}
public static double getCharismaAllyHealthBonus(int charismaLevel) {
if (charismaLevel <= 0) return 0.0d;
double perPoint = 0.0025d;
double max = 0.25d;
return Math.min(charismaLevel * perPoint * getScaling(), max);
}
public static double getCharismaAllyDamageBonus(int charismaLevel) {
if (charismaLevel <= 0) return 0.0d;
double perPoint = 0.0020d;
double max = 0.20d;
return Math.min(charismaLevel * perPoint * getScaling(), max);
}
public static void applyCharismaAllyBuff(LivingEntity entity, int charismaLevel) {
if (charismaLevel <= 0) {
clearCharismaAllyBuff(entity);
return;
}
double healthBonus = getCharismaAllyHealthBonus(charismaLevel);
AttributeInstance health = entity.getAttribute(Attributes.MAX_HEALTH);
if (health != null) {
health.removeModifier(CHARISMA_ALLY_HEALTH_UUID);
if (healthBonus > 0.0d) {
health.addTransientModifier(new AttributeModifier(
CHARISMA_ALLY_HEALTH_UUID,
"Ascend Charisma ally max health",
healthBonus,
AttributeModifier.Operation.MULTIPLY_BASE
));
}
}
double damageBonus = getCharismaAllyDamageBonus(charismaLevel);
AttributeInstance damage = entity.getAttribute(Attributes.ATTACK_DAMAGE);
if (damage != null) {
damage.removeModifier(CHARISMA_ALLY_DAMAGE_UUID);
if (damageBonus > 0.0d) {
damage.addTransientModifier(new AttributeModifier(
CHARISMA_ALLY_DAMAGE_UUID,
"Ascend Charisma ally damage",
damageBonus,
AttributeModifier.Operation.MULTIPLY_BASE
));
}
}
}
public static void clearCharismaAllyBuff(LivingEntity entity) {
AttributeInstance health = entity.getAttribute(Attributes.MAX_HEALTH);
if (health != null) {
health.removeModifier(CHARISMA_ALLY_HEALTH_UUID);
}
AttributeInstance damage = entity.getAttribute(Attributes.ATTACK_DAMAGE);
if (damage != null) {
damage.removeModifier(CHARISMA_ALLY_DAMAGE_UUID);
}
if (entity.getHealth() > entity.getMaxHealth()) {
entity.setHealth(entity.getMaxHealth());
}
}
public static double getWeaponScalingMultiplier(int scalingLevel) { public static double getWeaponScalingMultiplier(int scalingLevel) {
if (scalingLevel <= 0) return 1.0d; if (scalingLevel <= 0) return 1.0d;
double percent = mediumScalingPercent(scalingLevel); double percent = mediumScalingPercent(scalingLevel);
@@ -14,11 +14,11 @@
"ascend.attribute.fortitude": "Fortitude", "ascend.attribute.fortitude": "Fortitude",
"ascend.attribute.fortitude.desc": "Increases max health and knockback resistance.", "ascend.attribute.fortitude.desc": "Increases max health and knockback resistance.",
"ascend.attribute.intelligence": "Intelligence", "ascend.attribute.intelligence": "Intelligence",
"ascend.attribute.intelligence.desc": "Increases max mana and mana regeneration. Also lowers enchantment cost and buffs potions.", "ascend.attribute.intelligence.desc": "Increases max mana and mana regeneration. Lowers enchantment/anvil costs, buffs potions, and boosts knowledge gain.",
"ascend.attribute.willpower": "Willpower", "ascend.attribute.willpower": "Willpower",
"ascend.attribute.willpower.desc": "Reduces sanity drain and improves stamina/tempo.", "ascend.attribute.willpower.desc": "Reduces sanity drain, improves stamina/tempo, and increases passive health regeneration.",
"ascend.attribute.charisma": "Charisma", "ascend.attribute.charisma": "Charisma",
"ascend.attribute.charisma.desc": "Increases max mana and improves trade deals.", "ascend.attribute.charisma.desc": "Increases max mana, improves trade deals, boosts villager reputation, and empowers tamed allies/golems.",
"ascend.attribute.light_scaling": "Light Scaling", "ascend.attribute.light_scaling": "Light Scaling",
"ascend.attribute.light_scaling.desc": "Improves light weapon performance.", "ascend.attribute.light_scaling.desc": "Improves light weapon performance.",
"ascend.attribute.medium_scaling": "Medium Scaling", "ascend.attribute.medium_scaling": "Medium Scaling",
@@ -36,4 +36,3 @@
"item.ascend.remembrance_essence": "Remembrance Essence", "item.ascend.remembrance_essence": "Remembrance Essence",
"block.ascend.shrine_of_remembrance": "Shrine of Remembrance" "block.ascend.shrine_of_remembrance": "Shrine of Remembrance"
} }