Pedia from mainmenu (#6788)

* Make Civilopedia independent of worldScreen or gameInfo

* Make Civilopedia callable from Main Menu

* Era category for Civilopedia

* Era category for Civilopedia - atlas
This commit is contained in:
SomeTroglodyte 2022-05-13 11:36:06 +02:00 committed by GitHub
parent ab82328211
commit 345ca0ec25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 873 additions and 771 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -1249,6 +1249,10 @@ Granted by: = Erteilt von:
[bonus] with [tech] = [bonus] mit [tech]
Difficulty levels = Schwierigkeitsgrade
The possible rewards are: = Mögliche Belohnungen:
Eras = Äras
Embarked strength: [amount]† = Stärke eingeschiffter Einheiten: [amount]†
Base unit buy cost: [amount]¤ = Einheiten-Kauf Basispreis: [amount]¤
Research agreement cost: [amount]¤ = Forschungsabkommen kosten: [amount]¤
# Policies

View File

@ -1255,8 +1255,13 @@ Granted by: =
[bonus] with [tech] =
Difficulty levels =
The possible rewards are: =
Eras =
Embarked strength: [amount]† =
Base unit buy cost: [amount]¤ =
Research agreement cost: [amount]¤ =
# Policies
S# Policies
Adopt policy =
Adopt free policy =

View File

@ -1,9 +1,11 @@
package com.unciv
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.Input
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.actions.Actions
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.GameStarter
@ -12,10 +14,12 @@ import com.unciv.logic.map.MapSize
import com.unciv.logic.map.MapSizeNew
import com.unciv.logic.map.MapType
import com.unciv.logic.map.mapgenerator.MapGenerator
import com.unciv.models.metadata.BaseRuleset
import com.unciv.models.ruleset.RulesetCache
import com.unciv.ui.MultiplayerScreen
import com.unciv.ui.mapeditor.*
import com.unciv.models.metadata.GameSetupInfo
import com.unciv.ui.civilopedia.CivilopediaScreen
import com.unciv.ui.crashhandling.crashHandlingThread
import com.unciv.ui.crashhandling.postCrashHandlingRunnable
import com.unciv.ui.images.ImageGetter
@ -56,7 +60,7 @@ class MainMenuScreen: BaseScreen() {
keyPressDispatcher[key] = function
table.addTooltip(key, 32f)
}
table.pack()
return table
}
@ -87,7 +91,7 @@ class MainMenuScreen: BaseScreen() {
}
val column1 = Table().apply { defaults().pad(10f).fillX() }
val column2 = if(singleColumn) column1 else Table().apply { defaults().pad(10f).fillX() }
val column2 = if (singleColumn) column1 else Table().apply { defaults().pad(10f).fillX() }
val autosaveGame = GameSaver.getSave(GameSaver.autoSaveFileName, false)
if (autosaveGame.exists()) {
@ -127,7 +131,7 @@ class MainMenuScreen: BaseScreen() {
column2.add(optionsTable).row()
val table=Table().apply { defaults().pad(10f) }
val table = Table().apply { defaults().pad(10f) }
table.add(column1)
if (!singleColumn) table.add(column2)
table.pack()
@ -144,6 +148,18 @@ class MainMenuScreen: BaseScreen() {
}
ExitGamePopup(this)
}
val helpButton = "?".toLabel(fontSize = 32)
.apply { setAlignment(Align.center) }
.surroundWithCircle(40f, color = ImageGetter.getBlue())
.apply { actor.y -= 2.5f } // compensate font baseline (empirical)
.surroundWithCircle(42f, resizeActor = false)
helpButton.touchable = Touchable.enabled
helpButton.onClick { openCivilopedia() }
keyPressDispatcher[Input.Keys.F1] = { openCivilopedia() }
helpButton.addTooltip(KeyCharAndCode(Input.Keys.F1), 20f)
helpButton.setPosition(20f, 20f)
stage.addActor(helpButton)
}
@ -222,6 +238,13 @@ class MainMenuScreen: BaseScreen() {
}
}
private fun openCivilopedia() {
val ruleset =RulesetCache[game.settings.lastGameSetup?.gameParameters?.baseRuleset]
?: RulesetCache[BaseRuleset.Civ_V_GnK.fullName]
?: return
game.setScreen(CivilopediaScreen(ruleset, this))
}
override fun resize(width: Int, height: Int) {
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
game.setScreen(MainMenuScreen())

View File

@ -64,6 +64,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
val alertBattle = false
lateinit var worldScreen: WorldScreen
fun getWorldScreenOrNull() = if (this::worldScreen.isInitialized) worldScreen else null
var isInitialized = false

View File

@ -461,7 +461,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
for (unique in uniqueObjects) {
@Suppress("NON_EXHAUSTIVE_WHEN")
when (unique.type) { // TODO: Lots of typification…
when (unique.type) {
UniqueType.OnlyAvailableWhen->
if (!unique.conditionalsApply(civInfo, cityConstructions.cityInfo))
rejectionReasons.add(RejectionReason.ShouldNotBeDisplayed)

View File

@ -3,9 +3,9 @@ package com.unciv.models.ruleset
import com.badlogic.gdx.graphics.Color
import com.unciv.logic.civilization.CityStateType
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.models.ruleset.unique.IHasUniques
import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueTarget
import com.unciv.models.ruleset.unique.*
import com.unciv.ui.civilopedia.FormattedLine
import com.unciv.ui.utils.Fonts
import com.unciv.ui.utils.colorFromRGB
class Era : RulesetObject(), IHasUniques {
@ -32,8 +32,52 @@ class Era : RulesetObject(), IHasUniques {
val allyBonusObjects: Map<CityStateType, List<Unique>> by lazy { initBonuses(allyBonus) }
var iconRGB: List<Int>? = null
companion object {
private val eraConditionals = setOf(UniqueType.ConditionalBeforeEra, UniqueType.ConditionalDuringEra, UniqueType.ConditionalStartingFromEra)
}
override fun getUniqueTarget() = UniqueTarget.Era
override fun makeLink() = "" // No own category on Civilopedia screen
override fun makeLink() = "Era/$name"
override fun getCivilopediaTextHeader() = FormattedLine(name, header = 2, color = getHexColor())
override fun getCivilopediaTextLines(ruleset: Ruleset) = sequence {
yield(FormattedLine("Embarked strength: [$embarkDefense]${Fonts.strength}"))
yield(FormattedLine("Base unit buy cost: [$baseUnitBuyCost]${Fonts.gold}"))
yield(FormattedLine("Research agreement cost: [$researchAgreementCost]${Fonts.gold}"))
yield(FormattedLine())
yieldAll(ruleset.technologies.values.asSequence()
.filter { it.era() == name }
.map { FormattedLine(it.name, it.makeLink()) })
if (uniques.isNotEmpty()) yield(FormattedLine())
yieldAll(uniqueObjects.asSequence().map { FormattedLine(it) })
val eraGatedObjects = getEraGatedObjects(ruleset).toList()
if (eraGatedObjects.isEmpty()) return@sequence
yield(FormattedLine())
yield(FormattedLine("{See also}:"))
yieldAll(eraGatedObjects.map { FormattedLine(it.name, it.makeLink()) })
}.toList()
private fun getEraGatedObjects(ruleset: Ruleset): Sequence<IRulesetObject> {
val policyBranches = ruleset.policyBranches.values.asSequence()
.filter { it.era == name }
return policyBranches +
// This second part is empty in our base rulesets, yes
ruleset.allRulesetObjects()
.flatMap { obj ->
obj.getMatchingUniques(
UniqueType.OnlyAvailableWhen,
StateForConditionals.IgnoreConditionals
)
.map { unique -> obj to unique }
}.filter { (_, unique) ->
unique.conditionals.any {
it.type in eraConditionals
}
}.map { it.first }.distinct()
}
private fun initBonuses(bonusMap: Map<String, List<String>>): Map<CityStateType, List<Unique>> {
val objectMap = HashMap<CityStateType, List<Unique>>()

View File

@ -54,8 +54,10 @@ open class Policy : RulesetObject() {
val lineList = ArrayList<FormattedLine>()
lineList += if (this is PolicyBranch) {
val eraColor = ruleset.eras[era]?.getHexColor() ?: ""
FormattedLine("{Unlocked at} {${branch.era}}", header = 4, color = eraColor)
val era = ruleset.eras[era]
val eraColor = era?.getHexColor() ?: ""
val eraLink = era?.makeLink() ?: ""
FormattedLine("{Unlocked at} {${branch.era}}", header = 4, color = eraColor, link = eraLink)
} else {
FormattedLine("Policy branch: [${branch.name}]", link = branch.makeLink())
}

View File

@ -52,14 +52,14 @@ class Technology: RulesetObject() {
}
val viewingCiv = UncivGame.Current.worldScreen.viewingCiv
val enabledUnits = getEnabledUnits(viewingCiv)
val enabledUnits = getEnabledUnits(ruleset, viewingCiv)
if (enabledUnits.any()) {
lineList += "{Units enabled}: "
for (unit in enabledUnits)
lineList += " * " + unit.name.tr() + " (" + unit.getShortDescription() + ")"
}
val enabledBuildings = getEnabledBuildings(viewingCiv)
val enabledBuildings = getEnabledBuildings(ruleset, viewingCiv)
val regularBuildings = enabledBuildings.filter { !it.isAnyWonder() }
if (regularBuildings.any()) {
@ -75,7 +75,7 @@ class Technology: RulesetObject() {
lineList += " * " + wonder.name.tr() + " (" + wonder.getShortDescription() + ")"
}
for (obj in getObsoletedObjects(viewingCiv))
for (obj in getObsoletedObjects(ruleset, viewingCiv))
lineList += "[${obj.name}] obsoleted"
for (resource in ruleset.tileResources.values.asSequence().filter { it.revealedBy == name }
@ -94,7 +94,7 @@ class Technology: RulesetObject() {
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia.
*/
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia" unique this needs refactoring
fun getEnabledBuildings(civInfo: CivilizationInfo) = getFilteredBuildings(civInfo)
fun getEnabledBuildings(ruleset: Ruleset, civInfo: CivilizationInfo?) = getFilteredBuildings(ruleset, civInfo)
{ it.requiredTech == name }
/**
@ -103,41 +103,48 @@ class Technology: RulesetObject() {
*/
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia" unique this needs refactoring
fun getObsoletedObjects(civInfo: CivilizationInfo): Sequence<RulesetStatsObject> =
(getFilteredBuildings(civInfo){true}
+ civInfo.gameInfo.ruleSet.tileResources.values.asSequence()
+ civInfo.gameInfo.ruleSet.tileImprovements.values.filter {
it.uniqueTo==null || it.uniqueTo == civInfo.civName
}).filter { it.getMatchingUniques(UniqueType.ObsoleteWith).any { it.params[0] == name } }
fun getObsoletedObjects(ruleset: Ruleset, civInfo: CivilizationInfo?): Sequence<RulesetStatsObject> =
(getFilteredBuildings(ruleset, civInfo){true}
+ ruleset.tileResources.values.asSequence()
+ ruleset.tileImprovements.values.filter {
it.uniqueTo == null || it.uniqueTo == civInfo?.civName
}).filter { obj: RulesetStatsObject ->
obj.getMatchingUniques(UniqueType.ObsoleteWith).any { it.params[0] == name }
}
// Helper: common filtering for both getEnabledBuildings and getObsoletedBuildings, difference via predicate parameter
private fun getFilteredBuildings(civInfo: CivilizationInfo, predicate: (Building)->Boolean): Sequence<Building> {
val nuclearWeaponsEnabled = civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled
val religionEnabled = civInfo.gameInfo.isReligionEnabled()
return civInfo.gameInfo.ruleSet.buildings.values.asSequence()
private fun getFilteredBuildings(
ruleset: Ruleset,
civInfo: CivilizationInfo?,
predicate: (Building)->Boolean
): Sequence<Building> {
val (nuclearWeaponsEnabled, religionEnabled) = getNukeAndReligionSwitches(civInfo)
return ruleset.buildings.values.asSequence()
.filter {
predicate(it) // expected to be the most selective, thus tested first
&& (it.uniqueTo == civInfo.civName || it.uniqueTo==null && civInfo.getEquivalentBuilding(it) == it)
&& (it.uniqueTo == civInfo?.civName || it.uniqueTo == null && civInfo?.getEquivalentBuilding(it) == it)
&& (nuclearWeaponsEnabled || !it.hasUnique(UniqueType.EnablesNuclearWeapons))
&& (religionEnabled || !it.hasUnique(UniqueType.HiddenWithoutReligion))
&& !it.hasUnique(UniqueType.HiddenFromCivilopedia)
}
}
private fun getNukeAndReligionSwitches(civInfo: CivilizationInfo?): Pair<Boolean, Boolean> {
if (civInfo == null) return true to true
return civInfo.gameInfo.run { gameParameters.nuclearWeaponsEnabled to isReligionEnabled() }
}
/**
* Returns a Sequence of [BaseUnit]s enabled by this Technology, filtered for [civInfo]'s uniques,
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia.
*/
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia"/HiddenFromCivilopedia unique this needs refactoring
fun getEnabledUnits(civInfo: CivilizationInfo): Sequence<BaseUnit> {
val nuclearWeaponsEnabled = civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled
val religionEnabled = civInfo.gameInfo.isReligionEnabled()
return civInfo.gameInfo.ruleSet.units.values.asSequence()
fun getEnabledUnits(ruleset: Ruleset, civInfo: CivilizationInfo?): Sequence<BaseUnit> {
val (nuclearWeaponsEnabled, religionEnabled) = getNukeAndReligionSwitches(civInfo)
return ruleset.units.values.asSequence()
.filter {
it.requiredTech == name
&& (it.uniqueTo == civInfo.civName || it.uniqueTo==null && civInfo.getEquivalentUnit(it) == it)
&& (it.uniqueTo == civInfo?.civName || it.uniqueTo == null && civInfo?.getEquivalentUnit(it) == it)
&& (nuclearWeaponsEnabled || !it.isNuclearWeapon())
&& (religionEnabled || !it.hasUnique(UniqueType.HiddenWithoutReligion))
&& !it.hasUnique(UniqueType.HiddenFromCivilopedia)
@ -218,8 +225,8 @@ class Technology: RulesetObject() {
}
}
val viewingCiv = UncivGame.Current.worldScreen.viewingCiv
val enabledUnits = getEnabledUnits(viewingCiv)
val viewingCiv = UncivGame.Current.getWorldScreenOrNull()?.viewingCiv
val enabledUnits = getEnabledUnits(ruleset, viewingCiv)
if (enabledUnits.any()) {
lineList += FormattedLine()
lineList += FormattedLine("{Units enabled}:")
@ -227,7 +234,7 @@ class Technology: RulesetObject() {
lineList += FormattedLine(unit.name.tr() + " (" + unit.getShortDescription() + ")", link = unit.makeLink())
}
val enabledBuildings = getEnabledBuildings(viewingCiv)
val enabledBuildings = getEnabledBuildings(ruleset, viewingCiv)
.partition { it.isAnyWonder() }
if (enabledBuildings.first.isNotEmpty()) {
lineList += FormattedLine()
@ -242,7 +249,7 @@ class Technology: RulesetObject() {
lineList += FormattedLine(building.name.tr() + " (" + building.getShortDescription() + ")", link = building.makeLink())
}
val obsoletedObjects = getObsoletedObjects(viewingCiv)
val obsoletedObjects = getObsoletedObjects(ruleset, viewingCiv)
if (obsoletedObjects.any()) {
lineList += FormattedLine()
obsoletedObjects.forEach {

View File

@ -183,6 +183,11 @@ enum class CivilopediaCategories (
getImage = null,
KeyCharAndCode('D'),
"OtherIcons/Quickstart"
),
Era ("Eras", false,
getImage = null,
KeyCharAndCode('D'),
"OtherIcons/Tyrannosaurus"
);
fun getByOffset(offset: Int) = values()[(ordinal + count + offset) % count]

View File

@ -205,6 +205,7 @@ class CivilopediaScreen(
CivilopediaCategories.Difficulty -> ruleset.difficulties.values
CivilopediaCategories.Belief -> (ruleset.beliefs.values.asSequence() +
Belief.getCivilopediaReligionEntry(ruleset)).toList()
CivilopediaCategories.Era -> ruleset.eras.values
}
for (loopCategory in CivilopediaCategories.values()) {
@ -312,7 +313,7 @@ class CivilopediaScreen(
override fun resize(width: Int, height: Int) {
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
game.setScreen(CivilopediaScreen(game.worldScreen.gameInfo.ruleSet, previousScreen, currentCategory, currentEntry))
game.setScreen(CivilopediaScreen(ruleset, previousScreen, currentCategory, currentEntry))
}
}
}

View File

@ -430,7 +430,9 @@ interface ICivilopediaText {
/** Generate automatic lines from object metadata.
*
* Please do not rely on a UncivGame.Current.gameInfo being initialized, this should be able to run from the main menu.
* This function ***MUST not rely*** on [UncivGame.Current.gameInfo][UncivGame.gameInfo]
* **or** [UncivGame.Current.worldScreen][UncivGame.worldScreen] being initialized,
* this should be able to run from the main menu.
* (And the info displayed should be about the **ruleset**, not the player situation)
*
* Default implementation is empty - no need to call super in overrides.

View File

@ -57,18 +57,18 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
val tech = ruleset.technologies[techName]!!
for (unit in tech.getEnabledUnits(techManager.civInfo))
for (unit in tech.getEnabledUnits(ruleset, techManager.civInfo))
techEnabledIcons.add(ImageGetter.getConstructionImage(unit.name).surroundWithCircle(techIconSize))
for (building in tech.getEnabledBuildings(techManager.civInfo))
for (building in tech.getEnabledBuildings(ruleset, techManager.civInfo))
techEnabledIcons.add(ImageGetter.getConstructionImage(building.name).surroundWithCircle(techIconSize))
for (obj in tech.getObsoletedObjects(techManager.civInfo)) {
val obsoletedIcon = when {
obj is Building -> ImageGetter.getConstructionImage(obj.name)
for (obj in tech.getObsoletedObjects(ruleset, techManager.civInfo)) {
val obsoletedIcon = when (obj) {
is Building -> ImageGetter.getConstructionImage(obj.name)
.surroundWithCircle(techIconSize)
obj is TileResource -> ImageGetter.getResourceImage(obj.name, techIconSize)
obj is TileImprovement -> ImageGetter.getImprovementIcon(obj.name, techIconSize)
is TileResource -> ImageGetter.getResourceImage(obj.name, techIconSize)
is TileImprovement -> ImageGetter.getImprovementIcon(obj.name, techIconSize)
else -> continue
}.also {
val closeImage = ImageGetter.getRedCross(techIconSize / 2, 1f)

View File

@ -687,6 +687,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
- [Party](https://thenounproject.com/icon/party-1784941/) by Adrien Coquet for WLTK header on City Overview
- [Party](https://thenounproject.com/icon/party-2955155/) by Lars Meiertoberens as additional WLKT decoration
- [turn right](https://thenounproject.com/icon/turn-right-1920867/) by Alice Design for Resource Overview
- [Tyrannosaurus Rex](https://thenounproject.com/icon/tyrannosaurus-rex-4130976/) by Amethyst Studio for Civilopedia Eras header
### Main menu