chore: Moved unit functions to UnitManager

This commit is contained in:
Yair Morgenstern 2023-01-18 17:32:42 +02:00
parent 1a64184600
commit 4488a6fe51
37 changed files with 320 additions and 348 deletions

View File

@ -6,9 +6,9 @@ import com.unciv.json.HashMapVector2
import com.unciv.json.json
import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.PerpetualConstruction
import com.unciv.logic.civilization.managers.TechManager
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
import com.unciv.logic.civilization.managers.TechManager
import com.unciv.models.ruleset.ModOptions
import com.unciv.models.ruleset.Ruleset
@ -178,7 +178,7 @@ object BackwardCompatibility {
fun GameInfo.convertFortify() {
val reg = Regex("""^Fortify\s+(\d+)([\w\s]*)""")
for (civInfo in civilizations) {
for (unit in civInfo.getCivUnits()) {
for (unit in civInfo.units.getCivUnits()) {
if (unit.action != null && reg.matches(unit.action!!)) {
val (turns, heal) = reg.find(unit.action!!)!!.destructured
unit.turnsFortified = turns.toInt()

View File

@ -538,7 +538,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
yieldAll(civilizations.filter { it.isCityState() })
yieldAll(civilizations.filter { !it.isCityState() })
}) {
for (unit in civInfo.getCivUnits())
for (unit in civInfo.units.getCivUnits())
unit.updateVisibleTiles(false) // this needs to be done after all the units are assigned to their civs and all other transients are set
civInfo.cache.updateSightAndResources() // only run ONCE and not for each unit - this is a huge performance saver!

View File

@ -323,7 +323,7 @@ object GameStarter {
}
fun placeNearStartingPosition(unitName: String) {
civ.placeUnitNearTile(startingLocation.position, unitName)
civ.units.placeUnitNearTile(startingLocation.position, unitName)
}
// Determine starting units based on starting era

View File

@ -128,8 +128,8 @@ object Automation {
}
val totalCarriableUnits =
civInfo.getCivUnits().count { it.matchesFilter(carryFilter) }
val totalCarryingSlots = civInfo.getCivUnits().sumOf { getCarryAmount(it) }
civInfo.units.getCivUnits().count { it.matchesFilter(carryFilter) }
val totalCarryingSlots = civInfo.units.getCivUnits().sumOf { getCarryAmount(it) }
return totalCarriableUnits < totalCarryingSlots
}

View File

@ -30,7 +30,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private val units = cityInfo.getRuleset().units.values
private val civUnits = civInfo.getCivUnits()
private val civUnits = civInfo.units.getCivUnits()
private val militaryUnits = civUnits.count { it.baseUnit.isMilitary() }
private val workers = civUnits.count { it.hasUniqueToBuildImprovements && it.isCivilian() }.toFloat()
private val cities = civInfo.cities.size

View File

@ -10,9 +10,9 @@ class BarbarianAutomation(val civInfo: CivilizationInfo) {
fun automate() {
// ranged go first, after melee and then everyone else
civInfo.getCivUnits().filter { it.baseUnit.isRanged() }.forEach { automateUnit(it) }
civInfo.getCivUnits().filter { it.baseUnit.isMelee() }.forEach { automateUnit(it) }
civInfo.getCivUnits().filter { !it.baseUnit.isRanged() && !it.baseUnit.isMelee() }.forEach { automateUnit(it) }
civInfo.units.getCivUnits().filter { it.baseUnit.isRanged() }.forEach { automateUnit(it) }
civInfo.units.getCivUnits().filter { it.baseUnit.isMelee() }.forEach { automateUnit(it) }
civInfo.units.getCivUnits().filter { !it.baseUnit.isRanged() && !it.baseUnit.isMelee() }.forEach { automateUnit(it) }
}
private fun automateUnit(unit: MapUnit) {

View File

@ -16,11 +16,11 @@ import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.managers.ReligionState
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.civilization.managers.ReligionState
import com.unciv.logic.map.BFS
import com.unciv.logic.map.TileInfo
import com.unciv.logic.trade.Trade
@ -512,7 +512,7 @@ object NextTurnAutomation {
if (resourceCount >= Automation.getReservedSpaceResourceAmount(civInfo))
continue
val unitToDisband = civInfo.getCivUnits()
val unitToDisband = civInfo.units.getCivUnits()
.filter { it.baseUnit.requiresResource(resource) }
.minByOrNull { it.getForceEvaluation() }
unitToDisband?.disband()
@ -710,7 +710,7 @@ object NextTurnAutomation {
if (civInfo.cities.isEmpty() || civInfo.diplomacy.isEmpty()) return
if (civInfo.isAtWar() || civInfo.getHappiness() <= 0) return
val ourMilitaryUnits = civInfo.getCivUnits().filter { !it.isCivilian() }.count()
val ourMilitaryUnits = civInfo.units.getCivUnits().filter { !it.isCivilian() }.count()
if (ourMilitaryUnits < civInfo.cities.size) return
if (ourMilitaryUnits < 4) return // to stop AI declaring war at the beginning of games when everyone isn't set up well enough
@ -751,7 +751,7 @@ object NextTurnAutomation {
val ourCity = closestCities.city1
val theirCity = closestCities.city2
if (civInfo.getCivUnits().filter { it.isMilitary() }.none {
if (civInfo.units.getCivUnits().filter { it.isMilitary() }.none {
val damageRecievedWhenAttacking =
BattleDamage.calculateDamageToAttacker(
MapUnitCombatant(it),
@ -875,7 +875,7 @@ object NextTurnAutomation {
private fun automateUnits(civInfo: CivilizationInfo) {
val sortedUnits = civInfo.getCivUnits().sortedBy { unit ->
val sortedUnits = civInfo.units.getCivUnits().sortedBy { unit ->
when {
unit.baseUnit.isAirUnit() -> 2
unit.baseUnit.isRanged() -> 3
@ -915,7 +915,7 @@ object NextTurnAutomation {
val settlerUnits = civInfo.gameInfo.ruleSet.units.values
.filter { it.hasUnique(UniqueType.FoundCity) && it.isBuildable(civInfo) }
if (settlerUnits.isEmpty()) return
if (civInfo.getCivUnits().none { it.hasUnique(UniqueType.FoundCity) }
if (civInfo.units.getCivUnits().none { it.hasUnique(UniqueType.FoundCity) }
&& civInfo.cities.none {
val currentConstruction = it.cityConstructions.getCurrentConstruction()
currentConstruction is BaseUnit && currentConstruction.hasUnique(UniqueType.FoundCity)

View File

@ -41,7 +41,7 @@ object ReligionAutomation {
val citiesWithoutOurReligion = civInfo.cities.filter { it.religion.getMajorityReligion() != civInfo.religionManager.religion!! }
// The original had a cap at 4 missionaries total, but 1/4 * the number of cities should be more appropriate imo
if (citiesWithoutOurReligion.count() >
4 * civInfo.getCivUnits().count { it.canDoReligiousAction(Constants.spreadReligion) || it.canDoReligiousAction(Constants.removeHeresy) }
4 * civInfo.units.getCivUnits().count { it.canDoReligiousAction(Constants.spreadReligion) || it.canDoReligiousAction(Constants.removeHeresy) }
) {
val (city, pressureDifference) = citiesWithoutOurReligion.map { city ->
city to city.religion.getPressureDeficit(civInfo.religionManager.religion?.name)
@ -57,7 +57,7 @@ object ReligionAutomation {
val holyCity = civInfo.religionManager.getHolyCity()
if (holyCity != null
&& holyCity in civInfo.cities
&& civInfo.getCivUnits().count { it.hasUnique(UniqueType.PreventSpreadingReligion) } == 0
&& civInfo.units.getCivUnits().count { it.hasUnique(UniqueType.PreventSpreadingReligion) } == 0
&& !holyCity.religion.isProtectedByInquisitor()
) {
buyInquisitorNear(civInfo, holyCity)
@ -77,7 +77,7 @@ object ReligionAutomation {
// Todo: buy Great People post industrial era
// Just buy missionaries to spread our religion outside of our civ
if (civInfo.getCivUnits().count { it.canDoReligiousAction(Constants.spreadReligion) } < 4) {
if (civInfo.units.getCivUnits().count { it.canDoReligiousAction(Constants.spreadReligion) } < 4) {
buyMissionaryInAnyCity(civInfo)
return
}

View File

@ -197,7 +197,7 @@ object SpecificUnitAutomation {
if (unit.civInfo.gameInfo.turns == 0) { // Special case, we want AI to settle in place on turn 1.
val foundCityAction = UnitActions.getFoundCityAction(unit, unit.getTile())
// Depending on era and difficulty we might start with more than one settler. In that case settle the one with the best location
val otherSettlers = unit.civInfo.getCivUnits().filter { it.currentMovement > 0 && it.baseUnit == unit.baseUnit }
val otherSettlers = unit.civInfo.units.getCivUnits().filter { it.currentMovement > 0 && it.baseUnit == unit.baseUnit }
if(foundCityAction?.action != null &&
otherSettlers.none {
rankTileAsCityCenter(it.getTile(), nearbyTileRankings, emptySequence()) > rankTileAsCityCenter(unit.getTile(), nearbyTileRankings, emptySequence())

View File

@ -10,8 +10,8 @@ import com.unciv.logic.battle.ICombatant
import com.unciv.logic.battle.MapUnitCombatant
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.managers.ReligionState
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.civilization.managers.ReligionState
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.models.ruleset.unique.UniqueType
@ -148,7 +148,7 @@ object UnitAutomation {
if (tryRunAwayIfNeccessary(unit)) return
if (unit.currentTile.isCityCenter() && unit.currentTile.getCity()!!.isCapital()
&& !unit.hasUnique(UniqueType.AddInCapital) && unit.civInfo.getCivUnits().any { unit.hasUnique(UniqueType.AddInCapital) }){
&& !unit.hasUnique(UniqueType.AddInCapital) && unit.civInfo.units.getCivUnits().any { unit.hasUnique(UniqueType.AddInCapital) }){
// First off get out of the way, then decide if you actually want to do something else
val tilesCanMoveTo = unit.movement.getDistanceToTiles()
.filter { unit.movement.canMoveTo(it.key) }
@ -418,7 +418,7 @@ object UnitAutomation {
}
private fun tryAccompanySettlerOrGreatPerson(unit: MapUnit): Boolean {
val settlerOrGreatPersonToAccompany = unit.civInfo.getCivUnits()
val settlerOrGreatPersonToAccompany = unit.civInfo.units.getCivUnits()
.firstOrNull {
val tile = it.currentTile
it.isCivilian() &&

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.math.Vector2
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.automation.civilization.NextTurnAutomation
import com.unciv.logic.automation.unit.AttackableTile
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.CivilizationInfo
@ -17,7 +18,6 @@ import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.RoadStatus
import com.unciv.logic.map.TileInfo
import com.unciv.logic.automation.unit.AttackableTile
import com.unciv.models.UnitActionType
import com.unciv.models.helpers.UnitMovementMemoryType
import com.unciv.models.ruleset.unique.StateForConditionals
@ -204,12 +204,8 @@ object Battle {
if (defeatedUnitYieldSourceType == "Cost") unitCost else unitStr
val yieldAmount = (yieldTypeSourceAmount * yieldPercent).toInt()
// This should be unnecessary as we check this for uniques when reading them in
try {
val stat = Stat.valueOf(unique.params[3])
civUnit.getCivInfo().addStat(stat, yieldAmount)
} catch (ex: Exception) {
} // parameter is not a stat
val stat = Stat.valueOf(unique.params[3])
civUnit.getCivInfo().addStat(stat, yieldAmount)
}
// CS friendship from killing barbarians
@ -297,7 +293,7 @@ object Battle {
/** Places a [unitName] unit near [tile] after being attacked by [attacker].
* Adds a notification to [attacker]'s civInfo and returns whether the captured unit could be placed */
private fun spawnCapturedUnit(unitName: String, attacker: ICombatant, tile: TileInfo): Boolean {
val addedUnit = attacker.getCivInfo().placeUnitNearTile(tile.position, unitName) ?: return false
val addedUnit = attacker.getCivInfo().units.placeUnitNearTile(tile.position, unitName) ?: return false
addedUnit.currentMovement = 0f
addedUnit.health = 50
attacker.getCivInfo().addNotification("An enemy [${unitName}] has joined us!", addedUnit.getTile().position, NotificationCategory.War, unitName)
@ -310,7 +306,7 @@ object Battle {
return true
}
private data class DamageDealt(val attackerDealt: Int, val defenderDealt: Int) {}
private data class DamageDealt(val attackerDealt: Int, val defenderDealt: Int)
private fun takeDamage(attacker: ICombatant, defender: ICombatant): DamageDealt {
var potentialDamageToDefender = BattleDamage.calculateDamageToDefender(attacker, defender)
@ -631,7 +627,7 @@ object Battle {
// This is so that future checks which check if a unit has been captured are caught give the right answer
// For example, in postBattleMoveToAttackedTile
capturedUnit.civInfo = attacker.getCivInfo()
attacker.getCivInfo().placeUnitNearTile(capturedUnitTile.position, Constants.worker)
attacker.getCivInfo().units.placeUnitNearTile(capturedUnitTile.position, Constants.worker)
}
else -> capturedUnit.capturedBy(attacker.getCivInfo())
}
@ -860,7 +856,7 @@ object Battle {
var potentialInterceptors = sequence<MapUnit> { }
for (interceptingCiv in UncivGame.Current.gameInfo!!.civilizations
.filter {attacker.getCivInfo().isAtWarWith(it)}) {
potentialInterceptors += interceptingCiv.getCivUnits()
potentialInterceptors += interceptingCiv.units.getCivUnits()
.filter { it.canIntercept(attackedTile) }
}
@ -880,8 +876,6 @@ object Battle {
interceptor.currentTile.position,
attacker.unit.currentTile.position
)
val locationsAttackerUnknown =
LocationAction(interceptor.currentTile.position, attackedTile.position)
val locationsInterceptorUnknown =
LocationAction(attackedTile.position, attacker.unit.currentTile.position)
@ -952,7 +946,7 @@ object Battle {
if (attacker.unit.hasUnique(UniqueType.CannotBeIntercepted, StateForConditionals(attacker.getCivInfo(), ourCombatant = attacker, theirCombatant = defender, attackedTile = attackedTile)))
return
// Pick highest chance interceptor
for (interceptor in interceptingCiv.getCivUnits()
for (interceptor in interceptingCiv.units.getCivUnits()
.filter { it.canIntercept(attackedTile) }
.sortedByDescending { it.interceptChance() }
) {

View File

@ -1,10 +1,10 @@
package com.unciv.logic.battle
import com.unciv.logic.automation.unit.SpecificUnitAutomation
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.logic.automation.unit.SpecificUnitAutomation // for Kdoc
object GreatGeneralImplementation {
@ -27,7 +27,7 @@ object GreatGeneralImplementation {
*/
fun getGreatGeneralBonus(unit: MapUnit): Pair<String, Int> {
val civInfo = unit.civInfo
val allGenerals = civInfo.getCivUnits()
val allGenerals = civInfo.units.getCivUnits()
.filter { it.hasStrengthBonusInRadiusUnique }
if (allGenerals.none()) return Pair("", 0)

View File

@ -7,8 +7,6 @@ import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat
import java.util.*
import kotlin.collections.HashMap
class CivConstructions : IsPartOfGameInfoSerialization {
@ -132,7 +130,7 @@ class CivConstructions : IsPartOfGameInfoSerialization {
it.cityConstructions.containsBuildingOrEquivalent(objectToCount.name)
|| it.cityConstructions.isBeingConstructedOrEnqueued(objectToCount.name)
}
is BaseUnit -> civInfo.getCivUnits().count { it.name == objectToCount.name } +
is BaseUnit -> civInfo.units.getCivUnits().count { it.name == objectToCount.name } +
civInfo.cities.count { it.cityConstructions.isBeingConstructedOrEnqueued(objectToCount.name) }
else -> 0
}

View File

@ -23,6 +23,7 @@ import com.unciv.logic.civilization.managers.QuestManager
import com.unciv.logic.civilization.managers.ReligionManager
import com.unciv.logic.civilization.managers.RuinsManager
import com.unciv.logic.civilization.managers.TechManager
import com.unciv.logic.civilization.managers.UnitManager
import com.unciv.logic.civilization.managers.VictoryManager
import com.unciv.logic.civilization.transients.CivInfoStatsForNextTurn
import com.unciv.logic.civilization.transients.CivInfoTransientCache
@ -87,19 +88,8 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
@Transient
lateinit var nation: Nation
/**
* We never add or remove from here directly, could cause comodification problems.
* Instead, we create a copy list with the change, and replace this list.
* The other solution, casting toList() every "get", has a performance cost
*/
@Transient
private var units = listOf<MapUnit>()
/**
* Index in the unit list above of the unit that is potentially due and is next up for button "Next unit".
*/
@Transient
private var nextPotentiallyDueAt = 0
val units = UnitManager(this)
@Transient
var viewableTiles = setOf<TileInfo>()
@ -315,6 +305,8 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
return toReturn
}
//region pure functions
fun getDifficulty(): Difficulty {
if (isHuman()) return gameInfo.getDifficulty()
@ -495,79 +487,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
}
//region Units
fun getCivUnitsSize(): Int = units.size
fun getCivUnits(): Sequence<MapUnit> = units.asSequence()
fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.isGreatPerson() }
// Similar to getCivUnits(), but the returned list is rotated so that the
// 'nextPotentiallyDueAt' unit is first here.
private fun getCivUnitsStartingAtNextDue(): Sequence<MapUnit> = sequenceOf(units.subList(nextPotentiallyDueAt, units.size) + units.subList(0, nextPotentiallyDueAt)).flatten()
fun addUnit(mapUnit: MapUnit, updateCivInfo: Boolean = true) {
// Since we create a new list anyway (otherwise some concurrent modification
// exception will happen), also rearrange existing units so that
// 'nextPotentiallyDueAt' becomes 0. This way new units are always last to be due
// (can be changed as wanted, just have a predictable place).
val newList = getCivUnitsStartingAtNextDue().toMutableList()
newList.add(mapUnit)
units = newList
nextPotentiallyDueAt = 0
if (updateCivInfo) {
// Not relevant when updating TileInfo transients, since some info of the civ itself isn't yet available,
// and in any case it'll be updated once civ info transients are
updateStatsForNextTurn() // unit upkeep
if (mapUnit.baseUnit.getResourceRequirements().isNotEmpty())
cache.updateCivResources()
}
}
fun removeUnit(mapUnit: MapUnit) {
// See comment in addUnit().
val newList = getCivUnitsStartingAtNextDue().toMutableList()
newList.remove(mapUnit)
units = newList
nextPotentiallyDueAt = 0
updateStatsForNextTurn() // unit upkeep
if (mapUnit.baseUnit.getResourceRequirements().isNotEmpty())
cache.updateCivResources()
}
fun getIdleUnits() = getCivUnits().filter { it.isIdle() }
fun getDueUnits(): Sequence<MapUnit> = getCivUnitsStartingAtNextDue().filter { it.due && it.isIdle() }
fun shouldGoToDueUnit() = UncivGame.Current.settings.checkForDueUnits && getDueUnits().any()
// Return the next due unit, but preferably not 'unitToSkip': this is returned only if it is the only remaining due unit.
fun cycleThroughDueUnits(unitToSkip: MapUnit? = null): MapUnit? {
if (units.none()) return null
var returnAt = nextPotentiallyDueAt
var fallbackAt = -1
do {
if (units[returnAt].due && units[returnAt].isIdle()) {
if (units[returnAt] != unitToSkip) {
nextPotentiallyDueAt = (returnAt + 1) % units.size
return units[returnAt]
}
else fallbackAt = returnAt
}
returnAt = (returnAt + 1) % units.size
} while (returnAt != nextPotentiallyDueAt)
if (fallbackAt >= 0) {
nextPotentiallyDueAt = (fallbackAt + 1) % units.size
return units[fallbackAt]
}
else return null
}
//endregion
fun shouldOpenTechPicker(): Boolean {
if (!tech.canResearchTech()) return false
if (tech.freeTechs != 0) return true
@ -661,7 +580,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
fun isDefeated() = when {
isBarbarian() || isSpectator() -> false // Barbarians and voyeurs can't lose
hasEverOwnedOriginalCapital == true -> cities.isEmpty()
else -> getCivUnits().none()
else -> units.getCivUnits().none()
}
fun getEra(): Era = tech.era
@ -757,7 +676,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
private fun calculateMilitaryMight(): Int {
var sum = 1 // minimum value, so we never end up with 0
for (unit in units) {
for (unit in units.getCivUnits()) {
sum += if (unit.baseUnit.isWaterUnit())
unit.getForceEvaluation() / 2 // Really don't value water units highly
else
@ -988,50 +907,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
if (action is LocationAction && action.locations.isEmpty()) null else action, category))
}
fun addUnit(unitName: String, city: CityInfo? = null): MapUnit? {
if (cities.isEmpty()) return null
if (!gameInfo.ruleSet.units.containsKey(unitName)) return null
val cityToAddTo = city ?: cities.random()
val unit = getEquivalentUnit(unitName)
val placedUnit = placeUnitNearTile(cityToAddTo.location, unit.name)
// silently bail if no tile to place the unit is found
?: return null
if (unit.isGreatPerson()) {
addNotification("A [${unit.name}] has been born in [${cityToAddTo.name}]!", placedUnit.getTile().position, NotificationCategory.General, unit.name)
}
if (placedUnit.hasUnique(UniqueType.ReligiousUnit) && gameInfo.isReligionEnabled()) {
placedUnit.religion =
when {
placedUnit.hasUnique(UniqueType.TakeReligionOverBirthCity)
&& religionManager.religion?.isMajorReligion() == true ->
religionManager.religion!!.name
city != null -> city.cityConstructions.cityInfo.religion.getMajorityReligionName()
else -> religionManager.religion?.name
}
placedUnit.setupAbilityUses(cityToAddTo)
}
for (unique in getMatchingUniques(UniqueType.LandUnitsCrossTerrainAfterUnitGained)) {
if (unit.matchesFilter(unique.params[1])) {
passThroughImpassableUnlocked = true // Update the cached Boolean
passableImpassables.add(unique.params[0]) // Add to list of passable impassables
}
}
return placedUnit
}
/** Tries to place the a [unitName] unit into the [TileInfo] closest to the given the [location]
* @param location where to try to place the unit
* @param unitName name of the [BaseUnit] to create and place
* @return created [MapUnit] or null if no suitable location was found
* */
fun placeUnitNearTile(location: Vector2, unitName: String): MapUnit? {
return gameInfo.tileMap.placeUnitNearTile(location, unitName, this)
}
fun addCity(location: Vector2) {
val newCity = CityInfo(this, location)
newCity.cityConstructions.chooseNextConstruction()
@ -1042,7 +917,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
else "The City-State of [$civName] has been destroyed!"
for (civ in gameInfo.civilizations)
civ.addNotification(destructionText, NotificationCategory.General, civName, NotificationIcon.Death)
getCivUnits().forEach { it.destroy() }
units.getCivUnits().forEach { it.destroy() }
tradeRequests.clear() // if we don't do this then there could be resources taken by "pending" trades forever
for (diplomacyManager in diplomacy.values) {
diplomacyManager.trades.clear()

View File

@ -12,8 +12,8 @@ import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.Proximity
import com.unciv.models.ruleset.nation.CityStateType
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.nation.CityStateType
import com.unciv.models.ruleset.tile.ResourceSupplyList
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.Unique
@ -78,7 +78,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
return
val giftedUnit = giftableUnits.random()
val cities = NextTurnAutomation.getClosestCities(receivingCiv, civInfo) ?: return
val placedUnit = receivingCiv.placeUnitNearTile(cities.city1.location, giftedUnit.name)
val placedUnit = receivingCiv.units.placeUnitNearTile(cities.city1.location, giftedUnit.name)
?: return
val locations = LocationAction(placedUnit.getTile().position, cities.city2.location)
receivingCiv.addNotification( "[${civInfo.civName}] gave us a [${giftedUnit.name}] as a gift!", locations,
@ -108,7 +108,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
?: return // That filter _can_ result in no candidates, if so, quit silently
// placing the unit may fail - in that case stay quiet
val placedUnit = receivingCiv.placeUnitNearTile(city.location, militaryUnit.name) ?: return
val placedUnit = receivingCiv.units.placeUnitNearTile(city.location, militaryUnit.name) ?: return
// The unit should have bonuses from Barracks, Alhambra etc as if it was built in the CS capital
militaryUnit.addConstructionBonuses(placedUnit, civInfo.getCapital()!!.cityConstructions)
@ -283,7 +283,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
// https://github.com/Gedemon/Civ5-DLL/blob/master/CvGameCoreDLL_Expansion1/CvMinorCivAI.cpp, line 7812
var cost = (500 * civInfo.gameInfo.speed.goldCostModifier).toInt()
// Plus disband value of all units
for (unit in civInfo.getCivUnits()) {
for (unit in civInfo.units.getCivUnits()) {
cost += unit.baseUnit.getDisbandGold(civInfo)
}
// Round to lower multiple of 5
@ -318,7 +318,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
civInfo.getCapital()!!.location,
NotificationCategory.Diplomacy, civInfo.civName,
NotificationIcon.Diplomacy, otherCiv.civName)
for (unit in civInfo.getCivUnits())
for (unit in civInfo.units.getCivUnits())
unit.gift(otherCiv)
// Make sure this CS can never be liberated
@ -435,7 +435,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
it.value.isCivilian() && it.value.isBuildable(civInfo)
}
if (buildableWorkerLikeUnits.isEmpty()) return // Bad luck?
demandingCiv.placeUnitNearTile(civInfo.getCapital()!!.location, buildableWorkerLikeUnits.keys.random())
demandingCiv.units.placeUnitNearTile(civInfo.getCapital()!!.location, buildableWorkerLikeUnits.keys.random())
civInfo.getDiplomacyManager(demandingCiv).addInfluence(-50f)
cityStateBullied(demandingCiv)
@ -462,7 +462,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
val diplomacy = civInfo.getDiplomacyManager(otherCiv)
if (diplomacy.hasFlag(DiplomacyFlags.AngerFreeIntrusion)) continue // They recently helped us
val unitsInBorder = otherCiv.getCivUnits().count { !it.isCivilian() && it.getTile().getOwner() == civInfo }
val unitsInBorder = otherCiv.units.getCivUnits().count { !it.isCivilian() && it.getTile().getOwner() == civInfo }
if (unitsInBorder > 0 && diplomacy.relationshipLevel() < RelationshipLevel.Friend) {
diplomacy.addInfluence(-10f)
if (!diplomacy.hasFlag(DiplomacyFlags.BorderConflict)) {
@ -489,7 +489,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
if (civInfo.gameInfo.gameParameters.noBarbarians) return 0
val barbarianCiv = civInfo.gameInfo.civilizations.firstOrNull { it.isBarbarian() }
?: return 0
return barbarianCiv.getCivUnits().count { it.threatensCiv(civInfo) }
return barbarianCiv.units.getCivUnits().count { it.threatensCiv(civInfo) }
}
fun threateningBarbarianKilledBy(otherCiv: CivilizationInfo) {
@ -651,7 +651,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
/** Asks all met majors that haven't yet declared wor on [attacker] to at least give some units */
fun askForUnitGifts(attacker: CivilizationInfo) {
if (attacker.isDefeated() || civInfo.isDefeated()) // nevermind, someone died
if (attacker.isDefeated() || civInfo.isDefeated()) // never mind, someone died
return
if (civInfo.cities.isEmpty()) // Can't receive units with no cities
return

View File

@ -419,7 +419,7 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization {
hasOpenBorders = newHasOpenBorders
if (bordersWereClosed) { // borders were closed, get out!
for (unit in civInfo.getCivUnits()
for (unit in civInfo.units.getCivUnits()
.filter { it.currentTile.getOwner()?.civName == otherCivName }.toList()) {
unit.movement.teleportToClosestMoveableTile()
}
@ -750,7 +750,7 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization {
diplomaticStatus = DiplomaticStatus.Peace
val otherCiv = otherCiv()
// Get out of others' territory
for (unit in civInfo.getCivUnits().filter { it.getTile().getOwner() == otherCiv }.toList())
for (unit in civInfo.units.getCivUnits().filter { it.getTile().getOwner() == otherCiv }.toList())
unit.movement.teleportToClosestMoveableTile()
for (thirdCiv in civInfo.getKnownCivs()) {

View File

@ -5,7 +5,6 @@ import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.IsPartOfGameInfoSerialization
import com.unciv.logic.civilization.diplomacy.CityStatePersonality
import com.unciv.logic.civilization.CivFlags
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.DiplomacyAction
@ -13,6 +12,7 @@ import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.civilization.Proximity
import com.unciv.logic.civilization.diplomacy.CityStatePersonality
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.map.TileInfo
@ -407,7 +407,7 @@ class QuestManager : IsPartOfGameInfoSerialization {
QuestName.Route.value -> assignee.isCapitalConnectedToCity(civInfo.getCapital()!!)
QuestName.ConnectResource.value -> assignee.detailedCivResources.map { it.resource }.contains(civInfo.gameInfo.ruleSet.tileResources[assignedQuest.data1])
QuestName.ConstructWonder.value -> assignee.cities.any { it.cityConstructions.isBuilt(assignedQuest.data1) }
QuestName.GreatPerson.value -> assignee.getCivGreatPeople().any { it.baseUnit.getReplacedUnit(civInfo.gameInfo.ruleSet).name == assignedQuest.data1 }
QuestName.GreatPerson.value -> assignee.units.getCivGreatPeople().any { it.baseUnit.getReplacedUnit(civInfo.gameInfo.ruleSet).name == assignedQuest.data1 }
QuestName.FindPlayer.value -> assignee.hasMetCivTerritory(civInfo.gameInfo.getCivilization(assignedQuest.data1))
QuestName.FindNaturalWonder.value -> assignee.naturalWonders.contains(assignedQuest.data1)
QuestName.PledgeToProtect.value -> assignee in civInfo.cityStateFunctions.getProtectorCivs()
@ -549,7 +549,7 @@ class QuestManager : IsPartOfGameInfoSerialization {
/** Gets notified when we are attacked, for war with major pseudo-quest */
fun wasAttackedBy(attacker: CivilizationInfo) {
// Set target number units to kill
val totalMilitaryUnits = attacker.getCivUnits().count { !it.isCivilian() }
val totalMilitaryUnits = attacker.units.getCivUnits().count { !it.isCivilian() }
val unitsToKill = max(3, totalMilitaryUnits / 4)
unitsToKillForCiv[attacker.civName] = unitsToKill
@ -752,8 +752,8 @@ class QuestManager : IsPartOfGameInfoSerialization {
private fun getGreatPersonForQuest(challenger: CivilizationInfo): BaseUnit? {
val ruleSet = civInfo.gameInfo.ruleSet
val challengerGreatPeople = challenger.getCivGreatPeople().map { it.baseUnit.getReplacedUnit(ruleSet) }
val cityStateGreatPeople = civInfo.getCivGreatPeople().map { it.baseUnit.getReplacedUnit(ruleSet) }
val challengerGreatPeople = challenger.units.getCivGreatPeople().map { it.baseUnit.getReplacedUnit(ruleSet) }
val cityStateGreatPeople = civInfo.units.getCivGreatPeople().map { it.baseUnit.getReplacedUnit(ruleSet) }
val greatPeople = challenger.greatPeople.getGreatPeople()
.map { it.getReplacedUnit(ruleSet) }

View File

@ -178,7 +178,7 @@ class ReligionManager : IsPartOfGameInfoSerialization {
val birthCity =
if (religionState <= ReligionState.Pantheon) civInfo.getCapital()
else civInfo.religionManager.getHolyCity()
val prophet = civInfo.addUnit(prophetUnitName, birthCity) ?: return
val prophet = civInfo.units.addUnit(prophetUnitName, birthCity) ?: return
prophet.religion = religion!!.name
storedFaith -= faithForNextGreatProphet()
civInfo.civConstructions.boughtItemsWithIncreasingPrice.add(prophetUnitName, 1)
@ -368,7 +368,7 @@ class ReligionManager : IsPartOfGameInfoSerialization {
foundingCityId = null
shouldChoosePantheonBelief = false
for (unit in civInfo.getCivUnits())
for (unit in civInfo.units.getCivUnits())
if (unit.hasUnique(UniqueType.ReligiousUnit) && unit.hasUnique(UniqueType.TakeReligionOverBirthCity))
unit.religion = newReligion.name
}

View File

@ -68,7 +68,7 @@ class TechManager : IsPartOfGameInfoSerialization {
/** When moving towards a certain tech, the user doesn't have to manually pick every one. */
var techsToResearch = ArrayList<String>()
var overflowScience = 0
private var overflowScience = 0
var techsInProgress = HashMap<String, Int>()
/** In civ IV, you can auto-convert a certain percentage of gold in cities to science */
@ -340,7 +340,7 @@ class TechManager : IsPartOfGameInfoSerialization {
for (unique in civInfo.getMatchingUniques(UniqueType.ReceiveFreeUnitWhenDiscoveringTech)) {
if (unique.params[1] != techName) continue
civInfo.addUnit(unique.params[0])
civInfo.units.addUnit(unique.params[0])
}
for (unique in civInfo.getMatchingUniques(UniqueType.MayanGainGreatPerson)) {
if (unique.params[1] != techName) continue
@ -385,7 +385,7 @@ class TechManager : IsPartOfGameInfoSerialization {
}
}
fun updateEra() {
private fun updateEra() {
val ruleset = civInfo.gameInfo.ruleSet
if (ruleset.technologies.isEmpty() || researchedTechnologies.isEmpty())
return

View File

@ -38,7 +38,7 @@ class TurnManager(val civInfo: CivilizationInfo) {
if (civInfo.cities.isNotEmpty()) { //if no city available, addGreatPerson will throw exception
val greatPerson = civInfo.greatPeople.getNewGreatPerson()
if (greatPerson != null && civInfo.gameInfo.ruleSet.units.containsKey(greatPerson))
civInfo.addUnit(greatPerson)
civInfo.units.addUnit(greatPerson)
civInfo.religionManager.startTurn()
if (civInfo.isLongCountActive())
MayaCalendar.startTurnForMaya(civInfo)
@ -50,11 +50,11 @@ class TurnManager(val civInfo: CivilizationInfo) {
updateRevolts()
for (city in civInfo.cities) city.startTurn() // Most expensive part of startTurn
for (unit in civInfo.getCivUnits()) unit.startTurn()
for (unit in civInfo.units.getCivUnits()) unit.startTurn()
if (civInfo.playerType == PlayerType.Human && UncivGame.Current.settings.automatedUnitsMoveOnTurnStart) {
civInfo.hasMovedAutomatedUnits = true
for (unit in civInfo.getCivUnits())
for (unit in civInfo.units.getCivUnits())
unit.doAction()
} else civInfo.hasMovedAutomatedUnits = false
@ -228,7 +228,7 @@ class TurnManager(val civInfo: CivilizationInfo) {
// disband units until there are none left OR the gold values are normal
if (!civInfo.isBarbarian() && civInfo.gold < -100 && nextTurnStats.gold.toInt() < 0) {
for (i in 1 until (civInfo.gold / -100)) {
var civMilitaryUnits = civInfo.getCivUnits().filter { it.baseUnit.isMilitary() }
var civMilitaryUnits = civInfo.units.getCivUnits().filter { it.baseUnit.isMilitary() }
if (civMilitaryUnits.any()) {
val unitToDisband = civMilitaryUnits.first()
unitToDisband.disband()
@ -263,7 +263,7 @@ class TurnManager(val civInfo: CivilizationInfo) {
civInfo.temporaryUniques.endTurn()
civInfo.goldenAges.endTurn(civInfo.getHappiness())
civInfo.getCivUnits().forEach { it.endTurn() } // This is the most expensive part of endTurn
civInfo.units.getCivUnits().forEach { it.endTurn() } // This is the most expensive part of endTurn
civInfo.diplomacy.values.toList().forEach { it.nextTurn() } // we copy the diplomacy values so if it changes in-loop we won't crash
civInfo.cache.updateHasActiveEnemyMovementPenalty()
@ -272,7 +272,7 @@ class TurnManager(val civInfo: CivilizationInfo) {
updateWinningCiv() // Maybe we did something this turn to win
}
fun updateWinningCiv(){
private fun updateWinningCiv(){
if (civInfo.gameInfo.victoryData != null) return // Game already won
val victoryType = civInfo.victoryManager.getVictoryTypeAchieved()

View File

@ -0,0 +1,143 @@
package com.unciv.logic.civilization.managers
import com.badlogic.gdx.math.Vector2
import com.unciv.UncivGame
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.BaseUnit
class UnitManager(val civInfo:CivilizationInfo) {
/**
* We never add or remove from here directly, could cause comodification problems.
* Instead, we create a copy list with the change, and replace this list.
* The other solution, casting toList() every "get", has a performance cost
*/
@Transient
private var unitList = listOf<MapUnit>()
/**
* Index in the unit list above of the unit that is potentially due and is next up for button "Next unit".
*/
@Transient
private var nextPotentiallyDueAt = 0
fun addUnit(unitName: String, city: CityInfo? = null): MapUnit? {
if (civInfo.cities.isEmpty()) return null
if (!civInfo.gameInfo.ruleSet.units.containsKey(unitName)) return null
val cityToAddTo = city ?: civInfo.cities.random()
val unit = civInfo.getEquivalentUnit(unitName)
val placedUnit = placeUnitNearTile(cityToAddTo.location, unit.name)
// silently bail if no tile to place the unit is found
?: return null
if (unit.isGreatPerson()) {
civInfo.addNotification("A [${unit.name}] has been born in [${cityToAddTo.name}]!", placedUnit.getTile().position, NotificationCategory.General, unit.name)
}
if (placedUnit.hasUnique(UniqueType.ReligiousUnit) && civInfo.gameInfo.isReligionEnabled()) {
placedUnit.religion =
when {
placedUnit.hasUnique(UniqueType.TakeReligionOverBirthCity)
&& civInfo.religionManager.religion?.isMajorReligion() == true ->
civInfo.religionManager.religion!!.name
city != null -> city.cityConstructions.cityInfo.religion.getMajorityReligionName()
else -> civInfo.religionManager.religion?.name
}
placedUnit.setupAbilityUses(cityToAddTo)
}
for (unique in civInfo.getMatchingUniques(UniqueType.LandUnitsCrossTerrainAfterUnitGained)) {
if (unit.matchesFilter(unique.params[1])) {
civInfo.passThroughImpassableUnlocked = true // Update the cached Boolean
civInfo.passableImpassables.add(unique.params[0]) // Add to list of passable impassables
}
}
return placedUnit
}
/** Tries to place the a [unitName] unit into the [TileInfo] closest to the given the [location]
* @param location where to try to place the unit
* @param unitName name of the [BaseUnit] to create and place
* @return created [MapUnit] or null if no suitable location was found
* */
fun placeUnitNearTile(location: Vector2, unitName: String): MapUnit? {
return civInfo.gameInfo.tileMap.placeUnitNearTile(location, unitName, civInfo)
}
fun getCivUnitsSize(): Int = unitList.size
fun getCivUnits(): Sequence<MapUnit> = unitList.asSequence()
fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.isGreatPerson() }
// Similar to getCivUnits(), but the returned list is rotated so that the
// 'nextPotentiallyDueAt' unit is first here.
private fun getCivUnitsStartingAtNextDue(): Sequence<MapUnit> = sequenceOf(unitList.subList(nextPotentiallyDueAt, unitList.size) + unitList.subList(0, nextPotentiallyDueAt)).flatten()
fun addUnit(mapUnit: MapUnit, updateCivInfo: Boolean = true) {
// Since we create a new list anyway (otherwise some concurrent modification
// exception will happen), also rearrange existing units so that
// 'nextPotentiallyDueAt' becomes 0. This way new units are always last to be due
// (can be changed as wanted, just have a predictable place).
val newList = getCivUnitsStartingAtNextDue().toMutableList()
newList.add(mapUnit)
unitList = newList
nextPotentiallyDueAt = 0
if (updateCivInfo) {
// Not relevant when updating TileInfo transients, since some info of the civ itself isn't yet available,
// and in any case it'll be updated once civ info transients are
civInfo.updateStatsForNextTurn() // unit upkeep
if (mapUnit.baseUnit.getResourceRequirements().isNotEmpty())
civInfo.cache.updateCivResources()
}
}
fun removeUnit(mapUnit: MapUnit) {
// See comment in addUnit().
val newList = getCivUnitsStartingAtNextDue().toMutableList()
newList.remove(mapUnit)
unitList = newList
nextPotentiallyDueAt = 0
civInfo.updateStatsForNextTurn() // unit upkeep
if (mapUnit.baseUnit.getResourceRequirements().isNotEmpty())
civInfo.cache.updateCivResources()
}
fun getIdleUnits() = getCivUnits().filter { it.isIdle() }
fun getDueUnits(): Sequence<MapUnit> = getCivUnitsStartingAtNextDue().filter { it.due && it.isIdle() }
fun shouldGoToDueUnit() = UncivGame.Current.settings.checkForDueUnits && getDueUnits().any()
// Return the next due unit, but preferably not 'unitToSkip': this is returned only if it is the only remaining due unit.
fun cycleThroughDueUnits(unitToSkip: MapUnit? = null): MapUnit? {
if (unitList.none()) return null
var returnAt = nextPotentiallyDueAt
var fallbackAt = -1
do {
if (unitList[returnAt].due && unitList[returnAt].isIdle()) {
if (unitList[returnAt] != unitToSkip) {
nextPotentiallyDueAt = (returnAt + 1) % unitList.size
return unitList[returnAt]
}
else fallbackAt = returnAt
}
returnAt = (returnAt + 1) % unitList.size
} while (returnAt != nextPotentiallyDueAt)
if (fallbackAt >= 0) {
nextPotentiallyDueAt = (fallbackAt + 1) % unitList.size
return unitList[fallbackAt]
}
else return null
}
}

View File

@ -35,7 +35,7 @@ class CivInfoStatsForNextTurn(val civInfo: CivilizationInfo) {
freeUnits += unique.params[0].toInt()
}
var unitsToPayFor = civInfo.getCivUnits()
var unitsToPayFor = civInfo.units.getCivUnits()
if (civInfo.hasUnique(UniqueType.UnitsInCitiesNoMaintenance))
unitsToPayFor = unitsToPayFor.filterNot {
it.getTile().isCityCenter() && it.canGarrison()
@ -160,7 +160,7 @@ class CivInfoStatsForNextTurn(val civInfo: CivilizationInfo) {
}
return totalSupply.toInt()
}
fun getUnitSupplyDeficit(): Int = max(0,civInfo.getCivUnitsSize() - getUnitSupply())
fun getUnitSupplyDeficit(): Int = max(0,civInfo.units.getCivUnitsSize() - getUnitSupply())
/** Per each supply missing, a player gets -10% production. Capped at -70%. */
fun getUnitSupplyProductionPenalty(): Float = -min(getUnitSupplyDeficit() * 10f, 70f)
@ -228,7 +228,6 @@ class CivInfoStatsForNextTurn(val civInfo: CivilizationInfo) {
if (!containsKey(key)) put(key, value)
else put(key, value+get(key)!!)
}
fun HashMap<String, Float>.add(key:String, value: Int) = add(key, value.toFloat())
statMap["Base happiness"] = civInfo.getDifficulty().baseHappiness.toFloat()
@ -289,7 +288,7 @@ class CivInfoStatsForNextTurn(val civInfo: CivilizationInfo) {
return statMap
}
fun getGlobalStatsFromUniques():StatMap {
private fun getGlobalStatsFromUniques():StatMap {
val statMap = StatMap()
if (civInfo.religionManager.religion != null) {
for (unique in civInfo.religionManager.religion!!.getFounderUniques()) {

View File

@ -69,7 +69,7 @@ class CivInfoTransientCache(val civInfo: CivilizationInfo) {
private fun updateViewableInvisibleTiles() {
val newViewableInvisibleTiles = HashSet<TileInfo>()
for (unit in civInfo.getCivUnits()) {
for (unit in civInfo.units.getCivUnits()) {
val invisibleUnitUniques = unit.getMatchingUniques(UniqueType.CanSeeInvisibleUnits)
if (invisibleUnitUniques.none()) continue
val visibleUnitTypes = invisibleUnitUniques.map { it.params[0] }
@ -103,7 +103,7 @@ class CivInfoTransientCache(val civInfo: CivilizationInfo) {
newViewableTiles.addAll(ownedTiles)
val neighboringUnownedTiles = ownedTiles.flatMap { tile -> tile.neighbors.filter { it.getOwner() != civInfo } }
newViewableTiles.addAll(neighboringUnownedTiles)
newViewableTiles.addAll(civInfo.getCivUnits().flatMap { unit -> unit.viewableTiles.asSequence().filter { it.getOwner() != civInfo } })
newViewableTiles.addAll(civInfo.units.getCivUnits().flatMap { unit -> unit.viewableTiles.asSequence().filter { it.getOwner() != civInfo } })
for (otherCiv in civInfo.getKnownCivs()) {
if (otherCiv.getAllyCiv() == civInfo.civName || otherCiv.civName == civInfo.getAllyCiv()) {
@ -229,7 +229,7 @@ class CivInfoTransientCache(val civInfo: CivilizationInfo) {
for (diplomacyManager in civInfo.diplomacy.values)
newDetailedCivResources.add(diplomacyManager.resourcesFromTrade())
for (unit in civInfo.getCivUnits())
for (unit in civInfo.units.getCivUnits())
newDetailedCivResources.subtractResourceRequirements(
unit.baseUnit.getResourceRequirements(), civInfo.gameInfo.ruleSet, "Units")

View File

@ -433,9 +433,6 @@ class MapUnit : IsPartOfGameInfoSerialization {
fun isPreparingAirSweep() = action == UnitActionType.AirSweep.value
fun isSetUpForSiege() = action == UnitActionType.SetUp.value
/** For display in Unit Overview */
fun getActionLabel() = if (action == null) "" else if (isFortified()) UnitActionType.Fortify.value else if (isMoving()) "Moving" else action!!
fun isMilitary() = baseUnit.isMilitary()
fun isCivilian() = baseUnit.isCivilian()
@ -552,9 +549,9 @@ class MapUnit : IsPartOfGameInfoSerialization {
// and the civ currently has 0 horses, we need to see if the upgrade will be buildable
// WHEN THE CURRENT UNIT IS NOT HERE
// TODO redesign without kludge: Inform getRejectionReasons about 'virtually available' resources somehow
civInfo.removeUnit(this)
civInfo.units.removeUnit(this)
val rejectionReasons = unitToUpgradeTo.getRejectionReasons(civInfo)
civInfo.addUnit(this)
civInfo.units.addUnit(this)
var relevantRejectionReasons = rejectionReasons.asSequence().filterNot { it.rejectionReason == RejectionReason.Unbuildable }
if (ignoreRequirements)
@ -912,7 +909,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
civInfo.attacksSinceTurnStart.addAll(attacksSinceTurnStart.asSequence().map { CivilizationInfo.HistoricalAttackMemory(this.name, currentPosition, it) })
currentMovement = 0f
removeFromTile()
civInfo.removeUnit(this)
civInfo.units.removeUnit(this)
civInfo.cache.updateViewableTiles()
if (destroyTransportedUnit) {
// all transported units should be destroyed as well
@ -924,7 +921,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
}
fun gift(recipient: CivilizationInfo) {
civInfo.removeUnit(this)
civInfo.units.removeUnit(this)
civInfo.cache.updateViewableTiles()
// all transported units should be destroyed as well
currentTile.getUnits().filter { it.isTransported && isTransportTypeOf(it) }
@ -1073,11 +1070,11 @@ class MapUnit : IsPartOfGameInfoSerialization {
fun assignOwner(civInfo: CivilizationInfo, updateCivInfo: Boolean = true) {
owner = civInfo.civName
this.civInfo = civInfo
civInfo.addUnit(this, updateCivInfo)
civInfo.units.addUnit(this, updateCivInfo)
}
fun capturedBy(captor: CivilizationInfo) {
civInfo.removeUnit(this)
civInfo.units.removeUnit(this)
assignOwner(captor)
currentMovement = 0f
// It's possible that the unit can no longer stand on the tile it was captured on.

View File

@ -7,8 +7,8 @@ import com.unciv.logic.IsPartOfGameInfoSerialization
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.mapgenerator.MapLandmassGenerator
import com.unciv.models.metadata.Player
import com.unciv.models.ruleset.nation.Nation
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.nation.Nation
import com.unciv.models.ruleset.tile.TerrainType
import com.unciv.models.ruleset.unique.UniqueType
import java.lang.Integer.max
@ -529,7 +529,7 @@ class TileMap : IsPartOfGameInfoSerialization {
}
if (unitToPlaceTile == null) {
civInfo.removeUnit(unit) // since we added it to the civ units in the previous assignOwner
civInfo.units.removeUnit(unit) // since we added it to the civ units in the previous assignOwner
return null // we didn't actually create a unit...
}

View File

@ -14,41 +14,6 @@ import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.models.ruleset.BeliefType
import com.unciv.models.ruleset.Victory
import com.unciv.models.ruleset.unique.UniqueType.CityStateCanGiftGreatPeople
import com.unciv.models.ruleset.unique.UniqueType.ConditionalTimedUnique
import com.unciv.models.ruleset.unique.UniqueType.FoundCity
import com.unciv.models.ruleset.unique.UniqueType.FreeSpecificBuildings
import com.unciv.models.ruleset.unique.UniqueType.FreeStatBuildings
import com.unciv.models.ruleset.unique.UniqueType.MayanGainGreatPerson
import com.unciv.models.ruleset.unique.UniqueType.OneTimeAmountFreePolicies
import com.unciv.models.ruleset.unique.UniqueType.OneTimeAmountFreeTechs
import com.unciv.models.ruleset.unique.UniqueType.OneTimeAmountFreeUnits
import com.unciv.models.ruleset.unique.UniqueType.OneTimeEnterGoldenAge
import com.unciv.models.ruleset.unique.UniqueType.OneTimeFreeBelief
import com.unciv.models.ruleset.unique.UniqueType.OneTimeFreeGreatPerson
import com.unciv.models.ruleset.unique.UniqueType.OneTimeFreePolicy
import com.unciv.models.ruleset.unique.UniqueType.OneTimeFreeTech
import com.unciv.models.ruleset.unique.UniqueType.OneTimeFreeTechRuins
import com.unciv.models.ruleset.unique.UniqueType.OneTimeFreeUnit
import com.unciv.models.ruleset.unique.UniqueType.OneTimeFreeUnitRuins
import com.unciv.models.ruleset.unique.UniqueType.OneTimeGainPantheon
import com.unciv.models.ruleset.unique.UniqueType.OneTimeGainPopulation
import com.unciv.models.ruleset.unique.UniqueType.OneTimeGainPopulationRandomCity
import com.unciv.models.ruleset.unique.UniqueType.OneTimeGainProphet
import com.unciv.models.ruleset.unique.UniqueType.OneTimeGainStat
import com.unciv.models.ruleset.unique.UniqueType.OneTimeGainStatRange
import com.unciv.models.ruleset.unique.UniqueType.OneTimeGlobalSpiesWhenEnteringEra
import com.unciv.models.ruleset.unique.UniqueType.OneTimeRevealCrudeMap
import com.unciv.models.ruleset.unique.UniqueType.OneTimeRevealEntireMap
import com.unciv.models.ruleset.unique.UniqueType.OneTimeRevealSpecificMapTiles
import com.unciv.models.ruleset.unique.UniqueType.OneTimeTriggerVoting
import com.unciv.models.ruleset.unique.UniqueType.OneTimeUnitGainPromotion
import com.unciv.models.ruleset.unique.UniqueType.OneTimeUnitGainXP
import com.unciv.models.ruleset.unique.UniqueType.OneTimeUnitHeal
import com.unciv.models.ruleset.unique.UniqueType.OneTimeUnitSpecialUpgrade
import com.unciv.models.ruleset.unique.UniqueType.OneTimeUnitUpgrade
import com.unciv.models.ruleset.unique.UniqueType.StrategicResourcesIncrease
import com.unciv.models.ruleset.unique.UniqueType.UnitsGainPromotion
import com.unciv.models.stats.Stat
import com.unciv.models.translations.fillPlaceholders
import com.unciv.models.translations.hasPlaceholderParameters
@ -66,7 +31,7 @@ object UniqueTriggerActivation {
tile: TileInfo? = null,
notification: String? = null
): Boolean {
val timingConditional = unique.conditionals.firstOrNull { it.type == ConditionalTimedUnique }
val timingConditional = unique.conditionals.firstOrNull { it.type == UniqueType.ConditionalTimedUnique }
if (timingConditional != null) {
civInfo.temporaryUniques.add(TemporaryUnique(unique, timingConditional.params[0].toInt()))
return true
@ -81,13 +46,13 @@ object UniqueTriggerActivation {
val ruleSet = civInfo.gameInfo.ruleSet
when (unique.type) {
OneTimeFreeUnit -> {
UniqueType.OneTimeFreeUnit -> {
val unitName = unique.params[0]
val unit = ruleSet.units[unitName]
if (chosenCity == null || unit == null || (unit.hasUnique(FoundCity) && civInfo.isOneCityChallenger()))
if (chosenCity == null || unit == null || (unit.hasUnique(UniqueType.FoundCity) && civInfo.isOneCityChallenger()))
return false
val placedUnit = civInfo.addUnit(unitName, chosenCity)
val placedUnit = civInfo.units.addUnit(unitName, chosenCity)
if (notification != null && placedUnit != null) {
civInfo.addNotification(
notification,
@ -98,15 +63,15 @@ object UniqueTriggerActivation {
}
return true
}
OneTimeAmountFreeUnits -> {
UniqueType.OneTimeAmountFreeUnits -> {
val unitName = unique.params[1]
val unit = ruleSet.units[unitName]
if (chosenCity == null || unit == null || (unit.hasUnique(FoundCity) && civInfo.isOneCityChallenger()))
if (chosenCity == null || unit == null || (unit.hasUnique(UniqueType.FoundCity) && civInfo.isOneCityChallenger()))
return false
val tilesUnitsWerePlacedOn: MutableList<Vector2> = mutableListOf()
for (i in 1..unique.params[0].toInt()) {
val placedUnit = civInfo.addUnit(unitName, chosenCity)
val placedUnit = civInfo.units.addUnit(unitName, chosenCity)
if (placedUnit != null)
tilesUnitsWerePlacedOn.add(placedUnit.getTile().position)
}
@ -120,7 +85,7 @@ object UniqueTriggerActivation {
}
return true
}
OneTimeFreeUnitRuins -> {
UniqueType.OneTimeFreeUnitRuins -> {
var unit = civInfo.getEquivalentUnit(unique.params[0])
if ( unit.hasUnique(UniqueType.FoundCity) && civInfo.isOneCityChallenger()) {
val replacementUnit = ruleSet.units.values.firstOrNull{it.getMatchingUniques(UniqueType.BuildImprovements)
@ -131,7 +96,7 @@ object UniqueTriggerActivation {
val placingTile =
tile ?: civInfo.cities.random().getCenterTile()
val placedUnit = civInfo.placeUnitNearTile(placingTile.position, unit.name)
val placedUnit = civInfo.units.placeUnitNearTile(placingTile.position, unit.name)
if (notification != null && placedUnit != null) {
val notificationText =
if (notification.hasPlaceholderParameters())
@ -148,7 +113,7 @@ object UniqueTriggerActivation {
return placedUnit != null
}
OneTimeFreePolicy -> {
UniqueType.OneTimeFreePolicy -> {
// spectators get all techs at start of game, and if (in a mod) a tech gives a free policy, the game gets stuck on the policy picker screen
if (civInfo.isSpectator()) return false
civInfo.policies.freePolicies++
@ -157,7 +122,7 @@ object UniqueTriggerActivation {
}
return true
}
OneTimeAmountFreePolicies -> {
UniqueType.OneTimeAmountFreePolicies -> {
if (civInfo.isSpectator()) return false
civInfo.policies.freePolicies += unique.params[0].toInt()
if (notification != null) {
@ -165,7 +130,7 @@ object UniqueTriggerActivation {
}
return true
}
OneTimeEnterGoldenAge -> {
UniqueType.OneTimeEnterGoldenAge -> {
civInfo.goldenAges.enterGoldenAge()
if (notification != null) {
civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Happiness)
@ -173,22 +138,22 @@ object UniqueTriggerActivation {
return true
}
OneTimeFreeGreatPerson, MayanGainGreatPerson -> {
UniqueType.OneTimeFreeGreatPerson, UniqueType.MayanGainGreatPerson -> {
if (civInfo.isSpectator()) return false
val greatPeople = civInfo.greatPeople.getGreatPeople()
if (unique.type == MayanGainGreatPerson && civInfo.greatPeople.longCountGPPool.isEmpty())
if (unique.type == UniqueType.MayanGainGreatPerson && civInfo.greatPeople.longCountGPPool.isEmpty())
civInfo.greatPeople.longCountGPPool = greatPeople.map { it.name }.toHashSet()
if (civInfo.isHuman()) {
civInfo.greatPeople.freeGreatPeople++
// Anyone an idea for a good icon?
if (unique.type == MayanGainGreatPerson) {
if (unique.type == UniqueType.MayanGainGreatPerson) {
civInfo.greatPeople.mayaLimitedFreeGP++
civInfo.addNotification(notification!!, MayaLongCountAction(), NotificationCategory.General, MayaCalendar.notificationIcon)
} else if (notification != null)
civInfo.addNotification(notification, NotificationCategory.General)
return true
} else {
if (unique.type == MayanGainGreatPerson)
if (unique.type == UniqueType.MayanGainGreatPerson)
greatPeople.removeAll { it.name !in civInfo.greatPeople.longCountGPPool }
if (greatPeople.isEmpty()) return false
var greatPerson = greatPeople.random()
@ -204,13 +169,13 @@ object UniqueTriggerActivation {
if (scientificGP != null) greatPerson = scientificGP
}
if (unique.type == MayanGainGreatPerson)
if (unique.type == UniqueType.MayanGainGreatPerson)
civInfo.greatPeople.longCountGPPool.remove(greatPerson.name)
return civInfo.addUnit(greatPerson.name, chosenCity) != null
return civInfo.units.addUnit(greatPerson.name, chosenCity) != null
}
}
OneTimeGainPopulation -> {
UniqueType.OneTimeGainPopulation -> {
val applicableCities = when (unique.params[1]) {
"in this city" -> sequenceOf(cityInfo!!)
"in other cities" -> civInfo.cities.asSequence().filter { it != cityInfo }
@ -228,7 +193,7 @@ object UniqueTriggerActivation {
)
return applicableCities.any()
}
OneTimeGainPopulationRandomCity -> {
UniqueType.OneTimeGainPopulationRandomCity -> {
if (civInfo.cities.isEmpty()) return false
val randomCity = civInfo.cities.random(tileBasedRandom)
randomCity.population.addPopulation(unique.params[0].toInt())
@ -247,7 +212,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeFreeTech -> {
UniqueType.OneTimeFreeTech -> {
if (civInfo.isSpectator()) return false
civInfo.tech.freeTechs += 1
if (notification != null) {
@ -255,7 +220,7 @@ object UniqueTriggerActivation {
}
return true
}
OneTimeAmountFreeTechs -> {
UniqueType.OneTimeAmountFreeTechs -> {
if (civInfo.isSpectator()) return false
civInfo.tech.freeTechs += unique.params[0].toInt()
if (notification != null) {
@ -263,7 +228,7 @@ object UniqueTriggerActivation {
}
return true
}
OneTimeFreeTechRuins -> {
UniqueType.OneTimeFreeTechRuins -> {
val researchableTechsFromThatEra = ruleSet.technologies.values
.filter {
(it.column!!.era == unique.params[1] || unique.params[1] == "any era")
@ -289,7 +254,7 @@ object UniqueTriggerActivation {
return true
}
StrategicResourcesIncrease -> {
UniqueType.StrategicResourcesIncrease -> {
civInfo.cache.updateCivResources()
if (notification != null) {
civInfo.addNotification(
@ -301,7 +266,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeRevealEntireMap -> {
UniqueType.OneTimeRevealEntireMap -> {
if (notification != null) {
civInfo.addNotification(notification, LocationAction(tile?.position), NotificationCategory.General, NotificationIcon.Scout)
}
@ -309,12 +274,12 @@ object UniqueTriggerActivation {
return true
}
UnitsGainPromotion -> {
UniqueType.UnitsGainPromotion -> {
val filter = unique.params[0]
val promotion = unique.params[1]
val promotedUnitLocations: MutableList<Vector2> = mutableListOf()
for (unit in civInfo.getCivUnits()) {
for (unit in civInfo.units.getCivUnits()) {
if (unit.matchesFilter(filter)
&& ruleSet.unitPromotions.values.any {
it.name == promotion && unit.type.name in it.unitTypes
@ -336,7 +301,7 @@ object UniqueTriggerActivation {
return promotedUnitLocations.isNotEmpty()
}
CityStateCanGiftGreatPeople -> {
UniqueType.CityStateCanGiftGreatPeople -> {
civInfo.addFlag(
CivFlags.CityStateGreatPersonGift.name,
civInfo.cityStateFunctions.turnsForGreatPersonFromCityState() / 2
@ -362,7 +327,7 @@ object UniqueTriggerActivation {
// Note that the way this is implemented now, this unique does NOT stack
// I could parametrize the [Allied], but eh.
OneTimeGainStat -> {
UniqueType.OneTimeGainStat -> {
val stat = Stat.safeValueOf(unique.params[1]) ?: return false
if (stat !in Stat.statsWithCivWideField
@ -374,7 +339,7 @@ object UniqueTriggerActivation {
civInfo.addNotification(notification, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon)
return true
}
OneTimeGainStatRange -> {
UniqueType.OneTimeGainStatRange -> {
val stat = Stat.safeValueOf(unique.params[2]) ?: return false
if (stat !in Stat.statsWithCivWideField
@ -402,7 +367,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeGainPantheon -> {
UniqueType.OneTimeGainPantheon -> {
if (civInfo.religionManager.religionState != ReligionState.None) return false
val gainedFaith = civInfo.religionManager.faithForPantheon(2)
if (gainedFaith == 0) return false
@ -419,7 +384,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeGainProphet -> {
UniqueType.OneTimeGainProphet -> {
if (civInfo.religionManager.getGreatProphetEquivalent() == null) return false
val gainedFaith =
(civInfo.religionManager.faithForNextGreatProphet() * (unique.params[0].toFloat() / 100f)).toInt()
@ -437,7 +402,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeFreeBelief -> {
UniqueType.OneTimeFreeBelief -> {
if (!civInfo.isMajorCiv()) return false
val beliefType = BeliefType.valueOf(unique.params[0])
val religionManager = civInfo.religionManager
@ -454,7 +419,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeRevealSpecificMapTiles -> {
UniqueType.OneTimeRevealSpecificMapTiles -> {
if (tile == null) return false
val nearbyRevealableTiles = tile
.getTilesInDistance(unique.params[2].toInt())
@ -491,7 +456,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeRevealCrudeMap -> {
UniqueType.OneTimeRevealCrudeMap -> {
if (tile == null) return false
val revealCenter = tile.getTilesAtDistance(unique.params[0].toInt())
.filter { !civInfo.hasExplored(it) }
@ -514,7 +479,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeTriggerVoting -> {
UniqueType.OneTimeTriggerVoting -> {
for (civ in civInfo.gameInfo.civilizations)
if (!civ.isBarbarian() && !civ.isSpectator())
civ.addFlag(
@ -526,7 +491,7 @@ object UniqueTriggerActivation {
return true
}
OneTimeGlobalSpiesWhenEnteringEra -> {
UniqueType.OneTimeGlobalSpiesWhenEnteringEra -> {
if (!civInfo.isMajorCiv()) return false
if (!civInfo.gameInfo.isEspionageEnabled()) return false
val currentEra = civInfo.getEra().name
@ -544,7 +509,7 @@ object UniqueTriggerActivation {
return true
}
FreeStatBuildings, FreeSpecificBuildings -> {
UniqueType.FreeStatBuildings, UniqueType.FreeSpecificBuildings -> {
civInfo.civConstructions.tryAddFreeBuildings()
return true // not fully correct
}
@ -561,20 +526,20 @@ object UniqueTriggerActivation {
notification: String? = null
): Boolean {
when (unique.type) {
OneTimeUnitHeal -> {
UniqueType.OneTimeUnitHeal -> {
unit.healBy(unique.params[0].toInt())
if (notification != null)
unit.civInfo.addNotification(notification, unit.getTile().position, NotificationCategory.Units) // Do we have a heal icon?
return true
}
OneTimeUnitGainXP -> {
UniqueType.OneTimeUnitGainXP -> {
if (!unit.baseUnit.isMilitary()) return false
unit.promotions.XP += unique.params[0].toInt()
if (notification != null)
unit.civInfo.addNotification(notification, unit.getTile().position, NotificationCategory.Units)
return true
}
OneTimeUnitUpgrade -> {
UniqueType.OneTimeUnitUpgrade -> {
val upgradeAction = UnitActions.getFreeUpgradeAction(unit)
?: return false
upgradeAction.action!!()
@ -582,7 +547,7 @@ object UniqueTriggerActivation {
unit.civInfo.addNotification(notification, unit.getTile().position, NotificationCategory.Units)
return true
}
OneTimeUnitSpecialUpgrade -> {
UniqueType.OneTimeUnitSpecialUpgrade -> {
val upgradeAction = UnitActions.getAncientRuinsUpgradeAction(unit)
?: return false
upgradeAction.action!!()
@ -590,7 +555,7 @@ object UniqueTriggerActivation {
unit.civInfo.addNotification(notification, unit.getTile().position, NotificationCategory.Units)
return true
}
OneTimeUnitGainPromotion -> {
UniqueType.OneTimeUnitGainPromotion -> {
val promotion = unit.civInfo.gameInfo.ruleSet.unitPromotions.keys
.firstOrNull { it == unique.params[0] }
?: return false

View File

@ -452,7 +452,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
override fun postBuildEvent(cityConstructions: CityConstructions, boughtWith: Stat?): Boolean {
val civInfo = cityConstructions.cityInfo.civInfo
val unit = civInfo.placeUnitNearTile(cityConstructions.cityInfo.location, name)
val unit = civInfo.units.placeUnitNearTile(cityConstructions.cityInfo.location, name)
?: return false // couldn't place the unit, so there's actually no unit =(
//movement penalty
@ -512,10 +512,6 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
}
fun getDirectUpgradeUnit(civInfo: CivilizationInfo): BaseUnit {
return civInfo.getEquivalentUnit(upgradesTo!!)
}
fun getReplacedUnit(ruleset: Ruleset): BaseUnit {
return if (replaces == null) this
else ruleset.units[replaces!!]!!

View File

@ -3,8 +3,8 @@ package com.unciv.ui.overviewscreen
import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.ui.utils.KeyCharAndCode
import com.unciv.ui.overviewscreen.EmpireOverviewTab.EmpireOverviewTabPersistableData
import com.unciv.ui.utils.KeyCharAndCode
private typealias FactoryType = (CivilizationInfo, EmpireOverviewScreen, EmpireOverviewTabPersistableData?) -> EmpireOverviewTab
@ -39,7 +39,7 @@ enum class EmpireOverviewCategories(
Units("OtherIcons/Shield", 'U', Align.topLeft,
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?)
= UnitOverviewTab(viewingPlayer, overviewScreen, persistedData),
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.getCivUnits().none().toState()),
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.units.getCivUnits().none().toState()),
Politics("OtherIcons/Politics", 'P', Align.top,
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?)
= GlobalPoliticsOverviewTable(viewingPlayer, overviewScreen, persistedData),
@ -64,7 +64,7 @@ enum class EmpireOverviewCategories(
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?)
= NotificationsOverviewTable(worldScreen = UncivGame.Current.worldScreen!!, viewingPlayer, overviewScreen, persistedData),
fun (_: CivilizationInfo) = EmpireOverviewTabState.Normal)
; //must be here
constructor(iconName: String, shortcutChar: Char, scrollAlign: Int, factory: FactoryType, stateTester: StateTesterType = { _ -> EmpireOverviewTabState.Normal })

View File

@ -9,6 +9,7 @@ import com.unciv.Constants
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.models.UnitActionType
import com.unciv.ui.audio.SoundPlayer
import com.unciv.ui.images.IconTextButton
import com.unciv.ui.images.ImageGetter
@ -120,7 +121,7 @@ class UnitOverviewTab(
it.addLabeledValue("Population", stats.getUnitSupplyFromPop())
it.addSeparator()
it.addLabeledValue("Total Supply", stats.getUnitSupply())
it.addLabeledValue("In Use", viewingPlayer.getCivUnitsSize())
it.addLabeledValue("In Use", viewingPlayer.units.getCivUnitsSize())
it.addSeparator()
it.addLabeledValue("Supply Deficit", deficit)
it.addLabeledValue("Production Penalty", "${stats.getUnitSupplyProductionPenalty().toInt()}%")
@ -155,7 +156,7 @@ class UnitOverviewTab(
val game = overviewScreen.game
defaults().pad(5f)
for (unit in viewingPlayer.getCivUnits().sortedWith(
for (unit in viewingPlayer.units.getCivUnits().sortedWith(
compareBy({ it.displayName() },
{ !it.due },
{ it.currentMovement <= Constants.minimumMovementEpsilon },
@ -184,7 +185,15 @@ class UnitOverviewTab(
overviewScreen.game.replaceCurrentScreen(EmpireOverviewScreen(viewingPlayer, "", "")) })
}
add(editIcon)
if (unit.action == null) add() else add(unit.getActionLabel().toLabel())
fun getActionLabel(unit:MapUnit) = when {
unit.action == null -> ""
unit.isFortified() -> UnitActionType.Fortify.value
unit.isMoving() -> "Moving"
else -> unit.action!!
}
if (unit.action == null) add() else add(getActionLabel(unit).toLabel())
if (baseUnit.strength > 0) add(baseUnit.strength.toLabel()) else add()
if (baseUnit.rangedStrength > 0) add(baseUnit.rangedStrength.toLabel()) else add()
add(unit.getMovementString().toLabel())

View File

@ -32,7 +32,7 @@ class GreatPersonPickerScreen(val civInfo:CivilizationInfo) : PickerScreen() {
}
rightSideButton.onClick(UncivSound.Choir) {
civInfo.addUnit(theChosenOne!!.name, civInfo.getCapital())
civInfo.units.addUnit(theChosenOne!!.name, civInfo.getCapital())
civInfo.greatPeople.freeGreatPeople--
if (useMayaLongCount) {
civInfo.greatPeople.mayaLimitedFreeGP--

View File

@ -61,7 +61,7 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
return button
}
fun addLeaderName(civInfo: CivilizationInfo) {
private fun addLeaderName(civInfo: CivilizationInfo) {
add(LeaderIntroTable(civInfo))
addSeparator()
}
@ -350,7 +350,7 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
originalOwner.cities.minByOrNull { it.getCenterTile().aerialDistanceTo(tile) }
if (closestCity != null) {
// Attempt to place the unit near their nearest city
originalOwner.placeUnitNearTile(closestCity.location, unitName)
originalOwner.units.placeUnitNearTile(closestCity.location, unitName)
}
if (originalOwner.isCityState()) {
@ -369,7 +369,7 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
// This is so that future checks which check if a unit has been captured are caught give the right answer
// For example, in postBattleMoveToAttackedTile
capturedUnit.civInfo = captor
captor.placeUnitNearTile(tile.position, Constants.worker)
captor.units.placeUnitNearTile(tile.position, Constants.worker)
} else
capturedUnit.capturedBy(captor)
}).row()

View File

@ -18,8 +18,8 @@ import com.unciv.UncivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.UncivShowableException
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.managers.ReligionState
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.civilization.managers.ReligionState
import com.unciv.logic.event.EventBus
import com.unciv.logic.map.MapVisualization
import com.unciv.logic.multiplayer.MultiplayerGameUpdated
@ -173,7 +173,7 @@ class WorldScreen(
val tileToCenterOn: Vector2 =
when {
viewingCiv.cities.isNotEmpty() && viewingCiv.getCapital() != null -> viewingCiv.getCapital()!!.location
viewingCiv.getCivUnits().any() -> viewingCiv.getCivUnits().first().getTile().position
viewingCiv.units.getCivUnits().any() -> viewingCiv.units.getCivUnits().first().getTile().position
else -> Vector2.Zero
}
@ -411,7 +411,7 @@ class WorldScreen(
mapHolder.resetArrows()
if (UncivGame.Current.settings.showUnitMovements) {
val allUnits = gameInfo.civilizations.asSequence().flatMap { it.getCivUnits() }
val allUnits = gameInfo.civilizations.asSequence().flatMap { it.units.getCivUnits() }
val allAttacks = allUnits.map { unit -> unit.attacksSinceTurnStart.asSequence().map { attacked -> Triple(unit.civInfo, unit.getTile().position, attacked) } }.flatten() +
gameInfo.civilizations.asSequence().flatMap { civInfo -> civInfo.attacksSinceTurnStart.asSequence().map { Triple(civInfo, it.source, it.target) } }
mapHolder.updateMovementOverlay(
@ -509,7 +509,7 @@ class WorldScreen(
if (viewingCiv.isAtWar() && !completedTasks.contains("Conquer a city"))
return "Conquer a city!\nBring an enemy city down to low health > " +
"\nEnter the city with a melee unit"
if (viewingCiv.getCivUnits().any { it.baseUnit.movesLikeAirUnits() } && !completedTasks.contains("Move an air unit"))
if (viewingCiv.units.getCivUnits().any { it.baseUnit.movesLikeAirUnits() } && !completedTasks.contains("Move an air unit"))
return "Move an air unit!\nSelect an air unit > select another city within range > " +
"\nMove the unit to the other city"
if (!completedTasks.contains("See your stats breakdown"))
@ -537,10 +537,10 @@ class WorldScreen(
}
displayTutorial(TutorialTrigger.AfterConquering) { viewingCiv.cities.any { it.hasJustBeenConquered } }
displayTutorial(TutorialTrigger.InjuredUnits) { gameInfo.getCurrentPlayerCivilization().getCivUnits().any { it.health < 100 } }
displayTutorial(TutorialTrigger.InjuredUnits) { gameInfo.getCurrentPlayerCivilization().units.getCivUnits().any { it.health < 100 } }
displayTutorial(TutorialTrigger.Workers) {
gameInfo.getCurrentPlayerCivilization().getCivUnits().any {
gameInfo.getCurrentPlayerCivilization().units.getCivUnits().any {
it.hasUniqueToBuildImprovements && it.isCivilian() && !it.isGreatPerson()
}
}
@ -642,7 +642,7 @@ class WorldScreen(
// Try to select something new if we already have the next pending unit selected.
if (bottomUnitTable.selectedUnit != null)
bottomUnitTable.selectedUnit!!.due = false
val nextDueUnit = viewingCiv.cycleThroughDueUnits(bottomUnitTable.selectedUnit)
val nextDueUnit = viewingCiv.units.cycleThroughDueUnits(bottomUnitTable.selectedUnit)
if (nextDueUnit != null) {
mapHolder.setCenterPosition(
nextDueUnit.currentTile.position,
@ -774,11 +774,11 @@ class WorldScreen(
game.pushScreen(DiplomaticVotePickerScreen(viewingCiv))
}
viewingCiv.shouldGoToDueUnit() ->
viewingCiv.units.shouldGoToDueUnit() ->
NextTurnAction("Next unit", Color.LIGHT_GRAY,
"NotificationIcons/NextUnit") { switchToNextUnit() }
!game.settings.automatedUnitsMoveOnTurnStart && !viewingCiv.hasMovedAutomatedUnits && viewingCiv.getCivUnits()
!game.settings.automatedUnitsMoveOnTurnStart && !viewingCiv.hasMovedAutomatedUnits && viewingCiv.units.getCivUnits()
.any { it.currentMovement > Constants.minimumMovementEpsilon && (it.isMoving() || it.isAutomated() || it.isExploring()) } ->
NextTurnAction("Move automated units", Color.LIGHT_GRAY,
"NotificationIcons/MoveAutomatedUnits") {
@ -786,7 +786,7 @@ class WorldScreen(
isPlayersTurn = false // Disable state changes
nextTurnButton.disable()
Concurrency.run("Move automated units") {
for (unit in viewingCiv.getCivUnits())
for (unit in viewingCiv.units.getCivUnits())
unit.doAction()
launchOnGLThread {
shouldUpdate = true
@ -857,7 +857,7 @@ class WorldScreen(
.flatMap { it.cities.asSequence() }.any { viewingCiv.hasExplored(it.location) }
}
displayTutorial(TutorialTrigger.ApolloProgram) { viewingCiv.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts) }
displayTutorial(TutorialTrigger.SiegeUnits) { viewingCiv.getCivUnits().any { it.baseUnit.isProbablySiegeUnit() } }
displayTutorial(TutorialTrigger.SiegeUnits) { viewingCiv.units.getCivUnits().any { it.baseUnit.isProbablySiegeUnit() } }
displayTutorial(TutorialTrigger.Embarking) { viewingCiv.hasUnique(UniqueType.LandUnitEmbarkation) }
displayTutorial(TutorialTrigger.NaturalWonders) { viewingCiv.naturalWonders.size > 0 }
displayTutorial(TutorialTrigger.WeLoveTheKingDay) { viewingCiv.cities.any { it.demandedResource != "" } }

View File

@ -29,7 +29,7 @@ class IdleUnitButton (
enable()
onClick {
val idleUnits = unitTable.worldScreen.viewingCiv.getIdleUnits()
val idleUnits = unitTable.worldScreen.viewingCiv.units.getIdleUnits()
if (idleUnits.none()) return@onClick
val unitToSelect: MapUnit

View File

@ -443,13 +443,13 @@ object UnitActions {
title = title,
action = {
unit.destroy(destroyTransportedUnit = false)
val newUnit = civInfo.placeUnitNearTile(unitTile.position, upgradedUnit.name)
val newUnit = civInfo.units.placeUnitNearTile(unitTile.position, upgradedUnit.name)
/** We were UNABLE to place the new unit, which means that the unit failed to upgrade!
* The only known cause of this currently is "land units upgrading to water units" which fail to be placed.
*/
if (newUnit == null) {
val resurrectedUnit = civInfo.placeUnitNearTile(unitTile.position, unit.name)!!
val resurrectedUnit = civInfo.units.placeUnitNearTile(unitTile.position, unit.name)!!
unit.copyStatisticsTo(resurrectedUnit)
} else { // Managed to upgrade
if (!isFree) civInfo.addGold(-goldCostOfUpgrade)
@ -476,8 +476,7 @@ object UnitActions {
private fun addTransformAction(
unit: MapUnit,
actionList: ArrayList<UnitAction>,
maxSteps: Int = Int.MAX_VALUE
actionList: ArrayList<UnitAction>
) {
val upgradeAction = getTransformAction(unit)
if (upgradeAction != null) actionList += upgradeAction
@ -516,13 +515,13 @@ object UnitActions {
title = title,
action = {
unit.destroy()
val newUnit = civInfo.placeUnitNearTile(unitTile.position, upgradedUnit.name)
val newUnit = civInfo.units.placeUnitNearTile(unitTile.position, upgradedUnit.name)
/** We were UNABLE to place the new unit, which means that the unit failed to upgrade!
* The only known cause of this currently is "land units upgrading to water units" which fail to be placed.
*/
if (newUnit == null) {
val resurrectedUnit = civInfo.placeUnitNearTile(unitTile.position, unit.name)!!
val resurrectedUnit = civInfo.units.placeUnitNearTile(unitTile.position, unit.name)!!
unit.copyStatisticsTo(resurrectedUnit)
} else { // Managed to upgrade
unit.copyStatisticsTo(newUnit)
@ -771,7 +770,7 @@ object UnitActions {
}
}
fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, city: CityInfo) {
private fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, city: CityInfo) {
if (!unit.civInfo.religionManager.maySpreadReligionAtAll(unit)) return
actionList += UnitAction(UnitActionType.SpreadReligion,
title = "Spread [${unit.getReligionDisplayName()!!}]",

View File

@ -61,10 +61,10 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
// This is so that not on every update(), we will update the unit table.
// Most of the time it's the same unit with the same stats so why waste precious time?
var selectedUnitHasChanged = false
private var selectedUnitHasChanged = false
val separator: Actor
var bg = Image(BaseScreen.skinStrings.getUiBackground("WorldScreen/UnitTable",
private var bg = Image(BaseScreen.skinStrings.getUiBackground("WorldScreen/UnitTable",
BaseScreen.skinStrings.roundedEdgeRectangleMidShape,
BaseScreen.skinStrings.skinConfig.baseColor.darken(0.5f)))
@ -131,7 +131,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
}
}
if (worldScreen.viewingCiv.getIdleUnits().any()) { // more efficient to do this check once for both
if (worldScreen.viewingCiv.units.getIdleUnits().any()) { // more efficient to do this check once for both
prevIdleUnitButton.enable()
nextIdleUnitButton.enable()
} else {
@ -323,16 +323,13 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
else -> null
}
if (curUnit == null) {
nextUnit = priorityUnit
} else {
nextUnit = when {
nextUnit = when {
curUnit == null -> priorityUnit
curUnit == civUnit && milUnit != null && milUnit.isEligible() -> {if (civUnit.isPrioritized()) milUnit else null}
curUnit == milUnit && civUnit != null && civUnit.isEligible() -> {if (civUnit.isPrioritized()) null else civUnit}
else -> priorityUnit
}
}
when {
forceSelectUnit != null ->

View File

@ -495,7 +495,7 @@ class UnitMovementAlgorithmsTests {
unit.civInfo = civInfo
unit.baseUnit.uniques.add("Can carry [2] [Aircraft] units")
unit.updateUniques(ruleSet)
civInfo.addUnit(unit, false)
civInfo.units.addUnit(unit, false)
val fighters = ArrayList<MapUnit>()
for (i in 0..1) {
@ -507,14 +507,14 @@ class UnitMovementAlgorithmsTests {
tile.airUnits += newFighter
newFighter.name = "Fighter"
newFighter.isTransported = true
civInfo.addUnit(newFighter, false)
civInfo.units.addUnit(newFighter, false)
fighters += newFighter
}
// simulate ejecting all units within foreign territory
for (unit in civInfo.getCivUnits()) unit.movement.teleportToClosestMoveableTile()
for (unit in civInfo.units.getCivUnits()) unit.movement.teleportToClosestMoveableTile()
Assert.assertTrue("Transport and transported units must be teleported to the same tile",
civInfo.getCivUnits().toList().size == 3 && civInfo.getCivUnits().all { it.getTile() == newTiles.last() })
civInfo.units.getCivUnits().toList().size == 3 && civInfo.units.getCivUnits().all { it.getTile() == newTiles.last() })
}
}

View File

@ -4,17 +4,17 @@ import com.badlogic.gdx.Gdx
import com.unciv.UncivGame
import com.unciv.json.json
import com.unciv.logic.GameInfo
import com.unciv.logic.files.UncivFiles
import com.unciv.logic.GameStarter
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.files.UncivFiles
import com.unciv.logic.map.MapParameters
import com.unciv.logic.map.MapSize
import com.unciv.logic.map.MapSizeNew
import com.unciv.models.metadata.GameParameters
import com.unciv.models.metadata.GameSettings
import com.unciv.models.metadata.GameSetupInfo
import com.unciv.models.metadata.Player
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.metadata.GameSetupInfo
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.utils.debug
import org.junit.After
@ -71,7 +71,7 @@ class SerializationTests {
// Found a city otherwise too many classes have no instance and are not tested
val civ = game.getCurrentPlayerCivilization()
val unit = civ.getCivUnits().first { it.hasUnique(UniqueType.FoundCity) }
val unit = civ.units.getCivUnits().first { it.hasUnique(UniqueType.FoundCity) }
val tile = unit.getTile()
unit.civInfo.addCity(tile.position)
if (tile.ruleset.tileImprovements.containsKey("City center"))
@ -108,7 +108,7 @@ class SerializationTests {
return
}
val pattern = """\{(\w+)\${'$'}delegate:\{class:kotlin.SynchronizedLazyImpl,"""
val pattern = """\{(\w+)\\${'$'}delegate:\{class:kotlin.SynchronizedLazyImpl,"""
val matches = Regex(pattern).findAll(json)
matches.forEach {
debug("Lazy missing `@delegate:Transient` annotation: %s", it.groups[1]!!.value)