mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-29 23:10:39 -04:00
City State Barbarian Invasion and War with Major pseudo-quests (#5454)
* barbarian invasion event * war with major pseudo-quest * include latecomers * diplomacy screen * more notifications * fixes * reviews
This commit is contained in:
parent
551e6e1d54
commit
ac422d25cb
@ -205,6 +205,17 @@ We have married into the ruling family of [civName], bringing them under our con
|
||||
You have broken your Pledge to Protect [civName]! =
|
||||
City-States grow wary of your aggression. The resting point for Influence has decreased by [amount] for [civName]. =
|
||||
|
||||
[cityState] is being attacked by [civName] and asks all major civilizations to help them out by gifting them military units. =
|
||||
[cityState] is being invaded by Barbarians! Destroy Barbarians near their territory to earn Influence. =
|
||||
[cityState] is grateful that you killed a Barbarian that was threatening them! =
|
||||
[cityState] is being attacked by [civName]! Kill [amount] of the attacker's military units and they will be immensely grateful. =
|
||||
[cityState] is deeply grateful for your assistance in the war against [civName]! =
|
||||
[cityState] no longer needs your assistance against [civName]. =
|
||||
War against [civName] =
|
||||
We need you to help us defend against [civName]. Killing [amount] of their military units would slow their offensive. =
|
||||
Currently you have killed [amount] of their military units. =
|
||||
You need to find them first! =
|
||||
|
||||
Cultured =
|
||||
Maritime =
|
||||
Mercantile =
|
||||
|
@ -173,6 +173,11 @@ object Battle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CS war with major pseudo-quest
|
||||
for (cityState in UncivGame.Current.gameInfo.getAliveCityStates()) {
|
||||
cityState.questManager.militaryUnitKilledBy(civUnit.getCivInfo(), defeatedUnit.getCivInfo())
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryCaptureUnit(attacker: MapUnitCombatant, defender: MapUnitCombatant): Boolean {
|
||||
|
@ -473,7 +473,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
private fun getNumThreateningBarbarians(): Int {
|
||||
fun getNumThreateningBarbarians(): Int {
|
||||
if (civInfo.gameInfo.gameParameters.noBarbarians) return 0
|
||||
val barbarianCiv = civInfo.gameInfo.civilizations.firstOrNull { it.isBarbarian() }
|
||||
?: return 0
|
||||
@ -482,11 +482,17 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
|
||||
fun threateningBarbarianKilledBy(otherCiv: CivilizationInfo) {
|
||||
val diplomacy = civInfo.getDiplomacyManager(otherCiv)
|
||||
if (diplomacy.diplomaticStatus == DiplomaticStatus.War) return // No reward for enemies
|
||||
|
||||
diplomacy.addInfluence(12f)
|
||||
|
||||
if (diplomacy.hasFlag(DiplomacyFlags.AngerFreeIntrusion))
|
||||
diplomacy.setFlag(DiplomacyFlags.AngerFreeIntrusion, diplomacy.getFlag(DiplomacyFlags.AngerFreeIntrusion) + 5)
|
||||
else
|
||||
diplomacy.setFlag(DiplomacyFlags.AngerFreeIntrusion, 5)
|
||||
|
||||
otherCiv.addNotification("[${civInfo.civName}] is grateful that you killed a Barbarian that was threatening them!",
|
||||
DiplomacyAction(civInfo.civName), civInfo.civName)
|
||||
}
|
||||
|
||||
/** A city state was bullied. What are its protectors going to do about it??? */
|
||||
@ -593,6 +599,10 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
protector.popupAlerts.add(PopupAlert(AlertType.AttackedProtectedMinor,
|
||||
attacker.civName + "@" + civInfo.civName)) // we need to pass both civs as argument, hence the horrible chimera
|
||||
}
|
||||
|
||||
// Set up war with major pseudo-quest
|
||||
civInfo.questManager.wasAttackedBy(attacker)
|
||||
civInfo.getDiplomacyManager(attacker).setFlag(DiplomacyFlags.RecentlyAttacked, 2) // Reminder to ask for unit gifts in 2 turns
|
||||
}
|
||||
|
||||
/** A city state was destroyed. Its protectors are going to be upset! */
|
||||
@ -621,4 +631,18 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
.forEach { it.questManager.cityStateConquered(civInfo, attacker) }
|
||||
}
|
||||
|
||||
/** Asks all met majors that haven't yet declared wor on [attacker] to at least give some units */
|
||||
fun askForUnitGifts(attacker: CivilizationInfo) {
|
||||
if (attacker.isDefeated() || civInfo.isDefeated()) // nevermind, someone died
|
||||
return
|
||||
if (civInfo.cities.isEmpty()) // Can't receive units with no cities
|
||||
return
|
||||
|
||||
for (thirdCiv in civInfo.getKnownCivs().filter {
|
||||
it != attacker && it.isAlive() && it.knows(attacker) && !it.isAtWarWith(attacker) }) {
|
||||
thirdCiv.addNotification("[${civInfo.civName}] is being attacked by [${attacker.civName}] and asks all major civilizations to help them out by gifting them military units.",
|
||||
civInfo.getCapital().location, civInfo.civName, "OtherIcons/Present")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -508,6 +508,8 @@ class CivilizationInfo {
|
||||
}
|
||||
for ((key, value) in giftAmount)
|
||||
otherCiv.addStat(key, value.toInt())
|
||||
|
||||
questManager.justMet(otherCiv) // Include them in war with major pseudo-quest
|
||||
}
|
||||
|
||||
fun discoverNaturalWonder(naturalWonderName: String) {
|
||||
@ -891,6 +893,7 @@ class CivilizationInfo {
|
||||
fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
|
||||
|
||||
fun getRecentBullyingCountdown() = flagsCountdown[CivFlags.RecentlyBullied.name]
|
||||
fun getTurnsTillCallForBarbHelp() = flagsCountdown[CivFlags.TurnsTillCallForBarbHelp.name]
|
||||
|
||||
fun mayVoteForDiplomaticVictory() =
|
||||
getTurnsTillNextDiplomaticVote() == 0
|
||||
@ -1184,6 +1187,7 @@ class CivilizationInfo {
|
||||
fun getFreeTechForCityState() {
|
||||
cityStateFunctions.getFreeTechForCityState()
|
||||
}
|
||||
fun getNumThreateningBarbarians() = cityStateFunctions.getNumThreateningBarbarians()
|
||||
fun threateningBarbarianKilledBy(otherCiv: CivilizationInfo) {
|
||||
cityStateFunctions.threateningBarbarianKilledBy(otherCiv)
|
||||
}
|
||||
@ -1214,5 +1218,6 @@ enum class CivFlags {
|
||||
TurnsTillNextDiplomaticVote,
|
||||
ShowDiplomaticVotingResults,
|
||||
ShouldResetDiplomaticVotes,
|
||||
RecentlyBullied
|
||||
RecentlyBullied,
|
||||
TurnsTillCallForBarbHelp,
|
||||
}
|
||||
|
@ -56,6 +56,12 @@ class QuestManager {
|
||||
/** Number of turns left before this city state can start a new individual quest */
|
||||
private var individualQuestCountdown: HashMap<String, Int> = HashMap()
|
||||
|
||||
/** Target number of units to kill for this war, for war with major pseudo-quest */
|
||||
private var unitsToKillForCiv: HashMap<String, Int> = HashMap()
|
||||
|
||||
/** For this attacker, number of units killed by each civ */
|
||||
private var unitsKilledFromCiv: HashMap<String, HashMap<String, Int>> = HashMap()
|
||||
|
||||
/** Returns true if [civInfo] have active quests for [challenger] */
|
||||
fun haveQuestsFor(challenger: CivilizationInfo): Boolean = assignedQuests.any { it.assignee == challenger.civName }
|
||||
|
||||
@ -76,6 +82,11 @@ class QuestManager {
|
||||
toReturn.globalQuestCountdown = globalQuestCountdown
|
||||
toReturn.individualQuestCountdown.putAll(individualQuestCountdown)
|
||||
toReturn.assignedQuests.addAll(assignedQuests)
|
||||
toReturn.unitsToKillForCiv.putAll(unitsToKillForCiv)
|
||||
for ((attacker, unitsKilled) in unitsKilledFromCiv) {
|
||||
toReturn.unitsKilledFromCiv[attacker] = HashMap()
|
||||
toReturn.unitsKilledFromCiv[attacker]!!.putAll(unitsKilled)
|
||||
}
|
||||
return toReturn
|
||||
}
|
||||
|
||||
@ -105,6 +116,9 @@ class QuestManager {
|
||||
|
||||
tryStartNewGlobalQuest()
|
||||
tryStartNewIndividualQuests()
|
||||
|
||||
tryBarbarianInvasion()
|
||||
tryEndWarWithMajorQuests()
|
||||
}
|
||||
|
||||
private fun decrementQuestCountdowns() {
|
||||
@ -201,6 +215,22 @@ class QuestManager {
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryBarbarianInvasion() {
|
||||
if ((civInfo.getTurnsTillCallForBarbHelp() == null || civInfo.getTurnsTillCallForBarbHelp() == 0)
|
||||
&& civInfo.getNumThreateningBarbarians() >= 2) {
|
||||
|
||||
for (otherCiv in civInfo.getKnownCivs().filter {
|
||||
it.isMajorCiv()
|
||||
&& it.isAlive()
|
||||
&& !it.isAtWarWith(civInfo)
|
||||
&& it.getProximity(civInfo) <= Proximity.Far }) {
|
||||
otherCiv.addNotification("[${civInfo.civName}] is being invaded by Barbarians! Destroy Barbarians near their territory to earn Influence.",
|
||||
LocationAction(listOf(civInfo.getCapital().location)), civInfo.civName, NotificationIcon.War)
|
||||
}
|
||||
civInfo.addFlag(CivFlags.TurnsTillCallForBarbHelp.name, 30)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGlobalQuests() {
|
||||
val globalQuestsExpired = assignedQuests.filter { it.isGlobal() && it.isExpired() }.map { it.questName }.distinct()
|
||||
for (globalQuestName in globalQuestsExpired)
|
||||
@ -484,6 +514,100 @@ class QuestManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets notified when we are attacked, for war with major pseudo-quest */
|
||||
fun wasAttackedBy(attacker: CivilizationInfo) {
|
||||
// Set target number units to kill
|
||||
val totalMilitaryUnits = attacker.getCivUnits().count { !it.isCivilian() }
|
||||
val unitsToKill = max(3, totalMilitaryUnits / 4)
|
||||
unitsToKillForCiv[attacker.civName] = unitsToKill
|
||||
|
||||
|
||||
val location = if (civInfo.cities.isEmpty()) null
|
||||
else civInfo.getCapital().location
|
||||
|
||||
// Ask for assistance
|
||||
for (thirdCiv in civInfo.getKnownCivs().filter { it.isAlive() && !it.isAtWarWith(civInfo) }) {
|
||||
if (location != null)
|
||||
thirdCiv.addNotification("[${civInfo.civName}] is being attacked by [${attacker.civName}]! Kill [$unitsToKill] of the attacker's military units and they will be immensely grateful.",
|
||||
location, civInfo.civName, "OtherIcons/Quest")
|
||||
else thirdCiv.addNotification("[${civInfo.civName}] is being attacked by [${attacker.civName}]! Kill [$unitsToKill] of the attacker's military units and they will be immensely grateful.",
|
||||
civInfo.civName, "OtherIcons/Quest")
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets notified when [killed]'s military unit was killed by [killer], for war with major pseudo-quest */
|
||||
fun militaryUnitKilledBy(killer: CivilizationInfo, killed: CivilizationInfo) {
|
||||
if (!warWithMajorActive(killed)) return
|
||||
|
||||
// No credit if we're at war or haven't met
|
||||
if (!civInfo.knows(killer) || civInfo.isAtWarWith(killer)) return
|
||||
|
||||
// Make the map if we haven't already
|
||||
if (unitsKilledFromCiv[killed.civName] == null)
|
||||
unitsKilledFromCiv[killed.civName] = HashMap()
|
||||
|
||||
// Update kill count
|
||||
val updatedKillCount = 1 + (unitsKilledFromCiv[killed.civName]!![killer.civName] ?: 0)
|
||||
unitsKilledFromCiv[killed.civName]!![killer.civName] = updatedKillCount
|
||||
|
||||
// Quest complete?
|
||||
if (updatedKillCount >= unitsToKillForCiv[killed.civName]!!) {
|
||||
killer.addNotification("[${civInfo.civName}] is deeply grateful for your assistance in the war against [${killed.civName}]!",
|
||||
DiplomacyAction(civInfo.civName), civInfo.civName, "OtherIcons/Quest")
|
||||
civInfo.getDiplomacyManager(killer).addInfluence(100f) // yikes
|
||||
endWarWithMajorQuest(killed)
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when a major civ meets the city-state for the first time. Mainly for war with major pseudo-quest. */
|
||||
fun justMet(otherCiv: CivilizationInfo) {
|
||||
val location = if (civInfo.cities.isEmpty()) null
|
||||
else civInfo.getCapital().location
|
||||
|
||||
for ((attackerName, unitsToKill) in unitsToKillForCiv) {
|
||||
if (location != null)
|
||||
otherCiv.addNotification("[${civInfo.civName}] is being attacked by [$attackerName]! Kill [$unitsToKill] of the attacker's military units and they will be immensely grateful.",
|
||||
location, civInfo.civName, "OtherIcons/Quest")
|
||||
else otherCiv.addNotification("[${civInfo.civName}] is being attacked by [$attackerName]! Kill [$unitsToKill] of the attacker's military units and they will be immensely grateful.",
|
||||
civInfo.civName, "OtherIcons/Quest")
|
||||
}
|
||||
}
|
||||
|
||||
/** Ends War with Major pseudo-quests that aren't relevant any longer */
|
||||
private fun tryEndWarWithMajorQuests() {
|
||||
for (attacker in unitsToKillForCiv.keys.map { civInfo.gameInfo.getCivilization(it) }) {
|
||||
if (civInfo.isDefeated()
|
||||
|| attacker.isDefeated()
|
||||
|| !civInfo.isAtWarWith(attacker)) {
|
||||
endWarWithMajorQuest(attacker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun endWarWithMajorQuest(attacker: CivilizationInfo) {
|
||||
for (thirdCiv in civInfo.getKnownCivs().filterNot { it.isDefeated() || it == attacker || it.isAtWarWith(civInfo) }) {
|
||||
if (unitsKilledSoFar(attacker, thirdCiv) >= unitsToKill(attacker)) // Don't show the notification to the one who won the quest
|
||||
continue
|
||||
thirdCiv.addNotification("[${civInfo.civName}] no longer needs your assistance against [${attacker.civName}].",
|
||||
DiplomacyAction(civInfo.civName), civInfo.civName, "OtherIcons/Quest")
|
||||
}
|
||||
unitsToKillForCiv.remove(attacker.civName)
|
||||
unitsKilledFromCiv.remove(attacker.civName)
|
||||
}
|
||||
|
||||
fun warWithMajorActive(target: CivilizationInfo): Boolean {
|
||||
return unitsToKillForCiv.containsKey(target.civName)
|
||||
}
|
||||
|
||||
fun unitsToKill(target: CivilizationInfo): Int {
|
||||
return unitsToKillForCiv[target.civName] ?: 0
|
||||
}
|
||||
|
||||
fun unitsKilledSoFar(target: CivilizationInfo, viewingCiv: CivilizationInfo): Int {
|
||||
val killMap = unitsKilledFromCiv[target.civName] ?: return 0
|
||||
return killMap[viewingCiv.civName] ?: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets notified when given gold by [donorCiv].
|
||||
*/
|
||||
|
@ -52,6 +52,7 @@ enum class DiplomacyFlags {
|
||||
Denunciation,
|
||||
WaryOf,
|
||||
Bullied,
|
||||
RecentlyAttacked,
|
||||
}
|
||||
|
||||
enum class DiplomaticModifiers {
|
||||
@ -527,6 +528,9 @@ class DiplomacyManager() {
|
||||
DiplomacyFlags.AgreedToNotSettleNearUs.name -> {
|
||||
addModifier(DiplomaticModifiers.FulfilledPromiseToNotSettleCitiesNearUs, 10f)
|
||||
}
|
||||
DiplomacyFlags.RecentlyAttacked.name -> {
|
||||
civInfo.cityStateFunctions.askForUnitGifts(otherCiv())
|
||||
}
|
||||
// These modifiers don't tick down normally, instead there is a threshold number of turns
|
||||
DiplomacyFlags.RememberDestroyedProtectedMinor.name -> { // 125
|
||||
removeModifier(DiplomaticModifiers.DestroyedProtectedMinor)
|
||||
|
@ -369,6 +369,11 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): CameraStageBaseScreen()
|
||||
diplomacyTable.addSeparator()
|
||||
diplomacyTable.add(getQuestTable(assignedQuest)).row()
|
||||
}
|
||||
|
||||
for (target in otherCiv.getKnownCivs().filter { otherCiv.questManager.warWithMajorActive(it) }) {
|
||||
diplomacyTable.addSeparator()
|
||||
diplomacyTable.add(getWarWithMajorTable(target, otherCiv)).row()
|
||||
}
|
||||
|
||||
return diplomacyTable
|
||||
}
|
||||
@ -567,6 +572,24 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): CameraStageBaseScreen()
|
||||
return questTable
|
||||
}
|
||||
|
||||
private fun getWarWithMajorTable(target: CivilizationInfo, otherCiv: CivilizationInfo): Table {
|
||||
val warTable = Table()
|
||||
warTable.defaults().pad(10f)
|
||||
|
||||
val title = "War against [${target.civName}]"
|
||||
val description = "We need you to help us defend against [${target.civName}]. Killing [${otherCiv.questManager.unitsToKill(target)}] of their military units would slow their offensive."
|
||||
val progress = if (viewingCiv.knows(target)) "Currently you have killed [${otherCiv.questManager.unitsKilledSoFar(target, viewingCiv)}] of their military units."
|
||||
else "You need to find them first!"
|
||||
|
||||
warTable.add(title.toLabel(fontSize = 24)).row()
|
||||
warTable.add(description.toLabel().apply { wrap = true; setAlignment(Align.center) })
|
||||
.width(stage.width / 2).row()
|
||||
warTable.add(progress.toLabel().apply { wrap = true; setAlignment(Align.center) })
|
||||
.width(stage.width / 2).row()
|
||||
|
||||
return warTable
|
||||
}
|
||||
|
||||
private fun getMajorCivDiplomacyTable(otherCiv: CivilizationInfo): Table {
|
||||
val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(viewingCiv)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user