Added Replacable Parts tech and Great War Infantry

Musketman now upgrades to Rifleman
Solved more rare concurrency problems
This commit is contained in:
Yair Morgenstern 2018-12-02 23:05:05 +02:00
parent 28ae533acc
commit 24b27a791f
22 changed files with 427 additions and 387 deletions

2
.gitignore vendored
View file

@ -127,3 +127,5 @@ gradle.properties
SaveFiles/ SaveFiles/
android/android-release.apk android/android-release.apk
android/assets/GameSettings.json android/assets/GameSettings.json
android/release/output.json
android/release/android-release.apk

View file

@ -57,6 +57,7 @@ All the following are from [the Noun Project](https://thenounproject.com) licenc
### Modern ### Modern
* [Helmet](https://thenounproject.com/term/helmet/25216/) By Daniel Turner for Great War Infantry
* [Tank](https://thenounproject.com/term/tank/1287510/) By corpus delicti for Landship * [Tank](https://thenounproject.com/term/tank/1287510/) By corpus delicti for Landship
### Great People ### Great People

View file

@ -2,8 +2,8 @@
![](https://travis-ci.org/yairm210/UnCiv.svg?branch=master) ![](https://travis-ci.org/yairm210/UnCiv.svg?branch=master)
[![LibGDX](https://img.shields.io/badge/libgdx-1.9.6-red.svg)](https://libgdx.badlogicgames.com/) [![LibGDX](https://img.shields.io/badge/libgdx-1.9.9-red.svg)](https://libgdx.badlogicgames.com/)
[![Kotlin](https://img.shields.io/badge/kotlin-1.2.41-orange.svg)](http://kotlinlang.org/) [![Kotlin](https://img.shields.io/badge/kotlin-1.3.10-orange.svg)](http://kotlinlang.org/)
## What is this? ## What is this?

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 KiB

After

Width:  |  Height:  |  Size: 780 KiB

View file

@ -363,6 +363,11 @@
prerequisites:["Electricity"], prerequisites:["Electricity"],
baseDescription:"Does nothing since we have no sea tiles - In theory, allows construction of offshore platforms and submarines" baseDescription:"Does nothing since we have no sea tiles - In theory, allows construction of offshore platforms and submarines"
},*/ },*/
{
name:"Replacable Parts",
row:4,
prerequisites:["Electricity","Steam Power"]
},
{ {
name:"Radio", name:"Radio",
row:5, row:5,
@ -385,7 +390,7 @@
{ {
name:"Plastics", name:"Plastics",
row:3, row:3,
prerequisites:["Biology"] prerequisites:["Biology","Replacable Parts"]
},/* },/*
{ {
name:"Electronics", name:"Electronics",

View file

@ -292,6 +292,8 @@
strength:24, strength:24,
cost: 150, cost: 150,
requiredTech:"Gunpowder", requiredTech:"Gunpowder",
upgradesTo:"Rifleman,"
obsoleteTech:"Rifling",
hurryCostModifier:20 hurryCostModifier:20
}, },
{ {
@ -303,6 +305,8 @@
strength:28, strength:28,
cost: 150, cost: 150,
requiredTech:"Gunpowder", requiredTech:"Gunpowder",
upgradesTo:"Rifleman,"
obsoleteTech:"Rifling",
hurryCostModifier:20 hurryCostModifier:20
}, },
{ {
@ -337,6 +341,8 @@
strength:34, strength:34,
cost: 225, cost: 225,
requiredTech:"Rifling", requiredTech:"Rifling",
obsoleteTech:"Replacable Parts",
upgradesTo:"Great War Infantry",
hurryCostModifier:20 hurryCostModifier:20
}, },
{ {
@ -347,6 +353,7 @@
cost: 225, cost: 225,
requiredTech:"Military Science", requiredTech:"Military Science",
requiredResource:"Horses", requiredResource:"Horses",
upgradesTo:"Landship",
uniques:["Can move after attacking","No defensive terrain bonus","Penalty vs City 33%" ], uniques:["Can move after attacking","No defensive terrain bonus","Penalty vs City 33%" ],
hurryCostModifier:20 hurryCostModifier:20
}, },
@ -359,7 +366,8 @@
range:3, range:3,
cost: 320, cost: 320,
requiredTech:"Dynamite", requiredTech:"Dynamite",
uniques:["Bonus vs City 200%","No defensive terrain bonus","Must set up to ranged attack","Limited Visibility","Indirect Fire"], uniques:["Bonus vs City 200%","No defensive terrain bonus",
"Must set up to ranged attack","Limited Visibility","Indirect Fire"],
hurryCostModifier:20 hurryCostModifier:20
}, },
{ {
@ -387,6 +395,15 @@
uniques:["Can move after attacking","No defensive terrain bonus"], uniques:["Can move after attacking","No defensive terrain bonus"],
hurryCostModifier:20 hurryCostModifier:20
}, },
{
name:"Great War Infantry",
unitType:"Melee",
movement:2,
strength:50,
cost: 320,
requiredTech:"Replacable Parts",
hurryCostModifier:20
},
/* Great people */ /* Great people */

View file

@ -21,8 +21,8 @@ android {
applicationId "com.unciv.game" applicationId "com.unciv.game"
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 26
versionCode 169 versionCode 170
versionName "2.10.8" versionName "2.10.9"
} }
// Had to add this crap for Travis to build, it wanted to sign the app // Had to add this crap for Travis to build, it wanted to sign the app

View file

@ -62,7 +62,7 @@ class UnCivGame : Game() {
fun setWorldScreen() { fun setWorldScreen() {
setScreen(worldScreen) setScreen(worldScreen)
Gdx.input.inputProcessor = worldScreen.stage Gdx.input.inputProcessor = worldScreen.stage
worldScreen.update() // This can set the screen to the policy picker or tech picker screen, so the input processor must come before worldScreen.shouldUpdate=true // This can set the screen to the policy picker or tech picker screen, so the input processor must come before
} }
override fun resume() { override fun resume() {

View file

@ -34,7 +34,8 @@ class UnitAutomation{
return SpecificUnitAutomation().automateWorkBoats(unit) return SpecificUnitAutomation().automateWorkBoats(unit)
} }
if(unit.name.startsWith("Great")){ if(unit.name.startsWith("Great")
&& unit.name in GreatPersonManager().statToGreatPersonMapping.values){ // So "Great War Infantry" isn't caught here
return SpecificUnitAutomation().automateGreatPerson(unit)// I don't know what to do with you yet. return SpecificUnitAutomation().automateGreatPerson(unit)// I don't know what to do with you yet.
} }
@ -357,10 +358,8 @@ class SpecificUnitAutomation{
if (unit.currentMovement > 0 && unit.currentTile == closestReachableResource) { if (unit.currentMovement > 0 && unit.currentTile == closestReachableResource) {
val createImprovementAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen) val createImprovementAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen)
.firstOrNull { it.name.startsWith("Create") } // could be either fishing boats or oil well .firstOrNull { it.name.startsWith("Create") } // could be either fishing boats or oil well
if (createImprovementAction != null) { if (createImprovementAction != null)
createImprovementAction.action() return createImprovementAction.action() // unit is already gone, can't "explore"
return // unit is already gone, can't "explore"
}
} }
} }
else UnitAutomation().explore(unit, unit.getDistanceToTiles()) else UnitAutomation().explore(unit, unit.getDistanceToTiles())
@ -402,10 +401,7 @@ class SpecificUnitAutomation{
.firstOrNull { unit.movementAlgs().canReach(it) } .firstOrNull { unit.movementAlgs().canReach(it) }
if(bestCityLocation==null) // We got a badass over here, all tiles within 5 are taken? Screw it, random walk. if(bestCityLocation==null) // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
{ return UnitAutomation().explore(unit, unit.getDistanceToTiles())
UnitAutomation().explore(unit, unit.getDistanceToTiles())
return
}
if(bestCityLocation.getTilesInDistance(3).any { it.isCityCenter() }) if(bestCityLocation.getTilesInDistance(3).any { it.isCityCenter() })
throw Exception("City within distance") throw Exception("City within distance")
@ -430,7 +426,7 @@ class SpecificUnitAutomation{
for(city in citiesByStatBoost){ for(city in citiesByStatBoost){
val pathToCity =unit.movementAlgs().getShortestPath(city.getCenterTile()) val pathToCity =unit.movementAlgs().getShortestPath(city.getCenterTile())
if(pathToCity.isEmpty()) continue if(pathToCity.isEmpty()) continue
if(pathToCity.size>2) { if(pathToCity.size>2){
unit.movementAlgs().headTowards(city.getCenterTile()) unit.movementAlgs().headTowards(city.getCenterTile())
return return
} }

View file

@ -84,7 +84,7 @@ class TechPickerScreen(internal val civInfo: CivilizationInfo) : PickerScreen()
} else } else
civTech.techsToResearch = techsToResearch civTech.techsToResearch = techsToResearch
game.setWorldScreen() game.setWorldScreen()
game.worldScreen.update() game.worldScreen.shouldUpdate=true
dispose() dispose()
} }

View file

@ -118,7 +118,7 @@ class MinimapHolder(val tileMapHolder: TileMapHolder): Table(){
populationImage.onClick { populationImage.onClick {
settings.showWorkedTiles = !settings.showWorkedTiles settings.showWorkedTiles = !settings.showWorkedTiles
populationImage.image.color.a = if(settings.showWorkedTiles) 1f else 0.5f populationImage.image.color.a = if(settings.showWorkedTiles) 1f else 0.5f
worldScreen.update() worldScreen.shouldUpdate=true
} }
toggleIconTable.add(populationImage).row() toggleIconTable.add(populationImage).row()
@ -128,7 +128,7 @@ class MinimapHolder(val tileMapHolder: TileMapHolder): Table(){
resourceImage.onClick { resourceImage.onClick {
settings.showResourcesAndImprovements = !settings.showResourcesAndImprovements settings.showResourcesAndImprovements = !settings.showResourcesAndImprovements
resourceImage.image.color.a = if(settings.showResourcesAndImprovements) 1f else 0.5f resourceImage.image.color.a = if(settings.showResourcesAndImprovements) 1f else 0.5f
worldScreen.update() worldScreen.shouldUpdate=true
} }
toggleIconTable.add(resourceImage) toggleIconTable.add(resourceImage)
toggleIconTable.pack() toggleIconTable.pack()

View file

@ -102,7 +102,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
} }
worldScreen.bottomBar.unitTable.tileSelected(tileInfo) worldScreen.bottomBar.unitTable.tileSelected(tileInfo)
worldScreen.update() worldScreen.shouldUpdate=true
} }
private fun addMoveHereButtonToTile(selectedUnit: MapUnit, tileInfo: TileInfo, tileGroup: WorldTileGroup) { private fun addMoveHereButtonToTile(selectedUnit: MapUnit, tileInfo: TileInfo, tileGroup: WorldTileGroup) {
@ -114,7 +114,8 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
val turnsToGetThere = selectedUnit.movementAlgs().getShortestPath(tileInfo).size val turnsToGetThere = selectedUnit.movementAlgs().getShortestPath(tileInfo).size
val numberCircle = ImageGetter.getImage("OtherIcons/Circle").apply { width = size / 2; height = size / 2;color = Color.BLUE } val numberCircle = ImageGetter.getImage("OtherIcons/Circle").apply { width = size / 2; height = size / 2;color = Color.BLUE }
moveHereButton.addActor(numberCircle) moveHereButton.addActor(numberCircle)
moveHereButton.addActor(Label(turnsToGetThere.toString(), CameraStageBaseScreen.skin).apply { center(numberCircle); setFontColor(Color.WHITE) }) moveHereButton.addActor(Label(turnsToGetThere.toString(), CameraStageBaseScreen.skin)
.apply { center(numberCircle); setFontColor(Color.WHITE) })
val unitIcon = ImageGetter.getUnitImage(selectedUnit, size / 2) val unitIcon = ImageGetter.getUnitImage(selectedUnit, size / 2)
unitIcon.y = size - unitIcon.height unitIcon.y = size - unitIcon.height
@ -132,7 +133,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
// we don't update it directly because we're on a different thread; instead, we tell it to update itself // we don't update it directly because we're on a different thread; instead, we tell it to update itself
worldScreen.shouldUpdate = true worldScreen.shouldUpdate = true
moveToOverlay!!.remove() moveToOverlay?.remove()
moveToOverlay = null moveToOverlay = null
} }
} }
@ -220,7 +221,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
// Here it's the same, only the Y axis is inverted - when at 0 we're at the top, not bottom - so we invert it back. // Here it's the same, only the Y axis is inverted - when at 0 we're at the top, not bottom - so we invert it back.
scrollY = maxY - (tileGroup.y + tileGroup.width / 2 - worldScreen.stage.height / 2) scrollY = maxY - (tileGroup.y + tileGroup.width / 2 - worldScreen.stage.height / 2)
updateVisualScroll() updateVisualScroll()
worldScreen.update() worldScreen.shouldUpdate=true
} }

View file

@ -86,8 +86,10 @@ class WorldScreen : CameraStageBaseScreen() {
createNextTurnButton() // needs civ table to be positioned createNextTurnButton() // needs civ table to be positioned
} }
// This is private so that we will set the shouldUpdate to true instead.
fun update() { // That way, not only do we save a lot of unneccesary updates, we also ensure that all updates are called from the main GL thread
// and we don't get any silly concurrency problems!
private fun update() {
// many of the display functions will be called with the game clone and not the actual game, // many of the display functions will be called with the game clone and not the actual game,
// because that's guaranteed to stay the exact same and so we won't get any concurrent modification exceptions // because that's guaranteed to stay the exact same and so we won't get any concurrent modification exceptions

View file

@ -137,7 +137,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
attackButton.onClick { attackButton.onClick {
attacker.unit.moveToTile(attackableEnemy.tileToAttackFrom) attacker.unit.moveToTile(attackableEnemy.tileToAttackFrom)
battle.attack(attacker, defender) battle.attack(attacker, defender)
worldScreen.update() worldScreen.shouldUpdate=true
} }
} }

