Skip to content

Commit

Permalink
Implement conditional stat module and migrate airborne to it
Browse files Browse the repository at this point in the history
  • Loading branch information
KnightMiner committed Jan 1, 2024
1 parent a138802 commit d25fce0
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"type": "tconstruct:composable",
"level_display": "tconstruct:default",
"tooltip_display": "always",
"modules": [
{
"type": "tconstruct:conditional_mining_speed",
"blocks": "tconstruct:any",
"entity": {
"type": "mantle:inverted",
"predicate": "tconstruct:on_ground"
},
"require_effective": false,
"percent": true,
"flat": 4.0,
"hooks": [
"tconstruct:break_speed"
]
},
{
"type": "tconstruct:conditional_stat",
"stat": "tconstruct:accuracy",
"entity": "tconstruct:airborne",
"percent": false,
"flat": 0.5
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package slimeknights.tconstruct.library.json.predicate.block;

import net.minecraft.world.level.block.state.BlockState;
import slimeknights.mantle.data.GenericLoaderRegistry.IGenericLoader;
import slimeknights.mantle.data.GenericLoaderRegistry.SingletonLoader;
import slimeknights.mantle.data.predicate.IJsonPredicate;
import slimeknights.mantle.data.predicate.block.BlockPredicate;

public interface TinkerBlockPredicate {
/** Predicate that matches any block */
BlockPredicate ANY = SingletonLoader.singleton(loader -> new BlockPredicate() {
@Override
public boolean matches(BlockState input) {
return true;
}

@Override
public IGenericLoader<? extends IJsonPredicate<BlockState>> getLoader() {
return loader;
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

public interface TinkerLivingEntityPredicate {

/** Entities that are in the air */
/** Entities that are in the air, notably does not count you as airborne if swimming, riding, or climbing */
LivingEntityPredicate AIRBORNE = simple(entity -> !entity.isOnGround() && !entity.onClimbable() && !entity.isInWater() && !entity.isPassenger());
/** Checks if the entity is on the ground */
LivingEntityPredicate ON_GROUND = simple(Entity::isOnGround);
/** Entities that are in the air */
LivingEntityPredicate CROUCHING = simple(Entity::isCrouching);
/** Entities with eyes in water */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package slimeknights.tconstruct.library.modifiers.modules.behavior;

import com.google.gson.JsonObject;
import lombok.Setter;
import lombok.experimental.Accessors;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.entity.LivingEntity;
import slimeknights.mantle.data.GenericLoaderRegistry.IGenericLoader;
import slimeknights.mantle.data.predicate.IJsonPredicate;
import slimeknights.mantle.data.predicate.entity.LivingEntityPredicate;
import slimeknights.tconstruct.library.json.math.ModifierFormula;
import slimeknights.tconstruct.library.json.math.ModifierFormula.FallbackFormula;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.ModifierHook;
import slimeknights.tconstruct.library.modifiers.TinkerHooks;
import slimeknights.tconstruct.library.modifiers.hook.ConditionalStatModifierHook;
import slimeknights.tconstruct.library.modifiers.modules.ModifierModule;
import slimeknights.tconstruct.library.modifiers.modules.ModifierModuleCondition;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;
import slimeknights.tconstruct.library.tools.stat.FloatToolStat;
import slimeknights.tconstruct.library.tools.stat.INumericToolStat;
import slimeknights.tconstruct.library.tools.stat.ToolStats;

import javax.annotation.Nullable;
import java.util.List;

import static slimeknights.tconstruct.library.json.math.ModifierFormula.FallbackFormula.BOOST;
import static slimeknights.tconstruct.library.json.math.ModifierFormula.FallbackFormula.PERCENT;

/**
* Module for common conditional stats, such as on ranged tools
* @param stat Stat to boost
* @param holder Condition on the tool holder
* @param formula Formula to apply
* @param percent If true, the formula is a percent formula
* @param condition Standard modifier module conditions
*/
public record ConditionalStatModule(INumericToolStat<?> stat, IJsonPredicate<LivingEntity> holder, ModifierFormula formula, boolean percent, ModifierModuleCondition condition) implements ModifierModule, ConditionalStatModifierHook, ConditionalStatTooltip {
private static final List<ModifierHook<?>> DEFAULT_HOOKS = List.of(TinkerHooks.CONDITIONAL_STAT, TinkerHooks.TOOLTIP);
/** Variables for the modifier formula */
private static final String[] VARIABLES = { "level", "value", "multiplier" };
// variables for the formula
/** Value from the previous conditional modifier */
public static final int VALUE = 1;
/** Stat multiplier from the tool */
public static final int MULTIPLIER = 2;

@Nullable
@Override
public Integer getPriority() {
// run multipliers a bit later
return percent ? 75 : null;
}

@Override
public float modifyStat(IToolStackView tool, ModifierEntry modifier, LivingEntity living, FloatToolStat stat, float baseValue, float multiplier) {
if (this.stat == stat && condition.matches(tool, modifier) && this.holder.matches(living)) {
return formula.apply(formula.computeLevel(tool, modifier), baseValue, multiplier);
}
return baseValue;
}

@Override
public float computeTooltipValue(IToolStackView tool, ModifierEntry entry) {
return formula.apply(formula.computeLevel(tool, entry), 1, tool.getMultiplier(this.stat));
}

@Override
public List<ModifierHook<?>> getDefaultHooks() {
return DEFAULT_HOOKS;
}

@Override
public IGenericLoader<? extends ConditionalStatModule> getLoader() {
return LOADER;
}

public static final IGenericLoader<ConditionalStatModule> LOADER = new IGenericLoader<>() {
@Override
public ConditionalStatModule deserialize(JsonObject json) {
boolean percent = GsonHelper.getAsBoolean(json, "percent", false);
return new ConditionalStatModule(
ToolStats.numericFromJson(GsonHelper.getAsString(json, "stat")),
LivingEntityPredicate.LOADER.getAndDeserialize(json, "entity"),
ModifierFormula.deserialize(json, VARIABLES, percent ? PERCENT : BOOST), percent,
ModifierModuleCondition.deserializeFrom(json)
);
}

@Override
public void serialize(ConditionalStatModule object, JsonObject json) {
object.condition.serializeInto(json);
json.addProperty("stat", object.stat.getName().toString());
json.add("entity", LivingEntityPredicate.LOADER.serialize(object.holder));
json.addProperty("percent", object.percent);
object.formula.serialize(json, VARIABLES);
}

@Override
public ConditionalStatModule fromNetwork(FriendlyByteBuf buffer) {
boolean percent = buffer.readBoolean();
return new ConditionalStatModule(
ToolStats.numericFromNetwork(buffer),
LivingEntityPredicate.LOADER.fromNetwork(buffer),
ModifierFormula.fromNetwork(buffer, VARIABLES.length, percent ? PERCENT : BOOST), percent,
ModifierModuleCondition.fromNetwork(buffer));
}

@Override
public void toNetwork(ConditionalStatModule object, FriendlyByteBuf buffer) {
buffer.writeBoolean(object.percent);
buffer.writeUtf(object.stat.getName().toString());
LivingEntityPredicate.LOADER.toNetwork(object.holder, buffer);
object.formula.toNetwork(buffer);
object.condition.toNetwork(buffer);
}
};


/* Builder */

/** Creates a builder instance */
public static Builder stat(INumericToolStat<?> stat) {
return new Builder(stat);
}

/** Builder class */
public static class Builder extends ModifierFormula.Builder<Builder,ConditionalStatModule> {
private final INumericToolStat<?> stat;
@Setter
@Accessors(fluent = true)
private IJsonPredicate<LivingEntity> holder = LivingEntityPredicate.ANY;
private boolean percent = false;

private Builder(INumericToolStat<?> stat) {
super(VARIABLES, BOOST);
this.stat = stat;
}

@Override
protected FallbackFormula getFormula() {
return percent ? PERCENT : BOOST;
}

/** Sets this to a percent boost formula */
public Builder percent() {
this.percent = true;
return this;
}

@Override
protected ConditionalStatModule build(ModifierFormula formula) {
return new ConditionalStatModule(stat, holder, formula, percent, condition);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import slimeknights.tconstruct.library.json.predicate.block.BlockPropertiesPredicate;
import slimeknights.tconstruct.library.json.predicate.block.SetBlockPredicate;
import slimeknights.tconstruct.library.json.predicate.block.TagBlockPredicate;
import slimeknights.tconstruct.library.json.predicate.block.TinkerBlockPredicate;
import slimeknights.tconstruct.library.json.predicate.damage.DamageSourcePredicate;
import slimeknights.tconstruct.library.json.predicate.damage.SourceAttackerPredicate;
import slimeknights.tconstruct.library.json.predicate.damage.SourceMessagePredicate;
Expand Down Expand Up @@ -175,6 +176,7 @@ void registerRecipeSerializers(RegistryEvent.Register<RecipeSerializer<?>> event
BlockPredicate.LOADER.register(TConstruct.getResource("requires_tool"), BlockPredicate.REQUIRES_TOOL.getLoader());
BlockPredicate.LOADER.register(TConstruct.getResource("set"), SetBlockPredicate.LOADER);
BlockPredicate.LOADER.register(TConstruct.getResource("tag"), TagBlockPredicate.LOADER);
slimeknights.mantle.data.predicate.block.BlockPredicate.LOADER.register(TConstruct.getResource("any"), TinkerBlockPredicate.ANY.getLoader());
slimeknights.mantle.data.predicate.block.BlockPredicate.LOADER.register(TConstruct.getResource("block_properties"), BlockPropertiesPredicate.LOADER);
// entity predicates
LivingEntityPredicate.LOADER.register(TConstruct.getResource("and"), LivingEntityPredicate.AND);
Expand All @@ -192,6 +194,7 @@ void registerRecipeSerializers(RegistryEvent.Register<RecipeSerializer<?>> event
LivingEntityPredicate.LOADER.register(TConstruct.getResource("feet_in_water"), LivingEntityPredicate.FEET_IN_WATER.getLoader());
// mantle
slimeknights.mantle.data.predicate.entity.LivingEntityPredicate.LOADER.register(TConstruct.getResource("airborne"), TinkerLivingEntityPredicate.AIRBORNE.getLoader());
slimeknights.mantle.data.predicate.entity.LivingEntityPredicate.LOADER.register(TConstruct.getResource("on_ground"), TinkerLivingEntityPredicate.ON_GROUND.getLoader());
slimeknights.mantle.data.predicate.entity.LivingEntityPredicate.LOADER.register(TConstruct.getResource("crouching"), TinkerLivingEntityPredicate.CROUCHING.getLoader());
slimeknights.mantle.data.predicate.entity.LivingEntityPredicate.LOADER.register(TConstruct.getResource("eyes_in_water"), TinkerLivingEntityPredicate.EYES_IN_WATER.getLoader());
slimeknights.mantle.data.predicate.entity.LivingEntityPredicate.LOADER.register(TConstruct.getResource("feet_in_water"), TinkerLivingEntityPredicate.FEET_IN_WATER.getLoader());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import slimeknights.tconstruct.library.modifiers.modules.armor.ReplaceBlockWalkerModule;
import slimeknights.tconstruct.library.modifiers.modules.armor.ToolActionWalkerTransformModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.AttributeModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.ConditionalStatModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.ExtinguishCampfireModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.IncrementalModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.ReduceToolDamageModule;
Expand Down Expand Up @@ -191,7 +192,6 @@
import slimeknights.tconstruct.tools.modifiers.traits.general.StoneshieldModifier;
import slimeknights.tconstruct.tools.modifiers.traits.general.TannedModifier;
import slimeknights.tconstruct.tools.modifiers.traits.general.TastyModifier;
import slimeknights.tconstruct.tools.modifiers.traits.harvest.AirborneModifier;
import slimeknights.tconstruct.tools.modifiers.traits.harvest.DwarvenModifier;
import slimeknights.tconstruct.tools.modifiers.traits.harvest.MaintainedModifier;
import slimeknights.tconstruct.tools.modifiers.traits.harvest.MomentumModifier;
Expand Down Expand Up @@ -490,7 +490,9 @@ public TinkerModifiers() {
public static final StaticModifier<DwarvenModifier> dwarven = MODIFIERS.register("dwarven", DwarvenModifier::new);
public static final StaticModifier<OvergrowthModifier> overgrowth = MODIFIERS.register("overgrowth", OvergrowthModifier::new);
public static final StaticModifier<RagingModifier> raging = MODIFIERS.register("raging", RagingModifier::new);
public static final StaticModifier<AirborneModifier> airborne = MODIFIERS.register("airborne", AirborneModifier::new);
/** @deprecated use {@link ModifierIds#airborne} */
@Deprecated
public static final DynamicModifier<Modifier> airborne = MODIFIERS.registerDynamic("airborne");
// traits - tier 3
public static final StaticModifier<OvercastModifier> overcast = MODIFIERS.register("overcast", OvercastModifier::new);
public static final StaticModifier<LaceratingModifier> lacerating = MODIFIERS.register("lacerating", LaceratingModifier::new);
Expand Down Expand Up @@ -658,6 +660,7 @@ void registerSerializers(RegistryEvent.Register<RecipeSerializer<?>> event) {
ModifierModule.LOADER.register(TConstruct.getResource("tool_actions"), ToolActionsModule.LOADER);
ModifierModule.LOADER.register(TConstruct.getResource("tool_action_transform"), ToolActionTransformModule.LOADER);
// build
ModifierModule.LOADER.register(TConstruct.getResource("conditional_stat"), ConditionalStatModule.LOADER);
ModifierModule.LOADER.register(TConstruct.getResource("constant_enchantment"), EnchantmentModule.Constant.LOADER);
ModifierModule.LOADER.register(TConstruct.getResource("modifier_slot"), ModifierSlotModule.LOADER);
ModifierModule.LOADER.register(TConstruct.getResource("rarity"), RarityModule.LOADER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public class ModifierIds {
// traits - tier 2
public static final ModifierId sturdy = id("sturdy");
public static final ModifierId scorching = id("scorching");
public static final ModifierId airborne = id("airborne");
// traits - tier 2 compat
public static final ModifierId dense = id("dense");
public static final ModifierId lustrous = id("lustrous");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import slimeknights.tconstruct.library.data.tinkering.AbstractModifierProvider;
import slimeknights.tconstruct.library.json.RandomLevelingValue;
import slimeknights.tconstruct.library.json.predicate.block.BlockPropertiesPredicate;
import slimeknights.tconstruct.library.json.predicate.block.TinkerBlockPredicate;
import slimeknights.tconstruct.library.json.predicate.damage.DamageSourcePredicate;
import slimeknights.tconstruct.library.json.predicate.damage.SourceMessagePredicate;
import slimeknights.tconstruct.library.json.predicate.entity.TinkerLivingEntityPredicate;
Expand All @@ -47,6 +48,7 @@
import slimeknights.tconstruct.library.modifiers.modules.armor.ReplaceBlockWalkerModule;
import slimeknights.tconstruct.library.modifiers.modules.armor.ToolActionWalkerTransformModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.AttributeModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.ConditionalStatModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.ExtinguishCampfireModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.IncrementalModule;
import slimeknights.tconstruct.library.modifiers.modules.behavior.ReduceToolDamageModule;
Expand Down Expand Up @@ -321,6 +323,11 @@ protected void addModifiers() {
// traits - tier 2
buildModifier(ModifierIds.sturdy).addModule(StatBoostModule.multiplyBase(ToolStats.DURABILITY).eachLevel(0.15f));
buildModifier(ModifierIds.scorching).addModule(ConditionalMeleeDamageModule.target(LivingEntityPredicate.ON_FIRE).eachLevel(2f));
buildModifier(ModifierIds.airborne)
// 400% boost means 5x mining speed
.addModule(ConditionalMiningSpeedModule.blocks(TinkerBlockPredicate.ANY).holder(TinkerLivingEntityPredicate.ON_GROUND.inverted()).percent().allowIneffective().flat(4), TinkerHooks.BREAK_SPEED)
// accuracy gets a 0.5 boost under the stricter version of in air (no boost just for being on a ladder)
.addModule(ConditionalStatModule.stat(ToolStats.ACCURACY).holder(TinkerLivingEntityPredicate.AIRBORNE).flat(0.5f));
// traits - tier 2 compat
addModifier(ModifierIds.lustrous, new Modifier());
buildModifier(ModifierIds.sharpweight)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ protected void addMaterialTraits() {
addDefaultTraits(MaterialIds.whitestone, TinkerModifiers.stoneshield);
// tier 2 - binding
addDefaultTraits(MaterialIds.chain, ModifierIds.reinforced);
addDefaultTraits(MaterialIds.skyslimeVine, TinkerModifiers.airborne);
addDefaultTraits(MaterialIds.skyslimeVine, ModifierIds.airborne);

// tier 3
addDefaultTraits(MaterialIds.slimesteel, TinkerModifiers.overcast, TinkerModifiers.overslime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
import slimeknights.tconstruct.library.tools.stat.FloatToolStat;
import slimeknights.tconstruct.library.tools.stat.ToolStats;

/**
* @deprecated use {@link slimeknights.tconstruct.library.modifiers.modules.mining.ConditionalMiningSpeedModule}
* and {@link slimeknights.tconstruct.library.modifiers.modules.behavior.ConditionalStatModule}
* */
@Deprecated
public class AirborneModifier extends NoLevelsModifier implements ConditionalStatModifierHook {
@Override
public int getPriority() {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/assets/tconstruct/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -2294,6 +2294,7 @@
"modifier.tconstruct.airborne": "Airborne",
"modifier.tconstruct.airborne.flavor": "Take to the skies!",
"modifier.tconstruct.airborne.description": "Tool no longer is penalized when mining in the air and gains a projectile accuracy bonus",
"modifier.tconstruct.airborne.accuracy": "Airborne Accuracy",

"__comment": "Tier 3 Traits",
"modifier.tconstruct.overcast": "Overcast",
Expand Down

0 comments on commit d25fce0

Please sign in to comment.