package mindustry.content;

import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import mindustry.entities.*;
import mindustry.entities.abilities.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.units.UnitAssembler.*;

import static arc.graphics.g2d.Draw.rect;
import static arc.graphics.g2d.Draw.*;
import static arc.graphics.g2d.Lines.*;
import static arc.math.Angles.*;
import static mindustry.Vars.*;

public class Fx{
    public static final Rand rand = new Rand();
    public static final Vec2 v = new Vec2();

    public static final Effect

    none = new Effect(0, 0f, e -> {}),

    blockCrash = new Effect(90f, e -> {
        if(!(e.data instanceof Block block)) return;

        alpha(e.fin() + 0.5f);
        float offset = Mathf.lerp(0f, 180f, e.fout());
        color(0f, 0f, 0f, 0.44f);
        rect(block.fullIcon, e.x - offset * 4f, e.y, (float)block.size * 8f, (float)block.size * 8f);
        color(Color.white);
        rect(block.fullIcon, e.x + offset, e.y + offset * 5f, (float)block.size * 8f, (float)block.size * 8f);
    }),

    trailFade = new Effect(400f, e -> {
        if(!(e.data instanceof Trail trail)) return;
        //lifetime is how many frames it takes to fade out the trail
        e.lifetime = trail.length * 1.4f;

        if(!state.isPaused()){
            trail.shorten();
        }
        trail.drawCap(e.color, e.rotation);
        trail.draw(e.color, e.rotation);
    }),

    unitSpawn = new Effect(30f, e -> {
        if(!(e.data instanceof UnitType unit)) return;

        TextureRegion region = unit.fullIcon;

        float scl = (1f + e.fout() * 2f) * region.scl();

        alpha(e.fout());
        mixcol(Color.white, e.fin());

        rect(region, e.x, e.y, 180f);

        reset();

        alpha(e.fin());

        rect(region, e.x, e.y, region.width * scl, region.height * scl, e.rotation - 90);
    }),

    unitCapKill = new Effect(80f, e -> {
        color(Color.scarlet);
        alpha(e.fout(Interp.pow4Out));

        float size = 10f + e.fout(Interp.pow10In) * 25f;
        Draw.rect(Icon.warning.getRegion(), e.x, e.y, size, size);
    }),

    unitEnvKill = new Effect(80f, e -> {
        color(Color.scarlet);
        alpha(e.fout(Interp.pow4Out));

        float size = 10f + e.fout(Interp.pow10In) * 25f;
        Draw.rect(Icon.cancel.getRegion(), e.x, e.y, size, size);
    }),

    unitControl = new Effect(30f, e -> {
        if(!(e.data instanceof Unit select)) return;

        boolean block = select instanceof BlockUnitc;

        mixcol(Pal.accent, 1f);
        alpha(e.fout());
        rect(block ? ((BlockUnitc)select).tile().block.fullIcon : select.type.fullIcon, select.x, select.y, block ? 0f : select.rotation - 90f);
        alpha(1f);
        Lines.stroke(e.fslope());
        Lines.square(select.x, select.y, e.fout() * select.hitSize * 2f, 45);
        Lines.stroke(e.fslope() * 2f);
        Lines.square(select.x, select.y, e.fout() * select.hitSize * 3f, 45f);
        reset();
    }),

    unitDespawn = new Effect(100f, e -> {
        if(!(e.data instanceof Unit select) || select.type == null) return;

        float scl = e.fout(Interp.pow2Out);
        float p = Draw.scl;
        Draw.scl *= scl;

        mixcol(Pal.accent, 1f);
        rect(select.type.fullIcon, select.x, select.y, select.rotation - 90f);
        reset();

        Draw.scl = p;
    }),

    unitSpirit = new Effect(17f, e -> {
        if(!(e.data instanceof Position to)) return;

        color(Pal.accent);

        Tmp.v1.set(e.x, e.y).interpolate(Tmp.v2.set(to), e.fin(), Interp.pow2In);
        float x = Tmp.v1.x, y = Tmp.v1.y;
        float size = 2.5f * e.fin();

        Fill.square(x, y, 1.5f * size, 45f);

        Tmp.v1.set(e.x, e.y).interpolate(Tmp.v2.set(to), e.fin(), Interp.pow5In);
        x = Tmp.v1.x;
        y = Tmp.v1.y;

        Fill.square(x, y, size, 45f);
    }),

    itemTransfer = new Effect(12f, e -> {
        if(!(e.data instanceof Position to)) return;
        Tmp.v1.set(e.x, e.y).interpolate(Tmp.v2.set(to), e.fin(), Interp.pow3)
        .add(Tmp.v2.sub(e.x, e.y).nor().rotate90(1).scl(Mathf.randomSeedRange(e.id, 1f) * e.fslope() * 10f));
        float x = Tmp.v1.x, y = Tmp.v1.y;
        float size = 1f;

        color(Pal.accent);
        Fill.circle(x, y, e.fslope() * 3f * size);

        color(e.color);
        Fill.circle(x, y, e.fslope() * 1.5f * size);
    }),

    pointBeam = new Effect(25f, 300f, e -> {
        if(!(e.data instanceof Position pos)) return;

        Draw.color(e.color, e.fout());
        Lines.stroke(1.5f);
        Lines.line(e.x, e.y, pos.getX(), pos.getY());
        Drawf.light(e.x, e.y, pos.getX(), pos.getY(), 20f, e.color, 0.6f * e.fout());
    }),

    pointHit = new Effect(8f, e -> {
        color(Color.white, e.color, e.fin());
        stroke(e.fout() + 0.2f);
        Lines.circle(e.x, e.y, e.fin() * 6f);
    }),

    lightning = new Effect(10f, 500f, e -> {
        if(!(e.data instanceof Seq)) return;
        Seq<Vec2> lines = e.data();

        stroke(3f * e.fout());
        color(e.color, Color.white, e.fin());

        for(int i = 0; i < lines.size - 1; i++){
            Vec2 cur = lines.get(i);
            Vec2 next = lines.get(i + 1);

            Lines.line(cur.x, cur.y, next.x, next.y, false);
        }

        for(Vec2 p : lines){
            Fill.circle(p.x, p.y, Lines.getStroke() / 2f);
        }
    }),

    coreBuildShockwave = new Effect(120, 500f, e -> {
        e.lifetime = e.rotation;

        color(Pal.command);
        stroke(e.fout(Interp.pow5Out) * 4f);
        Lines.circle(e.x, e.y, e.fin() * e.rotation * 2f);
    }),

    coreBuildBlock = new Effect(80f, e -> {
        if(!(e.data instanceof Block block)) return;

        mixcol(Pal.accent, 1f);
        alpha(e.fout());
        rect(block.fullIcon, e.x, e.y);
    }).layer(Layer.turret - 5f),

