diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/android-app/.gradle/7.5/checksums/checksums.lock b/android-app/.gradle/7.5/checksums/checksums.lock new file mode 100644 index 0000000..4aeb5ce Binary files /dev/null and b/android-app/.gradle/7.5/checksums/checksums.lock differ diff --git a/android-app/.gradle/7.5/fileChanges/last-build.bin b/android-app/.gradle/7.5/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/android-app/.gradle/7.5/fileChanges/last-build.bin differ diff --git a/android-app/.gradle/7.5/fileHashes/fileHashes.lock b/android-app/.gradle/7.5/fileHashes/fileHashes.lock new file mode 100644 index 0000000..2411039 Binary files /dev/null and b/android-app/.gradle/7.5/fileHashes/fileHashes.lock differ diff --git a/android-app/.gradle/7.5/gc.properties b/android-app/.gradle/7.5/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.gradle/8.10/checksums/checksums.lock b/android-app/.gradle/8.10/checksums/checksums.lock new file mode 100644 index 0000000..9bcb542 Binary files /dev/null and b/android-app/.gradle/8.10/checksums/checksums.lock differ diff --git a/android-app/.gradle/8.10/checksums/md5-checksums.bin b/android-app/.gradle/8.10/checksums/md5-checksums.bin new file mode 100644 index 0000000..509ec20 Binary files /dev/null and b/android-app/.gradle/8.10/checksums/md5-checksums.bin differ diff --git a/android-app/.gradle/8.10/checksums/sha1-checksums.bin b/android-app/.gradle/8.10/checksums/sha1-checksums.bin new file mode 100644 index 0000000..6bd57f3 Binary files /dev/null and b/android-app/.gradle/8.10/checksums/sha1-checksums.bin differ diff --git a/android-app/.gradle/8.10/dependencies-accessors/gc.properties b/android-app/.gradle/8.10/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.gradle/8.10/executionHistory/executionHistory.bin b/android-app/.gradle/8.10/executionHistory/executionHistory.bin new file mode 100644 index 0000000..1e5933b Binary files /dev/null and b/android-app/.gradle/8.10/executionHistory/executionHistory.bin differ diff --git a/android-app/.gradle/8.10/executionHistory/executionHistory.lock b/android-app/.gradle/8.10/executionHistory/executionHistory.lock new file mode 100644 index 0000000..d420e60 Binary files /dev/null and b/android-app/.gradle/8.10/executionHistory/executionHistory.lock differ diff --git a/android-app/.gradle/8.10/fileChanges/last-build.bin b/android-app/.gradle/8.10/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/android-app/.gradle/8.10/fileChanges/last-build.bin differ diff --git a/android-app/.gradle/8.10/fileHashes/fileHashes.bin b/android-app/.gradle/8.10/fileHashes/fileHashes.bin new file mode 100644 index 0000000..5fd80eb Binary files /dev/null and b/android-app/.gradle/8.10/fileHashes/fileHashes.bin differ diff --git a/android-app/.gradle/8.10/fileHashes/fileHashes.lock b/android-app/.gradle/8.10/fileHashes/fileHashes.lock new file mode 100644 index 0000000..eaa5424 Binary files /dev/null and b/android-app/.gradle/8.10/fileHashes/fileHashes.lock differ diff --git a/android-app/.gradle/8.10/gc.properties b/android-app/.gradle/8.10/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.gradle/8.11.1/checksums/checksums.lock b/android-app/.gradle/8.11.1/checksums/checksums.lock new file mode 100644 index 0000000..3a2f1da Binary files /dev/null and b/android-app/.gradle/8.11.1/checksums/checksums.lock differ diff --git a/android-app/.gradle/8.11.1/checksums/md5-checksums.bin b/android-app/.gradle/8.11.1/checksums/md5-checksums.bin new file mode 100644 index 0000000..9f7f27d Binary files /dev/null and b/android-app/.gradle/8.11.1/checksums/md5-checksums.bin differ diff --git a/android-app/.gradle/8.11.1/checksums/sha1-checksums.bin b/android-app/.gradle/8.11.1/checksums/sha1-checksums.bin new file mode 100644 index 0000000..27b3854 Binary files /dev/null and b/android-app/.gradle/8.11.1/checksums/sha1-checksums.bin differ diff --git a/android-app/.gradle/8.11.1/executionHistory/executionHistory.bin b/android-app/.gradle/8.11.1/executionHistory/executionHistory.bin new file mode 100644 index 0000000..1e65897 Binary files /dev/null and b/android-app/.gradle/8.11.1/executionHistory/executionHistory.bin differ diff --git a/android-app/.gradle/8.11.1/executionHistory/executionHistory.lock b/android-app/.gradle/8.11.1/executionHistory/executionHistory.lock new file mode 100644 index 0000000..152df37 Binary files /dev/null and b/android-app/.gradle/8.11.1/executionHistory/executionHistory.lock differ diff --git a/android-app/.gradle/8.11.1/fileChanges/last-build.bin b/android-app/.gradle/8.11.1/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/android-app/.gradle/8.11.1/fileChanges/last-build.bin differ diff --git a/android-app/.gradle/8.11.1/fileHashes/fileHashes.bin b/android-app/.gradle/8.11.1/fileHashes/fileHashes.bin new file mode 100644 index 0000000..9dde153 Binary files /dev/null and b/android-app/.gradle/8.11.1/fileHashes/fileHashes.bin differ diff --git a/android-app/.gradle/8.11.1/fileHashes/fileHashes.lock b/android-app/.gradle/8.11.1/fileHashes/fileHashes.lock new file mode 100644 index 0000000..8190865 Binary files /dev/null and b/android-app/.gradle/8.11.1/fileHashes/fileHashes.lock differ diff --git a/android-app/.gradle/8.11.1/fileHashes/resourceHashesCache.bin b/android-app/.gradle/8.11.1/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000..92a1969 Binary files /dev/null and b/android-app/.gradle/8.11.1/fileHashes/resourceHashesCache.bin differ diff --git a/android-app/.gradle/8.11.1/gc.properties b/android-app/.gradle/8.11.1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.gradle/8.8/checksums/checksums.lock b/android-app/.gradle/8.8/checksums/checksums.lock new file mode 100644 index 0000000..7f0b8e9 Binary files /dev/null and b/android-app/.gradle/8.8/checksums/checksums.lock differ diff --git a/android-app/.gradle/8.8/checksums/md5-checksums.bin b/android-app/.gradle/8.8/checksums/md5-checksums.bin new file mode 100644 index 0000000..efd3160 Binary files /dev/null and b/android-app/.gradle/8.8/checksums/md5-checksums.bin differ diff --git a/android-app/.gradle/8.8/checksums/sha1-checksums.bin b/android-app/.gradle/8.8/checksums/sha1-checksums.bin new file mode 100644 index 0000000..0c5c37d Binary files /dev/null and b/android-app/.gradle/8.8/checksums/sha1-checksums.bin differ diff --git a/android-app/.gradle/8.8/dependencies-accessors/gc.properties b/android-app/.gradle/8.8/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.gradle/8.8/fileChanges/last-build.bin b/android-app/.gradle/8.8/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/android-app/.gradle/8.8/fileChanges/last-build.bin differ diff --git a/android-app/.gradle/8.8/fileHashes/fileHashes.bin b/android-app/.gradle/8.8/fileHashes/fileHashes.bin new file mode 100644 index 0000000..689e596 Binary files /dev/null and b/android-app/.gradle/8.8/fileHashes/fileHashes.bin differ diff --git a/android-app/.gradle/8.8/fileHashes/fileHashes.lock b/android-app/.gradle/8.8/fileHashes/fileHashes.lock new file mode 100644 index 0000000..28f50b2 Binary files /dev/null and b/android-app/.gradle/8.8/fileHashes/fileHashes.lock differ diff --git a/android-app/.gradle/8.8/gc.properties b/android-app/.gradle/8.8/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.gradle/8.9/checksums/checksums.lock b/android-app/.gradle/8.9/checksums/checksums.lock new file mode 100644 index 0000000..347b45b Binary files /dev/null and b/android-app/.gradle/8.9/checksums/checksums.lock differ diff --git a/android-app/.gradle/8.9/dependencies-accessors/gc.properties b/android-app/.gradle/8.9/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.gradle/8.9/executionHistory/executionHistory.lock b/android-app/.gradle/8.9/executionHistory/executionHistory.lock new file mode 100644 index 0000000..1716945 Binary files /dev/null and b/android-app/.gradle/8.9/executionHistory/executionHistory.lock differ diff --git a/android-app/.gradle/8.9/fileChanges/last-build.bin b/android-app/.gradle/8.9/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/android-app/.gradle/8.9/fileChanges/last-build.bin differ diff --git a/android-app/.gradle/8.9/fileHashes/fileHashes.lock b/android-app/.gradle/8.9/fileHashes/fileHashes.lock new file mode 100644 index 0000000..bbe8235 Binary files /dev/null and b/android-app/.gradle/8.9/fileHashes/fileHashes.lock differ diff --git a/android-app/.gradle/8.9/gc.properties b/android-app/.gradle/8.9/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android-app/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..8909f05 Binary files /dev/null and b/android-app/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/android-app/.gradle/buildOutputCleanup/cache.properties b/android-app/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..86e9063 --- /dev/null +++ b/android-app/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Tue Mar 25 23:50:46 EDT 2025 +gradle.version=8.11.1 diff --git a/android-app/.gradle/buildOutputCleanup/outputFiles.bin b/android-app/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000..86fc029 Binary files /dev/null and b/android-app/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/android-app/.gradle/config.properties b/android-app/.gradle/config.properties new file mode 100644 index 0000000..f2e3794 --- /dev/null +++ b/android-app/.gradle/config.properties @@ -0,0 +1,2 @@ +#Tue Mar 25 23:36:25 EDT 2025 +java.home=/home/corey/Downloads/android-studio-2024.3.1.13-linux/android-studio/jbr diff --git a/android-app/.gradle/file-system.probe b/android-app/.gradle/file-system.probe new file mode 100644 index 0000000..c7a2e14 Binary files /dev/null and b/android-app/.gradle/file-system.probe differ diff --git a/android-app/.gradle/nb-cache/android-app-623140067/project-info.ser b/android-app/.gradle/nb-cache/android-app-623140067/project-info.ser new file mode 100644 index 0000000..5c2e123 Binary files /dev/null and b/android-app/.gradle/nb-cache/android-app-623140067/project-info.ser differ diff --git a/android-app/.gradle/nb-cache/app-271321845/project-info.ser b/android-app/.gradle/nb-cache/app-271321845/project-info.ser new file mode 100644 index 0000000..5de0ac0 Binary files /dev/null and b/android-app/.gradle/nb-cache/app-271321845/project-info.ser differ diff --git a/android-app/.gradle/nb-cache/subprojects.ser b/android-app/.gradle/nb-cache/subprojects.ser new file mode 100644 index 0000000..faa7cf7 Binary files /dev/null and b/android-app/.gradle/nb-cache/subprojects.ser differ diff --git a/android-app/.gradle/nb-cache/trust/388F7AB02DFD0183E3FD2FB1B5B4206B0C7407CAFFD7F76F3C1EF65077068EC5 b/android-app/.gradle/nb-cache/trust/388F7AB02DFD0183E3FD2FB1B5B4206B0C7407CAFFD7F76F3C1EF65077068EC5 new file mode 100644 index 0000000..f62917f --- /dev/null +++ b/android-app/.gradle/nb-cache/trust/388F7AB02DFD0183E3FD2FB1B5B4206B0C7407CAFFD7F76F3C1EF65077068EC5 @@ -0,0 +1 @@ +8B64E587393F94313EFF41233F48D4848E985369BD2E4B833B25B3765823A6DB diff --git a/android-app/.gradle/vcs-1/gc.properties b/android-app/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/android-app/.idea/.gitignore b/android-app/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/android-app/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/android-app/.idea/.name b/android-app/.idea/.name new file mode 100644 index 0000000..8f54013 --- /dev/null +++ b/android-app/.idea/.name @@ -0,0 +1 @@ +Tetris3D \ No newline at end of file diff --git a/android-app/.idea/AndroidProjectSystem.xml b/android-app/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/android-app/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/android-app/.idea/appInsightsSettings.xml b/android-app/.idea/appInsightsSettings.xml new file mode 100644 index 0000000..371f2e2 --- /dev/null +++ b/android-app/.idea/appInsightsSettings.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/android-app/.idea/caches/deviceStreaming.xml b/android-app/.idea/caches/deviceStreaming.xml new file mode 100644 index 0000000..9e9ba09 --- /dev/null +++ b/android-app/.idea/caches/deviceStreaming.xml @@ -0,0 +1,607 @@ + + + + + + \ No newline at end of file diff --git a/android-app/.idea/compiler.xml b/android-app/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/android-app/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android-app/.idea/deploymentTargetSelector.xml b/android-app/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/android-app/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/android-app/.idea/gradle.xml b/android-app/.idea/gradle.xml new file mode 100644 index 0000000..639c779 --- /dev/null +++ b/android-app/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/android-app/.idea/kotlinc.xml b/android-app/.idea/kotlinc.xml new file mode 100644 index 0000000..2b8a50f --- /dev/null +++ b/android-app/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/android-app/.idea/migrations.xml b/android-app/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/android-app/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/android-app/.idea/misc.xml b/android-app/.idea/misc.xml new file mode 100644 index 0000000..74dd639 --- /dev/null +++ b/android-app/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/android-app/.idea/runConfigurations.xml b/android-app/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/android-app/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/android-app/.idea/vcs.xml b/android-app/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/android-app/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android-app/README.md b/android-app/README.md new file mode 100644 index 0000000..b12cb8b --- /dev/null +++ b/android-app/README.md @@ -0,0 +1,57 @@ +# 3D Tetris - Android App + +This is the native Android implementation of the 3D Tetris game. The mobile-specific optimizations from the web app have been removed, and instead, this native app has been created to provide a better experience on Android devices. + +## Project Structure + +- `app/src/main/java/com/tetris3d/` - Contains all Java/Kotlin source files + - `MainActivity.kt` - Main activity that hosts the game + - `game/` - Game logic implementation + - `views/` - Custom views for rendering game elements + +- `app/src/main/res/` - Contains all resources + - `layout/` - XML layouts for the UI + - `values/` - String resources, colors, and themes + - `drawable/` - Icon and image resources + +## Features + +- 3D Tetris gameplay with modern graphics +- Customizable options for 3D effects and animations +- Physical button controls optimized for touch +- Score tracking and level progression +- Game state persistence + +## Required Implementation + +The following components still need to be implemented to complete the Android app: + +1. TetrisGame class - Core game logic ported from JavaScript +2. TetrisGameView - Custom view for rendering the game +3. NextPieceView - Custom view for rendering the next piece preview +4. Tetromino classes - Classes for different tetromino pieces +5. Game renderer - OpenGL ES or Canvas-based renderer for the 3D effects + +## Dependencies + +- AndroidX libraries for UI components +- Kotlin coroutines for game loop threading + +## Building and Running + +1. Open the project in Android Studio +2. Build the project using Gradle +3. Deploy to an Android device or emulator + +## Development Process + +The Android app was created by: + +1. Analyzing the web implementation of the game +2. Removing mobile-specific optimizations from the web code +3. Creating a native Android app structure +4. Implementing the UI layouts and resources +5. Porting the core game logic from JavaScript to Kotlin +6. Adding Android-specific features and optimizations + +The core game mechanics are kept identical to the web version, ensuring a consistent experience across platforms. \ No newline at end of file diff --git a/android-app/app/build.gradle b/android-app/app/build.gradle new file mode 100644 index 0000000..dd59f99 --- /dev/null +++ b/android-app/app/build.gradle @@ -0,0 +1,50 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.tetris3d' + compileSdk 33 + + defaultConfig { + applicationId "com.tetris3d" + minSdk 26 + targetSdk 33 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + viewBinding true + } +} + +dependencies { + implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.9.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1' + implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0' + implementation 'androidx.navigation:navigation-ui-ktx:2.6.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} \ No newline at end of file diff --git a/android-app/app/src/main/AndroidManifest.xml b/android-app/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3f8982d --- /dev/null +++ b/android-app/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/android-app/app/src/main/java/com/tetris3d/MainActivity.kt b/android-app/app/src/main/java/com/tetris3d/MainActivity.kt new file mode 100644 index 0000000..634ac69 --- /dev/null +++ b/android-app/app/src/main/java/com/tetris3d/MainActivity.kt @@ -0,0 +1,254 @@ +package com.tetris3d + +import android.app.Dialog +import android.os.Bundle +import android.view.View +import android.widget.Button +import android.widget.NumberPicker +import android.widget.SeekBar +import android.widget.Switch +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import com.tetris3d.game.GameOptions +import com.tetris3d.game.TetrisGame +import com.tetris3d.views.NextPieceView +import com.tetris3d.views.TetrisGameView + +class MainActivity : AppCompatActivity() { + private lateinit var gameView: TetrisGameView + private lateinit var nextPieceView: NextPieceView + private lateinit var scoreText: TextView + private lateinit var linesText: TextView + private lateinit var levelText: TextView + private lateinit var startButton: Button + private lateinit var pauseButton: Button + private lateinit var optionsButton: Button + + private lateinit var tetrisGame: TetrisGame + private lateinit var gameOptions: GameOptions + + private var gameOverDialog: Dialog? = null + private var optionsDialog: Dialog? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + initViews() + initGameOptions() + initGame() + setupButtonListeners() + } + + private fun initViews() { + gameView = findViewById(R.id.tetrisGameView) + nextPieceView = findViewById(R.id.nextPieceView) + scoreText = findViewById(R.id.scoreText) + linesText = findViewById(R.id.linesText) + levelText = findViewById(R.id.levelText) + startButton = findViewById(R.id.startButton) + pauseButton = findViewById(R.id.pauseButton) + optionsButton = findViewById(R.id.optionsButton) + } + + private fun initGameOptions() { + gameOptions = GameOptions( + enable3DEffects = true, + enableSpinAnimations = true, + animationSpeed = 0.05f, + startingLevel = 1 + ) + + // Load saved options from SharedPreferences + val prefs = getSharedPreferences("TetrisOptions", MODE_PRIVATE) + gameOptions.enable3DEffects = prefs.getBoolean("enable3DEffects", true) + gameOptions.enableSpinAnimations = prefs.getBoolean("enableSpinAnimations", true) + gameOptions.animationSpeed = prefs.getFloat("animationSpeed", 0.05f) + gameOptions.startingLevel = prefs.getInt("startingLevel", 1) + } + + private fun initGame() { + tetrisGame = TetrisGame(gameOptions) + gameView.setGame(tetrisGame) + nextPieceView.setGame(tetrisGame) + + tetrisGame.setGameStateListener(object : TetrisGame.GameStateListener { + override fun onScoreChanged(score: Int) { + runOnUiThread { + scoreText.text = score.toString() + } + } + + override fun onLinesChanged(lines: Int) { + runOnUiThread { + linesText.text = lines.toString() + } + } + + override fun onLevelChanged(level: Int) { + runOnUiThread { + levelText.text = level.toString() + } + } + + override fun onGameOver(finalScore: Int) { + runOnUiThread { + showGameOverDialog(finalScore) + } + } + + override fun onNextPieceChanged() { + runOnUiThread { + nextPieceView.invalidate() + } + } + }) + + // Start a new game automatically + tetrisGame.startNewGame() + updateControls() + } + + private fun setupButtonListeners() { + startButton.setOnClickListener { + if (tetrisGame.isGameOver) { + tetrisGame.startNewGame() + } else { + tetrisGame.start() + } + updateControls() + } + + pauseButton.setOnClickListener { + if (tetrisGame.isRunning) { + tetrisGame.pause() + } else { + tetrisGame.resume() + } + updateControls() + } + + optionsButton.setOnClickListener { + showOptionsDialog() + } + } + + private fun updateControls() { + if (tetrisGame.isRunning) { + startButton.visibility = View.GONE + pauseButton.text = getString(R.string.pause) + } else if (tetrisGame.isGameOver) { + startButton.visibility = View.VISIBLE + startButton.text = getString(R.string.start) + pauseButton.text = getString(R.string.pause) + } else { + startButton.visibility = View.GONE + pauseButton.text = getString(R.string.start) + } + } + + private fun showGameOverDialog(finalScore: Int) { + if (gameOverDialog != null && gameOverDialog!!.isShowing) { + gameOverDialog!!.dismiss() + } + + val view = layoutInflater.inflate(R.layout.dialog_game_over, null) + val scoreText = view.findViewById(R.id.textFinalScore) + val playAgainButton = view.findViewById + diff --git a/script.js b/script.js index 800b148..d44a24a 100644 --- a/script.js +++ b/script.js @@ -2072,68 +2072,17 @@ function handleResize() { const gameWrapper = document.querySelector('.game-wrapper'); const scoreContainer = document.querySelector('.score-container'); - if (isMobile || forceMobileControls) { - // Scale the canvas to fit mobile screen - const viewportWidth = Math.min(window.innerWidth, document.documentElement.clientWidth); - const viewportHeight = Math.min(window.innerHeight, document.documentElement.clientHeight); - - // Detect orientation - const isPortrait = viewportHeight > viewportWidth; - - // Calculate available game area (accounting for UI elements) - const titleHeight = 40; // Estimate for title - const scoreWidth = isPortrait ? 120 : 100; // Width for score container in portrait/landscape - const availableWidth = viewportWidth - scoreWidth - 20; // Subtract score width + padding - const availableHeight = viewportHeight - titleHeight - 20; // Subtract title height + padding - - // Calculate optimal dimensions while maintaining aspect ratio - const gameRatio = ROWS / COLS; - - // Calculate scale based on available space - let targetWidth, targetHeight; - - if (isPortrait) { - // For portrait, prioritize fitting the width - targetWidth = availableWidth * 0.95; - targetHeight = targetWidth * gameRatio; - - // If too tall, scale down based on height - if (targetHeight > availableHeight * 0.95) { - targetHeight = availableHeight * 0.95; - targetWidth = targetHeight / gameRatio; - } - } else { - // For landscape, prioritize fitting the height - targetHeight = availableHeight * 0.95; - targetWidth = targetHeight / gameRatio; - - // If too wide, scale down based on width - if (targetWidth > availableWidth * 0.95) { - targetWidth = availableWidth * 0.95; - targetHeight = targetWidth * gameRatio; - } + // Make sure canvas maintains its dimensions + canvas.width = COLS * BLOCK_SIZE; + canvas.height = ROWS * BLOCK_SIZE; + + // Redraw the board and current piece + drawBoard(); + if (p) { + p.draw(); + if (showShadow) { + p.drawShadow(); } - - // Apply dimensions - canvas.style.width = `${targetWidth}px`; - canvas.style.height = `${targetHeight}px`; - - // Force a redraw of the nextPiece preview to fix rendering issues - if (nextPiece) { - setTimeout(() => { - nextPieceCtx.clearRect(0, 0, nextPieceCanvas.width, nextPieceCanvas.height); - nextPiece.drawNextPiece(); - }, 100); - } - - // Show touch controls mode - document.body.classList.add('mobile-mode'); - } else { - // Reset to desktop layout - canvas.style.width = ''; - canvas.style.height = ''; - - document.body.classList.remove('mobile-mode'); } } diff --git a/style.css b/style.css index 96b4f96..bb84ba2 100644 --- a/style.css +++ b/style.css @@ -404,16 +404,11 @@ canvas#tetris { max-width: 200px; } +/* Mobile-only class should be hidden always since we're removing mobile support */ .mobile-only { display: none; } -@media (max-width: 768px) { - .mobile-only { - display: flex; - } -} - /* Toggle switch styles */ .switch { position: relative; @@ -504,14 +499,7 @@ input[type=range]::-moz-range-thumb { box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); } -/* Mobile-specific styles */ -.mobile-mode { - overflow: hidden; - overscroll-behavior: none; - touch-action: none; -} - -/* 3D Rotation buttons */ +/* 3D Rotation buttons - keep these for desktop */ .rotate-buttons { position: fixed; bottom: 20px; @@ -541,257 +529,10 @@ input[type=range]::-moz-range-thumb { background: linear-gradient(45deg, #0062cc, #00b3ff); } -/* Performance toggle */ -.perf-toggle { - display: flex; - align-items: center; - margin-top: 5px; - background: rgba(0, 0, 0, 0.3); - padding: 5px; - border-radius: 4px; - font-size: 11px; -} - -.perf-label { - margin-left: 5px; - color: #00ffff; - cursor: pointer; -} - -#toggle-mobile-performance { - cursor: pointer; -} - -/* Mobile mode adjustments for rotation buttons */ -.mobile-mode .rotate-buttons { - bottom: 15px; - right: 15px; - gap: 8px; -} - -.mobile-mode .rotate-btn { - padding: 10px 12px; - font-size: 12px; -} - -/* Mobile landscape orientation */ -@media (orientation: landscape) { - .mobile-mode .rotate-buttons { - flex-direction: row; - bottom: 10px; - right: 10px; - } -} - -/* Smaller devices */ -@media (max-width: 400px) { - .mobile-mode .rotate-btn { - padding: 8px 10px; - font-size: 11px; - } -} - -.mobile-mode .game-container { - flex-direction: row; - align-items: flex-start; - gap: 5px; - padding: 10px; - max-width: 100vw; - box-sizing: border-box; - margin-top: 40px; /* Reduced top margin */ -} - -.mobile-mode .game-title { - font-size: 22px; - top: 5px; - text-shadow: 0 0 8px rgba(255, 0, 255, 0.7); -} - -.mobile-mode .game-wrapper { - display: flex; - flex-direction: column; - align-items: center; - margin: 0; - order: 2; /* Move game board to the right */ - flex-grow: 1; -} - -.mobile-mode .score-container { - width: auto; - min-width: 100px; - max-width: 120px; - display: flex; - flex-direction: column; - align-items: center; - gap: 5px; - padding: 8px; - background: rgba(0, 0, 0, 0.7); - order: 1; /* Move score container to the left */ - margin-right: 5px; - border-radius: 8px; - height: auto; - align-self: stretch; -} - -.mobile-mode .score-container p { - margin: 3px 0; - font-size: 10px; -} - -.mobile-mode .game-btn { - margin: 3px; - padding: 6px 8px; - font-size: 9px; - width: 95%; -} - -.mobile-mode .controls-info { - display: none; -} - -.mobile-mode #next-piece-preview { - position: relative; - top: auto; - bottom: auto; - left: auto; - right: auto; - transform: none; - margin: 0 0 10px 0; - background: rgba(0, 0, 0, 0.7); - z-index: 10; - border-color: rgba(0, 255, 255, 0.5); - width: 90%; /* Make it fit inside the score container */ - box-sizing: border-box; -} - -.mobile-mode #next-piece-preview h3 { - font-size: 12px; - margin-bottom: 5px; -} - -.mobile-mode canvas#tetris { - max-width: 100%; - height: auto; - border-radius: 8px; -} - -/* Portrait vs landscape adjustments */ -@media (orientation: portrait) { - .mobile-mode .game-container { - padding-top: 35px; - height: calc(100vh - 40px); - } - - .mobile-mode .score-container { - height: calc(100vh - 80px); - justify-content: flex-start; - } - - .mobile-mode canvas#tetris { - height: calc(100vh - 80px); - width: auto; - object-fit: contain; - } -} - -@media (orientation: landscape) { - .mobile-mode .game-container { - margin-top: 35px; - padding: 5px; - height: calc(100vh - 40px); - } - - .mobile-mode .score-container { - height: calc(100vh - 50px); - max-width: 100px; - } - - .mobile-mode canvas#tetris { - height: calc(100vh - 50px); - width: auto; - object-fit: contain; - } -} - -/* Smaller devices */ -@media (max-width: 400px) { - .mobile-mode .game-title { - font-size: 18px; - } - - .mobile-mode .score-container p { - font-size: 9px; - } - - .mobile-mode .score-container { - min-width: 85px; - max-width: 90px; - } - - .mobile-mode .game-btn { - font-size: 8px; - padding: 5px 6px; - } -} - -/* Tablet optimization */ -@media (min-width: 768px) and (max-width: 1024px) { - .mobile-mode .game-container { - flex-direction: row; - flex-wrap: wrap; - gap: 20px; - } - - .mobile-mode .game-title { - font-size: 28px; - } -} - -/* Touch instructions */ -.touch-instructions { - display: none; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: rgba(0, 0, 0, 0.9); - border: 2px solid rgba(0, 255, 255, 0.7); - border-radius: 10px; - padding: 15px; - z-index: 1000; - box-shadow: 0 0 20px rgba(0, 255, 255, 0.5); - transition: opacity 1s ease; - text-align: center; - max-width: 90vw; -} - -.touch-instructions h3 { - color: #00ffff; - text-shadow: 0 0 5px #00ffff, 0 0 10px #00ffff; - margin-bottom: 15px; - font-size: 16px; -} - -.mobile-mode .touch-instructions { - display: block; -} - -.touch-instructions p { - margin: 10px 0; - font-size: 13px; - color: #fff; - text-align: left; -} - -.touch-instructions p b { - color: #ff9900; - text-shadow: 0 0 5px rgba(255, 153, 0, 0.5); -} - -.touch-instructions.fade-out { - opacity: 0; -} +/* Performance toggle - Since we're removing mobile support */ +/* Removing mobile-specific media queries and styles */ +/* Removing touch-instructions class and related styles */ .instructions-btn { margin-top: 15px; background: linear-gradient(45deg, #ff9900, #ff5500);