mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-08 23:08:35 +07:00
Improved clarity and moddability of building improvements (#6712)
* Renamed tile.hasUnique, deprecated `Indestructable`, unique for citadels Also refactored the consumption of (great) people out of UnitActions. * Reworked when improvements can be build somewhere for more clarity * Made resources improvable by multiple improvements; Offshore Platform * Fix compatability * WIP * Fixed the tests, but better * I suppose I might as well update this now that we're a version later
This commit is contained in:
BIN
android/Images/ImprovementIcons/Offshore Platform.png
Normal file
BIN
android/Images/ImprovementIcons/Offshore Platform.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
@ -44,22 +44,27 @@
|
||||
"name": "Camp",
|
||||
"turnsToBuild": 7,
|
||||
"techRequired": "Trapping",
|
||||
"uniques": ["Does not need removal of [Forest]","Does not need removal of [Jungle]","[+1 Gold] <after discovering [Economics]>"],
|
||||
"uniques": ["Does not need removal of [Forest]","Does not need removal of [Jungle]","[+1 Gold] <after discovering [Economics]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "C"
|
||||
},
|
||||
{
|
||||
"name": "Oil well",
|
||||
"terrainsCanBeBuiltOn": ["Coast"],
|
||||
"turnsToBuild": 9,
|
||||
"techRequired": "Biology",
|
||||
"uniques": ["Cannot be built on [Coast] tiles <before discovering [Refrigeration]>"],
|
||||
"uniques": ["Can only be built to improve a resource", "Cannot be built on [Water] tiles"],
|
||||
"shortcutKey": "W"
|
||||
},
|
||||
{
|
||||
"name": "Offshore Platform",
|
||||
"techRequired": "Refrigeration",
|
||||
"uniques": ["Can only be built to improve a resource", "Can only be built on [Water] tiles"],
|
||||
"shortcutKey": "P"
|
||||
},
|
||||
{
|
||||
"name": "Pasture",
|
||||
"turnsToBuild": 8,
|
||||
"techRequired": "Animal Husbandry",
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>"],
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "P"
|
||||
},
|
||||
{
|
||||
@ -67,14 +72,14 @@
|
||||
"turnsToBuild": 6,
|
||||
"gold": 1,
|
||||
"techRequired": "Calendar",
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>"],
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "P"
|
||||
},
|
||||
{
|
||||
"name": "Quarry",
|
||||
"turnsToBuild": 8,
|
||||
"techRequired": "Masonry",
|
||||
"uniques": ["[+1 Production] <after discovering [Chemistry]>"],
|
||||
"uniques": ["[+1 Production] <after discovering [Chemistry]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "Q"
|
||||
},
|
||||
{
|
||||
@ -82,7 +87,8 @@
|
||||
"terrainsCanBeBuiltOn": ["Coast"],
|
||||
"food": 1,
|
||||
"techRequired": "Sailing",
|
||||
"uniques": ["[+1 Gold] <after discovering [Compass]>"]
|
||||
"uniques": ["[+1 Gold] <after discovering [Compass]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "F"
|
||||
},
|
||||
|
||||
// Military improvement
|
||||
@ -98,6 +104,7 @@
|
||||
// Transportation
|
||||
{
|
||||
"name": "Road",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"turnsToBuild": 4,
|
||||
"techRequired": "The Wheel",
|
||||
// "Costs [1] gold per turn when in your territory" does nothing and is only to inform the user
|
||||
@ -111,6 +118,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Railroad",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"turnsToBuild": 4,
|
||||
"techRequired": "Railroads",
|
||||
"uniques": ["Can be built outside your borders", "Costs [2] gold per turn when in your territory"],
|
||||
@ -119,10 +127,11 @@
|
||||
},
|
||||
|
||||
// Removals
|
||||
// Any improvement that starts with 'Remove ' is automatically changed into
|
||||
// the improvement that removes the terrainfeature after it.
|
||||
{
|
||||
"name": "Remove Forest",
|
||||
"turnsToBuild": 4,
|
||||
"terrainsCanBeBuiltOn": ["Forest"],
|
||||
"techRequired": "Mining",
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X",
|
||||
@ -131,7 +140,6 @@
|
||||
{
|
||||
"name": "Remove Jungle",
|
||||
"turnsToBuild": 7,
|
||||
"terrainsCanBeBuiltOn": ["Jungle"],
|
||||
"techRequired": "Bronze Working",
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X"
|
||||
@ -139,15 +147,13 @@
|
||||
{
|
||||
"name": "Remove Fallout",
|
||||
"turnsToBuild": 2,
|
||||
"terrainsCanBeBuiltOn": ["Fallout"],
|
||||
// Has no tile improvements as it can always be built
|
||||
// Has no tech requirements as it can always be built
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X"
|
||||
},
|
||||
{
|
||||
"name": "Remove Marsh",
|
||||
"turnsToBuild": 6,
|
||||
"terrainsCanBeBuiltOn": ["Marsh"],
|
||||
"techRequired": "Masonry",
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X"
|
||||
@ -173,43 +179,51 @@
|
||||
// Great Person improvements
|
||||
{
|
||||
"name": "Academy",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"science": 8,
|
||||
"uniques": ["Great Improvement", "[+2 Science] <after discovering [Scientific Theory]>", "[+2 Science] <after discovering [Atomic Theory]>"]
|
||||
},
|
||||
{
|
||||
"name": "Landmark",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"culture": 6,
|
||||
"uniques": ["Great Improvement"]
|
||||
},
|
||||
{
|
||||
"name": "Manufactory",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"production": 4,
|
||||
"uniques": ["Great Improvement", "[+1 Production] <after discovering [Chemistry]>"]
|
||||
},
|
||||
{
|
||||
"name": "Customs house",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"gold": 4,
|
||||
"uniques": ["Great Improvement", "[+1 Gold] <after discovering [Economics]>"]
|
||||
},
|
||||
{
|
||||
"name": "Holy site",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"faith": 6,
|
||||
"uniques": ["Great Improvement"]
|
||||
},
|
||||
{
|
||||
"name": "Citadel",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": [
|
||||
"Great Improvement",
|
||||
"Gives a defensive bonus of [100]%",
|
||||
"Adjacent enemy units ending their turn take [30] damage",
|
||||
"Can be built just outside your borders",
|
||||
"Constructing it will take over the tiles around it and assign them to your closest city"]
|
||||
"Constructing it will take over the tiles around it and assign them to your closest city"
|
||||
]
|
||||
},
|
||||
|
||||
//Civilization unique improvements
|
||||
{
|
||||
"name": "Moai",
|
||||
"uniqueTo": "Polynesia",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"culture": 1,
|
||||
"turnsToBuild": 4,
|
||||
"uniques": ["Can only be built on [Coastal] tiles", "[+1 Culture] for each adjacent [Moai]", "[+1 Gold] <after discovering [Flight]>"],
|
||||
@ -240,26 +254,31 @@
|
||||
"shortcutKey": "F"
|
||||
},
|
||||
|
||||
// Unbuildable improvements
|
||||
{
|
||||
"name": "Ancient ruins",
|
||||
"uniques": ["Unpillagable", "Provides a random bonus when entered"]
|
||||
"name": "Ancient ruins",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": ["Unpillagable", "Provides a random bonus when entered", "Unbuildable"]
|
||||
},
|
||||
{
|
||||
"name": "City ruins",
|
||||
"uniques": ["Unpillagable"],
|
||||
"name": "City ruins",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": ["Unpillagable", "Unbuildable"],
|
||||
"civilopediaText": [{"text":"A bleak reminder of the destruction wreaked by War"}]
|
||||
},
|
||||
{
|
||||
"name": "City center",
|
||||
"uniques": ["Unpillagable", "Indestructible"],
|
||||
"name": "City center",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": ["Unpillagable", "Irremovable", "Unbuildable"],
|
||||
"civilopediaText": [
|
||||
{"text":"Marks the center of a city"},
|
||||
{"text":"Appearance changes with the technological era of the owning civilization"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Barbarian encampment",
|
||||
"uniques": ["Unpillagable"],
|
||||
"name": "Barbarian encampment",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": ["Unpillagable", "Unbuildable"],
|
||||
"civilopediaText": [{"text":"Home to uncivilized barbarians, will spawn a hostile unit from time to time"}]
|
||||
}
|
||||
]
|
||||
|
@ -157,7 +157,7 @@
|
||||
"revealedBy": "Biology",
|
||||
"terrainsCanBeFoundOn": ["Desert","Coast","Tundra","Snow","Marsh","Jungle"],
|
||||
"production": 1,
|
||||
"improvement": "Oil well",
|
||||
"improvedBy": ["Oil well", "Offshore Platform"],
|
||||
"improvementStats": {"production": 3},
|
||||
"uniques": ["Deposits in [Coast] tiles always provide [4] resources",
|
||||
"Guaranteed with Strategic Balance resource option",
|
||||
|
@ -44,22 +44,27 @@
|
||||
"name": "Camp",
|
||||
"turnsToBuild": 7,
|
||||
"techRequired": "Trapping",
|
||||
"uniques": ["Does not need removal of [Forest]","Does not need removal of [Jungle]","[+1 Gold] <after discovering [Economics]>"],
|
||||
"uniques": ["Does not need removal of [Forest]","Does not need removal of [Jungle]","[+1 Gold] <after discovering [Economics]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "C"
|
||||
},
|
||||
{
|
||||
"name": "Oil well",
|
||||
"terrainsCanBeBuiltOn": ["Coast"],
|
||||
"turnsToBuild": 9,
|
||||
"techRequired": "Biology",
|
||||
"uniques": ["Cannot be built on [Coast] tiles <before discovering [Refrigeration]>"],
|
||||
"uniques": ["Can only be built to improve a resource", "Cannot be built on [Water] tiles"],
|
||||
"shortcutKey": "W"
|
||||
},
|
||||
{
|
||||
"name": "Offshore Platform",
|
||||
"techRequired": "Refrigeration",
|
||||
"uniques": ["Can only be built to improve a resource", "Can only be built on [Water] tiles"],
|
||||
"shortcutKey": "P"
|
||||
},
|
||||
{
|
||||
"name": "Pasture",
|
||||
"turnsToBuild": 8,
|
||||
"techRequired": "Animal Husbandry",
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>"],
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "P"
|
||||
},
|
||||
{
|
||||
@ -67,14 +72,14 @@
|
||||
"turnsToBuild": 6,
|
||||
"gold": 1,
|
||||
"techRequired": "Calendar",
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>"],
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "P"
|
||||
},
|
||||
{
|
||||
"name": "Quarry",
|
||||
"turnsToBuild": 8,
|
||||
"techRequired": "Masonry",
|
||||
"uniques": ["[+1 Production] <after discovering [Chemistry]>"],
|
||||
"uniques": ["[+1 Production] <after discovering [Chemistry]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "Q"
|
||||
},
|
||||
{
|
||||
@ -82,7 +87,8 @@
|
||||
"terrainsCanBeBuiltOn": ["Coast"],
|
||||
"food": 1,
|
||||
"techRequired": "Sailing",
|
||||
"uniques": ["[+1 Gold] <after discovering [Compass]>"]
|
||||
"uniques": ["[+1 Gold] <after discovering [Compass]>", "Can only be built to improve a resource"],
|
||||
"shortcutKey": "F"
|
||||
},
|
||||
|
||||
// Military improvement
|
||||
@ -98,6 +104,7 @@
|
||||
// Transportation
|
||||
{
|
||||
"name": "Road",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"turnsToBuild": 4,
|
||||
"techRequired": "The Wheel",
|
||||
// "Costs [1] gold per turn when in your territory" does nothing and is only to inform the user
|
||||
@ -111,6 +118,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Railroad",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"turnsToBuild": 4,
|
||||
"techRequired": "Railroads",
|
||||
"uniques": ["Can be built outside your borders", "Costs [2] gold per turn when in your territory"],
|
||||
@ -119,10 +127,11 @@
|
||||
},
|
||||
|
||||
// Removals
|
||||
// Any improvement that starts with 'Remove ' is automatically changed into
|
||||
// the improvement that removes the terrainfeature after it.
|
||||
{
|
||||
"name": "Remove Forest",
|
||||
"turnsToBuild": 4,
|
||||
"terrainsCanBeBuiltOn": ["Forest"],
|
||||
"techRequired": "Mining",
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X",
|
||||
@ -131,7 +140,6 @@
|
||||
{
|
||||
"name": "Remove Jungle",
|
||||
"turnsToBuild": 7,
|
||||
"terrainsCanBeBuiltOn": ["Jungle"],
|
||||
"techRequired": "Bronze Working",
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X"
|
||||
@ -139,15 +147,13 @@
|
||||
{
|
||||
"name": "Remove Fallout",
|
||||
"turnsToBuild": 2,
|
||||
"terrainsCanBeBuiltOn": ["Fallout"],
|
||||
// Has no tile improvements as it can always be built
|
||||
// Has no tech requirements as it can always be built
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X"
|
||||
},
|
||||
{
|
||||
"name": "Remove Marsh",
|
||||
"turnsToBuild": 6,
|
||||
"terrainsCanBeBuiltOn": ["Marsh"],
|
||||
"techRequired": "Masonry",
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X"
|
||||
@ -173,43 +179,51 @@
|
||||
// Great Person improvements
|
||||
{
|
||||
"name": "Academy",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"science": 8,
|
||||
"uniques": ["Great Improvement", "[+2 Science] <after discovering [Scientific Theory]>"]
|
||||
},
|
||||
{
|
||||
"name": "Landmark",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"culture": 6,
|
||||
"uniques": ["Great Improvement"]
|
||||
},
|
||||
{
|
||||
"name": "Manufactory",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"production": 4,
|
||||
"uniques": ["Great Improvement", "[+1 Production] <after discovering [Chemistry]>"]
|
||||
},
|
||||
{
|
||||
"name": "Customs house",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"gold": 4,
|
||||
"uniques": ["Great Improvement", "[+1 Gold] <after discovering [Economics]>"]
|
||||
},
|
||||
{
|
||||
"name": "Holy site",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"faith": 6,
|
||||
"uniques": ["Great Improvement"]
|
||||
},
|
||||
{
|
||||
"name": "Citadel",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": [
|
||||
"Great Improvement",
|
||||
"Gives a defensive bonus of [100]%",
|
||||
"Adjacent enemy units ending their turn take [30] damage",
|
||||
"Can be built just outside your borders",
|
||||
"Constructing it will take over the tiles around it and assign them to your closest city"]
|
||||
"Constructing it will take over the tiles around it and assign them to your closest city"
|
||||
]
|
||||
},
|
||||
|
||||
//Civilization unique improvements
|
||||
{
|
||||
"name": "Moai",
|
||||
"uniqueTo": "Polynesia",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"culture": 1,
|
||||
"turnsToBuild": 4,
|
||||
"uniques": ["Can only be built on [Coastal] tiles", "[+1 Culture] for each adjacent [Moai]", "[+1 Gold] <after discovering [Flight]>"],
|
||||
@ -230,26 +244,31 @@
|
||||
"shortcutKey": "F"
|
||||
},
|
||||
|
||||
// Unbuildable improvements
|
||||
{
|
||||
"name": "Ancient ruins",
|
||||
"uniques": ["Unpillagable", "Provides a random bonus when entered"]
|
||||
"name": "Ancient ruins",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": ["Unpillagable", "Provides a random bonus when entered", "Unbuildable"]
|
||||
},
|
||||
{
|
||||
"name": "City ruins",
|
||||
"uniques": ["Unpillagable"],
|
||||
"name": "City ruins",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": ["Unpillagable", "Unbuildable"],
|
||||
"civilopediaText": [{"text":"A bleak reminder of the destruction wreaked by War"}]
|
||||
},
|
||||
{
|
||||
"name": "City center",
|
||||
"uniques": ["Unpillagable", "Indestructible"],
|
||||
"name": "City center",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": ["Unpillagable", "Irremovable", "Unbuildable"],
|
||||
"civilopediaText": [
|
||||
{"text":"Marks the center of a city"},
|
||||
{"text":"Appearance changes with the technological era of the owning civilization"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Barbarian encampment",
|
||||
"uniques": ["Unpillagable"],
|
||||
"name": "Barbarian encampment",
|
||||
"terrainsCanBeBuiltOn": ["Land"],
|
||||
"uniques": ["Unpillagable", "Unbuildable"],
|
||||
"civilopediaText": [{"text":"Home to uncivilized barbarians, will spawn a hostile unit from time to time"}]
|
||||
}
|
||||
]
|
||||
|
@ -160,7 +160,7 @@
|
||||
"revealedBy": "Biology",
|
||||
"terrainsCanBeFoundOn": ["Desert","Coast","Tundra","Snow","Marsh","Jungle"],
|
||||
"production": 1,
|
||||
"improvement": "Oil well",
|
||||
"improvedBy": ["Oil well", "Offshore Platform"],
|
||||
"improvementStats": {"production": 3},
|
||||
"uniques": ["Deposits in [Coast] tiles always provide [4] resources",
|
||||
"Guaranteed with Strategic Balance resource option",
|
||||
|
@ -30,6 +30,7 @@ object Constants {
|
||||
val vegetation = arrayOf(forest, jungle)
|
||||
|
||||
// Note the difference in case. **Not** interchangeable!
|
||||
// TODO this is very opaque behaviour to modders
|
||||
/** The "Fresh water" terrain _unique_ */
|
||||
const val freshWater = "Fresh water"
|
||||
/** The "Fresh Water" terrain _filter_ */
|
||||
|
@ -236,7 +236,7 @@ class Encampment() {
|
||||
|| it.isCityCenter()
|
||||
|| it.getFirstUnit() != null
|
||||
|| (it.isWater && !canSpawnBoats)
|
||||
|| (it.hasUnique(UniqueType.FreshWater) && it.isWater) // No Lakes
|
||||
|| (it.terrainHasUnique(UniqueType.FreshWater) && it.isWater) // No Lakes
|
||||
}
|
||||
if (validTiles.isEmpty()) return false
|
||||
|
||||
|
@ -162,10 +162,12 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
}
|
||||
for (i in 1..10) bfs.nextStep()
|
||||
if (!bfs.getReachedTiles()
|
||||
.any { it.hasViewableResource(civInfo) && it.improvement == null && it.getOwner() == civInfo
|
||||
&& it.tileResource.improvement != null
|
||||
&& it.canBuildImprovement(it.ruleset.tileImprovements[it.tileResource.improvement]!!, civInfo)
|
||||
.any { tile ->
|
||||
tile.hasViewableResource(civInfo) && tile.improvement == null && tile.getOwner() == civInfo
|
||||
&& tile.tileResource.getImprovements().any {
|
||||
tile.canBuildImprovement(tile.ruleset.tileImprovements[it]!!, civInfo)
|
||||
}
|
||||
}
|
||||
) return
|
||||
|
||||
addChoice(
|
||||
|
@ -38,8 +38,8 @@ object SpecificUnitAutomation {
|
||||
unit.movement.headTowards(closestReachableResource)
|
||||
|
||||
// could be either fishing boats or oil well
|
||||
val improvement = closestReachableResource.tileResource.improvement
|
||||
if (unit.currentTile == closestReachableResource && improvement != null)
|
||||
val isImprovable = closestReachableResource.tileResource.getImprovements().any()
|
||||
if (isImprovable && unit.currentTile == closestReachableResource)
|
||||
UnitActions.getWaterImprovementAction(unit)?.action?.invoke()
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +299,7 @@ class WorkerAutomation(
|
||||
if (chosenImprovement != null && tile.canBuildImprovement(chosenImprovement, civInfo) && unit.canBuildImprovement(chosenImprovement, tile)) return true
|
||||
|
||||
} else if (!tile.containsGreatImprovement() && tile.hasViewableResource(civInfo)
|
||||
&& tile.tileResource.improvement != tile.improvement
|
||||
&& tile.tileResource.isImprovedBy(tile.improvement!!)
|
||||
&& (chooseImprovement(unit, tile) // if the chosen improvement is not null and buildable
|
||||
.let { it != null && tile.canBuildImprovement(it, civInfo) && unit.canBuildImprovement(it, tile)}))
|
||||
return true
|
||||
@ -354,17 +354,14 @@ class WorkerAutomation(
|
||||
tile.terrainFeatures.isNotEmpty()
|
||||
&& isUnbuildableAndRemovable(lastTerrain)
|
||||
&& !isResourceImprovementAllowedOnFeature(tile) -> Constants.remove + lastTerrain.name
|
||||
else -> tile.tileResource.improvement
|
||||
else -> tile.tileResource.getImprovements().firstOrNull { it in potentialTileImprovements }
|
||||
}
|
||||
|
||||
val improvementString = when {
|
||||
tile.improvementInProgress != null -> tile.improvementInProgress!!
|
||||
improvementStringForResource != null -> {
|
||||
if (potentialTileImprovements.containsKey(improvementStringForResource))
|
||||
improvementStringForResource
|
||||
// if this is a resource that HAS an improvement, but this unit can't build it, don't waste your time
|
||||
else return null
|
||||
}
|
||||
improvementStringForResource != null -> improvementStringForResource
|
||||
// if this is a resource that HAS an improvement, but this unit can't build it, don't waste your time
|
||||
tile.resource != null && tile.tileResource.getImprovements().any() -> return null
|
||||
tile.containsGreatImprovement() -> return null
|
||||
tile.containsUnfinishedGreatImprovement() -> return null
|
||||
|
||||
@ -392,11 +389,10 @@ class WorkerAutomation(
|
||||
* Assumes the caller ensured that terrainFeature and resource are both present!
|
||||
*/
|
||||
private fun isResourceImprovementAllowedOnFeature(tile: TileInfo): Boolean {
|
||||
val resourceImprovementName = tile.tileResource.improvement
|
||||
?: return false
|
||||
val resourceImprovement = ruleSet.tileImprovements[resourceImprovementName]
|
||||
?: return false
|
||||
return tile.terrainFeatures.any { resourceImprovement.isAllowedOnFeature(it) }
|
||||
return tile.tileResource.getImprovements().any { resourceImprovementName ->
|
||||
val resourceImprovement = ruleSet.tileImprovements[resourceImprovementName] ?: return false
|
||||
tile.terrainFeatures.any { resourceImprovement.isAllowedOnFeature(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -732,7 +732,7 @@ object Battle {
|
||||
}
|
||||
|
||||
// Pillage improvements, remove roads, add fallout
|
||||
if (tile.improvement != null && !tile.getTileImprovement()!!.hasUnique(UniqueType.Indestructible)) {
|
||||
if (tile.improvement != null && !tile.getTileImprovement()!!.hasUnique(UniqueType.Irremovable)) {
|
||||
if (tile.getTileImprovement()!!.hasUnique(UniqueType.Unpillagable)) {
|
||||
tile.improvement = null
|
||||
} else {
|
||||
@ -741,29 +741,28 @@ object Battle {
|
||||
}
|
||||
tile.roadStatus = RoadStatus.None
|
||||
if (tile.isLand && !tile.isImpassible() && !tile.isCityCenter()) {
|
||||
if (tile.hasUnique(UniqueType.DestroyableByNukesChance)) {
|
||||
if (tile.terrainHasUnique(UniqueType.DestroyableByNukesChance)) {
|
||||
for (terrainFeature in tile.terrainFeatureObjects) {
|
||||
for (unique in terrainFeature.getMatchingUniques(UniqueType.DestroyableByNukesChance)) {
|
||||
if (Random().nextFloat() >= unique.params[0].toFloat() / 100f) continue
|
||||
tile.removeTerrainFeature(terrainFeature.name)
|
||||
if (!tile.terrainFeatures.contains("Fallout") && !tile.hasUnique(UniqueType.Indestructible))
|
||||
if (!tile.terrainFeatures.contains("Fallout"))
|
||||
tile.addTerrainFeature("Fallout")
|
||||
}
|
||||
}
|
||||
} else if (Random().nextFloat() < 0.5f && !tile.terrainFeatures.contains("Fallout") && !tile.hasUnique(UniqueType.Indestructible)) {
|
||||
} else if (Random().nextFloat() < 0.5f && !tile.terrainFeatures.contains("Fallout")) {
|
||||
tile.addTerrainFeature("Fallout")
|
||||
}
|
||||
if (!tile.hasUnique(UniqueType.DestroyableByNukes)) return
|
||||
|
||||
if (!tile.terrainHasUnique(UniqueType.DestroyableByNukes)) return
|
||||
|
||||
// Deprecated as of 3.19.19 -- If removed, the two successive `if`s above should be merged
|
||||
val destructionChance = if (tile.hasUnique(UniqueType.ResistsNukes)) 0.25f
|
||||
val destructionChance = if (tile.terrainHasUnique(UniqueType.ResistsNukes)) 0.25f
|
||||
else 0.5f
|
||||
if (Random().nextFloat() < destructionChance) {
|
||||
for (terrainFeature in tile.terrainFeatureObjects)
|
||||
if (terrainFeature.hasUnique(UniqueType.DestroyableByNukes))
|
||||
tile.removeTerrainFeature(terrainFeature.name)
|
||||
if (!tile.hasUnique(UniqueType.Indestructible))
|
||||
tile.addTerrainFeature("Fallout")
|
||||
tile.addTerrainFeature("Fallout")
|
||||
}
|
||||
//
|
||||
}
|
||||
|
@ -410,12 +410,14 @@ class CityInfo {
|
||||
if (resource.revealedBy != null && !civInfo.tech.isResearched(resource.revealedBy!!)) return 0
|
||||
|
||||
// Even if the improvement exists (we conquered an enemy city or somesuch) or we have a city on it, we won't get the resource until the correct tech is researched
|
||||
if (resource.improvement != null) {
|
||||
val improvement = getRuleset().tileImprovements[resource.improvement!!]!!
|
||||
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!)) return 0
|
||||
if (resource.getImprovements().any()) {
|
||||
if (!resource.getImprovements().any { improvementString ->
|
||||
val improvement = getRuleset().tileImprovements[improvementString]!!
|
||||
improvement.techRequired == null || civInfo.tech.isResearched(improvement.techRequired!!)
|
||||
}) return 0
|
||||
}
|
||||
|
||||
if (resource.improvement == tileInfo.improvement || tileInfo.isCityCenter()
|
||||
if ((tileInfo.improvement != null && resource.isImprovedBy(tileInfo.improvement!!)) || tileInfo.isCityCenter()
|
||||
// Per https://gaming.stackexchange.com/questions/53155/do-manufactories-and-customs-houses-sacrifice-the-strategic-or-luxury-resources
|
||||
|| resource.resourceType == ResourceType.Strategic && tileInfo.containsGreatImprovement()
|
||||
) {
|
||||
|
@ -378,7 +378,7 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
|| cityInfo.isWorked(it)
|
||||
|| it.owningCity == cityInfo && (it.getTileImprovement()
|
||||
?.hasUnique(UniqueType.TileProvidesYieldWithoutPopulation) == true
|
||||
|| it.hasUnique(UniqueType.TileProvidesYieldWithoutPopulation))
|
||||
|| it.terrainHasUnique(UniqueType.TileProvidesYieldWithoutPopulation))
|
||||
})
|
||||
stats.add(cell.getTileStats(cityInfo, cityInfo.civInfo, localUniqueCache))
|
||||
statsFromTiles = stats
|
||||
|
@ -122,7 +122,7 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo) {
|
||||
var goldGained = 0
|
||||
val discoveredNaturalWonders = civInfo.gameInfo.civilizations.filter { it != civInfo && it.isMajorCiv() }
|
||||
.flatMap { it.naturalWonders }
|
||||
if (tile.hasUnique(UniqueType.GrantsGoldToFirstToDiscover)
|
||||
if (tile.terrainHasUnique(UniqueType.GrantsGoldToFirstToDiscover)
|
||||
&& !discoveredNaturalWonders.contains(tile.naturalWonder!!)) {
|
||||
goldGained += 500
|
||||
}
|
||||
|
@ -20,8 +20,10 @@ import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.*
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.ruleset.unit.UnitType
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.ui.utils.filterAndLogic
|
||||
import com.unciv.ui.utils.toPercent
|
||||
import com.unciv.ui.worldscreen.unit.UnitActions
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.pow
|
||||
|
||||
@ -661,10 +663,11 @@ class MapUnit {
|
||||
tile.improvementInProgress == RoadStatus.Road.name -> tile.roadStatus = RoadStatus.Road
|
||||
tile.improvementInProgress == RoadStatus.Railroad.name -> tile.roadStatus = RoadStatus.Railroad
|
||||
else -> {
|
||||
tile.improvement = tile.improvementInProgress
|
||||
if (tile.resource != null) civInfo.updateDetailedCivResources()
|
||||
val improvement = civInfo.gameInfo.ruleSet.tileImprovements[tile.improvementInProgress]!!
|
||||
improvement.handleImprovementCompletion(this)
|
||||
}
|
||||
}
|
||||
|
||||
tile.improvementInProgress = null
|
||||
}
|
||||
|
||||
@ -851,6 +854,30 @@ class MapUnit {
|
||||
assignOwner(recipient)
|
||||
recipient.updateViewableTiles()
|
||||
}
|
||||
|
||||
/** Destroys the unit and gives stats if its a great person */
|
||||
fun consume() {
|
||||
addStatsPerGreatPersonUsage()
|
||||
destroy()
|
||||
}
|
||||
|
||||
private fun addStatsPerGreatPersonUsage() {
|
||||
if (!isGreatPerson()) return
|
||||
|
||||
val gainedStats = Stats()
|
||||
for (unique in civInfo.getMatchingUniques(UniqueType.ProvidesGoldWheneverGreatPersonExpended)) {
|
||||
gainedStats.gold += (100 * civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
||||
}
|
||||
for (unique in civInfo.getMatchingUniques(UniqueType.ProvidesStatsWheneverGreatPersonExpended)) {
|
||||
gainedStats.add(unique.stats)
|
||||
}
|
||||
|
||||
if (gainedStats.isEmpty()) return
|
||||
|
||||
for (stat in gainedStats)
|
||||
civInfo.addStat(stat.key, stat.value.toInt())
|
||||
civInfo.addNotification("By expending your [$name] you gained [${gainedStats}]!", getTile().position, name)
|
||||
}
|
||||
|
||||
fun removeFromTile() = currentTile.removeUnit(this)
|
||||
|
||||
|
@ -243,11 +243,19 @@ open class TileInfo {
|
||||
|
||||
fun isRoughTerrain() = getAllTerrains().any{ it.isRough() }
|
||||
|
||||
fun hasUnique(uniqueType: UniqueType) = getAllTerrains().any { it.hasUnique(uniqueType) }
|
||||
fun getMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(tile=this) ): Sequence<Unique> {
|
||||
/** Checks whether any of the TERRAINS of this tile has a certain unqiue */
|
||||
fun terrainHasUnique(uniqueType: UniqueType) = getAllTerrains().any { it.hasUnique(uniqueType) }
|
||||
/** Get all uniques of this type that any TERRAIN on this tile has */
|
||||
fun getTerrainMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(tile=this) ): Sequence<Unique> {
|
||||
return getAllTerrains().flatMap { it.getMatchingUniques(uniqueType, stateForConditionals) }
|
||||
}
|
||||
|
||||
|
||||
/** Get all uniques of this type that any part of this tile has: terrains, improvement, resource */
|
||||
fun getMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(tile=this)) =
|
||||
getTerrainMatchingUniques(uniqueType, stateForConditionals) +
|
||||
(getTileImprovement()?.getMatchingUniques(uniqueType, stateForConditionals) ?: sequenceOf()) +
|
||||
if (resource == null) sequenceOf() else tileResource.getMatchingUniques(uniqueType, stateForConditionals)
|
||||
|
||||
fun getWorkingCity(): CityInfo? {
|
||||
val civInfo = getOwner() ?: return null
|
||||
return civInfo.cities.firstOrNull { it.isWorked(this) }
|
||||
@ -256,7 +264,7 @@ open class TileInfo {
|
||||
fun isWorked(): Boolean = getWorkingCity() != null
|
||||
fun providesYield() = getCity() != null && (isCityCenter() || isWorked()
|
||||
|| getTileImprovement()?.hasUnique(UniqueType.TileProvidesYieldWithoutPopulation) == true
|
||||
|| hasUnique(UniqueType.TileProvidesYieldWithoutPopulation))
|
||||
|| terrainHasUnique(UniqueType.TileProvidesYieldWithoutPopulation))
|
||||
|
||||
fun isLocked(): Boolean {
|
||||
val workingCity = getWorkingCity()
|
||||
@ -405,7 +413,7 @@ open class TileInfo {
|
||||
|
||||
fun getImprovementStats(improvement: TileImprovement, observingCiv: CivilizationInfo, city: CityInfo?): Stats {
|
||||
val stats = improvement.cloneStats()
|
||||
if (hasViewableResource(observingCiv) && tileResource.improvement == improvement.name
|
||||
if (hasViewableResource(observingCiv) && tileResource.isImprovedBy(improvement.name)
|
||||
&& tileResource.improvementStats != null
|
||||
)
|
||||
stats.add(tileResource.improvementStats!!.clone()) // resource-specific improvement
|
||||
@ -463,40 +471,6 @@ open class TileInfo {
|
||||
return stats
|
||||
}
|
||||
|
||||
/** Returns true if the [improvement] can be built on this [TileInfo] */
|
||||
fun canBuildImprovement(improvement: TileImprovement, civInfo: CivilizationInfo): Boolean {
|
||||
return when {
|
||||
improvement.uniqueTo != null && improvement.uniqueTo != civInfo.civName -> false
|
||||
improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!) -> false
|
||||
getOwner() != civInfo && !(
|
||||
improvement.hasUnique(UniqueType.CanBuildOutsideBorders)
|
||||
|| ( // citadel can be built only next to or within own borders
|
||||
improvement.hasUnique(UniqueType.CanBuildJustOutsideBorders)
|
||||
&& neighbors.any { it.getOwner() == civInfo }
|
||||
)
|
||||
) -> false
|
||||
improvement.getMatchingUniques(UniqueType.OnlyAvailableWhen, StateForConditionals.IgnoreConditionals).any {
|
||||
!it.conditionalsApply(StateForConditionals(civInfo, tile=this))
|
||||
} -> false
|
||||
improvement.getMatchingUniques(UniqueType.ObsoleteWith).any {
|
||||
civInfo.tech.isResearched(it.params[0])
|
||||
} -> return false
|
||||
improvement.getMatchingUniques(UniqueType.CannotBuildOnTile, StateForConditionals(civInfo=civInfo, tile=this)).any {
|
||||
matchesTerrainFilter(it.params[0], civInfo)
|
||||
} -> false
|
||||
improvement.getMatchingUniques(UniqueType.ConsumesResources).any {
|
||||
civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
||||
} -> false
|
||||
// Calling this function does double the check for 'cannot be build on tile', but this is unavoidable.
|
||||
// Only in this function do we have the civInfo of the civ, so only here we can check whether
|
||||
// conditionals apply. Additionally, the function below is also called when determining if
|
||||
// an improvement can be on the tile in the given ruleset, in which case we do want to
|
||||
// assume that all conditionals apply, which is done automatically when we don't include
|
||||
// any state for conditionals. Therefore, duplicating the check is the easiest option.
|
||||
else -> canImprovementBeBuiltHere(improvement, hasViewableResource(civInfo))
|
||||
}
|
||||
}
|
||||
|
||||
// This should be the only adjacency function
|
||||
fun isAdjacentTo(terrainFilter:String): Boolean {
|
||||
// Rivers are odd, as they aren't technically part of any specific tile but still count towards adjacency
|
||||
@ -505,47 +479,103 @@ open class TileInfo {
|
||||
return (neighbors + this).any { neighbor -> neighbor.matchesFilter(terrainFilter) }
|
||||
}
|
||||
|
||||
/** Returns true if the [improvement] can be built on this [TileInfo] */
|
||||
fun canBuildImprovement(improvement: TileImprovement, civInfo: CivilizationInfo): Boolean {
|
||||
val stateForConditionals = StateForConditionals(civInfo, tile=this)
|
||||
return when {
|
||||
improvement.uniqueTo != null && improvement.uniqueTo != civInfo.civName -> false
|
||||
improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!) -> false
|
||||
getOwner() != civInfo && !(
|
||||
improvement.hasUnique(UniqueType.CanBuildOutsideBorders, stateForConditionals)
|
||||
|| ( // citadel can be built only next to or within own borders
|
||||
improvement.hasUnique(UniqueType.CanBuildJustOutsideBorders, stateForConditionals)
|
||||
&& neighbors.any { it.getOwner() == civInfo }
|
||||
)
|
||||
) -> false
|
||||
improvement.getMatchingUniques(UniqueType.OnlyAvailableWhen, StateForConditionals.IgnoreConditionals).any {
|
||||
!it.conditionalsApply(stateForConditionals)
|
||||
} -> false
|
||||
improvement.getMatchingUniques(UniqueType.ObsoleteWith, stateForConditionals).any {
|
||||
civInfo.tech.isResearched(it.params[0])
|
||||
} -> false
|
||||
improvement.getMatchingUniques(UniqueType.ConsumesResources, stateForConditionals).any {
|
||||
civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
||||
} -> false
|
||||
improvement.hasUnique(UniqueType.Unbuildable, stateForConditionals) -> false
|
||||
else -> canImprovementBeBuiltHere(improvement, hasViewableResource(civInfo), stateForConditionals)
|
||||
}
|
||||
}
|
||||
|
||||
/** Without regards to what CivInfo it is, a lot of the checks are just for the improvement on the tile.
|
||||
* Doubles as a check for the map editor.
|
||||
*/
|
||||
fun canImprovementBeBuiltHere(improvement: TileImprovement, resourceIsVisible: Boolean = resource != null): Boolean {
|
||||
private fun canImprovementBeBuiltHere(
|
||||
improvement: TileImprovement,
|
||||
resourceIsVisible: Boolean = resource != null,
|
||||
stateForConditionals: StateForConditionals = StateForConditionals(tile=this)
|
||||
): Boolean {
|
||||
val topTerrain = getLastTerrain()
|
||||
|
||||
return when {
|
||||
improvement.name == this.improvement -> false
|
||||
isCityCenter() -> false
|
||||
improvement.getMatchingUniques(UniqueType.CannotBuildOnTile, StateForConditionals(tile = this)).any {
|
||||
// First we handle a few special improvements
|
||||
// Can only cancel if there is actually an improvement being built
|
||||
improvement.name == Constants.cancelImprovementOrder -> (this.improvementInProgress != null)
|
||||
// Can only remove if the feature is actually there
|
||||
improvement.name.startsWith(Constants.remove) -> terrainFeatures.any { it == improvement.name.removePrefix(Constants.remove) }
|
||||
// Can only build roads if on land and they are better than the current road
|
||||
RoadStatus.values().any { it.name == improvement.name } -> !isWater && RoadStatus.valueOf(improvement.name) > roadStatus
|
||||
// Can always build road removal improvements
|
||||
improvement.name == roadStatus.removeAction -> true
|
||||
|
||||
// Then we check if there is any reason to not allow this improvement to be build
|
||||
|
||||
// Can't build if there is already an irremovable improvement here
|
||||
this.improvement != null && getTileImprovement()!!.hasUnique(UniqueType.Irremovable, stateForConditionals) -> false
|
||||
|
||||
// Can't build if any terrain specifically prevents building this improvement
|
||||
getTerrainMatchingUniques(UniqueType.RestrictedBuildableImprovements, stateForConditionals).any {
|
||||
unique -> !improvement.matchesFilter(unique.params[0])
|
||||
} -> false
|
||||
|
||||
// Can't build if the improvement specifically prevents building on some present feature
|
||||
improvement.getMatchingUniques(UniqueType.CannotBuildOnTile, stateForConditionals).any {
|
||||
unique -> matchesTerrainFilter(unique.params[0])
|
||||
} -> false
|
||||
// Road improvements can change on tiles with irremovable improvements - nothing else can, though.
|
||||
RoadStatus.values().none { it.name == improvement.name || it.removeAction == improvement.name }
|
||||
&& getTileImprovement().let { it != null && it.hasUnique( UniqueType.Irremovable) } -> false
|
||||
|
||||
// Terrain blocks BUILDING improvements - removing things (such as fallout) is fine
|
||||
!improvement.name.startsWith(Constants.remove) &&
|
||||
getAllTerrains().any { it.getMatchingUniques(UniqueType.RestrictedBuildableImprovements)
|
||||
.any { unique -> !improvement.matchesFilter(unique.params[0]) } } -> false
|
||||
|
||||
// Decide cancelImprovementOrder earlier, otherwise next check breaks it
|
||||
improvement.name == Constants.cancelImprovementOrder -> (this.improvementInProgress != null)
|
||||
// Tiles with no terrains, and no turns to build, are like great improvements - they're placeable
|
||||
improvement.terrainsCanBeBuiltOn.isEmpty() && improvement.turnsToBuild == 0 && isLand -> true
|
||||
improvement.terrainsCanBeBuiltOn.contains(topTerrain.name) -> true
|
||||
improvement.getMatchingUniques(UniqueType.MustBeNextTo).any {
|
||||
// Can't build if an improvement is only allowed to be built on specific tiles and this is not one of them
|
||||
// If multiple uniques of this type exists, we want all to match (e.g. Hill _and_ Forest would be meaningful)
|
||||
improvement.getMatchingUniques(UniqueType.CanOnlyBeBuiltOnTile, stateForConditionals).let {
|
||||
it.any() && it.any { unique -> !matchesTerrainFilter(unique.params[0]) }
|
||||
} -> false
|
||||
|
||||
// Can't build if the improvement requires an adjacent terrain that is not present
|
||||
improvement.getMatchingUniques(UniqueType.MustBeNextTo, stateForConditionals).any {
|
||||
!isAdjacentTo(it.params[0])
|
||||
} -> false
|
||||
!isWater && RoadStatus.values().any { it.name == improvement.name && it > roadStatus } -> true
|
||||
improvement.name == roadStatus.removeAction -> true
|
||||
|
||||
// Can't build on unbuildable terrains - EXCEPT when specifically allowed to
|
||||
topTerrain.unbuildable && !improvement.isAllowedOnFeature(topTerrain.name) -> false
|
||||
|
||||
// Can't build it if it is only allowed to improve resources and it doesn't improve this reousrce
|
||||
improvement.hasUnique(UniqueType.CanOnlyImproveResource, stateForConditionals) && (
|
||||
!resourceIsVisible || !tileResource.isImprovedBy(improvement.name)
|
||||
) -> false
|
||||
|
||||
// At this point we know this is a normal improvement and that there is no reason not to allow it to be built.
|
||||
// Lastly we check if the improvement may be built on this terrain or resource
|
||||
improvement.canBeBuiltOn(topTerrain.name) -> true
|
||||
isLand && improvement.canBeBuiltOn("Land") -> true
|
||||
isWater && improvement.canBeBuiltOn("Water") -> true
|
||||
// DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests.
|
||||
improvement.hasUnique(UniqueType.ImprovementBuildableByFreshWater) && isAdjacentTo(Constants.freshWater) -> true
|
||||
|
||||
// If an unique of this type exists, we want all to match (e.g. Hill _and_ Forest would be meaningful).
|
||||
improvement.getMatchingUniques(UniqueType.CanOnlyBeBuiltOnTile).let {
|
||||
it.any() && it.all { unique -> matchesTerrainFilter(unique.params[0]) }
|
||||
} -> true
|
||||
|
||||
else -> resourceIsVisible && tileResource.improvement == improvement.name
|
||||
improvement.hasUnique(UniqueType.ImprovementBuildableByFreshWater, stateForConditionals)
|
||||
&& isAdjacentTo(Constants.freshWater) -> true
|
||||
// I don't particularly like this check, but it is required to build mines on non-hill resources
|
||||
resourceIsVisible && tileResource.isImprovedBy(improvement.name) -> true
|
||||
// DEPRECATED since 4.0.14, REMOVE SOON:
|
||||
isLand && improvement.terrainsCanBeBuiltOn.isEmpty() && !improvement.hasUnique(UniqueType.CanOnlyImproveResource) -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@ -739,7 +769,8 @@ open class TileInfo {
|
||||
else
|
||||
FormattedLine(resource!!, link="Resource/$resource")
|
||||
if (resource != null && viewingCiv != null && hasViewableResource(viewingCiv)) {
|
||||
val tileImprovement = ruleset.tileImprovements[tileResource.improvement]
|
||||
val resourceImprovement = tileResource.getImprovements().firstOrNull { canBuildImprovement(ruleset.tileImprovements[it]!!, viewingCiv) }
|
||||
val tileImprovement = ruleset.tileImprovements[resourceImprovement]
|
||||
if (tileImprovement?.techRequired != null
|
||||
&& !viewingCiv.tech.isResearched(tileImprovement.techRequired!!)) {
|
||||
lineList += FormattedLine(
|
||||
@ -1005,11 +1036,7 @@ open class TileInfo {
|
||||
return
|
||||
}
|
||||
improvement = null // Unset, and check if it can be reset. If so, do it, if not, invalid.
|
||||
if (canImprovementBeBuiltHere(improvementObject)
|
||||
// Allow building 'other' improvements like city ruins, barb encampments, Great Improvements etc
|
||||
|| (improvementObject.terrainsCanBeBuiltOn.isEmpty()
|
||||
&& ruleset.tileResources.values.none { it.improvement == improvementObject.name }
|
||||
&& !isImpassible() && isLand))
|
||||
if (canImprovementBeBuiltHere(improvementObject, stateForConditionals = StateForConditionals.IgnoreConditionals))
|
||||
improvement = improvementObject.name
|
||||
}
|
||||
|
||||
|
@ -360,7 +360,7 @@ class TileMap {
|
||||
|| cTileHeight > bNeighborHeight // c>b
|
||||
|| (
|
||||
currentTileHeight == bNeighborHeight // a==b
|
||||
&& !bNeighbor.hasUnique(UniqueType.BlocksLineOfSightAtSameElevation)
|
||||
&& !bNeighbor.terrainHasUnique(UniqueType.BlocksLineOfSightAtSameElevation)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -660,7 +660,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||
it.resource != null
|
||||
&& requiredNearbyImprovedResources!!.contains(it.resource!!)
|
||||
&& it.getOwner() == civInfo
|
||||
&& (it.tileResource.improvement == it.improvement || it.isCityCenter()
|
||||
&& ((it.improvement != null && it.tileResource.isImprovedBy(it.improvement!!)) || it.isCityCenter()
|
||||
|| (it.getTileImprovement()?.isGreatImprovement() == true && it.tileResource.resourceType == ResourceType.Strategic)
|
||||
)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import com.unciv.json.json
|
||||
import com.unciv.logic.BackwardCompatibility.updateDeprecations
|
||||
import com.unciv.logic.UncivShowableException
|
||||
import com.unciv.logic.map.MapParameters
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.models.Counter
|
||||
import com.unciv.models.ModConstants
|
||||
import com.unciv.models.metadata.BaseRuleset
|
||||
@ -665,6 +666,9 @@ class Ruleset {
|
||||
lines += "${resource.name} revealed by tech ${resource.revealedBy} which does not exist!"
|
||||
if (resource.improvement != null && !tileImprovements.containsKey(resource.improvement!!))
|
||||
lines += "${resource.name} improved by improvement ${resource.improvement} which does not exist!"
|
||||
for (improvement in resource.improvedBy)
|
||||
if (!tileImprovements.containsKey(improvement))
|
||||
lines += "${resource.name} improved by improvement $improvement which does not exist!"
|
||||
for (terrain in resource.terrainsCanBeFoundOn)
|
||||
if (!terrains.containsKey(terrain))
|
||||
lines += "${resource.name} can be found on terrain $terrain which does not exist!"
|
||||
@ -675,8 +679,20 @@ class Ruleset {
|
||||
if (improvement.techRequired != null && !technologies.containsKey(improvement.techRequired!!))
|
||||
lines += "${improvement.name} requires tech ${improvement.techRequired} which does not exist!"
|
||||
for (terrain in improvement.terrainsCanBeBuiltOn)
|
||||
if (!terrains.containsKey(terrain))
|
||||
if (!terrains.containsKey(terrain) && terrain != "Land" && terrain != "Water")
|
||||
lines += "${improvement.name} can be built on terrain $terrain which does not exist!"
|
||||
if (improvement.terrainsCanBeBuiltOn.isEmpty()
|
||||
&& !improvement.hasUnique(UniqueType.CanOnlyImproveResource)
|
||||
&& !improvement.hasUnique(UniqueType.Unbuildable)
|
||||
&& !improvement.name.startsWith(Constants.remove)
|
||||
&& improvement.name !in RoadStatus.values().map { it.removeAction }
|
||||
&& improvement.name != Constants.cancelImprovementOrder
|
||||
) {
|
||||
lines.add(
|
||||
"${improvement.name} has an empty `terrainsCanBeBuiltOn`, isn't allowed to only improve resources and isn't unbuildable! Support for this will soon end. Either give this the unique \"Unbuildable\", \"Can only be built to improve a resource\" or add \"Land\", \"Water\" or any other value to `terrainsCanBeBuiltOn`.",
|
||||
RulesetErrorSeverity.Warning
|
||||
)
|
||||
}
|
||||
checkUniques(improvement, lines, rulesetSpecific, forOptionsPopup)
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.utils.toPercent
|
||||
import com.unciv.ui.worldscreen.unit.UnitActions
|
||||
import java.util.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@ -23,16 +24,17 @@ class TileImprovement : RulesetStatsObject() {
|
||||
var uniqueTo:String? = null
|
||||
override fun getUniqueTarget() = UniqueTarget.Improvement
|
||||
val shortcutKey: Char? = null
|
||||
val turnsToBuild: Int = 0 // This is the base cost.
|
||||
// This is the base cost. A cost of 0 means created instead of buildable.
|
||||
val turnsToBuild: Int = 0
|
||||
|
||||
|
||||
fun getTurnsToBuild(civInfo: CivilizationInfo, unit: MapUnit?): Int {
|
||||
fun getTurnsToBuild(civInfo: CivilizationInfo, unit: MapUnit): Int {
|
||||
val state = StateForConditionals(civInfo, unit = unit)
|
||||
val uniques = civInfo.getMatchingUniques(UniqueType.TileImprovementTime, state) +
|
||||
(unit?.getMatchingUniques(UniqueType.TileImprovementTime, state) ?: sequenceOf())
|
||||
return uniques.fold(turnsToBuild.toFloat() * civInfo.gameInfo.gameParameters.gameSpeed.modifier) {
|
||||
it, unique -> it * unique.params[0].toPercent()
|
||||
}.roundToInt().coerceAtLeast(1)
|
||||
return unit.getMatchingUniques(UniqueType.TileImprovementTime, state, checkCivInfoUniques = true)
|
||||
.fold(turnsToBuild.toFloat() * civInfo.gameInfo.gameParameters.gameSpeed.modifier) { it, unique ->
|
||||
it * unique.params[0].toPercent()
|
||||
}.roundToInt()
|
||||
.coerceAtLeast(1)
|
||||
// In some weird cases it was possible for something to take 0 turns, leading to it instead never finishing
|
||||
}
|
||||
|
||||
@ -48,7 +50,7 @@ class TileImprovement : RulesetStatsObject() {
|
||||
}
|
||||
lines += "Can be built on".tr() + terrainsCanBeBuiltOnString.joinToString(", ", " ") //language can be changed when setting changes.
|
||||
}
|
||||
for (resource: TileResource in ruleset.tileResources.values.filter { it.improvement == name }) {
|
||||
for (resource: TileResource in ruleset.tileResources.values.filter { it.isImprovedBy(name) }) {
|
||||
if (resource.improvementStats == null) continue
|
||||
val statsString = resource.improvementStats.toString()
|
||||
lines += "[${statsString}] <in [${resource.name}] tiles>".tr()
|
||||
@ -65,6 +67,22 @@ class TileImprovement : RulesetStatsObject() {
|
||||
fun isRoad() = RoadStatus.values().any { it != RoadStatus.None && it.name == this.name }
|
||||
fun isAncientRuinsEquivalent() = hasUnique(UniqueType.IsAncientRuinsEquivalent)
|
||||
|
||||
fun canBeBuiltOn(terrain: String): Boolean {
|
||||
return terrain in terrainsCanBeBuiltOn
|
||||
}
|
||||
|
||||
fun handleImprovementCompletion(builder: MapUnit) {
|
||||
if (hasUnique(UniqueType.TakesOverAdjacentTiles))
|
||||
UnitActions.takeOverTilesAround(builder)
|
||||
if (builder.getTile().resource != null) {
|
||||
val city = builder.getTile().getCity()
|
||||
if (city != null) {
|
||||
city.cityStats.update()
|
||||
city.civInfo.updateDetailedCivResources()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check: Is this improvement allowed on a [given][name] terrain feature?
|
||||
*
|
||||
@ -75,7 +93,7 @@ class TileImprovement : RulesetStatsObject() {
|
||||
* so this check is done in conjunction - for the user, success means he does not need to remove
|
||||
* a terrain feature, thus the unique name.
|
||||
*/
|
||||
fun isAllowedOnFeature(name: String) = getMatchingUniques(UniqueType.NoFeatureRemovalNeeded).any { it.params[0] == name }
|
||||
fun isAllowedOnFeature(name: String) = terrainsCanBeBuiltOn.contains(name) || getMatchingUniques(UniqueType.NoFeatureRemovalNeeded).any { it.params[0] == name }
|
||||
|
||||
/** Implements [UniqueParameterType.ImprovementFilter][com.unciv.models.ruleset.unique.UniqueParameterType.ImprovementFilter] */
|
||||
fun matchesFilter(filter: String): Boolean {
|
||||
@ -117,7 +135,7 @@ class TileImprovement : RulesetStatsObject() {
|
||||
}
|
||||
|
||||
var addedLineBeforeResourceBonus = false
|
||||
for (resource in ruleset.tileResources.values.filter { it.improvement == name }) {
|
||||
for (resource in ruleset.tileResources.values.filter { it.isImprovedBy(name) }) {
|
||||
if (resource.improvementStats == null) continue
|
||||
if (!addedLineBeforeResourceBonus) {
|
||||
addedLineBeforeResourceBonus = true
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.unciv.models.ruleset.tile
|
||||
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.ruleset.Belief
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetStatsObject
|
||||
@ -15,12 +17,21 @@ class TileResource : RulesetStatsObject() {
|
||||
var improvement: String? = null
|
||||
var improvementStats: Stats? = null
|
||||
var revealedBy: String? = null
|
||||
var improvedBy: List<String> = listOf()
|
||||
var majorDepositAmount: DepositAmount = DepositAmount()
|
||||
var minorDepositAmount: DepositAmount = DepositAmount()
|
||||
|
||||
val _allImprovements by lazy {
|
||||
if (improvement == null) improvedBy
|
||||
else improvedBy + improvement!!
|
||||
}
|
||||
|
||||
fun getImprovements(): List<String> {
|
||||
return _allImprovements
|
||||
}
|
||||
|
||||
override fun getUniqueTarget() = UniqueTarget.Resource
|
||||
|
||||
|
||||
|
||||
override fun makeLink() = "Resource/$name"
|
||||
|
||||
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
|
||||
@ -45,7 +56,7 @@ class TileResource : RulesetStatsObject() {
|
||||
}
|
||||
}
|
||||
|
||||
if (improvement != null) {
|
||||
for (improvement in getImprovements()) {
|
||||
textList += FormattedLine()
|
||||
textList += FormattedLine("Improved by [$improvement]", link = "Improvement/$improvement")
|
||||
if (improvementStats != null && !improvementStats!!.isEmpty())
|
||||
@ -114,6 +125,16 @@ class TileResource : RulesetStatsObject() {
|
||||
return textList
|
||||
}
|
||||
|
||||
fun isImprovedBy(improvementName: String): Boolean {
|
||||
return getImprovements().contains(improvementName)
|
||||
}
|
||||
|
||||
fun getImprovingImprovement(tile: TileInfo, civInfo: CivilizationInfo): String? {
|
||||
return getImprovements().firstOrNull {
|
||||
tile.canBuildImprovement(civInfo.gameInfo.ruleSet.tileImprovements[it]!!, civInfo)
|
||||
}
|
||||
}
|
||||
|
||||
class DepositAmount {
|
||||
var sparse: Int = 1
|
||||
var default: Int = 2
|
||||
|
@ -316,7 +316,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
||||
///////////////////////////////////////// region CONSTRUCTION UNIQUES /////////////////////////////////////////
|
||||
|
||||
|
||||
Unbuildable("Unbuildable", UniqueTarget.Building, UniqueTarget.Unit),
|
||||
Unbuildable("Unbuildable", UniqueTarget.Building, UniqueTarget.Unit, UniqueTarget.Improvement),
|
||||
CannotBePurchased("Cannot be purchased", UniqueTarget.Building, UniqueTarget.Unit),
|
||||
CanBePurchasedWithStat("Can be purchased with [stat] [cityFilter]", UniqueTarget.Building, UniqueTarget.Unit),
|
||||
CanBePurchasedForAmountStat("Can be purchased for [amount] [stat] [cityFilter]", UniqueTarget.Building, UniqueTarget.Unit),
|
||||
@ -437,6 +437,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
||||
NoDefensiveTerrainPenalty("No defensive terrain penalty", UniqueTarget.Unit, UniqueTarget.Global),
|
||||
NoDamagePenalty("Damage is ignored when determining unit Strength", UniqueTarget.Unit, UniqueTarget.Global),
|
||||
Uncapturable("Uncapturable", UniqueTarget.Unit),
|
||||
// Replace with "Withdraws before melee combat <with [amount]% chance>"?
|
||||
MayWithdraw("May withdraw before melee ([amount]%)", UniqueTarget.Unit),
|
||||
CannotCaptureCities("Unable to capture cities", UniqueTarget.Unit),
|
||||
|
||||
@ -614,6 +615,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
||||
CanOnlyBeBuiltOnTile("Can only be built on [tileFilter] tiles", UniqueTarget.Improvement),
|
||||
CannotBuildOnTile("Cannot be built on [tileFilter] tiles", UniqueTarget.Improvement),
|
||||
NoFeatureRemovalNeeded("Does not need removal of [tileFilter]", UniqueTarget.Improvement),
|
||||
CanOnlyImproveResource("Can only be built to improve a resource", UniqueTarget.Improvement),
|
||||
|
||||
DefensiveBonus("Gives a defensive bonus of [relativeAmount]%", UniqueTarget.Improvement),
|
||||
ImprovementMaintenance("Costs [amount] gold per turn when in your territory", UniqueTarget.Improvement), // Unused
|
||||
@ -622,9 +624,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
||||
|
||||
GreatImprovement("Great Improvement", UniqueTarget.Improvement),
|
||||
IsAncientRuinsEquivalent("Provides a random bonus when entered", UniqueTarget.Improvement),
|
||||
TakesOverAdjacentTiles("Constructing it will take over the tiles around it and assign them to your closest city", UniqueTarget.Improvement),
|
||||
|
||||
Unpillagable("Unpillagable", UniqueTarget.Improvement),
|
||||
Indestructible("Indestructible", UniqueTarget.Improvement),
|
||||
Irremovable("Irremovable", UniqueTarget.Improvement),
|
||||
//endregion
|
||||
|
||||
@ -751,6 +753,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
||||
|
||||
// region DEPRECATED AND REMOVED
|
||||
|
||||
@Deprecated("as of 4.0.15", ReplaceWith("Irremovable"), DeprecationLevel.ERROR)
|
||||
Indestructible("Indestructible", UniqueTarget.Improvement),
|
||||
|
||||
@Deprecated("as of 3.19.1", ReplaceWith("[stats] from every [Wonder]"), DeprecationLevel.ERROR)
|
||||
StatsFromWondersDeprecated("[stats] from every Wonder", UniqueTarget.Global, UniqueTarget.FollowerBelief),
|
||||
@Deprecated("as of 3.19.3", ReplaceWith("[stats] from every [buildingFilter] <in cities where this religion has at least [amount] followers>"), DeprecationLevel.ERROR)
|
||||
|
@ -229,7 +229,7 @@ class ResourcesOverviewTab(
|
||||
if (!tile.hasViewableResource(viewingPlayer)) continue
|
||||
val tileResource = tile.tileResource
|
||||
if (tileResource.resourceType == ResourceType.Bonus) continue
|
||||
if (tile.improvement == tileResource.improvement) continue
|
||||
if (tile.improvement != null && tileResource.isImprovedBy(tile.improvement!!)) continue
|
||||
if (tileResource.resourceType == ResourceType.Strategic && tile.getTileImprovement()?.isGreatImprovement() == true) continue
|
||||
resourceSupplyList.add(tileResource, 1, ExtraInfoOrigin.Unimproved.name)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import kotlin.math.roundToInt
|
||||
class ImprovementPickerScreen(
|
||||
private val tileInfo: TileInfo,
|
||||
private val unit: MapUnit,
|
||||
private val onAccept: ()->Unit
|
||||
private val onAccept: ()->Unit,
|
||||
) : PickerScreen() {
|
||||
private var selectedImprovement: TileImprovement? = null
|
||||
private val gameInfo = tileInfo.tileMap.gameInfo
|
||||
@ -98,12 +98,12 @@ class ImprovementPickerScreen(
|
||||
|
||||
var labelText = improvement.name.tr()
|
||||
val turnsToBuild = if (tileInfo.improvementInProgress == improvement.name) tileInfo.turnsToImprovement
|
||||
else improvement.getTurnsToBuild(currentPlayerCiv, unit)
|
||||
else improvement.getTurnsToBuild(currentPlayerCiv, unit)
|
||||
|
||||
if (turnsToBuild > 0) labelText += " - $turnsToBuild${Fonts.turn}"
|
||||
val provideResource = tileInfo.hasViewableResource(currentPlayerCiv) && tileInfo.tileResource.improvement == improvement.name
|
||||
val provideResource = tileInfo.hasViewableResource(currentPlayerCiv) && tileInfo.tileResource.isImprovedBy(improvement.name)
|
||||
if (provideResource) labelText += "\n" + "Provides [${tileInfo.resource}]".tr()
|
||||
val removeImprovement = (improvement.name != RoadStatus.Road.name
|
||||
&& improvement.name != RoadStatus.Railroad.name
|
||||
val removeImprovement = (improvement.isRoad()
|
||||
&& !improvement.name.startsWith(Constants.remove)
|
||||
&& improvement.name != Constants.cancelImprovementOrder)
|
||||
if (tileInfo.improvement != null && removeImprovement) labelText += "\n" + "Replaces [${tileInfo.improvement}]".tr()
|
||||
@ -162,7 +162,7 @@ class ImprovementPickerScreen(
|
||||
statIcons.add(ImageGetter.getResourceImage(tileInfo.resource.toString(), 30f)).pad(3f)
|
||||
|
||||
// icon for removing the resource by replacing improvement
|
||||
if (removeImprovement && tileInfo.hasViewableResource(currentPlayerCiv) && tileInfo.tileResource.improvement == tileInfo.improvement) {
|
||||
if (removeImprovement && tileInfo.hasViewableResource(currentPlayerCiv) && tileInfo.improvement != null && tileInfo.tileResource.isImprovedBy(tileInfo.improvement!!)) {
|
||||
val resourceIcon = ImageGetter.getResourceImage(tileInfo.resource!!, 30f)
|
||||
statIcons.add(ImageGetter.getCrossedImage(resourceIcon, 30f))
|
||||
}
|
||||
|
@ -425,10 +425,8 @@ class DiplomacyScreen(
|
||||
|
||||
for (improvableTile in improvableResourceTiles)
|
||||
for (tileImprovement in improvements.values)
|
||||
if (improvableTile.canBuildImprovement(
|
||||
tileImprovement,
|
||||
otherCiv
|
||||
) && improvableTile.tileResource.improvement == tileImprovement.name
|
||||
if (improvableTile.tileResource.isImprovedBy(tileImprovement.name)
|
||||
&& improvableTile.canBuildImprovement(tileImprovement, otherCiv)
|
||||
)
|
||||
needsImprovements = true
|
||||
|
||||
@ -490,9 +488,11 @@ class DiplomacyScreen(
|
||||
return diplomacyTable
|
||||
}
|
||||
|
||||
fun getImprovableResourceTiles(otherCiv:CivilizationInfo) = otherCiv.getCapital().getTiles()
|
||||
.filter { it.hasViewableResource(otherCiv) && it.tileResource.resourceType!=ResourceType.Bonus
|
||||
&& it.tileResource.improvement != it.improvement }
|
||||
fun getImprovableResourceTiles(otherCiv:CivilizationInfo) = otherCiv.getCapital().getTiles().filter {
|
||||
it.hasViewableResource(otherCiv)
|
||||
&& it.tileResource.resourceType != ResourceType.Bonus
|
||||
&& (it.improvement == null || !it.tileResource.isImprovedBy(it.improvement!!))
|
||||
}
|
||||
|
||||
private fun getImprovementGiftTable(otherCiv: CivilizationInfo): Table {
|
||||
val improvementGiftTable = getCityStateDiplomacyTableHeader(otherCiv)
|
||||
@ -504,7 +504,7 @@ class DiplomacyScreen(
|
||||
|
||||
for (improvableTile in improvableResourceTiles) {
|
||||
for (tileImprovement in tileImprovements.values) {
|
||||
if (improvableTile.tileResource.improvement == tileImprovement.name
|
||||
if (improvableTile.tileResource.isImprovedBy(tileImprovement.name)
|
||||
&& improvableTile.canBuildImprovement(tileImprovement, otherCiv)
|
||||
) {
|
||||
val improveTileButton =
|
||||
|
@ -545,7 +545,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
|
||||
|
||||
displayTutorial(Tutorial.Workers) {
|
||||
gameInfo.getCurrentPlayerCivilization().getCivUnits().any {
|
||||
it.hasUniqueToBuildImprovements && it.isCivilian()
|
||||
it.hasUniqueToBuildImprovements && it.isCivilian() && !it.isGreatPerson()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -624,7 +624,9 @@ class OptionsPopup(
|
||||
for ((tile, resource) in ownedTiles zip resourceTypes) {
|
||||
tile.resource = resource.name
|
||||
tile.resourceAmount = 999
|
||||
tile.improvement = resource.improvement
|
||||
// Debug option, so if it crashes on this that's relatively fine
|
||||
// If this becomes a problem, check if such an improvement exists and otherwise plop down a great improvement or so
|
||||
tile.improvement = resource.getImprovements().first()
|
||||
}
|
||||
game.gameInfo.getCurrentPlayerCivilization().updateSightAndResources()
|
||||
game.worldScreen.shouldUpdate = true
|
||||
|
@ -19,7 +19,6 @@ import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.unique.UniqueTriggerActivation
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.pickerscreens.ImprovementPickerScreen
|
||||
import com.unciv.ui.pickerscreens.PromotionPickerScreen
|
||||
@ -135,7 +134,7 @@ object UnitActions {
|
||||
val tile = unit.currentTile
|
||||
if (!tile.isWater || !unit.hasUnique(UniqueType.CreateWaterImprovements) || tile.resource == null) return null
|
||||
|
||||
val improvementName = tile.tileResource.improvement ?: return null
|
||||
val improvementName = tile.tileResource.getImprovingImprovement(tile, unit.civInfo) ?: return null
|
||||
val improvement = tile.ruleset.tileImprovements[improvementName] ?: return null
|
||||
if (!tile.canBuildImprovement(improvement, unit.civInfo)) return null
|
||||
|
||||
@ -439,7 +438,6 @@ object UnitActions {
|
||||
actionList += getAddInCapitalAction(unit, tile)
|
||||
}
|
||||
|
||||
|
||||
private fun addGreatPersonActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
|
||||
|
||||
if (unit.currentMovement > 0) for (unique in unit.getUniques()) when (unique.placeholderText) {
|
||||
@ -447,8 +445,7 @@ object UnitActions {
|
||||
actionList += UnitAction(UnitActionType.HurryResearch,
|
||||
action = {
|
||||
unit.civInfo.tech.addScience(unit.civInfo.tech.getScienceFromGreatScientist())
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}.takeIf { unit.civInfo.tech.currentTechnologyName() != null }
|
||||
)
|
||||
}
|
||||
@ -457,8 +454,7 @@ object UnitActions {
|
||||
actionList += UnitAction(UnitActionType.StartGoldenAge,
|
||||
action = {
|
||||
unit.civInfo.goldenAges.enterGoldenAge(turnsToGoldenAge)
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}.takeIf { unit.currentTile.getOwner() != null && unit.currentTile.getOwner() == unit.civInfo }
|
||||
)
|
||||
}
|
||||
@ -476,8 +472,7 @@ object UnitActions {
|
||||
constructIfEnough()
|
||||
}
|
||||
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}.takeIf { canHurryWonder }
|
||||
)
|
||||
}
|
||||
@ -507,8 +502,7 @@ object UnitActions {
|
||||
constructIfEnough()
|
||||
}
|
||||
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}.takeIf { canHurryConstruction }
|
||||
)
|
||||
}
|
||||
@ -526,8 +520,7 @@ object UnitActions {
|
||||
tile.owningCity!!.civInfo.getDiplomacyManager(unit.civInfo).addInfluence(influenceEarned)
|
||||
unit.civInfo.addNotification("Your trade mission to [${tile.owningCity!!.civInfo}] has earned you [${goldEarned}] gold and [$influenceEarned] influence!",
|
||||
tile.owningCity!!.civInfo.civName, NotificationIcon.Gold, NotificationIcon.Culture)
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}.takeIf { canConductTradeMission }
|
||||
)
|
||||
}
|
||||
@ -544,9 +537,8 @@ object UnitActions {
|
||||
|
||||
fun getFoundReligionAction(unit: MapUnit): () -> Unit {
|
||||
return {
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.civInfo.religionManager.useProphetForFoundingReligion(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,9 +553,8 @@ object UnitActions {
|
||||
|
||||
fun getEnhanceReligionAction(unit: MapUnit): () -> Unit {
|
||||
return {
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.civInfo.religionManager.useProphetForEnhancingReligion(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,8 +577,7 @@ object UnitActions {
|
||||
private fun useActionWithLimitedUses(unit: MapUnit, action: String) {
|
||||
unit.abilityUsesLeft[action] = unit.abilityUsesLeft[action]!! - 1
|
||||
if (unit.abilityUsesLeft[action]!! <= 0) {
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}
|
||||
}
|
||||
|
||||
@ -665,16 +655,8 @@ object UnitActions {
|
||||
unitTile.removeCreatesOneImprovementMarker()
|
||||
unitTile.improvement = improvementName
|
||||
unitTile.stopWorkingOnImprovement()
|
||||
if (improvement.hasUnique(UniqueType.TakeOverTilesAroundWhenBuilt))
|
||||
takeOverTilesAround(unit)
|
||||
val city = unitTile.getCity()
|
||||
if (city != null) {
|
||||
city.cityStats.update()
|
||||
city.civInfo.updateDetailedCivResources()
|
||||
}
|
||||
if (unit.isGreatPerson())
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.destroy()
|
||||
improvement.handleImprovementCompletion(unit)
|
||||
unit.consume()
|
||||
}.takeIf {
|
||||
resourcesAvailable
|
||||
&& unit.currentMovement > 0f
|
||||
@ -688,7 +670,7 @@ object UnitActions {
|
||||
return finalActions
|
||||
}
|
||||
|
||||
private fun takeOverTilesAround(unit: MapUnit) {
|
||||
fun takeOverTilesAround(unit: MapUnit) {
|
||||
// This method should only be called for a citadel - therefore one of the neighbour tile
|
||||
// must belong to unit's civ, so minByOrNull in the nearestCity formula should be never `null`.
|
||||
// That is, unless a mod does not specify the proper unique - then fallbackNearestCity will take over.
|
||||
@ -732,26 +714,6 @@ object UnitActions {
|
||||
otherCiv.addNotification("[${unit.civInfo}] has stolen your territory!", unit.currentTile.position, unit.civInfo.civName, NotificationIcon.War)
|
||||
}
|
||||
|
||||
private fun addStatsPerGreatPersonUsage(unit: MapUnit) {
|
||||
if (!unit.isGreatPerson()) return
|
||||
|
||||
val civInfo = unit.civInfo
|
||||
|
||||
val gainedStats = Stats()
|
||||
for (unique in civInfo.getMatchingUniques(UniqueType.ProvidesGoldWheneverGreatPersonExpended)) {
|
||||
gainedStats.gold += (100 * civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
||||
}
|
||||
for (unique in civInfo.getMatchingUniques(UniqueType.ProvidesStatsWheneverGreatPersonExpended)) {
|
||||
gainedStats.add(unique.stats)
|
||||
}
|
||||
|
||||
if (gainedStats.isEmpty()) return
|
||||
|
||||
for (stat in gainedStats)
|
||||
civInfo.addStat(stat.key, stat.value.toInt())
|
||||
civInfo.addNotification("By expending your [${unit.name}] you gained [${gainedStats}]!", unit.getTile().position, unit.name)
|
||||
}
|
||||
|
||||
private fun addFortifyActions(actionList: ArrayList<UnitAction>, unit: MapUnit, showingAdditionalActions: Boolean) {
|
||||
if (unit.isFortified() && !showingAdditionalActions) {
|
||||
actionList += UnitAction(
|
||||
@ -860,8 +822,7 @@ object UnitActions {
|
||||
if (!unique.conditionals.any { it.type == UniqueType.ConditionalConsumeUnit }) continue
|
||||
val unitAction = UnitAction(type = UnitActionType.TriggerUnique, unique.text){
|
||||
UniqueTriggerActivation.triggerCivwideUnique(unique, unit.civInfo)
|
||||
addStatsPerGreatPersonUsage(unit)
|
||||
unit.destroy()
|
||||
unit.consume()
|
||||
}
|
||||
actionList += unitAction
|
||||
}
|
||||
|
@ -212,6 +212,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
|
||||
- [Citadel](https://thenounproject.com/term/fort/1697646/) By Adrien Coquet
|
||||
- [Village](https://thenounproject.com/search/?q=city+center&i=476338) by Andrey Vasiliev
|
||||
- [pumping station](https://thenounproject.com/search/?q=dike&i=555172) by Peter van Driel for Polder
|
||||
- [Oil Platform](https://thenounproject.com/icon/oil-platform-1364795/) by Georgiana Ionescu for Offshore Platform
|
||||
|
||||
### Buildings
|
||||
|
||||
|
@ -829,7 +829,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
Applicable to: Building, Improvement
|
||||
|
||||
??? example "Unbuildable"
|
||||
Applicable to: Building, Unit
|
||||
Applicable to: Building, Unit, Improvement
|
||||
|
||||
??? example "Cannot be purchased"
|
||||
Applicable to: Building, Unit
|
||||
@ -1396,6 +1396,9 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
|
||||
Applicable to: Improvement
|
||||
|
||||
??? example "Can only be built to improve a resource"
|
||||
Applicable to: Improvement
|
||||
|
||||
??? example "Gives a defensive bonus of [relativeAmount]%"
|
||||
Example: "Gives a defensive bonus of [+20]%"
|
||||
|
||||
@ -1420,10 +1423,10 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
??? example "Provides a random bonus when entered"
|
||||
Applicable to: Improvement
|
||||
|
||||
??? example "Unpillagable"
|
||||
??? example "Constructing it will take over the tiles around it and assign them to your closest city"
|
||||
Applicable to: Improvement
|
||||
|
||||
??? example "Indestructible"
|
||||
??? example "Unpillagable"
|
||||
Applicable to: Improvement
|
||||
|
||||
??? example "Irremovable"
|
||||
|
@ -6,6 +6,9 @@ import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.testing.GdxTestRunner
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
@ -42,11 +45,23 @@ class TileImprovementConstructionTests {
|
||||
fun allTerrainSpecificImprovementsCanBeBuilt() {
|
||||
|
||||
for (improvement in ruleSet.tileImprovements.values) {
|
||||
val terrain = improvement.terrainsCanBeBuiltOn.firstOrNull() ?: continue
|
||||
var terrain = improvement.terrainsCanBeBuiltOn.firstOrNull() ?: continue
|
||||
if (terrain == "Land") terrain = ruleSet.terrains.values.first { it.type == TerrainType.Land }.name
|
||||
if (terrain == "Water") terrain = ruleSet.terrains.values.first { it.type == TerrainType.Water }.name
|
||||
// If this improvement requires additional conditions to be true,
|
||||
// its too complex to handle all of them, so just skip it and hope its fine
|
||||
// I would like some comments on whether this approach is fine or if it's better if I handle every single unique here as well
|
||||
if (improvement.hasUnique(UniqueType.CanOnlyBeBuiltOnTile, StateForConditionals.IgnoreConditionals)) continue
|
||||
if (improvement.hasUnique(UniqueType.Unbuildable, StateForConditionals.IgnoreConditionals)) continue
|
||||
|
||||
val tile = getTile()
|
||||
tile.baseTerrain = terrain
|
||||
if (improvement.hasUnique(UniqueType.CanOnlyImproveResource, StateForConditionals.IgnoreConditionals)) {
|
||||
tile.resource = ruleSet.tileResources.values.firstOrNull { it.isImprovedBy(improvement.name) }?.name ?: continue
|
||||
}
|
||||
tile.setTransients()
|
||||
if (improvement.uniqueTo != null) civInfo.civName = improvement.uniqueTo!!
|
||||
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertTrue(improvement.name, canBeBuilt)
|
||||
}
|
||||
@ -57,8 +72,12 @@ class TileImprovementConstructionTests {
|
||||
for (improvement in ruleSet.tileImprovements.values) {
|
||||
val tile = getTile()
|
||||
tile.resource = ruleSet.tileResources.values
|
||||
.firstOrNull { it.improvement == improvement.name }?.name
|
||||
.firstOrNull { it.isImprovedBy(improvement.name) }?.name
|
||||
if (tile.resource == null) continue
|
||||
// If this improvement requires additional conditions to be true,
|
||||
// its too complex to handle all of them, so just skip it and hope its fine
|
||||
if (improvement.hasUnique(UniqueType.CanOnlyBeBuiltOnTile, StateForConditionals.IgnoreConditionals)) continue
|
||||
|
||||
tile.setTransients()
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertTrue(improvement.name, canBeBuilt)
|
||||
@ -116,11 +135,11 @@ class TileImprovementConstructionTests {
|
||||
civInfo.civName = "OtherCiv"
|
||||
|
||||
for (resource in ruleSet.tileResources.values) {
|
||||
if (resource.improvement == null) continue
|
||||
val improvement = ruleSet.tileImprovements[resource.improvement]!!
|
||||
if (improvement.terrainsCanBeBuiltOn.isNotEmpty()) continue
|
||||
if (resource.getImprovements().isEmpty()) continue
|
||||
val improvement = ruleSet.tileImprovements[resource.getImprovements().first()]!!
|
||||
if (!improvement.hasUnique(UniqueType.CanOnlyImproveResource)) continue
|
||||
val wrongResource = ruleSet.tileResources.values.firstOrNull {
|
||||
it != resource && it.improvement != improvement.name
|
||||
it != resource && !it.isImprovedBy(improvement.name)
|
||||
} ?: continue
|
||||
val tile = getTile()
|
||||
tile.baseTerrain = "Plains"
|
||||
|
Reference in New Issue
Block a user