diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 6db70b759e..a649edf7ad 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -101,6 +101,7 @@ coreattack = < Core is under attack! > nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent database = Core Database database.button = Database +viewfields = View Content Fields savegame = Save Game loadgame = Load Game joingame = Join Game diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index 7f5c86d086..a7c4038a51 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -448,7 +448,7 @@ public class ContentParser{ T two = (T)Vars.content.getByName(ctype, jsonData.asString()); if(two != null) return two; - throw new IllegalArgumentException("\"" + jsonData.name + "\": No " + ctype + " found with name '" + jsonData.asString() + "'.\nMake sure '" + jsonData.asString() + "' is spelled correctly, and that it really exists!\nThis may also occur because its file failed to parse."); + throw new IllegalArgumentException((jsonData.name == null ? "" : "\"" + jsonData.name + "\": ") + "No " + ctype + " found with name '" + jsonData.asString() + "'.\nMake sure '" + jsonData.asString() + "' is spelled correctly, and that it really exists!\nThis may also occur because its file failed to parse."); } } diff --git a/core/src/mindustry/mod/ContentPatcher.java b/core/src/mindustry/mod/ContentPatcher.java index 7e535891dd..ea4e1c5917 100644 --- a/core/src/mindustry/mod/ContentPatcher.java +++ b/core/src/mindustry/mod/ContentPatcher.java @@ -75,6 +75,7 @@ public class ContentPatcher{ try{ JsonValue value = parser.getJson().fromJson(null, Jval.read(patch).toString(Jformat.plain)); + set.json = value; currentlyApplying = set; set.name = value.getString("name", ""); @@ -381,9 +382,13 @@ public class ContentPatcher{ }else{ //assign each field manually var childFields = parser.getJson().getFields(prevValue.getClass().isAnonymousClass() ? prevValue.getClass().getSuperclass() : prevValue.getClass()); + for(var child : jsv){ if(child.name != null){ - assign(prevValue, child.name, child, !childFields.containsKey(child.name) ? null : new FieldData(childFields.get(child.name)), object, field); + assign(prevValue, child.name, child, + metadata != null && metadata.type == ObjectMap.class ? metadata : + !childFields.containsKey(child.name) ? null : + new FieldData(childFields.get(child.name)), object, field); } } } @@ -541,6 +546,15 @@ public class ContentPatcher{ public FieldData(FieldMetadata data){ this(data.field.getType(), data.elementType, data.keyType); } + + @Override + public String toString(){ + return "FieldData{" + + "type=" + type + + ", elementType=" + elementType + + ", keyType=" + keyType + + '}'; + } } private static class PatchRecord{ diff --git a/core/src/mindustry/ui/dialogs/ContentInfoDialog.java b/core/src/mindustry/ui/dialogs/ContentInfoDialog.java index e2100b2e50..d19bebacb0 100644 --- a/core/src/mindustry/ui/dialogs/ContentInfoDialog.java +++ b/core/src/mindustry/ui/dialogs/ContentInfoDialog.java @@ -10,6 +10,7 @@ import mindustry.ctype.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.input.*; +import mindustry.ui.*; import mindustry.world.meta.*; import static arc.Core.*; @@ -93,6 +94,15 @@ public class ContentInfoDialog extends BaseDialog{ table.row(); } + if(settings.getBool("console")){ + table.button("@viewfields", Icon.link, Styles.grayt, () -> { + Class contentClass = content.getClass(); + if(contentClass.isAnonymousClass()) contentClass = contentClass.getSuperclass(); + + Core.app.openURI("https://mindustrygame.github.io/wiki/Modding%20Classes/" + contentClass.getSimpleName()); + }).margin(8f).pad(4f).size(300f, 50f).row(); + } + content.displayExtra(table); ScrollPane pane = new ScrollPane(table); diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 566532dec5..88ffdba888 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -86,6 +86,7 @@ public class Block extends UnlockableContent implements Senseable{ /** if true, {@link #buildEditorConfig(Table)} will be called for configuring this block in the editor. */ public boolean editorConfigurable; /** the last configuration value applied to this block. */ + @NoPatch public @Nullable Object lastConfig; /** whether to save the last config and apply it to newly placed blocks */ public boolean saveConfig = false; diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 431edcdd4f..f64b5bea6a 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -467,6 +467,13 @@ public class ServerControl implements ApplicationListener{ info("Map directory: &fi@", customMapDirectory.file().getAbsoluteFile().toString()); }); + handler.register("reloadpatches", "Reload all patch files from disk.", arg -> { + loadPatchFiles(); + if(contentPatches.isEmpty()){ + err("No valid content patch files found."); + } + }); + handler.register("reloadmaps", "Reload all maps from disk.", arg -> { int beforeMaps = maps.all().size; maps.reload(); diff --git a/tests/src/test/java/PatcherTests.java b/tests/src/test/java/PatcherTests.java index ece3b1d86e..e23cc9149e 100644 --- a/tests/src/test/java/PatcherTests.java +++ b/tests/src/test/java/PatcherTests.java @@ -6,6 +6,7 @@ import mindustry.entities.abilities.*; import mindustry.entities.bullet.*; import mindustry.gen.*; import mindustry.type.*; +import mindustry.world.blocks.defense.turrets.*; import mindustry.world.blocks.units.*; import mindustry.world.meta.*; import org.junit.jupiter.api.*; @@ -296,6 +297,66 @@ public class PatcherTests{ assertFalse(UnitTypes.dagger.immunities.contains(StatusEffects.fast)); } + @Test + void testAmmoReassign() throws Exception{ + Vars.state.patcher.apply(Seq.with(""" + block.fuse.ammoTypes: { + titanium: "-" + surge-alloy: { + type: LaserBulletType + ammoMultiplier: 1 + reloadMultiplier: 0.5 + damage: 100 + colors: ["000000", "ff0000", "ffffff"] + } + } + """)); + + assertEquals(new Seq<>(), Vars.state.patcher.patches.first().warnings); + assertTrue(((ItemTurret)Blocks.fuse).ammoTypes.containsKey(Items.surgeAlloy)); + assertFalse(((ItemTurret)Blocks.fuse).ammoTypes.containsKey(Items.titanium)); + assertEquals(100, ((ItemTurret)Blocks.fuse).ammoTypes.get(Items.surgeAlloy).damage); + + Vars.logic.reset(); + + assertFalse(((ItemTurret)Blocks.fuse).ammoTypes.containsKey(Items.surgeAlloy)); + assertTrue(((ItemTurret)Blocks.fuse).ammoTypes.containsKey(Items.titanium)); + } + + @Test + void testIndexAccess() throws Exception{ + float oldDamage = UnitTypes.dagger.weapons.first().bullet.damage; + Vars.state.patcher.apply(Seq.with(""" + unit.dagger.weapons.0.bullet.damage: 100 + """)); + + assertEquals(new Seq<>(), Vars.state.patcher.patches.first().warnings); + assertEquals(100, UnitTypes.dagger.weapons.first().bullet.damage); + + Vars.logic.reset(); + + assertEquals(oldDamage, UnitTypes.dagger.weapons.first().bullet.damage); + } + + @Test + void testAddWeapon() throws Exception{ + Vars.state.patcher.apply(Seq.with(""" + unit.flare.weapons.+: { + x: 0 + y: 0 + reload: 10 + bullet: { + type: LaserBulletType + damage: 100 + } + } + """)); + + assertEquals(new Seq<>(), Vars.state.patcher.patches.first().warnings); + assertEquals(3, UnitTypes.flare.weapons.size); + assertEquals(100, UnitTypes.flare.weapons.peek().bullet.damage); + } + @Test void testBigPatch() throws Exception{ Vars.state.patcher.apply(Seq.with("""