-
Notifications
You must be signed in to change notification settings - Fork 754
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a very powerful hook that allows a modifier to add additional modifiers to the tool. Example usecases: * Add a modifier with special usages as an effect of a custom modifier, e.g. reinforced * Add an internal modifier to run part of a modifier with a different priority Create a modifier that dynamically adds other modifiers under some conditions
- Loading branch information
1 parent
5b495bc
commit 793e549
Showing
8 changed files
with
198 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
src/main/java/slimeknights/tconstruct/library/modifiers/hook/build/ModifierTraitHook.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package slimeknights.tconstruct.library.modifiers.hook.build; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import slimeknights.tconstruct.TConstruct; | ||
import slimeknights.tconstruct.library.modifiers.Modifier; | ||
import slimeknights.tconstruct.library.modifiers.ModifierEntry; | ||
import slimeknights.tconstruct.library.modifiers.TinkerHooks; | ||
import slimeknights.tconstruct.library.tools.context.ToolRebuildContext; | ||
import slimeknights.tconstruct.library.tools.nbt.ModifierNBT; | ||
|
||
import java.util.Collection; | ||
import java.util.HashSet; | ||
import java.util.LinkedHashSet; | ||
import java.util.Set; | ||
|
||
/** Hook for a modifier to add in other modifiers */ | ||
public interface ModifierTraitHook { | ||
/** | ||
* Add all traits from this modifier to the builder. | ||
* This hook may be called multiple times during the building process if multiple modifiers have the same trait, use {@code firstEncounter} to distinguish. | ||
* Do not call this method directly, call it through {@link TraitBuilder}. | ||
* @param context Tool building context, note that volatile data has not yet been filled and modifiers does not include traits | ||
* @param modifier Modifier entry | ||
* @param state State keeping track of traits, use methods on this object to add traits | ||
* @param firstEncounter If true, this is the first time this modifier has been seen while rebuilding the stats | ||
*/ | ||
void addTraits(ToolRebuildContext context, ModifierEntry modifier, TraitBuilder state, boolean firstEncounter); | ||
|
||
/** Builder that handles adding traits that can themselves contain traits */ | ||
@RequiredArgsConstructor | ||
class TraitBuilder { | ||
/** Set of all modifiers that have been encountered during this rebuild */ | ||
private final Set<Modifier> seenModifiers = new HashSet<>(); | ||
/** Modifiers that are currently adding their traits, prevents adding traits for a modifier inside itself, which will recurse infinitely */ | ||
private final Set<Modifier> currentStack = new LinkedHashSet<>(); | ||
/** Context for tool building */ | ||
private final ToolRebuildContext context; | ||
/** Builder instance */ | ||
private final ModifierNBT.Builder builder; | ||
|
||
/** Adds the given modifier to the builder and adds all its traits */ | ||
public void addEntry(ModifierEntry entry) { | ||
builder.add(entry); | ||
addTraits(entry); | ||
} | ||
|
||
/** Adds all traits for the given modifier entry */ | ||
private void addTraits(ModifierEntry entry) { | ||
Modifier modifier = entry.getModifier(); | ||
// if the modifier lacks the trait hook, then we can skip tracking it, no need to add it to any data structures | ||
ModifierTraitHook hook = modifier.getHooks().getOrNull(TinkerHooks.MODIFIER_TRAITS); | ||
if (hook != null) { | ||
// if this modifier is already on the stack, ignore it to avoid infinite recursion | ||
if (currentStack.contains(modifier)) { | ||
TConstruct.LOG.error("Encountered {} as a child of itself, previous stack {}", modifier.getId(), currentStack); | ||
} else { | ||
// not on the stack? add it, then recursively add traits | ||
currentStack.add(modifier); | ||
hook.addTraits(context, entry, this, !hasSeenModifier(modifier)); | ||
seenModifiers.add(modifier); | ||
currentStack.remove(modifier); | ||
} | ||
} | ||
} | ||
|
||
/** Checks if the given modifier has been seen before */ | ||
public boolean hasSeenModifier(Modifier modifier) { | ||
return seenModifiers.contains(modifier); | ||
} | ||
} | ||
|
||
/** Merger that runs all hooks */ | ||
record AllMerger(Collection<ModifierTraitHook> modules) implements ModifierTraitHook { | ||
@Override | ||
public void addTraits(ToolRebuildContext context, ModifierEntry modifier, TraitBuilder state, boolean firstEncounter) { | ||
for (ModifierTraitHook module : modules) { | ||
module.addTraits(context, modifier, state, firstEncounter); | ||
} | ||
} | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
src/main/java/slimeknights/tconstruct/library/modifiers/modules/ModifierTraitModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package slimeknights.tconstruct.library.modifiers.modules; | ||
|
||
import com.google.gson.JsonObject; | ||
import net.minecraft.network.FriendlyByteBuf; | ||
import net.minecraft.util.GsonHelper; | ||
import slimeknights.mantle.data.GenericLoaderRegistry.IGenericLoader; | ||
import slimeknights.tconstruct.library.modifiers.ModifierEntry; | ||
import slimeknights.tconstruct.library.modifiers.ModifierHook; | ||
import slimeknights.tconstruct.library.modifiers.ModifierId; | ||
import slimeknights.tconstruct.library.modifiers.TinkerHooks; | ||
import slimeknights.tconstruct.library.modifiers.hook.build.ModifierTraitHook; | ||
import slimeknights.tconstruct.library.tools.context.ToolRebuildContext; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* Module for a modifier to have a nested modifier as a trait. | ||
*/ | ||
public record ModifierTraitModule(ModifierEntry modifier, boolean fixedLevel) implements ModifierTraitHook, ModifierModule { | ||
private static final List<ModifierHook<?>> DEFAULT_HOOKS = List.of(TinkerHooks.MODIFIER_TRAITS); | ||
|
||
public ModifierTraitModule(ModifierId id, int level, boolean fixedLevel) { | ||
this(new ModifierEntry(id, level), fixedLevel); | ||
} | ||
|
||
@Override | ||
public void addTraits(ToolRebuildContext context, ModifierEntry self, TraitBuilder state, boolean firstEncounter) { | ||
if (fixedLevel) { | ||
// fixed levels do not need to add again if already added | ||
if (firstEncounter) { | ||
state.addEntry(this.modifier); | ||
} | ||
} else { | ||
// level of the trait is based on the level of the modifier, just multiply the two | ||
state.addEntry(this.modifier.withLevel(this.modifier.getLevel() * self.getLevel())); | ||
} | ||
} | ||
|
||
@Override | ||
public List<ModifierHook<?>> getDefaultHooks() { | ||
return DEFAULT_HOOKS; | ||
} | ||
|
||
@Override | ||
public IGenericLoader<? extends ModifierTraitModule> getLoader() { | ||
return LOADER; | ||
} | ||
|
||
public static final IGenericLoader<ModifierTraitModule> LOADER = new IGenericLoader<>() { | ||
@Override | ||
public ModifierTraitModule deserialize(JsonObject json) { | ||
ModifierEntry modifier = ModifierEntry.fromJson(json); | ||
boolean fixedLevel = GsonHelper.getAsBoolean(json, "fixed_level"); | ||
return new ModifierTraitModule(modifier, fixedLevel); | ||
} | ||
|
||
@Override | ||
public void serialize(ModifierTraitModule object, JsonObject json) { | ||
object.modifier.toJson(json); | ||
json.addProperty("fixed_level", object.fixedLevel); | ||
} | ||
|
||
@Override | ||
public ModifierTraitModule fromNetwork(FriendlyByteBuf buffer) { | ||
ModifierEntry modifier = ModifierEntry.read(buffer); | ||
boolean fixedLevel = buffer.readBoolean(); | ||
return new ModifierTraitModule(modifier, fixedLevel); | ||
} | ||
|
||
@Override | ||
public void toNetwork(ModifierTraitModule object, FriendlyByteBuf buffer) { | ||
object.modifier.write(buffer); | ||
buffer.writeBoolean(object.fixedLevel); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters