mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 01:39:40 +07:00
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:
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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.
|
||||||
|
Reference in New Issue
Block a user