More intelligent suicide units / Bugfixes
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
70
core/src/mindustry/ai/types/SuicideAI.java
Normal file
70
core/src/mindustry/ai/types/SuicideAI.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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", "\\\\\\"));
|
||||
|
||||
Reference in New Issue
Block a user