Implement support for iron's spellbooks
This commit is contained in:
+44
-38
@@ -58,22 +58,16 @@ minecraft {
|
|||||||
// Default run configurations.
|
// Default run configurations.
|
||||||
// These can be tweaked, removed, or duplicated as needed.
|
// These can be tweaked, removed, or duplicated as needed.
|
||||||
runs {
|
runs {
|
||||||
// applies to all the run configs below
|
|
||||||
configureEach {
|
configureEach {
|
||||||
workingDirectory project.file('run')
|
workingDirectory project.file('run')
|
||||||
|
|
||||||
// Recommended logging data for a userdev environment
|
|
||||||
// The markers can be added/remove as needed separated by commas.
|
|
||||||
// "SCAN": For mods scan.
|
|
||||||
// "REGISTRIES": For firing of registry events.
|
|
||||||
// "REGISTRYDUMP": For getting the contents of all registries.
|
|
||||||
property 'forge.logging.markers', 'REGISTRIES'
|
property 'forge.logging.markers', 'REGISTRIES'
|
||||||
|
|
||||||
// Recommended logging level for the console
|
|
||||||
// You can set various levels here.
|
|
||||||
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
|
||||||
property 'forge.logging.console.level', 'debug'
|
property 'forge.logging.console.level', 'debug'
|
||||||
|
|
||||||
|
// IMPORTANT: enable refmap remapping for mixins (Geckolib, Player Animator, etc.)
|
||||||
|
property 'mixin.env.remapRefMap', 'true'
|
||||||
|
property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg"
|
||||||
|
|
||||||
mods {
|
mods {
|
||||||
"${mod_id}" {
|
"${mod_id}" {
|
||||||
source sourceSets.main
|
source sourceSets.main
|
||||||
@@ -82,7 +76,6 @@ minecraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client {
|
client {
|
||||||
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
|
|
||||||
property 'forge.enabledGameTestNamespaces', mod_id
|
property 'forge.enabledGameTestNamespaces', mod_id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,18 +84,12 @@ minecraft {
|
|||||||
args '--nogui'
|
args '--nogui'
|
||||||
}
|
}
|
||||||
|
|
||||||
// This run config launches GameTestServer and runs all registered gametests, then exits.
|
|
||||||
// By default, the server will crash when no gametests are provided.
|
|
||||||
// The gametest system is also enabled by default for other run configs under the /test command.
|
|
||||||
gameTestServer {
|
gameTestServer {
|
||||||
property 'forge.enabledGameTestNamespaces', mod_id
|
property 'forge.enabledGameTestNamespaces', mod_id
|
||||||
}
|
}
|
||||||
|
|
||||||
data {
|
data {
|
||||||
// example of overriding the workingDirectory set in configureEach above
|
|
||||||
workingDirectory project.file('run-data')
|
workingDirectory project.file('run-data')
|
||||||
|
|
||||||
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
|
||||||
args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,30 +107,49 @@ repositories {
|
|||||||
// flatDir {
|
// flatDir {
|
||||||
// dir 'libs'
|
// dir 'libs'
|
||||||
// }
|
// }
|
||||||
|
maven {
|
||||||
|
name = "Iron's Maven - Release"
|
||||||
|
url = "https://code.redspace.io/releases"
|
||||||
|
content {
|
||||||
|
includeGroup "io.redspace.ironsspellbooks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maven {
|
||||||
|
name = "Iron's Maven - Snapshots"
|
||||||
|
url = "https://code.redspace.io/snapshots"
|
||||||
|
content {
|
||||||
|
includeGroup "io.redspace.ironsspellbooks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maven { url = "https://maven.enginehub.org/repo/" }
|
||||||
|
maven { url = "https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/" }
|
||||||
|
maven { url = "https://maven.theillusivec4.top" }
|
||||||
|
maven { url = "https://cursemaven.com" }
|
||||||
|
maven { url = "https://maven.blamejared.com" }
|
||||||
|
maven { url = "https://maven.kosmx.dev/" }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Specify the version of Minecraft to use.
|
|
||||||
// Any artifact can be supplied so long as it has a "userdev" classifier artifact and is a compatible patcher artifact.
|
|
||||||
// The "userdev" classifier will be requested and setup by ForgeGradle.
|
|
||||||
// If the group id is "net.minecraft" and the artifact id is one of ["client", "server", "joined"],
|
|
||||||
// then special handling is done to allow a setup of a vanilla dependency without the use of an external repository.
|
|
||||||
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
||||||
|
|
||||||
// Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings
|
// Iron's Spellbooks API only
|
||||||
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
|
compileOnly fg.deobf("io.redspace.ironsspellbooks:irons_spellbooks:${irons_spells_version}:api")
|
||||||
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}")
|
implementation fg.deobf("io.redspace.ironsspellbooks:irons_spellbooks:${irons_spells_version}")
|
||||||
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}")
|
|
||||||
// runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}")
|
|
||||||
|
|
||||||
// Example mod dependency using a mod jar from ./libs with a flat dir repository
|
// GECKOLIB — deobfuscated, full mod
|
||||||
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
|
implementation fg.deobf("software.bernie.geckolib:geckolib-forge-${geckolib_version}")
|
||||||
// The group id is ignored when searching -- in this case, it is "blank"
|
|
||||||
// implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}")
|
|
||||||
|
|
||||||
// For more info:
|
// PLAYER ANIMATOR — deobf
|
||||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
implementation fg.deobf("dev.kosmx.player-anim:player-animation-lib-forge:${player_animator_version}")
|
||||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
|
||||||
|
// CAELUS — API only
|
||||||
|
compileOnly fg.deobf("top.theillusivec4.caelus:caelus-forge:${caelus_version}:api")
|
||||||
|
implementation fg.deobf("top.theillusivec4.caelus:caelus-forge:${caelus_version}")
|
||||||
|
|
||||||
|
// CURIOS — API only
|
||||||
|
compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_version}:api")
|
||||||
|
implementation fg.deobf("top.theillusivec4.curios:curios-forge:${curios_version}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -152,11 +158,11 @@ dependencies {
|
|||||||
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
|
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
|
||||||
tasks.named('processResources', ProcessResources).configure {
|
tasks.named('processResources', ProcessResources).configure {
|
||||||
var replaceProperties = [
|
var replaceProperties = [
|
||||||
minecraft_version: minecraft_version, minecraft_version_range: minecraft_version_range,
|
minecraft_version : minecraft_version, minecraft_version_range: minecraft_version_range,
|
||||||
forge_version: forge_version, forge_version_range: forge_version_range,
|
forge_version : forge_version, forge_version_range: forge_version_range,
|
||||||
loader_version_range: loader_version_range,
|
loader_version_range: loader_version_range,
|
||||||
mod_id: mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version,
|
mod_id : mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version,
|
||||||
mod_authors: mod_authors, mod_description: mod_description,
|
mod_authors : mod_authors, mod_description: mod_description,
|
||||||
]
|
]
|
||||||
inputs.properties replaceProperties
|
inputs.properties replaceProperties
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ loader_version_range=[47,)
|
|||||||
# The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"].
|
# The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"].
|
||||||
# Additional mapping channels can be registered through the "channelProviders" extension in a Gradle plugin.
|
# Additional mapping channels can be registered through the "channelProviders" extension in a Gradle plugin.
|
||||||
#
|
#
|
||||||
|
irons_spells_version=1.20.1-3.4.0.11
|
||||||
|
caelus_version=3.1.0+1.20
|
||||||
|
curios_version=5.4.7+1.20.1
|
||||||
|
geckolib_version=1.20.1:4.7.1.1
|
||||||
|
player_animator_version=1.0.2-rc1+1.20
|
||||||
# | Channel | Version | |
|
# | Channel | Version | |
|
||||||
# |-----------|----------------------|--------------------------------------------------------------------------------|
|
# |-----------|----------------------|--------------------------------------------------------------------------------|
|
||||||
# | official | MCVersion | Official field/method names from Mojang mapping files |
|
# | official | MCVersion | Official field/method names from Mojang mapping files |
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
package net.fretux.ascend.compat;
|
||||||
|
|
||||||
|
import io.redspace.ironsspellbooks.api.registry.AttributeRegistry;
|
||||||
|
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class IronsSpellbooksCompat {
|
||||||
|
private static final UUID INT_MAX_MANA_UUID =
|
||||||
|
UUID.fromString("11111111-1111-4111-8111-111111111111");
|
||||||
|
private static final UUID INT_SPELL_POWER_UUID =
|
||||||
|
UUID.fromString("22222222-2222-4222-8222-222222222222");
|
||||||
|
private static final UUID INT_MANA_REGEN_UUID =
|
||||||
|
UUID.fromString("33333333-3333-4333-8333-333333333333");
|
||||||
|
private static final UUID INT_COOLDOWN_REDUCTION_UUID =
|
||||||
|
UUID.fromString("44444444-4444-4444-8444-444444444444");
|
||||||
|
private static final UUID CHA_MAX_MANA_UUID =
|
||||||
|
UUID.fromString("55555555-5555-4555-8555-555555555555");
|
||||||
|
// reserved for future spell-trade hooks
|
||||||
|
private static final UUID CHA_SPELL_TRADE_UUID =
|
||||||
|
UUID.fromString("66666666-6666-4666-8666-666666666666");
|
||||||
|
private static final UUID MAG_SPELL_POWER_UUID =
|
||||||
|
UUID.fromString("77777777-7777-4777-8777-777777777777");
|
||||||
|
private static final UUID MAG_MANA_REGEN_UUID =
|
||||||
|
UUID.fromString("88888888-8888-4888-8888-888888888888");
|
||||||
|
private static final UUID MAG_COOLDOWN_REDUCTION_UUID =
|
||||||
|
UUID.fromString("99999999-9999-4999-8999-999999999999");
|
||||||
|
|
||||||
|
private IronsSpellbooksCompat() {}
|
||||||
|
|
||||||
|
public static boolean isLoaded() {
|
||||||
|
return ModList.get().isLoaded("irons_spellbooks");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void applyAll(Player player) {
|
||||||
|
if (!isLoaded()) return;
|
||||||
|
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||||
|
applyIntelligence(player, stats.getAttributeLevel("intelligence"));
|
||||||
|
applyCharisma(player, stats.getAttributeLevel("charisma"));
|
||||||
|
applyMagicScaling(player, stats.getAttributeLevel("magic_scaling"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void applyIntelligence(Player player, int intelligence) {
|
||||||
|
if (intelligence <= 0) {
|
||||||
|
clearIntelligence(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applyAddModifier(
|
||||||
|
player,
|
||||||
|
(Attribute) AttributeRegistry.MAX_MANA.get(),
|
||||||
|
INT_MAX_MANA_UUID,
|
||||||
|
"Ascend INT max mana",
|
||||||
|
intelligence * 2.0d
|
||||||
|
);
|
||||||
|
double spellPowerPerPoint = 0.004d;
|
||||||
|
double spellPowerMax = 0.40d;
|
||||||
|
double spellPowerBonus = Math.min(intelligence * spellPowerPerPoint, spellPowerMax);
|
||||||
|
applyMultiplyBaseModifier(
|
||||||
|
player,
|
||||||
|
(Attribute) AttributeRegistry.SPELL_POWER.get(),
|
||||||
|
INT_SPELL_POWER_UUID,
|
||||||
|
"Ascend INT spell power",
|
||||||
|
spellPowerBonus
|
||||||
|
);
|
||||||
|
double regenPerPoint = 0.002d;
|
||||||
|
double regenMax = 0.25d;
|
||||||
|
double regenBonus = Math.min(intelligence * regenPerPoint, regenMax);
|
||||||
|
applyMultiplyBaseModifier(
|
||||||
|
player,
|
||||||
|
(Attribute) AttributeRegistry.MANA_REGEN.get(),
|
||||||
|
INT_MANA_REGEN_UUID,
|
||||||
|
"Ascend INT mana regen",
|
||||||
|
regenBonus
|
||||||
|
);
|
||||||
|
double cdrPerPoint = 0.0015d;
|
||||||
|
double cdrMax = 0.20d;
|
||||||
|
double cdrBonus = Math.min(intelligence * cdrPerPoint, cdrMax);
|
||||||
|
applyMultiplyBaseModifier(
|
||||||
|
player,
|
||||||
|
(Attribute) AttributeRegistry.COOLDOWN_REDUCTION.get(),
|
||||||
|
INT_COOLDOWN_REDUCTION_UUID,
|
||||||
|
"Ascend INT cooldown reduction",
|
||||||
|
cdrBonus
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void clearIntelligence(Player player) {
|
||||||
|
removeModifier(player, (Attribute) AttributeRegistry.MAX_MANA.get(), INT_MAX_MANA_UUID);
|
||||||
|
removeModifier(player, (Attribute) AttributeRegistry.SPELL_POWER.get(), INT_SPELL_POWER_UUID);
|
||||||
|
removeModifier(player, (Attribute) AttributeRegistry.MANA_REGEN.get(), INT_MANA_REGEN_UUID);
|
||||||
|
removeModifier(player, (Attribute) AttributeRegistry.COOLDOWN_REDUCTION.get(), INT_COOLDOWN_REDUCTION_UUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void applyCharisma(Player player, int charisma) {
|
||||||
|
if (charisma <= 0) {
|
||||||
|
clearCharisma(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double manaPerPoint = 0.5d;
|
||||||
|
double bonusMana = charisma * manaPerPoint;
|
||||||
|
applyAddModifier(
|
||||||
|
player,
|
||||||
|
(Attribute) AttributeRegistry.MAX_MANA.get(),
|
||||||
|
CHA_MAX_MANA_UUID,
|
||||||
|
"Ascend CHA max mana",
|
||||||
|
bonusMana
|
||||||
|
);
|
||||||
|
// Trade / reputation hooks can use CHA_SPELL_TRADE_UUID later.
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void clearCharisma(Player player) {
|
||||||
|
removeModifier(player, (Attribute) AttributeRegistry.MAX_MANA.get(), CHA_MAX_MANA_UUID);
|
||||||
|
// Any future CHA ISS modifiers get cleared here.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void applyMagicScaling(Player player, int magicScaling) {
|
||||||
|
if (magicScaling <= 0) {
|
||||||
|
clearMagicScaling(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double spPerPoint = 0.006d;
|
||||||
|
double spMax = 0.60d;
|
||||||
|
double spBonus = Math.min(magicScaling * spPerPoint, spMax);
|
||||||
|
applyMultiplyBaseModifier(
|
||||||
|
player,
|
||||||
|
(Attribute) AttributeRegistry.SPELL_POWER.get(),
|
||||||
|
MAG_SPELL_POWER_UUID,
|
||||||
|
"Ascend Magic Scaling spell power",
|
||||||
|
spBonus
|
||||||
|
);
|
||||||
|
double regenPerPoint = 0.001d;
|
||||||
|
double regenMax = 0.15d;
|
||||||
|
double regenBonus = Math.min(magicScaling * regenPerPoint, regenMax);
|
||||||
|
applyMultiplyBaseModifier(
|
||||||
|
player,
|
||||||
|
(Attribute) AttributeRegistry.MANA_REGEN.get(),
|
||||||
|
MAG_MANA_REGEN_UUID,
|
||||||
|
"Ascend Magic Scaling mana regen",
|
||||||
|
regenBonus
|
||||||
|
);
|
||||||
|
|
||||||
|
double cdrPerPoint = 0.001d;
|
||||||
|
double cdrMax = 0.10d;
|
||||||
|
double cdrBonus = Math.min(magicScaling * cdrPerPoint, cdrMax);
|
||||||
|
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.MANA_REGEN.get(), MAG_MANA_REGEN_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;
|
||||||
|
AttributeInstance inst = player.getAttribute(attribute);
|
||||||
|
if (inst == null) return;
|
||||||
|
inst.removeModifier(id);
|
||||||
|
if (value != 0.0d) {
|
||||||
|
inst.addTransientModifier(new AttributeModifier(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
AttributeModifier.Operation.ADDITION
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyMultiplyBaseModifier(Player player, Attribute attribute, UUID id, String name, double value) {
|
||||||
|
if (attribute == null) return;
|
||||||
|
AttributeInstance inst = player.getAttribute(attribute);
|
||||||
|
if (inst == null) return;
|
||||||
|
inst.removeModifier(id);
|
||||||
|
if (value != 0.0d) {
|
||||||
|
inst.addTransientModifier(new AttributeModifier(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
AttributeModifier.Operation.MULTIPLY_BASE
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeModifier(Player player, Attribute attribute, UUID id) {
|
||||||
|
if (attribute == null) return;
|
||||||
|
AttributeInstance inst = player.getAttribute(attribute);
|
||||||
|
if (inst != null) {
|
||||||
|
inst.removeModifier(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
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.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.MerchantMenu;
|
||||||
|
import net.minecraft.world.item.trading.MerchantOffer;
|
||||||
|
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
|
||||||
|
@Mod.EventBusSubscriber(modid = AscendMod.MODID)
|
||||||
|
public class CharismaTradeEvents {
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onOpenMerchant(PlayerContainerEvent.Open event) {
|
||||||
|
Player player = event.getEntity();
|
||||||
|
if (player.level().isClientSide) return;
|
||||||
|
|
||||||
|
AbstractContainerMenu menu = event.getContainer();
|
||||||
|
if (!(menu instanceof MerchantMenu merchantMenu)) return;
|
||||||
|
|
||||||
|
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
|
||||||
|
int cha = stats.getAttributeLevel("charisma");
|
||||||
|
if (cha <= 0) return;
|
||||||
|
|
||||||
|
double discountFraction = StatEffects.getCharismaTradeDiscount(cha);
|
||||||
|
if (discountFraction <= 0.0d) return;
|
||||||
|
|
||||||
|
var offers = merchantMenu.getOffers();
|
||||||
|
if (offers == null) return;
|
||||||
|
|
||||||
|
for (MerchantOffer offer : offers) {
|
||||||
|
if (offer == null) continue;
|
||||||
|
|
||||||
|
// Base cost of first input (emeralds, etc.)
|
||||||
|
int base = offer.getBaseCostA().getCount();
|
||||||
|
if (base <= 0) continue;
|
||||||
|
|
||||||
|
// Desired discounted cost
|
||||||
|
int target = (int) Math.max(1, Math.floor(base * (1.0d - discountFraction)));
|
||||||
|
|
||||||
|
// Current special price diff already includes:
|
||||||
|
// popularity, hero-of-village, etc.
|
||||||
|
int currentSpecial = offer.getSpecialPriceDiff();
|
||||||
|
|
||||||
|
// We want: base + newSpecial = target
|
||||||
|
int newSpecial = target - base;
|
||||||
|
|
||||||
|
// Adjust relative to current to avoid stacking each open.
|
||||||
|
int delta = newSpecial - currentSpecial;
|
||||||
|
if (delta != 0) {
|
||||||
|
offer.addToSpecialPriceDiff(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package net.fretux.ascend.event;
|
||||||
|
|
||||||
|
import net.fretux.ascend.AscendMod;
|
||||||
|
import net.fretux.ascend.player.PlayerStatsProvider;
|
||||||
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
|
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 perPoint = 0.005d;
|
||||||
|
double maxBonus = 0.40d;
|
||||||
|
double bonusMult = 1.0d + Math.min(magicScaling * perPoint, maxBonus);
|
||||||
|
modified = (float) (modified * bonusMult);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
double attackSpeed = 4.0d;
|
||||||
|
if (player.getAttribute(Attributes.ATTACK_SPEED) != null) {
|
||||||
|
attackSpeed = player.getAttribute(Attributes.ATTACK_SPEED).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
String key =
|
||||||
|
(attackSpeed >= 1.8d) ? "light_scaling" :
|
||||||
|
(attackSpeed >= 1.2d) ? "medium_scaling" :
|
||||||
|
"heavy_scaling";
|
||||||
|
|
||||||
|
int level = stats.getAttributeLevel(key);
|
||||||
|
if (level > 0) {
|
||||||
|
double perPoint =
|
||||||
|
key.equals("light_scaling") ? 0.003d :
|
||||||
|
key.equals("medium_scaling") ? 0.004d :
|
||||||
|
0.005d;
|
||||||
|
double maxBonus = 0.40d;
|
||||||
|
double bonusMult = 1.0d + Math.min(level * perPoint, maxBonus);
|
||||||
|
modified = (float) (modified * bonusMult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.fretux.ascend.player;
|
package net.fretux.ascend.player;
|
||||||
|
|
||||||
|
import net.fretux.ascend.compat.IronsSpellbooksCompat;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
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;
|
||||||
@@ -7,46 +8,51 @@ import net.minecraft.world.entity.ai.attributes.Attributes;
|
|||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static net.fretux.ascend.compat.IronsSpellbooksCompat.applyCharisma;
|
||||||
|
import static net.fretux.ascend.compat.IronsSpellbooksCompat.applyIntelligence;
|
||||||
|
|
||||||
public final class StatEffects {
|
public final class StatEffects {
|
||||||
|
|
||||||
// ===== Strength UUIDs =====
|
|
||||||
private static final UUID STRENGTH_DAMAGE_UUID =
|
private static final UUID STRENGTH_DAMAGE_UUID =
|
||||||
UUID.fromString("a7b8b2c3-9b0d-4e1a-8ee3-21f6a7b2c3d4");
|
UUID.fromString("a7b8b2c3-9b0d-4e1a-8ee3-21f6a7b2c3d4");
|
||||||
private static final UUID STRENGTH_KNOCKBACK_UUID =
|
private static final UUID STRENGTH_KNOCKBACK_UUID =
|
||||||
UUID.fromString("b17f0d74-9cd9-4f90-9ec8-6a1046e2f912");
|
UUID.fromString("b17f0d74-9cd9-4f90-9ec8-6a1046e2f912");
|
||||||
|
|
||||||
// ===== Agility UUIDs =====
|
|
||||||
private static final UUID AGILITY_SPEED_UUID =
|
private static final UUID AGILITY_SPEED_UUID =
|
||||||
UUID.fromString("c12e9b6f-1d79-4a49-b57f-7f563c10aa11");
|
UUID.fromString("c2f4e9a1-3b8d-4f21-9d44-8176f8791a11");
|
||||||
private static final UUID AGILITY_STEALTH_UUID =
|
|
||||||
UUID.fromString("d4e8a9f2-3b5c-4d0a-9f11-8acb3f0b77e2");
|
|
||||||
|
|
||||||
// ===== Fortitude UUIDs =====
|
|
||||||
private static final UUID FORTITUDE_HEALTH_UUID =
|
private static final UUID FORTITUDE_HEALTH_UUID =
|
||||||
UUID.fromString("e2b8ff31-2c3d-4a60-9ce4-12aa4d3f9b77");
|
UUID.fromString("d3a4b5c6-7d8e-4f90-8a1b-223344556677");
|
||||||
private static final UUID FORTITUDE_KB_RESIST_UUID =
|
private static final UUID FORTITUDE_KB_RESIST_UUID =
|
||||||
UUID.fromString("f4c1a9d2-6b8e-4f01-8a21-9e7c5b3d12aa");
|
UUID.fromString("e4b5c6d7-8e9f-4012-9abc-334455667788");
|
||||||
|
|
||||||
private StatEffects() {}
|
private StatEffects() {}
|
||||||
|
|
||||||
/** Call on login/clone and after stat changes. */
|
|
||||||
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 -> {
|
||||||
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"));
|
applyFortitude(player, stats.getAttributeLevel("fortitude"));
|
||||||
applyIntelligence(player, stats.getAttributeLevel("intelligence"));
|
// - Willpower: see getWillpowerSanityMultiplier / getWillpowerTempoMultiplier.
|
||||||
|
if (IronsSpellbooksCompat.isLoaded()) {
|
||||||
|
IronsSpellbooksCompat.applyAll(player);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== STRENGTH ==========
|
/**
|
||||||
|
* Strength:
|
||||||
|
* - +0.1 attack damage per point (0.05 hearts)
|
||||||
|
* - +0.03 knockback per point
|
||||||
|
* - Armor bypass handled in your combat event using strength level.
|
||||||
|
*/
|
||||||
public static void applyStrength(Player player, int strengthLevel) {
|
public static void applyStrength(Player player, int strengthLevel) {
|
||||||
|
// Damage
|
||||||
AttributeInstance atk = player.getAttribute(Attributes.ATTACK_DAMAGE);
|
AttributeInstance atk = player.getAttribute(Attributes.ATTACK_DAMAGE);
|
||||||
if (atk != null) {
|
if (atk != null) {
|
||||||
atk.removeModifier(STRENGTH_DAMAGE_UUID);
|
atk.removeModifier(STRENGTH_DAMAGE_UUID);
|
||||||
if (strengthLevel > 0) {
|
if (strengthLevel > 0) {
|
||||||
double dmgBonus = strengthLevel * 0.1d; // 0.05 hearts per point
|
double dmgBonus = strengthLevel * 0.1d;
|
||||||
atk.addTransientModifier(new AttributeModifier(
|
atk.addTransientModifier(new AttributeModifier(
|
||||||
STRENGTH_DAMAGE_UUID,
|
STRENGTH_DAMAGE_UUID,
|
||||||
"Ascend Strength damage bonus",
|
"Ascend Strength damage bonus",
|
||||||
@@ -56,6 +62,7 @@ public final class StatEffects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Knockback
|
||||||
AttributeInstance kb = player.getAttribute(Attributes.ATTACK_KNOCKBACK);
|
AttributeInstance kb = player.getAttribute(Attributes.ATTACK_KNOCKBACK);
|
||||||
if (kb != null) {
|
if (kb != null) {
|
||||||
kb.removeModifier(STRENGTH_KNOCKBACK_UUID);
|
kb.removeModifier(STRENGTH_KNOCKBACK_UUID);
|
||||||
@@ -71,102 +78,149 @@ public final class StatEffects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== AGILITY ==========
|
/**
|
||||||
|
* Agility:
|
||||||
|
* - Small movement speed bonus per point.
|
||||||
|
* - Stealth handled externally (e.g. other mods can read agility).
|
||||||
|
*/
|
||||||
public static void applyAgility(Player player, int agilityLevel) {
|
public static void applyAgility(Player player, int agilityLevel) {
|
||||||
// Movement speed
|
|
||||||
AttributeInstance move = player.getAttribute(Attributes.MOVEMENT_SPEED);
|
AttributeInstance move = player.getAttribute(Attributes.MOVEMENT_SPEED);
|
||||||
if (move != null) {
|
if (move != null) {
|
||||||
move.removeModifier(AGILITY_SPEED_UUID);
|
move.removeModifier(AGILITY_SPEED_UUID);
|
||||||
if (agilityLevel > 0) {
|
if (agilityLevel > 0) {
|
||||||
// ~+2.5% at 10, +25% at 100
|
// Example: +0.002 per point = +0.2 at 100 (about +20% on base 0.1)
|
||||||
double speedBonus = agilityLevel * 0.00025d;
|
double bonus = agilityLevel * 0.002d;
|
||||||
move.addTransientModifier(new AttributeModifier(
|
move.addTransientModifier(new AttributeModifier(
|
||||||
AGILITY_SPEED_UUID,
|
AGILITY_SPEED_UUID,
|
||||||
"Ascend Agility speed bonus",
|
"Ascend Agility speed bonus",
|
||||||
speedBonus,
|
bonus,
|
||||||
AttributeModifier.Operation.ADDITION
|
AttributeModifier.Operation.ADDITION
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stealth via follow range reduction
|
|
||||||
AttributeInstance follow = player.getAttribute(Attributes.FOLLOW_RANGE);
|
|
||||||
if (follow != null) {
|
|
||||||
follow.removeModifier(AGILITY_STEALTH_UUID);
|
|
||||||
if (agilityLevel > 0) {
|
|
||||||
double perPoint = 0.003d; // 0.3% per point
|
|
||||||
double maxReduction = 0.5d; // up to -50%
|
|
||||||
double reduction = Math.min(agilityLevel * perPoint, maxReduction);
|
|
||||||
|
|
||||||
if (reduction > 0.0d) {
|
|
||||||
double multiplier = 1.0d - reduction;
|
|
||||||
follow.addTransientModifier(new AttributeModifier(
|
|
||||||
AGILITY_STEALTH_UUID,
|
|
||||||
"Ascend Agility stealth bonus",
|
|
||||||
multiplier - 1.0d,
|
|
||||||
AttributeModifier.Operation.MULTIPLY_TOTAL
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== FORTITUDE ==========
|
/**
|
||||||
|
* Fortitude:
|
||||||
|
* - Max HP up per point.
|
||||||
|
* - Knockback taken reduced via attribute.
|
||||||
|
* (Poison/hunger/debuff tuning handled via your other event hooks.)
|
||||||
|
*/
|
||||||
public static void applyFortitude(Player player, int fortitudeLevel) {
|
public static void applyFortitude(Player player, int fortitudeLevel) {
|
||||||
// Max HP
|
// Max health
|
||||||
AttributeInstance maxHp = player.getAttribute(Attributes.MAX_HEALTH);
|
AttributeInstance hp = player.getAttribute(Attributes.MAX_HEALTH);
|
||||||
if (maxHp != null) {
|
if (hp != null) {
|
||||||
maxHp.removeModifier(FORTITUDE_HEALTH_UUID);
|
hp.removeModifier(FORTITUDE_HEALTH_UUID);
|
||||||
if (fortitudeLevel > 0) {
|
if (fortitudeLevel > 0) {
|
||||||
// +0.2 HP per point (1 heart per 10 points)
|
// Example: 0.25 HP (1/8 heart) per point = +12.5 hearts at 100
|
||||||
double bonusHp = fortitudeLevel * 0.2d;
|
double healthBonus = fortitudeLevel * 0.25d;
|
||||||
maxHp.addTransientModifier(new AttributeModifier(
|
hp.addTransientModifier(new AttributeModifier(
|
||||||
FORTITUDE_HEALTH_UUID,
|
FORTITUDE_HEALTH_UUID,
|
||||||
"Ascend Fortitude health bonus",
|
"Ascend Fortitude health bonus",
|
||||||
bonusHp,
|
healthBonus,
|
||||||
AttributeModifier.Operation.ADDITION
|
AttributeModifier.Operation.ADDITION
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float maxHealthNow = player.getMaxHealth();
|
|
||||||
if (player.getHealth() > maxHealthNow) {
|
|
||||||
player.setHealth(maxHealthNow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Knockback resistance
|
// Knockback resistance
|
||||||
AttributeInstance kbRes = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
|
AttributeInstance kbRes = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
|
||||||
if (kbRes != null) {
|
if (kbRes != null) {
|
||||||
kbRes.removeModifier(FORTITUDE_KB_RESIST_UUID);
|
kbRes.removeModifier(FORTITUDE_KB_RESIST_UUID);
|
||||||
if (fortitudeLevel > 0) {
|
if (fortitudeLevel > 0) {
|
||||||
// 0.3% per point, up to 30% at 100 (with safety cap)
|
// Tiny scaling; cap at 60% here
|
||||||
double bonus = fortitudeLevel * 0.003d;
|
double res = Math.min(0.006d * fortitudeLevel, 0.60d);
|
||||||
double safeBonus = Math.min(bonus, 0.8d);
|
kbRes.addTransientModifier(new AttributeModifier(
|
||||||
if (safeBonus > 0) {
|
FORTITUDE_KB_RESIST_UUID,
|
||||||
kbRes.addTransientModifier(new AttributeModifier(
|
"Ascend Fortitude knockback resist",
|
||||||
FORTITUDE_KB_RESIST_UUID,
|
res,
|
||||||
"Ascend Fortitude knockback resist",
|
AttributeModifier.Operation.ADDITION
|
||||||
safeBonus,
|
));
|
||||||
AttributeModifier.Operation.ADDITION
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== INTELLIGENCE ==========
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intelligence:
|
* Intelligence:
|
||||||
* - Max mana is exposed via PlayerStats (see below).
|
* - Used for mana & potion/enchant effects via other hooks.
|
||||||
* - Potions & enchanting are handled via event hooks.
|
* - Here we only expose a mana bonus helper.
|
||||||
*
|
|
||||||
* This exists for future attribute-based hooks if needed.
|
|
||||||
*/
|
*/
|
||||||
public static void applyIntelligence(Player player, int intelligenceLevel) {
|
|
||||||
// No direct vanilla attribute to touch here for mana.
|
// ===== Willpower external API =====
|
||||||
// Potion & enchanting effects are implemented in event handlers.
|
|
||||||
|
/**
|
||||||
|
* Sanity drain multiplier from Willpower.
|
||||||
|
* Other mods can call this directly.
|
||||||
|
*
|
||||||
|
* - Starts at 1.0
|
||||||
|
* - Reduces up to 50% at high Willpower.
|
||||||
|
*/
|
||||||
|
public static float getWillpowerSanityMultiplier(int willpowerLevel) {
|
||||||
|
if (willpowerLevel <= 0) return 1.0f;
|
||||||
|
double reduction = Math.min(willpowerLevel * 0.005d, 0.5d); // up to -50%
|
||||||
|
return (float) (1.0d - reduction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tempo gain/multiplier from Willpower.
|
||||||
|
* Other mods can plug this into stamina/tempo/sprint systems.
|
||||||
|
*
|
||||||
|
* - Starts at 1.0
|
||||||
|
* - Increases up to +40%.
|
||||||
|
*/
|
||||||
|
public static float getWillpowerTempoMultiplier(int willpowerLevel) {
|
||||||
|
if (willpowerLevel <= 0) return 1.0f;
|
||||||
|
double bonus = Math.min(willpowerLevel * 0.004d, 0.4d); // up to +40%
|
||||||
|
return (float) (1.0d + bonus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience: from player capability → sanity multiplier. */
|
||||||
|
public static float getPlayerSanityMultiplier(Player player) {
|
||||||
|
return player.getCapability(PlayerStatsProvider.PLAYER_STATS)
|
||||||
|
.map(stats -> getWillpowerSanityMultiplier(stats.getAttributeLevel("willpower")))
|
||||||
|
.orElse(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience: from player capability → tempo multiplier. */
|
||||||
|
public static float getPlayerTempoMultiplier(Player player) {
|
||||||
|
return player.getCapability(PlayerStatsProvider.PLAYER_STATS)
|
||||||
|
.map(stats -> getWillpowerTempoMultiplier(stats.getAttributeLevel("willpower")))
|
||||||
|
.orElse(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Intelligence external helper (for your mana system) =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example mana bonus: 1 mana per INT, capped by caller if needed.
|
||||||
|
*/
|
||||||
|
public static int getIntelligenceManaBonus(int intelligenceLevel) {
|
||||||
|
return Math.max(0, intelligenceLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Charisma helper (used in trade discount handler) =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charisma → trade discount fraction.
|
||||||
|
* Returns value in [0, max], e.g. 0.0 = no discount, 0.25 = 25% cheaper.
|
||||||
|
*/
|
||||||
|
public static double getCharismaTradeDiscount(int charismaLevel) {
|
||||||
|
if (charismaLevel <= 0) return 0.0d;
|
||||||
|
double perPoint = 0.01d;
|
||||||
|
double max = 0.30d;
|
||||||
|
return Math.min(charismaLevel * perPoint, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getWeaponScalingMultiplier(int scalingLevel) {
|
||||||
|
if (scalingLevel <= 0) return 1.0d;
|
||||||
|
double perPoint = 0.005d;
|
||||||
|
double maxBonus = 0.50d;
|
||||||
|
return 1.0d + Math.min(scalingLevel * perPoint, maxBonus);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getMagicScalingMultiplier(int scalingLevel) {
|
||||||
|
if (scalingLevel <= 0) return 1.0d;
|
||||||
|
double perPoint = 0.0075d;
|
||||||
|
double maxBonus = 0.75d;
|
||||||
|
return 1.0d + Math.min(scalingLevel * perPoint, maxBonus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user