diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c28d47b..d8da12c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,8 @@ + + () - // Base path for attack textures (updated for new folder structure) - private val attackTexturesPath = "battle_sprites/extracted_atksprites" + // Get the external storage directory for attack sprites + private fun getAttackTexturesPath(): String { + return "VBHelper/battle_sprites/extracted_atksprites" + } fun getAttackSprite(characterId: String, isLarge: Boolean = false): Bitmap? { println("AttackSpriteManager: Getting attack sprite for characterId=$characterId, isLarge=$isLarge") @@ -55,9 +58,10 @@ class AttackSpriteManager(private val context: Context) { return null } - // Load the attack sprite - val attackFilePath = "$attackTexturesPath/$attackFileName.png" - val attackFile = File(context.filesDir, attackFilePath) + // Load the attack sprite from external storage + val externalDir = Environment.getExternalStorageDirectory() + val attackFilePath = "${getAttackTexturesPath()}/$attackFileName.png" + val attackFile = File(externalDir, attackFilePath) println("AttackSpriteManager: Attack file path = ${attackFile.absolutePath}") println("AttackSpriteManager: Attack file exists = ${attackFile.exists()}") @@ -85,8 +89,9 @@ class AttackSpriteManager(private val context: Context) { } try { - // Load character data from JSON file - val characterDataFile = File(context.filesDir, "battle_sprites/extracted_digimon_stats/character_data/CharacterData.json") + // Load character data from JSON file in external storage + val externalDir = Environment.getExternalStorageDirectory() + val characterDataFile = File(externalDir, "VBHelper/battle_sprites/extracted_digimon_stats/character_data/CharacterData.json") println("AttackSpriteManager: Character data file path = ${characterDataFile.absolutePath}") println("AttackSpriteManager: Character data file exists = ${characterDataFile.exists()}") diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/BattleSpriteManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/BattleSpriteManager.kt index 316d486..f7fd5c1 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/BattleSpriteManager.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/BattleSpriteManager.kt @@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.battle import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.os.Environment import com.google.gson.Gson import java.io.File @@ -37,8 +38,11 @@ class BattleSpriteManager(private val context: Context) { private val gson = Gson() private val spriteCache = mutableMapOf() - // Base directory where your sprites are stored - private val spriteBaseDir = File(context.filesDir, "battle_sprites/extracted_assets") + // Get the external storage directory for sprite files + private fun getSpriteBaseDir(): File { + val externalDir = Environment.getExternalStorageDirectory() + return File(externalDir, "VBHelper/battle_sprites/extracted_assets") + } fun loadSprite(spriteName: String, atlasName: String): Bitmap? { val cacheKey = "${spriteName}_${atlasName}" @@ -49,6 +53,7 @@ class BattleSpriteManager(private val context: Context) { } // Debug: Check if base directory exists + val spriteBaseDir = getSpriteBaseDir() if (!spriteBaseDir.exists()) { println("Sprite base directory does not exist: ${spriteBaseDir.absolutePath}") return null @@ -130,7 +135,7 @@ class BattleSpriteManager(private val context: Context) { // Helper method to get available sprites for an atlas fun getAvailableSprites(atlasName: String): List { try { - val spritesDir = File(spriteBaseDir, "sprites") + val spritesDir = File(getSpriteBaseDir(), "sprites") if (!spritesDir.exists()) { return emptyList() } @@ -154,7 +159,7 @@ class BattleSpriteManager(private val context: Context) { // Helper method to get available atlases fun getAvailableAtlases(): List { try { - val texturesDir = File(spriteBaseDir, "extracted_textures") + val texturesDir = File(getSpriteBaseDir(), "extracted_textures") if (!texturesDir.exists()) { return emptyList() } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/HitEffectSpriteManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/HitEffectSpriteManager.kt index 33d2189..6536040 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/HitEffectSpriteManager.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/HitEffectSpriteManager.kt @@ -4,13 +4,17 @@ import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Rect +import android.os.Environment import java.io.File class HitEffectSpriteManager(private val context: Context) { private val spriteCache = mutableMapOf() - // Base directory where hit effect sprites are stored - private val hitSpritesDir = File(context.filesDir, "battle_sprites/extracted_hit_sprites") + // Get the external storage directory for hit effect sprites + private fun getHitSpritesDir(): File { + val externalDir = Environment.getExternalStorageDirectory() + return File(externalDir, "VBHelper/battle_sprites/extracted_hit_sprites") + } /** * Load a hit sprite (hit_01.png, hit_02.png, hit_02_white.png) @@ -26,6 +30,7 @@ class HitEffectSpriteManager(private val context: Context) { } try { + val hitSpritesDir = getHitSpritesDir() val spriteFile = File(hitSpritesDir, "$spriteName.png") if (!spriteFile.exists()) { @@ -68,7 +73,7 @@ class HitEffectSpriteManager(private val context: Context) { } try { - val spritesheetFile = File(hitSpritesDir, "$spritesheetName.png") + val spritesheetFile = File(getHitSpritesDir(), "$spritesheetName.png") if (!spritesheetFile.exists()) { println("Damage effect spritesheet not found: ${spritesheetFile.absolutePath}") @@ -118,28 +123,21 @@ class HitEffectSpriteManager(private val context: Context) { } /** - * Get available hit sprite names - * @return List of available hit sprite names + * Get all available hit sprites + * @return List of hit sprite names (without .png extension) */ fun getAvailableHitSprites(): List { - try { - if (!hitSpritesDir.exists()) { - return emptyList() - } - - val hitFiles = hitSpritesDir.listFiles { file -> - file.name.startsWith("hit_") && file.name.endsWith(".png") - } ?: emptyArray() - - return hitFiles.map { file -> - file.name.substringBefore(".png") - }.sorted() - - } catch (e: Exception) { - println("Error getting available hit sprites: ${e.message}") - e.printStackTrace() + val hitSpritesDir = getHitSpritesDir() + + if (!hitSpritesDir.exists()) { return emptyList() } + + return hitSpritesDir.listFiles { file -> + file.name.startsWith("hit_") && file.name.endsWith(".png") + }?.map { file -> + file.name.substringBefore(".png") + }?.sorted() ?: emptyList() } /** @@ -148,11 +146,11 @@ class HitEffectSpriteManager(private val context: Context) { */ fun getAvailableDamageEffectSpritesheets(): List { try { - if (!hitSpritesDir.exists()) { + if (!getHitSpritesDir().exists()) { return emptyList() } - val dmgFiles = hitSpritesDir.listFiles { file -> + val dmgFiles = getHitSpritesDir().listFiles { file -> file.name.startsWith("dmg_ef") && file.name.endsWith(".png") } ?: emptyArray() diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/IndividualSpriteManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/IndividualSpriteManager.kt index f4b0d25..f6b9179 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/IndividualSpriteManager.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/IndividualSpriteManager.kt @@ -3,13 +3,17 @@ package com.github.nacabaro.vbhelper.battle import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.os.Environment import java.io.File class IndividualSpriteManager(private val context: Context) { private val spriteCache = mutableMapOf() - // Base directory where individual sprite PNGs are stored - private val spriteBaseDir = File(context.filesDir, "battle_sprites/extracted_assets/sprites") + // Get the external storage directory for sprite files + private fun getSpriteBaseDir(): File { + val externalDir = Environment.getExternalStorageDirectory() + return File(externalDir, "VBHelper/battle_sprites/extracted_assets/sprites") + } /** * Load a specific sprite frame for a character @@ -26,6 +30,7 @@ class IndividualSpriteManager(private val context: Context) { } // Debug: Check if base directory exists + val spriteBaseDir = getSpriteBaseDir() if (!spriteBaseDir.exists()) { println("Sprite base directory does not exist: ${spriteBaseDir.absolutePath}") return null @@ -68,51 +73,38 @@ class IndividualSpriteManager(private val context: Context) { * @return List of frame numbers (1-12) that exist for this character */ fun getAvailableFrames(characterId: String): List { - try { - val characterDir = File(spriteBaseDir, characterId) - if (!characterDir.exists()) { - println("Character directory not found: ${characterDir.absolutePath}") - return emptyList() - } - - val spriteFiles = characterDir.listFiles { file -> - file.name.startsWith("${characterId}_") && file.name.endsWith(".png") - } ?: emptyArray() - - return spriteFiles.mapNotNull { file -> - // Extract frame number from filename (e.g., "dim012_mon03_01.png" -> 1) - val frameNumberStr = file.name.substringAfter("_").substringBefore(".png") - frameNumberStr.toIntOrNull() - }.sorted() - - } catch (e: Exception) { - println("Error getting available frames: ${e.message}") - e.printStackTrace() + val spriteBaseDir = getSpriteBaseDir() + val characterDir = File(spriteBaseDir, characterId) + + if (!characterDir.exists()) { return emptyList() } + + val spriteFiles = characterDir.listFiles { file -> + file.name.startsWith("${characterId}_") && file.name.endsWith(".png") + } ?: emptyArray() + + return spriteFiles.mapNotNull { file -> + val fileName = file.name + val frameMatch = Regex("${characterId}_(\\d{2})\\.png").find(fileName) + frameMatch?.groupValues?.get(1)?.toIntOrNull() + }.sorted() } /** - * Get all available characters + * Get all available character IDs * @return List of character IDs that have sprite directories */ fun getAvailableCharacters(): List { - try { - if (!spriteBaseDir.exists()) { - return emptyList() - } - - val characterDirs = spriteBaseDir.listFiles { file -> - file.isDirectory && file.name.matches(Regex("dim\\d+_mon\\d+.*")) - } ?: emptyArray() - - return characterDirs.map { it.name }.sorted() - - } catch (e: Exception) { - println("Error getting available characters: ${e.message}") - e.printStackTrace() + val spriteBaseDir = getSpriteBaseDir() + + if (!spriteBaseDir.exists()) { return emptyList() } + + return spriteBaseDir.listFiles { file -> + file.isDirectory && file.listFiles()?.any { it.name.endsWith(".png") } == true + }?.map { it.name }?.sorted() ?: emptyList() } /** @@ -128,7 +120,7 @@ class IndividualSpriteManager(private val context: Context) { * @return true if the character has sprite files, false otherwise */ fun hasCharacterSprites(characterId: String): Boolean { - val characterDir = File(spriteBaseDir, characterId) + val characterDir = File(getSpriteBaseDir(), characterId) if (!characterDir.exists()) { return false } diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/SpriteFileManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/SpriteFileManager.kt index 5dd3699..2f663cb 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/SpriteFileManager.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/SpriteFileManager.kt @@ -1,15 +1,22 @@ package com.github.nacabaro.vbhelper.battle import android.content.Context +import android.os.Environment import java.io.File import java.io.FileOutputStream import java.io.IOException class SpriteFileManager(private val context: Context) { - fun copySpriteFilesToInternalStorage() { + // Get the external storage directory for sprite files + private fun getSpriteBaseDir(): File { + val externalDir = Environment.getExternalStorageDirectory() + return File(externalDir, "VBHelper/battle_sprites") + } + + fun copySpriteFilesToExternalStorage() { try { - println("Starting sprite file copy process...") + println("Starting sprite file copy process to external storage...") // Debug: List what's in the assets directory val assetManager = context.assets @@ -47,17 +54,17 @@ class SpriteFileManager(private val context: Context) { } } - // Create the base directory for battle_sprites - val battleSpritesDir = File(context.filesDir, "battle_sprites") + // Create the base directory for battle_sprites in external storage + val battleSpritesDir = getSpriteBaseDir() if (!battleSpritesDir.exists()) { battleSpritesDir.mkdirs() - println("Created battle_sprites directory: ${battleSpritesDir.absolutePath}") + println("Created battle_sprites directory in external storage: ${battleSpritesDir.absolutePath}") } else { - println("battle_sprites directory already exists: ${battleSpritesDir.absolutePath}") + println("battle_sprites directory already exists in external storage: ${battleSpritesDir.absolutePath}") } - // Copy all subdirectories from battle_sprites assets to internal storage - println("Copying all battle_sprites subdirectories...") + // Copy all subdirectories from battle_sprites assets to external storage + println("Copying all battle_sprites subdirectories to external storage...") battleSpritesFiles?.forEach { subdir -> val sourcePath = "battle_sprites/$subdir" val targetDir = File(battleSpritesDir, subdir) @@ -65,7 +72,7 @@ class SpriteFileManager(private val context: Context) { copyAssetDirectory(sourcePath, targetDir) } - println("Sprite files copied successfully to: ${battleSpritesDir.absolutePath}") + println("Sprite files copied successfully to external storage: ${battleSpritesDir.absolutePath}") // Verify that attack sprites were copied val atkspritesDir = File(battleSpritesDir, "extracted_atksprites") @@ -95,7 +102,7 @@ class SpriteFileManager(private val context: Context) { } } catch (e: Exception) { - println("Error copying sprite files: ${e.message}") + println("Error copying sprite files to external storage: ${e.message}") e.printStackTrace() } } @@ -180,7 +187,7 @@ class SpriteFileManager(private val context: Context) { } fun checkSpriteFilesExist(): Boolean { - val battleSpritesDir = File(context.filesDir, "battle_sprites") + val battleSpritesDir = getSpriteBaseDir() val extractedAssetsDir = File(battleSpritesDir, "extracted_assets") val extractedStatsDir = File(battleSpritesDir, "extracted_digimon_stats") val atkspritesDir = File(battleSpritesDir, "extracted_atksprites") @@ -204,7 +211,7 @@ class SpriteFileManager(private val context: Context) { fun clearSpriteFiles() { try { - val battleSpritesDir = File(context.filesDir, "battle_sprites") + val battleSpritesDir = getSpriteBaseDir() if (battleSpritesDir.exists()) { deleteDirectory(battleSpritesDir) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/screens/BattlesScreen.kt b/app/src/main/java/com/github/nacabaro/vbhelper/screens/BattlesScreen.kt index ab82952..c9e2f79 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/screens/BattlesScreen.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/screens/BattlesScreen.kt @@ -66,9 +66,72 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.foundation.Image import androidx.compose.ui.graphics.asImageBitmap import android.graphics.BitmapFactory +import android.os.Environment import java.io.File import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.foundation.layout.width + +@Composable +fun isLandscapeMode(): Boolean { + val configuration = LocalConfiguration.current + return configuration.screenWidthDp > configuration.screenHeightDp +} + +@Composable +fun getLandscapeModifier(): Modifier { + val configuration = LocalConfiguration.current + val isLandscape = configuration.screenWidthDp > configuration.screenHeightDp + return if (isLandscape) { + Modifier.width(200.dp).height(8.dp) + } else { + Modifier.fillMaxWidth().height(10.dp) + } +} + +@Composable +fun getLandscapeAlignment(): Alignment { + val configuration = LocalConfiguration.current + val isLandscape = configuration.screenWidthDp > configuration.screenHeightDp + return if (isLandscape) Alignment.Center else Alignment.TopStart +} + +@Composable +fun getLandscapeHorizontalAlignment(): Alignment.Horizontal { + val configuration = LocalConfiguration.current + val isLandscape = configuration.screenWidthDp > configuration.screenHeightDp + return if (isLandscape) Alignment.CenterHorizontally else Alignment.Start +} + +@Composable +fun getLandscapeFontSize(): androidx.compose.ui.unit.TextUnit { + val configuration = LocalConfiguration.current + val isLandscape = configuration.screenWidthDp > configuration.screenHeightDp + return if (isLandscape) 14.sp else 16.sp +} + +@Composable +fun getLandscapeBoxModifier(): Modifier { + val configuration = LocalConfiguration.current + val isLandscape = configuration.screenWidthDp > configuration.screenHeightDp + return if (isLandscape) { + Modifier + .width(220.dp) // Slightly wider than the progress bar to accommodate padding + .background( + color = Color.Gray.copy(alpha = 0.6f), + shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp) + ) + .padding(8.dp) + } else { + Modifier + .fillMaxWidth() + .background( + color = Color.Gray.copy(alpha = 0.6f), + shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp) + ) + .padding(8.dp) + } +} @Composable fun AnimatedDamageNumber( @@ -266,13 +329,13 @@ fun BattleScreen( delay(16) // 60 FPS } println("Phase 2 completed, applying damage and starting Phase 3") - battleSystem.completeAttackAnimation(opponentDamage = pendingOpponentDamage) + battleSystem.completeAttackAnimation(opponentDamage = pendingOpponentDamage) // Hide damage number and reset pending damage after animation if (showOpponentDamageNumber) { delay(800) // Wait for damage number animation (scale up + hold + fade out) showOpponentDamageNumber = false - pendingOpponentDamage = 0f + pendingOpponentDamage = 0f println("DEBUG: Hiding opponent damage number and resetting pending damage") } @@ -344,18 +407,18 @@ fun BattleScreen( } println("Phase 3 completed, applying damage and resetting") println("DEBUG: pendingPlayerDamage = $pendingPlayerDamage") - battleSystem.completeAttackAnimation(playerDamage = pendingPlayerDamage) + battleSystem.completeAttackAnimation(playerDamage = pendingPlayerDamage) // Hide damage number and reset pending damage after animation if (showPlayerDamageNumber) { delay(800) // Wait for damage number animation (scale up + hold + fade out) showPlayerDamageNumber = false - pendingPlayerDamage = 0f + pendingPlayerDamage = 0f println("DEBUG: Hiding player damage number and resetting pending damage") } - battleSystem.resetAttackState() - battleSystem.enableAttackButton() + battleSystem.resetAttackState() + battleSystem.enableAttackButton() // Check if battle is over if (battleSystem.checkBattleOver()) { @@ -733,21 +796,16 @@ fun MiddleBattleView( // Enemy HP bar and text with background box Box( - modifier = Modifier - .fillMaxWidth() - .background( - color = Color.Gray.copy(alpha = 0.6f), - shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp) - ) - .padding(8.dp) + modifier = getLandscapeBoxModifier(), + contentAlignment = getLandscapeAlignment() ) { - Column { + Column( + horizontalAlignment = getLandscapeHorizontalAlignment() + ) { // Enemy HP bar (top) LinearProgressIndicator( progress = battleSystem.opponentHP / (opponentCharacter?.baseHp?.toFloat() ?: 100f), - modifier = Modifier - .fillMaxWidth() - .height(10.dp), + modifier = getLandscapeModifier(), color = Color.Red, trackColor = Color.Gray ) @@ -757,7 +815,7 @@ fun MiddleBattleView( // Enemy HP display numbers Text( text = "Enemy HP: ${battleSystem.opponentHP.toInt()}/${opponentCharacter?.baseHp ?: 100}", - fontSize = 16.sp, + fontSize = getLandscapeFontSize(), color = Color.White, style = TextStyle( shadow = Shadow( @@ -962,9 +1020,7 @@ fun MiddleBattleView( // Critical bar LinearProgressIndicator( progress = battleSystem.critBarProgress / 100f, - modifier = Modifier - .fillMaxWidth() - .height(10.dp), + modifier = getLandscapeModifier(), color = Color.Yellow, trackColor = Color.Gray ) @@ -973,21 +1029,16 @@ fun MiddleBattleView( // Player HP bar and text with background box Box( - modifier = Modifier - .fillMaxWidth() - .background( - color = Color.Gray.copy(alpha = 0.6f), - shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp) - ) - .padding(8.dp) + modifier = getLandscapeBoxModifier(), + contentAlignment = getLandscapeAlignment() ) { - Column { + Column( + horizontalAlignment = getLandscapeHorizontalAlignment() + ) { // Player HP bar LinearProgressIndicator( progress = battleSystem.playerHP / (activeCharacter?.baseHp?.toFloat() ?: 100f), - modifier = Modifier - .fillMaxWidth() - .height(10.dp), + modifier = getLandscapeModifier(), color = Color.Green, trackColor = Color.Gray ) @@ -997,7 +1048,7 @@ fun MiddleBattleView( // Player HP display numbers Text( text = "HP: ${battleSystem.playerHP.toInt()}/${activeCharacter?.baseHp ?: 100}", - fontSize = 16.sp, + fontSize = getLandscapeFontSize(), color = Color.White, style = TextStyle( shadow = Shadow( @@ -1193,31 +1244,26 @@ fun PlayerBattleView( // Health bar and text with background box Box( - modifier = Modifier - .fillMaxWidth() - .background( - color = Color.Gray.copy(alpha = 0.6f), - shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp) - ) - .padding(8.dp) + modifier = getLandscapeBoxModifier(), + contentAlignment = getLandscapeAlignment() ) { - Column { - // Health bar - LinearProgressIndicator( - progress = battleSystem.playerHP / (activeCharacter?.baseHp?.toFloat() ?: 100f), - modifier = Modifier - .fillMaxWidth() - .height(10.dp), - color = Color.Green, - trackColor = Color.Gray - ) + Column( + horizontalAlignment = getLandscapeHorizontalAlignment() + ) { + // Health bar + LinearProgressIndicator( + progress = battleSystem.playerHP / (activeCharacter?.baseHp?.toFloat() ?: 100f), + modifier = getLandscapeModifier(), + color = Color.Green, + trackColor = Color.Gray + ) Spacer(modifier = Modifier.height(4.dp)) - // Health display numbers - Text( - text = "HP: ${battleSystem.playerHP.toInt()}/${activeCharacter?.baseHp ?: 100}", - fontSize = 16.sp, + // Health display numbers + Text( + text = "HP: ${battleSystem.playerHP.toInt()}/${activeCharacter?.baseHp ?: 100}", + fontSize = getLandscapeFontSize(), color = Color.White, style = TextStyle( shadow = Shadow( @@ -1363,9 +1409,7 @@ fun PlayerBattleView( // Critical bar LinearProgressIndicator( progress = battleSystem.critBarProgress / 100f, - modifier = Modifier - .fillMaxWidth() - .height(10.dp), + modifier = getLandscapeModifier(), color = Color.Yellow, trackColor = Color.Gray ) @@ -1422,22 +1466,22 @@ fun PlayerBattleView( // Match is still ongoing - update HP and continue println("Round ${apiResult.currentRound}: Player HP=${apiResult.playerHP}, Opponent HP=${apiResult.opponentHP}") - // Set pending damage based on API result + // Set pending damage based on API result if (apiResult.playerAttackDamage > 0) { - // Player attack hit - enemy takes damage at end of player animation - println("Player attack hit! Enemy will take ${apiResult.playerAttackDamage} damage") - onSetPendingDamage(0f, apiResult.playerAttackDamage.toFloat()) // Opponent takes damage - battleSystem.setAttackHitState(true) + // Player attack hit - enemy takes damage at end of player animation + println("Player attack hit! Enemy will take ${apiResult.playerAttackDamage} damage") + onSetPendingDamage(0f, apiResult.playerAttackDamage.toFloat()) // Opponent takes damage + battleSystem.setAttackHitState(true) // Also check if enemy counter-attacks and hits if (apiResult.opponentAttackDamage > 0) { println("Enemy counter-attack hits! Player takes ${apiResult.opponentAttackDamage} damage") onSetPendingDamage(apiResult.opponentAttackDamage.toFloat(), apiResult.playerAttackDamage.toFloat()) // Both take damage } - } else { + } else { // Player attack missed - enemy counter-attacks println("Player attack missed! Enemy counter-attacks") - battleSystem.setAttackHitState(false) + battleSystem.setAttackHitState(false) // Set up counter-attack - determine if it hits based on API result val counterAttackHits = apiResult.opponentAttackDamage > 0 println("Setting up counter-attack: counterAttackHits=$counterAttackHits, opponentAttackDamage=${apiResult.opponentAttackDamage}") @@ -1458,7 +1502,7 @@ fun PlayerBattleView( battleSystem.setupCounterAttack(finalCounterAttackHits) // Set the opponent attack hit state for Phase 3 battleSystem.handleOpponentAttackResult(finalCounterAttackHits) - } + } } 2 -> { // Match is over - transition to results screen @@ -1514,28 +1558,23 @@ fun EnemyBattleView( MultiLayerAnimatedBattleBackground(modifier = Modifier.fillMaxSize()) // Top section: Enemy HP bar and HP numbers - Column( + Column( modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { // Enemy HP bar and text with background box Box( - modifier = Modifier - .fillMaxWidth() - .background( - color = Color.Gray.copy(alpha = 0.6f), - shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp) - ) - .padding(8.dp) + modifier = getLandscapeBoxModifier(), + contentAlignment = getLandscapeAlignment() ) { - Column { + Column( + horizontalAlignment = getLandscapeHorizontalAlignment() + ) { // Enemy HP bar LinearProgressIndicator( progress = battleSystem.opponentHP / (activeCharacter?.baseHp?.toFloat() ?: 100f), - modifier = Modifier - .fillMaxWidth() - .height(10.dp), + modifier = getLandscapeModifier(), color = Color.Red, trackColor = Color.Gray ) @@ -1545,7 +1584,7 @@ fun EnemyBattleView( // Enemy HP display numbers Text( text = "Enemy HP: ${battleSystem.opponentHP.toInt()}/${activeCharacter?.baseHp ?: 100}", - fontSize = 16.sp, + fontSize = getLandscapeFontSize(), color = Color.White, style = TextStyle( shadow = Shadow( @@ -1561,25 +1600,25 @@ fun EnemyBattleView( // Middle section: Enemy Digimon Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), + modifier = Modifier + .fillMaxSize() + .padding(16.dp), contentAlignment = Alignment.Center - ) { + ) { // Enemy Digimon - Box( - modifier = Modifier - .fillMaxWidth() - .size(80.dp), - contentAlignment = Alignment.CenterEnd - ) { - // Determine animation type based on battle state + Box( + modifier = Modifier + .fillMaxWidth() + .size(80.dp), + contentAlignment = Alignment.CenterEnd + ) { + // Determine animation type based on battle state val animationType = when { battleSystem.isOpponentDodging -> DigimonAnimationType.WALK // Use walk animation for dodge battleSystem.isOpponentHitDelayed -> DigimonAnimationType.SLEEP // Use sleep animation for hit effect (injured sprite) battleSystem.attackPhase == 2 -> DigimonAnimationType.IDLE // Player attack on enemy screen - else -> DigimonAnimationType.IDLE - } + else -> DigimonAnimationType.IDLE + } // Calculate vertical offset for dodge animation val verticalOffset = if (battleSystem.isOpponentDodging) { @@ -1608,65 +1647,65 @@ fun EnemyBattleView( } else { 0.dp } - - AnimatedSpriteImage( - characterId = activeCharacter?.charaId ?: "dim011_mon01", - animationType = animationType, + + AnimatedSpriteImage( + characterId = activeCharacter?.charaId ?: "dim011_mon01", + animationType = animationType, modifier = Modifier .size(80.dp) .offset( x = hitOffset, y = verticalOffset ), - contentScale = ContentScale.Fit, + contentScale = ContentScale.Fit, reloadMappings = false, animationOffset = 375L // Offset enemy animation by half the idle duration - ) - - // Attack sprite visibility and positioning based on attack phase - val shouldShowAttack = when (battleSystem.attackPhase) { + ) + + // Attack sprite visibility and positioning based on attack phase + val shouldShowAttack = when (battleSystem.attackPhase) { 2 -> true // Player attack on enemy screen - else -> false - } - - if (shouldShowAttack) { + else -> false + } + + if (shouldShowAttack) { val xOffset = (attackAnimationProgress * 400 - 350).dp // Player attack on enemy screen - start more to the left // Use player's character ID for player attack val characterId = playerCharacter?.charaId ?: "dim011_mon01" - - // Handle sprite transition - LaunchedEffect(characterId, battleSystem.attackPhase) { - if ((previousCharacterId != null && previousCharacterId != characterId) || - (previousAttackPhase != null && previousAttackPhase != battleSystem.attackPhase)) { - // Character ID or attack phase changed, start transition - isTransitioning = true - delay(100) // Brief invisibility period - isTransitioning = false - } - previousCharacterId = characterId - previousAttackPhase = battleSystem.attackPhase + + // Handle sprite transition + LaunchedEffect(characterId, battleSystem.attackPhase) { + if ((previousCharacterId != null && previousCharacterId != characterId) || + (previousAttackPhase != null && previousAttackPhase != battleSystem.attackPhase)) { + // Character ID or attack phase changed, start transition + isTransitioning = true + delay(100) // Brief invisibility period + isTransitioning = false } - + previousCharacterId = characterId + previousAttackPhase = battleSystem.attackPhase + } + println("EnemyBattleView - Attack sprite - Phase: ${battleSystem.attackPhase}, Progress: $attackAnimationProgress, X Offset: $xOffset, CurrentView: ${battleSystem.currentView}") - + if (!isTransitioning && !hideEnemyAttackSprite) { - AttackSpriteImage( - characterId = characterId, - isLarge = true, - modifier = Modifier - .size(60.dp) - .offset( - x = xOffset, - y = 0.dp - ) + AttackSpriteImage( + characterId = characterId, + isLarge = true, + modifier = Modifier + .size(60.dp) + .offset( + x = xOffset, + y = 0.dp + ) .scale(-1f, 1f), // Flip player attacks - contentScale = ContentScale.Fit - ) - } + contentScale = ContentScale.Fit + ) } } } + } } } @@ -1735,10 +1774,10 @@ fun BattlesScreen() { println("BATTLESCREEN: LaunchedEffect triggered - checking sprite files...") val spriteFileManager = SpriteFileManager(context) if (!spriteFileManager.checkSpriteFilesExist()) { - println("BATTLESCREEN: Copying sprite files to internal storage...") - spriteFileManager.copySpriteFilesToInternalStorage() + println("BATTLESCREEN: Copying sprite files to external storage...") + spriteFileManager.copySpriteFilesToExternalStorage() } else { - println("BATTLESCREEN: Sprite files already exist in internal storage") + println("BATTLESCREEN: Sprite files already exist in external storage") } } @@ -2048,9 +2087,9 @@ fun BattlesScreen() { topBar = { // Only show TopBanner when not in battle mode if (currentView != "battle-main" && currentView != "battle-results") { - TopBanner( - text = "Online battles" - ) + TopBanner( + text = "Online battles" + ) } } ) { contentPadding -> @@ -2077,6 +2116,7 @@ fun BattlesScreen() { championButton() ultimateButton() megaButton() + /* Button( onClick = { showSpriteTester = true @@ -2098,6 +2138,8 @@ fun BattlesScreen() { ) { Text("Clear Sprite Files") } + + */ } } } @@ -2387,10 +2429,11 @@ fun AnimatedBattleBackground( println("DEBUG: Screen dimensions = ${screenWidth.value}x${screenHeight.value}dp") } - // Load background image from internal storage + // Load background image from external storage LaunchedEffect(Unit) { try { - val backgroundFile = File(context.filesDir, "battle_sprites/extracted_battlebgs/BattleBg_0015_BattleBg_0012.png") + val externalDir = Environment.getExternalStorageDirectory() + val backgroundFile = File(externalDir, "VBHelper/battle_sprites/extracted_battlebgs/BattleBg_0015_BattleBg_0012.png") if (backgroundFile.exists()) { backgroundBitmap = BitmapFactory.decodeFile(backgroundFile.absolutePath) println("Successfully loaded battle background: ${backgroundFile.absolutePath}") @@ -2469,11 +2512,13 @@ fun MultiLayerAnimatedBattleBackground( println("DEBUG: Multi-layer screen dimensions = ${screenWidth.value}x${screenHeight.value}dp") } - // Load all three background layers from internal storage + // Load all three background layers from external storage LaunchedEffect(Unit) { try { + val externalDir = Environment.getExternalStorageDirectory() + // Back layer (BattleBg_0018_BattleBg_0013.png) - val backLayerFile = File(context.filesDir, "battle_sprites/extracted_battlebgs/BattleBg_0018_BattleBg_0013.png") + val backLayerFile = File(externalDir, "VBHelper/battle_sprites/extracted_battlebgs/BattleBg_0018_BattleBg_0013.png") if (backLayerFile.exists()) { backLayerBitmap = BitmapFactory.decodeFile(backLayerFile.absolutePath) println("Successfully loaded back layer background: ${backLayerFile.absolutePath}") @@ -2482,7 +2527,7 @@ fun MultiLayerAnimatedBattleBackground( } // Middle layer (BattleBg_0015_BattleBg_0012.png) - val middleLayerFile = File(context.filesDir, "battle_sprites/extracted_battlebgs/BattleBg_0015_BattleBg_0012.png") + val middleLayerFile = File(externalDir, "VBHelper/battle_sprites/extracted_battlebgs/BattleBg_0015_BattleBg_0012.png") if (middleLayerFile.exists()) { middleLayerBitmap = BitmapFactory.decodeFile(middleLayerFile.absolutePath) println("Successfully loaded middle layer background: ${middleLayerFile.absolutePath}") @@ -2491,7 +2536,7 @@ fun MultiLayerAnimatedBattleBackground( } // Front layer (BattleBg_0005_BattleBg_0011.png) - val frontLayerFile = File(context.filesDir, "battle_sprites/extracted_battlebgs/BattleBg_0005_BattleBg_0011.png") + val frontLayerFile = File(externalDir, "VBHelper/battle_sprites/extracted_battlebgs/BattleBg_0005_BattleBg_0011.png") if (frontLayerFile.exists()) { frontLayerBitmap = BitmapFactory.decodeFile(frontLayerFile.absolutePath) println("Successfully loaded front layer background: ${frontLayerFile.absolutePath}")