Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Added

- Added the `cannot_modify_cost` tag for patterns that should ignore the `media_consumption` attribute when calculating cost, by Robotgiggle in [987](https://github.com/FallingColors/HexMod/pull/987).
- Added a config options to rescale the cost of some or all patterns ([#1041](https://github.com/FallingColors/HexMod/pull/1041)) @Robotgiggle

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import at.petrak.hexcasting.api.casting.mishaps.MishapDisallowedSpell;
import at.petrak.hexcasting.api.casting.mishaps.MishapEntityTooFarAway;
import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.mod.HexTags;
import at.petrak.hexcasting.api.pigment.FrozenPigment;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.HexAttributes;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
Expand All @@ -38,6 +40,7 @@

import static at.petrak.hexcasting.api.HexAPI.modLoc;
import static at.petrak.hexcasting.api.casting.eval.CastingEnvironmentComponent.*;
import static at.petrak.hexcasting.api.utils.HexUtils.isOfTag;

/**
* Environment within which hexes are cast.
Expand Down Expand Up @@ -193,14 +196,19 @@ public void precheckAction(PatternShapeMatch match) throws Mishap {
throw new MishapDisallowedSpell("disallowed", loc);
}

costModifier = this.getCostModifier(match);
costModifier = (loc != null) ? this.getCostModifier(loc) : 1.0;
}

/**
* Casting env subclasses can override this to modify the cost for a given action
* Gets the cost modifier for a given action. By default, this is based on the cost scaling values
* in the config. Casting env subclasses can override this to modify the cost in other ways.
*/
protected double getCostModifier(PatternShapeMatch match) {
return 1.0;
protected double getCostModifier(@NotNull ResourceLocation loc) {
if (isOfTag(IXplatAbstractions.INSTANCE.getActionRegistry(), loc, HexTags.Actions.CANNOT_MODIFY_COST)) {
// blacklisted actions can still be manually scaled in the config, but the global scaling doesn't apply
return HexConfig.server().getActionCostScaling(loc);
}
return HexConfig.server().getActionCostScaling(loc) * HexConfig.server().globalCostScaling();
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
Expand Down Expand Up @@ -71,12 +72,12 @@ public ServerPlayer getCaster() {
}

@Override
protected double getCostModifier(PatternShapeMatch match) {
ResourceLocation loc = actionKey(match);
if (loc != null && isOfTag(IXplatAbstractions.INSTANCE.getActionRegistry(), loc, HexTags.Actions.CANNOT_MODIFY_COST)) {
return 1.0;
protected double getCostModifier(@NotNull ResourceLocation loc) {
var base = super.getCostModifier(loc);
if (isOfTag(IXplatAbstractions.INSTANCE.getActionRegistry(), loc, HexTags.Actions.CANNOT_MODIFY_COST)) {
return base;
}
return this.caster.getAttributeValue(HexAttributes.MEDIA_CONSUMPTION_MODIFIER);
return base * this.caster.getAttributeValue(HexAttributes.MEDIA_CONSUMPTION_MODIFIER);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ public interface ServerConfigAccess {

boolean isActionAllowedInCircles(ResourceLocation actionID);

double getActionCostScaling(ResourceLocation actionID);

double globalCostScaling();

boolean doesGreaterTeleportSplatItems();

boolean doVillagersTakeOffenseAtMindMurder();
Expand All @@ -94,6 +98,7 @@ public interface ServerConfigAccess {
int DEFAULT_MAX_SPELL_CIRCLE_LENGTH = 1024;
int DEFAULT_OP_BREAK_HARVEST_LEVEL = 3;

double DEFAULT_GLOBAL_COST_SCALING = 1.0;
double DEFAULT_TRADER_SCROLL_CHANCE = 0.2;
boolean DEFAULT_GREATER_TELEPORT_SPLATS_ITEMS = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public static final class Actions {
public static final TagKey<ActionRegistryEntry> CAN_START_ENLIGHTEN = create("can_start_enlighten");

/**
* Actions that should not be affected by the media_consumption attribute
* Actions that should not be affected by the media_consumption attribute or the globalCostScaling config
*/
public static final TagKey<ActionRegistryEntry> CANNOT_MODIFY_COST = create("cannot_modify_cost");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,11 +393,19 @@
},
actionDenyList: {
"": "Action Deny List",
"@Tooltip": "Resource locations of disallowed actions. Trying to cast one of these will result in a mishap. For example, hexcasting:get_caster will prevent Mind's Reflection",
"@Tooltip": "Resource locations of disallowed actions. Trying to cast one of these will result in a mishap. For example, hexcasting:get_caster will prevent Mind's Reflection.",
},
circleActionDenyList: {
"": "Circle Action Deny List",
"@Tooltip": "Resource locations of disallowed actions within circles. Trying to cast one of these from a circle will result in a mishap.",
"@Tooltip": "Resource locations of disallowed actions within circles. Trying to cast one of these from a circle will result in a mishap. For example, hexcasting:get_caster will prevent Mind's Reflection.",
},
globalCostScaling: {
"": "Global Cost Scaling Factor",
"@Tooltip": "All media costs, except for actions in the #hexcasting:cannot_modify_cost tag, will be multiplied by this value. If you want to modify the cost of an action in that tag, use the per-action cost scaling list.",
},
costRescaleListRaw: {
"": "Per-Action Cost Scaling Factors",
"@Tooltip": "Maps resource locations to the scaling factor for that specific action's media cost. For example, hexcasting:add_motion 3 will make Impulse cost 3x as much.",
},
greaterTeleportSplatsItems: {
"": "Greater Teleport Splats Items",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.google.gson.GsonBuilder;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import me.shedaniel.autoconfig.AutoConfig;
import me.shedaniel.autoconfig.ConfigData;
import me.shedaniel.autoconfig.annotation.Config;
Expand Down Expand Up @@ -209,6 +211,11 @@ public static final class Server implements HexConfig.ServerConfigAccess, Config
@ConfigEntry.Gui.Tooltip
private List<String> circleActionDenyList = List.of();
@ConfigEntry.Gui.Tooltip
private List<String> costRescaleListRaw = List.of();
private transient Object2DoubleMap<ResourceLocation> costRescaleList;
@ConfigEntry.Gui.Tooltip
private double globalCostScaling = DEFAULT_GLOBAL_COST_SCALING;
@ConfigEntry.Gui.Tooltip
private boolean greaterTeleportSplatsItems = DEFAULT_GREATER_TELEPORT_SPLATS_ITEMS;
@ConfigEntry.Gui.Tooltip
private boolean villagersOffendedByMindMurder = DEFAULT_VILLAGERS_DISLIKE_MIND_MURDER;
Expand Down Expand Up @@ -257,6 +264,18 @@ public void validatePostLoad() throws ValidationException {
this.maxSpellCircleLength = Math.max(this.maxSpellCircleLength, 4);
this.traderScrollChance = Mth.clamp(this.traderScrollChance, 0.0, 1.0);

this.costRescaleList = new Object2DoubleOpenHashMap<>();
try {
for (var auugh : this.costRescaleListRaw) {
String[] split = auugh.split(" ");
ResourceLocation loc = new ResourceLocation(split[0]);
double scale = Double.parseDouble(split[1]);
this.costRescaleList.put(loc, scale);
}
} catch (Exception e) {
throw new ValidationException("Bad parsing of action cost rescaling", e);
}

this.scrollInjections = new Object2IntOpenHashMap<>();
try {
for (var auugh : this.scrollInjectionsRaw) {
Expand All @@ -265,7 +284,6 @@ public void validatePostLoad() throws ValidationException {
int count = Integer.parseInt(split[1]);
this.scrollInjections.put(loc, count);
}

} catch (Exception e) {
throw new ValidationException("Bad parsing of scroll injects", e);
}
Expand Down Expand Up @@ -320,6 +338,14 @@ public boolean isActionAllowedInCircles(ResourceLocation actionID) {
return noneMatch(circleActionDenyList, actionID);
}

@Override
public double getActionCostScaling(ResourceLocation actionID) {
return this.costRescaleList.getOrDefault(actionID, 1.0);
}

@Override
public double globalCostScaling() { return globalCostScaling; }

@Override
public boolean doesGreaterTeleportSplatItems() { return greaterTeleportSplatsItems; }

Expand Down
60 changes: 50 additions & 10 deletions Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ public static class Server implements HexConfig.ServerConfigAccess {
private static ForgeConfigSpec.IntValue maxSpellCircleLength;
private static ForgeConfigSpec.ConfigValue<List<? extends String>> actionDenyList;
private static ForgeConfigSpec.ConfigValue<List<? extends String>> circleActionDenyList;
private static ForgeConfigSpec.ConfigValue<List<? extends String>> costRescaleList;
private static ForgeConfigSpec.DoubleValue globalCostScaling;

private static ForgeConfigSpec.BooleanValue greaterTeleportSplatsItems;
private static ForgeConfigSpec.BooleanValue villagersOffendedByMindMurder;
Expand All @@ -185,10 +187,30 @@ public Server(ForgeConfigSpec.Builder builder) {
maxOpCount = builder.comment("The maximum number of actions that can be executed in one tick, to avoid " +
"hanging the server.")
.defineInRange("maxOpCount", DEFAULT_MAX_OP_COUNT, 0, Integer.MAX_VALUE);

opBreakHarvestLevel = builder.comment(
"The harvest level of the Break Block spell.",
"0 = wood, 1 = stone, 2 = iron, 3 = diamond, 4 = netherite."
).defineInRange("opBreakHarvestLevel", DEFAULT_OP_BREAK_HARVEST_LEVEL, 0, 4);

greaterTeleportSplatsItems = builder.comment(
"Should items fly out of the player's inventory when using Greater Teleport?"
).define("greaterTeleportSplatsItems", DEFAULT_GREATER_TELEPORT_SPLATS_ITEMS);

actionDenyList = builder.comment(
"Resource locations of disallowed actions. Trying to cast one of these will result in a mishap. " +
"For example, \"hexcasting:get_caster\" will prevent Mind's Reflection.")
.defineList("actionDenyList", List.of(), Server::isValidReslocArg);

costRescaleList = builder.comment(
"Maps resource locations to the scaling factor for that specific action's media cost. " +
"For example, \"hexcasting:add_motion 3\" will make Impulse cost 3x as much.")
.defineList("costRescaleList", List.of(), Server::isValidReslocDoublePair);

globalCostScaling = builder.comment(
"All media costs, except for actions in the #hexcasting:cannot_modify_cost tag, will be multiplied by this value." +
"If you want to modify the cost of an action in that tag, use the per-action list above.")
.defineInRange("globalCostScaling", DEFAULT_GLOBAL_COST_SCALING, 0.0, Double.MAX_VALUE);
builder.pop();

builder.push("Spell Circles");
Expand All @@ -197,7 +219,7 @@ public Server(ForgeConfigSpec.Builder builder) {

circleActionDenyList = builder.comment(
"Resource locations of disallowed actions within circles. Trying to cast one of these in a circle" +
" will result in a mishap. For example: hexcasting:get_caster will prevent Mind's Reflection.")
" will result in a mishap. For example, \"hexcasting:get_caster\" will prevent Mind's Reflection.")
.defineList("circleActionDenyList", List.of(), Server::isValidReslocArg);
builder.pop();

Expand All @@ -206,17 +228,8 @@ public Server(ForgeConfigSpec.Builder builder) {
.defineInRange("traderScrollChance", DEFAULT_TRADER_SCROLL_CHANCE, 0.0, 1.0);

// builders for loot (eg. scroll/lore/cypher pools and chances) should go here

builder.pop();

actionDenyList = builder.comment(
"Resource locations of disallowed actions. Trying to cast one of these will result in a mishap.")
.defineList("actionDenyList", List.of(), Server::isValidReslocArg);

greaterTeleportSplatsItems = builder.comment(
"Should items fly out of the player's inventory when using Greater Teleport?"
).define("greaterTeleportSplatsItems", DEFAULT_GREATER_TELEPORT_SPLATS_ITEMS);

villagersOffendedByMindMurder = builder.comment(
"Should villagers take offense when you flay the mind of their fellow villagers?")
.define("villagersOffendedByMindMurder", true);
Expand Down Expand Up @@ -254,6 +267,20 @@ public boolean isActionAllowedInCircles(ResourceLocation actionID) {
return noneMatch(circleActionDenyList.get(), actionID);
}

@Override
public double getActionCostScaling(ResourceLocation actionID) {
for (var entry : costRescaleList.get()) {
String[] split = entry.split(" ");
if (actionID.toString().equals(split[0])) {
return Double.parseDouble(split[1]);
}
}
return 1.0;
}

@Override
public double globalCostScaling() { return globalCostScaling.get(); }

@Override
public boolean doesGreaterTeleportSplatItems() { return greaterTeleportSplatsItems.get(); }

Expand All @@ -280,5 +307,18 @@ public double traderScrollChance() {
private static boolean isValidReslocArg(Object o) {
return o instanceof String s && ResourceLocation.isValidResourceLocation(s);
}

private static boolean isValidReslocDoublePair(Object o) {
if (o instanceof String s) {
String[] split = s.split(" ");
try {
Double.parseDouble(split[1]);
} catch (NumberFormatException e) {
return false;
}
return ResourceLocation.isValidResourceLocation(split[0]);
}
return false;
}
}
}
Loading