mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 13:55:54 -04:00
Fix Pixel unit nation colors after combat (#7033)
* Fix Pixel Unit national colors reverting to white after battle * A little linting on top A little linting on top
This commit is contained in:
parent
068e1587bc
commit
02226018fa
@ -34,7 +34,7 @@ open class TileGroup(
|
||||
/*
|
||||
Layers (reordered in TileGroupMap):
|
||||
Base image (+ overlay)
|
||||
Terrain Feature overlay (including roads and pixel units)
|
||||
Terrain Feature overlay (including roads)
|
||||
Misc: improvements, resources, yields, worked, resources, border, arrows, and starting locations in editor
|
||||
Pixel Units
|
||||
Highlight, Fog, Crosshair layer (in that order)
|
||||
|
@ -1,13 +1,7 @@
|
||||
package com.unciv.ui.worldscreen.bottombar
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.math.Interpolation
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.FloatAction
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.UncivGame
|
||||
@ -23,6 +17,8 @@ import com.unciv.ui.audio.Sounds
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.worldscreen.WorldScreen
|
||||
import com.unciv.ui.worldscreen.bottombar.BattleTableHelpers.flashWoundedCombatants
|
||||
import com.unciv.ui.worldscreen.bottombar.BattleTableHelpers.getHealthBar
|
||||
import kotlin.math.max
|
||||
|
||||
class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
@ -30,7 +26,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
init {
|
||||
isVisible = false
|
||||
skin = BaseScreen.skin
|
||||
background = ImageGetter.getBackground(ImageGetter.getBlue().apply { a=0.8f })
|
||||
background = ImageGetter.getBackground(ImageGetter.getBlue().apply { a = 0.8f })
|
||||
|
||||
defaults().pad(5f)
|
||||
pad(5f)
|
||||
@ -132,114 +128,94 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
add(defender.getDefendingStrength(attacker.isRanged()).toString() + defenceIcon).row()
|
||||
|
||||
|
||||
val quarterScreen = worldScreen.stage.width/4
|
||||
val quarterScreen = worldScreen.stage.width / 4
|
||||
|
||||
fun getModifierTable(key: String, value: Int) = Table().apply {
|
||||
val description = if (key.startsWith("vs "))
|
||||
("vs [" + key.drop(3) + "]").tr()
|
||||
else key.tr()
|
||||
val percentage = (if (value > 0) "+" else "") + value + "%"
|
||||
val upOrDownLabel = if (value > 0f) "⬆".toLabel(Color.GREEN)
|
||||
else "⬇".toLabel(Color.RED)
|
||||
|
||||
add(upOrDownLabel)
|
||||
val modifierLabel = "$percentage $description".toLabel(fontSize = 14).apply { wrap = true }
|
||||
add(modifierLabel).width(quarterScreen - upOrDownLabel.minWidth)
|
||||
}
|
||||
val attackerModifiers =
|
||||
BattleDamage.getAttackModifiers(attacker, defender).map {
|
||||
val description = if (it.key.startsWith("vs "))
|
||||
("vs [" + it.key.replace("vs ", "") + "]").tr()
|
||||
else it.key.tr()
|
||||
val percentage = (if (it.value > 0) "+" else "") + it.value + "%"
|
||||
|
||||
val upOrDownLabel = if (it.value > 0f) "⬆".toLabel(Color.GREEN) else "⬇".toLabel(
|
||||
Color.RED)
|
||||
val modifierTable = Table()
|
||||
modifierTable.add(upOrDownLabel)
|
||||
val modifierLabel = "$percentage $description".toLabel(fontSize = 14).apply { wrap=true }
|
||||
modifierTable.add(modifierLabel).width(quarterScreen)
|
||||
modifierTable
|
||||
getModifierTable(it.key, it.value)
|
||||
}
|
||||
val defenderModifiers =
|
||||
if (defender is MapUnitCombatant)
|
||||
BattleDamage.getDefenceModifiers(attacker, defender).map {
|
||||
val description = if(it.key.startsWith("vs ")) ("vs ["+it.key.replace("vs ","")+"]").tr() else it.key.tr()
|
||||
val percentage = (if(it.value>0)"+" else "")+ it.value +"%"
|
||||
val upOrDownLabel = if (it.value > 0f) "⬆".toLabel(Color.GREEN) else "⬇".toLabel(
|
||||
Color.RED)
|
||||
val modifierTable = Table()
|
||||
modifierTable.add(upOrDownLabel)
|
||||
val modifierLabel = "$percentage $description".toLabel(fontSize = 14).apply { wrap=true }
|
||||
modifierTable.add(modifierLabel).width(quarterScreen)
|
||||
modifierTable
|
||||
getModifierTable(it.key, it.value)
|
||||
}
|
||||
else listOf()
|
||||
|
||||
for (i in 0..max(attackerModifiers.size,defenderModifiers.size)) {
|
||||
if (attackerModifiers.size > i)
|
||||
add(attackerModifiers[i])
|
||||
else add().width(quarterScreen)
|
||||
if (defenderModifiers.size > i)
|
||||
add(defenderModifiers[i])
|
||||
else add().width(quarterScreen)
|
||||
for (i in 0..max(attackerModifiers.size, defenderModifiers.size)) {
|
||||
if (i < attackerModifiers.size) add(attackerModifiers[i]) else add()
|
||||
if (i < defenderModifiers.size) add(defenderModifiers[i]) else add()
|
||||
row().pad(2f)
|
||||
}
|
||||
|
||||
// from Battle.addXp(), check for can't gain more XP from Barbarians
|
||||
val modConstants = attacker.getCivInfo().gameInfo.ruleSet.modOptions.constants
|
||||
if (attacker is MapUnitCombatant && attacker.unit.promotions.totalXpProduced() >= modConstants.maxXPfromBarbarians
|
||||
val maxXPFromBarbarians = attacker.getCivInfo().gameInfo.ruleSet.modOptions.constants.maxXPfromBarbarians
|
||||
if (attacker is MapUnitCombatant && attacker.unit.promotions.totalXpProduced() >= maxXPFromBarbarians
|
||||
&& defender.getCivInfo().isBarbarian()){
|
||||
add("Cannot gain more XP from Barbarians".tr().toLabel(fontSize = 16).apply { wrap=true }).width(quarterScreen)
|
||||
add("Cannot gain more XP from Barbarians".toLabel(fontSize = 16).apply { wrap = true }).width(quarterScreen)
|
||||
row()
|
||||
}
|
||||
|
||||
var damageToDefender = BattleDamage.calculateDamageToDefender(attacker, defender, true)
|
||||
var damageToAttacker = BattleDamage.calculateDamageToAttacker(attacker, defender, true)
|
||||
|
||||
|
||||
if (damageToAttacker>attacker.getHealth() && damageToDefender>defender.getHealth()) // when damage exceeds health, we don't want to show negative health numbers
|
||||
// Also if both parties are supposed to die it's not indicative of who is more likely to win
|
||||
// So we "normalize" the damages until one dies
|
||||
{
|
||||
if(damageToDefender/defender.getHealth().toFloat() > damageToAttacker/attacker.getHealth().toFloat()) // defender dies quicker ie first
|
||||
{
|
||||
if (damageToAttacker > attacker.getHealth() && damageToDefender > defender.getHealth()) {
|
||||
// when damage exceeds health, we don't want to show negative health numbers
|
||||
// Also if both parties are supposed to die it's not indicative of who is more likely to win
|
||||
// So we "normalize" the damages until one dies
|
||||
if (damageToDefender * attacker.getHealth() > damageToAttacker * defender.getHealth()) { // defender dies quicker ie first
|
||||
// Both damages *= (defender.health/damageToDefender)
|
||||
damageToDefender = defender.getHealth()
|
||||
damageToAttacker *= (defender.getHealth()/damageToDefender.toFloat()).toInt()
|
||||
} else{ // attacker dies first
|
||||
damageToAttacker *= (defender.getHealth() / damageToDefender.toFloat()).toInt()
|
||||
} else { // attacker dies first
|
||||
// Both damages *= (attacker.health/damageToAttacker)
|
||||
damageToAttacker = attacker.getHealth()
|
||||
damageToDefender *= (attacker.getHealth()/damageToAttacker.toFloat()).toInt()
|
||||
damageToDefender *= (attacker.getHealth() / damageToAttacker.toFloat()).toInt()
|
||||
}
|
||||
}
|
||||
else if (damageToAttacker>attacker.getHealth()) damageToAttacker=attacker.getHealth()
|
||||
else if (damageToDefender>defender.getHealth()) damageToDefender=defender.getHealth()
|
||||
else if (damageToAttacker > attacker.getHealth()) damageToAttacker = attacker.getHealth()
|
||||
else if (damageToDefender > defender.getHealth()) damageToDefender = defender.getHealth()
|
||||
|
||||
|
||||
if(attacker.isMelee() && (defender.isCivilian()
|
||||
|| defender is CityCombatant && defender.isDefeated())) {
|
||||
add("")
|
||||
add(
|
||||
if (defender.isCivilian()
|
||||
&& (defender as MapUnitCombatant).unit.hasUnique(UniqueType.Uncapturable)
|
||||
) ""
|
||||
else if (defender.isCivilian()) "Captured!".tr()
|
||||
else "Occupied!".tr()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
else {
|
||||
if (attacker.isMelee() &&
|
||||
(defender.isCivilian() || defender is CityCombatant && defender.isDefeated())) {
|
||||
add()
|
||||
val defeatedText = when {
|
||||
!defender.isCivilian() -> "Occupied!"
|
||||
(defender as MapUnitCombatant).unit.hasUnique(UniqueType.Uncapturable) -> ""
|
||||
else -> "Captured!"
|
||||
}
|
||||
add(defeatedText.toLabel())
|
||||
} else {
|
||||
add(getHealthBar(attacker.getHealth(), attacker.getMaxHealth(), damageToAttacker))
|
||||
add(getHealthBar(defender.getHealth(), defender.getMaxHealth(), damageToDefender))
|
||||
}
|
||||
|
||||
row().pad(5f)
|
||||
val attackText : String = when (attacker) {
|
||||
val attackText: String = when (attacker) {
|
||||
is CityCombatant -> "Bombard"
|
||||
else -> "Attack"
|
||||
}
|
||||
val attackButton = attackText.toTextButton().apply { color= Color.RED }
|
||||
|
||||
var attackableTile : AttackableTile? = null
|
||||
var attackableTile: AttackableTile? = null
|
||||
|
||||
if (attacker.canAttack()) {
|
||||
if (attacker is MapUnitCombatant) {
|
||||
attackableTile = BattleHelper
|
||||
.getAttackableEnemies(attacker.unit, attacker.unit.movement.getDistanceToTiles())
|
||||
.firstOrNull{ it.tileToAttack == defender.getTile()}
|
||||
}
|
||||
else if (attacker is CityCombatant)
|
||||
{
|
||||
} else if (attacker is CityCombatant) {
|
||||
val canBombard = UnitAutomation.getBombardTargets(attacker.city).contains(defender.getTile())
|
||||
if (canBombard) {
|
||||
attackableTile = AttackableTile(attacker.getTile(), defender.getTile(), 0f)
|
||||
@ -250,9 +226,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
if (!worldScreen.isPlayersTurn || attackableTile == null) {
|
||||
attackButton.disable()
|
||||
attackButton.label.color = Color.GRAY
|
||||
}
|
||||
|
||||
else {
|
||||
} else {
|
||||
attackButton.onClick(UncivSound.Silent) { // onAttackButtonClicked will do the sound
|
||||
onAttackButtonClicked(attacker, defender, attackableTile, damageToAttacker, damageToDefender)
|
||||
}
|
||||
@ -262,7 +236,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
|
||||
pack()
|
||||
|
||||
setPosition(worldScreen.stage.width/2-width/2, 5f)
|
||||
setPosition(worldScreen.stage.width / 2 - width / 2, 5f)
|
||||
}
|
||||
|
||||
private fun onAttackButtonClicked(
|
||||
@ -283,46 +257,9 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
Sounds.play(attacker.getAttackSound())
|
||||
Battle.attackOrNuke(attacker, attackableTile)
|
||||
|
||||
val actorsToFlashRed = arrayListOf<Actor>()
|
||||
|
||||
if (damageToDefender != 0)
|
||||
actorsToFlashRed.addAll(getMapActorsForCombatant(defender))
|
||||
if (damageToAttacker != 0)
|
||||
actorsToFlashRed.addAll(getMapActorsForCombatant(attacker))
|
||||
fun updateRedPercent(percent: Float) {
|
||||
for (actor in actorsToFlashRed)
|
||||
actor.color = Color.WHITE.cpy().lerp(Color.RED, percent)
|
||||
}
|
||||
worldScreen.stage.addAction(Actions.sequence(
|
||||
object : FloatAction(0f, 1f, 0.3f, Interpolation.sine) {
|
||||
override fun update(percent: Float) = updateRedPercent(percent)
|
||||
},
|
||||
object : FloatAction(0f, 1f, 0.3f, Interpolation.sine) {
|
||||
override fun update(percent: Float) = updateRedPercent(1 - percent)
|
||||
}
|
||||
))
|
||||
worldScreen.flashWoundedCombatants(attacker, damageToAttacker, defender, damageToDefender)
|
||||
}
|
||||
|
||||
fun getMapActorsForCombatant(combatant: ICombatant):Sequence<Actor> =
|
||||
sequence {
|
||||
val tilegroups =
|
||||
worldScreen.mapHolder.tileGroups[combatant.getTile()]!!
|
||||
when {
|
||||
combatant.isCity() -> yieldAll(tilegroups.mapNotNull { it.icons.improvementIcon })
|
||||
combatant.isCivilian() -> {
|
||||
for (tileGroup in tilegroups) {
|
||||
tileGroup.icons.civilianUnitIcon?.let { yield(it) }
|
||||
yieldAll(tileGroup.pixelCivilianUnitGroup.children)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
for (tileGroup in tilegroups) {
|
||||
tileGroup.icons.militaryUnitIcon?.let { yield(it) }
|
||||
yieldAll(tileGroup.pixelMilitaryUnitGroup.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun simulateNuke(attacker: MapUnitCombatant, targetTile: TileInfo){
|
||||
clear()
|
||||
@ -372,37 +309,6 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
|
||||
pack()
|
||||
|
||||
setPosition(worldScreen.stage.width/2-width/2, 5f)
|
||||
setPosition(worldScreen.stage.width / 2 - width / 2, 5f)
|
||||
}
|
||||
|
||||
private fun getHealthBar(currentHealth: Int, maxHealth: Int, expectedDamage:Int): Table {
|
||||
val healthBar = Table()
|
||||
val totalWidth = 100f
|
||||
fun addHealthToBar(image: Image, amount:Int) {
|
||||
val width = totalWidth * amount/maxHealth
|
||||
healthBar.add(image).size(width.coerceIn(0f,totalWidth),3f)
|
||||
}
|
||||
addHealthToBar(ImageGetter.getDot(Color.BLACK), maxHealth-currentHealth)
|
||||
|
||||
val damagedHealth = ImageGetter.getDot(Color.FIREBRICK)
|
||||
if (UncivGame.Current.settings.continuousRendering) {
|
||||
damagedHealth.addAction(Actions.repeat(RepeatAction.FOREVER, Actions.sequence(
|
||||
Actions.color(Color.BLACK,0.7f),
|
||||
Actions.color(Color.FIREBRICK,0.7f)
|
||||
))) }
|
||||
addHealthToBar(damagedHealth,expectedDamage)
|
||||
|
||||
val remainingHealth = currentHealth-expectedDamage
|
||||
val remainingHealthDot = ImageGetter.getWhiteDot()
|
||||
remainingHealthDot.color = when {
|
||||
remainingHealth / maxHealth.toFloat() > 2 / 3f -> Color.GREEN
|
||||
remainingHealth / maxHealth.toFloat() > 1 / 3f -> Color.ORANGE
|
||||
else -> Color.RED
|
||||
}
|
||||
addHealthToBar(remainingHealthDot ,remainingHealth)
|
||||
|
||||
healthBar.pack()
|
||||
return healthBar
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,94 @@
|
||||
package com.unciv.ui.worldscreen.bottombar
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.math.Interpolation
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.FloatAction
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.battle.ICombatant
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.worldscreen.WorldScreen
|
||||
|
||||
object BattleTableHelpers {
|
||||
|
||||
fun WorldScreen.flashWoundedCombatants(
|
||||
attacker: ICombatant, damageToAttacker: Int,
|
||||
defender: ICombatant, damageToDefender: Int
|
||||
) {
|
||||
fun getMapActorsForCombatant(combatant: ICombatant):Sequence<Actor> =
|
||||
sequence {
|
||||
val tilegroups = mapHolder.tileGroups[combatant.getTile()]!!
|
||||
when {
|
||||
combatant.isCity() -> yieldAll(tilegroups.mapNotNull { it.icons.improvementIcon })
|
||||
combatant.isCivilian() -> {
|
||||
for (tileGroup in tilegroups) {
|
||||
tileGroup.icons.civilianUnitIcon?.let { yield(it) }
|
||||
yieldAll(tileGroup.pixelCivilianUnitGroup.children)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
for (tileGroup in tilegroups) {
|
||||
tileGroup.icons.militaryUnitIcon?.let { yield(it) }
|
||||
yieldAll(tileGroup.pixelMilitaryUnitGroup.children)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val actorsToFlashRed =
|
||||
sequence {
|
||||
if (damageToDefender != 0) yieldAll(getMapActorsForCombatant(defender))
|
||||
if (damageToAttacker != 0) yieldAll(getMapActorsForCombatant(attacker))
|
||||
}.mapTo(arrayListOf()) { it to it.color.cpy() }
|
||||
|
||||
fun updateRedPercent(percent: Float) {
|
||||
for ((actor, color) in actorsToFlashRed)
|
||||
actor.color = color.cpy().lerp(Color.RED, percent)
|
||||
}
|
||||
|
||||
stage.addAction(
|
||||
Actions.sequence(
|
||||
object : FloatAction(0f, 1f, 0.3f, Interpolation.sine) {
|
||||
override fun update(percent: Float) = updateRedPercent(percent)
|
||||
},
|
||||
object : FloatAction(0f, 1f, 0.3f, Interpolation.sine) {
|
||||
override fun update(percent: Float) = updateRedPercent(1 - percent)
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
fun getHealthBar(currentHealth: Int, maxHealth: Int, expectedDamage: Int): Table {
|
||||
val healthBar = Table()
|
||||
val totalWidth = 100f
|
||||
fun addHealthToBar(image: Image, amount:Int) {
|
||||
val width = totalWidth * amount / maxHealth
|
||||
healthBar.add(image).size(width.coerceIn(0f, totalWidth),3f)
|
||||
}
|
||||
addHealthToBar(ImageGetter.getDot(Color.BLACK), maxHealth - currentHealth)
|
||||
|
||||
val damagedHealth = ImageGetter.getDot(Color.FIREBRICK)
|
||||
if (UncivGame.Current.settings.continuousRendering) {
|
||||
damagedHealth.addAction(Actions.repeat(
|
||||
RepeatAction.FOREVER, Actions.sequence(
|
||||
Actions.color(Color.BLACK, 0.7f),
|
||||
Actions.color(Color.FIREBRICK, 0.7f)
|
||||
))) }
|
||||
addHealthToBar(damagedHealth,expectedDamage)
|
||||
|
||||
val remainingHealth = currentHealth - expectedDamage
|
||||
val remainingHealthDot = ImageGetter.getWhiteDot()
|
||||
remainingHealthDot.color = when {
|
||||
remainingHealth / maxHealth.toFloat() > 2 / 3f -> Color.GREEN
|
||||
remainingHealth / maxHealth.toFloat() > 1 / 3f -> Color.ORANGE
|
||||
else -> Color.RED
|
||||
}
|
||||
addHealthToBar(remainingHealthDot ,remainingHealth)
|
||||
|
||||
healthBar.pack()
|
||||
return healthBar
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user