Added the Byzantine empire (#5299)

* Added Byzantine Empire as a nation

* Added unique units

* Implemented unique ability

* Refactored out the beliefContainer

* Implemented reviews

* Credits

* Enumified the uniques & added missing translatable strings
This commit is contained in:
Xander Lenstra 2021-09-26 15:58:24 +02:00 committed by GitHub
parent 7f539aa4fa
commit d07eed31b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1073 additions and 982 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 646 KiB

After

Width:  |  Height:  |  Size: 654 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: 1005 KiB

View File

@ -787,9 +787,9 @@
"attacked": "Fate is against you. You earned the animosity of Carthage in your exploration. Your days are numbered.",
"defeated": "The fates became to hate me. This is it? You wouldn't destroy us so without their help.",
"introduction": "The Phoenicians welcome you to this most pleasant kingdom. I am Dido, the queen of Carthage and all that belongs to it.",
"neutralHello": "It is done.",
"neutralHello": "Greetings.",
"hateHello": "What is it now?",
"tradeRequest": "I had an idea and I realized I should tell it to you!",
"tradeRequest": "I just had the marvelous idea, and I think you'll appreciate it too.",
"outerColor": [205,205,205],
"innerColor": [81,0,137],
"uniqueName": "Phoenician Heritage",
@ -800,6 +800,32 @@
"Oea","Theveste","Ibossim","Thapsus","Aleria","Tingis","Abyla","Sabratha","Rusadir","Baecula",
"Saldae"]
},
{
"name": "Byzantium",
"leaderName": "Theodora",
"adjective": ["Byzantine"],
"startBias": ["Coats"],
"preferredVictoryType": "Cultural",
"startIntroPart1": "All hail the most magnificent and magnanimous Empress Theodora, beloved of Byzantium and of Rome! From the lowly ranks of actress and courtesan you became the most powerful woman in the Roman Empire, consort to Justinian I. Starting in the late 520's AD, you joined your husband in a series of important spiritual and legal reforms, creating many laws which elevated the status of and promoted equal treatment of women in the empire. You also aided in the restoration and construction of many aqueducts, bridges, and churches across Constantinople, culminating in the creation of the Hagia Sophia, one of the most splendid architectural wonders of the world.",
"startIntroPart2": "Beautiful Empress, Byzantium is in need of your wisdom and strength - her people are lost without your light to lead them. The Byzantine Empire may have fallen once, but its spirit is still intact waiting to be reborn anew. Can you return Byzantium to the heights of glory it once enjoyed? Can you create a civilization to stand the test of time?",
"declaringWar": "It is always a shame to destroy a thing of beauty. Happily, you are not one.",
"attacked": "Now darling, tantrums are most unbecoming. I shall have to teach you a lesson.",
"defeated": "Like a child playing with toys you are. My people will never love you, nor suffer this indignation gracefully.",
"introduction": "My, isn't this a pleasant surprise - what may I call you, oh mysterious stranger? I am Theodora, beloved of Byzantium.",
"neutralHello": "Hello again.",
"tradeRequest": "I have heard that you adept at certain kinds of ... interactions. Show me.",
"outerColor": [114, 162, 233],
"innerColor": [61, 0, 109],
"uniqueName": "Patriarchate of Constantinople",
"uniques": ["May choose [1] additional belief(s) of any type when [founding] a religion"],
"cities": ["Constantinople", "Adrianople", "Nicaea", "Antioch", "Varna", "Ohrid", "Nicomedia", "Trebizond", "Cherson", "Sardica",
"Ani", "Dyrrachium", "Edessa", "Chalcedon", "Naissus", "Bari", "Iconium", "Prilep", "Samosata", "Kars", "Nicopolis", "Theodosiopolis",
"Tyana", "Gaza", "Kerkyra", "Phoenice", "Selymbria", "Sillyon", "Chrysopolis", "Vodena", "Caesarea", "Traianoupoli", "Constantia", "Athens",
"Patra", "Korinthos"]
},

View File

