From 76717664699dc259a24395cfbc7bb6eab7c498ab Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 2 Aug 2024 17:42:22 -0400 Subject: [PATCH] Beam node link previews, closes Anuken/Mindustry-Suggestions/issues/5140 --- core/src/mindustry/world/Block.java | 8 ++ .../world/blocks/power/BeamNode.java | 93 +++++++++++++++++++ gradle.properties | 2 +- 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index d2b2cbf2fa..1576cfb162 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -441,6 +441,14 @@ public class Block extends UnlockableContent implements Senseable{ Drawf.square(other.x, other.y, other.block.size * tilesize / 2f + 2f, Pal.place); }); + + BeamNode.getNodeLinks(tile, this, player.team(), other -> { + BeamNode node = (BeamNode)other.block; + Draw.color(node.laserColor1, Renderer.laserOpacity * 0.5f); + node.drawLaser(other.x, other.y, x * tilesize + offset, y * tilesize + offset, size, other.block.size); + + Drawf.square(other.x, other.y, other.block.size * tilesize / 2f + 2f, Pal.place); + }); } } } diff --git a/core/src/mindustry/world/blocks/power/BeamNode.java b/core/src/mindustry/world/blocks/power/BeamNode.java index b99590d2be..163a074a52 100644 --- a/core/src/mindustry/world/blocks/power/BeamNode.java +++ b/core/src/mindustry/world/blocks/power/BeamNode.java @@ -1,14 +1,17 @@ package mindustry.world.blocks.power; +import arc.func.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; import arc.struct.*; +import arc.util.*; import mindustry.*; import mindustry.annotations.Annotations.*; import mindustry.core.*; import mindustry.entities.*; +import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.input.*; @@ -20,6 +23,9 @@ import java.util.*; import static mindustry.Vars.*; public class BeamNode extends PowerBlock{ + //maximum expected range of any beam node; used for previews + private static final int maxRange = 30; + public int range = 5; public @Load(value = "@-beam", fallback = "power-beam") TextureRegion laser; @@ -105,12 +111,99 @@ public class BeamNode extends PowerBlock{ } } + /** Iterates through linked nodes of a block at a tile. All returned buildings are beam nodes. */ + public static void getNodeLinks(Tile tile, Block block, Team team, Cons others){ + var tree = team.data().buildingTree; + + if(tree == null) return; + + float cx = tile.worldx() + block.offset, cy = tile.worldy() + block.offset, s = block.size * tilesize/2f, r = maxRange * tilesize; + + for(int i = 0; i < 4; i++){ + int dx = Geometry.d4[i].x, dy = Geometry.d4[i].y; + for(int length = 1; length < maxRange; length++){ + Tile other = tile.nearby(dx * i, dy * i); + if(other == null || other.build == null || other.build.isInsulated()) break; + + Building build = other.build; + + if(build.team == team && build.block.hasPower && build.block.connectedPower){ + if(build instanceof BeamNodeBuild){ + if(build.tileX() == tile.x || build.tileY() == tile.y){ + others.get(build); + break; + } + }else{ //it's a power structure that is blocking the way to any potential beam node. + break; + } + } + } + + switch(i){ + case 0 -> Tmp.r1.set(cx - s, cy - s, r, s * 2f); + case 1 -> Tmp.r1.set(cx - s, cy - s, s * 2f, r); + case 2 -> Tmp.r1.set(cx + s, cy - s, -r, s * 2f).normalize(); + case 3 -> Tmp.r1.set(cx - s, cy + s, s * 2f, -r).normalize(); + } + + tempBuilds.clear(); + tree.intersect(Tmp.r1, tempBuilds); + int fi = i; + Building closest = tempBuilds.min(b -> b instanceof BeamNodeBuild node && node.couldConnect((fi + 2) % 4, block, tile.x, tile.y), b -> b.dst2(cx, cy)); + if(closest != null){ + others.get(closest); + } + } + } + + /** Note that x1 and y1 are expected to be coordinates of the node to draw the beam from. */ + public void drawLaser(float x1, float y1, float x2, float y2, int size1, int size2){ + float w = laserWidth; + float dst = Math.max(Math.abs(x1 - x2), Math.abs(y2 - y1)) / tilesize; + float sizeOff = dst * tilesize - (size1 + size2) * tilesize/2f; + + //don't draw lasers for adjacent blocks + if(dst > 1 + size/2){ + var point = Geometry.d4(Tile.relativeTo(x1, y1, x2, y2)); + float poff = tilesize/2f; + Drawf.laser(laser, laserEnd, x1 + poff*size*point.x, y1 + poff*size*point.y, x1 + (poff*size + sizeOff) * point.x, y1 + (poff*size + sizeOff) * point.y, w); + } + } + public class BeamNodeBuild extends Building{ //current links in cardinal directions public Building[] links = new Building[4]; public Tile[] dests = new Tile[4]; public int lastChange = -2; + /** @return whether a beam could theoretically connect with the specified block at a position */ + public boolean couldConnect(int direction, Block target, int targetX, int targetY){ + int offset = -(target.size - 1) / 2; + int minX = targetX + offset, minY = targetY + offset, maxX = targetX + offset + target.size - 1, maxY = targetY + offset + target.size - 1; + var dir = Geometry.d4[direction]; + + int rangeOffset = size/2; + + //find first block with power in range + for(int j = 1 + rangeOffset; j <= range + rangeOffset; j++){ + var other = world.tile(tile.x + j * dir.x, tile.y + j * dir.y); + + if(other == null) return false; + + //hit insulated wall + if((other.build != null && other.build.isInsulated()) || (other.block().hasPower && other.block().connectedPower && other.team() == team)){ + return false; + } + + //within target rectangle + if(other.x >= minX && other.y >= minY && other.x <= maxX && other.y <= maxY){ + return true; + } + } + + return false; + } + @Override public void updateTile(){ //TODO this block technically does not need to update every frame, perhaps put it in a special list. diff --git a/gradle.properties b/gradle.properties index 5c00967a82..048ee4d9ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,4 @@ org.gradle.caching=true org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 android.enableR8.fullMode=false -archash=1fdfcdc213 +archash=6676d38433