New splash damage algorithm
This commit is contained in:
@@ -1609,6 +1609,12 @@ public class Fx{
|
||||
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 -> {
|
||||
color(e.color);
|
||||
alpha(e.fslope() * 0.4f);
|
||||
|
||||
@@ -565,9 +565,9 @@ public class UnitTypes implements ContentList{
|
||||
hitEffect = Fx.pulverize;
|
||||
lifetime = 10f;
|
||||
speed = 1f;
|
||||
splashDamageRadius = 70f;
|
||||
splashDamageRadius = 58f;
|
||||
instantDisappear = true;
|
||||
splashDamage = 80f;
|
||||
splashDamage = 85f;
|
||||
killShooter = true;
|
||||
hittable = false;
|
||||
collidesAir = true;
|
||||
@@ -769,7 +769,7 @@ public class UnitTypes implements ContentList{
|
||||
width = height = 19f;
|
||||
collidesTiles = true;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamageRadius = 95f;
|
||||
splashDamageRadius = 80f;
|
||||
splashDamage = 65f;
|
||||
backColor = Pal.sapBulletBack;
|
||||
frontColor = lightningColor = Pal.sapBullet;
|
||||
@@ -867,7 +867,7 @@ public class UnitTypes implements ContentList{
|
||||
width = height = 25f;
|
||||
collidesTiles = collides = true;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamageRadius = 90f;
|
||||
splashDamageRadius = 80f;
|
||||
splashDamage = 75f;
|
||||
backColor = Pal.sapBulletBack;
|
||||
frontColor = lightningColor = Pal.sapBullet;
|
||||
@@ -888,7 +888,7 @@ public class UnitTypes implements ContentList{
|
||||
lifetime = 90f;
|
||||
width = height = 20f;
|
||||
collidesTiles = false;
|
||||
splashDamageRadius = 80f;
|
||||
splashDamageRadius = 70f;
|
||||
splashDamage = 40f;
|
||||
backColor = Pal.sapBulletBack;
|
||||
frontColor = lightningColor = Pal.sapBullet;
|
||||
@@ -1367,7 +1367,7 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
healPercent = 15f;
|
||||
splashDamage = 230f;
|
||||
splashDamageRadius = 120f;
|
||||
splashDamageRadius = 81f;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
@@ -1537,7 +1537,7 @@ public class UnitTypes implements ContentList{
|
||||
width = 15f;
|
||||
collidesTiles = false;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamageRadius = 60f;
|
||||
splashDamageRadius = 50f;
|
||||
splashDamage = 80f;
|
||||
backColor = Pal.missileYellowBack;
|
||||
frontColor = Pal.missileYellow;
|
||||
|
||||
@@ -6,7 +6,6 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.game.EventType.*;
|
||||
@@ -25,11 +24,10 @@ public class Damage{
|
||||
private static Rect hitrect = new Rect();
|
||||
private static Vec2 tr = new Vec2(), seg1 = new Vec2(), seg2 = new Vec2();
|
||||
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 Building tmpBuilding;
|
||||
private static Unit tmpUnit;
|
||||
private static IntFloatMap damages = new IntFloatMap();
|
||||
|
||||
/** 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){
|
||||
@@ -365,64 +363,85 @@ public class Damage{
|
||||
|
||||
if(ground){
|
||||
if(!complete){
|
||||
int trad = (int)(radius / tilesize);
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
if(tile != null){
|
||||
tileDamage(team, tile.x, tile.y, trad, damage);
|
||||
}
|
||||
//increase damage slightly to compensate for new algorithm
|
||||
tileDamage(team, World.toTile(x), World.toTile(y), radius / tilesize, damage * 1.1f);
|
||||
}else{
|
||||
completeDamage(team, x, y, radius, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
public static void tileDamage(Team team, int x, int y, float baseRadius, float damage){
|
||||
|
||||
Core.app.post(() -> {
|
||||
|
||||
bits.clear();
|
||||
propagation.clear();
|
||||
int bitOffset = bits.width() / 2;
|
||||
var in = world.build(x, y);
|
||||
//spawned inside a multiblock. this means that damage needs to be dealt directly.
|
||||
//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));
|
||||
//clamp radius to fit bits
|
||||
int radius = Math.min(baseRadius, bits.width() / 2);
|
||||
//cap radius to prevent lag
|
||||
float radius = Math.min(baseRadius, 30), rad2 = radius * radius;
|
||||
int rays = Mathf.ceil(radius * 2 * Mathf.pi);
|
||||
double spacing = Math.PI * 2.0 / rays;
|
||||
damages.clear();
|
||||
|
||||
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);
|
||||
//raycast from each angle
|
||||
for(int i = 0; i <= rays; i++){
|
||||
float dealt = 0f;
|
||||
int startX = x;
|
||||
int startY = y;
|
||||
int endX = x + (int)(Math.cos(spacing * i) * radius), endY = y + (int)(Math.sin(spacing * i) * radius);
|
||||
|
||||
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);
|
||||
Tile tile = world.tile(startx + x, starty + y);
|
||||
while(startX != endX || startY != endY){
|
||||
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;
|
||||
|
||||
//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;
|
||||
if(next - dealt <= 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
if(2 * error - yDist > xDist - 2 * error){
|
||||
error += yDist;
|
||||
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){
|
||||
@@ -443,11 +462,4 @@ public class Damage{
|
||||
float scaled = Mathf.lerp(1f - dist / radius, 1f, falloff);
|
||||
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{
|
||||
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 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 heating = 0.01f; //heating per frame * fullness
|
||||
public float smokeThreshold = 0.3f; //threshold at which block starts smoking
|
||||
public int explosionRadius = 40;
|
||||
public int explosionDamage = 1350;
|
||||
public int explosionRadius = 20;
|
||||
public int explosionDamage = 1250;
|
||||
public float flashThreshold = 0.46f; //heat threshold at which the lights start flashing
|
||||
public float coolantPower = 0.5f;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user