From 7462aae94cea765cb58dbcbf8b828a4cecf45afa Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Sun, 5 Jan 2020 22:11:10 +0200 Subject: [PATCH] Big changes to mods and rulesets - almost production ready! Rulesets are heavy to load so we now have a RulesetCache, which can construct "custom" rulesets with a list of mods! We now pack mod images on Desktop run, and load the atlases for the loaded mods on load game! --- .../Images/UnitIcons/Maori Warrior.png | Bin 0 -> 3017 bytes android/assets/modss/myFirstMod/game.atlas | 13 +++++ android/assets/modss/myFirstMod/game.png | Bin 0 -> 3813 bytes .../myFirstMod/jsons/Units.json | 0 core/src/com/unciv/UncivGame.kt | 10 ++-- core/src/com/unciv/logic/GameInfo.kt | 8 +-- core/src/com/unciv/logic/GameStarter.kt | 14 +++--- .../unciv/models/metadata/GameParameters.kt | 1 + core/src/com/unciv/models/ruleset/Ruleset.kt | 46 ++++++++++++++---- .../com/unciv/ui/mapeditor/MapEditorScreen.kt | 4 +- .../unciv/ui/newgamescreen/NewGameScreen.kt | 8 ++- .../NewGameScreenOptionsTable.kt | 42 +++------------- core/src/com/unciv/ui/utils/ImageGetter.kt | 30 +++++++++--- .../com/unciv/app/desktop/DesktopLauncher.kt | 8 +++ 14 files changed, 110 insertions(+), 74 deletions(-) create mode 100644 android/assets/modss/myFirstMod/Images/UnitIcons/Maori Warrior.png create mode 100644 android/assets/modss/myFirstMod/game.atlas create mode 100644 android/assets/modss/myFirstMod/game.png rename android/assets/{mods => modss}/myFirstMod/jsons/Units.json (100%) diff --git a/android/assets/modss/myFirstMod/Images/UnitIcons/Maori Warrior.png b/android/assets/modss/myFirstMod/Images/UnitIcons/Maori Warrior.png new file mode 100644 index 0000000000000000000000000000000000000000..5b9139b57b2616af495dfdd78593da35abc5fee7 GIT binary patch literal 3017 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 LNkvXXu0mjfaT?Eq literal 0 HcmV?d00001 diff --git a/android/assets/modss/myFirstMod/game.atlas b/android/assets/modss/myFirstMod/game.atlas new file mode 100644 index 0000000000..edc265c261 --- /dev/null +++ b/android/assets/modss/myFirstMod/game.atlas @@ -0,0 +1,13 @@ + +game.png +size: 128,128 +format: RGBA8888 +filter: MipMapLinearLinear,MipMapLinearLinear +repeat: none +UnitIcons/Maori Warrior + rotate: false + xy: 2, 2 + size: 100, 100 + orig: 100, 100 + offset: 0, 0 + index: -1 diff --git a/android/assets/modss/myFirstMod/game.png b/android/assets/modss/myFirstMod/game.png new file mode 100644 index 0000000000000000000000000000000000000000..170be4d1e8445b083ffde2f6d4856315c7dfbcca GIT binary patch literal 3813 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 Gk^cv8!7Muf literal 0 HcmV?d00001 diff --git a/android/assets/mods/myFirstMod/jsons/Units.json b/android/assets/modss/myFirstMod/jsons/Units.json similarity index 100% rename from android/assets/mods/myFirstMod/jsons/Units.json rename to android/assets/modss/myFirstMod/jsons/Units.json diff --git a/core/src/com/unciv/UncivGame.kt b/core/src/com/unciv/UncivGame.kt index 21bebfab65..06f6f33473 100644 --- a/core/src/com/unciv/UncivGame.kt +++ b/core/src/com/unciv/UncivGame.kt @@ -13,7 +13,7 @@ import com.unciv.logic.GameStarter import com.unciv.logic.map.MapParameters import com.unciv.models.metadata.GameParameters import com.unciv.models.metadata.GameSettings -import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.RulesetCache import com.unciv.models.translations.TranslationFileReader import com.unciv.models.translations.Translations import com.unciv.ui.LanguagePickerScreen @@ -37,7 +37,7 @@ class UncivGame( /** For when you need to test something in an advanced game and don't have time to faff around */ val superchargedForDebug = false - var rewriteTranslationFiles = false + var rewriteTranslationFiles = true lateinit var worldScreen: WorldScreen @@ -46,7 +46,6 @@ class UncivGame( val musicLocation = "music/thatched-villagers.mp3" var isInitialized = false - lateinit var ruleset:Ruleset val translations = Translations() @@ -68,7 +67,7 @@ class UncivGame( screen = LoadingScreen() thread(name="LoadJSON") { - ruleset = Ruleset(true) + RulesetCache.loadRulesets() if (rewriteTranslationFiles) { // Yes, also when running from the Jar. Sue me. translations.readAllLanguagesTranslation() @@ -122,7 +121,8 @@ class UncivGame( fun loadGame(gameInfo:GameInfo){ this.gameInfo = gameInfo - + ImageGetter.ruleset = gameInfo.ruleSet + ImageGetter.refreshAltas() worldScreen = WorldScreen(gameInfo.getPlayerToViewAs()) setWorldScreen() } diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index a846b2be14..516fa852b6 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -10,8 +10,10 @@ import com.unciv.logic.civilization.LocationAction import com.unciv.logic.civilization.PlayerType import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileMap -import com.unciv.models.ruleset.Difficulty import com.unciv.models.metadata.GameParameters +import com.unciv.models.ruleset.Difficulty +import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.RulesetCache import java.util.* class GameInfo { @@ -20,7 +22,7 @@ class GameInfo { /** This is used in multiplayer games, where I may have a saved game state on my phone * that is inconsistent with the saved game on the cloud */ @Transient var isUpToDate=false - @Transient var ruleSet = UncivGame.Current.ruleset + @Transient lateinit var ruleSet:Ruleset var civilizations = mutableListOf() var difficulty="Chieftain" // difficulty is game-wide, think what would happen if 2 human players could play on different difficulties? @@ -203,7 +205,7 @@ class GameInfo { // will be done here, and not in CivInfo.setTransients or CityInfo fun setTransients() { tileMap.gameInfo = this - + ruleSet = RulesetCache.getComplexRuleset(gameParameters.mods) // Renames as of version 3.1.8, because of translation conflicts with the property "Range" and the difficulty "Immortal" // Needs to be BEFORE tileMap.setTransients, because the units' setTransients is called from there diff --git a/core/src/com/unciv/logic/GameStarter.kt b/core/src/com/unciv/logic/GameStarter.kt index b41fdbcf56..8076b12c96 100644 --- a/core/src/com/unciv/logic/GameStarter.kt +++ b/core/src/com/unciv/logic/GameStarter.kt @@ -2,11 +2,11 @@ package com.unciv.logic import com.badlogic.gdx.math.Vector2 import com.unciv.Constants -import com.unciv.UncivGame import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.map.* -import com.unciv.models.ruleset.Ruleset import com.unciv.models.metadata.GameParameters +import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.RulesetCache import java.util.* import kotlin.collections.ArrayList import kotlin.math.max @@ -17,20 +17,20 @@ class GameStarter{ val gameInfo = GameInfo() gameInfo.gameParameters = newGameParameters - val gameBasics = UncivGame.Current.ruleset + val ruleset = RulesetCache.getComplexRuleset(newGameParameters.mods) if(mapParameters.name!="") gameInfo.tileMap = MapSaver().loadMap(mapParameters.name) - else gameInfo.tileMap = MapGenerator().generateMap(mapParameters, gameBasics) + else gameInfo.tileMap = MapGenerator().generateMap(mapParameters, ruleset) gameInfo.tileMap.mapParameters = mapParameters - gameInfo.tileMap.setTransients(gameBasics) + gameInfo.tileMap.setTransients(ruleset) gameInfo.tileMap.gameInfo = gameInfo // need to set this transient before placing units in the map gameInfo.difficulty = newGameParameters.difficulty - addCivilizations(newGameParameters, gameInfo, gameBasics) // this is before the setTransients so gameInfo doesn't yet have the gameBasics + addCivilizations(newGameParameters, gameInfo, ruleset) // this is before the setTransients so gameInfo doesn't yet have the gameBasics gameInfo.setTransients() // needs to be before placeBarbarianUnit because it depends on the tilemap having its gameinfo set @@ -41,7 +41,7 @@ class GameStarter{ for (tech in gameInfo.getDifficulty().aiFreeTechs) civInfo.tech.addTechnology(tech) - for (tech in gameBasics.technologies.values + for (tech in ruleset.technologies.values .filter { it.era() < newGameParameters.startingEra }) if (!civInfo.tech.isResearched(tech.name)) civInfo.tech.addTechnology(tech.name) diff --git a/core/src/com/unciv/models/metadata/GameParameters.kt b/core/src/com/unciv/models/metadata/GameParameters.kt index db0f274acc..69e1217e3b 100644 --- a/core/src/com/unciv/models/metadata/GameParameters.kt +++ b/core/src/com/unciv/models/metadata/GameParameters.kt @@ -20,4 +20,5 @@ class GameParameters { // Default values are the default new game var startingEra = TechEra.Ancient var isOnlineMultiplayer = false + var mods = HashSet() } \ No newline at end of file diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index d86ef88554..e2263be7f7 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -13,12 +13,11 @@ import com.unciv.models.ruleset.unit.Promotion import com.unciv.models.stats.INamed import kotlin.collections.set -class Ruleset(load: Boolean = true) { +class Ruleset() { private val jsonParser = JsonParser() var name = "" - val mods = LinkedHashSet() val buildings = LinkedHashMap() val terrains = LinkedHashMap() val tileResources = LinkedHashMap() @@ -29,19 +28,14 @@ class Ruleset(load: Boolean = true) { val nations = LinkedHashMap() val policyBranches = LinkedHashMap() val difficulties = LinkedHashMap() + val mods = HashSet() fun clone(): Ruleset { - val newRuleset = Ruleset(false) + val newRuleset = Ruleset() newRuleset.add(this) return newRuleset } - init { - if (load) { - load(Gdx.files.internal("jsons")) - } - } - private fun createHashmap(items: Array): LinkedHashMap { val hashMap = LinkedHashMap() for (item in items) @@ -63,7 +57,7 @@ class Ruleset(load: Boolean = true) { units.putAll(ruleset.units) } - fun clearExceptModNames() { + fun clear() { buildings.clear() difficulties.clear() nations.clear() @@ -75,6 +69,7 @@ class Ruleset(load: Boolean = true) { tileResources.clear() unitPromotions.clear() units.clear() + mods.clear() } fun load(folderHandle :FileHandle ) { @@ -146,3 +141,34 @@ class Ruleset(load: Boolean = true) { } } +/** Loading mods is expensive, so let's only do it once and + * save all of the loaded rulesets somewhere for later use + * */ +object RulesetCache :HashMap(){ + fun loadRulesets(){ + this[""] = Ruleset().apply { load(Gdx.files.internal("jsons")) } + + for(modFolder in Gdx.files.local("mods").list()){ + try{ + val modRuleset = Ruleset() + modRuleset.load(modFolder.child("jsons")) + modRuleset.name = modFolder.name() + this[modRuleset.name] = modRuleset + } + catch (ex:Exception){} + } + } + + fun getBaseRuleset() = this[""]!! + + fun getComplexRuleset(mods:Collection): Ruleset { + val newRuleset = Ruleset() + newRuleset.add(getBaseRuleset()) + for(mod in mods) + if(containsKey(mod)) { + newRuleset.add(this[mod]!!) + newRuleset.mods+=mod + } + return newRuleset + } +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt b/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt index da24f25525..2a22a9ab49 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt @@ -6,16 +6,16 @@ import com.badlogic.gdx.scenes.scene2d.InputEvent import com.badlogic.gdx.scenes.scene2d.InputListener import com.badlogic.gdx.scenes.scene2d.actions.Actions import com.badlogic.gdx.scenes.scene2d.ui.TextButton -import com.unciv.UncivGame import com.unciv.logic.MapSaver import com.unciv.logic.map.TileMap +import com.unciv.models.ruleset.RulesetCache import com.unciv.models.translations.tr import com.unciv.ui.utils.CameraStageBaseScreen import com.unciv.ui.utils.onClick import com.unciv.ui.utils.setFontSize class MapEditorScreen(): CameraStageBaseScreen() { - val ruleset = UncivGame.Current.ruleset + val ruleset = RulesetCache.getBaseRuleset() var mapName = "My first map" var tileMap = TileMap() diff --git a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt index f2430fcfde..fc978e4a0b 100644 --- a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt +++ b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt @@ -10,12 +10,12 @@ import com.unciv.logic.GameInfo import com.unciv.logic.GameSaver import com.unciv.logic.GameStarter import com.unciv.logic.civilization.PlayerType +import com.unciv.models.ruleset.RulesetCache import com.unciv.models.translations.tr import com.unciv.ui.pickerscreens.PickerScreen import com.unciv.ui.utils.disable import com.unciv.ui.utils.enable import com.unciv.ui.utils.onClick -import com.unciv.ui.worldscreen.WorldScreen import com.unciv.ui.worldscreen.optionstable.OnlineMultiplayer import com.unciv.ui.worldscreen.optionstable.PopupTable import java.util.* @@ -25,7 +25,7 @@ class NewGameScreen: PickerScreen(){ val newGameParameters= UncivGame.Current.gameInfo.gameParameters val mapParameters = UncivGame.Current.gameInfo.tileMap.mapParameters - val ruleSet = UncivGame.Current.ruleset.clone() + val ruleSet = RulesetCache.getComplexRuleset(newGameParameters.mods) init { setDefaultCloseAction() @@ -105,9 +105,7 @@ class NewGameScreen: PickerScreen(){ override fun render(delta: Float) { if(newGame!=null){ - game.gameInfo=newGame!! - game.worldScreen = WorldScreen(newGame!!.currentPlayerCiv) - game.setWorldScreen() + game.loadGame(newGame!!) } super.render(delta) } diff --git a/core/src/com/unciv/ui/newgamescreen/NewGameScreenOptionsTable.kt b/core/src/com/unciv/ui/newgamescreen/NewGameScreenOptionsTable.kt index b4ba87195e..f1cfdfd68b 100644 --- a/core/src/com/unciv/ui/newgamescreen/NewGameScreenOptionsTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/NewGameScreenOptionsTable.kt @@ -1,6 +1,5 @@ package com.unciv.ui.newgamescreen -import com.badlogic.gdx.Gdx import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.ui.CheckBox import com.badlogic.gdx.scenes.scene2d.ui.SelectBox @@ -9,7 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener import com.badlogic.gdx.utils.Array import com.unciv.logic.MapSaver import com.unciv.models.metadata.GameSpeed -import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.VictoryType import com.unciv.models.ruleset.tech.TechEra import com.unciv.models.translations.tr @@ -20,7 +19,6 @@ class NewGameScreenOptionsTable(val newGameScreen: NewGameScreen, val updatePlay : Table(CameraStageBaseScreen.skin) { val newGameParameters = newGameScreen.newGameParameters val mapParameters = newGameScreen.mapParameters - val baseRuleset = newGameScreen.ruleSet.clone() val ruleset = newGameScreen.ruleSet init { @@ -221,46 +219,22 @@ class NewGameScreenOptionsTable(val newGameScreen: NewGameScreen, val updatePlay fun addModCheckboxes() { - - val modFolders = Gdx.files.local("mods") - if(!modFolders.exists()) return - val loadableMods = ArrayList() - - for (modFolder in modFolders.list()) { - if (modFolder.list().any { it.name() == "jsons" }) { - val modRuleset = Ruleset(false) - - try { - modRuleset.load(modFolder.child("jsons")) - modRuleset.name = modFolder.nameWithoutExtension() - loadableMods.add(modRuleset) - - } catch (ex: Exception) { - print(ex.message) - } - } - } + val modRulesets = RulesetCache.filter { it.key!="" }.values + if(modRulesets.isEmpty()) return fun reloadMods(){ - ruleset.clearExceptModNames() - ruleset.add(baseRuleset) - for(modName in ruleset.mods){ - val correspondingMod = loadableMods.first { it.name==modName } - ruleset.add(correspondingMod) - } + ruleset.clear() + ruleset.add(RulesetCache.getComplexRuleset(newGameParameters.mods)) } - if(loadableMods.isEmpty()) return - - add("{Mods}:".tr()).colspan(2).row() val modCheckboxTable = Table().apply { defaults().pad(10f) } - for(mod in loadableMods){ + for(mod in modRulesets){ val checkBox = CheckBox(mod.name,CameraStageBaseScreen.skin) checkBox.addListener(object : ChangeListener() { override fun changed(event: ChangeEvent?, actor: Actor?) { - if(checkBox.isChecked) ruleset.mods.add(mod.name) - else ruleset.mods.remove(mod.name) + if(checkBox.isChecked) newGameParameters.mods.add(mod.name) + else newGameParameters.mods.remove(mod.name) reloadMods() updatePlayerPickerTable() } diff --git a/core/src/com/unciv/ui/utils/ImageGetter.kt b/core/src/com/unciv/ui/utils/ImageGetter.kt index 7f2dcc5671..346dc9bfd2 100644 --- a/core/src/com/unciv/ui/utils/ImageGetter.kt +++ b/core/src/com/unciv/ui/utils/ImageGetter.kt @@ -1,5 +1,6 @@ package com.unciv.ui.utils +import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.g2d.TextureAtlas @@ -11,8 +12,8 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.utils.Drawable import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable import com.badlogic.gdx.utils.Align -import com.unciv.UncivGame import com.unciv.models.ruleset.Nation +import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.tile.ResourceType object ImageGetter { @@ -24,6 +25,7 @@ object ImageGetter { // So, we now use TexturePacker in the DesktopLauncher class to pack all the different images into single images, // and the atlas is what tells us what was packed where. var atlas = TextureAtlas("game.atlas") + var ruleset = Ruleset() // We then shove all the drawables into a hashmap, because the atlas specifically tells us // that the search on it is inefficient @@ -33,14 +35,26 @@ object ImageGetter { setTextureRegionDrawables() } - fun getRuleSet() = UncivGame.Current.ruleset fun setTextureRegionDrawables(){ textureRegionDrawables.clear() + // These are the srawables from the base game for(region in atlas.regions){ val drawable =TextureRegionDrawable(region) textureRegionDrawables[region.name] = drawable } + + // These are from the mods + for(mod in ruleset.mods){ + val modAltasFile = Gdx.files.local("mods/$mod/game.atlas") + if (modAltasFile.exists()) { + val modAtlas = TextureAtlas(modAltasFile) + for (region in modAtlas.regions) { + val drawable = TextureRegionDrawable(region) + textureRegionDrawables[region.name] = drawable + } + } + } } fun refreshAltas() { @@ -112,13 +126,13 @@ object ImageGetter { return getImage("OtherIcons/Stop") if(improvementName.startsWith("StartingLocation ")){ val nationName = improvementName.removePrefix("StartingLocation ") - val nation = getRuleSet().nations[nationName]!! + val nation = ruleset.nations[nationName]!! return getNationIndicator(nation,size) } val iconGroup = getImage("ImprovementIcons/$improvementName").surroundWithCircle(size) - val improvement = getRuleSet().tileImprovements[improvementName]!! + val improvement = ruleset.tileImprovements[improvementName]!! when { improvement.food>0 -> iconGroup.circle.color= foodCircleColor improvement.production>0 -> iconGroup.circle.color= productionCircleColor @@ -131,8 +145,8 @@ object ImageGetter { } fun getConstructionImage(construction: String): Image { - if(getRuleSet().buildings.containsKey(construction)) return getImage("BuildingIcons/$construction") - if(getRuleSet().units.containsKey(construction)) return getUnitIcon(construction) + if(ruleset.buildings.containsKey(construction)) return getImage("BuildingIcons/$construction") + if(ruleset.units.containsKey(construction)) return getUnitIcon(construction) if(construction=="Nothing") return getImage("OtherIcons/Stop") return getStatIcon(construction) } @@ -180,7 +194,7 @@ object ImageGetter { fun getResourceImage(resourceName: String, size:Float): Actor { val iconGroup = getImage("ResourceIcons/$resourceName").surroundWithCircle(size) - val resource = getRuleSet().tileResources[resourceName]!! + val resource = ruleset.tileResources[resourceName]!! when { resource.food>0 -> iconGroup.circle.color= foodCircleColor resource.production>0 -> iconGroup.circle.color= productionCircleColor @@ -204,7 +218,7 @@ object ImageGetter { fun getTechIconGroup(techName: String, circleSize: Float): Group { var techIconColor = Color.WHITE - when (getRuleSet().technologies[techName]!!.era().name) { + when (ruleset.technologies[techName]!!.era().name) { "Ancient" -> techIconColor = colorFromRGB(255, 87, 35) "Classical" -> techIconColor = colorFromRGB(233, 31, 99) "Medieval" -> techIconColor = colorFromRGB(157, 39, 176) diff --git a/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt b/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt index 3bd99f6100..89f14d0a22 100644 --- a/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt +++ b/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt @@ -53,6 +53,14 @@ internal object DesktopLauncher { settings.filterMin = Texture.TextureFilter.MipMapLinearLinear TexturePacker.process(settings, "../Images", ".", "game") + // pack for mods as well + val modDirectory = File("../assets/mods") + if(modDirectory.exists()) { + for (mod in modDirectory.listFiles()!!){ + TexturePacker.process(settings, mod.path + "/Images", mod.path, "game") + } + } + val texturePackingTime = System.currentTimeMillis() - startTime println("Packing textures - "+texturePackingTime+"ms") }