Add mod compatibility for extended map editor (#2840)

* Add mod compatibility for extended map editor

* fix crashes when loading scenario with mods enabled

* update imagegetter ruleset when loading scenario

* reload ruleset on new scenrio

* Fixed crash when units/improvements from the missing mods present in the map editor.

* removeMissingModReferences - better name
This commit is contained in:
Alexander Korolyov 2020-07-14 17:17:37 +02:00 committed by GitHub
parent 4f919b32de
commit a0daa6be4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 63 additions and 35 deletions

View File

@ -3,6 +3,7 @@ package com.unciv.ui.mapeditor
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.map.Scenario import com.unciv.logic.map.Scenario
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache
import com.unciv.ui.newgamescreen.GameOptionsTable import com.unciv.ui.newgamescreen.GameOptionsTable
import com.unciv.ui.newgamescreen.GameSetupInfo import com.unciv.ui.newgamescreen.GameSetupInfo
import com.unciv.ui.newgamescreen.PlayerPickerTable import com.unciv.ui.newgamescreen.PlayerPickerTable
@ -17,10 +18,10 @@ import com.unciv.ui.utils.*
* @param [mapEditorScreen] previous screen from map editor. * @param [mapEditorScreen] previous screen from map editor.
*/ */
class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScreen, PickerScreen() { class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScreen, PickerScreen() {
override var gameSetupInfo: GameSetupInfo = mapEditorScreen.gameSetupInfo override var gameSetupInfo = mapEditorScreen.gameSetupInfo.clone()
override var ruleset: Ruleset = mapEditorScreen.ruleset override var ruleset = RulesetCache.getComplexRuleset(gameSetupInfo.gameParameters)
var playerPickerTable = PlayerPickerTable(this, this.gameSetupInfo.gameParameters) var playerPickerTable = PlayerPickerTable(this, gameSetupInfo.gameParameters)
var gameOptionsTable = GameOptionsTable(mapEditorScreen) { desiredCiv: String -> playerPickerTable.update(desiredCiv) } var gameOptionsTable = GameOptionsTable(this) { desiredCiv: String -> playerPickerTable.update(desiredCiv) }
init { init {
@ -34,7 +35,8 @@ class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScree
rightSideButton.onClick { rightSideButton.onClick {
mapEditorScreen.gameSetupInfo = gameSetupInfo mapEditorScreen.gameSetupInfo = gameSetupInfo
mapEditorScreen.scenario = Scenario(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters) mapEditorScreen.scenario = Scenario(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters)
mapEditorScreen.ruleset = ruleset //TODO: figure out whether it is necessary mapEditorScreen.ruleset.clear()
mapEditorScreen.ruleset.add(ruleset)
mapEditorScreen.tileEditorOptions.update() mapEditorScreen.tileEditorOptions.update()
mapEditorScreen.mapHolder.updateTileGroups() mapEditorScreen.mapHolder.updateTileGroups()
UncivGame.Current.setScreen(mapEditorScreen) UncivGame.Current.setScreen(mapEditorScreen)

View File

@ -18,17 +18,14 @@ import com.unciv.ui.newgamescreen.GameSetupInfo
import com.unciv.ui.newgamescreen.IPreviousScreen import com.unciv.ui.newgamescreen.IPreviousScreen
import com.unciv.ui.utils.* import com.unciv.ui.utils.*
class MapEditorScreen(): IPreviousScreen, CameraStageBaseScreen() { class MapEditorScreen(): CameraStageBaseScreen() {
// need for compatibility with PickerScreen
override fun setRightSideButtonEnabled(boolean: Boolean) {}
var mapName = "" var mapName = ""
var tileMap = TileMap() var tileMap = TileMap()
var scenarioName = "" // when loading map: mapName is taken as default for scenarioName var scenarioName = "" // when loading map: mapName is taken as default for scenarioName
var scenario: Scenario? = null // main indicator whether scenario information is present var scenario: Scenario? = null // main indicator whether scenario information is present
override var ruleset = RulesetCache.getBaseRuleset() var ruleset = RulesetCache.getBaseRuleset()
override var gameSetupInfo = GameSetupInfo() var gameSetupInfo = GameSetupInfo()
lateinit var mapHolder: EditorMapHolder lateinit var mapHolder: EditorMapHolder
lateinit var tileEditorOptions: TileEditorOptionsTable lateinit var tileEditorOptions: TileEditorOptionsTable
@ -63,7 +60,12 @@ class MapEditorScreen(): IPreviousScreen, CameraStageBaseScreen() {
mapName = scenarioName mapName = scenarioName
this.scenario = scenario this.scenario = scenario
this.scenarioName = scenarioName this.scenarioName = scenarioName
gameSetupInfo.gameParameters = scenario.gameParameters gameSetupInfo.gameParameters = scenario.gameParameters
ruleset = RulesetCache.getComplexRuleset(scenario.gameParameters)
ImageGetter.ruleset = ruleset
ImageGetter.setTextureRegionDrawables()
initialize() initialize()
} }

View File

@ -3,10 +3,8 @@ package com.unciv.ui.mapeditor
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.ui.Slider import com.badlogic.gdx.scenes.scene2d.ui.Slider
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
@ -18,7 +16,6 @@ import com.unciv.models.metadata.Player
import com.unciv.models.ruleset.Nation import com.unciv.models.ruleset.Nation
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.tile.TerrainType import com.unciv.models.ruleset.tile.TerrainType
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.tilegroups.TileGroup import com.unciv.ui.tilegroups.TileGroup
import com.unciv.ui.tilegroups.TileSetStrings import com.unciv.ui.tilegroups.TileSetStrings
@ -226,23 +223,26 @@ class TileEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(Camera
val nationsTable = Table() val nationsTable = Table()
// default player - barbarians // default player - first MajorCiv player
var currentPlayer = "" val defaultPlayer = gameParameters.players.first{
var currentNation: Nation? = ruleset.nations.values.firstOrNull{ it.isBarbarian() } it.chosenCiv != Constants.spectator && it.chosenCiv != Constants.random
}
var currentPlayer = getPlayerIndexString(defaultPlayer)
var currentNation: Nation = ruleset.nations[defaultPlayer.chosenCiv]!!
var currentUnit = ruleset.units.values.first() var currentUnit = ruleset.units.values.first()
fun setUnitTileAction(){ fun setUnitTileAction(){
if (currentNation == null) return val unitImage = ImageGetter.getUnitIcon(currentUnit.name, currentNation.getInnerColor())
val unitImage = ImageGetter.getUnitIcon(currentUnit.name, currentNation!!.getInnerColor()) .surroundWithCircle(40f*0.9f).apply { circle.color=currentNation.getOuterColor() }
.surroundWithCircle(40f).apply { color=currentNation!!.getOuterColor() } .surroundWithCircle(40f, false).apply { circle.color=currentNation.getInnerColor() }
setCurrentHex(unitImage, currentUnit.name.tr()+ " - $currentPlayer ("+currentNation!!.name.tr()+")")
setCurrentHex(unitImage, currentUnit.name.tr()+ " - $currentPlayer ("+currentNation.name.tr()+")")
tileAction = { tileAction = {
val unit = MapUnit() val unit = MapUnit()
unit.baseUnit = currentUnit unit.baseUnit = currentUnit
unit.name = currentUnit.name unit.name = currentUnit.name
unit.owner = currentNation!!.name unit.owner = currentNation.name
unit.civInfo = CivilizationInfo(currentNation!!.name).apply { nation=currentNation!! } // needed for the unit icon to render correctly unit.civInfo = CivilizationInfo(currentNation.name).apply { nation=currentNation } // needed for the unit icon to render correctly
unit.updateUniques() unit.updateUniques()
if (unit.movement.canMoveTo(it)) { if (unit.movement.canMoveTo(it)) {
when { when {

View File

@ -170,7 +170,6 @@ class GameOptionsTable(previousScreen: IPreviousScreen, val updatePlayerPickerTa
val modRulesets = RulesetCache.values.filter { it.name != "" } val modRulesets = RulesetCache.values.filter { it.name != "" }
if (modRulesets.isEmpty()) return if (modRulesets.isEmpty()) return
add("Mods:".toLabel(fontSize = 24)).padTop(16f).colspan(2).row() add("Mods:".toLabel(fontSize = 24)).padTop(16f).colspan(2).row()
val modCheckboxTable = Table().apply { defaults().pad(5f) } val modCheckboxTable = Table().apply { defaults().pad(5f) }
for (mod in modRulesets) { for (mod in modRulesets) {

View File

@ -103,8 +103,12 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() {
scenarioFileSelectBox.onChange { scenarioFileSelectBox.onChange {
mapParameters.name = scenarioFileSelectBox.selected!! mapParameters.name = scenarioFileSelectBox.selected!!
val scenario = MapSaver.loadScenario(mapParameters.name) val scenario = MapSaver.loadScenario(mapParameters.name)
newGameScreen.gameSetupInfo.gameParameters = scenario.gameParameters newGameScreen.apply {
newGameScreen.updateTables() gameSetupInfo.gameParameters = scenario.gameParameters
newGameOptionsTable.gameParameters = scenario.gameParameters
newGameOptionsTable.reloadRuleset()
updateTables()
}
} }
return scenarioFileSelectBox return scenarioFileSelectBox
} }

View File

@ -23,6 +23,14 @@ class GameSetupInfo(var gameId:String, var gameParameters: GameParameters, var m
constructor() : this("", GameParameters(), MapParameters()) constructor() : this("", GameParameters(), MapParameters())
constructor(gameInfo: GameInfo) : this("", gameInfo.gameParameters.clone(), gameInfo.tileMap.mapParameters) constructor(gameInfo: GameInfo) : this("", gameInfo.gameParameters.clone(), gameInfo.tileMap.mapParameters)
constructor(gameParameters: GameParameters, mapParameters: MapParameters) : this("", gameParameters, mapParameters) constructor(gameParameters: GameParameters, mapParameters: MapParameters) : this("", gameParameters, mapParameters)
fun clone(): GameSetupInfo {
val toReturn = GameSetupInfo()
toReturn.gameId = this.gameId
toReturn.gameParameters = this.gameParameters
toReturn.mapParameters = this.mapParameters
return toReturn
}
} }
class NewGameScreen(previousScreen:CameraStageBaseScreen, _gameSetupInfo: GameSetupInfo?=null): IPreviousScreen, PickerScreen() { class NewGameScreen(previousScreen:CameraStageBaseScreen, _gameSetupInfo: GameSetupInfo?=null): IPreviousScreen, PickerScreen() {

View File

@ -78,7 +78,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
val availableCiv = getAvailablePlayerCivs().firstOrNull { !it.isSpectator() } val availableCiv = getAvailablePlayerCivs().firstOrNull { !it.isSpectator() }
if (availableCiv != null) player = Player(availableCiv.name) if (availableCiv != null) player = Player(availableCiv.name)
// Spectators only Humans // Spectators only Humans
else player = Player("Spectator").apply { playerType = PlayerType.Human } else player = Player(Constants.spectator).apply { playerType = PlayerType.Human }
} }
gameParameters.players.add(player) gameParameters.players.add(player)
update() update()
@ -89,13 +89,12 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
} }
/** /**
* If new mod removes nations already chosen by some player * Reassigns removed mod references to random civilization
* sets first civilization available in the ruleset
*/ */
private fun reassignRemovedModReferences() { private fun reassignRemovedModReferences() {
for (player in gameParameters.players) { for (player in gameParameters.players) {
if (!previousScreen.ruleset.nations.containsKey(player.chosenCiv)) if (!previousScreen.ruleset.nations.containsKey(player.chosenCiv))
player.chosenCiv = "Random" player.chosenCiv = Constants.random
} }
} }
@ -107,7 +106,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
// No auto-select if desiredCiv already used // No auto-select if desiredCiv already used
if (gameParameters.players.any { it.chosenCiv == desiredCiv }) return if (gameParameters.players.any { it.chosenCiv == desiredCiv }) return
// Do auto-select, silently no-op if no suitable slot (human with 'random' choice) // Do auto-select, silently no-op if no suitable slot (human with 'random' choice)
gameParameters.players.firstOrNull { it.chosenCiv == "Random" && it.playerType == PlayerType.Human }?.chosenCiv = desiredCiv gameParameters.players.firstOrNull { it.chosenCiv == Constants.random && it.playerType == PlayerType.Human }?.chosenCiv = desiredCiv
} }
/** /**
@ -190,7 +189,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
*/ */
private fun getNationTable(player: Player): Table { private fun getNationTable(player: Player): Table {
val nationTable = Table() val nationTable = Table()
val nationImage = if (player.chosenCiv == "Random") "?".toLabel(Color.WHITE, 25) val nationImage = if (player.chosenCiv == Constants.random) "?".toLabel(Color.WHITE, 25)
.apply { this.setAlignment(Align.center) } .apply { this.setAlignment(Align.center) }
.surroundWithCircle(36f).apply { circle.color = Color.BLACK } .surroundWithCircle(36f).apply { circle.color = Color.BLACK }
.surroundWithCircle(40f, false).apply { circle.color = Color.WHITE } .surroundWithCircle(40f, false).apply { circle.color = Color.WHITE }
@ -221,10 +220,10 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
.apply { this.setAlignment(Align.center) } .apply { this.setAlignment(Align.center) }
.surroundWithCircle(45f).apply { circle.color = Color.BLACK } .surroundWithCircle(45f).apply { circle.color = Color.BLACK }
.surroundWithCircle(50f, false).apply { circle.color = Color.WHITE }).pad(10f) .surroundWithCircle(50f, false).apply { circle.color = Color.WHITE }).pad(10f)
randomPlayerTable.add("Random".toLabel()) randomPlayerTable.add(Constants.random.toLabel())
randomPlayerTable.touchable = Touchable.enabled randomPlayerTable.touchable = Touchable.enabled
randomPlayerTable.onClick { randomPlayerTable.onClick {
player.chosenCiv = "Random" player.chosenCiv = Constants.random
nationsPopup.close() nationsPopup.close()
update() update()
} }

View File

@ -297,6 +297,8 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings)
val tileIsViewable = viewingCiv == null || isViewable(viewingCiv) val tileIsViewable = viewingCiv == null || isViewable(viewingCiv)
val showMilitaryUnit = viewingCiv == null || showMilitaryUnit(viewingCiv) val showMilitaryUnit = viewingCiv == null || showMilitaryUnit(viewingCiv)
removeMissingModReferences()
updateTileImage(viewingCiv) updateTileImage(viewingCiv)
updateRivers(tileInfo.hasBottomRightRiver, tileInfo.hasBottomRiver, tileInfo.hasBottomLeftRiver) updateRivers(tileInfo.hasBottomRightRiver, tileInfo.hasBottomRiver, tileInfo.hasBottomLeftRiver)
updateTerrainBaseImage() updateTerrainBaseImage()
@ -318,6 +320,18 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings)
fogImage.isVisible = !(tileIsViewable || showEntireMap) fogImage.isVisible = !(tileIsViewable || showEntireMap)
} }
private fun removeMissingModReferences() {
val improvementName = tileInfo.improvement
if(improvementName != null && improvementName.startsWith("StartingLocation ")){
val nationName = improvementName.removePrefix("StartingLocation ")
if (!tileInfo.ruleset.nations.containsKey(nationName))
tileInfo.improvement = null
}
for (unit in tileInfo.getUnits())
if (!tileInfo.ruleset.nations.containsKey(unit.owner)) unit.removeFromTile()
}
private fun updateTerrainBaseImage() { private fun updateTerrainBaseImage() {
if (tileInfo.baseTerrain == baseTerrain) return if (tileInfo.baseTerrain == baseTerrain) return
baseTerrain = tileInfo.baseTerrain baseTerrain = tileInfo.baseTerrain