Use Events for the floating "Tutorials" (#11717)
* Split off reuses of CityStateIcons/Cultured to allow modding separately * Reposition floating tutorials in case the TopBar moved its buttons * Event definition, art and basic support * Split off Event rendering from AlertPopup * Support Event presentation modes and replace hardcoded floating tutorials * "Meet another civilization" art - can't find any better * Tweak TranslationFileWriter and some polishing
BIN
android/Images.Icons/OtherIcons/HiddenTutorialTask.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
android/Images.Icons/OtherIcons/Score.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
android/assets/ExtraImages/Tutorials/Conquer a city.png
Normal file
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 33 KiB |
BIN
android/assets/ExtraImages/Tutorials/Create a trade route.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
android/assets/ExtraImages/Tutorials/Enter city screen.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
android/assets/ExtraImages/Tutorials/Found city.png
Normal file
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 26 KiB |
BIN
android/assets/ExtraImages/Tutorials/Move an air unit.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
android/assets/ExtraImages/Tutorials/Move unit.png
Normal file
After Width: | Height: | Size: 8.0 KiB |
BIN
android/assets/ExtraImages/Tutorials/Open the options table.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
android/assets/ExtraImages/Tutorials/Pass a turn.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
android/assets/ExtraImages/Tutorials/Pick construction.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
android/assets/ExtraImages/Tutorials/Pick technology.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
android/assets/ExtraImages/Tutorials/Reassign worked tiles.png
Normal file
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 17 KiB |
@ -11,6 +11,20 @@ CityStateIcons/Cultured
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
OtherIcons/HiddenTutorialTask
|
||||
rotate: false
|
||||
xy: 127, 1001
|
||||
size: 100, 100
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
OtherIcons/Score
|
||||
rotate: false
|
||||
xy: 127, 1001
|
||||
size: 100, 100
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
CityStateIcons/Maritime
|
||||
rotate: false
|
||||
xy: 1819, 1821
|
||||
@ -431,27 +445,6 @@ StatIcons/Faith
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
NotificationIcons/Loading
|
||||
rotate: false
|
||||
xy: 1063, 1820
|
||||
size: 100, 100
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
NotificationIcons/Working
|
||||
rotate: false
|
||||
xy: 1063, 1820
|
||||
size: 100, 100
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
OtherIcons/Loading
|
||||
rotate: false
|
||||
xy: 1063, 1820
|
||||
size: 100, 100
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
NotificationIcons/PickConstruction
|
||||
rotate: false
|
||||
xy: 1555, 1497
|
||||
@ -739,6 +732,27 @@ OtherIcons/Load
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
OtherIcons/Loading
|
||||
rotate: false
|
||||
xy: 1063, 1820
|
||||
size: 100, 100
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
NotificationIcons/Loading
|
||||
rotate: false
|
||||
xy: 1063, 1820
|
||||
size: 100, 100
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
NotificationIcons/Working
|
||||
rotate: false
|
||||
xy: 1063, 1820
|
||||
size: 100, 100
|
||||
orig: 100, 100
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
OtherIcons/LockSmall
|
||||
rotate: false
|
||||
xy: 725, 869
|
||||
|
190
android/assets/jsons/Civ V - Gods & Kings/Events.json
Normal file
@ -0,0 +1,190 @@
|
||||
[
|
||||
{
|
||||
"name":"Tutorial Task: [Move unit]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Move a unit!", "centered":true},
|
||||
{"extraImage":"Tutorials/Move unit", "imageSize":140},
|
||||
{"text":"Click on a unit → Click on a destination → Click the arrow popup."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [Units] is greater than [0]>",
|
||||
"Unavailable <if tutorial [Move unit] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Found city]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Found a city!", "centered":true},
|
||||
{"extraImage":"Tutorials/Found city", "imageSize":140},
|
||||
{"text":"Select the Settler → Click on 'Found city'."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [Settler] is greater than [0]>",
|
||||
"Unavailable <if tutorial [Found city] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Enter city screen]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Enter the city screen!", "centered":true},
|
||||
{"extraImage":"Tutorials/Enter city screen", "imageSize":100},
|
||||
{"text":"Click the city button twice."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [Cities] is greater than [0]>",
|
||||
"Unavailable <if tutorial [Enter city screen] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Pick technology]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Pick a technology to research!", "centered":true},
|
||||
{"extraImage":"Tutorials/Pick technology", "imageSize":180},
|
||||
{"text":"Click on the tech button → select technology → click 'Research' (bottom right)."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled>",
|
||||
"Unavailable <if tutorial [Pick technology] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Pick construction]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Pick a construction!", "centered":true},
|
||||
{"extraImage":"Tutorials/Pick construction", "imageSize":120},
|
||||
{"text":"Enter city screen → Click on a unit or building → click 'add to queue'."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [Cities] is greater than [0]>",
|
||||
"Unavailable <if tutorial [Pick construction] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Pass a turn]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Pass a turn!", "centered":true},
|
||||
{"extraImage":"Tutorials/Pass a turn", "imageSize":180},
|
||||
{"text":"Cycle through units with 'Next unit' → Click 'Next turn'."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [turns] is less than [3]>",
|
||||
"Unavailable <if tutorial [Pass a turn] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Reassign worked tiles]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Reassign worked tiles!", "centered":true},
|
||||
{"extraImage":"Tutorials/Reassign worked tiles", "imageSize":140},
|
||||
{"text":"Enter city screen → click the assigned tile to unassign → click an unassigned tile to assign population."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [Cities] is greater than [0]>",
|
||||
"Unavailable <if tutorial [Reassign worked tiles] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Meet another civilization]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Meet another civilization!", "centered": true},
|
||||
{"extraImage":"Tutorials/Meet another civilization", "imageSize":160},
|
||||
{"text":"Explore the map until you encounter another civilization!"},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [turns] is greater than [1]>",
|
||||
"Unavailable <if tutorial [Meet another civilization] is completed>"
|
||||
],
|
||||
"choices": [
|
||||
{
|
||||
"text":"Got it",
|
||||
"triggeredUniques": ["Mark tutorial [Meet another civilization] complete"],
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Open the options table]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Open the options dialog!", "centered":true},
|
||||
{"extraImage":"Tutorials/Open the options table", "imageSize":130},
|
||||
{"text":"Click the menu button (top left) → click 'Options'."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled>",
|
||||
"Unavailable <if tutorial [Open the options table] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Construct an improvement]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Construct an improvement!", "centered":true},
|
||||
{"extraImage":"Tutorials/Construct an improvement", "imageSize":150},
|
||||
{"text":"Construct a Worker unit →> Move it to a Plains or Grassland tile → Click 'Construct improvement' → Choose the farm → Leave the worker there until it's finished."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [Cities] is greater than [0]>",
|
||||
"Unavailable <if tutorial [Construct an improvement] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Create a trade route]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Create a trade route!", "centered":true},
|
||||
{"extraImage":"Tutorials/Create a trade route", "imageSize":120},
|
||||
{"text":"Construct roads between your capital and another city. Or, automate your worker and let him get to that eventually."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [Cities] is greater than [1]>",
|
||||
"Unavailable <if tutorial [Create a trade route] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Conquer a city]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Conquer a city!", "centered":true},
|
||||
{"extraImage":"Tutorials/Conquer a city", "imageSize":160},
|
||||
{"text":"Bring an enemy city down to low health → Enter the city with a melee unit."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when at war>",
|
||||
"Unavailable <if tutorial [Conquer a city] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [Move an air unit]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"Move an air unit!", "centered":true},
|
||||
{"extraImage":"Tutorials/Move an air unit", "imageSize":140},
|
||||
{"text":"Select an air unit →> select another city within range → Move the unit to the other city."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled> <when number of [Air units] is greater than [0]>",
|
||||
"Unavailable <if tutorial [Move an air unit] is completed>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"Tutorial Task: [See your stats breakdown]",
|
||||
"presentation":"Floating",
|
||||
"civilopediaText":[
|
||||
{"text":"See your stats breakdown!", "centered":true},
|
||||
{"extraImage":"Tutorials/See your stats breakdown", "imageSize":140},
|
||||
{"text":"Enter the Overview screen (top right corner) → Click on 'Stats'."},
|
||||
],
|
||||
"uniques": [
|
||||
"Only available <if tutorials are enabled>",
|
||||
"Unavailable <if tutorial [See your stats breakdown] is completed>"
|
||||
]
|
||||
},
|
||||
]
|
@ -4,27 +4,38 @@ import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.models.ruleset.unique.Conditionals
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueTriggerActivation
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.components.input.KeyCharAndCode
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import com.unciv.ui.screens.civilopediascreen.ICivilopediaText
|
||||
|
||||
|
||||
class Event : INamed, ICivilopediaText {
|
||||
|
||||
override var name = ""
|
||||
class Event : RulesetObject() {
|
||||
enum class Presentation { None, Alert, Floating }
|
||||
val presentation = Presentation.Alert
|
||||
var text = ""
|
||||
override var civilopediaText = listOf<FormattedLine>()
|
||||
|
||||
override fun getUniqueTarget() = UniqueTarget.Event
|
||||
override fun makeLink() = "Event/$name"
|
||||
|
||||
// todo: add unrepeatable events
|
||||
|
||||
var choices = ArrayList<EventChoice>()
|
||||
|
||||
/** @return `null` when no choice passes the condition tests, so client code can easily bail using Elvis `?:`. */
|
||||
fun getMatchingChoices(stateForConditionals: StateForConditionals) =
|
||||
choices.filter { it.matchesConditions(stateForConditionals) }.ifEmpty { null }
|
||||
/** @return `null` when no choice passes the condition tests, so client code can easily bail using Elvis `?:`.
|
||||
* An empty list is possible when the Event definition contains no choices and the event's conditions are fulfilled.
|
||||
*/
|
||||
fun getMatchingChoices(stateForConditionals: StateForConditionals): Collection<EventChoice>? {
|
||||
if (!isAvailable(stateForConditionals)) return null
|
||||
if (choices.isEmpty()) return emptyList()
|
||||
return choices.filter { it.matchesConditions(stateForConditionals) }.ifEmpty { null }
|
||||
}
|
||||
|
||||
fun isAvailable(stateForConditionals: StateForConditionals) =
|
||||
getMatchingUniques(UniqueType.OnlyAvailable, StateForConditionals.IgnoreConditionals).none { !it.conditionalsApply(stateForConditionals) } &&
|
||||
getMatchingUniques(UniqueType.Unavailable, stateForConditionals).none()
|
||||
}
|
||||
|
||||
class EventChoice : ICivilopediaText {
|
||||
@ -44,8 +55,10 @@ class EventChoice : ICivilopediaText {
|
||||
fun matchesConditions(stateForConditionals: StateForConditionals) =
|
||||
conditionObjects.all { Conditionals.conditionalApplies(null, it, stateForConditionals) }
|
||||
|
||||
fun triggerChoice(civ: Civilization) {
|
||||
fun triggerChoice(civ: Civilization): Boolean {
|
||||
var success = false
|
||||
for (unique in triggeredUniqueObjects)
|
||||
UniqueTriggerActivation.triggerUnique(unique, civ)
|
||||
if (UniqueTriggerActivation.triggerUnique(unique, civ)) success = true
|
||||
return success
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.unciv.models.ruleset.unique
|
||||
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.battle.CombatAction
|
||||
import com.unciv.logic.city.City
|
||||
@ -103,6 +104,8 @@ object Conditionals {
|
||||
UniqueType.ConditionalEveryTurns -> checkOnGameInfo { turns % condition.params[0].toInt() == 0 }
|
||||
UniqueType.ConditionalBeforeTurns -> checkOnGameInfo { turns < condition.params[0].toInt() }
|
||||
UniqueType.ConditionalAfterTurns -> checkOnGameInfo { turns >= condition.params[0].toInt() }
|
||||
UniqueType.ConditionalTutorialsEnabled -> UncivGame.Current.settings.showTutorials
|
||||
UniqueType.ConditionalTutorialCompleted -> condition.params[0] in UncivGame.Current.settings.tutorialTasksCompleted
|
||||
|
||||
UniqueType.ConditionalCivFilter -> checkOnCiv { matchesFilter(condition.params[0]) }
|
||||
UniqueType.ConditionalWar -> checkOnCiv { isAtWar() }
|
||||
|
@ -12,20 +12,27 @@ object Countables {
|
||||
|
||||
val gameInfo = stateForConditionals.gameInfo ?: return null
|
||||
|
||||
if (countable == "year") return stateForConditionals.gameInfo!!.getYear(gameInfo.turns)
|
||||
if (countable == "turns") return gameInfo.turns
|
||||
if (countable == "year") return gameInfo.getYear(gameInfo.turns)
|
||||
|
||||
val civInfo = stateForConditionals.relevantCiv ?: return null
|
||||
|
||||
if (countable == "Cities") return civInfo.cities.size
|
||||
if (countable == "Units") return civInfo.units.getCivUnitsSize()
|
||||
if (countable == "Air units") return civInfo.units.getCivUnits().count { it.baseUnit.movesLikeAirUnits() }
|
||||
|
||||
if (gameInfo.ruleset.tileResources.containsKey(countable))
|
||||
return stateForConditionals.getResourceAmount(countable)
|
||||
|
||||
if (countable in gameInfo.ruleset.units){
|
||||
return civInfo.units.getCivUnits().count { it.name == countable }
|
||||
}
|
||||
val unitTypeName = countable.removeSuffix(" units").removeSurrounding("[", "]")
|
||||
if (unitTypeName in gameInfo.ruleset.unitTypes)
|
||||
return civInfo.units.getCivUnits().count { it.type.name == unitTypeName }
|
||||
|
||||
if (countable in gameInfo.ruleset.buildings){
|
||||
if (countable in gameInfo.ruleset.units)
|
||||
return civInfo.units.getCivUnits().count { it.name == countable }
|
||||
|
||||
if (countable in gameInfo.ruleset.buildings)
|
||||
return civInfo.cities.count { it.cityConstructions.containsBuildingOrEquivalent(countable) }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ enum class UniqueParameterType(
|
||||
Countable("countable", "1000", "This indicates a number or a numeric variable") {
|
||||
// todo add more countables
|
||||
private val knownValues = setOf(
|
||||
"year"
|
||||
"year", "turns", "Cities", "Units"
|
||||
)
|
||||
|
||||
override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when {
|
||||
@ -74,6 +74,7 @@ enum class UniqueParameterType(
|
||||
Stat.isStat(parameterText) -> true
|
||||
parameterText in ruleset.tileResources -> true
|
||||
parameterText in ruleset.units -> true
|
||||
parameterText.removeSuffix(" units").removeSurrounding("[", "]") in ruleset.unitTypes -> true
|
||||
else -> parameterText in ruleset.buildings
|
||||
}
|
||||
},
|
||||
|
@ -58,6 +58,7 @@ enum class UniqueTarget(
|
||||
Tutorial,
|
||||
CityState(inheritsFrom = Global),
|
||||
ModOptions,
|
||||
Event,
|
||||
|
||||
// Modifiers
|
||||
Conditional("Modifiers that can be added to other uniques to limit when they will be active", modifierType = ModifierType.Conditional),
|
||||
|
@ -23,6 +23,7 @@ import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.UpgradeUnitAction
|
||||
import com.unciv.models.ruleset.BeliefType
|
||||
import com.unciv.models.ruleset.Event
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
@ -115,11 +116,20 @@ object UniqueTriggerActivation {
|
||||
val event = ruleset.events[unique.params[0]] ?: return null
|
||||
val choices = event.getMatchingChoices(stateForConditionals)
|
||||
?: return null
|
||||
return {
|
||||
if (civInfo.isAI()) choices.random().triggerChoice(civInfo)
|
||||
else civInfo.popupAlerts.add(PopupAlert(AlertType.Event, event.name))
|
||||
if (civInfo.isAI() || event.presentation == Event.Presentation.None) return {
|
||||
choices.randomOrNull()?.triggerChoice(civInfo) ?: false
|
||||
}
|
||||
if (event.presentation == Event.Presentation.Alert) return {
|
||||
civInfo.popupAlerts.add(PopupAlert(AlertType.Event, event.name))
|
||||
true
|
||||
}
|
||||
// if (event.presentation == Event.Presentation.Floating) return { //todo: Park them in a Queue in GameInfo???
|
||||
throw NotImplementedError("Event ${event.name} has presentation type ${event.presentation} which is not implemented for use via TriggerEvent")
|
||||
}
|
||||
|
||||
UniqueType.MarkTutorialComplete -> return {
|
||||
UncivGame.Current.settings.addCompletedTutorialTask(unique.params[0])
|
||||
true
|
||||
}
|
||||
|
||||
UniqueType.OneTimeFreeUnit -> {
|
||||
|
@ -276,15 +276,16 @@ enum class UniqueType(
|
||||
MaxNumberBuildable("Limited to [amount] per Civilization", UniqueTarget.Building, UniqueTarget.Unit),
|
||||
HiddenBeforeAmountPolicies("Hidden until [amount] social policy branches have been completed", UniqueTarget.Building, UniqueTarget.Unit),
|
||||
/** A special unique, as it only activates [RejectionReasonType] when it has conditionals that *do not* apply.
|
||||
* Meant to be used together with conditionals, like "Buildable only <after adopting [policy]> <while the empire is happy>".
|
||||
* Meant to be used together with conditionals, like `"Only available <after adopting [Piety]> <while the empire is happy>"`.
|
||||
* Restricts Upgrade/Transform pathways.
|
||||
* @See [CanOnlyBeBuiltWhen]
|
||||
*/
|
||||
OnlyAvailable("Only available", UniqueTarget.Unit, UniqueTarget.Building, UniqueTarget.Improvement,
|
||||
UniqueTarget.Policy, UniqueTarget.Tech, UniqueTarget.Promotion, UniqueTarget.Ruins, UniqueTarget.FollowerBelief, UniqueTarget.FounderBelief,
|
||||
UniqueTarget.Policy, UniqueTarget.Tech, UniqueTarget.Promotion, UniqueTarget.Ruins,
|
||||
UniqueTarget.FollowerBelief, UniqueTarget.FounderBelief, UniqueTarget.Event,
|
||||
docDescription = "Meant to be used together with conditionals, like \"Only available <after adopting [policy]> <while the empire is happy>\". Only allows Building when ALL conditionals are met. Will also block Upgrade and Transform actions. See also CanOnlyBeBuiltWhen"),
|
||||
Unavailable("Unavailable", UniqueTarget.Unit, UniqueTarget.Building, UniqueTarget.Improvement,
|
||||
UniqueTarget.Policy, UniqueTarget.Tech, UniqueTarget.Promotion, UniqueTarget.Ruins,
|
||||
UniqueTarget.Policy, UniqueTarget.Tech, UniqueTarget.Promotion, UniqueTarget.Ruins, UniqueTarget.Event,
|
||||
docDescription = "Meant to be used together with conditionals, like \"Unavailable <after generating a Great Prophet>\"."),
|
||||
|
||||
ConvertFoodToProductionWhenConstructed("Excess Food converted to Production when under construction", UniqueTarget.Building, UniqueTarget.Unit),
|
||||
@ -644,7 +645,8 @@ enum class UniqueType(
|
||||
ConditionalEveryTurns("every [positiveAmount] turns", UniqueTarget.Conditional),
|
||||
ConditionalBeforeTurns("before [amount] turns", UniqueTarget.Conditional),
|
||||
ConditionalAfterTurns("after [amount] turns", UniqueTarget.Conditional),
|
||||
|
||||
ConditionalTutorialsEnabled("if tutorials are enabled", UniqueTarget.Conditional, flags = UniqueFlag.setOfHiddenToUsers), // Hidden as no translations needed for now
|
||||
ConditionalTutorialCompleted("if tutorial [comment] is completed", UniqueTarget.Conditional, flags = UniqueFlag.setOfHiddenToUsers), // Hidden as no translations needed for now
|
||||
|
||||
/////// civ conditionals
|
||||
ConditionalCivFilter("for [civFilter]", UniqueTarget.Conditional),
|
||||
@ -890,6 +892,7 @@ enum class UniqueType(
|
||||
AllowRazeHolyCity("Allow raze holy city", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
|
||||
SuppressWarnings("Suppress warning [validationWarning]", *UniqueTarget.CanIncludeSuppression, flags = UniqueFlag.setOfHiddenNoConditionals, docDescription = Suppression.uniqueDocDescription),
|
||||
MarkTutorialComplete("Mark tutorial [comment] complete", UniqueTarget.Event, flags = UniqueFlag.setOfHiddenNoConditionals),
|
||||
|
||||
// Declarative Mod compatibility (see [ModCompatibility]):
|
||||
// Note there is currently no display for these, but UniqueFlag.HiddenToUsers is not set.
|
||||
|
@ -428,7 +428,7 @@ object TranslationFileWriter {
|
||||
// Promotion names are not uniques but since we did the "[unitName] ability"
|
||||
// they need the "parameters" treatment too
|
||||
// Same for victory milestones
|
||||
(field.name == "uniques" || field.name == "promotions" || field.name == "milestones")
|
||||
(field.name in fieldsToProcessParameters)
|
||||
&& (fieldValue is java.util.AbstractCollection<*>) ->
|
||||
for (item in fieldValue)
|
||||
if (item is String) submitString(item, Unique(item)) else serializeElement(item!!)
|
||||
@ -464,6 +464,8 @@ object TranslationFileWriter {
|
||||
"RuinReward.uniques", "TerrainType.name",
|
||||
"CityStateType.friendBonusUniques", "CityStateType.allyBonusUniques",
|
||||
"Era.citySound",
|
||||
"keyShortcut",
|
||||
"Event.name" // Presently not shown anywhere
|
||||
)
|
||||
|
||||
/** Specifies Enums where the name property _is_ translatable, by Class name */
|
||||
@ -476,6 +478,11 @@ object TranslationFileWriter {
|
||||
UniqueParameterType.Comment
|
||||
)
|
||||
|
||||
private val fieldsToProcessParameters = setOf(
|
||||
"uniques", "promotions", "milestones",
|
||||
"triggeredUniques", "conditions"
|
||||
)
|
||||
|
||||
private fun isFieldTypeRelevant(type: Class<*>) =
|
||||
type == String::class.java ||
|
||||
type == java.util.ArrayList::class.java ||
|
||||
|
@ -225,7 +225,7 @@ class StatsOverviewTab(
|
||||
private fun updateScoreTable() = scoreTable.apply {
|
||||
clear()
|
||||
val scoreHeader = Table()
|
||||
val scoreIcon = ImageGetter.getImage("CityStateIcons/Cultured")
|
||||
val scoreIcon = ImageGetter.getImage("OtherIcons/Score")
|
||||
scoreIcon.color = Color.FIREBRICK
|
||||
scoreHeader.add(scoreIcon).padRight(1f).size(Constants.headingFontSize.toFloat())
|
||||
scoreHeader.add("Score".toLabel(fontSize = Constants.headingFontSize))
|
||||
|
@ -10,7 +10,7 @@ enum class RankingType(
|
||||
val idForSerialization: String
|
||||
) {
|
||||
// production, gold, happiness, and culture already have icons added when the line is `tr()`anslated
|
||||
Score({ ImageGetter.getImage("CityStateIcons/Cultured").apply { color = Color.FIREBRICK } }, "S"),
|
||||
Score({ ImageGetter.getImage("OtherIcons/Score").apply { color = Color.FIREBRICK } }, "S"),
|
||||
Population({ ImageGetter.getStatIcon("Population") }, "N"),
|
||||
CropYield("Crop Yield", { ImageGetter.getStatIcon("Food") }, "C"),
|
||||
Production("P"),
|
||||
|
@ -18,26 +18,20 @@ import com.unciv.logic.civilization.PopupAlert
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.models.ruleset.EventChoice
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.fillPlaceholders
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.audio.MusicMood
|
||||
import com.unciv.ui.audio.MusicTrackChooserFlags
|
||||
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
|
||||
import com.unciv.ui.components.extensions.disable
|
||||
import com.unciv.ui.components.extensions.pad
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.components.extensions.toTextButton
|
||||
import com.unciv.ui.components.input.KeyCharAndCode
|
||||
import com.unciv.ui.components.input.KeyboardBinding
|
||||
import com.unciv.ui.components.input.keyShortcuts
|
||||
import com.unciv.ui.components.input.onActivation
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.popups.Popup
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import com.unciv.ui.screens.civilopediascreen.MarkupRenderer
|
||||
import com.unciv.ui.screens.diplomacyscreen.LeaderIntroTable
|
||||
import com.unciv.ui.screens.victoryscreen.VictoryScreen
|
||||
import java.util.EnumSet
|
||||
@ -507,18 +501,9 @@ class AlertPopup(
|
||||
/** Returns if event was triggered correctly */
|
||||
private fun addEvent(): Boolean {
|
||||
val event = gameInfo.ruleset.events[popupAlert.value] ?: return false
|
||||
|
||||
val stateForConditionals = StateForConditionals(gameInfo.currentPlayerCiv)
|
||||
val choices = event.getMatchingChoices(stateForConditionals)
|
||||
?: return false
|
||||
|
||||
if (event.text.isNotEmpty())
|
||||
addGoodSizedLabel(event.text)
|
||||
if (event.civilopediaText.isNotEmpty()) {
|
||||
add(event.renderCivilopediaText(stageWidth * 0.5f, ::openCivilopedia)).row()
|
||||
}
|
||||
|
||||
for (choice in choices) addChoice(choice)
|
||||
val render = RenderEvent(event, worldScreen) { close() }
|
||||
if (!render.isValid) return false
|
||||
add(render).pad(0f).row()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -529,30 +514,4 @@ class AlertPopup(
|
||||
worldScreen.shouldUpdate = true
|
||||
super.close()
|
||||
}
|
||||
|
||||
private fun addChoice(choice: EventChoice) {
|
||||
addSeparator()
|
||||
|
||||
val button = choice.text.toTextButton()
|
||||
button.onActivation {
|
||||
close()
|
||||
choice.triggerChoice(gameInfo.currentPlayerCiv)
|
||||
}
|
||||
val key = KeyCharAndCode.parse(choice.keyShortcut)
|
||||
if (key != KeyCharAndCode.UNKNOWN) {
|
||||
button.keyShortcuts.add(key)
|
||||
button.addTooltip(key)
|
||||
}
|
||||
add(button).row()
|
||||
|
||||
val lines = (
|
||||
choice.civilopediaText.asSequence()
|
||||
+ choice.triggeredUniqueObjects.asSequence()
|
||||
.filterNot { it.isHiddenToUsers() }
|
||||
.map { FormattedLine(it) }
|
||||
).asIterable()
|
||||
add(MarkupRenderer.render(lines, stageWidth * 0.5f, linkAction = ::openCivilopedia)).row()
|
||||
}
|
||||
|
||||
private fun openCivilopedia(link: String) = worldScreen.openCivilopedia(link)
|
||||
}
|
||||
|
78
core/src/com/unciv/ui/screens/worldscreen/RenderEvent.kt
Normal file
@ -0,0 +1,78 @@
|
||||
package com.unciv.ui.screens.worldscreen
|
||||
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.models.ruleset.Event
|
||||
import com.unciv.models.ruleset.EventChoice
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
|
||||
import com.unciv.ui.components.extensions.addSeparator
|
||||
import com.unciv.ui.components.extensions.toTextButton
|
||||
import com.unciv.ui.components.input.KeyCharAndCode
|
||||
import com.unciv.ui.components.input.keyShortcuts
|
||||
import com.unciv.ui.components.input.onActivation
|
||||
import com.unciv.ui.components.widgets.WrappableLabel
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import com.unciv.ui.screens.civilopediascreen.MarkupRenderer
|
||||
|
||||
/** Renders an [Event] for [AlertPopup] or a floating tutorial task on [WorldScreen] */
|
||||
class RenderEvent(
|
||||
event: Event,
|
||||
val worldScreen: WorldScreen,
|
||||
val onChoice: (EventChoice) -> Unit
|
||||
) : Table() {
|
||||
private val gameInfo get() = worldScreen.gameInfo
|
||||
private val stageWidth get() = worldScreen.stage.width
|
||||
|
||||
val isValid: Boolean
|
||||
|
||||
//todo check generated translations
|
||||
|
||||
init {
|
||||
defaults().fillX().center().pad(5f)
|
||||
|
||||
val stateForConditionals = StateForConditionals(gameInfo.currentPlayerCiv)
|
||||
val choices = event.getMatchingChoices(stateForConditionals)
|
||||
isValid = choices != null
|
||||
if (isValid) {
|
||||
if (event.text.isNotEmpty()) {
|
||||
add(WrappableLabel(event.text, stageWidth * 0.5f).apply {
|
||||
wrap = true
|
||||
setAlignment(Align.center)
|
||||
optimizePrefWidth()
|
||||
}).row()
|
||||
}
|
||||
if (event.civilopediaText.isNotEmpty()) {
|
||||
add(event.renderCivilopediaText(stageWidth * 0.5f, ::openCivilopedia)).row()
|
||||
}
|
||||
|
||||
for (choice in choices!!) addChoice(choice)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addChoice(choice: EventChoice) {
|
||||
addSeparator()
|
||||
|
||||
val button = choice.text.toTextButton()
|
||||
button.onActivation {
|
||||
onChoice(choice)
|
||||
choice.triggerChoice(gameInfo.currentPlayerCiv)
|
||||
}
|
||||
val key = KeyCharAndCode.parse(choice.keyShortcut)
|
||||
if (key != KeyCharAndCode.UNKNOWN) {
|
||||
button.keyShortcuts.add(key)
|
||||
button.addTooltip(key)
|
||||
}
|
||||
add(button).row()
|
||||
|
||||
val lines = (
|
||||
choice.civilopediaText.asSequence()
|
||||
+ choice.triggeredUniqueObjects.asSequence()
|
||||
.filterNot { it.isHiddenToUsers() }
|
||||
.map { FormattedLine(it) }
|
||||
).asIterable()
|
||||
add(MarkupRenderer.render(lines, stageWidth * 0.5f, linkAction = ::openCivilopedia)).row()
|
||||
}
|
||||
|
||||
private fun openCivilopedia(link: String) = worldScreen.openCivilopedia(link)
|
||||
}
|
@ -21,11 +21,12 @@ import com.unciv.logic.multiplayer.storage.MultiplayerAuthException
|
||||
import com.unciv.logic.trade.TradeEvaluation
|
||||
import com.unciv.models.TutorialTrigger
|
||||
import com.unciv.models.metadata.GameSetupInfo
|
||||
import com.unciv.models.ruleset.Event
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.components.extensions.centerX
|
||||
import com.unciv.ui.components.extensions.darken
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.components.input.KeyShortcutDispatcherVeto
|
||||
import com.unciv.ui.components.input.KeyboardBinding
|
||||
import com.unciv.ui.components.input.KeyboardPanningListener
|
||||
@ -122,6 +123,7 @@ class WorldScreen(
|
||||
private val tutorialTaskTable = Table().apply {
|
||||
background = skinStrings.getUiBackground("WorldScreen/TutorialTaskTable", tintColor = skinStrings.skinConfig.baseColor.darken(0.5f))
|
||||
}
|
||||
private var tutorialTaskTableHash = 0
|
||||
|
||||
private var nextTurnUpdateJob: Job? = null
|
||||
|
||||
@ -149,10 +151,10 @@ class WorldScreen(
|
||||
stage.scrollFocus = mapHolder
|
||||
stage.addActor(notificationsScroll) // very low in z-order, so we're free to let it extend _below_ tile info and minimap if we want
|
||||
stage.addActor(minimapWrapper)
|
||||
stage.addActor(tutorialTaskTable) // behind topBar!
|
||||
stage.addActor(topBar)
|
||||
stage.addActor(statusButtons)
|
||||
stage.addActor(techPolicyAndDiplomacy)
|
||||
stage.addActor(tutorialTaskTable)
|
||||
|
||||
stage.addActor(zoomController)
|
||||
zoomController.isVisible = UncivGame.Current.settings.showZoomButtons
|
||||
@ -401,6 +403,8 @@ class WorldScreen(
|
||||
else mapHolder.updateTiles(viewingCiv)
|
||||
|
||||
topBar.update(selectedCiv)
|
||||
if (tutorialTaskTable.isVisible)
|
||||
tutorialTaskTable.y = topBar.getYForTutorialTask() - tutorialTaskTable.height
|
||||
|
||||
if (techPolicyAndDiplomacy.update())
|
||||
displayTutorial(TutorialTrigger.OtherCivEncountered)
|
||||
@ -450,50 +454,16 @@ class WorldScreen(
|
||||
zoomController.setPosition(stage.width - posZoomFromRight - 10f, 10f, Align.bottomRight)
|
||||
}
|
||||
|
||||
private fun getCurrentTutorialTask(): String {
|
||||
val completedTasks = game.settings.tutorialTasksCompleted
|
||||
if (!completedTasks.contains("Move unit"))
|
||||
return "Move a unit!\nClick on a unit > Click on a destination > Click the arrow popup"
|
||||
if (!completedTasks.contains("Found city"))
|
||||
return "Found a city!\nSelect the Settler (flag unit) > Click on 'Found city' (bottom-left corner)"
|
||||
if (!completedTasks.contains("Enter city screen"))
|
||||
return "Enter the city screen!\nClick the city button twice"
|
||||
if (!completedTasks.contains("Pick technology"))
|
||||
return "Pick a technology to research!\nClick on the tech button (greenish, top left) > " +
|
||||
"\n select technology > click 'Research' (bottom right)"
|
||||
if (!completedTasks.contains("Pick construction"))
|
||||
return "Pick a construction!\nEnter city screen > Click on a unit or building (bottom left side) >" +
|
||||
" \n click 'add to queue'"
|
||||
if (!completedTasks.contains("Pass a turn"))
|
||||
return "Pass a turn!\nCycle through units with 'Next unit' > Click 'Next turn'"
|
||||
if (!completedTasks.contains("Reassign worked tiles"))
|
||||
return "Reassign worked tiles!\nEnter city screen > click the assigned (green) tile to unassign > " +
|
||||
"\n click an unassigned tile to assign population"
|
||||
if (!completedTasks.contains("Meet another civilization"))
|
||||
return "Meet another civilization!\nExplore the map until you encounter another civilization!"
|
||||
if (!completedTasks.contains("Open the options table"))
|
||||
return "Open the options table!\nClick the menu button (top left) > click 'Options'"
|
||||
if (!completedTasks.contains("Construct an improvement"))
|
||||
return "Construct an improvement!\nConstruct a Worker unit > Move to a Plains or Grassland tile > " +
|
||||
"\n Click 'Construct improvement' (above the unit table, bottom left)" +
|
||||
"\n > Choose the farm > \n Leave the worker there until it's finished"
|
||||
if (!completedTasks.contains("Create a trade route")
|
||||
&& viewingCiv.cache.citiesConnectedToCapitalToMediums.any { it.key.civ == viewingCiv })
|
||||
game.settings.addCompletedTutorialTask("Create a trade route")
|
||||
if (viewingCiv.cities.size > 1 && !completedTasks.contains("Create a trade route"))
|
||||
return "Create a trade route!\nConstruct roads between your capital and another city" +
|
||||
"\nOr, automate your worker and let him get to that eventually"
|
||||
if (viewingCiv.isAtWar() && !completedTasks.contains("Conquer a city"))
|
||||
return "Conquer a city!\nBring an enemy city down to low health > " +
|
||||
"\nEnter the city with a melee unit"
|
||||
if (viewingCiv.units.getCivUnits().any { it.baseUnit.movesLikeAirUnits() } && !completedTasks.contains("Move an air unit"))
|
||||
return "Move an air unit!\nSelect an air unit > select another city within range > " +
|
||||
"\nMove the unit to the other city"
|
||||
if (!completedTasks.contains("See your stats breakdown"))
|
||||
return "See your stats breakdown!\nEnter the Overview screen (top right corner) >" +
|
||||
"\nClick on 'Stats'"
|
||||
|
||||
return ""
|
||||
private fun getCurrentTutorialTask(): Event? {
|
||||
if (!game.settings.tutorialTasksCompleted.contains("Create a trade route")) {
|
||||
if (viewingCiv.cache.citiesConnectedToCapitalToMediums.any { it.key.civ == viewingCiv })
|
||||
game.settings.addCompletedTutorialTask("Create a trade route")
|
||||
}
|
||||
val stateForConditionals = StateForConditionals(viewingCiv)
|
||||
return gameInfo.ruleset.events.values.firstOrNull {
|
||||
it.presentation == Event.Presentation.Floating &&
|
||||
it.isAvailable(stateForConditionals)
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayTutorialsOnUpdate() {
|
||||
@ -524,27 +494,38 @@ class WorldScreen(
|
||||
}
|
||||
|
||||
private fun displayTutorialTaskOnUpdate() {
|
||||
tutorialTaskTable.clear()
|
||||
val tutorialTask = getCurrentTutorialTask()
|
||||
if (tutorialTask == "" || !game.settings.showTutorials || viewingCiv.isDefeated()) {
|
||||
fun setInvisible() {
|
||||
tutorialTaskTable.isVisible = false
|
||||
return
|
||||
tutorialTaskTable.clear()
|
||||
tutorialTaskTableHash = 0
|
||||
}
|
||||
if (!game.settings.showTutorials || viewingCiv.isDefeated()) return setInvisible()
|
||||
val tutorialTask = getCurrentTutorialTask() ?: return setInvisible()
|
||||
|
||||
tutorialTaskTable.isVisible = true
|
||||
if (!UncivGame.Current.isTutorialTaskCollapsed) {
|
||||
tutorialTaskTable.add(tutorialTask.toLabel()
|
||||
.apply { setAlignment(Align.center) }).pad(10f)
|
||||
val hash = tutorialTask.hashCode() // Default implementation is OK - we see the same instance or not
|
||||
if (hash != tutorialTaskTableHash) {
|
||||
val renderEvent = RenderEvent(tutorialTask, this) {
|
||||
shouldUpdate = true
|
||||
}
|
||||
if (!renderEvent.isValid) return setInvisible()
|
||||
tutorialTaskTable.clear()
|
||||
tutorialTaskTable.add(renderEvent).pad(10f)
|
||||
tutorialTaskTableHash = hash
|
||||
}
|
||||
} else {
|
||||
tutorialTaskTable.add(ImageGetter.getImage("CityStateIcons/Cultured").apply { setSize(30f,30f) }).pad(5f)
|
||||
tutorialTaskTable.clear()
|
||||
tutorialTaskTable.add(ImageGetter.getImage("OtherIcons/HiddenTutorialTask").apply { setSize(30f,30f) }).pad(5f)
|
||||
tutorialTaskTableHash = 0
|
||||
}
|
||||
tutorialTaskTable.pack()
|
||||
tutorialTaskTable.centerX(stage)
|
||||
tutorialTaskTable.y = topBar.y - tutorialTaskTable.height
|
||||
tutorialTaskTable.y = topBar.getYForTutorialTask() - tutorialTaskTable.height
|
||||
tutorialTaskTable.onClick {
|
||||
UncivGame.Current.isTutorialTaskCollapsed = !UncivGame.Current.isTutorialTaskCollapsed
|
||||
displayTutorialTaskOnUpdate()
|
||||
}
|
||||
tutorialTaskTable.isVisible = true
|
||||
}
|
||||
|
||||
private fun updateSelectedCiv() {
|
||||
|
@ -67,6 +67,7 @@ class WorldScreenTopBar(internal val worldScreen: WorldScreen) : Table() {
|
||||
private val overviewButton = OverviewAndSupplyTable(worldScreen)
|
||||
private val leftFiller: BackgroundActor
|
||||
private val rightFiller: BackgroundActor
|
||||
private var baseHeight = 0f
|
||||
|
||||
companion object {
|
||||
/** When the "fillers" are used, this is added to the required height, alleviating the "gap" problem a little. */
|
||||
@ -100,6 +101,8 @@ class WorldScreenTopBar(internal val worldScreen: WorldScreen) : Table() {
|
||||
setLayoutEnabled(true)
|
||||
}
|
||||
|
||||
internal fun getYForTutorialTask(): Float = y + height - baseHeight
|
||||
|
||||
/** Performs the layout tricks mentioned in the class Kdoc */
|
||||
private fun updateLayout() {
|
||||
val targetWidth = stage.width
|
||||
@ -122,7 +125,7 @@ class WorldScreenTopBar(internal val worldScreen: WorldScreen) : Table() {
|
||||
add(resourceTable).colspan(3).growX().width(targetWidth).row()
|
||||
layout() // force rowHeight calculation - validate is not enough - Table quirks
|
||||
val statsRowHeight = getRowHeight(0)
|
||||
val baseHeight = statsRowHeight + getRowHeight(1)
|
||||
baseHeight = statsRowHeight + getRowHeight(1)
|
||||
|
||||
fun addFillers(fillerHeight: Float) {
|
||||
add(leftFiller).size(selectedCivWidth, fillerHeight + gapFillingExtraHeight)
|
||||
|
@ -745,6 +745,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
|
||||
- [down](https://thenounproject.com/icon/down-39378/) by Cengiz SARI for Show unit destination
|
||||
- [Cat](https://thenounproject.com/icon/cat-158942/) by Josi for Politics overview diagram legend
|
||||
- [Bell](https://thenounproject.com/icon/bell-2054409) by Lyhn, transparency modified, for Notifications (overview, unhide button)
|
||||
- [Galileo Donatello](https://en.wikipedia.org/wiki/File:Galileo_Donato.jpg) for the "Meet another civilization" tutorial: Public domain
|
||||
|
||||
### Main menu
|
||||
|
||||
|
@ -287,8 +287,10 @@ Allowed values are:
|
||||
Indicates *something that can be counted*, used both for comparisons and for multiplying uniques
|
||||
|
||||
Allowed values:
|
||||
- `year`
|
||||
- `year`, `turns`
|
||||
- `Cities`, `Units`, `Air units` - these count your total number
|
||||
- Unit name (counts your existing units)
|
||||
- `<unit type> units` (e.g. `Mounted units`) - counts your units by their type (this is not a filter, use the unitType verbatim)
|
||||
- Building name (counts your existing buildings)
|
||||
- Stat name - gets the stat *reserve*, not the amount per turn (can be city stats or civilization stats, depending on where the unique is used)
|
||||
- Resource name (can be city stats or civilization stats, depending on where the unique is used)
|
||||
|
@ -1007,11 +1007,11 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
|
||||
??? example "Only available"
|
||||
Meant to be used together with conditionals, like "Only available <after adopting [policy]> <while the empire is happy>". Only allows Building when ALL conditionals are met. Will also block Upgrade and Transform actions. See also CanOnlyBeBuiltWhen
|
||||
Applicable to: Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, Promotion, Improvement, Ruins
|
||||
Applicable to: Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, Promotion, Improvement, Ruins, Event
|
||||
|
||||
??? example "Unavailable"
|
||||
Meant to be used together with conditionals, like "Unavailable <after generating a Great Prophet>".
|
||||
Applicable to: Tech, Policy, Building, Unit, Promotion, Improvement, Ruins
|
||||
Applicable to: Tech, Policy, Building, Unit, Promotion, Improvement, Ruins, Event
|
||||
|
||||
??? example "Cannot be hurried"
|
||||
Applicable to: Tech, Building
|
||||
@ -1915,6 +1915,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
|
||||
Applicable to: ModOptions
|
||||
|
||||
## Event uniques
|
||||
??? example "Mark tutorial [comment] complete"
|
||||
Example: "Mark tutorial [comment] complete"
|
||||
|
||||
Applicable to: Event
|
||||
|
||||
## Conditional uniques
|
||||
!!! note ""
|
||||
|
||||
@ -1945,6 +1951,14 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
|
||||
Applicable to: Conditional
|
||||
|
||||
??? example "<if tutorials are enabled>"
|
||||
Applicable to: Conditional
|
||||
|
||||
??? example "<if tutorial [comment] is completed>"
|
||||
Example: "<if tutorial [comment] is completed>"
|
||||
|
||||
Applicable to: Conditional
|
||||
|
||||
??? example "<for [civFilter]>"
|
||||
Example: "<for [City-States]>"
|
||||
|
||||
|