Big rework of Policy Picker UI, added new ninepatch RoundedEdgeRectangleSmall (#8218)

Co-authored-by: tunerzinc@gmail.com <vfylfhby>
This commit is contained in:
vegeta1k95
2022-12-24 18:17:00 +01:00
committed by GitHub
parent 1fdbea0a45
commit 78c4f6de68
25 changed files with 786 additions and 221 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

View File

@ -4,6 +4,69 @@ size: 2048, 128
format: RGBA8888
filter: MipMapLinearLinear, MipMapLinearLinear
repeat: none
PolicyBranchIcons/Autocracy
rotate: false
xy: 62, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyBranchIcons/Commerce
rotate: false
xy: 178, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyBranchIcons/Honor
rotate: false
xy: 468, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Military Tradition
rotate: false
xy: 468, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyBranchIcons/Liberty
rotate: false
xy: 584, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyBranchIcons/Patronage
rotate: false
xy: 932, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyBranchIcons/Piety
rotate: false
xy: 990, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyBranchIcons/Rationalism
rotate: false
xy: 1164, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyBranchIcons/Tradition
rotate: false
xy: 1512, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Aesthetics
rotate: false
xy: 4, 62
@ -20,337 +83,344 @@ PolicyIcons/Aristocracy
index: -1
PolicyIcons/Citizenship
rotate: false
xy: 62, 62
xy: 62, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Civil Society
rotate: false
xy: 62, 4
xy: 120, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Collective Rule
rotate: false
xy: 120, 62
xy: 120, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Communism
PolicyBranchIcons/Freedom
rotate: false
xy: 120, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Constitution
rotate: false
xy: 178, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Cultural Diplomacy
PolicyIcons/Communism
rotate: false
xy: 178, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Democracy
PolicyBranchIcons/Order
rotate: false
xy: 178, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Constitution
rotate: false
xy: 236, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Discipline
PolicyIcons/Cultural Diplomacy
rotate: false
xy: 236, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Educated Elite
PolicyIcons/Democracy
rotate: false
xy: 294, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Fascism
PolicyIcons/Discipline
rotate: false
xy: 294, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Free Religion
PolicyIcons/Educated Elite
rotate: false
xy: 352, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Free Speech
PolicyIcons/Fascism
rotate: false
xy: 352, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Free Thought
PolicyIcons/Free Religion
rotate: false
xy: 410, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Humanism
PolicyIcons/Free Speech
rotate: false
xy: 410, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Landed Elite
PolicyIcons/Free Thought
rotate: false
xy: 468, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Legalism
rotate: false
xy: 468, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Mandate Of Heaven
PolicyIcons/Humanism
rotate: false
xy: 526, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Mercantilism
PolicyIcons/Landed Elite
rotate: false
xy: 526, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Merchant Navy
PolicyIcons/Legalism
rotate: false
xy: 584, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Meritocracy
rotate: false
xy: 584, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Militarism
PolicyIcons/Mandate Of Heaven
rotate: false
xy: 642, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Military Caste
PolicyIcons/Mercantilism
rotate: false
xy: 642, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Military Tradition
PolicyIcons/Merchant Navy
rotate: false
xy: 700, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Monarchy
PolicyIcons/Meritocracy
rotate: false
xy: 700, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Nationalism
PolicyIcons/Militarism
rotate: false
xy: 758, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Naval Tradition
PolicyIcons/Military Caste
rotate: false
xy: 758, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Oligarchy
PolicyIcons/Monarchy
rotate: false
xy: 816, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Organized Religion
PolicyIcons/Nationalism
rotate: false
xy: 816, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Philantropy
PolicyIcons/Naval Tradition
rotate: false
xy: 874, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Planned Economy
PolicyIcons/Oligarchy
rotate: false
xy: 874, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Police State
PolicyIcons/Organized Religion
rotate: false
xy: 932, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Populism
rotate: false
xy: 932, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Professional Army
PolicyIcons/Philantropy
rotate: false
xy: 990, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Protectionism
rotate: false
xy: 990, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Reformation
PolicyIcons/Planned Economy
rotate: false
xy: 1048, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Representation
PolicyIcons/Police State
rotate: false
xy: 1048, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Republic
PolicyIcons/Populism
rotate: false
xy: 1106, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Scholasticism
PolicyIcons/Professional Army
rotate: false
xy: 1106, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Scientific Revolution
PolicyIcons/Protectionism
rotate: false
xy: 1164, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Secularism
rotate: false
xy: 1164, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Socialism
PolicyIcons/Reformation
rotate: false
xy: 1222, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Sovereignty
PolicyIcons/Representation
rotate: false
xy: 1222, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Theocracy
PolicyIcons/Republic
rotate: false
xy: 1280, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Total War
PolicyIcons/Scholasticism
rotate: false
xy: 1280, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Trade Unions
PolicyIcons/Scientific Revolution
rotate: false
xy: 1338, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/United Front
PolicyIcons/Secularism
rotate: false
xy: 1338, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Universal Suffrage
PolicyIcons/Socialism
rotate: false
xy: 1396, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Warrior Code
PolicyIcons/Sovereignty
rotate: false
xy: 1396, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Theocracy
rotate: false
xy: 1454, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Total War
rotate: false
xy: 1454, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Trade Unions
rotate: false
xy: 1512, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/United Front
rotate: false
xy: 1570, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Universal Suffrage
rotate: false
xy: 1570, 4
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
PolicyIcons/Warrior Code
rotate: false
xy: 1628, 62
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

@ -1,12 +1,12 @@
Skin.png
size: 256, 64
size: 128, 128
format: RGBA8888
filter: Linear, Linear
repeat: none
Skins/Minimal/checkbox
rotate: false
xy: 199, 23
xy: 52, 16
size: 31, 31
split: 0, 0, 0, 0
orig: 31, 31
@ -14,7 +14,7 @@ Skins/Minimal/checkbox
index: -1
Skins/Minimal/checkbox-pressed
rotate: false
xy: 160, 23
xy: 64, 55
size: 31, 31
split: 0, 0, 0, 0
orig: 31, 31
@ -22,7 +22,7 @@ Skins/Minimal/checkbox-pressed
index: -1
Skins/Minimal/rectangleWithOutline
rotate: false
xy: 64, 13
xy: 112, 121
size: 3, 3
split: 0, 0, 0, 0
orig: 3, 3
@ -30,16 +30,25 @@ Skins/Minimal/rectangleWithOutline
index: -1
Skins/Minimal/roundedEdgeRectangle
rotate: false
xy: 4, 4
xy: 4, 74
size: 52, 50
split: 19, 20, 19, 21
pad: 12, 13, 7, 7
orig: 52, 50
offset: 0, 0
index: -1
Skins/Minimal/roundedEdgeRectangle-small
rotate: false
xy: 4, 4
size: 24, 24
split: 10, 10, 10, 10
pad: 10, 10, 2, 2
orig: 24, 24
offset: 0, 0
index: -1
Skins/Minimal/select-box
rotate: false
xy: 112, 24
xy: 64, 94
size: 40, 30
split: 7, 9, 7, 7
orig: 40, 30
@ -47,7 +56,7 @@ Skins/Minimal/select-box
index: -1
Skins/Minimal/select-box-pressed
rotate: false
xy: 64, 24
xy: 4, 36
size: 40, 30
split: 7, 9, 7, 7
orig: 40, 30

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -111,21 +111,21 @@ EmojiIcons/Happiness
index: -1
EmojiIcons/Production
rotate: false
xy: 588, 720
xy: 646, 720
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
EmojiIcons/Science
rotate: false
xy: 762, 720
xy: 896, 805
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
EmojiIcons/Turn
rotate: false
xy: 1868, 1546
xy: 1270, 962
size: 50, 50
orig: 50, 50
offset: 0, 0
@ -370,35 +370,56 @@ ImprovementIcons/Trading post
index: -1
NotificationIcons/EnhanceReligion
rotate: false
xy: 190, 716
xy: 1927, 1828
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
NotificationIcons/FoundPantheon
rotate: false
xy: 190, 716
xy: 1927, 1828
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
NotificationIcons/FoundReligion
rotate: false
xy: 190, 716
xy: 1927, 1828
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
NotificationIcons/ReformReligion
rotate: false
xy: 190, 716
xy: 1927, 1828
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
StatIcons/Faith
rotate: false
xy: 190, 716
xy: 1927, 1828
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
NotificationIcons/Loading
rotate: false
xy: 622, 1534
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
NotificationIcons/Working
rotate: false
xy: 622, 1534
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
OtherIcons/Loading
rotate: false
xy: 622, 1534
size: 100, 100
orig: 100, 100
offset: 0, 0
@ -503,7 +524,7 @@ OtherIcons/Banner
index: -1
OtherIcons/Camera
rotate: false
xy: 1270, 987
xy: 1328, 1045
size: 25, 25
orig: 25, 25
offset: 0, 0
@ -634,27 +655,6 @@ OtherIcons/Load
orig: 100, 100
offset: 0, 0
index: -1
OtherIcons/Loading
rotate: false
xy: 622, 1534
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
NotificationIcons/Loading
rotate: false
xy: 622, 1534
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
NotificationIcons/Working
rotate: false
xy: 622, 1534
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
OtherIcons/Lock
rotate: false
xy: 730, 1642
@ -662,6 +662,13 @@ OtherIcons/Lock
orig: 100, 100
offset: 0, 0
index: -1
OtherIcons/LockSmall
rotate: false
xy: 530, 720
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
OtherIcons/MapEditor
rotate: false
xy: 622, 1426
@ -692,7 +699,7 @@ OtherIcons/Multiplayer
index: -1
OtherIcons/Nations
rotate: false
xy: 530, 720
xy: 588, 720
size: 50, 50
orig: 50, 50
offset: 0, 0
@ -930,7 +937,7 @@ UnitActionIcons/Swap
index: -1
OtherIcons/Terrains
rotate: false
xy: 1868, 1604
xy: 1868, 1546
size: 50, 50
orig: 50, 50
offset: 0, 0
@ -1308,14 +1315,14 @@ StatIcons/Population
index: -1
StatIcons/Range
rotate: false
xy: 646, 720
xy: 704, 720
size: 50, 50
orig: 50, 50
offset: 0, 0
index: -1
StatIcons/RangedStrength
rotate: false
xy: 704, 720
xy: 762, 720
size: 50, 50
orig: 50, 50
offset: 0, 0
@ -1343,7 +1350,7 @@ StatIcons/Specialist
index: -1
StatIcons/Strength
rotate: false
xy: 1868, 1662
xy: 1868, 1604
size: 50, 50
orig: 50, 50
offset: 0, 0
@ -1392,14 +1399,14 @@ UnitActionIcons/Repair
index: -1
UnitActionIcons/EnhanceReligion
rotate: false
xy: 1927, 1828
xy: 190, 716
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
UnitActionIcons/FoundReligion
rotate: false
xy: 1927, 1828
xy: 190, 716
size: 100, 100
orig: 100, 100
offset: 0, 0
@ -1483,7 +1490,7 @@ UnitActionIcons/SetUp
index: -1
UnitActionIcons/ShowMore
rotate: false
xy: 896, 805
xy: 1868, 1662
size: 50, 50
orig: 50, 50
offset: 0, 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 579 KiB

After

Width:  |  Height:  |  Size: 580 KiB

View File

@ -437,7 +437,7 @@
"[+1 Happiness] from every [Public School]"
],
"row": 1,
"column": 5
"column": 4
},
{
"name": "Free Thought",
@ -447,21 +447,21 @@
],
"requires": ["Secularism"],
"row": 2,
"column": 1
"column": 2
},
{
"name": "Sovereignty",
"uniques": ["[+1 Gold] from all [Science] buildings"],
"requires": ["Humanism"],
"row": 2,
"column": 5
"column": 4
},
{
"name": "Scientific Revolution",
"uniques": ["Science gained from research agreements [+50]%"],
"requires": ["Free Thought"],
"row": 3,
"column": 1
"column": 2
},
{
"name": "Rationalism Complete",
@ -555,13 +555,13 @@
"name": "Populism",
"uniques": ["[+25]% Strength <for [Wounded] units>"],
"row": 1,
"column": 1
"column": 2
},
{
"name": "Militarism",
"uniques": ["[Gold] cost of purchasing [All] units [-33]%"],
"row": 1,
"column": 5
"column": 4
},
{
"name": "Fascism",
@ -571,7 +571,7 @@
],
"requires": ["Populism", "Militarism"],
"row": 2,
"column": 3
"column": 2
},
{
"name": "Police State",
@ -582,7 +582,7 @@
// There are also some uniques regarding espoinage, which as of this writing is not yet implemented
"requires": ["Militarism"],
"row": 2,
"column": 5
"column": 4
},
{
"name": "Total War",
@ -592,7 +592,7 @@
],
"requires": ["Police State", "Fascism"],
"row": 3,
"column": 4
"column": 3
},
{
"name": "Autocracy Complete",

View File

@ -440,7 +440,7 @@
"[+1 Happiness] from every [Public School]"
],
"row": 1,
"column": 5
"column": 4
},
{
"name": "Free Thought",
@ -450,21 +450,21 @@
],
"requires": ["Secularism"],
"row": 2,
"column": 1
"column": 2
},
{
"name": "Sovereignty",
"uniques": ["[+15]% [Science] <while the empire is happy>"],
"requires": ["Humanism"],
"row": 2,
"column": 5
"column": 4
},
{
"name": "Scientific Revolution",
"uniques": ["[2] Free Technologies"],
"requires": ["Free Thought"],
"row": 3,
"column": 1
"column": 2
},
{
"name": "Rationalism Complete",
@ -555,13 +555,13 @@
"name": "Populism",
"uniques": ["[+25]% Strength <for [Wounded] units>"],
"row": 1,
"column": 1
"column": 2
},
{
"name": "Militarism",
"uniques": ["[Gold] cost of purchasing [All] units [-33]%"],
"row": 1,
"column": 5
"column": 4
},
{
"name": "Fascism",
@ -570,7 +570,7 @@
],
"requires": ["Populism", "Militarism"],
"row": 2,
"column": 3
"column": 2
},
{
"name": "Police State",
@ -580,7 +580,7 @@
],
"requires": ["Militarism"],
"row": 2,
"column": 5
"column": 4
},
{
"name": "Total War",
@ -590,7 +590,7 @@
],
"requires": ["Police State", "Fascism"],
"row": 3,
"column": 4
"column": 3
},
{
"name": "Autocracy Complete",

View File

@ -1426,12 +1426,19 @@ Unit type =
# Policies
Adopt =
Completed =
On adoption =
On completion =
Cannot be adopted together with =
Adopt policy =
Adopt free policy =
Unlocked at =
Gain 2 free technologies =
All policies adopted =
Policy branch: [branchName] =
Are you sure you want to adopt [branchName]? =
# Religions

View File

@ -1,7 +1,9 @@
package com.unciv.models.ruleset
import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueFlag
import com.unciv.models.ruleset.unique.UniqueTarget
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.translations.tr
import com.unciv.ui.civilopedia.FormattedLine
@ -31,17 +33,13 @@ open class Policy : RulesetObject() {
/** Used in PolicyPickerScreen to display Policy properties */
fun getDescription(): String {
val policyText = ArrayList<String>()
policyText += name
policyText += uniques
if (policyBranchType != PolicyBranchType.BranchComplete) {
policyText += if (requires!!.isNotEmpty())
"Requires [" + requires!!.joinToString { it.tr() } + "]"
else
"{Unlocked at} {${branch.era}}"
}
return policyText.joinToString("\n") { it.tr() }
var text = uniques
.filter { !it.contains(UniqueType.OnlyAvailableWhen.text) }
.joinToString("\n", transform = { "$it".tr() })
if (policyBranchType != PolicyBranchType.BranchStart
&& policyBranchType != PolicyBranchType.BranchComplete)
text = name + "\n" + text
return text
}
override fun makeLink() = "Policy/$name"

View File

@ -10,6 +10,7 @@ class SkinStrings(skin: String = UncivGame.Current.settings.skin) {
val skinConfig = SkinCache[skin] ?: SkinConfig()
// Default shapes must always end with "Shape" so the UiElementDocsWriter can identify them
val roundedEdgeRectangleSmallShape = skinLocation + "roundedEdgeRectangle-small"
val roundedEdgeRectangleShape = skinLocation + "roundedEdgeRectangle"
val rectangleWithOutlineShape = skinLocation + "rectangleWithOutline"
val selectBoxShape = skinLocation + "select-box"

View File

@ -1,8 +1,13 @@
package com.unciv.ui.pickerscreens
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.ui.Button
import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.TutorialTrigger
@ -10,25 +15,157 @@ import com.unciv.models.UncivSound
import com.unciv.models.ruleset.Policy
import com.unciv.models.ruleset.Policy.PolicyBranchType
import com.unciv.models.ruleset.PolicyBranch
import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.translations.tr
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popup.ConfirmPopup
import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.KeyCharAndCode
import com.unciv.ui.utils.BorderedTable
import com.unciv.ui.utils.RecreateOnResize
import com.unciv.ui.utils.extensions.addSeparator
import com.unciv.ui.utils.extensions.brighten
import com.unciv.ui.utils.extensions.center
import com.unciv.ui.utils.extensions.colorFromRGB
import com.unciv.ui.utils.extensions.darken
import com.unciv.ui.utils.extensions.disable
import com.unciv.ui.utils.extensions.enable
import com.unciv.ui.utils.extensions.onClick
import com.unciv.ui.utils.extensions.pad
import com.unciv.ui.utils.extensions.toTextButton
import com.unciv.ui.utils.extensions.toGroup
import com.unciv.ui.utils.extensions.toLabel
import com.unciv.ui.worldscreen.WorldScreen
import java.lang.Integer.max
import kotlin.math.abs
import kotlin.math.min
object Colors {
val policyPickable = colorFromRGB(47,67,92).darken(0.3f)
val policyNotPickable = colorFromRGB(20, 20, 20)
val policySelected = colorFromRGB(37,87,82)
val branchCompleted = colorFromRGB(255, 205, 0)
val branchNotAdopted = colorFromRGB(10,90,130).darken(0.5f)
val branchAdopted = colorFromRGB(100, 90, 10).darken(0.5f)
}
object Sizes {
val paddingVertical = 10f
val paddingHorizontal = 20f
val paddingBetweenHor = 10f
val paddingBetweenVer = 20f
val iconSize = 50f
}
fun Policy.isAdopted() : Boolean {
val worldScreen = UncivGame.Current.worldScreen ?: return false
val viewingCiv = worldScreen.viewingCiv
return viewingCiv.policies.isAdopted(this.name)
}
fun Policy.isPickable() : Boolean {
val worldScreen = UncivGame.Current.worldScreen ?: return false
val viewingCiv = worldScreen.viewingCiv
if (!worldScreen.isPlayersTurn
|| worldScreen.viewingCiv.isSpectator() // viewingCiv var points to selectedCiv in case of spectator
|| viewingCiv.isDefeated()
|| viewingCiv.policies.isAdopted(this.name)
|| this.policyBranchType == PolicyBranchType.BranchComplete
|| !viewingCiv.policies.isAdoptable(this)
|| !viewingCiv.policies.canAdoptPolicy()
)
return false
return true
}
class PolicyButton(val policy: Policy, size: Float = 30f) : BorderedTable(
style = BaseScreen.skinStrings.roundedEdgeRectangleSmallShape,
borderSize = 2f
) {
val icon = ImageGetter.getImage("PolicyIcons/" + policy.name)
val highlight = ImageGetter.getImage("UnitFlagIcons/UnitFlagSelection")
var isSelected = false
set(value) {
field = value
updateState()
}
init {
icon.setSize(size*0.7f, size*0.7f)
highlight.setSize(size*1.35f, size*1.35f)
addActor(icon)
add(highlight).center()
updateState()
pack()
width = size
height = size
icon.toFront()
icon.center(this)
highlight.toBack()
highlight.isVisible = false
highlight.center(this)
}
fun onClick(function: () -> Unit): PolicyButton {
(this as Actor).onClick(function = {
function()
updateState()
})
return this
}
fun updateState() {
val worldScreen = UncivGame.Current.worldScreen ?: return
val viewingCiv = worldScreen.viewingCiv
val isPickable = policy.isPickable()
val isAdopted = viewingCiv.policies.isAdopted(policy.name)
highlight.isVisible = isSelected
when {
isSelected && isPickable -> {
setBackgroundColor(Colors.policySelected)
}
isPickable -> {
setBackgroundColor(Colors.policyPickable)
}
isAdopted -> {
icon.color = Color.GOLD
setBackgroundColor(colorFromRGB(10,90,100).darken(0.8f))
}
else -> {
icon.color.a = 0.2f
setBackgroundColor(Colors.policyNotPickable)
}
}
}
}
class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo = worldScreen.viewingCiv)
: PickerScreen(), RecreateOnResize {
internal val viewingCiv: CivilizationInfo = civInfo
private var pickedPolicy: Policy? = null
private var policyNameToButton = HashMap<String, BorderedTable>()
private var selectedPolicyButton: PolicyButton? = null
init {
val policies = viewingCiv.policies
@ -49,15 +186,15 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
closeButton.disable()
rightSideButton.onClick(UncivSound.Policy) {
val policy = pickedPolicy!!
val policy = selectedPolicyButton!!.policy
// Evil people clicking on buttons too fast to confuse the screen - #4977
if (!policyIsPickable(policy)) return@onClick
if (!policy.isPickable()) return@onClick
viewingCiv.policies.adopt(policy)
// If we've moved to another screen in the meantime (great person pick, victory screen) ignore this
if (game.screen !is PolicyPickerScreen || !policies.canAdoptPolicy()) {
if (game.screen !is PolicyPickerScreen) {
game.popScreen()
} else {
val policyScreen = PolicyPickerScreen(worldScreen)
@ -97,18 +234,29 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
// Actually create and distribute the policy branches
var wrapper = Table()
var wrapperWidth = 0f // Either pack() each round or cumulate separately
for ( (index, branch) in branches.values.withIndex()) {
var leftToRight = true
var index = 0
var switchOnIndex = rowChangeCount
while (index < branches.size) {
val branch = branches.values.elementAt(index)
val branchGroup = getBranchGroup(branch)
wrapperWidth += branchGroup.width + 20f // 20 is the horizontal padding in wrapper.add
if (index > 0 && index % rowChangeCount == 0 || wrapperWidth > rowChangeWidth) {
if (index > 0 && index == switchOnIndex || wrapperWidth > rowChangeWidth) {
index += rowChangeCount - if (leftToRight) 1 else - 2
switchOnIndex -= if (leftToRight) 1 else rowChangeCount - 2
leftToRight = !leftToRight
topTable.add(wrapper).pad(5f,10f)
topTable.addSeparator()
topTable.addSeparator().pad(0f, 10f)
wrapper = Table()
wrapperWidth = branchGroup.width
wrapperWidth = 0f
continue
}
wrapper.add(branchGroup).pad(10f)
wrapper.add(branchGroup).growY().growX()
index += if (leftToRight) 1 else -1
}
topTable.add(wrapper).pad(5f,10f)
@ -116,7 +264,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
// total padding, or up to where the axis is centered, whichever is smaller
splitPane.pack() // packs topTable but also ensures scrollPane.maxXY is calculated
if (topTable.height > scrollPane.height) {
val vScroll = min(15f, scrollPane.maxY / 2)
val vScroll = min(0f, scrollPane.maxY / 2)
scrollPane.scrollY = vScroll
}
if (topTable.width > scrollPane.width) {
@ -126,31 +274,25 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
scrollPane.updateVisualScroll()
}
private fun policyIsPickable(policy: Policy):Boolean {
if (!worldScreen.isPlayersTurn
|| worldScreen.viewingCiv.isSpectator() // viewingCiv var points to selectedCiv in case of spectator
|| viewingCiv.isDefeated()
|| viewingCiv.policies.isAdopted(policy.name)
|| policy.policyBranchType == PolicyBranchType.BranchComplete
|| !viewingCiv.policies.isAdoptable(policy)
|| !viewingCiv.policies.canAdoptPolicy()
)
return false
return true
}
private fun pickPolicy(button: PolicyButton) {
private fun pickPolicy(policy: Policy) {
if (!policyIsPickable(policy)) {
val policy = button.policy
rightSideButton.isVisible = !viewingCiv.policies.isAdopted(policy.name)
if (!policy.isPickable()) {
rightSideButton.disable()
} else {
rightSideButton.enable()
}
if (viewingCiv.gameInfo.gameParameters.godMode && pickedPolicy == policy
if (viewingCiv.gameInfo.gameParameters.godMode && selectedPolicyButton?.policy == policy
&& viewingCiv.policies.isAdoptable(policy)) {
viewingCiv.policies.adopt(policy)
game.replaceCurrentScreen(PolicyPickerScreen(worldScreen))
}
pickedPolicy = policy
selectedPolicyButton?.isSelected = false
selectedPolicyButton = button
selectedPolicyButton?.isSelected = true
descriptionLabel.setText(policy.getDescription())
}
@ -160,55 +302,342 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
* @param branch the policy branch to display
* @return a [Table], with outer padding _zero_
*/
private fun getBranchGroup(branch: PolicyBranch): Table {
val branchGroup = Table()
branchGroup.row()
branchGroup.add(getPolicyButton(branch, false))
.minWidth(160f).padBottom(15f).row()
private fun getBranchGroup(branch: PolicyBranch): Group {
var currentRow = 1
var currentColumn = 1
val branchTable = Table()
for (policy in branch.policies) {
if (policy.policyBranchType == PolicyBranchType.BranchComplete) continue
if (policy.row > currentRow) {
branchTable.row().pad(2.5f)
currentRow++
currentColumn = 1
}
if (policy.column > currentColumn) {
branchTable.add().colspan(policy.column - currentColumn) // empty space
}
branchTable.add(getPolicyButton(policy, true)).colspan(2)
currentColumn = policy.column + 2
// Calculate preferred size
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 prefHeight = Sizes.paddingVertical*2 + Sizes.iconSize*maxRow + Sizes.paddingBetweenVer*(maxRow - 1)
// Main table
val colorBg = if (branch.isAdopted()) Colors.branchAdopted else Colors.branchNotAdopted
val branchGroup = BorderedTable(innerColor = colorBg)
// Header
val header = getBranchHeader(branch)
branchGroup.add(header).growX().row()
// Description
val onAdoption = branch.getDescription()
val onCompletion = branch.policies.last().getDescription()
var text = ""
if (viewingCiv.gameInfo.ruleSet.eras[branch.era]!!.eraNumber > viewingCiv.getEraNumber())
text += "{Unlocked at} {${branch.era}}" + "\n\n"
text += "{On adoption}:" + "\n\n" + onAdoption + "\n\n" +
"{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 exclusive = ArrayList<String>()
branch.uniqueMap[UniqueType.OnlyAvailableWhen.text]?.forEach {
it.conditionals.forEach { exclusive += it.params }
}
branchGroup.add(branchTable).height(150f).row()
if (exclusive.isNotEmpty()) {
val forbidden = ("{Cannot be adopted together with} " + exclusive.joinToString()).toLabel(Color.RED, 13)
forbidden.setFillParent(false)
forbidden.setAlignment(Align.topLeft)
forbidden.wrap = true
labelTable.add(forbidden).pad(0f, 20f, 17f, 20f).grow()
}
// Add the finisher button.
branchGroup.add(getPolicyButton(branch.policies.last(), false)).padTop(15f)
// 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
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)
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 rotate = if (groupCell.actor == group) -90f else 90f
if (groupCell.actor == group)
topBtnCell.clearActor()
else
topBtnCell.setActor(topBtn)
groupCell.clearActor()
groupCell.setActor(newActor)
((header.cells[0].actor as Table).cells[0] as Cell<Actor>).clearActor()
((header.cells[0].actor as Table).cells[0] as Cell<Actor>).setActor(ImageGetter.
getImage("OtherIcons/BackArrow").apply { rotation = rotate}.toGroup(10f))
}
// Ensure dimensions are calculated
branchGroup.pack()
return branchGroup
}
private fun getPolicyButton(policy: Policy, image: Boolean): Button {
var policyButton = Button(skin)
if (image) {
val policyImage = ImageGetter.getImage("PolicyIcons/" + policy.name)
policyButton.add(policyImage).size(30f)
} else {
policyButton = policy.name.toTextButton()
private fun drawLines(branch: PolicyBranch) {
for (policy in branch.policies) {
if (policy.policyBranchType == PolicyBranchType.BranchComplete)
continue
if (policy.requires == null)
continue
val policyButton = policyNameToButton[policy.name]
val group = policyButton!!.parent
for (prereqName in policy.requires!!) {
if (prereqName == branch.name)
continue
val prereqButton = policyNameToButton[prereqName]
drawLine(group,
// Top center
policyButton.x+policyButton.width/2,
policyButton.y+policyButton.height,
// Bottom center
prereqButton!!.x + prereqButton.width/2,
prereqButton.y)
}
}
if (viewingCiv.policies.isAdopted(policy.name)) policyButton.color = Color.GREEN // existing
else if (!viewingCiv.policies.isAdoptable(policy)) policyButton.color = Color.GRAY // non-available
}
private fun drawLine(group: Group, policyX: Float, policyY: Float, prereqX: Float, prereqY:Float) {
policyButton.onClick { pickPolicy(policy) }
policyButton.pack()
return policyButton
val lineColor = Color.WHITE
val lineSize = 2f
if (policyX != prereqX) {
val r = 3f
val deltaX = policyX - prereqX // can be > 0 or < 0
val deltaY = prereqY - policyY // always > 0
val bendingY = Sizes.paddingBetweenVer / 2
// Top line
val line = ImageGetter.getWhiteDot().apply {
width = lineSize
height = deltaY - bendingY - r
x = prereqX - width / 2
y = prereqY - height
}
// Bottom line
val line1 = ImageGetter.getWhiteDot().apply {
width = lineSize
height = bendingY - r
x = policyX - width / 2
y = policyY
}
// Middle line
val line2 = ImageGetter.getWhiteDot().apply {
width = abs(deltaX) - 2*r
height = lineSize
x = policyX + (if (deltaX > 0f) -width - r else r)
y = policyY + bendingY - lineSize/2
}
val line3: Image? // Top -> Middle
val line4: Image? // Bottom -> Middle
if (deltaX < 0) {
line3 = ImageGetter.getLine(line2.x + line2.width - lineSize/2, line2.y + lineSize/2,
line.x + lineSize/2, line.y + lineSize/2, lineSize)
line4 = ImageGetter.getLine(line2.x, line2.y + lineSize/2,
line1.x + lineSize/2, line1.y + line1.height, lineSize)
} else {
line3 = ImageGetter.getLine(line2.x, line2.y + line2.height/2,
line.x + lineSize/2, line.y, lineSize)
line4 = ImageGetter.getLine(line2.x + line2.width - lineSize/2, line2.y + lineSize/2,
line1.x + lineSize/2, line1.y + line1.height - lineSize/2, lineSize)
}
line.color = lineColor
line1.color = lineColor
line2.color = lineColor
line3.color = lineColor
line4.color = lineColor
group.addActor(line)
group.addActor(line1)
group.addActor(line2)
group.addActor(line3)
group.addActor(line4)
} else {
val line = ImageGetter.getWhiteDot().apply {
width = lineSize
height = prereqY - policyY
x = policyX - width / 2
y = policyY
}
line.color = lineColor
group.addActor(line)
}
}
private fun getBranchHeader(branch: PolicyBranch): Table {
val header = BorderedTable(innerColor = colorFromRGB(47,90,92), borderSize = 5f)
header.pad(5f)
val table = Table()
val iconPath = "PolicyBranchIcons/" + branch.name
val icon = if (ImageGetter.imageExists(iconPath)) ImageGetter.getImage(iconPath).apply {
setOrigin(Align.center)
setOrigin(25f, 25f)
align = Align.center
}.toGroup(15f) else null
val expandIcon = ImageGetter.getImage("OtherIcons/BackArrow").apply { rotation = 90f }.toGroup(10f)
table.add(expandIcon).minWidth(15f).expandX().left()
table.add(branch.name.uppercase().toLabel(fontSize = 14).apply { setAlignment(Align.center) }).center()
table.add(icon).expandX().left().padLeft(5f)
header.add(table).minWidth(150f).growX()
header.pack()
return header
}
private fun getTopButton(branch: PolicyBranch): Table {
val policy: Policy
val text: String
val lockIcon = ImageGetter.getImage("OtherIcons/LockSmall")
.apply { color = Color.WHITE }.toGroup(15f)
lockIcon.isVisible = false
if (viewingCiv.policies.isAdopted(branch.name)) {
policy = branch.policies.last()
text = "Completed"
} else if (viewingCiv.gameInfo.ruleSet.eras[branch.era]!!.eraNumber > viewingCiv.getEraNumber()) {
policy = branch
text = branch.era
} else {
policy = branch
text = "Adopt"
}
val label = text.toLabel(fontSize = 14)
label.setAlignment(Align.center)
val color = when {
policy.isPickable() -> Colors.policyPickable
viewingCiv.policies.isAdopted(policy.name) -> {
label.color = colorFromRGB(150, 70, 40)
Colors.branchCompleted
}
else -> {
lockIcon.isVisible = true
label.color.a = 0.5f
Colors.policyNotPickable}
}
val table = BorderedTable(style = skinStrings.roundedEdgeRectangleSmallShape, innerColor = color, borderSize = 2f)
table.add(label).minHeight(30f).minWidth(150f).growX()
table.addActor(lockIcon)
table.onClick(function = {})
table.onClick {
if (policy.isPickable())
ConfirmPopup(
this,
"Are you sure you want to adopt [${branch.name}]?",
"Adopt", true, action = {
viewingCiv.policies.adopt(policy, false)
val policyScreen = PolicyPickerScreen(worldScreen)
policyScreen.scrollPane.scrollPercentX = scrollPane.scrollPercentX
policyScreen.scrollPane.scrollPercentY = scrollPane.scrollPercentY
policyScreen.scrollPane.updateVisualScroll()
game.replaceCurrentScreen(policyScreen)
}
).open(force = true)
}
table.pack()
lockIcon.setPosition(table.width, table.height / 2 - lockIcon.height/2)
return table
}
private fun getPolicyButton(policy: Policy, size: Float = 30f): BorderedTable {
val button = PolicyButton(policy, size = size)
button.onClick { pickPolicy(button = button) }
return button
}
override fun recreate(): BaseScreen = PolicyPickerScreen(worldScreen, viewingCiv)

View File

@ -0,0 +1,44 @@
package com.unciv.ui.utils
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.ui.utils.extensions.center
open class BorderedTable(
val style: String = BaseScreen.skinStrings.rectangleWithOutlineShape,
val innerColor: Color = Color.BLACK,
val borderSize: Float = 5f
) : Table() {
private var bgBorder: Image = Image(BaseScreen.skinStrings.getUiBackground("", style, Color.WHITE))
private var bgInner: Image = Image(BaseScreen.skinStrings.getUiBackground("", style, innerColor))
init {
this.addActor(bgBorder)
this.addActor(bgInner)
bgInner.toBack()
bgBorder.toBack()
}
fun setBackgroundColor(color: Color) {
bgInner.remove()
bgInner = Image(BaseScreen.skinStrings.getUiBackground("", style, color))
addActor(bgInner)
bgInner.zIndex = bgBorder.zIndex + 1
sizeChanged()
}
override fun sizeChanged() {
super.sizeChanged()
bgBorder.setSize(width + borderSize, height + borderSize)
bgInner.setSize(width, height)
bgBorder.center(this)
bgInner.center(this)
}
}