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
)
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) {
private val gson = Gson()
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"
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: "<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(
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
}

View File

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

View File

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

View File

@ -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<APIBattleCharacter>()) }
var activeCharacter by remember { mutableStateOf<APIBattleCharacter?>(null) }
var selectedOpponent by remember { mutableStateOf<APIBattleCharacter?>(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" -> {