mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 09:48:12 +07:00
Upgrade path redone
This commit is contained in:
@ -4,7 +4,7 @@ object BuildConfig {
|
|||||||
const val kotlinVersion = "1.7.21"
|
const val kotlinVersion = "1.7.21"
|
||||||
const val appName = "Unciv"
|
const val appName = "Unciv"
|
||||||
const val appCodeNumber = 781
|
const val appCodeNumber = 781
|
||||||
const val appVersion = "4.3.11-patch1"
|
const val appVersion = "4.3.11-upgrade-redone"
|
||||||
|
|
||||||
const val gdxVersion = "1.11.0"
|
const val gdxVersion = "1.11.0"
|
||||||
const val roboVMVersion = "2.3.1"
|
const val roboVMVersion = "2.3.1"
|
||||||
|
@ -91,18 +91,6 @@ class RejectionReasons: HashSet<RejectionReasonInstance>() {
|
|||||||
|
|
||||||
fun contains(rejectionReason: RejectionReason) = any { it.rejectionReason == rejectionReason }
|
fun contains(rejectionReason: RejectionReason) = any { it.rejectionReason == rejectionReason }
|
||||||
|
|
||||||
fun isOkForUnitUpgradeIgnoringRequirements(
|
|
||||||
ignoreTechPolicyEraWonderRequirements: Boolean = false,
|
|
||||||
ignoreResources: Boolean = false
|
|
||||||
): Boolean {
|
|
||||||
var relevantRejectionReasons = this.asSequence().filterNot { it.rejectionReason == RejectionReason.Unbuildable }
|
|
||||||
if (ignoreTechPolicyEraWonderRequirements)
|
|
||||||
relevantRejectionReasons = relevantRejectionReasons.filterNot { it.rejectionReason in techPolicyEraWonderRequirements }
|
|
||||||
if (ignoreResources)
|
|
||||||
relevantRejectionReasons = relevantRejectionReasons.filterNot { it.rejectionReason == RejectionReason.ConsumesResources }
|
|
||||||
return relevantRejectionReasons.none()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasAReasonToBeRemovedFromQueue(): Boolean {
|
fun hasAReasonToBeRemovedFromQueue(): Boolean {
|
||||||
return any { it.rejectionReason in reasonsToDefinitivelyRemoveFromQueue }
|
return any { it.rejectionReason in reasonsToDefinitivelyRemoveFromQueue }
|
||||||
}
|
}
|
||||||
@ -117,7 +105,7 @@ class RejectionReasons: HashSet<RejectionReasonInstance>() {
|
|||||||
|
|
||||||
// Used for constant variables in the functions above
|
// Used for constant variables in the functions above
|
||||||
companion object {
|
companion object {
|
||||||
private val techPolicyEraWonderRequirements = hashSetOf(
|
val techPolicyEraWonderRequirements = hashSetOf(
|
||||||
RejectionReason.Obsoleted,
|
RejectionReason.Obsoleted,
|
||||||
RejectionReason.RequiresTech,
|
RejectionReason.RequiresTech,
|
||||||
RejectionReason.RequiresPolicy,
|
RejectionReason.RequiresPolicy,
|
||||||
|
@ -10,6 +10,7 @@ import com.unciv.logic.battle.Battle
|
|||||||
import com.unciv.logic.battle.MapUnitCombatant
|
import com.unciv.logic.battle.MapUnitCombatant
|
||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
import com.unciv.logic.city.RejectionReason
|
import com.unciv.logic.city.RejectionReason
|
||||||
|
import com.unciv.logic.city.RejectionReasons
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.logic.civilization.LocationAction
|
import com.unciv.logic.civilization.LocationAction
|
||||||
import com.unciv.logic.civilization.NotificationIcon
|
import com.unciv.logic.civilization.NotificationIcon
|
||||||
@ -493,88 +494,72 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Follow the upgrade chain, stopping when there is no [BaseUnit.upgradesTo] or a tech is not researched.
|
|
||||||
* @param [actionAllowStep] Will be called for each upgrade allowed by tech and has a double purpose:
|
|
||||||
* Side effects, e.g. for aggregation, are allowed, and
|
|
||||||
* returning `false` will abort the upgrade chain and not include the step in the final count.
|
|
||||||
* @return Number of allowed upgrade steps
|
|
||||||
*/
|
|
||||||
private fun followUpgradePath(
|
|
||||||
maxSteps: Int = Int.MAX_VALUE,
|
|
||||||
actionAllowStep: (oldUnit: BaseUnit, newUnit: BaseUnit)->Boolean
|
|
||||||
): Int {
|
|
||||||
var unit = baseUnit()
|
|
||||||
var steps = 0
|
|
||||||
|
|
||||||
// Go up the upgrade tree until you find the last one which is buildable
|
/** Returns FULL upgrade path, without checking what we can or cannot build currently.
|
||||||
while(steps < maxSteps) {
|
* Does not contain current baseunit, so will be empty if no upgrades. */
|
||||||
if (unit.upgradesTo == null) break
|
fun getUpgradePath(): List<BaseUnit>{
|
||||||
val newUnit = unit.getDirectUpgradeUnit(civInfo)
|
var currentUnit = baseUnit
|
||||||
val techName = newUnit.requiredTech
|
val upgradeList = arrayListOf<BaseUnit>()
|
||||||
if (techName != null && !civInfo.tech.isResearched(techName)) break
|
while (currentUnit.upgradesTo != null){
|
||||||
if (!actionAllowStep(unit, newUnit)) break
|
val nextUpgrade = civInfo.getEquivalentUnit(currentUnit.upgradesTo!!)
|
||||||
unit = newUnit
|
upgradeList.add(currentUnit)
|
||||||
steps++
|
currentUnit = nextUpgrade
|
||||||
}
|
}
|
||||||
return steps
|
return upgradeList
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the base unit this map unit could upgrade to, respecting researched tech and nation uniques only.
|
/** Get the base unit this map unit could upgrade to, respecting researched tech and nation uniques only.
|
||||||
* Note that if the unit can't upgrade, the current BaseUnit is returned.
|
* Note that if the unit can't upgrade, the current BaseUnit is returned.
|
||||||
* @param maxSteps follow the upgrade chain only this far. Useful values are default (directly upgrade to what tech ultimately allows) or 1 (Civ5 behaviour)
|
|
||||||
*/
|
*/
|
||||||
// Used from UnitAutomation, UI action, canUpgrade
|
// Used from UnitAutomation, UI action, canUpgrade
|
||||||
fun getUnitToUpgradeTo(maxSteps: Int = Int.MAX_VALUE): BaseUnit {
|
fun getUnitToUpgradeTo(): BaseUnit {
|
||||||
var unit = baseUnit()
|
val upgradePath = getUpgradePath()
|
||||||
followUpgradePath(maxSteps) { _, newUnit ->
|
|
||||||
unit = newUnit
|
|
||||||
true
|
|
||||||
}
|
|
||||||
return unit
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check if the default upgrade would do more than one step
|
fun isInvalidUpgradeDestination(baseUnit: BaseUnit): Boolean{
|
||||||
* - to avoid showing both the single step and normal upgrades in UnitActions */
|
if (baseUnit.requiredTech != null && !civInfo.tech.isResearched(baseUnit.requiredTech!!))
|
||||||
fun canUpgradeMultipleSteps(): Boolean {
|
return true
|
||||||
return 1 < followUpgradePath(2) { _, _ -> true }
|
if (baseUnit.getMatchingUniques(UniqueType.OnlyAvailableWhen).any {
|
||||||
|
!it.conditionalsApply(StateForConditionals(civInfo, unit = this ))
|
||||||
|
}) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (baseUnit in upgradePath.reversed()){
|
||||||
|
if (isInvalidUpgradeDestination(baseUnit)) continue
|
||||||
|
return baseUnit
|
||||||
|
}
|
||||||
|
return baseUnit
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check whether this unit can upgrade to [unitToUpgradeTo]. This does not check or follow the
|
/** Check whether this unit can upgrade to [unitToUpgradeTo]. This does not check or follow the
|
||||||
* normal upgrade chain defined by [BaseUnit.upgradesTo], unless [unitToUpgradeTo] is left at default.
|
* normal upgrade chain defined by [BaseUnit.upgradesTo], unless [unitToUpgradeTo] is left at default.
|
||||||
* @param maxSteps only used for default of [unitToUpgradeTo], ignored otherwise.
|
|
||||||
* @param ignoreRequirements Ignore possible tech/policy/building requirements (e.g. resource requirements still count).
|
* @param ignoreRequirements Ignore possible tech/policy/building requirements (e.g. resource requirements still count).
|
||||||
* Used for upgrading units via ancient ruins.
|
* Used for upgrading units via ancient ruins.
|
||||||
* @param ignoreResources Ignore resource requirements (tech still counts)
|
* @param ignoreResources Ignore resource requirements (tech still counts)
|
||||||
* Used to display disabled Upgrade button
|
* Used to display disabled Upgrade button
|
||||||
*/
|
*/
|
||||||
fun canUpgrade(
|
fun canUpgrade(
|
||||||
maxSteps: Int = Int.MAX_VALUE,
|
unitToUpgradeTo: BaseUnit = getUnitToUpgradeTo(),
|
||||||
unitToUpgradeTo: BaseUnit = getUnitToUpgradeTo(maxSteps),
|
|
||||||
ignoreRequirements: Boolean = false,
|
ignoreRequirements: Boolean = false,
|
||||||
ignoreResources: Boolean = false
|
ignoreResources: Boolean = false
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (name == unitToUpgradeTo.name) return false
|
if (name == unitToUpgradeTo.name) return false
|
||||||
val rejectionReasons = unitToUpgradeTo.getRejectionReasons(civInfo)
|
|
||||||
if (rejectionReasons.isOkForUnitUpgradeIgnoringRequirements(ignoreRequirements, ignoreResources)) return true
|
|
||||||
|
|
||||||
// The resource requirements check above did not consider that the resources
|
|
||||||
// this unit currently "consumes" are available for an upgrade too - if that's one of the
|
|
||||||
// reasons, repeat the check with those resources in the pool.
|
|
||||||
if (!rejectionReasons.contains(RejectionReason.ConsumesResources))
|
|
||||||
return false
|
|
||||||
|
|
||||||
//TODO redesign without kludge: Inform getRejectionReasons about 'virtually available' resources somehow
|
|
||||||
|
|
||||||
// We need to remove the unit from the civ for this check,
|
// We need to remove the unit from the civ for this check,
|
||||||
// because if the unit requires, say, horses, and so does its upgrade,
|
// because if the unit requires, say, horses, and so does its upgrade,
|
||||||
// and the civ currently has 0 horses, we need to see if the upgrade will be buildable
|
// and the civ currently has 0 horses, we need to see if the upgrade will be buildable
|
||||||
// WHEN THE CURRENT UNIT IS NOT HERE
|
// WHEN THE CURRENT UNIT IS NOT HERE
|
||||||
|
// TODO redesign without kludge: Inform getRejectionReasons about 'virtually available' resources somehow
|
||||||
civInfo.removeUnit(this)
|
civInfo.removeUnit(this)
|
||||||
val canUpgrade = unitToUpgradeTo.getRejectionReasons(civInfo)
|
val rejectionReasons = unitToUpgradeTo.getRejectionReasons(civInfo)
|
||||||
.isOkForUnitUpgradeIgnoringRequirements(ignoreTechPolicyEraWonderRequirements = ignoreRequirements)
|
|
||||||
civInfo.addUnit(this)
|
civInfo.addUnit(this)
|
||||||
return canUpgrade
|
|
||||||
|
var relevantRejectionReasons = rejectionReasons.asSequence().filterNot { it.rejectionReason == RejectionReason.Unbuildable }
|
||||||
|
if (ignoreRequirements)
|
||||||
|
relevantRejectionReasons = relevantRejectionReasons.filterNot { it.rejectionReason in RejectionReasons.techPolicyEraWonderRequirements }
|
||||||
|
if (ignoreResources)
|
||||||
|
relevantRejectionReasons = relevantRejectionReasons.filterNot { it.rejectionReason == RejectionReason.ConsumesResources }
|
||||||
|
return relevantRejectionReasons.none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Determine gold cost of a Unit Upgrade, potentially over several steps.
|
/** Determine gold cost of a Unit Upgrade, potentially over several steps.
|
||||||
@ -601,17 +586,21 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
|||||||
for (unique in civInfo.getMatchingUniques(UniqueType.UnitUpgradeCost, stateForConditionals))
|
for (unique in civInfo.getMatchingUniques(UniqueType.UnitUpgradeCost, stateForConditionals))
|
||||||
civModifier *= unique.params[0].toPercent()
|
civModifier *= unique.params[0].toPercent()
|
||||||
|
|
||||||
followUpgradePath(actionAllowStep = fun(oldUnit: BaseUnit, newUnit: BaseUnit): Boolean {
|
val upgradePath = getUpgradePath()
|
||||||
|
var currentUnit = baseUnit
|
||||||
|
for (baseUnit in upgradePath) {
|
||||||
// do clamping and rounding here so upgrading stepwise costs the same as upgrading far down the chain
|
// do clamping and rounding here so upgrading stepwise costs the same as upgrading far down the chain
|
||||||
var stepCost = constants.base
|
var stepCost = constants.base
|
||||||
stepCost += (constants.perProduction * (newUnit.cost - oldUnit.cost)).coerceAtLeast(0f)
|
stepCost += (constants.perProduction * (baseUnit.cost - currentUnit.cost)).coerceAtLeast(0f)
|
||||||
val era = ruleset.eras[ruleset.technologies[newUnit.requiredTech]?.era()]
|
val era = ruleset.eras[ruleset.technologies[baseUnit.requiredTech]?.era()]
|
||||||
if (era != null)
|
if (era != null)
|
||||||
stepCost *= (1f + era.eraNumber * constants.eraMultiplier)
|
stepCost *= (1f + era.eraNumber * constants.eraMultiplier)
|
||||||
stepCost = (stepCost * civModifier).pow(constants.exponent)
|
stepCost = (stepCost * civModifier).pow(constants.exponent)
|
||||||
goldCostOfUpgrade += (stepCost / constants.roundTo).toInt() * constants.roundTo
|
goldCostOfUpgrade += (stepCost / constants.roundTo).toInt() * constants.roundTo
|
||||||
return newUnit != unitToUpgradeTo // stop at requested BaseUnit to upgrade to
|
if (baseUnit == unitToUpgradeTo)
|
||||||
})
|
break // stop at requested BaseUnit to upgrade to
|
||||||
|
currentUnit = baseUnit
|
||||||
|
}
|
||||||
|
|
||||||
return goldCostOfUpgrade
|
return goldCostOfUpgrade
|
||||||
}
|
}
|
||||||
|
@ -126,15 +126,17 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
|
|
||||||
fun ruleset() = state.civInfo!!.gameInfo.ruleSet
|
fun ruleset() = state.civInfo!!.gameInfo.ruleSet
|
||||||
|
|
||||||
|
val relevantUnit by lazy {
|
||||||
|
if (state.ourCombatant != null && state.ourCombatant is MapUnitCombatant) state.ourCombatant.unit
|
||||||
|
else state.unit
|
||||||
|
}
|
||||||
|
|
||||||
val relevantTile by lazy { state.attackedTile
|
val relevantTile by lazy { state.attackedTile
|
||||||
?: state.tile
|
?: state.tile
|
||||||
?: state.unit?.getTile()
|
?: state.unit?.getTile()
|
||||||
?: state.cityInfo?.getCenterTile()
|
?: state.cityInfo?.getCenterTile()
|
||||||
}
|
}
|
||||||
val relevantUnit by lazy {
|
|
||||||
if (state.ourCombatant != null && state.ourCombatant is MapUnitCombatant) state.ourCombatant.unit
|
|
||||||
else state.unit
|
|
||||||
}
|
|
||||||
|
|
||||||
val stateBasedRandom by lazy { Random(state.hashCode()) }
|
val stateBasedRandom by lazy { Random(state.hashCode()) }
|
||||||
|
|
||||||
|
@ -89,9 +89,6 @@ object UnitActions {
|
|||||||
addSleepActions(actionList, unit, true)
|
addSleepActions(actionList, unit, true)
|
||||||
addFortifyActions(actionList, unit, true)
|
addFortifyActions(actionList, unit, true)
|
||||||
|
|
||||||
if (unit.canUpgradeMultipleSteps())
|
|
||||||
addUnitUpgradeAction(unit, actionList, 1)
|
|
||||||
|
|
||||||
addSwapAction(unit, actionList, worldScreen)
|
addSwapAction(unit, actionList, worldScreen)
|
||||||
addDisbandAction(actionList, unit, worldScreen)
|
addDisbandAction(actionList, unit, worldScreen)
|
||||||
addGiftAction(unit, actionList, tile)
|
addGiftAction(unit, actionList, tile)
|
||||||
@ -394,17 +391,15 @@ object UnitActions {
|
|||||||
|
|
||||||
private fun addUnitUpgradeAction(
|
private fun addUnitUpgradeAction(
|
||||||
unit: MapUnit,
|
unit: MapUnit,
|
||||||
actionList: ArrayList<UnitAction>,
|
actionList: ArrayList<UnitAction>
|
||||||
maxSteps: Int = Int.MAX_VALUE
|
|
||||||
) {
|
) {
|
||||||
val upgradeAction = getUpgradeAction(unit, maxSteps)
|
val upgradeAction = getUpgradeAction(unit)
|
||||||
if (upgradeAction != null) actionList += upgradeAction
|
if (upgradeAction != null) actionList += upgradeAction
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Common implementation for [getUpgradeAction], [getFreeUpgradeAction] and [getAncientRuinsUpgradeAction] */
|
/** Common implementation for [getUpgradeAction], [getFreeUpgradeAction] and [getAncientRuinsUpgradeAction] */
|
||||||
private fun getUpgradeAction(
|
private fun getUpgradeAction(
|
||||||
unit: MapUnit,
|
unit: MapUnit,
|
||||||
maxSteps: Int,
|
|
||||||
isFree: Boolean,
|
isFree: Boolean,
|
||||||
isSpecial: Boolean
|
isSpecial: Boolean
|
||||||
): UnitAction? {
|
): UnitAction? {
|
||||||
@ -416,10 +411,11 @@ object UnitActions {
|
|||||||
val upgradesTo = unit.baseUnit().upgradesTo
|
val upgradesTo = unit.baseUnit().upgradesTo
|
||||||
val specialUpgradesTo = unit.baseUnit().specialUpgradesTo
|
val specialUpgradesTo = unit.baseUnit().specialUpgradesTo
|
||||||
val upgradedUnit = when {
|
val upgradedUnit = when {
|
||||||
isSpecial && specialUpgradesTo != null -> civInfo.getEquivalentUnit (specialUpgradesTo)
|
isSpecial && specialUpgradesTo != null -> civInfo.getEquivalentUnit(specialUpgradesTo)
|
||||||
isFree && upgradesTo != null -> civInfo.getEquivalentUnit(upgradesTo) // getUnitToUpgradeTo can't ignore tech
|
(isFree || isSpecial) && upgradesTo != null -> civInfo.getEquivalentUnit(upgradesTo) // Only get DIRECT upgrade
|
||||||
else -> unit.getUnitToUpgradeTo(maxSteps)
|
else -> unit.getUnitToUpgradeTo() // Get EVENTUAL upgrade, all the way up the chain
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unit.canUpgrade(unitToUpgradeTo = upgradedUnit, ignoreRequirements = isFree, ignoreResources = true))
|
if (!unit.canUpgrade(unitToUpgradeTo = upgradedUnit, ignoreRequirements = isFree, ignoreResources = true))
|
||||||
return null
|
return null
|
||||||
|
|
||||||
@ -470,12 +466,12 @@ object UnitActions {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUpgradeAction(unit: MapUnit, maxSteps: Int = Int.MAX_VALUE) =
|
fun getUpgradeAction(unit: MapUnit) =
|
||||||
getUpgradeAction(unit, maxSteps, isFree = false, isSpecial = false)
|
getUpgradeAction(unit, isFree = false, isSpecial = false)
|
||||||
fun getFreeUpgradeAction(unit: MapUnit) =
|
fun getFreeUpgradeAction(unit: MapUnit) =
|
||||||
getUpgradeAction(unit, 1, isFree = true, isSpecial = false)
|
getUpgradeAction(unit, isFree = true, isSpecial = false)
|
||||||
fun getAncientRuinsUpgradeAction(unit: MapUnit) =
|
fun getAncientRuinsUpgradeAction(unit: MapUnit) =
|
||||||
getUpgradeAction(unit, 1, isFree = true, isSpecial = true)
|
getUpgradeAction(unit, isFree = true, isSpecial = true)
|
||||||
|
|
||||||
private fun addBuildingImprovementsAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo, worldScreen: WorldScreen, unitTable: UnitTable) {
|
private fun addBuildingImprovementsAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo, worldScreen: WorldScreen, unitTable: UnitTable) {
|
||||||
if (!unit.hasUniqueToBuildImprovements) return
|
if (!unit.hasUniqueToBuildImprovements) return
|
||||||
|
Reference in New Issue
Block a user