Updated client for attack battle loop.

This commit is contained in:
lightheel 2025-08-02 07:49:43 -04:00
parent a044d24f5f
commit 6c9d057917
4 changed files with 198 additions and 65 deletions

View File

@ -13,6 +13,20 @@ data class CharacterData(
val laugeFileName: String 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<String>
)
class AttackSpriteManager(private val context: Context) { 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>()
@ -21,9 +35,11 @@ class AttackSpriteManager(private val context: Context) {
private val attackTexturesPath = "Battle_Sprites_Reference/extracted_assets/atk_textures" private val attackTexturesPath = "Battle_Sprites_Reference/extracted_assets/atk_textures"
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")
try { try {
// Get character data // Get character data
val characterData = getCharacterData(characterId) ?: return null val characterData = getCharacterData(characterId) ?: return null
println("AttackSpriteManager: Got character data: $characterData")
// Determine which attack file to use // Determine which attack file to use
val attackFileName = if (isLarge) { val attackFileName = if (isLarge) {
@ -31,44 +47,99 @@ class AttackSpriteManager(private val context: Context) {
} else { } else {
characterData.smalefilename characterData.smalefilename
} }
println("AttackSpriteManager: Attack filename = $attackFileName")
// Skip if no attack file // 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 // Load the attack sprite
val attackFilePath = "$attackTexturesPath/$attackFileName.png" val attackFilePath = "$attackTexturesPath/$attackFileName.png"
val attackFile = File(context.filesDir, attackFilePath) val attackFile = File(context.filesDir, attackFilePath)
println("AttackSpriteManager: Attack file path = ${attackFile.absolutePath}")
println("AttackSpriteManager: Attack file exists = ${attackFile.exists()}")
return if (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 { } else {
println("AttackSpriteManager: Attack file does not exist")
null null
} }
} catch (e: Exception) { } catch (e: Exception) {
println("AttackSpriteManager: Exception occurred: ${e.message}")
e.printStackTrace() e.printStackTrace()
return null return null
} }
} }
private fun getCharacterData(characterId: String): CharacterData? { private fun getCharacterData(characterId: String): CharacterData? {
println("AttackSpriteManager: Getting character data for characterId=$characterId")
// Check cache first // Check cache first
if (characterDataCache.containsKey(characterId)) { if (characterDataCache.containsKey(characterId)) {
println("AttackSpriteManager: Found character data in cache")
return characterDataCache[characterId] return characterDataCache[characterId]
} }
try { try {
// Load character data from JSON file // Load character data from JSON file
val characterDataFile = File(context.filesDir, "Battle_Sprites_Reference/extracted_digimon_stats/character_data/CharacterData.json") 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()) { 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() val jsonContent = characterDataFile.readText()
// Parse the JSON and find the character with matching charaId println("AttackSpriteManager: JSON content length = ${jsonContent.length}")
// This is a simplified version - you'll need to parse the actual JSON structure
// 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: "<UnknownObject<Character> 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( val characterData = CharacterData(
name = characterId, name = characterId,
charaId = characterId, charaId = characterId,
@ -77,9 +148,11 @@ class AttackSpriteManager(private val context: Context) {
) )
characterDataCache[characterId] = characterData characterDataCache[characterId] = characterData
println("AttackSpriteManager: Created default character data: $characterData")
return characterData return characterData
} catch (e: Exception) { } catch (e: Exception) {
println("AttackSpriteManager: Exception in getCharacterData: ${e.message}")
e.printStackTrace() e.printStackTrace()
return null return null
} }

View File

@ -9,16 +9,26 @@ class SpriteFileManager(private val context: Context) {
fun copySpriteFilesToInternalStorage() { fun copySpriteFilesToInternalStorage() {
try { try {
// Create the base directory // Create the base directory for extracted_assets
val baseDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_assets") val extractedAssetsDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_assets")
if (!baseDir.exists()) { if (!extractedAssetsDir.exists()) {
baseDir.mkdirs() extractedAssetsDir.mkdirs()
} }
// Copy files from assets to internal storage // Create the base directory for extracted_digimon_stats
copyAssetDirectory("Battle_Sprites_Reference/extracted_assets", baseDir) 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) { } catch (e: Exception) {
println("Error copying sprite files: ${e.message}") println("Error copying sprite files: ${e.message}")
@ -74,7 +84,12 @@ class SpriteFileManager(private val context: Context) {
} }
fun checkSpriteFilesExist(): Boolean { fun checkSpriteFilesExist(): Boolean {
val baseDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_assets") val extractedAssetsDir = File(context.filesDir, "Battle_Sprites_Reference/extracted_assets")
return baseDir.exists() && baseDir.listFiles()?.isNotEmpty() == true 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
} }
} }

View File

@ -24,11 +24,13 @@ fun AttackSpriteImage(
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(characterId, isLarge) { LaunchedEffect(characterId, isLarge) {
println("AttackSpriteImage: Loading attack sprite for characterId=$characterId, isLarge=$isLarge")
coroutineScope.launch { coroutineScope.launch {
val attackSpriteManager = AttackSpriteManager(context) val attackSpriteManager = AttackSpriteManager(context)
val loadedBitmap = withContext(Dispatchers.IO) { val loadedBitmap = withContext(Dispatchers.IO) {
attackSpriteManager.getAttackSprite(characterId, isLarge) attackSpriteManager.getAttackSprite(characterId, isLarge)
} }
println("AttackSpriteImage: Loaded bitmap = ${loadedBitmap != null}")
bitmap = loadedBitmap bitmap = loadedBitmap
} }
} }

View File

@ -196,6 +196,7 @@ fun BattleScreen(
playerName: String = "Player", playerName: String = "Player",
opponentName: String = "Opponent", opponentName: String = "Opponent",
activeCharacter: APIBattleCharacter? = null, activeCharacter: APIBattleCharacter? = null,
opponentCharacter: APIBattleCharacter? = null,
onBattleComplete: (String?) -> Unit = {}, onBattleComplete: (String?) -> Unit = {},
onExitBattle: () -> 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( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(Color.Black) .background(Color.Black)
) { ) {
println("Current battle view: ${battleSystem.currentView}")
when (battleSystem.currentView) { when (battleSystem.currentView) {
0 -> PlayerBattleView( 0 -> {
battleSystem = battleSystem, println("Showing PlayerBattleView")
stage = stage, PlayerBattleView(
playerName = playerName, battleSystem = battleSystem,
attackAnimationProgress = animationProgress, stage = stage,
onAttackClick = { playerName = playerName,
battleSystem.startPlayerAttack() attackAnimationProgress = animationProgress,
// Apply damage after animation onAttackClick = {
battleSystem.applyDamage(false, 20f) // Opponent takes damage battleSystem.startPlayerAttack()
}, // Apply damage after animation
activeCharacter = activeCharacter battleSystem.applyDamage(false, 20f) // Opponent takes damage
) },
1 -> OpponentBattleView( activeCharacter = activeCharacter
battleSystem = battleSystem, )
stage = stage, }
opponentName = opponentName, 1 -> {
attackAnimationProgress = animationProgress, println("Showing OpponentBattleView")
activeCharacter = activeCharacter OpponentBattleView(
) battleSystem = battleSystem,
stage = stage,
opponentName = opponentName,
attackAnimationProgress = animationProgress,
activeCharacter = opponentCharacter
)
}
} }
// Exit button // Exit button
@ -330,7 +352,7 @@ fun PlayerBattleView(
) { ) {
SpriteImage( SpriteImage(
spriteName = "00", // The sprite number (00, 01, 02, etc.) spriteName = "00", // The sprite number (00, 01, 02, etc.)
atlasName = when (stage) { atlasName = activeCharacter?.charaId ?: when (stage) {
"rookie" -> "dim000_mon01" "rookie" -> "dim000_mon01"
"champion" -> "dim012_mon04" "champion" -> "dim012_mon04"
"ultimate" -> "dim137_mon09" "ultimate" -> "dim137_mon09"
@ -401,8 +423,12 @@ fun PlayerBattleView(
) )
// Attack button // Attack button
println("PlayerBattleView: Attack button enabled = ${battleSystem.isAttackButtonEnabled}")
Button( Button(
onClick = onAttackClick, onClick = {
println("Attack button clicked!")
onAttackClick()
},
enabled = battleSystem.isAttackButtonEnabled, enabled = battleSystem.isAttackButtonEnabled,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -475,7 +501,7 @@ fun OpponentBattleView(
) { ) {
SpriteImage( SpriteImage(
spriteName = "00", // The sprite number (00, 01, 02, etc.) spriteName = "00", // The sprite number (00, 01, 02, etc.)
atlasName = when (stage) { atlasName = activeCharacter?.charaId ?: when (stage) {
"rookie" -> "dim000_mon01" "rookie" -> "dim000_mon01"
"champion" -> "dim012_mon04" "champion" -> "dim012_mon04"
"ultimate" -> "dim137_mon09" "ultimate" -> "dim137_mon09"
@ -537,6 +563,7 @@ fun BattlesScreen() {
var opponentsList by remember { mutableStateOf(ArrayList<APIBattleCharacter>()) } var opponentsList by remember { mutableStateOf(ArrayList<APIBattleCharacter>()) }
var activeCharacter by remember { mutableStateOf<APIBattleCharacter?>(null) } var activeCharacter by remember { mutableStateOf<APIBattleCharacter?>(null) }
var selectedOpponent by remember { mutableStateOf<APIBattleCharacter?>(null) }
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
var selectedStage by remember { mutableStateOf("") } var selectedStage by remember { mutableStateOf("") }
@ -835,6 +862,7 @@ fun BattlesScreen() {
Button( Button(
onClick = { onClick = {
activeCharacter?.let { activeCharacter?.let {
selectedOpponent = opponent
RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 0, 0, opponent.name, 0) { apiResult -> RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 0, 0, opponent.name, 0) { apiResult ->
currentView = "battle-main" currentView = "battle-main"
} }
@ -872,6 +900,7 @@ fun BattlesScreen() {
Button( Button(
onClick = { onClick = {
activeCharacter?.let { activeCharacter?.let {
selectedOpponent = opponent
RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 1, 0, opponent.name, 1) { apiResult -> RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 1, 0, opponent.name, 1) { apiResult ->
currentView = "battle-main" currentView = "battle-main"
} }
@ -909,6 +938,7 @@ fun BattlesScreen() {
Button( Button(
onClick = { onClick = {
activeCharacter?.let { activeCharacter?.let {
selectedOpponent = opponent
RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 2, 0, opponent.name, 2) { apiResult -> RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 2, 0, opponent.name, 2) { apiResult ->
currentView = "battle-main" currentView = "battle-main"
} }
@ -946,6 +976,7 @@ fun BattlesScreen() {
Button( Button(
onClick = { onClick = {
activeCharacter?.let { activeCharacter?.let {
selectedOpponent = opponent
RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 3, 0, opponent.name, 3) { apiResult -> RetrofitHelper().getPVPWinner(context, 0, 2, it.name, 3, 0, opponent.name, 3) { apiResult ->
currentView = "battle-main" currentView = "battle-main"
} }
@ -970,35 +1001,47 @@ fun BattlesScreen() {
} }
"battle-main" -> { "battle-main" -> {
var battleSystem by remember { mutableStateOf(ArenaBattleSystem()) } val battleSystem = remember { ArenaBattleSystem() }
var currentStage by remember { mutableStateOf("rookie") }
// Initialize battle with character stats // Determine the current stage based on the character's stage
LaunchedEffect(activeCharacter) { val currentStage = when (activeCharacter?.stage) {
activeCharacter?.let { character -> 0 -> "rookie"
battleSystem.initializeBattle( 1 -> "champion"
playerHP = character.currentHp.toFloat(), 2 -> "ultimate"
opponentHP = character.currentHp.toFloat(), 3 -> "mega"
playerMaxHP = character.baseHp.toFloat(), else -> "rookie"
opponentMaxHP = character.baseHp.toFloat()
)
}
} }
BattleScreen( // Initialize battle with character stats
battleSystem = battleSystem, LaunchedEffect(activeCharacter, selectedOpponent) {
stage = currentStage, activeCharacter?.let { playerCharacter ->
playerName = activeCharacter?.name ?: "Player", selectedOpponent?.let { opponentCharacter ->
opponentName = "Opponent", println("Initializing battle with player: ${playerCharacter.name}, opponent: ${opponentCharacter.name}")
activeCharacter = activeCharacter, battleSystem.initializeBattle(
onBattleComplete = { winner -> playerHP = playerCharacter.currentHp.toFloat(),
// Handle battle completion opponentHP = opponentCharacter.currentHp.toFloat(),
currentView = "battle-results" playerMaxHP = playerCharacter.baseHp.toFloat(),
}, opponentMaxHP = opponentCharacter.baseHp.toFloat()
onExitBattle = { )
currentView = "main" }
} }
) }
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" -> { "battle-results" -> {