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.
|
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
|
## 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.
|
- 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
|
// CURIOS
|
||||||
implementation fg.deobf("top.theillusivec4.curios:curios-forge:${curios_version}")
|
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.
|
// 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.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=All Rights Reserved
|
mod_license=All Rights Reserved
|
||||||
# The mod version. See https://semver.org/
|
# 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.
|
# 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.
|
# This should match the base package used for the mod sources.
|
||||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
# 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.eventbus.api.IEventBus;
|
||||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import com.mojang.logging.LogUtils;
|
||||||
|
|
||||||
@Mod(AscendMod.MODID)
|
@Mod(AscendMod.MODID)
|
||||||
public class AscendMod {
|
public class AscendMod {
|
||||||
public static final String MODID = "ascend";
|
public static final String MODID = "ascend";
|
||||||
|
public static final Logger LOGGER = LogUtils.getLogger();
|
||||||
|
|
||||||
public AscendMod() {
|
public AscendMod() {
|
||||||
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
|
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||||
|
|||||||
@@ -14,26 +14,29 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class StatsScreen extends Screen {
|
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 String[] ATTRIBUTES = {
|
||||||
private static final int HEIGHT = 240;
|
"vigor", "mind", "strength", "agility",
|
||||||
|
"intelligence", "willpower", "charisma", "arcane"
|
||||||
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 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<>();
|
private static final Map<String, Component> ATTRIBUTE_TOOLTIPS = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -42,95 +45,139 @@ public class StatsScreen extends Screen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
protected void init() {
|
protected void init() {
|
||||||
super.init();
|
super.init();
|
||||||
this.leftPos = (this.width - WIDTH) / 2;
|
leftPos = (width - WIDTH) / 2;
|
||||||
this.topPos = (this.height - HEIGHT) / 2;
|
topPos = (height - HEIGHT) / 2;
|
||||||
this.plusButtons.clear();
|
plusButtons.clear();
|
||||||
this.clearWidgets();
|
clearWidgets();
|
||||||
int y = topPos + 55;
|
|
||||||
for (String attr : ATTRIBUTES) {
|
for (int index = 0; index < ATTRIBUTES.length; index++) {
|
||||||
final String key = attr;
|
String attribute = ATTRIBUTES[index];
|
||||||
Button plus = Button.builder(Component.literal("+"), (button) -> {
|
int column = index % 2;
|
||||||
PacketHandler.INSTANCE.sendToServer(new ServerboundSpendPointPacket(key));
|
int row = index / 2;
|
||||||
})
|
int cardX = leftPos + PADDING + column * (CARD_WIDTH + COLUMN_GAP);
|
||||||
.bounds(leftPos + WIDTH - 40, y - 6, 30, 18)
|
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();
|
.build();
|
||||||
this.addRenderableWidget(plus);
|
addRenderableWidget(plus);
|
||||||
this.plusButtons.put(key, plus);
|
plusButtons.put(attribute, plus);
|
||||||
y += 16;
|
|
||||||
}
|
}
|
||||||
Button done = Button.builder(Component.translatable("gui.done"), (btn) -> onClose())
|
|
||||||
.bounds(leftPos + WIDTH / 2 - 40, topPos + HEIGHT - 24, 80, 20)
|
addRenderableWidget(Button.builder(Component.translatable("gui.done"), button -> onClose())
|
||||||
.build();
|
.bounds(leftPos + WIDTH / 2 - 45, topPos + HEIGHT - 24, 90, 18)
|
||||||
this.addRenderableWidget(done);
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics gui, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
this.renderBackground(gui);
|
renderBackground(graphics);
|
||||||
RenderSystem.enableBlend();
|
drawWindow(graphics);
|
||||||
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);
|
|
||||||
hoveredAttributeKey = null;
|
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) {
|
if (plus != null) {
|
||||||
plus.setX(leftPos + WIDTH - 40);
|
int cost = stats.getCostToUpgrade(attribute);
|
||||||
plus.setY(y - 6);
|
plus.active = stats.getUnspentPoints() >= cost && value < stats.getMaxAttributePoints();
|
||||||
plus.active = (unspent >= cost);
|
plus.setMessage(value >= stats.getMaxAttributePoints()
|
||||||
plus.setMessage(Component.literal("+" + cost));
|
? 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) {
|
if (hoveredAttributeKey != null) {
|
||||||
Component tooltip = ATTRIBUTE_TOOLTIPS.get(hoveredAttributeKey);
|
graphics.renderTooltip(font, ATTRIBUTE_TOOLTIPS.get(hoveredAttributeKey), mouseX, mouseY);
|
||||||
if (tooltip != null) {
|
|
||||||
gui.renderTooltip(this.font, tooltip, 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
|
@Override
|
||||||
|
|||||||
@@ -62,7 +62,10 @@ public class AscendCommand {
|
|||||||
String attr = StringArgumentType.getString(ctx, "attribute");
|
String attr = StringArgumentType.getString(ctx, "attribute");
|
||||||
int value = IntegerArgumentType.getInteger(ctx, "value");
|
int value = IntegerArgumentType.getInteger(ctx, "value");
|
||||||
target.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
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);
|
StatEffects.applyAll(target);
|
||||||
PlayerStatsProvider.sync(target);
|
PlayerStatsProvider.sync(target);
|
||||||
ctx.getSource().sendSuccess(() ->
|
ctx.getSource().sendSuccess(() ->
|
||||||
|
|||||||
@@ -1,9 +1,43 @@
|
|||||||
package net.fretux.ascend.compat;
|
package net.fretux.ascend.compat;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraftforge.fml.ModList;
|
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() {
|
public static boolean isMindMotionPresent() {
|
||||||
return ModList.get().isLoaded("mindmotion");
|
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;
|
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.PlayerStatsProvider;
|
||||||
|
import net.fretux.ascend.player.StatEffects;
|
||||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||||
@@ -11,250 +10,63 @@ import net.minecraftforge.fml.ModList;
|
|||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class IronsSpellbooksCompat {
|
/** Optional Iron's Spellbooks bridge. All Iron's classes are isolated in Loaded. */
|
||||||
private static final UUID INT_MAX_MANA_UUID =
|
public final class IronsSpellbooksCompat {
|
||||||
UUID.fromString("11111111-1111-4111-8111-111111111111");
|
private static final UUID MIND_MANA = UUID.fromString("11111111-1111-4111-8111-111111111111");
|
||||||
private static final UUID INT_SPELL_POWER_UUID =
|
private static final UUID INT_SPELL_POWER = UUID.fromString("22222222-2222-4222-8222-222222222222");
|
||||||
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_MANA_REGEN_UUID =
|
private static final UUID INT_COOLDOWN = UUID.fromString("44444444-4444-4444-8444-444444444444");
|
||||||
UUID.fromString("33333333-3333-4333-8333-333333333333");
|
private static final UUID LEGACY_CHARISMA_MANA = UUID.fromString("55555555-5555-4555-8555-555555555555");
|
||||||
private static final UUID INT_COOLDOWN_REDUCTION_UUID =
|
private static final UUID LEGACY_MAGIC_POWER = UUID.fromString("77777777-7777-4777-8777-777777777777");
|
||||||
UUID.fromString("44444444-4444-4444-8444-444444444444");
|
private static final UUID LEGACY_MAGIC_REGEN = UUID.fromString("88888888-8888-4888-8888-888888888888");
|
||||||
private static final UUID CHA_MAX_MANA_UUID =
|
private static final UUID LEGACY_MAGIC_COOLDOWN = UUID.fromString("99999999-9999-4999-8999-999999999999");
|
||||||
UUID.fromString("55555555-5555-4555-8555-555555555555");
|
private static final UUID WILL_CAST_TIME = UUID.fromString("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa");
|
||||||
// reserved for future spell-trade hooks
|
private static final UUID CHARISMA_SUPPORT = UUID.fromString("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb");
|
||||||
private static final UUID CHA_SPELL_TRADE_UUID =
|
private static final UUID CHARISMA_SUMMON_DAMAGE = UUID.fromString("dddddddd-dddd-4ddd-8ddd-dddddddddddd");
|
||||||
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");
|
|
||||||
|
|
||||||
|
private IronsSpellbooksCompat() {}
|
||||||
private IronsSpellbooksCompat() {
|
public static boolean isLoaded() { return ModList.get().isLoaded("irons_spellbooks"); }
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isLoaded() {
|
|
||||||
return ModList.get().isLoaded("irons_spellbooks");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void applyAll(Player player) {
|
public static void applyAll(Player player) {
|
||||||
if (!isLoaded()) return;
|
if (!isLoaded()) return;
|
||||||
|
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> Loaded.apply(player,
|
||||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
stats.getAttributeLevel("mind"), stats.getAttributeLevel("intelligence"),
|
||||||
int intelligence = stats.getAttributeLevel("intelligence");
|
stats.getAttributeLevel("willpower"), stats.getAttributeLevel("charisma")));
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class Loaded {
|
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) {
|
private static void update(Player player, Attribute attribute, UUID id, String name, double value, AttributeModifier.Operation operation) {
|
||||||
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) {
|
|
||||||
if (attribute == null) return;
|
if (attribute == null) return;
|
||||||
AttributeInstance inst = player.getAttribute(attribute);
|
AttributeInstance instance = player.getAttribute(attribute);
|
||||||
if (inst == null) return;
|
if (instance == null) return;
|
||||||
inst.removeModifier(id);
|
instance.removeModifier(id);
|
||||||
if (value != 0.0d) {
|
if (value != 0) instance.addTransientModifier(new AttributeModifier(id, name, value, operation));
|
||||||
inst.addTransientModifier(new AttributeModifier(
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
AttributeModifier.Operation.ADDITION
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
private static void remove(Player player, Attribute attribute, UUID id) {
|
||||||
|
|
||||||
private static void applyMultiplyBaseModifier(Player player, Attribute attribute, UUID id, String name, double value) {
|
|
||||||
if (attribute == null) return;
|
if (attribute == null) return;
|
||||||
AttributeInstance inst = player.getAttribute(attribute);
|
AttributeInstance instance = player.getAttribute(attribute);
|
||||||
if (inst == null) return;
|
if (instance != null) instance.removeModifier(id);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,65 +4,85 @@ import net.minecraftforge.common.ForgeConfigSpec;
|
|||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
public class AscendConfig {
|
public class AscendConfig {
|
||||||
|
|
||||||
public static class Common {
|
public static class Common {
|
||||||
public final ForgeConfigSpec.IntValue xpPerMobKill;
|
public final ForgeConfigSpec.IntValue xpPerMobKill, xpPerBlockBreak, xpPerCraft, xpPerSmelt,
|
||||||
public final ForgeConfigSpec.IntValue xpPerBlockBreak;
|
xpPerTrade, xpPerDamageTaken, xpPerMovement, xpPerPotion, pointsPerLevel,
|
||||||
public final ForgeConfigSpec.IntValue xpPerCraft;
|
maxAscendLevel, maxAttributePoints;
|
||||||
public final ForgeConfigSpec.IntValue xpPerSmelt;
|
public final ForgeConfigSpec.DoubleValue xpMultiplier, attributeScalingMultiplier;
|
||||||
public final ForgeConfigSpec.IntValue xpPerTrade;
|
public final ForgeConfigSpec.IntValue softCap1, softCap2, softCap3, softCap4;
|
||||||
public final ForgeConfigSpec.IntValue xpPerDamageTaken;
|
public final ForgeConfigSpec.DoubleValue softCapGain2, softCapGain3, softCapGain4, softCapGain5;
|
||||||
public final ForgeConfigSpec.IntValue xpPerMovement;
|
public final ForgeConfigSpec.DoubleValue vigorMaxHealthBonus, mindMaxManaBonus, mindManaRegenBonus,
|
||||||
public final ForgeConfigSpec.IntValue xpPerPotion;
|
strengthArmorPenetration, armorPenetrationCap, strengthKnockbackBonus, agilityMovementSpeedBonus,
|
||||||
public final ForgeConfigSpec.IntValue pointsPerLevel;
|
intelligenceSpellPowerBonus, intelligenceCooldownBonus, willpowerCastTimeReduction,
|
||||||
public final ForgeConfigSpec.IntValue maxAscendLevel;
|
arcaneBeneficialDurationBonus, arcaneHarmfulDurationBonus;
|
||||||
public final ForgeConfigSpec.IntValue maxAttributePoints;
|
public final ForgeConfigSpec.DoubleValue gradeE, gradeD, gradeC, gradeB, gradeA, gradeS;
|
||||||
|
|
||||||
public final ForgeConfigSpec.DoubleValue xpMultiplier;
|
|
||||||
public final ForgeConfigSpec.DoubleValue attributeScalingMultiplier;
|
|
||||||
|
|
||||||
Common(ForgeConfigSpec.Builder builder) {
|
Common(ForgeConfigSpec.Builder builder) {
|
||||||
builder.push("Ascend Progression");
|
builder.push("Ascend Progression");
|
||||||
|
xpPerMobKill = intValue(builder, "xpPerMobKill", "Ascend XP gained for killing a mob.", 10, 0, 1000);
|
||||||
xpPerMobKill = builder.comment("Ascend XP gained for killing a mob.")
|
xpPerBlockBreak = intValue(builder, "xpPerBlockBreak", "Ascend XP gained for breaking ore, stone, or logs.", 2, 0, 1000);
|
||||||
.defineInRange("xpPerMobKill", 10, 0, 1000);
|
xpPerCraft = intValue(builder, "xpPerCraft", "Ascend XP gained for crafting.", 1, 0, 1000);
|
||||||
xpPerBlockBreak = builder.comment("Ascend XP gained for breaking a block (ore/stone/log).")
|
xpPerSmelt = intValue(builder, "xpPerSmelt", "Ascend XP gained for smelting.", 1, 0, 1000);
|
||||||
.defineInRange("xpPerBlockBreak", 2, 0, 1000);
|
xpPerTrade = intValue(builder, "xpPerTrade", "Ascend XP gained for villager trading.", 5, 0, 1000);
|
||||||
xpPerCraft = builder.comment("Ascend XP gained for crafting items.")
|
xpPerDamageTaken = intValue(builder, "xpPerDamageTaken", "Ascend XP gained from dangerous survival conditions.", 2, 0, 1000);
|
||||||
.defineInRange("xpPerCraft", 1, 0, 1000);
|
xpPerMovement = intValue(builder, "xpPerMovement", "Ascend XP gained for movement activities.", 1, 0, 1000);
|
||||||
xpPerSmelt = builder.comment("Ascend XP gained for smelting items.")
|
xpPerPotion = intValue(builder, "xpPerPotion", "Ascend XP gained for potion use.", 3, 0, 1000);
|
||||||
.defineInRange("xpPerSmelt", 1, 0, 1000);
|
pointsPerLevel = intValue(builder, "pointsPerLevel", "Attribute points gained per Ascend level.", 15, 1, 1000);
|
||||||
xpPerTrade = builder.comment("Ascend XP gained for trading with villagers.")
|
maxAscendLevel = intValue(builder, "maxAscendLevel", "Maximum Ascend level.", 20, 1, 100);
|
||||||
.defineInRange("xpPerTrade", 5, 0, 1000);
|
maxAttributePoints = intValue(builder, "maxAttributePoints", "Maximum investment in one attribute.", 100, 1, 1000);
|
||||||
xpPerDamageTaken = builder.comment("Ascend XP gained for taking damage.")
|
xpMultiplier = doubleValue(builder, "xpMultiplier", "Global Ascend XP multiplier.", 1.0, 0, 100);
|
||||||
.defineInRange("xpPerDamageTaken", 2, 0, 1000);
|
attributeScalingMultiplier = doubleValue(builder, "attributeScalingMultiplier", "Global multiplier applied to attribute curves.", 1.0, 0.1, 10);
|
||||||
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);
|
|
||||||
|
|
||||||
builder.pop();
|
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 ForgeConfigSpec COMMON_SPEC;
|
||||||
public static final Common COMMON;
|
public static final Common COMMON;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
final Pair<Common, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Common::new);
|
Pair<Common, ForgeConfigSpec> pair = new ForgeConfigSpec.Builder().configure(Common::new);
|
||||||
COMMON_SPEC = specPair.getRight();
|
COMMON = pair.getLeft();
|
||||||
COMMON = specPair.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.PlayerStatsProvider;
|
||||||
import net.fretux.ascend.player.StatEffects;
|
import net.fretux.ascend.player.StatEffects;
|
||||||
import net.minecraft.core.BlockPos;
|
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.effect.MobEffectCategory;
|
||||||
import net.minecraft.world.entity.ai.gossip.GossipType;
|
import net.minecraft.world.entity.ai.gossip.GossipType;
|
||||||
import net.minecraft.world.entity.npc.Villager;
|
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.eventbus.api.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import net.minecraftforge.event.level.BlockEvent;
|
import net.minecraftforge.event.level.BlockEvent;
|
||||||
import net.minecraftforge.event.entity.living.LivingDamageEvent;
|
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.animal.IronGolem;
|
import net.minecraft.world.entity.animal.IronGolem;
|
||||||
import net.minecraft.world.entity.animal.SnowGolem;
|
import net.minecraft.world.entity.animal.SnowGolem;
|
||||||
@@ -76,7 +72,7 @@ public class ActivityEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onPlayerTickFortitude(TickEvent.PlayerTickEvent event) {
|
public static void onPlayerTickSurvivalXP(TickEvent.PlayerTickEvent event) {
|
||||||
Player player = event.player;
|
Player player = event.player;
|
||||||
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
||||||
if (player.tickCount % 100 != 0) return;
|
if (player.tickCount % 100 != 0) return;
|
||||||
@@ -133,13 +129,6 @@ public class ActivityEvents {
|
|||||||
Player player = event.player;
|
Player player = event.player;
|
||||||
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
||||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
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;
|
if (player.tickCount % 200 != 0) return;
|
||||||
boolean hasNegativeEffect = player.getActiveEffects().stream()
|
boolean hasNegativeEffect = player.getActiveEffects().stream()
|
||||||
.anyMatch(e -> e.getEffect().getCategory() == MobEffectCategory.HARMFUL);
|
.anyMatch(e -> e.getEffect().getCategory() == MobEffectCategory.HARMFUL);
|
||||||
@@ -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) {
|
private static boolean isCharismaAlly(Player player, LivingEntity entity) {
|
||||||
if (entity instanceof TamableAnimal tame) {
|
if (entity instanceof TamableAnimal tame) {
|
||||||
return tame.isTame() && tame.isOwnedBy(player);
|
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.AscendMod;
|
||||||
import net.fretux.ascend.player.PlayerStatsProvider;
|
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||||
import net.fretux.ascend.player.StatEffects;
|
import net.fretux.ascend.player.StatEffects;
|
||||||
|
import net.fretux.ascend.compat.AscendMMCompat;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
@@ -52,10 +53,10 @@ public class PlayerEvents {
|
|||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
|
public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||||
if (!event.getEntity().level().isClientSide) {
|
if (!event.getEntity().level().isClientSide) {
|
||||||
float healthBeforeFortitude = event.getEntity().getHealth();
|
float healthBeforeVigor = event.getEntity().getHealth();
|
||||||
StatEffects.applyAll(event.getEntity());
|
StatEffects.applyAll(event.getEntity());
|
||||||
if (Math.abs(healthBeforeFortitude - 20.0F) < 0.001F
|
if (Math.abs(healthBeforeVigor - 20.0F) < 0.001F
|
||||||
&& event.getEntity().getMaxHealth() > healthBeforeFortitude) {
|
&& event.getEntity().getMaxHealth() > healthBeforeVigor) {
|
||||||
event.getEntity().setHealth(event.getEntity().getMaxHealth());
|
event.getEntity().setHealth(event.getEntity().getMaxHealth());
|
||||||
PENDING_FULL_HEALTH_RESTORE.add(event.getEntity().getUUID());
|
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
|
@SubscribeEvent
|
||||||
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
|
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
|
||||||
Player player = event.player;
|
Player player = event.player;
|
||||||
if (player.level().isClientSide
|
if (player.level().isClientSide || event.phase != TickEvent.Phase.END) return;
|
||||||
|| event.phase != TickEvent.Phase.END
|
if (!PENDING_FULL_HEALTH_RESTORE.remove(player.getUUID())) return;
|
||||||
|| !PENDING_FULL_HEALTH_RESTORE.remove(player.getUUID())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
StatEffects.applyAll(player);
|
StatEffects.applyAll(player);
|
||||||
player.setHealth(player.getMaxHealth());
|
player.setHealth(player.getMaxHealth());
|
||||||
PlayerStatsProvider.sync(player);
|
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.compat.AscendMMCompat;
|
||||||
import net.fretux.ascend.item.RemembranceEssenceItem;
|
import net.fretux.ascend.item.RemembranceEssenceItem;
|
||||||
import net.fretux.ascend.player.PlayerStatsProvider;
|
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||||
import net.fretux.mindmotion.player.PlayerCapabilityProvider;
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
@@ -54,29 +53,19 @@ public class ServerboundShrineChoicePacket {
|
|||||||
}
|
}
|
||||||
case "understand" -> {
|
case "understand" -> {
|
||||||
if (!AscendMMCompat.isMindMotionPresent()) return;
|
if (!AscendMMCompat.isMindMotionPresent()) return;
|
||||||
player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(sanity -> {
|
if (AscendMMCompat.consumeClearMind(player)) {
|
||||||
float sanityPercent = sanity.getSanity() / sanity.getMaxSanity();
|
|
||||||
if (sanityPercent >= 0.90f) {
|
|
||||||
stats.addKnowledgeScaled(5);
|
stats.addKnowledgeScaled(5);
|
||||||
sanity.setSanity(0);
|
|
||||||
sanity.setInsanity(0);
|
|
||||||
player.sendSystemMessage(Component.literal("YOUR MIND EXPANDS AS IT BREAKS."));
|
player.sendSystemMessage(Component.literal("YOUR MIND EXPANDS AS IT BREAKS."));
|
||||||
RemembranceEssenceItem.consume(player);
|
RemembranceEssenceItem.consume(player);
|
||||||
AscendAdvancements.grant(player, "use_shrine");
|
AscendAdvancements.grant(player, "use_shrine");
|
||||||
} else {
|
} else player.sendSystemMessage(Component.literal("YOUR MIND IS NOT YET CLEAR ENOUGH."));
|
||||||
player.sendSystemMessage(Component.literal("YOUR MIND IS NOT YET CLEAR ENOUGH."));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
PlayerStatsProvider.sync(player);
|
PlayerStatsProvider.sync(player);
|
||||||
}
|
}
|
||||||
case "rest" -> {
|
case "rest" -> {
|
||||||
if (!AscendMMCompat.isMindMotionPresent()) return;
|
if (!AscendMMCompat.isMindMotionPresent()) return;
|
||||||
if (stats.getKnowledge() >= 10) {
|
if (stats.getKnowledge() >= 10) {
|
||||||
stats.addKnowledge(-10);
|
stats.addKnowledge(-10);
|
||||||
player.getCapability(PlayerCapabilityProvider.SANITY).ifPresent(sanity -> {
|
AscendMMCompat.restoreMind(player);
|
||||||
sanity.setSanity(sanity.getMaxSanity());
|
|
||||||
sanity.setInsanity(0);
|
|
||||||
});
|
|
||||||
player.sendSystemMessage(Component.literal("THE SHRINE RESTORES YOUR FRACTURED MIND."));
|
player.sendSystemMessage(Component.literal("THE SHRINE RESTORES YOUR FRACTURED MIND."));
|
||||||
RemembranceEssenceItem.consume(player);
|
RemembranceEssenceItem.consume(player);
|
||||||
AscendAdvancements.grant(player, "use_shrine");
|
AscendAdvancements.grant(player, "use_shrine");
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class ServerboundSpendPointPacket {
|
|||||||
boolean success = stats.spendPoints(attribute);
|
boolean success = stats.spendPoints(attribute);
|
||||||
if (success) {
|
if (success) {
|
||||||
StatEffects.applyAll(player);
|
StatEffects.applyAll(player);
|
||||||
if ("fortitude".equals(attribute)) {
|
if ("vigor".equals(attribute)) {
|
||||||
float gainedMaxHealth = player.getMaxHealth() - previousMaxHealth;
|
float gainedMaxHealth = player.getMaxHealth() - previousMaxHealth;
|
||||||
if (gainedMaxHealth > 0.0F) {
|
if (gainedMaxHealth > 0.0F) {
|
||||||
player.setHealth(Math.min(player.getHealth() + gainedMaxHealth, player.getMaxHealth()));
|
player.setHealth(Math.min(player.getHealth() + gainedMaxHealth, player.getMaxHealth()));
|
||||||
|
|||||||
@@ -1,199 +1,118 @@
|
|||||||
package net.fretux.ascend.player;
|
package net.fretux.ascend.player;
|
||||||
|
|
||||||
|
import net.fretux.ascend.AscendMod;
|
||||||
import net.fretux.ascend.config.AscendConfig;
|
import net.fretux.ascend.config.AscendConfig;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class PlayerStats {
|
public class PlayerStats {
|
||||||
private static final String[] ATTRIBUTE_KEYS = {
|
public static final int CURRENT_DATA_VERSION = 2;
|
||||||
"strength", "agility", "fortitude", "intelligence",
|
public static final String[] ATTRIBUTE_KEYS = {"vigor", "mind", "strength", "agility", "intelligence", "willpower", "charisma", "arcane"};
|
||||||
"willpower", "charisma",
|
private static final String[] LEGACY_REFUND_KEYS = {"fortitude", "light_scaling", "medium_scaling", "heavy_scaling", "magic_scaling"};
|
||||||
"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 final Map<String, Integer> attributes = new HashMap<>();
|
private int dataVersion = CURRENT_DATA_VERSION;
|
||||||
private final Map<String, Integer> lastRespecBuild = new HashMap<>();
|
|
||||||
private int ascendLevel = 1;
|
private int ascendLevel = 1;
|
||||||
private int ascendXP = 0;
|
private int ascendXP;
|
||||||
private int unspentPoints = getPointsPerLevel();
|
private int unspentPoints = getPointsPerLevel();
|
||||||
private int maxAscendLevel = getConfiguredMaxAscendLevel();
|
private int maxAscendLevel = getConfiguredMaxAscendLevel();
|
||||||
private int maxAttributePoints = getConfiguredMaxAttributePoints();
|
private int maxAttributePoints = getConfiguredMaxAttributePoints();
|
||||||
private int respecCount = 0;
|
private int knowledge;
|
||||||
private boolean everInvestedWeaponScaling = false;
|
private boolean hasUsedMoonseye;
|
||||||
public PlayerStats() {
|
private int respecCount;
|
||||||
for (String attr : ATTRIBUTE_KEYS) {
|
private int lastMigrationRefund;
|
||||||
attributes.put(attr, 0);
|
|
||||||
}
|
public PlayerStats() { for (String key : ATTRIBUTE_KEYS) attributes.put(key, 0); }
|
||||||
}
|
|
||||||
|
|
||||||
public void addAscendXP(int amount) {
|
public void addAscendXP(int amount) {
|
||||||
if (amount <= 0 || ascendLevel >= getMaxAscendLevel()) return;
|
if (amount <= 0 || ascendLevel >= getMaxAscendLevel()) return;
|
||||||
ascendXP += amount;
|
ascendXP += amount;
|
||||||
while (ascendLevel < getMaxAscendLevel()) {
|
while (ascendLevel < getMaxAscendLevel()) {
|
||||||
int xpNeeded = getXPToNextAscendLevel();
|
int needed = getXPToNextAscendLevel();
|
||||||
if (ascendXP < xpNeeded) break;
|
if (ascendXP < needed) break;
|
||||||
ascendXP -= xpNeeded;
|
ascendXP -= needed;
|
||||||
ascendLevel++;
|
ascendLevel++;
|
||||||
unspentPoints += getPointsPerLevel();
|
unspentPoints += getPointsPerLevel();
|
||||||
addKnowledgeScaled(1);
|
addKnowledgeScaled(1);
|
||||||
}
|
}
|
||||||
if (ascendLevel >= getMaxAscendLevel()) {
|
if (ascendLevel >= getMaxAscendLevel()) ascendXP = Math.min(ascendXP, getXPToNextAscendLevel());
|
||||||
int xpNeeded = getXPToNextAscendLevel();
|
|
||||||
if (ascendXP > xpNeeded) {
|
|
||||||
ascendXP = xpNeeded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getAscendLevel() {
|
public int getAscendLevel() { return ascendLevel; }
|
||||||
return ascendLevel;
|
public int getAscendXP() { return ascendXP; }
|
||||||
}
|
public int getUnspentPoints() { return unspentPoints; }
|
||||||
|
public int getMaxAscendLevel() { return maxAscendLevel; }
|
||||||
public int getAscendXP() {
|
public int getMaxAttributePoints() { return maxAttributePoints; }
|
||||||
return ascendXP;
|
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() {
|
public int getXPToNextAscendLevel() {
|
||||||
if (ascendLevel >= getMaxAscendLevel()) return 0;
|
if (ascendLevel >= getMaxAscendLevel()) return 0;
|
||||||
double base = 100.0;
|
return (int) Math.ceil(100.0 * Math.pow(1.25, ascendLevel - 1) + ascendLevel * 100.0);
|
||||||
double growth = 1.25;
|
|
||||||
return (int) Math.ceil(base * Math.pow(growth, ascendLevel - 1) + (ascendLevel * 100));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getUnspentPoints() {
|
public void addUnspentPoints(int amount) { unspentPoints = Math.max(0, unspentPoints + amount); }
|
||||||
return unspentPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addUnspentPoints(int amount) {
|
|
||||||
unspentPoints = Math.max(0, unspentPoints + amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean spendPoints(String attribute) {
|
public boolean spendPoints(String attribute) {
|
||||||
if (!attributes.containsKey(attribute)) return false;
|
if (!attributes.containsKey(attribute) || unspentPoints <= 0) return false;
|
||||||
int level = attributes.get(attribute);
|
int value = attributes.get(attribute);
|
||||||
if (level >= getMaxAttributePoints()) {
|
if (value >= getMaxAttributePoints()) return false;
|
||||||
return false;
|
attributes.put(attribute, value + 1);
|
||||||
}
|
unspentPoints--;
|
||||||
if (unspentPoints <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
unspentPoints -= 1;
|
|
||||||
attributes.put(attribute, level + 1);
|
|
||||||
if (isWeaponScaling(attribute)) {
|
|
||||||
everInvestedWeaponScaling = true;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCostToUpgrade(String attribute) {
|
public int getCostToUpgrade(String attribute) { return attributes.containsKey(attribute) ? 1 : 0; }
|
||||||
return 1;
|
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) {
|
public void addKnowledge(int amount) { knowledge = Math.max(0, knowledge + amount); }
|
||||||
return attributes.getOrDefault(attribute, 0);
|
public int addKnowledgeScaled(int amount) { addKnowledge(amount); return amount; }
|
||||||
}
|
|
||||||
|
|
||||||
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 refundAllPoints() {
|
public void refundAllPoints() {
|
||||||
int totalSpent = 0;
|
int total = 0;
|
||||||
Map<String, Integer> refundedBuild = new HashMap<>();
|
lastRespecBuild.clear();
|
||||||
for (String key : ATTRIBUTE_KEYS) {
|
for (String key : ATTRIBUTE_KEYS) {
|
||||||
int lvl = attributes.getOrDefault(key, 0);
|
int value = attributes.getOrDefault(key, 0);
|
||||||
totalSpent += lvl;
|
total += value;
|
||||||
refundedBuild.put(key, lvl);
|
lastRespecBuild.put(key, value);
|
||||||
attributes.put(key, 0);
|
attributes.put(key, 0);
|
||||||
}
|
}
|
||||||
if (totalSpent > 0) {
|
if (total > 0) respecCount++;
|
||||||
lastRespecBuild.clear();
|
unspentPoints += total;
|
||||||
lastRespecBuild.putAll(refundedBuild);
|
|
||||||
respecCount++;
|
|
||||||
}
|
|
||||||
unspentPoints += totalSpent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRespecCount() {
|
|
||||||
return respecCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasEverInvestedWeaponScaling() {
|
|
||||||
return everInvestedWeaponScaling;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasNearlyRestoredLastBuild() {
|
public boolean hasNearlyRestoredLastBuild() {
|
||||||
if (lastRespecBuild.isEmpty()) return false;
|
if (lastRespecBuild.isEmpty()) return false;
|
||||||
int originalTotal = 0;
|
int originalTotal = 0, currentTotal = 0, difference = 0;
|
||||||
int currentTotal = 0;
|
|
||||||
int totalDifference = 0;
|
|
||||||
for (String key : ATTRIBUTE_KEYS) {
|
for (String key : ATTRIBUTE_KEYS) {
|
||||||
int original = lastRespecBuild.getOrDefault(key, 0);
|
int original = lastRespecBuild.getOrDefault(key, 0);
|
||||||
int current = attributes.getOrDefault(key, 0);
|
int current = attributes.getOrDefault(key, 0);
|
||||||
originalTotal += original;
|
originalTotal += original;
|
||||||
currentTotal += current;
|
currentTotal += current;
|
||||||
totalDifference += Math.abs(original - current);
|
difference += Math.abs(original - current);
|
||||||
}
|
}
|
||||||
int allowedDifference = Math.max(2, (int) Math.ceil(originalTotal * 0.05D));
|
int allowed = Math.max(2, (int) Math.ceil(originalTotal * 0.05));
|
||||||
return originalTotal > 0 && currentTotal > 0
|
return originalTotal > 0 && currentTotal >= originalTotal - allowed && difference <= allowed;
|
||||||
&& currentTotal >= originalTotal - allowedDifference
|
|
||||||
&& totalDifference <= allowedDifference;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasUsedMoonseye() {
|
|
||||||
return hasUsedMoonseye;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHasUsedMoonseye(boolean used) {
|
|
||||||
this.hasUsedMoonseye = used;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHasUsedMoonseye(boolean used) { hasUsedMoonseye = used; }
|
||||||
|
|
||||||
public CompoundTag serializeNBT() {
|
public CompoundTag serializeNBT() {
|
||||||
CompoundTag tag = new CompoundTag();
|
CompoundTag tag = new CompoundTag();
|
||||||
|
tag.putInt("DataVersion", dataVersion);
|
||||||
tag.putInt("AscendLevel", ascendLevel);
|
tag.putInt("AscendLevel", ascendLevel);
|
||||||
tag.putInt("AscendXP", ascendXP);
|
tag.putInt("AscendXP", ascendXP);
|
||||||
tag.putInt("UnspentPoints", unspentPoints);
|
tag.putInt("UnspentPoints", unspentPoints);
|
||||||
@@ -202,82 +121,63 @@ public class PlayerStats {
|
|||||||
tag.putInt("Knowledge", knowledge);
|
tag.putInt("Knowledge", knowledge);
|
||||||
tag.putBoolean("HasUsedMoonseye", hasUsedMoonseye);
|
tag.putBoolean("HasUsedMoonseye", hasUsedMoonseye);
|
||||||
tag.putInt("RespecCount", respecCount);
|
tag.putInt("RespecCount", respecCount);
|
||||||
tag.putBoolean("EverInvestedWeaponScaling", everInvestedWeaponScaling);
|
|
||||||
CompoundTag attrTag = new CompoundTag();
|
CompoundTag attrTag = new CompoundTag();
|
||||||
for (Map.Entry<String, Integer> entry : attributes.entrySet()) {
|
attributes.forEach(attrTag::putInt);
|
||||||
attrTag.putInt(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
tag.put("Attributes", attrTag);
|
tag.put("Attributes", attrTag);
|
||||||
CompoundTag respecBuildTag = new CompoundTag();
|
CompoundTag respecTag = new CompoundTag();
|
||||||
for (Map.Entry<String, Integer> entry : lastRespecBuild.entrySet()) {
|
lastRespecBuild.forEach(respecTag::putInt);
|
||||||
respecBuildTag.putInt(entry.getKey(), entry.getValue());
|
tag.put("LastRespecBuild", respecTag);
|
||||||
}
|
|
||||||
tag.put("LastRespecBuild", respecBuildTag);
|
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deserializeNBT(CompoundTag tag) {
|
public void deserializeNBT(CompoundTag tag) {
|
||||||
maxAscendLevel = tag.contains("MaxAscendLevel")
|
lastMigrationRefund = 0;
|
||||||
? Math.max(1, tag.getInt("MaxAscendLevel"))
|
maxAscendLevel = tag.contains("MaxAscendLevel") ? Math.max(1, tag.getInt("MaxAscendLevel")) : getConfiguredMaxAscendLevel();
|
||||||
: getConfiguredMaxAscendLevel();
|
maxAttributePoints = tag.contains("MaxAttributePoints") ? Math.max(1, tag.getInt("MaxAttributePoints")) : getConfiguredMaxAttributePoints();
|
||||||
maxAttributePoints = tag.contains("MaxAttributePoints")
|
ascendLevel = Math.max(1, Math.min(tag.contains("AscendLevel") ? tag.getInt("AscendLevel") : 1, maxAscendLevel));
|
||||||
? Math.max(1, tag.getInt("MaxAttributePoints"))
|
ascendXP = Math.max(0, tag.contains("AscendXP") ? tag.getInt("AscendXP") : 0);
|
||||||
: getConfiguredMaxAttributePoints();
|
unspentPoints = tag.contains("UnspentPoints") ? Math.max(0, tag.getInt("UnspentPoints")) : getPointsPerLevel();
|
||||||
ascendLevel = tag.contains("AscendLevel") ? tag.getInt("AscendLevel") : 1;
|
knowledge = Math.max(0, tag.getInt("Knowledge"));
|
||||||
ascendLevel = Math.max(1, Math.min(ascendLevel, getMaxAscendLevel()));
|
hasUsedMoonseye = tag.getBoolean("HasUsedMoonseye");
|
||||||
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())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
respecCount = Math.max(0, tag.getInt("RespecCount"));
|
respecCount = Math.max(0, tag.getInt("RespecCount"));
|
||||||
everInvestedWeaponScaling = tag.contains("EverInvestedWeaponScaling")
|
CompoundTag attrTag = tag.contains("Attributes") ? tag.getCompound("Attributes") : new CompoundTag();
|
||||||
? tag.getBoolean("EverInvestedWeaponScaling")
|
for (String key : ATTRIBUTE_KEYS) attributes.put(key, Math.max(0, Math.min(attrTag.getInt(key), maxAttributePoints)));
|
||||||
: getAttributeLevel("light_scaling") > 0
|
dataVersion = tag.contains("DataVersion") ? Math.max(1, tag.getInt("DataVersion")) : 1;
|
||||||
|| getAttributeLevel("medium_scaling") > 0
|
if (dataVersion < CURRENT_DATA_VERSION) {
|
||||||
|| getAttributeLevel("heavy_scaling") > 0;
|
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();
|
lastRespecBuild.clear();
|
||||||
if (tag.contains("LastRespecBuild")) {
|
if (tag.contains("LastRespecBuild")) {
|
||||||
CompoundTag respecBuildTag = tag.getCompound("LastRespecBuild");
|
CompoundTag previous = tag.getCompound("LastRespecBuild");
|
||||||
for (String key : ATTRIBUTE_KEYS) {
|
for (String key : ATTRIBUTE_KEYS) lastRespecBuild.put(key, Math.max(0, previous.getInt(key)));
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
int maxXp = getXPToNextAscendLevel();
|
int maxXp = getXPToNextAscendLevel();
|
||||||
if (ascendXP > maxXp) {
|
if (maxXp > 0) ascendXP = Math.min(ascendXP, maxXp);
|
||||||
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() {
|
private static int getPointsPerLevel() { return configValue(AscendConfig.COMMON.pointsPerLevel); }
|
||||||
return AscendConfig.COMMON.maxAscendLevel.get();
|
private static int getConfiguredMaxAscendLevel() { return configValue(AscendConfig.COMMON.maxAscendLevel); }
|
||||||
}
|
private static int getConfiguredMaxAttributePoints() { return configValue(AscendConfig.COMMON.maxAttributePoints); }
|
||||||
|
|
||||||
private static int getConfiguredMaxAttributePoints() {
|
private static int configValue(net.minecraftforge.common.ForgeConfigSpec.IntValue value) {
|
||||||
return AscendConfig.COMMON.maxAttributePoints.get();
|
return AscendConfig.COMMON_SPEC.isLoaded() ? value.get() : value.getDefault();
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isWeaponScaling(String attribute) {
|
|
||||||
return "light_scaling".equals(attribute)
|
|
||||||
|| "medium_scaling".equals(attribute)
|
|
||||||
|| "heavy_scaling".equals(attribute);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public class PlayerStatsProvider implements ICapabilitySerializable<CompoundTag>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||||
|
stats.refreshConfiguredMaxAscendLevel();
|
||||||
AscendAdvancements.checkMilestones(serverPlayer, stats);
|
AscendAdvancements.checkMilestones(serverPlayer, stats);
|
||||||
CompoundTag data = stats.serializeNBT();
|
CompoundTag data = stats.serializeNBT();
|
||||||
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> serverPlayer),
|
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.compat.IronsSpellbooksCompat;
|
||||||
import net.fretux.ascend.config.AscendConfig;
|
import net.fretux.ascend.config.AscendConfig;
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public final class StatEffects {
|
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 =
|
private StatEffects() {}
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void applyAll(Player player) {
|
public static void applyAll(Player player) {
|
||||||
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||||
|
applyVigor(player, stats.getAttributeLevel("vigor"));
|
||||||
applyStrength(player, stats.getAttributeLevel("strength"));
|
applyStrength(player, stats.getAttributeLevel("strength"));
|
||||||
applyAgility(player, stats.getAttributeLevel("agility"));
|
applyAgility(player, stats.getAttributeLevel("agility"));
|
||||||
applyFortitude(player, stats.getAttributeLevel("fortitude"));
|
|
||||||
if (IronsSpellbooksCompat.isLoaded()) {
|
|
||||||
IronsSpellbooksCompat.applyAll(player);
|
IronsSpellbooksCompat.applyAll(player);
|
||||||
}
|
if (player.getHealth() > player.getMaxHealth()) player.setHealth(player.getMaxHealth());
|
||||||
if (player.getHealth() > player.getMaxHealth()) {
|
|
||||||
player.setHealth(player.getMaxHealth());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applyStrength(Player player, int strengthLevel) {
|
public static void applyVigor(Player player, int level) {
|
||||||
// Always remove first so resets correctly.
|
AttributeInstance health = player.getAttribute(Attributes.MAX_HEALTH);
|
||||||
AttributeInstance atk = player.getAttribute(Attributes.ATTACK_DAMAGE);
|
if (health == null) return;
|
||||||
if (atk != null) {
|
health.removeModifier(VIGOR_HEALTH_UUID);
|
||||||
atk.removeModifier(STRENGTH_DAMAGE_UUID);
|
double bonus = getVigorHealthBonus(level);
|
||||||
if (strengthLevel > 0) {
|
if (bonus > 0) health.addPermanentModifier(new AttributeModifier(VIGOR_HEALTH_UUID,
|
||||||
double dmgBonus = Math.min(strengthLevel * 0.06d * getScaling(), 6.0d);
|
"Ascend Vigor health bonus", bonus, AttributeModifier.Operation.ADDITION));
|
||||||
atk.addTransientModifier(new AttributeModifier(
|
|
||||||
STRENGTH_DAMAGE_UUID,
|
|
||||||
"Ascend Strength damage bonus",
|
|
||||||
dmgBonus,
|
|
||||||
AttributeModifier.Operation.ADDITION
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AttributeInstance kb = player.getAttribute(Attributes.ATTACK_KNOCKBACK);
|
public static void applyStrength(Player player, int level) {
|
||||||
if (kb != null) {
|
AttributeInstance attack = player.getAttribute(Attributes.ATTACK_DAMAGE);
|
||||||
kb.removeModifier(STRENGTH_KNOCKBACK_UUID);
|
if (attack != null) attack.removeModifier(UUID.fromString("a7b8b2c3-9b0d-4e1a-8ee3-21f6a7b2c3d4"));
|
||||||
if (strengthLevel > 0) {
|
AttributeInstance resistance = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
|
||||||
double kbBonus = scaledRatio(strengthLevel, 0.002d, 0.2d);
|
if (resistance != null) resistance.removeModifier(REMOVED_HEALTH_STAT_KB_UUID);
|
||||||
kb.addTransientModifier(new AttributeModifier(
|
AttributeInstance knockback = player.getAttribute(Attributes.ATTACK_KNOCKBACK);
|
||||||
STRENGTH_KNOCKBACK_UUID,
|
if (knockback == null) return;
|
||||||
"Ascend Strength knockback bonus",
|
knockback.removeModifier(STRENGTH_KNOCKBACK_UUID);
|
||||||
kbBonus,
|
double bonus = getStrengthKnockbackBonus(level);
|
||||||
AttributeModifier.Operation.ADDITION
|
if (bonus > 0) knockback.addTransientModifier(new AttributeModifier(STRENGTH_KNOCKBACK_UUID,
|
||||||
));
|
"Ascend Strength knockback bonus", bonus, AttributeModifier.Operation.ADDITION));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applyAgility(Player player, int agilityLevel) {
|
public static void applyAgility(Player player, int level) {
|
||||||
AttributeInstance move = player.getAttribute(Attributes.MOVEMENT_SPEED);
|
AttributeInstance movement = player.getAttribute(Attributes.MOVEMENT_SPEED);
|
||||||
if (move != null) {
|
if (movement == null) return;
|
||||||
move.removeModifier(AGILITY_SPEED_UUID);
|
movement.removeModifier(AGILITY_SPEED_UUID);
|
||||||
if (agilityLevel > 0) {
|
double bonus = getAgilityMovementSpeedBonus(level);
|
||||||
double bonus = scaledRatio(agilityLevel, 0.0008d, 0.08d);
|
if (bonus > 0) movement.addTransientModifier(new AttributeModifier(AGILITY_SPEED_UUID,
|
||||||
move.addTransientModifier(new AttributeModifier(
|
"Ascend Agility movement bonus", bonus, AttributeModifier.Operation.MULTIPLY_BASE));
|
||||||
AGILITY_SPEED_UUID,
|
|
||||||
"Ascend Agility speed bonus",
|
|
||||||
bonus,
|
|
||||||
AttributeModifier.Operation.ADDITION
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applyFortitude(Player player, int fortitudeLevel) {
|
public static double getVigorHealthBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.vigorMaxHealthBonus); }
|
||||||
AttributeInstance hp = player.getAttribute(Attributes.MAX_HEALTH);
|
public static double getStrengthKnockbackBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.strengthKnockbackBonus); }
|
||||||
if (hp != null) {
|
public static double getStrengthArmorPenetration(int level) { return normalized(level) * configured(AscendConfig.COMMON.strengthArmorPenetration); }
|
||||||
hp.removeModifier(FORTITUDE_HEALTH_UUID);
|
public static double getAgilityMovementSpeedBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.agilityMovementSpeedBonus); }
|
||||||
if (fortitudeLevel > 0) {
|
public static int getMindManaBonus(int level) { return (int) Math.floor(normalized(level) * configured(AscendConfig.COMMON.mindMaxManaBonus)); }
|
||||||
double healthBonus = Math.min(fortitudeLevel * 0.18d * getScaling(), 18.0d);
|
public static double getMindManaRegenBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.mindManaRegenBonus); }
|
||||||
// Max health must be present while Minecraft loads the player's saved Health
|
public static double getIntelligenceSpellPowerBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.intelligenceSpellPowerBonus); }
|
||||||
// value, otherwise values above the vanilla 20 HP are clamped before login.
|
public static double getIntelligenceCooldownBonus(int level) { return normalized(level) * configured(AscendConfig.COMMON.intelligenceCooldownBonus); }
|
||||||
hp.addPermanentModifier(new AttributeModifier(
|
public static double getWillpowerCastTimeReduction(int level) { return normalized(level) * configured(AscendConfig.COMMON.willpowerCastTimeReduction); }
|
||||||
FORTITUDE_HEALTH_UUID,
|
public static double getArcaneBeneficialDurationMultiplier(int level) { return 1.0 + normalized(level) * configured(AscendConfig.COMMON.arcaneBeneficialDurationBonus); }
|
||||||
"Ascend Fortitude health bonus",
|
public static double getArcaneHarmfulDurationMultiplier(int level) { return 1.0 + normalized(level) * configured(AscendConfig.COMMON.arcaneHarmfulDurationBonus); }
|
||||||
healthBonus,
|
|
||||||
AttributeModifier.Operation.ADDITION
|
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; }
|
||||||
AttributeInstance kbRes = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
|
|
||||||
if (kbRes != null) {
|
public static void applyCharismaAllyBuff(LivingEntity entity, int level) {
|
||||||
kbRes.removeModifier(FORTITUDE_KB_RESIST_UUID);
|
if (level <= 0) { clearCharismaAllyBuff(entity); return; }
|
||||||
if (fortitudeLevel > 0) {
|
updateModifier(entity.getAttribute(Attributes.MAX_HEALTH), CHARISMA_ALLY_HEALTH_UUID,
|
||||||
double res = scaledRatio(fortitudeLevel, 0.003d, 0.3d);
|
"Ascend Charisma ally max health", getCharismaAllyHealthBonus(level));
|
||||||
kbRes.addTransientModifier(new AttributeModifier(
|
updateModifier(entity.getAttribute(Attributes.ATTACK_DAMAGE), CHARISMA_ALLY_DAMAGE_UUID,
|
||||||
FORTITUDE_KB_RESIST_UUID,
|
"Ascend Charisma ally damage", getCharismaAllyDamageBonus(level));
|
||||||
"Ascend Fortitude knockback resist",
|
|
||||||
res,
|
|
||||||
AttributeModifier.Operation.ADDITION
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getWillpowerSanityMultiplier(int willpowerLevel) {
|
private static void updateModifier(AttributeInstance instance, UUID id, String name, double value) {
|
||||||
if (willpowerLevel <= 0) return 1.0f;
|
if (instance == null) return;
|
||||||
double reduction = scaledRatio(willpowerLevel, 0.0035d, 0.35d);
|
instance.removeModifier(id);
|
||||||
return (float) (1.0d - reduction);
|
if (value > 0) instance.addTransientModifier(new AttributeModifier(id, name, value, AttributeModifier.Operation.MULTIPLY_BASE));
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearCharismaAllyBuff(LivingEntity entity) {
|
public static void clearCharismaAllyBuff(LivingEntity entity) {
|
||||||
AttributeInstance health = entity.getAttribute(Attributes.MAX_HEALTH);
|
AttributeInstance health = entity.getAttribute(Attributes.MAX_HEALTH);
|
||||||
if (health != null) {
|
if (health != null) health.removeModifier(CHARISMA_ALLY_HEALTH_UUID);
|
||||||
health.removeModifier(CHARISMA_ALLY_HEALTH_UUID);
|
|
||||||
}
|
|
||||||
AttributeInstance damage = entity.getAttribute(Attributes.ATTACK_DAMAGE);
|
AttributeInstance damage = entity.getAttribute(Attributes.ATTACK_DAMAGE);
|
||||||
if (damage != null) {
|
if (damage != null) damage.removeModifier(CHARISMA_ALLY_DAMAGE_UUID);
|
||||||
damage.removeModifier(CHARISMA_ALLY_DAMAGE_UUID);
|
if (entity.getHealth() > entity.getMaxHealth()) entity.setHealth(entity.getMaxHealth());
|
||||||
}
|
|
||||||
if (entity.getHealth() > entity.getMaxHealth()) {
|
|
||||||
entity.setHealth(entity.getMaxHealth());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getWeaponScalingMultiplier(int scalingLevel) {
|
private static double normalized(int level) { return AttributeScaling.getNormalizedAttributeValue(level); }
|
||||||
return scalingMultiplier(scalingLevel, 0.0035d, 0.35d);
|
private static double configured(net.minecraftforge.common.ForgeConfigSpec.DoubleValue value) {
|
||||||
}
|
return AscendConfig.COMMON_SPEC.isLoaded() ? value.get() : value.getDefault();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,32 +7,38 @@
|
|||||||
"ascend.stats.xp_max": "Ascend XP: MAX",
|
"ascend.stats.xp_max": "Ascend XP: MAX",
|
||||||
"ascend.stats.unspent": "Unspent Points: %s",
|
"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": "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": "Agility",
|
||||||
"ascend.attribute.agility.desc": "Increases movement speed and mobility.",
|
"ascend.attribute.agility.desc": "Improves agile and ranged weapon scaling and movement speed.",
|
||||||
"ascend.attribute.fortitude": "Fortitude",
|
"ascend.attribute.agility.short": "Improves agile and ranged weapons",
|
||||||
"ascend.attribute.fortitude.desc": "Increases max health and knockback resistance.",
|
|
||||||
"ascend.attribute.intelligence": "Intelligence",
|
"ascend.attribute.intelligence": "Intelligence",
|
||||||
"ascend.attribute.intelligence.desc": "Increases max mana and mana regeneration. 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": "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": "Charisma",
|
||||||
"ascend.attribute.charisma.desc": "Increases max mana, improves trade deals, boosts villager reputation, and empowers tamed allies/golems.",
|
"ascend.attribute.charisma.desc": "Improves villager trading and strengthens nearby allies.",
|
||||||
"ascend.attribute.light_scaling": "Light Scaling",
|
"ascend.attribute.charisma.short": "Improves trading and allies",
|
||||||
"ascend.attribute.light_scaling.desc": "Improves light weapon performance.",
|
"ascend.attribute.arcane": "Arcane",
|
||||||
"ascend.attribute.medium_scaling": "Medium Scaling",
|
"ascend.attribute.arcane.desc": "Increases the duration of beneficial and harmful status effects.",
|
||||||
"ascend.attribute.medium_scaling.desc": "Improves medium weapon performance.",
|
"ascend.attribute.arcane.short": "Improves status effects",
|
||||||
"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.",
|
|
||||||
"item.ascend.moonseye_tome": "Moonseye Tome",
|
"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.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!",
|
"item.ascend.moonseye_tome.used": "You gained %s Knowledge from the Moonseye Tome!",
|
||||||
"itemGroup.ascend": "Ascend",
|
"itemGroup.ascend": "Ascend",
|
||||||
"ascend.attribute.display": "%s: %s",
|
"ascend.attribute.display": "%s: %s",
|
||||||
"ascend.stats.knowledge": "Knowledge: %s",
|
"ascend.stats.knowledge": "Knowledge: %s",
|
||||||
|
"ascend.weapon_scaling.title": "Attribute Scaling",
|
||||||
|
"ascend.weapon_scaling.grade": "%s: %s",
|
||||||
"item.ascend.remembrance_essence": "Remembrance Essence",
|
"item.ascend.remembrance_essence": "Remembrance Essence",
|
||||||
"block.ascend.shrine_of_remembrance": "Shrine of Remembrance",
|
"block.ascend.shrine_of_remembrance": "Shrine of Remembrance",
|
||||||
|
|
||||||
@@ -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.title": "You Cannot Hit What You Cannot See",
|
||||||
"advancements.ascend.cannot_hit_what_you_cannot_see.description": "Reach 100 Agility",
|
"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.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.title": "Big Brain Time",
|
||||||
"advancements.ascend.big_brain_small_helmet.description": "Reach 100 Intelligence",
|
"advancements.ascend.big_brain_small_helmet.description": "Reach 100 Intelligence",
|
||||||
"advancements.ascend.mana_without_purpose.title": "Mana Without Purpose",
|
"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.title": "Unlicensed Therapist",
|
||||||
"advancements.ascend.unlicensed_therapist.description": "Reach 100 Willpower",
|
"advancements.ascend.unlicensed_therapist.description": "Reach 100 Willpower",
|
||||||
"advancements.ascend.talk_no_jutsu.title": "Talk No Jutsu",
|
"advancements.ascend.talk_no_jutsu.title": "Talk No Jutsu",
|
||||||
"advancements.ascend.talk_no_jutsu.description": "Reach 100 Charisma",
|
"advancements.ascend.talk_no_jutsu.description": "Reach 100 Charisma",
|
||||||
"advancements.ascend.perfectly_balanced.title": "Perfectly Balanced",
|
"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.perfectly_balanced.description": "Invest exactly the same number of points in all eight 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.indecisive_chosen_one.title": "Indecisive Chosen One",
|
"advancements.ascend.indecisive_chosen_one.title": "Indecisive Chosen One",
|
||||||
"advancements.ascend.indecisive_chosen_one.description": "Respec ten times on the same character",
|
"advancements.ascend.indecisive_chosen_one.description": "Respec ten times on the same character",
|
||||||
"advancements.ascend.config_enjoyer.title": "Config Enjoyer",
|
"advancements.ascend.config_enjoyer.title": "Config Enjoyer",
|
||||||
|
|||||||
Reference in New Issue
Block a user