summaryrefslogtreecommitdiff
path: root/app/src/main/java/space
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/space')
-rw-r--r--app/src/main/java/space/jakob/mines/GameActivity.kt93
-rw-r--r--app/src/main/java/space/jakob/mines/GameView.kt113
-rw-r--r--app/src/main/java/space/jakob/mines/Grid.kt7
3 files changed, 126 insertions, 87 deletions
diff --git a/app/src/main/java/space/jakob/mines/GameActivity.kt b/app/src/main/java/space/jakob/mines/GameActivity.kt
index 7e8313c..9240dd9 100644
--- a/app/src/main/java/space/jakob/mines/GameActivity.kt
+++ b/app/src/main/java/space/jakob/mines/GameActivity.kt
@@ -27,61 +27,62 @@ class GameActivity : AppCompatActivity() {
// Implements the callbacks for gestureDetector.
val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
- override fun onDown(e: MotionEvent): Boolean {
- with (gameView) {
- grid?.let {
- val (x, y) = toGridCoordinates(e.x.toInt(), e.y.toInt())
- it.reveal(x, y)
- invalidate()
- }
- }
- return false
- }
+ // TODO: Replace with onUp
+ // override fun onDown(e: MotionEvent): Boolean {
+ // with (gameView) {
+ // grid?.let {
+ // val (x, y) = toGridCoordinates(e.x.toInt(), e.y.toInt())
+ // it.reveal(x, y)
+ // invalidate()
+ // }
+ // }
+ // return false
+ // }
- override fun onScroll(e1: MotionEvent, e2: MotionEvent, dx: Float, dy: Float): Boolean {
- with (gameView) {
- cameraX = (cameraX + dx / 100.0).coerceIn(0.0, gameView.maxCameraX)
- cameraY = (cameraY + dy / 100.0).coerceIn(0.0, gameView.maxCameraY)
- invalidate()
- }
- return false
- }
+ // override fun onScroll(e1: MotionEvent, e2: MotionEvent, dx: Float, dy: Float): Boolean {
+ // with (gameView) {
+ // cameraX = (cameraX + dx / 100.0).coerceIn(0.0, gameView.maxCameraX)
+ // cameraY = (cameraY + dy / 100.0).coerceIn(0.0, gameView.maxCameraY)
+ // invalidate()
+ // }
+ // return false
+ // }
}
// Implements the callbacks for scaleGestureDetector.
val scaleListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
- override fun onScale(scaleGestureDetector: ScaleGestureDetector) : Boolean {
- with (gameView) {
- grid?.let {
- val factor = scaleGestureDetector.getScaleFactor()
+ // override fun onScale(scaleGestureDetector: ScaleGestureDetector) : Boolean {
+ // with (gameView) {
+ // grid?.let {
+ // val factor = scaleGestureDetector.getScaleFactor()
- var currentDistance = sightX - cameraX
- var desiredDistance = currentDistance / factor
- var deltaDistance = Math.abs(desiredDistance - currentDistance) / 2
+ // var currentDistance = sightX - cameraX
+ // var desiredDistance = currentDistance / factor
+ // var deltaDistance = Math.abs(desiredDistance - currentDistance) / 2
- if (factor < 1.0) {
- cameraX = (cameraX - deltaDistance).coerceAtLeast(0.0)
- sightX = (sightX + deltaDistance).coerceAtMost(it.width.toDouble() - cameraX)
- } else {
- cameraX = cameraX + deltaDistance
- sightX = sightX - deltaDistance
- }
+ // if (factor < 1.0) {
+ // cameraX = (cameraX - deltaDistance).coerceAtLeast(0.0)
+ // sightX = (sightX + deltaDistance).coerceAtMost(it.width.toDouble() - cameraX)
+ // } else {
+ // cameraX = cameraX + deltaDistance
+ // sightX = sightX - deltaDistance
+ // }
- currentDistance = sightY - cameraY
- desiredDistance = currentDistance / factor
- deltaDistance = Math.abs(desiredDistance - currentDistance) / 2
+ // currentDistance = sightY - cameraY
+ // desiredDistance = currentDistance / factor
+ // deltaDistance = Math.abs(desiredDistance - currentDistance) / 2
- if (factor < 1.0) {
- cameraY = (cameraY - deltaDistance).coerceAtLeast(0.0)
- sightY = (sightY + deltaDistance).coerceAtMost(it.height.toDouble() - cameraY)
- } else {
- cameraY = cameraY + deltaDistance
- sightY = sightY - deltaDistance
- }
- }
- }
- return false
- }
+ // if (factor < 1.0) {
+ // cameraY = (cameraY - deltaDistance).coerceAtLeast(0.0)
+ // sightY = (sightY + deltaDistance).coerceAtMost(it.height.toDouble() - cameraY)
+ // } else {
+ // cameraY = cameraY + deltaDistance
+ // sightY = sightY - deltaDistance
+ // }
+ // }
+ // }
+ // return false
+ // }
}
override fun onTouchEvent(event: MotionEvent): Boolean {
diff --git a/app/src/main/java/space/jakob/mines/GameView.kt b/app/src/main/java/space/jakob/mines/GameView.kt
index 36fe2ec..52a8283 100644
--- a/app/src/main/java/space/jakob/mines/GameView.kt
+++ b/app/src/main/java/space/jakob/mines/GameView.kt
@@ -22,8 +22,8 @@ import android.util.AttributeSet
import android.view.View
import android.util.Log
-const val atlasWidth = 3
-const val tileSpacing = 2.0 / 27.0
+import kotlin.math.ceil
+import kotlin.math.floor
enum class AtlasEntry {
BLANK, MASKED, UNMASKED,
@@ -35,52 +35,47 @@ class GameView(context: Context, attrs: AttributeSet) : View(context, attrs) {
val atlas = BitmapFactory.decodeResource(context.resources, R.drawable.atlas);
val tileWidth = atlas.width / 3
+ // FIXME: Is this correct? `breadth` tiles, excluding halfsies and such?
+ val scaledTileWidth: Int
+ get() = (minOf(width, height) / cameraBreadth).toInt()
+
var grid: Grid? = null
+ // All of the state for maintaining the view's current positioning. For an
+ // explanation of these values, see `drawGrid`.
var cameraX = 0.0
var cameraY = 0.0
- var sightX = 5.0
- var sightY = 5.0
-
- val maxCameraX: Double
- get() = grid?.let {
- it.width - sightX
- } ?: 0.0
- val maxCameraY: Double
- get() = grid?.let {
- it.height - sightY
- } ?: 0.0
-
- // The size, in pixels, of an individual tile based on the current scale
- // factor.
- val scaledWidth: Int
- get() = width / sightX.toInt()
- val scaledHeight: Int
- get() = height / sightY.toInt()
+ var cameraBreadth = 5.0
+ // TODO: Break up into multiple expressions. This isn't readable.
/**
* Returns the grid coordinates for the tile at the given screen coordinates.
*/
fun toGridCoordinates(x: Int, y: Int) = Pair(
- (x + (scaledWidth * (cameraX % 1)).toInt()) / scaledWidth + cameraX.toInt(),
- (y + (scaledHeight * (cameraY % 1)).toInt()) / scaledHeight + cameraY.toInt()
+ (x + (scaledTileWidth * (cameraX % 1)).toInt()) / scaledTileWidth + cameraX.toInt(),
+ (y + (scaledTileWidth * (cameraY % 1)).toInt()) / scaledTileWidth + cameraY.toInt()
)
private fun atlasRect(entry: AtlasEntry): Rect {
+ val atlasWidth = 3
+
val x = (entry.ordinal % atlasWidth) * tileWidth
val y = (entry.ordinal / atlasWidth) * tileWidth
return Rect(x, y, x + tileWidth, y + tileWidth)
}
private fun drawTile(canvas: Canvas, tile: Tile, x: Int, y: Int) {
- val xSpacing = (scaledWidth * tileSpacing).toInt()
- val ySpacing = (scaledWidth * tileSpacing).toInt()
+ val tileSpacing = 2.0 / 27.0
+ val xSpacing = (scaledTileWidth * tileSpacing).toInt()
+ val ySpacing = (scaledTileWidth * tileSpacing).toInt()
+
+ // TODO: Opportunity for more ASCII art explanations :)
val dst = Rect(
x + xSpacing,
y + ySpacing,
- x + scaledWidth - xSpacing,
- y + scaledHeight - ySpacing
+ x + scaledTileWidth - xSpacing,
+ y + scaledTileWidth - ySpacing
)
if (tile.masked) {
@@ -106,24 +101,62 @@ class GameView(context: Context, attrs: AttributeSet) : View(context, attrs) {
}
private fun drawGrid(canvas: Canvas, grid: Grid) {
- val startX = cameraX.toInt() // Truncates the fractional component.
- val startY = cameraY.toInt()
- val howManyX = (sightX.toInt() + 1).coerceAtMost(grid.width - startX - 1)
- val howManyY = (sightY.toInt() + 1).coerceAtMost(grid.height - startY - 1)
+ // The state maintained for drawing the view is a position, (`cameraX`,
+ // `cameraY`), representing the centroid of a rectangle whose sides are
+ // of length `cameraBreadthX` and `cameraBreadthY`. Only the tiles
+ // intersecting this rectangle are drawn.
+ //
+ // +============+
+ // / | |
+ // | | |
+ // | | |
+ // cameraBreadthY < | x |
+ // | | \__________ (cameraX, cameraY)
+ // | | |
+ // \ | |
+ // +============+
+ // \_____ ______/
+ // v
+ // cameraBreadthX
+
+ val cameraBreadthX = if (width > height) {
+ cameraBreadth * width.toDouble() / height.toDouble()
+ } else {
+ cameraBreadth
+ }
+ val cameraBreadthY = if (height > width) {
+ cameraBreadth * height.toDouble() / width.toDouble()
+ } else {
+ cameraBreadth
+ }
+
+ // This is where the fractional component comes into play -- drawing
+ // tiles that are partially obscured by the limited reaches of the
+ // camera view.
+ val offsetX = (scaledTileWidth * ((cameraX + (cameraBreadthX / 2)) % 1)).toInt()
+ val offsetY = (scaledTileWidth * ((cameraY + (cameraBreadthY / 2)) % 1) / 2).toInt()
+
+ val howManyX = ceil(cameraBreadthX).toInt()
+ val howManyY = ceil(cameraBreadthY).toInt()
+
+ val startX = cameraX - (cameraBreadthX / 2)
+ val startY = cameraY - (cameraBreadthY / 2)
for (i in 0..howManyX) {
for (j in 0..howManyY) {
- val tile = grid[startX + i, startY + j]
-
- val xOffset = (scaledWidth * (cameraX % 1)).toInt()
- val yOffset = (scaledHeight * (cameraY % 1)).toInt()
+ // The fractional components here are truncated, giving a
+ // equivalent indices into `grid`. Of course, these indices
+ // aren't necessarily within the bounds of the grid. If a tile
+ // doesn't exist, we'll just draw blank space.
+ val x = (startX + i.toDouble()).toInt()
+ val y = (startY + j.toDouble()).toInt()
- drawTile(
- canvas,
- tile,
- i * scaledWidth - xOffset,
- j * scaledHeight - yOffset
- )
+ if (grid.valid(x, y)) {
+ val tile = grid[x, y]
+ val screenX = i * scaledTileWidth
+ val screenY = j * scaledTileWidth
+ drawTile(canvas, tile, screenX + offsetX, screenY + offsetY)
+ }
}
}
}
diff --git a/app/src/main/java/space/jakob/mines/Grid.kt b/app/src/main/java/space/jakob/mines/Grid.kt
index b61f5f1..2eec411 100644
--- a/app/src/main/java/space/jakob/mines/Grid.kt
+++ b/app/src/main/java/space/jakob/mines/Grid.kt
@@ -31,12 +31,17 @@ class Grid(val width: Int, val height: Int, val tiles: Array<Tile>) {
}
private fun index(x: Int, y: Int) = y * width + x
- private fun valid(x: Int, y: Int) = y in 0 until height && x in 0 until width
private fun place(x: Int, y: Int, tile: Tile) {
tiles[index(x, y)] = tile
}
/**
+ * Returns whether or not @see get on the given index would raise an
+ * @exception IllegalArgumentException
+ */
+ fun valid(x: Int, y: Int) = y in 0 until height && x in 0 until width
+
+ /**
* Returns the tile at the given coordinates.
*
* @throws IllegalArgumentException if the X coordinate is outside