-
Notifications
You must be signed in to change notification settings - Fork 755
/
TankModule.java
231 lines (203 loc) · 8.97 KB
/
TankModule.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package slimeknights.tconstruct.library.modifiers.modules.fluid;
import lombok.Getter;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.TooltipFlag;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import slimeknights.mantle.client.TooltipKey;
import slimeknights.mantle.data.loadable.Loadables;
import slimeknights.mantle.data.loadable.record.RecordLoadable;
import slimeknights.mantle.data.registry.GenericLoaderRegistry.IGenericLoader;
import slimeknights.tconstruct.TConstruct;
import slimeknights.tconstruct.library.modifiers.Modifier;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.ModifierHooks;
import slimeknights.tconstruct.library.modifiers.ModifierId;
import slimeknights.tconstruct.library.modifiers.hook.display.TooltipModifierHook;
import slimeknights.tconstruct.library.module.HookProvider;
import slimeknights.tconstruct.library.module.ModuleHook;
import slimeknights.tconstruct.library.tools.capability.ToolFluidCapability;
import slimeknights.tconstruct.library.tools.capability.ToolFluidCapability.FluidModifierHook;
import slimeknights.tconstruct.library.tools.nbt.IToolContext;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;
import slimeknights.tconstruct.library.tools.nbt.ModDataNBT;
import javax.annotation.Nullable;
import java.util.List;
import java.util.function.BiFunction;
/**
* Module granting a tool a tank.
* TODO: cleanup fluid keys, ideally JSON only would specify one key
*/
public class TankModule extends TankCapacityModule implements FluidModifierHook, TooltipModifierHook {
private static final String FILLED_KEY = TConstruct.makeTranslationKey("modifier", "tank.filled");
private static final String CAPACITY_KEY = TConstruct.makeTranslationKey("modifier", "tank.capacity");
private static final List<ModuleHook<?>> DEFAULT_HOOKS = HookProvider.<TankModule>defaultHooks(ModifierHooks.VOLATILE_DATA, ToolFluidCapability.HOOK, ModifierHooks.TOOLTIP);
/** Default key for owner */
public static final ResourceLocation DEFAULT_OWNER_KEY = TConstruct.getResource("tank_owner");
/** Default key for fluid */
public static final ResourceLocation DEFAULT_FLUID_KEY = TConstruct.getResource("tank_fluid");
/** Loader instance */
public static final RecordLoadable<TankModule> LOADER = RecordLoadable.create(
CAPACITY_KEY_FIELD, CAPACITY_FIELD, SCALE_CAPACITY_FIELD,
Loadables.RESOURCE_LOCATION.defaultField("fluid_key", DEFAULT_FLUID_KEY, TankModule::getFluidKey),
Loadables.RESOURCE_LOCATION.defaultField("owner_key", DEFAULT_OWNER_KEY, TankModule::getOwnerKey),
TankModule::new);
/** Helper function to parse a fluid from NBT */
public static final BiFunction<CompoundTag, String, FluidStack> PARSE_FLUID = (nbt, key) -> FluidStack.loadFluidStackFromNBT(nbt.getCompound(key));
/** Volatile NBT string indicating which modifier is in charge of logic for the one tank */
@Getter
private final ResourceLocation ownerKey;
/** Persistent NBT compound containing the fluid in the tank */
@Getter
private final ResourceLocation fluidKey;
public TankModule(ResourceLocation capacityKey, int capacity, boolean scaleCapacity, ResourceLocation fluidKey, ResourceLocation ownerKey) {
super(capacityKey, capacity, scaleCapacity);
this.ownerKey = ownerKey;
this.fluidKey = fluidKey;
}
public TankModule(int capacity, boolean scaleCapacity) {
this(DEFAULT_CAPACITY_KEY, capacity, scaleCapacity, DEFAULT_FLUID_KEY, DEFAULT_OWNER_KEY);
}
@Override
public void addTooltip(IToolStackView tool, ModifierEntry modifier, @Nullable Player player, List<Component> tooltip, TooltipKey tooltipKey, TooltipFlag tooltipFlag) {
if (isOwner(tool, modifier.getId())) {
FluidStack current = getFluid(tool);
if (!current.isEmpty()) {
tooltip.add(Component.translatable(FILLED_KEY, current.getAmount(), current.getDisplayName()));
}
tooltip.add(Component.translatable(CAPACITY_KEY, getCapacity(tool)));
}
}
/* Properties */
/** Checks if the given modifier is the owner of the tank */
public boolean isOwner(IToolContext tool, ModifierId modifier) {
ResourceLocation key = getOwnerKey();
if (key == null) {
return true;
}
return modifier.toString().equals(tool.getVolatileData().getString(key));
}
// TODO: may be worth separating tanks vs unique tanks, unique tanks are used for drain/fill hooks while total tanks for anyone interacting directly such as modifiers
@Override
public int getTanks(IToolContext tool, Modifier modifier) {
return isOwner(tool, modifier.getId()) ? 1 : 0;
}
@Override
public int getTankCapacity(IToolStackView tool, ModifierEntry modifier, int tank) {
return getCapacity(tool);
}
@Override
public void addVolatileData(IToolContext context, ModifierEntry modifier, ModDataNBT volatileData) {
super.addVolatileData(context, modifier, volatileData);
ResourceLocation ownerKey = getOwnerKey();
if (!volatileData.contains(ownerKey, Tag.TAG_STRING)) {
volatileData.putString(ownerKey, modifier.getId().toString());
}
ToolFluidCapability.addTanks(context, modifier.getModifier(), volatileData, this);
}
/* Fluid setting */
/** Gets the fluid in the tank */
public FluidStack getFluid(IToolStackView tool) {
return tool.getPersistentData().get(getFluidKey(), PARSE_FLUID);
}
/** Sets the fluid in the tank */
public FluidStack setFluid(IToolStackView tool, FluidStack fluid) {
if (fluid.isEmpty()) {
tool.getPersistentData().remove(getFluidKey());
return fluid;
}
int capacity = getCapacity(tool);
if (fluid.getAmount() > capacity) {
fluid.setAmount(capacity);
}
tool.getPersistentData().put(getFluidKey(), fluid.writeToNBT(new CompoundTag()));
return fluid;
}
@Override
public FluidStack getFluidInTank(IToolStackView tool, ModifierEntry modifier, int tank) {
return getFluid(tool);
}
/* Filling and draining */
@Override
public int fill(IToolStackView tool, ModifierEntry modifier, FluidStack resource, FluidAction action) {
// make sure this modifier is in charge of the tank, that is first come first serve
if (!resource.isEmpty() && isOwner(tool, modifier.getId())) {
// if empty, just directly fill, setFluid will check capacity
FluidStack current = getFluid(tool);
int capacity = getCapacity(tool);
if (current.isEmpty()) {
if (action.execute()) {
setFluid(tool, resource);
}
return Math.min(resource.getAmount(), capacity);
}
// if the fluid matches and we have space, update
if (current.getAmount() < capacity && current.isFluidEqual(resource)) {
int filled = Math.min(resource.getAmount(), capacity - current.getAmount());
if (filled > 0 && action.execute()) {
current.grow(filled);
setFluid(tool, current);
}
return filled;
}
}
return 0;
}
@Override
public FluidStack drain(IToolStackView tool, ModifierEntry modifier, FluidStack resource, FluidAction action) {
if (!resource.isEmpty() && isOwner(tool, modifier.getId())) {
// ensure we have something and it matches the request
FluidStack current = getFluid(tool);
if (!current.isEmpty() && current.isFluidEqual(resource)) {
// create the drained stack
FluidStack drained = new FluidStack(current, Math.min(current.getAmount(), resource.getAmount()));
// if executing, removing it
if (action.execute()) {
if (drained.getAmount() == current.getAmount()) {
setFluid(tool, FluidStack.EMPTY);
} else {
current.shrink(drained.getAmount());
setFluid(tool, current);
}
}
return drained;
}
}
return FluidStack.EMPTY;
}
@Override
public FluidStack drain(IToolStackView tool, ModifierEntry modifier, int maxDrain, FluidAction action) {
if (maxDrain > 0 && isOwner(tool, modifier.getId())) {
// ensure we have something and it matches the request
FluidStack current = getFluid(tool);
if (!current.isEmpty()) {
// create the drained stack
FluidStack drained = new FluidStack(current, Math.min(current.getAmount(), maxDrain));
// if executing, removing it
if (action.execute()) {
if (drained.getAmount() == current.getAmount()) {
setFluid(tool, FluidStack.EMPTY);
} else {
current.shrink(drained.getAmount());
setFluid(tool, current);
}
}
return drained;
}
}
return FluidStack.EMPTY;
}
/* Module logic */
@Override
public List<ModuleHook<?>> getDefaultHooks() {
return DEFAULT_HOOKS;
}
@Override
public IGenericLoader<? extends TankModule> getLoader() {
return LOADER;
}
}