mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-05 21:11:35 +07:00
Assign Population Improvements (#6650)
City management UI to allow focusing automatic worker placement Improvements to worker / specialist assignment routines
This commit is contained in:
parent
a272e8e7ba
commit
a2bc1a1a29
Binary file not shown.
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 14 KiB |
@ -1075,6 +1075,90 @@ TileSets/FantasyHex/Arrows/UnitHasAttacked
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/Generic
|
||||
rotate: false
|
||||
xy: 4, 6
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/Generic
|
||||
rotate: false
|
||||
xy: 4, 6
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitAttacked
|
||||
rotate: false
|
||||
xy: 190, 1164
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitAttacked
|
||||
rotate: false
|
||||
xy: 190, 1164
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitMoved
|
||||
rotate: false
|
||||
xy: 298, 1150
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitMoved
|
||||
rotate: false
|
||||
xy: 298, 1150
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitMoving
|
||||
rotate: false
|
||||
xy: 112, 6
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitMoving
|
||||
rotate: false
|
||||
xy: 112, 6
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitTeleported
|
||||
rotate: false
|
||||
xy: 622, 1228
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitTeleported
|
||||
rotate: false
|
||||
xy: 622, 1228
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitWithdrew
|
||||
rotate: false
|
||||
xy: 730, 1228
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitWithdrew
|
||||
rotate: false
|
||||
xy: 730, 1228
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/AtollOverlay
|
||||
rotate: false
|
||||
xy: 4, 830
|
||||
@ -1082,6 +1166,118 @@ TileSets/Default/AtollOverlay
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConcaveConvexInner
|
||||
rotate: false
|
||||
xy: 406, 1411
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConcaveConvexInner
|
||||
rotate: false
|
||||
xy: 406, 1411
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConcaveConvexOuter
|
||||
rotate: false
|
||||
xy: 406, 1165
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConcaveConvexOuter
|
||||
rotate: false
|
||||
xy: 406, 1165
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConcaveInner
|
||||
rotate: false
|
||||
xy: 622, 1205
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConcaveInner
|
||||
rotate: false
|
||||
xy: 622, 1205
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConcaveOuter
|
||||
rotate: false
|
||||
xy: 1378, 1273
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConcaveOuter
|
||||
rotate: false
|
||||
xy: 1378, 1273
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConvexConcaveInner
|
||||
rotate: false
|
||||
xy: 495, 1165
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConvexConcaveInner
|
||||
rotate: false
|
||||
xy: 495, 1165
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConvexConcaveOuter
|
||||
rotate: false
|
||||
xy: 711, 1205
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConvexConcaveOuter
|
||||
rotate: false
|
||||
xy: 711, 1205
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConvexInner
|
||||
rotate: false
|
||||
xy: 1378, 1250
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConvexInner
|
||||
rotate: false
|
||||
xy: 1378, 1250
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConvexOuter
|
||||
rotate: false
|
||||
xy: 1467, 1273
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConvexOuter
|
||||
rotate: false
|
||||
xy: 1467, 1273
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/CityOverlay
|
||||
rotate: false
|
||||
xy: 1470, 1944
|
||||
@ -1089,6 +1285,34 @@ TileSets/Default/CityOverlay
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Crosshair
|
||||
rotate: false
|
||||
xy: 482, 1944
|
||||
size: 116, 100
|
||||
orig: 116, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Crosshair
|
||||
rotate: false
|
||||
xy: 482, 1944
|
||||
size: 116, 100
|
||||
orig: 116, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/CrosshatchHexagon
|
||||
rotate: false
|
||||
xy: 4, 1340
|
||||
size: 273, 236
|
||||
orig: 273, 236
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/CrosshatchHexagon
|
||||
rotate: false
|
||||
xy: 4, 1340
|
||||
size: 273, 236
|
||||
orig: 273, 236
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/FalloutOverlay
|
||||
rotate: false
|
||||
xy: 590, 1836
|
||||
@ -1110,6 +1334,20 @@ TileSets/Default/ForestOverlay
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Highlight
|
||||
rotate: false
|
||||
xy: 4, 1832
|
||||
size: 284, 212
|
||||
orig: 284, 212
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Highlight
|
||||
rotate: false
|
||||
xy: 4, 1832
|
||||
size: 284, 212
|
||||
orig: 284, 212
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/HillOverlay
|
||||
rotate: false
|
||||
xy: 590, 1728
|
||||
@ -1175,263 +1413,25 @@ TileSets/Default/Road
|
||||
index: -1
|
||||
TileSets/Default/Tiles/River-Bottom
|
||||
rotate: false
|
||||
xy: 1574, 735
|
||||
xy: 1534, 694
|
||||
size: 32, 28
|
||||
orig: 32, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Tiles/River-BottomLeft
|
||||
rotate: false
|
||||
xy: 1574, 699
|
||||
xy: 1534, 658
|
||||
size: 32, 28
|
||||
orig: 32, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Tiles/River-BottomRight
|
||||
rotate: false
|
||||
xy: 1574, 663
|
||||
xy: 1534, 622
|
||||
size: 32, 28
|
||||
orig: 32, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/Generic
|
||||
rotate: false
|
||||
xy: 4, 6
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/Generic
|
||||
rotate: false
|
||||
xy: 4, 6
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitAttacked
|
||||
rotate: false
|
||||
xy: 190, 1164
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitAttacked
|
||||
rotate: false
|
||||
xy: 190, 1164
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitMoved
|
||||
rotate: false
|
||||
xy: 298, 1150
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitMoved
|
||||
rotate: false
|
||||
xy: 298, 1150
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitMoving
|
||||
rotate: false
|
||||
xy: 112, 6
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitMoving
|
||||
rotate: false
|
||||
xy: 112, 6
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitTeleported
|
||||
rotate: false
|
||||
xy: 622, 1228
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitTeleported
|
||||
rotate: false
|
||||
xy: 622, 1228
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Arrows/UnitWithdrew
|
||||
rotate: false
|
||||
xy: 730, 1228
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Arrows/UnitWithdrew
|
||||
rotate: false
|
||||
xy: 730, 1228
|
||||
size: 100, 60
|
||||
orig: 100, 60
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConcaveConvexInner
|
||||
rotate: false
|
||||
xy: 406, 1411
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConcaveConvexInner
|
||||
rotate: false
|
||||
xy: 406, 1411
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConcaveConvexOuter
|
||||
rotate: false
|
||||
xy: 406, 1165
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConcaveConvexOuter
|
||||
rotate: false
|
||||
xy: 406, 1165
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConcaveInner
|
||||
rotate: false
|
||||
xy: 622, 1205
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConcaveInner
|
||||
rotate: false
|
||||
xy: 622, 1205
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConcaveOuter
|
||||
rotate: false
|
||||
xy: 1378, 1273
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConcaveOuter
|
||||
rotate: false
|
||||
xy: 1378, 1273
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConvexConcaveInner
|
||||
rotate: false
|
||||
xy: 495, 1165
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConvexConcaveInner
|
||||
rotate: false
|
||||
xy: 495, 1165
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConvexConcaveOuter
|
||||
rotate: false
|
||||
xy: 711, 1205
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConvexConcaveOuter
|
||||
rotate: false
|
||||
xy: 711, 1205
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConvexInner
|
||||
rotate: false
|
||||
xy: 1378, 1250
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConvexInner
|
||||
rotate: false
|
||||
xy: 1378, 1250
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Borders/ConvexOuter
|
||||
rotate: false
|
||||
xy: 1467, 1273
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Borders/ConvexOuter
|
||||
rotate: false
|
||||
xy: 1467, 1273
|
||||
size: 81, 15
|
||||
orig: 81, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Crosshair
|
||||
rotate: false
|
||||
xy: 482, 1944
|
||||
size: 116, 100
|
||||
orig: 116, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Crosshair
|
||||
rotate: false
|
||||
xy: 482, 1944
|
||||
size: 116, 100
|
||||
orig: 116, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/CrosshatchHexagon
|
||||
rotate: false
|
||||
xy: 4, 1340
|
||||
size: 273, 236
|
||||
orig: 273, 236
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/CrosshatchHexagon
|
||||
rotate: false
|
||||
xy: 4, 1340
|
||||
size: 273, 236
|
||||
orig: 273, 236
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Highlight
|
||||
rotate: false
|
||||
xy: 4, 1832
|
||||
size: 284, 212
|
||||
orig: 284, 212
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/Default/Highlight
|
||||
rotate: false
|
||||
xy: 4, 1832
|
||||
size: 284, 212
|
||||
orig: 284, 212
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Railroad
|
||||
rotate: false
|
||||
xy: 505, 1728
|
||||
@ -2316,21 +2316,21 @@ TileSets/FantasyHex/Tiles/Quarry+Stone
|
||||
index: -1
|
||||
TileSets/FantasyHex/Tiles/River-Bottom
|
||||
rotate: false
|
||||
xy: 1534, 694
|
||||
xy: 1574, 735
|
||||
size: 32, 28
|
||||
orig: 32, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Tiles/River-BottomLeft
|
||||
rotate: false
|
||||
xy: 1534, 658
|
||||
xy: 1574, 699
|
||||
size: 32, 28
|
||||
orig: 32, 28
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
TileSets/FantasyHex/Tiles/River-BottomRight
|
||||
rotate: false
|
||||
xy: 1534, 622
|
||||
xy: 1574, 663
|
||||
size: 32, 28
|
||||
orig: 32, 28
|
||||
offset: 0, 0
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
@ -929,6 +929,8 @@ Nothing =
|
||||
Annex city =
|
||||
Specialist Buildings =
|
||||
Specialist Allocation =
|
||||
Manual Specialists =
|
||||
Auto Specialists =
|
||||
Specialists =
|
||||
[specialist] slots =
|
||||
Food eaten =
|
||||
@ -956,6 +958,16 @@ Worked by [cityName] =
|
||||
Lock =
|
||||
Unlock =
|
||||
Move to city =
|
||||
Reset Citizens =
|
||||
Citizen Management =
|
||||
Avoid Growth =
|
||||
Default Focus =
|
||||
Food Focus =
|
||||
Production Focus =
|
||||
Gold Focus =
|
||||
Science Focus =
|
||||
Culture Focus =
|
||||
Happiness Focus =
|
||||
Please enter a new name for your city =
|
||||
Please select a tile for this building's [improvement] =
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.logic.city.CityFocus
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.INonPerpetualConstruction
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
@ -8,7 +9,6 @@ import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.MilestoneType
|
||||
import com.unciv.models.ruleset.Victory
|
||||
import com.unciv.models.ruleset.Victory.Focus
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
@ -16,46 +16,94 @@ import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.ui.victoryscreen.RankingType
|
||||
|
||||
object Automation {
|
||||
|
||||
fun rankTileForCityWork(tile: TileInfo, city: CityInfo, foodWeight: Float = 1f): Float {
|
||||
fun rankTileForCityWork(tile: TileInfo, city: CityInfo, cityStats: Stats): Float {
|
||||
val stats = tile.getTileStats(city, city.civInfo)
|
||||
return rankStatsForCityWork(stats, city, foodWeight)
|
||||
return rankStatsForCityWork(stats, city, cityStats)
|
||||
}
|
||||
|
||||
private fun rankStatsForCityWork(stats: Stats, city: CityInfo, foodWeight: Float = 1f): Float {
|
||||
var rank = 0f
|
||||
if (city.population.population < 5) {
|
||||
// "small city" - we care more about food and less about global problems like gold science and culture
|
||||
rank += stats.food * 1.2f * foodWeight
|
||||
rank += stats.production
|
||||
rank += stats.science / 2
|
||||
rank += stats.culture / 2
|
||||
rank += stats.gold / 5 // it's barely worth anything at this point
|
||||
} else {
|
||||
if (stats.food <= 2 || city.civInfo.getHappiness() > 5) rank += stats.food * 1.2f * foodWeight // food get more value to keep city growing
|
||||
else rank += (2.4f + (stats.food - 2) / 2) * foodWeight // 1.2 point for each food up to 2, from there on half a point
|
||||
|
||||
if (city.civInfo.gold < 0 && city.civInfo.statsForNextTurn.gold <= 0)
|
||||
rank += stats.gold // we have a global problem
|
||||
else rank += stats.gold / 3 // 3 gold is worse than 2 production
|
||||
|
||||
rank += stats.production
|
||||
rank += stats.science
|
||||
if (city.tiles.size < 12 || city.civInfo.wantsToFocusOn(Victory.Focus.Culture)) {
|
||||
rank += stats.culture
|
||||
} else rank += stats.culture / 2
|
||||
fun rankSpecialist(specialist: String, cityInfo: CityInfo, cityStats: Stats): Float {
|
||||
val stats = cityInfo.cityStats.getStatsOfSpecialist(specialist)
|
||||
var rank = rankStatsForCityWork(stats, cityInfo, cityStats, true)
|
||||
// derive GPP score
|
||||
var gpp = 0f
|
||||
if (cityInfo.getRuleset().specialists.containsKey(specialist)) { // To solve problems in total remake mods
|
||||
val specialistInfo = cityInfo.getRuleset().specialists[specialist]!!
|
||||
gpp = specialistInfo.greatPersonPoints.sumValues().toFloat()
|
||||
}
|
||||
gpp = gpp * (100 + cityInfo.currentGPPBonus) / 100
|
||||
rank += gpp * 3 // GPP weight
|
||||
return rank
|
||||
}
|
||||
|
||||
internal fun rankSpecialist(stats: Stats, cityInfo: CityInfo): Float {
|
||||
var rank = rankStatsForCityWork(stats, cityInfo)
|
||||
rank += 0.3f //GPP bonus
|
||||
return rank
|
||||
private fun rankStatsForCityWork(stats: Stats, city: CityInfo, cityStats: Stats, specialist: Boolean = false): Float {
|
||||
val cityAIFocus = city.cityAIFocus
|
||||
val yieldStats = stats.clone()
|
||||
|
||||
if (specialist) {
|
||||
// If you have the Food Bonus, count as 1 extra food production (base is 2food)
|
||||
for (unique in city.getMatchingUniques(UniqueType.FoodConsumptionBySpecialists))
|
||||
if (city.matchesFilter(unique.params[1]))
|
||||
yieldStats.food -= (unique.params[0].toFloat() / 100f) * 2f // base 2 food per Pop
|
||||
// Specialist Happiness Percentage Change 0f-1f
|
||||
for (unique in city.getMatchingUniques(UniqueType.UnhappinessFromPopulationTypePercentageChange))
|
||||
if (city.matchesFilter(unique.params[2]) && unique.params[1] == "Specialists")
|
||||
yieldStats.happiness -= (unique.params[0].toFloat() / 100f) // relative val is negative, make positive
|
||||
if (city.civInfo.getHappiness() < 0) yieldStats.happiness *= 2 // double weight for unhappy civilization
|
||||
}
|
||||
|
||||
val surplusFood = cityStats[Stat.Food]
|
||||
// Apply base weights
|
||||
yieldStats.applyRankingWeights()
|
||||
|
||||
if (surplusFood > 0 && city.avoidGrowth) {
|
||||
yieldStats.food = 0f // don't need more food!
|
||||
} else {
|
||||
if (cityAIFocus != CityFocus.NoFocus && cityAIFocus != CityFocus.FoodFocus && cityAIFocus != CityFocus.ProductionGrowthFocus && cityAIFocus != CityFocus.GoldGrowthFocus) {
|
||||
// Focus on non-food/growth
|
||||
if (surplusFood < 0)
|
||||
yieldStats.food *= 8 // Starving, need Food, get to 0
|
||||
else
|
||||
yieldStats.food /= 2
|
||||
} else if (!city.avoidGrowth) {
|
||||
// NoFocus or Food/Growth Focus. Target +2 Food Surplus
|
||||
if (surplusFood < 2)
|
||||
yieldStats.food *= 8
|
||||
else if (cityAIFocus != CityFocus.FoodFocus)
|
||||
yieldStats.food /= 2
|
||||
if (city.population.population < 5 && cityAIFocus != CityFocus.FoodFocus)
|
||||
// NoFocus or GoldGrow or ProdGrow, not Avoid Growth, pop < 5. FoodFocus already does this up
|
||||
yieldStats.food *= 3
|
||||
}
|
||||
}
|
||||
|
||||
if (city.population.population < 5) {
|
||||
// "small city" - we care more about food and less about global problems like gold science and culture
|
||||
// Food already handled above. Science/Culture have low weights in Stats already
|
||||
yieldStats.gold /= 2 // it's barely worth anything at this point
|
||||
} else {
|
||||
if (city.civInfo.gold < 0 && city.civInfo.statsForNextTurn.gold <= 0)
|
||||
yieldStats.gold *= 2 // We have a global problem
|
||||
|
||||
if (city.tiles.size < 12 || city.civInfo.wantsToFocusOn(Focus.Culture))
|
||||
yieldStats.culture *= 2
|
||||
|
||||
if (city.civInfo.getHappiness() < 0 && !specialist) // since this doesn't get updated, may overshoot
|
||||
yieldStats.happiness *= 2
|
||||
|
||||
if (city.civInfo.wantsToFocusOn(Focus.Science))
|
||||
yieldStats.science *= 2
|
||||
}
|
||||
|
||||
// Apply City focus
|
||||
cityAIFocus.applyWeightTo(yieldStats)
|
||||
|
||||
return yieldStats.values.sum()
|
||||
}
|
||||
|
||||
fun tryTrainMilitaryUnit(city: CityInfo) {
|
||||
@ -74,8 +122,8 @@ object Automation {
|
||||
mapUnit.getMatchingUniques(UniqueType.CarryAirUnits).firstOrNull() ?: return 0
|
||||
if (mapUnitCarryUnique.params[1] != carryFilter) return 0 //Carries a different type of unit
|
||||
return mapUnitCarryUnique.params[0].toInt() +
|
||||
mapUnit.getMatchingUniques(UniqueType.CarryExtraAirUnits)
|
||||
.filter { it.params[1] == carryFilter }.sumOf { it.params[0].toInt() }
|
||||
mapUnit.getMatchingUniques(UniqueType.CarryExtraAirUnits)
|
||||
.filter { it.params[1] == carryFilter }.sumOf { it.params[0].toInt() }
|
||||
}
|
||||
|
||||
val totalCarriableUnits =
|
||||
@ -97,13 +145,15 @@ object Automation {
|
||||
findWaterConnectedCitiesAndEnemies.stepToEnd()
|
||||
if (findWaterConnectedCitiesAndEnemies.getReachedTiles().none {
|
||||
(it.isCityCenter() && it.getOwner() != city.civInfo)
|
||||
|| (it.militaryUnit != null && it.militaryUnit!!.civInfo != city.civInfo)
|
||||
|| (it.militaryUnit != null && it.militaryUnit!!.civInfo != city.civInfo)
|
||||
}) // there is absolutely no reason for you to make water units on this body of water.
|
||||
militaryUnits = militaryUnits.filter { !it.isWaterUnit() }
|
||||
|
||||
|
||||
val carryingOnlyUnits = militaryUnits.filter { it.hasUnique(UniqueType.CarryAirUnits)
|
||||
&& it.hasUnique(UniqueType.CannotAttack) }.toList()
|
||||
val carryingOnlyUnits = militaryUnits.filter {
|
||||
it.hasUnique(UniqueType.CarryAirUnits)
|
||||
&& it.hasUnique(UniqueType.CannotAttack)
|
||||
}.toList()
|
||||
|
||||
for (unit in carryingOnlyUnits)
|
||||
if (providesUnneededCarryingSlots(unit, city.civInfo))
|
||||
@ -125,9 +175,11 @@ object Automation {
|
||||
.map { it.unitType }
|
||||
.distinct()
|
||||
if (availableTypes.none()) return null
|
||||
val bestUnitsForType = availableTypes.map { type -> militaryUnits
|
||||
val bestUnitsForType = availableTypes.map { type ->
|
||||
militaryUnits
|
||||
.filter { unit -> unit.unitType == type }
|
||||
.maxByOrNull { unit -> unit.cost }!! }
|
||||
.maxByOrNull { unit -> unit.cost }!!
|
||||
}
|
||||
// Check the maximum force evaluation for the shortlist so we can prune useless ones (ie scouts)
|
||||
val bestForce = bestUnitsForType.maxOf { it.getForceEvaluation() }
|
||||
chosenUnit = bestUnitsForType.filter { it.uniqueTo != null || it.getForceEvaluation() > bestForce / 3 }.toList().random()
|
||||
@ -172,14 +224,14 @@ object Automation {
|
||||
|
||||
/** Checks both feasibility of Buildings with a CreatesOneImprovement unique
|
||||
* and resource scarcity making a construction undesirable.
|
||||
*/
|
||||
*/
|
||||
fun allowAutomatedConstruction(
|
||||
civInfo: CivilizationInfo,
|
||||
cityInfo: CityInfo,
|
||||
construction: INonPerpetualConstruction
|
||||
): Boolean {
|
||||
return allowCreateImprovementBuildings(civInfo, cityInfo, construction)
|
||||
&& allowSpendingResource(civInfo, construction)
|
||||
&& allowSpendingResource(civInfo, construction)
|
||||
}
|
||||
|
||||
/** Checks both feasibility of Buildings with a [UniqueType.CreatesOneImprovement] unique (appropriate tile available).
|
||||
@ -241,7 +293,7 @@ object Automation {
|
||||
val neededForBuilding = civInfo.lastEraResourceUsedForBuilding[resource] != null
|
||||
// Don't care about old units
|
||||
val neededForUnits = civInfo.lastEraResourceUsedForUnit[resource] != null
|
||||
&& civInfo.lastEraResourceUsedForUnit[resource]!! >= civInfo.getEraNumber()
|
||||
&& civInfo.lastEraResourceUsedForUnit[resource]!! >= civInfo.getEraNumber()
|
||||
|
||||
// No need to save for both
|
||||
if (!neededForBuilding || !neededForUnits) {
|
||||
@ -283,11 +335,11 @@ object Automation {
|
||||
}
|
||||
|
||||
/** Support [UniqueType.CreatesOneImprovement] unique - find best tile for placement automation */
|
||||
fun getTileForConstructionImprovement(cityInfo: CityInfo, improvement: TileImprovement): TileInfo? {
|
||||
fun getTileForConstructionImprovement(cityInfo: CityInfo, improvement: TileImprovement): TileInfo? {
|
||||
return cityInfo.getTiles().filter {
|
||||
it.canBuildImprovement(improvement, cityInfo.civInfo)
|
||||
}.maxByOrNull {
|
||||
rankTileForCityWork(it, cityInfo)
|
||||
rankTileForCityWork(it, cityInfo, cityInfo.cityStats.currentCityStats)
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,7 +402,7 @@ object Automation {
|
||||
if (adjacentTile.hasViewableResource(cityInfo.civInfo) &&
|
||||
(adjacentDistance < 3 ||
|
||||
adjacentTile.tileResource.resourceType != ResourceType.Bonus
|
||||
)
|
||||
)
|
||||
) score -= 1
|
||||
if (adjacentTile.naturalWonder != null) {
|
||||
if (adjacentDistance < 3) adjacentNaturalWonder = true
|
||||
@ -381,7 +433,7 @@ object Automation {
|
||||
}
|
||||
}
|
||||
|
||||
enum class ThreatLevel{
|
||||
enum class ThreatLevel {
|
||||
VeryLow,
|
||||
Low,
|
||||
Medium,
|
||||
|
@ -853,7 +853,7 @@ object NextTurnAutomation {
|
||||
city.annexCity()
|
||||
}
|
||||
|
||||
city.reassignPopulation()
|
||||
city.reassignAllPopulation()
|
||||
|
||||
city.cityConstructions.chooseNextConstruction()
|
||||
if (city.health < city.getMaxHealth())
|
||||
|
@ -19,6 +19,7 @@ import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
@ -34,6 +35,49 @@ enum class CityFlags {
|
||||
Resistance
|
||||
}
|
||||
|
||||
// if tableEnabled == true, then Stat != null
|
||||
enum class CityFocus(val label: String, val tableEnabled: Boolean, val stat: Stat? = null) {
|
||||
NoFocus("Default Focus", true, null) {
|
||||
override fun getStatMultiplier(stat: Stat) = 1f // actually redundant, but that's two steps to see
|
||||
},
|
||||
FoodFocus("${Stat.Food.name} Focus", true, Stat.Food),
|
||||
ProductionFocus("${Stat.Production.name} Focus", true, Stat.Production),
|
||||
GoldFocus("${Stat.Gold.name} Focus", true, Stat.Gold),
|
||||
ScienceFocus("${Stat.Science.name} Focus", true, Stat.Science),
|
||||
CultureFocus("${Stat.Culture.name} Focus", true, Stat.Culture),
|
||||
GoldGrowthFocus("Gold Growth Focus", false) {
|
||||
override fun getStatMultiplier(stat: Stat) = when (stat) {
|
||||
Stat.Gold, Stat.Food -> 2f
|
||||
else -> 1f
|
||||
}
|
||||
},
|
||||
ProductionGrowthFocus("Production Growth Focus", false) {
|
||||
override fun getStatMultiplier(stat: Stat) = when (stat) {
|
||||
Stat.Production, Stat.Food -> 2f
|
||||
else -> 1f
|
||||
}
|
||||
},
|
||||
FaithFocus("${Stat.Faith.name} Focus", true, Stat.Faith),
|
||||
HappinessFocus("${Stat.Happiness.name} Focus", false, Stat.Happiness);
|
||||
//GreatPersonFocus;
|
||||
|
||||
open fun getStatMultiplier(stat: Stat) = when (this.stat) {
|
||||
stat -> 3f
|
||||
else -> 1f
|
||||
}
|
||||
|
||||
fun applyWeightTo(stats: Stats) {
|
||||
for (stat in Stat.values()) {
|
||||
stats[stat] *= getStatMultiplier(stat)
|
||||
}
|
||||
}
|
||||
|
||||
fun safeValueOf(stat: Stat): CityFocus {
|
||||
return values().firstOrNull { it.stat == stat } ?: NoFocus
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CityInfo {
|
||||
@Suppress("JoinDeclarationAndAssignment")
|
||||
@Transient
|
||||
@ -81,10 +125,15 @@ class CityInfo {
|
||||
|
||||
/** Tiles that the population in them won't be reassigned */
|
||||
var lockedTiles = HashSet<Vector2>()
|
||||
var manualSpecialists = false
|
||||
var isBeingRazed = false
|
||||
var attackedThisTurn = false
|
||||
var hasSoldBuildingThisTurn = false
|
||||
var isPuppet = false
|
||||
var updateCitizens = false // flag so that on endTurn() the Governor reassigns Citizens
|
||||
var cityAIFocus: CityFocus = CityFocus.NoFocus
|
||||
var avoidGrowth: Boolean = false
|
||||
@Transient var currentGPPBonus: Int = 0 // temporary variable saved for rankSpecialist()
|
||||
|
||||
/** The very first found city is the _original_ capital,
|
||||
* while the _current_ capital can be any other city after the original one is captured.
|
||||
@ -148,7 +197,6 @@ class CityInfo {
|
||||
}
|
||||
|
||||
population.autoAssignPopulation()
|
||||
cityStats.update()
|
||||
|
||||
// Update proximity rankings for all civs
|
||||
for (otherCiv in civInfo.gameInfo.getAliveMajorCivs()) {
|
||||
@ -302,6 +350,10 @@ class CityInfo {
|
||||
toReturn.isOriginalCapital = isOriginalCapital
|
||||
toReturn.flagsCountdown.putAll(flagsCountdown)
|
||||
toReturn.demandedResource = demandedResource
|
||||
toReturn.updateCitizens = updateCitizens
|
||||
toReturn.cityAIFocus = cityAIFocus
|
||||
toReturn.avoidGrowth = avoidGrowth
|
||||
toReturn.manualSpecialists = manualSpecialists
|
||||
return toReturn
|
||||
}
|
||||
|
||||
@ -436,7 +488,7 @@ class CityInfo {
|
||||
fun isGrowing() = foodForNextTurn() > 0
|
||||
fun isStarving() = foodForNextTurn() < 0
|
||||
|
||||
private fun foodForNextTurn() = cityStats.currentCityStats.food.roundToInt()
|
||||
fun foodForNextTurn() = cityStats.currentCityStats.food.roundToInt()
|
||||
|
||||
/** Take null to mean infinity. */
|
||||
fun getNumTurnsToNewPopulation(): Int? {
|
||||
@ -453,10 +505,32 @@ class CityInfo {
|
||||
if (!isStarving()) return null
|
||||
return population.foodStored / -foodForNextTurn() + 1
|
||||
}
|
||||
|
||||
|
||||
fun containsBuildingUnique(uniqueType: UniqueType) =
|
||||
cityConstructions.getBuiltBuildings().flatMap { it.uniqueObjects }.any { it.isOfType(uniqueType) }
|
||||
|
||||
fun getGreatPersonPercentageBonus(): Int{
|
||||
var allGppPercentageBonus = 0
|
||||
for (unique in getMatchingUniques(UniqueType.GreatPersonPointPercentage)
|
||||
+ getMatchingUniques(UniqueType.GreatPersonPointPercentageDeprecated)
|
||||
) {
|
||||
if (!matchesFilter(unique.params[1])) continue
|
||||
allGppPercentageBonus += unique.params[0].toInt()
|
||||
}
|
||||
|
||||
// Sweden UP
|
||||
for (otherCiv in civInfo.getKnownCivs()) {
|
||||
if (!civInfo.getDiplomacyManager(otherCiv).hasFlag(DiplomacyFlags.DeclarationOfFriendship))
|
||||
continue
|
||||
|
||||
for (ourUnique in civInfo.getMatchingUniques(UniqueType.GreatPersonBoostWithFriendship))
|
||||
allGppPercentageBonus += ourUnique.params[0].toInt()
|
||||
for (theirUnique in otherCiv.getMatchingUniques(UniqueType.GreatPersonBoostWithFriendship))
|
||||
allGppPercentageBonus += theirUnique.params[0].toInt()
|
||||
}
|
||||
return allGppPercentageBonus
|
||||
}
|
||||
|
||||
fun getGreatPersonPointsForNextTurn(): HashMap<String, Counter<String>> {
|
||||
val sourceToGPP = HashMap<String, Counter<String>>()
|
||||
|
||||
@ -481,24 +555,7 @@ class CityInfo {
|
||||
gppCounter.add(unitName, gppCounter[unitName]!! * unique.params[1].toInt() / 100)
|
||||
}
|
||||
|
||||
var allGppPercentageBonus = 0
|
||||
for (unique in getMatchingUniques(UniqueType.GreatPersonPointPercentage)
|
||||
+ getMatchingUniques(UniqueType.GreatPersonPointPercentageDeprecated)
|
||||
) {
|
||||
if (!matchesFilter(unique.params[1])) continue
|
||||
allGppPercentageBonus += unique.params[0].toInt()
|
||||
}
|
||||
|
||||
// Sweden UP
|
||||
for (otherCiv in civInfo.getKnownCivs()) {
|
||||
if (!civInfo.getDiplomacyManager(otherCiv).hasFlag(DiplomacyFlags.DeclarationOfFriendship))
|
||||
continue
|
||||
|
||||
for (ourUnique in civInfo.getMatchingUniques(UniqueType.GreatPersonBoostWithFriendship))
|
||||
allGppPercentageBonus += ourUnique.params[0].toInt()
|
||||
for (theirUnique in otherCiv.getMatchingUniques(UniqueType.GreatPersonBoostWithFriendship))
|
||||
allGppPercentageBonus += theirUnique.params[0].toInt()
|
||||
}
|
||||
val allGppPercentageBonus = getGreatPersonPercentageBonus()
|
||||
|
||||
for (unitName in gppCounter.keys)
|
||||
gppCounter.add(unitName, gppCounter[unitName]!! * allGppPercentageBonus / 100)
|
||||
@ -585,7 +642,11 @@ class CityInfo {
|
||||
tryUpdateRoadStatus()
|
||||
attackedThisTurn = false
|
||||
|
||||
if (isPuppet) reassignPopulation()
|
||||
if (isPuppet) reassignAllPopulation()
|
||||
else if (updateCitizens){
|
||||
reassignPopulation()
|
||||
updateCitizens = false
|
||||
}
|
||||
|
||||
// The ordering is intentional - you get a turn without WLTKD even if you have the next resource already
|
||||
if (!hasFlag(CityFlags.WeLoveTheKing))
|
||||
@ -644,19 +705,23 @@ class CityInfo {
|
||||
demandedResource = ""
|
||||
}
|
||||
|
||||
fun reassignPopulation() {
|
||||
var foodWeight = 1f
|
||||
var foodPerTurn = 0f
|
||||
while (foodWeight < 3 && foodPerTurn <= 0) {
|
||||
workedTiles = hashSetOf()
|
||||
population.specialistAllocations.clear()
|
||||
for (i in 0..population.population)
|
||||
population.autoAssignPopulation(foodWeight)
|
||||
cityStats.update()
|
||||
// Reassign all Specialists and Unlock all tiles
|
||||
// Mainly for automated cities, Puppets, just captured
|
||||
fun reassignAllPopulation() {
|
||||
manualSpecialists = false
|
||||
reassignPopulation(resetLocked = true)
|
||||
}
|
||||
|
||||
foodPerTurn = foodForNextTurn().toFloat()
|
||||
foodWeight += 0.5f
|
||||
fun reassignPopulation(resetLocked: Boolean = false) {
|
||||
if (resetLocked) {
|
||||
workedTiles = hashSetOf()
|
||||
lockedTiles = hashSetOf()
|
||||
} else {
|
||||
workedTiles = lockedTiles
|
||||
}
|
||||
if (!manualSpecialists)
|
||||
population.specialistAllocations.clear()
|
||||
population.autoAssignPopulation()
|
||||
}
|
||||
|
||||
fun endTurn() {
|
||||
@ -905,4 +970,4 @@ class CityInfo {
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ class CityInfoConquestFunctions(val city: CityInfo){
|
||||
|
||||
health = getMaxHealth() / 2 // I think that cities recover to half health when conquered?
|
||||
if (population.population > 1) population.addPopulation(-1 - population.population / 4) // so from 2-4 population, remove 1, from 5-8, remove 2, etc.
|
||||
reassignPopulation()
|
||||
reassignAllPopulation()
|
||||
|
||||
if (!reconqueredCityWhileStillInResistance && foundingCiv != receivingCiv.civName) {
|
||||
// add resistance
|
||||
|
@ -2,9 +2,12 @@ package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.automation.Automation
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.Counter
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.ui.utils.toPercent
|
||||
import com.unciv.ui.utils.withItem
|
||||
import com.unciv.ui.utils.withoutItem
|
||||
import kotlin.math.floor
|
||||
@ -82,7 +85,7 @@ class PopulationManager {
|
||||
if (percentOfFoodCarriedOver > 95) percentOfFoodCarriedOver = 95
|
||||
foodStored += (getFoodToNextPopulation() * percentOfFoodCarriedOver / 100f).toInt()
|
||||
addPopulation(1)
|
||||
autoAssignPopulation()
|
||||
cityInfo.updateCitizens = true
|
||||
cityInfo.civInfo.addNotification("[${cityInfo.name}] has grown!", cityInfo.location, NotificationIcon.Growth)
|
||||
}
|
||||
}
|
||||
@ -109,34 +112,50 @@ class PopulationManager {
|
||||
addPopulation(-population + count)
|
||||
}
|
||||
|
||||
internal fun autoAssignPopulation(foodWeight: Float = 1f) {
|
||||
internal fun autoAssignPopulation() {
|
||||
cityInfo.cityStats.update() // calculate current stats with current assignments
|
||||
val cityStats = cityInfo.cityStats.currentCityStats
|
||||
cityInfo.currentGPPBonus = cityInfo.getGreatPersonPercentageBonus() // pre-calculate
|
||||
var specialistFoodBonus = 2f // See CityStats.calcFoodEaten()
|
||||
for (unique in cityInfo.getMatchingUniques(UniqueType.FoodConsumptionBySpecialists))
|
||||
if (cityInfo.matchesFilter(unique.params[1]))
|
||||
specialistFoodBonus *= unique.params[0].toPercent()
|
||||
specialistFoodBonus = 2f - specialistFoodBonus
|
||||
|
||||
for (i in 1..getFreePopulation()) {
|
||||
//evaluate tiles
|
||||
val bestTile: TileInfo? = cityInfo.getTiles()
|
||||
val (bestTile, valueBestTile) = cityInfo.getTiles()
|
||||
.filter { it.aerialDistanceTo(cityInfo.getCenterTile()) <= 3 }
|
||||
.filterNot { it.providesYield() }
|
||||
.maxByOrNull { Automation.rankTileForCityWork(it, cityInfo, foodWeight) }
|
||||
val valueBestTile = if (bestTile == null) 0f
|
||||
else Automation.rankTileForCityWork(bestTile, cityInfo, foodWeight)
|
||||
.associateWith { Automation.rankTileForCityWork(it, cityInfo, cityStats) }
|
||||
.maxByOrNull { it.value }
|
||||
?: object : Map.Entry<TileInfo?, Float> {
|
||||
override val key: TileInfo? = null
|
||||
override val value = 0f
|
||||
}
|
||||
|
||||
val bestJob: String? = getMaxSpecialists()
|
||||
val bestJob: String? = if (cityInfo.manualSpecialists) null else getMaxSpecialists()
|
||||
.filter { specialistAllocations[it.key]!! < it.value }
|
||||
.map { it.key }
|
||||
.maxByOrNull { Automation.rankSpecialist(getStatsOfSpecialist(it), cityInfo) }
|
||||
|
||||
.maxByOrNull { Automation.rankSpecialist(it, cityInfo, cityStats) }
|
||||
|
||||
var valueBestSpecialist = 0f
|
||||
if (bestJob != null) {
|
||||
val specialistStats = getStatsOfSpecialist(bestJob)
|
||||
valueBestSpecialist = Automation.rankSpecialist(specialistStats, cityInfo)
|
||||
valueBestSpecialist = Automation.rankSpecialist(bestJob, cityInfo, cityStats)
|
||||
}
|
||||
|
||||
//assign population
|
||||
if (valueBestTile > valueBestSpecialist) {
|
||||
if (bestTile != null)
|
||||
if (bestTile != null) {
|
||||
cityInfo.workedTiles = cityInfo.workedTiles.withItem(bestTile.position)
|
||||
} else if (bestJob != null) specialistAllocations.add(bestJob, 1)
|
||||
cityStats[Stat.Food] += bestTile.getTileStats(cityInfo, cityInfo.civInfo)[Stat.Food]
|
||||
}
|
||||
} else if (bestJob != null) {
|
||||
specialistAllocations.add(bestJob, 1)
|
||||
cityStats[Stat.Food] += specialistFoodBonus
|
||||
}
|
||||
}
|
||||
cityInfo.cityStats.update()
|
||||
}
|
||||
|
||||
fun unassignExtraPopulation() {
|
||||
@ -162,19 +181,19 @@ class PopulationManager {
|
||||
cityInfo.workedTiles.asSequence()
|
||||
.map { cityInfo.tileMap[it] }
|
||||
.minByOrNull {
|
||||
Automation.rankTileForCityWork(it, cityInfo)
|
||||
Automation.rankTileForCityWork(it, cityInfo, cityInfo.cityStats.currentCityStats)
|
||||
+(if (it.isLocked()) 10 else 0)
|
||||
}!!
|
||||
}
|
||||
val valueWorstTile = if (worstWorkedTile == null) 0f
|
||||
else Automation.rankTileForCityWork(worstWorkedTile, cityInfo)
|
||||
else Automation.rankTileForCityWork(worstWorkedTile, cityInfo, cityInfo.cityStats.currentCityStats)
|
||||
|
||||
//evaluate specialists
|
||||
val worstJob: String? = specialistAllocations.keys
|
||||
.minByOrNull { Automation.rankSpecialist(getStatsOfSpecialist(it), cityInfo) }
|
||||
val worstJob: String? = if (cityInfo.manualSpecialists) null else specialistAllocations.keys
|
||||
.minByOrNull { Automation.rankSpecialist(it, cityInfo, cityInfo.cityStats.currentCityStats) }
|
||||
var valueWorstSpecialist = 0f
|
||||
if (worstJob != null)
|
||||
valueWorstSpecialist = Automation.rankSpecialist(getStatsOfSpecialist(worstJob), cityInfo)
|
||||
valueWorstSpecialist = Automation.rankSpecialist(worstJob, cityInfo, cityInfo.cityStats.currentCityStats)
|
||||
|
||||
|
||||
//un-assign population
|
||||
|
@ -257,6 +257,9 @@ class TechManager {
|
||||
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
|
||||
}
|
||||
updateTransientBooleans()
|
||||
for (city in civInfo.cities) {
|
||||
city.updateCitizens = true
|
||||
}
|
||||
|
||||
civInfo.addNotification("Research of [$techName] has completed!", TechAction(techName), NotificationIcon.Science, techName)
|
||||
civInfo.popupAlerts.add(PopupAlert(AlertType.TechResearched, techName))
|
||||
|
@ -678,6 +678,7 @@ class MapUnit {
|
||||
}
|
||||
|
||||
tile.improvementInProgress = null
|
||||
tile.getCity()?.updateCitizens = true
|
||||
}
|
||||
|
||||
|
||||
|
@ -126,6 +126,17 @@ open class Stats(
|
||||
}
|
||||
|
||||
operator fun div(number: Float) = times(1/number)
|
||||
|
||||
/** Apply weighting for Production Ranking */
|
||||
fun applyRankingWeights(){
|
||||
food *= 14
|
||||
production *= 12
|
||||
gold *= 8 // 3 gold worth about 2 production
|
||||
science *= 7
|
||||
culture *= 6
|
||||
happiness *= 10 // base
|
||||
faith *= 5
|
||||
}
|
||||
|
||||
/** ***Not*** only a debug helper. It returns a string representing the content, already _translated_.
|
||||
*
|
||||
|
62
core/src/com/unciv/ui/cityscreen/CitizenManagementTable.kt
Normal file
62
core/src/com/unciv/ui/cityscreen/CitizenManagementTable.kt
Normal file
@ -0,0 +1,62 @@
|
||||
package com.unciv.ui.cityscreen
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.logic.city.CityFocus
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.utils.*
|
||||
|
||||
class CitizenManagementTable(val cityScreen: CityScreen) : Table() {
|
||||
private val innerTable = Table()
|
||||
val city = cityScreen.city
|
||||
|
||||
init {
|
||||
innerTable.background = ImageGetter.getBackground(ImageGetter.getBlue().darken(0.5f))
|
||||
add(innerTable).pad(2f).fill()
|
||||
background = ImageGetter.getBackground(Color.WHITE)
|
||||
}
|
||||
|
||||
fun update(visible: Boolean = false) {
|
||||
innerTable.clear()
|
||||
|
||||
if (!visible) {
|
||||
isVisible = false
|
||||
return
|
||||
}
|
||||
isVisible = true
|
||||
|
||||
val colorSelected = BaseScreen.skin.get("selection", Color::class.java)
|
||||
val colorButton = BaseScreen.skin.get("color", Color::class.java)
|
||||
// effectively a button, but didn't want to rewrite TextButton style
|
||||
// and much more compact and can control backgrounds easily based on settings
|
||||
val avoidLabel = "Avoid Growth".toLabel()
|
||||
val avoidCell = Table()
|
||||
avoidCell.touchable = Touchable.enabled
|
||||
avoidCell.add(avoidLabel).pad(5f)
|
||||
avoidCell.onClick { city.avoidGrowth = !city.avoidGrowth; city.reassignPopulation(); cityScreen.update() }
|
||||
|
||||
avoidCell.background = ImageGetter.getBackground(if (city.avoidGrowth) colorSelected else colorButton)
|
||||
innerTable.add(avoidCell).colspan(2).growX().pad(3f)
|
||||
innerTable.row()
|
||||
|
||||
for (focus in CityFocus.values()) {
|
||||
if (!focus.tableEnabled) continue
|
||||
if (focus == CityFocus.FaithFocus && !city.civInfo.gameInfo.isReligionEnabled()) continue
|
||||
val label = focus.label.toLabel()
|
||||
val cell = Table()
|
||||
cell.touchable = Touchable.enabled
|
||||
cell.add(label).pad(5f)
|
||||
cell.onClick { city.cityAIFocus = focus; city.reassignPopulation(); cityScreen.update() }
|
||||
|
||||
cell.background = ImageGetter.getBackground(if (city.cityAIFocus == focus) colorSelected else colorButton)
|
||||
innerTable.add(cell).growX().pad(3f)
|
||||
if (focus.stat != null)
|
||||
innerTable.add(ImageGetter.getStatIcon(focus.stat.name)).size(20f).padRight(5f)
|
||||
innerTable.row()
|
||||
}
|
||||
|
||||
pack()
|
||||
}
|
||||
|
||||
}
|
@ -15,10 +15,10 @@ import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.map.TileGroupMap
|
||||
import com.unciv.ui.popup.ToastPopup
|
||||
import com.unciv.ui.tilegroups.TileGroup
|
||||
import com.unciv.ui.tilegroups.TileSetStrings
|
||||
import com.unciv.ui.utils.*
|
||||
import java.util.*
|
||||
@ -56,6 +56,12 @@ class CityScreen(
|
||||
/** Displays raze city button - sits on TOP CENTER */
|
||||
private var razeCityButtonHolder = Table()
|
||||
|
||||
/** Displays reset locks button - sits on BOT RIGHT */
|
||||
private var resetCitizensButtonHolder = Table()
|
||||
|
||||
/** Displays reset locks button - sits on BOT RIGHT */
|
||||
private var citizenManagementButtonHolder = Table()
|
||||
|
||||
/** Displays city stats info */
|
||||
private var cityStatsTable = CityStatsTable(this)
|
||||
|
||||
@ -65,6 +71,10 @@ class CityScreen(
|
||||
/** Displays selected construction info, alternate with tileTable - sits on BOTTOM RIGHT */
|
||||
private var selectedConstructionTable = ConstructionInfoTable(this)
|
||||
|
||||
/** Displays selected construction info, alternate with tileTable - sits on BOTTOM RIGHT */
|
||||
private var citizenManagementTable = CitizenManagementTable(this)
|
||||
var citizenManagementVisible = false
|
||||
|
||||
/** Displays city name, allows switching between cities - sits on BOTTOM CENTER */
|
||||
private var cityPickerTable = CityScreenCityPickerTable(this)
|
||||
|
||||
@ -109,10 +119,27 @@ class CityScreen(
|
||||
|
||||
//stage.setDebugTableUnderMouse(true)
|
||||
stage.addActor(cityStatsTable)
|
||||
val resetCitizensButton = "Reset Citizens".toTextButton()
|
||||
resetCitizensButton.labelCell.pad(5f)
|
||||
resetCitizensButton.onClick { city.reassignPopulation(resetLocked = true); update() }
|
||||
resetCitizensButtonHolder.add(resetCitizensButton)
|
||||
resetCitizensButtonHolder.pack()
|
||||
stage.addActor(resetCitizensButtonHolder)
|
||||
val citizenManagementButton = "Citizen Management".toTextButton()
|
||||
citizenManagementButton.labelCell.pad(5f)
|
||||
citizenManagementButton.onClick {
|
||||
clearSelection()
|
||||
citizenManagementVisible = true
|
||||
update()
|
||||
}
|
||||
citizenManagementButtonHolder.add(citizenManagementButton)
|
||||
citizenManagementButtonHolder.pack()
|
||||
stage.addActor(citizenManagementButtonHolder)
|
||||
constructionsTable.addActorsToStage()
|
||||
stage.addActor(cityInfoTable)
|
||||
stage.addActor(selectedConstructionTable)
|
||||
stage.addActor(tileTable)
|
||||
stage.addActor(citizenManagementTable)
|
||||
stage.addActor(cityPickerTable) // add late so it's top in Z-order and doesn't get covered in cramped portrait
|
||||
stage.addActor(exitCityButton)
|
||||
update()
|
||||
@ -150,7 +177,24 @@ class CityScreen(
|
||||
tileTable.setPosition(stage.width - posFromEdge, posFromEdge, Align.bottomRight)
|
||||
selectedConstructionTable.update(selectedConstruction)
|
||||
selectedConstructionTable.setPosition(stage.width - posFromEdge, posFromEdge, Align.bottomRight)
|
||||
|
||||
citizenManagementTable.update(citizenManagementVisible)
|
||||
citizenManagementTable.setPosition(stage.width - posFromEdge, posFromEdge, Align.bottomRight)
|
||||
if (selectedTile == null && selectedConstruction == null && !citizenManagementVisible)
|
||||
resetCitizensButtonHolder.setPosition(stage.width - posFromEdge,
|
||||
posFromEdge, Align.bottomRight)
|
||||
else if (selectedConstruction != null)
|
||||
resetCitizensButtonHolder.setPosition(stage.width - posFromEdge,
|
||||
posFromEdge + selectedConstructionTable.height + 10f, Align.bottomRight)
|
||||
else if (selectedTile != null)
|
||||
resetCitizensButtonHolder.setPosition(stage.width - posFromEdge,
|
||||
posFromEdge + tileTable.height + 10f, Align.bottomRight)
|
||||
else
|
||||
resetCitizensButtonHolder.setPosition(stage.width - posFromEdge,
|
||||
posFromEdge + citizenManagementTable.height + 10f, Align.bottomRight)
|
||||
citizenManagementButtonHolder.isVisible = !citizenManagementVisible
|
||||
citizenManagementButtonHolder.setPosition(stage.width - posFromEdge,
|
||||
posFromEdge + resetCitizensButtonHolder.y + resetCitizensButtonHolder.height + 10f, Align.bottomRight)
|
||||
|
||||
// In portrait mode only: calculate already occupied horizontal space
|
||||
val rightMargin = when {
|
||||
!isPortrait() -> 0f
|
||||
@ -343,9 +387,12 @@ class CityScreen(
|
||||
if (tileGroup.isWorkable && canChangeState) {
|
||||
if (!tileInfo.providesYield() && cityInfo.population.getFreePopulation() > 0) {
|
||||
cityInfo.workedTiles.add(tileInfo.position)
|
||||
cityInfo.lockedTiles.add(tileInfo.position)
|
||||
game.settings.addCompletedTutorialTask("Reassign worked tiles")
|
||||
} else if (tileInfo.isWorked() && !tileInfo.isLocked())
|
||||
} else if (tileInfo.isWorked()) {
|
||||
cityInfo.workedTiles.remove(tileInfo.position)
|
||||
cityInfo.lockedTiles.remove(tileInfo.position)
|
||||
}
|
||||
cityInfo.cityStats.update()
|
||||
}
|
||||
update()
|
||||
@ -365,11 +412,13 @@ class CityScreen(
|
||||
pickTileData = null
|
||||
}
|
||||
selectedTile = null
|
||||
citizenManagementVisible = false
|
||||
}
|
||||
private fun selectTile(newTile: TileInfo?) {
|
||||
selectedConstruction = null
|
||||
selectedQueueEntryTargetTile = null
|
||||
pickTileData = null
|
||||
citizenManagementVisible = false
|
||||
selectedTile = newTile
|
||||
}
|
||||
fun clearSelection() = selectTile(null)
|
||||
|
@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.city.CityFlags
|
||||
import com.unciv.logic.city.CityFocus
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.tr
|
||||
@ -35,9 +36,24 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
innerTable.clear()
|
||||
|
||||
val miniStatsTable = Table()
|
||||
val selected = BaseScreen.skin.get("selection", Color::class.java)
|
||||
for ((stat, amount) in cityInfo.cityStats.currentCityStats) {
|
||||
if (stat == Stat.Faith && !cityInfo.civInfo.gameInfo.isReligionEnabled()) continue
|
||||
miniStatsTable.add(ImageGetter.getStatIcon(stat.name)).size(20f).padRight(5f)
|
||||
val icon = Table()
|
||||
if (cityInfo.cityAIFocus.stat == stat) {
|
||||
icon.add(ImageGetter.getStatIcon(stat.name).surroundWithCircle(27f, false, color = selected))
|
||||
icon.onClick {
|
||||
cityInfo.cityAIFocus = CityFocus.NoFocus
|
||||
cityInfo.reassignPopulation(); cityScreen.update()
|
||||
}
|
||||
} else {
|
||||
icon.add(ImageGetter.getStatIcon(stat.name).surroundWithCircle(27f, false, color = Color.CLEAR))
|
||||
icon.onClick {
|
||||
cityInfo.cityAIFocus = cityInfo.cityAIFocus.safeValueOf(stat)
|
||||
cityInfo.reassignPopulation(); cityScreen.update()
|
||||
}
|
||||
}
|
||||
miniStatsTable.add(icon).size(27f).padRight(5f)
|
||||
val valueToDisplay = if (stat == Stat.Happiness) cityInfo.cityStats.happinessList.values.sum() else amount
|
||||
miniStatsTable.add(round(valueToDisplay).toInt().toLabel()).padRight(10f)
|
||||
}
|
||||
@ -57,6 +73,8 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
private fun addText() {
|
||||
val unassignedPopString = "{Unassigned population}: ".tr() +
|
||||
cityInfo.population.getFreePopulation().toString() + "/" + cityInfo.population.population
|
||||
val unassignedPopLabel = unassignedPopString.toLabel()
|
||||
unassignedPopLabel.onClick { cityInfo.reassignPopulation(); cityScreen.update() }
|
||||
|
||||
var turnsToExpansionString =
|
||||
if (cityInfo.cityStats.currentCityStats.culture > 0 && cityInfo.expansion.getChoosableTiles().any()) {
|
||||
@ -80,7 +98,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
}.tr()
|
||||
turnsToPopString += " (${cityInfo.population.foodStored}${Fonts.food}/${cityInfo.population.getFoodToNextPopulation()}${Fonts.food})"
|
||||
|
||||
innerTable.add(unassignedPopString.toLabel()).row()
|
||||
innerTable.add(unassignedPopLabel).row()
|
||||
innerTable.add(turnsToExpansionString.toLabel()).row()
|
||||
innerTable.add(turnsToPopString.toLabel()).row()
|
||||
|
||||
|
@ -6,15 +6,27 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.utils.*
|
||||
|
||||
class SpecialistAllocationTable(val cityScreen: CityScreen): Table(BaseScreen.skin){
|
||||
class SpecialistAllocationTable(val cityScreen: CityScreen) : Table(BaseScreen.skin) {
|
||||
val cityInfo = cityScreen.city
|
||||
|
||||
fun update() {
|
||||
clear()
|
||||
|
||||
// Auto/Manual Specialists Toggle
|
||||
// Color of "color" coming from Skin.json that's loaded into BaseScreen
|
||||
// 5 columns: unassignButton, AllocationTable, assignButton, SeparatorVertical, SpecialistsStatsTabe
|
||||
if (cityInfo.manualSpecialists) {
|
||||
val manualSpecialists = "Manual Specialists".toLabel().addBorder(5f, BaseScreen.skin.get("color", Color::class.java))
|
||||
manualSpecialists.onClick { cityInfo.manualSpecialists = false; cityInfo.reassignPopulation(); cityScreen.update() }
|
||||
add(manualSpecialists).colspan(5).row()
|
||||
} else {
|
||||
val autoSpecialists = "Auto Specialists".toLabel().addBorder(5f, BaseScreen.skin.get("color", Color::class.java))
|
||||
autoSpecialists.onClick { cityInfo.manualSpecialists = true; update() }
|
||||
add(autoSpecialists).colspan(5).row()
|
||||
}
|
||||
for ((specialistName, maxSpecialists) in cityInfo.population.getMaxSpecialists()) {
|
||||
if (!cityInfo.getRuleset().specialists.containsKey(specialistName)) // specialist doesn't exist in this ruleset, probably a mod
|
||||
continue
|
||||
@ -31,7 +43,7 @@ class SpecialistAllocationTable(val cityScreen: CityScreen): Table(BaseScreen.sk
|
||||
}
|
||||
|
||||
|
||||
fun getAllocationTable(assignedSpecialists: Int, maxSpecialists: Int, specialistName: String):Table{
|
||||
fun getAllocationTable(assignedSpecialists: Int, maxSpecialists: Int, specialistName: String): Table {
|
||||
|
||||
val specialistIconTable = Table()
|
||||
val specialistObject = cityInfo.getRuleset().specialists[specialistName]!!
|
||||
@ -44,14 +56,15 @@ class SpecialistAllocationTable(val cityScreen: CityScreen): Table(BaseScreen.sk
|
||||
return specialistIconTable
|
||||
}
|
||||
|
||||
private fun getAssignButton(assignedSpecialists: Int, maxSpecialists: Int, specialistName: String):Actor {
|
||||
private fun getAssignButton(assignedSpecialists: Int, maxSpecialists: Int, specialistName: String): Actor {
|
||||
|
||||
if (assignedSpecialists >= maxSpecialists || cityInfo.isPuppet) return Table()
|
||||
val assignButton = "+".toLabel(Color.BLACK, Constants.headingFontSize)
|
||||
.apply { this.setAlignment(Align.center) }
|
||||
.surroundWithCircle(30f).apply { circle.color= Color.GREEN.darken(0.2f) }
|
||||
.apply { this.setAlignment(Align.center) }
|
||||
.surroundWithCircle(30f).apply { circle.color = Color.GREEN.darken(0.2f) }
|
||||
assignButton.onClick {
|
||||
cityInfo.population.specialistAllocations.add(specialistName, 1)
|
||||
cityInfo.manualSpecialists = true
|
||||
cityInfo.cityStats.update()
|
||||
cityScreen.update()
|
||||
}
|
||||
@ -60,17 +73,18 @@ class SpecialistAllocationTable(val cityScreen: CityScreen): Table(BaseScreen.sk
|
||||
return assignButton
|
||||
}
|
||||
|
||||
private fun getUnassignButton(assignedSpecialists: Int, specialistName: String):Actor {
|
||||
val unassignButton = "-".toLabel(Color.BLACK,Constants.headingFontSize)
|
||||
.apply { this.setAlignment(Align.center) }
|
||||
.surroundWithCircle(30f).apply { circle.color= Color.RED.darken(0.1f) }
|
||||
private fun getUnassignButton(assignedSpecialists: Int, specialistName: String): Actor {
|
||||
val unassignButton = "-".toLabel(Color.BLACK, Constants.headingFontSize)
|
||||
.apply { this.setAlignment(Align.center) }
|
||||
.surroundWithCircle(30f).apply { circle.color = Color.RED.darken(0.1f) }
|
||||
unassignButton.onClick {
|
||||
cityInfo.population.specialistAllocations.add(specialistName, -1)
|
||||
cityInfo.manualSpecialists = true
|
||||
cityInfo.cityStats.update()
|
||||
cityScreen.update()
|
||||
}
|
||||
|
||||
if (assignedSpecialists <= 0 || cityInfo.isPuppet) unassignButton.isVisible=false
|
||||
if (assignedSpecialists <= 0 || cityInfo.isPuppet) unassignButton.isVisible = false
|
||||
if (!UncivGame.Current.worldScreen.isPlayersTurn) unassignButton.clear()
|
||||
return unassignButton
|
||||
}
|
||||
@ -88,7 +102,7 @@ class SpecialistAllocationTable(val cityScreen: CityScreen): Table(BaseScreen.sk
|
||||
}
|
||||
|
||||
|
||||
fun asExpander(onChange: (()->Unit)?): ExpanderTab {
|
||||
fun asExpander(onChange: (() -> Unit)?): ExpanderTab {
|
||||
return ExpanderTab(
|
||||
title = "{Specialists}:",
|
||||
fontSize = Constants.defaultFontSize,
|
||||
|
@ -282,6 +282,7 @@ object UnitActions {
|
||||
tile.setPillaged()
|
||||
unit.civInfo.lastSeenImprovement.remove(tile.position)
|
||||
if (tile.resource != null) tile.getOwner()?.updateDetailedCivResources() // this might take away a resource
|
||||
tile.getCity()?.updateCitizens = true
|
||||
|
||||
val freePillage = unit.hasUnique(UniqueType.NoMovementToPillage, checkCivInfoUniques = true)
|
||||
if (!freePillage) unit.useMovementPoints(1f)
|
||||
|
Loading…
Reference in New Issue
Block a user