Fix auto-drop issue for pieces near top of board, add spawn grace period and hard drop cooldown

This commit is contained in:
cmclark00 2025-03-28 18:59:13 -04:00
parent a47d83d905
commit 1c57c438ce
2 changed files with 91 additions and 13 deletions

View file

@ -133,6 +133,10 @@ class GameView @JvmOverloads constructor(
private var lastTapTime = 0L
private var lastRotationTime = 0L
private var lastMoveTime = 0L
private var lastHardDropTime = 0L // Track when the last hard drop occurred
private val hardDropCooldown = 250L // Reduced from 500ms to 250ms
private var touchFreezeUntil = 0L // Time until which touch events should be ignored
private val pieceLockFreezeTime = 300L // Time to freeze touch events after piece locks
private var minSwipeVelocity = 1200 // Increased from 800 to require more deliberate swipes
private val maxTapMovement = 20f // Maximum movement allowed for a tap (in pixels)
private val minTapTime = 100L // Minimum time for a tap (in milliseconds)
@ -171,7 +175,12 @@ class GameView @JvmOverloads constructor(
// Connect our callbacks to the GameBoard
gameBoard.onPieceMove = { onPieceMove?.invoke() }
gameBoard.onPieceLock = { onPieceLock?.invoke() }
gameBoard.onPieceLock = {
// Freeze touch events for a brief period after a piece locks
touchFreezeUntil = System.currentTimeMillis() + pieceLockFreezeTime
Log.d(TAG, "Piece locked - freezing touch events until ${touchFreezeUntil}")
onPieceLock?.invoke()
}
gameBoard.onLineClear = { lineCount, clearedLines ->
Log.d(TAG, "Received line clear from GameBoard: $lineCount lines")
try {
@ -697,6 +706,13 @@ class GameView @JvmOverloads constructor(
return true
}
// Ignore touch events during the freeze period after a piece locks
val currentTime = System.currentTimeMillis()
if (currentTime < touchFreezeUntil) {
Log.d(TAG, "Ignoring touch event - freeze active for ${touchFreezeUntil - currentTime}ms more")
return true
}
when (event.action) {
MotionEvent.ACTION_DOWN -> {
// Record start of touch
@ -781,21 +797,34 @@ class GameView @JvmOverloads constructor(
val moveTime = System.currentTimeMillis() - lastTapTime
val deltaY = event.y - startY
val deltaX = event.x - startX
val currentTime = System.currentTimeMillis()
// Only allow hard drops with a deliberate downward swipe
// Requires: predominantly vertical movement, minimum distance, and minimum velocity
if (moveTime > 0 &&
deltaY > blockSize * minHardDropDistance && // Require longer swipe for hard drop
// Check if this might have been a hard drop gesture
val isVerticalSwipe = moveTime > 0 &&
deltaY > blockSize * minHardDropDistance &&
(deltaY / moveTime) * 1000 > minSwipeVelocity &&
abs(deltaX) < abs(deltaY) * 0.3f) { // Require more purely vertical movement (reduced from 0.5f to 0.3f)
gameBoard.hardDrop()
invalidate()
abs(deltaX) < abs(deltaY) * 0.3f
// Check cooldown separately for better logging
val isCooldownActive = currentTime - lastHardDropTime <= hardDropCooldown
if (isVerticalSwipe) {
if (isCooldownActive) {
// Log when we're blocking a hard drop due to cooldown
Log.d("GameView", "Hard drop blocked by cooldown - time since last: ${currentTime - lastHardDropTime}ms, cooldown: ${hardDropCooldown}ms")
} else {
// Process the hard drop
Log.d("GameView", "Hard drop detected - deltaY: $deltaY, velocity: ${(deltaY / moveTime) * 1000}, ratio: ${abs(deltaX) / abs(deltaY)}")
gameBoard.hardDrop()
lastHardDropTime = currentTime // Update the last hard drop time
invalidate()
}
} else if (moveTime < minTapTime &&
abs(deltaY) < maxTapMovement &&
abs(deltaX) < maxTapMovement) {
// Quick tap with minimal movement (rotation)
val currentTime = System.currentTimeMillis()
if (currentTime - lastRotationTime >= rotationCooldown) {
Log.d("GameView", "Rotation detected")
gameBoard.rotate()
lastRotationTime = currentTime
invalidate()

View file

@ -63,6 +63,10 @@ class GameBoard(
// Store the last cleared lines
private val lastClearedLines = mutableListOf<Int>()
// Add spawn protection variables
private var pieceSpawnTime = 0L
private val spawnGracePeriod = 250L // Changed from 150ms to 250ms
init {
spawnNextPiece()
spawnPiece()
@ -122,6 +126,8 @@ class GameBoard(
* Spawns the current tetromino at the top of the board
*/
fun spawnPiece() {
Log.d(TAG, "spawnPiece() started - current states: isHardDropInProgress=$isHardDropInProgress, isPieceLocking=$isPieceLocking")
currentPiece = nextPiece
spawnNextPiece()
@ -130,9 +136,15 @@ class GameBoard(
x = (width - getWidth()) / 2
y = 0
Log.d(TAG, "spawnPiece() - new piece spawned at position (${x},${y}), type=${type}")
// Set the spawn time for the grace period
pieceSpawnTime = System.currentTimeMillis()
// Check if the piece can be placed (Game Over condition)
if (!canMove(0, 0)) {
isGameOver = true
Log.d(TAG, "spawnPiece() - Game Over condition detected")
}
}
}
@ -173,6 +185,13 @@ class GameBoard(
onPieceMove?.invoke()
true
} else {
// Check if we're within the spawn grace period
val currentTime = System.currentTimeMillis()
if (currentTime - pieceSpawnTime < spawnGracePeriod) {
Log.d(TAG, "moveDown() - not locking piece due to spawn grace period (${currentTime - pieceSpawnTime}ms < ${spawnGracePeriod}ms)")
return false
}
lockPiece()
false
}
@ -191,8 +210,19 @@ class GameBoard(
* Hard drop the current piece
*/
fun hardDrop() {
if (isHardDropInProgress || isPieceLocking) return // Prevent multiple hard drops
if (isHardDropInProgress || isPieceLocking) {
Log.d(TAG, "hardDrop() called but blocked: isHardDropInProgress=$isHardDropInProgress, isPieceLocking=$isPieceLocking")
return // Prevent multiple hard drops
}
// Check if we're within the spawn grace period
val currentTime = System.currentTimeMillis()
if (currentTime - pieceSpawnTime < spawnGracePeriod) {
Log.d(TAG, "hardDrop() - blocked due to spawn grace period (${currentTime - pieceSpawnTime}ms < ${spawnGracePeriod}ms)")
return
}
Log.d(TAG, "hardDrop() started - setting isHardDropInProgress=true")
isHardDropInProgress = true
val piece = currentPiece ?: return
@ -202,12 +232,16 @@ class GameBoard(
dropDistance++
}
Log.d(TAG, "hardDrop() - piece will drop $dropDistance cells, position before: (${piece.x},${piece.y})")
// Move piece down until it can't move anymore
while (canMove(0, 1)) {
piece.y++
onPieceMove?.invoke()
}
Log.d(TAG, "hardDrop() - piece final position: (${piece.x},${piece.y})")
// Add hard drop points (2 points per cell)
score += dropDistance * 2
@ -298,7 +332,12 @@ class GameBoard(
* Lock the current piece in place
*/
private fun lockPiece() {
if (isPieceLocking) return // Prevent recursive locking
if (isPieceLocking) {
Log.d(TAG, "lockPiece() called but blocked: isPieceLocking=$isPieceLocking")
return // Prevent recursive locking
}
Log.d(TAG, "lockPiece() started - setting isPieceLocking=true, current isHardDropInProgress=$isHardDropInProgress")
isPieceLocking = true
val piece = currentPiece ?: return
@ -324,15 +363,25 @@ class GameBoard(
// Find and clear lines immediately
findAndClearLines()
// IMPORTANT: Reset the hard drop flag before spawning a new piece
// This prevents the immediate hard drop of the next piece
if (isHardDropInProgress) {
Log.d(TAG, "lockPiece() - resetting isHardDropInProgress=false BEFORE spawning new piece")
isHardDropInProgress = false
}
// Log piece position before spawning new piece
Log.d(TAG, "lockPiece() - about to spawn new piece at y=${piece.y}, isHardDropInProgress=$isHardDropInProgress")
// Spawn new piece immediately
spawnPiece()
// Allow holding piece again after locking
canHold = true
// Reset both states after everything is done
// Reset locking state
isPieceLocking = false
isHardDropInProgress = false
Log.d(TAG, "lockPiece() completed - reset flags: isPieceLocking=false, isHardDropInProgress=$isHardDropInProgress")
}
/**