From 3a78b0e9781e62da7cd487357c15342da0f30728 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Sat, 15 Apr 2017 20:45:33 -0400 Subject: [PATCH] v0.6.0: implemented a room placer based on degree angles --- .../levels/builders/Builder.java | 115 +++++++++++++++++- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/builders/Builder.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/builders/Builder.java index 3b83e0365..3e114d4dd 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/builders/Builder.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/builders/Builder.java @@ -22,7 +22,9 @@ package com.shatteredpixel.shatteredpixeldungeon.levels.builders; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room; +import com.watabou.utils.GameMath; import com.watabou.utils.Point; +import com.watabou.utils.PointF; import com.watabou.utils.Random; import com.watabou.utils.Rect; @@ -37,11 +39,13 @@ public abstract class Builder { //builders take a list of rooms and returns them as a connected map //returns null on failure public abstract ArrayList build(ArrayList rooms); - - protected Rect findFreeSpace(Point start, ArrayList rooms, int maxSize){ + + //returns a rectangle representing the maximum amount of free space from a specific start point + protected Rect findFreeSpace(Point start, ArrayList collision, int maxSize){ Rect space = new Rect(start.x-maxSize, start.y-maxSize, start.x+maxSize, start.y+maxSize); - - ArrayList colliding = new ArrayList<>(rooms); + + //shallow copy + ArrayList colliding = new ArrayList<>(collision); do{ //remove any rooms we aren't currently overlapping @@ -119,4 +123,107 @@ public abstract class Builder { return space; } + + private static final double A = 180 / Math.PI; + + //Attempts to place a room such that the angle between the center of the previous room + // and it matches the given angle ([0-360), where 0 is straight up) as closely as possible. + //Note that getting an exactly correct angle is harder the closer that angle is to diagonal. + //Returns the exact angle between the centerpoints of the two rooms, or -1 if placement fails. + protected float placeRoom( ArrayList collision, Room prev, Room next, float angle){ + + //wrap angle around to always be [0-360) + angle %= 360f; + if (angle < 0){ + angle += 360f; + } + + PointF prevCenter = new PointF((prev.left + prev.right)/2f, (prev.top + prev.bottom)/2f); + + // calculating using y = mx+b, straight line formula + double m = Math.tan(angle/A + Math.PI/2.0); + double b = prevCenter.y -m*prevCenter.x; + + //using the line equation, we find the point along the prev room where the line exists + Point start; + int direction; + if (Math.abs(m) >= 1){ + if (angle < 90 || angle > 270){ + direction = Room.TOP; + start = new Point( (int)Math.round((prev.top - b)/m), prev.top); + } else { + direction = Room.BOTTOM; + start = new Point( (int)Math.round((prev.bottom - b)/m), prev.bottom); + } + } else { + if (angle < 180){ + direction = Room.RIGHT; + start = new Point(prev.right, (int) Math.round(m * prev.right + b)); + } else { + direction = Room.LEFT; + start = new Point(prev.left, (int) Math.round(m * prev.left + b)); + } + } + + //cap it to a valid connection point for most rooms + if (direction == Room.TOP || direction == Room.BOTTOM) { + start.x = (int) GameMath.gate(prev.left + 1, start.x, prev.right - 1); + } else { + start.y = (int) GameMath.gate(prev.top + 1, start.y, prev.bottom - 1); + } + + //space checking + Rect space = findFreeSpace(start, collision, Math.max(next.maxWidth(), next.maxHeight())); + if (!next.setSizeWithLimit(space.width()+1, space.height()+1)){ + return -1; + } + + //find the ideal center for this new room using the line equation and known dimensions + PointF targetCenter = new PointF(); + if (direction == Room.TOP) { + targetCenter.y = prev.top - (next.height() - 1) / 2f; + targetCenter.x = (float) ((targetCenter.y - b) / m); + next.setPos(Math.round(targetCenter.x - (next.width() - 1) / 2f), prev.top - (next.height() - 1)); + + } else if (direction == Room.BOTTOM) { + targetCenter.y = prev.bottom + (next.height() - 1) / 2f; + targetCenter.x = (float) ((targetCenter.y - b) / m); + next.setPos(Math.round(targetCenter.x - (next.width() - 1) / 2f), prev.bottom); + + } else if (direction == Room.RIGHT) { + targetCenter.x = prev.right + (next.width()-1)/2f; + targetCenter.y = (float)(m*targetCenter.x + b); + next.setPos( prev.right, Math.round(targetCenter.y - (next.height()-1)/2f)); + + } else if (direction == Room.LEFT) { + targetCenter.x = prev.left - (next.width()-1)/2f; + targetCenter.y = (float)(m*targetCenter.x + b); + next.setPos( prev.left - (next.width() - 1), Math.round(targetCenter.y - (next.height()-1)/2f)); + + } + + //perform connection bounds and target checking, move the room if necessary + if (direction == Room.TOP || direction == Room.BOTTOM){ + if (next.right < prev.left+2) next.shift(prev.left+2-next.right, 0); + else if (next.left > prev.right-2) next.shift(prev.right-2-next.left, 0); + + if (next.right > space.right) next.shift( space.right - next.right, 0); + else if (next.left < space.left) next.shift( space.left - next.left, 0); + } else { + if (next.bottom < prev.top+2) next.shift(0, prev.top+2-next.bottom); + else if (next.top > prev.bottom-2) next.shift(0, prev.bottom-2-next.top); + + if (next.bottom > space.bottom) next.shift( 0, space.bottom - next.bottom); + else if (next.top < space.top) next.shift( 0, space.top - next.top); + } + + //attempt to connect, return the result angle if successful. + if (next.connect(prev)){ + PointF nextCenter = new PointF((next.left + next.right)/2f, (next.top + next.bottom)/2f); + double trueM = (nextCenter.y - prevCenter.y)/(nextCenter.x - prevCenter.x); + return (float)(A*(Math.atan(trueM) + Math.PI/2f)); + } else { + return -1; + } + } }