Moved sprites to load from external Android storage.

Setup landscape view for battle.
This commit is contained in:
lightheel 2025-08-08 07:44:36 -04:00
parent f0760f9ed0
commit bb1c29bbb4
7 changed files with 280 additions and 226 deletions

View File

@ -5,6 +5,8 @@
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".di.VBHelper"

View File

@ -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
@ -31,8 +32,10 @@ class AttackSpriteManager(private val context: Context) {
private val gson = Gson()
private val characterDataCache = mutableMapOf<String, CharacterData>()
// 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()}")

View File

@ -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<String, Bitmap>()
// 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<String> {
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<String> {
try {
val texturesDir = File(spriteBaseDir, "extracted_textures")
val texturesDir = File(getSpriteBaseDir(), "extracted_textures")
if (!texturesDir.exists()) {
return emptyList()
}

View File

@ -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<String, Bitmap>()
// 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<String> {
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<String> {
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()

View File

@ -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<String, Bitmap>()
// 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<Int> {
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<String> {
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
}

View File

@ -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)

View File

@ -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}")