Fix minimum votes needed for a diplomatic victory (#9932)

* Diplomatic Vote tweak: Defensively make sure Spectator can't vote

* Diplomatic Vote tweak: Fix votesNeededForDiplomaticVictory formula and base count
This commit is contained in:
SomeTroglodyte 2023-08-21 17:17:32 +02:00 committed by GitHub
parent cc43a41ec7
commit ff806b5f3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 9 deletions

View File

@ -704,7 +704,10 @@ class Civilization : IsPartOfGameInfoSerialization {
fun getTurnsTillCallForBarbHelp() = flagsCountdown[CivFlags.TurnsTillCallForBarbHelp.name]
fun mayVoteForDiplomaticVictory() =
getTurnsTillNextDiplomaticVote() == 0
// Does not need checks for Barbarians or dead civs because the callers already ensure that
// (NextTurnAutomation.tryVoteForDiplomaticVictory and NextTurnAction.WorldCongressVote)
!isSpectator()
&& getTurnsTillNextDiplomaticVote() == 0
&& civName !in gameInfo.diplomaticVictoryVotesCast.keys
// Only vote if there is someone to vote for, may happen in one-more-turn mode
&& gameInfo.civilizations.any { it.isMajorCiv() && !it.isDefeated() && it != this }

View File

@ -37,12 +37,14 @@ class VictoryManager : IsPartOfGameInfoSerialization {
return results
}
private fun getVotingCivs() = civInfo.gameInfo.civilizations.asSequence()
.filterNot { it.isBarbarian() || it.isSpectator() || it.isDefeated() }
/** Finds the Building and Owner of the United Nations (or whatever the Mod called it)
* - if it's built at all and only if the owner is alive
* @return `first`: Building name, `second`: Owner civ name; both null if not found
*/
fun getUNBuildingAndOwnerNames(): Pair<String?, String?> = civInfo.gameInfo.civilizations.asSequence()
.filterNot { it.isBarbarian() || it.isSpectator() || it.isDefeated() }
fun getUNBuildingAndOwnerNames(): Pair<String?, String?> = getVotingCivs()
.flatMap { civ -> civ.cities.asSequence()
.flatMap { it.cityConstructions.getBuiltBuildings() }
.filter { it.hasUnique(UniqueType.OneTimeTriggerVoting, stateForConditionals = StateForConditionals.IgnoreConditionals) }
@ -50,13 +52,19 @@ class VictoryManager : IsPartOfGameInfoSerialization {
}.firstOrNull() ?: (null to null)
private fun votesNeededForDiplomaticVictory(): Int {
val civCount = civInfo.gameInfo.civilizations.count { !it.isDefeated() }
// The original counts "teams ever alive", which excludes Observer and Barbarians.
// The "ever alive" part sounds unfair - could make a Vote unwinnable?
// CvGame.cpp::DoUpdateDiploVictory() in the source code of the original
return (
if (civCount > 28) 0.35 * civCount
else (67 - 1.1 * civCount) / 100 * civCount
).toInt()
// So this is a slightly arbitrary decision: Apply original formula to count of available votes
// - including catering for the possibility we're voting without a UN thanks to razing -
val (_, civOwningUN) = getUNBuildingAndOwnerNames()
val voteCount = getVotingCivs().count() + (if (civOwningUN != null) 1 else 0)
// CvGame.cpp::DoUpdateDiploVictory() in the source code of the original - same integer math and rounding!
// To verify run `(1..30).map { voteCount -> voteCount to voteCount * (67 - (1.1 * voteCount).toInt()) / 100 + 1 }`
// ... and compare with: "4 votes needed to win in a game with 5 players, 7 with 13 and 11 with 28"
if (voteCount > 28) return voteCount * 35 / 100
return voteCount * (67 - (1.1 * voteCount).toInt()) / 100 + 1
}
fun hasEnoughVotesForDiplomaticVictory(): Boolean {