diff --git a/src/main/java/net/minecraftforge/client/model/generators/MultiPartBlockStateBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/MultiPartBlockStateBuilder.java index a10cb4a5bc..a3c33c9104 100644 --- a/src/main/java/net/minecraftforge/client/model/generators/MultiPartBlockStateBuilder.java +++ b/src/main/java/net/minecraftforge/client/model/generators/MultiPartBlockStateBuilder.java @@ -29,6 +29,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import net.minecraft.block.Block; @@ -75,11 +76,15 @@ public final class MultiPartBlockStateBuilder implements IGeneratedBlockstate { public BlockStateProvider.ConfiguredModelList models; public boolean useOr; public final Multimap, Comparable> conditions = MultimapBuilder.linkedHashKeys().arrayListValues().build(); + public final List nestedConditionGroups = new ArrayList<>(); PartBuilder(BlockStateProvider.ConfiguredModelList models) { this.models = models; } + /** + * Makes this part get applied if any of the conditions/condition groups are true, instead of all of them needing to be true. + */ public PartBuilder useOr() { this.useOr = true; return this; @@ -99,6 +104,7 @@ public final class MultiPartBlockStateBuilder implements IGeneratedBlockstate { * @throws IllegalArgumentException if {@code prop} has already been configured * @throws IllegalArgumentException if {@code prop} is not applicable to the * current block's state + * @throws IllegalStateException if {@code !nestedConditionGroups.isEmpty()} */ @SafeVarargs public final > PartBuilder condition(Property prop, T... values) { @@ -107,31 +113,33 @@ public final class MultiPartBlockStateBuilder implements IGeneratedBlockstate { Preconditions.checkArgument(values.length > 0, "Value list must not be empty"); Preconditions.checkArgument(!conditions.containsKey(prop), "Cannot set condition for property \"%s\" more than once", prop.getName()); Preconditions.checkArgument(canApplyTo(owner), "IProperty %s is not valid for the block %s", prop, owner); + Preconditions.checkState(nestedConditionGroups.isEmpty(), "Can't have normal conditions if there are already nested condition groups"); this.conditions.putAll(prop, Arrays.asList(values)); return this; } + /** + * Allows having nested groups of conditions if there are not any normal conditions. + * @throws IllegalStateException if {@code !conditions.isEmpty()} + */ + public final ConditionGroup nestedGroup() + { + Preconditions.checkState(conditions.isEmpty(), "Can't have nested condition groups if there are already normal conditions"); + ConditionGroup group = new ConditionGroup(); + this.nestedConditionGroups.add(group); + return group; + } + public MultiPartBlockStateBuilder end() { return MultiPartBlockStateBuilder.this; } JsonObject toJson() { JsonObject out = new JsonObject(); if (!conditions.isEmpty()) { - JsonObject when = new JsonObject(); - for (Entry, Collection>> e : conditions.asMap().entrySet()) { - StringBuilder activeString = new StringBuilder(); - for (Comparable val : e.getValue()) { - if (activeString.length() > 0) - activeString.append("|"); - activeString.append(((Property) e.getKey()).getName(val)); - } - when.addProperty(e.getKey().getName(), activeString.toString()); - } - if (useOr) { - JsonObject innerWhen = when; - when = new JsonObject(); - when.add("OR", innerWhen); - } - out.add("when", when); + out.add("when", MultiPartBlockStateBuilder.toJson(this.conditions, this.useOr)); + } + else if (!nestedConditionGroups.isEmpty()) + { + out.add("when", MultiPartBlockStateBuilder.toJson(this.nestedConditionGroups, this.useOr)); } out.add("apply", models.toJSON()); return out; @@ -140,5 +148,142 @@ public final class MultiPartBlockStateBuilder implements IGeneratedBlockstate { public boolean canApplyTo(Block b) { return b.getStateDefinition().getProperties().containsAll(conditions.keySet()); } + + public class ConditionGroup + { + public final Multimap, Comparable> conditions = MultimapBuilder.linkedHashKeys().arrayListValues().build(); + public final List nestedConditionGroups = new ArrayList<>(); + private ConditionGroup parent = null; + public boolean useOr; + + /** + * Set a condition for this part, which consists of a property and a set of + * valid values. Can be called multiple times for multiple different properties. + * + * @param the type of the property value + * @param prop the property + * @param values a set of valid values + * @return this builder + * @throws NullPointerException if {@code prop} is {@code null} + * @throws NullPointerException if {@code values} is {@code null} + * @throws IllegalArgumentException if {@code values} is empty + * @throws IllegalArgumentException if {@code prop} has already been configured + * @throws IllegalArgumentException if {@code prop} is not applicable to the + * current block's state + * @throws IllegalStateException if {@code !nestedConditionGroups.isEmpty()} + */ + @SafeVarargs + public final > ConditionGroup condition(Property prop, T... values) + { + Preconditions.checkNotNull(prop, "Property must not be null"); + Preconditions.checkNotNull(values, "Value list must not be null"); + Preconditions.checkArgument(values.length > 0, "Value list must not be empty"); + Preconditions.checkArgument(!conditions.containsKey(prop), "Cannot set condition for property \"%s\" more than once", prop.getName()); + Preconditions.checkArgument(canApplyTo(owner), "IProperty %s is not valid for the block %s", prop, owner); + Preconditions.checkState(nestedConditionGroups.isEmpty(), "Can't have normal conditions if there are already nested condition groups"); + this.conditions.putAll(prop, Arrays.asList(values)); + return this; + } + + /** + * Allows having nested groups of conditions if there are not any normal conditions. + * @throws IllegalStateException if {@code !conditions.isEmpty()} + */ + public ConditionGroup nestedGroup() + { + Preconditions.checkState(conditions.isEmpty(), "Can't have nested condition groups if there are already normal conditions"); + ConditionGroup group = new ConditionGroup(); + group.parent = this; + this.nestedConditionGroups.add(group); + return group; + } + + /** + * Ends this nested condition group and returns the parent condition group + * + * @throws IllegalStateException If this is not a nested condition group + */ + public ConditionGroup endNestedGroup() + { + if (parent == null) + throw new IllegalStateException("This condition group is not nested, use end() instead"); + return parent; + } + + /** + * Ends this condition group and returns the part builder + * + * @throws IllegalStateException If this is a nested condition group + */ + public MultiPartBlockStateBuilder.PartBuilder end() + { + if (this.parent != null) + throw new IllegalStateException("This is a nested condition group, use endNestedGroup() instead"); + return MultiPartBlockStateBuilder.PartBuilder.this; + } + + /** + * Makes this part get applied if any of the conditions/condition groups are true, instead of all of them needing to be true. + */ + public ConditionGroup useOr() + { + this.useOr = true; + return this; + } + + JsonObject toJson() + { + if (!this.conditions.isEmpty()) + { + return MultiPartBlockStateBuilder.toJson(this.conditions, this.useOr); + } + else if (!this.nestedConditionGroups.isEmpty()) + { + return MultiPartBlockStateBuilder.toJson(this.nestedConditionGroups, this.useOr); + } + return new JsonObject(); + } + } + } + + private static JsonObject toJson(List conditions, boolean useOr) + { + JsonObject groupJson = new JsonObject(); + JsonArray innerGroupJson = new JsonArray(); + groupJson.add(useOr ? "OR" : "AND", innerGroupJson); + for (PartBuilder.ConditionGroup group : conditions) + { + innerGroupJson.add(group.toJson()); + } + return groupJson; + } + + private static JsonObject toJson(Multimap, Comparable> conditions, boolean useOr) + { + JsonObject groupJson = new JsonObject(); + for (Entry, Collection>> e : conditions.asMap().entrySet()) + { + StringBuilder activeString = new StringBuilder(); + for (Comparable val : e.getValue()) + { + if (activeString.length() > 0) + activeString.append("|"); + activeString.append(((Property) e.getKey()).getName(val)); + } + groupJson.addProperty(e.getKey().getName(), activeString.toString()); + } + if (useOr) + { + JsonArray innerWhen = new JsonArray(); + for (Entry entry : groupJson.entrySet()) + { + JsonObject obj = new JsonObject(); + obj.add(entry.getKey(), entry.getValue()); + innerWhen.add(obj); + } + groupJson = new JsonObject(); + groupJson.add("OR", innerWhen); + } + return groupJson; } }