mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-09 12:15:04 -04:00
Add unique to allow for generalized great generals (#10828)
* Add unique to allow for generalized great generals * Don't add compatibility for rulesets with conditional generals * Add to rulesets * add in pre Kotlin 9 parenthesis * whoops missed a parenthesis * I guess pre Kotlin 9 parenthesis was unnecessary, whoops * Add back old variables to clone function * Move the list of all potential generals to Ruleset * Move list of unit construction rejections to IConstruction * flip !any{} to none{} * Fix imports * Typo
This commit is contained in:
parent
6e84377090
commit
665b5aa87c
@ -1653,6 +1653,7 @@
|
|||||||
"Empire enters a [8]-turn Golden Age <by consuming this unit>",
|
"Empire enters a [8]-turn Golden Age <by consuming this unit>",
|
||||||
"[+15]% Strength bonus for [Military] units within [2] tiles",
|
"[+15]% Strength bonus for [Military] units within [2] tiles",
|
||||||
"Can instantly construct a [Citadel] improvement <by consuming this unit>",
|
"Can instantly construct a [Citadel] improvement <by consuming this unit>",
|
||||||
|
"Can be earned through combat",
|
||||||
"Great Person - [War]", "Unbuildable", "Uncapturable"],
|
"Great Person - [War]", "Unbuildable", "Uncapturable"],
|
||||||
"movement": 2
|
"movement": 2
|
||||||
},
|
},
|
||||||
@ -1666,6 +1667,7 @@
|
|||||||
"[+15]% Strength bonus for [Military] units within [2] tiles",
|
"[+15]% Strength bonus for [Military] units within [2] tiles",
|
||||||
"All adjacent units heal [+15] HP when healing", "[+15] HP when healing",
|
"All adjacent units heal [+15] HP when healing", "[+15] HP when healing",
|
||||||
"Can instantly construct a [Citadel] improvement <by consuming this unit>",
|
"Can instantly construct a [Citadel] improvement <by consuming this unit>",
|
||||||
|
"Can be earned through combat",
|
||||||
"Great Person - [War]", "Unbuildable", "Uncapturable"],
|
"Great Person - [War]", "Unbuildable", "Uncapturable"],
|
||||||
"movement": 5
|
"movement": 5
|
||||||
},
|
},
|
||||||
|
@ -1296,6 +1296,7 @@
|
|||||||
"Empire enters a [8]-turn Golden Age <by consuming this unit>",
|
"Empire enters a [8]-turn Golden Age <by consuming this unit>",
|
||||||
"[+15]% Strength bonus for [Military] units within [2] tiles",
|
"[+15]% Strength bonus for [Military] units within [2] tiles",
|
||||||
"Can instantly construct a [Citadel] improvement <by consuming this unit>",
|
"Can instantly construct a [Citadel] improvement <by consuming this unit>",
|
||||||
|
"Can be earned through combat",
|
||||||
"Great Person - [War]", "Unbuildable", "Uncapturable"],
|
"Great Person - [War]", "Unbuildable", "Uncapturable"],
|
||||||
"movement": 2
|
"movement": 2
|
||||||
},
|
},
|
||||||
@ -1309,6 +1310,7 @@
|
|||||||
"[+15]% Strength bonus for [Military] units within [2] tiles",
|
"[+15]% Strength bonus for [Military] units within [2] tiles",
|
||||||
"All adjacent units heal [+15] HP when healing", "[+15] HP when healing",
|
"All adjacent units heal [+15] HP when healing", "[+15] HP when healing",
|
||||||
"Can instantly construct a [Citadel] improvement <by consuming this unit>",
|
"Can instantly construct a [Citadel] improvement <by consuming this unit>",
|
||||||
|
"Can be earned through combat",
|
||||||
"Great Person - [War]", "Unbuildable", "Uncapturable"],
|
"Great Person - [War]", "Unbuildable", "Uncapturable"],
|
||||||
"movement": 5
|
"movement": 5
|
||||||
},
|
},
|
||||||
|
@ -37,6 +37,19 @@ object BackwardCompatibility {
|
|||||||
removeTechAndPolicies()
|
removeTechAndPolicies()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun GameInfo.migrateGreatGeneralPools() {
|
||||||
|
for (civ in civilizations) civ.greatPeople.run {
|
||||||
|
if (pointsForNextGreatGeneral >= pointsForNextGreatGeneralCounter["Great General"]) {
|
||||||
|
pointsForNextGreatGeneralCounter["Great General"] = pointsForNextGreatGeneral
|
||||||
|
} else pointsForNextGreatGeneral = pointsForNextGreatGeneralCounter["Great General"]
|
||||||
|
|
||||||
|
|
||||||
|
if (greatGeneralPoints >= greatGeneralPointsCounter["Great General"]) {
|
||||||
|
greatGeneralPointsCounter["Great General"] = greatGeneralPoints
|
||||||
|
} else greatGeneralPoints = greatGeneralPointsCounter["Great General"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun GameInfo.removeUnitsAndPromotions() {
|
private fun GameInfo.removeUnitsAndPromotions() {
|
||||||
for (tile in tileMap.values) {
|
for (tile in tileMap.values) {
|
||||||
for (unit in tile.getUnits().toList()) {
|
for (unit in tile.getUnits().toList()) {
|
||||||
|
@ -7,6 +7,7 @@ import com.unciv.UncivGame.Version
|
|||||||
import com.unciv.json.json
|
import com.unciv.json.json
|
||||||
import com.unciv.logic.BackwardCompatibility.convertFortify
|
import com.unciv.logic.BackwardCompatibility.convertFortify
|
||||||
import com.unciv.logic.BackwardCompatibility.guaranteeUnitPromotions
|
import com.unciv.logic.BackwardCompatibility.guaranteeUnitPromotions
|
||||||
|
import com.unciv.logic.BackwardCompatibility.migrateGreatGeneralPools
|
||||||
import com.unciv.logic.BackwardCompatibility.migrateToTileHistory
|
import com.unciv.logic.BackwardCompatibility.migrateToTileHistory
|
||||||
import com.unciv.logic.BackwardCompatibility.removeMissingModReferences
|
import com.unciv.logic.BackwardCompatibility.removeMissingModReferences
|
||||||
import com.unciv.logic.GameInfo.Companion.CURRENT_COMPATIBILITY_NUMBER
|
import com.unciv.logic.GameInfo.Companion.CURRENT_COMPATIBILITY_NUMBER
|
||||||
@ -670,6 +671,8 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
|||||||
guaranteeUnitPromotions()
|
guaranteeUnitPromotions()
|
||||||
|
|
||||||
migrateToTileHistory()
|
migrateToTileHistory()
|
||||||
|
|
||||||
|
migrateGreatGeneralPools()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCivilizationState() {
|
private fun updateCivilizationState() {
|
||||||
|
@ -482,19 +482,29 @@ object Battle {
|
|||||||
promotions.XP += xpGained
|
promotions.XP += xpGained
|
||||||
|
|
||||||
if (!otherIsBarbarian && civ.isMajorCiv()) { // Can't get great generals from Barbarians
|
if (!otherIsBarbarian && civ.isMajorCiv()) { // Can't get great generals from Barbarians
|
||||||
val greatGeneralPointsBonus = thisCombatant
|
var greatGeneralUnits = civ.gameInfo.ruleset.greatGeneralUnits
|
||||||
.getMatchingUniques(UniqueType.GreatPersonEarnedFaster, stateForConditionals, true)
|
.filter { it.hasUnique(UniqueType.GreatPersonFromCombat, stateForConditionals) &&
|
||||||
.filter { unique ->
|
// Check if the unit is allowed for the Civ, ignoring build constrants
|
||||||
val unitName = unique.params[0]
|
it.getRejectionReasons(civ).none { reason ->
|
||||||
// From the unique we know this unit exists
|
!reason.isConstructionRejection() &&
|
||||||
val unit = civ.gameInfo.ruleset.units[unitName]!!
|
// Allow Generals even if not allowed via tech
|
||||||
unit.uniques.contains("Great Person - [War]")
|
!reason.techPolicyEraWonderRequirements() }
|
||||||
}
|
}.asSequence()
|
||||||
.sumOf { it.params[1].toDouble() }
|
// For compatibility with older rulesets
|
||||||
val greatGeneralPointsModifier = 1.0 + greatGeneralPointsBonus / 100
|
if (civ.gameInfo.ruleset.greatGeneralUnits.isEmpty() &&
|
||||||
|
civ.gameInfo.ruleset.units["Great General"] != null)
|
||||||
|
greatGeneralUnits += civ.gameInfo.ruleset.units["Great General"]!!
|
||||||
|
|
||||||
val greatGeneralPointsGained = (xpGained * greatGeneralPointsModifier).toInt()
|
for (unit in greatGeneralUnits) {
|
||||||
civ.greatPeople.greatGeneralPoints += greatGeneralPointsGained
|
val greatGeneralPointsBonus = thisCombatant
|
||||||
|
.getMatchingUniques(UniqueType.GreatPersonEarnedFaster, stateForConditionals, true)
|
||||||
|
.filter { unit.matchesFilter(it.params[0]) }
|
||||||
|
.sumOf { it.params[1].toDouble() }
|
||||||
|
val greatGeneralPointsModifier = 1.0 + greatGeneralPointsBonus / 100
|
||||||
|
|
||||||
|
val greatGeneralPointsGained = (xpGained * greatGeneralPointsModifier).toInt()
|
||||||
|
civ.greatPeople.greatGeneralPointsCounter[unit.name] += greatGeneralPointsGained
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!thisCombatant.isDefeated() && !unitCouldAlreadyPromote && promotions.canBePromoted()) {
|
if (!thisCombatant.isDefeated() && !unitCouldAlreadyPromote && promotions.canBePromoted()) {
|
||||||
|
@ -19,8 +19,10 @@ class GreatPersonManager : IsPartOfGameInfoSerialization {
|
|||||||
/** Base points, without speed modifier */
|
/** Base points, without speed modifier */
|
||||||
var pointsForNextGreatPersonCounter = Counter<String>() // Initial values assigned in getPointsRequiredForGreatPerson as needed
|
var pointsForNextGreatPersonCounter = Counter<String>() // Initial values assigned in getPointsRequiredForGreatPerson as needed
|
||||||
var pointsForNextGreatGeneral = 200
|
var pointsForNextGreatGeneral = 200
|
||||||
|
var pointsForNextGreatGeneralCounter = Counter<String>() // Initial values assigned when needed
|
||||||
|
|
||||||
var greatPersonPointsCounter = Counter<String>()
|
var greatPersonPointsCounter = Counter<String>()
|
||||||
|
var greatGeneralPointsCounter = Counter<String>()
|
||||||
var greatGeneralPoints = 0
|
var greatGeneralPoints = 0
|
||||||
var freeGreatPeople = 0
|
var freeGreatPeople = 0
|
||||||
/** Marks subset of [freeGreatPeople] as subject to maya ability restrictions (each only once untill all used) */
|
/** Marks subset of [freeGreatPeople] as subject to maya ability restrictions (each only once untill all used) */
|
||||||
@ -33,6 +35,8 @@ class GreatPersonManager : IsPartOfGameInfoSerialization {
|
|||||||
toReturn.freeGreatPeople = freeGreatPeople
|
toReturn.freeGreatPeople = freeGreatPeople
|
||||||
toReturn.greatPersonPointsCounter = greatPersonPointsCounter.clone()
|
toReturn.greatPersonPointsCounter = greatPersonPointsCounter.clone()
|
||||||
toReturn.pointsForNextGreatPersonCounter = pointsForNextGreatPersonCounter.clone()
|
toReturn.pointsForNextGreatPersonCounter = pointsForNextGreatPersonCounter.clone()
|
||||||
|
toReturn.pointsForNextGreatGeneralCounter = pointsForNextGreatGeneralCounter.clone()
|
||||||
|
toReturn.greatGeneralPointsCounter = greatGeneralPointsCounter.clone()
|
||||||
toReturn.pointsForNextGreatGeneral = pointsForNextGreatGeneral
|
toReturn.pointsForNextGreatGeneral = pointsForNextGreatGeneral
|
||||||
toReturn.greatGeneralPoints = greatGeneralPoints
|
toReturn.greatGeneralPoints = greatGeneralPoints
|
||||||
toReturn.mayaLimitedFreeGP = mayaLimitedFreeGP
|
toReturn.mayaLimitedFreeGP = mayaLimitedFreeGP
|
||||||
@ -54,10 +58,16 @@ class GreatPersonManager : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getNewGreatPerson(): String? {
|
fun getNewGreatPerson(): String? {
|
||||||
if (greatGeneralPoints > pointsForNextGreatGeneral) {
|
for ((unit, value) in greatGeneralPointsCounter){
|
||||||
greatGeneralPoints -= pointsForNextGreatGeneral
|
if (pointsForNextGreatGeneralCounter[unit] == 0) {
|
||||||
pointsForNextGreatGeneral += 50
|
pointsForNextGreatGeneralCounter[unit] = 200
|
||||||
return "Great General"
|
}
|
||||||
|
val requiredPoints = pointsForNextGreatGeneralCounter[unit]
|
||||||
|
if (value > requiredPoints) {
|
||||||
|
greatGeneralPointsCounter[unit] -= requiredPoints
|
||||||
|
pointsForNextGreatGeneralCounter[unit] += 50
|
||||||
|
return unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((greatPerson, value) in greatPersonPointsCounter) {
|
for ((greatPerson, value) in greatPersonPointsCounter) {
|
||||||
|
@ -118,6 +118,8 @@ class RejectionReason(val type: RejectionReasonType,
|
|||||||
|
|
||||||
fun isImportantRejection(): Boolean = type in orderedImportantRejectionTypes
|
fun isImportantRejection(): Boolean = type in orderedImportantRejectionTypes
|
||||||
|
|
||||||
|
fun isConstructionRejection(): Boolean = type in constructionRejectionReasonType
|
||||||
|
|
||||||
/** Returns the index of [orderedImportantRejectionTypes] with the smallest index having the
|
/** Returns the index of [orderedImportantRejectionTypes] with the smallest index having the
|
||||||
* highest precedence */
|
* highest precedence */
|
||||||
fun getRejectionPrecedence(): Int {
|
fun getRejectionPrecedence(): Int {
|
||||||
@ -152,6 +154,12 @@ class RejectionReason(val type: RejectionReasonType,
|
|||||||
RejectionReasonType.MaxNumberBuildable,
|
RejectionReasonType.MaxNumberBuildable,
|
||||||
RejectionReasonType.NoPlaceToPutUnit,
|
RejectionReasonType.NoPlaceToPutUnit,
|
||||||
)
|
)
|
||||||
|
// Used for units spawned, not built
|
||||||
|
private val constructionRejectionReasonType = listOf(
|
||||||
|
RejectionReasonType.Unbuildable,
|
||||||
|
RejectionReasonType.CannotBeBuiltUnhappiness,
|
||||||
|
RejectionReasonType.CannotBeBuilt,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import com.unciv.models.ruleset.tile.Terrain
|
|||||||
import com.unciv.models.ruleset.tile.TileImprovement
|
import com.unciv.models.ruleset.tile.TileImprovement
|
||||||
import com.unciv.models.ruleset.tile.TileResource
|
import com.unciv.models.ruleset.tile.TileResource
|
||||||
import com.unciv.models.ruleset.unique.IHasUniques
|
import com.unciv.models.ruleset.unique.IHasUniques
|
||||||
|
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||||
import com.unciv.models.ruleset.unique.Unique
|
import com.unciv.models.ruleset.unique.Unique
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.ruleset.unit.BaseUnit
|
import com.unciv.models.ruleset.unit.BaseUnit
|
||||||
@ -69,6 +70,10 @@ class Ruleset {
|
|||||||
var victories = LinkedHashMap<String, Victory>()
|
var victories = LinkedHashMap<String, Victory>()
|
||||||
var cityStateTypes = LinkedHashMap<String, CityStateType>()
|
var cityStateTypes = LinkedHashMap<String, CityStateType>()
|
||||||
|
|
||||||
|
val greatGeneralUnits by lazy {
|
||||||
|
units.values.filter { it.hasUnique(UniqueType.GreatPersonFromCombat, StateForConditionals.IgnoreConditionals) }
|
||||||
|
}
|
||||||
|
|
||||||
val mods = LinkedHashSet<String>()
|
val mods = LinkedHashSet<String>()
|
||||||
var modOptions = ModOptions()
|
var modOptions = ModOptions()
|
||||||
|
|
||||||
|
@ -437,6 +437,7 @@ enum class UniqueType(
|
|||||||
// XP
|
// XP
|
||||||
FlatXPGain("[amount] XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global),
|
FlatXPGain("[amount] XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global),
|
||||||
PercentageXPGain("[relativeAmount]% XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global),
|
PercentageXPGain("[relativeAmount]% XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global),
|
||||||
|
GreatPersonFromCombat("Can be earned through combat", UniqueTarget.Unit),
|
||||||
GreatPersonEarnedFaster("[greatPerson] is earned [relativeAmount]% faster", UniqueTarget.Unit, UniqueTarget.Global),
|
GreatPersonEarnedFaster("[greatPerson] is earned [relativeAmount]% faster", UniqueTarget.Unit, UniqueTarget.Global),
|
||||||
|
|
||||||
// Invisibility
|
// Invisibility
|
||||||
|
@ -211,10 +211,14 @@ class StatsOverviewTab(
|
|||||||
add(greatPersonPointsPerTurn[greatPerson].toLabel()).right().row()
|
add(greatPersonPointsPerTurn[greatPerson].toLabel()).right().row()
|
||||||
}
|
}
|
||||||
|
|
||||||
val pointsForGreatGeneral = viewingPlayer.greatPeople.greatGeneralPoints
|
val greatGeneralPoints = viewingPlayer.greatPeople.greatGeneralPointsCounter
|
||||||
val pointsForNextGreatGeneral = viewingPlayer.greatPeople.pointsForNextGreatGeneral
|
val pointsForNextGreatGeneral = viewingPlayer.greatPeople.pointsForNextGreatGeneralCounter
|
||||||
add("Great General".toLabel()).left()
|
for ((unit, points) in greatGeneralPoints) {
|
||||||
add("$pointsForGreatGeneral/$pointsForNextGreatGeneral".toLabel())
|
val pointsToGreatGeneral = pointsForNextGreatGeneral[unit]
|
||||||
|
add(unit.toLabel()).left()
|
||||||
|
add("$points/$pointsToGreatGeneral".toLabel())
|
||||||
|
}
|
||||||
|
|
||||||
pack()
|
pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,8 +216,8 @@ class BattleTest {
|
|||||||
Battle.attack(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(defaultDefenderUnit))
|
Battle.attack(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(defaultDefenderUnit))
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(5, attackerCiv.greatPeople.greatGeneralPoints)
|
assertEquals(5, attackerCiv.greatPeople.greatGeneralPointsCounter["Great General"])
|
||||||
assertEquals(4, defenderCiv.greatPeople.greatGeneralPoints)
|
assertEquals(4, defenderCiv.greatPeople.greatGeneralPointsCounter["Great General"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -230,8 +230,8 @@ class BattleTest {
|
|||||||
Battle.attack(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(barbarianUnit))
|
Battle.attack(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(barbarianUnit))
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(0, attackerCiv.greatPeople.greatGeneralPoints)
|
assertEquals(0, attackerCiv.greatPeople.greatGeneralPointsCounter["Great General"])
|
||||||
assertEquals(0, barbarianCiv.greatPeople.greatGeneralPoints)
|
assertEquals(0, barbarianCiv.greatPeople.greatGeneralPointsCounter["Great General"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -243,7 +243,7 @@ class BattleTest {
|
|||||||
Battle.attack(MapUnitCombatant(attackerUnit), MapUnitCombatant(defaultDefenderUnit))
|
Battle.attack(MapUnitCombatant(attackerUnit), MapUnitCombatant(defaultDefenderUnit))
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(10, attackerCiv.greatPeople.greatGeneralPoints)
|
assertEquals(10, attackerCiv.greatPeople.greatGeneralPointsCounter["Great General"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user