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 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Ironclad Inquisitor
rotate: false rotate: false
xy: 760, 646 xy: 760, 646
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Jaguar Ironclad
rotate: false rotate: false
xy: 868, 754 xy: 868, 754
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Janissary Jaguar
rotate: false rotate: false
xy: 976, 862 xy: 976, 862
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Jet Fighter Janissary
rotate: false rotate: false
xy: 328, 106 xy: 328, 106
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Keshik Jet Fighter
rotate: false rotate: false
xy: 436, 214 xy: 436, 214
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Khan Keshik
rotate: false rotate: false
xy: 544, 322 xy: 544, 322
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Knight Khan
rotate: false rotate: false
xy: 652, 430 xy: 652, 430
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Lancer Knight
rotate: false rotate: false
xy: 760, 538 xy: 760, 538
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Landship Lancer
rotate: false rotate: false
xy: 868, 646 xy: 868, 646
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Landsknecht Landship
rotate: false rotate: false
xy: 976, 754 xy: 976, 754
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Legion Landsknecht
rotate: false rotate: false
xy: 1084, 862 xy: 1084, 862
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Longbowman Legion
rotate: false rotate: false
xy: 436, 106 xy: 436, 106
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Longswordsman Longbowman
rotate: false rotate: false
xy: 544, 214 xy: 544, 214
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Machine Gun Longswordsman
rotate: false rotate: false
xy: 652, 322 xy: 652, 322
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Mandekalu Cavalry Machine Gun
rotate: false rotate: false
xy: 760, 430 xy: 760, 430
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Maori Warrior Mandekalu Cavalry
rotate: false rotate: false
xy: 868, 538 xy: 868, 538
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Marine Maori Warrior
rotate: false rotate: false
xy: 976, 646 xy: 976, 646
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Mechanized Infantry Marine
rotate: false rotate: false
xy: 1084, 754 xy: 1084, 754
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Minuteman Mechanized Infantry
rotate: false rotate: false
xy: 1192, 862 xy: 1192, 862
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Missile Cruiser Minuteman
rotate: false rotate: false
xy: 544, 106 xy: 544, 106
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Missionary Missile Cruiser
rotate: false rotate: false
xy: 652, 214 xy: 652, 214
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Mobile SAM Missionary
rotate: false rotate: false
xy: 760, 322 xy: 760, 322
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Modern Armor Mobile SAM
rotate: false rotate: false
xy: 868, 430 xy: 868, 430
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Mohawk Warrior Modern Armor
rotate: false rotate: false
xy: 976, 538 xy: 976, 538
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Musketeer Mohawk Warrior
rotate: false rotate: false
xy: 1084, 646 xy: 1084, 646
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Musketeer
rotate: false
xy: 1192, 754
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Musketman Musketman
rotate: false rotate: false
xy: 1192, 755 xy: 1300, 863
size: 100, 99 size: 100, 99
orig: 100, 99 orig: 100, 99
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Naresuan's Elephant Naresuan's Elephant
rotate: false rotate: false
xy: 1300, 862 xy: 652, 106
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Norwegian Ski Infantry Norwegian Ski Infantry
rotate: false rotate: false
xy: 652, 106 xy: 760, 214
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Nuclear Missile Nuclear Missile
rotate: false rotate: false
xy: 760, 214 xy: 868, 322
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Nuclear Submarine Nuclear Submarine
rotate: false rotate: false
xy: 868, 322 xy: 976, 430
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Panzer Panzer
rotate: false rotate: false
xy: 976, 430 xy: 1084, 538
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Paratrooper Paratrooper
rotate: false rotate: false
xy: 1084, 538 xy: 1192, 646
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Persian Immortal Persian Immortal
rotate: false rotate: false
xy: 1192, 647 xy: 1300, 755
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Pikeman Pikeman
rotate: false rotate: false
xy: 1300, 754 xy: 1408, 862
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Privateer Privateer
rotate: false rotate: false
xy: 1408, 862 xy: 760, 106
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Rifleman Rifleman
rotate: false rotate: false
xy: 760, 106 xy: 868, 214
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Rocket Artillery Rocket Artillery
rotate: false rotate: false
xy: 868, 214 xy: 976, 322
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Samurai Samurai
rotate: false rotate: false
xy: 976, 322 xy: 1084, 430
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Scout Scout
rotate: false rotate: false
xy: 1084, 430 xy: 1192, 538
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Sea Beggar Sea Beggar
rotate: false rotate: false
xy: 1192, 539 xy: 1300, 647
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Settler Settler
rotate: false rotate: false
xy: 1300, 646 xy: 1408, 754
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Ship of the Line Ship of the Line
rotate: false rotate: false
xy: 1408, 754 xy: 1516, 862
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Sipahi Sipahi
rotate: false rotate: false
xy: 1516, 862 xy: 868, 106
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Slinger Slinger
rotate: false rotate: false
xy: 868, 106 xy: 976, 214
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Spearman Spearman
rotate: false rotate: false
xy: 976, 214 xy: 1084, 322
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Stealth Bomber Stealth Bomber
rotate: false rotate: false
xy: 1084, 322 xy: 1192, 430
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Submarine Submarine
rotate: false rotate: false
xy: 1192, 431 xy: 1300, 539
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Swordsman Swordsman
rotate: false rotate: false
xy: 1300, 538 xy: 1408, 646
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Tank Tank
rotate: false rotate: false
xy: 1408, 646 xy: 1516, 754
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Tercio Tercio
rotate: false rotate: false
xy: 1516, 754 xy: 1624, 862
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Trebuchet Trebuchet
rotate: false rotate: false
xy: 1624, 862 xy: 976, 106
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Triplane Triplane
rotate: false rotate: false
xy: 976, 106 xy: 1084, 214
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Trireme Trireme
rotate: false rotate: false
xy: 1084, 213 xy: 1192, 321
size: 100, 101 size: 100, 101
orig: 100, 101 orig: 100, 101
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Turtle Ship Turtle Ship
rotate: false rotate: false
xy: 1192, 323 xy: 1300, 431
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
War Chariot War Chariot
rotate: false rotate: false
xy: 1300, 430 xy: 1408, 538
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
War Elephant War Elephant
rotate: false rotate: false
xy: 1408, 538 xy: 1516, 646
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Warrior Warrior
rotate: false rotate: false
xy: 1516, 646 xy: 1624, 754
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Work Boats Work Boats
rotate: false rotate: false
xy: 1624, 754 xy: 1732, 862
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Worker Worker
rotate: false rotate: false
xy: 1732, 862 xy: 1084, 106
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Zero Zero
rotate: false rotate: false
xy: 1192, 215 xy: 1192, 213
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 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", "name": "Great Prophet",
"unitType": "Civilian", "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", "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"], "Great Person - [Faith]", "Unbuildable", "Religious Unit", "Hidden when religion is disabled"],
"movement": 2, "movement": 2,
@ -1528,11 +1528,22 @@
{ {
"name": "Missionary", "name": "Missionary",
"unitType": "Civilian", "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]", "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"], "[-1] Visibility Range", "Unbuildable", "Religious Unit", "Hidden when religion is disabled"],
"movement": 4, "movement": 4,
"religiousStrength": 1000 "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 */ /* 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 Wonder = Versnel Wonder
Hurry Construction = Versnel Constructie Hurry Construction = Versnel Constructie
Spread Religion = Verkondig Religie Spread Religion = Verkondig Religie
Remove Heresy = Verwijder Heidense Denkbeelden
Spread [religionName] = Verkondig [religionName] Spread [religionName] = Verkondig [religionName]
Found a Religion = Begin een religie 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! = 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! = 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! 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]!
[policyBranch] policy branch unlocked! = Het/De [policyBranch] beleid is ontgrendeld! [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") # terrainFilters (so for uniques like: "[stats] from [terrainFilter] tiles")
# Requires translation! # Requires translation!
All = All = Alle
Water = Water Water = Water
Land = Land Land = Land
# Requires translation! # Requires translation!

View File

@ -735,6 +735,7 @@ Hurry Construction =
Hurry Construction (+[productionAmount]) = Hurry Construction (+[productionAmount]) =
Spread Religion = Spread Religion =
Spread [religionName] = Spread [religionName] =
Remove Heresy =
Found a Religion = 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]! =

