Founding Religions (#4505)

* Fixed crashes on loading save games with religion

* Added missing credit

* Religious cities now show religion icons in the city button

* Add icons for religions

* You can now found beliefs, with snazzy icons!

* Fixed bug which made prophets impossible to generate

* Added missing translatable strings

* Fixed translation tests properly

* Implemented requested changes

* Implemented part of the requested changes

* Removed SplitPane in favor of Table

* Removed unused code

* Capped the amount of foundable religions to the amount of religions
This commit is contained in:
Xander Lenstra
2021-07-14 21:09:51 +02:00
committed by GitHub
parent 477051c616
commit 2752c713b9
28 changed files with 975 additions and 675 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -0,0 +1,7 @@
[
"Buddhism",
"Christianity",
"Hinduism",
"Islam",
"Taoism"
]

View File

@ -1477,7 +1477,8 @@
{ {
"name": "Great Prophet", "name": "Great Prophet",
"unitType": "Civilian", "unitType": "Civilian",
"uniques": ["Can construct [Holy site] if it hasn't spread religion yet", "Can spread religion [4] times","Great Person - [Faith]", "Unbuildable"], "uniques": ["Can construct [Holy site] if it hasn't spread religion yet", "Can spread religion [4] times",
"May found a religion", "Great Person - [Faith]", "Unbuildable"],
"movement": 2 "movement": 2
}, },
{ {

View File

@ -723,6 +723,7 @@ Your trade mission to [civName] has earned you [goldAmount] gold and [influenceA
Hurry Wonder = Versnel Wonder Hurry Wonder = Versnel Wonder
Spread Religion = Verkondig Religie Spread Religion = Verkondig Religie
Spread [religionName] = Verkondig [religionName] Spread [religionName] = Verkondig [religionName]
Found a Religion = Begin een religie
Your citizens have been happy with your rule for so long that the empire enters a Golden Age! = Jouw dorpelingen zijn voor zo een lange tijd blij met jouw leiderschap dat jouw rijk de Gulden Eeuw binnen gaat. Your citizens have been happy with your rule for so long that the empire enters a Golden Age! = Jouw dorpelingen zijn voor zo een lange tijd blij met jouw leiderschap dat jouw rijk de Gulden Eeuw binnen gaat.
You have entered the [newEra]! = Jij bent in het/de [newEra] aangekomen! You have entered the [newEra]! = Jij bent in het/de [newEra] aangekomen!
[civName] has entered the [eraName]! = [civName] is aangekomen in de [eraName]! [civName] has entered the [eraName]! = [civName] is aangekomen in de [eraName]!

View File

@ -679,6 +679,7 @@ Your trade mission to [civName] has earned you [goldAmount] gold and [influenceA
Hurry Wonder = Hurry Wonder =
Spread Religion = Spread Religion =
Spread [religionName] = Spread [religionName] =
Found a Religion =
Your citizens have been happy with your rule for so long that the empire enters a Golden Age! = Your citizens have been happy with your rule for so long that the empire enters a Golden Age! =
You have entered the [newEra]! = You have entered the [newEra]! =
[civName] has entered the [eraName]! = [civName] has entered the [eraName]! =
@ -904,6 +905,11 @@ Unlocked at =
Gain 2 free technologies = Gain 2 free technologies =
All policies adopted = All policies adopted =
# Religions
Choose an Icon and name for your Religion =
Found [religionName] =
# Terrains # Terrains
Impassable = Impassable =

View File

@ -395,7 +395,7 @@ object Battle {
UncivGame.Current.settings.addCompletedTutorialTask("Conquer a city") UncivGame.Current.settings.addCompletedTutorialTask("Conquer a city")
} else { } else {
city.puppetCity(attackerCiv) city.puppetCity(attackerCiv)
if (city.population.population < 4 && !city.isOriginalCapital) { if (city.population.population < 4 && city.canBeDestroyed()) {
city.annexCity() city.annexCity()
city.isBeingRazed = true city.isBeingRazed = true
} }

View File

@ -433,7 +433,7 @@ class CityInfo {
} }
} else population.nextTurn(foodForNextTurn()) } else population.nextTurn(foodForNextTurn())
if (civInfo.gameInfo.hasReligionEnabled()) religion.getAffectedBySurroundingCities() // if (civInfo.gameInfo.hasReligionEnabled()) religion.getAffectedBySurroundingCities()
if (this in civInfo.cities) { // city was not destroyed if (this in civInfo.cities) { // city was not destroyed
health = min(health + 20, getMaxHealth()) health = min(health + 20, getMaxHealth())
@ -442,10 +442,9 @@ class CityInfo {
} }
fun destroyCity(overrideSafeties: Boolean = false) { fun destroyCity(overrideSafeties: Boolean = false) {
// Original capitals can't be destroyed. // Original capitals and holy cities cannot be destroyed,
// Unless they are captured by a one-city-challenger for some reason. // unless, of course, they are captured by a one-city-challenger.
// This was tested in the original. if (!canBeDestroyed() && !overrideSafeties) return
if (isOriginalCapital && !overrideSafeties) return
for (airUnit in getCenterTile().airUnits.toList()) airUnit.destroy() //Destroy planes stationed in city for (airUnit in getCenterTile().airUnits.toList()) airUnit.destroy() //Destroy planes stationed in city
@ -595,5 +594,13 @@ class CityInfo {
// Note that we don't query religion here, as those only have local effects (for now at least) // Note that we don't query religion here, as those only have local effects (for now at least)
} }
fun isHolyCity(): Boolean {
return civInfo.gameInfo.religions.values.any { it.holyCityId == id }
}
fun canBeDestroyed(): Boolean {
return !isCapital() && !isHolyCity()
}
//endregion //endregion
} }

View File

@ -1,5 +1,6 @@
package com.unciv.logic.civilization package com.unciv.logic.civilization
import com.unciv.logic.city.CityInfo
import com.unciv.logic.map.MapUnit import com.unciv.logic.map.MapUnit
import com.unciv.models.Religion import com.unciv.models.Religion
import com.unciv.models.ruleset.Belief import com.unciv.models.ruleset.Belief
@ -15,7 +16,7 @@ class ReligionManager {
var religion: Religion? = null var religion: Religion? = null
// You might ask why this is the transient variable, and not the one in GameInfo. // You might ask why this is the transient variable, and not the one in GameInfo.
// After all, filling a hashmap is much easier than later distributing its contents over multiple classes. // After all, filling a hashmap is much easier than later distributing its contents over multiple classes.
// I would agree that that is better. However, there is, of course, a problem. // There is, however, a problem.
// When founding a religion, the religion of your pantheon doesn't immediately disappear. // When founding a religion, the religion of your pantheon doesn't immediately disappear.
// It just stops growing. Your new religion will then have to spread out from your holy city // It just stops growing. Your new religion will then have to spread out from your holy city
// and convert these cities. This means, that civilizations can have multiple active religions // and convert these cities. This means, that civilizations can have multiple active religions
@ -25,9 +26,18 @@ class ReligionManager {
private var greatProphetsEarned = 0 private var greatProphetsEarned = 0
var religionState = ReligionState.None
private set
private var foundingCityId: String? = null
// Only used for keeping track of the city a prophet was used when founding a religion
fun clone(): ReligionManager { fun clone(): ReligionManager {
val clone = ReligionManager() val clone = ReligionManager()
clone.foundingCityId = foundingCityId
clone.storedFaith = storedFaith clone.storedFaith = storedFaith
clone.religionState = religionState
clone.greatProphetsEarned = greatProphetsEarned
return clone return clone
} }
@ -45,17 +55,7 @@ class ReligionManager {
} }
fun startTurn() { fun startTurn() {
if (canGenerateProphet()) { if (canGenerateProphet()) generateProphet()
val prophetSpawnChange = (5f + storedFaith - faithForNextGreatProphet()) / 100f
if (Random(civInfo.gameInfo.turns).nextFloat() < prophetSpawnChange) {
val birthCity = civInfo.cities.filter { it.religion.getMajorityReligion() == religion!!.name }.random()
val prophet = civInfo.addUnit("Great Prophet", birthCity)
if (prophet == null) return
prophet.religion = religion!!.name
prophet.abilityUsedCount["Religion Spread"] = 0
storedFaith -= faithForNextGreatProphet()
}
}
} }
fun endTurn(faithFromNewTurn: Int) { fun endTurn(faithFromNewTurn: Int) {
@ -65,8 +65,8 @@ class ReligionManager {
private fun faithForPantheon() = 10 + civInfo.gameInfo.civilizations.count { it.isMajorCiv() && it.religionManager.religion != null } * 5 private fun faithForPantheon() = 10 + civInfo.gameInfo.civilizations.count { it.isMajorCiv() && it.religionManager.religion != null } * 5
fun canFoundPantheon(): Boolean { fun canFoundPantheon(): Boolean {
if (religion != null) return false
if (!civInfo.gameInfo.hasReligionEnabled()) return false if (!civInfo.gameInfo.hasReligionEnabled()) return false
if (religionState != ReligionState.None) return false
if (!civInfo.isMajorCiv()) return false if (!civInfo.isMajorCiv()) return false
if (civInfo.gameInfo.ruleSet.beliefs.values.none { isPickablePantheonBelief(it) }) if (civInfo.gameInfo.ruleSet.beliefs.values.none { isPickablePantheonBelief(it) })
return false return false
@ -75,7 +75,7 @@ class ReligionManager {
fun isPickablePantheonBelief(belief: Belief): Boolean { fun isPickablePantheonBelief(belief: Belief): Boolean {
if (belief.type != "Pantheon") return false if (belief.type != "Pantheon") return false
if (civInfo.gameInfo.civilizations.any { it.religionManager.religion != null && it.religionManager.religion!!.pantheonBeliefs.contains(belief.name)}) if (civInfo.gameInfo.civilizations.any { it.religionManager.religion != null && it.religionManager.religion!!.followerBeliefs.contains(belief.name)})
return false return false
return true return true
} }
@ -83,23 +83,101 @@ class ReligionManager {
fun choosePantheonBelief(belief: Belief) { fun choosePantheonBelief(belief: Belief) {
storedFaith -= faithForPantheon() storedFaith -= faithForPantheon()
religion = Religion(belief.name, civInfo.gameInfo, civInfo.civName) religion = Religion(belief.name, civInfo.gameInfo, civInfo.civName)
religion!!.pantheonBeliefs.add(belief.name) religion!!.followerBeliefs.add(belief.name)
civInfo.gameInfo.religions[belief.name] = religion!! civInfo.gameInfo.religions[belief.name] = religion!!
// This should later be changed when religions can have multiple beliefs // This should later be changed when religions can have multiple beliefs
civInfo.getCapital().religion[belief.name] = 100 // Capital is religious, other cities are not civInfo.getCapital().religion[belief.name] = 100 // Capital is religious, other cities are not
religionState = ReligionState.Pantheon
} }
// https://www.reddit.com/r/civ/comments/2m82wu/can_anyone_detail_the_finer_points_of_great/ // https://www.reddit.com/r/civ/comments/2m82wu/can_anyone_detail_the_finer_points_of_great/
// Game files (globaldefines.xml) // Game files (globaldefines.xml)
fun faithForNextGreatProphet() = ((200 + 100 * greatProphetsEarned * (greatProphetsEarned + 1)/2) * civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt() private fun faithForNextGreatProphet() = (
(200 + 100 * greatProphetsEarned * (greatProphetsEarned + 1) / 2) *
civInfo.gameInfo.gameParameters.gameSpeed.modifier
).toInt()
fun canGenerateProphet(): Boolean { private fun canGenerateProphet(): Boolean {
if (religion == null || !religion!!.hasPantheon()) return false // First get a pantheon, then we'll talk about a real religion if (religion == null || religionState == ReligionState.None) return false // First get a pantheon, then we'll talk about a real religion
if (storedFaith < faithForNextGreatProphet()) return false if (storedFaith < faithForNextGreatProphet()) return false
// In the base game, great prophets shouldn't generate anymore starting from the industrial era // In the base game, great prophets shouldn't generate anymore starting from the industrial era
// This is difficult to implement in the current codebase, probably requires an additional variable in eras.json // This is difficult to implement in the current codebase, probably requires an additional variable in eras.json
// Also only if you either [have founded a religion] or [the max amount of religions (players/2 + 1) has not been reached].
// As this is yet to be implemented, this function does almost nothing
return true return true
} }
private fun generateProphet() {
val prophetSpawnChange = (5f + storedFaith - faithForNextGreatProphet()) / 100f
if (Random(civInfo.gameInfo.turns).nextFloat() < prophetSpawnChange) {
val birthCity =
if (religionState == ReligionState.Pantheon) civInfo.getCapital()
else civInfo.cities.firstOrNull { it.id == religion!!.holyCityId }
val prophet = civInfo.addUnit("Great Prophet", birthCity)
if (prophet == null) return
prophet.religion = religion!!.name
prophet.abilityUsedCount["Religion Spread"] = 0
storedFaith -= faithForNextGreatProphet()
}
}
fun mayUseGreatProphetAtAll(prophet: MapUnit): Boolean {
if (religion == null) return false // First found a pantheon
if (religion!!.isMajorReligion()) return false // Already created a major religion
if (prophet.abilityUsedCount["Religion Spread"] != 0) return false // Already used its power for other things
val foundedReligionsCount = civInfo.gameInfo.civilizations.count {
it.religionManager.religion != null && it.religionManager.religion!!.isMajorReligion()
}
if (foundedReligionsCount >= civInfo.gameInfo.civilizations.count { it.isMajorCiv() } / 2 + 1)
return false // Too bad, too many religions have already been founded.
if (foundedReligionsCount >= civInfo.gameInfo.ruleSet.religions.count())
return false
// Mod maker did not provide enough religions for the amount of civs present
return true
}
fun mayUseGreatProphetNow(prophet: MapUnit): Boolean {
if (!mayUseGreatProphetAtAll(prophet)) return false
if (!prophet.getTile().isCityCenter()) return false
return true
}
fun useGreatProphet(prophet: MapUnit) {
if (!mayUseGreatProphetNow(prophet)) return // How did you do this?
religionState = ReligionState.FoundingReligion
foundingCityId = prophet.getTile().getCity()!!.id
}
fun foundReligion(iconName: String, name: String, founderBelief: String, followerBelief: String) {
val newReligion = Religion(name, civInfo.gameInfo, civInfo.civName)
newReligion.iconName = iconName
if (religion != null) {
newReligion.followerBeliefs.addAll(religion!!.followerBeliefs)
}
newReligion.followerBeliefs.add(followerBelief)
newReligion.founderBeliefs.add(founderBelief)
newReligion.holyCityId = foundingCityId
religion = newReligion
civInfo.gameInfo.religions[name] = newReligion
religionState = ReligionState.Religion
val holyCity = civInfo.cities.firstOrNull { it.id == newReligion.holyCityId }!!
holyCity.religion.clear()
holyCity.religion[name] = 100
foundingCityId = null
}
}
enum class ReligionState {
None,
Pantheon,
FoundingReligion, // Great prophet used, but religion has not yet been founded
Religion,
EnhancingReligion, // Great prophet used, but religion has not yet been enhanced
EnhancedReligion
} }

View File

@ -8,11 +8,15 @@ import com.unciv.models.stats.INamed
/** Data object for Religions */ /** Data object for Religions */
class Religion() : INamed { class Religion() : INamed {
var pantheonBeliefs: HashSet<String> = hashSetOf() override lateinit var name: String
var iconName: String = "Pantheon"
lateinit var foundingCivName: String
var holyCityId: String? = null
var founderBeliefs: HashSet<String> = hashSetOf() var founderBeliefs: HashSet<String> = hashSetOf()
var followerBeliefs: HashSet<String> = hashSetOf() var followerBeliefs: HashSet<String> = hashSetOf()
override lateinit var name: String
lateinit var foundingCivName: String
@Transient @Transient
lateinit var gameInfo: GameInfo lateinit var gameInfo: GameInfo
@ -25,7 +29,8 @@ class Religion() : INamed {
fun clone(): Religion { fun clone(): Religion {
val toReturn = Religion(name, gameInfo, foundingCivName) val toReturn = Religion(name, gameInfo, foundingCivName)
toReturn.pantheonBeliefs.addAll(pantheonBeliefs) toReturn.iconName = iconName
toReturn.holyCityId = holyCityId
toReturn.founderBeliefs.addAll(founderBeliefs) toReturn.founderBeliefs.addAll(founderBeliefs)
toReturn.followerBeliefs.addAll(followerBeliefs) toReturn.followerBeliefs.addAll(followerBeliefs)
return toReturn return toReturn
@ -44,7 +49,7 @@ class Religion() : INamed {
} }
fun getFollowerUniques(): Sequence<Unique> { fun getFollowerUniques(): Sequence<Unique> {
return getUniquesOfBeliefs((followerBeliefs + pantheonBeliefs).toHashSet()) return getUniquesOfBeliefs(followerBeliefs)
} }
fun getFounderUniques(): Sequence<Unique> { fun getFounderUniques(): Sequence<Unique> {
@ -56,10 +61,12 @@ class Religion() : INamed {
} }
fun isMajorReligion(): Boolean { fun isMajorReligion(): Boolean {
return founderBeliefs.isNotEmpty() && followerBeliefs.isNotEmpty() if ("" in followerBeliefs) return true // Temporary as a result of follower beliefs not yet being implemented
return founderBeliefs.isNotEmpty() && followerBeliefs.any { gameInfo.ruleSet.beliefs[it]!!.type == "Follower"}
} }
fun hasPantheon(): Boolean { fun hasPantheon(): Boolean {
return pantheonBeliefs.isNotEmpty() // Temporary as a result of follower beliefs not yet being implemented
return followerBeliefs.any { it != "" && gameInfo.ruleSet.beliefs[it]!!.type == "Pantheon" }
} }
} }

View File

@ -35,5 +35,6 @@ enum class UnitActionType(val value: String) {
StartGoldenAge("Start Golden Age"), StartGoldenAge("Start Golden Age"),
HurryWonder("Hurry Wonder"), HurryWonder("Hurry Wonder"),
ConductTradeMission("Conduct Trade Mission"), ConductTradeMission("Conduct Trade Mission"),
FoundReligion("Found a Religion"),
DisbandUnit("Disband unit") DisbandUnit("Disband unit")
} }

View File

@ -45,21 +45,23 @@ class Ruleset {
var modWithReligionLoaded = false var modWithReligionLoaded = false
var name = "" var name = ""
val beliefs = LinkedHashMap<String, Belief>()
val religions = ArrayList<String>()
val buildings = LinkedHashMap<String, Building>() val buildings = LinkedHashMap<String, Building>()
val terrains = LinkedHashMap<String, Terrain>() val difficulties = LinkedHashMap<String, Difficulty>()
val tileResources = LinkedHashMap<String, TileResource>()
val tileImprovements = LinkedHashMap<String, TileImprovement>()
val technologies = LinkedHashMap<String, Technology>()
val eras = LinkedHashMap<String, Era>() val eras = LinkedHashMap<String, Era>()
val units = LinkedHashMap<String, BaseUnit>()
val unitPromotions = LinkedHashMap<String, Promotion>()
val nations = LinkedHashMap<String, Nation>() val nations = LinkedHashMap<String, Nation>()
val policies = LinkedHashMap<String, Policy>()
val policyBranches = LinkedHashMap<String, PolicyBranch>()
val quests = LinkedHashMap<String, Quest>() val quests = LinkedHashMap<String, Quest>()
val specialists = LinkedHashMap<String, Specialist>() val specialists = LinkedHashMap<String, Specialist>()
val policyBranches = LinkedHashMap<String, PolicyBranch>() val technologies = LinkedHashMap<String, Technology>()
val policies = LinkedHashMap<String, Policy>() val terrains = LinkedHashMap<String, Terrain>()
val beliefs = LinkedHashMap<String, Belief>() val tileImprovements = LinkedHashMap<String, TileImprovement>()
val difficulties = LinkedHashMap<String, Difficulty>() val tileResources = LinkedHashMap<String, TileResource>()
val units = LinkedHashMap<String, BaseUnit>()
val unitPromotions = LinkedHashMap<String, Promotion>()
val mods = LinkedHashSet<String>() val mods = LinkedHashSet<String>()
var modOptions = ModOptions() var modOptions = ModOptions()
@ -82,10 +84,12 @@ class Ruleset {
difficulties.putAll(ruleset.difficulties) difficulties.putAll(ruleset.difficulties)
eras.putAll(ruleset.eras) eras.putAll(ruleset.eras)
nations.putAll(ruleset.nations) nations.putAll(ruleset.nations)
for (nationToRemove in ruleset.modOptions.nationsToRemove) nations.remove(nationToRemove)
policyBranches.putAll(ruleset.policyBranches) policyBranches.putAll(ruleset.policyBranches)
policies.putAll(ruleset.policies) policies.putAll(ruleset.policies)
beliefs.putAll(ruleset.beliefs) beliefs.putAll(ruleset.beliefs)
quests.putAll(ruleset.quests) quests.putAll(ruleset.quests)
religions.addAll(ruleset.religions)
specialists.putAll(ruleset.specialists) specialists.putAll(ruleset.specialists)
technologies.putAll(ruleset.technologies) technologies.putAll(ruleset.technologies)
for (techToRemove in ruleset.modOptions.techsToRemove) technologies.remove(techToRemove) for (techToRemove in ruleset.modOptions.techsToRemove) technologies.remove(techToRemove)
@ -95,7 +99,6 @@ class Ruleset {
unitPromotions.putAll(ruleset.unitPromotions) unitPromotions.putAll(ruleset.unitPromotions)
units.putAll(ruleset.units) units.putAll(ruleset.units)
for (unitToRemove in ruleset.modOptions.unitsToRemove) units.remove(unitToRemove) for (unitToRemove in ruleset.modOptions.unitsToRemove) units.remove(unitToRemove)
for (nationToRemove in ruleset.modOptions.nationsToRemove) nations.remove(nationToRemove)
mods += ruleset.mods mods += ruleset.mods
modWithReligionLoaded = modWithReligionLoaded || ruleset.modWithReligionLoaded modWithReligionLoaded = modWithReligionLoaded || ruleset.modWithReligionLoaded
} }
@ -110,6 +113,7 @@ class Ruleset {
mods.clear() mods.clear()
nations.clear() nations.clear()
policies.clear() policies.clear()
religions.clear()
quests.clear() quests.clear()
technologies.clear() technologies.clear()
terrains.clear() terrains.clear()
@ -157,6 +161,7 @@ class Ruleset {
val erasFile = folderHandle.child("Eras.json") val erasFile = folderHandle.child("Eras.json")
if (erasFile.exists()) eras += createHashmap(jsonParser.getFromJson(Array<Era>::class.java, erasFile)) if (erasFile.exists()) eras += createHashmap(jsonParser.getFromJson(Array<Era>::class.java, erasFile))
val unitsFile = folderHandle.child("Units.json") val unitsFile = folderHandle.child("Units.json")
if (unitsFile.exists()) units += createHashmap(jsonParser.getFromJson(Array<BaseUnit>::class.java, unitsFile)) if (unitsFile.exists()) units += createHashmap(jsonParser.getFromJson(Array<BaseUnit>::class.java, unitsFile))
@ -189,6 +194,9 @@ class Ruleset {
if (beliefsFile.exists()) if (beliefsFile.exists())
beliefs += createHashmap(jsonParser.getFromJson(Array<Belief>::class.java, beliefsFile)) beliefs += createHashmap(jsonParser.getFromJson(Array<Belief>::class.java, beliefsFile))
val religionsFile = folderHandle.child("Religions.json")
if (religionsFile.exists())
religions += jsonParser.getFromJson(Array<String>::class.java, religionsFile).toList()
val nationsFile = folderHandle.child("Nations.json") val nationsFile = folderHandle.child("Nations.json")
if (nationsFile.exists()) { if (nationsFile.exists()) {

View File

@ -147,7 +147,7 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() {
val razeCityButton = "Raze city".toTextButton() val razeCityButton = "Raze city".toTextButton()
razeCityButton.labelCell.pad(10f) razeCityButton.labelCell.pad(10f)
razeCityButton.onClick { city.isBeingRazed = true; update() } razeCityButton.onClick { city.isBeingRazed = true; update() }
if (!canChangeState || city.isOriginalCapital) if (!canChangeState || !city.canBeDestroyed())
razeCityButton.disable() razeCityButton.disable()
razeCityButtonHolder.add(razeCityButton).colspan(cityPickerTable.columns) razeCityButtonHolder.add(razeCityButton).colspan(cityPickerTable.columns)

View File

@ -0,0 +1,99 @@
package com.unciv.ui.pickerscreens
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.UncivSound
import com.unciv.models.ruleset.Belief
import com.unciv.models.translations.tr
import com.unciv.ui.utils.*
import kotlin.math.max
class FoundReligionPickerScreen (
private val choosingCiv: CivilizationInfo,
private val gameInfo: GameInfo
): PickerScreen() {
// Roughly follows the layout of the original (although I suck at UI designing, so please improve this)
private val topReligionIcons = Table() // Top of the layout, contains icons for religions
private val leftChosenBeliefs: ScrollPane // Left middle part, contains buttons to select the types of beliefs to choose
private val rightBeliefsToChoose: ScrollPane // Right middle part, contains the beliefs to choose
private val middlePanes = Table()
private var previouslySelectedIcon: Button? = null
private var iconName: String? = null
private var religionName: String? = null
private var chosenFounderBelief: Belief? = null
private var chosenFollowerBelief: Belief? = null
init {
closeButton.isVisible = true
setDefaultCloseAction()
setupReligionIcons()
leftChosenBeliefs = ScrollPane(Table())
rightBeliefsToChoose = ScrollPane(Table())
middlePanes.add(leftChosenBeliefs)
middlePanes.addSeparatorVertical()
middlePanes.add(rightBeliefsToChoose)
topTable.add(topReligionIcons).row()
// commented out, as the middle panes will always be empty for now, and this will create a random line otherwise
// topTable.addSeparator()
topTable.add(middlePanes)
rightSideButton.label = "Choose a religion".toLabel()
rightSideButton.onClick(UncivSound.Choir) {
choosingCiv.religionManager.foundReligion(iconName!!, religionName!!, "", "", /**chosenFollowerBelief!!.name, chosenFounderBelief!!.name*/)
UncivGame.Current.setWorldScreen()
}
}
private fun checkAndEnableRightSideButton() {
if (religionName == null) return
// check if founder belief chosen
// check if follower belief chosen
rightSideButton.enable()
}
private fun setupReligionIcons() {
topReligionIcons.clear()
// This should later be replaced with a user-modifiable text field, but not in this PR
val descriptionLabel = "Choose an Icon and name for your Religion".toLabel()
val iconsTable = Table()
iconsTable.align(Align.center)
for (religionName in gameInfo.ruleSet.religions) {
if (gameInfo.religions.keys.any { it == religionName }) continue
val image = ImageGetter.getReligionIcon(religionName)
image.color = Color.BLACK
val icon = image.surroundWithCircle(60f)
val button = Button(icon, skin)
val translatedReligionName = religionName.tr()
button.onClick {
if (previouslySelectedIcon != null) {
previouslySelectedIcon!!.enable()
}
iconName = religionName
this.religionName = religionName
previouslySelectedIcon = button
button.disable()
descriptionLabel.setText(translatedReligionName)
rightSideButton.label = "Found [$translatedReligionName]".toLabel()
checkAndEnableRightSideButton()
}
if (religionName == this.religionName) button.disable()
iconsTable.add(button).pad(5f)
}
iconsTable.row()
topReligionIcons.add(iconsTable).padBottom(10f).row()
topReligionIcons.add(descriptionLabel).center()
}
}

View File

@ -12,7 +12,7 @@ open class PickerScreen(val disableScroll: Boolean = false) : CameraStageBaseScr
protected var descriptionLabel: Label protected var descriptionLabel: Label
protected var rightSideGroup = VerticalGroup() protected var rightSideGroup = VerticalGroup()
protected var rightSideButton: TextButton protected var rightSideButton: TextButton
private var screenSplit = 0.85f protected var screenSplit = 0.85f
/** /**
* The table displaying the choices from which to pick (usually). * The table displaying the choices from which to pick (usually).

View File

@ -252,6 +252,13 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
iconTable.add(nationIcon).size(20f) iconTable.add(nationIcon).size(20f)
} }
val cityReligionName = city.religion.getMajorityReligion()
if (cityReligionName != null) {
val cityReligion = city.civInfo.gameInfo.religions[cityReligionName]!!
val religionImage = ImageGetter.getReligionIcon(cityReligion.iconName)
iconTable.add(religionImage).size(20f).padLeft(5f).fillY()
}
iconTable.pack() iconTable.pack()
cityStrengthLabel.x = label.x // so it'll be aligned right above the city name cityStrengthLabel.x = label.x // so it'll be aligned right above the city name
cityStrengthLabel.setY(iconTable.height, Align.top) cityStrengthLabel.setY(iconTable.height, Align.top)

View File

@ -56,8 +56,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
private fun updateLeftSideTable() { private fun updateLeftSideTable() {
leftSideTable.clear() leftSideTable.clear()
for (civ in viewingCiv.gameInfo.civilizations for (civ in viewingCiv.gameInfo.civilizations
.filterNot { it.isDefeated() || it == viewingCiv || it.isBarbarian() || it.isSpectator() }) { .filterNot { it.isDefeated() || it == viewingCiv || it.isBarbarian() || it.isSpectator() || viewingCiv.knows(it) }) {
if (!viewingCiv.knows(civ)) continue
val civIndicator = ImageGetter.getNationIndicator(civ.nation, 100f) val civIndicator = ImageGetter.getNationIndicator(civ.nation, 100f)

View File

@ -288,6 +288,10 @@ object ImageGetter {
return circle return circle
} }
fun getReligionIcon(iconName: String): Image {
return getImage("ReligionIcons/$iconName")
}
fun getBlue() = Color(0x004085bf) fun getBlue() = Color(0x004085bf)
fun getCircle() = getImage("OtherIcons/Circle") fun getCircle() = getImage("OtherIcons/Circle")

View File

@ -142,7 +142,7 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
close() close()
} }
add("Raze".toTextButton().apply { add("Raze".toTextButton().apply {
if (city.isOriginalCapital) disable() if (!city.canBeDestroyed()) disable()
else { else {
onClick(function = razeAction) onClick(function = razeAction)
keyPressDispatcher['r'] = razeAction keyPressDispatcher['r'] = razeAction

View File

@ -19,6 +19,7 @@ import com.unciv.UncivGame
import com.unciv.logic.GameInfo import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver import com.unciv.logic.GameSaver
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.ReligionState
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.models.Tutorial import com.unciv.models.Tutorial
import com.unciv.models.UncivSound import com.unciv.models.UncivSound
@ -698,6 +699,11 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
game.setScreen(PantheonPickerScreen(viewingCiv, gameInfo)) game.setScreen(PantheonPickerScreen(viewingCiv, gameInfo))
} }
viewingCiv.religionManager.religionState == ReligionState.FoundingReligion ->
NextTurnAction("Found Religion", Color.WHITE) {
game.setScreen(FoundReligionPickerScreen(viewingCiv, gameInfo))
}
else -> else ->
NextTurnAction("${Fonts.turn}{Next turn}", Color.WHITE) { NextTurnAction("${Fonts.turn}{Next turn}", Color.WHITE) {
game.settings.addCompletedTutorialTask("Pass a turn") game.settings.addCompletedTutorialTask("Pass a turn")

View File

@ -63,6 +63,7 @@ object UnitActions {
// //
addCreateWaterImprovements(unit, actionList) addCreateWaterImprovements(unit, actionList)
addGreatPersonActions(unit, actionList, tile) addGreatPersonActions(unit, actionList, tile)
addFoundReligionAction(unit, actionList, tile)
addSpreadReligionActions(unit, actionList, tile) addSpreadReligionActions(unit, actionList, tile)
actionList += getImprovementConstructionActions(unit, tile) actionList += getImprovementConstructionActions(unit, tile)
addDisbandAction(actionList, unit, worldScreen) addDisbandAction(actionList, unit, worldScreen)
@ -372,7 +373,8 @@ object UnitActions {
unit.civInfo.tech.addScience(unit.civInfo.tech.getScienceFromGreatScientist()) unit.civInfo.tech.addScience(unit.civInfo.tech.getScienceFromGreatScientist())
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy() unit.destroy()
}.takeIf { unit.civInfo.tech.currentTechnologyName() != null }) }.takeIf { unit.civInfo.tech.currentTechnologyName() != null }
)
} }
"Can start an []-turn golden age" -> { "Can start an []-turn golden age" -> {
val turnsToGoldenAge = unique.params[0].toInt() val turnsToGoldenAge = unique.params[0].toInt()
@ -382,7 +384,8 @@ object UnitActions {
unit.civInfo.goldenAges.enterGoldenAge(turnsToGoldenAge) unit.civInfo.goldenAges.enterGoldenAge(turnsToGoldenAge)
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy() unit.destroy()
}.takeIf { unit.currentTile.getOwner() != null && unit.currentTile.getOwner() == unit.civInfo }) }.takeIf { unit.currentTile.getOwner() != null && unit.currentTile.getOwner() == unit.civInfo }
)
} }
"Can speed up construction of a wonder" -> { "Can speed up construction of a wonder" -> {
val canHurryWonder = if (!tile.isCityCenter()) false val canHurryWonder = if (!tile.isCityCenter()) false
@ -400,7 +403,8 @@ object UnitActions {
} }
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy() unit.destroy()
}.takeIf { canHurryWonder }) }.takeIf { canHurryWonder }
)
} }
"Can undertake a trade mission with City-State, giving a large sum of gold and [] Influence" -> { "Can undertake a trade mission with City-State, giving a large sum of gold and [] Influence" -> {
val canConductTradeMission = tile.owningCity?.civInfo?.isCityState() == true val canConductTradeMission = tile.owningCity?.civInfo?.isCityState() == true
@ -419,16 +423,30 @@ object UnitActions {
tile.owningCity!!.civInfo.civName, NotificationIcon.Gold, NotificationIcon.Culture) tile.owningCity!!.civInfo.civName, NotificationIcon.Gold, NotificationIcon.Culture)
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy() unit.destroy()
}.takeIf { canConductTradeMission }) }.takeIf { canConductTradeMission }
)
} }
} }
} }
private fun addFoundReligionAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
if (!unit.hasUnique("May found a religion")) return // should later also include enhance religion
if (!unit.civInfo.religionManager.mayUseGreatProphetAtAll(unit)) return
actionList += UnitAction(UnitActionType.FoundReligion,
uncivSound = UncivSound.Choir,
action = {
addGoldPerGreatPersonUsage(unit.civInfo)
unit.civInfo.religionManager.useGreatProphet(unit)
unit.destroy()
}.takeIf { unit.civInfo.religionManager.mayUseGreatProphetNow(unit) }
)
}
private fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) { private fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
if (!unit.hasUnique("Can spread religion [] times")) return if (!unit.hasUnique("Can spread religion [] times")) return
if (unit.religion == null) return if (unit.religion == null) return
val maxReligionSpreads = unit.maxReligionSpreads() val maxReligionSpreads = unit.maxReligionSpreads()
if (!unit.abilityUsedCount.containsKey("Religion Spread")) return // This should be impossible anways, but just in case if (!unit.abilityUsedCount.containsKey("Religion Spread")) return // This should be impossible anyways, but just in case
if (maxReligionSpreads <= unit.abilityUsedCount["Religion Spread"]!!) return if (maxReligionSpreads <= unit.abilityUsedCount["Religion Spread"]!!) return
val city = tile.getCity() val city = tile.getCity()
actionList += UnitAction(UnitActionType.SpreadReligion, actionList += UnitAction(UnitActionType.SpreadReligion,

View File

@ -178,6 +178,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Fort](https://thenounproject.com/term/fort/1697645/) By Adrien Coquet * [Fort](https://thenounproject.com/term/fort/1697645/) By Adrien Coquet
* [Citadel](https://thenounproject.com/term/fort/1697646/) By Adrien Coquet * [Citadel](https://thenounproject.com/term/fort/1697646/) By Adrien Coquet
* [Village](https://thenounproject.com/search/?q=city+center&i=476338) by Andrey Vasiliev * [Village](https://thenounproject.com/search/?q=city+center&i=476338) by Andrey Vasiliev
* [pumping station](https://thenounproject.com/search/?q=dike&i=555172) by Peter van Driel for Polder
## Buildings ## Buildings
@ -551,6 +552,14 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Anchor](https://thenounproject.com/term/anchor/676586) by Gregor Cresnar for Amphibious * [Anchor](https://thenounproject.com/term/anchor/676586) by Gregor Cresnar for Amphibious
* [survival knife](https://thenounproject.com/search/?q=survival&i=2663392) by b faris for Survivalism * [survival knife](https://thenounproject.com/search/?q=survival&i=2663392) by b faris for Survivalism
## Religions
* [Lightning Bolt](https://thenounproject.com/search/?q=lightning+bolt&i=154820) by sian huxtable for Pantheon
* [Christianity](https://thenounproject.com/search/?q=christianity&i=181) by Public Domain Nouns for Christianity
* [Islam](https://thenounproject.com/search/?q=Islam&i=2431350) by Muhammed Riza for Islam
* [taoism](https://thenounproject.com/search/?q=Taoism&i=740812) by parkjisun for Taosim
* [Buddhism](https://thenounproject.com/search/?q=buddhism&i=38764) by Julio Yanes for Buddhism
* [Hinduism](https://thenounproject.com/search/?q=hinduism&i=20383) by Mugda Damle for Hinduism
## Others ## Others
* [Circle](https://thenounproject.com/term/circle/1841891/) By Aybige * [Circle](https://thenounproject.com/term/circle/1841891/) By Aybige