diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index fc95300e51..aed6127661 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -472,7 +472,7 @@ editor.generation = Generation editor.objectives = Objectives editor.locales = Locale Bundles editor.patches.guide = Patch Guide -editor.patches = Content Patches +editor.patches = Data Patches editor.patch: Patchset: {0} editor.patches.none = [lightgray]No patchsets loaded. editor.patches.errors = Patchset Errors diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index 1bf6b45c99..c0b52edf8a 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -270,7 +270,6 @@ public class CommandAI extends AIController{ Building targetBuild = world.buildWorld(targetPos.x, targetPos.y); - //TODO: should the unit stop when it finds a target? if( (hasStance(UnitStance.patrol) && !hasStance(UnitStance.pursueTarget) && target != null && unit.within(target, unit.type.range - 2f) && !unit.type.circleTarget) || @@ -347,7 +346,7 @@ public class CommandAI extends AIController{ if(move){ if(unit.type.circleTarget && attackTarget != null){ target = attackTarget; - circleAttack(80f); + circleAttack(unit.type.circleTargetRadius); }else{ moveTo(vecOut, withinAttackRange ? engageRange : @@ -362,7 +361,7 @@ public class CommandAI extends AIController{ attackTarget = null; } - if(unit.isFlying() && move && (attackTarget == null || !unit.within(attackTarget, unit.type.range))){ + if(unit.isFlying() && move && !(unit.type.circleTarget && !unit.type.omniMovement) && (attackTarget == null || !unit.within(attackTarget, unit.type.range))){ unit.lookAt(vecMovePos); }else{ faceTarget(); @@ -379,7 +378,11 @@ public class CommandAI extends AIController{ } }else if(target != null){ - faceTarget(); + if(unit.type.circleTarget && shouldFire()){ + circleAttack(unit.type.circleTargetRadius); + }else{ + faceTarget(); + } } } diff --git a/core/src/mindustry/ai/types/FlyingAI.java b/core/src/mindustry/ai/types/FlyingAI.java index c328b41b41..3122a56951 100644 --- a/core/src/mindustry/ai/types/FlyingAI.java +++ b/core/src/mindustry/ai/types/FlyingAI.java @@ -19,7 +19,7 @@ public class FlyingAI extends AIController{ if(target != null && unit.hasWeapons()){ if(unit.type.circleTarget){ - circleAttack(120f); + circleAttack(unit.type.circleTargetRadius); }else{ moveTo(target, unit.type.range * 0.8f); unit.lookAt(target); diff --git a/core/src/mindustry/ai/types/RepairAI.java b/core/src/mindustry/ai/types/RepairAI.java index af0140eaba..bf3e9782ff 100644 --- a/core/src/mindustry/ai/types/RepairAI.java +++ b/core/src/mindustry/ai/types/RepairAI.java @@ -30,7 +30,7 @@ public class RepairAI extends AIController{ if(target != null && target instanceof Building b && b.team == unit.team){ if(unit.type.circleTarget){ - circleAttack(120f); + circleAttack(unit.type.circleTargetRadius); }else if(!target.within(unit, unit.type.range * 0.65f)){ moveTo(target, unit.type.range * 0.65f); } diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 6f3b7f6c58..92d85d7123 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -995,18 +995,25 @@ public class UnitTypes{ flying = true; health = 70; engineOffset = 5.75f; - //TODO balance - //targetAir = false; targetFlags = new BlockFlag[]{BlockFlag.generator, null}; hitSize = 9; itemCapacity = 10; + circleTarget = true; + omniMovement = false; + rotateSpeed = 5f; + circleTargetRadius = 60f; weapons.add(new Weapon(){{ - y = 0f; - x = 2f; - reload = 20f; + y = 1f; + x = 0f; + minShootVelocity = 2f; + shootCone = 10f; + reload = 80f; + shoot.shots = 3; + shoot.shotDelay = 3f; ejectEffect = Fx.casing1; - bullet = new BasicBulletType(2.5f, 9){{ + mirror = false; + bullet = new BasicBulletType(2.5f, 15){{ width = 7f; height = 9f; lifetime = 45f; @@ -1014,7 +1021,7 @@ public class UnitTypes{ smokeEffect = Fx.shootSmallSmoke; ammoMultiplier = 2; }}; - shootSound = Sounds.pew; + shootSound = Sounds.shootDagger; }}); }}; @@ -1035,9 +1042,12 @@ public class UnitTypes{ targetFlags = new BlockFlag[]{BlockFlag.factory, null}; circleTarget = true; ammoType = new ItemAmmoType(Items.graphite); + omniMovement = false; + rotateSpeed = 4.5f; + circleTargetRadius = 40f; weapons.add(new Weapon(){{ - minShootVelocity = 0.75f; + minShootVelocity = 1f; x = 3f; shootY = 0f; reload = 12f; diff --git a/core/src/mindustry/core/GameState.java b/core/src/mindustry/core/GameState.java index 7b7f00612d..e52bb00a0b 100644 --- a/core/src/mindustry/core/GameState.java +++ b/core/src/mindustry/core/GameState.java @@ -44,7 +44,7 @@ public class GameState{ /** Team data. Gets reset every new game. */ public Teams teams = new Teams(); /** Handles JSON edits of game content. */ - public ContentPatcher patcher = new ContentPatcher(); + public DataPatcher patcher = new DataPatcher(); /** Number of enemies in the game; only used clientside in servers. */ public int enemies; /** Map being playtested (not edited!) */ diff --git a/core/src/mindustry/entities/part/RegionPart.java b/core/src/mindustry/entities/part/RegionPart.java index 9474e819b7..cd75fe2add 100644 --- a/core/src/mindustry/entities/part/RegionPart.java +++ b/core/src/mindustry/entities/part/RegionPart.java @@ -98,7 +98,7 @@ public class RegionPart extends DrawPart{ int i = params.sideOverride == -1 ? s : params.sideOverride; //can be null - var region = drawRegion ? regions[Math.min(i, regions.length - 1)] : null; + var region = drawRegion && regions.length > 0 ? regions[Math.min(i, regions.length - 1)] : null; float sign = (i == 0 ? 1 : -1) * params.sideMultiplier; Tmp.v1.set((x + mx) * sign, y + my).rotateRadExact((params.rotation - 90) * Mathf.degRad); diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index 6a59aca4c5..52f0833acb 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -28,6 +28,7 @@ public class AIController implements UnitController{ /** main target that is being faced */ protected @Nullable Teamc target; protected @Nullable Teamc bomberTarget; + protected boolean turningAway; { resetTimers(); @@ -155,7 +156,8 @@ public class AIController implements UnitController{ } public void targetInvalidated(){ - //TODO: try this for normal units, reset the target timer + //immediately find a new target + timer.reset(timerTarget, -1f); } public void updateWeapons(){ @@ -169,7 +171,7 @@ public class AIController implements UnitController{ noTargetTime += Time.delta; if(invalid(target)){ - if(target != null && !target.isAdded()){ + if(target instanceof Healthc h && !h.isValid()){ targetInvalidated(); } target = null; @@ -300,14 +302,32 @@ public class AIController implements UnitController{ } public void circleAttack(float circleLength){ + if(target == null) return; + vec.set(target).sub(unit); float ang = unit.angleTo(target); float diff = Angles.angleDist(ang, unit.rotation()); + if(target instanceof Unit u && u.collisionLayer() == unit.collisionLayer()){ + float avoidDist = u.physicSize() + 30f; + if(turningAway){ + + vec.setLength(prefSpeed()).scl(-1f); + unit.movePref(vec); + + if(!unit.within(u, unit.type.circleTargetRadius*0.5f + u.physicSize())){ + turningAway = false; + } + return; + }else if(unit.within(u, avoidDist)){ + turningAway = true; + } + } + if(diff > 70f && vec.len() < circleLength){ vec.setAngle(unit.vel().angle()); - }else{ + }else if(unit.type.omniMovement){ //non-omni movement units don't need to do this as the turning is already smoothed out vec.setAngle(Angles.moveToward(unit.vel().angle(), vec.angle(), 6f)); } diff --git a/core/src/mindustry/mod/ContentPatcher.java b/core/src/mindustry/mod/DataPatcher.java similarity index 99% rename from core/src/mindustry/mod/ContentPatcher.java rename to core/src/mindustry/mod/DataPatcher.java index 5976aa685b..8690c50717 100644 --- a/core/src/mindustry/mod/ContentPatcher.java +++ b/core/src/mindustry/mod/DataPatcher.java @@ -22,7 +22,7 @@ import java.util.*; /** The current implementation is awful. Consider it a proof of concept. */ @SuppressWarnings("unchecked") -public class ContentPatcher{ +public class DataPatcher{ private static final Object root = new Object(); private static final ObjectMap nameToType = new ObjectMap<>(); private static ContentParser parser = createParser(); @@ -129,6 +129,8 @@ public class ContentPatcher{ if(!Vars.headless){ if(object instanceof DrawPart part && parent instanceof MappableContent cont){ part.load(cont.name); + }else if(object instanceof DrawPart part && parent instanceof Weapon w){ + part.load(w.name); }else if(object instanceof DrawBlock draw && parent instanceof Block block){ draw.load(block); }else if(object instanceof Weapon weapon){ diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 2fc7618c33..9a5b9d154f 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -93,6 +93,8 @@ public class UnitType extends UnlockableContent implements Senseable{ mineRange = 70f, /** range at which this unit can build */ buildRange = Vars.buildingRange, + /** radius for circleTarget, if true */ + circleTargetRadius = 80f, /** multiplier for damage this (flying) unit deals when crashing on enemy things */ crashDamageMultiplier = 1f, /** multiplier for health that this flying unit has for its wreck, based on its max health. */