From 8eb24ac273a44460d82e19f31c869e7cae3da7bb Mon Sep 17 00:00:00 2001 From: SimonCeder <63475501+SimonCeder@users.noreply.github.com> Date: Sat, 18 Sep 2021 19:28:12 +0200 Subject: [PATCH] Carthage civ (#5224) * Add Carthage * Implement uniques * performance improvement, better elephant * AI avoids taking too much damage from mountains * more performance * better AI * can't settle cities on mountains * AI improvement * AI improvement * revisions, damagePerTurn in Terrains.json * terrain damage stored as unique in json, damage also works for terrain features * don't change game.png --- .../UnitIcons/African Forest Elephant.png | Bin 0 -> 7120 bytes .../UnitIcons/Quinquereme.png | Bin 0 -> 7801 bytes android/Images/NationIcons/Carthage.png | Bin 0 -> 14706 bytes .../assets/jsons/Civ V - Vanilla/Nations.json | 27 ++++++++++ .../jsons/Civ V - Vanilla/Terrains.json | 2 +- .../assets/jsons/Civ V - Vanilla/Units.json | 30 +++++++++++ .../automation/SpecificUnitAutomation.kt | 5 +- .../unciv/logic/automation/UnitAutomation.kt | 34 ++++++++---- .../logic/civilization/CivilizationInfo.kt | 22 ++++++++ core/src/com/unciv/logic/map/MapUnit.kt | 50 ++++++++++-------- .../unciv/logic/map/UnitMovementAlgorithms.kt | 15 +++++- core/src/com/unciv/models/ruleset/Ruleset.kt | 5 +- .../com/unciv/models/ruleset/tile/Terrain.kt | 9 ++++ .../unciv/ui/worldscreen/unit/UnitActions.kt | 2 +- docs/Credits.md | 3 ++ 15 files changed, 165 insertions(+), 39 deletions(-) create mode 100644 android/Images.Construction/UnitIcons/African Forest Elephant.png create mode 100644 android/Images.Construction/UnitIcons/Quinquereme.png create mode 100644 android/Images/NationIcons/Carthage.png diff --git a/android/Images.Construction/UnitIcons/African Forest Elephant.png b/android/Images.Construction/UnitIcons/African Forest Elephant.png new file mode 100644 index 0000000000000000000000000000000000000000..f9f72bad4486684b59d82b1318e67c29d4020f2b GIT binary patch literal 7120 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 literal 0 HcmV?d00001 diff --git a/android/Images.Construction/UnitIcons/Quinquereme.png b/android/Images.Construction/UnitIcons/Quinquereme.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0a30285a05aa695188f94ae697abd2f223a790 GIT binary patch literal 7801 zcmeHLc{r5q_a9_ml0C#=$kG@y#xj#B*~eOzkdiSA!)(orJ!MK!Ax2DusHrSjicl#_ zp+!Pj%92#b5=o(=-%#)S_Flj5?~nJoe&7GzXRha-=eh55KIfdzIrq8do)bG=?Ip#O z#Q*?+B-+8oo%fAfy+wq0BW)zZ69C|!i}v(kx?>`MG&+?)B;kR~C>kD!k0cTRfXJ8A z9CC_^hQ#q7jFV~@5|)&5Z&u^85bDtWvk&19371N59oEP9@dXYGepg~kF84m&aA>%I z1hp}7LtK({YlAX&G}pY*Gq?gSJBZTDBhLq~_ZGMZ(tWo${(kCk9flC-^i3veMOAVr zRP3TrgvN{1%KKP8*nTnhoev+Nap5-lG8|jbc>|WU{}S=GpI78%diS{OH(5D!0;*wl zyp5RwndX+~s%M^>A!J5B`5s_)^Kv9@?qF-MTFZFr2S;Zb5os3I|ERd&jTGmxa%QST z`2~sr9<)AjNDN6X=}+<0lNETJ?~p7eIZ<-e^Vtk~6qrN?^eqFlTmLJw%o zbrRa_)@?^F%84MPBxfuwiw7zkQ#3_sIb#D#*s8DB=P)g~g3|hEiD25l9ZnX9Mm^l8pON5KW0mE~7GGSb3L^j71KXWl?6uycx2qO?57p^jISQ`nD$+U=J@wL~<^yU0 zCLLmJr;6;Vc&&AszUv|_U^(@!Ycl0|WLoy7=?ljT2xg+$y(e4=z?L-20Uf?|a5us^ zk0e!{TMtQ(lROHNye8tG1aaNetmxeEVu1&yRsC-IUH{|tn`RZF#qS&&vZ5B$boh1G^iWeZ7UfcSwH+9AGfqnQ1aVIC6XLN74pPM=YpC z%mB)Kz2GH5{2k2xHZI%BuwFmjzcWEMGCnO;?(tdKX86I@UKBs+&Z){RKNyEo3injV z0fEv_^^X%}snTxLmqKr11O4@!^gkj#)}j?bhj6#1KB~N7KURJG_6doQcXqRalX*R22mi|vs#3^o+c1P0=Rh!m9O%(m5sBmhknv0mFp^B7Fp!Zb%{5#k?|F3^q6u6RVTPhKeOz_| zt*LZ85N-%JGy>a362oDd+r)qtbQ}TcZe#Zgg7=2f3}!NENC+e%BEm4j*pNyOf%c$!Bhy@5 z{)DG6ezCyg0}_d$L7;|45HcC^y9R@48_omy<a5%poY=0c-wG1g{k#B z1P=SBJ}rz+S_=nu>+n1BYS)2{14M4kdu$ z<~R&E5QpKtni|2N1T$D*pb`8x2sb*BSCtsj?_RA!;doFuD8?9XhTxeu<>A52p;#~i z4>bql;AR9Y1{w&(;*8gza9E@rl}^U+(n%y^g76R;C1`D7m2l*aooJLM%+TnsnVlpI zlfY9zX*v@rVUd3=coNBY4<=@nO{l3c)Wp;jYHkEIfy2yx)837zGkBG_iU~C`g#C(V5qSt42FawkZ>adBN)=iNE7laV94sO{v%=w$p7NRVol(; zHo(*SImc@+ylw^gvt9k-Y?a3U;`J*R|3wQt=zop;BYyv->tDM55d;57_}}XKm#%-r zz&{fHx4Qn{=o0(uz=Nmoeg#GFjx!r!E9ShTkWiqLy$#^U>L>rsm2}>WD9yo_0RV{0 zuHJlr>>S?V79h+-yVwf96P1*M>gEn_djSAQxT9@$c=G<%$&IRzZgI*`rn5HA#0KyQs4_=a2YRVRKmDu?R{x~Dtp~m;kY+7&v86V()>cb1EiTl? zGqKQN-T4Q1>!w%cT66qT-n0$88(dK0))yBoe7F-E6a95^TkO~Q-dOGRzh9^K3C2B+ z-WVEw#L>dX{RWql!|3ufVf7)T0NX+XL2hV1vf>e6`P2&O@3u#Tb!bszXASoSPK-`LUOB!;^IF^zi~^v-H#A(eT78v}}!7!b|@cwR}` zbQ?XPU34CFkO8UZc=B0aZKCpJ@+IHLiW{kztBtEYe6!2x6E**6`3tcqw58i{NyDZF zeIUoO-M&GcRb(38R=F@HU~<>gz|GafNTL?t10ZQ{?4RO~6f2xF@!2SY!sd+QOY)&hEYdv$fO8@uTP>*fWZ1PoYgiw*%aVMb`&sxdULach*hdMY!G)X=k*swjx1zS_;X}idojp=Xg34BT zeWU#M;Q$CEF>;QSIVV9Yjf-L10EZp4`+2Xyd=j&n&%Pc`G+A$&l^Ofpd?%7zLH4!S{FVMSlX7GTTkY9;Q9_ z?t0BmNn@TFtYTXDFpq6f>`2-L6s=sl4dNsa( z3g&t|9q!b%aXQQitpd6ej2eTYALlTVU!x1Td~6*DdCPV0}l6MmpU{tKh1v6;Z7Z zQ>^f*N-ae$I$eIkCg04cZ^$oX;vTK!uuQG$xJvrMwT)UJ;heOc>%DEeRt_cT`jNCL zp?v4Q6{gX0(bns0gng9UkB|fu1YQXr_Zc#V^=!>%oe=$8VXm{>7Op<-66!~q+#_ru zk|Qc{y}6*X;WVi;_~NBO(xbHLjaoR5oGcUZ3h?W!pehx;o|mr-D<`RNhRLAkiKrb- zMb*kRUGWrOE0mG>Wi?l~C0B78{&t~n9MslEr_uU_=*ZkR^HhpmaJIo$eV{^cIk)soL!eBvWOE*bD$ zT|?~SPG2)@5I&!42xy58pdW+z+~JTC8>(3muU{fK7msC>8gB1`>Lq2y*cy&oh9Nmxq10ORJ|$p-U%mBU`wu@(Ai zCPTWJ{=BkD=k@0Y+4C*&W%XJvyXymGw~pndF{=>*2gIqBO?%#gtXk~-pEBCt>E9Ul z1cYjqo1sG&0XuC&SyjoxU=f zTD4dkh_d&B`)a)%-<5jZs=DqupJ?Gw$$xE6q-|OG5&U4R_H?;rxk(!-#@^ttUrWmI zJu6SN56$?a74I|Sl+g)&@#dMSpVN9hSV4j70-~&hyk{{9a=}C}dnKrsVdtK^p>tm& zeI`IFn9JS84(B}YSKghRmD`gPyR02{{bQ-zmTEQinn)A7t{jk6rjxQ_bZiQ@-Z5HV z=VCVjsjscAELH3N{IJePQq@PoCGOZ0uDOA?eM!7OTWBLupfvECL2CS$8;)g_CCFn{ zL+o3bj)@G4N`-S*BUJsgldicQltVbBqF@6(=JS9FR?WZ|)kE^5*RD zRA+$w-c2YM_+Vd1NVw$#)C4JtWLO!DE?(@k zZ2gWfG=ybkUA6SuK-PdbAYUSC?l#~Gk8Xf%!M$35_4mJWc2X=^%zhBtgI0c>@Y&bl(?nQPP*a zAN4N@no92{4ZU=B$~4nJCFQjD$mVjZ6KADFH@spiRIPJmGciX>T-v^iqq`eQ+i&)} z&rJs^fTY5D0ysLcOI7@ns-G2FZwb`iUq_GWN7_}jh6xQ_(;jFH2|6o7a0RJ#L$sIo zKk^|7)K*&}+V!P5-|V`SG|>@$>u;K3RLAkXJJKk<0jA%qD>*7{HWC$?^VR{EvMz_Q zw@K}PjZ%`gN!|vo?hjlr)V}W@e7%tSu*9T8OD=KUKIlT}iOPhA&$}11^j%}~7Ixm! zkBS|>FmHFCz_`?_oaL81t`kci_u2PI;GK%?V#u`vTTX|duLvw1JZG&qdLrYfK%Ds0 z(t;6{l$uo(_?#1Z@}xbV2gz~@e7ag<wl?WjVsa5l;1l!VuuboLpO9>Y};|gQfNLg>w3uT5Nz44j#qUe z`RU-_y8XM% z0_Gj^Ow5X6FOrvDWKARbsZfOWU|+||$r!fjh`=lOE<35ksGKBWrI46?b1FxLnz==iCscd* zFUoxA?Q|_|NO`8jN&Rrpz~g9X9U(In73V$0Qh0guClTb~Q&}SxwfB@20o8{@1<7&5L_ws#m&S&R{W; zCBrF$n?^1p$CO#(@`cJO@(#)DFXslzER6^5X^I?J;@i7%E{PrAf7;<|5r0P+b1jqZ=#S<-W~Ixk4(SN8&)cTKT^jIW%#a&mXFjMe`mz%KkISAgd)Okz W@zeUE+GO5=1Aw-5wJEa-IPyQ4UEHPs literal 0 HcmV?d00001 diff --git a/android/Images/NationIcons/Carthage.png b/android/Images/NationIcons/Carthage.png new file mode 100644 index 0000000000000000000000000000000000000000..445bc6d2053e96efbc61b9310858995a89059141 GIT binary patch literal 14706 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 aBU~7>z2#fwcAMYddte!5=^9CsH~$BLZ#~rj literal 0 HcmV?d00001 diff --git a/android/assets/jsons/Civ V - Vanilla/Nations.json b/android/assets/jsons/Civ V - Vanilla/Nations.json index 50283ba95e..06dfd2328a 100644 --- a/android/assets/jsons/Civ V - Vanilla/Nations.json +++ b/android/assets/jsons/Civ V - Vanilla/Nations.json @@ -773,6 +773,33 @@ "Kapfenberg", "Hallein", "Bischofshofen", "Waidhofen", "Saalbach", "Lienz", "Steyr" ] }, + { + "name": "Carthage", + "leaderName": "Dido", + "adjective": ["Carthaginian"], + "startBias": ["Coast"], + "preferredVictoryType": "Domination", + + "startIntroPart1": "Blessings and salutations to you, revered Queen Dido, founder of the legendary kingdom of Carthage. Chronicled by the words of the great poet Virgil, your husband Acerbas was murdered at the hands of your own brother, King Pygmalion of Tyre, who subsequently claimed the treasures of Acerbas that were now rightfully yours. Fearing the lengths from which your brother would pursue this vast wealth, you and your compatriots sailed for new lands. Arriving on the shores of North Africa, you tricked the local king with the simple manipulation of an ox hide, laying out a vast expanse of territory for your new home, the future kingdom of Carthage.", + "startIntroPart2": "Clever and inquisitive Dido, the world longs for a leader who can provide a shelter from the coming storm, guided by brilliant intuition and cunning. Can you lead the people in the creation of a new kingdom to rival that of once mighty Carthage? Can you build a civilization that will stand the test of time?", + + "declaringWar": "Tell me, do you all know how numerous my armies, elephants and the gdadons are? No? Today, you shall find out!", + "attacked": "Fate is against you. You earned the animosity of Carthage in your exploration. Your days are numbered.", + "defeated": "The fates became to hate me. This is it? You wouldn't destroy us so without their help.", + "introduction": "The Phoenicians welcome you to this most pleasant kingdom. I am Dido, the queen of Carthage and all that belongs to it.", + "neutralHello": "It is done.", + "hateHello": "What is it now?", + "tradeRequest": "I had an idea and I realized I should tell it to you!", + "outerColor": [205,205,205], + "innerColor": [81,0,137], + "uniqueName": "Phoenician Heritage", + "uniques": ["Gain a free [Harbor] [in all coastal cities]","Land units may cross [Mountain] tiles after the first [Great General] is earned", + "Units ending their turn on [Mountain] tiles take [50] damage"], + "cities": ["Carthage","Utique","Hippo Regius","Gades","Saguntum","Carthago Nova","Panormus","Lilybaeum","Hadrumetum","Zama Regia", + "Karalis","Malaca","Leptis Magna","Hippo Diarrhytus","Motya","Sulci","Leptis Parva","Tharros","Soluntum","Lixus", + "Oea","Theveste","Ibossim","Thapsus","Aleria","Tingis","Abyla","Sabratha","Rusadir","Baecula", + "Saldae"] + }, diff --git a/android/assets/jsons/Civ V - Vanilla/Terrains.json b/android/assets/jsons/Civ V - Vanilla/Terrains.json index f7383dd6da..a7a6fb169e 100644 --- a/android/assets/jsons/Civ V - Vanilla/Terrains.json +++ b/android/assets/jsons/Civ V - Vanilla/Terrains.json @@ -86,7 +86,7 @@ "impassable": true, "defenceBonus": 0.25, "RGB": [120, 120, 120], - "uniques":["Rough terrain", "Has an elevation of [4] for visibility calculations", "Occurs in chains at high elevations"] + "uniques":["Rough terrain", "Has an elevation of [4] for visibility calculations", "Occurs in chains at high elevations", "Units ending their turn on this terrain take [50] damage"] }, { "name": "Snow", diff --git a/android/assets/jsons/Civ V - Vanilla/Units.json b/android/assets/jsons/Civ V - Vanilla/Units.json index 2e80c1894c..405731ae7f 100644 --- a/android/assets/jsons/Civ V - Vanilla/Units.json +++ b/android/assets/jsons/Civ V - Vanilla/Units.json @@ -161,6 +161,20 @@ "obsoleteTech": "Astronomy", "attackSound": "nonmetalhit" }, + { + "name": "Quinquereme", + "unitType": "Melee Water", + "uniqueTo": "Carthage", + "replaces": "Trireme", + "movement": 4, + "strength": 13, + "cost": 45, + "requiredTech": "Sailing", + "uniques": ["Cannot enter ocean tiles"], + "upgradesTo": "Caravel", + "obsoleteTech": "Astronomy", + "attackSound": "nonmetalhit" + }, /* { "name": "Galley", @@ -339,6 +353,22 @@ "hurryCostModifier": 20, "attackSound": "horse" }, + { + "name": "African Forest Elephant", + "unitType": "Mounted", + "uniqueTo": "Carthage", + "replaces": "Horseman", + "movement": 3, + "strength": 14, + "cost": 100, + "requiredTech": "Horseback Riding", + "upgradesTo": "Knight", + "obsoleteTech": "Metallurgy", + "promotions": ["Great Generals II"], + "uniques": ["Can move after attacking", "No defensive terrain bonus", "-[33]% Strength vs [City]","[-10]% Strength for enemy [Military] units in adjacent [All] tiles"], + "hurryCostModifier": 20, + "attackSound": "elephant" + }, { "name": "Catapult", "unitType": "Siege", diff --git a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt index 951216c532..3dbe984677 100644 --- a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt @@ -156,7 +156,8 @@ object SpecificUnitAutomation { } if (unit.getTile().militaryUnit == null // Don't move until you're accompanied by a military unit - && !unit.civInfo.isCityState()) return // ..unless you're a city state that was unable to settle its city on turn 1 + && !unit.civInfo.isCityState() // ..unless you're a city state that was unable to settle its city on turn 1 + && unit.getDamageFromTerrain() < unit.health) return // Also make sure we won't die waiting val tilesNearCities = unit.civInfo.gameInfo.getCities().asSequence() .flatMap { @@ -177,7 +178,7 @@ object SpecificUnitAutomation { val possibleCityLocations = unit.getTile().getTilesInDistance(5) .filter { val tileOwner = it.getOwner() - it.isLand && (tileOwner == null || tileOwner == unit.civInfo) // don't allow settler to settle inside other civ's territory + it.isLand && !it.isImpassible() && (tileOwner == null || tileOwner == unit.civInfo) // don't allow settler to settle inside other civ's territory && (unit.currentTile == it || unit.movement.canMoveTo(it)) && it !in tilesNearCities }.toList() diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index d81318ad8f..14ef3748dc 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -19,7 +19,8 @@ object UnitAutomation { && (tile.getOwner() == null || !tile.getOwner()!!.isCityState()) && tile.neighbors.any { it.position !in unit.civInfo.exploredTiles } && unit.movement.canReach(tile) - && (!unit.civInfo.isCityState() || tile.neighbors.any { it.getOwner() == unit.civInfo }) // Don't want city-states exploring far outside their borders + && (!unit.civInfo.isCityState() || tile.neighbors.any { it.getOwner() == unit.civInfo } // Don't want city-states exploring far outside their borders + && unit.getDamageFromTerrain(tile) <= 0) // Don't take unnecessary damage } internal fun tryExplore(unit: MapUnit): Boolean { @@ -66,7 +67,8 @@ object UnitAutomation { .filter { unit.movement.canMoveTo(it.key) && unit.movement.canReach(it.key) } val reachableTilesMaxWalkingDistance = reachableTiles - .filter { it.value.totalDistance == unit.currentMovement } + .filter { it.value.totalDistance == unit.currentMovement + && unit.getDamageFromTerrain(it.key) <= 0 } // Don't end turn on damaging terrain for no good reason if (reachableTilesMaxWalkingDistance.any()) unit.movement.moveToTile(reachableTilesMaxWalkingDistance.toList().random().first) else if (reachableTiles.any()) unit.movement.moveToTile(reachableTiles.keys.random()) } @@ -87,6 +89,9 @@ object UnitAutomation { if (unit.civInfo.isBarbarian()) throw IllegalStateException("Barbarians is not allowed here.") + // Might die next turn - move! + if (unit.health <= unit.getDamageFromTerrain() && tryHealUnit(unit)) return + if (unit.isCivilian()) { if (tryRunAwayIfNeccessary(unit)) return @@ -209,7 +214,10 @@ object UnitAutomation { .filter { it.isCityCenter() && it.getCity()!!.civInfo.isAtWarWith(unit.civInfo) } .flatMap { it.getTilesInDistance(it.getCity()!!.range) } - val dangerousTiles = (tilesInRangeOfAttack + tilesWithinBombardmentRange).toHashSet() + val tilesWithTerrainDamage = unit.currentTile.getTilesInDistance(3) + .filter { unit.getDamageFromTerrain(it) > 0 } + + val dangerousTiles = (tilesInRangeOfAttack + tilesWithinBombardmentRange + tilesWithTerrainDamage).toHashSet() val viableTilesForHealing = unitDistanceToTiles.keys @@ -230,7 +238,7 @@ object UnitAutomation { val bestTileForHealingRank = unit.rankTileForHealing(bestTileForHealing) if (currentUnitTile != bestTileForHealing - && bestTileForHealingRank > unit.rankTileForHealing(currentUnitTile)) + && bestTileForHealingRank > unit.rankTileForHealing(currentUnitTile) - unit.getDamageFromTerrain()) unit.movement.moveToTile(bestTileForHealing) unit.fortifyIfCan() @@ -272,16 +280,18 @@ object UnitAutomation { unitDistanceToTiles, tilesToCheck = unit.getTile().getTilesInDistance(CLOSE_ENEMY_TILES_AWAY_LIMIT).toList() ).filter { - // Ignore units that would 1-shot you if you attacked + // Ignore units that would 1-shot you if you attacked. Account for taking terrain damage after the fact. BattleDamage.calculateDamageToAttacker(MapUnitCombatant(unit), it.tileToAttackFrom, - Battle.getMapCombatantOfTile(it.tileToAttack)!!) < unit.health + Battle.getMapCombatantOfTile(it.tileToAttack)!!) + + unit.getDamageFromTerrain(it.tileToAttackFrom) < unit.health } if (unit.baseUnit.isRanged()) closeEnemies = closeEnemies.filterNot { it.tileToAttack.isCityCenter() && it.tileToAttack.getCity()!!.health == 1 } - val closestEnemy = closeEnemies.minByOrNull { it.tileToAttack.aerialDistanceTo(unit.getTile()) } + val closestEnemy = closeEnemies.filter { unit.getDamageFromTerrain(it.tileToAttackFrom) <= 0 } // Don't attack from a mountain + .minByOrNull { it.tileToAttack.aerialDistanceTo(unit.getTile()) } if (closestEnemy != null) { unit.movement.headTowards(closestEnemy.tileToAttackFrom) @@ -313,7 +323,8 @@ object UnitAutomation { val reachableTileNearSiegedCity = siegedCities .flatMap { it.getCenterTile().getTilesAtDistance(2) } .sortedBy { it.aerialDistanceTo(unit.currentTile) } - .firstOrNull { unit.movement.canMoveTo(it) && unit.movement.canReach(it) } + .firstOrNull { unit.movement.canMoveTo(it) && unit.movement.canReach(it) + && unit.getDamageFromTerrain(it) <= 0 } // Avoid ending up on damaging terrain if (reachableTileNearSiegedCity != null) { unit.movement.headTowards(reachableTileNearSiegedCity) @@ -359,6 +370,7 @@ object UnitAutomation { .filter { it.key.aerialDistanceTo(closestReachableEnemyCity) <= unitRange && it.key !in tilesInBombardRange + && unit.getDamageFromTerrain(it.key) <= 0 // Don't set up on a mountain } .minByOrNull { it.value.totalDistance }?.key @@ -377,7 +389,7 @@ object UnitAutomation { // don't head straight to the city, try to head to landing grounds - // this is against tha AI's brilliant plan of having everyone embarked and attacking via sea when unnecessary. val tileToHeadTo = closestReachableEnemyCity.getTilesInDistanceRange(3..4) - .filter { it.isLand } + .filter { it.isLand && unit.getDamageFromTerrain(it) <= 0 } // Don't head for hurty terrain .sortedBy { it.aerialDistanceTo(unit.currentTile) } .firstOrNull { unit.movement.canReach(it) } @@ -500,7 +512,7 @@ object UnitAutomation { } /** Returns whether the civilian spends its turn hiding and not moving */ - fun tryRunAwayIfNeccessary(unit: MapUnit): Boolean { + fun tryRunAwayIfNeccessary(unit: MapUnit): Boolean { // This is a little 'Bugblatter Beast of Traal': Run if we can attack an enemy // Cheaper than determining which enemies could attack us next turn //todo - stay when we're stacked with a good military unit??? @@ -534,7 +546,7 @@ object UnitAutomation { return } val tileFurthestFromEnemy = reachableTiles.keys - .filter { unit.movement.canMoveTo(it) } + .filter { unit.movement.canMoveTo(it) && unit.getDamageFromTerrain(it) < unit.health } .maxByOrNull { countDistanceToClosestEnemy(unit, it) } ?: return // can't move anywhere! unit.movement.moveToTile(tileFurthestFromEnemy) diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 636e659bc7..e46babc1c0 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -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 @@ -97,6 +98,12 @@ class CivilizationInfo { @Transient private var cachedMilitaryMight = -1 + @Transient + var passThroughImpassableUnlocked = false // Cached Boolean equal to passableImpassables.isNotEmpty() + + @Transient + var nonStandardTerrainDamage = false + var playerType = PlayerType.AI /** Used in online multiplayer for human players */ @@ -149,6 +156,8 @@ class CivilizationInfo { // default false once we no longer want legacy save-game compatibility var hasEverOwnedOriginalCapital: Boolean? = null + val passableImpassables = HashSet() // For Carthage-like uniques + // For Aggressor, Warmonger status private var numMinorCivsAttacked = 0 @@ -197,6 +206,7 @@ class CivilizationInfo { toReturn.boughtConstructionsWithGloballyIncreasingPrice.putAll(boughtConstructionsWithGloballyIncreasingPrice) // toReturn.hasEverOwnedOriginalCapital = hasEverOwnedOriginalCapital + toReturn.passableImpassables.addAll(passableImpassables) toReturn.numMinorCivsAttacked = numMinorCivsAttacked return toReturn } @@ -643,6 +653,11 @@ class CivilizationInfo { cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo cityInfo.setTransients() } + + passThroughImpassableUnlocked = passableImpassables.isNotEmpty() + // Cache whether this civ gets nonstandard terrain damage for performance reasons. + nonStandardTerrainDamage = getMatchingUniques("Units ending their turn on [] tiles take [] damage") + .any { gameInfo.ruleSet.terrains[it.params[0]]!!.damagePerTurn != it.params[1].toInt() } } fun updateSightAndResources() { @@ -913,6 +928,13 @@ class CivilizationInfo { } placedUnit.setupAbilityUses() } + + for (unique in getMatchingUniques("Land units may cross [] tiles after the first [] is earned")) { + if (unit.matchesFilter(unique.params[1])) { + passThroughImpassableUnlocked = true // Update the cached Boolean + passableImpassables.add(unique.params[0]) // Add to list of passable impassables + } + } return placedUnit } diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 0df7532e70..bcc3161956 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -672,8 +672,8 @@ class MapUnit { } } - getCitadelDamage() - getTerrainDamage() + doCitadelDamage() + doTerrainDamage() } fun startTurn() { @@ -882,30 +882,38 @@ class MapUnit { return damageFactor } - private fun getTerrainDamage() { - // hard coded mountain damage for now - if (getTile().baseTerrain == Constants.mountain) { - val tileDamage = 50 - health -= tileDamage + private fun doTerrainDamage() { + val tileDamage = getDamageFromTerrain() + health -= tileDamage - if (health <= 0) { - civInfo.addNotification( - "Our [$name] took [$tileDamage] tile damage and was destroyed", - currentTile.position, - name, - NotificationIcon.Death - ) - destroy() - } else civInfo.addNotification( - "Our [$name] took [$tileDamage] tile damage", + if (health <= 0) { + civInfo.addNotification( + "Our [$name] took [$tileDamage] tile damage and was destroyed", currentTile.position, - name + name, + NotificationIcon.Death ) - } - + destroy() + } else if (tileDamage > 0) civInfo.addNotification( + "Our [$name] took [$tileDamage] tile damage", + currentTile.position, + name + ) } - private fun getCitadelDamage() { + fun getDamageFromTerrain(tile: TileInfo = currentTile): Int { + if (civInfo.nonStandardTerrainDamage) { + for (unique in getMatchingUniques("Units ending their turn on [] tiles take [] damage")) { + if (unique.params[0] in tile.getAllTerrains().map { it.name }) { + return unique.params[1].toInt() // Use the damage from the unique + } + } + } + // Otherwise fall back to the defined standard damage + return tile.getAllTerrains().sumBy { it.damagePerTurn } + } + + private fun doCitadelDamage() { // Check for Citadel damage - note: 'Damage does not stack with other Citadels' val citadelTile = currentTile.neighbors .firstOrNull { diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index a3fdfe4f3d..8a0160228a 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -167,7 +167,13 @@ class UnitMovementAlgorithms(val unit:MapUnit) { * Does not consider if the [destination] tile can actually be entered, use [canMoveTo] for that. * Returns an empty list if there's no way to get to the destination. */ - fun getShortestPath(destination: TileInfo): List { + fun getShortestPath(destination: TileInfo, avoidDamagingTerrain: Boolean = false): List { + // First try and find a path without damaging terrain + if (!avoidDamagingTerrain && unit.civInfo.passThroughImpassableUnlocked && unit.baseUnit.isLandUnit()) { + val damageFreePath = getShortestPath(destination, true) + if (damageFreePath.isNotEmpty()) return damageFreePath + } + val currentTile = unit.getTile() if (currentTile.position == destination) return listOf(currentTile) // edge case that's needed, so that workers will know that they can reach their own tile. *sigh* @@ -187,6 +193,9 @@ class UnitMovementAlgorithms(val unit:MapUnit) { for (tileToCheck in tilesToCheck) { val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn) for (reachableTile in distanceToTilesThisTurn.keys) { + // Avoid damaging terrain on first pass + if (avoidDamagingTerrain && unit.getDamageFromTerrain(reachableTile) > 0) + continue if (reachableTile == destination) distanceToDestination[tileToCheck] = distanceToTilesThisTurn[reachableTile]!!.totalDistance else { @@ -546,7 +555,9 @@ class UnitMovementAlgorithms(val unit:MapUnit) { if (tile.isImpassible()) { // special exception - ice tiles are technically impassible, but some units can move through them anyway // helicopters can pass through impassable tiles like mountains - if (!(tile.terrainFeatures.contains(Constants.ice) && unit.canEnterIceTiles) && !unit.canPassThroughImpassableTiles) + if (!(tile.terrainFeatures.contains(Constants.ice) && unit.canEnterIceTiles) && !unit.canPassThroughImpassableTiles + // carthage-like uniques sometimes allow passage through impassible tiles + && !(unit.civInfo.passThroughImpassableUnlocked && unit.civInfo.passableImpassables.contains(tile.getLastTerrain().name))) return false } if (tile.isLand diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index c4aad507a7..ee57e69285 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -170,7 +170,10 @@ class Ruleset { if (buildingsFile.exists()) buildings += createHashmap(jsonParser.getFromJson(Array::class.java, buildingsFile)) val terrainsFile = folderHandle.child("Terrains.json") - if (terrainsFile.exists()) terrains += createHashmap(jsonParser.getFromJson(Array::class.java, terrainsFile)) + if (terrainsFile.exists()) { + terrains += createHashmap(jsonParser.getFromJson(Array::class.java, terrainsFile)) + for (terrain in terrains.values) terrain.setTransients() + } val resourcesFile = folderHandle.child("TileResources.json") if (resourcesFile.exists()) tileResources += createHashmap(jsonParser.getFromJson(Array::class.java, resourcesFile)) diff --git a/core/src/com/unciv/models/ruleset/tile/Terrain.kt b/core/src/com/unciv/models/ruleset/tile/Terrain.kt index 8712e6aad8..76c7305c1e 100644 --- a/core/src/com/unciv/models/ruleset/tile/Terrain.kt +++ b/core/src/com/unciv/models/ruleset/tile/Terrain.kt @@ -40,6 +40,9 @@ class Terrain : NamedStats(), ICivilopediaText, IHasUniques { var defenceBonus:Float = 0f var impassable = false + @Transient + var damagePerTurn = 0 + override var civilopediaText = listOf() fun isRough(): Boolean = uniques.contains("Rough terrain") @@ -141,4 +144,10 @@ class Terrain : NamedStats(), ICivilopediaText, IHasUniques { return textList } + + fun setTransients() { + damagePerTurn = uniqueObjects.sumBy { + if (it.placeholderText == "Units ending their turn on this terrain take [] damage") it.params[0].toInt() else 0 + } + } } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index a763ca4900..a15361bff2 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -161,7 +161,7 @@ object UnitActions { * (no movement left, too close to another city). */ fun getFoundCityAction(unit: MapUnit, tile: TileInfo): UnitAction? { - if (!unit.hasUnique("Founds a new city") || tile.isWater) return null + if (!unit.hasUnique("Founds a new city") || tile.isWater || tile.isImpassible()) return null if (unit.currentMovement <= 0 || tile.getTilesInDistance(3).any { it.isCityCenter() }) return UnitAction(UnitActionType.FoundCity, action = null) diff --git a/docs/Credits.md b/docs/Credits.md index 8b99c8006c..40f273258a 100644 --- a/docs/Credits.md +++ b/docs/Credits.md @@ -42,6 +42,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https: * [Bow](https://thenounproject.com/search/?q=bow&i=101736) By Arthur Shlain for Bowman * [Fishing Vessel](https://thenounproject.com/term/fishing-vessel/23815/) By Luis Prado for Work Boats * [Greek Trireme](https://thenounproject.com/search/?q=ancient%20boat&i=1626303) By Zachary McCune for Trireme +* [Greek Trireme](https://thenounproject.com/search/?q=ancient%20boat&i=1626303) By Zachary McCune for Quinquereme * [Chariot](https://thenounproject.com/search/?q=Chariot&i=1189930) By Andrew Doane for Chariot Archer * [Elephant](https://thenounproject.com/Luis/uploads/?i=14048) By Luis Prado for War Elephant * [Centaur](https://thenounproject.com/search/?q=horse+archer&i=1791296) by Michael Wohlwend for Horse Archer @@ -62,6 +63,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https: * [Roman Helmet](https://thenounproject.com/search/?q=legion&i=440134) By parkjisun for Legion * [Horse](https://thenounproject.com/search/?q=Horse&i=1373793) By AFY Studio for Horseman * [Horse Head](https://thenounproject.com/search/?q=Cavalry&i=374037) By Juan Pablo Bravo for Companion Cavalry +* [Elephant](https://thenounproject.com/term/elephant/1302749) By Angriawan Ditya Zulkarnain for African Forest Elephant * [Judge](https://thenounproject.com/search/?q=judge&i=1076388) By Krisztián Mátyás for Courthouse * [Petra](https://thenounproject.com/search/?q=petra&i=2855893) By Ranah Pixel Studio for Petra @@ -553,6 +555,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https: * [Lion](https://thenounproject.com/search/?q=lion&i=76154) by Nikki Rodriguez for The Netherlands * [Three Crowns](https://thenounproject.com/search/?q=three+crowns&i=1155972) by Daniel Falk for Sweden * [Flag of Austria](https://thenounproject.com/term/flag-of-austria/3292053/) by Olena Panasovska, UA for Austria +* [Elephant](https://thenounproject.com/term/elephant/564421/) by Hea Poh Lin for Carthage ## Promotions