Implemented Inquisitors (#4909)

* Added inquisitor unit including image

* Inquisitor now blocks spreading of religions

* Added 'remove heresy' action

* Fixed tests

* Reworded remove heresy unique, updated sprites

* Fix Crash

* Implemented requested changes & fixed a few minor bugs

* Implemented requested changes
This commit is contained in:
Xander Lenstra 2021-08-23 19:26:37 +02:00 committed by GitHub
parent df1695f782
commit 89ea30af95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 960 additions and 841 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -354,422 +354,429 @@ Infantry
orig: 100, 100
offset: 0, 0
index: -1
Ironclad
Inquisitor
rotate: false
xy: 760, 646
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Jaguar
Ironclad
rotate: false
xy: 868, 754
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Janissary
Jaguar
rotate: false
xy: 976, 862
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Jet Fighter
Janissary
rotate: false
xy: 328, 106
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Keshik
Jet Fighter
rotate: false
xy: 436, 214
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Khan
Keshik
rotate: false
xy: 544, 322
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Knight
Khan
rotate: false
xy: 652, 430
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Lancer
Knight
rotate: false
xy: 760, 538
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Landship
Lancer
rotate: false
xy: 868, 646
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Landsknecht
Landship
rotate: false
xy: 976, 754
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Legion
Landsknecht
rotate: false
xy: 1084, 862
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Longbowman
Legion
rotate: false
xy: 436, 106
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Longswordsman
Longbowman
rotate: false
xy: 544, 214
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Machine Gun
Longswordsman
rotate: false
xy: 652, 322
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Mandekalu Cavalry
Machine Gun
rotate: false
xy: 760, 430
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Maori Warrior
Mandekalu Cavalry
rotate: false
xy: 868, 538
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Marine
Maori Warrior
rotate: false
xy: 976, 646
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Mechanized Infantry
Marine
rotate: false
xy: 1084, 754
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Minuteman
Mechanized Infantry
rotate: false
xy: 1192, 862
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Missile Cruiser
Minuteman
rotate: false
xy: 544, 106
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Missionary
Missile Cruiser
rotate: false
xy: 652, 214
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Mobile SAM
Missionary
rotate: false
xy: 760, 322
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Modern Armor
Mobile SAM
rotate: false
xy: 868, 430
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Mohawk Warrior
Modern Armor
rotate: false
xy: 976, 538
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Musketeer
Mohawk Warrior
rotate: false
xy: 1084, 646
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Musketeer
rotate: false
xy: 1192, 754
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Musketman
rotate: false
xy: 1192, 755
xy: 1300, 863
size: 100, 99
orig: 100, 99
offset: 0, 0
index: -1
Naresuan's Elephant
rotate: false
xy: 1300, 862
xy: 652, 106
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Norwegian Ski Infantry
rotate: false
xy: 652, 106
xy: 760, 214
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Nuclear Missile
rotate: false
xy: 760, 214
xy: 868, 322
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Nuclear Submarine
rotate: false
xy: 868, 322
xy: 976, 430
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Panzer
rotate: false
xy: 976, 430
xy: 1084, 538
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Paratrooper
rotate: false
xy: 1084, 538
xy: 1192, 646
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Persian Immortal
rotate: false
xy: 1192, 647
xy: 1300, 755
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Pikeman
rotate: false
xy: 1300, 754
xy: 1408, 862
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Privateer
rotate: false
xy: 1408, 862
xy: 760, 106
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Rifleman
rotate: false
xy: 760, 106
xy: 868, 214
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Rocket Artillery
rotate: false
xy: 868, 214
xy: 976, 322
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Samurai
rotate: false
xy: 976, 322
xy: 1084, 430
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Scout
rotate: false
xy: 1084, 430
xy: 1192, 538
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Sea Beggar
rotate: false
xy: 1192, 539
xy: 1300, 647
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Settler
rotate: false
xy: 1300, 646
xy: 1408, 754
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Ship of the Line
rotate: false
xy: 1408, 754
xy: 1516, 862
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Sipahi
rotate: false
xy: 1516, 862
xy: 868, 106
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Slinger
rotate: false
xy: 868, 106
xy: 976, 214
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Spearman
rotate: false
xy: 976, 214
xy: 1084, 322
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Stealth Bomber
rotate: false
xy: 1084, 322
xy: 1192, 430
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Submarine
rotate: false
xy: 1192, 431
xy: 1300, 539
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Swordsman
rotate: false
xy: 1300, 538
xy: 1408, 646
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Tank
rotate: false
xy: 1408, 646
xy: 1516, 754
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Tercio
rotate: false
xy: 1516, 754
xy: 1624, 862
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Trebuchet
rotate: false
xy: 1624, 862
xy: 976, 106
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Triplane
rotate: false
xy: 976, 106
xy: 1084, 214
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Trireme
rotate: false
xy: 1084, 213
xy: 1192, 321
size: 100, 101
orig: 100, 101
offset: 0, 0
index: -1
Turtle Ship
rotate: false
xy: 1192, 323
xy: 1300, 431
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
War Chariot
rotate: false
xy: 1300, 430
xy: 1408, 538
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
War Elephant
rotate: false
xy: 1408, 538
xy: 1516, 646
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Warrior
rotate: false
xy: 1516, 646
xy: 1624, 754
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Work Boats
rotate: false
xy: 1624, 754
xy: 1732, 862
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Worker
rotate: false
xy: 1732, 862
xy: 1084, 106
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Zero
rotate: false
xy: 1192, 215
xy: 1192, 213
size: 100, 100
orig: 100, 100
offset: 0, 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

After

Width:  |  Height:  |  Size: 346 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
android/assets/game2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1500,7 +1500,7 @@
{
"name": "Great Prophet",
"unitType": "Civilian",
"uniques": ["Can construct [Holy site] if it hasn't spread religion yet", "Can spread religion [4] times",
"uniques": ["Can construct [Holy site] if it hasn't used other actions yet", "Can [Spread Religion] [4] times",
"May found a religion", "May enter foreign tiles without open borders", "[-1] Visibility Range",
"Great Person - [Faith]", "Unbuildable", "Religious Unit", "Hidden when religion is disabled"],
"movement": 2,
@ -1528,11 +1528,22 @@
{
"name": "Missionary",
"unitType": "Civilian",
"uniques": ["Can spread religion [2] times", "May enter foreign tiles without open borders, but loses [250] religious strength each turn it ends there",
"uniques": ["Can [Spread Religion] [2] times", "May enter foreign tiles without open borders, but loses [250] religious strength each turn it ends there",
"Can be purchased with [Faith] [in all cities in which the majority religion is a major religion]",
"[-1] Visibility Range", "Unbuildable", "Religious Unit", "Hidden when religion is disabled"],
"movement": 4,
"religiousStrength": 1000
},
{
"name": "Inquisitor",
"unitType": "Civilian",
"uniques": ["Prevents spreading of religion to the city it is next to",
// ToDo: change this to an _enhanced_ religion when that is implemented
"Can [Remove Foreign religions from your own cities] [1] times",
"Can be purchased with [Faith] [in all cities in which the majority religion is a major religion]",
"[+1] Visibility Range", "Hidden when religion is disabled", "Unbuildable", "Religious Unit"
],
"movement": 3,
}
/* Spaceship Parts */

View File

@ -826,9 +826,10 @@ Your trade mission to [civName] has earned you [goldAmount] gold and [influenceA
Hurry Wonder = Versnel Wonder
Hurry Construction = Versnel Constructie
Spread Religion = Verkondig Religie
Remove Heresy = Verwijder Heidense Denkbeelden
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.
Found a Religion = Richt een religie op
Your citizens have been happy with your rule for so long that the empire enters a Golden Age! = Je inwoners zijn al zo lang blij met jouw leiderschap, dat jouw rijk een Gouden Eeuw binnengaat!
You have entered the [newEra]! = Jij bent in het/de [newEra] aangekomen!
[civName] has entered the [eraName]! = [civName] is aangekomen in de [eraName]!
[policyBranch] policy branch unlocked! = Het/De [policyBranch] beleid is ontgrendeld!
@ -843,7 +844,7 @@ Buildings = Gebouwen
# terrainFilters (so for uniques like: "[stats] from [terrainFilter] tiles")
# Requires translation!
All =
All = Alle
Water = Water
Land = Land
# Requires translation!

View File

@ -735,6 +735,7 @@ Hurry Construction =
Hurry Construction (+[productionAmount]) =
Spread Religion =
Spread [religionName] =
Remove Heresy =
Found a Religion =
Your citizens have been happy with your rule for so long that the empire enters a Golden Age! =
You have entered the [newEra]! =

Binary file not shown.

View File

@ -9,6 +9,8 @@ object Constants {
const val settler = "Settler"
const val settlerUnique = "Founds a new city"
const val eraSpecificUnit = "Era Starting Unit"
const val spreadReligionAbilityCount = "Spread Religion"
const val removeHeresyAbilityCount = "Remove Foreign religions from your own cities"
const val hiddenWithoutReligionUnique = "Hidden when religion is disabled"
const val hideFromCivilopediaUnique = "Will not be displayed in Civilopedia"

View File

@ -444,13 +444,6 @@ object Battle {
}
private fun captureCivilianUnit(attacker: ICombatant, defender: MapUnitCombatant, checkDefeat: Boolean = true) {
// barbarians don't capture civilians
if (attacker.getCivInfo().isBarbarian()
|| defender.unit.hasUnique("Uncapturable")) {
defender.takeDamage(100)
return
}
// need to save this because if the unit is captured its owner wil be overwritten
val defenderCiv = defender.getCivInfo()
@ -460,21 +453,28 @@ object Battle {
val capturedUnitTile = capturedUnit.getTile()
// Apparently in Civ V, captured settlers are converted to workers.
if (capturedUnit.name == Constants.settler) {
capturedUnit.destroy()
// This is so that future checks which check if a unit has been captured are caught give the right answer
// For example, in postBattleMoveToAttackedTile
capturedUnit.civInfo = attacker.getCivInfo()
attacker.getCivInfo().placeUnitNearTile(capturedUnitTile.position, Constants.worker)
} else {
capturedUnit.civInfo.removeUnit(capturedUnit)
capturedUnit.assignOwner(attacker.getCivInfo())
capturedUnit.currentMovement = 0f
// It's possible that the unit can no longer stand on the tile it was captured on.
// For example, because it's embarked and the capturing civ cannot embark units yet.
if (!capturedUnit.movement.canPassThrough(capturedUnitTile)) {
capturedUnit.movement.teleportToClosestMoveableTile()
when {
// Uncapturable units are destroyed (units captured by barbarians also - for now)
defender.unit.hasUnique("Uncapturable") || attacker.getCivInfo().isBarbarian() -> {
capturedUnit.destroy()
}
// Captured settlers are converted to workers.
capturedUnit.name == Constants.settler -> {
capturedUnit.destroy()
// This is so that future checks which check if a unit has been captured are caught give the right answer
// For example, in postBattleMoveToAttackedTile
capturedUnit.civInfo = attacker.getCivInfo()
attacker.getCivInfo().placeUnitNearTile(capturedUnitTile.position, Constants.worker)
}
else -> {
capturedUnit.civInfo.removeUnit(capturedUnit)
capturedUnit.assignOwner(attacker.getCivInfo())
capturedUnit.currentMovement = 0f
// It's possible that the unit can no longer stand on the tile it was captured on.
// For example, because it's embarked and the capturing civ cannot embark units yet.
if (!capturedUnit.movement.canPassThrough(capturedUnitTile)) {
capturedUnit.movement.teleportToClosestMoveableTile()
}
}
}

View File

@ -683,7 +683,6 @@ class CityInfo {
}
fun getImprovableTiles(): Sequence<TileInfo> = getTiles()
.filter {it.hasViewableResource(civInfo) && it.improvement == null}
//endregion
}

View File

@ -16,7 +16,7 @@ class CityInfoReligionManager {
val religionsAtSomePointAdopted: HashSet<String> = hashSetOf()
private val pressures: Counter<String> = Counter()
// `getNumberOfFollowers()` was called a surprisingly large amount of time, so caching it feels useful
// Cached because using `updateNumberOfFollowers` to get this value resulted in many calls
@Transient
private val followers: Counter<String> = Counter()
@ -68,6 +68,10 @@ class CityInfoReligionManager {
return getUniques().filter { it.placeholderText == unique }
}
fun getPressures(): Counter<String> {
return pressures.clone()
}
fun clearAllPressures() {
pressures.clear()
// We add pressure for following no religion
@ -85,6 +89,13 @@ class CityInfoReligionManager {
updateNumberOfFollowers(shouldUpdateFollowers)
}
}
fun removeAllPressuresExceptFor(religion: String) {
val pressureFromThisReligion = pressures[religion]!!
clearAllPressures()
pressures.add(religion, pressureFromThisReligion)
updateNumberOfFollowers()
}
fun updatePressureOnPopulationChange(populationChangeAmount: Int) {
val majorityReligion =
@ -204,9 +215,12 @@ class CityInfoReligionManager {
fun getMajorityReligionName(): String? {
if (followers.isEmpty()) return null
val religionWithMaxFollowers = followers.maxByOrNull { it.value }!!
return if (religionWithMaxFollowers.value >= cityInfo.population.population / 2) religionWithMaxFollowers.key
else null
val religionWithMaxPressure = pressures.maxByOrNull { it.value }!!.key
return when {
religionWithMaxPressure == Constants.noReligionName -> null
followers[religionWithMaxPressure]!! >= cityInfo.population.population / 2 -> religionWithMaxPressure
else -> null
}
}
fun getMajorityReligion(): Religion? {

View File

@ -1,6 +1,7 @@
package com.unciv.logic.civilization
import com.badlogic.gdx.math.Vector2
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.UncivShowableException
@ -803,8 +804,6 @@ class CivilizationInfo {
placedUnit.religion =
if (city != null) city.cityConstructions.cityInfo.religion.getMajorityReligionName()
else religionManager.religion?.name
if (placedUnit.hasUnique("Can spread religion [] times"))
placedUnit.abilityUsedCount["Religion Spread"] = 0
}
return placedUnit

View File

@ -1,5 +1,6 @@
package com.unciv.logic.civilization
import com.unciv.Constants
import com.unciv.logic.map.MapUnit
import com.unciv.models.Religion
import com.unciv.models.ruleset.Belief
@ -125,7 +126,7 @@ class ReligionManager {
fun mayFoundReligionAtAll(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
if (prophet.abilityUsedCount.any { it.value != 0 }) return false // Already used its power for other things
if (!civInfo.isMajorCiv()) return false // Only major civs may use religion
val foundedReligionsCount = civInfo.gameInfo.civilizations.count {

View File

@ -446,6 +446,14 @@ class MapUnit {
promotions.setTransients(this)
baseUnit = ruleset.units[name]
?: throw java.lang.Exception("Unit $name is not found!")
// "Religion Spread" ability deprecated since 3.16.7, replaced with "Spread Religion"
if ("Religion Spread" in abilityUsedCount) {
abilityUsedCount[Constants.spreadReligionAbilityCount] = abilityUsedCount["Religion Spread"]!!
abilityUsedCount.remove("Religion Spread")
}
//
updateUniques()
}
@ -955,22 +963,29 @@ class MapUnit {
return matchingUniques.any { improvement.matchesFilter(it.params[0]) || tile.matchesTerrainFilter(it.params[0]) }
}
fun maxReligionSpreads(): Int {
return getMatchingUniques("Can spread religion [] times").sumBy { it.params[0].toInt() }
fun religiousActionsUnitCanDo(): Sequence<String> {
return getMatchingUniques("Can [] [] times")
.map { it.params[0] }
}
fun canSpreadReligion(): Boolean {
return hasUnique("Can spread religion [] times")
fun canDoReligiousAction(action: String): Boolean {
return getMatchingUniques("Can [] [] times").any { it.params[0] == action }
}
fun getMaxReligiousActionUses(action: String): Int {
return getMatchingUniques("Can [] [] times")
.filter { it.params[0] == action }
.sumBy { it.params[1].toInt() }
}
fun getPressureAddedFromSpread(): Int {
return baseUnit.religiousStrength
}
fun getReligionString(): String {
val maxSpreads = maxReligionSpreads()
if (abilityUsedCount["Religion Spread"] == null) return "" // That is, either the key doesn't exist, or it does exist and the value is null.
return "${maxSpreads - abilityUsedCount["Religion Spread"]!!}/${maxSpreads}"
fun getActionString(action: String): String {
val maxActionUses = getMaxReligiousActionUses(action)
if (abilityUsedCount[action] == null) return "0/0" // Something went wrong
return "${maxActionUses - abilityUsedCount[action]!!}/${maxActionUses}"
}
fun actionsOnDeselect() {

View File

@ -436,6 +436,11 @@ class TileMap {
unit.promotions.addPromotion(unique.params[1], true)
}
}
// If this unit has special abilities that need to be kept track of, start doing so here
for (action in unit.religiousActionsUnitCanDo()) {
unit.abilityUsedCount[action] = 0
}
// And update civ stats, since the new unit changes both unit upkeep and resource consumption
civInfo.updateStatsForNextTurn()

View File

@ -9,6 +9,7 @@ private enum class UncivSoundConstants (val value: String) {
Chimes("chimes"),
Coin("coin"),
Choir("choir"),
Fire("fire"),
Policy("policy"),
Paper("paper"),
Whoosh("whoosh"),
@ -57,6 +58,7 @@ class UncivSound private constructor (
val Construction = UncivSound(UncivSoundConstants.Construction)
val Swap = UncivSound(UncivSoundConstants.Swap)
val Silent = UncivSound(UncivSoundConstants.Silent)
val Fire = UncivSound(UncivSoundConstants.Fire)
/** Creates an UncivSound instance for a custom sound.
* @param filename The base filename without extension.
*/

View File

@ -114,8 +114,6 @@ enum class UnitActionType(
//
Create("Create",
null, 'i', UncivSound.Chimes),
SpreadReligion("Spread Religion",
null, 'g', UncivSound.Choir),
HurryResearch("Hurry Research",
{ ImageGetter.getUnitIcon("Great Scientist") }, 'g', UncivSound.Chimes),
StartGoldenAge("Start Golden Age",
@ -128,6 +126,10 @@ enum class UnitActionType(
{ ImageGetter.getUnitIcon("Great Merchant") }, 'g', UncivSound.Chimes),
FoundReligion("Found a Religion",
{ ImageGetter.getUnitIcon("Great Prophet") }, 'g', UncivSound.Choir),
SpreadReligion("Spread Religion",
null, 'g', UncivSound.Choir),
RemoveHeresy("Remove Heresy",
{ ImageGetter.getImage("OtherIcons/Remove Heresy") }, 'h', UncivSound.Fire),
DisbandUnit("Disband unit",
{ ImageGetter.getImage("OtherIcons/DisbandUnit") }, KeyCharAndCode.DEL),
GiftUnit("Gift unit",

View File

@ -330,8 +330,6 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
if (unit.hasUnique("Religious Unit")) {
unit.religion = cityConstructions.cityInfo.religion.getMajorityReligionName()
if (unit.canSpreadReligion())
unit.abilityUsedCount["Religion Spread"] = 0
}
if (this.isCivilian()) return true // tiny optimization makes save files a few bytes smaller
@ -340,12 +338,10 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
for (unique in
// Deprecated since 3.15.9
cityConstructions.cityInfo.getMatchingUniques("New [] units start with [] Experience") +
//
cityConstructions.cityInfo.getMatchingUniques("New [] units start with [] Experience []")
.filter { cityConstructions.cityInfo.matchesFilter(it.params[2]) } +
// Deprecated since 3.15.9
cityConstructions.cityInfo.getMatchingUniques("New [] units start with [] Experience") +
cityConstructions.cityInfo.getLocalMatchingUniques("New [] units start with [] Experience in this city")
//
) {

View File

@ -83,7 +83,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
}
private fun addReligionInfo() {
val label = cityInfo.religion.getMajorityReligionName()
val label = cityInfo.religion.getMajorityReligion()?.iconName
?: "None"
val icon = if (label == "None") "Religion" else label
val expanderTab =
@ -97,7 +97,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
onChange = {
pack()
// We have to re-anchor as our position in the city screen, otherwise it expands upwards.
// This probably should be refactored so its placed somewhere else in due time
// ToDo: This probably should be refactored so its placed somewhere else in due time
setPosition(stage.width - CityScreen.posFromEdge, stage.height - CityScreen.posFromEdge, Align.topRight)
}
) {

View File

@ -4,6 +4,7 @@ import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.automation.WorkerAutomation
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PlayerType
@ -56,9 +57,9 @@ object UnitActions {
addGreatPersonActions(unit, actionList, tile)
addFoundReligionAction(unit, actionList, tile)
actionList += getImprovementConstructionActions(unit, tile)
addSpreadReligionActions(unit, actionList, tile)
addActionsWithLimitedUses(unit, actionList, tile)
addToggleActionsAction(unit, actionList, unitTable)
return actionList
@ -374,6 +375,7 @@ object UnitActions {
private fun addAutomateBuildingImprovementsAction(unit: MapUnit, actionList: ArrayList<UnitAction>) {
if (!unit.hasUniqueToBuildImprovements) return
if (unit.isAutomated()) return
actionList += UnitAction(UnitActionType.Automate,
isCurrentAction = unit.isAutomated(),
@ -489,37 +491,76 @@ object UnitActions {
}.takeIf { unit.civInfo.religionManager.mayFoundReligionNow(unit) }
)
}
private fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
if (!unit.hasUnique("Can spread religion [] times")) return
private fun addActionsWithLimitedUses(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
val actionsToAdd = unit.religiousActionsUnitCanDo()
if (actionsToAdd.none()) return
if (unit.religion == null || unit.civInfo.gameInfo.religions[unit.religion]!!.isPantheon()) return
val maxReligionSpreads = unit.maxReligionSpreads()
if (!unit.abilityUsedCount.containsKey("Religion Spread")) return // This should be impossible anyways, but just in case
if (maxReligionSpreads <= unit.abilityUsedCount["Religion Spread"]!!) return
val city = tile.getCity() ?: return
for (action in actionsToAdd) {
if (!unit.abilityUsedCount.containsKey(action)) continue
val maxActionUses = unit.getMaxReligiousActionUses(action)
if (maxActionUses <= unit.abilityUsedCount[action]!!) continue
when (action) {
Constants.spreadReligionAbilityCount -> addSpreadReligionActions(unit, actionList, city, maxActionUses)
Constants.removeHeresyAbilityCount -> addRemoveHeresyActions(unit, actionList, city, maxActionUses)
}
}
}
private fun useActionWithLimitedUses(unit: MapUnit, action: String, maximumUses: Int) {
unit.abilityUsedCount[action] = unit.abilityUsedCount[action]!! + 1
if (unit.abilityUsedCount[action] == maximumUses) {
if (unit.isGreatPerson())
addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy()
}
}
private fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, city: CityInfo, maxSpreadUses: Int) {
val blockedByInquisitor =
city.getCenterTile()
.getTilesInDistance(1)
.flatMap { it.getUnits() }
.any {
it.hasUnique("Prevents spreading of religion to the city it is next to")
&& it.religion != unit.religion
}
actionList += UnitAction(UnitActionType.SpreadReligion,
title = "Spread [${unit.religion!!}]",
action = {
unit.abilityUsedCount["Religion Spread"] = unit.abilityUsedCount["Religion Spread"]!! + 1
val followersOfOtherReligions = city.religion.getFollowersOfOtherReligionsThan(unit.religion!!)
for (unique in unit.civInfo.getMatchingUniques("When spreading religion to a city, gain [] times the amount of followers of other religions as []")) {
unit.civInfo.addStat(Stat.valueOf(unique.params[1]), followersOfOtherReligions * unique.params[0].toInt())
}
city.religion.addPressure(unit.religion!!, unit.getPressureAddedFromSpread())
unit.currentMovement = 0f
if (unit.abilityUsedCount["Religion Spread"] == maxReligionSpreads) {
addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy()
}
}.takeIf { unit.currentMovement > 0 }
useActionWithLimitedUses(unit, Constants.spreadReligionAbilityCount, maxSpreadUses)
}.takeIf { unit.currentMovement > 0 && !blockedByInquisitor }
)
}
private fun addRemoveHeresyActions(unit: MapUnit, actionList: ArrayList<UnitAction>, city: CityInfo, maxHerseyUses: Int) {
if (city.civInfo != unit.civInfo) return
// Only allow the action if the city actually has any foreign religion
// This will almost be always due to pressure from cities close-by
if (city.religion.getPressures().none { it.key != unit.religion!! }) return
actionList += UnitAction(UnitActionType.RemoveHeresy,
title = "Remove Heresy",
action = {
city.religion.removeAllPressuresExceptFor(unit.religion!!)
unit.currentMovement = 0f
useActionWithLimitedUses(unit, Constants.removeHeresyAbilityCount, maxHerseyUses)
}.takeIf { unit.currentMovement > 0f }
)
}
fun getImprovementConstructionActions(unit: MapUnit, tile: TileInfo): ArrayList<UnitAction> {
val finalActions = ArrayList<UnitAction>()
var uniquesToCheck = unit.getMatchingUniques("Can construct []")
if (unit.abilityUsedCount.containsKey("Religion Spread") && unit.abilityUsedCount["Religion Spread"]!! == 0 && unit.canSpreadReligion())
uniquesToCheck += unit.getMatchingUniques("Can construct [] if it hasn't spread religion yet")
if (unit.religiousActionsUnitCanDo().all { unit.abilityUsedCount[it] == 0 })
uniquesToCheck += unit.getMatchingUniques("Can construct [] if it hasn't used other actions yet")
for (unique in uniquesToCheck) {
val improvementName = unique.params[0]
val improvement = tile.ruleset.tileImprovements[improvementName]

View File

@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.battle.CityCombatant
import com.unciv.logic.city.CityInfo
@ -163,9 +164,14 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
unitDescriptionTable.add(unit.promotions.XP.toString() + "/" + unit.promotions.xpForNextPromotion())
}
if (unit.canSpreadReligion()) {
if (unit.canDoReligiousAction(Constants.spreadReligionAbilityCount)) {
unitDescriptionTable.add(ImageGetter.getStatIcon("Faith")).size(20f)
unitDescriptionTable.add(unit.getReligionString())
unitDescriptionTable.add(unit.getActionString(Constants.spreadReligionAbilityCount))
}
if (unit.canDoReligiousAction(Constants.removeHeresyAbilityCount)) {
unitDescriptionTable.add(ImageGetter.getImage("OtherIcons/Remove Heresy")).size(20f)
unitDescriptionTable.add(unit.getActionString(Constants.removeHeresyAbilityCount))
}
if (unit.baseUnit.religiousStrength > 0) {

View File

@ -133,6 +133,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Dove](https://thenounproject.com/search/?q=dove&i=1344088) by sandra for Great Prophet
* [General](https://thenounproject.com/search/?q=general&i=933566) By anbileru adaleru for Great General
* [Religion](https://thenounproject.com/search/?q=preach&i=53064) by Bruno Gätjens González adapted for Missionary
* [invisibility cloak ](https://thenounproject.com/term/invisibility-cloak/1419648/) by Locad for Inquisitor
## Resources
@ -614,6 +615,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Hourglass](https://thenounproject.com/search/?q=hourglass&i=142268) by I Create Stuff for the 'Turn' icon
* [Dove](https://thenounproject.com/search/?q=dove&i=1344084) by Sandra for Faith
* [Shield](https://thenounproject.com/search/?q=shield&i=813568) by Gregor Cresnar for Religious Strength
* [skill sword flame](https://thenounproject.com/term/skill-sword-flame/2360212/) by Maxicons) for Remove Heresy
## Main menu
@ -627,7 +629,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
# Sound credits
Sounds are from FreeSound.org and are either Creative Commons or Public Domain
Sounds are from FreeSound.org unless otherwise noted and are either Creative Commons or Public Domain unless otherwise noted
* [Soft two-fingered snap](https://freesound.org/people/EathanMarkson/sounds/388958/) By EathanMarkson as 'click' for most clicks
* [Pencil1](https://freesound.org/people/stijn/sounds/43673/) By stijn as 'paper' for opening and closing the tech picker
@ -670,6 +672,8 @@ Sounds are from FreeSound.org and are either Creative Commons or Public Domain
* [elephant 44](https://freesound.org/people/y89312/sounds/139875/) by y89312 for Naruesan's Elephant sound
* Excerpt from [Missile Strike](https://freesound.org/people/BaDoink/sounds/570690/) by BaDoink for guided missile
* Excerpt from [Campfire](https://www.soundjay.com/nature/sounds/campfire-1.mp3) by SoundJay for Fire & 'remove heresy' action of inquisitor
# Music
The following music is from https://filmmusic.io
"Thatched Villagers" by Kevin MacLeod (https://incompetech.com)