Resolved #646 - Barbarians now spawn from Encampments!

This commit is contained in:
Yair Morgenstern 2019-05-31 17:36:19 +03:00
parent b068e6f88f
commit 4b4f5e77b8
14 changed files with 489 additions and 421 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 991 KiB

After

Width:  |  Height:  |  Size: 998 KiB

View File

@ -412,7 +412,7 @@
"Bodhgaya","Kushinagar","Amaravati","Gaur","Gwalior","Jaipur","Karachi"]
},
/*
{
{ // REQUIRES BARBARIAN CAMPS
name:"Germany",
leaderName:"Otto von Bismark",
adjective:["German"],
@ -450,14 +450,14 @@
"Recklinghausen","Gצttingen","Wolfsburg","Koblenz","Hildesheim","Erlangen"]
},
{
{ // REQUIRES RIVERS
name:"Aztecs",
leaderName:"Montezuma I",
adjective:["Aztec"],
startBias:["Jungle"],
startIntroPart1: "Welcome, o divine Montezuma! We grovel in awe at your magnificence! May the heaven shower all manner of good things upon you all the days of your life! Your are the leader of the mighty Aztec people, wandering nomads from a lost home in the north who in the 12th century came to live in the mesa central in the heart of what would come to be call Mexico. Surrounded by many tribes fighting to control the rich land surrounding the sacred lakes of Texoco, Xaltocan and Zampango. Through cunning alliances and martial prowess, within a mere two hundred years, the Aztecs came to dominate the Central American basin, ruling a mighty empire stretching from sea to sea. But the empire fell soon under the assault of the accursed Spaniards, wielding fiendish weapons the likes of which your faithful warriors had never seen."
startIntroPart2: "O great king Mintezuma, your people call upon you once more, to rise up and lead them to glory, bring them wealth and power, and give them dominion over their foes and rivals. Will you answer their call, glorious leader? Will you build a civilization that stands the test of time?"
startIntroPart2: "O great king Montezuma, your people call upon you once more, to rise up and lead them to glory, bring them wealth and power, and give them dominion over their foes and rivals. Will you answer their call, glorious leader? Will you build a civilization that stands the test of time?"
declaringWar:"Xi-miqa-can! Xi-miqa-can! Xi-miqa-can! (Die, die, die!)"
attacked:"Excellent! Let the blood flow in raging torrents!"

View File

@ -145,10 +145,7 @@
improvingTechStats:{gold:1}
},
{
name:"Ancient ruins"
},
{
name:"City ruins"
}
{ name:"Ancient ruins" },
{ name:"City ruins" },
{ name:"Barbarian encampment" },
]

View File

@ -10,5 +10,7 @@ class Constants{
const val jungle = "Jungle"
const val hill = "Hill"
const val peaceTreaty = "Peace Treaty"
const val barbarianEncampment = "Barbarian encampment"
const val ancientRuins = "Ancient ruins"
}
}

View File

@ -17,7 +17,7 @@ class UnCivGame(val version: String) : Game() {
* This exists so that when debugging we can see the entire map.
* Remember to turn this to false before commit and upload!
*/
val viewEntireMapForDebug = false
val viewEntireMapForDebug = true
// For when you need to test something in an advanced game and don't have time to faff around
val superchargedForDebug = false

View File

