/
ReplaceBlockWalkerModule.java
147 lines (126 loc) · 6.75 KB
/
ReplaceBlockWalkerModule.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package slimeknights.tconstruct.library.modifiers.modules.armor;
import com.google.common.collect.ImmutableList;
import lombok.Setter;
import lombok.experimental.Accessors;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.ForgeEventFactory;
import slimeknights.mantle.data.loadable.common.BlockStateLoadable;
import slimeknights.mantle.data.loadable.record.RecordLoadable;
import slimeknights.mantle.data.predicate.IJsonPredicate;
import slimeknights.mantle.data.predicate.block.BlockPredicate;
import slimeknights.mantle.data.registry.GenericLoaderRegistry.IGenericLoader;
import slimeknights.tconstruct.library.json.IntRange;
import slimeknights.tconstruct.library.json.LevelingValue;
import slimeknights.tconstruct.library.json.predicate.tool.ToolContextPredicate;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.modules.ModifierModule;
import slimeknights.tconstruct.library.tools.nbt.IToolContext;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;
import slimeknights.tconstruct.tools.TinkerModifiers;
import java.util.List;
import static slimeknights.tconstruct.library.modifiers.modules.ModifierModuleCondition.MODIFIER_LEVEL;
/**
* Module to replace blocks with another block while walking.
* @param replacements List of replacements to perform
* @param radius Range to affect
* @param tool Tool condition
*/
public record ReplaceBlockWalkerModule(List<BlockReplacement> replacements, LevelingValue radius, IJsonPredicate<IToolContext> tool) implements ArmorWalkRadiusModule<Void>, ModifierModule {
public static final RecordLoadable<ReplaceBlockWalkerModule> LOADER = RecordLoadable.create(
BlockReplacement.LOADABLE.list().requiredField("replace", ReplaceBlockWalkerModule::replacements),
LevelingValue.LOADABLE.requiredField("radius", ReplaceBlockWalkerModule::radius),
ToolContextPredicate.LOADER.defaultField("tool", ReplaceBlockWalkerModule::tool),
ReplaceBlockWalkerModule::new);
@Override
public float getRadius(IToolStackView tool, ModifierEntry modifier) {
return radius.compute(modifier.getLevel() + tool.getModifierLevel(TinkerModifiers.expanded.getId()));
}
@Override
public void onWalk(IToolStackView tool, ModifierEntry modifier, LivingEntity living, BlockPos prevPos, BlockPos newPos) {
if (this.tool.matches(tool)) {
ArmorWalkRadiusModule.super.onWalk(tool, modifier, living, prevPos, newPos);
}
}
@Override
public void walkOn(IToolStackView tool, ModifierEntry entry, LivingEntity living, Level world, BlockPos target, MutableBlockPos mutable, Void context) {
if (world.isEmptyBlock(target)) {
mutable.set(target.getX(), target.getY() - 1, target.getZ());
int level = entry.getLevel();
for (BlockReplacement replacement : replacements) {
if (replacement.level.test(level)) {
// target handles matching any desired states like fluid level
BlockState state = replacement.state;
if (replacement.target.matches(world.getBlockState(mutable))
&& state.canSurvive(world, mutable) && world.isUnobstructed(state, mutable, CollisionContext.empty())
&& !ForgeEventFactory.onBlockPlace(living, BlockSnapshot.create(world.dimension(), world, mutable), Direction.UP)) {
world.setBlockAndUpdate(mutable, state);
world.scheduleTick(mutable, state.getBlock(), Mth.nextInt(living.getRandom(), 60, 120));
// stop after the first successful replacement, there is no reason to do multiple consecutive
break;
}
}
}
}
}
/** Represents a single replacement handled by this module */
private record BlockReplacement(IJsonPredicate<BlockState> target, BlockState state, IntRange level) {
public static final RecordLoadable<BlockReplacement> LOADABLE = RecordLoadable.create(
BlockPredicate.LOADER.defaultField("target", BlockReplacement::target),
BlockStateLoadable.DIFFERENCE.directField(BlockReplacement::state), // pulling from this object directly means the keys used are block and properties
MODIFIER_LEVEL.defaultField("modifier_level", BlockReplacement::level),
BlockReplacement::new);
}
@Override
public IGenericLoader<? extends ModifierModule> getLoader() {
return LOADER;
}
/* Builder */
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unused") // API
public static class Builder implements LevelingValue.Builder<ReplaceBlockWalkerModule> {
private final ImmutableList.Builder<BlockReplacement> replacements = ImmutableList.builder();
@Setter
@Accessors(fluent = true)
private IJsonPredicate<IToolContext> tool = ToolContextPredicate.ANY;
private Builder() {}
/** Replaces at the given level range */
private Builder replaceLevelRange(IJsonPredicate<BlockState> target, BlockState replacement, IntRange modifierLevel) {
this.replacements.add(new BlockReplacement(target, replacement, modifierLevel));
return this;
}
/** Adds the given replacement only at the given level range */
public Builder replaceLevelRange(IJsonPredicate<BlockState> target, BlockState replacement, int min, int max) {
return replaceLevelRange(target, replacement, MODIFIER_LEVEL.range(min, max));
}
/** Adds the given replacement only at the given level range */
public Builder replaceMinLevel(IJsonPredicate<BlockState> target, BlockState replacement, int max) {
return replaceLevelRange(target, replacement, MODIFIER_LEVEL.max(max));
}
/** Adds the given replacement only at the given level range */
public Builder replaceMaxLevel(IJsonPredicate<BlockState> target, BlockState replacement, int min) {
return replaceLevelRange(target, replacement, MODIFIER_LEVEL.min(min));
}
/** Adds the given replacement only at the given level range */
public Builder replaceAlways(IJsonPredicate<BlockState> target, BlockState replacement) {
return replaceLevelRange(target, replacement, MODIFIER_LEVEL);
}
@Override
public ReplaceBlockWalkerModule amount(float flat, float eachLevel) {
List<BlockReplacement> replacements = this.replacements.build();
if (replacements.isEmpty()) {
throw new IllegalStateException("Must have at least 1 replacement");
}
return new ReplaceBlockWalkerModule(replacements, new LevelingValue(flat, eachLevel), tool);
}
}
}