From 6c9d057917ea8d5fe4f45650c7201310d6538ee8 Mon Sep 17 00:00:00 2001 From: lightheel Date: Sat, 2 Aug 2025 07:49:43 -0400 Subject: [PATCH] Updated client for attack battle loop. --- .../vbhelper/battle/AttackSpriteManager.kt | 85 ++++++++++- .../vbhelper/battle/SpriteFileManager.kt | 33 ++-- .../vbhelper/components/AttackSpriteImage.kt | 2 + .../vbhelper/screens/BattlesScreen.kt | 143 ++++++++++++------ 4 files changed, 198 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/battle/AttackSpriteManager.kt b/app/src/main/java/com/github/nacabaro/vbhelper/battle/AttackSpriteManager.kt index 301c35f..6131c92 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/battle/AttackSpriteManager.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/battle/AttackSpriteManager.kt @@ -13,6 +13,20 @@ data class CharacterData( val laugeFileName: String ) +data class CharacterDataResponse( + val name: String, + val type: String, + val source_file: String, + val collection: String, + val unity_collection_id: String, + val relative_path: String, + val all_attributes: CharacterDataAttributes +) + +data class CharacterDataAttributes( + val DataList: List +) + class AttackSpriteManager(private val context: Context) { private val gson = Gson() private val characterDataCache = mutableMapOf() @@ -21,9 +35,11 @@ class AttackSpriteManager(private val context: Context) { private val attackTexturesPath = "Battle_Sprites_Reference/extracted_assets/atk_textures" fun getAttackSprite(characterId: String, isLarge: Boolean = false): Bitmap? { + println("AttackSpriteManager: Getting attack sprite for characterId=$characterId, isLarge=$isLarge") try { // Get character data val characterData = getCharacterData(characterId) ?: return null + println("AttackSpriteManager: Got character data: $characterData") // Determine which attack file to use val attackFileName = if (isLarge) { @@ -31,44 +47,99 @@ class AttackSpriteManager(private val context: Context) { } else { characterData.smalefilename } + println("AttackSpriteManager: Attack filename = $attackFileName") // Skip if no attack file - if (attackFileName == "0") return null + if (attackFileName == "0") { + println("AttackSpriteManager: Skipping attack file (filename is '0')") + return null + } // Load the attack sprite val attackFilePath = "$attackTexturesPath/$attackFileName.png" val attackFile = File(context.filesDir, attackFilePath) + println("AttackSpriteManager: Attack file path = ${attackFile.absolutePath}") + println("AttackSpriteManager: Attack file exists = ${attackFile.exists()}") return if (attackFile.exists()) { - BitmapFactory.decodeFile(attackFile.absolutePath) + val bitmap = BitmapFactory.decodeFile(attackFile.absolutePath) + println("AttackSpriteManager: Successfully loaded bitmap = ${bitmap != null}") + bitmap } else { + println("AttackSpriteManager: Attack file does not exist") null } } catch (e: Exception) { + println("AttackSpriteManager: Exception occurred: ${e.message}") e.printStackTrace() return null } } private fun getCharacterData(characterId: String): CharacterData? { + println("AttackSpriteManager: Getting character data for characterId=$characterId") // Check cache first if (characterDataCache.containsKey(characterId)) { + println("AttackSpriteManager: Found character data in cache") return characterDataCache[characterId] } try { // Load character data from JSON file val characterDataFile = File(context.filesDir, "Battle_Sprites_Reference/extracted_digimon_stats/character_data/CharacterData.json") + println("AttackSpriteManager: Character data file path = ${characterDataFile.absolutePath}") + println("AttackSpriteManager: Character data file exists = ${characterDataFile.exists()}") if (!characterDataFile.exists()) { - return null + println("AttackSpriteManager: Character data file does not exist, using default data") + // For now, return a default character data + val characterData = CharacterData( + name = characterId, + charaId = characterId, + smalefilename = "atk_s_02", // Default small attack + laugeFileName = "atk_l_04" // Default large attack + ) + + characterDataCache[characterId] = characterData + return characterData } val jsonContent = characterDataFile.readText() - // Parse the JSON and find the character with matching charaId - // This is a simplified version - you'll need to parse the actual JSON structure + println("AttackSpriteManager: JSON content length = ${jsonContent.length}") - // For now, return a default character data + // Parse the JSON response + val response = gson.fromJson(jsonContent, CharacterDataResponse::class.java) + + // Search through the DataList for the matching characterId + for (characterString in response.all_attributes.DataList) { + // Extract charaId from the string format: " id=0, charaId='dim000_mon03', ...>" + val charaIdMatch = Regex("charaId='([^']+)'").find(characterString) + if (charaIdMatch != null) { + val foundCharaId = charaIdMatch.groupValues[1] + if (foundCharaId == characterId) { + // Extract smalefilename and laugeFileName + val smallFileMatch = Regex("smalefilename='([^']+)'").find(characterString) + val largeFileMatch = Regex("laugeFileName='([^']+)'").find(characterString) + + val smallFileName = smallFileMatch?.groupValues?.get(1) ?: "0" + val largeFileName = largeFileMatch?.groupValues?.get(1) ?: "0" + + val characterData = CharacterData( + name = characterId, + charaId = characterId, + smalefilename = smallFileName, + laugeFileName = largeFileName + ) + + characterDataCache[characterId] = characterData + println("AttackSpriteManager: Found character data: $characterData") + return characterData + } + } + } + + // If character not found, return default data + println("AttackSpriteManager: Character not found in JSON, using default data") val characterData = CharacterData( name = characterId, charaId = characterId, @@ -77,9 +148,11 @@ class AttackSpriteManager(private val context: Context) { ) characterDataCache[characterId] = characterData + println("AttackSpriteManager: Created default character data: $characterData") return characterData } catch (e: Exception) { + println("AttackSpriteManager: Exception in getCharacterData: ${e.message}") e.printStackTrace() return null } 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 e9769b8..56f4667 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 @@ -9,16 +9,26 @@ class SpriteFileManager(private val context: Context) { fun copySpriteFilesToInternalStorage() { try { - // Create the base directory - val baseDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_assets") - if (!baseDir.exists()) { - baseDir.mkdirs() + // Create the base directory for extracted_assets + val extractedAssetsDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_assets") + if (!extractedAssetsDir.exists()) { + extractedAssetsDir.mkdirs() } - // Copy files from assets to internal storage - copyAssetDirectory("Battle_Sprites_Reference/extracted_assets", baseDir) + // Create the base directory for extracted_digimon_stats + val extractedStatsDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_digimon_stats") + if (!extractedStatsDir.exists()) { + extractedStatsDir.mkdirs() + } - println("Sprite files copied successfully to: ${baseDir.absolutePath}") + // Copy extracted_assets files from assets to internal storage + copyAssetDirectory("Battle_Sprites_Reference/extracted_assets", extractedAssetsDir) + + // Copy extracted_digimon_stats files from assets to internal storage + copyAssetDirectory("Battle_Sprites_Reference/extracted_digimon_stats", extractedStatsDir) + + println("Sprite files copied successfully to: ${extractedAssetsDir.absolutePath}") + println("Stats files copied successfully to: ${extractedStatsDir.absolutePath}") } catch (e: Exception) { println("Error copying sprite files: ${e.message}") @@ -74,7 +84,12 @@ class SpriteFileManager(private val context: Context) { } fun checkSpriteFilesExist(): Boolean { - val baseDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_assets") - return baseDir.exists() && baseDir.listFiles()?.isNotEmpty() == true + val extractedAssetsDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_assets") + val extractedStatsDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_digimon_stats") + + val assetsExist = extractedAssetsDir.exists() && extractedAssetsDir.listFiles()?.isNotEmpty() == true + val statsExist = extractedStatsDir.exists() && extractedStatsDir.listFiles()?.isNotEmpty() == true + + return assetsExist && statsExist } } \ No newline at end of file diff --git a/app/src/main/java/com/github/nacabaro/vbhelper/components/AttackSpriteImage.kt b/app/src/main/java/com/github/nacabaro/vbhelper/components/AttackSpriteImage.kt index 9bf6b12..51f4ed3 100644 --- a/app/src/main/java/com/github/nacabaro/vbhelper/components/AttackSpriteImage.kt +++ b/app/src/main/java/com/github/nacabaro/vbhelper/components/AttackSpriteImage.kt @@ -24,11 +24,13 @@ fun AttackSpriteImage( val context = LocalContext.current LaunchedEffect(characterId, isLarge) { + println("AttackSpriteImage: Loading attack sprite for characterId=$characterId, isLarge=$isLarge") coroutineScope.launch { val attackSpriteManager = AttackSpriteManager(context) val loadedBitmap = withContext(Dispatchers.IO) { attackSpriteManager.getAttackSprite(characterId, isLarge) } + println("AttackSpriteImage: Loaded bitmap = ${loadedBitmap != null}") bitmap = loadedBitmap } } 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 d249003..50ad664 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 @@ -196,6 +196,7 @@ fun BattleScreen( playerName: String = "Player", opponentName: String = "Opponent", activeCharacter: APIBattleCharacter? = null, + opponentCharacter: APIBattleCharacter? = null, onBattleComplete: (String?) -> Unit = {}, onExitBattle: () -> Unit = {} ) { @@ -232,31 +233,52 @@ fun BattleScreen( } } + // Opponent AI - automatically attack back after player attack + LaunchedEffect(battleSystem.currentView) { + if (battleSystem.currentView == 1 && !battleSystem.isAttacking) { + // Wait a bit before opponent attacks + delay(1000) + if (battleSystem.currentView == 1 && !battleSystem.isBattleOver()) { + println("Opponent attacking back!") + battleSystem.startOpponentAttack() + // Apply damage to player + battleSystem.applyDamage(true, 15f) // Player takes damage + } + } + } + Box( modifier = Modifier .fillMaxSize() .background(Color.Black) ) { + println("Current battle view: ${battleSystem.currentView}") when (battleSystem.currentView) { - 0 -> PlayerBattleView( - battleSystem = battleSystem, - stage = stage, - playerName = playerName, - attackAnimationProgress = animationProgress, - onAttackClick = { - battleSystem.startPlayerAttack() - // Apply damage after animation - battleSystem.applyDamage(false, 20f) // Opponent takes damage - }, - activeCharacter = activeCharacter - ) - 1 -> OpponentBattleView( - battleSystem = battleSystem, - stage = stage, - opponentName = opponentName, - attackAnimationProgress = animationProgress, - activeCharacter = activeCharacter - ) + 0 -> { + println("Showing PlayerBattleView") + PlayerBattleView( + battleSystem = battleSystem, + stage = stage, + playerName = playerName, + attackAnimationProgress = animationProgress, + onAttackClick = { + battleSystem.startPlayerAttack() + // Apply damage after animation + battleSystem.applyDamage(false, 20f) // Opponent takes damage + }, + activeCharacter = activeCharacter + ) + } + 1 -> { + println("Showing OpponentBattleView") + OpponentBattleView( + battleSystem = battleSystem, + stage = stage, + opponentName = opponentName, + attackAnimationProgress = animationProgress, + activeCharacter = opponentCharacter + ) + } } // Exit button @@ -330,7 +352,7 @@ fun PlayerBattleView( ) { SpriteImage( spriteName = "00", // The sprite number (00, 01, 02, etc.) - atlasName = when (stage) { + atlasName = activeCharacter?.charaId ?: when (stage) { "rookie" -> "dim000_mon01" "champion" -> "dim012_mon04" "ultimate" -> "dim137_mon09" @@ -401,8 +423,12 @@ fun PlayerBattleView( ) // Attack button + println("PlayerBattleView: Attack button enabled = ${battleSystem.isAttackButtonEnabled}") Button( - onClick = onAttackClick, + onClick = { + println("Attack button clicked!") + onAttackClick() + }, enabled = battleSystem.isAttackButtonEnabled, modifier = Modifier .fillMaxWidth() @@ -475,7 +501,7 @@ fun OpponentBattleView( ) { SpriteImage( spriteName = "00", // The sprite number (00, 01, 02, etc.) - atlasName = when (stage) { + atlasName = activeCharacter?.charaId ?: when (stage) { "rookie" -> "dim000_mon01" "champion" -> "dim012_mon04" "ultimate" -> "dim137_mon09" @@ -537,6 +563,7 @@ fun BattlesScreen() { var opponentsList by remember { mutableStateOf(ArrayList()) } var activeCharacter by remember { mutableStateOf(null) } + var selectedOpponent by remember { mutableStateOf(null) } var expanded by remember { mutableStateOf(false) } var selectedStage by remember { mutableStateOf("") } @@ -835,6 +862,7 @@ fun BattlesScreen() { Button( onClick = { activeCharacter?.let { + selectedOpponent = opponent RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 0, 0, opponent.name, 0) { apiResult -> currentView = "battle-main" } @@ -872,6 +900,7 @@ fun BattlesScreen() { Button( onClick = { activeCharacter?.let { + selectedOpponent = opponent RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 1, 0, opponent.name, 1) { apiResult -> currentView = "battle-main" } @@ -909,6 +938,7 @@ fun BattlesScreen() { Button( onClick = { activeCharacter?.let { + selectedOpponent = opponent RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 2, 0, opponent.name, 2) { apiResult -> currentView = "battle-main" } @@ -946,6 +976,7 @@ fun BattlesScreen() { Button( onClick = { activeCharacter?.let { + selectedOpponent = opponent RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 3, 0, opponent.name, 3) { apiResult -> currentView = "battle-main" } @@ -970,35 +1001,47 @@ fun BattlesScreen() { } "battle-main" -> { - var battleSystem by remember { mutableStateOf(ArenaBattleSystem()) } - var currentStage by remember { mutableStateOf("rookie") } - - // Initialize battle with character stats - LaunchedEffect(activeCharacter) { - activeCharacter?.let { character -> - battleSystem.initializeBattle( - playerHP = character.currentHp.toFloat(), - opponentHP = character.currentHp.toFloat(), - playerMaxHP = character.baseHp.toFloat(), - opponentMaxHP = character.baseHp.toFloat() - ) - } + val battleSystem = remember { ArenaBattleSystem() } + + // Determine the current stage based on the character's stage + val currentStage = when (activeCharacter?.stage) { + 0 -> "rookie" + 1 -> "champion" + 2 -> "ultimate" + 3 -> "mega" + else -> "rookie" } - - BattleScreen( - battleSystem = battleSystem, - stage = currentStage, - playerName = activeCharacter?.name ?: "Player", - opponentName = "Opponent", - activeCharacter = activeCharacter, - onBattleComplete = { winner -> - // Handle battle completion - currentView = "battle-results" - }, - onExitBattle = { - currentView = "main" - } - ) + + // Initialize battle with character stats + LaunchedEffect(activeCharacter, selectedOpponent) { + activeCharacter?.let { playerCharacter -> + selectedOpponent?.let { opponentCharacter -> + println("Initializing battle with player: ${playerCharacter.name}, opponent: ${opponentCharacter.name}") + battleSystem.initializeBattle( + playerHP = playerCharacter.currentHp.toFloat(), + opponentHP = opponentCharacter.currentHp.toFloat(), + playerMaxHP = playerCharacter.baseHp.toFloat(), + opponentMaxHP = opponentCharacter.baseHp.toFloat() + ) + } + } + } + + BattleScreen( + battleSystem = battleSystem, + stage = currentStage, + playerName = activeCharacter?.name ?: "Player", + opponentName = selectedOpponent?.name ?: "Opponent", + activeCharacter = activeCharacter, + opponentCharacter = selectedOpponent, + onBattleComplete = { winner -> + println("Battle complete! Winner: $winner") + currentView = "battle-results" + }, + onExitBattle = { + currentView = "main" + } + ) } "battle-results" -> {