-
Notifications
You must be signed in to change notification settings - Fork 752
/
ModifierNBT.java
240 lines (218 loc) · 7.43 KB
/
ModifierNBT.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
232
233
234
235
236
237
238
239
240
package slimeknights.tconstruct.library.tools.nbt;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import slimeknights.tconstruct.library.modifiers.Modifier;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.ModifierId;
import slimeknights.tconstruct.library.modifiers.ModifierManager;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* NBT object containing all current modifiers
*/
@EqualsAndHashCode
@RequiredArgsConstructor
public class ModifierNBT {
public static final String TAG_MODIFIER = "name";
public static final String TAG_LEVEL = "level";
/** Instance containing no modifiers */
static final ModifierNBT EMPTY = new ModifierNBT(Collections.emptyList());
/** Sorted list of modifiers */
@Getter
private final List<ModifierEntry> modifiers;
/**
* Checks if the NBT has no modifiers
* @return True if there are no modifiers
*/
public boolean isEmpty() {
return modifiers.isEmpty();
}
/**
* Gets the level of a modifier
* @param modifier Modifier to check
* @return Modifier level, or 0 if modifier is missing
*/
public int getLevel(ModifierId modifier) {
for (ModifierEntry entry : modifiers) {
if (entry.matches(modifier)) {
return entry.getLevel();
}
}
return 0;
}
/**
* Creates a copy of this NBT with the given modifier added. Result will be unsorted
* Do not use if you need to make multiple additions, use {@link ModifierNBT.Builder}
* @param modifier Modifier
* @param level Levels of the modifier to add
* @return Instance with the given modifier
*/
public ModifierNBT withModifier(ModifierId modifier, int level) {
if (level <= 0) {
throw new IllegalArgumentException("Invalid level, must be above zero");
}
// rather than using the builder, just use a raw list builder
// easier for adding a single entry, and the cases that call this method don't care about sorting
ImmutableList.Builder<ModifierEntry> builder = ImmutableList.builder();
boolean found = false;
for (ModifierEntry entry : this.modifiers) {
// first match increases the level
// shouldn't be a second match (all the methods are protected), but just in case we prevent modifier duplication
if (!found && entry.matches(modifier)) {
builder.add(entry.withLevel(entry.getLevel() + level));
found = true;
} else {
builder.add(entry);
}
}
// if no matching modifier, create a new entry
if (!found) {
builder.add(new ModifierEntry(modifier, level));
}
return new ModifierNBT(builder.build());
}
/**
* Creates a copy of this NBT without the given modifier
* @param modifier Modifier to remove
* @param level Level to remove
* @return ModifierNBT without the given modifier
*/
public ModifierNBT withoutModifier(ModifierId modifier, int level) {
if (level <= 0) {
throw new IllegalArgumentException("Invalid level, must be above zero");
}
// rather than using the builder, just use a raw list builder
// easier for adding a single entry, and the cases that call this method don't care about sorting
ImmutableList.Builder<ModifierEntry> builder = ImmutableList.builder();
for (ModifierEntry entry : this.modifiers) {
if (entry.matches(modifier) && level > 0) {
if (entry.getLevel() > level) {
builder.add(entry.withLevel(entry.getLevel() - level));
level = 0;
} else {
level -= entry.getLevel();
}
} else {
builder.add(entry);
}
}
return new ModifierNBT(builder.build());
}
/** Re-adds the modifier list from NBT */
public static ModifierNBT readFromNBT(@Nullable Tag inbt) {
if (inbt == null || inbt.getId() != Tag.TAG_LIST) {
return EMPTY;
}
ListTag listNBT = (ListTag)inbt;
if (listNBT.getElementType() != Tag.TAG_COMPOUND) {
return EMPTY;
}
ImmutableList.Builder<ModifierEntry> builder = ImmutableList.builder();
for (int i = 0; i < listNBT.size(); i++) {
CompoundTag tag = listNBT.getCompound(i);
if (tag.contains(TAG_MODIFIER) && tag.contains(TAG_LEVEL)) {
ModifierId id = ModifierId.tryParse(tag.getString(TAG_MODIFIER));
int level = tag.getInt(TAG_LEVEL);
if (id != null && level > 0) {
builder.add(new ModifierEntry(id, level));
}
}
}
return new ModifierNBT(builder.build());
}
/** Writes these modifiers to NBT */
public ListTag serializeToNBT() {
ListTag list = new ListTag();
for (ModifierEntry entry : modifiers) {
CompoundTag tag = new CompoundTag();
tag.putString(TAG_MODIFIER, entry.getId().toString());
tag.putShort(TAG_LEVEL, (short)entry.getLevel());
list.add(tag);
}
return list;
}
/**
* Creates a new builder for modifier NBT
* @return Builder instance
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder class for creating a modifier list with multiple additions. Builder results will be sorted
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Builder {
/** Intentionally using modifiers to ensure they are resolved */
private final Map<Modifier, Integer> modifiers = new LinkedHashMap<>();
/**
* Adds a single modifier to the builder
* @param modifier Modifier
* @param level Modifier level
* @return Builder instance
*/
public Builder add(Modifier modifier, int level) {
if (level <= 0) {
throw new IllegalArgumentException("Level must be above 0");
}
// skip if its the empty modifier, no sense tracking
if (modifier != ModifierManager.INSTANCE.getDefaultValue()) {
Integer value = modifiers.get(modifier);
if (value != null) {
level += value;
}
modifiers.put(modifier, level);
}
return this;
}
/**
* Adds an entry to the builder
* @param entry Entry to add
* @return Builder instance
*/
public Builder add(ModifierEntry entry) {
add(entry.getModifier(), entry.getLevel());
return this;
}
/**
* Adds an entry to the builder
* @param entries Entries to add
* @return Builder instance
*/
public Builder add(List<ModifierEntry> entries) {
for (ModifierEntry entry : entries) {
add(entry);
}
return this;
}
/**
* Adds all modifiers from the given modifier NBT
* @param nbt NBT object
* @return Builder instance
*/
public Builder add(ModifierNBT nbt) {
add(nbt.getModifiers());
return this;
}
/** Builds the NBT */
public ModifierNBT build() {
// converts the map into a list of entries, priority sorted
// note priority is negated so higher numbers go first
List<ModifierEntry> list = modifiers.entrySet().stream()
.map(entry -> new ModifierEntry(entry.getKey(), entry.getValue()))
.sorted().collect(Collectors.toList());
return new ModifierNBT(ImmutableList.copyOf(list));
}
}
}