From 0f76aeba05c439178dc33838b3f8f4f1353068e8 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 6 Jun 2020 11:36:14 -0400 Subject: [PATCH] More intelligent suicide units / Bugfixes --- core/src/mindustry/ai/types/GroundAI.java | 6 +- core/src/mindustry/ai/types/SuicideAI.java | 70 +++++++++++++++++++ core/src/mindustry/content/Bullets.java | 9 +-- core/src/mindustry/content/Fx.java | 4 +- core/src/mindustry/content/UnitTypes.java | 21 ++++-- core/src/mindustry/entities/Damage.java | 68 ++++++++++-------- .../entities/bullet/LiquidBulletType.java | 11 ++- .../mindustry/entities/comp/WeaponsComp.java | 2 +- core/src/mindustry/io/SaveVersion.java | 7 +- 9 files changed, 144 insertions(+), 54 deletions(-) create mode 100644 core/src/mindustry/ai/types/SuicideAI.java diff --git a/core/src/mindustry/ai/types/GroundAI.java b/core/src/mindustry/ai/types/GroundAI.java index 8acef6526d..b32f431682 100644 --- a/core/src/mindustry/ai/types/GroundAI.java +++ b/core/src/mindustry/ai/types/GroundAI.java @@ -25,13 +25,11 @@ public class GroundAI extends AIController{ Tilec core = unit.closestEnemyCore(); if(core != null){ - float dst = unit.dst(core); - - if(dst < unit.range() / 1.1f){ + if(unit.within(core,unit.range() / 1.1f)){ target = core; } - if(dst > unit.range() * 0.5f){ + if(!unit.within(core, unit.range() * 0.5f)){ moveToCore(FlagTarget.enemyCores); } } diff --git a/core/src/mindustry/ai/types/SuicideAI.java b/core/src/mindustry/ai/types/SuicideAI.java new file mode 100644 index 0000000000..d56baec2e5 --- /dev/null +++ b/core/src/mindustry/ai/types/SuicideAI.java @@ -0,0 +1,70 @@ +package mindustry.ai.types; + +import mindustry.*; +import mindustry.ai.Pathfinder.*; +import mindustry.entities.*; +import mindustry.gen.*; +import mindustry.world.*; + +public class SuicideAI extends GroundAI{ + static boolean blockedByBlock; + + @Override + public void update(){ + + if(Units.invalidateTarget(target, unit.team(), unit.x(), unit.y(), Float.MAX_VALUE)){ + target = null; + } + + if(retarget()){ + targetClosest(); + } + + Tilec core = unit.closestEnemyCore(); + + boolean rotate = false, shoot = false; + + if(!Units.invalidateTarget(target, unit, unit.range())){ + rotate = true; + shoot = unit.within(target, unit.type().weapons.first().bullet.range() + + (target instanceof Tilec ? ((Tilec)target).block().size * Vars.tilesize / 2f : ((Hitboxc)target).hitSize() / 2f)); + + if(unit.type().hasWeapons()){ + unit.aimLook(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed)); + } + + blockedByBlock = false; + + //raycast for target + boolean blocked = Vars.world.raycast(unit.tileX(), unit.tileY(), target.tileX(), target.tileY(), (x, y) -> { + Tile tile = Vars.world.tile(x, y); + if(tile != null && tile.entity == target) return false; + if(tile != null && tile.entity != null && tile.entity.team() != unit.team()){ + blockedByBlock = true; + return true; + }else{ + return tile == null || tile.solid(); + } + }); + + //shoot when there's an enemy block in the way + if(blockedByBlock){ + shoot = true; + } + + if(!blocked){ + //move towards target directly + unit.moveAt(vec.set(target).sub(unit).limit(unit.type().speed)); + } + + }else{ + if(core != null){ + moveToCore(FlagTarget.enemyCores); + } + + if(unit.moving()) unit.lookAt(unit.vel().angle()); + } + + unit.controlWeapons(rotate, shoot); + } +} diff --git a/core/src/mindustry/content/Bullets.java b/core/src/mindustry/content/Bullets.java index 391237aa71..ff8e820cbb 100644 --- a/core/src/mindustry/content/Bullets.java +++ b/core/src/mindustry/content/Bullets.java @@ -36,7 +36,7 @@ public class Bullets implements ContentList{ waterShot, cryoShot, slagShot, oilShot, //environment, misc. - fireball, basicFlame, pyraFlame, driverBolt, healBullet, healBulletBig, frag, eruptorShot, + fireball, basicFlame, pyraFlame, driverBolt, healBullet, healBulletBig, frag, //bombs bombExplosive, bombIncendiary, bombOil; @@ -544,13 +544,6 @@ public class Bullets implements ContentList{ drag = 0.03f; }}; - eruptorShot = new LiquidBulletType(Liquids.slag){{ - damage = 2; - speed = 2.1f; - drag = 0.02f; - shootEffect = Fx.shootSmall; - }}; - oilShot = new LiquidBulletType(Liquids.oil){{ drag = 0.03f; }}; diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 38d22fa6c1..0ff88c9164 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -125,10 +125,10 @@ public class Fx{ stroke(3f * e.fout()); color(e.color, Color.white, e.fin()); - beginLine(); - linePoint(e.x, e.y); + beginLine(); lines.each(Lines::linePoint); + linePoint(e.x, e.y); endLine(); int i = 0; diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 55fe471ab0..1f77f1b843 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -119,23 +119,28 @@ public class UnitTypes implements ContentList{ }}; crawler = new UnitType("crawler"){{ + defaultController = SuicideAI::new; + speed = 0.8f; hitsize = 8f; - health = 120; + health = 140; sway = 0.25f; + range = 40f; + weapons.add(new Weapon(){{ reload = 12f; shootCone = 180f; ejectEffect = Fx.none; shootSound = Sounds.explosion; - bullet = new BombBulletType(2f, 3f, "clear"){{ + bullet = new BombBulletType(0f, 0f, "clear"){{ hitEffect = Fx.pulverize; - lifetime = 30f; - speed = 1.1f; + lifetime = 10f; + speed = 1f; splashDamageRadius = 55f; instantDisappear = true; splashDamage = 30f; killShooter = true; + hittable = false; }}; }}); }}; @@ -233,10 +238,16 @@ public class UnitTypes implements ContentList{ reload = 10f; alternate = true; ejectEffect = Fx.none; - bullet = Bullets.eruptorShot; recoil = 1f; x = 7f; shootSound = Sounds.flame; + + bullet = new LiquidBulletType(Liquids.slag){{ + damage = 11; + speed = 2.3f; + drag = 0.02f; + shootEffect = Fx.shootSmall; + }}; }}); }}; diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index 65f13e0c1b..5ca412c66f 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -207,47 +207,53 @@ public class Damage{ } } - public static void tileDamage(Team team, int startx, int starty, int radius, float baseDamage){ - bits.clear(); - propagation.clear(); - int bitOffset = bits.width() / 2; + public static void tileDamage(Team team, int startx, int starty, int baseRadius, float baseDamage){ + //tile damage is posted, so that destroying a block that causes a chain explosion will run in the next frame + //this prevents recursive damage calls from messing up temporary variables + Core.app.post(() -> { - propagation.addFirst(PropCell.get((byte)0, (byte)0, (short)baseDamage)); - //clamp radius to fit bits - radius = Math.min(radius, bits.width() / 2); + bits.clear(); + propagation.clear(); + int bitOffset = bits.width() / 2; - while(!propagation.isEmpty()){ - int prop = propagation.removeLast(); - int x = PropCell.x(prop); - int y = PropCell.y(prop); - int damage = PropCell.damage(prop); - //manhattan distance used for calculating falloff, results in a diamond pattern - int dst = Math.abs(x) + Math.abs(y); + propagation.addFirst(PropCell.get((byte)0, (byte)0, (short)baseDamage)); + //clamp radius to fit bits + int radius = Math.min(baseRadius, bits.width() / 2); - int scaledDamage = (int)(damage * (1f - (float)dst / radius)); + while(!propagation.isEmpty()){ + int prop = propagation.removeLast(); + int x = PropCell.x(prop); + int y = PropCell.y(prop); + int damage = PropCell.damage(prop); + //manhattan distance used for calculating falloff, results in a diamond pattern + int dst = Math.abs(x) + Math.abs(y); - bits.set(bitOffset + x, bitOffset + y); - Tilec tile = world.ent(startx + x, starty + y); + int scaledDamage = (int)(damage * (1f - (float)dst / radius)); - if(scaledDamage <= 0 || tile == null) continue; + bits.set(bitOffset + x, bitOffset + y); + Tile tile = world.tile(startx + x, starty + y); - //apply damage to entity if needed - if(tile.team() != team){ - int health = (int)tile.health(); - if(tile.health() > 0){ - tile.damage(scaledDamage); - scaledDamage -= health; + if(scaledDamage <= 0 || tile == null) continue; - if(scaledDamage <= 0) continue; + //apply damage to entity if needed + if(tile.entity != null && tile.team() != team){ + int health = (int)tile.entity.health(); + if(tile.entity.health() > 0){ + tile.entity.damage(scaledDamage); + scaledDamage -= health; + + if(scaledDamage <= 0) continue; + } + } + + for(Point2 p : Geometry.d4){ + if(!bits.get(bitOffset + x + p.x, bitOffset + y + p.y)){ + propagation.addFirst(PropCell.get((byte)(x + p.x), (byte)(y + p.y), (short)scaledDamage)); + } } } + }); - for(Point2 p : Geometry.d4){ - if(!bits.get(bitOffset + x + p.x, bitOffset + y + p.y)){ - propagation.addFirst(PropCell.get((byte)(x + p.x), (byte)(y + p.y), (short)scaledDamage)); - } - } - } } private static void completeDamage(Team team, float x, float y, float radius, float damage){ diff --git a/core/src/mindustry/entities/bullet/LiquidBulletType.java b/core/src/mindustry/entities/bullet/LiquidBulletType.java index 4a7ddf1718..835c8a4d32 100644 --- a/core/src/mindustry/entities/bullet/LiquidBulletType.java +++ b/core/src/mindustry/entities/bullet/LiquidBulletType.java @@ -30,7 +30,7 @@ public class LiquidBulletType extends BulletType{ hitEffect = Fx.hitLiquid; smokeEffect = Fx.none; shootEffect = Fx.none; - drag = 0.009f; + drag = 0.001f; knockback = 0.55f; } @@ -61,7 +61,14 @@ public class LiquidBulletType extends BulletType{ public void draw(Bulletc b){ Draw.color(liquid.color, Color.white, b.fout() / 100f); - Fill.circle(b.x(), b.y(), 0.5f + b.fout() * 2.5f); + Fill.circle(b.x(), b.y(), 3f); + } + + @Override + public void despawned(Bulletc b){ + super.despawned(b); + + hit(b, b.x(), b.y()); } @Override diff --git a/core/src/mindustry/entities/comp/WeaponsComp.java b/core/src/mindustry/entities/comp/WeaponsComp.java index dc69bba1c7..03bb238722 100644 --- a/core/src/mindustry/entities/comp/WeaponsComp.java +++ b/core/src/mindustry/entities/comp/WeaponsComp.java @@ -37,7 +37,7 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc{ void setupWeapons(UnitType def){ mounts = new WeaponMount[def.weapons.size]; - range = 0f; + range = def.range; for(int i = 0; i < mounts.length; i++){ mounts[i] = new WeaponMount(def.weapons.get(i)); range = Math.max(range, def.weapons.get(i).bullet.range()); diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index 0e9cb72ead..6e17150ca7 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -90,7 +90,8 @@ public abstract class SaveVersion extends SaveFileReader{ "height", world.height(), "viewpos", Tmp.v1.set(player == null ? Vec2.ZERO : player).toString(), "controlledType", headless || control.input.controlledType == null ? "null" : control.input.controlledType.name, - "nocores", state.rules.defaultTeam.cores().isEmpty() + "nocores", state.rules.defaultTeam.cores().isEmpty(), + "playerteam", player == null ? state.rules.defaultTeam.uid : player.team().uid ).merge(tags)); } @@ -111,6 +112,10 @@ public abstract class SaveVersion extends SaveFileReader{ player.set(Tmp.v1); control.input.controlledType = content.getByName(ContentType.unit, map.get("controlledType", "")); + Team team = Team.get(map.getInt("playerteam", state.rules.defaultTeam.uid)); + if(!net.client() && team != Team.derelict){ + player.team(team); + } } Map worldmap = maps.byName(map.get("mapname", "\\\\\\"));