-
Notifications
You must be signed in to change notification settings - Fork 753
/
MaterialStatProvider.java
154 lines (139 loc) · 5.83 KB
/
MaterialStatProvider.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
package slimeknights.tconstruct.library.tools.stat;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minecraft.resources.ResourceLocation;
import slimeknights.mantle.data.loadable.ErrorFactory;
import slimeknights.mantle.registration.object.IdAwareObject;
import slimeknights.tconstruct.library.materials.MaterialRegistry;
import slimeknights.tconstruct.library.materials.definition.MaterialId;
import slimeknights.tconstruct.library.materials.stats.IMaterialStats;
import slimeknights.tconstruct.library.materials.stats.MaterialStatsId;
import slimeknights.tconstruct.library.tools.definition.module.material.ToolMaterialHook.WeightedStatType;
import slimeknights.tconstruct.library.tools.nbt.MaterialNBT;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Extendable utilities for a material stat builder.
*/
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class MaterialStatProvider implements IdAwareObject {
@Getter
private final ResourceLocation id;
private final Set<MaterialStatsId> requiredType;
private final Set<MaterialStatsId> otherTypes;
/**
* Called after bonuses are processed to set the unique stats for this builder.
* @param builder Stats builder
*/
public abstract void addStats(List<WeightedStatType> statTypes, MaterialNBT materials, ModifierStatsBuilder builder);
/* Helpers */
/**
* Fetches the given stat from the material, getting the default stats if missing
* @param material Material type
* @param statsId Stat type
* @param <T> Stat type
* @return Stat, or default if the part type accepts it, null if the part type does not
*/
@Nullable
public static <T extends IMaterialStats> T fetchStatsOrDefault(MaterialId material, MaterialStatsId statsId) {
return MaterialRegistry.getInstance().<T>getMaterialStats(material, statsId)
.orElseGet(() -> MaterialRegistry.getInstance().getDefaultStats(statsId));
}
/**
* Gets a list of all stats for the given part type
* @param statsId Stat type
* @param materials Materials list
* @param statTypes List of required components, filters stat types
* @param <T> Type of stats
* @return List of stats
*/
public static <T extends IMaterialStats> List<T> listOfCompatibleWith(MaterialStatsId statsId, MaterialNBT materials, List<WeightedStatType> statTypes) {
ImmutableList.Builder<T> builder = ImmutableList.builder();
// iterating both lists at once, precondition that they have the same size
int size = statTypes.size();
for (int i = 0; i < size; i++) {
// ensure stat type is valid
WeightedStatType statType = statTypes.get(i);
if (statType.stat().equals(statsId)) {
T stats = fetchStatsOrDefault(materials.get(i).getId(), statType.stat());
if (stats != null) {
// add a copy of the stat once per weight, lazy way to do weighting
for (int w = 0; w < statType.weight(); w++) {
builder.add(stats);
}
}
}
}
return builder.build();
}
/**
* Gets the average value from a list of stat types
* @param stats Stat list
* @param statGetter Function to get the value
* @param <T> Material type
* @return Average value
*/
public static <T extends IMaterialStats> float getAverageValue(List<T> stats, Function<T, ? extends Number> statGetter) {
return getAverageValue(stats, statGetter, 0);
}
/**
* Gets the average value from a list of stat types
* @param stats Stat list
* @param statGetter Function to get the value
* @param missingValue Default value to use for missing stats
* @param <T> Material type
* @return Average value
*/
public static <T extends IMaterialStats, N extends Number> float getAverageValue(List<T> stats, Function<T, N> statGetter, double missingValue) {
return (float)stats.stream()
.mapToDouble(value -> statGetter.apply(value).doubleValue())
.average()
.orElse(missingValue);
}
/**
* Gets the average value from a list of stat types
* @param stats Stat list
* @param statGetter Function to get the value
* @param <T> Material type
* @return Average value
*/
public static <T extends IMaterialStats, N extends Number> float getTotalValue(List<T> stats, Function<T, N> statGetter) {
return (float)stats.stream()
.mapToDouble(value -> statGetter.apply(value).doubleValue())
.sum();
}
/* JSON parsing */
/** Joins the stats by commas */
private static String join(Stream<MaterialStatsId> stats) {
return stats.map(MaterialStatsId::toString).collect(Collectors.joining(", "));
}
/**
* Validates that the given parts meet the requirements
* @param stats Stats to validate
* @param error Error factory
*/
public void validate(List<WeightedStatType> stats, ErrorFactory error) {
// have a required type, make sure it exists and no unexpected types exist
if (stats.isEmpty()) {
throw error.create(id + " must have at least one tool part");
}
boolean foundHead = false;
for (WeightedStatType weightedStat : stats) {
MaterialStatsId statType = weightedStat.stat();
if (requiredType.contains(statType)) {
foundHead = true;
} else if (!otherTypes.contains(statType)) {
throw new IllegalStateException(id + " does not support type " + statType + ", must be one of: " + join(Stream.concat(requiredType.stream(), otherTypes.stream())));
}
}
if (!foundHead) {
throw new IllegalStateException(id + " must use at least one of " + join(requiredType.stream()) + " part");
}
}
}