Added City states (#681)

* Add Milan as 1st city state. Choose box for number of city states.

* City states don't get settlers.

* Added diplomancy relationship. Now increase by gift and decreases every turn.

* Friendly culture city states provides culture bonus.

* 0 city states by default.

* Disable many trade items for city states.

* Fix part 1.

* Fix diplomacy screen and pop-ups.

* City state doesn't build world wonders.

* City states destroy city when conquering.

* Fixed : Trying to move into border of uncountered civs caused crash.

* City states don't exchange tech or declare war on others.

* Fix a very strange problem : you could trade introduction of AI to itself.

* City states automatically get all invented techs.

* Pops defeat msg before AI founds any city.

* Fix conquest victory with city states.

* Fixed : AI city under seige change production every turn.
This commit is contained in:
Duan Tao
2019-05-01 00:33:32 +08:00
committed by Yair Morgenstern
parent 307aea1cc0
commit f2333b5839
16 changed files with 199 additions and 39 deletions

View File

@ -1,5 +1,6 @@
[
{
//nations
name:"Babylon",
leaderName:"Nebuchadnezzar II",
adjective:["Babylonian"],
@ -919,6 +920,68 @@
cities:["Moson Kahni","Te-Moak","Agaidika","Goshute","Pohokwi","Washakie","Timbisha","Hukandeka","Duckwater","Tukudeka","Kuchundeka","Yomba","Kamudeka","Ely","Yambadeka","Tetadeka","Deheyaeka","Pengwideka","Winnemucca","Skull Valley","Big Pine","Duck Valley","Nampa","Bannock","Yahandeka"]
},
*/
//City states
{
name:"Milan",
adjective:["Milan"],
cityStateType:"Cultured",
startBias:["Coast"],
//TO DO : better dialogs
declaringWar:"Declare war.",
attacked:"Let's fight.",
defeated:"GoodBye.",
introduction:"Hi.",
neutralHello:"Greetings.",
neutralLetsHearIt:["I'm listening.","What do you want?"],
neutralNo:["No!","Certainly not.","Unacceptable!"],
neutralYes:["Completed!","Yes!","Agreed!"],
hateHello:"What do YOU want?!",
hateLetsHearIt:["I'm certainly listening.","I'm listening!"],
hateNo:["No!","Certainly not!","Unacceptable!"],
hateYes:["Yes!"],
afterPeace:"Peace then.",
tradeRequest:"Trade?",
mainColor:[0,100,0],
secondaryColor:[213,249,255],
cities:["Milan"]
},
{
name:"Florence",
adjective:["Florence"],
cityStateType:"Cultured",
startBias:["Coast"],
//TO DO : better dialogs
declaringWar:"Declare war.",
attacked:"Let's fight.",
defeated:"GoodBye.",
introduction:"Hi.",
neutralHello:"Greetings.",
neutralLetsHearIt:["I'm listening.","What do you want?"],
neutralNo:["No!","Certainly not.","Unacceptable!"],
neutralYes:["Completed!","Yes!","Agreed!"],
hateHello:"What do YOU want?!",
hateLetsHearIt:["I'm certainly listening.","I'm listening!"],
hateNo:["No!","Certainly not!","Unacceptable!"],
hateYes:["Yes!"],
afterPeace:"Peace then.",
tradeRequest:"Trade?",
mainColor:[255, 250, 240],
secondaryColor:[238, 44, 44],
cities:["Florence"]
}
//Barbarian
{
name:"Barbarians",
mainColor:[0,0,0],

View File

@ -18,6 +18,7 @@ class GameParameters{
var numberOfHumanPlayers=1
var humanNations=ArrayList<String>().apply { add("Babylon") }
var numberOfEnemies=3
var numberOfCityStates=0
var mapType= MapType.Perlin
var noBarbarians=false
var mapFileName :String?=null
@ -31,12 +32,15 @@ class GameStarter{
gameInfo.tileMap = TileMap(newGameParameters)
gameInfo.tileMap.gameInfo = gameInfo // need to set this transient before placing units in the map
val startingLocations = getStartingLocations(
newGameParameters.numberOfEnemies+newGameParameters.numberOfHumanPlayers, gameInfo.tileMap)
newGameParameters.numberOfEnemies+newGameParameters.numberOfHumanPlayers+newGameParameters.numberOfCityStates,
gameInfo.tileMap)
val availableCivNames = Stack<String>()
availableCivNames.addAll(GameBasics.Nations.keys.shuffled())
availableCivNames.addAll(GameBasics.Nations.filter { !it.value.isCityState() }.keys.shuffled())
availableCivNames.removeAll(newGameParameters.humanNations)
availableCivNames.remove("Barbarians")
val availableCityStatesNames = Stack<String>()
availableCityStatesNames.addAll(GameBasics.Nations.filter { it.value.isCityState() }.keys.shuffled())
for(nation in newGameParameters.humanNations) {
val playerCiv = CivilizationInfo(nation)
@ -53,6 +57,10 @@ class GameStarter{
gameInfo.civilizations.add(civ)
}
for (cityStateName in availableCityStatesNames.take(newGameParameters.numberOfCityStates)) {
val civ = CivilizationInfo(cityStateName)
gameInfo.civilizations.add(civ)
}
gameInfo.setTransients() // needs to be before placeBarbarianUnit because it depends on the tilemap having its gameinfo set

View File

@ -44,6 +44,16 @@ class NextTurnAutomation{
private fun exchangeTechs(civInfo: CivilizationInfo) {
if(!civInfo.gameInfo.getDifficulty().aisExchangeTechs) return
if (civInfo.isCityState()) { //City states automatically get all invented techs
for (otherCiv in civInfo.getKnownCivs().filterNot { it.isCityState() }) {
for (entry in otherCiv.tech.techsResearched
.filterNot { civInfo.tech.isResearched(it) }
.filter { civInfo.tech.canBeResearched(it) }) {
civInfo.tech.addTechnology(entry)
}
}
return
}
val otherCivList = civInfo.getKnownCivs()
.filter { it.playerType == PlayerType.AI && !it.isBarbarianCivilization() }
@ -227,6 +237,7 @@ class NextTurnAutomation{
}
private fun declareWar(civInfo: CivilizationInfo) {
if (civInfo.isCityState()) return
if (civInfo.cities.isNotEmpty() && civInfo.diplomacy.isNotEmpty()) {
val ourMilitaryUnits = civInfo.getCivUnits().filter { !it.type.isCivilian() }.size
if (!civInfo.isAtWar() && civInfo.happiness > 0
@ -295,6 +306,7 @@ class NextTurnAutomation{
}
private fun trainSettler(civInfo: CivilizationInfo) {
if(civInfo.isCityState()) return
if(civInfo.isAtWar()) return // don't train settlers when you could be training troops.
if (civInfo.cities.any()
&& civInfo.happiness > civInfo.cities.size + 5

View File

@ -169,7 +169,7 @@ class Battle(val gameInfo:GameInfo) {
if(civilianUnit!=null) captureCivilianUnit(attacker,MapUnitCombatant(civilianUnit!!))
}
if (attacker.getCivInfo().isBarbarianCivilization()){
if (attacker.getCivInfo().isBarbarianCivilization() || attacker.getCivInfo().isCityState()){
city.destroyCity()
}
else {
@ -219,7 +219,10 @@ class Battle(val gameInfo:GameInfo) {
defender.takeDamage(100)
return
} // barbarians don't capture civilians!
if (attacker.getCivInfo().isCityState() && defender.getName() == "Settler") {
defender.takeDamage(100)
return
}
if (defender.getCivInfo().isDefeated()) {//Last settler captured
defender.getCivInfo().destroy()
attacker.getCivInfo().popupAlerts.add(PopupAlert(AlertType.Defeated,defender.getCivInfo().civName))

View File

@ -21,6 +21,7 @@ import com.unciv.models.gamebasics.tech.TechEra
import com.unciv.models.gamebasics.tile.ResourceType
import com.unciv.models.gamebasics.tile.TileResource
import com.unciv.models.gamebasics.tr
import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats
import java.util.*
import kotlin.collections.ArrayList
@ -57,6 +58,7 @@ class CivilizationInfo {
@Deprecated("As of 2.11.1") var difficulty = "Chieftain"
var playerType = PlayerType.AI
var civName = ""
var cityStateType = ""
var tech = TechManager()
var policies = PolicyManager()
var goldenAges = GoldenAgeManager()
@ -80,6 +82,7 @@ class CivilizationInfo {
constructor(civName: String) {
this.civName = civName
tech.techsResearched.add("Agriculture") // can't be .addTechnology because the civInfo isn't assigned yet
cityStateType = GameBasics.Nations[civName]!!.cityStateType
}
fun clone(): CivilizationInfo {
@ -98,6 +101,7 @@ class CivilizationInfo {
toReturn.exploredTiles.addAll(exploredTiles)
toReturn.notifications.addAll(notifications)
toReturn.citiesCreated = citiesCreated
toReturn.cityStateType = cityStateType
return toReturn
}
@ -118,6 +122,7 @@ class CivilizationInfo {
return translatedNation
}
fun isCityState(): Boolean = (cityStateType != "")
fun getDiplomacyManager(civInfo: CivilizationInfo) = diplomacy[civInfo.civName]!!
fun getKnownCivs() = diplomacy.values.map { it.otherCiv() }
@ -137,6 +142,19 @@ class CivilizationInfo {
}
}
//City states culture bonus
for (otherCivName in diplomacy.keys) {
val otherCiv = gameInfo.getCivilization(otherCivName)
if (otherCiv.isCityState() && otherCiv.diplomacy[civName]!!.attitude > 60) {
var cultureBonus = Stats()
cultureBonus.add(Stat.Culture, 5.0f * getEra().ordinal)
if (statMap.containsKey("City States"))
statMap["City States"] = statMap["City States"]!! + cultureBonus
else
statMap["City States"] = cultureBonus
}
}
for (entry in getHappinessForNextTurn()) {
if (!statMap.containsKey(entry.key))
statMap[entry.key] = Stats()

View File

@ -31,7 +31,7 @@ class VictoryManager {
fun hasWonCulturalVictory() = civInfo.policies.adoptedPolicies.count{it.endsWith("Complete")} > 3
fun hasWonConquestVictory() = civInfo.gameInfo.civilizations.all { it==civInfo || it.isDefeated() }
fun hasWonConquestVictory() = civInfo.gameInfo.civilizations.all { it==civInfo || it.isDefeated() || it.isCityState() }
fun hasWon() = hasWonConquestVictory() || hasWonCulturalVictory() || hasWonScientificVictory()
}

View File

@ -28,12 +28,14 @@ class DiplomacyManager() {
* The JSON serialize/deserialize REFUSES to deserialize hashmap keys as Enums, so I'm forced to use strings instead =(
* This is so sad Alexa play Despacito */
var flagsCountdown = HashMap<String,Int>()
var attitude: Float = 0f //positive means our civ is friendly to the other civ
fun clone(): DiplomacyManager {
val toReturn = DiplomacyManager()
toReturn.otherCivName=otherCivName
toReturn.diplomaticStatus=diplomaticStatus
toReturn.trades.addAll(trades.map { it.clone() })
toReturn.attitude = attitude
toReturn.flagsCountdown.putAll(flagsCountdown)
toReturn.hasOpenBorders=hasOpenBorders
return toReturn
@ -143,6 +145,14 @@ class DiplomacyManager() {
if(flagsCountdown[flag]==0) flagsCountdown.remove(flag)
}
if (attitude > 1f) {
attitude -= 1f
} else if (attitude < -1f) {
attitude += 1f
} else {
attitude = 0f
}
}
fun declareWar(){
@ -173,4 +183,4 @@ class DiplomacyManager() {
otherCiv.getDiplomacyManager(civInfo).flagsCountdown[DiplomacyFlags.DeclinedPeace.toString()]=10
}
//endregion
}
}

View File

@ -438,8 +438,10 @@ class MapUnit {
actions.add {
val chosenUnit = listOf("Settler","Worker","Warrior").random()
civInfo.placeUnitNearTile(currentTile.position,chosenUnit)
civInfo.addNotification("A [$chosenUnit] has joined us!",currentTile.position, Color.BROWN)
if (!civInfo.isCityState() || chosenUnit != "Settler") { //City states don't get settler from ruins
civInfo.placeUnitNearTile(currentTile.position, chosenUnit)
civInfo.addNotification("A [$chosenUnit] has joined us!", currentTile.position, Color.BROWN)
}
}
if(!type.isCivilian())

View File

@ -14,10 +14,12 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
fun getAvailableOffers(civInfo: CivilizationInfo, otherCivilization: CivilizationInfo): TradeOffersList {
val offers = TradeOffersList()
if (civInfo.isCityState() && otherCivilization.isCityState()) return offers
if(civInfo.isAtWarWith(otherCivilization))
offers.add(TradeOffer("Peace Treaty", TradeType.Treaty, 20))
if(!otherCivilization.getDiplomacyManager(civInfo).hasOpenBorders
&& !otherCivilization.isCityState()
&& civInfo.tech.getTechUniques().contains("Enables Open Borders agreements")
&& otherCivilization.tech.getTechUniques().contains("Enables Open Borders agreements"))
offers.add(TradeOffer("Open Borders", TradeType.Agreement, 30))
@ -27,30 +29,40 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
else TradeType.Strategic_Resource
offers.add(TradeOffer(entry.key.name, resourceTradeType, 30, entry.value))
}
for(entry in civInfo.tech.techsResearched
.filterNot { otherCivilization.tech.isResearched(it) }
.filter { otherCivilization.tech.canBeResearched(it) }){
offers.add(TradeOffer(entry, TradeType.Technology, 0))
if (!civInfo.isCityState() && !otherCivilization.isCityState()) {
for (entry in civInfo.tech.techsResearched
.filterNot { otherCivilization.tech.isResearched(it) }
.filter { otherCivilization.tech.canBeResearched(it) }) {
offers.add(TradeOffer(entry, TradeType.Technology, 0))
}
}
offers.add(TradeOffer("Gold".tr(), TradeType.Gold, 0, civInfo.gold))
offers.add(TradeOffer("Gold per turn".tr(), TradeType.Gold_Per_Turn, 30, civInfo.getStatsForNextTurn().gold.toInt()))
for(city in civInfo.cities.filterNot { it.isCapital() })
offers.add(TradeOffer(city.name, TradeType.City, 0))
val otherCivsWeKnow = civInfo.getKnownCivs()
.filter { it != otherCivilization && !it.isBarbarianCivilization() && !it.isDefeated() }
val civsWeKnowAndTheyDont = otherCivsWeKnow
.filter { !otherCivilization.diplomacy.containsKey(it.civName) && !it.isDefeated() }
for(thirdCiv in civsWeKnowAndTheyDont){
offers.add(TradeOffer("Introduction to " + thirdCiv.civName, TradeType.Introduction, 0))
if (!civInfo.isCityState() && !otherCivilization.isCityState()) {
for (city in civInfo.cities.filterNot { it.isCapital() })
offers.add(TradeOffer(city.name, TradeType.City, 0))
}
val civsWeBothKnow = otherCivsWeKnow
.filter { otherCivilization.diplomacy.containsKey(it.civName) }
val civsWeArentAtWarWith = civsWeBothKnow
.filter { civInfo.getDiplomacyManager(it).diplomaticStatus== DiplomaticStatus.Peace }
for(thirdCiv in civsWeArentAtWarWith){
offers.add(TradeOffer("Declare war on "+thirdCiv.civName,TradeType.WarDeclaration,0))
val otherCivsWeKnow = civInfo.getKnownCivs()
.filter { it.civName != otherCivilization.civName && !it.isBarbarianCivilization() && !it.isDefeated() }
val civsWeKnowAndTheyDont = otherCivsWeKnow
.filter { !otherCivilization.diplomacy.containsKey(it.civName) && !it.isDefeated() }
if (!otherCivilization.isCityState()) {
for (thirdCiv in civsWeKnowAndTheyDont) {
offers.add(TradeOffer("Introduction to " + thirdCiv.civName, TradeType.Introduction, 0))
}
}
if (!civInfo.isCityState() && !otherCivilization.isCityState()) {
val civsWeBothKnow = otherCivsWeKnow
.filter { otherCivilization.diplomacy.containsKey(it.civName) }
val civsWeArentAtWarWith = civsWeBothKnow
.filter { civInfo.getDiplomacyManager(it).diplomaticStatus == DiplomaticStatus.Peace }
for (thirdCiv in civsWeArentAtWarWith) {
offers.add(TradeOffer("Declare war on " + thirdCiv.civName, TradeType.WarDeclaration, 0))
}
}
return offers
@ -103,6 +115,17 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
transferTrade(ourCivilization,otherCivilization,currentTrade)
transferTrade(otherCivilization,ourCivilization,currentTrade.reverse())
//Buy friendship with gold.
if (currentTrade.theirOffers.isEmpty()) {
for (trade in currentTrade.ourOffers) {
if (trade.type == TradeType.Gold) {
otherCivilization.getDiplomacyManager(ourCivilization).attitude += trade.amount / 10
}
if (trade.type == TradeType.Gold_Per_Turn) {
otherCivilization.getDiplomacyManager(ourCivilization).attitude += trade.amount * trade.duration / 10
}
}
}
}
}

View File

@ -208,6 +208,9 @@ class Building : NamedStats(), IConstruction{
if(civInfo.cities.any { it!=construction.cityInfo && it.cityConstructions.isBeingConstructed(name) })
return "Wonder is being built elsewhere"
if(civInfo.isCityState())
return "No world wonders for city state"
}

View File

@ -13,7 +13,7 @@ class Nation : INamed {
}
var leaderName=""
var cityStateType=""
var declaringWar=""
var attacked=""
var defeated=""
@ -34,5 +34,6 @@ class Nation : INamed {
if(secondaryColor==null) return Color.BLACK
return colorFromRGB(secondaryColor!![0], secondaryColor!![1], secondaryColor!![2])
}
fun isCityState(): Boolean = (cityStateType != "")
lateinit var cities: List<String>
}

View File

@ -131,6 +131,7 @@ class BaseUnit : INamed, IConstruction, ICivilopedia {
if (uniqueTo!=null && uniqueTo!=civInfo.civName) return "Unique to $uniqueTo"
if (GameBasics.Units.values.any { it.uniqueTo==civInfo.civName && it.replaces==name }) return "Our unique unit replaces this"
if (requiredResource!=null && !civInfo.hasResource(requiredResource!!)) return "Requires $requiredResource"
if (name == "Settler" && civInfo.isCityState()) return "No settler for city state"
return ""
}

View File

@ -289,15 +289,13 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){
currentPlayerCivInfo.diplomacy.containsKey(civ.civName)
fun createDiplomacyGroup(): Group {
val relevantCivs = currentPlayerCivInfo.gameInfo.civilizations.filter { !it.isBarbarianCivilization() }
val relevantCivs = currentPlayerCivInfo.gameInfo.civilizations.filter { !it.isBarbarianCivilization() && !it.isCityState() }
val groupSize = 500f
val group = Group()
group.setSize(groupSize,groupSize)
val civGroups = HashMap<String, Actor>()
for(i in 0..relevantCivs.lastIndex){
val civ = relevantCivs[i]
val civGroup = Table()
val civGroupBackground = ImageGetter.getDrawable("OtherIcons/civTableBackground.png")
@ -328,7 +326,6 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){
group.addActor(civGroup)
}
for(civ in relevantCivs.filter { playerKnows(it) && !it.isDefeated() })
for(diplomacy in civ.diplomacy.values.filter { !it.otherCiv().isBarbarianCivilization() && playerKnows(it.otherCiv()) && !it.otherCiv().isDefeated()}){
val civGroup = civGroups[civ.civName]!!
@ -344,7 +341,6 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){
statusLine.toBack()
}
return group
}
}

View File

@ -35,7 +35,7 @@ class NewGameScreen: PickerScreen(){
val mainTable = Table()
mainTable.add(getOptionsTable())
for(nation in GameBasics.Nations.values.filterNot { it.name == "Barbarians" }){
for(nation in GameBasics.Nations.values.filterNot { it.name == "Barbarians" || it.isCityState() }){
val nationTable = NationTable(nation,newGameParameters,skin,stage.width/3 ){updateNationTables()}
nationTables.add(nationTable)
civPickerTable.add(nationTable).row()
@ -162,7 +162,7 @@ class NewGameScreen: PickerScreen(){
newGameOptionsTable.add("{Number of human players}:".tr())
val humanPlayers = SelectBox<Int>(skin)
val humanPlayersArray = Array<Int>()
(1..GameBasics.Nations.size).forEach { humanPlayersArray.add(it) }
(1..GameBasics.Nations.filter{ !it.value.isCityState() }.size).forEach { humanPlayersArray.add(it) }
humanPlayers.items = humanPlayersArray
humanPlayers.selected = newGameParameters.numberOfHumanPlayers
newGameOptionsTable.add(humanPlayers).pad(10f).row()
@ -171,11 +171,19 @@ class NewGameScreen: PickerScreen(){
newGameOptionsTable.add("{Number of enemies}:".tr())
val enemiesSelectBox = SelectBox<Int>(skin)
val enemiesArray = Array<Int>()
(0..GameBasics.Nations.size - 1).forEach { enemiesArray.add(it) }
(0..GameBasics.Nations.filter{ !it.value.isCityState() }.size - 1).forEach { enemiesArray.add(it) }
enemiesSelectBox.items = enemiesArray
enemiesSelectBox.selected = newGameParameters.numberOfEnemies
newGameOptionsTable.add(enemiesSelectBox).pad(10f).row()
newGameOptionsTable.add("{Number of city states}:".tr())
val cityStatesSelectBox = SelectBox<Int>(skin)
val cityStatesArray = Array<Int>()
(0..GameBasics.Nations.filter{ it.value.isCityState() }.size).forEach { cityStatesArray.add(it) }
cityStatesSelectBox.items = cityStatesArray
cityStatesSelectBox.selected = newGameParameters.numberOfCityStates
newGameOptionsTable.add(cityStatesSelectBox).pad(10f).row()
humanPlayers.addListener(object : ChangeListener() {
override fun changed(event: ChangeEvent?, actor: Actor?) {
newGameParameters.numberOfHumanPlayers = humanPlayers.selected
@ -193,6 +201,12 @@ class NewGameScreen: PickerScreen(){
removeExtraHumanNations(humanPlayers)
}
})
cityStatesSelectBox.addListener(object : ChangeListener() {
override fun changed(event: ChangeEvent?, actor: Actor?) {
newGameParameters.numberOfCityStates = cityStatesSelectBox.selected
}
})
}
private fun addDifficultySelectBox(newGameOptionsTable: Table) {

View File

@ -87,7 +87,7 @@ class VictoryScreen : PickerScreen() {
val table=Table()
table.defaults().pad(5f)
for (civ in playerCivInfo.gameInfo.civilizations) {
if (civ.isPlayerCivilization() || civ.isBarbarianCivilization()) continue
if (civ.isPlayerCivilization() || civ.isBarbarianCivilization() || civ.isCityState()) continue
val civName =
if (playerCivInfo.diplomacy.containsKey(civ.civName)) civ.civName
else "???"

View File

@ -68,9 +68,16 @@ class DiplomacyScreen:CameraStageBaseScreen() {
}
private fun getDiplomacyTable(civ: CivilizationInfo): Table {
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
val diplomacyTable = Table()
diplomacyTable.defaults().pad(10f)
val leaderName = "[" + civ.getNation().leaderName + "] of [" + civ.civName + "]"
var leaderName: String
if (civ.isCityState()) {
leaderName = "City State [" + civ.civName + "]"
} else {
leaderName = "[" + civ.getNation().leaderName + "] of [" + civ.civName + "]"
}
leaderName = leaderName + " : Attitude " + civ.getDiplomacyManager(currentPlayerCiv).attitude.toInt().toString()
diplomacyTable.add(leaderName.toLabel())
diplomacyTable.addSeparator()
@ -78,7 +85,6 @@ class DiplomacyScreen:CameraStageBaseScreen() {
tradeButton.onClick { setTrade(civ) }
diplomacyTable.add(tradeButton).row()
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
val civDiplomacy = currentPlayerCiv.getDiplomacyManager(civ)
if (!currentPlayerCiv.isAtWarWith(civ)) {