Quests fixes and additions (#5301)

* enable all quests

* implement more quests

* weighting for quests

* global quests

* fixes

* fixes

* Update template.properties

* general string

* reviews
This commit is contained in:
SimonCeder 2021-09-24 09:05:30 +02:00 committed by GitHub
parent e9e0f2c55f
commit fa813f8f5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 279 additions and 38 deletions

View File

@ -22,12 +22,12 @@
{ {
"name": "Acquire Great Person", "name": "Acquire Great Person",
"description": "Great People can change the course of a Civilization! You will be rewarded for acquiring a new [greatPerson]." "description": "Great People can change the course of a Civilization! You will be rewarded for acquiring a new [greatPerson]."
},/* },
{ {
"name": "Conquer City State", "name": "Conquer City State",
"description": "You will be rewarded for conquering the city state of [cityState]!", "description": "It's time to erase the City State of [cityState] from the map. You will be greatly rewarded for conquering them!",
"influence": 80 "influence": 80
},*/ },
{ {
"name": "Find Player", "name": "Find Player",
"description": "You have yet to discover where [civName] set up their cities. You will be rewarded for finding their territories.", "description": "You have yet to discover where [civName] set up their cities. You will be rewarded for finding their territories.",
@ -36,18 +36,17 @@
{ {
"name": "Find Natural Wonder", "name": "Find Natural Wonder",
"description": "Send your best explorers on a quest to discover Natural Wonders. Nobody knows the location of [naturalWonder] yet." "description": "Send your best explorers on a quest to discover Natural Wonders. Nobody knows the location of [naturalWonder] yet."
} },
/* G&K */ /* G&K */
/*
{ {
"name": "Give Gold", "name": "Give Gold",
"description": "We are suffering great poverty, and unless we receive a sum of [250] Gold, it's only a matter of time before we collapse.", "description": "We are suffering great poverty after being robbed by [civName], and unless we receive a sum of Gold, it's only a matter of time before we collapse.",
"influence": 20, "influence": 20,
"duration": 30 "duration": 30
}, },
{ {
"name": "Pledge to Protect", "name": "Pledge to Protect",
"description": "Our peoples deserve to be protected by the likes of you. By signing a Protection Pedging, you'll confirm the bond that ties us.", "description": "We need your protection to stop the aggressions of [civName]. By signing a Pledge of Protection, you'll confirm the bond that ties us.",
"influence": 20, "influence": 20,
"duration": 30 "duration": 30
}, },
@ -66,7 +65,7 @@
"minimumCivs": 3 "minimumCivs": 3
}, },
{ {
"name": "Contest Techs", "name": "Contest Technologies",
"description": "The civilization with the largest number of new Technologies researched will gain a reward.", "description": "The civilization with the largest number of new Technologies researched will gain a reward.",
"type": "Global", "type": "Global",
"duration": 30, "duration": 30,
@ -74,7 +73,7 @@
}, },
{ {
"name": "Invest", "name": "Invest",
"description": "Our people are rejoycing thanks to a tourism boom. For a certain amount of time, any Gold donation will yeld double the Influence.", "description": "Our people are rejoicing thanks to a tourism boom. For a certain amount of time, any Gold donation will yield [50]% extra Influence.",
"type": "Global", "type": "Global",
"influence": 0, "influence": 0,
"duration": 30, "duration": 30,
@ -82,19 +81,18 @@
}, },
{ {
"name": "Bully City State", "name": "Bully City State",
"description": "" "description": "We are tired of the pretensions of [cityState]. If someone were to put them in their place by Demanding Tribute from them, they would be rewarded.",
"duration": 30 "duration": 30
}, },
{ {
"name": "Denounce Civilization", "name": "Denounce Civilization",
"description": "", "description": "We have been forced to pay tribute to [civName]! We need you to tell the world of their ill deeds.",
"duration": 30 "duration": 30
}, },
{ {
"name": "Spread Religion", "name": "Spread Religion",
"description": "" "description": "We have heard the tenets of [religionName] and are most curious. Will you send missionaries to teach us about your religion?"
}, }
*/
/* BNW */ /* BNW */
/* /*
{ {

View File

@ -134,6 +134,8 @@ Ally =
[questName] (+[influenceAmount] influence) = [questName] (+[influenceAmount] influence) =
[remainingTurns] turns remaining = [remainingTurns] turns remaining =
Current leader is [civInfo] with [amount] [stat] generated. =
Current leader is [civInfo] with [amount] Technologies discovered. =
## Diplomatic modifiers ## Diplomatic modifiers

View File

@ -19,7 +19,7 @@ import kotlin.math.pow
/** Class containing city-state-specific functions */ /** Class containing city-state-specific functions */
class CityStateFunctions(val civInfo: CivilizationInfo) { class CityStateFunctions(val civInfo: CivilizationInfo) {
/** Attempts to initialize the city state, returning true if successful. */ /** Attempts to initialize the city state, returning true if successful. */
fun initCityState(ruleset: Ruleset, startingEra: String, unusedMajorCivs: Collection<String>): Boolean { fun initCityState(ruleset: Ruleset, startingEra: String, unusedMajorCivs: Collection<String>): Boolean {
val cityStateType = ruleset.nations[civInfo.civName]?.cityStateType val cityStateType = ruleset.nations[civInfo.civName]?.cityStateType
@ -55,7 +55,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
// Unique unit for militaristic city-states // Unique unit for militaristic city-states
if (allPossibleBonuses.any { it.isOfType(UniqueType.CityStateMilitaryUnits) } if (allPossibleBonuses.any { it.isOfType(UniqueType.CityStateMilitaryUnits) }
|| (fallback && cityStateType == CityStateType.Militaristic) // Fallback for badly defined Eras.json || (fallback && cityStateType == CityStateType.Militaristic) // Fallback for badly defined Eras.json
) { ) {
val possibleUnits = ruleset.units.values.filter { it.requiredTech != null val possibleUnits = ruleset.units.values.filter { it.requiredTech != null
&& ruleset.eras[ruleset.technologies[it.requiredTech!!]!!.era()]!!.eraNumber > ruleset.eras[startingEra]!!.eraNumber // Not from the start era or before && ruleset.eras[ruleset.technologies[it.requiredTech!!]!!.era()]!!.eraNumber > ruleset.eras[startingEra]!!.eraNumber // Not from the start era or before
@ -127,6 +127,10 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
} }
for (unique in donorCiv.getMatchingUniques("Gifts of Gold to City-States generate []% more Influence")) for (unique in donorCiv.getMatchingUniques("Gifts of Gold to City-States generate []% more Influence"))
influenceGained *= 1f + unique.params[0].toFloat() / 100f influenceGained *= 1f + unique.params[0].toFloat() / 100f
// Bonus due to "Invest" quests
influenceGained *= civInfo.questManager.getInvestmentMultiplier(donorCiv.civName)
influenceGained -= influenceGained % 5 influenceGained -= influenceGained % 5
if (influenceGained < 5f) influenceGained = 5f if (influenceGained < 5f) influenceGained = 5f
return influenceGained.toInt() return influenceGained.toInt()
@ -137,6 +141,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
donorCiv.addGold(-giftAmount) donorCiv.addGold(-giftAmount)
civInfo.addGold(giftAmount) civInfo.addGold(giftAmount)
civInfo.getDiplomacyManager(donorCiv).addInfluence(influenceGainedByGift(donorCiv, giftAmount).toFloat()) civInfo.getDiplomacyManager(donorCiv).addInfluence(influenceGainedByGift(donorCiv, giftAmount).toFloat())
civInfo.questManager.receivedGoldGift(donorCiv)
} }
fun getProtectorCivs() : List<CivilizationInfo> { fun getProtectorCivs() : List<CivilizationInfo> {
@ -507,6 +512,13 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
protector.popupAlerts.add(PopupAlert(AlertType.BulliedProtectedMinor, protector.popupAlerts.add(PopupAlert(AlertType.BulliedProtectedMinor,
bully.civName + "@" + civInfo.civName)) // we need to pass both civs as argument, hence the horrible chimera bully.civName + "@" + civInfo.civName)) // we need to pass both civs as argument, hence the horrible chimera
} }
// Set a diplomatic flag so we remember for future quests (and not to give them any)
civInfo.getDiplomacyManager(bully).setFlag(DiplomacyFlags.Bullied, 20)
// Notify all city states that we were bullied (for quests)
civInfo.gameInfo.getAliveCityStates()
.forEach { it.questManager.cityStateBullied(civInfo, bully) }
} }
/** A city state was attacked. What are its protectors going to do about it??? Also checks for Wary */ /** A city state was attacked. What are its protectors going to do about it??? Also checks for Wary */
@ -531,6 +543,8 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
continue continue
if (!cityState.knows(attacker)) // Must have met if (!cityState.knows(attacker)) // Must have met
continue continue
if (cityState.questManager.wantsDead(civInfo.civName)) // Must not want us dead
continue
var probability: Int var probability: Int
if (attacker.isMinorCivWarmonger()) { if (attacker.isMinorCivWarmonger()) {
@ -601,6 +615,10 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
protector.addNotification("[${attacker.civName}] has destroyed [${civInfo.civName}], whom you had pledged to protect!", attacker.civName, protector.addNotification("[${attacker.civName}] has destroyed [${civInfo.civName}], whom you had pledged to protect!", attacker.civName,
NotificationIcon.Death, civInfo.civName) NotificationIcon.Death, civInfo.civName)
} }
// Notify all city states that we were killed (for quest completion)
civInfo.gameInfo.getAliveCityStates()
.forEach { it.questManager.cityStateConquered(civInfo, attacker) }
} }
} }

View File

@ -162,6 +162,9 @@ class CivilizationInfo {
// For Aggressor, Warmonger status // For Aggressor, Warmonger status
private var numMinorCivsAttacked = 0 private var numMinorCivsAttacked = 0
var totalCultureForContests = 0
var totalFaithForContests = 0
constructor() constructor()
constructor(civName: String) { constructor(civName: String) {
@ -209,6 +212,8 @@ class CivilizationInfo {
toReturn.hasEverOwnedOriginalCapital = hasEverOwnedOriginalCapital toReturn.hasEverOwnedOriginalCapital = hasEverOwnedOriginalCapital
toReturn.passableImpassables.addAll(passableImpassables) toReturn.passableImpassables.addAll(passableImpassables)
toReturn.numMinorCivsAttacked = numMinorCivsAttacked toReturn.numMinorCivsAttacked = numMinorCivsAttacked
toReturn.totalCultureForContests = totalCultureForContests
toReturn.totalFaithForContests = totalFaithForContests
return toReturn return toReturn
} }
@ -730,6 +735,7 @@ class CivilizationInfo {
val nextTurnStats = statsForNextTurn val nextTurnStats = statsForNextTurn
policies.endTurn(nextTurnStats.culture.toInt()) policies.endTurn(nextTurnStats.culture.toInt())
totalCultureForContests += nextTurnStats.culture.toInt()
if (isCityState()) if (isCityState())
questManager.endTurn() questManager.endTurn()
@ -754,6 +760,7 @@ class CivilizationInfo {
tech.endTurn(nextTurnStats.science.toInt()) tech.endTurn(nextTurnStats.science.toInt())
religionManager.endTurn(nextTurnStats.faith.toInt()) religionManager.endTurn(nextTurnStats.faith.toInt())
totalFaithForContests += nextTurnStats.faith.toInt()
if (isMajorCiv()) greatPeople.addGreatPersonPoints(getGreatPersonPointsForNextTurn()) // City-states don't get great people! if (isMajorCiv()) greatPeople.addGreatPersonPoints(getGreatPersonPointsForNextTurn()) // City-states don't get great people!
@ -861,10 +868,12 @@ class CivilizationInfo {
fun addStat(stat: Stat, amount: Int) { fun addStat(stat: Stat, amount: Int) {
when (stat) { when (stat) {
Stat.Culture -> policies.addCulture(amount) Stat.Culture -> { policies.addCulture(amount)
totalCultureForContests += amount }
Stat.Science -> tech.addScience(amount) Stat.Science -> tech.addScience(amount)
Stat.Gold -> addGold(amount) Stat.Gold -> addGold(amount)
Stat.Faith -> religionManager.storedFaith += amount Stat.Faith -> { religionManager.storedFaith += amount
totalFaithForContests += amount }
else -> {} else -> {}
// Food and Production wouldn't make sense to be added nationwide // Food and Production wouldn't make sense to be added nationwide
// Happiness cannot be added as it is recalculated again, use a unique instead // Happiness cannot be added as it is recalculated again, use a unique instead

View File

@ -4,7 +4,8 @@ import com.badlogic.gdx.math.Vector2
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.GameInfo import com.unciv.logic.GameInfo
import com.unciv.logic.map.BFS import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.Quest import com.unciv.models.ruleset.Quest
@ -13,7 +14,9 @@ import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.tile.TileResource import com.unciv.models.ruleset.tile.TileResource
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.translations.fillPlaceholders import com.unciv.models.translations.fillPlaceholders
import com.unciv.models.translations.getPlaceholderParameters
import com.unciv.ui.utils.randomWeighted import com.unciv.ui.utils.randomWeighted
import com.unciv.ui.utils.toPercent
import kotlin.math.max import kotlin.math.max
import kotlin.random.Random import kotlin.random.Random
@ -55,6 +58,18 @@ class QuestManager {
/** Returns true if [civInfo] have active quests for [challenger] */ /** Returns true if [civInfo] have active quests for [challenger] */
fun haveQuestsFor(challenger: CivilizationInfo): Boolean = assignedQuests.any { it.assignee == challenger.civName } fun haveQuestsFor(challenger: CivilizationInfo): Boolean = assignedQuests.any { it.assignee == challenger.civName }
/** Returns true if [civInfo] has asked anyone to conquer [target] */
fun wantsDead(target: String): Boolean = assignedQuests.any { it.questName == QuestName.ConquerCityState.value && it.data1 == target }
/** Returns the influence multiplier for [donor] from a Investment quest that [civInfo] might have (assumes only one) */
fun getInvestmentMultiplier(donor: String): Float {
val investmentQuest = assignedQuests.firstOrNull { it.questName == QuestName.Invest.value && it.assignee == donor }
return if (investmentQuest == null)
1f
else
investmentQuest.data1.toPercent()
}
fun clone(): QuestManager { fun clone(): QuestManager {
val toReturn = QuestManager() val toReturn = QuestManager()
toReturn.globalQuestCountdown = globalQuestCountdown toReturn.globalQuestCountdown = globalQuestCountdown
@ -247,6 +262,8 @@ class QuestManager {
for (assignee in assignees) { for (assignee in assignees) {
val playerReligion = civInfo.gameInfo.religions.values.firstOrNull { it.foundingCivName == assignee.civName && it.isMajorReligion() }
var data1 = "" var data1 = ""
var data2 = "" var data2 = ""
@ -261,6 +278,17 @@ class QuestManager {
QuestName.GreatPerson.value -> data1 = getGreatPersonForQuest(assignee)!!.name QuestName.GreatPerson.value -> data1 = getGreatPersonForQuest(assignee)!!.name
QuestName.FindPlayer.value -> data1 = getCivilizationToFindForQuest(assignee)!!.civName QuestName.FindPlayer.value -> data1 = getCivilizationToFindForQuest(assignee)!!.civName
QuestName.FindNaturalWonder.value -> data1 = getNaturalWonderToFindForQuest(assignee)!! QuestName.FindNaturalWonder.value -> data1 = getNaturalWonderToFindForQuest(assignee)!!
QuestName.ConquerCityState.value -> data1 = getCityStateTarget(assignee)!!.civName
QuestName.BullyCityState.value -> data1 = getCityStateTarget(assignee)!!.civName
QuestName.PledgeToProtect.value -> data1 = getMostRecentBully()!!
QuestName.GiveGold.value -> data1 = getMostRecentBully()!!
QuestName.DenounceCiv.value -> data1 = getMostRecentBully()!!
QuestName.SpreadReligion.value -> { data1 = playerReligion!!.getReligionDisplayName() // For display
data2 = playerReligion.name } // To check completion
QuestName.ContestCulture.value -> data1 = assignee.totalCultureForContests.toString()
QuestName.ContestFaith.value -> data1 = assignee.totalFaithForContests.toString()
QuestName.ContestTech.value -> data1 = assignee.tech.getNumberOfTechsResearched().toString()
QuestName.Invest.value -> data1 = quest.description.getPlaceholderParameters().first()
} }
val newQuest = AssignedQuest( val newQuest = AssignedQuest(
@ -294,22 +322,33 @@ class QuestManager {
return false return false
if (assignedQuests.any { it.assignee == challenger.civName && it.questName == quest.name }) if (assignedQuests.any { it.assignee == challenger.civName && it.questName == quest.name })
return false return false
if (quest.isIndividual() && civInfo.getDiplomacyManager(challenger).hasFlag(DiplomacyFlags.Bullied))
return false
val mostRecentBully = getMostRecentBully()
val playerReligion = civInfo.gameInfo.religions.values.firstOrNull() { it.foundingCivName == challenger.civName && it.isMajorReligion() }?.name
return when (quest.name) { return when (quest.name) {
QuestName.ClearBarbarianCamp.value -> getBarbarianEncampmentForQuest() != null QuestName.ClearBarbarianCamp.value -> getBarbarianEncampmentForQuest() != null
QuestName.Route.value -> { QuestName.Route.value -> !challenger.cities.none()
if (challenger.cities.none() || !civInfo.hasEverBeenFriendWith(challenger) && !civInfo.isCapitalConnectedToCity(challenger.getCapital())
|| civInfo.isCapitalConnectedToCity(challenger.getCapital())) return false // Need to have a city within 7 tiles on the same continent
&& challenger.cities.any { it.getCenterTile().aerialDistanceTo(civInfo.getCapital().getCenterTile()) <= 7
val bfs = BFS(civInfo.getCapital().getCenterTile()) { it.isLand && !it.isImpassible() } && it.getCenterTile().getContinent() == civInfo.getCapital().getCenterTile().getContinent() }
bfs.stepUntilDestination(challenger.getCapital().getCenterTile()) QuestName.ConnectResource.value -> getResourceForQuest(challenger) != null
bfs.hasReachedTile(challenger.getCapital().getCenterTile()) QuestName.ConstructWonder.value -> getWonderToBuildForQuest(challenger) != null
} QuestName.GreatPerson.value -> getGreatPersonForQuest(challenger) != null
QuestName.ConnectResource.value -> civInfo.hasEverBeenFriendWith(challenger) && getResourceForQuest(challenger) != null QuestName.FindPlayer.value -> getCivilizationToFindForQuest(challenger) != null
QuestName.ConstructWonder.value -> civInfo.hasEverBeenFriendWith(challenger) && getWonderToBuildForQuest(challenger) != null QuestName.FindNaturalWonder.value -> getNaturalWonderToFindForQuest(challenger) != null
QuestName.GreatPerson.value -> civInfo.hasEverBeenFriendWith(challenger) && getGreatPersonForQuest(challenger) != null QuestName.PledgeToProtect.value -> mostRecentBully != null && challenger !in civInfo.getProtectorCivs()
QuestName.FindPlayer.value -> civInfo.hasEverBeenFriendWith(challenger) && getCivilizationToFindForQuest(challenger) != null QuestName.GiveGold.value -> mostRecentBully != null
QuestName.FindNaturalWonder.value -> civInfo.hasEverBeenFriendWith(challenger) && getNaturalWonderToFindForQuest(challenger) != null QuestName.DenounceCiv.value -> mostRecentBully != null && challenger.knows(mostRecentBully)
&& !challenger.getDiplomacyManager(mostRecentBully).hasFlag(DiplomacyFlags.Denunciation)
&& challenger.getDiplomacyManager(mostRecentBully).diplomaticStatus != DiplomaticStatus.War
&& !( challenger.playerType == PlayerType.Human && civInfo.gameInfo.getCivilization(mostRecentBully).playerType == PlayerType.Human)
QuestName.SpreadReligion.value -> playerReligion != null && civInfo.getCapital().religion.getMajorityReligion()?.name != playerReligion
QuestName.ConquerCityState.value -> getCityStateTarget(challenger) != null && civInfo.cityStatePersonality != CityStatePersonality.Friendly
QuestName.BullyCityState.value -> getCityStateTarget(challenger) != null
else -> true else -> true
} }
} }
@ -324,6 +363,9 @@ class QuestManager {
QuestName.GreatPerson.value -> assignee.getCivGreatPeople().any { it.baseUnit.getReplacedUnit(civInfo.gameInfo.ruleSet).name == assignedQuest.data1 } QuestName.GreatPerson.value -> assignee.getCivGreatPeople().any { it.baseUnit.getReplacedUnit(civInfo.gameInfo.ruleSet).name == assignedQuest.data1 }
QuestName.FindPlayer.value -> assignee.hasMetCivTerritory(civInfo.gameInfo.getCivilization(assignedQuest.data1)) QuestName.FindPlayer.value -> assignee.hasMetCivTerritory(civInfo.gameInfo.getCivilization(assignedQuest.data1))
QuestName.FindNaturalWonder.value -> assignee.naturalWonders.contains(assignedQuest.data1) QuestName.FindNaturalWonder.value -> assignee.naturalWonders.contains(assignedQuest.data1)
QuestName.PledgeToProtect.value -> assignee in civInfo.getProtectorCivs()
QuestName.DenounceCiv.value -> assignee.getDiplomacyManager(assignedQuest.data1).hasFlag(DiplomacyFlags.Denunciation)
QuestName.SpreadReligion.value -> civInfo.getCapital().religion.getMajorityReligion() == civInfo.gameInfo.religions[assignedQuest.data2]
else -> false else -> false
} }
} }
@ -335,6 +377,9 @@ class QuestManager {
QuestName.ClearBarbarianCamp.value -> civInfo.gameInfo.tileMap[assignedQuest.data1.toInt(), assignedQuest.data2.toInt()].improvement != Constants.barbarianEncampment QuestName.ClearBarbarianCamp.value -> civInfo.gameInfo.tileMap[assignedQuest.data1.toInt(), assignedQuest.data2.toInt()].improvement != Constants.barbarianEncampment
QuestName.ConstructWonder.value -> civInfo.gameInfo.getCities().any { it.civInfo != assignee && it.cityConstructions.isBuilt(assignedQuest.data1) } QuestName.ConstructWonder.value -> civInfo.gameInfo.getCities().any { it.civInfo != assignee && it.cityConstructions.isBuilt(assignedQuest.data1) }
QuestName.FindPlayer.value -> civInfo.gameInfo.getCivilization(assignedQuest.data1).isDefeated() QuestName.FindPlayer.value -> civInfo.gameInfo.getCivilization(assignedQuest.data1).isDefeated()
QuestName.ConquerCityState.value -> civInfo.gameInfo.getCivilization(assignedQuest.data1).isDefeated()
QuestName.BullyCityState.value -> civInfo.gameInfo.getCivilization(assignedQuest.data1).isDefeated()
QuestName.DenounceCiv.value -> civInfo.gameInfo.getCivilization(assignedQuest.data1).isDefeated()
else -> false else -> false
} }
} }
@ -358,14 +403,29 @@ class QuestManager {
/** Returns the score for the [assignedQuest] */ /** Returns the score for the [assignedQuest] */
private fun getScoreForQuest(assignedQuest: AssignedQuest): Int { private fun getScoreForQuest(assignedQuest: AssignedQuest): Int {
@Suppress("UNUSED_VARIABLE") // This is a work in progress
val assignee = civInfo.gameInfo.getCivilization(assignedQuest.assignee) val assignee = civInfo.gameInfo.getCivilization(assignedQuest.assignee)
return when (assignedQuest.questName) { return when (assignedQuest.questName) {
// Waiting for contest quests QuestName.ContestCulture.value -> assignee.totalCultureForContests - assignedQuest.data1.toInt()
QuestName.ContestFaith.value -> assignee.totalFaithForContests - assignedQuest.data1.toInt()
QuestName.ContestTech.value -> assignee.tech.getNumberOfTechsResearched() - assignedQuest.data1.toInt()
else -> 0 else -> 0
} }
} }
/** Returns a string with the leading civ and their score for [questName] */
fun getLeaderStringForQuest(questName: String): String {
val leadingQuest = assignedQuests.filter { it.questName == questName }.maxByOrNull { getScoreForQuest(it) }
if (leadingQuest == null)
return ""
return when (questName){
QuestName.ContestCulture.value -> "Current leader is ${leadingQuest.assignee} with ${getScoreForQuest(leadingQuest)} [Culture] generated."
QuestName.ContestFaith.value -> "Current leader is ${leadingQuest.assignee} with ${getScoreForQuest(leadingQuest)} [Faith] generated."
QuestName.ContestTech.value -> "Current leader is ${leadingQuest.assignee} with ${getScoreForQuest(leadingQuest)} Technologies discovered."
else -> ""
}
}
/** /**
* Gets notified a barbarian camp in [location] has been cleared by [civInfo]. * Gets notified a barbarian camp in [location] has been cleared by [civInfo].
* Since [QuestName.ClearBarbarianCamp] is a global quest, it could have been assigned to * Since [QuestName.ClearBarbarianCamp] is a global quest, it could have been assigned to
@ -383,6 +443,59 @@ class QuestManager {
assignedQuests.removeAll(matchingQuests) assignedQuests.removeAll(matchingQuests)
} }
/**
* Gets notified the city state [cityState] was just conquered by [attacker].
*/
fun cityStateConquered(cityState: CivilizationInfo, attacker: CivilizationInfo) {
val matchingQuests = assignedQuests.asSequence()
.filter { it.questName == QuestName.ConquerCityState.value }
.filter { it.data1 == cityState.civName && it.assignee == attacker.civName}
for (quest in matchingQuests)
giveReward(quest)
assignedQuests.removeAll(matchingQuests)
}
/**
* Gets notified the city state [cityState] was just bullied by [bully].
*/
fun cityStateBullied(cityState: CivilizationInfo, bully: CivilizationInfo) {
val matchingQuests = assignedQuests.asSequence()
.filter { it.questName == QuestName.BullyCityState.value }
.filter { it.data1 == cityState.civName && it.assignee == bully.civName}
for (quest in matchingQuests)
giveReward(quest)
assignedQuests.removeAll(matchingQuests)
// What idiots haha oh wait that's us
if (civInfo == cityState) {
// Revoke most quest types from the bully
val revokedQuests = assignedQuests.asSequence()
.filter { it.isIndividual() || it.questName == QuestName.Invest.value }
assignedQuests.removeAll(revokedQuests)
if (revokedQuests.count() > 0)
bully.addNotification("[${civInfo.civName}] cancelled the quests they had given you because you demanded tribute from them.",
DiplomacyAction(civInfo.civName), civInfo.civName, "OtherIcons/Quest")
}
}
/**
* Gets notified when given gold by [donorCiv].
*/
fun receivedGoldGift(donorCiv: CivilizationInfo) {
val matchingQuests = assignedQuests.asSequence()
.filter { it.questName == QuestName.GiveGold.value }
.filter { it.assignee == donorCiv.civName}
for (quest in matchingQuests)
giveReward(quest)
assignedQuests.removeAll(matchingQuests)
}
/** /**
* Returns the weight of the [questName], depends on city state trait and personality * Returns the weight of the [questName], depends on city state trait and personality
*/ */
@ -440,6 +553,56 @@ class QuestManager {
if (trait == CityStateType.Militaristic) if (trait == CityStateType.Militaristic)
weight *= 3f weight *= 3f
} }
QuestName.GiveGold.value -> {
when (trait) {
CityStateType.Militaristic -> weight *= 2f
CityStateType.Mercantile -> weight *= 3.5f
else -> weight *= 3f
}
}
QuestName.PledgeToProtect.value -> {
when (trait) {
CityStateType.Militaristic -> weight *= 2f
CityStateType.Cultured -> weight *= 3.5f
else -> weight *= 3f
}
}
QuestName.BullyCityState.value -> {
when (personality) {
CityStatePersonality.Hostile -> weight *= 2f
CityStatePersonality.Irrational -> weight *= 1.5f
CityStatePersonality.Friendly -> weight *= .3f
}
}
QuestName.DenounceCiv.value -> {
when (trait) {
CityStateType.Religious -> weight *= 2.5f
CityStateType.Maritime -> weight *= 2f
else -> weight *= 1.5f
}
}
QuestName.SpreadReligion.value -> {
if (trait == CityStateType.Religious)
weight *= 3f
}
QuestName.ContestCulture.value -> {
if (trait == CityStateType.Cultured)
weight *= 2f
}
QuestName.ContestFaith.value -> {
when (trait) {
CityStateType.Religious -> weight *= 2f
else -> weight *= .5f
}
}
QuestName.ContestTech.value -> {
if (trait == CityStateType.Religious)
weight *= .5f
}
QuestName.Invest.value -> {
if (trait == CityStateType.Mercantile)
weight *= 1.5f
}
} }
return weight return weight
} }
@ -486,11 +649,21 @@ class QuestManager {
} }
private fun getWonderToBuildForQuest(challenger: CivilizationInfo): Building? { private fun getWonderToBuildForQuest(challenger: CivilizationInfo): Building? {
val startingEra = civInfo.gameInfo.ruleSet.eras[civInfo.gameInfo.gameParameters.startingEra]!!
val wonders = civInfo.gameInfo.ruleSet.buildings.values val wonders = civInfo.gameInfo.ruleSet.buildings.values
.filter { building -> .filter { building ->
building.isWonder && // Buildable wonder
(building.requiredTech == null || challenger.tech.isResearched(building.requiredTech!!)) && building.isWonder
civInfo.gameInfo.getCities().none { it.cityConstructions.isBuilt(building.name) } && (building.requiredTech == null || challenger.tech.isResearched(building.requiredTech!!))
&& civInfo.gameInfo.getCities().none { it.cityConstructions.isBuilt(building.name) }
// Can't be disabled
&& building.name !in startingEra.startingObsoleteWonders
&& (civInfo.gameInfo.gameParameters.religionEnabled || !building.hasUnique("Hidden when religion is disabled"))
// Can't be more than 25% built anywhere
&& civInfo.gameInfo.getCities().none {
it.cityConstructions.getWorkDone(building.name) * 3 > it.cityConstructions.getRemainingWork(building.name) }
// Can't be a unique wonder
&& building.uniqueTo == null
} }
if (wonders.isNotEmpty()) if (wonders.isNotEmpty())
@ -545,6 +718,29 @@ class QuestManager {
return null return null
} }
/**
* Returns a city-state [CivilizationInfo] that [civInfo] wants to target for hostile quests
*/
private fun getCityStateTarget(challenger: CivilizationInfo): CivilizationInfo? {
val closestProximity = civInfo.gameInfo.getAliveCityStates()
.mapNotNull { civInfo.proximity[it.civName] }.filter { it != Proximity.None }.minByOrNull { it.ordinal }
if (closestProximity == null || closestProximity == Proximity.Distant) // None close enough
return null
val validTargets = civInfo.getKnownCivs().filter { it.isCityState() && challenger.knows(it)
&& civInfo.proximity[it.civName] == closestProximity }
return validTargets.randomOrNull()
}
/** Returns a [CivilizationInfo] of the civ that most recently bullied [civInfo].
* Note: forgets after 20 turns has passed! */
private fun getMostRecentBully(): String? {
val bullies = civInfo.diplomacy.values.filter { it.hasFlag(DiplomacyFlags.Bullied)}
return bullies.maxByOrNull { it.getFlag(DiplomacyFlags.Bullied) }?.otherCivName
}
//endregion //endregion
} }

View File

@ -51,6 +51,7 @@ enum class DiplomacyFlags {
RememberSidedWithProtectedMinor, RememberSidedWithProtectedMinor,
Denunciation, Denunciation,
WaryOf, WaryOf,
Bullied,
} }
enum class DiplomaticModifiers { enum class DiplomaticModifiers {

View File

@ -11,6 +11,15 @@ enum class QuestName(val value: String) {
ConquerCityState("Conquer City State"), ConquerCityState("Conquer City State"),
FindPlayer("Find Player"), FindPlayer("Find Player"),
FindNaturalWonder("Find Natural Wonder"), FindNaturalWonder("Find Natural Wonder"),
GiveGold("Give Gold"),
PledgeToProtect("Pledge to Protect"),
ContestCulture("Contest Culture"),
ContestFaith("Contest Faith"),
ContestTech("Contest Technologies"),
Invest("Invest"),
BullyCityState("Bully City State"),
DenounceCiv("Denounce Civilization"),
SpreadReligion("Spread Religion"),
None("") None("")
} }

View File

@ -541,7 +541,10 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
val quest: Quest = viewingCiv.gameInfo.ruleSet.quests[assignedQuest.questName]!! val quest: Quest = viewingCiv.gameInfo.ruleSet.quests[assignedQuest.questName]!!
val remainingTurns: Int = assignedQuest.getRemainingTurns() val remainingTurns: Int = assignedQuest.getRemainingTurns()
val title = "[${quest.name}] (+[${quest.influence.toInt()}] influence)" val title = if (quest.influence > 0)
"[${quest.name}] (+[${quest.influence.toInt()}] influence)"
else
quest.name
val description = assignedQuest.getDescription() val description = assignedQuest.getDescription()
questTable.add(title.toLabel(fontSize = 24)).row() questTable.add(title.toLabel(fontSize = 24)).row()
@ -549,6 +552,11 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
.width(stage.width / 2).row() .width(stage.width / 2).row()
if (quest.duration > 0) if (quest.duration > 0)
questTable.add("[${remainingTurns}] turns remaining".toLabel()).row() questTable.add("[${remainingTurns}] turns remaining".toLabel()).row()
if (quest.isGlobal()) {
val leaderString = viewingCiv.gameInfo.getCivilization(assignedQuest.assigner).questManager.getLeaderStringForQuest(assignedQuest.questName)
if (leaderString != "")
questTable.add(leaderString.toLabel()).row()
}
questTable.onClick { questTable.onClick {
assignedQuest.onClickAction() assignedQuest.onClickAction()