[1.16] Add missing Multipart Blockstate Builder feature: Nested condition groups (#7677)

This commit is contained in:
mrp-v2 authored and GitHub committed 2021-04-22 22:55:18 -07:00
1 parent 9d0d249eaf
commit 7bf02e7dcd
1 file changed
+161 -16
@@ -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<Property<?>, Comparable<?>> conditions = MultimapBuilder.linkedHashKeys().arrayListValues().build();
public final List<ConditionGroup> 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 <T extends Comparable<T>> PartBuilder condition(Property<T> 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<Property<?>, Collection<Comparable<?>>> 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<Property<?>, Comparable<?>> conditions = MultimapBuilder.linkedHashKeys().arrayListValues().build();
public final List<ConditionGroup> 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 <T> 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 <T extends Comparable<T>> ConditionGroup condition(Property<T> 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<PartBuilder.ConditionGroup> 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<Property<?>, Comparable<?>> conditions, boolean useOr)
{
JsonObject groupJson = new JsonObject();
for (Entry<Property<?>, Collection<Comparable<?>>> 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<String, JsonElement> 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;
}
}