More intelligent suicide units / Bugfixes

This commit is contained in:
Anuken
2020-06-06 11:36:14 -04:00
parent 1e3a190d5a
commit 0f76aeba05
9 changed files with 144 additions and 54 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}};

View File

@@ -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;

View File

@@ -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;
}};
}});
}};

View File

@@ -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){

View File

@@ -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

View File

@@ -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());

View File

@@ -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", "<none>"));
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", "\\\\\\"));