diff --git a/core/assets/maps/hidden-serpulo/30.msav b/core/assets/maps/hidden-serpulo/30.msav new file mode 100644 index 0000000000..f197c6d64a Binary files /dev/null and b/core/assets/maps/hidden-serpulo/30.msav differ diff --git a/core/src/mindustry/core/GameState.java b/core/src/mindustry/core/GameState.java index 1058c84de0..90bff9fcb2 100644 --- a/core/src/mindustry/core/GameState.java +++ b/core/src/mindustry/core/GameState.java @@ -6,6 +6,7 @@ import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.maps.*; +import mindustry.mod.*; import mindustry.type.*; import mindustry.world.blocks.*; @@ -46,6 +47,8 @@ public class GameState{ public int enemies; /** Map being playtested (not edited!) */ public @Nullable Map playtestingMap; + /** Null if not content patches have been applied. */ + public @Nullable ContentPatcher patcher; /** Current game state. */ private State state = State.menu; diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index ae6c90c923..fa5953d45c 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -260,6 +260,10 @@ public class Logic implements ApplicationListener{ public void reset(){ State prev = state.getState(); + if(state.patcher != null){ + state.patcher.unapply(); + state.patcher = null; + } //recreate gamestate - sets state to menu state = new GameState(); //fire change event, since it was technically changed diff --git a/core/src/mindustry/entities/Predict.java b/core/src/mindustry/entities/Predict.java index 67ca74c42b..32ad22d0dc 100644 --- a/core/src/mindustry/entities/Predict.java +++ b/core/src/mindustry/entities/Predict.java @@ -3,6 +3,7 @@ package mindustry.entities; import arc.math.*; import arc.math.geom.*; import arc.util.*; +import mindustry.entities.bullet.*; import mindustry.gen.*; /** @@ -55,13 +56,21 @@ public class Predict{ return intercept(src, dst, 0, 0, v); } + public static Vec2 intercept(Position src, Position dst, BulletType bullet){ + return intercept(src, dst, 0f, 0f, bullet.keepVelocity, bullet.speed); + } + public static Vec2 intercept(Position src, Position dst, float offsetx, float offsety, float v){ + return intercept(src, dst, offsetx, offsety, true, v); + } + + public static Vec2 intercept(Position src, Position dst, float offsetx, float offsety, boolean useSrcVelocity, float v){ float ddx = 0, ddy = 0; if(dst instanceof Hitboxc h){ ddx += h.deltaX(); ddy += h.deltaY(); } - if(src instanceof Hitboxc h){ + if(src instanceof Hitboxc h && useSrcVelocity){ ddx -= h.deltaX(); ddy -= h.deltaY(); } diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index 3b8b03d110..6a59aca4c5 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -111,7 +111,7 @@ public class AIController implements UnitController{ public void faceTarget(){ if(unit.type.omniMovement || unit instanceof Mechc){ if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type.faceTarget && unit.type.hasWeapons()){ - unit.lookAt(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed)); + unit.lookAt(Predict.intercept(unit, target, unit.type.weapons.first().bullet)); }else if(unit.moving()){ unit.lookAt(unit.vel().angle()); } @@ -218,7 +218,7 @@ public class AIController implements UnitController{ shoot = bomberTarget != null; } - Vec2 to = Predict.intercept(unit, mount.target, weapon.bullet.speed); + Vec2 to = Predict.intercept(unit, mount.target, weapon.bullet); mount.aimX = to.x; mount.aimY = to.y; } diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index b5019d2535..6be78abcb5 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -1003,7 +1003,6 @@ public class MobileInput extends InputHandler implements GestureListener{ float attractDst = 15f; float speed = unit.speed(); float range = unit.hasWeapons() ? unit.range() : 0f; - float bulletSpeed = unit.hasWeapons() ? type.weapons.first().bullet.speed : 0f; float mouseAngle = unit.angleTo(unit.aimX(), unit.aimY()); boolean aimCursor = omni && player.shooting && type.hasWeapons() && !boosted && type.faceTarget; @@ -1081,7 +1080,7 @@ public class MobileInput extends InputHandler implements GestureListener{ //this may be a bad idea, aiming for a point far in front could work better, test it out unit.aim(Core.input.mouseWorldX(), Core.input.mouseWorldY()); }else{ - Vec2 intercept = player.unit().type.weapons.contains(w -> w.predictTarget) ? Predict.intercept(unit, target, bulletSpeed) : Tmp.v1.set(target); + Vec2 intercept = player.unit().type.weapons.contains(w -> w.predictTarget) ? Predict.intercept(unit, target, type.weapons.first().bullet) : Tmp.v1.set(target); player.mouseX = intercept.x; player.mouseY = intercept.y; diff --git a/core/src/mindustry/maps/SectorSubmissions.java b/core/src/mindustry/maps/SectorSubmissions.java index 97ea5a9b99..9a62e0e287 100644 --- a/core/src/mindustry/maps/SectorSubmissions.java +++ b/core/src/mindustry/maps/SectorSubmissions.java @@ -61,6 +61,7 @@ public class SectorSubmissions{ registerSerpuloSector(254, "wpx", "https://discord.com/channels/391020510269669376/1379928045577703424/1420456601667502193"); registerSerpuloSector(0, "Jamespire", "https://discord.com/channels/391020510269669376/1379926780860698784/1418590967384117311"); registerSerpuloSector(103, "enwyz", "https://discord.com/channels/391020510269669376/1379926839559979030/1429203869514207255"); + registerSerpuloSector(30, "cyan", "https://discord.com/channels/391020510269669376/1379926800854945823/1423932799647481910"); /* UNUSED SECTORS: registerHiddenSectors(serpulo, diff --git a/core/src/mindustry/mod/ContentPatcher.java b/core/src/mindustry/mod/ContentPatcher.java index b97c24fee9..3f030493a4 100644 --- a/core/src/mindustry/mod/ContentPatcher.java +++ b/core/src/mindustry/mod/ContentPatcher.java @@ -19,7 +19,7 @@ public class ContentPatcher{ private static final Object root = new Object(); private static final ObjectMap nameToType = new ObjectMap<>(); - private Json json; //TODO ContentParser.json?? + private Json json; private boolean applied; private ContentLoader contentLoader; private ObjectSet usedpatches = new ObjectSet<>(); @@ -38,24 +38,35 @@ public class ContentPatcher{ applied = true; contentLoader = Vars.content.copy(); - JsonValue value = json.fromJson(null, Jval.read(patch).toString(Jformat.plain)); - for(var child : value){ - assign(root, child.name, child, null, null, null); - } + try{ + JsonValue value = json.fromJson(null, Jval.read(patch).toString(Jformat.plain)); + for(var child : value){ + assign(root, child.name, child, null, null, null); + } - afterCallbacks.each(Runnable::run); + afterCallbacks.each(Runnable::run); + }catch(Exception e){ + Log.err("Failed to apply patch: " + patch, e); + } } - public void unapply() throws Exception{ + public void unapply(){ if(!applied) return; Vars.content = contentLoader; applied = false; resetters.reverse(); - resetters.each(Runnable::run); + for(var reset : resetters){ + try{ + reset.run(); + }catch(Throwable e){ + Log.err("Failed to un-apply patch!", e); + } + } resetters.clear(); + //this should never throw an exception afterCallbacks.each(Runnable::run); afterCallbacks.clear(); } @@ -160,8 +171,7 @@ public class ContentPatcher{ }else if(object instanceof ObjectMap map){ if(metadata == null){ warn("ObjectMap cannot be parsed without metadata: @.@", parentObject, parentField); - throw new RuntimeException(); - //return; + return; } Object key = convertKeyType(field, metadata.keyType); if(key == null){ diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 50dfe94b01..25badf4493 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -324,7 +324,7 @@ public class Weapon implements Cloneable{ shoot = mount.target.within(mountX, mountY, bullet.range + Math.abs(shootY) + (mount.target instanceof Sized s ? s.hitSize()/2f : 0f)) && can; if(predictTarget){ - Vec2 to = Predict.intercept(unit, mount.target, bullet.speed); + Vec2 to = Predict.intercept(unit, mount.target, bullet); mount.aimX = to.x; mount.aimY = to.y; }else{