Fixed atk sprite import bug. Readjusted sprites to load from individual files instead of full spritesheets.

This commit is contained in:
lightheel 2025-08-05 06:54:55 -04:00
parent fb09350825
commit 71ba5e0207
7 changed files with 356 additions and 163 deletions

View File

@ -13,15 +13,23 @@ fun AnimatedSpriteImage(
characterId: String, characterId: String,
animationType: DigimonAnimationType = DigimonAnimationType.IDLE, animationType: DigimonAnimationType = DigimonAnimationType.IDLE,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
contentScale: ContentScale = ContentScale.Fit contentScale: ContentScale = ContentScale.Fit,
reloadMappings: Boolean = false
) { ) {
val context = LocalContext.current val context = LocalContext.current
val spriteManager = remember { BattleSpriteManager(context) } val spriteManager = remember { IndividualSpriteManager(context) }
val animationStateMachine = remember { DigimonAnimationStateMachine(characterId, context) } val animationStateMachine = remember { DigimonAnimationStateMachine(characterId, context) }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
var bitmap by remember { mutableStateOf<android.graphics.Bitmap?>(null) } var bitmap by remember { mutableStateOf<android.graphics.Bitmap?>(null) }
// Reload mappings when reloadMappings parameter changes
LaunchedEffect(reloadMappings) {
if (reloadMappings) {
animationStateMachine.reloadMappings()
}
}
// Start the animation when the component is first created // Start the animation when the component is first created
LaunchedEffect(characterId) { LaunchedEffect(characterId) {
coroutineScope.launch { coroutineScope.launch {
@ -41,17 +49,16 @@ fun AnimatedSpriteImage(
} }
// Update sprite when animation state changes // Update sprite when animation state changes
LaunchedEffect(animationStateMachine.currentSpriteIndex) { LaunchedEffect(animationStateMachine.currentFrameNumber) {
val spriteName = animationStateMachine.getCurrentSpriteName() val frameNumber = animationStateMachine.getCurrentFrame()
val atlasName = animationStateMachine.getCurrentAtlasName()
println("Loading animated sprite: $spriteName from atlas: $atlasName") println("Loading animated sprite frame: $frameNumber for character: $characterId")
bitmap = spriteManager.loadSprite(spriteName, atlasName) bitmap = spriteManager.loadSpriteFrame(characterId, frameNumber)
if (bitmap == null) { if (bitmap == null) {
println("Failed to load animated sprite: $spriteName from atlas: $atlasName") println("Failed to load animated sprite frame: $frameNumber for character: $characterId")
} else { } else {
println("Successfully loaded animated sprite: $spriteName from atlas: $atlasName") println("Successfully loaded animated sprite frame: $frameNumber for character: $characterId")
} }
} }

View File

@ -31,8 +31,8 @@ class AttackSpriteManager(private val context: Context) {
private val gson = Gson() private val gson = Gson()
private val characterDataCache = mutableMapOf<String, CharacterData>() private val characterDataCache = mutableMapOf<String, CharacterData>()
// Base path for attack textures // Base path for attack textures (updated for new folder structure)
private val attackTexturesPath = "battle_sprites/extracted_assets/extracted_atksprites" private val attackTexturesPath = "battle_sprites/extracted_atksprites"
fun getAttackSprite(characterId: String, isLarge: Boolean = false): Bitmap? { fun getAttackSprite(characterId: String, isLarge: Boolean = false): Bitmap? {
println("AttackSpriteManager: Getting attack sprite for characterId=$characterId, isLarge=$isLarge") println("AttackSpriteManager: Getting attack sprite for characterId=$characterId, isLarge=$isLarge")

View File

@ -6,7 +6,6 @@ import androidx.compose.runtime.setValue
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import android.content.Context import android.content.Context
import java.io.File import java.io.File
import com.google.gson.Gson
enum class DigimonAnimationType { enum class DigimonAnimationType {
IDLE, IDLE,
@ -25,7 +24,7 @@ enum class DigimonAnimationType {
data class AnimationState( data class AnimationState(
val type: DigimonAnimationType, val type: DigimonAnimationType,
val spriteIndex: Int, // 00, 01, 02, etc. val frameNumber: Int, // 1-12 for individual PNG files
val duration: Long = 100L, // Duration in milliseconds val duration: Long = 100L, // Duration in milliseconds
val loop: Boolean = true val loop: Boolean = true
) )
@ -37,30 +36,31 @@ class DigimonAnimationStateMachine(
var currentAnimation by mutableStateOf<DigimonAnimationType>(DigimonAnimationType.IDLE) var currentAnimation by mutableStateOf<DigimonAnimationType>(DigimonAnimationType.IDLE)
private set private set
var currentSpriteIndex by mutableStateOf(0) var currentFrameNumber by mutableStateOf(1)
private set private set
var isPlaying by mutableStateOf(false) var isPlaying by mutableStateOf(false)
private set private set
// Animation mapping based on m_Name values // Direct mapping of frame numbers (1-12) to animation types
private val mNameToAnimationType = mapOf( // This is based on the standard Digimon sprite frame order
"01" to DigimonAnimationType.IDLE, private val frameToAnimationType = mapOf(
"02" to DigimonAnimationType.IDLE2, 1 to DigimonAnimationType.IDLE,
"03" to DigimonAnimationType.WALK, 2 to DigimonAnimationType.IDLE2,
"04" to DigimonAnimationType.WALK2, 3 to DigimonAnimationType.WALK,
"05" to DigimonAnimationType.RUN, 4 to DigimonAnimationType.WALK2,
"06" to DigimonAnimationType.RUN2, 5 to DigimonAnimationType.RUN,
"07" to DigimonAnimationType.WORKOUT, 6 to DigimonAnimationType.RUN2,
"08" to DigimonAnimationType.WORKOUT2, 7 to DigimonAnimationType.WORKOUT,
"09" to DigimonAnimationType.HAPPY, 8 to DigimonAnimationType.WORKOUT2,
"10" to DigimonAnimationType.SLEEP, 9 to DigimonAnimationType.HAPPY,
"11" to DigimonAnimationType.ATTACK, 10 to DigimonAnimationType.SLEEP,
"12" to DigimonAnimationType.FLEE 11 to DigimonAnimationType.ATTACK,
12 to DigimonAnimationType.FLEE
) )
// Cache for sprite file mappings // Reverse mapping for getting frame numbers for each animation type
private var spriteFileMappings: Map<DigimonAnimationType, List<String>> = emptyMap() private val animationTypeToFrames = frameToAnimationType.entries.groupBy({ it.value }, { it.key })
// Animation durations for each type // Animation durations for each type
private val animationDurations = mapOf( private val animationDurations = mapOf(
@ -79,58 +79,8 @@ class DigimonAnimationStateMachine(
) )
init { init {
loadSpriteFileMappings() println("Initialized DigimonAnimationStateMachine for character: $characterId")
} println("Available animation types: ${animationTypeToFrames.keys}")
private fun loadSpriteFileMappings() {
try {
val spriteBaseDir = File(context.filesDir, "battle_sprites/extracted_assets/sprites")
val gson = Gson()
val mappings = mutableMapOf<DigimonAnimationType, MutableList<String>>()
// Initialize all animation types
DigimonAnimationType.values().forEach { animationType ->
mappings[animationType] = mutableListOf()
}
println("Loading sprite mappings for character: $characterId")
// Scan all sprite files for this character
val spriteFiles = spriteBaseDir.listFiles { file ->
file.name.startsWith("${characterId}_sprite_") && file.name.endsWith(".json")
}
println("Found ${spriteFiles?.size ?: 0} sprite files for $characterId")
spriteFiles?.forEach { spriteFile ->
println("Processing sprite file: ${spriteFile.name}")
val spriteDataJson = spriteFile.readText()
val spriteData = gson.fromJson(spriteDataJson, SpriteData::class.java)
println(" m_Name: ${spriteData.m_Name}")
// Get the animation type from m_Name
val animationType = mNameToAnimationType[spriteData.m_Name]
if (animationType != null) {
// Extract the sprite index from filename (e.g., "dim000_mon01_sprite_00.json" -> "00")
val spriteIndex = spriteFile.name.substringAfter("_sprite_").substringBefore(".json")
mappings[animationType]?.add(spriteIndex)
println(" Mapped to animation type: $animationType with sprite index: $spriteIndex")
} else {
println(" Unknown m_Name: ${spriteData.m_Name}")
}
}
// Convert to immutable map
spriteFileMappings = mappings.mapValues { it.value.sorted() }
println("Final sprite mappings for $characterId: $spriteFileMappings")
} catch (e: Exception) {
println("Error loading sprite file mappings: ${e.message}")
e.printStackTrace()
}
} }
suspend fun playAnimation(animationType: DigimonAnimationType) { suspend fun playAnimation(animationType: DigimonAnimationType) {
@ -141,27 +91,22 @@ class DigimonAnimationStateMachine(
currentAnimation = animationType currentAnimation = animationType
isPlaying = true isPlaying = true
val frameSequence = spriteFileMappings[animationType] ?: listOf("00") val frameNumbers = animationTypeToFrames[animationType] ?: listOf(1)
val duration = animationDurations[animationType] ?: 100L val duration = animationDurations[animationType] ?: 100L
// Ensure we have at least one frame println("Playing animation: $animationType with frames: $frameNumbers")
if (frameSequence.isEmpty()) {
println("Warning: No sprite files found for animation type $animationType")
currentSpriteIndex = 0
return
}
// For non-looping animations like ATTACK, play once and return to IDLE // For non-looping animations like ATTACK, play once and return to IDLE
if (animationType == DigimonAnimationType.ATTACK) { if (animationType == DigimonAnimationType.ATTACK) {
currentSpriteIndex = frameSequence.firstOrNull()?.toIntOrNull() ?: 0 currentFrameNumber = frameNumbers.firstOrNull() ?: 1
delay(duration) delay(duration)
playAnimation(DigimonAnimationType.IDLE) playAnimation(DigimonAnimationType.IDLE)
} else { } else {
// For looping animations, cycle through frames // For looping animations, cycle through frames
var frameIndex = 0 var frameIndex = 0
while (isPlaying && currentAnimation == animationType) { while (isPlaying && currentAnimation == animationType) {
val spriteIndex = frameSequence[frameIndex % frameSequence.size] val frameNumber = frameNumbers[frameIndex % frameNumbers.size]
currentSpriteIndex = spriteIndex.toIntOrNull() ?: 0 currentFrameNumber = frameNumber
delay(duration) delay(duration)
frameIndex++ frameIndex++
} }
@ -177,26 +122,21 @@ class DigimonAnimationStateMachine(
currentAnimation = DigimonAnimationType.IDLE currentAnimation = DigimonAnimationType.IDLE
isPlaying = true isPlaying = true
val idleFrames = spriteFileMappings[DigimonAnimationType.IDLE] ?: listOf("00") val idleFrames = animationTypeToFrames[DigimonAnimationType.IDLE] ?: listOf(1)
val idle2Frames = animationTypeToFrames[DigimonAnimationType.IDLE2] ?: listOf(2)
val idle2Frames = spriteFileMappings[DigimonAnimationType.HAPPY] ?: listOf("08")
// Combine frames for cycling idle animation // Combine frames for cycling idle animation
val combinedFrames = (idleFrames + idle2Frames).distinct() val combinedFrames = (idleFrames + idle2Frames).distinct()
if (combinedFrames.isEmpty()) { println("Playing idle animation with frames: $combinedFrames")
println("Warning: No idle sprite files found")
currentSpriteIndex = 0
return
}
val duration = animationDurations[DigimonAnimationType.IDLE] ?: 500L val duration = animationDurations[DigimonAnimationType.IDLE] ?: 500L
// Cycle through idle frames // Cycle through idle frames
var frameIndex = 0 var frameIndex = 0
while (isPlaying && currentAnimation == DigimonAnimationType.IDLE) { while (isPlaying && currentAnimation == DigimonAnimationType.IDLE) {
val spriteIndex = combinedFrames[frameIndex % combinedFrames.size] val frameNumber = combinedFrames[frameIndex % combinedFrames.size]
currentSpriteIndex = spriteIndex.toIntOrNull() ?: 0 currentFrameNumber = frameNumber
delay(duration) delay(duration)
frameIndex++ frameIndex++
} }
@ -206,11 +146,17 @@ class DigimonAnimationStateMachine(
isPlaying = false isPlaying = false
} }
fun getCurrentSpriteName(): String { fun getCurrentFrame(): Int {
return "${characterId}_sprite_${String.format("%02d", currentSpriteIndex)}" return currentFrameNumber
} }
fun getCurrentAtlasName(): String { fun getCurrentCharacterId(): String {
return characterId return characterId
} }
// Method to reload mappings (useful for testing)
fun reloadMappings() {
println("Reloading mappings for character: $characterId")
// No need to reload since we use direct frame mapping
}
} }

View File

@ -0,0 +1,142 @@
package com.github.nacabaro.vbhelper.battle
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
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")
/**
* Load a specific sprite frame for a character
* @param characterId The character ID (e.g., "dim012_mon03")
* @param frameNumber The frame number (1-12)
* @return Bitmap of the sprite frame, or null if not found
*/
fun loadSpriteFrame(characterId: String, frameNumber: Int): Bitmap? {
val cacheKey = "${characterId}_frame_${frameNumber}"
// Check cache first
if (spriteCache.containsKey(cacheKey)) {
return spriteCache[cacheKey]
}
// Debug: Check if base directory exists
if (!spriteBaseDir.exists()) {
println("Sprite base directory does not exist: ${spriteBaseDir.absolutePath}")
return null
}
try {
// Construct the sprite file path
val spriteFileName = "${characterId}_${String.format("%02d", frameNumber)}.png"
val spriteFile = File(spriteBaseDir, "$characterId/$spriteFileName")
if (!spriteFile.exists()) {
println("Sprite file not found: ${spriteFile.absolutePath}")
return null
}
// Load the PNG file directly
val bitmap = BitmapFactory.decodeFile(spriteFile.absolutePath)
if (bitmap == null) {
println("Failed to decode sprite file: ${spriteFile.absolutePath}")
return null
}
println("Successfully loaded sprite frame: $spriteFileName (${bitmap.width}x${bitmap.height})")
// Cache the result
spriteCache[cacheKey] = bitmap
return bitmap
} catch (e: Exception) {
println("Error loading sprite frame: ${e.message}")
e.printStackTrace()
return null
}
}
/**
* Get all available sprite frames for a character
* @param characterId The character ID
* @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()
return emptyList()
}
}
/**
* Get all available characters
* @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()
return emptyList()
}
}
/**
* Clear the sprite cache
*/
fun clearCache() {
spriteCache.clear()
}
/**
* Check if a character has sprite files
* @param characterId The character ID to check
* @return true if the character has sprite files, false otherwise
*/
fun hasCharacterSprites(characterId: String): Boolean {
val characterDir = File(spriteBaseDir, characterId)
if (!characterDir.exists()) {
return false
}
val spriteFiles = characterDir.listFiles { file ->
file.name.startsWith("${characterId}_") && file.name.endsWith(".png")
} ?: emptyArray()
return spriteFiles.isNotEmpty()
}
}

View File

@ -9,26 +9,90 @@ class SpriteFileManager(private val context: Context) {
fun copySpriteFilesToInternalStorage() { fun copySpriteFilesToInternalStorage() {
try { try {
// Create the base directory for extracted_assets println("Starting sprite file copy process...")
val extractedAssetsDir = File(context.filesDir, "battle_sprites/extracted_assets")
if (!extractedAssetsDir.exists()) { // Debug: List what's in the assets directory
extractedAssetsDir.mkdirs() val assetManager = context.assets
val battleSpritesFiles = assetManager.list("battle_sprites")
println("battle_sprites directory in assets contains: ${battleSpritesFiles?.joinToString(", ")}")
val extractedAssetsFiles = assetManager.list("battle_sprites/extracted_assets")
println("battle_sprites/extracted_assets directory in assets contains: ${extractedAssetsFiles?.joinToString(", ")}")
// Check specifically for extracted_atksprites in assets (now directly under battle_sprites)
val atkspritesInAssets = assetManager.list("battle_sprites/extracted_atksprites")
println("extracted_atksprites in assets contains: ${atkspritesInAssets?.size ?: 0} files")
if (atkspritesInAssets != null && atkspritesInAssets.isNotEmpty()) {
println("First few attack files in assets: ${atkspritesInAssets.take(5).joinToString(", ")}")
} }
// Create the base directory for extracted_digimon_stats // Check for extracted_battlebgs in assets (now directly under battle_sprites)
val extractedStatsDir = File(context.filesDir, "battle_sprites/extracted_digimon_stats") val battlebgsInAssets = assetManager.list("battle_sprites/extracted_battlebgs")
if (!extractedStatsDir.exists()) { println("extracted_battlebgs in assets contains: ${battlebgsInAssets?.size ?: 0} files")
extractedStatsDir.mkdirs() if (battlebgsInAssets != null && battlebgsInAssets.isNotEmpty()) {
println("First few battle background files in assets: ${battlebgsInAssets.take(5).joinToString(", ")}")
} }
// Copy extracted_assets files from assets to internal storage // Try to list all possible subdirectories in battle_sprites
copyAssetDirectory("battle_sprites/extracted_assets", extractedAssetsDir) println("Checking all possible subdirectories in battle_sprites...")
battleSpritesFiles?.forEach { subdir ->
try {
val subdirFiles = assetManager.list("battle_sprites/$subdir")
println(" $subdir contains: ${subdirFiles?.size ?: 0} files")
if (subdirFiles != null && subdirFiles.isNotEmpty()) {
println(" First few files: ${subdirFiles.take(3).joinToString(", ")}")
}
} catch (e: Exception) {
println(" Error listing $subdir: ${e.message}")
}
}
// Copy extracted_digimon_stats files from assets to internal storage // Create the base directory for battle_sprites
copyAssetDirectory("battle_sprites/extracted_digimon_stats", extractedStatsDir) val battleSpritesDir = File(context.filesDir, "battle_sprites")
if (!battleSpritesDir.exists()) {
battleSpritesDir.mkdirs()
println("Created battle_sprites directory: ${battleSpritesDir.absolutePath}")
} else {
println("battle_sprites directory already exists: ${battleSpritesDir.absolutePath}")
}
println("Sprite files copied successfully to: ${extractedAssetsDir.absolutePath}") // Copy all subdirectories from battle_sprites assets to internal storage
println("Stats files copied successfully to: ${extractedStatsDir.absolutePath}") println("Copying all battle_sprites subdirectories...")
battleSpritesFiles?.forEach { subdir ->
val sourcePath = "battle_sprites/$subdir"
val targetDir = File(battleSpritesDir, subdir)
println("Copying $sourcePath to ${targetDir.absolutePath}")
copyAssetDirectory(sourcePath, targetDir)
}
println("Sprite files copied successfully to: ${battleSpritesDir.absolutePath}")
// Verify that attack sprites were copied
val atkspritesDir = File(battleSpritesDir, "extracted_atksprites")
if (atkspritesDir.exists()) {
val attackFiles = atkspritesDir.listFiles()
println("Attack sprites directory exists with ${attackFiles?.size ?: 0} files")
if (attackFiles != null && attackFiles.isNotEmpty()) {
println("First few attack files: ${attackFiles.take(5).map { it.name }}")
}
} else {
println("WARNING: extracted_atksprites directory does not exist!")
// List what's actually in the battle_sprites directory
val battleSpritesContents = battleSpritesDir.listFiles()
println("battle_sprites directory contains: ${battleSpritesContents?.map { it.name }?.joinToString(", ")}")
}
// Verify that battle backgrounds were copied
val battlebgsDir = File(battleSpritesDir, "extracted_battlebgs")
if (battlebgsDir.exists()) {
val bgFiles = battlebgsDir.listFiles()
println("Battle backgrounds directory exists with ${bgFiles?.size ?: 0} files")
if (bgFiles != null && bgFiles.isNotEmpty()) {
println("First few battle background files: ${bgFiles.take(5).map { it.name }}")
}
} else {
println("WARNING: extracted_battlebgs directory does not exist!")
}
} catch (e: Exception) { } catch (e: Exception) {
println("Error copying sprite files: ${e.message}") println("Error copying sprite files: ${e.message}")
@ -41,6 +105,9 @@ class SpriteFileManager(private val context: Context) {
val assetManager = context.assets val assetManager = context.assets
val files = assetManager.list(assetPath) ?: return val files = assetManager.list(assetPath) ?: return
println("Copying asset directory: $assetPath (${files.size} items)")
println("Files found: ${files.joinToString(", ")}")
for (file in files) { for (file in files) {
val assetFilePath = if (assetPath.isEmpty()) file else "$assetPath/$file" val assetFilePath = if (assetPath.isEmpty()) file else "$assetPath/$file"
val targetFile = File(targetDir, file) val targetFile = File(targetDir, file)
@ -50,10 +117,12 @@ class SpriteFileManager(private val context: Context) {
targetFile.parentFile!!.mkdirs() targetFile.parentFile!!.mkdirs()
} }
// Check if it's a directory // Check if it's a directory by trying to list its contents
try {
val subFiles = assetManager.list(assetFilePath) val subFiles = assetManager.list(assetFilePath)
if (subFiles != null && subFiles.isNotEmpty()) { if (subFiles != null && subFiles.isNotEmpty()) {
// It's a directory, create it and copy contents // It's a directory, create it and copy contents
println("Copying subdirectory: $assetFilePath (${subFiles.size} files)")
if (!targetFile.exists()) { if (!targetFile.exists()) {
targetFile.mkdirs() targetFile.mkdirs()
} }
@ -62,9 +131,36 @@ class SpriteFileManager(private val context: Context) {
// It's a file, copy it // It's a file, copy it
copyAssetFile(assetFilePath, targetFile) copyAssetFile(assetFilePath, targetFile)
} }
} catch (e: Exception) {
// If we can't list contents, it's probably a file
println("Treating $assetFilePath as file (could not list contents)")
copyAssetFile(assetFilePath, targetFile)
}
}
// Special handling for extracted_atksprites - try to copy it directly if it wasn't found
if (assetPath == "battle_sprites/extracted_assets") {
println("Special handling: Checking for extracted_atksprites directory...")
try {
val atkspritesFiles = assetManager.list("battle_sprites/extracted_assets/extracted_atksprites")
if (atkspritesFiles != null && atkspritesFiles.isNotEmpty()) {
println("Found extracted_atksprites with ${atkspritesFiles.size} files")
val atkspritesDir = File(targetDir, "extracted_atksprites")
if (!atkspritesDir.exists()) {
atkspritesDir.mkdirs()
}
copyAssetDirectory("battle_sprites/extracted_assets/extracted_atksprites", atkspritesDir)
} else {
println("extracted_atksprites directory not found in assets")
} }
} catch (e: Exception) {
println("Error checking extracted_atksprites: ${e.message}")
}
}
} catch (e: Exception) { } catch (e: Exception) {
println("Error copying asset directory $assetPath: ${e.message}") println("Error copying asset directory $assetPath: ${e.message}")
e.printStackTrace()
} }
} }
@ -84,34 +180,34 @@ class SpriteFileManager(private val context: Context) {
} }
fun checkSpriteFilesExist(): Boolean { fun checkSpriteFilesExist(): Boolean {
val extractedAssetsDir = File(context.filesDir, "battle_sprites/extracted_assets") val battleSpritesDir = File(context.filesDir, "battle_sprites")
val extractedStatsDir = File(context.filesDir, "battle_sprites/extracted_digimon_stats") val extractedAssetsDir = File(battleSpritesDir, "extracted_assets")
val extractedStatsDir = File(battleSpritesDir, "extracted_digimon_stats")
val atkspritesDir = File(battleSpritesDir, "extracted_atksprites")
val battlebgsDir = File(battleSpritesDir, "extracted_battlebgs")
val battleSpritesExist = battleSpritesDir.exists() && battleSpritesDir.listFiles()?.isNotEmpty() == true
val assetsExist = extractedAssetsDir.exists() && extractedAssetsDir.listFiles()?.isNotEmpty() == true val assetsExist = extractedAssetsDir.exists() && extractedAssetsDir.listFiles()?.isNotEmpty() == true
val statsExist = extractedStatsDir.exists() && extractedStatsDir.listFiles()?.isNotEmpty() == true val statsExist = extractedStatsDir.exists() && extractedStatsDir.listFiles()?.isNotEmpty() == true
val atkspritesExist = atkspritesDir.exists() && atkspritesDir.listFiles()?.isNotEmpty() == true
val battlebgsExist = battlebgsDir.exists() && battlebgsDir.listFiles()?.isNotEmpty() == true
return assetsExist && statsExist println("Checking sprite files exist:")
println(" battle_sprites exists: $battleSpritesExist")
println(" extracted_assets exists: $assetsExist")
println(" extracted_digimon_stats exists: $statsExist")
println(" extracted_atksprites exists: $atkspritesExist")
println(" extracted_battlebgs exists: $battlebgsExist")
return battleSpritesExist && assetsExist && statsExist && atkspritesExist && battlebgsExist
} }
fun clearSpriteFiles() { fun clearSpriteFiles() {
try { try {
val extractedAssetsDir = File(context.filesDir, "battle_sprites/extracted_assets")
val extractedStatsDir = File(context.filesDir, "battle_sprites/extracted_digimon_stats")
if (extractedAssetsDir.exists()) {
deleteDirectory(extractedAssetsDir)
println("Cleared extracted_assets directory")
}
if (extractedStatsDir.exists()) {
deleteDirectory(extractedStatsDir)
println("Cleared extracted_digimon_stats directory")
}
// Also clear the battle_sprites directory if it's empty
val battleSpritesDir = File(context.filesDir, "battle_sprites") val battleSpritesDir = File(context.filesDir, "battle_sprites")
if (battleSpritesDir.exists() && battleSpritesDir.listFiles()?.isEmpty() == true) {
battleSpritesDir.delete() if (battleSpritesDir.exists()) {
deleteDirectory(battleSpritesDir)
println("Cleared battle_sprites directory") println("Cleared battle_sprites directory")
} }

View File

@ -6,34 +6,33 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import com.github.nacabaro.vbhelper.battle.BattleSpriteManager
@Composable @Composable
fun SpriteImage( fun SpriteImage(
spriteName: String, characterId: String,
atlasName: String, frameNumber: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
contentScale: ContentScale = ContentScale.Fit contentScale: ContentScale = ContentScale.Fit
) { ) {
val context = LocalContext.current val context = LocalContext.current
val spriteManager = remember { BattleSpriteManager(context) } val spriteManager = remember { IndividualSpriteManager(context) }
var bitmap by remember { mutableStateOf<android.graphics.Bitmap?>(null) } var bitmap by remember { mutableStateOf<android.graphics.Bitmap?>(null) }
LaunchedEffect(spriteName, atlasName) { LaunchedEffect(characterId, frameNumber) {
println("Loading sprite: $spriteName from atlas: $atlasName") println("Loading sprite frame: $frameNumber for character: $characterId")
bitmap = spriteManager.loadSprite(spriteName, atlasName) bitmap = spriteManager.loadSpriteFrame(characterId, frameNumber)
if (bitmap == null) { if (bitmap == null) {
println("Failed to load sprite: $spriteName from atlas: $atlasName") println("Failed to load sprite frame: $frameNumber for character: $characterId")
} else { } else {
println("Successfully loaded sprite: $spriteName from atlas: $atlasName") println("Successfully loaded sprite frame: $frameNumber for character: $characterId")
} }
} }
bitmap?.let { bmp -> bitmap?.let { bmp ->
Image( Image(
bitmap = bmp.asImageBitmap(), bitmap = bmp.asImageBitmap(),
contentDescription = "Sprite: $spriteName", contentDescription = "Sprite: $characterId frame $frameNumber",
modifier = modifier, modifier = modifier,
contentScale = contentScale contentScale = contentScale
) )

View File

@ -45,7 +45,6 @@ import com.github.nacabaro.vbhelper.battle.APIBattleCharacter
import android.util.Log import android.util.Log
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.battle.RetrofitHelper import com.github.nacabaro.vbhelper.battle.RetrofitHelper
import com.github.nacabaro.vbhelper.battle.SpriteImage
import com.github.nacabaro.vbhelper.battle.AttackSpriteImage import com.github.nacabaro.vbhelper.battle.AttackSpriteImage
import com.github.nacabaro.vbhelper.battle.SpriteFileManager import com.github.nacabaro.vbhelper.battle.SpriteFileManager
import com.github.nacabaro.vbhelper.battle.ArenaBattleSystem import com.github.nacabaro.vbhelper.battle.ArenaBattleSystem
@ -286,7 +285,8 @@ fun PlayerBattleView(
modifier = Modifier modifier = Modifier
.size(80.dp) .size(80.dp)
.scale(-1f, 1f), // Flip player Digimon horizontally .scale(-1f, 1f), // Flip player Digimon horizontally
contentScale = ContentScale.Fit contentScale = ContentScale.Fit,
reloadMappings = false
) )
// Attack sprite visibility and positioning based on attack phase // Attack sprite visibility and positioning based on attack phase
@ -500,7 +500,8 @@ fun OpponentBattleView(
characterId = activeCharacter?.charaId ?: "dim011_mon01", characterId = activeCharacter?.charaId ?: "dim011_mon01",
animationType = animationType, animationType = animationType,
modifier = Modifier.size(80.dp), modifier = Modifier.size(80.dp),
contentScale = ContentScale.Fit contentScale = ContentScale.Fit,
reloadMappings = false
) )
// Attack sprite visibility and positioning based on attack phase // Attack sprite visibility and positioning based on attack phase
@ -642,12 +643,13 @@ fun BattlesScreen() {
// Initialize sprite files on first load // Initialize sprite files on first load
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
println("BATTLESCREEN: LaunchedEffect triggered - checking sprite files...")
val spriteFileManager = SpriteFileManager(context) val spriteFileManager = SpriteFileManager(context)
if (!spriteFileManager.checkSpriteFilesExist()) { if (!spriteFileManager.checkSpriteFilesExist()) {
println("Copying sprite files to internal storage...") println("BATTLESCREEN: Copying sprite files to internal storage...")
spriteFileManager.copySpriteFilesToInternalStorage() spriteFileManager.copySpriteFilesToInternalStorage()
} else { } else {
println("Sprite files already exist in internal storage") println("BATTLESCREEN: Sprite files already exist in internal storage")
} }
} }
@ -856,7 +858,8 @@ fun BattlesScreen() {
characterId = testCharacterId, characterId = testCharacterId,
animationType = currentTestAnimation, animationType = currentTestAnimation,
modifier = Modifier.size(120.dp), modifier = Modifier.size(120.dp),
contentScale = ContentScale.Fit contentScale = ContentScale.Fit,
reloadMappings = false
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))