diff --git a/core/assets-raw/sprites/blocks/extra/block-select.png b/core/assets-raw/sprites/blocks/extra/block-select.png index f2c77caa2b..559131b263 100644 Binary files a/core/assets-raw/sprites/blocks/extra/block-select.png and b/core/assets-raw/sprites/blocks/extra/block-select.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index d816e2f14e..7799e73de5 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -216,6 +216,9 @@ quit.confirm.tutorial = Are you sure you know what you're doing?\nThe tutorial c loading = [accent]Loading... reloading = [accent]Reloading Mods... saving = [accent]Saving... +cancelbuilding = [accent][[{0}][] to clear plan +pausebuilding = [accent][[{0}][] to pause building +resumebuilding = [scarlet][[{0}][] to resume building wave = [accent]Wave {0} wave.waiting = [lightgray]Wave in {0} wave.waveInProgress = [lightgray]Wave in progress @@ -560,6 +563,7 @@ category.optional = Optional Enhancements setting.landscape.name = Lock Landscape setting.shadows.name = Shadows setting.linear.name = Linear Filtering +setting.hints.name = Hints setting.animatedwater.name = Animated Water setting.animatedshields.name = Animated Shields setting.antialias.name = Antialias[lightgray] (requires restart)[] @@ -614,6 +618,7 @@ command.rally = Rally command.retreat = Retreat keybind.gridMode.name = Block Select keybind.gridModeShift.name = Category Select +keybind.clear_building.name = Clear Building keybind.press = Press a key... keybind.press.axis = Press an axis or key... keybind.screenshot.name = Map Screenshot @@ -630,6 +635,7 @@ keybind.zoom_hold.name = Zoom Hold keybind.zoom.name = Zoom keybind.menu.name = Menu keybind.pause.name = Pause +keybind.pause_building.name = Pause/Resume Building keybind.minimap.name = Minimap keybind.dash.name = Dash keybind.chat.name = Chat diff --git a/core/assets/sprites/sprites.png b/core/assets/sprites/sprites.png index 3b1dbc0ad1..3c65367e70 100644 Binary files a/core/assets/sprites/sprites.png and b/core/assets/sprites/sprites.png differ diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 92e7c96134..08302458d2 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -177,7 +177,7 @@ public class Control implements ApplicationListener, Loadable{ }); Events.on(UnitDestroyEvent.class, e -> { - if(e.unit instanceof BaseUnit){ + if(e.unit instanceof BaseUnit && world.isZone()){ data.unlockContent(((BaseUnit)e.unit).getType()); } }); @@ -416,6 +416,7 @@ public class Control implements ApplicationListener, Loadable{ music.update(); loops.update(); + Time.updateGlobal(); if(Core.input.keyTap(Binding.fullscreen)){ boolean full = settings.getBool("fullscreen"); diff --git a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java index 79069ca5b4..6e1ab37030 100644 --- a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java +++ b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java @@ -6,8 +6,8 @@ import io.anuke.arc.collection.*; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.*; import io.anuke.arc.math.geom.*; -import io.anuke.arc.util.*; import io.anuke.arc.util.ArcAnnotate.*; +import io.anuke.arc.util.*; import io.anuke.mindustry.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.entities.type.*; @@ -188,6 +188,11 @@ public interface BuilderTrait extends Entity, TeamTrait{ /** Add another build requests to the tail of the queue, if it doesn't exist there yet. */ default void addBuildRequest(BuildRequest place){ + addBuildRequest(place, true); + } + + /** Add another build requests to the queue, if it doesn't exist there yet. */ + default void addBuildRequest(BuildRequest place, boolean tail){ for(BuildRequest request : buildQueue()){ if(request.x == place.x && request.y == place.y){ return; @@ -197,7 +202,11 @@ public interface BuilderTrait extends Entity, TeamTrait{ if(tile != null && tile.entity instanceof BuildEntity){ place.progress = tile.entity().progress; } - buildQueue().addLast(place); + if(tail){ + buildQueue().addLast(place); + }else{ + buildQueue().addFirst(place); + } } /** @@ -258,15 +267,19 @@ public interface BuilderTrait extends Entity, TeamTrait{ /** Class for storing build requests. Can be either a place or remove request. */ class BuildRequest{ - public final int x, y, rotation; - public final Block block; - public final boolean breaking; + public int x, y, rotation; + public @Nullable Block block; + public boolean breaking; public boolean hasConfig; public int config; public float progress; public boolean initialized; + //animation variables + public float animScale; + public float animInvalid; + /** This creates a build request. */ public BuildRequest(int x, int y, int rotation, Block block){ this.x = x; @@ -285,13 +298,42 @@ public interface BuilderTrait extends Entity, TeamTrait{ this.breaking = true; } + public BuildRequest(){ + + } + + public Rectangle bounds(Rectangle rect){ + if(breaking){ + return rect.set(-100f, -100f, 0f, 0f); + }else{ + return block.bounds(x, y, rect); + } + } + + public BuildRequest set(int x, int y, int rotation, Block block){ + this.x = x; + this.y = y; + this.rotation = rotation; + this.block = block; + this.breaking = false; + return this; + } + + public float drawx(){ + return x*tilesize + block.offset(); + } + + public float drawy(){ + return y*tilesize + block.offset(); + } + public BuildRequest configure(int config){ this.config = config; this.hasConfig = true; return this; } - public Tile tile(){ + public @Nullable Tile tile(){ return world.tile(x, y); } diff --git a/core/src/io/anuke/mindustry/entities/type/Player.java b/core/src/io/anuke/mindustry/entities/type/Player.java index 5b122c9ff2..36aa33c096 100644 --- a/core/src/io/anuke/mindustry/entities/type/Player.java +++ b/core/src/io/anuke/mindustry/entities/type/Player.java @@ -19,9 +19,7 @@ import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.traits.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; -import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.input.*; -import io.anuke.mindustry.input.InputHandler.*; import io.anuke.mindustry.io.*; import io.anuke.mindustry.net.Administration.*; import io.anuke.mindustry.net.*; @@ -51,7 +49,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ public String name = "noname"; public @Nullable String uuid, usid; - public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping; + public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping, isBuilding = true; public float boostHeat, shootHeat, destructTime; public boolean achievedFlight; public Color color = new Color(); @@ -357,7 +355,13 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ public void drawOver(){ if(dead) return; - drawMechanics(); + if(isBuilding()){ + if(!state.isPaused() && isBuilding){ + drawBuilding(); + } + }else{ + drawMining(); + } } @Override @@ -424,57 +428,16 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ /** Draw all current build requests. Does not draw the beam effect, only the positions. */ public void drawBuildRequests(){ - BuildRequest last = null; + if(!isLocal) return; + for(BuildRequest request : buildQueue()){ if(request.progress > 0.01f || (buildRequest() == request && request.initialized && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue; if(request.breaking){ - Block block = world.ltile(request.x, request.y).block(); - - //draw removal request - Lines.stroke(2f, Pal.removeBack); - - float rad = Mathf.absin(Time.time(), 7f, 1f) + block.size * tilesize / 2f - 1; - Lines.square( - request.x * tilesize + block.offset(), - request.y * tilesize + block.offset() - 1, - rad); - - Draw.color(Pal.remove); - - Lines.square( - request.x * tilesize + block.offset(), - request.y * tilesize + block.offset(), rad); + control.input.drawBreaking(request); }else{ - Draw.color(); - PlaceDraw draw = PlaceDraw.instance; - - draw.scalex = 1; - draw.scaley = 1; - draw.rotation = request.rotation; - - if(last == null){ - request.block.getPlaceDraw(draw, request.rotation, request.x, request.y, request.rotation); - }else{ - request.block.getPlaceDraw(draw, request.rotation, last.x - request.x, last.y - request.y, last.rotation); - } - - TextureRegion region = draw.region; - - Draw.rect(region, - request.x * tilesize + request.block.offset(), request.y * tilesize + request.block.offset(), - region.getWidth() * 1f * Draw.scl * draw.scalex, - region.getHeight() * 1f * Draw.scl * draw.scaley, request.block.rotate ? draw.rotation * 90 : 0); - - Draw.color(Pal.accent); - for(int i = 0; i < 4; i++){ - Point2 p = Geometry.d8edge[i]; - float offset = -Math.max(request.block.size - 1, 0) / 2f * tilesize; - Draw.rect("block-select", request.x * tilesize + request.block.offset() + offset * p.x, request.y * tilesize + request.block.offset() + offset * p.y, i * 90); - } - Draw.color(); - - last = request; + request.block.drawRequest(request, control.input.allRequests(), + Build.validPlace(getTeam(), request.x, request.y, request.block, request.rotation)); } } @@ -485,6 +448,18 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ //region update methods + @Override + public void updateMechanics(){ + if(isBuilding){ + updateBuilding(); + } + + //mine only when not building + if(buildRequest() == null){ + updateMining(); + } + } + @Override public void update(){ hitTime -= Time.delta(); @@ -517,7 +492,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ } BuildRequest request = buildRequest(); - if(isBuilding() && request.tile() != null && (request.tile().withinDst(x, y, placeDistance) || state.isEditor())){ + if(isBuilding() && isBuilding && request.tile() != null && (request.tile().withinDst(x, y, placeDistance) || state.isEditor())){ loops.play(Sounds.build, request.tile(), 0.75f); } @@ -817,6 +792,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ placeQueue.clear(); dead = true; lastText = null; + isBuilding = true; textFadeTime = 0f; target = null; moveTarget = null; @@ -910,7 +886,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ public void write(DataOutput buffer) throws IOException{ super.writeSave(buffer, !isLocal); TypeIO.writeStringData(buffer, name); - buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3)); + buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3)| (Pack.byteValue(isBuilding) << 4)); buffer.writeInt(Color.rgba8888(color)); buffer.writeByte(mech.id); buffer.writeInt(mining == null ? noSpawner : mining.pos()); @@ -932,6 +908,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ dead = (bools & 2) != 0; boolean boosting = (bools & 4) != 0; isTyping = (bools & 8) != 0; + boolean building = (bools & 16) != 0; color.set(buffer.readInt()); mech = content.getByID(ContentType.mech, buffer.readByte()); int mine = buffer.readInt(); @@ -950,6 +927,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ velocity.y = lastvy; }else{ mining = world.tile(mine); + isBuilding = building; isBoosting = boosting; } diff --git a/core/src/io/anuke/mindustry/graphics/BlockRenderer.java b/core/src/io/anuke/mindustry/graphics/BlockRenderer.java index 767ffa9ec2..e07f10316e 100644 --- a/core/src/io/anuke/mindustry/graphics/BlockRenderer.java +++ b/core/src/io/anuke/mindustry/graphics/BlockRenderer.java @@ -10,11 +10,10 @@ import io.anuke.arc.math.*; import io.anuke.arc.util.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.entities.type.base.*; -import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.Teams.*; import io.anuke.mindustry.world.*; -import io.anuke.mindustry.world.Block.*; import static io.anuke.arc.Core.camera; import static io.anuke.mindustry.Vars.*; @@ -131,7 +130,7 @@ public class BlockRenderer implements Disposable{ if(!camera.bounds(Tmp.r1).grow(tilesize * 2f).overlaps(Tmp.r2.setSize(b.size * tilesize).setCenter(block.x * tilesize + b.offset(), block.y * tilesize + b.offset()))) continue; Draw.alpha(0.5f); - Draw.mixcol(Pal.accent, 0.2f + Mathf.absin(5f, 0.2f)); + Draw.mixcol(Color.white, 0.2f + Mathf.absin(Time.globalTime(), 6f, 0.2f)); Draw.rect(b.icon(Cicon.full), block.x * tilesize + b.offset(), block.y * tilesize + b.offset(), b.rotate ? block.rotation * 90 : 0f); } Draw.reset(); diff --git a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java index b47b648f69..3c91d89a58 100644 --- a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java +++ b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java @@ -25,9 +25,9 @@ public class OverlayRenderer{ public void drawBottom(){ InputHandler input = control.input; - if(!input.isDrawing() || player.isDead()) return; + if(player.isDead()) return; - input.drawOutlined(); + input.drawBottom(); } public void drawTop(){ diff --git a/core/src/io/anuke/mindustry/input/Binding.java b/core/src/io/anuke/mindustry/input/Binding.java index 8f599a872f..90309b74ad 100644 --- a/core/src/io/anuke/mindustry/input/Binding.java +++ b/core/src/io/anuke/mindustry/input/Binding.java @@ -12,6 +12,8 @@ public enum Binding implements KeyBind{ select(KeyCode.MOUSE_LEFT), deselect(KeyCode.MOUSE_RIGHT), break_block(KeyCode.MOUSE_RIGHT), + clear_building(KeyCode.Q), + pause_building(KeyCode.E), rotate(new Axis(KeyCode.SCROLL)), rotateplaced(KeyCode.R), diagonal_placement(KeyCode.CONTROL_LEFT), diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index 3bd752086e..cc59ea8dae 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -5,15 +5,15 @@ import io.anuke.arc.Graphics.*; import io.anuke.arc.Graphics.Cursor.*; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.*; -import io.anuke.arc.math.geom.*; +import io.anuke.arc.scene.*; import io.anuke.arc.scene.ui.*; -import io.anuke.arc.util.*; -import io.anuke.mindustry.content.*; +import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.mindustry.core.GameState.*; +import io.anuke.mindustry.entities.traits.BuilderTrait.*; import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; -import io.anuke.mindustry.input.PlaceUtils.*; +import io.anuke.mindustry.ui.*; import io.anuke.mindustry.world.*; import static io.anuke.arc.Core.scene; @@ -23,44 +23,18 @@ import static io.anuke.mindustry.input.PlaceMode.*; public class DesktopInput extends InputHandler{ /** Current cursor type. */ private Cursor cursorType = SystemCursor.arrow; - /** Position where the player started dragging a line. */ private int selectX, selectY; + /** Last known line positions.*/ + private int lastLineX, lastLineY; /** Whether selecting mode is active. */ private PlaceMode mode; /** Animation scale for line. */ private float selectScale; - - private int prevX, prevY, prevRotation; - - /** Draws a placement icon for a specific block. */ - void drawPlace(int x, int y, Block block, int rotation, int prevX, int prevY, int prevRotation){ - if(validPlace(x, y, block, rotation)){ - block.getPlaceDraw(placeDraw, rotation, prevX, prevY, prevRotation); - - Draw.color(); - Draw.mixcol(Pal.accent, 0.12f + Mathf.absin(Time.time(), 8f, 0.35f)); - Draw.rect(placeDraw.region, x * tilesize + block.offset(), y * tilesize + block.offset(), - placeDraw.region.getWidth() * selectScale * Draw.scl * placeDraw.scalex, - placeDraw.region.getHeight() * selectScale * Draw.scl * placeDraw.scaley, - block.rotate ? placeDraw.rotation * 90 : 0); - - Draw.color(Pal.accent); - for(int i = 0; i < 4; i++){ - Point2 p = Geometry.d8edge[i]; - float offset = -Math.max(block.size - 1, 0) / 2f * tilesize; - if(i % 2 == 0) - Draw.rect("block-select", x * tilesize + block.offset() + offset * p.x, y * tilesize + block.offset() + offset * p.y, i * 90); - } - Draw.color(); - Draw.mixcol(); - }else{ - Draw.color(Pal.removeBack); - Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f - 1); - Draw.color(Pal.remove); - Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize / 2f - 1); - } - } + /** Selected build request for movement. */ + private @Nullable BuildRequest sreq; + /** Whether player is currently deleting removal requests. */ + private boolean deleting = false; @Override public boolean isDrawing(){ @@ -68,56 +42,63 @@ public class DesktopInput extends InputHandler{ } @Override - public void drawOutlined(){ + public void buildUI(Group group){ + group.fill(t -> { + t.bottom().update(() -> t.getColor().a = Mathf.lerpDelta(t.getColor().a, player.isBuilding() ? 1f : 0f, 0.15f)); + t.visible(() -> Core.settings.getBool("hints")); + t.table(Styles.black6, b -> { + b.defaults().left(); + b.label(() -> Core.bundle.format(!player.isBuilding ? "resumebuilding" : "pausebuilding", Core.keybinds.get(Binding.pause_building).key.name())).style(Styles.outlineLabel); + b.row(); + b.add(Core.bundle.format("cancelbuilding", Core.keybinds.get(Binding.clear_building).key.name())).style(Styles.outlineLabel); + }).margin(10f); + }); + } + + @Override + public void drawTop(){ Lines.stroke(1f); int cursorX = tileX(Core.input.mouseX()); int cursorY = tileY(Core.input.mouseY()); //draw selection(s) if(mode == placing && block != null){ - prevX = selectX; - prevY = selectY; - prevRotation = rotation; - - iterateLine(selectX, selectY, cursorX, cursorY, l -> { - if(l.last && block.rotate){ - drawArrow(block, l.x, l.y, l.rotation); - } - drawPlace(l.x, l.y, block, l.rotation, prevX - l.x, prevY - l.y, prevRotation); - - prevX = l.x; - prevY = l.y; - prevRotation = l.rotation; - }); - - }else if(mode == breaking){ - NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, selectX, selectY, cursorX, cursorY, false, maxLength, 1f); - NormalizeResult dresult = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, false, maxLength); - - for(int x = dresult.x; x <= dresult.x2; x++){ - for(int y = dresult.y; y <= dresult.y2; y++){ - Tile tile = world.ltile(x, y); - if(tile == null || !validBreak(tile.x, tile.y)) continue; - - Draw.color(Pal.removeBack); - Lines.square(tile.drawx(), tile.drawy() - 1, tile.block().size * tilesize / 2f - 1); - Draw.color(Pal.remove); - Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize / 2f - 1); + for(int i = 0; i < lineRequests.size; i++){ + BuildRequest req = lineRequests.get(i); + if(i == lineRequests.size - 1 && req.block.rotate){ + drawArrow(block, req.x, req.y, req.rotation); } + drawRequest(lineRequests.get(i)); } - - Draw.color(Pal.removeBack); - Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y); - Draw.color(Pal.remove); - Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y); + }else if(mode == breaking){ + drawSelection(selectX, selectY, cursorX, cursorY); }else if(isPlacing()){ if(block.rotate){ drawArrow(block, cursorX, cursorY, rotation); } - drawPlace(cursorX, cursorY, block, rotation, cursorX, cursorY, rotation); + Draw.color(); + drawRequest(cursorX, cursorY, block, rotation); block.drawPlace(cursorX, cursorY, rotation, validPlace(cursorX, cursorY, block, rotation)); } + if(mode == none && !isPlacing()){ + BuildRequest req = getRequest(cursorX, cursorY); + if(req != null){ + drawSelected(req.x, req.y, req.breaking ? req.tile().block() : req.block, Pal.accent); + } + } + + if(sreq != null){ + boolean valid = validPlace(sreq.x, sreq.y, sreq.block, sreq.rotation, sreq); + if(sreq.block.rotate){ + drawArrow(sreq.block, sreq.x, sreq.y, sreq.rotation, valid); + } + + sreq.block.drawRequest(sreq, allRequests(), valid); + + drawSelected(sreq.x, sreq.y, sreq.block, getRequest(sreq.x, sreq.y, sreq.block.size, sreq) != null ? Pal.remove : Pal.accent); + } + Draw.reset(); } @@ -171,6 +152,14 @@ public class DesktopInput extends InputHandler{ rotation = Mathf.mod(rotation + (int)Core.input.axisTap(Binding.rotate), 4); + if(sreq != null){ + sreq.rotation = Mathf.mod(sreq.rotation + (int)Core.input.axisTap(Binding.rotate), 4); + } + + if(Math.abs((int)Core.input.axisTap(Binding.rotate)) > 0 && isPlacing() && mode == placing){ + updateLine(selectX, selectY); + } + Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY()); if(cursor != null){ @@ -186,6 +175,10 @@ public class DesktopInput extends InputHandler{ cursorType = ui.drillCursor; } + if(getRequest(cursor.x, cursor.y) != null && mode == none){ + cursorType = SystemCursor.hand; + } + if(canTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y)){ cursorType = ui.unloadCursor; } @@ -211,11 +204,46 @@ public class DesktopInput extends InputHandler{ player.setMineTile(null); } + if(Core.input.keyTap(Binding.clear_building)){ + player.clearBuilding(); + } + + if(sreq != null){ + float offset = ((sreq.block.size + 2) % 2) * tilesize / 2f; + float x = Core.input.mouseWorld().x + offset; + float y = Core.input.mouseWorld().y + offset; + sreq.x = (int)(x / tilesize); + sreq.y = (int)(y / tilesize); + } + + if(block == null || mode != placing){ + lineRequests.clear(); + } + + if(Core.input.keyTap(Binding.pause_building)){ + player.isBuilding = !player.isBuilding; + } + + if((cursorX != lastLineX || cursorY != lastLineY) && isPlacing() && mode == placing){ + updateLine(selectX, selectY); + lastLineX = cursorX; + lastLineY = cursorY; + } + if(Core.input.keyTap(Binding.select) && !Core.scene.hasMouse()){ + BuildRequest req = getRequest(cursorX, cursorY); + if(isPlacing()){ selectX = cursorX; selectY = cursorY; + lastLineX = cursorX; + lastLineY = cursorY; mode = placing; + updateLine(selectX, selectY); + }else if(req != null && !req.breaking && mode == none){ + sreq = req; + }else if(req != null && req.breaking){ + deleting = true; }else if(selected != null){ //only begin shooting if there's no cursor event if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.buildQueue().size == 0 && !droppingItem && @@ -225,23 +253,28 @@ public class DesktopInput extends InputHandler{ }else if(!ui.chatfrag.chatOpen()){ //if it's out of bounds, shooting is just fine player.isShooting = true; } - }else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) && - !(player.buildRequest() != null && player.buildRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){ - if(block == null){ - player.clearBuilding(); - } - + }else if(Core.input.keyTap(Binding.deselect) && block != null){ block = null; mode = none; }else if(Core.input.keyTap(Binding.break_block) && !Core.scene.hasMouse()){ //is recalculated because setting the mode to breaking removes potential multiblock cursor offset + deleting = false; mode = breaking; selectX = tileX(Core.input.mouseX()); selectY = tileY(Core.input.mouseY()); } - if (mode == placing && block != null){ - if (!overrideLineRotation && !Core.input.keyDown(Binding.diagonal_placement) && (selectX != cursorX || selectY != cursorY) && ((int) Core.input.axisTap(Binding.rotate) != 0)){ + if(Core.input.keyDown(Binding.select) && mode == none && !isPlacing() && deleting){ + BuildRequest req = getRequest(cursorX, cursorY); + if(req != null && req.breaking){ + player.buildQueue().remove(req); + } + }else{ + deleting = false; + } + + if(mode == placing && block != null){ + if(!overrideLineRotation && !Core.input.keyDown(Binding.diagonal_placement) && (selectX != cursorX || selectY != cursorY) && ((int) Core.input.axisTap(Binding.rotate) != 0)){ rotation = ((int)((Angles.angle(selectX, selectY, cursorX, cursorY) + 45) / 90f)) % 4; overrideLineRotation = true; } @@ -252,27 +285,24 @@ public class DesktopInput extends InputHandler{ if(Core.input.keyRelease(Binding.break_block) || Core.input.keyRelease(Binding.select)){ if(mode == placing && block != null){ //touch up while placing, place everything in selection - iterateLine(selectX, selectY, cursorX, cursorY, l -> { - rotation = l.rotation; - tryPlaceBlock(l.x, l.y); - }); + flushRequests(lineRequests); + lineRequests.clear(); Events.fire(new LineConfirmEvent()); }else if(mode == breaking){ //touch up while breaking, break everything in selection - NormalizeResult result = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, false, maxLength); - for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){ - for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){ - int wx = selectX + x * Mathf.sign(cursorX - selectX); - int wy = selectY + y * Mathf.sign(cursorY - selectY); - - tryBreakBlock(wx, wy); - } - } + removeSelection(selectX, selectY, cursorX, cursorY); } if(selected != null){ tryDropItems(selected.link(), Core.input.mouseWorld().x, Core.input.mouseWorld().y); } + if(sreq != null){ + if(getRequest(sreq.x, sreq.y, sreq.block.size, sreq) != null){ + player.buildQueue().remove(sreq, true); + } + sreq = null; + } + mode = none; } } @@ -298,6 +328,7 @@ public class DesktopInput extends InputHandler{ droppingItem = false; mode = none; block = null; + sreq = null; } } } diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index 4caaf25677..031818f343 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -7,8 +7,11 @@ import io.anuke.arc.function.*; import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.input.*; +import io.anuke.arc.input.GestureDetector.*; import io.anuke.arc.math.*; import io.anuke.arc.math.geom.*; +import io.anuke.arc.scene.*; +import io.anuke.arc.scene.event.*; import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.util.*; import io.anuke.mindustry.content.*; @@ -17,21 +20,26 @@ import io.anuke.mindustry.entities.effect.*; import io.anuke.mindustry.entities.traits.BuilderTrait.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.EventType.*; +import io.anuke.mindustry.game.Teams.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; +import io.anuke.mindustry.input.PlaceUtils.*; import io.anuke.mindustry.net.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.ui.fragments.*; import io.anuke.mindustry.world.*; +import java.util.*; + import static io.anuke.mindustry.Vars.*; -public abstract class InputHandler implements InputProcessor{ +public abstract class InputHandler implements InputProcessor, GestureListener{ /** Used for dropping items. */ final static float playerSelectRange = mobile ? 17f : 11f; /** Maximum line length. */ final static int maxLength = 100; final static Vector2 stackTrns = new Vector2(); + final static Rectangle r1 = new Rectangle(), r2 = new Rectangle(); /** Distance on the back from where items originate. */ final static float backTrns = 3f; @@ -41,9 +49,14 @@ public abstract class InputHandler implements InputProcessor{ public boolean overrideLineRotation; public int rotation; public boolean droppingItem; + public Group uiGroup; - protected PlaceDraw placeDraw = new PlaceDraw(); - private PlaceLine line = new PlaceLine(); + protected GestureDetector detector; + protected PlaceLine line = new PlaceLine(); + protected BuildRequest resultreq; + protected BuildRequest brequest = new BuildRequest(); + protected Array lineRequests = new Array<>(); + protected Array selectRequests = new Array<>(); //methods to override @@ -134,6 +147,14 @@ public abstract class InputHandler implements InputProcessor{ tile.block().configured(tile, player, value); } + public Eachable allRequests(){ + return cons -> { + for(BuildRequest request : player.buildQueue()) cons.accept(request); + for(BuildRequest request : selectRequests) cons.accept(request); + for(BuildRequest request : lineRequests) cons.accept(request); + }; + } + public OverlayFragment getFrag(){ return frag; } @@ -150,7 +171,11 @@ public abstract class InputHandler implements InputProcessor{ return Core.input.mouseY(); } - public void buildUI(Table table){ + public void buildPlacementUI(Table table){ + + } + + public void buildUI(Group group){ } @@ -158,7 +183,7 @@ public abstract class InputHandler implements InputProcessor{ } - public void drawOutlined(){ + public void drawBottom(){ } @@ -170,6 +195,194 @@ public abstract class InputHandler implements InputProcessor{ return false; } + public void drawSelected(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 void drawBreaking(BuildRequest request){ + if(request.breaking){ + drawBreaking(request.x, request.y); + }else{ + drawSelected(request.x, request.y, request.block, Pal.remove); + } + } + + public void drawBreaking(int x, int y){ + Tile tile = world.ltile(x, y); + if(tile == null) return; + Block block = tile.block(); + + drawSelected(x, y, block, Pal.remove); + } + + /** Returns the selection request that overlaps this position, or null. */ + protected BuildRequest getRequest(int x, int y){ + return getRequest(x, y, 1, null); + } + + /** Returns the selection request that overlaps this position, or null. */ + protected BuildRequest getRequest(int x, int y, int size, BuildRequest skip){ + float offset = ((size + 1) % 2) * tilesize / 2f; + r2.setSize(tilesize * size); + r2.setCenter(x * tilesize + offset, y * tilesize + offset); + resultreq = null; + + Predicate test = req -> { + if(req == skip) return false; + Tile other = req.tile(); + + if(other == null) return false; + + if(!req.breaking){ + r1.setSize(req.block.size * tilesize); + r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset()); + }else{ + r1.setSize(other.block().size * tilesize); + r1.setCenter(other.worldx() + other.block().offset(), other.worldy() + other.block().offset()); + } + + return r2.overlaps(r1); + }; + + for(BuildRequest req : player.buildQueue()){ + if(test.test(req)) return req; + } + + for(BuildRequest req : selectRequests){ + if(test.test(req)) return req; + } + + return null; + } + + protected void drawSelection(int x1, int y1, int x2, int y2){ + NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f); + NormalizeResult dresult = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength); + + for(int x = dresult.x; x <= dresult.x2; x++){ + for(int y = dresult.y; y <= dresult.y2; y++){ + Tile tile = world.ltile(x, y); + if(tile == null || !validBreak(tile.x, tile.y)) continue; + + drawBreaking(tile.x, tile.y); + } + } + + Tmp.r1.set(result.x, result.y, result.x2 - result.x, result.y2 - result.y); + + Draw.color(Pal.remove); + Lines.stroke(1f); + + for(BuildRequest req : player.buildQueue()){ + if(req.breaking) continue; + if(req.bounds(Tmp.r2).overlaps(Tmp.r1)){ + drawBreaking(req); + } + } + + for(BrokenBlock req : state.teams.get(player.getTeam()).brokenBlocks){ + Block block = content.block(req.block); + if(block.bounds(req.x, req.y, Tmp.r2).overlaps(Tmp.r1)){ + drawSelected(req.x, req.y, content.block(req.block), Pal.remove); + } + } + + Lines.stroke(2f); + + Draw.color(Pal.removeBack); + Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y); + Draw.color(Pal.remove); + Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y); + } + + protected void flushSelectRequests(Array requests){ + for(BuildRequest req : requests){ + if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){ + selectRequests.add(req); + } + } + } + + protected void flushRequests(Array requests){ + for(BuildRequest req : requests){ + if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){ + player.addBuildRequest(req); + } + } + } + + protected void drawRequest(BuildRequest request){ + drawRequest(request.x, request.y, request.block, request.rotation); + } + + /** Draws a placement icon for a specific block. */ + protected void drawRequest(int x, int y, Block block, int rotation){ + brequest.set(x, y, rotation, block); + block.drawRequest(brequest, allRequests(), validPlace(x, y, block, rotation)); + } + + /** Remove everything from the queue in a selection. */ + protected void removeSelection(int x1, int y1, int x2, int y2){ + removeSelection(x1, y1, x2, y2, false); + } + + /** Remove everything from the queue in a selection. */ + protected void removeSelection(int x1, int y1, int x2, int y2, boolean flush){ + NormalizeResult result = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength); + for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){ + for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){ + int wx = x1 + x * Mathf.sign(x2 - x1); + int wy = y1 + y * Mathf.sign(y2 - y1); + + if(!flush){ + tryBreakBlock(wx, wy); + }else{ + selectRequests.add(new BuildRequest(wx, wy)); + } + } + } + + //remove build requests + Tmp.r1.set(result.x * tilesize, result.y * tilesize, (result.x2 - result.x) * tilesize, (result.y2 - result.y) * tilesize); + Iterator it = player.buildQueue().iterator(); + while(it.hasNext()){ + BuildRequest req = it.next(); + if(!req.breaking && req.bounds(Tmp.r2).overlaps(Tmp.r1)){ + it.remove(); + } + } + + //remove blocks to rebuild + Iterator broken = state.teams.get(player.getTeam()).brokenBlocks.iterator(); + while(broken.hasNext()){ + BrokenBlock req = broken.next(); + Block block = content.block(req.block); + if(block.bounds(req.x, req.y, Tmp.r2).overlaps(Tmp.r1)){ + broken.remove(); + } + } + } + + protected void updateLine(int x1, int y1, int x2, int y2){ + lineRequests.clear(); + iterateLine(x1, y1, x2, y2, l -> { + rotation = l.rotation; + lineRequests.add(new BuildRequest(l.x, l.y, l.rotation, block)); + }); + } + + protected void updateLine(int x1, int y1){ + updateLine(x1, y1, tileX(getMouseX()), tileY(getMouseY())); + } + /** Handles tile tap events that are not platform specific. */ boolean tileTapped(Tile tile){ tile = tile.link(); @@ -216,7 +429,7 @@ public abstract class InputHandler implements InputProcessor{ //clear when the player taps on something else if(!consumed && !mobile && player.isBuilding() && block == null){ - player.clearBuilding(); + //player.clearBuilding(); block = null; return true; } @@ -301,16 +514,34 @@ public abstract class InputHandler implements InputProcessor{ table.clear(); } } + if(detector != null){ + Core.input.removeProcessor(detector); + } + if(uiGroup != null){ + uiGroup.remove(); + uiGroup = null; + } } public void add(){ + Core.input.addProcessor(detector = new GestureDetector(20, 0.5f, 0.4f, 0.15f, this)); Core.input.addProcessor(this); if(Core.scene != null){ Table table = (Table)Core.scene.find("inputTable"); if(table != null){ table.clear(); - buildUI(table); + buildPlacementUI(table); } + + uiGroup = new WidgetGroup(); + uiGroup.touchable(Touchable.childrenOnly); + uiGroup.setFillParent(true); + ui.hudGroup.addChild(uiGroup); + buildUI(uiGroup); + } + + if(player != null){ + player.isBuilding = true; } } @@ -356,6 +587,15 @@ public abstract class InputHandler implements InputProcessor{ } public boolean validPlace(int x, int y, Block type, int rotation){ + return validPlace(x, y, type, rotation, null); + } + + public boolean validPlace(int x, int y, Block type, int rotation, BuildRequest ignore){ + for(BuildRequest req : player.buildQueue()){ + if(req != ignore && !req.breaking && req.block.bounds(req.x, req.y, Tmp.r1).overlaps(type.bounds(x, y, Tmp.r2))){ + return false; + } + } return Build.validPlace(player.getTeam(), x, y, type, rotation); } @@ -407,8 +647,8 @@ public abstract class InputHandler implements InputProcessor{ float angle = Angles.angle(startX, startY, endX, endY); int baseRotation = rotation; - if (!overrideLineRotation || diagonal){ - baseRotation = (startX == endX && startY == endY) ? rotation : ((int)((angle + 45) / 90f)) % 4; + if(!overrideLineRotation || diagonal){ + baseRotation = (startX == endX && startY == endY) ? rotation : ((int)((angle + 45) / 90f)) % 4; } Tmp.r3.set(-1, -1, 0, 0); @@ -423,7 +663,7 @@ public abstract class InputHandler implements InputProcessor{ Point2 next = i == points.size - 1 ? null : points.get(i + 1); line.x = point.x; line.y = point.y; - if (!overrideLineRotation || diagonal){ + if(!overrideLineRotation || diagonal){ line.rotation = next != null ? Tile.relativeTo(point.x, point.y, next.x, next.y) : baseRotation; }else{ line.rotation = rotation; @@ -435,13 +675,6 @@ public abstract class InputHandler implements InputProcessor{ } } - public static class PlaceDraw{ - public int rotation, scalex, scaley; - public TextureRegion region; - - public static final PlaceDraw instance = new PlaceDraw(); - } - class PlaceLine{ public int x, y, rotation; public boolean last; diff --git a/core/src/io/anuke/mindustry/input/MobileInput.java b/core/src/io/anuke/mindustry/input/MobileInput.java index a5e8ec603f..63a4e0f618 100644 --- a/core/src/io/anuke/mindustry/input/MobileInput.java +++ b/core/src/io/anuke/mindustry/input/MobileInput.java @@ -2,12 +2,12 @@ package io.anuke.mindustry.input; import io.anuke.arc.*; import io.anuke.arc.collection.*; -import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.g2d.*; -import io.anuke.arc.input.*; import io.anuke.arc.input.GestureDetector.*; +import io.anuke.arc.input.*; import io.anuke.arc.math.*; import io.anuke.arc.math.geom.*; +import io.anuke.arc.scene.*; import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.util.*; import io.anuke.mindustry.content.*; @@ -19,7 +19,6 @@ import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; -import io.anuke.mindustry.input.PlaceUtils.*; import io.anuke.mindustry.ui.*; import io.anuke.mindustry.world.*; @@ -29,17 +28,15 @@ import static io.anuke.mindustry.input.PlaceMode.*; public class MobileInput extends InputHandler implements GestureListener{ /** Maximum speed the player can pan. */ private static final float maxPanSpeed = 1.3f; - private static Rectangle r1 = new Rectangle(), r2 = new Rectangle(); /** Distance to edge of screen to start panning. */ private final float edgePan = Scl.scl(60f); //gesture data private Vector2 vector = new Vector2(); private float lastZoom = -1; - private GestureDetector detector; /** Position where the player started dragging a line. */ - private int lineStartX, lineStartY; + private int lineStartX, lineStartY, lastLineX, lastLineY; /** Animation scale for line. */ private float lineScale; @@ -49,10 +46,8 @@ public class MobileInput extends InputHandler implements GestureListener{ /** Used for shifting build requests. */ private float shiftDeltaX, shiftDeltaY; - /** List of currently selected tiles to place. */ - private Array selection = new Array<>(); /** Place requests to be removed. */ - private Array removals = new Array<>(); + private Array removals = new Array<>(); /** Whether or not the player is currently shifting all placed tiles. */ private boolean selecting; /** Whether the player is currently in line-place mode. */ @@ -62,9 +57,9 @@ public class MobileInput extends InputHandler implements GestureListener{ /** Whether no recipe was available when switching to break mode. */ private Block lastBlock; /** Last placed request. Used for drawing block overlay. */ - private PlaceRequest lastPlaced; - - private int prevX, prevY, prevRotation; + private BuildRequest lastPlaced; + /** Down tracking for panning.*/ + private boolean down = false; //region utility methods @@ -99,10 +94,10 @@ public class MobileInput extends InputHandler implements GestureListener{ r2.setSize(block.size * tilesize); r2.setCenter(x * tilesize + block.offset(), y * tilesize + block.offset()); - for(PlaceRequest req : selection){ + for(BuildRequest req : selectRequests){ Tile other = req.tile(); - if(other == null || req.remove) continue; + if(other == null || req.breaking) continue; r1.setSize(req.block.size * tilesize); r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset()); @@ -128,16 +123,16 @@ public class MobileInput extends InputHandler implements GestureListener{ } /** Returns the selection request that overlaps this tile, or null. */ - PlaceRequest getRequest(Tile tile){ + BuildRequest getRequest(Tile tile){ r2.setSize(tilesize); r2.setCenter(tile.worldx(), tile.worldy()); - for(PlaceRequest req : selection){ + for(BuildRequest req : selectRequests){ Tile other = req.tile(); if(other == null) continue; - if(!req.remove){ + if(!req.breaking){ r1.setSize(req.block.size * tilesize); r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset()); @@ -156,8 +151,8 @@ public class MobileInput extends InputHandler implements GestureListener{ return null; } - void removeRequest(PlaceRequest request){ - selection.removeValue(request, true); + void removeRequest(BuildRequest request){ + selectRequests.removeValue(request, true); removals.add(request); } @@ -172,80 +167,8 @@ public class MobileInput extends InputHandler implements GestureListener{ //endregion //region UI and drawing - void drawRequest(PlaceRequest request, PlaceRequest prev){ - Tile tile = request.tile(); - - if(!request.remove){ - if(prev != null){ - request.block.getPlaceDraw(placeDraw, request.rotation, prev.x - request.x, prev.y - request.y, prev.rotation); - }else{ - request.block.getPlaceDraw(placeDraw, request.rotation, 0, 0, request.rotation); - } - - //draw placing request - float offset = request.block.offset(); - TextureRegion region = placeDraw.region; - - Draw.mixcol(Pal.accent, Mathf.clamp((1f - request.scale) / 0.5f + 0.12f + Mathf.absin(Time.time(), 8f, 0.35f))); - Draw.tint(Color.white, Pal.breakInvalid, request.redness); - - Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset, - region.getWidth() * request.scale * Draw.scl * placeDraw.scalex, - region.getHeight() * request.scale * Draw.scl * placeDraw.scaley, - request.block.rotate ? placeDraw.rotation * 90 : 0); - - Draw.mixcol(Pal.accent, 1f); - for(int i = 0; i < 4; i++){ - Point2 p = Geometry.d8edge[i]; - float poffset = -Math.max(request.block.size - 1, 0) / 2f * tilesize; - TextureRegion find = Core.atlas.find("block-select"); - if(i % 2 == 0) - Draw.rect("block-select", request.tile().x * tilesize + request.block.offset() + poffset * p.x, request.tile().y * tilesize + request.block.offset() + poffset * p.y, - find.getWidth() * Draw.scl * request.scale, find.getHeight() * Draw.scl * request.scale, i * 90); - } - Draw.color(); - }else{ - float rad = Math.max((tile.block().size * tilesize / 2f - 1) * request.scale, 1f); - - if(rad <= 1.01f) return; - Draw.mixcol(); - //draw removing request - Draw.tint(Pal.removeBack); - Lines.square(tile.drawx(), tile.drawy() - 1, rad); - Draw.tint(Pal.remove); - Lines.square(tile.drawx(), tile.drawy(), rad); - } - } - - /** Draws a placement icon for a specific block. */ - void drawPlace(int x, int y, Block block, int rotation, int prevX, int prevY, int prevRotation){ - if(validPlace(x, y, block, rotation) && !checkOverlapPlacement(x, y, block)){ - block.getPlaceDraw(placeDraw, rotation, prevX, prevY, prevRotation); - - Draw.color(); - Draw.rect(placeDraw.region, x * tilesize + block.offset(), y * tilesize + block.offset(), - placeDraw.region.getWidth() * Draw.scl * placeDraw.scalex, - placeDraw.region.getHeight() * Draw.scl * placeDraw.scaley, - block.rotate ? placeDraw.rotation * 90 : 0); - - Draw.color(Pal.accent); - for(int i = 0; i < 4; i++){ - Point2 p = Geometry.d8edge[i]; - float offset = -Math.max(block.size - 1, 0) / 2f * tilesize; - if(i % 2 == 0) - Draw.rect("block-select", x * tilesize + block.offset() + offset * p.x, y * tilesize + block.offset() + offset * p.y, i * 90); - } - Draw.color(); - }else{ - Draw.color(Pal.removeBack); - Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f - 1); - Draw.color(Pal.remove); - Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize / 2f - 1); - } - } - @Override - public void buildUI(Table table){ + public void buildPlacementUI(Table table){ table.addImage().color(Pal.gray).height(4f).colspan(4).growX(); table.row(); table.left().margin(0f).defaults().size(48f); @@ -267,12 +190,12 @@ public class MobileInput extends InputHandler implements GestureListener{ //confirm button table.addImageButton(Icon.checkSmall, Styles.clearPartiali, () -> { - for(PlaceRequest request : selection){ + for(BuildRequest request : selectRequests){ Tile tile = request.tile(); //actually place/break all selected blocks if(tile != null){ - if(!request.remove){ + if(!request.breaking){ rotation = request.rotation; Block before = block; block = request.block; @@ -285,25 +208,22 @@ public class MobileInput extends InputHandler implements GestureListener{ } //move all current requests to removal array so they fade out - removals.addAll(selection); - selection.clear(); + removals.addAll(selectRequests); + selectRequests.clear(); selecting = false; - }).visible(() -> !selection.isEmpty()).name("confirmplace"); - - Core.scene.table(t -> { - t.setName("cancelMobile"); - t.bottom().left().visible(() -> (player.isBuilding() || block != null || mode == breaking) && !state.is(State.menu)); - t.addImageTextButton("$cancel", Icon.cancelSmall, () -> { - player.clearBuilding(); - mode = none; - block = null; - }).width(155f); - }); + }).visible(() -> !selectRequests.isEmpty()).name("confirmplace"); } @Override - public boolean isDrawing(){ - return selection.size > 0 || removals.size > 0 || lineMode || player.target != null || mode != PlaceMode.none; + public void buildUI(Group group){ + group.fill(t -> { + t.bottom().left().visible(() -> (player.isBuilding() || block != null || mode == breaking) && !state.is(State.menu)); + t.addImageTextButton("$cancel", Icon.cancelSmall, () -> { + player.clearBuilding(); + mode = none; + block = null; + }).width(155f); + }); } @Override @@ -312,55 +232,52 @@ public class MobileInput extends InputHandler implements GestureListener{ } @Override - public void drawOutlined(){ + public void drawBottom(){ Lines.stroke(1f); //draw removals - for(PlaceRequest request : removals){ + for(BuildRequest request : removals){ Tile tile = request.tile(); if(tile == null) continue; - request.scale = Mathf.lerpDelta(request.scale, 0f, 0.2f); - request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f); + request.animScale = Mathf.lerpDelta(request.animScale, 0f, 0.2f); + request.animInvalid = Mathf.lerpDelta(request.animInvalid, 0f, 0.2f); - drawRequest(request, null); + //TODO + //drawRequest(request); } - PlaceRequest last = null; - //draw list of requests - for(PlaceRequest request : selection){ + for(BuildRequest request : selectRequests){ Tile tile = request.tile(); if(tile == null) continue; - if((!request.remove && validPlace(tile.x, tile.y, request.block, request.rotation)) - || (request.remove && validBreak(tile.x, tile.y))){ - request.scale = Mathf.lerpDelta(request.scale, 1f, 0.2f); - request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f); + if((!request.breaking && validPlace(tile.x, tile.y, request.block, request.rotation)) + || (request.breaking && validBreak(tile.x, tile.y))){ + request.animScale = Mathf.lerpDelta(request.animScale, 1f, 0.2f); + request.animInvalid = Mathf.lerpDelta(request.animInvalid, 0f, 0.2f); }else{ - request.scale = Mathf.lerpDelta(request.scale, 0.6f, 0.1f); - request.redness = Mathf.lerpDelta(request.redness, 0.9f, 0.2f); + request.animScale = Mathf.lerpDelta(request.animScale, 0.6f, 0.1f); + request.animInvalid = Mathf.lerpDelta(request.animInvalid, 0.9f, 0.2f); } Tmp.c1.set(Draw.getMixColor()); - if(!request.remove && request == lastPlaced && request.block != null){ + if(!request.breaking && request == lastPlaced && request.block != null){ Draw.mixcol(); if(request.block.rotate) drawArrow(request.block, tile.x, tile.y, request.rotation); } Draw.mixcol(Tmp.c1, 1f); - drawRequest(request, last); + drawRequest(request); //draw last placed request - if(!request.remove && request == lastPlaced && request.block != null){ + if(!request.breaking && request == lastPlaced && request.block != null){ Draw.mixcol(); request.block.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, request.block, rotation)); } - - last = request; } Draw.mixcol(); @@ -373,46 +290,16 @@ public class MobileInput extends InputHandler implements GestureListener{ if(mode == placing && block != null){ //draw placing - - prevX = lineStartX; - prevY = lineStartY; - prevRotation = rotation; - - iterateLine(lineStartX, lineStartY, tileX, tileY, l -> { - if(l.last && block.rotate){ - drawArrow(block, l.x, l.y, l.rotation); - } - drawPlace(l.x, l.y, block, l.rotation, prevX - l.x, prevY - l.y, prevRotation); - - rotation = l.rotation; - prevX = l.x; - prevY = l.y; - prevRotation = l.rotation; - }); - }else if(mode == breaking){ - //draw breaking - NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, lineStartX, lineStartY, tileX, tileY, false, maxLength, 1f); - NormalizeResult dresult = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, false, maxLength); - - for(int x = dresult.x; x <= dresult.x2; x++){ - for(int y = dresult.y; y <= dresult.y2; y++){ - Tile other = world.ltile(x, y); - if(other == null || !validBreak(other.x, other.y)) continue; - - Draw.color(Pal.removeBack); - Lines.square(other.drawx(), other.drawy() - 1, other.block().size * tilesize / 2f - 1); - Draw.color(Pal.remove); - Lines.square(other.drawx(), other.drawy(), other.block().size * tilesize / 2f - 1); + for(int i = 0; i < lineRequests.size; i++){ + BuildRequest req = lineRequests.get(i); + if(i == lineRequests.size - 1 && req.block.rotate){ + drawArrow(block, req.x, req.y, req.rotation); } + drawRequest(lineRequests.get(i)); } - - Draw.color(Pal.removeBack); - Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y); - Draw.color(Pal.remove); - Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y); - + }else if(mode == breaking){ + drawSelection(lineStartX, lineStartY, tileX, tileY); } - } TargetTrait target = player.target; @@ -438,31 +325,23 @@ public class MobileInput extends InputHandler implements GestureListener{ Draw.reset(); } + @Override + protected void drawRequest(BuildRequest request){ + drawRequest(request.x, request.y, request.block, request.rotation); + if(!request.breaking){ + drawSelected(request.x, request.y, request.block, Pal.accent); + } + } + //endregion //region input events - @Override - public void add(){ - Core.input.addProcessor(detector = new GestureDetector(20, 0.5f, 0.4f, 0.15f, this)); - super.add(); - } - - @Override - public void remove(){ - super.remove(); - if(detector != null){ - Core.input.removeProcessor(detector); - } - - if(Core.scene != null && Core.scene.find("cancelMobile") != null){ - Core.scene.find("cancelMobile").remove(); - } - } - @Override public boolean touchDown(int screenX, int screenY, int pointer, KeyCode button){ if(state.is(State.menu) || player.isDead()) return false; + down = true; + //get tile on cursor Tile cursor = tileAt(screenX, screenY); @@ -489,44 +368,20 @@ public class MobileInput extends InputHandler implements GestureListener{ public boolean touchUp(int screenX, int screenY, int pointer, KeyCode button){ lastZoom = renderer.getScale(); + if(!Core.input.isTouched()){ + down = false; + } + //place down a line if in line mode if(lineMode){ int tileX = tileX(screenX); int tileY = tileY(screenY); if(mode == placing && isPlacing()){ - iterateLine(lineStartX, lineStartY, tileX, tileY, l -> { - Tile tile = world.tile(l.x, l.y); - if(tile != null && checkOverlapPlacement(tile.x, tile.y, block)){ - return; - } - - PlaceRequest request = new PlaceRequest(l.x, l.y, block, l.rotation); - request.scale = 1f; - selection.add(request); - }); + flushSelectRequests(lineRequests); Events.fire(new LineConfirmEvent()); }else if(mode == breaking){ - //normalize area - NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, false, maxLength); - - //break everything in area - for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){ - for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){ - int wx = lineStartX + x * Mathf.sign(tileX - lineStartX); - int wy = lineStartY + y * Mathf.sign(tileY - lineStartY); - - Tile tar = world.ltile(wx, wy); - - if(tar == null) continue; - - if(!hasRequest(world.tile(tar.x, tar.y)) && validBreak(tar.x, tar.y)){ - PlaceRequest request = new PlaceRequest(tar.x, tar.y); - request.scale = 1f; - selection.add(request); - } - } - } + removeSelection(lineStartX, lineStartY, tileX, tileY, true); } lineMode = false; @@ -554,11 +409,14 @@ public class MobileInput extends InputHandler implements GestureListener{ //long pressing enables line mode otherwise lineStartX = cursor.x; lineStartY = cursor.y; + lastLineX = cursor.x; + lastLineY = cursor.y; lineMode = true; if(mode == breaking){ Effects.effect(Fx.tapBlock, cursor.worldx(), cursor.worldy(), 1f); }else if(block != null){ + updateLine(lineStartX, lineStartY, cursor.x, cursor.y); Effects.effect(Fx.tapBlock, cursor.worldx() + block.offset(), cursor.worldy() + block.offset(), block.size); } @@ -584,11 +442,11 @@ public class MobileInput extends InputHandler implements GestureListener{ removeRequest(getRequest(cursor)); }else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, block, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, block)){ //add to selection queue if it's a valid place position - selection.add(lastPlaced = new PlaceRequest(cursor.x, cursor.y, block, rotation)); + selectRequests.add(lastPlaced = new BuildRequest(cursor.x, cursor.y, rotation, block)); }else if(mode == breaking && validBreak(cursor.link().x, cursor.link().y) && !hasRequest(cursor.link())){ //add to selection queue if it's a valid BREAK position cursor = cursor.link(); - selection.add(new PlaceRequest(cursor.x, cursor.y)); + selectRequests.add(new BuildRequest(cursor.x, cursor.y)); }else if(!canTapPlayer(worldx, worldy) && !tileTapped(cursor.link())){ tryBeginMine(cursor); } @@ -599,7 +457,7 @@ public class MobileInput extends InputHandler implements GestureListener{ @Override public void update(){ if(state.is(State.menu) || player.isDead()){ - selection.clear(); + selectRequests.clear(); removals.clear(); mode = none; } @@ -629,8 +487,8 @@ public class MobileInput extends InputHandler implements GestureListener{ if(mode == none){ selecting = false; lineMode = false; - removals.addAll(selection); - selection.clear(); + removals.addAll(selectRequests); + selectRequests.clear(); } if(lineMode && mode == placing && block == null){ @@ -656,7 +514,7 @@ public class MobileInput extends InputHandler implements GestureListener{ lineScale = Mathf.lerpDelta(lineScale, 1f, 0.1f); //When in line mode, pan when near screen edges automatically - if(Core.input.isTouched(0) && lineMode){ + if(Core.input.isTouched(0)){ float screenX = Core.input.mouseX(), screenY = Core.input.mouseY(); float panX = 0, panY = 0; @@ -684,15 +542,24 @@ public class MobileInput extends InputHandler implements GestureListener{ Core.camera.position.x += vector.x; Core.camera.position.y += vector.y; } + + int lx = tileX(Core.input.mouseX()), ly = tileY(Core.input.mouseY()); + + if((lastLineX != lx || lastLineY != ly) && isPlacing()){ + lastLineX = lx; + lastLineY = ly; + updateLine(lineStartX, lineStartY, lx, ly); + } }else{ + lineRequests.clear(); lineScale = 0f; } //remove place requests that have disappeared for(int i = removals.size - 1; i >= 0; i--){ - PlaceRequest request = removals.get(i); + BuildRequest request = removals.get(i); - if(request.scale <= 0.0001f){ + if(request.animScale <= 0.0001f){ removals.remove(i); i--; } @@ -712,6 +579,8 @@ public class MobileInput extends InputHandler implements GestureListener{ return false; } + if(!down) return false; + if(selecting){ //pan all requests shiftDeltaX += deltaX; shiftDeltaY += deltaY; @@ -720,8 +589,8 @@ public class MobileInput extends InputHandler implements GestureListener{ int shiftedY = (int)(shiftDeltaY / tilesize); if(Math.abs(shiftedX) > 0 || Math.abs(shiftedY) > 0){ - for(PlaceRequest req : selection){ - if(req.remove) continue; //don't shift removal requests + for(BuildRequest req : selectRequests){ + if(req.breaking) continue; //don't shift removal requests req.x += shiftedX; req.y += shiftedY; } @@ -756,33 +625,4 @@ public class MobileInput extends InputHandler implements GestureListener{ } //endregion - - private class PlaceRequest{ - int x, y; - Block block; - int rotation; - boolean remove; - - //animation variables - float scale; - float redness; - - PlaceRequest(int x, int y, Block block, int rotation){ - this.x = x; - this.y = y; - this.block = block; - this.rotation = rotation; - this.remove = false; - } - - PlaceRequest(int x, int y){ - this.x = x; - this.y = y; - this.remove = true; - } - - Tile tile(){ - return world.tile(x, y); - } - } } diff --git a/core/src/io/anuke/mindustry/io/SaveVersion.java b/core/src/io/anuke/mindustry/io/SaveVersion.java index 76e7ce5e09..f1fceb5ce3 100644 --- a/core/src/io/anuke/mindustry/io/SaveVersion.java +++ b/core/src/io/anuke/mindustry/io/SaveVersion.java @@ -257,7 +257,7 @@ public abstract class SaveVersion extends SaveFileReader{ TeamData data = state.teams.get(team); int blocks = stream.readInt(); for(int j = 0; j < blocks; j++){ - data.brokenBlocks.addLast(new BrokenBlock(stream.readShort(), stream.readShort(), stream.readShort(), stream.readShort(), stream.readInt())); + data.brokenBlocks.addLast(new BrokenBlock(stream.readShort(), stream.readShort(), stream.readShort(), content.block(stream.readShort()).id, stream.readInt())); } } diff --git a/core/src/io/anuke/mindustry/mod/Mods.java b/core/src/io/anuke/mindustry/mod/Mods.java index fa483fbb6d..529fd0a021 100644 --- a/core/src/io/anuke/mindustry/mod/Mods.java +++ b/core/src/io/anuke/mindustry/mod/Mods.java @@ -125,7 +125,7 @@ public class Mods implements Loadable{ //get textures packed if(totalSprites > 0){ - TextureFilter filter = TextureFilter.Nearest; + TextureFilter filter = Core.settings.getBool("linear") ? TextureFilter.Linear : TextureFilter.Nearest; packer.updateTextureAtlas(Core.atlas, filter, filter, false); //generate new icons diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java index cc89bd8c47..f7e873305c 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -225,6 +225,8 @@ public class SettingsMenuDialog extends SettingsDialog{ game.checkPref("savecreate", true); + game.checkPref("hints", true); + if(steam){ game.checkPref("publichost", false, i -> { platform.updateLobby(); diff --git a/core/src/io/anuke/mindustry/ui/fragments/PlacementFragment.java b/core/src/io/anuke/mindustry/ui/fragments/PlacementFragment.java index c21f08a4e2..ac6cfe8edf 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/PlacementFragment.java @@ -10,6 +10,8 @@ import io.anuke.arc.scene.event.*; import io.anuke.arc.scene.style.*; import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.layout.*; +import io.anuke.arc.util.*; +import io.anuke.mindustry.entities.traits.BuilderTrait.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.EventType.*; @@ -17,7 +19,7 @@ import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.input.*; import io.anuke.mindustry.type.*; -import io.anuke.mindustry.ui.Styles; +import io.anuke.mindustry.ui.*; import io.anuke.mindustry.world.*; import static io.anuke.mindustry.Vars.*; @@ -74,17 +76,21 @@ public class PlacementFragment extends Fragment{ boolean gridUpdate(InputHandler input){ if(Core.input.keyDown(Binding.pick)){ //mouse eyedropper select - Tile tile = world.tileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y); + Tile tile = world.ltileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y); + Block tryRecipe = tile == null ? null : tile.block(); - if(tile != null){ - tile = tile.link(); - Block tryRecipe = tile.block(); - if(tryRecipe.isVisible() && unlocked(tryRecipe)){ - input.block = tryRecipe; - currentCategory = input.block.category; - return true; + for(BuildRequest req : player.buildQueue()){ + if(!req.breaking && req.block.bounds(req.x, req.y, Tmp.r1).contains(Core.input.mouseWorld())){ + tryRecipe = req.block; + break; } } + + if(tryRecipe != null && tryRecipe.isVisible() && unlocked(tryRecipe)){ + input.block = tryRecipe; + currentCategory = input.block.category; + return true; + } } if(!Core.input.keyDown(Binding.gridMode) || ui.chatfrag.chatOpen()) return false; @@ -263,7 +269,7 @@ public class PlacementFragment extends Fragment{ blocksSelect.margin(4).marginTop(0); blocksSelect.table(blocks -> blockTable = blocks).grow(); blocksSelect.row(); - blocksSelect.table(control.input::buildUI).name("inputTable").growX(); + blocksSelect.table(control.input::buildPlacementUI).name("inputTable").growX(); }).fillY().bottom().touchable(Touchable.enabled); frame.table(categories -> { categories.defaults().size(50f); diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 7f886a05e1..fe618ab7d9 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -19,11 +19,11 @@ import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.pooling.*; import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.effect.*; +import io.anuke.mindustry.entities.traits.BuilderTrait.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; -import io.anuke.mindustry.input.InputHandler.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.ui.*; import io.anuke.mindustry.world.blocks.*; @@ -667,10 +667,16 @@ public class Block extends BlockStorage{ } } - public void getPlaceDraw(PlaceDraw draw, int rotation, int prevX, int prevY, int prevRotation){ - draw.region = icon(Cicon.full); - draw.scalex = draw.scaley = 1; - draw.rotation = rotation; + public void drawRequest(BuildRequest req, Eachable list, boolean valid){ + Draw.reset(); + Draw.mixcol(!valid ? Pal.breakInvalid : Color.white, (!valid ? 0.4f : 0.24f) + Mathf.absin(Time.globalTime(), 6f, 0.28f)); + Draw.alpha(1f); + drawRequestRegion(req, list); + Draw.reset(); + } + + public void drawRequestRegion(BuildRequest req, Eachable list){ + Draw.rect(icon(Cicon.full), req.drawx(), req.drawy(), !rotate ? 0 : req.rotation * 90); } @Override @@ -785,6 +791,10 @@ public class Block extends BlockStorage{ return ((size + 1) % 2) * tilesize / 2f; } + public Rectangle bounds(int x, int y, Rectangle rect){ + return rect.setSize(size * tilesize).setCenter(x * tilesize + offset(), y * tilesize + offset()); + } + public boolean isMultiblock(){ return size > 1; } diff --git a/core/src/io/anuke/mindustry/world/Edges.java b/core/src/io/anuke/mindustry/world/Edges.java index dc9586aca0..623dd1d434 100644 --- a/core/src/io/anuke/mindustry/world/Edges.java +++ b/core/src/io/anuke/mindustry/world/Edges.java @@ -50,10 +50,14 @@ public class Edges{ } public static Tile getFacingEdge(Tile tile, Tile other){ - if(!tile.block().isMultiblock()) return tile; - int size = tile.block().size; - return world.tile(tile.x + Mathf.clamp(other.x - tile.x, -(size - 1) / 2, (size / 2)), - tile.y + Mathf.clamp(other.y - tile.y, -(size - 1) / 2, (size / 2))); + return getFacingEdge(tile.block, tile.x, tile.y, other); + } + + public static Tile getFacingEdge(Block block, int tilex, int tiley, Tile other){ + if(!block.isMultiblock()) return world.tile(tilex, tiley); + int size = block.size; + return world.tile(tilex + Mathf.clamp(other.x - tilex, -(size - 1) / 2, (size / 2)), + tiley + Mathf.clamp(other.y - tiley, -(size - 1) / 2, (size / 2))); } public static Vector2[] getPixelPolygon(float radius){ diff --git a/core/src/io/anuke/mindustry/world/blocks/Autotiler.java b/core/src/io/anuke/mindustry/world/blocks/Autotiler.java new file mode 100644 index 0000000000..3256a47178 --- /dev/null +++ b/core/src/io/anuke/mindustry/world/blocks/Autotiler.java @@ -0,0 +1,96 @@ +package io.anuke.mindustry.world.blocks; + +import io.anuke.arc.function.*; +import io.anuke.arc.math.*; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.util.ArcAnnotate.*; +import io.anuke.mindustry.entities.traits.BuilderTrait.*; +import io.anuke.mindustry.world.*; + +import java.util.*; + +public interface Autotiler{ + class AutotilerHolder{ + static final int[] blendresult = new int[3]; + static final BuildRequest[] directionals = new BuildRequest[4]; + } + + default @Nullable int[] getTiling(BuildRequest req, Eachable list){ + if(req.tile() == null) return null; + BuildRequest[] directionals = AutotilerHolder.directionals; + + Arrays.fill(directionals, null); + list.each(other -> { + if(other.breaking || other == req) return; + + int i = 0; + for(Point2 point : Geometry.d4){ + int x = req.x + point.x, y = req.y + point.y; + if(x >= other.x -(other.block.size - 1) / 2 && x <= other.x + (other.block.size / 2) && y >= other.y -(other.block.size - 1) / 2 && y <= other.y + (other.block.size / 2)){ + directionals[i] = other; + } + i++; + } + }); + + return buildBlending(req.tile(), req.rotation, directionals); + } + + default int[] buildBlending(Tile tile, int rotation, BuildRequest[] directional){ + int[] blendresult = AutotilerHolder.blendresult; + blendresult[0] = 0; + blendresult[1] = blendresult[2] = 1; + int num = + (blends(tile, rotation, directional, 2) && blends(tile, rotation, directional, 1) && blends(tile, rotation, directional, 3)) ? 0 : + (blends(tile, rotation, directional, 1) && blends(tile, rotation, directional, 3)) ? 1 : + (blends(tile, rotation, directional, 1) && blends(tile, rotation, directional, 2)) ? 2 : + (blends(tile, rotation, directional, 3) && blends(tile, rotation, directional, 2)) ? 3 : + blends(tile, rotation, directional, 1) ? 4 : + blends(tile, rotation, directional, 3) ? 5 : + -1; + transformCase(num, blendresult); + return blendresult; + } + + default void transformCase(int num, int[] bits){ + if(num == 0){ + bits[0] = 3; + }else if(num == 1){ + bits[0] = 4; + }else if(num == 2){ + bits[0] = 2; + }else if(num == 3){ + bits[0] = 2; + bits[2] = -1; + }else if(num == 4){ + bits[0] = 1; + bits[2] = -1; + }else if(num == 5){ + bits[0] = 1; + } + } + + default boolean blends(Tile tile, int rotation, @Nullable BuildRequest[] directional, int direction){ + int realDir = Mathf.mod(rotation - direction, 4); + if(directional != null && directional[realDir] != null){ + BuildRequest req = directional[realDir]; + if(blends(tile, rotation, req.x, req.y, req.rotation, req.block)){ + return true; + } + } + return blends(tile, rotation, direction); + } + + default boolean blends(Tile tile, int rotation, int direction){ + Tile other = tile.getNearby(Mathf.mod(rotation - direction, 4)); + if(other != null) other = other.link(); + return other != null && blends(tile, rotation, other.x, other.y, other.rotation(), other.block()); + } + + default boolean lookingAt(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){ + return (Point2.equals(tile.x + Geometry.d4(rotation).x, tile.y + Geometry.d4(rotation).y, otherx, othery) + || (!otherblock.rotate || Point2.equals(otherx + Geometry.d4(otherrot).x, othery + Geometry.d4(otherrot).y, tile.x, tile.y))); + } + + boolean blends(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock); +} diff --git a/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java b/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java index 22e649b9c3..37c3ccc6fa 100644 --- a/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java @@ -110,8 +110,8 @@ public class BuildBlock extends Block{ //if the target is constructible, begin constructing if(entity.cblock != null){ - player.clearBuilding(); - player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.rotation(), entity.cblock)); + //player.clearBuilding(); + player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.rotation(), entity.cblock), false); } } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/ArmoredConveyor.java b/core/src/io/anuke/mindustry/world/blocks/distribution/ArmoredConveyor.java index 08ae878b5a..3bad2c3945 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/ArmoredConveyor.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/ArmoredConveyor.java @@ -1,6 +1,6 @@ package io.anuke.mindustry.world.blocks.distribution; -import io.anuke.arc.math.*; +import io.anuke.arc.math.geom.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.world.*; @@ -16,11 +16,8 @@ public class ArmoredConveyor extends Conveyor{ } @Override - protected boolean blends(Tile tile, int direction){ - Tile other = tile.getNearby(Mathf.mod(tile.rotation() - direction, 4)); - if(other != null) other = other.link(); - - return other != null && other.block().outputsItems() - && ((tile.getNearby(tile.rotation()) == other) || ((!other.block().rotate && Edges.getFacingEdge(other, tile).relativeTo(tile) == tile.rotation()) || (other.block().rotate && other.getNearby(other.rotation()) == tile))); + public boolean blends(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){ + return otherblock.outputsItems() && (Point2.equals(tile.x + Geometry.d4(rotation).x, tile.y + Geometry.d4(rotation).y, otherx, othery) + || ((!otherblock.rotate && Edges.getFacingEdge(otherblock, otherx, othery, tile).relativeTo(tile) == tile.rotation()) || Point2.equals(otherx + Geometry.d4(otherrot).x, othery + Geometry.d4(otherrot).y, tile.x, tile.y))); } } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/Conduit.java b/core/src/io/anuke/mindustry/world/blocks/distribution/Conduit.java index 6064d6483d..f8f4394482 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/Conduit.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/Conduit.java @@ -1,16 +1,17 @@ package io.anuke.mindustry.world.blocks.distribution; -import io.anuke.arc.Core; -import io.anuke.arc.graphics.g2d.Draw; -import io.anuke.arc.graphics.g2d.TextureRegion; -import io.anuke.arc.math.Mathf; -import io.anuke.mindustry.entities.type.TileEntity; -import io.anuke.mindustry.type.Liquid; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.LiquidBlock; -import io.anuke.mindustry.world.modules.LiquidModule; +import io.anuke.arc.*; +import io.anuke.arc.function.*; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.*; +import io.anuke.mindustry.entities.traits.BuilderTrait.*; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.*; +import io.anuke.mindustry.world.modules.*; -public class Conduit extends LiquidBlock{ +public class Conduit extends LiquidBlock implements Autotiler{ protected final int timerFlow = timers++; protected TextureRegion[] topRegions = new TextureRegion[7]; @@ -39,29 +40,33 @@ public class Conduit extends LiquidBlock{ super.onProximityUpdate(tile); ConduitEntity entity = tile.entity(); - entity.blendbits = 0; - entity.blendrot = 0; - - if(blends(tile, 2) && blends(tile, 1) && blends(tile, 3)){ - entity.blendbits = 3; - }else if(blends(tile, 1) && blends(tile, 3)){ - entity.blendbits = 6; - }else if(blends(tile, 1) && blends(tile, 2)){ - entity.blendbits = 2; - }else if(blends(tile, 3) && blends(tile, 2)){ - entity.blendbits = 4; - }else if(blends(tile, 1)){ - entity.blendbits = 5; - }else if(blends(tile, 3)){ - entity.blendbits = 1; - } + int[] bits = buildBlending(tile, tile.rotation(), null); + entity.blendbits = bits[0]; } - private boolean blends(Tile tile, int direction){ - Tile other = tile.getNearby(Mathf.mod(tile.rotation() - direction, 4)); - if(other != null) other = other.link(); + @Override + public void drawRequestRegion(BuildRequest req, Eachable list){ + int[] bits = getTiling(req, list); - return other != null && other.block().hasLiquids && other.block().outputsLiquid && ((tile.getNearby(tile.rotation()) == other) || (!other.block().rotate || other.getNearby(other.rotation()) == tile)); + if(bits == null) return; + + Draw.colorl(0.34f); + Draw.alpha(0.5f); + Draw.rect(botRegions[bits[0]], req.drawx(), req.drawy(), req.rotation * 90); + Draw.color(); + + + Draw.rect(topRegions[bits[0]], req.drawx(), req.drawy(), req.rotation * 90); + } + + @Override + public void transformCase(int num, int[] bits){ + bits[0] = num == 0 ? 3 : num == 1 ? 6 : num == 2 ? 2 : num == 3 ? 4 : num == 4 ? 5 : num == 5 ? 1 : 0; + } + + @Override + public boolean blends(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){ + return otherblock.hasLiquids && otherblock.outputsLiquid && lookingAt(tile, rotation, otherx, othery, otherrot, otherblock); } @Override @@ -113,7 +118,6 @@ public class Conduit extends LiquidBlock{ public static class ConduitEntity extends TileEntity{ public float smoothLiquid; - byte blendbits; - int blendrot; + int blendbits; } } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/Conveyor.java b/core/src/io/anuke/mindustry/world/blocks/distribution/Conveyor.java index faf6645c6b..c643eef9d1 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/Conveyor.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/Conveyor.java @@ -2,24 +2,26 @@ package io.anuke.mindustry.world.blocks.distribution; import io.anuke.arc.*; import io.anuke.arc.collection.*; +import io.anuke.arc.function.*; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.*; import io.anuke.arc.math.geom.*; import io.anuke.arc.util.*; +import io.anuke.mindustry.entities.traits.BuilderTrait.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; -import io.anuke.mindustry.input.InputHandler.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.*; import io.anuke.mindustry.world.meta.*; import java.io.*; import static io.anuke.mindustry.Vars.*; -public class Conveyor extends Block{ +public class Conveyor extends Block implements Autotiler{ private static final float itemSpace = 0.4f; private static final float minmove = 1f / (Short.MAX_VALUE - 2); private static ItemPos drawpos = new ItemPos(); @@ -27,6 +29,8 @@ public class Conveyor extends Block{ private static ItemPos pos2 = new ItemPos(); private final Vector2 tr1 = new Vector2(); private final Vector2 tr2 = new Vector2(); + private final int[] blendresult = new int[3]; + private final BuildRequest[] directionals = new BuildRequest[4]; private TextureRegion[][] regions = new TextureRegion[7][4]; @@ -90,55 +94,25 @@ public class Conveyor extends Block{ super.onProximityUpdate(tile); ConveyorEntity entity = tile.entity(); - entity.blendbits = 0; - entity.blendsclx = entity.blendscly = 1; - - if(blends(tile, 2) && blends(tile, 1) && blends(tile, 3)){ - entity.blendbits = 3; - }else if(blends(tile, 1) && blends(tile, 3)){ - entity.blendbits = 4; - }else if(blends(tile, 1) && blends(tile, 2)){ - entity.blendbits = 2; - }else if(blends(tile, 3) && blends(tile, 2)){ - entity.blendbits = 2; - entity.blendscly = -1; - }else if(blends(tile, 1)){ - entity.blendbits = 1; - entity.blendscly = -1; - }else if(blends(tile, 3)){ - entity.blendbits = 1; - } + int[] bits = buildBlending(tile, tile.rotation(), null); + entity.blendbits = bits[0]; + entity.blendsclx = bits[1]; + entity.blendscly = bits[2]; } @Override - public void getPlaceDraw(PlaceDraw draw, int rotation, int prevX, int prevY, int prevRotation){ - draw.rotation = rotation; - draw.scalex = draw.scaley = 1; + public void drawRequestRegion(BuildRequest req, Eachable list){ + int[] bits = getTiling(req, list); - int blendbits = 0; + if(bits == null) return; - if(blends(rotation, 1, prevX, prevY, prevRotation)){ - blendbits = 1; - draw.scaley = -1; - }else if(blends(rotation, 3, prevX, prevY, prevRotation)){ - blendbits = 1; - } - - draw.rotation = rotation; - draw.region = regions[blendbits][0]; + TextureRegion region = regions[bits[0]][0]; + Draw.rect(region, req.drawx(), req.drawy(), region.getWidth() * bits[1] * Draw.scl, region.getHeight() * bits[2] * Draw.scl, req.rotation * 90); } - protected boolean blends(int rotation, int offset, int prevX, int prevY, int prevRotation){ - Point2 left = Geometry.d4(rotation - offset); - return left.equals(prevX, prevY) && prevRotation == Mathf.mod(rotation + offset, 4); - } - - protected boolean blends(Tile tile, int direction){ - Tile other = tile.getNearby(Mathf.mod(tile.rotation() - direction, 4)); - if(other != null) other = other.link(); - - return other != null && other.block().outputsItems() - && ((tile.getNearby(tile.rotation()) == other) || (!other.block().rotate || other.getNearby(other.rotation()) == tile)); + @Override + public boolean blends(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){ + return otherblock.outputsItems() && lookingAt(tile, rotation, otherx, othery, otherrot, otherblock); } @Override diff --git a/gradle.properties b/gradle.properties index dcfbb32c6f..46915237bd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=59cff366c4d46577b659e153183eb824eaafd7f7 +archash=c4556cc2ecff581d9c0e06f826bc10e7cacbabd0 diff --git a/tests/src/test/java/ZoneTests.java b/tests/src/test/java/ZoneTests.java index ce5dedc053..a35355b0a0 100644 --- a/tests/src/test/java/ZoneTests.java +++ b/tests/src/test/java/ZoneTests.java @@ -1,5 +1,6 @@ import io.anuke.arc.collection.*; import io.anuke.arc.util.*; +import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.GameState.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.type.*; @@ -28,6 +29,7 @@ public class ZoneTests{ @TestFactory DynamicTest[] testZoneValidity(){ Array out = new Array<>(); + if(world == null) world = new World(); for(Zone zone : content.zones()){ out.add(dynamicTest(zone.name, () -> {