    pointShockwave = new Effect(20, e -> {
        color(e.color);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, e.finpow() * e.rotation);
        randLenVectors(e.id + 1, 8, 1f + 23f * e.finpow(), (x, y) ->
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f));
    }),

    moveCommand = new Effect(20, e -> {
        color(Pal.command);
        stroke(e.fout() * 5f);
        Lines.circle(e.x, e.y, 6f + e.fin() * 2f);
    }).layer(Layer.overlayUI),

    attackCommand = new Effect(20, e -> {
        color(Pal.remove);
        stroke(e.fout() * 5f);
        poly(e.x, e.y, 4, 7f + e.fin() * 2f);
    }).layer(Layer.overlayUI),

    commandSend = new Effect(28, e -> {
        color(Pal.command);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 4f + e.finpow() * e.rotation);
    }),

    upgradeCore = new Effect(120f, e -> {
        if(!(e.data instanceof Block block)) return;

        mixcol(Tmp.c1.set(Color.white).lerp(Pal.accent, e.fin()), 1f);
        alpha(e.fout());
        rect(block.fullIcon, e.x, e.y);
    }).layer(Layer.turret - 5f),

    upgradeCoreBloom = new Effect(80f, e -> {
        color(Pal.accent);
        stroke(4f * e.fout());
        Lines.square(e.x, e.y, tilesize / 2f * e.rotation + 2f);
    }),

    placeBlock = new Effect(16, e -> {
        color(Pal.accent);
        stroke(3f - e.fin() * 2f);
        Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f);
    }),

    coreLaunchConstruct = new Effect(35, e -> {
        color(Pal.accent);
        stroke(4f - e.fin() * 3f);
        Lines.square(e.x, e.y, tilesize / 2f * e.rotation * 1.2f + e.fin() * 5f);

        randLenVectors(e.id, 5 + (int)(e.rotation * 5), e.rotation * 3f + (tilesize * e.rotation) * e.finpow() * 1.5f, (x, y) -> {
            Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * (4f + e.rotation));
        });
    }),

    tapBlock = new Effect(12, e -> {
        color(Pal.accent);
        stroke(3f - e.fin() * 2f);
        Lines.circle(e.x, e.y, 4f + (tilesize / 1.5f * e.rotation) * e.fin());
    }),

    breakBlock = new Effect(12, e -> {
        color(Pal.remove);
        stroke(3f - e.fin() * 2f);
        Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f);

        randLenVectors(e.id, 3 + (int)(e.rotation * 3), e.rotation * 2f + (tilesize * e.rotation) * e.finpow(), (x, y) -> {
            Fill.square(e.x + x, e.y + y, 1f + e.fout() * (3f + e.rotation));
        });
    }),

    payloadDeposit = new Effect(30f, e -> {
        if(!(e.data instanceof YeetData data)) return;
        Tmp.v1.set(e.x, e.y).lerp(data.target, e.finpow());
        float x = Tmp.v1.x, y = Tmp.v1.y;

        scl(e.fout(Interp.pow3Out) * 1.05f);
        if(data.item instanceof Block block){
            Drawf.squareShadow(x, y, block.size * tilesize * 1.85f, 1f);
        }else if(data.item instanceof UnitType unit){
            unit.drawSoftShadow(e.x, e.y, e.rotation, 1f);
        }

        mixcol(Pal.accent, e.fin());
        rect(data.item.fullIcon, x, y, data.item instanceof Block ? 0f : e.rotation - 90f);
    }).layer(Layer.flyingUnitLow - 5f),

    select = new Effect(23, e -> {
        color(Pal.accent);
        stroke(e.fout() * 3f);
        Lines.circle(e.x, e.y, 3f + e.fin() * 14f);
    }),

    smoke = new Effect(100, e -> {
        color(Color.gray, Pal.darkishGray, e.fin());
        Fill.circle(e.x, e.y, (7f - e.fin() * 7f)/2f);
    }),

    fallSmoke = new Effect(110, e -> {
        color(Color.gray, Color.darkGray, e.rotation);
        Fill.circle(e.x, e.y, e.fout() * 3.5f);
    }),

    unitWreck = new Effect(200f, e -> {
        if(!(e.data instanceof TextureRegion reg)) return;

        Draw.mixcol(Pal.rubble, 1f);

        float vel = e.fin(Interp.pow5Out) * 2f * Mathf.randomSeed(e.id, 1f);
        float totalRot = Mathf.randomSeed(e.id + 1, 10f);
        Tmp.v1.trns(Mathf.randomSeed(e.id + 2, 360f), vel);

        Draw.z(Mathf.lerp(Layer.flyingUnitLow, Layer.debris, e.fin()));
        Draw.alpha(e.fout(Interp.pow5Out));

        Draw.rect(reg, e.x + Tmp.v1.x, e.y + Tmp.v1.y, e.rotation - 90 + totalRot * e.fin(Interp.pow5Out));
    }),

    rocketSmoke = new Effect(120, e -> {
        color(Color.gray);
        alpha(Mathf.clamp(e.fout()*1.6f - Interp.pow3In.apply(e.rotation)*1.2f));
        Fill.circle(e.x, e.y, (1f + 6f * e.rotation) - e.fin()*2f);
    }),

    rocketSmokeLarge = new Effect(220, e -> {
        color(Color.gray);
        alpha(Mathf.clamp(e.fout()*1.6f - Interp.pow3In.apply(e.rotation)*1.2f));
        Fill.circle(e.x, e.y, (1f + 6f * e.rotation * 1.3f) - e.fin()*2f);
    }),

    magmasmoke = new Effect(110, e -> {
        color(Color.gray);
        Fill.circle(e.x, e.y, e.fslope() * 6f);
    }),

    spawn = new Effect(30, e -> {
        stroke(2f * e.fout());
        color(Pal.accent);
        Lines.poly(e.x, e.y, 4, 5f + e.fin() * 12f);
    }),

    unitAssemble = new Effect(70, e -> {
        if(!(e.data instanceof UnitType type)) return;

        alpha(e.fout());
        mixcol(Pal.accent, e.fout());
        rect(type.fullIcon, e.x, e.y, e.rotation);
    }).layer(Layer.flyingUnit + 5f),

    padlaunch = new Effect(10, e -> {
        stroke(4f * e.fout());
        color(Pal.accent);
        Lines.poly(e.x, e.y, 4, 5f + e.fin() * 60f);
    }),

    breakProp = new Effect(23, e -> {
        float scl = Math.max(e.rotation, 1);
        color(Tmp.c1.set(e.color).mul(1.1f));
        randLenVectors(e.id, 6, 19f * e.finpow() * scl, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 3.5f * scl + 0.3f);
        });
    }).layer(Layer.debris),

    unitDrop = new Effect(30, e -> {
        color(Pal.lightishGray);
        randLenVectors(e.id, 9, 3 + 20f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.4f);
        });
    }).layer(Layer.debris),

    unitLand = new Effect(30, e -> {
        color(Tmp.c1.set(e.color).mul(1.1f));
        //TODO doesn't respect rotation / size
        randLenVectors(e.id, 6, 17f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.3f);
        });
    }).layer(Layer.debris),

    unitDust = new Effect(30, e -> {
        color(Tmp.c1.set(e.color).mul(1.3f));
        randLenVectors(e.id, 3, 8f * e.finpow(), e.rotation, 30f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.3f);
        });
    }).layer(Layer.debris),

    unitLandSmall = new Effect(30, e -> {
        color(Tmp.c1.set(e.color).mul(1.1f));
        randLenVectors(e.id, (int)(6 * e.rotation), 12f * e.finpow() * e.rotation, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.1f);
        });
    }).layer(Layer.debris),

    unitPickup = new Effect(18, e -> {
        color(Pal.lightishGray);
        stroke(e.fin() * 2f);
        Lines.poly(e.x, e.y, 4, 13f * e.fout());
    }).layer(Layer.debris),

    crawlDust = new Effect(35, e -> {
        color(Tmp.c1.set(e.color).mul(1.6f));
        randLenVectors(e.id, 2, 10f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fslope() * 4f + 0.3f);
        });
    }).layer(Layer.debris),

    landShock = new Effect(12, e -> {
        color(Pal.lancerLaser);
        stroke(e.fout() * 3f);
        Lines.poly(e.x, e.y, 12, 20f * e.fout());
    }).layer(Layer.debris),

    pickup = new Effect(18, e -> {
        color(Pal.lightishGray);
        stroke(e.fout() * 2f);
        Lines.spikes(e.x, e.y, 1f + e.fin() * 6f, e.fout() * 4f, 6);
    }),

    sparkExplosion = new Effect(30f, 160f, e -> {
        color(e.color);
        stroke(e.fout() * 3f);
        float circleRad = 6f + e.finpow() * e.rotation;
        Lines.circle(e.x, e.y, circleRad);

        rand.setSeed(e.id);
        for(int i = 0; i < 16; i++){
            float angle = rand.random(360f);
            float lenRand = rand.random(0.5f, 1f);
            Lines.lineAngle(e.x, e.y, angle, e.foutpow() * e.rotation * 0.8f * rand.random(1f, 0.6f) + 2f, e.finpow() * e.rotation * 1.2f * lenRand + 6f);
        }
    }),

    titanExplosion = new Effect(30f, 160f, e -> {
        color(e.color);
        stroke(e.fout() * 3f);
        float circleRad = 6f + e.finpow() * 60f;
        Lines.circle(e.x, e.y, circleRad);

        rand.setSeed(e.id);
        for(int i = 0; i < 16; i++){
            float angle = rand.random(360f);
            float lenRand = rand.random(0.5f, 1f);
            Lines.lineAngle(e.x, e.y, angle, e.foutpow() * 50f * rand.random(1f, 0.6f) + 2f, e.finpow() * 70f * lenRand + 6f);
        }
    }),

    titanExplosionLarge = new Effect(45f, 220f, e -> {
        color(e.color);
        stroke(e.fout() * 3f);
        float circleRad = 6f + e.finpow() * 110f;
        Lines.circle(e.x, e.y, circleRad);

        rand.setSeed(e.id);
        for(int i = 0; i < 21; i++){
            float angle = rand.random(360f);
            float lenRand = rand.random(0.5f, 1f);
            Lines.lineAngle(e.x, e.y, angle, e.foutpow() * 50f * rand.random(1f, 0.6f) + 2f, e.finpow() * 100f * lenRand + 6f);
        }
    }),

    titanExplosionSmall = new Effect(22f, 120f, e -> {
        color(e.color);
        stroke(e.fout() * 3f);
        float circleRad = 6f + e.finpow() * 45f;
        Lines.circle(e.x, e.y, circleRad);

        rand.setSeed(e.id);
        for(int i = 0; i < 12; i++){
            float angle = rand.random(360f);
            float lenRand = rand.random(0.5f, 1f);
            Lines.lineAngle(e.x, e.y, angle, e.foutpow() * 50f * rand.random(1f, 0.6f) + 2f, e.finpow() * 50f * lenRand + 6f);
        }
    }),

    titanExplosionFrag = new Effect(20f, 50f, e -> {
        color(e.color);
        stroke(e.fout() * 2f);
        float circleRad = 6f + e.finpow() * 20f;
        Lines.circle(e.x, e.y, circleRad);

        rand.setSeed(e.id);
        for(int i = 0; i < 8; i++){
            float angle = rand.random(360f);
            float lenRand = rand.random(0.5f, 1f);
            Tmp.v1.trns(angle, circleRad);

            for(int s : Mathf.signs){
                Drawf.tri(e.x + Tmp.v1.x, e.y + Tmp.v1.y, e.foutpow() * 15f, e.fout() * 20f * lenRand + 6f, angle + 90f + s * 90f);
            }
        }
    }),

    titanSmoke = new Effect(300f, 300f, b -> {
        float intensity = 3f;

        color(b.color, 0.7f);
        for(int i = 0; i < 4; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.5f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(2.9f * intensity), 22f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    float rad = fout * ((2f + intensity) * 2.35f);

                    Fill.circle(e.x + x, e.y + y, rad);
                    Drawf.light(e.x + x, e.y + y, rad * 2.5f, b.color, 0.5f);
                });
            });
        }
    }),

    titanSmokeLarge = new Effect(400f, 400f, b -> {
        float intensity = 4f;

        color(b.color, 0.65f);
        for(int i = 0; i < 4; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.5f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(2.9f * intensity), 26f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    float rad = fout * ((2f + intensity) * 2.35f);

                    Fill.circle(e.x + x, e.y + y, rad);
                    Drawf.light(e.x + x, e.y + y, rad * 2.5f, b.color, 0.5f);
                });
            });
        }
    }),
    titanSmokeSmall = new Effect(200f, 200f, b -> {
        float intensity = 2.5f;

        color(b.color, 0.7f);
        for(int i = 0; i < 4; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.5f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(2.9f * intensity), 18f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    float rad = fout * ((2f + intensity) * 2.35f);

                    Fill.circle(e.x + x, e.y + y, rad);
                    Drawf.light(e.x + x, e.y + y, rad * 2.5f, b.color, 0.5f);
                });
            });
        }
    }),


    smokeAoeCloud = new Effect(60f * 3f, 250f, e -> {
        color(e.color, 0.65f);

        randLenVectors(e.id, 80, 90f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 6f * Mathf.clamp(e.fin() / 0.1f) * Mathf.clamp(e.fout() / 0.1f));
        });
    }),

    missileTrailSmoke = new Effect(180f, 300f, b -> {
        float intensity = 2f;

        color(b.color, 0.7f);
        for(int i = 0; i < 4; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.5f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(2.9f * intensity), 13f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    float rad = fout * ((2f + intensity) * 2.35f);

                    Fill.circle(e.x + x, e.y + y, rad);
                    Drawf.light(e.x + x, e.y + y, rad * 2.5f, b.color, 0.5f);
                });
            });
        }
    }).layer(Layer.bullet - 1f),

    missileTrailSmokeSmall = new Effect(120f, 200f, b -> {
        float intensity = 1.3f;

        color(b.color, 0.7f);
        for(int i = 0; i < 3; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.5f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(2.9f * intensity), 13f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    float rad = fout * ((2f + intensity) * 2.35f);

                    Fill.circle(e.x + x, e.y + y, rad);
                    Drawf.light(e.x + x, e.y + y, rad * 2.5f, b.color, 0.5f);
                });
            });
        }
    }).layer(Layer.bullet - 1f),

    neoplasmSplat = new Effect(400f, 300f, b -> {
        float intensity = 3f;

        color(Pal.neoplasm1);
        for(int i = 0; i < 4; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.5f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(5f * intensity), 22f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    float rad = fout * ((2f + intensity) * 1.35f);

                    Fill.circle(e.x + x, e.y + y, rad);
                    Drawf.light(e.x + x, e.y + y, rad * 2.5f, b.color, 0.5f);
                });
            });
        }
    }).layer(Layer.bullet - 2f),

    scatheExplosion = new Effect(60f, 160f, e -> {
        color(e.color);
        stroke(e.fout() * 5f);
        float circleRad = 6f + e.finpow() * 60f;
        Lines.circle(e.x, e.y, circleRad);

        rand.setSeed(e.id);
        for(int i = 0; i < 16; i++){
            float angle = rand.random(360f);
            float lenRand = rand.random(0.5f, 1f);
            Tmp.v1.trns(angle, circleRad);

            for(int s : Mathf.signs){
                Drawf.tri(e.x + Tmp.v1.x, e.y + Tmp.v1.y, e.foutpow() * 40f, e.fout() * 30f * lenRand + 6f, angle + 90f + s * 90f);
            }
        }
    }),

    scatheExplosionSmall = new Effect(40f, 160f, e -> {
        color(e.color);
        stroke(e.fout() * 4f);
        float circleRad = 6f + e.finpow() * 40f;
        Lines.circle(e.x, e.y, circleRad);

        rand.setSeed(e.id);
        for(int i = 0; i < 16; i++){
            float angle = rand.random(360f);
            float lenRand = rand.random(0.5f, 1f);
            Tmp.v1.trns(angle, circleRad);

            for(int s : Mathf.signs){
                Drawf.tri(e.x + Tmp.v1.x, e.y + Tmp.v1.y, e.foutpow() * 30f, e.fout() * 25f * lenRand + 6f, angle + 90f + s * 90f);
            }
        }
    }),

    scatheLight = new Effect(60f, 160f, e -> {
        float circleRad = 6f + e.finpow() * 60f;

        color(e.color, e.foutpow());
        Fill.circle(e.x, e.y, circleRad);
    }).layer(Layer.bullet + 2f),

    scatheLightSmall = new Effect(60f, 160f, e -> {
        float circleRad = 6f + e.finpow() * 40f;

        color(e.color, e.foutpow());
        Fill.circle(e.x, e.y, circleRad);
    }).layer(Layer.bullet + 2f),

    titanLightSmall = new Effect(40f, 100f, e -> {
        float circleRad = 6f + e.finpow() * 20f;

        color(e.color, e.foutpow());
        Fill.circle(e.x, e.y, circleRad);
    }).layer(Layer.bullet + 2f),

    scatheSlash = new Effect(40f, 160f, e -> {
        Draw.color(e.color);
        for(int s : Mathf.signs){
            Drawf.tri(e.x, e.y, e.fout() * 25f, e.foutpow() * 66f + 6f, e.rotation + s * 90f);
        }
    }),

    dynamicSpikes = new Effect(40f, 100f, e -> {
        color(e.color);
        stroke(e.fout() * 2f);
        float circleRad = 4f + e.finpow() * e.rotation;
        Lines.circle(e.x, e.y, circleRad);

        for(int i = 0; i < 4; i++){
            Drawf.tri(e.x, e.y, 6f, e.rotation * 1.5f * e.fout(), i*90);
        }

        color();
        for(int i = 0; i < 4; i++){
            Drawf.tri(e.x, e.y, 3f, e.rotation * 1.45f / 3f * e.fout(), i*90);
        }

        Drawf.light(e.x, e.y, circleRad * 1.6f, Pal.heal, e.fout());
    }),

    greenBomb = new Effect(40f, 100f, e -> {
        color(Pal.heal);
        stroke(e.fout() * 2f);
        float circleRad = 4f + e.finpow() * 65f;
        Lines.circle(e.x, e.y, circleRad);

        color(Pal.heal);
        for(int i = 0; i < 4; i++){
            Drawf.tri(e.x, e.y, 6f, 100f * e.fout(), i*90);
        }

        color();
        for(int i = 0; i < 4; i++){
            Drawf.tri(e.x, e.y, 3f, 35f * e.fout(), i*90);
        }

        Drawf.light(e.x, e.y, circleRad * 1.6f, Pal.heal, e.fout());
    }),

    greenLaserCharge = new Effect(80f, 100f, e -> {
        color(Pal.heal);
        stroke(e.fin() * 2f);
        Lines.circle(e.x, e.y, 4f + e.fout() * 100f);

        Fill.circle(e.x, e.y, e.fin() * 20);

        randLenVectors(e.id, 20, 40f * e.fout(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fin() * 5f);
            Drawf.light(e.x + x, e.y + y, e.fin() * 15f, Pal.heal, 0.7f);
        });

        color();

        Fill.circle(e.x, e.y, e.fin() * 10);
        Drawf.light(e.x, e.y, e.fin() * 20f, Pal.heal, 0.7f);
    }).followParent(true).rotWithParent(true),

    greenLaserChargeSmall = new Effect(40f, 100f, e -> {
        color(Pal.heal);
        stroke(e.fin() * 2f);
        Lines.circle(e.x, e.y, e.fout() * 50f);
    }).followParent(true).rotWithParent(true),

    greenCloud = new Effect(80f, e -> {
        color(Pal.heal);
        randLenVectors(e.id, e.fin(), 7, 9f, (x, y, fin, fout) -> {
            Fill.circle(e.x + x, e.y + y, 5f * fout);
        });
    }),

    healWaveDynamic = new Effect(22, e -> {
        color(Pal.heal);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 4f + e.finpow() * e.rotation);
    }),

    healWave = new Effect(22, e -> {
        color(Pal.heal);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 4f + e.finpow() * 60f);
    }),

    heal = new Effect(11, e -> {
        color(Pal.heal);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 2f + e.finpow() * 7f);
    }),

    dynamicWave = new Effect(22, e -> {
        color(e.color, 0.7f);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 4f + e.finpow() * e.rotation);
    }),

    shieldWave = new Effect(22, e -> {
        color(e.color, 0.7f);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 4f + e.finpow() * 60f);
    }),

    shieldApply = new Effect(11, e -> {
        color(e.color, 0.7f);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 2f + e.finpow() * 7f);
    }),

    disperseTrail = new Effect(13, e -> {
        color(Color.white, e.color, e.fin());
        stroke(0.6f + e.fout() * 1.7f);
        rand.setSeed(e.id);

        for(int i = 0; i < 2; i++){
            float rot = e.rotation + rand.range(15f) + 180f;
            v.trns(rot, rand.random(e.fin() * 27f));
            lineAngle(e.x + v.x, e.y + v.y, rot, e.fout() * rand.random(2f, 7f) + 1.5f);
        }
    }),


    hitBulletSmall = new Effect(14, e -> {
        color(Color.white, Pal.lightOrange, e.fin());

        e.scaled(7f, s -> {
            stroke(0.5f + s.fout());
            Lines.circle(e.x, e.y, s.fin() * 5f);
        });

        stroke(0.5f + e.fout());

        randLenVectors(e.id, 5, e.fin() * 15f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 3 + 1f);
        });

        Drawf.light(e.x, e.y, 20f, Pal.lightOrange, 0.6f * e.fout());
    }),

    hitBulletColor = new Effect(14, e -> {
        color(Color.white, e.color, e.fin());

        e.scaled(7f, s -> {
            stroke(0.5f + s.fout());
            Lines.circle(e.x, e.y, s.fin() * 5f);
        });

        stroke(0.5f + e.fout());

        randLenVectors(e.id, 5, e.fin() * 15f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 3 + 1f);
        });

        Drawf.light(e.x, e.y, 20f, e.color, 0.6f * e.fout());
    }),

    hitSquaresColor = new Effect(14, e -> {
        color(Color.white, e.color, e.fin());

        e.scaled(7f, s -> {
            stroke(0.5f + s.fout());
            Lines.circle(e.x, e.y, s.fin() * 5f);
        });

        stroke(0.5f + e.fout());

        randLenVectors(e.id, 5, e.fin() * 17f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            Fill.square(e.x + x, e.y + y, e.fout() * 3.2f, ang);
        });

        Drawf.light(e.x, e.y, 20f, e.color, 0.6f * e.fout());
    }),

    hitFuse = new Effect(14, e -> {
        color(Color.white, Pal.surge, e.fin());

        e.scaled(7f, s -> {
            stroke(0.5f + s.fout());
            Lines.circle(e.x, e.y, s.fin() * 7f);
        });

        stroke(0.5f + e.fout());

        randLenVectors(e.id, 6, e.fin() * 15f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 3 + 1f);
        });
    }),

    hitBulletBig = new Effect(13, e -> {
        color(Color.white, Pal.lightOrange, e.fin());
        stroke(0.5f + e.fout() * 1.5f);

        randLenVectors(e.id, 8, e.finpow() * 30f, e.rotation, 50f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1.5f);
        });
    }),

    hitFlameSmall = new Effect(14, e -> {
        color(Pal.lightFlame, Pal.darkFlame, e.fin());
        stroke(0.5f + e.fout());

        randLenVectors(e.id, 2, 1f + e.fin() * 15f, e.rotation, 50f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 3 + 1f);
        });
    }),

    hitFlamePlasma = new Effect(14, e -> {
        color(Color.white, Pal.heal, e.fin());
        stroke(0.5f + e.fout());

        randLenVectors(e.id, 2, 1f + e.fin() * 15f, e.rotation, 50f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 3 + 1f);
        });
    }),

    hitLiquid = new Effect(16, e -> {
        color(e.color);

        randLenVectors(e.id, 5, 1f + e.fin() * 15f, e.rotation, 60f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 2f);
        });
    }),

    hitLaserBlast = new Effect(12, e -> {
        color(e.color);
        stroke(e.fout() * 1.5f);

        randLenVectors(e.id, 8, e.finpow() * 17f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f);
        });
    }),

    hitEmpSpark = new Effect(40, e -> {
        color(Pal.heal);
        stroke(e.fout() * 1.6f);

        randLenVectors(e.id, 18, e.finpow() * 27f, e.rotation, 360f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 6 + 1f);
        });
    }),

    hitLancer = new Effect(12, e -> {
        color(Color.white);
        stroke(e.fout() * 1.5f);

        randLenVectors(e.id, 8, e.finpow() * 17f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f);
        });
    }),

    hitBeam = new Effect(12, e -> {
        color(e.color);
        stroke(e.fout() * 2f);

        randLenVectors(e.id, 6, e.finpow() * 18f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f);
        });
    }),

    hitFlameBeam = new Effect(19, e -> {
        color(e.color);

        randLenVectors(e.id, 7, e.finpow() * 11f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 2 + 0.5f);
        });
    }),

    hitMeltdown = new Effect(12, e -> {
        color(Pal.meltdownHit);
        stroke(e.fout() * 2f);

        randLenVectors(e.id, 6, e.finpow() * 18f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f);
        });
    }),

    hitMeltHeal = new Effect(12, e -> {
        color(Pal.heal);
        stroke(e.fout() * 2f);

        randLenVectors(e.id, 6, e.finpow() * 18f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f);
        });
    }),

    instBomb = new Effect(15f, 100f, e -> {
        color(Pal.bulletYellowBack);
        stroke(e.fout() * 4f);
        Lines.circle(e.x, e.y, 4f + e.finpow() * 20f);

        for(int i = 0; i < 4; i++){
            Drawf.tri(e.x, e.y, 6f, 80f * e.fout(), i*90 + 45);
        }

        color();
        for(int i = 0; i < 4; i++){
            Drawf.tri(e.x, e.y, 3f, 30f * e.fout(), i*90 + 45);
        }

        Drawf.light(e.x, e.y, 150f, Pal.bulletYellowBack, 0.9f * e.fout());
    }),

    instTrail = new Effect(30, e -> {
        for(int i = 0; i < 2; i++){
            color(i == 0 ? Pal.bulletYellowBack : Pal.bulletYellow);

            float m = i == 0 ? 1f : 0.5f;

            float rot = e.rotation + 180f;
            float w = 15f * e.fout() * m;
            Drawf.tri(e.x, e.y, w, (30f + Mathf.randomSeedRange(e.id, 15f)) * m, rot);
            Drawf.tri(e.x, e.y, w, 10f * m, rot + 180f);
        }

        Drawf.light(e.x, e.y, 60f, Pal.bulletYellowBack, 0.6f * e.fout());
    }),

    instShoot = new Effect(24f, e -> {
        e.scaled(10f, b -> {
            color(Color.white, Pal.bulletYellowBack, b.fin());
            stroke(b.fout() * 3f + 0.2f);
            Lines.circle(b.x, b.y, b.fin() * 50f);
        });

        color(Pal.bulletYellowBack);

        for(int i : Mathf.signs){
            Drawf.tri(e.x, e.y, 13f * e.fout(), 85f, e.rotation + 90f * i);
            Drawf.tri(e.x, e.y, 13f * e.fout(), 50f, e.rotation + 20f * i);
        }

        Drawf.light(e.x, e.y, 180f, Pal.bulletYellowBack, 0.9f * e.fout());
    }),

    instHit = new Effect(20f, 200f, e -> {
        color(Pal.bulletYellowBack);

        for(int i = 0; i < 2; i++){
            color(i == 0 ? Pal.bulletYellowBack : Pal.bulletYellow);

            float m = i == 0 ? 1f : 0.5f;

            for(int j = 0; j < 5; j++){
                float rot = e.rotation + Mathf.randomSeedRange(e.id + j, 50f);
                float w = 23f * e.fout() * m;
                Drawf.tri(e.x, e.y, w, (80f + Mathf.randomSeedRange(e.id + j, 40f)) * m, rot);
                Drawf.tri(e.x, e.y, w, 20f * m, rot + 180f);
            }
        }

        e.scaled(10f, c -> {
            color(Pal.bulletYellow);
            stroke(c.fout() * 2f + 0.2f);
            Lines.circle(e.x, e.y, c.fin() * 30f);
        });

        e.scaled(12f, c -> {
            color(Pal.bulletYellowBack);
            randLenVectors(e.id, 25, 5f + e.fin() * 80f, e.rotation, 60f, (x, y) -> {
                Fill.square(e.x + x, e.y + y, c.fout() * 3f, 45f);
            });
        });
    }),

    hitLaser = new Effect(8, e -> {
        color(Color.white, Pal.heal, e.fin());
        stroke(0.5f + e.fout());
        Lines.circle(e.x, e.y, e.fin() * 5f);

        Drawf.light(e.x, e.y, 23f, Pal.heal, e.fout() * 0.7f);
    }),

    hitLaserColor = new Effect(8, e -> {
        color(Color.white, e.color, e.fin());
        stroke(0.5f + e.fout());
        Lines.circle(e.x, e.y, e.fin() * 5f);

        Drawf.light(e.x, e.y, 23f, e.color, e.fout() * 0.7f);
    }),

    despawn = new Effect(12, e -> {
        color(Pal.lighterOrange, Color.gray, e.fin());
        stroke(e.fout());

        randLenVectors(e.id, 7, e.fin() * 7f, e.rotation, 40f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 2 + 1f);
        });

    }),

    airBubble = new Effect(100f, e -> {
        randLenVectors(e.id, 1, e.fin() * 12f, (x, y) -> {
            rect(renderer.bubbles[Math.min((int)(renderer.bubbles.length * Mathf.curveMargin(e.fin(), 0.11f, 0.06f)), renderer.bubbles.length - 1)], e.x + x, e.y + y);
        });
    }).layer(Layer.flyingUnitLow + 1),

    flakExplosion = new Effect(20, e -> {
        color(Pal.bulletYellow);

        e.scaled(6, i -> {
            stroke(3f * i.fout());
            Lines.circle(e.x, e.y, 3f + i.fin() * 10f);
        });

        color(Color.gray);

        randLenVectors(e.id, 5, 2f + 23f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.5f);
        });

        color(Pal.lighterOrange);
        stroke(e.fout());

        randLenVectors(e.id + 1, 4, 1f + 23f * e.finpow(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f);
        });

        Drawf.light(e.x, e.y, 50f, Pal.lighterOrange, 0.8f * e.fout());
    }),

    plasticExplosion = new Effect(24, e -> {
        color(Pal.plastaniumFront);

        e.scaled(7, i -> {
            stroke(3f * i.fout());
            Lines.circle(e.x, e.y, 3f + i.fin() * 24f);
        });

        color(Color.gray);

        randLenVectors(e.id, 7, 2f + 28f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
        });

        color(Pal.plastaniumBack);
        stroke(e.fout());

        randLenVectors(e.id + 1, 4, 1f + 25f * e.finpow(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f);
        });

        Drawf.light(e.x, e.y, 50f, Pal.plastaniumBack, 0.8f * e.fout());
    }),

    plasticExplosionFlak = new Effect(28, e -> {
        color(Pal.plastaniumFront);

        e.scaled(7, i -> {
            stroke(3f * i.fout());
            Lines.circle(e.x, e.y, 3f + i.fin() * 34f);
        });

        color(Color.gray);

        randLenVectors(e.id, 7, 2f + 30f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
        });

        color(Pal.plastaniumBack);
        stroke(e.fout());

        randLenVectors(e.id + 1, 4, 1f + 30f * e.finpow(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f);
        });
    }),

    blastExplosion = new Effect(22, e -> {
        color(Pal.missileYellow);

        e.scaled(6, i -> {
            stroke(3f * i.fout());
            Lines.circle(e.x, e.y, 3f + i.fin() * 15f);
        });

        color(Color.gray);

        randLenVectors(e.id, 5, 2f + 23f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
        });

        color(Pal.missileYellowBack);
        stroke(e.fout());

        randLenVectors(e.id + 1, 4, 1f + 23f * e.finpow(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f);
        });

        Drawf.light(e.x, e.y, 45f, Pal.missileYellowBack, 0.8f * e.fout());
    }),

    sapExplosion = new Effect(25, e -> {
        color(Pal.sapBullet);

        e.scaled(6, i -> {
            stroke(3f * i.fout());
            Lines.circle(e.x, e.y, 3f + i.fin() * 80f);
        });

        color(Color.gray);

        randLenVectors(e.id, 9, 2f + 70 * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
        });

        color(Pal.sapBulletBack);
        stroke(e.fout());

        randLenVectors(e.id + 1, 8, 1f + 60f * e.finpow(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f);
        });

        Drawf.light(e.x, e.y, 90f, Pal.sapBulletBack, 0.8f * e.fout());
    }),

    massiveExplosion = new Effect(30, e -> {
        color(Pal.missileYellow);

        e.scaled(7, i -> {
            stroke(3f * i.fout());
            Lines.circle(e.x, e.y, 4f + i.fin() * 30f);
        });

        color(Color.gray);

        randLenVectors(e.id, 8, 2f + 30f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
        });

        color(Pal.missileYellowBack);
        stroke(e.fout());

        randLenVectors(e.id + 1, 6, 1f + 29f * e.finpow(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 4f);
        });

        Drawf.light(e.x, e.y, 50f, Pal.missileYellowBack, 0.8f * e.fout());
    }),

    artilleryTrail = new Effect(50, e -> {
        color(e.color);
        Fill.circle(e.x, e.y, e.rotation * e.fout());
    }).layer(Layer.bullet - 0.01f),

    incendTrail = new Effect(50, e -> {
        color(Pal.lightOrange);
        Fill.circle(e.x, e.y, e.rotation * e.fout());
    }),

    missileTrail = new Effect(50, e -> {
        color(e.color);
        Fill.circle(e.x, e.y, e.rotation * e.fout());
    }).layer(Layer.bullet - 0.001f), //below bullets

    missileTrailShort = new Effect(22, e -> {
        color(e.color);
        Fill.circle(e.x, e.y, e.rotation * e.fout());
    }).layer(Layer.bullet - 0.001f),

    colorTrail = new Effect(50, e -> {
        color(e.color);
        Fill.circle(e.x, e.y, e.rotation * e.fout());
    }),

    absorb = new Effect(12, e -> {
        color(Pal.accent);
        stroke(2f * e.fout());
        Lines.circle(e.x, e.y, 5f * e.fout());
    }),

    forceShrink = new Effect(20, e -> {
        color(e.color, e.fout());
        if(renderer.animateShields){
            Fill.poly(e.x, e.y, 6, e.rotation * e.fout());
        }else{
            stroke(1.5f);
            Draw.alpha(0.09f);
            Fill.poly(e.x, e.y, 6, e.rotation * e.fout());
            Draw.alpha(1f);
            Lines.poly(e.x, e.y, 6, e.rotation * e.fout());
        }
    }).layer(Layer.shields),

    flakExplosionBig = new Effect(30, e -> {
        color(Pal.bulletYellowBack);

        e.scaled(6, i -> {
            stroke(3f * i.fout());
            Lines.circle(e.x, e.y, 3f + i.fin() * 25f);
        });

        color(Color.gray);

        randLenVectors(e.id, 6, 2f + 23f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f);
        });

        color(Pal.bulletYellow);
        stroke(e.fout());

        randLenVectors(e.id + 1, 4, 1f + 23f * e.finpow(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f);
        });

        Drawf.light(e.x, e.y, 60f, Pal.bulletYellowBack, 0.7f * e.fout());
    }),

    burning = new Effect(35f, e -> {
        color(Pal.lightFlame, Pal.darkFlame, e.fin());

        randLenVectors(e.id, 3, 2f + e.fin() * 7f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.1f + e.fout() * 1.4f);
        });
    }),

    fireRemove = new Effect(70f, e -> {
        if(Fire.regions[0] == null) return;
        alpha(e.fout());
        rect(Fire.regions[((int)(e.rotation + e.fin() * Fire.frames)) % Fire.frames], e.x + Mathf.randomSeedRange((int)e.y, 2), e.y + Mathf.randomSeedRange((int)e.x, 2));
        Drawf.light(e.x, e.y, 50f + Mathf.absin(5f, 5f), Pal.lightFlame, 0.6f  * e.fout());
    }),

    fire = new Effect(50f, e -> {
        color(Pal.lightFlame, Pal.darkFlame, e.fin());

        randLenVectors(e.id, 2, 2f + e.fin() * 9f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 1.5f);
        });

        color();

        Drawf.light(e.x, e.y, 20f * e.fslope(), Pal.lightFlame, 0.5f);
    }),

    fireHit = new Effect(35f, e -> {
        color(Pal.lightFlame, Pal.darkFlame, e.fin());

        randLenVectors(e.id, 3, 2f + e.fin() * 10f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.2f + e.fout() * 1.6f);
        });

        color();
    }),

    fireSmoke = new Effect(35f, e -> {
        color(Color.gray);

        randLenVectors(e.id, 1, 2f + e.fin() * 7f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 1.5f);
        });
    }),

    //TODO needs a lot of work
    neoplasmHeal = new Effect(120f, e -> {
        color(Pal.neoplasm1, Pal.neoplasm2, e.fin());

        randLenVectors(e.id, 1, e.fin() * 3f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 2f);
        });
    }).followParent(true).rotWithParent(true).layer(Layer.bullet - 2),

    steam = new Effect(35f, e -> {
        color(Color.lightGray);

        randLenVectors(e.id, 2, 2f + e.fin() * 7f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 1.5f);
        });
    }),

    ventSteam = new Effect(140f, e -> {
        color(e.color, Pal.vent2, e.fin());

        alpha(e.fslope() * 0.78f);

        float length = 3f + e.finpow() * 10f;
        rand.setSeed(e.id);
        for(int i = 0; i < rand.random(3, 5); i++){
            v.trns(rand.random(360f), rand.random(length));
            Fill.circle(e.x + v.x, e.y + v.y, rand.random(1.2f, 3.5f) + e.fslope() * 1.1f);
        }
    }).layer(Layer.darkness - 1),

    drillSteam = new Effect(220f, e -> {

        float length = 3f + e.finpow() * 20f;
        rand.setSeed(e.id);
        for(int i = 0; i < 13; i++){
            v.trns(rand.random(360f), rand.random(length));
            float sizer = rand.random(1.3f, 3.7f);

            e.scaled(e.lifetime * rand.random(0.5f, 1f), b -> {
                color(Color.gray, b.fslope() * 0.93f);

                Fill.circle(e.x + v.x, e.y + v.y, sizer + b.fslope() * 1.2f);
            });
        }
    }).startDelay(30f),

    fluxVapor = new Effect(140f, e -> {
        color(e.color);
        alpha(e.fout() * 0.7f);

        randLenVectors(e.id, 2, 3f + e.finpow() * 10f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.6f + e.fin() * 5f);
        });
    }).layer(Layer.bullet - 1f),

    corrosionVapor = new Effect(50f, e -> {
        color(e.color);
        alpha(Interp.pow2Out.apply(e.fslope()) * 0.5f);

        randLenVectors(e.id, 2, 8f + e.finpow() * 3f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 3f);
        });
    }),

    vapor = new Effect(110f, e -> {
        color(e.color);
        alpha(e.fout());

        randLenVectors(e.id, 3, 2f + e.finpow() * 11f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.6f + e.fin() * 5f);
        });
    }),

    vaporSmall = new Effect(50f, e -> {
        color(e.color);
        alpha(e.fout());

        randLenVectors(e.id, 4, 2f + e.finpow() * 5f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 1f + e.fin() * 4f);
        });
    }),

    fireballsmoke = new Effect(25f, e -> {
        color(Color.gray);

        randLenVectors(e.id, 1, 2f + e.fin() * 7f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.2f + e.fout() * 1.5f);
        });
    }),

    ballfire = new Effect(25f, e -> {
        color(Pal.lightFlame, Pal.darkFlame, e.fin());

        randLenVectors(e.id, 2, 2f + e.fin() * 7f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.2f + e.fout() * 1.5f);
        });
    }),

    freezing = new Effect(40f, e -> {
        color(Liquids.cryofluid.color);

        randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 1.2f);
        });
    }),

    melting = new Effect(40f, e -> {
        color(Liquids.slag.color, Color.white, e.fout() / 5f + Mathf.randomSeedRange(e.id, 0.12f));

        randLenVectors(e.id, 2, 1f + e.fin() * 3f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, .2f + e.fout() * 1.2f);
        });
    }),

    wet = new Effect(80f, e -> {
        color(Liquids.water.color);
        alpha(Mathf.clamp(e.fin() * 2f));

        Fill.circle(e.x, e.y, e.fout());
    }),

    muddy = new Effect(80f, e -> {
        color(Pal.muddy);
        alpha(Mathf.clamp(e.fin() * 2f));

        Fill.circle(e.x, e.y, e.fout());
    }),

    sapped = new Effect(40f, e -> {
        color(Pal.sap);

        randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fslope() * 1.1f, 45f);
        });
    }),

    electrified = new Effect(40f, e -> {
        color(Pal.heal);

        randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fslope() * 1.1f, 45f);
        });
    }),

    sporeSlowed = new Effect(40f, e -> {
        color(Pal.spore);

        Fill.circle(e.x, e.y, e.fslope() * 1.1f);
    }),

    oily = new Effect(42f, e -> {
        color(Liquids.oil.color);

        randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout());
        });
    }),

    overdriven = new Effect(20f, e -> {
        color(e.color);

        randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fout() * 2.3f + 0.5f);
        });
    }),

    overclocked = new Effect(50f, e -> {
        color(e.color);

        Fill.square(e.x, e.y, e.fslope() * 2f, 45f);
    }),

    dropItem = new Effect(20f, e -> {
        float length = 20f * e.finpow();
        float size = 7f * e.fout();

        if(!(e.data instanceof Item item)) return;

        rect(item.fullIcon, e.x + trnsx(e.rotation, length), e.y + trnsy(e.rotation, length), size, size);
    }),

    shockwave = new Effect(10f, 80f, e -> {
        color(Color.white, Color.lightGray, e.fin());
        stroke(e.fout() * 2f + 0.2f);
        Lines.circle(e.x, e.y, e.fin() * 28f);
    }),

    bigShockwave = new Effect(10f, 80f, e -> {
        color(Color.white, Color.lightGray, e.fin());
        stroke(e.fout() * 3f);
        Lines.circle(e.x, e.y, e.fin() * 50f);
    }),

    spawnShockwave = new Effect(20f, 400f, e -> {
        color(Color.white, Color.lightGray, e.fin());
        stroke(e.fout() * 3f + 0.5f);
        Lines.circle(e.x, e.y, e.fin() * (e.rotation + 50f));
    }),

    podLandShockwave = new Effect(12f, 80f, e -> {
        color(Pal.accent);
        stroke(e.fout() * 2f + 0.2f);
        Lines.circle(e.x, e.y, e.fin() * 26f);
    }),

    explosion = new Effect(30, e -> {
        e.scaled(7, i -> {
            stroke(3f * i.fout());
            Lines.circle(e.x, e.y, 3f + i.fin() * 10f);
        });

        color(Color.gray);

        randLenVectors(e.id, 6, 2f + 19f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.5f);
            Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout());
        });

        color(Pal.lighterOrange, Pal.lightOrange, Color.gray, e.fin());
        stroke(1.5f * e.fout());

        randLenVectors(e.id + 1, 8, 1f + 23f * e.finpow(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f);
        });
    }),

    dynamicExplosion = new Effect(30, 500f, b -> {
        float intensity = b.rotation;
        float baseLifetime = 26f + intensity * 15f;
        b.lifetime = 43f + intensity * 35f;

        color(Color.gray);
        //TODO awful borders with linear filtering here
        alpha(0.9f);
        for(int i = 0; i < 4; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.4f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(3f * intensity), 14f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    Fill.circle(e.x + x, e.y + y, fout * ((2f + intensity) * 1.8f));
                });
            });
        }

        b.scaled(baseLifetime, e -> {
            e.scaled(5 + intensity * 2.5f, i -> {
                stroke((3.1f + intensity/5f) * i.fout());
                Lines.circle(e.x, e.y, (3f + i.fin() * 14f) * intensity);
                Drawf.light(e.x, e.y, i.fin() * 14f * 2f * intensity, Color.white, 0.9f * e.fout());
            });

            color(Pal.lighterOrange, Pal.lightOrange, Color.gray, e.fin());
            stroke((1.7f * e.fout()) * (1f + (intensity - 1f) / 2f));

            Draw.z(Layer.effect + 0.001f);
            randLenVectors(e.id + 1, e.finpow() + 0.001f, (int)(9 * intensity), 40f * intensity, (x, y, in, out) -> {
                lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + out * 4 * (3f + intensity));
                Drawf.light(e.x + x, e.y + y, (out * 4 * (3f + intensity)) * 3.5f, Draw.getColor(), 0.8f);
            });
        });
    }),

    reactorExplosion = new Effect(30, 500f, b -> {
        float intensity = 6.8f;
        float baseLifetime = 25f + intensity * 11f;
        b.lifetime = 50f + intensity * 65f;

        color(Pal.reactorPurple2);
        alpha(0.7f);
        for(int i = 0; i < 4; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.4f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(2.9f * intensity), 22f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    float rad = fout * ((2f + intensity) * 2.35f);

                    Fill.circle(e.x + x, e.y + y, rad);
                    Drawf.light(e.x + x, e.y + y, rad * 2.5f, Pal.reactorPurple, 0.5f);
                });
            });
        }

        b.scaled(baseLifetime, e -> {
            Draw.color();
            e.scaled(5 + intensity * 2f, i -> {
                stroke((3.1f + intensity/5f) * i.fout());
                Lines.circle(e.x, e.y, (3f + i.fin() * 14f) * intensity);
                Drawf.light(e.x, e.y, i.fin() * 14f * 2f * intensity, Color.white, 0.9f * e.fout());
            });

            color(Pal.lighterOrange, Pal.reactorPurple, e.fin());
            stroke((2f * e.fout()));

            Draw.z(Layer.effect + 0.001f);
            randLenVectors(e.id + 1, e.finpow() + 0.001f, (int)(8 * intensity), 28f * intensity, (x, y, in, out) -> {
                lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + out * 4 * (4f + intensity));
                Drawf.light(e.x + x, e.y + y, (out * 4 * (3f + intensity)) * 3.5f, Draw.getColor(), 0.8f);
            });
        });
    }),

    impactReactorExplosion = new Effect(30, 500f, b -> {
        float intensity = 8f;
        float baseLifetime = 25f + intensity * 15f;
        b.lifetime = 50f + intensity * 64f;

        color(Pal.lighterOrange);
        alpha(0.8f);
        for(int i = 0; i < 5; i++){
            rand.setSeed(b.id*2 + i);
            float lenScl = rand.random(0.25f, 1f);
            int fi = i;
            b.scaled(b.lifetime * lenScl, e -> {
                randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(2.8f * intensity), 25f * intensity, (x, y, in, out) -> {
                    float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
                    float rad = fout * ((2f + intensity) * 2.35f);

                    Fill.circle(e.x + x, e.y + y, rad);
                    Drawf.light(e.x + x, e.y + y, rad * 2.6f, Pal.lighterOrange, 0.7f);
                });
            });
        }

        b.scaled(baseLifetime, e -> {
            Draw.color();
            e.scaled(5 + intensity * 2f, i -> {
                stroke((3.1f + intensity/5f) * i.fout());
                Lines.circle(e.x, e.y, (3f + i.fin() * 14f) * intensity);
                Drawf.light(e.x, e.y, i.fin() * 14f * 2f * intensity, Color.white, 0.9f * e.fout());
            });

            color(Color.white, Pal.lighterOrange, e.fin());
            stroke((2f * e.fout()));

            Draw.z(Layer.effect + 0.001f);
            randLenVectors(e.id + 1, e.finpow() + 0.001f, (int)(8 * intensity), 30f * intensity, (x, y, in, out) -> {
                lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + out * 4 * (4f + intensity));
                Drawf.light(e.x + x, e.y + y, (out * 4 * (3f + intensity)) * 3.5f, Draw.getColor(), 0.8f);
            });
        });
    }),

    blockExplosionSmoke = new Effect(30, e -> {
        color(Color.gray);

        randLenVectors(e.id, 6, 4f + 30f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 3f);
            Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout());
        });
    }),

    steamCoolSmoke = new Effect(35f, e -> {
        color(Pal.water, Color.lightGray, e.fin(Interp.pow2Out));
        alpha(e.fout(Interp.pow3Out));

        randLenVectors(e.id, 4, e.finpow() * 7f, e.rotation, 30f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, Math.max(e.fout(), Math.min(1f, e.fin() * 8f)) * 2.8f);
        });
    }),

    smokePuff = new Effect(30, e -> {
        color(e.color);

        randLenVectors(e.id, 6, 4f + 30f * e.finpow(), (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 3f);
            Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout());
        });
    }),

    shootSmall = new Effect(8, e -> {
        color(Pal.lighterOrange, Pal.lightOrange, e.fin());
        float w = 1f + 5 * e.fout();
        Drawf.tri(e.x, e.y, w, 15f * e.fout(), e.rotation);
        Drawf.tri(e.x, e.y, w, 3f * e.fout(), e.rotation + 180f);
    }),

    shootSmallColor = new Effect(8, e -> {
        color(e.color, Color.gray, e.fin());
        float w = 1f + 5 * e.fout();
        Drawf.tri(e.x, e.y, w, 15f * e.fout(), e.rotation);
        Drawf.tri(e.x, e.y, w, 3f * e.fout(), e.rotation + 180f);
    }),

    shootHeal = new Effect(8, e -> {
        color(Pal.heal);
        float w = 1f + 5 * e.fout();
        Drawf.tri(e.x, e.y, w, 17f * e.fout(), e.rotation);
        Drawf.tri(e.x, e.y, w, 4f * e.fout(), e.rotation + 180f);
    }),

    shootHealYellow = new Effect(8, e -> {
        color(Pal.lightTrail);
        float w = 1f + 5 * e.fout();
        Drawf.tri(e.x, e.y, w, 17f * e.fout(), e.rotation);
        Drawf.tri(e.x, e.y, w, 4f * e.fout(), e.rotation + 180f);
    }),

    shootSmallSmoke = new Effect(20f, e -> {
        color(Pal.lighterOrange, Color.lightGray, Color.gray, e.fin());

        randLenVectors(e.id, 5, e.finpow() * 6f, e.rotation, 20f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 1.5f);
        });
    }),

    shootBig = new Effect(9, e -> {
        color(Pal.lighterOrange, Pal.lightOrange, e.fin());
        float w = 1.2f + 7 * e.fout();
        Drawf.tri(e.x, e.y, w, 25f * e.fout(), e.rotation);
        Drawf.tri(e.x, e.y, w, 4f * e.fout(), e.rotation + 180f);
    }),

    shootBig2 = new Effect(10, e -> {
        color(Pal.lightOrange, Color.gray, e.fin());
        float w = 1.2f + 8 * e.fout();
        Drawf.tri(e.x, e.y, w, 29f * e.fout(), e.rotation);
        Drawf.tri(e.x, e.y, w, 5f * e.fout(), e.rotation + 180f);
    }),

    shootBigColor = new Effect(11, e -> {
        color(e.color, Color.gray, e.fin());
        float w = 1.2f +9 * e.fout();
        Drawf.tri(e.x, e.y, w, 32f * e.fout(), e.rotation);
        Drawf.tri(e.x, e.y, w, 3f * e.fout(), e.rotation + 180f);
    }),

    shootTitan = new Effect(10, e -> {
        color(Pal.lightOrange, e.color, e.fin());
        float w = 1.3f + 10 * e.fout();
        Drawf.tri(e.x, e.y, w, 35f * e.fout(), e.rotation);
        Drawf.tri(e.x, e.y, w, 6f * e.fout(), e.rotation + 180f);
    }),

    shootBigSmoke = new Effect(17f, e -> {
        color(Pal.lighterOrange, Color.lightGray, Color.gray, e.fin());

        randLenVectors(e.id, 8, e.finpow() * 19f, e.rotation, 10f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 2f + 0.2f);
        });
    }),

    shootBigSmoke2 = new Effect(18f, e -> {
        color(Pal.lightOrange, Color.lightGray, Color.gray, e.fin());

        randLenVectors(e.id, 9, e.finpow() * 23f, e.rotation, 20f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 2.4f + 0.2f);
        });
    }),

    shootSmokeDisperse = new Effect(25f, e -> {
        color(Pal.lightOrange, Color.white, Color.gray, e.fin());

        randLenVectors(e.id, 9, e.finpow() * 29f, e.rotation, 18f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 2.2f + 0.1f);
        });
    }),

    shootSmokeSquare = new Effect(20f, e -> {
        color(Color.white, e.color, e.fin());

        rand.setSeed(e.id);
        for(int i = 0; i < 6; i++){
            float rot = e.rotation + rand.range(22f);
            v.trns(rot, rand.random(e.finpow() * 21f));
            Fill.poly(e.x + v.x, e.y + v.y, 4, e.fout() * 2f + 0.2f, rand.random(360f));
        }
    }),

    shootSmokeSquareSparse = new Effect(30f, e -> {
        color(Color.white, e.color, e.fin());

        rand.setSeed(e.id);
        for(int i = 0; i < 2; i++){
            float rot = e.rotation + rand.range(30f);
            v.trns(rot, rand.random(e.finpow() * 27f));
            Fill.poly(e.x + v.x, e.y + v.y, 4, e.fout() * 3.8f + 0.2f, rand.random(360f));
        }
    }),

    shootSmokeSquareBig = new Effect(32f, e -> {
        color(Color.white, e.color, e.fin());

        rand.setSeed(e.id);
        for(int i = 0; i < 13; i++){
            float rot = e.rotation + rand.range(26f);
            v.trns(rot, rand.random(e.finpow() * 30f));
            Fill.poly(e.x + v.x, e.y + v.y, 4, e.fout() * 4f + 0.2f, rand.random(360f));
        }
    }),

    shootSmokeTitan = new Effect(70f, e -> {
        rand.setSeed(e.id);
        for(int i = 0; i < 13; i++){
            v.trns(e.rotation + rand.range(30f), rand.random(e.finpow() * 40f));
            e.scaled(e.lifetime * rand.random(0.3f, 1f), b -> {
                color(e.color, Pal.lightishGray, b.fin());
                Fill.circle(e.x + v.x, e.y + v.y, b.fout() * 3.4f + 0.3f);
            });
        }
    }),

    shootSmokeSmite = new Effect(70f, e -> {
        rand.setSeed(e.id);
        for(int i = 0; i < 13; i++){
            float a = e.rotation + rand.range(30f);
            v.trns(a, rand.random(e.finpow() * 50f));
            e.scaled(e.lifetime * rand.random(0.3f, 1f), b -> {
                color(e.color);
                Lines.stroke(b.fout() * 3f + 0.5f);
                Lines.lineAngle(e.x + v.x, e.y + v.y, a, b.fout() * 8f + 0.4f);
            });
        }
    }),

    shootSmokeMissile = new Effect(130f, 300f, e -> {
        color(Pal.redLight);
        alpha(0.5f);
        rand.setSeed(e.id);
        for(int i = 0; i < 35; i++){
            v.trns(e.rotation + 180f + rand.range(21f), rand.random(e.finpow() * 90f)).add(rand.range(3f), rand.range(3f));
            e.scaled(e.lifetime * rand.random(0.2f, 1f), b -> {
                Fill.circle(e.x + v.x, e.y + v.y, b.fout() * 9f + 0.3f);
            });
        }
    }),

    shootSmokeMissileColor = new Effect(130f, 300f, e -> {
        color(e.color);
        alpha(0.5f);
        rand.setSeed(e.id);
        for(int i = 0; i < 35; i++){
            v.trns(e.rotation + 180f + rand.range(21f), rand.random(e.finpow() * 90f)).add(rand.range(3f), rand.range(3f));
            e.scaled(e.lifetime * rand.random(0.2f, 1f), b -> {
                Fill.circle(e.x + v.x, e.y + v.y, b.fout() * 9f + 0.3f);
            });
        }
    }),

    regenParticle = new Effect(100f, e -> {
        color(Pal.regen);

        Fill.square(e.x, e.y, e.fslope() * 1.5f + 0.14f, 45f);
    }),

    regenSuppressParticle = new Effect(35f, e -> {
        color(e.color, Color.white, e.fin());
        stroke(e.fout() * 1.4f + 0.5f);

        randLenVectors(e.id, 4, 17f * e.fin(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 3f + 0.5f);
        });
    }),

    regenSuppressSeek = new Effect(140f, e -> {
        e.lifetime = Mathf.randomSeed(e.id, 120f, 200f);

        if(!(e.data instanceof Position to)) return;

        Tmp.v2.set(to).sub(e.x, e.y).nor().rotate90(1).scl(Mathf.randomSeedRange(e.id, 1f) * 50f);

        Tmp.bz2.set(Tmp.v1.set(e.x, e.y), Tmp.v2.add(e.x, e.y), Tmp.v3.set(to));

        Tmp.bz2.valueAt(Tmp.v4, e.fout());

        color(e.color);
        Fill.circle(Tmp.v4.x, Tmp.v4.y, e.fslope() * 2f + 0.1f);
    }).followParent(false).rotWithParent(false),

    surgeCruciSmoke = new Effect(160f, e -> {
        color(Pal.slagOrange);
        alpha(0.6f);

        rand.setSeed(e.id);
        for(int i = 0; i < 3; i++){
            float len = rand.random(6f), rot = rand.range(40f) + e.rotation;

            e.scaled(e.lifetime * rand.random(0.3f, 1f), b -> {
                v.trns(rot, len * b.finpow());
                Fill.circle(e.x + v.x, e.y + v.y, 2f * b.fslope() + 0.2f);
            });
        }
    }),

    neoplasiaSmoke = new Effect(280f, e -> {
        color(Pal.neoplasmMid);
        alpha(0.6f);

        rand.setSeed(e.id);
        for(int i = 0; i < 6; i++){
            float len = rand.random(10f), rot = rand.range(120f) + e.rotation;

            e.scaled(e.lifetime * rand.random(0.3f, 1f), b -> {
                v.trns(rot, len * b.finpow());
                Fill.circle(e.x + v.x, e.y + v.y, 3.3f * b.fslope() + 0.2f);
            });
        }
    }),

    heatReactorSmoke = new Effect(180f, e -> {
        color(Color.gray);

        rand.setSeed(e.id);
        for(int i = 0; i < 5; i++){
            float len = rand.random(6f), rot = rand.range(50f) + e.rotation;

            e.scaled(e.lifetime * rand.random(0.3f, 1f), b -> {
                alpha(0.9f * b.fout());
                v.trns(rot, len * b.finpow());
                Fill.circle(e.x + v.x, e.y + v.y, 2.4f * b.fin() + 0.6f);
            });
        }
    }),

    circleColorSpark = new Effect(21f, e -> {
        color(Color.white, e.color, e.fin());
        stroke(e.fout() * 1.1f + 0.5f);

        randLenVectors(e.id, 9, 27f * e.fin(), 9f, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 5f + 0.5f);
        });
    }),

    colorSpark = new Effect(21f, e -> {
        color(Color.white, e.color, e.fin());
        stroke(e.fout() * 1.1f + 0.5f);

        randLenVectors(e.id, 5, 27f * e.fin(), e.rotation, 9f, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 5f + 0.5f);
        });
    }),

    colorSparkBig = new Effect(25f, e -> {
        color(Color.white, e.color, e.fin());
        stroke(e.fout() * 1.3f + 0.7f);

        randLenVectors(e.id, 8, 41f * e.fin(), e.rotation, 10f, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 6f + 0.5f);
        });
    }),

    randLifeSpark = new Effect(24f, e -> {
        color(Color.white, e.color, e.fin());
        stroke(e.fout() * 1.5f + 0.5f);

        rand.setSeed(e.id);
        for(int i = 0; i < 15; i++){
            float ang = e.rotation + rand.range(9f), len = rand.random(90f * e.finpow());
            e.scaled(e.lifetime * rand.random(0.5f, 1f), p -> {
                v.trns(ang, len);
                lineAngle(e.x + v.x, e.y + v.y, ang, p.fout() * 7f + 0.5f);
            });
        }
    }),

    shootPayloadDriver = new Effect(30f, e -> {
        color(Pal.accent);
        Lines.stroke(0.5f + 0.5f*e.fout());
        float spread = 9f;

        rand.setSeed(e.id);
        for(int i = 0; i < 20; i++){
            float ang = e.rotation + rand.range(17f);
            v.trns(ang, rand.random(e.fin() * 55f));
            Lines.lineAngle(e.x + v.x + rand.range(spread), e.y + v.y + rand.range(spread), ang, e.fout() * 5f * rand.random(1f) + 1f);
        }
    }),

    shootSmallFlame = new Effect(32f, 80f, e -> {
        color(Pal.lightFlame, Pal.darkFlame, Color.gray, e.fin());

        randLenVectors(e.id, 8, e.finpow() * 60f, e.rotation, 10f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.65f + e.fout() * 1.5f);
        });
    }),

    shootPyraFlame = new Effect(33f, 80f, e -> {
        color(Pal.lightPyraFlame, Pal.darkPyraFlame, Color.gray, e.fin());

        randLenVectors(e.id, 10, e.finpow() * 70f, e.rotation, 10f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.65f + e.fout() * 1.6f);
        });
    }),

    shootLiquid = new Effect(15f, 80f, e -> {
        color(e.color);

        randLenVectors(e.id, 2, e.finpow() * 15f, e.rotation, 11f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, 0.5f + e.fout() * 2.5f);
        });
    }),

    casing1 = new Effect(30f, e -> {
        color(Pal.lightOrange, Color.lightGray, Pal.lightishGray, e.fin());
        alpha(e.fout(0.3f));
        float rot = Math.abs(e.rotation) + 90f;
        int i = -Mathf.sign(e.rotation);

        float len = (2f + e.finpow() * 6f) * i;
        float lr = rot + e.fin() * 30f * i;
        Fill.rect(
            e.x + trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()),
            e.y + trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()),
            1f, 2f, rot + e.fin() * 50f * i
        );

    }).layer(Layer.bullet),

    casing2 = new Effect(34f, e -> {
        color(Pal.lightOrange, Color.lightGray, Pal.lightishGray, e.fin());
        alpha(e.fout(0.5f));
        float rot = Math.abs(e.rotation) + 90f;
        int i = -Mathf.sign(e.rotation);
        float len = (2f + e.finpow() * 10f) * i;
        float lr = rot + e.fin() * 20f * i;
        rect(Core.atlas.find("casing"),
            e.x + trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()),
            e.y + trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()),
            2f, 3f, rot + e.fin() * 50f * i
        );
    }).layer(Layer.bullet),

    casing3 = new Effect(40f, e -> {
        color(Pal.lightOrange, Pal.lightishGray, Pal.lightishGray, e.fin());
        alpha(e.fout(0.5f));
        float rot = Math.abs(e.rotation) + 90f;
        int i = -Mathf.sign(e.rotation);
        float len = (4f + e.finpow() * 9f) * i;
        float lr = rot + Mathf.randomSeedRange(e.id + i + 6, 20f * e.fin()) * i;

        rect(Core.atlas.find("casing"),
            e.x + trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()),
            e.y + trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()),
            2.5f, 4f,
            rot + e.fin() * 50f * i
        );
    }).layer(Layer.bullet),

    casing4 = new Effect(45f, e -> {
        color(Pal.lightOrange, Pal.lightishGray, Pal.lightishGray, e.fin());
        alpha(e.fout(0.5f));
        float rot = Math.abs(e.rotation) + 90f;
        int i = -Mathf.sign(e.rotation);
        float len = (4f + e.finpow() * 9f) * i;
        float lr = rot + Mathf.randomSeedRange(e.id + i + 6, 20f * e.fin()) * i;

        rect(Core.atlas.find("casing"),
        e.x + trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()),
        e.y + trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()),
        3f, 6f,
        rot + e.fin() * 50f * i
        );
    }).layer(Layer.bullet),

    casing2Double = new Effect(34f, e -> {
        color(Pal.lightOrange, Color.lightGray, Pal.lightishGray, e.fin());
        alpha(e.fout(0.5f));
        float rot = Math.abs(e.rotation) + 90f;
        for(int i : Mathf.signs){
            float len = (2f + e.finpow() * 10f) * i;
            float lr = rot + e.fin() * 20f * i;
            rect(Core.atlas.find("casing"),
            e.x + trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()),
            e.y + trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()),
            2f, 3f, rot + e.fin() * 50f * i
            );
        }

    }).layer(Layer.bullet),

    casing3Double = new Effect(40f, e -> {
        color(Pal.lightOrange, Pal.lightishGray, Pal.lightishGray, e.fin());
        alpha(e.fout(0.5f));
        float rot = Math.abs(e.rotation) + 90f;

        for(int i : Mathf.signs){
            float len = (4f + e.finpow() * 9f) * i;
            float lr = rot + Mathf.randomSeedRange(e.id + i + 6, 20f * e.fin()) * i;

            rect(Core.atlas.find("casing"),
            e.x + trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()),
            e.y + trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()),
            2.5f, 4f,
            rot + e.fin() * 50f * i
            );
        }

    }).layer(Layer.bullet),

    railShoot = new Effect(24f, e -> {
        e.scaled(10f, b -> {
            color(Color.white, Color.lightGray, b.fin());
            stroke(b.fout() * 3f + 0.2f);
            Lines.circle(b.x, b.y, b.fin() * 50f);
        });

        color(Pal.orangeSpark);

        for(int i : Mathf.signs){
            Drawf.tri(e.x, e.y, 13f * e.fout(), 85f, e.rotation + 90f * i);
        }
    }),

    railTrail = new Effect(16f, e -> {
        color(Pal.orangeSpark);

        for(int i : Mathf.signs){
            Drawf.tri(e.x, e.y, 10f * e.fout(), 24f, e.rotation + 90 + 90f * i);
        }

        Drawf.light(e.x, e.y, 60f * e.fout(), Pal.orangeSpark, 0.5f);
    }),

    railHit = new Effect(18f, 200f, e -> {
        color(Pal.orangeSpark);

        for(int i : Mathf.signs){
            Drawf.tri(e.x, e.y, 10f * e.fout(), 60f, e.rotation + 140f * i);
        }
    }),

    lancerLaserShoot = new Effect(21f, e -> {
        color(Pal.lancerLaser);

        for(int i : Mathf.signs){
            Drawf.tri(e.x, e.y, 4f * e.fout(), 29f, e.rotation + 90f * i);
        }
    }),

    lancerLaserShootSmoke = new Effect(26f, e -> {
        color(Color.white);
        float length = !(e.data instanceof Float) ? 70f : (Float)e.data;

        randLenVectors(e.id, 7, length, e.rotation, 0f, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fout() * 9f);
        });
    }),

    lancerLaserCharge = new Effect(38f, e -> {
        color(Pal.lancerLaser);

        randLenVectors(e.id, 14, 1f + 20f * e.fout(), e.rotation, 120f, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 3f + 1f);
        });
    }),

    lancerLaserChargeBegin = new Effect(60f, e -> {
        float margin = 1f - Mathf.curve(e.fin(), 0.9f);
        float fin = Math.min(margin, e.fin());

        color(Pal.lancerLaser);
        Fill.circle(e.x, e.y, fin * 3f);

        color();
        Fill.circle(e.x, e.y, fin * 2f);
    }),

    lightningCharge = new Effect(38f, e -> {
        color(Pal.lancerLaser);

        randLenVectors(e.id, 2, 1f + 20f * e.fout(), e.rotation, 120f, (x, y) -> {
            Drawf.tri(e.x + x, e.y + y, e.fslope() * 3f + 1, e.fslope() * 3f + 1, Mathf.angle(x, y));
        });
    }),

    sparkShoot = new Effect(12f, e -> {
        color(Color.white, e.color, e.fin());
        stroke(e.fout() * 1.2f + 0.6f);

        randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 3f, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 5f + 0.5f);
        });
    }),

    lightningShoot = new Effect(12f, e -> {
        color(Color.white, Pal.lancerLaser, e.fin());
        stroke(e.fout() * 1.2f + 0.5f);

        randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 5f + 2f);
        });
    }),

    thoriumShoot = new Effect(12f, e -> {
        color(Color.white, Pal.thoriumPink, e.fin());
        stroke(e.fout() * 1.2f + 0.5f);

        randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 5f + 2f);
        });
    }),

    reactorsmoke = new Effect(17, e -> {
        randLenVectors(e.id, 4, e.fin() * 8f, (x, y) -> {
            float size = 1f + e.fout() * 5f;
            color(Color.lightGray, Color.gray, e.fin());
            Fill.circle(e.x + x, e.y + y, size/2f);
        });
    }),

    redgeneratespark = new Effect(90, e -> {
        color(Pal.redSpark);
        alpha(e.fslope());

        rand.setSeed(e.id);
        for(int i = 0; i < 2; i++){
            v.trns(rand.random(360f), rand.random(e.finpow() * 9f)).add(e.x, e.y);
            Fill.circle(v.x, v.y, rand.random(1.4f, 2.4f));
        }
    }).layer(Layer.bullet - 1f),

    turbinegenerate = new Effect(100, e -> {
        color(Pal.vent);
        alpha(e.fslope() * 0.8f);

        rand.setSeed(e.id);
        for(int i = 0; i < 3; i++){
            v.trns(rand.random(360f), rand.random(e.finpow() * 14f)).add(e.x, e.y);
            Fill.circle(v.x, v.y, rand.random(1.4f, 3.4f));
        }
    }).layer(Layer.bullet - 1f),

    generatespark = new Effect(18, e -> {
        randLenVectors(e.id, 5, e.fin() * 8f, (x, y) -> {
            color(Pal.orangeSpark, Color.gray, e.fin());
            Fill.circle(e.x + x, e.y + y, e.fout() * 4f /2f);
        });
    }),

    fuelburn = new Effect(23, e -> {
        randLenVectors(e.id, 5, e.fin() * 9f, (x, y) -> {
            color(Color.lightGray, Color.gray, e.fin());
            Fill.circle(e.x + x, e.y + y, e.fout() * 2f);
        });
    }),

    incinerateSlag = new Effect(34, e -> {
        randLenVectors(e.id, 4, e.finpow() * 5f, (x, y) -> {
            color(Pal.slagOrange, Color.gray, e.fin());
            Fill.circle(e.x + x, e.y + y, e.fout() * 1.7f);
        });
    }),

    coreBurn = new Effect(23, e -> {
        randLenVectors(e.id, 5, e.fin() * 9f, (x, y) -> {
            float len = e.fout() * 4f;
            color(Pal.accent, Color.gray, e.fin());
            Fill.circle(e.x + x, e.y + y, len/2f);
        });
    }),

    plasticburn = new Effect(40, e -> {
        randLenVectors(e.id, 5, 3f + e.fin() * 5f, (x, y) -> {
            color(Pal.plasticBurn, Color.gray, e.fin());
            Fill.circle(e.x + x, e.y + y, e.fout());
        });
    }),

    conveyorPoof = new Effect(35, e -> {
        color(Pal.plasticBurn, Color.gray, e.fin());
        randLenVectors(e.id, 4, 3f + e.fin() * 4f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() * 1.11f);
        });
    }),

    pulverize = new Effect(40, e -> {
        randLenVectors(e.id, 5, 3f + e.fin() * 8f, (x, y) -> {
            color(Pal.stoneGray);
            Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.5f, 45);
        });
    }),

    pulverizeRed = new Effect(40, e -> {
        randLenVectors(e.id, 5, 3f + e.fin() * 8f, (x, y) -> {
            color(Pal.redDust, Pal.stoneGray, e.fin());
            Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.5f, 45);
        });
    }),

    pulverizeSmall = new Effect(30, e -> {
        randLenVectors(e.id, 3, e.fin() * 5f, (x, y) -> {
            color(Pal.stoneGray);
            Fill.square(e.x + x, e.y + y, e.fout() + 0.5f, 45);
        });
    }),

    pulverizeMedium = new Effect(30, e -> {
        randLenVectors(e.id, 5, 3f + e.fin() * 8f, (x, y) -> {
            color(Pal.stoneGray);
            Fill.square(e.x + x, e.y + y, e.fout() + 0.5f, 45);
        });
    }),

    producesmoke = new Effect(12, e -> {
        randLenVectors(e.id, 8, 4f + e.fin() * 18f, (x, y) -> {
            color(Color.white, Pal.accent, e.fin());
            Fill.square(e.x + x, e.y + y, 1f + e.fout() * 3f, 45);
        });
    }),

    artilleryTrailSmoke = new Effect(50, e -> {
        color(e.color);
        rand.setSeed(e.id);
        for(int i = 0; i < 13; i++){
            float fin = e.fin() / rand.random(0.5f, 1f), fout = 1f - fin, angle = rand.random(360f), len = rand.random(0.5f, 1f);

            if(fin <= 1f){
                Tmp.v1.trns(angle, fin * 24f * len);

                alpha((0.5f - Math.abs(fin - 0.5f)) * 2f);
                Fill.circle(e.x + Tmp.v1.x, e.y + Tmp.v1.y, 0.5f + fout * 4f);
            }
        }
    }),

    smokeCloud = new Effect(70, e -> {
        randLenVectors(e.id, e.fin(), 30, 30f, (x, y, fin, fout) -> {
            color(Color.gray);
            alpha((0.5f - Math.abs(fin - 0.5f)) * 2f);
            Fill.circle(e.x + x, e.y + y, 0.5f + fout * 4f);
        });
    }),

    smeltsmoke = new Effect(15, e -> {
        randLenVectors(e.id, 6, 4f + e.fin() * 5f, (x, y) -> {
            color(Color.white, e.color, e.fin());
            Fill.square(e.x + x, e.y + y, 0.5f + e.fout() * 2f, 45);
        });
    }),

    coalSmeltsmoke = new Effect(40f, e -> {
        randLenVectors(e.id, 0.2f + e.fin(), 4, 6.3f, (x, y, fin, out) -> {
            color(Color.darkGray, Pal.coalBlack, e.finpowdown());
            Fill.circle(e.x + x, e.y + y, out * 2f + 0.35f);
        });
    }),

    formsmoke = new Effect(40, e -> {
        randLenVectors(e.id, 6, 5f + e.fin() * 8f, (x, y) -> {
            color(Pal.plasticSmoke, Color.lightGray, e.fin());
            Fill.square(e.x + x, e.y + y, 0.2f + e.fout() * 2f, 45);
        });
    }),

    blastsmoke = new Effect(26, e -> {
        randLenVectors(e.id, 12, 1f + e.fin() * 23f, (x, y) -> {
            float size = 2f + e.fout() * 6f;
            color(Color.lightGray, Color.darkGray, e.fin());
            Fill.circle(e.x + x, e.y + y, size/2f);
        });
    }),

    lava = new Effect(18, e -> {
        randLenVectors(e.id, 3, 1f + e.fin() * 10f, (x, y) -> {
            float size = e.fslope() * 4f;
            color(Color.orange, Color.gray, e.fin());
            Fill.circle(e.x + x, e.y + y, size/2f);
        });
    }),

    dooropen = new Effect(10, e -> {
        stroke(e.fout() * 1.6f);
        Lines.square(e.x, e.y, e.rotation * tilesize / 2f + e.fin() * 2f);
    }),

    doorclose = new Effect(10, e -> {
        stroke(e.fout() * 1.6f);
        Lines.square(e.x, e.y, e.rotation * tilesize / 2f + e.fout() * 2f);
    }),

    dooropenlarge = new Effect(10, e -> {
        stroke(e.fout() * 1.6f);
        Lines.square(e.x, e.y, tilesize + e.fin() * 2f);
    }),

    doorcloselarge = new Effect(10, e -> {
        stroke(e.fout() * 1.6f);
        Lines.square(e.x, e.y, tilesize + e.fout() * 2f);
    }),

    generate = new Effect(11, e -> {
        color(Color.orange, Color.yellow, e.fin());
        stroke(1f);
        Lines.spikes(e.x, e.y, e.fin() * 5f, 2, 8);
    }),

    mineWallSmall = new Effect(50, e -> {
        color(e.color, Color.darkGray, e.fin());
        randLenVectors(e.id, 2, e.fin() * 6f, (x, y) -> {
            Fill.circle(e.x + x, e.y + y, e.fout() + 0.5f);
        });
    }),

    mineSmall = new Effect(30, e -> {
        color(e.color, Color.lightGray, e.fin());
        randLenVectors(e.id, 3, e.fin() * 5f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fout() + 0.5f, 45);
        });
    }),

    mine = new Effect(20, e -> {
        color(e.color, Color.lightGray, e.fin());
        randLenVectors(e.id, 6, 3f + e.fin() * 6f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fout() * 2f, 45);
        });
    }),

    mineBig = new Effect(30, e -> {
        color(e.color, Color.lightGray, e.fin());
        randLenVectors(e.id, 6, 4f + e.fin() * 8f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.2f, 45);
        });
    }),

    mineHuge = new Effect(40, e -> {
        color(e.color, Color.lightGray, e.fin());
        randLenVectors(e.id, 8, 5f + e.fin() * 10f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.5f, 45);
        });
    }),

    mineImpact = new Effect(90, e -> {
        color(e.color, Color.lightGray, e.fin());
        randLenVectors(e.id, 12, 5f + e.finpow() * 22f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fout() * 2.5f + 0.5f, 45);
        });
    }),

    mineImpactWave = new Effect(50f, e -> {
        color(e.color);

        stroke(e.fout() * 1.5f);

        randLenVectors(e.id, 12, 4f + e.finpow() * e.rotation, (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fout() * 5 + 1f);
        });

        e.scaled(30f, b -> {
            Lines.stroke(5f * b.fout());
            Lines.circle(e.x, e.y, b.finpow() * 28f);
        });
    }),

    payloadReceive = new Effect(30, e -> {
        color(Color.white, Pal.accent, e.fin());
        randLenVectors(e.id, 12, 7f + e.fin() * 13f, (x, y) -> {
            Fill.square(e.x + x, e.y + y, e.fout() * 2.1f + 0.5f, 45);
        });
    }),

    teleportActivate = new Effect(50, e -> {
        color(e.color);

        e.scaled(8f, e2 -> {
            stroke(e2.fout() * 4f);
            Lines.circle(e2.x, e2.y, 4f + e2.fin() * 27f);
        });

        stroke(e.fout() * 2f);

        randLenVectors(e.id, 30, 4f + 40f * e.fin(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 4f + 1f);
        });
    }),

    teleport = new Effect(60, e -> {
        color(e.color);
        stroke(e.fin() * 2f);
        Lines.circle(e.x, e.y, 7f + e.fout() * 8f);

        randLenVectors(e.id, 20, 6f + 20f * e.fout(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 4f + 1f);
        });
    }),

    teleportOut = new Effect(20, e -> {
        color(e.color);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 7f + e.fin() * 8f);

        randLenVectors(e.id, 20, 4f + 20f * e.fin(), (x, y) -> {
            lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 4f + 1f);
        });
    }),

    ripple = new Effect(30, e -> {
        e.lifetime = 30f*e.rotation;

        color(Tmp.c1.set(e.color).mul(1.5f));
        stroke(e.fout() * 1.4f);
        Lines.circle(e.x, e.y, (2f + e.fin() * 4f) * e.rotation);
    }).layer(Layer.debris),

    bubble = new Effect(20, e -> {
        color(Tmp.c1.set(e.color).shiftValue(0.1f));
        stroke(e.fout() + 0.2f);
        randLenVectors(e.id, 2, e.rotation * 0.9f, (x, y) -> {
            Lines.circle(e.x + x, e.y + y, 1f + e.fin() * 3f);
        });
    }),

    launchAccelerator = new Effect(22, e -> {
        color(Pal.accent);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 4f + e.finpow() * 160f);
    }),

    launch = new Effect(28, e -> {
        color(Pal.command);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, 4f + e.finpow() * 120f);
    }),

    launchPod = new Effect(50, e -> {
        color(Pal.engine);

        e.scaled(25f, f -> {
            stroke(f.fout() * 2f);
            Lines.circle(e.x, e.y, 4f + f.finpow() * 30f);
        });

        stroke(e.fout() * 2f);

        randLenVectors(e.id, 24, e.finpow() * 50f, (x, y) -> {
            float ang = Mathf.angle(x, y);
            lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f);
        });
    }),

    healWaveMend = new Effect(40, e -> {
        color(e.color);
        stroke(e.fout() * 2f);
        Lines.circle(e.x, e.y, e.finpow() * e.rotation);
    }),

    overdriveWave = new Effect(50, e -> {
        color(e.color);
        stroke(e.fout());
        Lines.circle(e.x, e.y, e.finpow() * e.rotation);
    }),

    healBlock = new Effect(20, e -> {
        color(Pal.heal);
        stroke(2f * e.fout() + 0.5f);
        Lines.square(e.x, e.y, 1f + (e.fin() * e.rotation * tilesize / 2f - 1f));
    }),

    healBlockFull = new Effect(20, e -> {
        if(!(e.data instanceof Block block)) return;

        mixcol(e.color, 1f);
        alpha(e.fout());
        Draw.rect(block.fullIcon, e.x, e.y);
    }),

    rotateBlock = new Effect(30, e -> {
        color(Pal.accent);
        alpha(e.fout() * 1);
        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);
        Fill.square(e.x, e.y, e.rotation * tilesize);
    }),

    shieldBreak = new Effect(40, e -> {
        color(e.color);
        stroke(3f * e.fout());
        if(e.data instanceof ForceFieldAbility ab){
            Lines.poly(e.x, e.y, ab.sides, e.rotation + e.fin(), ab.rotation);
            return;
        }

        Lines.poly(e.x, e.y, 6, e.rotation + e.fin());
    }).followParent(true),

    arcShieldBreak = new Effect(40, e -> {
        Lines.stroke(3 * e.fout(), e.color);
        if(e.data instanceof Unit u){
            ShieldArcAbility ab = (ShieldArcAbility) Structs.find(u.abilities, a -> a instanceof ShieldArcAbility);
            if(ab != null){
                Vec2 pos = Tmp.v1.set(ab.x, ab.y).rotate(u.rotation - 90f).add(u);
                Lines.arc(pos.x, pos.y, ab.radius + ab.width/2, ab.angle / 360f, u.rotation + ab.angleOffset - ab.angle / 2f);
                Lines.arc(pos.x, pos.y, ab.radius - ab.width/2, ab.angle / 360f, u.rotation + ab.angleOffset - ab.angle / 2f);
                for(int i : Mathf.signs){
                    float
                            px = pos.x + Angles.trnsx(u.rotation + ab.angleOffset - ab.angle / 2f * i, ab.radius + ab.width / 2),
                            py = pos.y + Angles.trnsy(u.rotation + ab.angleOffset - ab.angle / 2f * i, ab.radius + ab.width / 2),
                            px1 = pos.x + Angles.trnsx(u.rotation + ab.angleOffset - ab.angle / 2f * i, ab.radius - ab.width / 2),
                            py1 = pos.y + Angles.trnsy(u.rotation + ab.angleOffset - ab.angle / 2f * i, ab.radius - ab.width / 2);
                    Lines.line(px, py, px1, py1);
                }
            }
        }
    }).followParent(true),

    coreLandDust = new Effect(100f, e -> {
        color(e.color, e.fout(0.1f));
        rand.setSeed(e.id);
        Tmp.v1.trns(e.rotation, e.finpow() * 90f * rand.random(0.2f, 1f));
        Fill.circle(e.x + Tmp.v1.x, e.y + Tmp.v1.y, 8f * rand.random(0.6f, 1f) * e.fout(0.2f));
    }).layer(Layer.groundUnit + 1f),

    podLandDust = new Effect(70f, e -> {
        color(e.color, e.fout(0.1f));
        rand.setSeed(e.id);
        Tmp.v1.trns(e.rotation, e.finpow() * 35f * rand.random(0.2f, 1f));
        Fill.circle(e.x + Tmp.v1.x, e.y + Tmp.v1.y, 5f * rand.random(0.6f, 1f) * e.fout(0.2f));
    }).layer(Layer.groundUnit + 1f),

    unitShieldBreak = new Effect(35, e -> {
        if(!(e.data instanceof Unit unit)) return;

        float radius = unit.hitSize() * 1.3f;

        e.scaled(16f, c -> {
            color(e.color, 0.9f);
            stroke(c.fout() * 2f + 0.1f);

            randLenVectors(e.id, (int)(radius * 1.2f), radius/2f + c.finpow() * radius*1.25f, (x, y) -> {
                lineAngle(c.x + x, c.y + y, Mathf.angle(x, y), c.fout() * 5 + 1f);
            });
        });

        color(e.color, e.fout() * 0.9f);
        stroke(e.fout());
        Lines.circle(e.x, e.y, radius);
    }),

    chainLightning = new Effect(20f, 300f, e -> {
        if(!(e.data instanceof Position p)) return;
        float tx = p.getX(), ty = p.getY(), dst = Mathf.dst(e.x, e.y, tx, ty);
        Tmp.v1.set(p).sub(e.x, e.y).nor();

        float normx = Tmp.v1.x, normy = Tmp.v1.y;
        float range = 6f;
        int links = Mathf.ceil(dst / range);
        float spacing = dst / links;

        Lines.stroke(2.5f * e.fout());
        Draw.color(Color.white, e.color, e.fin());

        Lines.beginLine();

        Lines.linePoint(e.x, e.y);

        rand.setSeed(e.id);

        for(int i = 0; i < links; i++){
            float nx, ny;
            if(i == links - 1){
                nx = tx;
                ny = ty;
            }else{
                float len = (i + 1) * spacing;
                Tmp.v1.setToRandomDirection(rand).scl(range/2f);
                nx = e.x + normx * len + Tmp.v1.x;
                ny = e.y + normy * len + Tmp.v1.y;
            }

            Lines.linePoint(nx, ny);
        }

        Lines.endLine();
    }).followParent(false).rotWithParent(false),

    chainEmp = new Effect(30f, 300f, e -> {
        if(!(e.data instanceof Position p)) return;
        float tx = p.getX(), ty = p.getY(), dst = Mathf.dst(e.x, e.y, tx, ty);
        Tmp.v1.set(p).sub(e.x, e.y).nor();

        float normx = Tmp.v1.x, normy = Tmp.v1.y;
        float range = 6f;
        int links = Mathf.ceil(dst / range);
        float spacing = dst / links;

        Lines.stroke(4f * e.fout());
        Draw.color(Color.white, e.color, e.fin());

        Lines.beginLine();

        Lines.linePoint(e.x, e.y);

        rand.setSeed(e.id);

        for(int i = 0; i < links; i++){
            float nx, ny;
            if(i == links - 1){
                nx = tx;
                ny = ty;
            }else{
                float len = (i + 1) * spacing;
                Tmp.v1.setToRandomDirection(rand).scl(range/2f);
                nx = e.x + normx * len + Tmp.v1.x;
                ny = e.y + normy * len + Tmp.v1.y;
            }

            Lines.linePoint(nx, ny);
        }

        Lines.endLine();
    }).followParent(false).rotWithParent(false),

    legDestroy = new Effect(90f, 100f, e -> {
        if(!(e.data instanceof LegDestroyData data)) return;
        rand.setSeed(e.id);

        e.lifetime = rand.random(70f, 130f);

        Tmp.v1.trns(rand.random(360f), rand.random(data.region.width / 8f) * e.finpow());
        float ox = Tmp.v1.x, oy = Tmp.v1.y;

        alpha(e.foutpowdown());

        stroke(data.region.height * scl);
        line(data.region, data.a.x + ox, data.a.y + oy, data.b.x + ox, data.b.y + oy, false);
    }).layer(Layer.groundUnit + 5f),

    debugLine = new Effect(90f, 1000000000000f, e -> {
       if(!(e.data instanceof Vec2[] vec)) return;

       Draw.color(e.color);
       Lines.stroke(2f);

       if(vec.length == 2){
           Lines.line(vec[0].x, vec[0].y, vec[1].x, vec[1].y);
       }else{
           Lines.beginLine();
           for(Vec2 v : vec)
               Lines.linePoint(v.x, v.y);
           Lines.endLine();
       }

       Draw.reset();
    }),
    debugRect = new Effect(90f, 1000000000000f, e -> {
        if(!(e.data instanceof Rect rect)) return;

        Draw.color(e.color);
        Lines.stroke(2f);

        Lines.rect(rect);

        Draw.reset();
    });
}
