New splash damage algorithm

This commit is contained in:
Anuken
2021-02-15 18:00:47 -05:00
parent 710a55dc2d
commit c942331117
5 changed files with 77 additions and 59 deletions
+6
View File
@@ -1609,6 +1609,12 @@ public class Fx{
Fill.square(e.x, e.y, e.rotation * tilesize / 2f); Fill.square(e.x, e.y, e.rotation * tilesize / 2f);
}), }),
lightBlock = new Effect(60, e -> {
color(e.color);
alpha(e.fout() * 1);
Fill.square(e.x, e.y, e.rotation * tilesize / 2f);
}),
overdriveBlockFull = new Effect(60, e -> { overdriveBlockFull = new Effect(60, e -> {
color(e.color); color(e.color);
alpha(e.fslope() * 0.4f); alpha(e.fslope() * 0.4f);
+7 -7
View File
@@ -565,9 +565,9 @@ public class UnitTypes implements ContentList{
hitEffect = Fx.pulverize; hitEffect = Fx.pulverize;
lifetime = 10f; lifetime = 10f;
speed = 1f; speed = 1f;
splashDamageRadius = 70f; splashDamageRadius = 58f;
instantDisappear = true; instantDisappear = true;
splashDamage = 80f; splashDamage = 85f;
killShooter = true; killShooter = true;
hittable = false; hittable = false;
collidesAir = true; collidesAir = true;
@@ -769,7 +769,7 @@ public class UnitTypes implements ContentList{
width = height = 19f; width = height = 19f;
collidesTiles = true; collidesTiles = true;
ammoMultiplier = 4f; ammoMultiplier = 4f;
splashDamageRadius = 95f; splashDamageRadius = 80f;
splashDamage = 65f; splashDamage = 65f;
backColor = Pal.sapBulletBack; backColor = Pal.sapBulletBack;
frontColor = lightningColor = Pal.sapBullet; frontColor = lightningColor = Pal.sapBullet;
@@ -867,7 +867,7 @@ public class UnitTypes implements ContentList{
width = height = 25f; width = height = 25f;
collidesTiles = collides = true; collidesTiles = collides = true;
ammoMultiplier = 4f; ammoMultiplier = 4f;
splashDamageRadius = 90f; splashDamageRadius = 80f;
splashDamage = 75f; splashDamage = 75f;
backColor = Pal.sapBulletBack; backColor = Pal.sapBulletBack;
frontColor = lightningColor = Pal.sapBullet; frontColor = lightningColor = Pal.sapBullet;
@@ -888,7 +888,7 @@ public class UnitTypes implements ContentList{
lifetime = 90f; lifetime = 90f;
width = height = 20f; width = height = 20f;
collidesTiles = false; collidesTiles = false;
splashDamageRadius = 80f; splashDamageRadius = 70f;
splashDamage = 40f; splashDamage = 40f;
backColor = Pal.sapBulletBack; backColor = Pal.sapBulletBack;
frontColor = lightningColor = Pal.sapBullet; frontColor = lightningColor = Pal.sapBullet;
@@ -1367,7 +1367,7 @@ public class UnitTypes implements ContentList{
healPercent = 15f; healPercent = 15f;
splashDamage = 230f; splashDamage = 230f;
splashDamageRadius = 120f; splashDamageRadius = 81f;
}}; }};
}}); }});
}}; }};
@@ -1537,7 +1537,7 @@ public class UnitTypes implements ContentList{
width = 15f; width = 15f;
collidesTiles = false; collidesTiles = false;
ammoMultiplier = 4f; ammoMultiplier = 4f;
splashDamageRadius = 60f; splashDamageRadius = 50f;
splashDamage = 80f; splashDamage = 80f;
backColor = Pal.missileYellowBack; backColor = Pal.missileYellowBack;
frontColor = Pal.missileYellow; frontColor = Pal.missileYellow;
+61 -49
View File
@@ -6,7 +6,6 @@ import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.core.*; import mindustry.core.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
@@ -25,11 +24,10 @@ public class Damage{
private static Rect hitrect = new Rect(); private static Rect hitrect = new Rect();
private static Vec2 tr = new Vec2(), seg1 = new Vec2(), seg2 = new Vec2(); private static Vec2 tr = new Vec2(), seg1 = new Vec2(), seg2 = new Vec2();
private static Seq<Unit> units = new Seq<>(); private static Seq<Unit> units = new Seq<>();
private static GridBits bits = new GridBits(30, 30);
private static IntQueue propagation = new IntQueue();
private static IntSet collidedBlocks = new IntSet(); private static IntSet collidedBlocks = new IntSet();
private static Building tmpBuilding; private static Building tmpBuilding;
private static Unit tmpUnit; private static Unit tmpUnit;
private static IntFloatMap damages = new IntFloatMap();
/** Creates a dynamic explosion based on specified parameters. */ /** Creates a dynamic explosion based on specified parameters. */
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage){ public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage){
@@ -365,64 +363,85 @@ public class Damage{
if(ground){ if(ground){
if(!complete){ if(!complete){
int trad = (int)(radius / tilesize); //increase damage slightly to compensate for new algorithm
Tile tile = world.tileWorld(x, y); tileDamage(team, World.toTile(x), World.toTile(y), radius / tilesize, damage * 1.1f);
if(tile != null){
tileDamage(team, tile.x, tile.y, trad, damage);
}
}else{ }else{
completeDamage(team, x, y, radius, damage); completeDamage(team, x, y, radius, damage);
} }
} }
} }
public static void tileDamage(Team team, int startx, int starty, int baseRadius, float baseDamage){ public static void tileDamage(Team team, int x, int y, float baseRadius, float damage){
//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(() -> { Core.app.post(() -> {
bits.clear(); var in = world.build(x, y);
propagation.clear(); //spawned inside a multiblock. this means that damage needs to be dealt directly.
int bitOffset = bits.width() / 2; //why? because otherwise the building would absorb everything in one cell, which means much less damage than a nearby explosion.
//this needs to be compensated
if(in != null && in.team != team && in.block.size > 1 && in.health > damage){
//deal the damage of an entire side * 2, to be equivalent with the maximum "standard" side damage + 1
in.damage(damage * (in.block.size * 2));
//no need to continue with the explosion
return;
}
propagation.addFirst(PropCell.get((byte)0, (byte)0, (short)baseDamage)); //cap radius to prevent lag
//clamp radius to fit bits float radius = Math.min(baseRadius, 30), rad2 = radius * radius;
int radius = Math.min(baseRadius, bits.width() / 2); int rays = Mathf.ceil(radius * 2 * Mathf.pi);
double spacing = Math.PI * 2.0 / rays;
damages.clear();
while(!propagation.isEmpty()){ //raycast from each angle
int prop = propagation.removeLast(); for(int i = 0; i <= rays; i++){
int x = PropCell.x(prop); float dealt = 0f;
int y = PropCell.y(prop); int startX = x;
int damage = PropCell.damage(prop); int startY = y;
//manhattan distance used for calculating falloff, results in a diamond pattern int endX = x + (int)(Math.cos(spacing * i) * radius), endY = y + (int)(Math.sin(spacing * i) * radius);
int dst = Math.abs(x) + Math.abs(y);
int scaledDamage = (int)(damage * (1f - (float)dst / radius)); int xDist = Math.abs(endX - startX);
int yDist = -Math.abs(endY - startY);
int xStep = (startX < endX ? +1 : -1);
int yStep = (startY < endY ? +1 : -1);
int error = xDist + yDist;
bits.set(bitOffset + x, bitOffset + y); while(startX != endX || startY != endY){
Tile tile = world.tile(startx + x, starty + y); var build = world.build(startX, startY);
if(build != null && build.team != team){
//damage dealt at circle edge
float edgeScale = 0.6f;
float mult = (1f-(Mathf.dst2(startX, startY, x, y) / rad2) + edgeScale) / (1f + edgeScale);
float next = damage * mult - dealt;
//register damage dealt
int p = Point2.pack(startX, startY);
damages.put(p, Math.max(damages.get(p), next));
//register as hit
dealt += build.health;
if(scaledDamage <= 0 || tile == null) continue; if(next - dealt <= 0){
break;
//apply damage to entity if needed }
if(tile.build != null && tile.build.team != team){
int health = (int)(tile.build.health / (tile.block().size * tile.block().size));
if(tile.build.health > 0){
tile.build.damage(scaledDamage);
scaledDamage -= health;
if(scaledDamage <= 0) continue;
} }
}
for(Point2 p : Geometry.d4){ if(2 * error - yDist > xDist - 2 * error){
if(!bits.get(bitOffset + x + p.x, bitOffset + y + p.y)){ error += yDist;
propagation.addFirst(PropCell.get((byte)(x + p.x), (byte)(y + p.y), (short)scaledDamage)); startX += xStep;
}else{
error += xDist;
startY += yStep;
} }
} }
} }
});
//apply damage
for(var e : damages){
int cx = Point2.x(e.key), cy = Point2.y(e.key);
var build = world.build(cx, cy);
if(build != null){
build.damage(e.value);
}
}
});
} }
private static void completeDamage(Team team, float x, float y, float radius, float damage){ private static void completeDamage(Team team, float x, float y, float radius, float damage){
@@ -443,11 +462,4 @@ public class Damage{
float scaled = Mathf.lerp(1f - dist / radius, 1f, falloff); float scaled = Mathf.lerp(1f - dist / radius, 1f, falloff);
return damage * scaled; return damage * scaled;
} }
@Struct
static class PropCellStruct{
byte x;
byte y;
short damage;
}
} }
@@ -15,7 +15,7 @@ import static mindustry.Vars.*;
public class AIController implements UnitController{ public class AIController implements UnitController{
protected static final Vec2 vec = new Vec2(); protected static final Vec2 vec = new Vec2();
protected static final int timerTarget = 0, timerTarget2 = 1, timerTarget3 = 2, timerTarget4 = 2; protected static final int timerTarget = 0, timerTarget2 = 1, timerTarget3 = 2, timerTarget4 = 3;
protected Unit unit; protected Unit unit;
protected Interval timer = new Interval(4); protected Interval timer = new Interval(4);
@@ -33,8 +33,8 @@ public class NuclearReactor extends PowerGenerator{
public float itemDuration = 120; //time to consume 1 fuel public float itemDuration = 120; //time to consume 1 fuel
public float heating = 0.01f; //heating per frame * fullness public float heating = 0.01f; //heating per frame * fullness
public float smokeThreshold = 0.3f; //threshold at which block starts smoking public float smokeThreshold = 0.3f; //threshold at which block starts smoking
public int explosionRadius = 40; public int explosionRadius = 20;
public int explosionDamage = 1350; public int explosionDamage = 1250;
public float flashThreshold = 0.46f; //heat threshold at which the lights start flashing public float flashThreshold = 0.46f; //heat threshold at which the lights start flashing
public float coolantPower = 0.5f; public float coolantPower = 0.5f;