@ -175,6 +175,21 @@
"obsoleteTech": "Astronomy",
"attackSound": "nonmetalhit"
},
{
"name": "Dromon",
"unitType": "Ranged Water",
"uniqueTo": "Byzantium",
"replaces": "Trireme",
"movement": 4,
"strength": 8,
"rangedStrength": 10,
"cost": 56,
"requiredTech": "Sailing",
"uniques": ["Cannot enter ocean tiles", "+[50]% Strength vs [Water] units"],
"upgradesTo": "Galleass",
"obsoleteTech": "Astronomy",
"attackSound": "arrow"
},
/*
{
"name": "Galley",
@ -331,7 +346,7 @@
"requiredTech": "Horseback Riding",
"requiredResource": "Horses",
"upgradesTo": "Knight",
"obsoleteTech": "Metallurgy",
"obsoleteTech": "Chivalry",
"uniques": ["Can move after attacking","No defensive terrain bonus","-[33]% Strength vs [City]" ],
"hurryCostModifier": 20,
"attackSound": "horse"
@ -346,7 +361,7 @@
"cost": 75,
"requiredTech": "Horseback Riding",
"upgradesTo": "Knight",
"obsoleteTech": "Metallurgy",
"obsoleteTech": "Chivalry",
"promotions": ["Great Generals I"],
"requiredResource": "Horses",
"uniques": ["Can move after attacking", "No defensive terrain bonus", "-[33]% Strength vs [City]"],
@ -363,12 +378,28 @@
"cost": 100,
"requiredTech": "Horseback Riding",
"upgradesTo": "Knight",
"obsoleteTech": "Metallurgy",
"obsoleteTech": "Chivalry",
"promotions": ["Great Generals II"],
"uniques": ["Can move after attacking", "No defensive terrain bonus", "-[33]% Strength vs [City]","[-10]% Strength for enemy [Military] units in adjacent [All] tiles"],
"uniques": ["Can move after attacking", "No defensive terrain bonus", "-[33]% Strength vs [City]",
"[-10]% Strength for enemy [Military] units in adjacent [All] tiles"],
"hurryCostModifier": 20,
"attackSound": "elephant"
},
{
"name": "Cataphract",
"unitType": "Mounted",
"uniqueTo": "Byzantium",
"replaces": "Horseman",
"movement": 3,
"strength": 15,
"cost": 75,
"requiredTech": "Horseback Riding",
"upgradesTo": "Knight",
"obsoleteTech": "Chivalry",
"uniques": ["Can move after attacking", "-[25]% Strength vs [City]"],
"hurryCostModifier": 20,
"attackSound": "horse"
},
{
"name": "Catapult",
"unitType": "Siege",

View File

@ -1079,6 +1079,7 @@ Policy branch: [branchName] =
Choose an Icon and name for your Religion =
Choose a name for your religion =
Choose a [beliefType] belief! =
Choose any belief! =
Found [religionName] =
Enhance [religionName] =
Choose a pantheon =
@ -1189,9 +1190,12 @@ water units =
wounded units =
Wounded =
# For the All "newly-trained [relevant] units in this city receive the [] promotion" translation. Relevant as in 'units that can receive'
# For the "All newly-trained [relevant] units in this city receive the [] promotion" translation. Relevant as in 'units that can receive'
relevant =
# For "May choose [amount] additional [beliefType] beliefs when [founding/enhancing] a religion"
founding =
enhancing =
# Promotions
@ -1318,10 +1322,7 @@ ConditionalsPlacement =
# As this is still under development, conditionals will be added al the time. As a result,
# any translations added for this string will be removed immediately in the next version when more
# conditionals are added. As we don't want to make you retranslate this same line over and over,
# it's removed for now, but it will return once all planned conditionals have been added.
# <if this city has at least [amount] specialists> <when at war> <when not at war> <while the empire is happy> =
# it's removed for now, but it will return once all planned conditionals have been added.

View File

@ -13,6 +13,7 @@ import com.unciv.logic.map.BFS
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.logic.trade.*
import com.unciv.models.Counter
import com.unciv.models.ruleset.Belief
import com.unciv.models.ruleset.BeliefType
import com.unciv.models.ruleset.ModOptionsConstants
@ -22,7 +23,6 @@ import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat
import com.unciv.models.translations.tr
import com.unciv.ui.pickerscreens.BeliefContainer
import com.unciv.ui.victoryscreen.RankingType
import kotlin.math.min
@ -339,35 +339,26 @@ object NextTurnAutomation {
)
}
private fun chooseBeliefs(civInfo: CivilizationInfo, beliefContainer: BeliefContainer): HashSet<Belief> {
private fun chooseBeliefs(civInfo: CivilizationInfo, beliefsToChoose: Counter<BeliefType>): HashSet<Belief> {
val chosenBeliefs = hashSetOf<Belief>()
// The `continue`s should never be reached, but just in case I'd rather have the AI have a
// belief less than make the game crash. The `continue`s should only be reached whenever
// there are not enough beliefs to choose, but there should be, as otherwise we could
// not have used a great prophet to found/enhance our religion.
for (counter in 0 until beliefContainer.pantheonBeliefCount)
chosenBeliefs.add(
chooseBeliefOfType(civInfo, BeliefType.Pantheon, chosenBeliefs) ?: continue
)
for (counter in 0 until beliefContainer.followerBeliefCount)
chosenBeliefs.add(
chooseBeliefOfType(civInfo, BeliefType.Follower, chosenBeliefs) ?: continue
)
for (counter in 0 until beliefContainer.founderBeliefCount)
chosenBeliefs.add(
chooseBeliefOfType(civInfo, BeliefType.Founder, chosenBeliefs) ?: continue
)
for (counter in 0 until beliefContainer.enhancerBeliefCount)
chosenBeliefs.add(
chooseBeliefOfType(civInfo, BeliefType.Enhancer, chosenBeliefs) ?: continue
)
for (belief in BeliefType.values()) {
if (belief == BeliefType.None) continue
for (counter in 0 until (beliefsToChoose[belief] ?: 0))
chosenBeliefs.add(
chooseBeliefOfType(civInfo, belief, chosenBeliefs) ?: continue
)
}
return chosenBeliefs
}
private fun chooseBeliefOfType(civInfo: CivilizationInfo, beliefType: BeliefType, additionalBeliefsToExclude: HashSet<Belief> = hashSetOf()): Belief? {
return civInfo.gameInfo.ruleSet.beliefs
.filter {
it.value.type == beliefType
(it.value.type == beliefType || beliefType == BeliefType.Any)
&& !additionalBeliefsToExclude.contains(it.value)
&& !civInfo.gameInfo.religions.values
.flatMap { religion -> religion.getBeliefs(beliefType) }.contains(it.value)

View File

@ -1,10 +1,11 @@
package com.unciv.logic.civilization
import com.unciv.logic.map.MapUnit
import com.unciv.models.Counter
import com.unciv.models.Religion
import com.unciv.models.ruleset.Belief
import com.unciv.models.ruleset.BeliefType
import com.unciv.ui.pickerscreens.BeliefContainer
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.ui.utils.toPercent
import kotlin.random.Random
@ -204,10 +205,23 @@ class ReligionManager {
civInfo.religionManager.foundingCityId = prophet.getTile().getCity()!!.id
}
fun getBeliefsToChooseAtFounding(): BeliefContainer {
fun getBeliefsToChooseAtFounding(): Counter<BeliefType> {
val beliefsToChoose: Counter<BeliefType> = Counter()
beliefsToChoose.add(BeliefType.Founder, 1)
beliefsToChoose.add(BeliefType.Follower, 1)
if (shouldChoosePantheonBelief)
return BeliefContainer(pantheonBeliefCount = 1, founderBeliefCount = 1, followerBeliefCount = 1)
return BeliefContainer(founderBeliefCount = 1, followerBeliefCount = 1)
beliefsToChoose.add(BeliefType.Pantheon, 1)
for (unique in civInfo.getMatchingUniques(UniqueType.FreeExtraBeliefs)) {
if (unique.params[2] != "founding") continue
beliefsToChoose.add(BeliefType.valueOf(unique.params[1]), unique.params[0].toInt())
}
for (unique in civInfo.getMatchingUniques(UniqueType.FreeExtraAnyBeliefs)) {
if (unique.params[1] != "founding") continue
beliefsToChoose.add(BeliefType.Any, unique.params[0].toInt())
}
return beliefsToChoose
}
fun chooseBeliefs(iconName: String?, religionName: String?, beliefs: List<Belief>) {
@ -288,8 +302,21 @@ class ReligionManager {
religionState = ReligionState.EnhancingReligion
}
fun getBeliefsToChooseAtEnhancing(): BeliefContainer {
return BeliefContainer(followerBeliefCount = 1, enhancerBeliefCount = 1)
fun getBeliefsToChooseAtEnhancing(): Counter<BeliefType> {
val beliefsToChoose: Counter<BeliefType> = Counter()
beliefsToChoose.add(BeliefType.Follower, 1)
beliefsToChoose.add(BeliefType.Enhancer, 1)
for (unique in civInfo.getMatchingUniques(UniqueType.FreeExtraBeliefs)) {
if (unique.params[2] != "enhancing") continue
beliefsToChoose.add(BeliefType.valueOf(unique.params[1]), unique.params[0].toInt())
}
for (unique in civInfo.getMatchingUniques(UniqueType.FreeExtraAnyBeliefs)) {
if (unique.params[1] != "enhancing") continue
beliefsToChoose.add(BeliefType.Any, unique.params[0].toInt())
}
return beliefsToChoose
}
fun enhanceReligion(beliefs: List<Belief>) {

View File

@ -71,7 +71,8 @@ class Belief : RulesetObject() {
enum class BeliefType(val color: String) {
None(""),
Pantheon("#44c6cc"),
Follower("#ccaa44"),
Founder("#c00000"),
Enhancer("#72cc45")
Follower("#ccaa44"),
Enhancer("#72cc45"),
Any(""),
}

View File

@ -81,6 +81,9 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
ProvidesFreeBuildings("Provides a free [buildingName] [cityFilter]", UniqueTarget.Global),
GainFreeBuildings("Gain a free [buildingName] [cityFilter]", UniqueTarget.Global),
FreeExtraBeliefs("May choose [amount] additional [beliefType] beliefs when [foundingOrEnhancing] a religion", UniqueTarget.Global),
FreeExtraAnyBeliefs("May choose [amount] additional of any type when [foundingOrEnhancing] a religion", UniqueTarget.Global),
// I don't like the fact that currently "city state bonuses" are separate from the "global bonuses",
// todo: merge city state bonuses into global bonuses
CityStateStatsPerTurn("Provides [stats] per turn", UniqueTarget.CityState), // Should not be Happiness!

View File

@ -7,6 +7,7 @@ import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.Counter
import com.unciv.models.Religion
import com.unciv.models.UncivSound
import com.unciv.models.ruleset.Belief
@ -17,7 +18,7 @@ import com.unciv.ui.utils.*
class ReligiousBeliefsPickerScreen (
private val choosingCiv: CivilizationInfo,
private val gameInfo: GameInfo,
private val beliefsContainer: BeliefContainer,
private val beliefsToChoose: Counter<BeliefType>,
private val pickIconAndName: Boolean
): PickerScreen(disableScroll = true) {
@ -31,6 +32,8 @@ class ReligiousBeliefsPickerScreen (
private var previouslySelectedIcon: Button? = null
private var displayName: String? = null
private var religionName: String? = null
private val chosenBeliefs: Array<Belief?> = Array(beliefsToChoose.values.sum()) { null }
init {
closeButton.isVisible = true
@ -52,14 +55,14 @@ class ReligiousBeliefsPickerScreen (
if (pickIconAndName) rightSideButton.label = "Choose a religion".toLabel()
else rightSideButton.label = "Enhance [${choosingCiv.religionManager.religion!!.getReligionDisplayName()}]".toLabel()
rightSideButton.onClick(UncivSound.Choir) {
choosingCiv.religionManager.chooseBeliefs(displayName, religionName, beliefsContainer.chosenBeliefs.map { it!! })
choosingCiv.religionManager.chooseBeliefs(displayName, religionName, chosenBeliefs.map { it!! })
UncivGame.Current.setWorldScreen()
}
}
private fun checkAndEnableRightSideButton() {
if (pickIconAndName && (religionName == null || displayName == null)) return
if (beliefsContainer.chosenBeliefs.any { it == null }) return
if (chosenBeliefs.any { it == null }) return
rightSideButton.enable()
}
@ -157,8 +160,8 @@ class ReligiousBeliefsPickerScreen (
beliefButton.disable()
}
for (newBelief in beliefsContainer.chosenBeliefs.withIndex()) {
addChoosableBeliefButton(newBelief, beliefsContainer.getBeliefTypeFromIndex(newBelief.index))
for (newBelief in chosenBeliefs.withIndex()) {
addChoosableBeliefButton(newBelief, getBeliefTypeFromIndex(newBelief.index))
}
}
@ -166,16 +169,16 @@ class ReligiousBeliefsPickerScreen (
rightBeliefsToChoose.clear()
val availableBeliefs = gameInfo.ruleSet.beliefs.values
.filter {
it.type == beliefType
(it.type == beliefType || beliefType == BeliefType.Any)
&& gameInfo.religions.values.none {
religion -> religion.hasBelief(it.name)
}
&& (it !in beliefsContainer.chosenBeliefs)
&& (it !in chosenBeliefs)
}
for (belief in availableBeliefs) {
val beliefButton = convertBeliefToButton(belief)
beliefButton.onClick {
beliefsContainer.chosenBeliefs[leftButtonIndex] = belief
chosenBeliefs[leftButtonIndex] = belief
updateLeftTable()
checkAndEnableRightSideButton()
}
@ -204,22 +207,20 @@ class ReligiousBeliefsPickerScreen (
private fun emptyBeliefButton(beliefType: BeliefType): Button {
val contentsTable = Table()
contentsTable.add("Choose a [${beliefType.name}] belief!".toLabel())
if (beliefType != BeliefType.Any)
contentsTable.add("Choose a [${beliefType.name}] belief!".toLabel())
else
contentsTable.add("Choose any belief!".toLabel())
return Button(contentsTable, skin)
}
}
data class BeliefContainer(val pantheonBeliefCount: Int = 0, val founderBeliefCount: Int = 0, val followerBeliefCount: Int = 0, val enhancerBeliefCount: Int = 0) {
val chosenBeliefs: Array<Belief?> = Array(pantheonBeliefCount + founderBeliefCount + followerBeliefCount + enhancerBeliefCount) { null }
fun getBeliefTypeFromIndex(index: Int): BeliefType {
private fun getBeliefTypeFromIndex(index: Int): BeliefType {
return when {
index < pantheonBeliefCount -> BeliefType.Pantheon
index < pantheonBeliefCount + founderBeliefCount -> BeliefType.Founder
index < pantheonBeliefCount + founderBeliefCount + followerBeliefCount -> BeliefType.Follower
else -> BeliefType.Enhancer
index < beliefsToChoose.filter { it.key <= BeliefType.Pantheon }.values.sum() -> BeliefType.Pantheon
index < beliefsToChoose.filter { it.key <= BeliefType.Founder }.values.sum() -> BeliefType.Founder
index < beliefsToChoose.filter { it.key <= BeliefType.Follower }.values.sum() -> BeliefType.Follower
index < beliefsToChoose.filter { it.key <= BeliefType.Enhancer }.values.sum() -> BeliefType.Enhancer
else -> BeliefType.Any
}
}
}
}

View File

@ -43,6 +43,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Fishing Vessel](https://thenounproject.com/term/fishing-vessel/23815/) By Luis Prado for Work Boats
* [Greek Trireme](https://thenounproject.com/search/?q=ancient%20boat&i=1626303) By Zachary McCune for Trireme
* [Greek Trireme](https://thenounproject.com/search/?q=ancient%20boat&i=1626303) By Zachary McCune for Quinquereme. The original work has been modified.
* [dragon](https://thenounproject.com/search/?q=dragon&i=1646681) by BGBOXXX Design for Dromon
* [Chariot](https://thenounproject.com/search/?q=Chariot&i=1189930) By Andrew Doane for Chariot Archer
* [Elephant](https://thenounproject.com/Luis/uploads/?i=14048) By Luis Prado for War Elephant
* [Centaur](https://thenounproject.com/search/?q=horse+archer&i=1791296) by Michael Wohlwend for Horse Archer
@ -64,8 +65,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Horse](https://thenounproject.com/search/?q=Horse&i=1373793) By AFY Studio for Horseman
* [Horse Head](https://thenounproject.com/search/?q=Cavalry&i=374037) By Juan Pablo Bravo for Companion Cavalry
* [Elephant](https://thenounproject.com/term/elephant/1302749) By Angriawan Ditya Zulkarnain for African Forest Elephant. The original work has been modified.
* [Judge](https://thenounproject.com/search/?q=judge&i=1076388) By Krisztián Mátyás for Courthouse
* [Petra](https://thenounproject.com/search/?q=petra&i=2855893) By Ranah Pixel Studio for Petra
* [Horse](https://thenounproject.com/search/?q=horse&i=3270758) By Ranah Pixel Studio for Cataphract
### Medieval Era
@ -244,6 +244,8 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Canoe](https://thenounproject.com/term/canoe/402285/) By Viktor Fedyuk (Tim P) for Floating Gardens
* [Arc de Triomphe](https://thenounproject.com/search/?q=Arc+de+Triomphe&i=789893) By Andrejs Kirma, LV for National Epic
* [Bank](https://thenounproject.com/term/bank/213472/) by By P Thanga Vignesh for National Treasury
* [Judge](https://thenounproject.com/search/?q=judge&i=1076388) By Krisztián Mátyás for Courthouse
* [Petra](https://thenounproject.com/search/?q=petra&i=2855893) By Ranah Pixel Studio for Petra
### Medieval Era
@ -556,6 +558,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Three Crowns](https://thenounproject.com/search/?q=three+crowns&i=1155972) by Daniel Falk for Sweden
* [Flag of Austria](https://thenounproject.com/term/flag-of-austria/3292053/) by Olena Panasovska, UA for Austria. The original work has been modified.
* [Elephant](https://thenounproject.com/term/elephant/564421/) by Hea Poh Lin for Carthage. The original work has been modified.
* [Orthodox Cross](https://thenounproject.com/search/?q=orthodox&i=2069822) by Avana Vana for Byzantium
## Promotions