mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-07 00:41:39 +07:00
Terraforming! (#11152)
* Terraforming! * comment * whoops * And better tests, that actually output text. * SomeTrog caught this doc error :) * Fix natural wonder placement, it better belongs with base terrain Also, normalize tiles to ruleset after plonking a new terrain * Out Of Cheese Error, Redo From Start - Only terrains, no improvements - one problem at a time - Fit new functional trigger format - Respect occursOn - Teleport units out of tiles if they can't pass through (nat wonders for example) - No need for terrainFeature unique parameter - Tested with "Turn this tile into a [Grand Mesa] tile <for [0] movement>", "Turn this tile into a [Forest] tile <for [0] movement>" uniques * Don't allow city tiles to turn into water/wonders etc kudos @SomeTrog
This commit is contained in:
@ -151,18 +151,10 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
|
|||||||
val targetGroupSize = if (minGroupSize == maxGroupSize) maxGroupSize
|
val targetGroupSize = if (minGroupSize == maxGroupSize) maxGroupSize
|
||||||
else (minGroupSize..maxGroupSize).random(randomness.RNG)
|
else (minGroupSize..maxGroupSize).random(randomness.RNG)
|
||||||
|
|
||||||
var convertNeighborsExcept: String? = null
|
|
||||||
var convertUnique = wonder.getMatchingUniques(UniqueType.NaturalWonderConvertNeighbors).firstOrNull()
|
|
||||||
var convertNeighborsTo = convertUnique?.params?.get(0)
|
|
||||||
if (convertNeighborsTo == null) {
|
|
||||||
convertUnique = wonder.getMatchingUniques(UniqueType.NaturalWonderConvertNeighborsExcept).firstOrNull()
|
|
||||||
convertNeighborsExcept = convertUnique?.params?.get(0)
|
|
||||||
convertNeighborsTo = convertUnique?.params?.get(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (suitableLocations.size >= minGroupSize) {
|
if (suitableLocations.size >= minGroupSize) {
|
||||||
val location = suitableLocations.random(randomness.RNG)
|
val location = suitableLocations.random(randomness.RNG)
|
||||||
val list = mutableListOf(location)
|
val list = mutableListOf(location)
|
||||||
|
|
||||||
while (list.size < targetGroupSize) {
|
while (list.size < targetGroupSize) {
|
||||||
val allNeighbors = list.flatMap { it.neighbors }.minus(list).toHashSet()
|
val allNeighbors = list.flatMap { it.neighbors }.minus(list).toHashSet()
|
||||||
val candidates = suitableLocations.filter { it in allNeighbors }
|
val candidates = suitableLocations.filter { it in allNeighbors }
|
||||||
@ -171,30 +163,11 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
|
|||||||
}
|
}
|
||||||
if (list.size >= minGroupSize) {
|
if (list.size >= minGroupSize) {
|
||||||
list.forEach {
|
list.forEach {
|
||||||
clearTile(it)
|
placeNaturalWonder(wonder, location)
|
||||||
it.naturalWonder = wonder.name
|
|
||||||
if (wonder.turnsInto != null)
|
|
||||||
it.baseTerrain = wonder.turnsInto!!
|
|
||||||
// Add all tiles within a certain distance to a blacklist so NW:s don't cluster
|
// Add all tiles within a certain distance to a blacklist so NW:s don't cluster
|
||||||
blockedTiles.addAll(it.getTilesInDistance(it.tileMap.mapParameters.mapSize.height / 5))
|
blockedTiles.addAll(it.getTilesInDistance(it.tileMap.mapParameters.mapSize.height / 5))
|
||||||
}
|
}
|
||||||
if (convertNeighborsTo != null) {
|
|
||||||
for (tile in location.neighbors) {
|
|
||||||
if (tile.baseTerrain == convertNeighborsTo) continue
|
|
||||||
if (tile.baseTerrain == convertNeighborsExcept) continue
|
|
||||||
if (convertNeighborsTo == Constants.coast)
|
|
||||||
for (neighbor in tile.neighbors) {
|
|
||||||
// This is so we don't have this tile turn into Coast, and then it's touching a Lake tile.
|
|
||||||
// We just turn the lake tiles into this kind of tile.
|
|
||||||
if (neighbor.baseTerrain == Constants.lakes) {
|
|
||||||
neighbor.baseTerrain = tile.baseTerrain
|
|
||||||
neighbor.setTerrainTransients()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tile.baseTerrain = convertNeighborsTo
|
|
||||||
clearTile(tile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug("Natural Wonder %s @%s", wonder.name, location.position)
|
debug("Natural Wonder %s @%s", wonder.name, location.position)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -205,11 +178,47 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clearTile(tile: Tile) {
|
companion object {
|
||||||
tile.setTerrainFeatures(listOf())
|
fun placeNaturalWonder(wonder: Terrain, location: Tile) {
|
||||||
tile.resource = null
|
clearTile(location)
|
||||||
tile.removeImprovement()
|
location.naturalWonder = wonder.name
|
||||||
tile.setTerrainTransients()
|
if (wonder.turnsInto != null)
|
||||||
|
location.baseTerrain = wonder.turnsInto!!
|
||||||
|
|
||||||
|
var convertNeighborsExcept: String? = null
|
||||||
|
var convertUnique = wonder.getMatchingUniques(UniqueType.NaturalWonderConvertNeighbors).firstOrNull()
|
||||||
|
var convertNeighborsTo = convertUnique?.params?.get(0)
|
||||||
|
if (convertNeighborsTo == null) {
|
||||||
|
convertUnique = wonder.getMatchingUniques(UniqueType.NaturalWonderConvertNeighborsExcept).firstOrNull()
|
||||||
|
convertNeighborsExcept = convertUnique?.params?.get(0)
|
||||||
|
convertNeighborsTo = convertUnique?.params?.get(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convertNeighborsTo != null) {
|
||||||
|
for (tile in location.neighbors) {
|
||||||
|
if (tile.baseTerrain == convertNeighborsTo) continue
|
||||||
|
if (tile.baseTerrain == convertNeighborsExcept) continue
|
||||||
|
if (convertNeighborsTo == Constants.coast)
|
||||||
|
for (neighbor in tile.neighbors) {
|
||||||
|
// This is so we don't have this tile turn into Coast, and then it's touching a Lake tile.
|
||||||
|
// We just turn the lake tiles into this kind of tile.
|
||||||
|
if (neighbor.baseTerrain == Constants.lakes) {
|
||||||
|
neighbor.baseTerrain = tile.baseTerrain
|
||||||
|
neighbor.setTerrainTransients()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tile.baseTerrain = convertNeighborsTo
|
||||||
|
clearTile(tile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearTile(tile: Tile) {
|
||||||
|
tile.setTerrainFeatures(listOf())
|
||||||
|
tile.resource = null
|
||||||
|
tile.removeImprovement()
|
||||||
|
tile.setTerrainTransients()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Implements [UniqueParameterType.SimpleTerrain][com.unciv.models.ruleset.unique.UniqueParameterType.SimpleTerrain] */
|
/** Implements [UniqueParameterType.SimpleTerrain][com.unciv.models.ruleset.unique.UniqueParameterType.SimpleTerrain] */
|
||||||
|
@ -26,6 +26,7 @@ import com.unciv.models.ruleset.unique.UniqueMap
|
|||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.ui.components.extensions.withItem
|
import com.unciv.ui.components.extensions.withItem
|
||||||
import com.unciv.ui.components.extensions.withoutItem
|
import com.unciv.ui.components.extensions.withoutItem
|
||||||
|
import com.unciv.ui.screens.mapeditorscreen.TileInfoNormalizer
|
||||||
import com.unciv.utils.DebugUtils
|
import com.unciv.utils.DebugUtils
|
||||||
import com.unciv.utils.Log
|
import com.unciv.utils.Log
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
@ -832,6 +833,14 @@ open class Tile : IsPartOfGameInfoSerialization {
|
|||||||
else unitHeight
|
else unitHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setBaseTerrain(baseTerrainObject: Terrain){
|
||||||
|
baseTerrain = baseTerrainObject.name
|
||||||
|
this.baseTerrainObject = baseTerrainObject
|
||||||
|
TileInfoNormalizer.normalizeToRuleset(this, ruleset)
|
||||||
|
setTerrainFeatures(terrainFeatures)
|
||||||
|
setTerrainTransients()
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateUniqueMap() {
|
private fun updateUniqueMap() {
|
||||||
if (!::tileMap.isInitialized) return // This tile is a fake tile, for visual display only (e.g. map editor, civilopedia)
|
if (!::tileMap.isInitialized) return // This tile is a fake tile, for visual display only (e.g. map editor, civilopedia)
|
||||||
val terrainNameList = allTerrains.map { it.name }.toList()
|
val terrainNameList = allTerrains.map { it.name }.toList()
|
||||||
|
@ -375,7 +375,7 @@ enum class UniqueParameterType(
|
|||||||
/** Used by [NaturalWonderGenerator.trySpawnOnSuitableLocation][com.unciv.logic.map.mapgenerator.NaturalWonderGenerator.trySpawnOnSuitableLocation], only tests base terrain */
|
/** Used by [NaturalWonderGenerator.trySpawnOnSuitableLocation][com.unciv.logic.map.mapgenerator.NaturalWonderGenerator.trySpawnOnSuitableLocation], only tests base terrain */
|
||||||
BaseTerrain("baseTerrain", Constants.grassland, "The name of any terrain that is a base terrain according to the json file") {
|
BaseTerrain("baseTerrain", Constants.grassland, "The name of any terrain that is a base terrain according to the json file") {
|
||||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||||
UniqueType.UniqueParameterErrorSeverity? {
|
UniqueType.UniqueParameterErrorSeverity? {
|
||||||
if (ruleset.terrains[parameterText]?.type?.isBaseTerrain == true) return null
|
if (ruleset.terrains[parameterText]?.type?.isBaseTerrain == true) return null
|
||||||
return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific
|
return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,18 @@ import com.unciv.logic.civilization.NotificationIcon
|
|||||||
import com.unciv.logic.civilization.PolicyAction
|
import com.unciv.logic.civilization.PolicyAction
|
||||||
import com.unciv.logic.civilization.TechAction
|
import com.unciv.logic.civilization.TechAction
|
||||||
import com.unciv.logic.civilization.managers.ReligionState
|
import com.unciv.logic.civilization.managers.ReligionState
|
||||||
|
import com.unciv.logic.map.mapgenerator.NaturalWonderGenerator
|
||||||
import com.unciv.logic.map.mapunit.MapUnit
|
import com.unciv.logic.map.mapunit.MapUnit
|
||||||
import com.unciv.logic.map.tile.Tile
|
import com.unciv.logic.map.tile.Tile
|
||||||
import com.unciv.models.UpgradeUnitAction
|
import com.unciv.models.UpgradeUnitAction
|
||||||
import com.unciv.models.ruleset.BeliefType
|
import com.unciv.models.ruleset.BeliefType
|
||||||
|
import com.unciv.models.ruleset.tile.TerrainType
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
import com.unciv.models.translations.fillPlaceholders
|
import com.unciv.models.translations.fillPlaceholders
|
||||||
import com.unciv.models.translations.hasPlaceholderParameters
|
import com.unciv.models.translations.hasPlaceholderParameters
|
||||||
import com.unciv.ui.components.extensions.addToMapOfSets
|
import com.unciv.ui.components.extensions.addToMapOfSets
|
||||||
|
import com.unciv.ui.screens.mapeditorscreen.TileInfoNormalizer
|
||||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade
|
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
@ -926,6 +929,28 @@ object UniqueTriggerActivation {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniqueType.OneTimeChangeTerrain -> {
|
||||||
|
if (tile == null) return null
|
||||||
|
val terrain = ruleSet.terrains[unique.params[0]] ?: return null
|
||||||
|
if (terrain.type == TerrainType.TerrainFeature && !terrain.occursOn.contains(tile.lastTerrain.name))
|
||||||
|
return null
|
||||||
|
if (tile.terrainFeatures.contains(terrain.name)) return null
|
||||||
|
if (tile.isCityCenter() && terrain.type != TerrainType.Land) return null
|
||||||
|
if (terrain.type.isBaseTerrain && tile.baseTerrain == terrain.name) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
when (terrain.type) {
|
||||||
|
TerrainType.Land, TerrainType.Water -> tile.setBaseTerrain(terrain)
|
||||||
|
TerrainType.TerrainFeature -> tile.addTerrainFeature(terrain.name)
|
||||||
|
TerrainType.NaturalWonder -> NaturalWonderGenerator.placeNaturalWonder(terrain, tile)
|
||||||
|
}
|
||||||
|
TileInfoNormalizer.normalizeToRuleset(tile, ruleSet)
|
||||||
|
tile.getUnits().filter { !it.movement.canPassThrough(tile) }.toList()
|
||||||
|
.forEach { it.movement.teleportToClosestMoveableTile() }
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> return null
|
else -> return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -761,6 +761,8 @@ enum class UniqueType(
|
|||||||
SkipPromotion("Doing so will consume this opportunity to choose a Promotion", UniqueTarget.Promotion),
|
SkipPromotion("Doing so will consume this opportunity to choose a Promotion", UniqueTarget.Promotion),
|
||||||
FreePromotion("This Promotion is free", UniqueTarget.Promotion),
|
FreePromotion("This Promotion is free", UniqueTarget.Promotion),
|
||||||
|
|
||||||
|
OneTimeChangeTerrain("Turn this tile into a [terrainName] tile", UniqueTarget.UnitTriggerable),
|
||||||
|
|
||||||
UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Triggerable), // Not used in Vanilla
|
UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Triggerable), // Not used in Vanilla
|
||||||
FreeStatBuildings("Provides the cheapest [stat] building in your first [positiveAmount] cities for free", UniqueTarget.Triggerable), // used in Policy
|
FreeStatBuildings("Provides the cheapest [stat] building in your first [positiveAmount] cities for free", UniqueTarget.Triggerable), // used in Policy
|
||||||
FreeSpecificBuildings("Provides a [buildingName] in your first [positiveAmount] cities for free", UniqueTarget.Triggerable), // used in Policy
|
FreeSpecificBuildings("Provides a [buildingName] in your first [positiveAmount] cities for free", UniqueTarget.Triggerable), // used in Policy
|
||||||
|
@ -190,6 +190,11 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
|||||||
|
|
||||||
Applicable to: UnitTriggerable
|
Applicable to: UnitTriggerable
|
||||||
|
|
||||||
|
??? example "Turn this tile into a [terrain] tile"
|
||||||
|
Example: "Turn this tile into a [Unknown] tile"
|
||||||
|
|
||||||
|
Applicable to: UnitTriggerable
|
||||||
|
|
||||||
## Global uniques
|
## Global uniques
|
||||||
!!! note ""
|
!!! note ""
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ class BasicTests {
|
|||||||
for (paramType in entry.value) {
|
for (paramType in entry.value) {
|
||||||
if (paramType == UniqueParameterType.Unknown) {
|
if (paramType == UniqueParameterType.Unknown) {
|
||||||
val badParam = uniqueType.text.getPlaceholderParameters()[entry.index]
|
val badParam = uniqueType.text.getPlaceholderParameters()[entry.index]
|
||||||
debug("%s param[%s] type \"%s\" is unknown", uniqueType.name, entry.index, badParam)
|
println("${uniqueType.name} param[${entry.index}] type \"$badParam\" is unknown")
|
||||||
noUnknownParameters = false
|
noUnknownParameters = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user