mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-09 20:29:50 +07:00
Upgrade path redone
This commit is contained in:
parent
bfbabcb97e
commit
7427932985
@ -4,7 +4,7 @@ object BuildConfig {
|
||||
const val kotlinVersion = "1.7.21"
|
||||
const val appName = "Unciv"
|
||||
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 roboVMVersion = "2.3.1"
|
||||
|
@ -91,18 +91,6 @@ class RejectionReasons: HashSet<RejectionReasonInstance>() {
|
||||
|
||||
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 {
|
||||
return any { it.rejectionReason in reasonsToDefinitivelyRemoveFromQueue }
|
||||
}
|
||||
@ -117,7 +105,7 @@ class RejectionReasons: HashSet<RejectionReasonInstance>() {
|
||||
|
||||
// Used for constant variables in the functions above
|
||||
companion object {
|
||||
private val techPolicyEraWonderRequirements = hashSetOf(
|
||||
val techPolicyEraWonderRequirements = hashSetOf(
|
||||
RejectionReason.Obsoleted,
|
||||
RejectionReason.RequiresTech,
|
||||
RejectionReason.RequiresPolicy,
|
||||
|
@ -10,6 +10,7 @@ import com.unciv.logic.battle.Battle
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.RejectionReason
|
||||
import com.unciv.logic.city.RejectionReasons
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.LocationAction
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
@ -493,88 +494,72 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
||||
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
|
||||
while(steps < maxSteps) {
|
||||
if (unit.upgradesTo == null) break
|
||||
val newUnit = unit.getDirectUpgradeUnit(civInfo)
|
||||
val techName = newUnit.requiredTech
|
||||
if (techName != null && !civInfo.tech.isResearched(techName)) break
|
||||
if (!actionAllowStep(unit, newUnit)) break
|
||||
unit = newUnit
|
||||
steps++
|
||||
/** Returns FULL upgrade path, without checking what we can or cannot build currently.
|
||||
* Does not contain current baseunit, so will be empty if no upgrades. */
|
||||
fun getUpgradePath(): List<BaseUnit>{
|
||||
var currentUnit = baseUnit
|
||||
val upgradeList = arrayListOf<BaseUnit>()
|
||||
while (currentUnit.upgradesTo != null){
|
||||
val nextUpgrade = civInfo.getEquivalentUnit(currentUnit.upgradesTo!!)
|
||||
upgradeList.add(currentUnit)
|
||||
currentUnit = nextUpgrade
|
||||
}
|
||||
return steps
|
||||
return upgradeList
|
||||
}
|
||||
|
||||
/** 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.
|
||||
* @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
|
||||
fun getUnitToUpgradeTo(maxSteps: Int = Int.MAX_VALUE): BaseUnit {
|
||||
var unit = baseUnit()
|
||||
followUpgradePath(maxSteps) { _, newUnit ->
|
||||
unit = newUnit
|
||||
true
|
||||
}
|
||||
return unit
|
||||
}
|
||||
fun getUnitToUpgradeTo(): BaseUnit {
|
||||
val upgradePath = getUpgradePath()
|
||||
|
||||
/** Check if the default upgrade would do more than one step
|
||||
* - to avoid showing both the single step and normal upgrades in UnitActions */
|
||||
fun canUpgradeMultipleSteps(): Boolean {
|
||||
return 1 < followUpgradePath(2) { _, _ -> true }
|
||||
fun isInvalidUpgradeDestination(baseUnit: BaseUnit): Boolean{
|
||||
if (baseUnit.requiredTech != null && !civInfo.tech.isResearched(baseUnit.requiredTech!!))
|
||||
return 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
|
||||
* 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).
|
||||
* Used for upgrading units via ancient ruins.
|
||||
* @param ignoreResources Ignore resource requirements (tech still counts)
|
||||
* Used to display disabled Upgrade button
|
||||
*/
|
||||
fun canUpgrade(
|
||||
maxSteps: Int = Int.MAX_VALUE,
|
||||
unitToUpgradeTo: BaseUnit = getUnitToUpgradeTo(maxSteps),
|
||||
unitToUpgradeTo: BaseUnit = getUnitToUpgradeTo(),
|
||||
ignoreRequirements: Boolean = false,
|
||||
ignoreResources: Boolean = false
|
||||
): Boolean {
|
||||
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,
|
||||
// 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
|
||||
// WHEN THE CURRENT UNIT IS NOT HERE
|
||||
// TODO redesign without kludge: Inform getRejectionReasons about 'virtually available' resources somehow
|
||||
civInfo.removeUnit(this)
|
||||
val canUpgrade = unitToUpgradeTo.getRejectionReasons(civInfo)
|
||||
.isOkForUnitUpgradeIgnoringRequirements(ignoreTechPolicyEraWonderRequirements = ignoreRequirements)
|
||||
val rejectionReasons = unitToUpgradeTo.getRejectionReasons(civInfo)
|
||||
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.
|
||||
@ -601,17 +586,21 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
||||
for (unique in civInfo.getMatchingUniques(UniqueType.UnitUpgradeCost, stateForConditionals))
|
||||
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
|
||||
var stepCost = constants.base
|
||||
stepCost += (constants.perProduction * (newUnit.cost - oldUnit.cost)).coerceAtLeast(0f)
|
||||
val era = ruleset.eras[ruleset.technologies[newUnit.requiredTech]?.era()]
|
||||
stepCost += (constants.perProduction * (baseUnit.cost - currentUnit.cost)).coerceAtLeast(0f)
|
||||
val era = ruleset.eras[ruleset.technologies[baseUnit.requiredTech]?.era()]
|
||||
if (era != null)
|
||||
stepCost *= (1f + era.eraNumber * constants.eraMultiplier)
|
||||
stepCost = (stepCost * civModifier).pow(constants.exponent)
|
||||
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
|
||||
}
|
||||
|
@ -126,15 +126,17 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
|
||||
): Boolean {
|
||||
|
||||
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
|
||||
?: state.tile
|
||||
?: state.unit?.getTile()
|
||||
?: 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()) }
|
||||
|
||||
|
@ -89,9 +89,6 @@ object UnitActions {
|
||||
addSleepActions(actionList, unit, true)
|
||||
addFortifyActions(actionList, unit, true)
|
||||
|
||||
if (unit.canUpgradeMultipleSteps())
|
||||
addUnitUpgradeAction(unit, actionList, 1)
|
||||
|
||||
addSwapAction(unit, actionList, worldScreen)
|
||||
addDisbandAction(actionList, unit, worldScreen)
|
||||
addGiftAction(unit, actionList, tile)
|
||||
@ -394,17 +391,15 @@ object UnitActions {
|
||||
|
||||
private fun addUnitUpgradeAction(
|
||||
unit: MapUnit,
|
||||
actionList: ArrayList<UnitAction>,
|
||||
maxSteps: Int = Int.MAX_VALUE
|
||||
actionList: ArrayList<UnitAction>
|
||||
) {
|
||||
val upgradeAction = getUpgradeAction(unit, maxSteps)
|
||||
val upgradeAction = getUpgradeAction(unit)
|
||||
if (upgradeAction != null) actionList += upgradeAction
|
||||
}
|
||||
|
||||
/** Common implementation for [getUpgradeAction], [getFreeUpgradeAction] and [getAncientRuinsUpgradeAction] */
|
||||
private fun getUpgradeAction(
|
||||
unit: MapUnit,
|
||||
maxSteps: Int,
|
||||
isFree: Boolean,
|
||||
isSpecial: Boolean
|
||||
): UnitAction? {
|
||||
@ -416,10 +411,11 @@ object UnitActions {
|
||||
val upgradesTo = unit.baseUnit().upgradesTo
|
||||
val specialUpgradesTo = unit.baseUnit().specialUpgradesTo
|
||||
val upgradedUnit = when {
|
||||
isSpecial && specialUpgradesTo != null -> civInfo.getEquivalentUnit (specialUpgradesTo)
|
||||
isFree && upgradesTo != null -> civInfo.getEquivalentUnit(upgradesTo) // getUnitToUpgradeTo can't ignore tech
|
||||
else -> unit.getUnitToUpgradeTo(maxSteps)
|
||||
isSpecial && specialUpgradesTo != null -> civInfo.getEquivalentUnit(specialUpgradesTo)
|
||||
(isFree || isSpecial) && upgradesTo != null -> civInfo.getEquivalentUnit(upgradesTo) // Only get DIRECT upgrade
|
||||
else -> unit.getUnitToUpgradeTo() // Get EVENTUAL upgrade, all the way up the chain
|
||||
}
|
||||
|
||||
if (!unit.canUpgrade(unitToUpgradeTo = upgradedUnit, ignoreRequirements = isFree, ignoreResources = true))
|
||||
return null
|
||||
|
||||
@ -470,12 +466,12 @@ object UnitActions {
|
||||
)
|
||||
}
|
||||
|
||||
fun getUpgradeAction(unit: MapUnit, maxSteps: Int = Int.MAX_VALUE) =
|
||||
getUpgradeAction(unit, maxSteps, isFree = false, isSpecial = false)
|
||||
fun getUpgradeAction(unit: MapUnit) =
|
||||
getUpgradeAction(unit, isFree = false, isSpecial = false)
|
||||
fun getFreeUpgradeAction(unit: MapUnit) =
|
||||
getUpgradeAction(unit, 1, isFree = true, isSpecial = false)
|
||||
getUpgradeAction(unit, isFree = true, isSpecial = false)
|
||||
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) {
|
||||
if (!unit.hasUniqueToBuildImprovements) return
|
||||
|
Loading…
Reference in New Issue
Block a user