From ce82b3943e63027d71de3466deef7cce82921794 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 6 Feb 2022 20:54:57 -0500 Subject: [PATCH] No Erekir default AI / Core unit tweaks --- core/src/mindustry/ai/types/SuicideAI.java | 1 + core/src/mindustry/content/Blocks.java | 6 +- core/src/mindustry/content/UnitTypes.java | 83 ++++++++----------- .../mindustry/entities/bullet/BulletType.java | 2 +- .../entities/units/AIController.java | 8 ++ core/src/mindustry/type/UnitType.java | 6 ++ core/src/mindustry/type/Weapon.java | 4 + .../mindustry/type/unit/ErekirUnitType.java | 18 ++++ .../mindustry/type/unit/MissileUnitType.java | 4 +- .../src/mindustry/type/unit/TankUnitType.java | 5 +- .../type/weapons/RepairBeamWeapon.java | 69 +++++++++++++-- .../world/blocks/units/RepairPoint.java | 6 +- .../world/blocks/units/UnitAssembler.java | 8 +- 13 files changed, 147 insertions(+), 73 deletions(-) create mode 100644 core/src/mindustry/type/unit/ErekirUnitType.java diff --git a/core/src/mindustry/ai/types/SuicideAI.java b/core/src/mindustry/ai/types/SuicideAI.java index 70e365e775..aefc0c296b 100644 --- a/core/src/mindustry/ai/types/SuicideAI.java +++ b/core/src/mindustry/ai/types/SuicideAI.java @@ -19,6 +19,7 @@ public class SuicideAI extends GroundAI{ @Override public void updateUnit(){ + if(disabled()) return; if(Units.invalidateTarget(target, unit.team, unit.x, unit.y, Float.MAX_VALUE)){ target = null; diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index cf3c7391d9..c7e9f2c7fe 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -2546,7 +2546,7 @@ public class Blocks{ thrusterLength = 34/4f; armor = 5f; - unitCapModifier = 6; + unitCapModifier = 4; researchCostMultiplier = 0.07f; }}; @@ -2561,7 +2561,7 @@ public class Blocks{ thrusterLength = 40/4f; armor = 10f; - unitCapModifier = 10; + unitCapModifier = 8; researchCostMultiplier = 0.11f; }}; @@ -2576,7 +2576,7 @@ public class Blocks{ thrusterLength = 48/4f; armor = 15f; - unitCapModifier = 14; + unitCapModifier = 12; researchCostMultiplier = 0.11f; }}; diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 01f1cc57fc..d3229cc1e0 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -2674,14 +2674,12 @@ public class UnitTypes{ //endregion //region erekir - mech - bulwark = new UnitType("bulwark"){{ + bulwark = new ErekirUnitType("bulwark"){{ drag = 0.1f; speed = 0.6f; hitSize = 23f; health = 7000; armor = 5f; - outlineColor = Pal.darkOutline; - envDisabled = Env.space; abilities.add(new ShieldArcAbility(){{ region = "bulwark-shield"; @@ -2771,14 +2769,12 @@ public class UnitTypes{ }}); }}; - krepost = new UnitType("krepost"){{ + krepost = new ErekirUnitType("krepost"){{ drag = 0.1f; speed = 1.1f; hitSize = 44f; health = 18000; armor = 8f; - outlineColor = Pal.darkOutline; - envDisabled = Env.space; rotateSpeed = 1.6f; lockLegBase = true; legContinuousMove = true; @@ -2887,11 +2883,10 @@ public class UnitTypes{ //endregion //region erekir - flying - quell = new UnitType("quell"){{ + quell = new ErekirUnitType("quell"){{ defaultController = FlyingFollowAI::new; envDisabled = 0; - outlineColor = Pal.darkOutline; lowAltitude = false; flying = true; drag = 0.06f; @@ -2953,11 +2948,10 @@ public class UnitTypes{ ); }}; - disrupt = new UnitType("disrupt"){{ + disrupt = new ErekirUnitType("disrupt"){{ defaultController = FlyingFollowAI::new; envDisabled = 0; - outlineColor = Pal.darkOutline; lowAltitude = false; flying = true; drag = 0.07f; @@ -3122,12 +3116,11 @@ public class UnitTypes{ //region erekir - core //TODO bad name - evoke = new UnitType("evoke"){{ + evoke = new ErekirUnitType("evoke"){{ defaultController = BuilderAI::new; isCounted = false; envDisabled = 0; - outlineColor = Pal.darkOutline; lowAltitude = false; mineWalls = true; mineFloor = false; @@ -3136,15 +3129,14 @@ public class UnitTypes{ mineSpeed = 4f; mineTier = 4; buildSpeed = 0.8f; - drag = 0.06f; - speed = 2.9f; - rotateSpeed = 9f; - accel = 0.1f; + drag = 0.08f; + speed = 5.3f; + rotateSpeed = 7f; + accel = 0.09f; itemCapacity = 60; health = 300f; armor = 1f; hitSize = 9f; - commandLimit = 5; engineSize = 0; setEnginesMirror( @@ -3152,43 +3144,37 @@ public class UnitTypes{ new UnitEngine(23 / 4f, -22 / 4f, 2.2f, 315f) ); - weapons.add(new Weapon(){{ - reload = 17f; + weapons.add(new RepairBeamWeapon(){{ + reload = 25f; x = 0f; - y = 1f; - top = false; + y = 6.5f; + rotate = false; + shootY = 0f; + beamWidth = 0.7f; + repairSpeed = 0.25f; + aimDst = 0f; + shootCone = 15f; + fractionRepair = true; mirror = false; - bullet = new LaserBoltBulletType(){{ - speed = 4.2f; - frontColor = Color.white; - backColor = hitColor = trailColor = Pal.accent; + targetUnits = false; + targetBuildings = true; + autoTarget = false; + controllable = true; + laserColor = Pal.accent; + healColor = Pal.accent; - height = 6f; - trailLength = 5; - trailWidth = 2f; - - healColor = Pal.accent; - healPercent = 1f; - healAmount = 25f; - collidesTeam = true; - - lifetime = 31f; - shootEffect = Fx.colorSpark; - hitEffect = smokeEffect = despawnEffect = Fx.hitLaserColor; - - //TODO 0, or 1? - damage = 0; + bullet = new BulletType(){{ + maxRange = 70f; }}; }}); }}; - incite = new UnitType("incite"){{ + incite = new ErekirUnitType("incite"){{ defaultController = BuilderAI::new; isCounted = false; envDisabled = 0; - outlineColor = Pal.darkOutline; lowAltitude = false; flying = true; mineWalls = true; @@ -3206,7 +3192,6 @@ public class UnitTypes{ health = 600f; armor = 2f; hitSize = 18f; - commandLimit = 7; buildBeamOffset = 10f; engineSize = 0; payloadCapacity = Mathf.sqr(2f) * tilePayload; @@ -3243,12 +3228,11 @@ public class UnitTypes{ }}); }}; - emanate = new UnitType("emanate"){{ + emanate = new ErekirUnitType("emanate"){{ defaultController = BuilderAI::new; isCounted = false; envDisabled = 0; - outlineColor = Pal.darkOutline; lowAltitude = false; flying = true; targetAir = false; @@ -3266,7 +3250,6 @@ public class UnitTypes{ health = 1300f; armor = 3f; hitSize = 36f; - commandLimit = 9; buildBeamOffset = 72f / 4f; engineSize = 0; payloadCapacity = Mathf.sqr(3f) * tilePayload; @@ -3328,14 +3311,14 @@ public class UnitTypes{ internal = true; }}; - manifold = new UnitType("manifold"){{ + manifold = new ErekirUnitType("manifold"){{ defaultController = CargoAI::new; + defaultAI = true; isCounted = false; allowedInPayloads = false; logicControllable = false; envDisabled = 0; - outlineColor = Pal.darkOutline; lowAltitude = false; flying = true; drag = 0.06f; @@ -3345,7 +3328,6 @@ public class UnitTypes{ itemCapacity = 60; health = 200f; hitSize = 11f; - commandLimit = 0; engineSize = 2.3f; engineOffset = 6.5f; hidden = true; @@ -3355,9 +3337,10 @@ public class UnitTypes{ ); }}; - assemblyDrone = new UnitType("assembly-drone"){{ + assemblyDrone = new ErekirUnitType("assembly-drone"){{ defaultController = AssemblerAI::new; + defaultAI = true; flying = true; drag = 0.06f; accel = 0.11f; diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index 60efac8679..d88ed83135 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -231,7 +231,7 @@ public class BulletType extends Content implements Cloneable{ /** Returns maximum distance the bullet this bullet type has can travel. */ public float range(){ if(rangeOverride > 0) return rangeOverride; - return Mathf.zero(drag) ? speed * lifetime : Math.max(speed * (1f - Mathf.pow(1f - drag, lifetime)) / drag, maxRange); + return Math.max(Mathf.zero(drag) ? speed * lifetime : speed * (1f - Mathf.pow(1f - drag, lifetime)) / drag, maxRange); } /** @return continuous damage in damage/sec, or -1 if not continuous. */ diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index fe6d072579..ec0c768405 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -33,6 +33,10 @@ public class AIController implements UnitController{ @Override public void updateUnit(){ + if(disabled()){ + return; + } + //use fallback AI when possible if(useFallback() && (fallback != null || (fallback = fallback()) != null)){ if(fallback.unit != unit) fallback.unit(unit); @@ -45,6 +49,10 @@ public class AIController implements UnitController{ updateMovement(); } + public boolean disabled(){ + return !unit.team.isAI() && !unit.type.defaultAI; + } + @Nullable public AIController fallback(){ return null; diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 5954d4838a..6f25251e88 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -67,6 +67,10 @@ public class UnitType extends UnlockableContent{ public boolean logicControllable = true; public boolean playerControllable = true; public boolean allowedInPayloads = true; + /** If false, this unit has no AI when not controlled by a player, regardless of AI controller. */ + public boolean defaultAI = true; + /** TODO If true, core units need to "dock" to this unit to work, and can un-dock at the unit instead of respawning at core. */ + public boolean coreUnitDock = false; public boolean createWreck = true; public boolean createScorch = true; public boolean useUnitCap = true; @@ -503,6 +507,8 @@ public class UnitType extends UnlockableContent{ } this.weapons = mapped; + weapons.each(Weapon::init); + //dynamically create ammo capacity based on firing rate if(ammoCapacity < 0){ float shotsPerSecond = weapons.sumf(w -> w.useAmmo ? 60f / w.reload : 0f); diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 3901c59a3a..3c1b1a7e1e 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -465,6 +465,10 @@ public class Weapon implements Cloneable{ } } + public void init(){ + + } + public void load(){ region = Core.atlas.find(name); heatRegion = Core.atlas.find(name + "-heat"); diff --git a/core/src/mindustry/type/unit/ErekirUnitType.java b/core/src/mindustry/type/unit/ErekirUnitType.java new file mode 100644 index 0000000000..9f04228065 --- /dev/null +++ b/core/src/mindustry/type/unit/ErekirUnitType.java @@ -0,0 +1,18 @@ +package mindustry.type.unit; + +import mindustry.graphics.*; +import mindustry.type.*; +import mindustry.world.meta.*; + +/** Config class for special Erekir unit properties. */ +public class ErekirUnitType extends UnitType{ + + public ErekirUnitType(String name){ + super(name); + commandLimit = 0; + outlineColor = Pal.darkOutline; + envDisabled = Env.space; + defaultAI = false; + coreUnitDock = true; + } +} diff --git a/core/src/mindustry/type/unit/MissileUnitType.java b/core/src/mindustry/type/unit/MissileUnitType.java index 08d9a51eb4..a9113552d4 100644 --- a/core/src/mindustry/type/unit/MissileUnitType.java +++ b/core/src/mindustry/type/unit/MissileUnitType.java @@ -2,6 +2,7 @@ package mindustry.type.unit; import mindustry.ai.types.*; import mindustry.gen.*; +import mindustry.graphics.*; import mindustry.type.*; import mindustry.world.meta.*; @@ -31,6 +32,7 @@ public class MissileUnitType extends UnitType{ rotateSpeed = 2.5f; range = 30f; targetPriority = -1f; - //TODO weapons, etc + outlineColor = Pal.darkOutline; + //TODO weapon configs, etc? } } diff --git a/core/src/mindustry/type/unit/TankUnitType.java b/core/src/mindustry/type/unit/TankUnitType.java index d858e2be03..aa942b2092 100644 --- a/core/src/mindustry/type/unit/TankUnitType.java +++ b/core/src/mindustry/type/unit/TankUnitType.java @@ -1,10 +1,8 @@ package mindustry.type.unit; -import mindustry.graphics.*; -import mindustry.type.*; import mindustry.world.meta.*; -public class TankUnitType extends UnitType{ +public class TankUnitType extends ErekirUnitType{ public TankUnitType(String name){ super(name); @@ -14,7 +12,6 @@ public class TankUnitType extends UnitType{ rotateSpeed = 1.3f; envDisabled = Env.none; speed = 0.8f; - outlineColor = Pal.darkOutline; } } diff --git a/core/src/mindustry/type/weapons/RepairBeamWeapon.java b/core/src/mindustry/type/weapons/RepairBeamWeapon.java index 44f76f680f..7014830d21 100644 --- a/core/src/mindustry/type/weapons/RepairBeamWeapon.java +++ b/core/src/mindustry/type/weapons/RepairBeamWeapon.java @@ -7,9 +7,12 @@ import arc.math.*; import arc.math.geom.*; import arc.scene.ui.layout.*; import arc.util.*; +import mindustry.*; +import mindustry.content.*; import mindustry.entities.*; import mindustry.entities.units.*; import mindustry.gen.*; +import mindustry.graphics.*; import mindustry.type.*; import mindustry.world.blocks.units.*; import mindustry.world.meta.*; @@ -19,7 +22,9 @@ import mindustry.world.meta.*; * Rotation must be set to true. Fixed repair points are not supported. * */ public class RepairBeamWeapon extends Weapon{ - public boolean targetBuildings = false; + public boolean targetBuildings = false, targetUnits = true; + //TODO leads to wrong stats + public boolean fractionRepair = false; public float repairSpeed = 0.3f; public float beamWidth = 1f; @@ -29,6 +34,9 @@ public class RepairBeamWeapon extends Weapon{ public TextureRegion laser, laserEnd, laserTop, laserTopEnd; public Color laserColor = Color.valueOf("98ffa9"), laserTopColor = Color.white.cpy(); + //only for blocks + public Color healColor = Pal.heal; + public Effect healEffect = Fx.healBlockFull; public RepairBeamWeapon(String name){ super(name); @@ -72,9 +80,8 @@ public class RepairBeamWeapon extends Weapon{ @Override protected Teamc findTarget(Unit unit, float x, float y, float range, boolean air, boolean ground){ - var out = Units.closest(unit.team, x, y, range, u -> u != unit && u.damaged()); + var out = targetUnits ? Units.closest(unit.team, x, y, range, u -> u != unit && u.damaged()) : null; if(out != null || !targetBuildings) return out; - //TODO maybe buildings shouldn't be allowed at all return Units.findAllyTile(unit.team, x, y, range, Building::damaged); } @@ -92,11 +99,48 @@ public class RepairBeamWeapon extends Weapon{ public void update(Unit unit, WeaponMount mount){ super.update(unit, mount); - HealBeamMount heal = (HealBeamMount)mount; - heal.strength = Mathf.lerpDelta(heal.strength, Mathf.num(mount.target != null), 0.2f); + float + weaponRotation = unit.rotation - 90, + wx = unit.x + Angles.trnsx(weaponRotation, x, y), + wy = unit.y + Angles.trnsy(weaponRotation, x, y); - if(mount.target instanceof Healthc u){ - u.heal(repairSpeed * heal.strength * Time.delta); + HealBeamMount heal = (HealBeamMount)mount; + heal.target = null; + boolean canShoot = mount.shoot; + + if(!autoTarget){ + if(canShoot){ + heal.lastEnd.set(heal.aimX, heal.aimY); + + if(!rotate && !Angles.within(Angles.angle(wx, wy, heal.aimX, heal.aimY), unit.rotation, shootCone)){ + canShoot = false; + } + } + + //limit range + heal.lastEnd.sub(wx, wy).limit(range()).add(wx, wy); + + if(targetBuildings){ + var build = Vars.world.buildWorld(mount.aimX, mount.aimY); + if(build != null && build.team == unit.team){ + heal.target = build; + } + } + if(targetUnits){ + //TODO does not support healing units manually yet + } + } + + heal.strength = Mathf.lerpDelta(heal.strength, Mathf.num(autoTarget ? mount.target != null : canShoot), 0.2f); + + //create heal effect periodically + if(canShoot && mount.target instanceof Building b && b.damaged() && (heal.effectTimer += Time.delta) >= reload){ + healEffect.at(b.x, b.y, b.block.size, healColor); + heal.effectTimer = 0f; + } + + if(canShoot && mount.target instanceof Healthc u){ + u.heal(repairSpeed * heal.strength * Time.delta * (fractionRepair ? u.maxHealth() / 100f : 1f)); } } @@ -112,15 +156,22 @@ public class RepairBeamWeapon extends Weapon{ wy = unit.y + Angles.trnsy(weaponRotation, x, y); float z = Draw.z(); - RepairPoint.drawBeam(wx, wy, unit.rotation + mount.rotation, shootY, unit.id, mount.target == null ? null : (Sized)mount.target, unit.team, heal.strength, + RepairPoint.drawBeam(wx, wy, unit.rotation + mount.rotation, shootY, unit.id, mount.target == null || controllable ? null : (Sized)mount.target, unit.team, heal.strength, pulseStroke, pulseRadius, beamWidth, heal.lastEnd, heal.offset, laserColor, laserTopColor, laser, laserEnd, laserTop, laserTopEnd); Draw.z(z); } + @Override + public void init(){ + if(fractionRepair){ + bullet.healPercent = repairSpeed; + } + } + public static class HealBeamMount extends WeaponMount{ public Vec2 offset = new Vec2(), lastEnd = new Vec2(); - public float strength; + public float strength, effectTimer; public HealBeamMount(Weapon weapon){ super(weapon); diff --git a/core/src/mindustry/world/blocks/units/RepairPoint.java b/core/src/mindustry/world/blocks/units/RepairPoint.java index 3c723c49e3..c23a1ec4e6 100644 --- a/core/src/mindustry/world/blocks/units/RepairPoint.java +++ b/core/src/mindustry/world/blocks/units/RepairPoint.java @@ -98,18 +98,18 @@ public class RepairPoint extends Block{ return new TextureRegion[]{baseRegion, region}; } - public static void drawBeam(float x, float y, float rotation, float length, int id, Sized target, Team team, + public static void drawBeam(float x, float y, float rotation, float length, int id, @Nullable Sized target, Team team, float strength, float pulseStroke, float pulseRadius, float beamWidth, Vec2 lastEnd, Vec2 offset, Color laserColor, Color laserTopColor, TextureRegion laser, TextureRegion laserEnd, TextureRegion laserTop, TextureRegion laserTopEnd){ + rand.setSeed(id + (target instanceof Entityc e ? e.id() : 0)); + if(target != null){ float originX = x + Angles.trnsx(rotation, length), originY = y + Angles.trnsy(rotation, length); - rand.setSeed(id + (target instanceof Entityc e ? e.id() : 0)); - lastEnd.set(target).sub(originX, originY); lastEnd.setLength(Math.max(2f, lastEnd.len())); diff --git a/core/src/mindustry/world/blocks/units/UnitAssembler.java b/core/src/mindustry/world/blocks/units/UnitAssembler.java index ed88c6b350..f4fa881c9f 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssembler.java +++ b/core/src/mindustry/world/blocks/units/UnitAssembler.java @@ -172,7 +172,7 @@ public class UnitAssembler extends PayloadBlock{ public Seq units = new Seq<>(); public Seq modules = new Seq<>(); public BlockSeq blocks = new BlockSeq(); - public float progress, warmup, droneWarmup; + public float progress, warmup, droneWarmup, powerWarmup; public float invalidWarmup = 0f; public int currentTier = 0; public boolean wasOccupied = false; @@ -284,6 +284,7 @@ public class UnitAssembler extends PayloadBlock{ units.removeAll(u -> !u.isAdded() || u.dead || !(u.controller() instanceof AssemblerAI)); + powerWarmup = Mathf.lerpDelta(powerWarmup, efficiency(), 0.1f); droneWarmup = Mathf.lerpDelta(droneWarmup, units.size < dronesCreated ? efficiency() : 0f, 0.1f); totalDroneProgress += droneWarmup * Time.delta; @@ -380,6 +381,7 @@ public class UnitAssembler extends PayloadBlock{ var plan = plan(); + //draw the unit construction as outline Draw.draw(Layer.blockBuilding, () -> { Draw.color(Pal.accent, warmup); @@ -397,8 +399,9 @@ public class UnitAssembler extends PayloadBlock{ Draw.z(Layer.buildBeam); - //draw unit outline + //draw unit silhouette Draw.mixcol(Tmp.c1.set(Pal.accent).lerp(Pal.remove, invalidWarmup), 1f); + Draw.alpha(powerWarmup); Draw.rect(plan.unit.fullIcon, spawn.x, spawn.y); //build beams do not draw when invalid @@ -423,6 +426,7 @@ public class UnitAssembler extends PayloadBlock{ //draw full area Lines.stroke(2f, Pal.accent); + Draw.alpha(powerWarmup); Drawf.dashRectBasic(spawn.x - fulls, spawn.y - fulls, fulls*2f, fulls*2f); Draw.reset();