Notification for "Policy branch unlocked" clickable (#10655)

* Ruleset-validate some Unique amounts to be positive

* Add TechAction for tech gained from Ruins

* Allow PolicyPickerScreen to highlight a Policy

* Add the 'PolicyAction' NotificationAction

* Use the new PolicyAction
This commit is contained in:
SomeTroglodyte
2023-12-03 21:12:26 +01:00
committed by GitHub
parent 1f47fff05d
commit 5061b29197
8 changed files with 198 additions and 153 deletions

View File

@ -12,6 +12,7 @@ import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
import com.unciv.ui.screens.diplomacyscreen.DiplomacyScreen import com.unciv.ui.screens.diplomacyscreen.DiplomacyScreen
import com.unciv.ui.screens.overviewscreen.EmpireOverviewCategories import com.unciv.ui.screens.overviewscreen.EmpireOverviewCategories
import com.unciv.ui.screens.overviewscreen.EmpireOverviewScreen import com.unciv.ui.screens.overviewscreen.EmpireOverviewScreen
import com.unciv.ui.screens.pickerscreens.PolicyPickerScreen
import com.unciv.ui.screens.pickerscreens.PromotionPickerScreen import com.unciv.ui.screens.pickerscreens.PromotionPickerScreen
import com.unciv.ui.screens.pickerscreens.TechPickerScreen import com.unciv.ui.screens.pickerscreens.TechPickerScreen
import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.ui.screens.worldscreen.WorldScreen
@ -146,6 +147,15 @@ class OverviewAction(
} }
} }
/** Open policy picker, optionally preselecting [select] (how or if at all that works for branches is [PolicyPickerScreen]'s business) */
class PolicyAction(
private val select: String? = null
) : NotificationAction {
override fun execute(worldScreen: WorldScreen) {
worldScreen.game.pushScreen(PolicyPickerScreen(worldScreen.selectedCiv, worldScreen.canChangeState, select))
}
}
@Suppress("PrivatePropertyName") // These names *must* match their class name, see below @Suppress("PrivatePropertyName") // These names *must* match their class name, see below
internal class NotificationActionsDeserializer { internal class NotificationActionsDeserializer {
/* This exists as trick to leverage readFields for Json deserialization. /* This exists as trick to leverage readFields for Json deserialization.
@ -167,12 +177,13 @@ internal class NotificationActionsDeserializer {
private val CivilopediaAction: CivilopediaAction? = null private val CivilopediaAction: CivilopediaAction? = null
private val PromoteUnitAction: PromoteUnitAction? = null private val PromoteUnitAction: PromoteUnitAction? = null
private val OverviewAction: OverviewAction? = null private val OverviewAction: OverviewAction? = null
private val PolicyAction: PolicyAction? = null
fun read(json: Json, jsonData: JsonValue): List<NotificationAction> { fun read(json: Json, jsonData: JsonValue): List<NotificationAction> {
json.readFields(this, jsonData) json.readFields(this, jsonData)
return listOfNotNull( return listOfNotNull(
LocationAction, TechAction, CityAction, DiplomacyAction, MayaLongCountAction, LocationAction, TechAction, CityAction, DiplomacyAction, MayaLongCountAction,
MapUnitAction, CivilopediaAction, PromoteUnitAction, OverviewAction MapUnitAction, CivilopediaAction, PromoteUnitAction, OverviewAction, PolicyAction
) )
} }
} }

View File

@ -203,6 +203,7 @@ class PolicyManager : IsPartOfGameInfoSerialization {
triggerGlobalAlerts(policy, unique.params[0]) triggerGlobalAlerts(policy, unique.params[0])
} }
//todo Can this be mapped downstream to a PolicyAction:NotificationAction?
val triggerNotificationText = "due to adopting [${policy.name}]" val triggerNotificationText = "due to adopting [${policy.name}]"
for (unique in policy.uniqueObjects) for (unique in policy.uniqueObjects)
if (!unique.hasTriggerConditional()) if (!unique.hasTriggerConditional())

View File

@ -10,6 +10,7 @@ import com.unciv.logic.civilization.MayaLongCountAction
import com.unciv.logic.civilization.NotificationCategory import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.NotificationIcon import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PlayerType import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.civilization.PolicyAction
import com.unciv.logic.civilization.PopupAlert import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.TechAction import com.unciv.logic.civilization.TechAction
import com.unciv.logic.map.MapSize import com.unciv.logic.map.MapSize
@ -417,6 +418,7 @@ class TechManager : IsPartOfGameInfoSerialization {
if (!civInfo.isSpectator()) if (!civInfo.isSpectator())
civInfo.addNotification( civInfo.addNotification(
"[${policyBranch.name}] policy branch unlocked!", "[${policyBranch.name}] policy branch unlocked!",
PolicyAction(policyBranch.name),
NotificationCategory.General, NotificationCategory.General,
NotificationIcon.Culture NotificationIcon.Culture
) )

View File

@ -49,6 +49,16 @@ enum class UniqueParameterType(
} }
}, },
PositiveNumber("positiveAmount", "3", "This indicates a positive whole number, larger than zero, a '+' sign is optional") {
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
UniqueType.UniqueParameterErrorSeverity? {
val amount = parameterText.toIntOrNull()
?: return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant
if (amount <= 0) return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant
return null
}
},
Fraction("fraction", docExample = "0.5", "Indicates a fractional number, which can be negative") { Fraction("fraction", docExample = "0.5", "Indicates a fractional number, which can be negative") {
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? { override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? {
return if (parameterText.toFloatOrNull () == null) UniqueType.UniqueParameterErrorSeverity.RulesetInvariant return if (parameterText.toFloatOrNull () == null) UniqueType.UniqueParameterErrorSeverity.RulesetInvariant

View File

@ -9,8 +9,11 @@ import com.unciv.logic.civilization.Civilization
import com.unciv.logic.civilization.LocationAction import com.unciv.logic.civilization.LocationAction
import com.unciv.logic.civilization.MapUnitAction import com.unciv.logic.civilization.MapUnitAction
import com.unciv.logic.civilization.MayaLongCountAction import com.unciv.logic.civilization.MayaLongCountAction
import com.unciv.logic.civilization.NotificationAction
import com.unciv.logic.civilization.NotificationCategory import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.NotificationIcon import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PolicyAction
import com.unciv.logic.civilization.TechAction
import com.unciv.logic.civilization.managers.ReligionState import com.unciv.logic.civilization.managers.ReligionState
import com.unciv.logic.map.mapunit.MapUnit import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
@ -20,8 +23,8 @@ import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.models.translations.fillPlaceholders import com.unciv.models.translations.fillPlaceholders
import com.unciv.models.translations.hasPlaceholderParameters import com.unciv.models.translations.hasPlaceholderParameters
import com.unciv.ui.components.extensions.addToMapOfSets
import com.unciv.ui.components.MayaCalendar import com.unciv.ui.components.MayaCalendar
import com.unciv.ui.components.extensions.addToMapOfSets
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.random.Random import kotlin.random.Random
@ -189,7 +192,7 @@ object UniqueTriggerActivation {
"You gain the [$policyName] Policy") "You gain the [$policyName] Policy")
?: return true ?: return true
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Culture) civInfo.addNotification(notificationText, PolicyAction(policyName), NotificationCategory.General, NotificationIcon.Culture)
return true return true
} }
UniqueType.OneTimeEnterGoldenAge, UniqueType.OneTimeEnterGoldenAgeTurns -> { UniqueType.OneTimeEnterGoldenAge, UniqueType.OneTimeEnterGoldenAgeTurns -> {
@ -311,7 +314,11 @@ object UniqueTriggerActivation {
notification.fillPlaceholders(*(techsToResearch.map { it.name } notification.fillPlaceholders(*(techsToResearch.map { it.name }
.toTypedArray())) .toTypedArray()))
else notification else notification
civInfo.addNotification(notificationText, LocationAction(tile?.position), // Notification click for first tech only, supporting multiple adds little value.
// Relies on RulesetValidator catching <= 0!
val notificationActions: Sequence<NotificationAction> =
LocationAction(tile?.position) + TechAction(techsToResearch.first().name)
civInfo.addNotification(notificationText, notificationActions,
NotificationCategory.General, NotificationIcon.Science) NotificationCategory.General, NotificationIcon.Science)
} }
@ -326,7 +333,7 @@ object UniqueTriggerActivation {
"You have discovered the secrets of [$techName]") "You have discovered the secrets of [$techName]")
?: return true ?: return true
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Science) civInfo.addNotification(notificationText, TechAction(techName), NotificationCategory.General, NotificationIcon.Science)
return true return true
} }

View File

@ -696,26 +696,26 @@ enum class UniqueType(
OneTimeFreeUnit("Free [unit] appears", UniqueTarget.Triggerable), // used in Policies, Buildings OneTimeFreeUnit("Free [unit] appears", UniqueTarget.Triggerable), // used in Policies, Buildings
OneTimeAmountFreeUnits("[amount] free [unit] units appear", UniqueTarget.Triggerable), // used in Buildings OneTimeAmountFreeUnits("[positiveAmount] free [unit] units appear", UniqueTarget.Triggerable), // used in Buildings
OneTimeFreeUnitRuins("Free [unit] found in the ruins", UniqueTarget.Ruins), // Differs from "Free [] appears" in that it spawns near the ruins instead of in a city OneTimeFreeUnitRuins("Free [unit] found in the ruins", UniqueTarget.Ruins), // Differs from "Free [] appears" in that it spawns near the ruins instead of in a city
OneTimeFreePolicy("Free Social Policy", UniqueTarget.Triggerable), // used in Buildings OneTimeFreePolicy("Free Social Policy", UniqueTarget.Triggerable), // used in Buildings
OneTimeAmountFreePolicies("[amount] Free Social Policies", UniqueTarget.Triggerable), // Not used in Vanilla OneTimeAmountFreePolicies("[positiveAmount] Free Social Policies", UniqueTarget.Triggerable), // Not used in Vanilla
OneTimeEnterGoldenAge("Empire enters golden age", UniqueTarget.Triggerable), // used in Policies, Buildings OneTimeEnterGoldenAge("Empire enters golden age", UniqueTarget.Triggerable), // used in Policies, Buildings
OneTimeEnterGoldenAgeTurns("Empire enters a [amount]-turn Golden Age", UniqueTarget.Triggerable), OneTimeEnterGoldenAgeTurns("Empire enters a [positiveAmount]-turn Golden Age", UniqueTarget.Triggerable),
OneTimeFreeGreatPerson("Free Great Person", UniqueTarget.Triggerable), // used in Policies, Buildings OneTimeFreeGreatPerson("Free Great Person", UniqueTarget.Triggerable), // used in Policies, Buildings
OneTimeGainPopulation("[amount] population [cityFilter]", UniqueTarget.Triggerable), // used in CN tower OneTimeGainPopulation("[amount] population [cityFilter]", UniqueTarget.Triggerable), // used in CN tower
OneTimeGainPopulationRandomCity("[amount] population in a random city", UniqueTarget.Triggerable), OneTimeGainPopulationRandomCity("[amount] population in a random city", UniqueTarget.Triggerable),
OneTimeDiscoverTech("Discover [tech]", UniqueTarget.Triggerable), OneTimeDiscoverTech("Discover [tech]", UniqueTarget.Triggerable),
OneTimeAdoptPolicy("Adopt [policy]", UniqueTarget.Triggerable), OneTimeAdoptPolicy("Adopt [policy]", UniqueTarget.Triggerable),
OneTimeFreeTech("Free Technology", UniqueTarget.Triggerable), // used in Buildings OneTimeFreeTech("Free Technology", UniqueTarget.Triggerable), // used in Buildings
OneTimeAmountFreeTechs("[amount] Free Technologies", UniqueTarget.Triggerable), // used in Policy OneTimeAmountFreeTechs("[positiveAmount] Free Technologies", UniqueTarget.Triggerable), // used in Policy
OneTimeFreeTechRuins("[amount] free random researchable Tech(s) from the [era]", UniqueTarget.Triggerable), OneTimeFreeTechRuins("[positiveAmount] free random researchable Tech(s) from the [era]", UniqueTarget.Triggerable),
OneTimeRevealEntireMap("Reveals the entire map", UniqueTarget.Triggerable), // used in tech OneTimeRevealEntireMap("Reveals the entire map", UniqueTarget.Triggerable), // used in tech
OneTimeFreeBelief("Gain a free [beliefType] belief", UniqueTarget.Triggerable), OneTimeFreeBelief("Gain a free [beliefType] belief", UniqueTarget.Triggerable),
OneTimeTriggerVoting("Triggers voting for the Diplomatic Victory", UniqueTarget.Triggerable), // used in Building OneTimeTriggerVoting("Triggers voting for the Diplomatic Victory", UniqueTarget.Triggerable), // used in Building
OneTimeConsumeResources("Instantly consumes [amount] [stockpiledResource]", UniqueTarget.Triggerable), OneTimeConsumeResources("Instantly consumes [positiveAmount] [stockpiledResource]", UniqueTarget.Triggerable),
OneTimeProvideResources("Instantly provides [amount] [stockpiledResource]", UniqueTarget.Triggerable), OneTimeProvideResources("Instantly provides [positiveAmount] [stockpiledResource]", UniqueTarget.Triggerable),
OneTimeGainStat("Gain [amount] [stat]", UniqueTarget.Triggerable), OneTimeGainStat("Gain [amount] [stat]", UniqueTarget.Triggerable),
OneTimeGainStatSpeed("Gain [amount] [stat] (modified by game speed)", UniqueTarget.Triggerable), OneTimeGainStatSpeed("Gain [amount] [stat] (modified by game speed)", UniqueTarget.Triggerable),
@ -724,8 +724,8 @@ enum class UniqueType(
OneTimeGainProphet("Gain enough Faith for [amount]% of a Great Prophet", UniqueTarget.Triggerable), OneTimeGainProphet("Gain enough Faith for [amount]% of a Great Prophet", UniqueTarget.Triggerable),
// todo: The "up to [All]" used in vanilla json is not nice to read. Split? // todo: The "up to [All]" used in vanilla json is not nice to read. Split?
// Or just reword it without the 'up to', so it reads "Reveal [amount/'all'] [tileFilter] tiles within [amount] tiles" // Or just reword it without the 'up to', so it reads "Reveal [amount/'all'] [tileFilter] tiles within [amount] tiles"
OneTimeRevealSpecificMapTiles("Reveal up to [amount/'all'] [tileFilter] within a [amount] tile radius", UniqueTarget.Triggerable), OneTimeRevealSpecificMapTiles("Reveal up to [positiveAmount/'all'] [tileFilter] within a [amount] tile radius", UniqueTarget.Triggerable),
OneTimeRevealCrudeMap("From a randomly chosen tile [amount] tiles away from the ruins, reveal tiles up to [amount] tiles away with [amount]% chance", UniqueTarget.Ruins), OneTimeRevealCrudeMap("From a randomly chosen tile [positiveAmount] tiles away from the ruins, reveal tiles up to [positiveAmount] tiles away with [positiveAmount]% chance", UniqueTarget.Ruins),
OneTimeGlobalAlert("Triggers the following global alert: [comment]", UniqueTarget.Triggerable), // used in Policy OneTimeGlobalAlert("Triggers the following global alert: [comment]", UniqueTarget.Triggerable), // used in Policy
OneTimeGlobalSpiesWhenEnteringEra("Every major Civilization gains a spy once a civilization enters this era", UniqueTarget.Era), OneTimeGlobalSpiesWhenEnteringEra("Every major Civilization gains a spy once a civilization enters this era", UniqueTarget.Era),
@ -738,8 +738,8 @@ enum class UniqueType(
FreePromotion("This Promotion is free", UniqueTarget.Promotion), FreePromotion("This Promotion is free", UniqueTarget.Promotion),
UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Triggerable), // Not used in Vanilla UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Triggerable), // Not used in Vanilla
FreeStatBuildings("Provides the cheapest [stat] building in your first [amount] cities for free", UniqueTarget.Triggerable), // used in Policy FreeStatBuildings("Provides the cheapest [stat] building in your first [positiveAmount] cities for free", UniqueTarget.Triggerable), // used in Policy
FreeSpecificBuildings("Provides a [buildingName] in your first [amount] cities for free", UniqueTarget.Triggerable), // used in Policy FreeSpecificBuildings("Provides a [buildingName] in your first [positiveAmount] cities for free", UniqueTarget.Triggerable), // used in Policy
//endregion //endregion

View File

@ -53,7 +53,7 @@ private object PolicyColors {
val branchAdopted = colorFromRGB(100, 90, 10).darken(0.5f) val branchAdopted = colorFromRGB(100, 90, 10).darken(0.5f)
} }
fun Policy.isPickable(viewingCiv: Civilization, canChangeState: Boolean) : Boolean { private fun Policy.isPickable(viewingCiv: Civilization, canChangeState: Boolean) : Boolean {
if (!viewingCiv.isCurrentPlayer() if (!viewingCiv.isCurrentPlayer()
|| !canChangeState || !canChangeState
|| viewingCiv.isDefeated() || viewingCiv.isDefeated()
@ -66,7 +66,7 @@ fun Policy.isPickable(viewingCiv: Civilization, canChangeState: Boolean) : Boole
return true return true
} }
class PolicyButton(viewingCiv: Civilization, canChangeState: Boolean, val policy: Policy, size: Float = 30f) : BorderedTable( private class PolicyButton(viewingCiv: Civilization, canChangeState: Boolean, val policy: Policy, size: Float = 30f) : BorderedTable(
path = "PolicyScreen/PolicyButton", path = "PolicyScreen/PolicyButton",
defaultBgBorder = BaseScreen.skinStrings.roundedEdgeRectangleSmallShape, defaultBgBorder = BaseScreen.skinStrings.roundedEdgeRectangleSmallShape,
defaultBgShape = BaseScreen.skinStrings.roundedEdgeRectangleSmallShape, defaultBgShape = BaseScreen.skinStrings.roundedEdgeRectangleSmallShape,
@ -131,7 +131,11 @@ class PolicyButton(viewingCiv: Civilization, canChangeState: Boolean, val policy
} }
class PolicyPickerScreen(val viewingCiv: Civilization, val canChangeState: Boolean) class PolicyPickerScreen(
val viewingCiv: Civilization,
val canChangeState: Boolean,
select: String? = null
)
: PickerScreen(), RecreateOnResize { : PickerScreen(), RecreateOnResize {
object Sizes { object Sizes {
@ -142,10 +146,12 @@ class PolicyPickerScreen(val viewingCiv: Civilization, val canChangeState: Boole
const val iconSize = 50f const val iconSize = 50f
} }
private var policyNameToButton = HashMap<String, PolicyButton>() private val policyNameToButton = HashMap<String, PolicyButton>()
private var selectedPolicyButton: PolicyButton? = null private var selectedPolicyButton: PolicyButton? = null
init { init {
val branchToGroup = HashMap<String, BranchGroup>()
val policies = viewingCiv.policies val policies = viewingCiv.policies
displayTutorial(TutorialTrigger.CultureAndPolicies) displayTutorial(TutorialTrigger.CultureAndPolicies)
@ -204,8 +210,9 @@ class PolicyPickerScreen(val viewingCiv: Civilization, val canChangeState: Boole
wrapper.add().expand() wrapper.add().expand()
} else { } else {
val branch = branches.values.elementAt(r) val branch = branches.values.elementAt(r)
val branchGroup = getBranchGroup(branch) val branchGroup = BranchGroup(branch)
wrapper.add(branchGroup).growY().growX() wrapper.add(branchGroup).growY().growX()
branchToGroup[branch.name] = branchGroup
} }
} }
topTable.add(wrapper).pad(5f,10f) topTable.add(wrapper).pad(5f,10f)
@ -222,6 +229,11 @@ class PolicyPickerScreen(val viewingCiv: Civilization, val canChangeState: Boole
scrollPane.scrollX = hScroll scrollPane.scrollX = hScroll
} }
scrollPane.updateVisualScroll() scrollPane.updateVisualScroll()
when(select) {
in branches -> branchToGroup[select]?.toggle()
in policyNameToButton -> policyNameToButton[select]!!.also { pickPolicy(it) }
}
} }
private fun pickPolicy(button: PolicyButton) { private fun pickPolicy(button: PolicyButton) {
@ -252,133 +264,136 @@ class PolicyPickerScreen(val viewingCiv: Civilization, val canChangeState: Boole
* @param branch the policy branch to display * @param branch the policy branch to display
* @return a [Table], with outer padding _zero_ * @return a [Table], with outer padding _zero_
*/ */
private fun getBranchGroup(branch: PolicyBranch): Group { private inner class BranchGroup(branch: PolicyBranch) : BorderedTable(path = "PolicyScreen/PolicyBranchBackground") {
private val header = getBranchHeader(branch)
private val group = Group()
private val groupCell: Cell<Group>
private val topBtn = getTopButton(branch)
private val topBtnCell: Cell<Table>
private val labelTable = Table()
// Calculate preferred size init {
val maxCol = max(5, branch.policies.maxOf { it.column }) // Calculate preferred size
val maxRow = branch.policies.maxOf { it.row } val maxCol = max(5, branch.policies.maxOf { it.column })
val maxRow = branch.policies.maxOf { it.row }
val prefWidth = Sizes.paddingHorizontal *2 + Sizes.iconSize *maxCol - (Sizes.iconSize - Sizes.paddingBetweenHor)*(maxCol-1)/2 val prefWidth = Sizes.paddingHorizontal * 2 + Sizes.iconSize * maxCol - (Sizes.iconSize - Sizes.paddingBetweenHor) * (maxCol - 1) / 2
val prefHeight = Sizes.paddingVertical *2 + Sizes.iconSize *maxRow + Sizes.paddingBetweenVer *(maxRow - 1) val prefHeight = Sizes.paddingVertical * 2 + Sizes.iconSize * maxRow + Sizes.paddingBetweenVer * (maxRow - 1)
// Main table // Main table
val colorBg = if (viewingCiv.policies.isAdopted(branch.name)) PolicyColors.branchAdopted else PolicyColors.branchNotAdopted bgColor = if (viewingCiv.policies.isAdopted(branch.name)) PolicyColors.branchAdopted else PolicyColors.branchNotAdopted
val branchGroup = BorderedTable(path="PolicyScreen/PolicyBranchBackground")
.apply { bgColor = colorBg }
// Header // Header
val header = getBranchHeader(branch) add(header).growX().row()
branchGroup.add(header).growX().row()
// Description // Description
val onAdoption = branch.getDescription() val onAdoption = branch.getDescription()
val onCompletion = branch.policies.last().getDescription() val onCompletion = branch.policies.last().getDescription()
var text = "" var text = ""
if (viewingCiv.gameInfo.ruleset.eras[branch.era]!!.eraNumber > viewingCiv.getEraNumber()) if (viewingCiv.gameInfo.ruleset.eras[branch.era]!!.eraNumber > viewingCiv.getEraNumber())
text += "{Unlocked at} {${branch.era}}" + "\n\n" text += "{Unlocked at} {${branch.era}}" + "\n\n"
text += "{On adoption}:" + "\n\n" + onAdoption + "\n\n" + text += "{On adoption}:" + "\n\n" + onAdoption + "\n\n" +
"{On completion}:" + "\n\n" + onCompletion "{On completion}:" + "\n\n" + onCompletion
val labelTable = Table() val label = text.toLabel(fontSize = 13)
label.setFillParent(false)
label.setAlignment(Align.topLeft)
label.wrap = true
labelTable.add(label).pad(7f, 20f, 10f, 20f).grow().row()
val label = text.toLabel(fontSize = 13) val conditionals = LinkedHashMap<UniqueType, ArrayList<String>>()
label.setFillParent(false)
label.setAlignment(Align.topLeft)
label.wrap = true
labelTable.add(label).pad(7f,20f, 10f, 20f).grow().row()
val conditionals = LinkedHashMap<UniqueType, ArrayList<String>>() branch.uniqueMap[UniqueType.OnlyAvailableWhen.text]?.forEach { unique ->
unique.conditionals.forEach {
branch.uniqueMap[UniqueType.OnlyAvailableWhen.text]?.forEach { if (it.type != null) {
unique -> if (conditionals[it.type] == null)
unique.conditionals.forEach { conditionals[it.type] = ArrayList()
if (it.type != null) { conditionals[it.type]!!.add(it.params.toString().tr())
if (conditionals[it.type] == null) }
conditionals[it.type] = ArrayList()
conditionals[it.type]!!.add(it.params.toString().tr())
} }
} }
}
if (conditionals.isNotEmpty()) { if (conditionals.isNotEmpty()) {
var warning = UniqueType.OnlyAvailableWhen.text.tr() + ":\n" var warning = UniqueType.OnlyAvailableWhen.text.tr() + ":\n"
for ((k, v) in conditionals) { for ((k, v) in conditionals) {
warning += "" + k.text.fillPlaceholders(v.joinToString()).tr() + "\n" warning += "" + k.text.fillPlaceholders(v.joinToString()).tr() + "\n"
} }
val warningLabel = ColorMarkupLabel(warning, Color.RED, fontSize = 13) val warningLabel = ColorMarkupLabel(warning, Color.RED, fontSize = 13)
warningLabel.setAlignment(Align.topLeft) warningLabel.setAlignment(Align.topLeft)
warningLabel.wrap = true warningLabel.wrap = true
labelTable.add(warningLabel).pad(0f, 20f, 17f, 20f).grow() labelTable.add(warningLabel).pad(0f, 20f, 17f, 20f).grow()
}
// Top button
val topBtn = getTopButton(branch)
val topBtnCell = branchGroup.add(topBtn).growX().pad(10f, 10f, 0f, 10f)
topBtnCell.row()
// Main grid
val group = Group()
group.width = prefWidth
group.height = prefHeight
// Calculate grid points coordinates
val startX = Sizes.paddingHorizontal
val endX = prefWidth - Sizes.paddingHorizontal - Sizes.iconSize
val deltaX = (endX - startX)/(maxCol - 1)
val startY = prefHeight - Sizes.paddingVertical - Sizes.iconSize
val endY = Sizes.paddingVertical
val deltaY = (startY - endY)/(maxRow - 1)
val coords = Array(maxRow+1) { Array(maxCol+1) {Pair(0f,0f)}}
var row = 1
var col: Int
var posX: Float
var posY = startY
while (row <= maxRow) {
col = 1
posX = startX
while (col <= maxCol) {
coords[row][col] = Pair(posX, posY)
col += 1
posX += deltaX
} }
row += 1 // Top button
posY -= deltaY topBtnCell = add(topBtn).growX().pad(10f, 10f, 0f, 10f)
row()
// Main grid
group.width = prefWidth
group.height = prefHeight
// Calculate grid points coordinates
val startX = Sizes.paddingHorizontal
val endX = prefWidth - Sizes.paddingHorizontal - Sizes.iconSize
val deltaX = (endX - startX) / (maxCol - 1)
val startY = prefHeight - Sizes.paddingVertical - Sizes.iconSize
val endY = Sizes.paddingVertical
val deltaY = (startY - endY) / (maxRow - 1)
val coords = Array(maxRow + 1) { Array(maxCol + 1) { Pair(0f, 0f) } }
var row = 1
var col: Int
var posX: Float
var posY = startY
while (row <= maxRow) {
col = 1
posX = startX
while (col <= maxCol) {
coords[row][col] = Pair(posX, posY)
col += 1
posX += deltaX
}
row += 1
posY -= deltaY
}
// Create policy buttons at calculated coordinates
for (policy in branch.policies) {
if (policy.policyBranchType == PolicyBranchType.BranchComplete)
continue
val button = getPolicyButton(policy, size = Sizes.iconSize)
group.addActor(button)
val policyX = coords[policy.row][policy.column].first
val policyY = coords[policy.row][policy.column].second
button.x = policyX
button.y = policyY
policyNameToButton[policy.name] = button
}
// Draw connecting lines
drawLines(branch)
groupCell = add(group).minWidth(prefWidth).expandY().top()
row()
// Setup header clicks
header.onClick(::toggle)
// Ensure dimensions are calculated
pack()
} }
// Create policy buttons at calculated coordinates fun toggle() {
for (policy in branch.policies) {
if (policy.policyBranchType == PolicyBranchType.BranchComplete)
continue
val button = getPolicyButton(policy, size = Sizes.iconSize)
group.addActor(button)
val policyX = coords[policy.row][policy.column].first
val policyY = coords[policy.row][policy.column].second
button.x = policyX
button.y = policyY
policyNameToButton[policy.name] = button
}
// Draw connecting lines
drawLines(branch)
val groupCell = branchGroup.add(group).minWidth(prefWidth).expandY().top()
branchGroup.row()
// Setup header clicks
header.onClick {
val newActor = if (groupCell.actor == group) labelTable else group val newActor = if (groupCell.actor == group) labelTable else group
val rotate = if (groupCell.actor == group) -90f else 90f val rotate = if (groupCell.actor == group) -90f else 90f
@ -390,18 +405,16 @@ class PolicyPickerScreen(val viewingCiv: Civilization, val canChangeState: Boole
groupCell.clearActor() groupCell.clearActor()
groupCell.setActor(newActor) groupCell.setActor(newActor)
((header.cells[0].actor as Table).cells[0] as Cell<Actor>).clearActor() //todo resolve kludge by making BranchHeader a proper class
((header.cells[0].actor as Table).cells[0] as Cell<Actor>).setActor(ImageGetter. ((header.cells[0].actor as Table).cells[0] as Cell<Actor>)
getImage("OtherIcons/BackArrow").apply { rotation = rotate}.toGroup(10f)) .clearActor()
.setActor(
ImageGetter.getImage("OtherIcons/BackArrow").apply { rotation = rotate }.toGroup(10f)
)
} }
// Ensure dimensions are calculated
branchGroup.pack()
return branchGroup
} }
private fun drawLines(branch: PolicyBranch) { private fun drawLines(branch: PolicyBranch) {
for (policy in branch.policies) { for (policy in branch.policies) {
@ -659,7 +672,7 @@ class PolicyPickerScreen(val viewingCiv: Civilization, val canChangeState: Boole
} }
override fun recreate(): BaseScreen { override fun recreate(): BaseScreen {
val newScreen = PolicyPickerScreen(viewingCiv, canChangeState) val newScreen = PolicyPickerScreen(viewingCiv, canChangeState, selectedPolicyButton?.policy?.name)
newScreen.scrollPane.scrollPercentX = scrollPane.scrollPercentX newScreen.scrollPane.scrollPercentX = scrollPane.scrollPercentX
newScreen.scrollPane.scrollPercentY = scrollPane.scrollPercentY newScreen.scrollPane.scrollPercentY = scrollPane.scrollPercentY
newScreen.scrollPane.updateVisualScroll() newScreen.scrollPane.updateVisualScroll()

View File

@ -28,7 +28,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
Applicable to: Triggerable Applicable to: Triggerable
??? example "[amount] free [unit] units appear" ??? example "[positiveAmount] free [unit] units appear"
Example: "[3] free [Musketman] units appear" Example: "[3] free [Musketman] units appear"
Applicable to: Triggerable Applicable to: Triggerable
@ -36,7 +36,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
??? example "Free Social Policy" ??? example "Free Social Policy"
Applicable to: Triggerable Applicable to: Triggerable
??? example "[amount] Free Social Policies" ??? example "[positiveAmount] Free Social Policies"
Example: "[3] Free Social Policies" Example: "[3] Free Social Policies"
Applicable to: Triggerable Applicable to: Triggerable
@ -44,7 +44,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
??? example "Empire enters golden age" ??? example "Empire enters golden age"
Applicable to: Triggerable Applicable to: Triggerable
??? example "Empire enters a [amount]-turn Golden Age" ??? example "Empire enters a [positiveAmount]-turn Golden Age"
Example: "Empire enters a [3]-turn Golden Age" Example: "Empire enters a [3]-turn Golden Age"
Applicable to: Triggerable Applicable to: Triggerable
@ -75,12 +75,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
??? example "Free Technology" ??? example "Free Technology"
Applicable to: Triggerable Applicable to: Triggerable
??? example "[amount] Free Technologies" ??? example "[positiveAmount] Free Technologies"
Example: "[3] Free Technologies" Example: "[3] Free Technologies"
Applicable to: Triggerable Applicable to: Triggerable
??? example "[amount] free random researchable Tech(s) from the [era]" ??? example "[positiveAmount] free random researchable Tech(s) from the [era]"
Example: "[3] free random researchable Tech(s) from the [Ancient era]" Example: "[3] free random researchable Tech(s) from the [Ancient era]"
Applicable to: Triggerable Applicable to: Triggerable
@ -96,12 +96,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
??? example "Triggers voting for the Diplomatic Victory" ??? example "Triggers voting for the Diplomatic Victory"
Applicable to: Triggerable Applicable to: Triggerable
??? example "Instantly consumes [amount] [stockpiledResource]" ??? example "Instantly consumes [positiveAmount] [stockpiledResource]"
Example: "Instantly consumes [3] [StockpiledResource]" Example: "Instantly consumes [3] [StockpiledResource]"
Applicable to: Triggerable Applicable to: Triggerable
??? example "Instantly provides [amount] [stockpiledResource]" ??? example "Instantly provides [positiveAmount] [stockpiledResource]"
Example: "Instantly provides [3] [StockpiledResource]" Example: "Instantly provides [3] [StockpiledResource]"
Applicable to: Triggerable Applicable to: Triggerable
@ -129,7 +129,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
Applicable to: Triggerable Applicable to: Triggerable
??? example "Reveal up to [amount/'all'] [tileFilter] within a [amount] tile radius" ??? example "Reveal up to [positiveAmount/'all'] [tileFilter] within a [amount] tile radius"
Example: "Reveal up to [3] [Farm] within a [3] tile radius" Example: "Reveal up to [3] [Farm] within a [3] tile radius"
Applicable to: Triggerable Applicable to: Triggerable
@ -144,12 +144,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
Applicable to: Triggerable Applicable to: Triggerable
??? example "Provides the cheapest [stat] building in your first [amount] cities for free" ??? example "Provides the cheapest [stat] building in your first [positiveAmount] cities for free"
Example: "Provides the cheapest [Culture] building in your first [3] cities for free" Example: "Provides the cheapest [Culture] building in your first [3] cities for free"
Applicable to: Triggerable Applicable to: Triggerable
??? example "Provides a [buildingName] in your first [amount] cities for free" ??? example "Provides a [buildingName] in your first [positiveAmount] cities for free"
Example: "Provides a [Library] in your first [3] cities for free" Example: "Provides a [Library] in your first [3] cities for free"
Applicable to: Triggerable Applicable to: Triggerable
@ -1750,7 +1750,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
Applicable to: Ruins Applicable to: Ruins
??? example "From a randomly chosen tile [amount] tiles away from the ruins, reveal tiles up to [amount] tiles away with [amount]% chance" ??? example "From a randomly chosen tile [positiveAmount] tiles away from the ruins, reveal tiles up to [positiveAmount] tiles away with [positiveAmount]% chance"
Example: "From a randomly chosen tile [3] tiles away from the ruins, reveal tiles up to [3] tiles away with [3]% chance" Example: "From a randomly chosen tile [3] tiles away from the ruins, reveal tiles up to [3] tiles away with [3]% chance"
Applicable to: Ruins Applicable to: Ruins
@ -2207,6 +2207,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
*[improvementName]: The name of any improvement. *[improvementName]: The name of any improvement.
*[modFilter]: A Mod name, case-sensitive _or_ a simple wildcard filter beginning and ending in an Asterisk, case-insensitive. *[modFilter]: A Mod name, case-sensitive _or_ a simple wildcard filter beginning and ending in an Asterisk, case-insensitive.
*[policy]: The name of any policy. *[policy]: The name of any policy.
*[positiveAmount]: This indicates a positive whole number, larger than zero, a '+' sign is optional.
*[promotion]: The name of any promotion. *[promotion]: The name of any promotion.
*[relativeAmount]: This indicates a number, usually with a + or - sign, such as `+25` (this kind of parameter is often followed by '%' which is nevertheless not part of the value). *[relativeAmount]: This indicates a number, usually with a + or - sign, such as `+25` (this kind of parameter is often followed by '%' which is nevertheless not part of the value).
*[resource]: The name of any resource. *[resource]: The name of any resource.