package mindustry.graphics; import arc.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.ctype.*; import mindustry.gen.*; import mindustry.world.*; import java.util.*; import static mindustry.Vars.*; public class Drawf{ private static final Vec2[] vecs = new Vec2[]{new Vec2(), new Vec2(), new Vec2(), new Vec2()}; private static final FloatSeq points = new FloatSeq(); /** Bleeds a mod pixmap if linear filtering is enabled. */ public static void checkBleed(Pixmap pixmap){ if(Core.settings.getBool("linear", true)){ Pixmaps.bleed(pixmap); } } public static void underwater(Runnable run){ renderer.blocks.floor.drawUnderwater(run); } //TODO offset unused public static void flame(float x, float y, int divisions, float rotation, float length, float width, float pan){ float len1 = length * pan, len2 = length * (1f - pan); points.clear(); //left side; half arc beginning at 90 degrees and ending at 270 for(int i = 0; i < divisions; i++){ float rot = 90f + 180f * i / (float)divisions; Tmp.v1.trnsExact(rot, width); point( (Tmp.v1.x + width) / width * len1, //convert to 0..1, then multiply by desired length Tmp.v1.y, //Y axis remains unchanged x, y, rotation ); } //right side; half arc beginning at -90 (270) and ending at 90 for(int i = 0; i < divisions; i++){ float rot = -90f + 180f * i / (float)divisions; Tmp.v1.trnsExact(rot, width); point( len1 + (Tmp.v1.x) / width * len2, //convert to 0..1, then multiply by desired length and offset relative to previous segment Tmp.v1.y, //Y axis remains unchanged x, y, rotation ); } Fill.poly(points); } public static void flameFront(float x, float y, int divisions, float rotation, float length, float width){ //TODO I don't know why this is necessary yet. Does FIll.poly screw up with triangles? divisions = Mathf.round(divisions, 2) + 1; points.clear(); //right side; half arc beginning at -90 (270) and ending at 90 for(int i = 0; i <= divisions; i++){ float rot = -90f + 180f * i / (float)divisions; Tmp.v1.trnsExact(rot, width); point( (Tmp.v1.x) / width * length, //convert to 0..1, then multiply by desired length and offset relative to previous segment Tmp.v1.y, //Y axis remains unchanged x, y, rotation ); } Fill.poly(points); } private static void point(float x, float y, float baseX, float baseY, float rotation){ //TODO test exact and non-exact Tmp.v1.set(x, y).rotateRadExact(rotation * Mathf.degRad); points.add(Tmp.v1.x + baseX, Tmp.v1.y + baseY); } public static void buildBeam(float x, float y, float tx, float ty, float radius){ float ang = Angles.angle(x, y, tx, ty); vecs[0].set(tx - radius, ty - radius); vecs[1].set(tx + radius, ty - radius); vecs[2].set(tx - radius, ty + radius); vecs[3].set(tx + radius, ty + radius); Arrays.sort(vecs, Structs.comparingFloat(vec -> -Angles.angleDist(Angles.angle(x, y, vec.x, vec.y), ang))); Vec2 close = Geometry.findClosest(x, y, vecs); float x1 = vecs[0].x, y1 = vecs[0].y, x2 = close.x, y2 = close.y, x3 = vecs[1].x, y3 = vecs[1].y; if(renderer.animateShields){ if(close != vecs[0] && close != vecs[1]){ Fill.tri(x, y, x1, y1, x2, y2); Fill.tri(x, y, x3, y3, x2, y2); }else{ Fill.tri(x, y, x1, y1, x3, y3); } }else{ Lines.line(x, y, x1, y1); Lines.line(x, y, x3, y3); } } public static void additive(TextureRegion region, Color color, float x, float y){ additive(region, color, x, y, 0f, Layer.blockAdditive); } public static void additive(TextureRegion region, Color color, float x, float y, float rotation){ additive(region, color, x, y, rotation, Layer.blockAdditive); } public static void additive(TextureRegion region, Color color, float x, float y, float rotation, float layer){ additive(region, color, 1f, x, y, rotation, layer); } public static void additive(TextureRegion region, Color color, float alpha, float x, float y, float width, float height, float layer){ float pz = Draw.z(); Draw.z(layer); Draw.color(color, alpha * color.a); Draw.blend(Blending.additive); Draw.rect(region, x, y, width, height, 0f); Draw.blend(); Draw.color(); Draw.z(pz); } public static void additive(TextureRegion region, Color color, float alpha, float x, float y, float rotation, float layer){ float pz = Draw.z(); Draw.z(layer); Draw.color(color, alpha * color.a); Draw.blend(Blending.additive); Draw.rect(region, x, y, rotation); Draw.blend(); Draw.color(); Draw.z(pz); } public static void additive(TextureRegion region, Color color, float alpha, float x, float y, float rotation, float layer, float originX, float originY){ float pz = Draw.z(), w = region.width * region.scl() * Draw.xscl, h = region.height * region.scl() * Draw.yscl; Draw.z(layer); Draw.color(color, alpha * color.a); Draw.blend(Blending.additive); Draw.rect(region, x, y, w, h, w / 2f + originX * region.scl() * Draw.xscl, h / 2f + originY * region.scl() * Draw.yscl, rotation); Draw.blend(); Draw.color(); Draw.z(pz); } public static void limitLine(Position start, Position dest, float len1, float len2, Color color){ if(start.within(dest, len1 + len2)){ return; } Tmp.v1.set(dest).sub(start).setLength(len1); Tmp.v2.set(Tmp.v1).scl(-1f).setLength(len2); Drawf.line(color, start.getX() + Tmp.v1.x, start.getY() + Tmp.v1.y, dest.getX() + Tmp.v2.x, dest.getY() + Tmp.v2.y); } public static void limitLine(Position start, Position dest, float len1, float len2){ limitLine(start, dest, len1, len2, Pal.accent); } public static void dashLineDst(Color color, float x, float y, float x2, float y2){ dashLine(color, x, y, x2, y2, (int)(Mathf.dst(x, y, x2, y2) / tilesize * 1.6f)); } public static void dashLine(Color color, float x, float y, float x2, float y2){ dashLine(color, x, y, x2, y2, (int)(Math.max(Math.abs(x - x2), Math.abs(y - y2)) / tilesize * 2)); } public static void dashLine(Color color, float x, float y, float x2, float y2, int segments){ Lines.stroke(3f); Draw.color(Pal.gray, color.a); Lines.dashLine(x, y, x2, y2, segments); Lines.stroke(1f, color); Lines.dashLine(x, y, x2, y2, segments); Draw.reset(); } public static void line(Color color, float x, float y, float x2, float y2){ Lines.stroke(3f); Draw.color(Pal.gray, color.a); Lines.line(x, y, x2, y2); Lines.stroke(1f, color); Lines.line(x, y, x2, y2); Draw.reset(); } public static void dashLineBasic(float x, float y, float x2, float y2){ Lines.dashLine(x, y, x2, y2, (int)(Math.max(Math.abs(x - x2), Math.abs(y - y2)) / tilesize * 2)); } public static void dashSquare(Color color, float x, float y, float size){ dashRect(color, x - size/2f, y - size/2f, size, size); } public static void dashRect(Color color, Rect rect){ dashRect(color, rect.x, rect.y, rect.width, rect.height); } public static void dashRect(Color color, float x, float y, float width, float height){ dashLine(color, x, y, x + width, y); dashLine(color, x + width, y, x + width, y + height); dashLine(color, x + width, y + height, x, y + height); dashLine(color, x, y + height, x, y); } public static void dashSquareBasic(float x, float y, float size){ dashRectBasic(x - size/2f, y - size/2f, size, size); } public static void dashRectBasic(float x, float y, float width, float height){ dashLineBasic(x, y, x + width, y); dashLineBasic(x + width, y, x + width, y + height); dashLineBasic(x + width, y + height, x, y + height); dashLineBasic( x, y + height, x, y); } public static void target(float x, float y, float rad, Color color){ target(x, y, rad, 1, color); } public static void target(float x, float y, float rad, float alpha, Color color){ Lines.stroke(3f); Draw.color(Pal.gray, alpha); Lines.poly(x, y, 4, rad, Time.time * 1.5f); Lines.spikes(x, y, 3f/7f * rad, 6f/7f * rad, 4, Time.time * 1.5f); Lines.stroke(1f); Draw.color(color, alpha); Lines.poly(x, y, 4, rad, Time.time * 1.5f); Lines.spikes(x, y, 3f/7f * rad, 6f/7f * rad, 4, Time.time * 1.5f); Draw.reset(); } /** Sets Draw.z to the text layer, and returns the previous layer. */ public static float text(){ float z = Draw.z(); if(renderer.pixelate){ Draw.z(Layer.endPixeled); } return z; } public static void light(float x, float y, float radius, Color color, float opacity){ if(renderer == null) return; renderer.lights.add(x, y, radius, color, opacity); } public static void light(Position pos, float radius, Color color, float opacity){ if(renderer == null) return; light(pos.getX(), pos.getY(), radius, color, opacity); } public static void light(float x, float y, TextureRegion region, Color color, float opacity){ light(x, y, region, 0f, color, opacity); } public static void light(float x, float y, TextureRegion region, float rotation, Color color, float opacity){ if(renderer == null) return; renderer.lights.add(x, y, region, rotation, color, opacity); } public static void light(float x, float y, float x2, float y2){ if(renderer == null) return; renderer.lights.line(x, y, x2, y2, 30, Color.orange, 0.3f); } public static void light(float x, float y, float x2, float y2, float stroke, Color tint, float alpha){ if(renderer == null) return; renderer.lights.line(x, y, x2, y2, stroke, tint, alpha); } public static void selected(Building tile, Color color){ selected(tile.tile, color); } public static void selected(Tile tile, Color color){ selected(tile.x, tile.y, tile.block(), color); } public static void selected(int x, int y, Block block, Color color){ Draw.color(color); for(int i = 0; i < 4; i++){ Point2 p = Geometry.d8edge[i]; float offset = -Math.max(block.size - 1, 0) / 2f * tilesize; Draw.rect("block-select", x*tilesize + block.offset + offset * p.x, y*tilesize + block.offset + offset * p.y, i * 90); } Draw.reset(); } public static void shadow(float x, float y, float rad){ shadow(x, y, rad, 1f); } public static void squareShadow(float x, float y, float rad, float alpha){ Draw.color(0, 0, 0, 0.4f * alpha); Draw.rect("square-shadow", x, y, rad * Draw.xscl, rad * Draw.yscl); Draw.color(); } public static void shadow(float x, float y, float rad, float alpha){ Draw.color(0, 0, 0, 0.4f * alpha); Draw.rect("circle-shadow", x, y, rad * Draw.xscl, rad * Draw.yscl); Draw.color(); } public static void shadow(TextureRegion region, float x, float y, float rotation){ Draw.color(Pal.shadow); Draw.rect(region, x, y, rotation); Draw.color(); } public static void shadow(TextureRegion region, float x, float y){ Draw.color(Pal.shadow); Draw.rect(region, x, y); Draw.color(); } public static void shadow(TextureRegion region, float x, float y, float width, float height, float rotation){ Draw.color(Pal.shadow); Draw.rect(region, x, y, width, height, rotation); Draw.color(); } public static void liquid(TextureRegion region, float x, float y, float alpha, Color color, float rotation){ Draw.color(color, alpha * color.a); Draw.rect(region, x, y, rotation); Draw.color(); } public static void liquid(TextureRegion region, float x, float y, float alpha, Color color){ Draw.color(color, alpha * color.a); Draw.rect(region, x, y); Draw.color(); } public static void dashCircle(float x, float y, float rad, Color color){ Lines.stroke(3f, Pal.gray); Lines.dashCircle(x, y, rad); Lines.stroke(1f, color); Lines.dashCircle(x, y, rad); Draw.reset(); } public static void circles(float x, float y, float rad){ circles(x, y, rad, Pal.accent); } public static void circles(float x, float y, float rad, Color color){ Lines.stroke(3f, Pal.gray); Lines.circle(x, y, rad); Lines.stroke(1f, color); Lines.circle(x, y, rad); Draw.reset(); } public static void select(float x, float y, float radius, Color color){ Lines.stroke(3f, Pal.gray); Lines.square(x, y, radius + 1f); Lines.stroke(1f, color); Lines.square(x, y, radius); Draw.reset(); } public static void square(float x, float y, float radius, float rotation, Color color){ Lines.stroke(3f, Pal.gray.write(Tmp.c3).a(color.a)); Lines.square(x, y, radius + 1f, rotation); Lines.stroke(1f, color); Lines.square(x, y, radius + 1f, rotation); Draw.reset(); } public static void poly(float x, float y, int sides, float radius, float rotation, Color color){ Lines.stroke(3f, Pal.gray); Lines.poly(x, y, sides, radius + 1f, rotation); Lines.stroke(1f, color); Lines.poly(x, y, sides, radius + 1f, rotation); Draw.reset(); } public static void square(float x, float y, float radius, float rotation){ square(x, y, radius, rotation, Pal.accent); } public static void square(float x, float y, float radius, Color color){ square(x, y, radius, 45, color); } public static void square(float x, float y, float radius){ square(x, y, radius, 45); } public static void arrow(float x, float y, float x2, float y2, float length, float radius){ arrow(x, y, x2, y2, length, radius, Pal.accent); } public static void arrow(float x, float y, float x2, float y2, float length, float radius, Color color){ float angle = Angles.angle(x, y, x2, y2); float space = 2f; Tmp.v1.set(x2, y2).sub(x, y).limit(length); float vx = Tmp.v1.x + x, vy = Tmp.v1.y + y; Draw.color(Pal.gray); Fill.poly(vx, vy, 3, radius + space, angle); Draw.color(color); Fill.poly(vx, vy, 3, radius, angle); Draw.color(); } public static void laser(TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2){ laser(line, edge, edge, x, y, x2, y2, 1f); } public static void laser(TextureRegion line, TextureRegion start, TextureRegion end, float x, float y, float x2, float y2){ laser(line, start, end, x, y, x2, y2, 1f); } public static void laser(TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2, float scale){ laser(line, edge, edge, x, y, x2, y2, scale); } public static void laser(TextureRegion line, TextureRegion start, TextureRegion end, float x, float y, float x2, float y2, float scale){ float scl = 8f * scale * Draw.scl, rot = Mathf.angle(x2 - x, y2 - y); float vx = Mathf.cosDeg(rot) * scl, vy = Mathf.sinDeg(rot) * scl; Draw.rect(start, x, y, start.width * scale * start.scl(), start.height * scale * start.scl(), rot + 180); Draw.rect(end, x2, y2, end.width * scale * end.scl(), end.height * scale * end.scl(), rot); Lines.stroke(12f * scale); Lines.line(line, x + vx, y + vy, x2 - vx, y2 - vy, false); Lines.stroke(1f); light(x, y, x2, y2); } public static void tri(float x, float y, float width, float length, float rotation){ float fx = Angles.trnsx(rotation, length), fy = Angles.trnsy(rotation, length), rx = Angles.trnsx(rotation - 90f, width / 2f), ry = Angles.trnsy(rotation - 90f, width / 2f); Fill.tri( x + rx, y + ry, x + fx, y + fy, x - rx, y - ry ); } public static void construct(Building t, UnlockableContent content, float rotation, float progress, float alpha, float time){ construct(t, content.fullIcon, rotation, progress, alpha, time); } public static void construct(float x, float y, TextureRegion region, float rotation, float progress, float alpha, float time){ construct(x, y, region, Pal.accent, rotation, progress, alpha, time); } public static void construct(float x, float y, TextureRegion region, Color color, float rotation, float progress, float alpha, float time){ Shaders.build.region = region; Shaders.build.progress = progress; Shaders.build.color.set(color); Shaders.build.color.a = alpha; Shaders.build.time = -time / 20f; Draw.shader(Shaders.build); Draw.rect(region, x, y, rotation); Draw.shader(); Draw.reset(); } public static void construct(Building t, TextureRegion region, float rotation, float progress, float alpha, float time){ construct(t, region, Pal.accent, rotation, progress, alpha, time); } public static void construct(Building t, TextureRegion region, Color color, float rotation, float progress, float alpha, float time){ construct(t, region, color, rotation, progress, alpha, time, t.block.size * tilesize - 4f); } public static void construct(Building t, TextureRegion region, Color color, float rotation, float progress, float alpha, float time, float size){ Shaders.build.region = region; Shaders.build.progress = progress; Shaders.build.color.set(color); Shaders.build.color.a = alpha; Shaders.build.time = -time / 20f; Draw.shader(Shaders.build); Draw.rect(region, t.x, t.y, rotation); Draw.shader(); Draw.color(Pal.accent); Draw.alpha(alpha); Lines.lineAngleCenter(t.x + Mathf.sin(time, 20f, size / 2f), t.y, 90, size); Draw.reset(); } /** Draws a sprite that should be light-wise correct, when rotated. Provided sprite must be symmetrical in shape. */ public static void spinSprite(TextureRegion region, float x, float y, float r){ float a = Draw.getColorAlpha(); r = Mathf.mod(r, 90f); Draw.rect(region, x, y, r); Draw.alpha(r / 90f*a); Draw.rect(region, x, y, r - 90f); Draw.alpha(a); } }