From c942331117cb3809f576eeb8a9ab29e4d183019e Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 15 Feb 2021 18:00:47 -0500 Subject: [PATCH] New splash damage algorithm --- core/src/mindustry/content/Fx.java | 6 + core/src/mindustry/content/UnitTypes.java | 14 +-- core/src/mindustry/entities/Damage.java | 110 ++++++++++-------- .../entities/units/AIController.java | 2 +- .../world/blocks/power/NuclearReactor.java | 4 +- 5 files changed, 77 insertions(+), 59 deletions(-) diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index a97b3386c6..2b5fd4220b 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -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); diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 194c7a605d..14767e5a7a 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -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; diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index 285259f60e..83639450d0 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -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 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; - } } diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index 71bde595ce..c4fc27792a 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -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); diff --git a/core/src/mindustry/world/blocks/power/NuclearReactor.java b/core/src/mindustry/world/blocks/power/NuclearReactor.java index aa7eed3b71..dd15da24cc 100644 --- a/core/src/mindustry/world/blocks/power/NuclearReactor.java +++ b/core/src/mindustry/world/blocks/power/NuclearReactor.java @@ -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;