View file

@ -89,6 +89,6 @@ class WorldScreenDisplayOptionsTable() : PopupTable(){
pack() // Needed to show the background. pack() // Needed to show the background.
center(UnCivGame.Current.worldScreen.stage) center(UnCivGame.Current.worldScreen.stage)
UnCivGame.Current.worldScreen.update() UnCivGame.Current.worldScreen.shouldUpdate=true
} }
} }

View file

@ -41,7 +41,7 @@ class IdleUnitButton (internal val unitTable: UnitTable,
tileToSelect = tilesWithIdleUnits[index] tileToSelect = tilesWithIdleUnits[index]
} }
tileMapHolder.setCenterPosition(tileToSelect.position) tileMapHolder.setCenterPosition(tileToSelect.position)
unitTable.worldScreen.update() unitTable.worldScreen.shouldUpdate=true
} }
} }

View file

@ -185,7 +185,7 @@ class UnitActions {
actionList += UnitAction("Disband unit",unit.currentMovement != 0f actionList += UnitAction("Disband unit",unit.currentMovement != 0f
) { ) {
YesNoPopupTable("Do you really want to disband this unit?".tr(), YesNoPopupTable("Do you really want to disband this unit?".tr(),
{unit.destroy(); worldScreen.update()} ) {unit.destroy(); worldScreen.shouldUpdate=true} )
} }
return actionList return actionList

View file

@ -14,7 +14,9 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table(){
fun getIconForUnitAction(unitAction:String): Actor { fun getIconForUnitAction(unitAction:String): Actor {
if(unitAction.startsWith("Upgrade to")){ if(unitAction.startsWith("Upgrade to")){
val unitToUpgradeTo = Regex("""Upgrade to \[(\S*)\]""").find(unitAction)!!.groups[1]!!.value // Regexplaination: start with a [, take as many non-] chars as you can, until you reach a ].
// What you find between the first [ and the first ] that comes after it, will be group no. 1
val unitToUpgradeTo = Regex("""Upgrade to \[([^\]]*)\]""").find(unitAction)!!.groups[1]!!.value
return ImageGetter.getUnitIcon(unitToUpgradeTo) return ImageGetter.getUnitIcon(unitToUpgradeTo)
} }
when(unitAction){ when(unitAction){
@ -60,7 +62,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table(){
actionButton.add(Label(unitAction.name.tr(),CameraStageBaseScreen.skin) actionButton.add(Label(unitAction.name.tr(),CameraStageBaseScreen.skin)
.setFontColor(Color.WHITE)).pad(5f) .setFontColor(Color.WHITE)).pad(5f)
actionButton.pack() actionButton.pack()
actionButton.onClick({ unitAction.action(); UnCivGame.Current.worldScreen.update() }) actionButton.onClick { unitAction.action(); UnCivGame.Current.worldScreen.shouldUpdate=true }
if (!unitAction.canAct) actionButton.disable() if (!unitAction.canAct) actionButton.disable()
return actionButton return actionButton
} }