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 lastTapTime = 0L
private var lastRotationTime = 0L private var lastRotationTime = 0L
private var lastMoveTime = 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 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 maxTapMovement = 20f // Maximum movement allowed for a tap (in pixels)
private val minTapTime = 100L // Minimum time for a tap (in milliseconds) 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 // Connect our callbacks to the GameBoard
gameBoard.onPieceMove = { onPieceMove?.invoke() } 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 -> gameBoard.onLineClear = { lineCount, clearedLines ->
Log.d(TAG, "Received line clear from GameBoard: $lineCount lines") Log.d(TAG, "Received line clear from GameBoard: $lineCount lines")
try { try {
@ -697,6 +706,13 @@ class GameView @JvmOverloads constructor(
return true 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) { when (event.action) {
MotionEvent.ACTION_DOWN -> { MotionEvent.ACTION_DOWN -> {
// Record start of touch // Record start of touch
@ -781,21 +797,34 @@ class GameView @JvmOverloads constructor(
val moveTime = System.currentTimeMillis() - lastTapTime val moveTime = System.currentTimeMillis() - lastTapTime
val deltaY = event.y - startY val deltaY = event.y - startY
val deltaX = event.x - startX val deltaX = event.x - startX
val currentTime = System.currentTimeMillis()
// Only allow hard drops with a deliberate downward swipe // Check if this might have been a hard drop gesture
// Requires: predominantly vertical movement, minimum distance, and minimum velocity val isVerticalSwipe = moveTime > 0 &&
if (moveTime > 0 && deltaY > blockSize * minHardDropDistance &&
deltaY > blockSize * minHardDropDistance && // Require longer swipe for hard drop
(deltaY / moveTime) * 1000 > minSwipeVelocity && (deltaY / moveTime) * 1000 > minSwipeVelocity &&
abs(deltaX) < abs(deltaY) * 0.3f) { // Require more purely vertical movement (reduced from 0.5f to 0.3f) 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() gameBoard.hardDrop()
lastHardDropTime = currentTime // Update the last hard drop time
invalidate() invalidate()
}
} else if (moveTime < minTapTime && } else if (moveTime < minTapTime &&
abs(deltaY) < maxTapMovement && abs(deltaY) < maxTapMovement &&
abs(deltaX) < maxTapMovement) { abs(deltaX) < maxTapMovement) {
// Quick tap with minimal movement (rotation) // Quick tap with minimal movement (rotation)
val currentTime = System.currentTimeMillis()
if (currentTime - lastRotationTime >= rotationCooldown) { if (currentTime - lastRotationTime >= rotationCooldown) {
Log.d("GameView", "Rotation detected")
gameBoard.rotate() gameBoard.rotate()
lastRotationTime = currentTime lastRotationTime = currentTime
invalidate() invalidate()

View file

@ -63,6 +63,10 @@ class GameBoard(
// Store the last cleared lines // Store the last cleared lines
private val lastClearedLines = mutableListOf<Int>() private val lastClearedLines = mutableListOf<Int>()
// Add spawn protection variables
private var pieceSpawnTime = 0L
private val spawnGracePeriod = 250L // Changed from 150ms to 250ms
init { init {
spawnNextPiece() spawnNextPiece()
spawnPiece() spawnPiece()
@ -122,6 +126,8 @@ class GameBoard(
* Spawns the current tetromino at the top of the board * Spawns the current tetromino at the top of the board
*/ */
fun spawnPiece() { fun spawnPiece() {
Log.d(TAG, "spawnPiece() started - current states: isHardDropInProgress=$isHardDropInProgress, isPieceLocking=$isPieceLocking")
currentPiece = nextPiece currentPiece = nextPiece
spawnNextPiece() spawnNextPiece()
@ -130,9 +136,15 @@ class GameBoard(
x = (width - getWidth()) / 2 x = (width - getWidth()) / 2
y = 0 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) // Check if the piece can be placed (Game Over condition)
if (!canMove(0, 0)) { if (!canMove(0, 0)) {
isGameOver = true isGameOver = true
Log.d(TAG, "spawnPiece() - Game Over condition detected")
} }
} }
} }
@ -173,6 +185,13 @@ class GameBoard(
onPieceMove?.invoke() onPieceMove?.invoke()
true true
} else { } 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() lockPiece()
false false
} }
@ -191,8 +210,19 @@ class GameBoard(
* Hard drop the current piece * Hard drop the current piece
*/ */
fun hardDrop() { 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 isHardDropInProgress = true
val piece = currentPiece ?: return val piece = currentPiece ?: return
@ -202,12 +232,16 @@ class GameBoard(
dropDistance++ 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 // Move piece down until it can't move anymore
while (canMove(0, 1)) { while (canMove(0, 1)) {
piece.y++ piece.y++
onPieceMove?.invoke() onPieceMove?.invoke()
} }
Log.d(TAG, "hardDrop() - piece final position: (${piece.x},${piece.y})")
// Add hard drop points (2 points per cell) // Add hard drop points (2 points per cell)
score += dropDistance * 2 score += dropDistance * 2
@ -298,7 +332,12 @@ class GameBoard(
* Lock the current piece in place * Lock the current piece in place
*/ */
private fun lockPiece() { 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 isPieceLocking = true
val piece = currentPiece ?: return val piece = currentPiece ?: return
@ -324,15 +363,25 @@ class GameBoard(
// Find and clear lines immediately // Find and clear lines immediately
findAndClearLines() 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 // Spawn new piece immediately
spawnPiece() spawnPiece()
// Allow holding piece again after locking // Allow holding piece again after locking
canHold = true canHold = true
// Reset both states after everything is done // Reset locking state
isPieceLocking = false isPieceLocking = false
isHardDropInProgress = false Log.d(TAG, "lockPiece() completed - reset flags: isPieceLocking=false, isHardDropInProgress=$isHardDropInProgress")
} }
/** /**