Binary file not shown.

View File

@ -9,6 +9,8 @@ object Constants {
const val settler = "Settler" const val settler = "Settler"
const val settlerUnique = "Founds a new city" const val settlerUnique = "Founds a new city"
const val eraSpecificUnit = "Era Starting Unit" 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 hiddenWithoutReligionUnique = "Hidden when religion is disabled"
const val hideFromCivilopediaUnique = "Will not be displayed in Civilopedia" 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) { 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 // need to save this because if the unit is captured its owner wil be overwritten
val defenderCiv = defender.getCivInfo() val defenderCiv = defender.getCivInfo()
@ -460,21 +453,28 @@ object Battle {
val capturedUnitTile = capturedUnit.getTile() val capturedUnitTile = capturedUnit.getTile()
// Apparently in Civ V, captured settlers are converted to workers. when {
if (capturedUnit.name == Constants.settler) { // Uncapturable units are destroyed (units captured by barbarians also - for now)
capturedUnit.destroy() defender.unit.hasUnique("Uncapturable") || attacker.getCivInfo().isBarbarian() -> {
// This is so that future checks which check if a unit has been captured are caught give the right answer capturedUnit.destroy()
// For example, in postBattleMoveToAttackedTile }
capturedUnit.civInfo = attacker.getCivInfo() // Captured settlers are converted to workers.
attacker.getCivInfo().placeUnitNearTile(capturedUnitTile.position, Constants.worker) capturedUnit.name == Constants.settler -> {
} else { capturedUnit.destroy()
capturedUnit.civInfo.removeUnit(capturedUnit) // This is so that future checks which check if a unit has been captured are caught give the right answer
capturedUnit.assignOwner(attacker.getCivInfo()) // For example, in postBattleMoveToAttackedTile
capturedUnit.currentMovement = 0f capturedUnit.civInfo = attacker.getCivInfo()
// It's possible that the unit can no longer stand on the tile it was captured on. attacker.getCivInfo().placeUnitNearTile(capturedUnitTile.position, Constants.worker)
// For example, because it's embarked and the capturing civ cannot embark units yet. }
if (!capturedUnit.movement.canPassThrough(capturedUnitTile)) { else -> {
capturedUnit.movement.teleportToClosestMoveableTile() 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() fun getImprovableTiles(): Sequence<TileInfo> = getTiles()
.filter {it.hasViewableResource(civInfo) && it.improvement == null} .filter {it.hasViewableResource(civInfo) && it.improvement == null}
//endregion //endregion
} }

View File

@ -16,7 +16,7 @@ class CityInfoReligionManager {
val religionsAtSomePointAdopted: HashSet<String> = hashSetOf() val religionsAtSomePointAdopted: HashSet<String> = hashSetOf()
private val pressures: Counter<String> = Counter() 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 @Transient
private val followers: Counter<String> = Counter() private val followers: Counter<String> = Counter()
@ -68,6 +68,10 @@ class CityInfoReligionManager {
return getUniques().filter { it.placeholderText == unique } return getUniques().filter { it.placeholderText == unique }
} }
fun getPressures(): Counter<String> {
return pressures.clone()
}
fun clearAllPressures() { fun clearAllPressures() {
pressures.clear() pressures.clear()
// We add pressure for following no religion // We add pressure for following no religion
@ -85,6 +89,13 @@ class CityInfoReligionManager {
updateNumberOfFollowers(shouldUpdateFollowers) updateNumberOfFollowers(shouldUpdateFollowers)
} }
} }
fun removeAllPressuresExceptFor(religion: String) {
val pressureFromThisReligion = pressures[religion]!!
clearAllPressures()
pressures.add(religion, pressureFromThisReligion)
updateNumberOfFollowers()
}
fun updatePressureOnPopulationChange(populationChangeAmount: Int) { fun updatePressureOnPopulationChange(populationChangeAmount: Int) {
val majorityReligion = val majorityReligion =
@ -204,9 +215,12 @@ class CityInfoReligionManager {
fun getMajorityReligionName(): String? { fun getMajorityReligionName(): String? {
if (followers.isEmpty()) return null if (followers.isEmpty()) return null
val religionWithMaxFollowers = followers.maxByOrNull { it.value }!! val religionWithMaxPressure = pressures.maxByOrNull { it.value }!!.key
return if (religionWithMaxFollowers.value >= cityInfo.population.population / 2) religionWithMaxFollowers.key return when {
else null religionWithMaxPressure == Constants.noReligionName -> null
followers[religionWithMaxPressure]!! >= cityInfo.population.population / 2 -> religionWithMaxPressure
else -> null
}
} }
fun getMajorityReligion(): Religion? { fun getMajorityReligion(): Religion? {

View File

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

View File

@ -1,5 +1,6 @@
package com.unciv.logic.civilization package com.unciv.logic.civilization
import com.unciv.Constants
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
@ -125,7 +126,7 @@ class ReligionManager {
fun mayFoundReligionAtAll(prophet: MapUnit): Boolean { fun mayFoundReligionAtAll(prophet: MapUnit): Boolean {
if (religion == null) return false // First found a pantheon if (religion == null) return false // First found a pantheon
if (religion!!.isMajorReligion()) return false // Already created a major religion 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 if (!civInfo.isMajorCiv()) return false // Only major civs may use religion
val foundedReligionsCount = civInfo.gameInfo.civilizations.count { val foundedReligionsCount = civInfo.gameInfo.civilizations.count {

View File

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

View File

@ -436,6 +436,11 @@ class TileMap {
unit.promotions.addPromotion(unique.params[1], true) 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 // And update civ stats, since the new unit changes both unit upkeep and resource consumption
civInfo.updateStatsForNextTurn() civInfo.updateStatsForNextTurn()

View File

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

View File

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

View File

@ -330,8 +330,6 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
if (unit.hasUnique("Religious Unit")) { if (unit.hasUnique("Religious Unit")) {
unit.religion = cityConstructions.cityInfo.religion.getMajorityReligionName() 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 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 for (unique in
// Deprecated since 3.15.9
cityConstructions.cityInfo.getMatchingUniques("New [] units start with [] Experience") +
//
cityConstructions.cityInfo.getMatchingUniques("New [] units start with [] Experience []") cityConstructions.cityInfo.getMatchingUniques("New [] units start with [] Experience []")
.filter { cityConstructions.cityInfo.matchesFilter(it.params[2]) } + .filter { cityConstructions.cityInfo.matchesFilter(it.params[2]) } +
// Deprecated since 3.15.9 // Deprecated since 3.15.9
cityConstructions.cityInfo.getMatchingUniques("New [] units start with [] Experience") +
cityConstructions.cityInfo.getLocalMatchingUniques("New [] units start with [] Experience in this city") 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() { private fun addReligionInfo() {
val label = cityInfo.religion.getMajorityReligionName() val label = cityInfo.religion.getMajorityReligion()?.iconName
?: "None" ?: "None"
val icon = if (label == "None") "Religion" else label val icon = if (label == "None") "Religion" else label
val expanderTab = val expanderTab =
@ -97,7 +97,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
onChange = { onChange = {
pack() pack()
// We have to re-anchor as our position in the city screen, otherwise it expands upwards. // 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) 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.UncivGame
import com.unciv.logic.automation.UnitAutomation import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.automation.WorkerAutomation import com.unciv.logic.automation.WorkerAutomation
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.NotificationIcon import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PlayerType import com.unciv.logic.civilization.PlayerType
@ -56,9 +57,9 @@ object UnitActions {
addGreatPersonActions(unit, actionList, tile) addGreatPersonActions(unit, actionList, tile)
addFoundReligionAction(unit, actionList, tile) addFoundReligionAction(unit, actionList, tile)
actionList += getImprovementConstructionActions(unit, tile) actionList += getImprovementConstructionActions(unit, tile)
addSpreadReligionActions(unit, actionList, tile) addActionsWithLimitedUses(unit, actionList, tile)
addToggleActionsAction(unit, actionList, unitTable) addToggleActionsAction(unit, actionList, unitTable)
return actionList return actionList
@ -374,6 +375,7 @@ object UnitActions {
private fun addAutomateBuildingImprovementsAction(unit: MapUnit, actionList: ArrayList<UnitAction>) { private fun addAutomateBuildingImprovementsAction(unit: MapUnit, actionList: ArrayList<UnitAction>) {
if (!unit.hasUniqueToBuildImprovements) return if (!unit.hasUniqueToBuildImprovements) return
if (unit.isAutomated()) return
actionList += UnitAction(UnitActionType.Automate, actionList += UnitAction(UnitActionType.Automate,
isCurrentAction = unit.isAutomated(), isCurrentAction = unit.isAutomated(),
@ -489,37 +491,76 @@ object UnitActions {
}.takeIf { unit.civInfo.religionManager.mayFoundReligionNow(unit) } }.takeIf { unit.civInfo.religionManager.mayFoundReligionNow(unit) }
) )
} }
private fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) { private fun addActionsWithLimitedUses(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
if (!unit.hasUnique("Can spread religion [] times")) return val actionsToAdd = unit.religiousActionsUnitCanDo()
if (actionsToAdd.none()) return
if (unit.religion == null || unit.civInfo.gameInfo.religions[unit.religion]!!.isPantheon()) 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 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, actionList += UnitAction(UnitActionType.SpreadReligion,
title = "Spread [${unit.religion!!}]", title = "Spread [${unit.religion!!}]",
action = { action = {
unit.abilityUsedCount["Religion Spread"] = unit.abilityUsedCount["Religion Spread"]!! + 1
val followersOfOtherReligions = city.religion.getFollowersOfOtherReligionsThan(unit.religion!!) 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 []")) { 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()) unit.civInfo.addStat(Stat.valueOf(unique.params[1]), followersOfOtherReligions * unique.params[0].toInt())
} }
city.religion.addPressure(unit.religion!!, unit.getPressureAddedFromSpread()) city.religion.addPressure(unit.religion!!, unit.getPressureAddedFromSpread())
unit.currentMovement = 0f unit.currentMovement = 0f
if (unit.abilityUsedCount["Religion Spread"] == maxReligionSpreads) { useActionWithLimitedUses(unit, Constants.spreadReligionAbilityCount, maxSpreadUses)
addGoldPerGreatPersonUsage(unit.civInfo) }.takeIf { unit.currentMovement > 0 && !blockedByInquisitor }
unit.destroy() )
} }
}.takeIf { unit.currentMovement > 0 }
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> { fun getImprovementConstructionActions(unit: MapUnit, tile: TileInfo): ArrayList<UnitAction> {
val finalActions = ArrayList<UnitAction>() val finalActions = ArrayList<UnitAction>()
var uniquesToCheck = unit.getMatchingUniques("Can construct []") var uniquesToCheck = unit.getMatchingUniques("Can construct []")
if (unit.abilityUsedCount.containsKey("Religion Spread") && unit.abilityUsedCount["Religion Spread"]!! == 0 && unit.canSpreadReligion()) if (unit.religiousActionsUnitCanDo().all { unit.abilityUsedCount[it] == 0 })
uniquesToCheck += unit.getMatchingUniques("Can construct [] if it hasn't spread religion yet") uniquesToCheck += unit.getMatchingUniques("Can construct [] if it hasn't used other actions yet")
for (unique in uniquesToCheck) { for (unique in uniquesToCheck) {
val improvementName = unique.params[0] val improvementName = unique.params[0]
val improvement = tile.ruleset.tileImprovements[improvementName] 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.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup
import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.battle.CityCombatant import com.unciv.logic.battle.CityCombatant
import com.unciv.logic.city.CityInfo 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()) 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(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) { 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 * [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 * [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 * [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 ## 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 * [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 * [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 * [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 ## Main menu
@ -627,7 +629,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
# Sound credits # 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 * [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 * [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 * [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 [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 # Music
The following music is from https://filmmusic.io The following music is from https://filmmusic.io
"Thatched Villagers" by Kevin MacLeod (https://incompetech.com) "Thatched Villagers" by Kevin MacLeod (https://incompetech.com)