diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 029650172b..7d023fb767 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -705,6 +705,7 @@ mode.attack.description = Destroy the enemy's base. No waves.\n[gray]Requires a mode.custom = Custom Rules rules.infiniteresources = Infinite Resources +rules.reactorexplosions = Reactor Explosions rules.wavetimer = Wave Timer rules.waves = Waves rules.attack = Attack Mode diff --git a/core/src/io/anuke/mindustry/game/Rules.java b/core/src/io/anuke/mindustry/game/Rules.java index 94cf4225ad..c528bc954f 100644 --- a/core/src/io/anuke/mindustry/game/Rules.java +++ b/core/src/io/anuke/mindustry/game/Rules.java @@ -25,6 +25,8 @@ public class Rules{ public boolean pvp; /** Whether enemy units drop random items on death. */ public boolean unitDrops = true; + /** Whether reactors can explode and damage other blocks. */ + public boolean reactorExplosions = true; /** How fast unit pads build units. */ public float unitBuildSpeedMultiplier = 1f; /** How much health units start with. */ diff --git a/core/src/io/anuke/mindustry/io/JsonIO.java b/core/src/io/anuke/mindustry/io/JsonIO.java index 4dc253672b..1edc718c6a 100644 --- a/core/src/io/anuke/mindustry/io/JsonIO.java +++ b/core/src/io/anuke/mindustry/io/JsonIO.java @@ -4,7 +4,7 @@ import io.anuke.arc.util.serialization.*; import io.anuke.arc.util.serialization.Json.*; import io.anuke.mindustry.*; import io.anuke.mindustry.content.*; -import io.anuke.mindustry.ctype.MappableContent; +import io.anuke.mindustry.ctype.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.world.*; @@ -31,6 +31,10 @@ public class JsonIO{ } }; + public static Json json(){ + return json; + } + public static String write(Object object){ return json.toJson(object, object.getClass()); } diff --git a/core/src/io/anuke/mindustry/mod/ContentParser.java b/core/src/io/anuke/mindustry/mod/ContentParser.java index 286e2d62e0..3d76c4575b 100644 --- a/core/src/io/anuke/mindustry/mod/ContentParser.java +++ b/core/src/io/anuke/mindustry/mod/ContentParser.java @@ -10,8 +10,6 @@ import io.anuke.arc.func.*; import io.anuke.arc.graphics.*; import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.*; -import io.anuke.arc.util.reflect.Field; -import io.anuke.arc.util.reflect.*; import io.anuke.arc.util.serialization.*; import io.anuke.arc.util.serialization.Json.*; import io.anuke.arc.util.serialization.Jval.*; @@ -361,7 +359,7 @@ public class ContentParser{ private T make(Class type){ try{ - java.lang.reflect.Constructor cons = type.getDeclaredConstructor(); + Constructor cons = type.getDeclaredConstructor(); cons.setAccessible(true); return cons.newInstance(); }catch(Exception e){ @@ -371,7 +369,7 @@ public class ContentParser{ private T make(Class type, String name){ try{ - java.lang.reflect.Constructor cons = type.getDeclaredConstructor(String.class); + Constructor cons = type.getDeclaredConstructor(String.class); cons.setAccessible(true); return cons.newInstance(name); }catch(Exception e){ @@ -381,7 +379,7 @@ public class ContentParser{ private Prov supply(Class type){ try{ - java.lang.reflect.Constructor cons = type.getDeclaredConstructor(); + Constructor cons = type.getDeclaredConstructor(); return () -> { try{ return cons.newInstance(); @@ -457,7 +455,7 @@ public class ContentParser{ Field field = metadata.field; try{ field.set(object, parser.readValue(field.getType(), metadata.elementType, child, metadata.keyType)); - }catch(ReflectionException ex){ + }catch(IllegalAccessException ex){ throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex); }catch(SerializationException ex){ ex.addTrace(field.getName() + " (" + type.getName() + ")"); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java index b3bea499c3..095a0cd77c 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java @@ -135,13 +135,11 @@ public class CustomRulesDialog extends FloatingDialog{ number("$rules.dropzoneradius", false, f -> rules.dropZoneRadius = f * tilesize, () -> rules.dropZoneRadius / tilesize, () -> true); title("$rules.title.respawns"); - //limited respawns don't work on PvP, commented out until they're fixed - //check("$rules.limitedRespawns", b -> rules.limitedRespawns = b, () -> rules.limitedRespawns); - //number("$rules.respawns", true, f -> rules.respawns = (int)f, () -> rules.respawns, () -> rules.limitedRespawns); number("$rules.respawntime", f -> rules.respawnTime = f * 60f, () -> rules.respawnTime / 60f); title("$rules.title.resourcesbuilding"); check("$rules.infiniteresources", b -> rules.infiniteResources = b, () -> rules.infiniteResources); + check("$rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions); number("$rules.buildcostmultiplier", false, f -> rules.buildCostMultiplier = f, () -> rules.buildCostMultiplier, () -> !rules.infiniteResources); number("$rules.buildspeedmultiplier", f -> rules.buildSpeedMultiplier = f, () -> rules.buildSpeedMultiplier); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java b/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java index 9a1d12ac39..3eead0b96c 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java @@ -23,7 +23,7 @@ import io.anuke.mindustry.world.meta.StatUnit; import java.io.*; -import static io.anuke.mindustry.Vars.tilesize; +import static io.anuke.mindustry.Vars.*; public class NuclearReactor extends PowerGenerator{ protected final int timerFuel = timers++; @@ -127,7 +127,7 @@ public class NuclearReactor extends PowerGenerator{ int fuel = entity.items.get(consumes.get(ConsumeType.item).items[0].item); - if(fuel < 5 && entity.heat < 0.5f) return; + if((fuel < 5 && entity.heat < 0.5f) || !state.rules.reactorExplosions) return; Effects.shake(6f, 16f, tile.worldx(), tile.worldy()); Effects.effect(Fx.nuclearShockwave, tile.worldx(), tile.worldy()); diff --git a/gradle.properties b/gradle.properties index 8912a511da..7a2bc0d5fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=9cde8aeb1f4913d5f9c1ed1792d5c5c5d522298c +archash=68a64d9e8a3016ad433d0959be78f6acdcdaa385 diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index fc9014a987..063921fb6c 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -8,6 +8,8 @@ import io.anuke.arc.util.*; import io.anuke.arc.util.Timer; import io.anuke.arc.util.CommandHandler.*; import io.anuke.arc.util.Timer.*; +import io.anuke.arc.util.serialization.*; +import io.anuke.arc.util.serialization.JsonValue.*; import io.anuke.mindustry.*; import io.anuke.mindustry.core.GameState.*; import io.anuke.mindustry.core.*; @@ -59,7 +61,8 @@ public class ServerControl implements ApplicationListener{ "crashreport", false, "port", port, "logging", true, - "socket", false + "socket", false, + "globalrules", "{}" ); Log.setLogger(new LogHandler(){ @@ -250,8 +253,9 @@ public class ServerControl implements ApplicationListener{ logic.reset(); lastMode = preset; try{ - world.loadMap(result, result.applyRules(lastMode)); + world.loadMap(result, result.applyRules(lastMode)); state.rules = result.applyRules(preset); + applyRules(); logic.play(); info("Map loaded."); @@ -348,6 +352,58 @@ public class ServerControl implements ApplicationListener{ } }); + handler.register("rules", "[remove/add] [name] [value...]", "List, remove or add global rules. These will apply regardless of map.", arg -> { + String rules = Core.settings.getString("globalrules"); + JsonValue base = JsonIO.json().fromJson(null, rules); + + if(arg.length == 0){ + Log.info("&lyRules:\n{0}", JsonIO.print(rules)); + }else if(arg.length == 1){ + Log.err("Invalid usage. Specify which rule to remove or add."); + }else{ + if(!(arg[0].equals("remove") || arg[0].equals("add"))){ + Log.err("Invalid usage. Either add or remove rules."); + return; + } + + boolean remove = arg[0].equals("remove"); + if(remove){ + if(base.has(arg[1])){ + Log.info("Rule &lc'{0}'&lg removed.", arg[1]); + base.remove(arg[1]); + }else{ + Log.err("Rule not defined, so not removed."); + return; + } + }else{ + if(arg.length < 3){ + Log.err("Missing last argument. Specify which value to set the rule to."); + return; + } + + try{ + JsonValue value = new JsonReader().parse(arg[2]); + value.name = arg[1]; + + JsonValue parent = new JsonValue(ValueType.object); + parent.addChild(value); + + JsonIO.json().readField(state.rules, value.name, parent); + if(base.has(value.name)){ + base.remove(value.name); + } + base.addChild(arg[1], value); + Log.info("Changed rule: &ly{0}", value.toString().replace("\n", " ")); + }catch(Throwable e){ + Log.err("Error parsing rule JSON", e); + } + } + + Core.settings.putSave("globalrules", base.toString()); + Call.onSetRules(state.rules); + } + }); + handler.register("fillitems", "[team]", "Fill the core with items.", arg -> { if(!state.is(State.playing)){ err("Not playing. Host first."); @@ -744,6 +800,15 @@ public class ServerControl implements ApplicationListener{ mods.each(p -> p.registerClientCommands(netServer.clientCommands)); } + private void applyRules(){ + try{ + JsonValue value = JsonIO.json().fromJson(null, Core.settings.getString("globalrules")); + JsonIO.json().readFields(state.rules, value); + }catch(Throwable t){ + Log.err("Error applying custom rules, proceeding without them.", t); + } + } + private void displayStatus() { if(state.is(State.menu)){ info("Status: &rserver closed"); @@ -824,6 +889,7 @@ public class ServerControl implements ApplicationListener{ run.run(); logic.play(); state.rules = world.getMap().applyRules(lastMode); + applyRules(); for(Player p : players){ if(p.con == null) continue;