Fixed HP desync with battle.

This commit is contained in:
lightheel 2025-10-19 13:09:14 -04:00
parent 61daad459b
commit 0875b114d5

View File

@ -835,9 +835,9 @@ fun MiddleBattleView(
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
) { ) {
// Enemy Digimon (top half) // Enemy Digimon (top half)
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(1f), .weight(1f),
contentAlignment = Alignment.CenterEnd contentAlignment = Alignment.CenterEnd
) { ) {
@ -846,8 +846,8 @@ fun MiddleBattleView(
battleSystem.attackPhase == 1 -> DigimonAnimationType.ATTACK // Both attacking in Phase 1 battleSystem.attackPhase == 1 -> DigimonAnimationType.ATTACK // Both attacking in Phase 1
battleSystem.isOpponentDodging -> DigimonAnimationType.WALK battleSystem.isOpponentDodging -> DigimonAnimationType.WALK
battleSystem.isOpponentHitDelayed -> DigimonAnimationType.SLEEP battleSystem.isOpponentHitDelayed -> DigimonAnimationType.SLEEP
else -> DigimonAnimationType.IDLE else -> DigimonAnimationType.IDLE
} }
// Calculate vertical offset for enemy dodge animation // Calculate vertical offset for enemy dodge animation
val enemyVerticalOffset = if (battleSystem.isOpponentDodging) { val enemyVerticalOffset = if (battleSystem.isOpponentDodging) {
@ -873,17 +873,17 @@ fun MiddleBattleView(
} else { } else {
0.dp 0.dp
} }
AnimatedSpriteImage( AnimatedSpriteImage(
characterId = opponentCharacter?.charaId ?: "dim011_mon01", characterId = opponentCharacter?.charaId ?: "dim011_mon01",
animationType = enemyAnimationType, animationType = enemyAnimationType,
modifier = Modifier modifier = Modifier
.size(80.dp) .size(80.dp)
.offset( .offset(
x = enemyHitOffset, x = enemyHitOffset,
y = enemyVerticalOffset + 40.dp y = enemyVerticalOffset + 40.dp
), ),
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
reloadMappings = false, reloadMappings = false,
animationOffset = 375L // Offset enemy animation by half the idle duration animationOffset = 375L // Offset enemy animation by half the idle duration
) )
@ -1101,22 +1101,22 @@ fun MiddleBattleView(
// Match is still ongoing - update HP and continue // Match is still ongoing - update HP and continue
println("Round ${apiResult.currentRound}: Player HP=${apiResult.playerHP}, Opponent HP=${apiResult.opponentHP}") 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) { if (apiResult.playerAttackDamage > 0) {
// Player attack hit - enemy takes damage at end of player animation // Player attack hit - enemy takes damage at end of player animation
println("Player attack hit! Enemy will take ${apiResult.playerAttackDamage} damage") println("Player attack hit! Enemy will take ${apiResult.playerAttackDamage} damage")
onSetPendingDamage(0f, apiResult.playerAttackDamage.toFloat()) // Opponent takes damage onSetPendingDamage(0f, apiResult.playerAttackDamage.toFloat()) // Opponent takes damage
battleSystem.setAttackHitState(true) battleSystem.setAttackHitState(true)
// Also check if enemy counter-attacks and hits // Also check if enemy counter-attacks and hits
if (apiResult.opponentAttackDamage > 0) { if (apiResult.opponentAttackDamage > 0) {
println("Enemy counter-attack hits! Player takes ${apiResult.opponentAttackDamage} damage") println("Enemy counter-attack hits! Player takes ${apiResult.opponentAttackDamage} damage")
onSetPendingDamage(apiResult.opponentAttackDamage.toFloat(), apiResult.playerAttackDamage.toFloat()) // Both take damage onSetPendingDamage(apiResult.opponentAttackDamage.toFloat(), apiResult.playerAttackDamage.toFloat()) // Both take damage
} }
} else { } else {
// Player attack missed - enemy counter-attacks // Player attack missed - enemy counter-attacks
println("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 // Set up counter-attack - determine if it hits based on API result
val counterAttackHits = apiResult.opponentAttackDamage > 0 val counterAttackHits = apiResult.opponentAttackDamage > 0
println("Setting up counter-attack: counterAttackHits=$counterAttackHits, opponentAttackDamage=${apiResult.opponentAttackDamage}") println("Setting up counter-attack: counterAttackHits=$counterAttackHits, opponentAttackDamage=${apiResult.opponentAttackDamage}")
@ -1137,7 +1137,7 @@ fun MiddleBattleView(
battleSystem.setupCounterAttack(finalCounterAttackHits) battleSystem.setupCounterAttack(finalCounterAttackHits)
// Set the opponent attack hit state for Phase 3 // Set the opponent attack hit state for Phase 3
battleSystem.handleOpponentAttackResult(finalCounterAttackHits) battleSystem.handleOpponentAttackResult(finalCounterAttackHits)
} }
} }
2 -> { 2 -> {
// Match is over - transition to results screen // Match is over - transition to results screen
@ -1199,7 +1199,7 @@ fun PlayerBattleView(
MultiLayerAnimatedBattleBackground(modifier = Modifier.fillMaxSize(), backgroundSetIndex = selectedBackgroundSet) MultiLayerAnimatedBattleBackground(modifier = Modifier.fillMaxSize(), backgroundSetIndex = selectedBackgroundSet)
// Top section: HP bar and HP numbers // Top section: HP bar and HP numbers
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp) .padding(16.dp)
@ -1241,19 +1241,19 @@ fun PlayerBattleView(
// Middle section: Player Digimon only // Middle section: Player Digimon only
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(16.dp), .padding(16.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
// Player Digimon (left side) // Player Digimon (left side)
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.size(80.dp), .size(80.dp),
contentAlignment = Alignment.CenterStart contentAlignment = Alignment.CenterStart
) { ) {
// Determine animation type based on battle state // Determine animation type based on battle state
val animationType = when { val animationType = when {
battleSystem.isPlayerDodging -> DigimonAnimationType.WALK // Use walk animation for dodge battleSystem.isPlayerDodging -> DigimonAnimationType.WALK // Use walk animation for dodge
battleSystem.isPlayerHitDelayed -> DigimonAnimationType.SLEEP // Use sleep animation for hit effect (injured sprite) battleSystem.isPlayerHitDelayed -> DigimonAnimationType.SLEEP // Use sleep animation for hit effect (injured sprite)
@ -1261,8 +1261,8 @@ fun PlayerBattleView(
battleSystem.attackPhase == 2 -> DigimonAnimationType.ATTACK // Player attack on opponent screen battleSystem.attackPhase == 2 -> DigimonAnimationType.ATTACK // Player attack on opponent screen
battleSystem.attackPhase == 3 -> DigimonAnimationType.IDLE // Opponent attack on opponent screen battleSystem.attackPhase == 3 -> DigimonAnimationType.IDLE // Opponent attack on opponent screen
battleSystem.attackPhase == 4 -> DigimonAnimationType.IDLE // Opponent attack on player screen battleSystem.attackPhase == 4 -> DigimonAnimationType.IDLE // Opponent attack on player screen
else -> DigimonAnimationType.IDLE else -> DigimonAnimationType.IDLE
} }
// Calculate vertical offset for dodge animation // Calculate vertical offset for dodge animation
val verticalOffset = if (battleSystem.isPlayerDodging) { val verticalOffset = if (battleSystem.isPlayerDodging) {
@ -1291,10 +1291,10 @@ fun PlayerBattleView(
} else { } else {
0.dp 0.dp
} }
AnimatedSpriteImage( AnimatedSpriteImage(
characterId = activeCharacter?.charaId ?: "dim011_mon01", characterId = activeCharacter?.charaId ?: "dim011_mon01",
animationType = animationType, animationType = animationType,
modifier = Modifier modifier = Modifier
.size(80.dp) .size(80.dp)
.scale(-1f, 1f) // Flip player Digimon horizontally .scale(-1f, 1f) // Flip player Digimon horizontally
@ -1302,59 +1302,59 @@ fun PlayerBattleView(
x = hitOffset, x = hitOffset,
y = verticalOffset y = verticalOffset
), ),
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
reloadMappings = false, reloadMappings = false,
animationOffset = 0L // Player animation starts immediately animationOffset = 0L // Player animation starts immediately
) )
// Attack sprite visibility and positioning based on attack phase // Attack sprite visibility and positioning based on attack phase
val shouldShowAttack = when (battleSystem.attackPhase) { val shouldShowAttack = when (battleSystem.attackPhase) {
1 -> false // Both attacks from middle screen 1 -> false // Both attacks from middle screen
2 -> false // Player attack on enemy screen 2 -> false // Player attack on enemy screen
3 -> true // Enemy attack on player screen 3 -> true // Enemy attack on player screen
else -> false else -> false
}
if (shouldShowAttack) {
val xOffset = when (battleSystem.attackPhase) {
3 -> (-attackAnimationProgress * 400 + 350).dp // Enemy attack on player screen - start more to the right
else -> 0.dp
} }
if (shouldShowAttack) {
val xOffset = when (battleSystem.attackPhase) {
3 -> (-attackAnimationProgress * 400 + 350).dp // Enemy attack on player screen - start more to the right
else -> 0.dp
}
// Use opponent character ID for Phase 3 (enemy attack) // Use opponent character ID for Phase 3 (enemy attack)
val characterId = when (battleSystem.attackPhase) { val characterId = when (battleSystem.attackPhase) {
3 -> opponent?.charaId ?: "dim011_mon01" // Use opponent's character ID 3 -> opponent?.charaId ?: "dim011_mon01" // Use opponent's character ID
else -> activeCharacter?.charaId ?: "dim011_mon01" // Use player's character ID else -> activeCharacter?.charaId ?: "dim011_mon01" // Use player's character ID
}
// 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
// Handle sprite transition previousAttackPhase = battleSystem.attackPhase
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("PlayerBattleView - Attack sprite - Phase: ${battleSystem.attackPhase}, Progress: $attackAnimationProgress, X Offset: $xOffset, CurrentView: ${battleSystem.currentView}") println("PlayerBattleView - Attack sprite - Phase: ${battleSystem.attackPhase}, Progress: $attackAnimationProgress, X Offset: $xOffset, CurrentView: ${battleSystem.currentView}")
if (!isTransitioning && !hidePlayerAttackSprite) { if (!isTransitioning && !hidePlayerAttackSprite) {
AttackSpriteImage( AttackSpriteImage(
characterId = characterId, characterId = characterId,
isLarge = true, isLarge = true,
modifier = Modifier modifier = Modifier
.size(60.dp) .size(60.dp)
.offset( .offset(
x = xOffset, x = xOffset,
y = 0.dp y = 0.dp
) )
.scale(1f, 1f), // Don't flip enemy attacks on player screen .scale(1f, 1f), // Don't flip enemy attacks on player screen
contentScale = ContentScale.Fit contentScale = ContentScale.Fit
) )
} }
} }
} }
@ -1400,19 +1400,19 @@ fun EnemyBattleView(
Column( Column(
horizontalAlignment = getLandscapeHorizontalAlignment() horizontalAlignment = getLandscapeHorizontalAlignment()
) { ) {
// Enemy HP bar // Enemy HP bar
LinearProgressIndicator( LinearProgressIndicator(
progress = battleSystem.opponentHP / (activeCharacter?.baseHp?.toFloat() ?: 100f), progress = battleSystem.opponentHP / (activeCharacter?.baseHp?.toFloat() ?: 100f),
modifier = getLandscapeModifier(), modifier = getLandscapeModifier(),
color = Color.Red, color = Color.Red,
trackColor = Color.Gray trackColor = Color.Gray
) )
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
// Enemy HP display numbers // Enemy HP display numbers
Text( Text(
text = "Enemy HP: ${battleSystem.opponentHP.toInt()}/${activeCharacter?.baseHp ?: 100}", text = "Enemy HP: ${battleSystem.opponentHP.toInt()}/${activeCharacter?.baseHp ?: 100}",
fontSize = getLandscapeFontSize(), fontSize = getLandscapeFontSize(),
color = Color.White, color = Color.White,
style = TextStyle( style = TextStyle(
@ -1649,10 +1649,24 @@ fun BattlesScreen() {
// Format as "dim" + cardId + "_mon" + (charaIndex + 1) // Format as "dim" + cardId + "_mon" + (charaIndex + 1)
val formattedCardId = String.format("dim%03d_mon%02d", cardId, charaIndex + 1) val formattedCardId = String.format("dim%03d_mon%02d", cardId, charaIndex + 1)
// Create APIBattleCharacter from database character
val playerCharacter = APIBattleCharacter(
name = "Player Digimon", // We could get this from the database if needed
namekey = "player_digimon", // Name key for the character
charaId = formattedCardId, // Use the formatted card ID for sprite loading
stage = characterData.stage,
attribute = characterData.attribute.ordinal, // Convert enum to int
baseHp = 1000, // Default values - API will provide correct values
currentHp = 1000,
baseBp = 1000.0f,
baseAp = 1000.0f
)
// Update UI state on main thread // Update UI state on main thread
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
activeUserCharacter = activeChar activeUserCharacter = activeChar
activeCardId = formattedCardId activeCardId = formattedCardId
activeCharacter = playerCharacter // Set the active character for battle
} }
println("BATTLESCREEN: Loaded active character from database:") println("BATTLESCREEN: Loaded active character from database:")
@ -2069,25 +2083,30 @@ fun BattlesScreen() {
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
items(opponentsList) { opponent -> items(opponentsList) { opponent ->
Button( Button(
onClick = { onClick = {
activeCardId?.let { cardId -> activeCardId?.let { cardId ->
selectedOpponent = opponent selectedOpponent = opponent
// Randomly select background set (0, 1, or 2) // Randomly select background set (0, 1, or 2)
selectedBackgroundSet = kotlin.random.Random.nextInt(3) selectedBackgroundSet = kotlin.random.Random.nextInt(3)
RetrofitHelper().getPVPWinner(context, 0, 2, cardId, 0, 0, opponent.charaId, 0) { apiResult -> RetrofitHelper().getPVPWinner(context, 0, 2, cardId, 0, 0, opponent.charaId, 0) { apiResult ->
currentView = "battle-main" // Update player character HP from API response
} activeCharacter = activeCharacter?.copy(
baseHp = apiResult.playerHP,
currentHp = apiResult.playerHP
)
currentView = "battle-main"
}
} ?: run { } ?: run {
println("BATTLESCREEN: No active card ID found in database") println("BATTLESCREEN: No active card ID found in database")
} }
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text("Battle ${opponent.name}") Text("Battle ${opponent.name}")
}
} }
} }
}
backButton() backButton()
} }
@ -2125,25 +2144,30 @@ fun BattlesScreen() {
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
items(opponentsList) { opponent -> items(opponentsList) { opponent ->
Button( Button(
onClick = { onClick = {
activeCardId?.let { cardId -> activeCardId?.let { cardId ->
selectedOpponent = opponent selectedOpponent = opponent
// Randomly select background set (0, 1, or 2) // Randomly select background set (0, 1, or 2)
selectedBackgroundSet = kotlin.random.Random.nextInt(3) selectedBackgroundSet = kotlin.random.Random.nextInt(3)
RetrofitHelper().getPVPWinner(context, 0, 2, cardId, 1, 0, opponent.charaId, 1) { apiResult -> RetrofitHelper().getPVPWinner(context, 0, 2, cardId, 1, 0, opponent.charaId, 1) { apiResult ->
// Update player character HP from API response
activeCharacter = activeCharacter?.copy(
baseHp = apiResult.playerHP,
currentHp = apiResult.playerHP
)
currentView = "battle-main" currentView = "battle-main"
} }
} ?: run { } ?: run {
println("BATTLESCREEN: No active card ID found in database") println("BATTLESCREEN: No active card ID found in database")
} }
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text("Battle ${opponent.name}") Text("Battle ${opponent.name}")
}
} }
} }
}
backButton() backButton()
} }
@ -2181,25 +2205,30 @@ fun BattlesScreen() {
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
items(opponentsList) { opponent -> items(opponentsList) { opponent ->
Button( Button(
onClick = { onClick = {
activeCardId?.let { cardId -> activeCardId?.let { cardId ->
selectedOpponent = opponent selectedOpponent = opponent
// Randomly select background set (0, 1, or 2) // Randomly select background set (0, 1, or 2)
selectedBackgroundSet = kotlin.random.Random.nextInt(3) selectedBackgroundSet = kotlin.random.Random.nextInt(3)
RetrofitHelper().getPVPWinner(context, 0, 2, cardId, 2, 0, opponent.charaId, 2) { apiResult -> RetrofitHelper().getPVPWinner(context, 0, 2, cardId, 2, 0, opponent.charaId, 2) { apiResult ->
// Update player character HP from API response
activeCharacter = activeCharacter?.copy(
baseHp = apiResult.playerHP,
currentHp = apiResult.playerHP
)
currentView = "battle-main" currentView = "battle-main"
} }
} ?: run { } ?: run {
println("BATTLESCREEN: No active card ID found in database") println("BATTLESCREEN: No active card ID found in database")
} }
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text("Battle ${opponent.name}") Text("Battle ${opponent.name}")
}
} }
} }
}
backButton() backButton()
} }
@ -2237,25 +2266,30 @@ fun BattlesScreen() {
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
items(opponentsList) { opponent -> items(opponentsList) { opponent ->
Button( Button(
onClick = { onClick = {
activeCardId?.let { cardId -> activeCardId?.let { cardId ->
selectedOpponent = opponent selectedOpponent = opponent
// Randomly select background set (0, 1, or 2) // Randomly select background set (0, 1, or 2)
selectedBackgroundSet = kotlin.random.Random.nextInt(3) selectedBackgroundSet = kotlin.random.Random.nextInt(3)
RetrofitHelper().getPVPWinner(context, 0, 2, cardId, 3, 0, opponent.charaId, 3) { apiResult -> RetrofitHelper().getPVPWinner(context, 0, 2, cardId, 3, 0, opponent.charaId, 3) { apiResult ->
// Update player character HP from API response
activeCharacter = activeCharacter?.copy(
baseHp = apiResult.playerHP,
currentHp = apiResult.playerHP
)
currentView = "battle-main" currentView = "battle-main"
} }
} ?: run { } ?: run {
println("BATTLESCREEN: No active card ID found in database") println("BATTLESCREEN: No active card ID found in database")
} }
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text("Battle ${opponent.name}") Text("Battle ${opponent.name}")
}
} }
} }
}
backButton() backButton()
} }