Revamp the entire skillset and rebalance everything.
This commit is contained in:
@@ -2,6 +2,58 @@
|
||||
|
||||
This changelog covers all recorded changes after `1.0.0`, grouped by release version from the git history.
|
||||
|
||||
## 3.0.8
|
||||
|
||||
- Increased Agility's default maximum movement-speed bonus from 15% to 25% so the stat retains a stronger mobility identity alongside its weapon-grade damage scaling.
|
||||
- Raised the configurable minimum to 25%, automatically correcting older lower values while keeping higher custom values valid.
|
||||
|
||||
## 3.0.7
|
||||
|
||||
- Changed armor penetration to ignore the matching percentage of the target's actual armor before Minecraft's armor-and-toughness calculation.
|
||||
- Added a configurable 30% armor-penetration cap and a public per-hit Forge event allowing integrations to increase penetration, change the cap, or disable it.
|
||||
|
||||
## 3.0.6
|
||||
|
||||
- Added `update.md`, a complete release summary comparing every 100-point attribute and documenting all changes from 2.4.4 through 3.0.5.
|
||||
|
||||
## 3.0.5
|
||||
|
||||
- Restored the normal maximum Ascend level to 20 while retaining the configurable 1–100 range.
|
||||
- Made the active server configuration override stale maximum-level snapshots stored in existing player data.
|
||||
|
||||
## 3.0.4
|
||||
|
||||
- Increased Agility's maximum movement-speed bonus from 5% to a minimum configurable 15%, including correction of older configs below that value.
|
||||
- Removed Ascend's Sanity-loss reduction shim; Mind & Motion now remains the sole owner of Willpower's existing maximum Sanity and maximum Tempo capacity bonuses.
|
||||
- Corrected Willpower's Iron's Spellbooks cast-time modifier to use the API's required positive reduction value.
|
||||
- Audited and tested the Iron's Spellbooks mana, mana regeneration, spell power, cooldown, and cast-time attributes plus Mind & Motion's Sanity and Tempo capability setters.
|
||||
|
||||
## 3.0.3
|
||||
|
||||
- Removed the subtitle from the Ascend Stats screen and reclaimed the unused header space.
|
||||
- Reworded every attribute summary and tooltip with concise, direct descriptions of its gameplay effects, and widened the cards to keep the new text readable.
|
||||
|
||||
## 3.0.2
|
||||
|
||||
- Fixed Strength and Agility summaries being clipped by shortening their card text and separating all descriptions vertically from the upgrade buttons.
|
||||
|
||||
## 3.0.1
|
||||
|
||||
- Redesigned the Ascend Stats screen with a wider two-column layout, spacious attribute cards, color-coded stat accents, short role summaries, a dedicated progression panel, and a visible Ascend XP bar.
|
||||
- Improved upgrade controls with clearer maximum-value states, stronger unspent-point emphasis, and full attribute descriptions on card hover.
|
||||
|
||||
## 3.0.0
|
||||
|
||||
- Replaced the ten-stat model with Vigor, Mind, Strength, Agility, Intelligence, Willpower, Charisma, and Arcane; removed Fortitude and all four dedicated weapon-scaling stats.
|
||||
- Added a shared configurable soft-cap curve, configurable Vigor health, Strength knockback/armor penetration, conservative Agility movement speed, and distinct magic-stat roles.
|
||||
- Moved Iron's Spellbooks mana capacity and regeneration to Mind, spell power and cooldown bonuses to Intelligence, retained Willpower cast-time support, and removed Charisma's generic mana bonus.
|
||||
- Extended the optional Mind & Motion bridge so Willpower reduces Sanity loss through its public capability while retaining the existing Tempo and mental-resilience integration.
|
||||
- Added registry- and datapack-driven weapon scaling with S through E/NONE grades, main-attribute extensibility, vanilla weapon defaults, additive base-damage bonuses, scaling tooltips, and safe optional Mace support.
|
||||
- Added event-based Arcane duration bonuses for finite beneficial effects on self and harmful effects applied to enemies, with separate configurable curves and no per-tick extension.
|
||||
- Added player data format version 2 and a one-time server-side migration that refunds every point invested in Fortitude and the removed scaling stats while preserving retained attributes, level, XP, Knowledge, and unrelated player data.
|
||||
- Updated stats UI, commands, advancement checks, login/respawn/dimension synchronization, localization, and configuration for the new model.
|
||||
- Added automated tests for the soft-cap curve and legacy save migration, including double-refund prevention and new-player behavior.
|
||||
|
||||
## 2.4.4
|
||||
|
||||
- Switched Iron's Spellbooks `3.16.2` to its current Curse Maven artifact because the legacy API-classifier coordinate is no longer published.
|
||||
|
||||
@@ -155,6 +155,13 @@ dependencies {
|
||||
|
||||
// CURIOS
|
||||
implementation fg.deobf("top.theillusivec4.curios:curios-forge:${curios_version}")
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.10.2')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
}
|
||||
|
||||
tasks.named('test', Test).configure {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
// This block of code expands all declared replace properties in the specified resource targets.
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ mod_name=Ascend: SbA
|
||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||
mod_license=All Rights Reserved
|
||||
# The mod version. See https://semver.org/
|
||||
mod_version=2.4.4
|
||||
mod_version=3.0.8
|
||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||
# This should match the base package used for the mod sources.
|
||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||
|
||||
@@ -14,10 +14,13 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import org.slf4j.Logger;
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
@Mod(AscendMod.MODID)
|
||||
public class AscendMod {
|
||||
public static final String MODID = "ascend";
|
||||
public static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public AscendMod() {
|
||||
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
@@ -38,4 +41,4 @@ public class AscendMod {
|
||||
System.out.println("[Ascend] Registering packets... Side=" + FMLEnvironment.dist);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,26 +14,29 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class StatsScreen extends Screen {
|
||||
private static final int WIDTH = 480;
|
||||
private static final int HEIGHT = 266;
|
||||
private static final int PADDING = 12;
|
||||
private static final int COLUMN_GAP = 8;
|
||||
private static final int CARD_GAP = 4;
|
||||
private static final int CARD_HEIGHT = 40;
|
||||
private static final int CARDS_TOP = 64;
|
||||
private static final int CARD_WIDTH = (WIDTH - PADDING * 2 - COLUMN_GAP) / 2;
|
||||
|
||||
private static final int WIDTH = 220;
|
||||
private static final int HEIGHT = 240;
|
||||
|
||||
private int leftPos;
|
||||
private int topPos;
|
||||
|
||||
private final Minecraft mc = Minecraft.getInstance();
|
||||
private final Map<String, Button> plusButtons = new HashMap<>();
|
||||
private String hoveredAttributeKey = null;
|
||||
|
||||
public StatsScreen() {
|
||||
super(Component.literal("Ascend Stats"));
|
||||
}
|
||||
|
||||
private static final String[] ATTRIBUTES = new String[]{
|
||||
"strength", "agility", "fortitude", "intelligence",
|
||||
"willpower", "charisma",
|
||||
"light_scaling", "medium_scaling", "heavy_scaling", "magic_scaling"
|
||||
private static final String[] ATTRIBUTES = {
|
||||
"vigor", "mind", "strength", "agility",
|
||||
"intelligence", "willpower", "charisma", "arcane"
|
||||
};
|
||||
private static final Map<String, Integer> ATTRIBUTE_COLORS = Map.of(
|
||||
"vigor", 0xFFD85C5C,
|
||||
"mind", 0xFF6699E8,
|
||||
"strength", 0xFFD78445,
|
||||
"agility", 0xFF71C988,
|
||||
"intelligence", 0xFF9A7BE8,
|
||||
"willpower", 0xFF79B9C8,
|
||||
"charisma", 0xFFE28AB8,
|
||||
"arcane", 0xFFC878E0
|
||||
);
|
||||
private static final Map<String, Component> ATTRIBUTE_TOOLTIPS = new HashMap<>();
|
||||
|
||||
static {
|
||||
@@ -42,97 +45,141 @@ public class StatsScreen extends Screen {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
this.leftPos = (this.width - WIDTH) / 2;
|
||||
this.topPos = (this.height - HEIGHT) / 2;
|
||||
this.plusButtons.clear();
|
||||
this.clearWidgets();
|
||||
int y = topPos + 55;
|
||||
for (String attr : ATTRIBUTES) {
|
||||
final String key = attr;
|
||||
Button plus = Button.builder(Component.literal("+"), (button) -> {
|
||||
PacketHandler.INSTANCE.sendToServer(new ServerboundSpendPointPacket(key));
|
||||
})
|
||||
.bounds(leftPos + WIDTH - 40, y - 6, 30, 18)
|
||||
.build();
|
||||
this.addRenderableWidget(plus);
|
||||
this.plusButtons.put(key, plus);
|
||||
y += 16;
|
||||
}
|
||||
Button done = Button.builder(Component.translatable("gui.done"), (btn) -> onClose())
|
||||
.bounds(leftPos + WIDTH / 2 - 40, topPos + HEIGHT - 24, 80, 20)
|
||||
.build();
|
||||
this.addRenderableWidget(done);
|
||||
private final Minecraft minecraft = Minecraft.getInstance();
|
||||
private final Map<String, Button> plusButtons = new HashMap<>();
|
||||
private int leftPos;
|
||||
private int topPos;
|
||||
private String hoveredAttributeKey;
|
||||
|
||||
public StatsScreen() {
|
||||
super(Component.translatable("ascend.stats.title"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics gui, int mouseX, int mouseY, float partialTicks) {
|
||||
this.renderBackground(gui);
|
||||
RenderSystem.enableBlend();
|
||||
gui.fill(leftPos - 8, topPos - 8, leftPos + WIDTH + 8, topPos + HEIGHT + 8, 0xDD0C0B12);
|
||||
gui.fill(leftPos - 4, topPos - 4, leftPos + WIDTH + 4, topPos + HEIGHT + 4, 0xE0181422);
|
||||
gui.fill(leftPos - 4, topPos - 4, leftPos + WIDTH + 4, topPos + 20, 0xFF2A2036);
|
||||
gui.fill(leftPos - 4, topPos + 20, leftPos + WIDTH + 4, topPos + 21, 0x66FFFFFF);
|
||||
RenderSystem.disableBlend();
|
||||
gui.drawCenteredString(this.font, Component.translatable("ascend.stats.title"), this.width / 2, topPos + 6, 0xFFFFFF);
|
||||
protected void init() {
|
||||
super.init();
|
||||
leftPos = (width - WIDTH) / 2;
|
||||
topPos = (height - HEIGHT) / 2;
|
||||
plusButtons.clear();
|
||||
clearWidgets();
|
||||
|
||||
for (int index = 0; index < ATTRIBUTES.length; index++) {
|
||||
String attribute = ATTRIBUTES[index];
|
||||
int column = index % 2;
|
||||
int row = index / 2;
|
||||
int cardX = leftPos + PADDING + column * (CARD_WIDTH + COLUMN_GAP);
|
||||
int cardY = topPos + CARDS_TOP + row * (CARD_HEIGHT + CARD_GAP);
|
||||
Button plus = Button.builder(Component.literal("+"), button ->
|
||||
PacketHandler.INSTANCE.sendToServer(new ServerboundSpendPointPacket(attribute)))
|
||||
.bounds(cardX + CARD_WIDTH - 30, cardY + 5, 22, 18)
|
||||
.build();
|
||||
addRenderableWidget(plus);
|
||||
plusButtons.put(attribute, plus);
|
||||
}
|
||||
|
||||
addRenderableWidget(Button.builder(Component.translatable("gui.done"), button -> onClose())
|
||||
.bounds(leftPos + WIDTH / 2 - 45, topPos + HEIGHT - 24, 90, 18)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
drawWindow(graphics);
|
||||
hoveredAttributeKey = null;
|
||||
if (mc.player != null) {
|
||||
mc.player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
int level = stats.getAscendLevel();
|
||||
int maxLevel = stats.getMaxAscendLevel();
|
||||
int xp = stats.getAscendXP();
|
||||
int xpNext = stats.getXPToNextAscendLevel();
|
||||
int unspent = stats.getUnspentPoints();
|
||||
gui.drawString(font, Component.translatable("ascend.stats.level", level, maxLevel), leftPos + 10, topPos + 28, 0xFFE27D);
|
||||
if (level < maxLevel) {
|
||||
gui.drawString(font, Component.translatable("ascend.stats.xp", xp, xpNext), leftPos + 10, topPos + 40, 0xB4B4C8);
|
||||
} else {
|
||||
gui.drawString(font, Component.translatable("ascend.stats.xp_max"), leftPos + 10, topPos + 40, 0xB4B4C8);
|
||||
}
|
||||
int unspentLabelWidth = font.width("Unspent Points: " + unspent);
|
||||
int unspentX = leftPos + WIDTH - 10 - unspentLabelWidth;
|
||||
gui.fill(unspentX - 6, topPos + 24, leftPos + WIDTH - 4, topPos + 38, 0x662D3B1E);
|
||||
gui.drawString(font, Component.translatable("ascend.stats.unspent", unspent),
|
||||
unspentX, topPos + 28, 0x8CFF8C);
|
||||
int y = topPos + 64;
|
||||
for (String key : ATTRIBUTES) {
|
||||
int attrLevel = stats.getAttributeLevel(key);
|
||||
int cost = stats.getCostToUpgrade(key);
|
||||
Component name = Component.translatable("ascend.attribute." + key);
|
||||
Component label = Component.translatable("ascend.attribute.display", name, attrLevel);
|
||||
int labelX = leftPos + 10;
|
||||
gui.drawString(font, label, labelX, y, 0xE6E6F2);
|
||||
|
||||
Button plus = plusButtons.get(key);
|
||||
if (minecraft.player != null) {
|
||||
minecraft.player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
drawProgression(graphics, stats.getAscendLevel(), stats.getMaxAscendLevel(),
|
||||
stats.getAscendXP(), stats.getXPToNextAscendLevel(), stats.getKnowledge(), stats.getUnspentPoints());
|
||||
|
||||
for (int index = 0; index < ATTRIBUTES.length; index++) {
|
||||
String attribute = ATTRIBUTES[index];
|
||||
int column = index % 2;
|
||||
int row = index / 2;
|
||||
int cardX = leftPos + PADDING + column * (CARD_WIDTH + COLUMN_GAP);
|
||||
int cardY = topPos + CARDS_TOP + row * (CARD_HEIGHT + CARD_GAP);
|
||||
int value = stats.getAttributeLevel(attribute);
|
||||
boolean hovered = mouseX >= cardX && mouseX < cardX + CARD_WIDTH
|
||||
&& mouseY >= cardY && mouseY < cardY + CARD_HEIGHT;
|
||||
drawAttributeCard(graphics, attribute, value, stats.getMaxAttributePoints(), cardX, cardY, hovered);
|
||||
if (hovered) hoveredAttributeKey = attribute;
|
||||
|
||||
Button plus = plusButtons.get(attribute);
|
||||
if (plus != null) {
|
||||
plus.setX(leftPos + WIDTH - 40);
|
||||
plus.setY(y - 6);
|
||||
plus.active = (unspent >= cost);
|
||||
plus.setMessage(Component.literal("+" + cost));
|
||||
int cost = stats.getCostToUpgrade(attribute);
|
||||
plus.active = stats.getUnspentPoints() >= cost && value < stats.getMaxAttributePoints();
|
||||
plus.setMessage(value >= stats.getMaxAttributePoints()
|
||||
? Component.literal("✓") : Component.literal("+"));
|
||||
}
|
||||
|
||||
int labelWidth = font.width(label);
|
||||
if (mouseX >= labelX && mouseX <= labelX + labelWidth
|
||||
&& mouseY >= y && mouseY <= y + font.lineHeight) {
|
||||
hoveredAttributeKey = key;
|
||||
}
|
||||
y += 16;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
super.render(gui, mouseX, mouseY, partialTicks);
|
||||
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
if (hoveredAttributeKey != null) {
|
||||
Component tooltip = ATTRIBUTE_TOOLTIPS.get(hoveredAttributeKey);
|
||||
if (tooltip != null) {
|
||||
gui.renderTooltip(this.font, tooltip, mouseX, mouseY);
|
||||
}
|
||||
graphics.renderTooltip(font, ATTRIBUTE_TOOLTIPS.get(hoveredAttributeKey), mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawWindow(GuiGraphics graphics) {
|
||||
RenderSystem.enableBlend();
|
||||
graphics.fill(leftPos - 6, topPos - 6, leftPos + WIDTH + 6, topPos + HEIGHT + 6, 0xCC07070C);
|
||||
graphics.fill(leftPos - 3, topPos - 3, leftPos + WIDTH + 3, topPos + HEIGHT + 3, 0xFF30253C);
|
||||
graphics.fill(leftPos, topPos, leftPos + WIDTH, topPos + HEIGHT, 0xF016131E);
|
||||
graphics.fill(leftPos, topPos, leftPos + WIDTH, topPos + 20, 0xFF2A2036);
|
||||
graphics.fill(leftPos, topPos + 20, leftPos + WIDTH, topPos + 21, 0xFF655275);
|
||||
RenderSystem.disableBlend();
|
||||
|
||||
graphics.drawCenteredString(font, title, leftPos + WIDTH / 2, topPos + 6, 0xFFF4EAFB);
|
||||
}
|
||||
|
||||
private void drawProgression(GuiGraphics graphics, int level, int maxLevel, int xp, int xpNext,
|
||||
int knowledge, int unspent) {
|
||||
int panelX = leftPos + PADDING;
|
||||
int panelY = topPos + 26;
|
||||
int panelWidth = WIDTH - PADDING * 2;
|
||||
graphics.fill(panelX, panelY, panelX + panelWidth, panelY + 31, 0xAA211C29);
|
||||
graphics.fill(panelX, panelY, panelX + 3, panelY + 31, 0xFFCFB060);
|
||||
|
||||
graphics.drawString(font, Component.translatable("ascend.stats.level", level, maxLevel),
|
||||
panelX + 9, panelY + 5, 0xFFFFDF78, false);
|
||||
graphics.drawString(font, Component.translatable("ascend.stats.knowledge", knowledge),
|
||||
panelX + 112, panelY + 5, 0xFFA9C7FF, false);
|
||||
|
||||
Component points = Component.translatable("ascend.stats.unspent", unspent);
|
||||
int pointsWidth = font.width(points);
|
||||
int pointsX = panelX + panelWidth - pointsWidth - 9;
|
||||
graphics.fill(pointsX - 5, panelY + 2, panelX + panelWidth - 4, panelY + 16, 0x663D623D);
|
||||
graphics.drawString(font, points, pointsX, panelY + 5, unspent > 0 ? 0xFF87F58D : 0xFF9D9D9D, false);
|
||||
|
||||
int barX = panelX + 9;
|
||||
int barY = panelY + 20;
|
||||
int barWidth = panelWidth - 18;
|
||||
graphics.fill(barX, barY, barX + barWidth, barY + 5, 0xFF0C0A10);
|
||||
double progress = level >= maxLevel || xpNext <= 0 ? 1.0 : Math.min(1.0, (double) xp / xpNext);
|
||||
graphics.fill(barX + 1, barY + 1, barX + 1 + (int) ((barWidth - 2) * progress), barY + 4, 0xFF8E68C3);
|
||||
Component xpLabel = level >= maxLevel ? Component.translatable("ascend.stats.xp_max")
|
||||
: Component.translatable("ascend.stats.xp", xp, xpNext);
|
||||
graphics.drawCenteredString(font, xpLabel, barX + barWidth / 2, barY - 2, 0xFFC9C1D0);
|
||||
}
|
||||
|
||||
private void drawAttributeCard(GuiGraphics graphics, String attribute, int value, int maximum,
|
||||
int x, int y, boolean hovered) {
|
||||
int accent = ATTRIBUTE_COLORS.getOrDefault(attribute, 0xFFFFFFFF);
|
||||
graphics.fill(x, y, x + CARD_WIDTH, y + CARD_HEIGHT, hovered ? 0xE0332B3D : 0xC925202F);
|
||||
graphics.fill(x, y, x + 3, y + CARD_HEIGHT, accent);
|
||||
graphics.fill(x + 3, y, x + CARD_WIDTH, y + 1, hovered ? 0x667F6B8C : 0x334C4057);
|
||||
|
||||
graphics.drawString(font, Component.translatable("ascend.attribute." + attribute),
|
||||
x + 9, y + 7, 0xFFF1ECF4, false);
|
||||
Component valueLabel = Component.literal(value + " / " + maximum);
|
||||
int valueX = x + CARD_WIDTH - 38 - font.width(valueLabel);
|
||||
graphics.drawString(font, valueLabel, valueX, y + 7, value >= maximum ? 0xFFFFD66B : 0xFFC9C1D0, false);
|
||||
graphics.drawString(font, Component.translatable("ascend.attribute." + attribute + ".short"),
|
||||
x + 9, y + 26, 0xFF9F98A8, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPauseScreen() {
|
||||
return false;
|
||||
|
||||
@@ -62,7 +62,10 @@ public class AscendCommand {
|
||||
String attr = StringArgumentType.getString(ctx, "attribute");
|
||||
int value = IntegerArgumentType.getInteger(ctx, "value");
|
||||
target.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
stats.setAttributeLevel(attr, value);
|
||||
if (!stats.setAttributeLevel(attr, value)) {
|
||||
ctx.getSource().sendFailure(Component.literal("Unknown Ascend attribute: " + attr));
|
||||
return;
|
||||
}
|
||||
StatEffects.applyAll(target);
|
||||
PlayerStatsProvider.sync(target);
|
||||
ctx.getSource().sendSuccess(() ->
|
||||
|
||||
@@ -1,9 +1,43 @@
|
||||
package net.fretux.ascend.compat;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
|
||||
public class AscendMMCompat {
|
||||
/** Optional Mind & Motion bridge with all foreign types isolated in Loaded. */
|
||||
public final class AscendMMCompat {
|
||||
private AscendMMCompat() {}
|
||||
|
||||
public static boolean isMindMotionPresent() {
|
||||
return ModList.get().isLoaded("mindmotion");
|
||||
}
|
||||
|
||||
public static boolean consumeClearMind(Player player) {
|
||||
return isMindMotionPresent() && Loaded.consumeClearMind(player);
|
||||
}
|
||||
|
||||
public static void restoreMind(Player player) {
|
||||
if (isMindMotionPresent()) Loaded.restoreMind(player);
|
||||
}
|
||||
|
||||
private static final class Loaded {
|
||||
private static boolean consumeClearMind(Player player) {
|
||||
final boolean[] success = {false};
|
||||
player.getCapability(net.fretux.mindmotion.player.PlayerCapabilityProvider.SANITY).ifPresent(sanity -> {
|
||||
if (sanity.getMaxSanity() > 0 && sanity.getSanity() / sanity.getMaxSanity() >= 0.90f) {
|
||||
sanity.setSanity(0);
|
||||
sanity.setInsanity(0);
|
||||
success[0] = true;
|
||||
}
|
||||
});
|
||||
return success[0];
|
||||
}
|
||||
|
||||
private static void restoreMind(Player player) {
|
||||
player.getCapability(net.fretux.mindmotion.player.PlayerCapabilityProvider.SANITY).ifPresent(sanity -> {
|
||||
sanity.setSanity(sanity.getMaxSanity());
|
||||
sanity.setInsanity(0);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package net.fretux.ascend.compat;
|
||||
|
||||
import net.fretux.ascend.player.StatEffects;
|
||||
import io.redspace.ironsspellbooks.api.registry.AttributeRegistry;
|
||||
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||
import net.fretux.ascend.player.StatEffects;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||
@@ -11,250 +10,63 @@ import net.minecraftforge.fml.ModList;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class IronsSpellbooksCompat {
|
||||
private static final UUID INT_MAX_MANA_UUID =
|
||||
UUID.fromString("11111111-1111-4111-8111-111111111111");
|
||||
private static final UUID INT_SPELL_POWER_UUID =
|
||||
UUID.fromString("22222222-2222-4222-8222-222222222222");
|
||||
private static final UUID INT_MANA_REGEN_UUID =
|
||||
UUID.fromString("33333333-3333-4333-8333-333333333333");
|
||||
private static final UUID INT_COOLDOWN_REDUCTION_UUID =
|
||||
UUID.fromString("44444444-4444-4444-8444-444444444444");
|
||||
private static final UUID CHA_MAX_MANA_UUID =
|
||||
UUID.fromString("55555555-5555-4555-8555-555555555555");
|
||||
// reserved for future spell-trade hooks
|
||||
private static final UUID CHA_SPELL_TRADE_UUID =
|
||||
UUID.fromString("66666666-6666-4666-8666-666666666666");
|
||||
private static final UUID MAG_SPELL_POWER_UUID =
|
||||
UUID.fromString("77777777-7777-4777-8777-777777777777");
|
||||
private static final UUID MAG_MANA_REGEN_UUID =
|
||||
UUID.fromString("88888888-8888-4888-8888-888888888888");
|
||||
private static final UUID MAG_COOLDOWN_REDUCTION_UUID =
|
||||
UUID.fromString("99999999-9999-4999-8999-999999999999");
|
||||
private static final UUID WILL_MANA_COST_UUID =
|
||||
UUID.fromString("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa");
|
||||
private static final UUID CHA_SUPPORT_POWER_UUID =
|
||||
UUID.fromString("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb");
|
||||
private static final UUID CHA_SUMMON_HEALTH_UUID =
|
||||
UUID.fromString("cccccccc-cccc-4ccc-8ccc-cccccccccccc");
|
||||
private static final UUID CHA_SUMMON_DAMAGE_UUID =
|
||||
UUID.fromString("dddddddd-dddd-4ddd-8ddd-dddddddddddd");
|
||||
/** Optional Iron's Spellbooks bridge. All Iron's classes are isolated in Loaded. */
|
||||
public final class IronsSpellbooksCompat {
|
||||
private static final UUID MIND_MANA = UUID.fromString("11111111-1111-4111-8111-111111111111");
|
||||
private static final UUID INT_SPELL_POWER = UUID.fromString("22222222-2222-4222-8222-222222222222");
|
||||
private static final UUID MIND_REGEN = UUID.fromString("33333333-3333-4333-8333-333333333333");
|
||||
private static final UUID INT_COOLDOWN = UUID.fromString("44444444-4444-4444-8444-444444444444");
|
||||
private static final UUID LEGACY_CHARISMA_MANA = UUID.fromString("55555555-5555-4555-8555-555555555555");
|
||||
private static final UUID LEGACY_MAGIC_POWER = UUID.fromString("77777777-7777-4777-8777-777777777777");
|
||||
private static final UUID LEGACY_MAGIC_REGEN = UUID.fromString("88888888-8888-4888-8888-888888888888");
|
||||
private static final UUID LEGACY_MAGIC_COOLDOWN = UUID.fromString("99999999-9999-4999-8999-999999999999");
|
||||
private static final UUID WILL_CAST_TIME = UUID.fromString("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa");
|
||||
private static final UUID CHARISMA_SUPPORT = UUID.fromString("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb");
|
||||
private static final UUID CHARISMA_SUMMON_DAMAGE = UUID.fromString("dddddddd-dddd-4ddd-8ddd-dddddddddddd");
|
||||
|
||||
|
||||
private IronsSpellbooksCompat() {
|
||||
}
|
||||
|
||||
public static boolean isLoaded() {
|
||||
return ModList.get().isLoaded("irons_spellbooks");
|
||||
}
|
||||
private IronsSpellbooksCompat() {}
|
||||
public static boolean isLoaded() { return ModList.get().isLoaded("irons_spellbooks"); }
|
||||
|
||||
public static void applyAll(Player player) {
|
||||
if (!isLoaded()) return;
|
||||
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
int intelligence = stats.getAttributeLevel("intelligence");
|
||||
int charisma = stats.getAttributeLevel("charisma");
|
||||
int magicScaling = stats.getAttributeLevel("magic_scaling");
|
||||
int willpower = stats.getAttributeLevel("willpower");
|
||||
|
||||
Loaded.applyIntelligence(player, intelligence);
|
||||
Loaded.applyCharisma(player, charisma);
|
||||
Loaded.applyMagicScaling(player, magicScaling);
|
||||
Loaded.applyWillpowerCastEfficiency(player, willpower);
|
||||
});
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> Loaded.apply(player,
|
||||
stats.getAttributeLevel("mind"), stats.getAttributeLevel("intelligence"),
|
||||
stats.getAttributeLevel("willpower"), stats.getAttributeLevel("charisma")));
|
||||
}
|
||||
|
||||
private static final class Loaded {
|
||||
private Loaded() {
|
||||
private static void apply(Player player, int mind, int intelligence, int willpower, int charisma) {
|
||||
Attribute maxMana = io.redspace.ironsspellbooks.api.registry.AttributeRegistry.MAX_MANA.get();
|
||||
Attribute manaRegen = io.redspace.ironsspellbooks.api.registry.AttributeRegistry.MANA_REGEN.get();
|
||||
Attribute spellPower = io.redspace.ironsspellbooks.api.registry.AttributeRegistry.SPELL_POWER.get();
|
||||
Attribute cooldown = io.redspace.ironsspellbooks.api.registry.AttributeRegistry.COOLDOWN_REDUCTION.get();
|
||||
remove(player, maxMana, LEGACY_CHARISMA_MANA);
|
||||
remove(player, spellPower, LEGACY_MAGIC_POWER);
|
||||
remove(player, manaRegen, LEGACY_MAGIC_REGEN);
|
||||
remove(player, cooldown, LEGACY_MAGIC_COOLDOWN);
|
||||
update(player, maxMana, MIND_MANA, "Ascend Mind max mana", StatEffects.getMindManaBonus(mind), AttributeModifier.Operation.ADDITION);
|
||||
update(player, manaRegen, MIND_REGEN, "Ascend Mind mana regeneration", StatEffects.getMindManaRegenBonus(mind), AttributeModifier.Operation.MULTIPLY_BASE);
|
||||
update(player, spellPower, INT_SPELL_POWER, "Ascend Intelligence spell power", StatEffects.getIntelligenceSpellPowerBonus(intelligence), AttributeModifier.Operation.MULTIPLY_BASE);
|
||||
update(player, cooldown, INT_COOLDOWN, "Ascend Intelligence cooldown reduction", StatEffects.getIntelligenceCooldownBonus(intelligence), AttributeModifier.Operation.MULTIPLY_BASE);
|
||||
update(player, io.redspace.ironsspellbooks.api.registry.AttributeRegistry.CAST_TIME_REDUCTION.get(), WILL_CAST_TIME,
|
||||
"Ascend Willpower cast-time reduction", StatEffects.getWillpowerCastTimeReduction(willpower), AttributeModifier.Operation.MULTIPLY_BASE);
|
||||
update(player, io.redspace.ironsspellbooks.api.registry.AttributeRegistry.HOLY_SPELL_POWER.get(), CHARISMA_SUPPORT,
|
||||
"Ascend Charisma support spell power", StatEffects.getCharismaAllyDamageBonus(charisma), AttributeModifier.Operation.MULTIPLY_BASE);
|
||||
update(player, io.redspace.ironsspellbooks.api.registry.AttributeRegistry.SUMMON_DAMAGE.get(), CHARISMA_SUMMON_DAMAGE,
|
||||
"Ascend Charisma summon damage", StatEffects.getCharismaAllyDamageBonus(charisma), AttributeModifier.Operation.MULTIPLY_BASE);
|
||||
}
|
||||
|
||||
private static void applyIntelligence(Player player, int intelligence) {
|
||||
if (intelligence <= 0) {
|
||||
clearIntelligence(player);
|
||||
return;
|
||||
}
|
||||
applyAddModifier(
|
||||
player,
|
||||
(Attribute) AttributeRegistry.MAX_MANA.get(),
|
||||
INT_MAX_MANA_UUID,
|
||||
"Ascend INT max mana",
|
||||
StatEffects.getIntelligenceManaBonus(intelligence)
|
||||
);
|
||||
double regenBonus = StatEffects.getIntelligenceAnvilCostReduction(intelligence);
|
||||
applyMultiplyBaseModifier(
|
||||
player,
|
||||
(Attribute) AttributeRegistry.MANA_REGEN.get(),
|
||||
INT_MANA_REGEN_UUID,
|
||||
"Ascend INT mana regen",
|
||||
regenBonus
|
||||
);
|
||||
}
|
||||
|
||||
private static void clearIntelligence(Player player) {
|
||||
removeModifier(player, (Attribute) AttributeRegistry.MAX_MANA.get(), INT_MAX_MANA_UUID);
|
||||
removeModifier(player, (Attribute) AttributeRegistry.MANA_REGEN.get(), INT_MANA_REGEN_UUID);
|
||||
}
|
||||
|
||||
private static void applyWillpowerCastEfficiency(Player player, int willpower) {
|
||||
clearWillpower(player);
|
||||
if (willpower <= 0) {
|
||||
return;
|
||||
}
|
||||
double reduction = StatEffects.getWillpowerCastTimeReduction(willpower);
|
||||
Attribute attr = (Attribute) AttributeRegistry.CAST_TIME_REDUCTION.get();
|
||||
if (attr == null) {
|
||||
return;
|
||||
}
|
||||
AttributeInstance inst = player.getAttribute(attr);
|
||||
if (inst == null) {
|
||||
return;
|
||||
}
|
||||
inst.removeModifier(WILL_MANA_COST_UUID);
|
||||
if (reduction > 0.0d) {
|
||||
inst.addTransientModifier(new AttributeModifier(
|
||||
WILL_MANA_COST_UUID,
|
||||
"Ascend WILL casting time reduction",
|
||||
-reduction,
|
||||
AttributeModifier.Operation.MULTIPLY_BASE
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearWillpower(Player player) {
|
||||
Attribute attr = (Attribute) AttributeRegistry.CAST_TIME_REDUCTION.get();
|
||||
if (attr == null) return;
|
||||
removeModifier(player, attr, WILL_MANA_COST_UUID);
|
||||
}
|
||||
|
||||
private static void applyCharisma(Player player, int charisma) {
|
||||
clearCharisma(player);
|
||||
if (charisma <= 0) {
|
||||
return;
|
||||
}
|
||||
double manaPerPoint = 1.5d;
|
||||
double bonusMana = Math.min(charisma * manaPerPoint, 150.0d);
|
||||
applyAddModifier(
|
||||
player,
|
||||
(Attribute) AttributeRegistry.MAX_MANA.get(),
|
||||
CHA_MAX_MANA_UUID,
|
||||
"Ascend CHA max mana",
|
||||
bonusMana
|
||||
);
|
||||
double supportBonus = StatEffects.getCharismaAllyDamageBonus(charisma);
|
||||
Attribute supportAttr = (Attribute) AttributeRegistry.HOLY_SPELL_POWER.get();
|
||||
if (supportAttr != null && supportBonus > 0.0d) {
|
||||
applyMultiplyBaseModifier(
|
||||
player,
|
||||
supportAttr,
|
||||
CHA_SUPPORT_POWER_UUID,
|
||||
"Ascend CHA support spell power",
|
||||
supportBonus
|
||||
);
|
||||
}
|
||||
double minionHpBonus = Math.min(StatEffects.getCharismaAllyHealthBonus(charisma) * 1.25d, 0.25d);
|
||||
double minionDmgBonus = Math.min(StatEffects.getCharismaAllyDamageBonus(charisma) * 1.25d, 0.25d);
|
||||
Attribute summonHpAttr = null;
|
||||
Attribute summonDmgAttr = (Attribute) AttributeRegistry.SUMMON_DAMAGE.get();
|
||||
if (summonHpAttr != null && minionHpBonus > 0.0d) {
|
||||
applyMultiplyBaseModifier(
|
||||
player,
|
||||
summonHpAttr,
|
||||
CHA_SUMMON_HEALTH_UUID,
|
||||
"Ascend CHA summon max health",
|
||||
minionHpBonus
|
||||
);
|
||||
}
|
||||
if (summonDmgAttr != null && minionDmgBonus > 0.0d) {
|
||||
applyMultiplyBaseModifier(
|
||||
player,
|
||||
summonDmgAttr,
|
||||
CHA_SUMMON_DAMAGE_UUID,
|
||||
"Ascend CHA summon damage",
|
||||
minionDmgBonus
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearCharisma(Player player) {
|
||||
removeModifier(player, (Attribute) AttributeRegistry.MAX_MANA.get(), CHA_MAX_MANA_UUID);
|
||||
Attribute supportAttr = (Attribute) AttributeRegistry.HOLY_SPELL_POWER.get();
|
||||
Attribute summonHpAttr = null;
|
||||
Attribute summonDmgAttr = (Attribute) AttributeRegistry.SUMMON_DAMAGE.get();
|
||||
if (supportAttr != null) {
|
||||
removeModifier(player, supportAttr, CHA_SUPPORT_POWER_UUID);
|
||||
}
|
||||
if (summonHpAttr != null) {
|
||||
removeModifier(player, summonHpAttr, CHA_SUMMON_HEALTH_UUID);
|
||||
}
|
||||
if (summonDmgAttr != null) {
|
||||
removeModifier(player, summonDmgAttr, CHA_SUMMON_DAMAGE_UUID);
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyMagicScaling(Player player, int magicScaling) {
|
||||
if (magicScaling <= 0) {
|
||||
clearMagicScaling(player);
|
||||
return;
|
||||
}
|
||||
double spBonus = Math.min(StatEffects.getMagicScalingMultiplier(magicScaling) - 1.0d, 0.35d);
|
||||
applyMultiplyBaseModifier(
|
||||
player,
|
||||
(Attribute) AttributeRegistry.SPELL_POWER.get(),
|
||||
MAG_SPELL_POWER_UUID,
|
||||
"Ascend Magic Scaling spell power",
|
||||
spBonus
|
||||
);
|
||||
double cdrBonus = Math.min((StatEffects.getMagicScalingMultiplier(magicScaling) - 1.0d) * 0.4d, 0.14d);
|
||||
applyMultiplyBaseModifier(
|
||||
player,
|
||||
(Attribute) AttributeRegistry.COOLDOWN_REDUCTION.get(),
|
||||
MAG_COOLDOWN_REDUCTION_UUID,
|
||||
"Ascend Magic Scaling cooldown reduction",
|
||||
cdrBonus
|
||||
);
|
||||
}
|
||||
|
||||
private static void clearMagicScaling(Player player) {
|
||||
removeModifier(player, (Attribute) AttributeRegistry.SPELL_POWER.get(), MAG_SPELL_POWER_UUID);
|
||||
removeModifier(player, (Attribute) AttributeRegistry.COOLDOWN_REDUCTION.get(), MAG_COOLDOWN_REDUCTION_UUID);
|
||||
}
|
||||
|
||||
private static void applyAddModifier(Player player, Attribute attribute, UUID id, String name, double value) {
|
||||
private static void update(Player player, Attribute attribute, UUID id, String name, double value, AttributeModifier.Operation operation) {
|
||||
if (attribute == null) return;
|
||||
AttributeInstance inst = player.getAttribute(attribute);
|
||||
if (inst == null) return;
|
||||
inst.removeModifier(id);
|
||||
if (value != 0.0d) {
|
||||
inst.addTransientModifier(new AttributeModifier(
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
AttributeModifier.Operation.ADDITION
|
||||
));
|
||||
}
|
||||
AttributeInstance instance = player.getAttribute(attribute);
|
||||
if (instance == null) return;
|
||||
instance.removeModifier(id);
|
||||
if (value != 0) instance.addTransientModifier(new AttributeModifier(id, name, value, operation));
|
||||
}
|
||||
|
||||
private static void applyMultiplyBaseModifier(Player player, Attribute attribute, UUID id, String name, double value) {
|
||||
private static void remove(Player player, Attribute attribute, UUID id) {
|
||||
if (attribute == null) return;
|
||||
AttributeInstance inst = player.getAttribute(attribute);
|
||||
if (inst == null) return;
|
||||
inst.removeModifier(id);
|
||||
if (value != 0.0d) {
|
||||
inst.addTransientModifier(new AttributeModifier(
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
AttributeModifier.Operation.MULTIPLY_BASE
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeModifier(Player player, Attribute attribute, UUID id) {
|
||||
if (attribute == null) return;
|
||||
AttributeInstance inst = player.getAttribute(attribute);
|
||||
if (inst != null) {
|
||||
inst.removeModifier(id);
|
||||
}
|
||||
AttributeInstance instance = player.getAttribute(attribute);
|
||||
if (instance != null) instance.removeModifier(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,65 +4,85 @@ import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
public class AscendConfig {
|
||||
|
||||
public static class Common {
|
||||
public final ForgeConfigSpec.IntValue xpPerMobKill;
|
||||
public final ForgeConfigSpec.IntValue xpPerBlockBreak;
|
||||
public final ForgeConfigSpec.IntValue xpPerCraft;
|
||||
public final ForgeConfigSpec.IntValue xpPerSmelt;
|
||||
public final ForgeConfigSpec.IntValue xpPerTrade;
|
||||
public final ForgeConfigSpec.IntValue xpPerDamageTaken;
|
||||
public final ForgeConfigSpec.IntValue xpPerMovement;
|
||||
public final ForgeConfigSpec.IntValue xpPerPotion;
|
||||
public final ForgeConfigSpec.IntValue pointsPerLevel;
|
||||
public final ForgeConfigSpec.IntValue maxAscendLevel;
|
||||
public final ForgeConfigSpec.IntValue maxAttributePoints;
|
||||
|
||||
public final ForgeConfigSpec.DoubleValue xpMultiplier;
|
||||
public final ForgeConfigSpec.DoubleValue attributeScalingMultiplier;
|
||||
public final ForgeConfigSpec.IntValue xpPerMobKill, xpPerBlockBreak, xpPerCraft, xpPerSmelt,
|
||||
xpPerTrade, xpPerDamageTaken, xpPerMovement, xpPerPotion, pointsPerLevel,
|
||||
maxAscendLevel, maxAttributePoints;
|
||||
public final ForgeConfigSpec.DoubleValue xpMultiplier, attributeScalingMultiplier;
|
||||
public final ForgeConfigSpec.IntValue softCap1, softCap2, softCap3, softCap4;
|
||||
public final ForgeConfigSpec.DoubleValue softCapGain2, softCapGain3, softCapGain4, softCapGain5;
|
||||
public final ForgeConfigSpec.DoubleValue vigorMaxHealthBonus, mindMaxManaBonus, mindManaRegenBonus,
|
||||
strengthArmorPenetration, armorPenetrationCap, strengthKnockbackBonus, agilityMovementSpeedBonus,
|
||||
intelligenceSpellPowerBonus, intelligenceCooldownBonus, willpowerCastTimeReduction,
|
||||
arcaneBeneficialDurationBonus, arcaneHarmfulDurationBonus;
|
||||
public final ForgeConfigSpec.DoubleValue gradeE, gradeD, gradeC, gradeB, gradeA, gradeS;
|
||||
|
||||
Common(ForgeConfigSpec.Builder builder) {
|
||||
builder.push("Ascend Progression");
|
||||
|
||||
xpPerMobKill = builder.comment("Ascend XP gained for killing a mob.")
|
||||
.defineInRange("xpPerMobKill", 10, 0, 1000);
|
||||
xpPerBlockBreak = builder.comment("Ascend XP gained for breaking a block (ore/stone/log).")
|
||||
.defineInRange("xpPerBlockBreak", 2, 0, 1000);
|
||||
xpPerCraft = builder.comment("Ascend XP gained for crafting items.")
|
||||
.defineInRange("xpPerCraft", 1, 0, 1000);
|
||||
xpPerSmelt = builder.comment("Ascend XP gained for smelting items.")
|
||||
.defineInRange("xpPerSmelt", 1, 0, 1000);
|
||||
xpPerTrade = builder.comment("Ascend XP gained for trading with villagers.")
|
||||
.defineInRange("xpPerTrade", 5, 0, 1000);
|
||||
xpPerDamageTaken = builder.comment("Ascend XP gained for taking damage.")
|
||||
.defineInRange("xpPerDamageTaken", 2, 0, 1000);
|
||||
xpPerMovement = builder.comment("Ascend XP gained for movement actions.")
|
||||
.defineInRange("xpPerMovement", 1, 0, 1000);
|
||||
xpPerPotion = builder.comment("Ascend XP gained for using a potion.")
|
||||
.defineInRange("xpPerPotion", 3, 0, 1000);
|
||||
|
||||
pointsPerLevel = builder.comment("Unspent points gained per Ascend level.")
|
||||
.defineInRange("pointsPerLevel", 15, 1, 1000);
|
||||
maxAscendLevel = builder.comment("Maximum Ascend level.")
|
||||
.defineInRange("maxAscendLevel", 20, 1, 100);
|
||||
maxAttributePoints = builder.comment("Maximum points in a single attribute.")
|
||||
.defineInRange("maxAttributePoints", 100, 1, 1000);
|
||||
|
||||
xpMultiplier = builder.comment("Global multiplier for all Ascend XP gain.")
|
||||
.defineInRange("xpMultiplier", 1.0D, 0.0D, 100.0D);
|
||||
attributeScalingMultiplier = builder.comment("Global multiplier for all attribute-based stat bonuses (damage, mana, speed, etc.).")
|
||||
.defineInRange("attributeScalingMultiplier", 1.0D, 0.1D, 10.0D);
|
||||
|
||||
xpPerMobKill = intValue(builder, "xpPerMobKill", "Ascend XP gained for killing a mob.", 10, 0, 1000);
|
||||
xpPerBlockBreak = intValue(builder, "xpPerBlockBreak", "Ascend XP gained for breaking ore, stone, or logs.", 2, 0, 1000);
|
||||
xpPerCraft = intValue(builder, "xpPerCraft", "Ascend XP gained for crafting.", 1, 0, 1000);
|
||||
xpPerSmelt = intValue(builder, "xpPerSmelt", "Ascend XP gained for smelting.", 1, 0, 1000);
|
||||
xpPerTrade = intValue(builder, "xpPerTrade", "Ascend XP gained for villager trading.", 5, 0, 1000);
|
||||
xpPerDamageTaken = intValue(builder, "xpPerDamageTaken", "Ascend XP gained from dangerous survival conditions.", 2, 0, 1000);
|
||||
xpPerMovement = intValue(builder, "xpPerMovement", "Ascend XP gained for movement activities.", 1, 0, 1000);
|
||||
xpPerPotion = intValue(builder, "xpPerPotion", "Ascend XP gained for potion use.", 3, 0, 1000);
|
||||
pointsPerLevel = intValue(builder, "pointsPerLevel", "Attribute points gained per Ascend level.", 15, 1, 1000);
|
||||
maxAscendLevel = intValue(builder, "maxAscendLevel", "Maximum Ascend level.", 20, 1, 100);
|
||||
maxAttributePoints = intValue(builder, "maxAttributePoints", "Maximum investment in one attribute.", 100, 1, 1000);
|
||||
xpMultiplier = doubleValue(builder, "xpMultiplier", "Global Ascend XP multiplier.", 1.0, 0, 100);
|
||||
attributeScalingMultiplier = doubleValue(builder, "attributeScalingMultiplier", "Global multiplier applied to attribute curves.", 1.0, 0.1, 10);
|
||||
builder.pop();
|
||||
|
||||
builder.push("Attribute Soft Caps");
|
||||
softCap1 = intValue(builder, "firstSoftCap", "End of strong gains.", 20, 1, 1000);
|
||||
softCap2 = intValue(builder, "secondSoftCap", "End of good gains.", 40, 1, 1000);
|
||||
softCap3 = intValue(builder, "thirdSoftCap", "End of moderate gains.", 60, 1, 1000);
|
||||
softCap4 = intValue(builder, "fourthSoftCap", "End of weak gains.", 80, 1, 1000);
|
||||
softCapGain2 = doubleValue(builder, "secondSegmentGain", "Relative gain after the first cap.", 0.75, 0, 1);
|
||||
softCapGain3 = doubleValue(builder, "thirdSegmentGain", "Relative gain after the second cap.", 0.50, 0, 1);
|
||||
softCapGain4 = doubleValue(builder, "fourthSegmentGain", "Relative gain after the third cap.", 0.25, 0, 1);
|
||||
softCapGain5 = doubleValue(builder, "fifthSegmentGain", "Relative gain after the fourth cap.", 0.10, 0, 1);
|
||||
builder.pop();
|
||||
|
||||
builder.push("Attribute Effects");
|
||||
vigorMaxHealthBonus = doubleValue(builder, "vigorMaxHealthBonus", "Maximum extra health points from Vigor (20 = ten hearts).", 20, 0, 100);
|
||||
mindMaxManaBonus = doubleValue(builder, "mindMaxManaBonus", "Maximum extra Iron's Spellbooks mana from Mind.", 200, 0, 1000);
|
||||
mindManaRegenBonus = doubleValue(builder, "mindManaRegenBonus", "Maximum proportional mana regeneration bonus from Mind.", 0.30, 0, 5);
|
||||
strengthArmorPenetration = doubleValue(builder, "strengthArmorPenetration", "Maximum fraction of the target's armor ignored by Strength.", 0.20, 0, 1);
|
||||
armorPenetrationCap = doubleValue(builder, "armorPenetrationCap", "Default maximum armor fraction that can be ignored. Integrations may disable this cap per hit.", 0.30, 0, 1);
|
||||
strengthKnockbackBonus = doubleValue(builder, "strengthKnockbackBonus", "Maximum additive attack knockback from Strength.", 0.15, 0, 2);
|
||||
agilityMovementSpeedBonus = doubleValue(builder, "agilityMovementSpeedBonus", "Maximum proportional movement speed bonus from Agility.", 0.25, 0.25, 1);
|
||||
intelligenceSpellPowerBonus = doubleValue(builder, "intelligenceSpellPowerBonus", "Maximum proportional spell power bonus from Intelligence.", 0.35, 0, 5);
|
||||
intelligenceCooldownBonus = doubleValue(builder, "intelligenceCooldownBonus", "Maximum proportional spell cooldown reduction from Intelligence.", 0.15, 0, 1);
|
||||
willpowerCastTimeReduction = doubleValue(builder, "willpowerCastTimeReduction", "Maximum Iron's Spellbooks cast-time reduction from Willpower.", 0.20, 0, 0.95);
|
||||
arcaneBeneficialDurationBonus = doubleValue(builder, "arcaneBeneficialDurationBonus", "Maximum duration bonus for finite beneficial effects received by the player.", 0.30, 0, 5);
|
||||
arcaneHarmfulDurationBonus = doubleValue(builder, "arcaneHarmfulDurationBonus", "Maximum duration bonus for finite harmful effects applied by the player.", 0.40, 0, 5);
|
||||
builder.pop();
|
||||
|
||||
builder.push("Weapon Scaling Grades");
|
||||
gradeE = grade(builder, "gradeE", 0.10); gradeD = grade(builder, "gradeD", 0.20);
|
||||
gradeC = grade(builder, "gradeC", 0.30); gradeB = grade(builder, "gradeB", 0.40);
|
||||
gradeA = grade(builder, "gradeA", 0.50); gradeS = grade(builder, "gradeS", 0.60);
|
||||
builder.pop();
|
||||
}
|
||||
|
||||
private static ForgeConfigSpec.IntValue intValue(ForgeConfigSpec.Builder b, String key, String comment, int value, int min, int max) {
|
||||
return b.comment(comment).defineInRange(key, value, min, max);
|
||||
}
|
||||
private static ForgeConfigSpec.DoubleValue doubleValue(ForgeConfigSpec.Builder b, String key, String comment, double value, double min, double max) {
|
||||
return b.comment(comment).defineInRange(key, value, min, max);
|
||||
}
|
||||
private static ForgeConfigSpec.DoubleValue grade(ForgeConfigSpec.Builder b, String key, double value) {
|
||||
return doubleValue(b, key, "Maximum base-weapon-damage contribution for this grade.", value, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static final ForgeConfigSpec COMMON_SPEC;
|
||||
public static final Common COMMON;
|
||||
|
||||
static {
|
||||
final Pair<Common, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Common::new);
|
||||
COMMON_SPEC = specPair.getRight();
|
||||
COMMON = specPair.getLeft();
|
||||
Pair<Common, ForgeConfigSpec> pair = new ForgeConfigSpec.Builder().configure(Common::new);
|
||||
COMMON = pair.getLeft();
|
||||
COMMON_SPEC = pair.getRight();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,6 @@ import net.fretux.ascend.config.AscendConfig;
|
||||
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||
import net.fretux.ascend.player.StatEffects;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.effect.MobEffectCategory;
|
||||
import net.minecraft.world.entity.ai.gossip.GossipType;
|
||||
import net.minecraft.world.entity.npc.Villager;
|
||||
@@ -24,7 +21,6 @@ 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;
|
||||
import net.minecraft.world.entity.animal.IronGolem;
|
||||
import net.minecraft.world.entity.animal.SnowGolem;
|
||||
@@ -76,7 +72,7 @@ public class ActivityEvents {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTickFortitude(TickEvent.PlayerTickEvent event) {
|
||||
public static void onPlayerTickSurvivalXP(TickEvent.PlayerTickEvent event) {
|
||||
Player player = event.player;
|
||||
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
||||
if (player.tickCount % 100 != 0) return;
|
||||
@@ -133,13 +129,6 @@ public class ActivityEvents {
|
||||
Player player = event.player;
|
||||
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
int willpower = stats.getAttributeLevel("willpower");
|
||||
if (willpower > 0 && player.tickCount % 80 == 0) {
|
||||
float regen = StatEffects.getWillpowerHealthRegen(willpower);
|
||||
if (regen > 0.0f && player.getHealth() < player.getMaxHealth()) {
|
||||
player.heal(regen);
|
||||
}
|
||||
}
|
||||
if (player.tickCount % 200 != 0) return;
|
||||
boolean hasNegativeEffect = player.getActiveEffects().stream()
|
||||
.anyMatch(e -> e.getEffect().getCategory() == MobEffectCategory.HARMFUL);
|
||||
@@ -252,63 +241,6 @@ public class ActivityEvents {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerDealsDamage(LivingDamageEvent event) {
|
||||
if (!(event.getSource().getEntity() instanceof Player player)) return;
|
||||
if (player.level().isClientSide) return;
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
float damage = event.getAmount();
|
||||
var stack = player.getMainHandItem();
|
||||
if (!stack.isEmpty()) {
|
||||
double multiplier = 1.0d;
|
||||
if (stack.getItem() instanceof net.minecraft.world.item.AxeItem) {
|
||||
int heavy = stats.getAttributeLevel("heavy_scaling");
|
||||
multiplier = StatEffects.getHeavyWeaponScalingMultiplier(heavy);
|
||||
} else if (stack.getItem() instanceof net.minecraft.world.item.SwordItem) {
|
||||
int medium = stats.getAttributeLevel("medium_scaling");
|
||||
multiplier = StatEffects.getWeaponScalingMultiplier(medium);
|
||||
} else {
|
||||
int light = stats.getAttributeLevel("light_scaling");
|
||||
multiplier = StatEffects.getLightWeaponScalingMultiplier(light);
|
||||
}
|
||||
damage *= (float) multiplier;
|
||||
}
|
||||
event.setAmount(damage);
|
||||
});
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerEvasion(LivingHurtEvent event) {
|
||||
if (!(event.getEntity() instanceof Player player)) return;
|
||||
if (player.level().isClientSide) return;
|
||||
if (event.getAmount() <= 0) return;
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
int agility = stats.getAttributeLevel("agility");
|
||||
if (agility <= 0) return;
|
||||
double evadeChance = StatEffects.getAgilityEvasionChance(agility);
|
||||
if (player.getRandom().nextDouble() < evadeChance) {
|
||||
float original = event.getAmount();
|
||||
event.setAmount(original * 0.1f);
|
||||
if (player.level() instanceof ServerLevel serverLevel) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
double offsetX = (serverLevel.random.nextDouble() - 0.5) * 0.6;
|
||||
double offsetY = serverLevel.random.nextDouble() * 1.5;
|
||||
double offsetZ = (serverLevel.random.nextDouble() - 0.5) * 0.6;
|
||||
serverLevel.sendParticles(
|
||||
ParticleTypes.CLOUD,
|
||||
player.getX() + offsetX,
|
||||
player.getY() + offsetY,
|
||||
player.getZ() + offsetZ,
|
||||
1,
|
||||
0, 0, 0, 0.0
|
||||
);
|
||||
}
|
||||
}
|
||||
player.playSound(SoundEvents.ENDERMAN_TELEPORT, 0.4f, 1.5f);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isCharismaAlly(Player player, LivingEntity entity) {
|
||||
if (entity instanceof TamableAnimal tame) {
|
||||
return tame.isTame() && tame.isOwnedBy(player);
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
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.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 chance = StatEffects.getFortitudeEffectGuardChance(fortitude);
|
||||
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 chance = StatEffects.getFortitudeCleanseChance(fortitude);
|
||||
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()));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
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.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 reduction = StatEffects.getIntelligenceAnvilCostReduction(intelligence);
|
||||
int newLevel = (int) Math.max(1, Math.floor(original * (1.0d - reduction)));
|
||||
event.setEnchantLevel(newLevel);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
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.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;
|
||||
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();
|
||||
double durBonus = StatEffects.getIntelligenceAnvilCostReduction(intel);
|
||||
int newDuration = (int) (baseDuration * (1.0d + durBonus));
|
||||
if (newDuration <= baseDuration) return;
|
||||
player.removeEffect(inst.getEffect());
|
||||
player.addEffect(new MobEffectInstance(
|
||||
inst.getEffect(),
|
||||
newDuration,
|
||||
inst.getAmplifier(),
|
||||
inst.isAmbient(),
|
||||
inst.isVisible(),
|
||||
inst.showIcon()
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,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.fretux.ascend.compat.AscendMMCompat;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
@@ -52,10 +53,10 @@ public class PlayerEvents {
|
||||
@SubscribeEvent
|
||||
public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
if (!event.getEntity().level().isClientSide) {
|
||||
float healthBeforeFortitude = event.getEntity().getHealth();
|
||||
float healthBeforeVigor = event.getEntity().getHealth();
|
||||
StatEffects.applyAll(event.getEntity());
|
||||
if (Math.abs(healthBeforeFortitude - 20.0F) < 0.001F
|
||||
&& event.getEntity().getMaxHealth() > healthBeforeFortitude) {
|
||||
if (Math.abs(healthBeforeVigor - 20.0F) < 0.001F
|
||||
&& event.getEntity().getMaxHealth() > healthBeforeVigor) {
|
||||
event.getEntity().setHealth(event.getEntity().getMaxHealth());
|
||||
PENDING_FULL_HEALTH_RESTORE.add(event.getEntity().getUUID());
|
||||
}
|
||||
@@ -63,16 +64,34 @@ public class PlayerEvents {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
|
||||
if (!event.getEntity().level().isClientSide) {
|
||||
StatEffects.applyAll(event.getEntity());
|
||||
PlayerStatsProvider.sync(event.getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
|
||||
if (!event.getEntity().level().isClientSide) {
|
||||
StatEffects.applyAll(event.getEntity());
|
||||
PlayerStatsProvider.sync(event.getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
|
||||
Player player = event.player;
|
||||
if (player.level().isClientSide
|
||||
|| event.phase != TickEvent.Phase.END
|
||||
|| !PENDING_FULL_HEALTH_RESTORE.remove(player.getUUID())) {
|
||||
return;
|
||||
}
|
||||
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
||||
if (!PENDING_FULL_HEALTH_RESTORE.remove(player.getUUID())) return;
|
||||
StatEffects.applyAll(player);
|
||||
player.setHealth(player.getMaxHealth());
|
||||
PlayerStatsProvider.sync(player);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) {
|
||||
PENDING_FULL_HEALTH_RESTORE.remove(event.getEntity().getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
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.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.projectile.AbstractArrow;
|
||||
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.entity.projectile.ThrownTrident;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = AscendMod.MODID)
|
||||
public class WeaponMagicScalingEvents {
|
||||
@SubscribeEvent
|
||||
public static void onLivingHurt(LivingHurtEvent event) {
|
||||
if (!(event.getSource().getEntity() instanceof Player player)) return;
|
||||
if (event.getEntity() == player) return;
|
||||
if (player.level().isClientSide) return;
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
float base = event.getAmount();
|
||||
float modified = base;
|
||||
if (isMagicDamage(event.getSource())) {
|
||||
int magicScaling = stats.getAttributeLevel("magic_scaling");
|
||||
if (magicScaling > 0) {
|
||||
double bonusMult = StatEffects.getMagicScalingMultiplier(magicScaling);
|
||||
modified = (float) (modified * bonusMult);
|
||||
}
|
||||
} else if (isRangedDamage(event.getSource())) {
|
||||
double rangedMultiplier = getRangedScalingMultiplier(stats, event.getSource());
|
||||
modified = (float) (modified * rangedMultiplier);
|
||||
}
|
||||
|
||||
if (modified != base) {
|
||||
event.setAmount(modified);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isMagicDamage(DamageSource source) {
|
||||
String id = source.getMsgId();
|
||||
if (id == null) return false;
|
||||
return id.equals("magic")
|
||||
|| id.equals("indirectMagic")
|
||||
|| id.equals("wither")
|
||||
|| id.equals("dragonBreath")
|
||||
|| id.equals("thrown")
|
||||
|| id.equals("potion");
|
||||
}
|
||||
|
||||
private static boolean isRangedDamage(DamageSource source) {
|
||||
return source.getDirectEntity() instanceof Projectile
|
||||
|| source.getDirectEntity() instanceof AbstractArrow
|
||||
|| source.getDirectEntity() instanceof ThrownTrident
|
||||
|| source.getDirectEntity() instanceof FireworkRocketEntity;
|
||||
}
|
||||
|
||||
private static double getRangedScalingMultiplier(
|
||||
net.fretux.ascend.player.PlayerStats stats,
|
||||
DamageSource source
|
||||
) {
|
||||
double bonus = 1.0d;
|
||||
int agility = stats.getAttributeLevel("agility");
|
||||
if (agility > 0) {
|
||||
bonus *= StatEffects.getAgilityRangedDamageMultiplier(agility);
|
||||
}
|
||||
|
||||
if (source.getDirectEntity() instanceof ThrownTrident) {
|
||||
int strength = stats.getAttributeLevel("strength");
|
||||
if (strength > 0) {
|
||||
bonus *= StatEffects.getStrengthTridentDamageMultiplier(strength);
|
||||
}
|
||||
}
|
||||
|
||||
return bonus;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import net.fretux.ascend.advancement.AscendAdvancements;
|
||||
import net.fretux.ascend.compat.AscendMMCompat;
|
||||
import net.fretux.ascend.item.RemembranceEssenceItem;
|
||||
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||
import net.fretux.mindmotion.player.PlayerCapabilityProvider;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -54,29 +53,19 @@ public class ServerboundShrineChoicePacket {
|
||||
}
|
||||
case "understand" -> {
|
||||
if (!AscendMMCompat.isMindMotionPresent()) return;
|
||||
player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(sanity -> {
|
||||
float sanityPercent = sanity.getSanity() / sanity.getMaxSanity();
|
||||
if (sanityPercent >= 0.90f) {
|
||||
stats.addKnowledgeScaled(5);
|
||||
sanity.setSanity(0);
|
||||
sanity.setInsanity(0);
|
||||
player.sendSystemMessage(Component.literal("YOUR MIND EXPANDS AS IT BREAKS."));
|
||||
RemembranceEssenceItem.consume(player);
|
||||
AscendAdvancements.grant(player, "use_shrine");
|
||||
} else {
|
||||
player.sendSystemMessage(Component.literal("YOUR MIND IS NOT YET CLEAR ENOUGH."));
|
||||
}
|
||||
});
|
||||
if (AscendMMCompat.consumeClearMind(player)) {
|
||||
stats.addKnowledgeScaled(5);
|
||||
player.sendSystemMessage(Component.literal("YOUR MIND EXPANDS AS IT BREAKS."));
|
||||
RemembranceEssenceItem.consume(player);
|
||||
AscendAdvancements.grant(player, "use_shrine");
|
||||
} else player.sendSystemMessage(Component.literal("YOUR MIND IS NOT YET CLEAR ENOUGH."));
|
||||
PlayerStatsProvider.sync(player);
|
||||
}
|
||||
case "rest" -> {
|
||||
if (!AscendMMCompat.isMindMotionPresent()) return;
|
||||
if (stats.getKnowledge() >= 10) {
|
||||
stats.addKnowledge(-10);
|
||||
player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(sanity -> {
|
||||
sanity.setSanity(sanity.getMaxSanity());
|
||||
sanity.setInsanity(0);
|
||||
});
|
||||
AscendMMCompat.restoreMind(player);
|
||||
player.sendSystemMessage(Component.literal("THE SHRINE RESTORES YOUR FRACTURED MIND."));
|
||||
RemembranceEssenceItem.consume(player);
|
||||
AscendAdvancements.grant(player, "use_shrine");
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ServerboundSpendPointPacket {
|
||||
boolean success = stats.spendPoints(attribute);
|
||||
if (success) {
|
||||
StatEffects.applyAll(player);
|
||||
if ("fortitude".equals(attribute)) {
|
||||
if ("vigor".equals(attribute)) {
|
||||
float gainedMaxHealth = player.getMaxHealth() - previousMaxHealth;
|
||||
if (gainedMaxHealth > 0.0F) {
|
||||
player.setHealth(Math.min(player.getHealth() + gainedMaxHealth, player.getMaxHealth()));
|
||||
|
||||
@@ -1,199 +1,118 @@
|
||||
package net.fretux.ascend.player;
|
||||
|
||||
import net.fretux.ascend.AscendMod;
|
||||
import net.fretux.ascend.config.AscendConfig;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PlayerStats {
|
||||
private static final String[] ATTRIBUTE_KEYS = {
|
||||
"strength", "agility", "fortitude", "intelligence",
|
||||
"willpower", "charisma",
|
||||
"light_scaling", "medium_scaling", "heavy_scaling", "magic_scaling"
|
||||
};
|
||||
private final Map<String, Integer> attributes = new HashMap<>();
|
||||
private final Map<String, Integer> lastRespecBuild = new HashMap<>();
|
||||
public static final int CURRENT_DATA_VERSION = 2;
|
||||
public static final String[] ATTRIBUTE_KEYS = {"vigor", "mind", "strength", "agility", "intelligence", "willpower", "charisma", "arcane"};
|
||||
private static final String[] LEGACY_REFUND_KEYS = {"fortitude", "light_scaling", "medium_scaling", "heavy_scaling", "magic_scaling"};
|
||||
private final Map<String, Integer> attributes = new LinkedHashMap<>();
|
||||
private final Map<String, Integer> lastRespecBuild = new LinkedHashMap<>();
|
||||
private int dataVersion = CURRENT_DATA_VERSION;
|
||||
private int ascendLevel = 1;
|
||||
private int ascendXP = 0;
|
||||
private int ascendXP;
|
||||
private int unspentPoints = getPointsPerLevel();
|
||||
private int maxAscendLevel = getConfiguredMaxAscendLevel();
|
||||
private int maxAttributePoints = getConfiguredMaxAttributePoints();
|
||||
private int respecCount = 0;
|
||||
private boolean everInvestedWeaponScaling = false;
|
||||
public PlayerStats() {
|
||||
for (String attr : ATTRIBUTE_KEYS) {
|
||||
attributes.put(attr, 0);
|
||||
}
|
||||
}
|
||||
private int knowledge;
|
||||
private boolean hasUsedMoonseye;
|
||||
private int respecCount;
|
||||
private int lastMigrationRefund;
|
||||
|
||||
public PlayerStats() { for (String key : ATTRIBUTE_KEYS) attributes.put(key, 0); }
|
||||
|
||||
public void addAscendXP(int amount) {
|
||||
if (amount <= 0 || ascendLevel >= getMaxAscendLevel()) return;
|
||||
ascendXP += amount;
|
||||
while (ascendLevel < getMaxAscendLevel()) {
|
||||
int xpNeeded = getXPToNextAscendLevel();
|
||||
if (ascendXP < xpNeeded) break;
|
||||
ascendXP -= xpNeeded;
|
||||
int needed = getXPToNextAscendLevel();
|
||||
if (ascendXP < needed) break;
|
||||
ascendXP -= needed;
|
||||
ascendLevel++;
|
||||
unspentPoints += getPointsPerLevel();
|
||||
addKnowledgeScaled(1);
|
||||
}
|
||||
if (ascendLevel >= getMaxAscendLevel()) {
|
||||
int xpNeeded = getXPToNextAscendLevel();
|
||||
if (ascendXP > xpNeeded) {
|
||||
ascendXP = xpNeeded;
|
||||
}
|
||||
}
|
||||
if (ascendLevel >= getMaxAscendLevel()) ascendXP = Math.min(ascendXP, getXPToNextAscendLevel());
|
||||
}
|
||||
|
||||
public int getAscendLevel() {
|
||||
return ascendLevel;
|
||||
}
|
||||
|
||||
public int getAscendXP() {
|
||||
return ascendXP;
|
||||
}
|
||||
public int getAscendLevel() { return ascendLevel; }
|
||||
public int getAscendXP() { return ascendXP; }
|
||||
public int getUnspentPoints() { return unspentPoints; }
|
||||
public int getMaxAscendLevel() { return maxAscendLevel; }
|
||||
public int getMaxAttributePoints() { return maxAttributePoints; }
|
||||
public int getKnowledge() { return knowledge; }
|
||||
public int getRespecCount() { return respecCount; }
|
||||
public int getDataVersion() { return dataVersion; }
|
||||
public int getLastMigrationRefund() { return lastMigrationRefund; }
|
||||
public boolean hasUsedMoonseye() { return hasUsedMoonseye; }
|
||||
public Map<String, Integer> getAttributes() { return Collections.unmodifiableMap(attributes); }
|
||||
|
||||
public int getXPToNextAscendLevel() {
|
||||
if (ascendLevel >= getMaxAscendLevel()) return 0;
|
||||
double base = 100.0;
|
||||
double growth = 1.25;
|
||||
return (int) Math.ceil(base * Math.pow(growth, ascendLevel - 1) + (ascendLevel * 100));
|
||||
return (int) Math.ceil(100.0 * Math.pow(1.25, ascendLevel - 1) + ascendLevel * 100.0);
|
||||
}
|
||||
|
||||
public int getUnspentPoints() {
|
||||
return unspentPoints;
|
||||
}
|
||||
|
||||
public void addUnspentPoints(int amount) {
|
||||
unspentPoints = Math.max(0, unspentPoints + amount);
|
||||
}
|
||||
public void addUnspentPoints(int amount) { unspentPoints = Math.max(0, unspentPoints + amount); }
|
||||
|
||||
public boolean spendPoints(String attribute) {
|
||||
if (!attributes.containsKey(attribute)) return false;
|
||||
int level = attributes.get(attribute);
|
||||
if (level >= getMaxAttributePoints()) {
|
||||
return false;
|
||||
}
|
||||
if (unspentPoints <= 0) {
|
||||
return false;
|
||||
}
|
||||
unspentPoints -= 1;
|
||||
attributes.put(attribute, level + 1);
|
||||
if (isWeaponScaling(attribute)) {
|
||||
everInvestedWeaponScaling = true;
|
||||
}
|
||||
if (!attributes.containsKey(attribute) || unspentPoints <= 0) return false;
|
||||
int value = attributes.get(attribute);
|
||||
if (value >= getMaxAttributePoints()) return false;
|
||||
attributes.put(attribute, value + 1);
|
||||
unspentPoints--;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getCostToUpgrade(String attribute) {
|
||||
return 1;
|
||||
public int getCostToUpgrade(String attribute) { return attributes.containsKey(attribute) ? 1 : 0; }
|
||||
public int getAttributeLevel(String attribute) { return attributes.getOrDefault(attribute, 0); }
|
||||
|
||||
public boolean setAttributeLevel(String attribute, int level) {
|
||||
if (!attributes.containsKey(attribute)) return false;
|
||||
attributes.put(attribute, Math.max(0, Math.min(level, getMaxAttributePoints())));
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getAttributeLevel(String attribute) {
|
||||
return attributes.getOrDefault(attribute, 0);
|
||||
}
|
||||
|
||||
public int getMaxAscendLevel() {
|
||||
return maxAscendLevel;
|
||||
}
|
||||
|
||||
public int getMaxAttributePoints() {
|
||||
return maxAttributePoints;
|
||||
}
|
||||
|
||||
public void setAttributeLevel(String attribute, int level) {
|
||||
if (!attributes.containsKey(attribute)) return;
|
||||
int clamped = Math.max(0, Math.min(level, getMaxAttributePoints()));
|
||||
attributes.put(attribute, clamped);
|
||||
if (clamped > 0 && isWeaponScaling(attribute)) {
|
||||
everInvestedWeaponScaling = true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxMana() {
|
||||
int intelligence = getAttributeLevel("intelligence");
|
||||
int base = 50;
|
||||
int perPoint = 3;
|
||||
return base + intelligence * perPoint;
|
||||
}
|
||||
|
||||
private int knowledge = 0;
|
||||
private boolean hasUsedMoonseye = false; // Moonseye Tome First Use
|
||||
|
||||
public int getKnowledge() {
|
||||
return knowledge;
|
||||
}
|
||||
|
||||
public void addKnowledge(int 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 addKnowledge(int amount) { knowledge = Math.max(0, knowledge + amount); }
|
||||
public int addKnowledgeScaled(int amount) { addKnowledge(amount); return amount; }
|
||||
|
||||
public void refundAllPoints() {
|
||||
int totalSpent = 0;
|
||||
Map<String, Integer> refundedBuild = new HashMap<>();
|
||||
int total = 0;
|
||||
lastRespecBuild.clear();
|
||||
for (String key : ATTRIBUTE_KEYS) {
|
||||
int lvl = attributes.getOrDefault(key, 0);
|
||||
totalSpent += lvl;
|
||||
refundedBuild.put(key, lvl);
|
||||
int value = attributes.getOrDefault(key, 0);
|
||||
total += value;
|
||||
lastRespecBuild.put(key, value);
|
||||
attributes.put(key, 0);
|
||||
}
|
||||
if (totalSpent > 0) {
|
||||
lastRespecBuild.clear();
|
||||
lastRespecBuild.putAll(refundedBuild);
|
||||
respecCount++;
|
||||
}
|
||||
unspentPoints += totalSpent;
|
||||
}
|
||||
|
||||
public int getRespecCount() {
|
||||
return respecCount;
|
||||
}
|
||||
|
||||
public boolean hasEverInvestedWeaponScaling() {
|
||||
return everInvestedWeaponScaling;
|
||||
if (total > 0) respecCount++;
|
||||
unspentPoints += total;
|
||||
}
|
||||
|
||||
public boolean hasNearlyRestoredLastBuild() {
|
||||
if (lastRespecBuild.isEmpty()) return false;
|
||||
int originalTotal = 0;
|
||||
int currentTotal = 0;
|
||||
int totalDifference = 0;
|
||||
int originalTotal = 0, currentTotal = 0, difference = 0;
|
||||
for (String key : ATTRIBUTE_KEYS) {
|
||||
int original = lastRespecBuild.getOrDefault(key, 0);
|
||||
int current = attributes.getOrDefault(key, 0);
|
||||
originalTotal += original;
|
||||
currentTotal += current;
|
||||
totalDifference += Math.abs(original - current);
|
||||
difference += Math.abs(original - current);
|
||||
}
|
||||
int allowedDifference = Math.max(2, (int) Math.ceil(originalTotal * 0.05D));
|
||||
return originalTotal > 0 && currentTotal > 0
|
||||
&& currentTotal >= originalTotal - allowedDifference
|
||||
&& totalDifference <= allowedDifference;
|
||||
}
|
||||
|
||||
public boolean hasUsedMoonseye() {
|
||||
return hasUsedMoonseye;
|
||||
}
|
||||
|
||||
public void setHasUsedMoonseye(boolean used) {
|
||||
this.hasUsedMoonseye = used;
|
||||
int allowed = Math.max(2, (int) Math.ceil(originalTotal * 0.05));
|
||||
return originalTotal > 0 && currentTotal >= originalTotal - allowed && difference <= allowed;
|
||||
}
|
||||
|
||||
public void setHasUsedMoonseye(boolean used) { hasUsedMoonseye = used; }
|
||||
|
||||
public CompoundTag serializeNBT() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putInt("DataVersion", dataVersion);
|
||||
tag.putInt("AscendLevel", ascendLevel);
|
||||
tag.putInt("AscendXP", ascendXP);
|
||||
tag.putInt("UnspentPoints", unspentPoints);
|
||||
@@ -202,82 +121,63 @@ public class PlayerStats {
|
||||
tag.putInt("Knowledge", knowledge);
|
||||
tag.putBoolean("HasUsedMoonseye", hasUsedMoonseye);
|
||||
tag.putInt("RespecCount", respecCount);
|
||||
tag.putBoolean("EverInvestedWeaponScaling", everInvestedWeaponScaling);
|
||||
CompoundTag attrTag = new CompoundTag();
|
||||
for (Map.Entry<String, Integer> entry : attributes.entrySet()) {
|
||||
attrTag.putInt(entry.getKey(), entry.getValue());
|
||||
}
|
||||
attributes.forEach(attrTag::putInt);
|
||||
tag.put("Attributes", attrTag);
|
||||
CompoundTag respecBuildTag = new CompoundTag();
|
||||
for (Map.Entry<String, Integer> entry : lastRespecBuild.entrySet()) {
|
||||
respecBuildTag.putInt(entry.getKey(), entry.getValue());
|
||||
}
|
||||
tag.put("LastRespecBuild", respecBuildTag);
|
||||
CompoundTag respecTag = new CompoundTag();
|
||||
lastRespecBuild.forEach(respecTag::putInt);
|
||||
tag.put("LastRespecBuild", respecTag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void deserializeNBT(CompoundTag tag) {
|
||||
maxAscendLevel = tag.contains("MaxAscendLevel")
|
||||
? Math.max(1, tag.getInt("MaxAscendLevel"))
|
||||
: getConfiguredMaxAscendLevel();
|
||||
maxAttributePoints = tag.contains("MaxAttributePoints")
|
||||
? Math.max(1, tag.getInt("MaxAttributePoints"))
|
||||
: getConfiguredMaxAttributePoints();
|
||||
ascendLevel = tag.contains("AscendLevel") ? tag.getInt("AscendLevel") : 1;
|
||||
ascendLevel = Math.max(1, Math.min(ascendLevel, getMaxAscendLevel()));
|
||||
ascendXP = tag.contains("AscendXP") ? tag.getInt("AscendXP") : 0;
|
||||
if (tag.contains("UnspentPoints")) {
|
||||
unspentPoints = Math.max(0, tag.getInt("UnspentPoints"));
|
||||
} else {
|
||||
unspentPoints = getPointsPerLevel();
|
||||
}
|
||||
if (tag.contains("Attributes")) {
|
||||
CompoundTag attrTag = tag.getCompound("Attributes");
|
||||
for (String key : attributes.keySet()) {
|
||||
int loaded = attrTag.getInt(key);
|
||||
attributes.put(key, Math.max(0, Math.min(loaded, getMaxAttributePoints())));
|
||||
}
|
||||
}
|
||||
lastMigrationRefund = 0;
|
||||
maxAscendLevel = tag.contains("MaxAscendLevel") ? Math.max(1, tag.getInt("MaxAscendLevel")) : getConfiguredMaxAscendLevel();
|
||||
maxAttributePoints = tag.contains("MaxAttributePoints") ? Math.max(1, tag.getInt("MaxAttributePoints")) : getConfiguredMaxAttributePoints();
|
||||
ascendLevel = Math.max(1, Math.min(tag.contains("AscendLevel") ? tag.getInt("AscendLevel") : 1, maxAscendLevel));
|
||||
ascendXP = Math.max(0, tag.contains("AscendXP") ? tag.getInt("AscendXP") : 0);
|
||||
unspentPoints = tag.contains("UnspentPoints") ? Math.max(0, tag.getInt("UnspentPoints")) : getPointsPerLevel();
|
||||
knowledge = Math.max(0, tag.getInt("Knowledge"));
|
||||
hasUsedMoonseye = tag.getBoolean("HasUsedMoonseye");
|
||||
respecCount = Math.max(0, tag.getInt("RespecCount"));
|
||||
everInvestedWeaponScaling = tag.contains("EverInvestedWeaponScaling")
|
||||
? tag.getBoolean("EverInvestedWeaponScaling")
|
||||
: getAttributeLevel("light_scaling") > 0
|
||||
|| getAttributeLevel("medium_scaling") > 0
|
||||
|| getAttributeLevel("heavy_scaling") > 0;
|
||||
CompoundTag attrTag = tag.contains("Attributes") ? tag.getCompound("Attributes") : new CompoundTag();
|
||||
for (String key : ATTRIBUTE_KEYS) attributes.put(key, Math.max(0, Math.min(attrTag.getInt(key), maxAttributePoints)));
|
||||
dataVersion = tag.contains("DataVersion") ? Math.max(1, tag.getInt("DataVersion")) : 1;
|
||||
if (dataVersion < CURRENT_DATA_VERSION) {
|
||||
long refund = 0;
|
||||
for (String legacyKey : LEGACY_REFUND_KEYS) if (attrTag.contains(legacyKey)) refund += Math.max(0, attrTag.getInt(legacyKey));
|
||||
lastMigrationRefund = (int) Math.min(Integer.MAX_VALUE, refund);
|
||||
unspentPoints = (int) Math.min(Integer.MAX_VALUE, (long) unspentPoints + lastMigrationRefund);
|
||||
dataVersion = CURRENT_DATA_VERSION;
|
||||
AscendMod.LOGGER.info("Migrated Ascend player data to version {} and refunded {} removed-attribute points", CURRENT_DATA_VERSION, lastMigrationRefund);
|
||||
}
|
||||
lastRespecBuild.clear();
|
||||
if (tag.contains("LastRespecBuild")) {
|
||||
CompoundTag respecBuildTag = tag.getCompound("LastRespecBuild");
|
||||
for (String key : ATTRIBUTE_KEYS) {
|
||||
lastRespecBuild.put(key, Math.max(0, respecBuildTag.getInt(key)));
|
||||
}
|
||||
}
|
||||
if (tag.contains("Knowledge")) {
|
||||
knowledge = Math.max(0, tag.getInt("Knowledge"));
|
||||
}
|
||||
if (tag.contains("HasUsedMoonseye")) {
|
||||
hasUsedMoonseye = tag.getBoolean("HasUsedMoonseye");
|
||||
CompoundTag previous = tag.getCompound("LastRespecBuild");
|
||||
for (String key : ATTRIBUTE_KEYS) lastRespecBuild.put(key, Math.max(0, previous.getInt(key)));
|
||||
}
|
||||
int maxXp = getXPToNextAscendLevel();
|
||||
if (ascendXP > maxXp) {
|
||||
ascendXP = maxXp;
|
||||
}
|
||||
if (maxXp > 0) ascendXP = Math.min(ascendXP, maxXp);
|
||||
}
|
||||
|
||||
private static int getPointsPerLevel() {
|
||||
return AscendConfig.COMMON.pointsPerLevel.get();
|
||||
/**
|
||||
* Re-applies the server's current progression cap after persistent data is loaded.
|
||||
* MaxAscendLevel remains serialized because the same payload is sent to clients,
|
||||
* but a value captured by an older server configuration must not override the
|
||||
* configuration that is active now.
|
||||
*/
|
||||
public void refreshConfiguredMaxAscendLevel() {
|
||||
maxAscendLevel = getConfiguredMaxAscendLevel();
|
||||
ascendLevel = Math.max(1, Math.min(ascendLevel, maxAscendLevel));
|
||||
int maxXp = getXPToNextAscendLevel();
|
||||
if (maxXp > 0) ascendXP = Math.min(ascendXP, maxXp);
|
||||
}
|
||||
|
||||
private static int getConfiguredMaxAscendLevel() {
|
||||
return AscendConfig.COMMON.maxAscendLevel.get();
|
||||
}
|
||||
private static int getPointsPerLevel() { return configValue(AscendConfig.COMMON.pointsPerLevel); }
|
||||
private static int getConfiguredMaxAscendLevel() { return configValue(AscendConfig.COMMON.maxAscendLevel); }
|
||||
private static int getConfiguredMaxAttributePoints() { return configValue(AscendConfig.COMMON.maxAttributePoints); }
|
||||
|
||||
private static int getConfiguredMaxAttributePoints() {
|
||||
return AscendConfig.COMMON.maxAttributePoints.get();
|
||||
}
|
||||
|
||||
private static boolean isWeaponScaling(String attribute) {
|
||||
return "light_scaling".equals(attribute)
|
||||
|| "medium_scaling".equals(attribute)
|
||||
|| "heavy_scaling".equals(attribute);
|
||||
private static int configValue(net.minecraftforge.common.ForgeConfigSpec.IntValue value) {
|
||||
return AscendConfig.COMMON_SPEC.isLoaded() ? value.get() : value.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public class PlayerStatsProvider implements ICapabilitySerializable<CompoundTag>
|
||||
return;
|
||||
}
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
stats.refreshConfiguredMaxAscendLevel();
|
||||
AscendAdvancements.checkMilestones(serverPlayer, stats);
|
||||
CompoundTag data = stats.serializeNBT();
|
||||
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> serverPlayer),
|
||||
|
||||
@@ -2,288 +2,107 @@ package net.fretux.ascend.player;
|
||||
|
||||
import net.fretux.ascend.compat.IronsSpellbooksCompat;
|
||||
import net.fretux.ascend.config.AscendConfig;
|
||||
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.AttributeModifier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class StatEffects {
|
||||
private static final UUID STRENGTH_KNOCKBACK_UUID = UUID.fromString("b17f0d74-9cd9-4f90-9ec8-6a1046e2f912");
|
||||
private static final UUID AGILITY_SPEED_UUID = UUID.fromString("c2f4e9a1-3b8d-4f21-9d44-8176f8791a11");
|
||||
// Reuse the old persisted health UUID so upgrades cleanly replace old world modifiers.
|
||||
private static final UUID VIGOR_HEALTH_UUID = UUID.fromString("d3a4b5c6-7d8e-4f90-8a1b-223344556677");
|
||||
private static final UUID REMOVED_HEALTH_STAT_KB_UUID = 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 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("c2f4e9a1-3b8d-4f21-9d44-8176f8791a11");
|
||||
|
||||
private static final UUID FORTITUDE_HEALTH_UUID =
|
||||
UUID.fromString("d3a4b5c6-7d8e-4f90-8a1b-223344556677");
|
||||
private static final UUID FORTITUDE_KB_RESIST_UUID =
|
||||
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 static double getScaling() {
|
||||
return AscendConfig.COMMON.attributeScalingMultiplier.get();
|
||||
}
|
||||
|
||||
private static double scaledRatio(int level, double perPoint, double max) {
|
||||
if (level <= 0) return 0.0d;
|
||||
return Math.min(level * perPoint * getScaling(), max);
|
||||
}
|
||||
|
||||
private static double scalingMultiplier(int level, double perPoint, double maxBonus) {
|
||||
return 1.0d + scaledRatio(level, perPoint, maxBonus);
|
||||
}
|
||||
private StatEffects() {}
|
||||
|
||||
public static void applyAll(Player player) {
|
||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||
applyVigor(player, stats.getAttributeLevel("vigor"));
|
||||
applyStrength(player, stats.getAttributeLevel("strength"));
|
||||
applyAgility(player, stats.getAttributeLevel("agility"));
|
||||
applyFortitude(player, stats.getAttributeLevel("fortitude"));
|
||||
if (IronsSpellbooksCompat.isLoaded()) {
|
||||
IronsSpellbooksCompat.applyAll(player);
|
||||
}
|
||||
if (player.getHealth() > player.getMaxHealth()) {
|
||||
player.setHealth(player.getMaxHealth());
|
||||
}
|
||||
IronsSpellbooksCompat.applyAll(player);
|
||||
if (player.getHealth() > player.getMaxHealth()) player.setHealth(player.getMaxHealth());
|
||||
});
|
||||
}
|
||||
|
||||
public static void applyStrength(Player player, int strengthLevel) {
|
||||
// Always remove first so resets correctly.
|
||||
AttributeInstance atk = player.getAttribute(Attributes.ATTACK_DAMAGE);
|
||||
if (atk != null) {
|
||||
atk.removeModifier(STRENGTH_DAMAGE_UUID);
|
||||
if (strengthLevel > 0) {
|
||||
double dmgBonus = Math.min(strengthLevel * 0.06d * getScaling(), 6.0d);
|
||||
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 = scaledRatio(strengthLevel, 0.002d, 0.2d);
|
||||
kb.addTransientModifier(new AttributeModifier(
|
||||
STRENGTH_KNOCKBACK_UUID,
|
||||
"Ascend Strength knockback bonus",
|
||||
kbBonus,
|
||||
AttributeModifier.Operation.ADDITION
|
||||
));
|
||||
}
|
||||
}
|
||||
public static void applyVigor(Player player, int level) {
|
||||
AttributeInstance health = player.getAttribute(Attributes.MAX_HEALTH);
|
||||
if (health == null) return;
|
||||
health.removeModifier(VIGOR_HEALTH_UUID);
|
||||
double bonus = getVigorHealthBonus(level);
|
||||
if (bonus > 0) health.addPermanentModifier(new AttributeModifier(VIGOR_HEALTH_UUID,
|
||||
"Ascend Vigor health bonus", bonus, 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 bonus = scaledRatio(agilityLevel, 0.0008d, 0.08d);
|
||||
move.addTransientModifier(new AttributeModifier(
|
||||
AGILITY_SPEED_UUID,
|
||||
"Ascend Agility speed bonus",
|
||||
bonus,
|
||||
AttributeModifier.Operation.ADDITION
|
||||
));
|
||||
}
|
||||
}
|
||||
public static void applyStrength(Player player, int level) {
|
||||
AttributeInstance attack = player.getAttribute(Attributes.ATTACK_DAMAGE);
|
||||
if (attack != null) attack.removeModifier(UUID.fromString("a7b8b2c3-9b0d-4e1a-8ee3-21f6a7b2c3d4"));
|
||||
AttributeInstance resistance = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
|
||||
if (resistance != null) resistance.removeModifier(REMOVED_HEALTH_STAT_KB_UUID);
|
||||
AttributeInstance knockback = player.getAttribute(Attributes.ATTACK_KNOCKBACK);
|
||||
if (knockback == null) return;
|
||||
knockback.removeModifier(STRENGTH_KNOCKBACK_UUID);
|
||||
double bonus = getStrengthKnockbackBonus(level);
|
||||
if (bonus > 0) knockback.addTransientModifier(new AttributeModifier(STRENGTH_KNOCKBACK_UUID,
|
||||
"Ascend Strength knockback bonus", bonus, AttributeModifier.Operation.ADDITION));
|
||||
}
|
||||
|
||||
public static void applyFortitude(Player player, int fortitudeLevel) {
|
||||
AttributeInstance hp = player.getAttribute(Attributes.MAX_HEALTH);
|
||||
if (hp != null) {
|
||||
hp.removeModifier(FORTITUDE_HEALTH_UUID);
|
||||
if (fortitudeLevel > 0) {
|
||||
double healthBonus = Math.min(fortitudeLevel * 0.18d * getScaling(), 18.0d);
|
||||
// Max health must be present while Minecraft loads the player's saved Health
|
||||
// value, otherwise values above the vanilla 20 HP are clamped before login.
|
||||
hp.addPermanentModifier(new AttributeModifier(
|
||||
FORTITUDE_HEALTH_UUID,
|
||||
"Ascend Fortitude health bonus",
|
||||
healthBonus,
|
||||
AttributeModifier.Operation.ADDITION
|
||||
));
|
||||
}
|
||||
}
|
||||
AttributeInstance kbRes = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
|
||||
if (kbRes != null) {
|
||||
kbRes.removeModifier(FORTITUDE_KB_RESIST_UUID);
|
||||
if (fortitudeLevel > 0) {
|
||||
double res = scaledRatio(fortitudeLevel, 0.003d, 0.3d);
|
||||
kbRes.addTransientModifier(new AttributeModifier(
|
||||
FORTITUDE_KB_RESIST_UUID,
|
||||
"Ascend Fortitude knockback resist",
|
||||
res,
|
||||
AttributeModifier.Operation.ADDITION
|
||||
));
|
||||
}
|
||||
}
|
||||
public static void applyAgility(Player player, int level) {
|
||||
AttributeInstance movement = player.getAttribute(Attributes.MOVEMENT_SPEED);
|
||||
if (movement == null) return;
|
||||
movement.removeModifier(AGILITY_SPEED_UUID);
|
||||
double bonus = getAgilityMovementSpeedBonus(level);
|
||||
if (bonus > 0) movement.addTransientModifier(new AttributeModifier(AGILITY_SPEED_UUID,
|
||||
"Ascend Agility movement bonus", bonus, AttributeModifier.Operation.MULTIPLY_BASE));
|
||||
}
|
||||
|
||||
public static float getWillpowerSanityMultiplier(int willpowerLevel) {
|
||||
if (willpowerLevel <= 0) return 1.0f;
|
||||
double reduction = scaledRatio(willpowerLevel, 0.0035d, 0.35d);
|
||||
return (float) (1.0d - reduction);
|
||||
public static double getVigorHealthBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.vigorMaxHealthBonus); }
|
||||
public static double getStrengthKnockbackBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.strengthKnockbackBonus); }
|
||||
public static double getStrengthArmorPenetration(int level) { return normalized(level) * configured(AscendConfig.COMMON.strengthArmorPenetration); }
|
||||
public static double getAgilityMovementSpeedBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.agilityMovementSpeedBonus); }
|
||||
public static int getMindManaBonus(int level) { return (int) Math.floor(normalized(level) * configured(AscendConfig.COMMON.mindMaxManaBonus)); }
|
||||
public static double getMindManaRegenBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.mindManaRegenBonus); }
|
||||
public static double getIntelligenceSpellPowerBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.intelligenceSpellPowerBonus); }
|
||||
public static double getIntelligenceCooldownBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.intelligenceCooldownBonus); }
|
||||
public static double getWillpowerCastTimeReduction(int level) { return normalized(level) * configured(AscendConfig.COMMON.willpowerCastTimeReduction); }
|
||||
public static double getArcaneBeneficialDurationMultiplier(int level) { return 1.0 + normalized(level) * configured(AscendConfig.COMMON.arcaneBeneficialDurationBonus); }
|
||||
public static double getArcaneHarmfulDurationMultiplier(int level) { return 1.0 + normalized(level) * configured(AscendConfig.COMMON.arcaneHarmfulDurationBonus); }
|
||||
|
||||
public static double getCharismaTradeDiscount(int level) { return normalized(level) * 0.30; }
|
||||
public static int getCharismaVillagerReputationBonus(int level) { return (int) Math.floor(normalized(level) * 8.0); }
|
||||
public static double getCharismaAllyHealthBonus(int level) { return normalized(level) * 0.20; }
|
||||
public static double getCharismaAllyDamageBonus(int level) { return normalized(level) * 0.20; }
|
||||
|
||||
public static void applyCharismaAllyBuff(LivingEntity entity, int level) {
|
||||
if (level <= 0) { clearCharismaAllyBuff(entity); return; }
|
||||
updateModifier(entity.getAttribute(Attributes.MAX_HEALTH), CHARISMA_ALLY_HEALTH_UUID,
|
||||
"Ascend Charisma ally max health", getCharismaAllyHealthBonus(level));
|
||||
updateModifier(entity.getAttribute(Attributes.ATTACK_DAMAGE), CHARISMA_ALLY_DAMAGE_UUID,
|
||||
"Ascend Charisma ally damage", getCharismaAllyDamageBonus(level));
|
||||
}
|
||||
|
||||
public static float getWillpowerTempoMultiplier(int willpowerLevel) {
|
||||
if (willpowerLevel <= 0) return 1.0f;
|
||||
double bonus = scaledRatio(willpowerLevel, 0.003d, 0.3d);
|
||||
return (float) (1.0d + bonus);
|
||||
}
|
||||
|
||||
public static float getWillpowerHealthRegen(int willpowerLevel) {
|
||||
if (willpowerLevel <= 0) return 0.0f;
|
||||
double bonus = scaledRatio(willpowerLevel, 0.0125d, 1.25d);
|
||||
return (float) bonus;
|
||||
}
|
||||
|
||||
public static double getWillpowerCastTimeReduction(int willpowerLevel) {
|
||||
return scaledRatio(willpowerLevel, 0.002d, 0.2d);
|
||||
}
|
||||
|
||||
public static float getPlayerSanityMultiplier(Player player) {
|
||||
return player.getCapability(PlayerStatsProvider.PLAYER_STATS)
|
||||
.map(stats -> getWillpowerSanityMultiplier(stats.getAttributeLevel("willpower")))
|
||||
.orElse(1.0f);
|
||||
}
|
||||
|
||||
public static float getPlayerTempoMultiplier(Player player) {
|
||||
return player.getCapability(PlayerStatsProvider.PLAYER_STATS)
|
||||
.map(stats -> getWillpowerTempoMultiplier(stats.getAttributeLevel("willpower")))
|
||||
.orElse(1.0f);
|
||||
}
|
||||
|
||||
public static int getIntelligenceManaBonus(int intelligenceLevel) {
|
||||
return (int) Math.floor(Math.min(intelligenceLevel * 2.0d * getScaling(), 200.0d));
|
||||
}
|
||||
|
||||
public static double getIntelligenceAnvilCostReduction(int intelligenceLevel) {
|
||||
return scaledRatio(intelligenceLevel, 0.003d, 0.3d);
|
||||
}
|
||||
|
||||
public static int getIntelligenceKnowledgeGain(int intelligenceLevel, int baseGain) {
|
||||
if (baseGain <= 0) return baseGain;
|
||||
if (intelligenceLevel <= 0) return baseGain;
|
||||
double bonus = scaledRatio(intelligenceLevel, 0.003d, 0.3d);
|
||||
int extra = (int) Math.floor(baseGain * bonus);
|
||||
return baseGain + Math.max(0, extra);
|
||||
}
|
||||
|
||||
public static double getCharismaTradeDiscount(int charismaLevel) {
|
||||
return scaledRatio(charismaLevel, 0.003d, 0.3d);
|
||||
}
|
||||
|
||||
public static int getCharismaVillagerReputationBonus(int charismaLevel) {
|
||||
if (charismaLevel <= 0) return 0;
|
||||
return (int) Math.floor(Math.min(charismaLevel * 0.08d * getScaling(), 8.0d));
|
||||
}
|
||||
|
||||
public static double getCharismaAllyHealthBonus(int charismaLevel) {
|
||||
return scaledRatio(charismaLevel, 0.002d, 0.2d);
|
||||
}
|
||||
|
||||
public static double getCharismaAllyDamageBonus(int charismaLevel) {
|
||||
return scaledRatio(charismaLevel, 0.002d, 0.2d);
|
||||
}
|
||||
|
||||
public static double getFortitudeEffectGuardChance(int fortitudeLevel) {
|
||||
return scaledRatio(fortitudeLevel, 0.0025d, 0.25d);
|
||||
}
|
||||
|
||||
public static double getFortitudeCleanseChance(int fortitudeLevel) {
|
||||
return scaledRatio(fortitudeLevel, 0.002d, 0.2d);
|
||||
}
|
||||
|
||||
public static double getAgilityEvasionChance(int agilityLevel) {
|
||||
return scaledRatio(agilityLevel, 0.0015d, 0.15d);
|
||||
}
|
||||
|
||||
public static double getAgilityRangedDamageMultiplier(int agilityLevel) {
|
||||
return 1.0d + Math.min((getLightWeaponScalingMultiplier(agilityLevel) - 1.0d) * 0.7d, 0.25d);
|
||||
}
|
||||
|
||||
public static double getStrengthTridentDamageMultiplier(int strengthLevel) {
|
||||
return 1.0d + Math.min((getWeaponScalingMultiplier(strengthLevel) - 1.0d) * 0.6d, 0.2d);
|
||||
}
|
||||
|
||||
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
|
||||
));
|
||||
}
|
||||
}
|
||||
private static void updateModifier(AttributeInstance instance, UUID id, String name, double value) {
|
||||
if (instance == null) return;
|
||||
instance.removeModifier(id);
|
||||
if (value > 0) instance.addTransientModifier(new AttributeModifier(id, name, value, 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);
|
||||
}
|
||||
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) {
|
||||
return scalingMultiplier(scalingLevel, 0.0035d, 0.35d);
|
||||
if (damage != null) damage.removeModifier(CHARISMA_ALLY_DAMAGE_UUID);
|
||||
if (entity.getHealth() > entity.getMaxHealth()) entity.setHealth(entity.getMaxHealth());
|
||||
}
|
||||
|
||||
public static double getLightWeaponScalingMultiplier(int scalingLevel) {
|
||||
return scalingMultiplier(scalingLevel, 0.0035d, 0.35d);
|
||||
}
|
||||
|
||||
public static double getHeavyWeaponScalingMultiplier(int scalingLevel) {
|
||||
return scalingMultiplier(scalingLevel, 0.0035d, 0.35d);
|
||||
}
|
||||
|
||||
public static double getMagicScalingMultiplier(int scalingLevel) {
|
||||
return scalingMultiplier(scalingLevel, 0.0035d, 0.35d);
|
||||
private static double normalized(int level) { return AttributeScaling.getNormalizedAttributeValue(level); }
|
||||
private static double configured(net.minecraftforge.common.ForgeConfigSpec.DoubleValue value) {
|
||||
return AscendConfig.COMMON_SPEC.isLoaded() ? value.get() : value.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,32 +7,38 @@
|
||||
"ascend.stats.xp_max": "Ascend XP: MAX",
|
||||
"ascend.stats.unspent": "Unspent Points: %s",
|
||||
|
||||
"ascend.attribute.vigor": "Vigor",
|
||||
"ascend.attribute.vigor.desc": "Increases maximum health.",
|
||||
"ascend.attribute.vigor.short": "Increases maximum health",
|
||||
"ascend.attribute.mind": "Mind",
|
||||
"ascend.attribute.mind.desc": "Increases mana capacity and regeneration.",
|
||||
"ascend.attribute.mind.short": "Increases mana capacity and regeneration",
|
||||
"ascend.attribute.strength": "Strength",
|
||||
"ascend.attribute.strength.desc": "Increases melee damage, knockback and armor penetration.",
|
||||
"ascend.attribute.strength.desc": "Improves Strength weapon scaling, armor penetration, and knockback.",
|
||||
"ascend.attribute.strength.short": "Improves heavy weapon scaling",
|
||||
"ascend.attribute.agility": "Agility",
|
||||
"ascend.attribute.agility.desc": "Increases movement speed and mobility.",
|
||||
"ascend.attribute.fortitude": "Fortitude",
|
||||
"ascend.attribute.fortitude.desc": "Increases max health and knockback resistance.",
|
||||
"ascend.attribute.agility.desc": "Improves agile and ranged weapon scaling and movement speed.",
|
||||
"ascend.attribute.agility.short": "Improves agile and ranged weapons",
|
||||
"ascend.attribute.intelligence": "Intelligence",
|
||||
"ascend.attribute.intelligence.desc": "Increases max mana and mana regeneration. Lowers enchantment/anvil costs, buffs potions, and boosts knowledge gain.",
|
||||
"ascend.attribute.intelligence.desc": "Improves spell power and reduces spell cooldowns.",
|
||||
"ascend.attribute.intelligence.short": "Improves spell power and cooldowns",
|
||||
"ascend.attribute.willpower": "Willpower",
|
||||
"ascend.attribute.willpower.desc": "Reduces sanity drain, improves stamina/tempo, and increases passive health regeneration.",
|
||||
"ascend.attribute.willpower.desc": "Increases maximum Sanity and Tempo and improves spell casting.",
|
||||
"ascend.attribute.willpower.short": "Increases max Sanity and Tempo",
|
||||
"ascend.attribute.charisma": "Charisma",
|
||||
"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.desc": "Improves light weapon performance.",
|
||||
"ascend.attribute.medium_scaling": "Medium Scaling",
|
||||
"ascend.attribute.medium_scaling.desc": "Improves medium weapon performance.",
|
||||
"ascend.attribute.heavy_scaling": "Heavy Scaling",
|
||||
"ascend.attribute.heavy_scaling.desc": "Improves heavy weapon performance.",
|
||||
"ascend.attribute.magic_scaling": "Magic Scaling",
|
||||
"ascend.attribute.magic_scaling.desc": "Increases spell power, spell damage, and spell cooldown reduction.",
|
||||
"ascend.attribute.charisma.desc": "Improves villager trading and strengthens nearby allies.",
|
||||
"ascend.attribute.charisma.short": "Improves trading and allies",
|
||||
"ascend.attribute.arcane": "Arcane",
|
||||
"ascend.attribute.arcane.desc": "Increases the duration of beneficial and harmful status effects.",
|
||||
"ascend.attribute.arcane.short": "Improves status effects",
|
||||
"item.ascend.moonseye_tome": "Moonseye Tome",
|
||||
"item.ascend.moonseye_tome.desc": "A forbidden scripture that reveals knowledge of the Depths. Grants 100 Knowledge on first use, 50 afterwards.",
|
||||
"item.ascend.moonseye_tome.used": "You gained %s Knowledge from the Moonseye Tome!",
|
||||
"itemGroup.ascend": "Ascend",
|
||||
"ascend.attribute.display": "%s: %s",
|
||||
"ascend.stats.knowledge": "Knowledge: %s",
|
||||
"ascend.weapon_scaling.title": "Attribute Scaling",
|
||||
"ascend.weapon_scaling.grade": "%s: %s",
|
||||
"item.ascend.remembrance_essence": "Remembrance Essence",
|
||||
"block.ascend.shrine_of_remembrance": "Shrine of Remembrance",
|
||||
|
||||
@@ -58,33 +64,17 @@
|
||||
"advancements.ascend.cannot_hit_what_you_cannot_see.title": "You Cannot Hit What You Cannot See",
|
||||
"advancements.ascend.cannot_hit_what_you_cannot_see.description": "Reach 100 Agility",
|
||||
"advancements.ascend.health_bar_enthusiast.title": "Health Bar Enthusiast",
|
||||
"advancements.ascend.health_bar_enthusiast.description": "Reach 100 Fortitude",
|
||||
"advancements.ascend.health_bar_enthusiast.description": "Reach 100 Vigor",
|
||||
"advancements.ascend.big_brain_small_helmet.title": "Big Brain Time",
|
||||
"advancements.ascend.big_brain_small_helmet.description": "Reach 100 Intelligence",
|
||||
"advancements.ascend.mana_without_purpose.title": "Mana Without Purpose",
|
||||
"advancements.ascend.mana_without_purpose.description": "Reach 50 Intelligence without Iron's Spells installed",
|
||||
"advancements.ascend.mana_without_purpose.description": "Reach 100 Mind",
|
||||
"advancements.ascend.unlicensed_therapist.title": "Unlicensed Therapist",
|
||||
"advancements.ascend.unlicensed_therapist.description": "Reach 100 Willpower",
|
||||
"advancements.ascend.talk_no_jutsu.title": "Talk No Jutsu",
|
||||
"advancements.ascend.talk_no_jutsu.description": "Reach 100 Charisma",
|
||||
"advancements.ascend.perfectly_balanced.title": "Perfectly Balanced",
|
||||
"advancements.ascend.perfectly_balanced.description": "Invest exactly the same number of points in all ten attributes",
|
||||
"advancements.ascend.light_work.title": "Light Work",
|
||||
"advancements.ascend.light_work.description": "Reach 100 Light Scaling",
|
||||
"advancements.ascend.medium_rare.title": "Medium Rare",
|
||||
"advancements.ascend.medium_rare.description": "Reach 100 Medium Scaling",
|
||||
"advancements.ascend.compensating.title": "Compensating",
|
||||
"advancements.ascend.compensating.description": "Reach 100 Heavy Scaling",
|
||||
"advancements.ascend.quality_build.title": "Quality Build",
|
||||
"advancements.ascend.quality_build.description": "Reach 75 in all three weapon scaling attributes",
|
||||
"advancements.ascend.oops_all_magic.title": "Oops, All Magic",
|
||||
"advancements.ascend.oops_all_magic.description": "Reach 100 Magic Scaling before investing in weapon scaling",
|
||||
"advancements.ascend.glass_cannonical.title": "Glass Cannonical",
|
||||
"advancements.ascend.glass_cannonical.description": "Reach 90 Intelligence and Magic Scaling while keeping Fortitude below 10",
|
||||
"advancements.ascend.muscle_wizard.title": "Muscle Wizard",
|
||||
"advancements.ascend.muscle_wizard.description": "Reach 100 Strength and Magic Scaling",
|
||||
"advancements.ascend.optimized_casting_environment.title": "Optimized Casting Environment",
|
||||
"advancements.ascend.optimized_casting_environment.description": "Reach 80 Intelligence, Willpower, and Magic Scaling",
|
||||
"advancements.ascend.perfectly_balanced.description": "Invest exactly the same number of points in all eight attributes",
|
||||
"advancements.ascend.indecisive_chosen_one.title": "Indecisive Chosen One",
|
||||
"advancements.ascend.indecisive_chosen_one.description": "Respec ten times on the same character",
|
||||
"advancements.ascend.config_enjoyer.title": "Config Enjoyer",
|
||||
|
||||
Reference in New Issue
Block a user