@ -1,6 +1,7 @@
package com.unciv.logic
import com.badlogic.gdx.graphics.Color
import com.unciv.Constants
import com.unciv.GameParameters
import com.unciv.logic.automation.NextTurnAutomation
import com.unciv.logic.city.CityConstructions
@ -11,6 +12,7 @@ import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap
import com.unciv.models.gamebasics.Difficulty
import com.unciv.models.gamebasics.GameBasics
import java.util.*
class GameInfo {
@Transient lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn
@ -51,9 +53,25 @@ class GameInfo {
currentPlayerIndex = (currentPlayerIndex+1) % civilizations.size
if(currentPlayerIndex==0){
turns++
if (turns % 10 == 0 && !gameParameters.noBarbarians) { // every 10 turns add a barbarian in a random place
placeBarbarianUnit(null)
if (turns % 10 == 0 && !gameParameters.noBarbarians) {
val encampments = tileMap.values.filter { it.improvement==Constants.barbarianEncampment }
if(encampments.size < civilizations.filter { it.isMajorCiv() }.size*2) {
val newEncampmentTile = placeBarbarianEncampment(encampments)
if (newEncampmentTile != null)
placeBarbarianUnit(newEncampmentTile)
}
val totalBarbariansAllowedOnMap = encampments.size*3
var extraBarbarians = totalBarbariansAllowedOnMap - getBarbarianCivilization().getCivUnits().size
for (tile in tileMap.values.filter { it.improvement == Constants.barbarianEncampment }) {
if(extraBarbarians<=0) break
extraBarbarians--
placeBarbarianUnit(tile)
}
}
}
thisPlayer = civilizations[currentPlayerIndex]
thisPlayer.startTurn()
@ -105,19 +123,24 @@ class GameInfo {
}
}
fun placeBarbarianUnit(tileToPlace: TileInfo?) {
var tile = tileToPlace
if (tileToPlace == null) {
fun placeBarbarianEncampment(existingEncampments: List<TileInfo>): TileInfo? {
// Barbarians will only spawn in places that no one can see
val allViewableTiles = civilizations.filterNot { it.isBarbarianCivilization() }
.flatMap { it.viewableTiles }.toHashSet()
val viableTiles = tileMap.values.filter { it.militaryUnit == null
&& it.civilianUnit == null && !it.getBaseTerrain().impassable && it.baseTerrain!="Lakes"
&& !allViewableTiles.contains(it)}
if (viableTiles.isEmpty()) return // no place for more barbs =(
tile = viableTiles.random()
val tilesWithin3ofExistingEncampment = existingEncampments.flatMap { it.getTilesInDistance(3) }
val viableTiles = tileMap.values.filter {
!it.getBaseTerrain().impassable && it.isLand
&& it.terrainFeature==null
&& it !in tilesWithin3ofExistingEncampment
&& it !in allViewableTiles
}
if (viableTiles.isEmpty()) return null // no place for more barbs =(
val tile = viableTiles.random()
tile.improvement = Constants.barbarianEncampment
return tile
}
fun placeBarbarianUnit(tileToPlace: TileInfo) {
// if we don't make this into a separate list then the retain() will happen on the Tech keys,
// which effectively removes those techs from the game and causes all sorts of problems
val allResearchedTechs = GameBasics.Technologies.keys.toMutableList()
@ -127,9 +150,17 @@ class GameInfo {
val unitList = GameBasics.Units.values.filter { !it.unitType.isCivilian() && it.uniqueTo == null }
.filter{ allResearchedTechs.contains(it.requiredTech)
&& (it.obsoleteTech == null || !allResearchedTechs.contains(it.obsoleteTech!!)) }
val unit = if (unitList.isEmpty()) "Warrior" else unitList.random().name
tileMap.placeUnitNearTile(tile!!.position, unit, getBarbarianCivilization())
val landUnits = unitList.filter { it.unitType.isLandUnit() }
val waterUnits = unitList.filter { it.unitType.isWaterUnit() }
val unit:String
if (unitList.isEmpty()) unit="Warrior"
else if(waterUnits.isNotEmpty() && tileToPlace.neighbors.any{ it.baseTerrain=="Coast" } && Random().nextBoolean())
unit=waterUnits.random().name
else unit = landUnits.random().name
tileMap.placeUnitNearTile(tileToPlace.position, unit, getBarbarianCivilization())
}
// All cross-game data which needs to be altered (e.g. when removing or changing a name of a building/tech)

View File

@ -29,7 +29,7 @@ class SpecificUnitAutomation{
return createImprovementAction.action() // unit is already gone, can't "Explore"
}
}
else UnitAutomation().explore(unit, unit.getDistanceToTiles())
else UnitAutomation().tryExplore(unit, unit.getDistanceToTiles())
}
fun automateGreatGeneral(unit: MapUnit){
@ -95,8 +95,11 @@ class SpecificUnitAutomation{
.sortedByDescending { rankTileAsCityCenter(it, nearbyTileRankings) }
.firstOrNull { unit.movementAlgs().canReach(it) }
if(bestCityLocation==null) // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
return UnitAutomation().explore(unit, unit.getDistanceToTiles())
if(bestCityLocation==null) { // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
if(UnitAutomation().tryExplore(unit, unit.getDistanceToTiles())) return // try to find new areas
UnitAutomation().wander(unit, unit.getDistanceToTiles()) // go around aimlessly
return
}
if(bestCityLocation.getTilesInDistance(3).any { it.isCityCenter() })
throw Exception("City within distance")

View File

@ -40,6 +40,10 @@ class UnitAutomation{
val unitActions = UnitActions().getUnitActions(unit,UnCivGame.Current.worldScreen)
var unitDistanceToTiles = unit.getDistanceToTiles()
if(unit.civInfo.isBarbarianCivilization() &&
unit.currentTile.improvement==Constants.barbarianEncampment && unit.type.isLandUnit())
return // stay in the encampment
if(tryGoToRuin(unit,unitDistanceToTiles)){
if(unit.currentMovement==0f) return
unitDistanceToTiles = unit.getDistanceToTiles()
@ -61,7 +65,7 @@ class UnitAutomation{
if (tryAttackNearbyEnemy(unit)) return
// Barbarians try to pillage improvements if no targets reachable
if (unit.civInfo.isBarbarianCivilization() && pillageImprovement(unit, unitDistanceToTiles)) return
if (unit.civInfo.isBarbarianCivilization() && tryPillageImprovement(unit, unitDistanceToTiles)) return
if (tryGarrisoningUnit(unit)) return
@ -75,17 +79,21 @@ class UnitAutomation{
// Focus all units without a specific target on the enemy city closest to one of our cities
if (tryHeadTowardsEnemyCity(unit)) return
// else, go to a random space
explore(unit,unitDistanceToTiles)
// if both failed, then... there aren't any reachable tiles. Which is possible.
// else, try to go o unreached tiles
if(tryExplore(unit,unitDistanceToTiles)) return
// Barbarians just wander all over the place
if(unit.civInfo.isBarbarianCivilization())
wander(unit,unitDistanceToTiles)
}
fun tryHealUnit(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>):Boolean {
val tilesInDistance = unitDistanceToTiles.keys.filter { unit.canMoveTo(it) }
val unitTile = unit.getTile()
if (pillageImprovement(unit, unitDistanceToTiles)) return true
if (tryPillageImprovement(unit, unitDistanceToTiles)) return true
val tilesByHealingRate = tilesInDistance.groupBy { unit.rankTileForHealing(it) }
if(tilesByHealingRate.isEmpty()) return false
@ -105,7 +113,7 @@ class UnitAutomation{
return true
}
fun pillageImprovement(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) : Boolean {
fun tryPillageImprovement(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) : Boolean {
val tilesInDistance = unitDistanceToTiles.filter {it.value < unit.currentMovement}.keys
.filter { unit.canMoveTo(it) && it.improvement != null && UnitActions().canPillage(unit,it) }
@ -114,8 +122,8 @@ class UnitAutomation{
if (unit.getTile()!=tileToPillage)
unit.moveToTile(tileToPillage)
UnitActions().getUnitActions(unit,UnCivGame.Current.worldScreen)
.first { it.name=="Pillage" }.action()
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen)
.first { it.name == "Pillage" }.action()
return true
}
@ -125,7 +133,7 @@ class UnitAutomation{
if (combatant.isRanged()) return false
if (tile.isWater) return false // can't attack water units while embarked, only land
}
if (combatant.unit.hasUnique("Can only attack water") && tile.isLand) return false
if (tile.isLand && combatant.unit.hasUnique("Can only attack water")) return false
}
val tileCombatant = Battle(combatant.getCivInfo().gameInfo).getMapCombatantOfTile(tile)
@ -391,24 +399,26 @@ class UnitAutomation{
}
fun tryGoToRuin(unit:MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
val tileWithRuin = unitDistanceToTiles.keys.firstOrNull{unit.canMoveTo(it) && it.improvement == "Ancient ruins"}
if(unit.civInfo.isBarbarianCivilization()) return false // barbs don't have anything to do in ruins
val tileWithRuin = unitDistanceToTiles.keys.firstOrNull{unit.canMoveTo(it) && it.improvement == Constants.ancientRuins}
if(tileWithRuin==null) return false
unit.moveToTile(tileWithRuin)
return true
}
internal fun explore(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) {
internal fun tryExplore(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
if(tryGoToRuin(unit,unitDistanceToTiles))
{
if(unit.currentMovement==0f) return
if(unit.currentMovement==0f) return true
}
for(tile in unit.currentTile.getTilesInDistance(5))
if(unit.canMoveTo(tile) && tile.position !in unit.civInfo.exploredTiles
&& unit.movementAlgs().canReach(tile)){
unit.movementAlgs().headTowards(tile)
return
return true
}
return false
}
fun automatedExplore(unit:MapUnit){
@ -432,4 +442,15 @@ class UnitAutomation{
unit.civInfo.addNotification("[${unit.name}] finished exploring.", unit.currentTile.position, Color.GRAY)
}
fun wander(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) {
val reachableTiles= unitDistanceToTiles
.filter { unit.canMoveTo(it.key) && unit.movementAlgs().canReach(it.key) }
val reachableTilesMaxWalkingDistance = reachableTiles.filter { it.value == unit.currentMovement }
if (reachableTilesMaxWalkingDistance.any()) unit.moveToTile(reachableTilesMaxWalkingDistance.toList().random().first)
else if (reachableTiles.any()) unit.moveToTile(reachableTiles.toList().random().first)
}
}

View File

@ -456,8 +456,10 @@ class MapUnit {
tile.civilianUnit=this
else tile.militaryUnit=this
currentTile = tile
if(tile.improvement=="Ancient ruins" && !civInfo.isBarbarianCivilization())
if(tile.improvement==Constants.ancientRuins && !civInfo.isBarbarianCivilization())
getAncientRuinBonus()
if(tile.improvement==Constants.barbarianEncampment && !civInfo.isBarbarianCivilization())
tile.improvement=null // todo get bonus from clearing encampment
updateViewableTiles()
}

View File

@ -1,6 +1,7 @@
package com.unciv.logic.map
import com.badlogic.gdx.math.Vector2
import com.unciv.Constants
import com.unciv.Constants.Companion.mountain
import com.unciv.Constants.Companion.ocean
import com.unciv.logic.HexMath
@ -488,7 +489,7 @@ open class RandomMapGenerator {
fun maybeAddAncientRuins(tile: TileInfo) {
val baseTerrain = tile.getBaseTerrain()
if(baseTerrain.type!=TerrainType.Water && !baseTerrain.impassable && Random().nextDouble() < 1f/100)
tile.improvement = "Ancient ruins"
tile.improvement = Constants.ancientRuins
}

View File

@ -51,15 +51,13 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
}
val otherCivsWeKnow = civInfo.getKnownCivs()
.filter { it.civName != otherCivilization.civName && !it.isBarbarianCivilization() && !it.isDefeated() }
.filter { it.civName != otherCivilization.civName && it.isMajorCiv() && !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
@ -105,11 +103,11 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
if(offer.name==Constants.peaceTreaty) to.getDiplomacyManager(from).makePeace()
}
if(offer.type==TradeType.Introduction)
to.meetCivilization(to.gameInfo.getCivilization(offer.name.split(" ")[2]))
to.meetCivilization(to.gameInfo.getCivilization(offer.name.removePrefix("Introduction to " )))
if(offer.type==TradeType.WarDeclaration){
val nameOfCivToDeclareWarOn = offer.name.split(' ').last()
from.diplomacy[nameOfCivToDeclareWarOn]!!.declareWar()
val nameOfCivToDeclareWarOn = offer.name.removePrefix("Declare war on ")
from.getDiplomacyManager(nameOfCivToDeclareWarOn).declareWar()
}
}
}

View File

@ -10,6 +10,7 @@ import com.badlogic.gdx.scenes.scene2d.InputEvent
import com.badlogic.gdx.scenes.scene2d.actions.FloatAction
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener
import com.unciv.Constants
import com.unciv.UnCivGame
import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.city.CityInfo
@ -216,6 +217,9 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|| (!tileGroup.tileInfo.hasEnemySubmarine())
tileGroup.update(canSeeTile, showSubmarine)
if(tileGroup.tileInfo.improvement==Constants.barbarianEncampment)
tileGroup.showCircle(Color.RED)
val unitsInTile = tileGroup.tileInfo.getUnits()
val canSeeEnemy = unitsInTile.isNotEmpty() && unitsInTile.first().civInfo.isAtWarWith(civInfo)
&& (showSubmarine || unitsInTile.firstOrNull {!it.isInvisible()}!=null)
@ -266,7 +270,9 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
else 0.5f
for (tile in tileGroups.values) {
if (tile.populationImage != null) tile.populationImage!!.color.a = fadeout
if (tile.improvementImage != null) tile.improvementImage!!.color.a = fadeout
if (tile.improvementImage != null && tile.tileInfo.improvement!=Constants.barbarianEncampment
&& tile.tileInfo.improvement!=Constants.ancientRuins)
tile.improvementImage!!.color.a = fadeout
if (tile.resourceImage != null) tile.resourceImage!!.color.a = fadeout
}
}