Grouped targeting (closes #11537)

This commit is contained in:
Anuken
2026-09-03 19:02:51 -04:00
parent ef65b1f5b3
commit ca6b369b85
5 changed files with 48 additions and 6 deletions
+11 -2
View File
@@ -3652,7 +3652,7 @@ public class Blocks{
size = 1;
health = 260;
shootSound = Sounds.shootArc;
consumePower(3.3f);
coolant = consumeCoolant(0.1f);
coolantMultiplier = 30f;
@@ -4014,6 +4014,8 @@ public class Blocks{
frontColor = Pal.siliconAmmoFront;
lifeScaleRandMax = 1.08f;
lifeScaleRandMin = 0.95f;
unitSort = UnitSorts.closest;
}},
Items.pyratite, new ArtilleryBulletType(3f, 48){{
hitEffect = new MultiEffect(Fx.blastExplosion, Fx.shockwave);
@@ -4091,7 +4093,6 @@ public class Blocks{
ammoPerShot = 2;
velocityRnd = 0.2f;
scaleLifetimeOffset = 1f / 9f;
recoil = 6f;
shake = 2f;
range = 290f;
@@ -4101,6 +4102,8 @@ public class Blocks{
scaledHealth = 130;
depositCooldown = 2.0f;
shootSound = Sounds.shootRipple;
scaleLifetimeOffset = 1f / 10f;
unitSort = UnitSorts.grouped;
}};
cyclone = new ItemTurret("cyclone"){{
@@ -4694,6 +4697,8 @@ public class Blocks{
shrinkX = 0.2f;
shrinkY = 0.1f;
buildingDamageMultiplier = 0.3f;
unitSort = UnitSorts.grouped;
}},
Items.carbide, new ArtilleryBulletType(3.25f, 700, "shell"){{
hitEffect = new MultiEffect(Fx.titanExplosionSmall, Fx.titanSmokeSmall);
@@ -4732,6 +4737,8 @@ public class Blocks{
buildingDamageMultiplier = 0.2f;
fragLifeMin = 1.5f;
unitSort = UnitSorts.strongest;
fragBullets = 12;
fragBullet = new ArtilleryBulletType(0.5f, 50, "shell"){{
hitEffect = new MultiEffect(Fx.titanExplosionFrag, Fx.titanLightSmall, new WaveEffect(){{
@@ -4808,6 +4815,8 @@ public class Blocks{
buildingDamageMultiplier = 0f;
}};
}};
unitSort = UnitSorts.grouped;
}}
);
+7 -1
View File
@@ -15,10 +15,16 @@ public class UnitSorts{
mostArmor = (u, x, y) -> -u.armor + Mathf.dst2(u.x, u.y, x, y) / 6400f,
leastArmor = (u, x, y) -> u.armor + Mathf.dst2(u.x, u.y, x, y) / 6400f,
mostShield = (u, x, y) -> -u.shield + Mathf.dst2(u.x, u.y, x, y) / 6400f,
leastShield = (u, x, y) -> u.shield + Mathf.dst2(u.x, u.y, x, y) / 6400f;
leastShield = (u, x, y) -> u.shield + Mathf.dst2(u.x, u.y, x, y) / 6400f,
grouped = grouped(0.003f);
public static BuildingPriorityf
buildingDefault = b -> b.block.priority,
buildingWater = b -> b.block.priority + (b.liquids != null && b.liquids.get(Liquids.water) > 5f ? 10f : 0f);
/** @param dstWeight higher values make distance more important. Set to <= 0 to ignore distance. Ignores flying units. */
public static Sortf grouped(float dstWeight){
return (u, x, y) -> -u.team.data().getClustered(u.x, u.y) + (dstWeight > 0 ? Mathf.dst(u.x, u.y, x, y) * dstWeight : 0f);
}
}
@@ -14,6 +14,7 @@ import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.entities.Units.*;
import mindustry.entities.part.*;
import mindustry.entities.pattern.*;
import mindustry.game.EventType.*;
@@ -120,6 +121,8 @@ public class BulletType extends Content implements Cloneable{
public StatusEffect status = StatusEffects.none;
/** Intensity of applied status effect in terms of duration. */
public float statusDuration = 60 * 8f;
/** Turret only. Function for choosing which unit to target. Overrides turret sorting */
public Sortf unitSort = UnitSorts.closest;
/** Chance for this bullet to apply a status effect */
public float statusChance = 1f;
/** Turret only. If false, blocks will not be targeted. */
+23
View File
@@ -269,6 +269,7 @@ public class Teams{
public static class TeamData{
private static final IntSeq derelictBuffer = new IntSeq();
private static final float clusterChunkSize = 70f;
public final Team team;
@@ -278,6 +279,8 @@ public class Teams{
public @Nullable RtsAI rtsAi;
private boolean presentFlag;
private IntIntMap clusteredCounts = new IntIntMap();
private float lastClusterUpdateTimer = -100f;
/** Enemies with cores or spawn points. */
public Team[] coreEnemies = {};
@@ -470,6 +473,26 @@ public class Teams{
return team.rules().rtsAi || team.rules().buildAi;
}
/** @return approximate number of clustered ground units at a specific position */
public int getClustered(float x, float y){
//update based on ticks passed (no increment)
if(Time.time > lastClusterUpdateTimer + 10f){
lastClusterUpdateTimer = Time.time;
clusteredCounts.clear();
units.each(u -> {
//clusters are for artillery, which can't hit flying units
if(!u.isFlying()){
clusteredCounts.increment(clusterKey(u.x, u.y));
}
});
}
return clusteredCounts.get(clusterKey(x, y));
}
private static int clusterKey(float x, float y){
return ((Mathf.floor(x / clusterChunkSize) & 0xFFF) << 12) | (Mathf.floor(y / clusterChunkSize) & 0xFFF);
}
@Override
public String toString(){
return "TeamData{" +
@@ -639,14 +639,15 @@ public class Turret extends ReloadTurret{
}
protected Posc findEnemy(float range){
var ammo = peekAmmo();
Sortf sort = ammo.unitSort != UnitSorts.closest ? ammo.unitSort : unitSort;
if(targetAir && !targetGround){
return Units.bestEnemy(team, x, y, range, e -> !e.dead() && !e.isGrounded() && unitFilter.get(e), unitSort);
return Units.bestEnemy(team, x, y, range, e -> !e.dead() && !e.isGrounded() && unitFilter.get(e), sort);
}else{
var ammo = peekAmmo();
boolean buildings = targetGround && targetBlocks && (ammo == null || ammo.targetBlocks), missiles = ammo == null || ammo.targetMissiles;
return Units.bestTarget(team, x, y, range,
e -> !e.dead() && unitFilter.get(e) && (e.isGrounded() || targetAir) && (!e.isGrounded() || targetGround) && (missiles || !(e instanceof TimedKillc)),
b -> buildings && buildingFilter.get(b), unitSort);
b -> buildings && buildingFilter.get(b), sort);
}
}