diff --git a/core/src/mindustry/ctype/UnlockableContent.java b/core/src/mindustry/ctype/UnlockableContent.java index a54ea6b8a2..17b0e6fe3d 100644 --- a/core/src/mindustry/ctype/UnlockableContent.java +++ b/core/src/mindustry/ctype/UnlockableContent.java @@ -24,6 +24,7 @@ import static mindustry.Vars.*; /** Base interface for an unlockable content type. */ public abstract class UnlockableContent extends MappableContent{ /** Stat storage for this content. Initialized on demand. */ + @NoPatch public Stats stats = new Stats(); /** Localized, formal name. Never null. Set to internal name if not found in bundle. */ public String localizedName; diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index 9379e4b9eb..60714b368e 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -442,6 +442,52 @@ public class ContentParser{ } }; + public void readBlockConsumers(Block block, JsonValue value){ + for(JsonValue child : value){ + switch(child.name){ + case "remove" -> { + String[] values = child.isString() ? new String[]{child.asString()} : child.asStringArray(); + for(String type : values){ + Class consumeType = resolve("Consume" + Strings.capitalize(type), Consume.class); + if(consumeType != Consume.class){ + block.removeConsumers(b -> consumeType.isAssignableFrom(b.getClass())); + }else{ + Log.warn("Unknown consumer type '@' (Class: @) in consume: remove.", type, "Consume" + Strings.capitalize(type)); + } + } + } + case "item" -> block.consumeItem(find(ContentType.item, child.asString())); + case "itemCharged" -> block.consume((Consume)parser.readValue(ConsumeItemCharged.class, child)); + case "itemFlammable" -> block.consume((Consume)parser.readValue(ConsumeItemFlammable.class, child)); + case "itemRadioactive" -> block.consume((Consume)parser.readValue(ConsumeItemRadioactive.class, child)); + case "itemExplosive" -> block.consume((Consume)parser.readValue(ConsumeItemExplosive.class, child)); + case "itemList" -> block.consume((Consume)parser.readValue(ConsumeItemList.class, child)); + case "itemExplode" -> block.consume((Consume)parser.readValue(ConsumeItemExplode.class, child)); + case "items" -> block.consume( + child.isArray() ? new ConsumeItems(parser.readValue(ItemStack[].class, child)) : + child.isString() ? new ConsumeItems(new ItemStack[]{parser.readValue(ItemStack.class, child)}) : + parser.readValue(ConsumeItems.class, child)); + + case "liquidFlammable" -> block.consume((Consume)parser.readValue(ConsumeLiquidFlammable.class, child)); + case "liquid" -> block.consume((Consume)parser.readValue(ConsumeLiquid.class, child)); + case "liquids" -> block.consume( + child.isArray() ? new ConsumeLiquids(parser.readValue(LiquidStack[].class, child)) : + parser.readValue(ConsumeLiquids.class, child)); + case "coolant" -> block.consume((Consume)parser.readValue(ConsumeCoolant.class, child)); + case "power" -> { + if(child.isNumber()){ + block.consumePower(child.asFloat()); + }else{ + block.consume((Consume)parser.readValue(ConsumePower.class, child)); + } + } + case "powerBuffered" -> block.consumePowerBuffered(child.asFloat()); + default -> throw new IllegalArgumentException("Unknown consumption type: '" + child.name + "' for block '" + block.name + "'."); + } + } + value.remove("consumes"); + } + private ObjectMap> parsers = ObjectMap.of( ContentType.block, (TypeParser)(mod, name, value) -> { readBundle(ContentType.block, name, value); @@ -463,46 +509,7 @@ public class ContentParser{ read(() -> { if(value.has("consumes") && value.get("consumes").isObject()){ - for(JsonValue child : value.get("consumes")){ - switch(child.name){ - case "remove" -> { - String[] values = child.isString() ? new String[]{child.asString()} : child.asStringArray(); - for(String type : values){ - Class consumeType = resolve("Consume" + Strings.capitalize(type), Consume.class); - if(consumeType != Consume.class){ - block.removeConsumers(b -> consumeType.isAssignableFrom(b.getClass())); - }else{ - Log.warn("Unknown consumer type '@' (Class: @) in consume: remove.", type, "Consume" + Strings.capitalize(type)); - } - } - } - case "item" -> block.consumeItem(find(ContentType.item, child.asString())); - case "itemCharged" -> block.consume((Consume)parser.readValue(ConsumeItemCharged.class, child)); - case "itemFlammable" -> block.consume((Consume)parser.readValue(ConsumeItemFlammable.class, child)); - case "itemRadioactive" -> block.consume((Consume)parser.readValue(ConsumeItemRadioactive.class, child)); - case "itemExplosive" -> block.consume((Consume)parser.readValue(ConsumeItemExplosive.class, child)); - case "itemList" -> block.consume((Consume)parser.readValue(ConsumeItemList.class, child)); - case "itemExplode" -> block.consume((Consume)parser.readValue(ConsumeItemExplode.class, child)); - case "items" -> block.consume(child.isArray() ? - new ConsumeItems(parser.readValue(ItemStack[].class, child)) : - parser.readValue(ConsumeItems.class, child)); - case "liquidFlammable" -> block.consume((Consume)parser.readValue(ConsumeLiquidFlammable.class, child)); - case "liquid" -> block.consume((Consume)parser.readValue(ConsumeLiquid.class, child)); - case "liquids" -> block.consume(child.isArray() ? - new ConsumeLiquids(parser.readValue(LiquidStack[].class, child)) : - parser.readValue(ConsumeLiquids.class, child)); - case "coolant" -> block.consume((Consume)parser.readValue(ConsumeCoolant.class, child)); - case "power" -> { - if(child.isNumber()){ - block.consumePower(child.asFloat()); - }else{ - block.consume((Consume)parser.readValue(ConsumePower.class, child)); - } - } - case "powerBuffered" -> block.consumePowerBuffered(child.asFloat()); - default -> throw new IllegalArgumentException("Unknown consumption type: '" + child.name + "' for block '" + block.name + "'."); - } - } + readBlockConsumers(block, value.get("consumes")); value.remove("consumes"); } diff --git a/core/src/mindustry/mod/ContentPatcher.java b/core/src/mindustry/mod/ContentPatcher.java index 6474c7b684..8e5e8762b0 100644 --- a/core/src/mindustry/mod/ContentPatcher.java +++ b/core/src/mindustry/mod/ContentPatcher.java @@ -9,12 +9,13 @@ import arc.util.serialization.Jval.*; import mindustry.*; import mindustry.core.*; import mindustry.ctype.*; +import mindustry.world.*; +import mindustry.world.consumers.*; import java.lang.reflect.*; import java.util.*; /** The current implementation is awful. Consider it a proof of concept. */ -//TODO block consumer support @SuppressWarnings("unchecked") public class ContentPatcher{ private static final Object root = new Object(); @@ -113,7 +114,7 @@ public class ContentPatcher{ for(int i = 0; i < path.length - 1; i++){ Object[] result = resolve(object, path[i], metadata); if(result == null){ - //TODO report error + warn("Failed to resolve @.@", object, path[i]); return; } object = result[0]; @@ -231,6 +232,23 @@ public class ContentPatcher{ } Reflect.set(fobj, fdata.field, fv); }, value, true); + }else if(value instanceof JsonValue jsv && object instanceof Block bl && jsv.isObject() && field.equals("consumes")){ + modifiedField(bl, "consumeBuilder", Reflect.>get(Block.class, bl, "consumeBuilder").copy()); + boolean hadItems = bl.hasItems, hadLiquids = bl.hasLiquids, hadPower = bl.hasPower, acceptedItems = bl.acceptsItems; + reset(() -> { + bl.hasItems = hadItems; + bl.hasLiquids = hadLiquids; + bl.hasPower = hadPower; + bl.acceptsItems = acceptedItems; + }); + after(bl::reinitializeConsumers); + + try{ + Vars.mods.getContentParser().readBlockConsumers(bl, jsv); + }catch(Throwable e){ + Log.err(e); + warn("Failed to read consumers for '@': @", bl, Strings.getSimpleMessage(e)); + } }else{ warn("Unknown field: '@' for class '@'", field, actualType.getSimpleName()); } @@ -241,7 +259,7 @@ public class ContentPatcher{ Object prevValue = getter.get(); if(value instanceof JsonValue jsv){ //setting values from object - if(prevValue == null || !jsv.isObject() || jsv.has("type")){ + if(prevValue == null || !jsv.isObject() || jsv.has("type")){ if(UnlockableContent.class.isAssignableFrom(metadata.type) && (jsv.isObject())){ warn("New content must not be instantiated: @", jsv); return; diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 5d56afab8c..3c41d849e7 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -1348,6 +1348,21 @@ public class Block extends UnlockableContent implements Senseable{ } } + public void reinitializeConsumers(){ + consumers = consumeBuilder.toArray(Consume.class); + consPower = (ConsumePower)Structs.find(consumers, c -> c instanceof ConsumePower); + optionalConsumers = consumeBuilder.select(consume -> consume.optional && !consume.ignore()).toArray(Consume.class); + nonOptionalConsumers = consumeBuilder.select(consume -> !consume.optional && !consume.ignore()).toArray(Consume.class); + updateConsumers = consumeBuilder.select(consume -> consume.update && !consume.ignore()).toArray(Consume.class); + hasConsumers = consumers.length > 0; + itemFilter = new boolean[content.items().size]; + liquidFilter = new boolean[content.liquids().size]; + + for(Consume cons : consumers){ + cons.apply(this); + } + } + @Override public void load(){ super.load();