mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 15:27:50 +07:00
Tabbed options (#5081)
* Tabbed Options Screen * Tabbed Options Screen - atlas
This commit is contained in:
BIN
android/Images/OtherIcons/SecretOptions.png
Normal file
BIN
android/Images/OtherIcons/SecretOptions.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
BIN
android/Images/OtherIcons/Settings.png
Normal file
BIN
android/Images/OtherIcons/Settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
BIN
android/Images/OtherIcons/Speaker.png
Normal file
BIN
android/Images/OtherIcons/Speaker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
BIN
android/assets/ExtraImages/banner.png
Normal file
BIN
android/assets/ExtraImages/banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 967 KiB After Width: | Height: | Size: 997 KiB |
@ -1,6 +1,6 @@
|
||||
|
||||
# Tutorial tasks
|
||||
|
||||
|
||||
# Tutorial tasks
|
||||
|
||||
Move a unit!\nClick on a unit > Click on a destination > Click the arrow popup = Eine Einheit bewegen!\nKlicke auf eine Einheit > Klicke auf ein Ziel > Klicke auf das Pfeil-Popup.
|
||||
Found a city!\nSelect the Settler (flag unit) > Click on 'Found city' (bottom-left corner) = Eine Stadt gründen!\nWähle den Siedler (Flaggensymbol) > Klicke auf 'Stadt gründen' (unten links).
|
||||
Enter the city screen!\nClick the city button twice = Öffne den Stadtbildschirm!\n Klicke zweimal den Stadtknopf.
|
||||
@ -15,24 +15,24 @@ Create a trade route!\nConstruct roads between your capital and another city\nOr
|
||||
Conquer a city!\nBring an enemy city down to low health > \nEnter the city with a melee unit = Erobere eine Stadt!\nBringe eine feindliche Stadt auf wenig Leben > \nBetrete die Stadt mit einer Nahkampfeinheit.
|
||||
Move an air unit!\nSelect an air unit > select another city within range > \nMove the unit to the other city = Bewege eine Lufteinheit!\nWähle eine Lufteinheit > Wähle eine andere Stadt in Reichweite > \nVerschiebe die Einheit zu der anderen Stadt.
|
||||
See your stats breakdown!\nEnter the Overview screen (top right corner) >\nClick on 'Stats' = Schaue deine Statistiken an!\nGehe in den Übersichtsbildschirm (obere rechte Ecke) >\nKlicke auf 'Statistiken'.
|
||||
|
||||
|
||||
Oh no! It looks like something went DISASTROUSLY wrong! This is ABSOLUTELY not supposed to happen! Please send me (yairm210@hotmail.com) an email with the game information (menu -> save game -> copy game info -> paste into email) and I'll try to fix it as fast as I can! = Oh nein! Sieht aus, als wäre etwas katastrophal schief gelaufen! Das darf auf keinen Fall passieren! Bitte sende mir (yairm210@hotmail.com) eine Email mit den Spielinformationen (Menü -> Spiel speichern -> Spielinfo kopieren -> in Email einfügen) und ich werde versuchen, es so schnell wie möglich zu beheben!
|
||||
Oh no! It looks like something went DISASTROUSLY wrong! This is ABSOLUTELY not supposed to happen! Please send us an report and we'll try to fix it as fast as we can! = Oh nein! Sieht aus, als wäre etwas katastrophal schief gelaufen! Das darf auf keinen Fall passieren! Bitte sende uns einen Bericht und wir werden versuchen, es so schnell wie möglich zu beheben!
|
||||
|
||||
# Buildings
|
||||
|
||||
|
||||
# Buildings
|
||||
|
||||
Unsellable = Unverkäuflich
|
||||
Not displayed as an available construction unless [building] is built = Wird nicht als verfügbares Bauwerk angezeigt, bis [building] gebaut ist
|
||||
Not displayed as an available construction without [resource] = Wird nicht als verfügbares Bauwerk angezeigt, solange [resource] fehlt
|
||||
|
||||
|
||||
Choose a free great person = Wähle eine kostenlose Große Persönlichkeit
|
||||
Get [unitName] = Erhalte [unitName]
|
||||
|
||||
|
||||
Hydro Plant = Wasserkraftwerk
|
||||
[buildingName] obsoleted = [buildingName] ist nun veraltet
|
||||
|
||||
# Diplomacy,Trade,Nations
|
||||
|
||||
|
||||
# Diplomacy,Trade,Nations
|
||||
|
||||
Requires [buildingName] to be built in the city = Benötigt den Bau von [buildingName] in der Stadt
|
||||
Requires [buildingName] to be built in all cities = Benötigt den Bau von [buildingName] in allen Städten
|
||||
Provides a free [buildingName] in the city = Stellt das Gebäude [buildingName] in der Stadt kostenlos bereit
|
||||
@ -50,12 +50,12 @@ Requires [PolicyOrNationalWonder] = Benötigt [PolicyOrNationalWonder]
|
||||
Cannot be purchased = Kann nicht gekauft werden
|
||||
Can only be purchased = Kann nur gekauft werden
|
||||
See also = Siehe auch
|
||||
|
||||
|
||||
Requires at least one of the following: = Benötigt eine der folgenden Vorraussetzungen:
|
||||
Requires all of the following: = Benötigt folgende Vorraussetzungen:
|
||||
Leads to [techName] = [techName] kann nun erforscht werden
|
||||
Leads to: = Ermöglicht die Erforschung von:
|
||||
|
||||
|
||||
Current construction = Aktuelle Produktion
|
||||
Construction queue = Produktionswarteschlange
|
||||
Pick a construction = Wähle ein Bauwerk
|
||||
@ -66,7 +66,7 @@ Show stats drilldown = Zeige Statistiken
|
||||
Show construction queue = Zeige Produktionswarteschlange
|
||||
Save = Speichern
|
||||
Cancel = Abbrechen
|
||||
|
||||
|
||||
Diplomacy = Diplomatie
|
||||
War = Krieg
|
||||
Peace = Frieden
|
||||
@ -94,7 +94,7 @@ Indeed! = Auf jeden Fall!
|
||||
Denounce [civName]? = [civName] anprangern?
|
||||
Denounce ([numberOfTurns] turns) = Anprangern ([numberOfTurns] Runden)
|
||||
We will remember this. = Das werden wir nie vergessen!
|
||||
|
||||
|
||||
[civName] has declared war on [targetCivName]! = [civName] hat [targetCivName] den Krieg erklärt!
|
||||
[civName] and [targetCivName] have signed a Peace Treaty! = [civName] und [targetCivName] haben einen Friedensvertrag unterzeichnet!
|
||||
[civName] and [targetCivName] have signed the Declaration of Friendship! = [civName] und [targetCivName] haben die Freundschaftserklärung unterzeichnet!
|
||||
@ -102,7 +102,7 @@ We will remember this. = Das werden wir nie vergessen!
|
||||
Do you want to break your promise to [leaderName]? = Möchtest du dein Versprechen gegenüber [leaderName] brechen?
|
||||
We promised not to settle near them ([count] turns remaining) = Wir haben versprochen, nicht in ihrer Nähe zu siedeln ([count] Runden verbleiben)
|
||||
They promised not to settle near us ([count] turns remaining) = Sie haben versprochen, nicht in unserer Nähe zu siedeln ([count] Runden verbleiben)
|
||||
|
||||
|
||||
Unforgivable = Todfeind
|
||||
Afraid = Gefürchtet
|
||||
Enemy = Feind
|
||||
@ -111,12 +111,12 @@ Neutral = Neutral
|
||||
Favorable = Beliebt
|
||||
Friend = Freund
|
||||
Ally = Verbündeter
|
||||
|
||||
|
||||
[questName] (+[influenceAmount] influence) = [questName] (+[influenceAmount] Einfluss)
|
||||
[remainingTurns] turns remaining = [remainingTurns] Runden verbleiben
|
||||
|
||||
## Diplomatic modifiers
|
||||
|
||||
|
||||
## Diplomatic modifiers
|
||||
|
||||
You declared war on us! = Ihr habt uns den Krieg erklärt!
|
||||
Your warmongering ways are unacceptable to us. = Euer kriegerisches Verhalten ist für uns inakzeptabel.
|
||||
You have captured our cities! = Ihr habt unsere Städte erobert!
|
||||
@ -139,15 +139,15 @@ Your arrogant demands are in bad taste = Eure arroganten Forderungen sind geschm
|
||||
Your use of nuclear weapons is disgusting! = Euer Einsatz von Atomwaffen ist ekelhaft!
|
||||
You have stolen our lands! = Ihr habt unser Land geraubt!
|
||||
You gave us units! = Ihr habt uns Einheiten geschenkt!
|
||||
|
||||
|
||||
Demands = Forderungen
|
||||
Please don't settle new cities near us. = Bitte gründet keine neuen Städte in unserer Nähe.
|
||||
Very well, we shall look for new lands to settle. = Nun gut, wir werden uns nach neuem Land umsehen, um es zu besiedeln.
|
||||
We shall do as we please. = Wir werden tun, wie es uns beliebt.
|
||||
We noticed your new city near our borders, despite your promise. This will have....implications. = Wir haben eure neue Stadt in der Nähe unserer Grenzen bemerkt, entgegen eures Versprechens. Dies wird....Konsequenzen haben.
|
||||
|
||||
# City-States
|
||||
|
||||
|
||||
# City-States
|
||||
|
||||
Provides [amountOfCulture] culture at 30 Influence = Liefert [amountOfCulture] Kultur ab einem Einfluss von 30
|
||||
Provides 3 food in capital and 1 food in other cities at 30 Influence = Liefert 3 Nahrung in die Hauptstadt und 1 Nahrung in alle anderen Städte ab einem Einfluss von 30
|
||||
Provides 3 happiness at 30 Influence = Liefert 3 Zufriedenheit ab einem Einfluss von 30
|
||||
@ -203,7 +203,7 @@ Take worker (-50 Influence) = Arbeiter nehmen (-50 Einfluss)
|
||||
[civName] is afraid of your military power! = [civName] fürchtet sich vor deiner militärischen Macht!
|
||||
|
||||
|
||||
# Trades
|
||||
# Trades
|
||||
|
||||
Trade = Handel
|
||||
Offer trade = Handel anbieten
|
||||
@ -231,17 +231,17 @@ Declare war on [nation] = [nation] den Krieg erklären
|
||||
Luxury resources = Luxusressourcen
|
||||
Strategic resources = Strategische Ressourcen
|
||||
Owned: [amountOwned] = Im Besitz: [amountOwned]
|
||||
|
||||
# Nation picker
|
||||
|
||||
|
||||
# Nation picker
|
||||
|
||||
[resourceName] not required = [resourceName] nicht erforderlich
|
||||
Lost ability = Verlorene Fähigkeit
|
||||
National ability = Nationalfähigkeit
|
||||
[firstValue] vs [secondValue] = [firstValue] anstatt [secondValue]
|
||||
|
||||
|
||||
# New game screen
|
||||
|
||||
|
||||
|
||||
# New game screen
|
||||
|
||||
Uniques = Unikate
|
||||
Promotions = Beförderungen
|
||||
Load copied data = Aus Zwischenablage laden
|
||||
@ -305,9 +305,9 @@ World wrap requires a minimum width of 32 tiles = 'World Wrap' Karten müssen mi
|
||||
The provided map dimensions were too small = Die angegebenen Dimensionen waren zu klein
|
||||
The provided map dimensions were too big = Die angegebenen Dimensionen waren zu groß
|
||||
The provided map dimensions had an unacceptable aspect ratio = Die angegebenen Dimensionen hatten ein zu extremes Seitenverhältnis
|
||||
|
||||
|
||||
Difficulty = Schwierigkeitsgrad
|
||||
|
||||
|
||||
AI = KI
|
||||
Remove = Entfernen
|
||||
Random = Zufall
|
||||
@ -315,14 +315,14 @@ Human = Mensch
|
||||
Hotseat = Schleudersitz
|
||||
User ID = Spieler-ID
|
||||
Click to copy = Anklicken zum Kopieren
|
||||
|
||||
|
||||
|
||||
|
||||
Game Speed = Spielgeschwindigkeit
|
||||
Quick = Schnell
|
||||
Standard = Standard
|
||||
Epic = Episch
|
||||
Marathon = Marathon
|
||||
|
||||
|
||||
Starting Era = Startzeitalter
|
||||
It looks like we can't make a map with the parameters you requested! = Mit den von dir angegebenen Parametern kann keine Karte erzeugt werden!
|
||||
Maybe you put too many players into too small a map? = Vielleicht hast du zu viele Spieler in eine zu kleine Karte gepackt?
|
||||
@ -339,14 +339,14 @@ Base Ruleset = Basisregelsatz
|
||||
[amount] Improvements = [amount] Feldverbesserungen
|
||||
[amount] Religions = [amount] Religionen
|
||||
[amount] Beliefs = [amount] Glaubenssätze
|
||||
|
||||
|
||||
World Wrap = World Wrap
|
||||
World wrap maps are very memory intensive - creating large world wrap maps on Android can lead to crashes! = 'World Wrap' Karten verbrauchen sehr viel Speicher - Das erstellen von großen 'World Wrap' Karten kann bei Android zu einem Absturz führen!
|
||||
Anything above 80 by 50 may work very slowly on Android! = Auf Android kann alles über 80 mal 50 sehr langsam sein.
|
||||
Anything above 40 may work very slowly on Android! = Auf Android kann alles über 40 sehr langsam sein.
|
||||
|
||||
# Multiplayer
|
||||
|
||||
|
||||
# Multiplayer
|
||||
|
||||
Username = Spielername
|
||||
Multiplayer = Mehrspieler
|
||||
Could not download game! = Konnte das Spiel nicht herunterladen!
|
||||
@ -382,9 +382,9 @@ Resign = Aufgeben
|
||||
Are you sure you want to resign? = Willst du wirklich aufgeben?
|
||||
You can only resign if it's your turn = Du kannst nur aufgeben, wenn du am Zug bist
|
||||
[civName] resigned and is now controlled by AI = [civName] hat aufgegeben und wird nun von der KI gespielt
|
||||
|
||||
# Save game menu
|
||||
|
||||
|
||||
# Save game menu
|
||||
|
||||
Current saves = Gespeicherte Spiele
|
||||
Show autosaves = Zeige automatisch gespeicherte Spiele an
|
||||
Saved game name = Name des gespeicherten Spiels
|
||||
@ -411,13 +411,21 @@ Load from custom location = Laden von externem Speicherort
|
||||
Could not load game from custom location! = Laden von externem Speicherort fehlgeschlagen!
|
||||
Save to custom location = Speichern in externem Speicherort
|
||||
Could not save game to custom location! = Speichern in externem Speicherort fehlgeschlagen!
|
||||
|
||||
# Options
|
||||
|
||||
|
||||
# Options
|
||||
|
||||
Options = Optionen
|
||||
Display options = Anzeigeeinstellungen
|
||||
Gameplay options = Spielmechanikeinstellungen
|
||||
Other options = Andere Einstellungen
|
||||
About = Über
|
||||
Display = Anzeige
|
||||
Gameplay = Spielmechanik
|
||||
Sound = Sound
|
||||
Multiplayer = Mehrspieler
|
||||
Advanced = Erweitert
|
||||
Locate mod errors = Mod-Probleme
|
||||
Debug = Nur für Eingeweihte
|
||||
|
||||
See online Readme = Readme online öffnen
|
||||
Visit repository = Repository der Entwickler besuchen
|
||||
Turns between autosaves = Runden bis zum nächsten automatischen Speichern
|
||||
Sound effects volume = Lautstärke Soundeffekte
|
||||
Music volume = Lautstärke Musik
|
||||
@ -444,16 +452,18 @@ Show tile yields = Felderträge anzeigen
|
||||
Continuous rendering = Kontinuierliches Rendern
|
||||
When disabled, saves battery life but certain animations will be suspended = Es spart Akku, wenn es deaktiviert ist, aber bestimmte Animationen werden nicht angezeigt.
|
||||
Order trade offers by amount = Handelsangebote nach Menge sortieren
|
||||
Check extension mods based on vanilla = Erweiterungs-Mods mit Vanilla-Regelsatz prüfen
|
||||
Checking mods for errors... = Mods werden geprüft...
|
||||
Show experimental world wrap for maps = 'World Wrap'-Option für neue Karten anbieten
|
||||
HIGHLY EXPERIMENTAL - YOU HAVE BEEN WARNED! = WARNUNG: HOCHGRADIG EXPERIMENTELL - DU WURDEST GEWARNT!
|
||||
HIGHLY EXPERIMENTAL - UPDATES WILL BREAK SAVES! = WARNUNG: HOCHGRADIG EXPERIMENTELL - UPDATES WERDEN SPEICHERSTÄNDE ZERSTÖREN!
|
||||
Enable portrait orientation = Hochkant-Orientierung zulassen
|
||||
Generate translation files = Erstelle Übersetzungsdateien
|
||||
Translation files are generated successfully. = Die Übersetzungsdateien wurden erfolgreich erstellt.
|
||||
Locate mod errors = Mod-Fehler lokalisieren
|
||||
|
||||
# Notifications
|
||||
|
||||
Please note that translations are a community-based work in progress and are INCOMPLETE! The percentage shown is how much of the language is translated in-game. If you want to help translating the game into your language, click here. = Bitte beachte, daß die Übersetzungen eine andauernde Leistung einer Gemeinschaft von Freiwilligen sind und damit oft unvollständig. Die angezeigte Prozentzahl bedeutet den Anteil übersetzter Texte im gesamten Spiel. Wenn Du helfen willst, die Übersetzungen zu verbessern - dies ist ein Link zur Anleitung.
|
||||
|
||||
# Notifications
|
||||
|
||||
Research of [technologyName] has completed! = [technologyName] wurde erforscht!
|
||||
[construction] has become obsolete and was removed from the queue in [cityName]! = [construction] ist veraltet und wurde in [cityName] aus der Warteschlange entfernt!
|
||||
[construction] has become obsolete and was removed from the queue in [amount] cities! = [construction] ist veraltet und wurde in [amount] Städten aus der Warteschlange entfernt!
|
||||
@ -466,7 +476,7 @@ You have entered a Golden Age! = Ein Goldenes Zeitalter hat begonnen!
|
||||
A [greatPerson] has been born in [cityName]! = [cityName] - Ein [greatPerson] wurde geboren!
|
||||
We have encountered [civName]! = Wir sind auf [civName] getroffen!
|
||||
[cityStateName] has given us [stats] as a token of goodwill for meeting us = [cityStateName] hat uns [stats] als Zeichen des guten Willens für unsere Begegnung übergeben
|
||||
[cityStateName] has given us [stats] as we are the first major civ to meet them = [cityStateName] hat uns [stats] übergeben, da wir die erste bedeutende Zivilisation sind, die er getroffen hat
|
||||
[cityStateName] has given us [stats] as we are the first major civ to meet them = [cityStateName] hat uns [stats] übergeben, da wir die erste bedeutende Zivilisation sind, die sie getroffen haben
|
||||
Cannot provide unit upkeep for [unitName] - unit has been disbanded! = Der Unterhalt für [unitName] konnte nicht bezahlt werden - Einheit wurde aufgelöst!
|
||||
[cityName] has grown! = [cityName] ist gewachsen!
|
||||
[cityName] is starving! = [cityName] verhungert!
|
||||
@ -563,8 +573,8 @@ Your city [cityName] was converted to [religionName]! = Deine Stadt [cityName] k
|
||||
Your [unitName] lost its faith after spending too long inside enemy territory! = Deine [unitName] Einheit hat ihren Glauben verloren, nachdem sie zu lange in feindlichem Gebiet war!
|
||||
|
||||
|
||||
# World Screen UI
|
||||
|
||||
# World Screen UI
|
||||
|
||||
Working... = Bitte warten...
|
||||
Waiting for other players... = Warte auf andere Spieler...
|
||||
in = in
|
||||
@ -617,7 +627,7 @@ Yes = Ja
|
||||
No = Nein
|
||||
Acquire = Übernehmen
|
||||
Under construction = Im Bau
|
||||
|
||||
|
||||
Food = Nahrung
|
||||
Production = Produktion
|
||||
Gold = Gold
|
||||
@ -625,7 +635,7 @@ Happiness = Zufriedenheit
|
||||
Culture = Kultur
|
||||
Science = Wissenschaft
|
||||
Faith = Glaube
|
||||
|
||||
|
||||
Crop Yield = Ernteertrag
|
||||
Territory = Territorium
|
||||
Force = Kampfkraft
|
||||
@ -634,7 +644,7 @@ Golden Age = Goldenes Zeitalter
|
||||
[year] BC = [year] v. Chr.
|
||||
[year] AD = [year] n. Chr.
|
||||
Civilopedia = Civilopedia
|
||||
|
||||
|
||||
Start new game = Neues Spiel
|
||||
Save game = Spiel speichern
|
||||
Load game = Spiel laden
|
||||
@ -650,9 +660,9 @@ Close = Schließen
|
||||
Do you want to exit the game? = Willst du das Spiel beenden?
|
||||
Start bias: = Start-Präferenz:
|
||||
Avoid [terrain] = Meide [terrain]
|
||||
|
||||
|
||||
# City screen
|
||||
|
||||
|
||||
Exit city = Stadt verlassen
|
||||
Raze city = Stadt niederreißen
|
||||
Stop razing city = Niederreißen der Stadt stoppen
|
||||
@ -704,9 +714,9 @@ Move to city = Zur Stadt bewegen
|
||||
Invalid input! Please enter a different string. =
|
||||
# Requires translation!
|
||||
Please enter some text =
|
||||
|
||||
|
||||
# Technology UI
|
||||
|
||||
|
||||
Pick a tech = Technologie auswählen
|
||||
Pick a free tech = Kostenlose Technologie auswählen
|
||||
Research [technology] = [technology] erforschen
|
||||
@ -729,9 +739,9 @@ Attack = Angreifen
|
||||
Bombard = Bombardieren
|
||||
NUKE = Atomisieren
|
||||
Captured! = Gefangen!
|
||||
|
||||
|
||||
# Battle modifier categories
|
||||
|
||||
|
||||
defence vs ranged = Verteidigung gegen Fernkampf
|
||||
[percentage] to unit defence = [percentage] erhöhte Verteidigungsstärke
|
||||
Attacker Bonus = Angriffsbonus
|
||||
@ -753,11 +763,11 @@ defence vs [unitType] = Verteidigung gegen [unitType]
|
||||
[tileFilter] defence = [tileFilter] Verteidigung
|
||||
Defensive Bonus = Verteidigungsbonus
|
||||
Stacked with [unitType] = Auf gleichem Feld mit [unitType]
|
||||
|
||||
|
||||
The following improvements [stats]: = Die folgenden Verbesserungen [stats]:
|
||||
The following improvements on [tileType] tiles [stats]: = Die folgenden Verbesserungen auf [tileType] Feldern [stats]:
|
||||
|
||||
|
||||
|
||||
|
||||
Hurry Research = Forschung beschleunigen
|
||||
Conduct Trade Mission = Handelsmission durchführen
|
||||
Your trade mission to [civName] has earned you [goldAmount] gold and [influenceAmount] influence! = Deine Handelsmission zu [civName] hat dir [goldAmount] Gold und [influenceAmount] Einfluss eingebracht!
|
||||
@ -780,9 +790,9 @@ Policies = Politiken
|
||||
Base happiness = Grundzufriedenheit
|
||||
Occupied City = Besetzte Städte
|
||||
Buildings = Gebäude
|
||||
|
||||
|
||||
# terrainFilters (so for uniques like: "[stats] from [terrainFilter] tiles")
|
||||
|
||||
|
||||
All = Alle
|
||||
Water = Wasser
|
||||
Land = Land
|
||||
@ -800,14 +810,14 @@ Strategic resource = Strategische Ressource
|
||||
Fresh water = Frischwasser
|
||||
non-fresh water = nicht frisches Wasser
|
||||
Natural Wonder = Naturwunder
|
||||
|
||||
# improvementFilters
|
||||
|
||||
|
||||
# improvementFilters
|
||||
|
||||
All Road = Alle Straßen
|
||||
Great Improvement = Große Verbesserung
|
||||
Great = Große
|
||||
|
||||
|
||||
|
||||
|
||||
Wonders = Wunder
|
||||
Base values = Grundwerte
|
||||
Bonuses = Boni
|
||||
@ -833,9 +843,9 @@ Known and defeated ([numberOfCivs]) = Bekannt und besiegt ([numberOfCivs])
|
||||
Tiles = Felder
|
||||
Natural Wonders = Naturwunder
|
||||
Treasury deficit = Schatzkammerdefizit
|
||||
|
||||
# Victory
|
||||
|
||||
|
||||
# Victory
|
||||
|
||||
Science victory = Wissenschaftssieg
|
||||
Cultural victory = Kultursieg
|
||||
Conquest victory = Dominanzsieg
|
||||
@ -874,9 +884,9 @@ Vote for [civilizationName] = Abstimmen für [civilizationName]
|
||||
Continue = Fortfahren
|
||||
Abstained = Enthalten
|
||||
Vote for World Leader = Stimme für den Anführer der Welt ab
|
||||
|
||||
|
||||
# Capturing a city
|
||||
|
||||
|
||||
What would you like to do with the city? = Was möchtet Ihr mit dieser Stadt machen?
|
||||
Annex = Annektieren
|
||||
Annexed cities become part of your regular empire. = Annektierte Städte werden Teil Eures Reichs
|
||||
@ -896,14 +906,14 @@ Destroying the city instantly razes the city to the ground. = Zerstören macht d
|
||||
Remove your troops in our border immediately! = Entferne sofort deine Truppen aus unserem Gebiet!
|
||||
Sorry. = Entschuldigung.
|
||||
Never! = Niemals!
|
||||
|
||||
|
||||
Offer Declaration of Friendship ([30] turns) = Freundschaftserklärung anbieten ([30] Runden)
|
||||
My friend, shall we declare our friendship to the world? = Mein Freund, sollen wir unsere Freundschaft der Welt kundtun?
|
||||
Sign Declaration of Friendship ([30] turns) = Freundschaftserklärung unterzeichnen ([30] Runden)
|
||||
We are not interested. = Wir sind nicht interessiert.
|
||||
We have signed a Declaration of Friendship with [otherCiv]! = Wir haben eine Freundschaftserklärung mit [otherCiv] unterzeichnet!
|
||||
[otherCiv] has denied our Declaration of Friendship! = [otherCiv] hat unsere Freundschaftserklärung abgelehnt!
|
||||
|
||||
|
||||
Basics = Spielkonzepte
|
||||
Resources = Ressourcen
|
||||
Terrains = Gelände
|
||||
@ -971,7 +981,7 @@ Terrain feature [feature] does not exist in ruleset! = Geländemerkmal [feature]
|
||||
Resource [resource] does not exist in ruleset! = Ressource [resource] fehlt im Regelsatz!
|
||||
Improvement [improvement] does not exist in ruleset! = Verbesserung [improvement] fehlt im Regelsatz!
|
||||
Change map to fit selected ruleset? = Karte ändern, um sie dem neuen Regelsatz anzupassen?
|
||||
|
||||
|
||||
# Civilopedia difficulty levels
|
||||
Player settings = Spieler-Einstellungen
|
||||
Base Happiness = Basiszufriedenheit
|
||||
@ -997,7 +1007,7 @@ Major AI civilization bonus starting units = Haupt-KI Zivilisationsbonus Startei
|
||||
City state bonus starting units = Stadtstaaten Bonus Starteinheiten
|
||||
Turns until barbarians enter player tiles = Züge bis Barbaren Spielerfelder betreten
|
||||
Gold reward for clearing barbarian camps = Gold-Belohnung für das Räumen von Barbarenlagern
|
||||
|
||||
|
||||
# Other civilopedia things
|
||||
Nations = Nationen
|
||||
Available for [unitTypes] = Verfügbar für [unitTypes]
|
||||
@ -1010,9 +1020,9 @@ Granted by [param] = Von [param] erteilt
|
||||
Granted by: = Erteilt von:
|
||||
[bonus] with [tech] = [bonus] mit [tech]
|
||||
Difficulty levels = Schwierigkeitsgrade
|
||||
|
||||
|
||||
# Policies
|
||||
|
||||
|
||||
Adopt policy = Politik verabschieden
|
||||
Adopt free policy = Freie Politik verabschieden
|
||||
Unlocked at = Freigeschaltet bei
|
||||
@ -1048,12 +1058,12 @@ Cities following this religion: = Städte die dieser Religion folgen
|
||||
Click an icon to see the stats of this religion = Klicke auf ein Icon, um die Statistiken dieser Religion anzuzeigen
|
||||
|
||||
# Terrains
|
||||
|
||||
|
||||
Impassable = Unpassierbar
|
||||
Rare feature = Seltene Geländeform
|
||||
|
||||
|
||||
# Resources
|
||||
|
||||
|
||||
Bison = Bisons
|
||||
Copper = Kupfer
|
||||
Cocoa = Kakao
|
||||
@ -1063,9 +1073,9 @@ Truffles = Trüffel
|
||||
Strategic = Strategisch
|
||||
Bonus = Bonus
|
||||
Luxury = Luxus
|
||||
|
||||
|
||||
# Unit types
|
||||
|
||||
|
||||
City = Stadt
|
||||
Civilian = Zivilist
|
||||
Melee = Nahkampf
|
||||
@ -1074,21 +1084,21 @@ Scout = Späher
|
||||
Mounted = Beritten
|
||||
Armor = Panzerung
|
||||
Siege = Belagerung
|
||||
|
||||
|
||||
WaterCivilian = Wasser-Zivilist
|
||||
WaterMelee = Wassernahkampf
|
||||
WaterRanged = Wasserfernkampf
|
||||
WaterSubmarine = U-Boote
|
||||
WaterAircraftCarrier = Flugzeugträger
|
||||
|
||||
|
||||
Fighter = Jagdflugzeug
|
||||
Bomber = Bomber
|
||||
AtomicBomber = Atombomber
|
||||
Missile = Rakete
|
||||
|
||||
|
||||
|
||||
|
||||
# Unit filters and other unit related things
|
||||
|
||||
|
||||
Air = Luft
|
||||
air units = Lufteinheiten
|
||||
Barbarian = Barbar
|
||||
@ -1106,13 +1116,13 @@ Unbuildable = nicht baubar
|
||||
water units = Wassereinheiten
|
||||
wounded units = verwundete Einheiten
|
||||
Wounded = Verwundet
|
||||
|
||||
|
||||
# For the All "newly-trained [relevant] units in this city receive the [] promotion" translation. Relevant as in 'units that can receive'
|
||||
relevant = relevante
|
||||
|
||||
|
||||
|
||||
|
||||
# Promotions
|
||||
|
||||
|
||||
Pick promotion = Wähle eine Beförderung
|
||||
OR = ODER
|
||||
units in open terrain = Einheiten im offenen Gelände
|
||||
@ -1125,10 +1135,9 @@ Dogfighting II = Kurvenkampf II
|
||||
Dogfighting III = Kurvenkampf III
|
||||
Choose name for [unitName] = Wähle Namen für [unitName]
|
||||
[unitFilter] units gain the [promotion] promotion = [unitFilter] Einheiten erhalten die [promotion] Beförderung
|
||||
|
||||
|
||||
# Multiplayer Turn Checker Service
|
||||
|
||||
Multiplayer options = Mehrspieler Einstellungen
|
||||
|
||||
Enable out-of-game turn notifications = Aktiviere Zug Benachrichtigungen außerhalb des Spiels
|
||||
Time between turn checks out-of-game (in minutes) = Intervall zwischen Zug Prüfungen (in Minuten)
|
||||
Show persistent notification for turn notifier service = Zeige dauerhafte Benachrichtigung für den Zug-Benachrichtungsdienst
|
||||
@ -1136,10 +1145,10 @@ Take user ID from clipboard = Spieler-ID aus der Zwischenablage übernehmen
|
||||
Doing this will reset your current user ID to the clipboard contents - are you sure? = Dies wird deine Spieler-ID auf den Inhalt der Zwischenablage zurücksetzen - bist du sicher?
|
||||
ID successfully set! = Spieler-ID erfolgreich gesetzt!
|
||||
Invalid ID! = Ungültige Spieler-ID!
|
||||
|
||||
|
||||
|
||||
|
||||
# Mods
|
||||
|
||||
|
||||
Mods = Modifikationen
|
||||
Download [modName] = [modName] herunterladen
|
||||
Update [modName] = [modName] aktualisieren
|
||||
@ -1167,9 +1176,9 @@ No description provided = Keine Beschreibung mitgeliefert
|
||||
Author: [author] = Autor: [author]
|
||||
Size: [size] kB = Größe: [size] kB
|
||||
The mod you selected is incompatible with the defined ruleset! = Die gewählte Modifikation ist inkompatibel!
|
||||
|
||||
|
||||
# Uniques that are relevant to more than one type of game object
|
||||
|
||||
|
||||
[stats] from every [param] = Alle [param] geben [stats]
|
||||
[stats] from [param] tiles in this city = [stats] von [param] Feld in dieser Stadt
|
||||
[stats] from every [param] on [tileFilter] tiles = [stats] von jedem [param] auf [tileFilter] Feldern
|
||||
@ -1183,7 +1192,7 @@ Can only be built on [tileFilter] tiles = Kann nur auf [tileFilter]-Feldern geba
|
||||
Cannot be built on [tileFilter] tiles = Kann nicht auf [tileFilter]-Feldern gebaut werden
|
||||
Does not need removal of [feature] = Hierfür muß [feature] nicht entfernt werden
|
||||
Gain a free [building] [cityFilter] = Erhalte [building] umsonst [cityFilter]
|
||||
|
||||
|
||||
# City filters
|
||||
in this city = in dieser Stadt
|
||||
in all cities = in allen Städten
|
||||
|
@ -420,9 +420,16 @@ Could not save game to custom location! =
|
||||
# Options
|
||||
|
||||
Options =
|
||||
Display options =
|
||||
Gameplay options =
|
||||
Other options =
|
||||
About =
|
||||
Display =
|
||||
Gameplay =
|
||||
Sound =
|
||||
Advanced =
|
||||
Multiplayer =
|
||||
Locate mod errors =
|
||||
Debug =
|
||||
|
||||
See online Readme =
|
||||
Turns between autosaves =
|
||||
Sound effects volume =
|
||||
Music volume =
|
||||
@ -444,18 +451,19 @@ off =
|
||||
Show pixel units =
|
||||
Show pixel improvements =
|
||||
Enable nuclear weapons =
|
||||
Fontset =
|
||||
Show tile yields =
|
||||
Continuous rendering =
|
||||
When disabled, saves battery life but certain animations will be suspended =
|
||||
Order trade offers by amount =
|
||||
Check extension mods based on vanilla =
|
||||
Checking mods for errors... =
|
||||
Show experimental world wrap for maps =
|
||||
HIGHLY EXPERIMENTAL - YOU HAVE BEEN WARNED! =
|
||||
HIGHLY EXPERIMENTAL - UPDATES WILL BREAK SAVES! =
|
||||
Enable portrait orientation =
|
||||
Generate translation files =
|
||||
Translation files are generated successfully. =
|
||||
Locate mod errors =
|
||||
Please note that translations are a community-based work in progress and are INCOMPLETE! The percentage shown is how much of the language is translated in-game. If you want to help translating the game into your language, click here. =
|
||||
|
||||
# Notifications
|
||||
|
||||
@ -1135,7 +1143,6 @@ Choose name for [unitName] =
|
||||
|
||||
# Multiplayer Turn Checker Service
|
||||
|
||||
Multiplayer options =
|
||||
Enable out-of-game turn notifications =
|
||||
Time between turn checks out-of-game (in minutes) =
|
||||
Show persistent notification for turn notifier service =
|
||||
|
@ -43,7 +43,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
|
||||
*/
|
||||
var viewEntireMapForDebug = false
|
||||
/** For when you need to test something in an advanced game and don't have time to faff around */
|
||||
val superchargedForDebug = false
|
||||
var superchargedForDebug = false
|
||||
|
||||
/** Simulate until this turn on the first "Next turn" button press.
|
||||
* Does not update World View changes until finished.
|
||||
|
@ -1,69 +1,31 @@
|
||||
package com.unciv.ui
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.MainMenuScreen
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.pickerscreens.PickerScreen
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.utils.enable
|
||||
import com.unciv.ui.utils.onClick
|
||||
import com.unciv.ui.utils.LanguageTable
|
||||
import com.unciv.ui.utils.LanguageTable.Companion.addLanguageTables
|
||||
import com.unciv.ui.worldscreen.mainmenu.OptionsPopup
|
||||
|
||||
|
||||
class LanguageTable(val language:String, val percentComplete: Int):Table(){
|
||||
private val blue = ImageGetter.getBlue()
|
||||
private val darkBlue = blue.cpy().lerp(Color.BLACK,0.5f)!!
|
||||
|
||||
init{
|
||||
pad(10f)
|
||||
defaults().pad(10f)
|
||||
left()
|
||||
if(ImageGetter.imageExists("FlagIcons/$language"))
|
||||
add(ImageGetter.getImage("FlagIcons/$language")).size(40f)
|
||||
|
||||
val spaceSplitLang = language.replace("_"," ")
|
||||
add("$spaceSplitLang ($percentComplete%)".toLabel())
|
||||
update("")
|
||||
touchable = Touchable.enabled // so click listener is activated when any part is clicked, not only children
|
||||
pack()
|
||||
}
|
||||
|
||||
fun update(chosenLanguage:String){
|
||||
background = ImageGetter.getBackground( if(chosenLanguage==language) blue else darkBlue)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LanguagePickerScreen : PickerScreen(){
|
||||
/** A [PickerScreen] to select a language, used once on the initial run after a fresh install.
|
||||
* After that, [OptionsPopup] provides the functionality.
|
||||
* Reusable code is in [LanguageTable] and [addLanguageTables].
|
||||
*/
|
||||
class LanguagePickerScreen : PickerScreen() {
|
||||
var chosenLanguage = "English"
|
||||
|
||||
private val languageTables = ArrayList<LanguageTable>()
|
||||
private val languageTables: ArrayList<LanguageTable>
|
||||
|
||||
fun update(){
|
||||
fun update() {
|
||||
languageTables.forEach { it.update(chosenLanguage) }
|
||||
}
|
||||
|
||||
init {
|
||||
closeButton.isVisible = false
|
||||
/// trimMargin is overhead, but easier to maintain and see when it might get trimmed without wrap:
|
||||
val translationDisclaimer = """
|
||||
|Please note that translations are a community-based work in progress and are INCOMPLETE!
|
||||
|The percentage shown is how much of the language is translated in-game.
|
||||
|If you want to help translating the game into your language,
|
||||
| instructions are in the Github readme! (Menu > Community > Github)
|
||||
""".trimMargin()
|
||||
topTable.add(translationDisclaimer.toLabel()).pad(10f).row()
|
||||
val tableLanguages = Table()
|
||||
tableLanguages.defaults().uniformX()
|
||||
tableLanguages.defaults().pad(10.0f)
|
||||
tableLanguages.defaults().fillX()
|
||||
topTable.add(tableLanguages).row()
|
||||
|
||||
val languageCompletionPercentage = UncivGame.Current.translations
|
||||
.percentCompleteOfLanguages
|
||||
languageTables.addAll(languageCompletionPercentage
|
||||
.map { LanguageTable(it.key,if(it.key=="English") 100 else it.value) }
|
||||
.sortedByDescending { it.percentComplete} )
|
||||
languageTables = topTable.addLanguageTables(stage.width - 60f)
|
||||
|
||||
languageTables.forEach {
|
||||
it.onClick {
|
||||
@ -71,7 +33,6 @@ class LanguagePickerScreen : PickerScreen(){
|
||||
rightSideButton.enable()
|
||||
update()
|
||||
}
|
||||
tableLanguages.add(it).row()
|
||||
}
|
||||
|
||||
rightSideButton.setText("Pick language".tr())
|
||||
@ -89,4 +50,4 @@ class LanguagePickerScreen : PickerScreen(){
|
||||
game.setScreen(MainMenuScreen())
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
68
core/src/com/unciv/ui/utils/LanguageTable.kt
Normal file
68
core/src/com/unciv/ui/utils/LanguageTable.kt
Normal file
@ -0,0 +1,68 @@
|
||||
package com.unciv.ui.utils
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.civilopedia.MarkupRenderer
|
||||
import java.util.ArrayList
|
||||
|
||||
/** Represents a row in the Language picker, used both in OptionsPopup and in LanguagePickerScreen */
|
||||
internal class LanguageTable(val language:String, val percentComplete: Int): Table(){
|
||||
private val blue = ImageGetter.getBlue()
|
||||
private val darkBlue = blue.cpy().lerp(Color.BLACK,0.5f)!!
|
||||
|
||||
init{
|
||||
pad(10f)
|
||||
defaults().pad(10f)
|
||||
left()
|
||||
if(ImageGetter.imageExists("FlagIcons/$language"))
|
||||
add(ImageGetter.getImage("FlagIcons/$language")).size(40f)
|
||||
|
||||
val spaceSplitLang = language.replace("_"," ")
|
||||
add("$spaceSplitLang ($percentComplete%)".toLabel())
|
||||
update("")
|
||||
touchable =
|
||||
Touchable.enabled // so click listener is activated when any part is clicked, not only children
|
||||
pack()
|
||||
}
|
||||
|
||||
fun update(chosenLanguage:String){
|
||||
background = ImageGetter.getBackground(if (chosenLanguage == language) blue else darkBlue)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** Extension to add the Language boxes to a Table, used both in OptionsPopup and in LanguagePickerScreen */
|
||||
internal fun Table.addLanguageTables(expectedWidth: Float): ArrayList<LanguageTable> {
|
||||
val languageTables = ArrayList<LanguageTable>()
|
||||
|
||||
val translationDisclaimer = FormattedLine(
|
||||
text = "Please note that translations are a community-based work in progress and are" +
|
||||
" INCOMPLETE! The percentage shown is how much of the language is translated in-game." +
|
||||
" If you want to help translating the game into your language, click here.",
|
||||
link = "https://github.com/yairm210/Unciv/wiki/Translating",
|
||||
size = 15
|
||||
)
|
||||
add(MarkupRenderer.render(listOf(translationDisclaimer),expectedWidth)).pad(5f).row()
|
||||
|
||||
val tableLanguages = Table()
|
||||
tableLanguages.defaults().uniformX()
|
||||
tableLanguages.defaults().pad(10.0f)
|
||||
tableLanguages.defaults().fillX()
|
||||
|
||||
val languageCompletionPercentage = UncivGame.Current.translations
|
||||
.percentCompleteOfLanguages
|
||||
languageTables.addAll(languageCompletionPercentage
|
||||
.map { LanguageTable(it.key, if (it.key == "English") 100 else it.value) }
|
||||
.sortedByDescending { it.percentComplete} )
|
||||
|
||||
languageTables.forEach {
|
||||
tableLanguages.add(it).row()
|
||||
}
|
||||
add(tableLanguages).row()
|
||||
|
||||
return languageTables
|
||||
}
|
||||
}
|
||||
}
|
355
core/src/com/unciv/ui/utils/TabbedPager.kt
Normal file
355
core/src/com/unciv/ui/utils/TabbedPager.kt
Normal file
@ -0,0 +1,355 @@
|
||||
package com.unciv.ui.utils
|
||||
|
||||
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.*
|
||||
import com.unciv.UncivGame
|
||||
import kotlin.math.min
|
||||
|
||||
/*
|
||||
Unimplemented ideas:
|
||||
Allow "fixed header" content that does not participate in scrolling
|
||||
(OptionsPopup mod check tab)
|
||||
`scrollAlign: Align` property controls initial content scroll position (currently it's Align.top)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements a 'Tabs' widget where different pages can be switched by selecting a header button.
|
||||
*
|
||||
* Each page is an Actor, passed to the Widget via [addPage]. Pages can be [removed][removePage],
|
||||
* [replaced][replacePage] or dynamically added after the Widget is already shown.
|
||||
|
||||
* Pages are automatically scrollable, switching pages preserves scroll positions individually.
|
||||
* Pages can be disabled or secret - any 'secret' pages added require a later call to [askForPassword]
|
||||
* to activate them (or discard if the password is wrong).
|
||||
*
|
||||
* The size parameters are lower and upper bounds of the page content area. The widget will always report
|
||||
* these bounds (plus header height) as layout properties min/max-Width/Height, and measure the content
|
||||
* area of added pages and set the reported pref-W/H to their maximum within these bounds. But, if a
|
||||
* maximum is not specified, that coordinate will grow with content unlimited, and layout max-W/H will
|
||||
* always report the same as pref-W/H.
|
||||
*/
|
||||
//region Fields and initialization
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused") // All member are part of our API
|
||||
class TabbedPager(
|
||||
private val minimumWidth: Float = 0f,
|
||||
private var maximumWidth: Float = Float.MAX_VALUE,
|
||||
private val minimumHeight: Float = 0f,
|
||||
private var maximumHeight: Float = Float.MAX_VALUE,
|
||||
private val headerFontSize: Int = 18,
|
||||
private val headerFontColor: Color = Color.WHITE,
|
||||
private val highlightColor: Color = Color.BLUE,
|
||||
backgroundColor: Color = ImageGetter.getBlue().lerp(Color.BLACK, 0.5f),
|
||||
private val headerPadding: Float = 10f,
|
||||
capacity: Int = 4
|
||||
) : Table() {
|
||||
|
||||
private class PageState(
|
||||
var content: Actor,
|
||||
var disabled: Boolean = false,
|
||||
val onActivation: ((Int, String)->Unit)? = null
|
||||
) {
|
||||
var scrollX = 0f
|
||||
var scrollY = 0f
|
||||
|
||||
var button: Button = Button(CameraStageBaseScreen.skin)
|
||||
var buttonX = 0f
|
||||
var buttonW = 0f
|
||||
}
|
||||
|
||||
private var preferredWidth = minimumWidth
|
||||
private val growMaxWidth = maximumWidth == Float.MAX_VALUE
|
||||
private val limitWidth = maximumWidth
|
||||
private var preferredHeight = minimumHeight
|
||||
private val growMaxHeight = maximumHeight == Float.MAX_VALUE
|
||||
private val limitHeight = maximumHeight
|
||||
|
||||
private val pages = ArrayList<PageState>(capacity)
|
||||
|
||||
/**
|
||||
* Index of currently selected page, or -1 of none. Read-only, use [selectPage] to change.
|
||||
*/
|
||||
var activePage = -1
|
||||
private set
|
||||
|
||||
private val header = Table(CameraStageBaseScreen.skin)
|
||||
private val headerScroll = AutoScrollPane(header)
|
||||
private var headerHeight = 0f
|
||||
|
||||
private val contentScroll = AutoScrollPane(null)
|
||||
|
||||
private val deferredSecretPages = ArrayDeque<PageState>(0)
|
||||
private var askPasswordLock = false
|
||||
|
||||
init {
|
||||
background = ImageGetter.getBackground(backgroundColor)
|
||||
header.defaults().pad(headerPadding, headerPadding * 0.5f)
|
||||
headerScroll.setOverscroll(false,false)
|
||||
headerScroll.setScrollingDisabled(false, true)
|
||||
// Measure header height, most likely its final value
|
||||
removePage(addPage("Dummy"))
|
||||
add(headerScroll).growX().minHeight(headerHeight).row()
|
||||
add(contentScroll).grow().row()
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region Widget interface
|
||||
|
||||
// The following are part of the Widget interface and serve dynamic sizing
|
||||
override fun getPrefWidth() = preferredWidth
|
||||
fun setPrefWidth(width: Float) {
|
||||
if (width !in minimumWidth..maximumWidth) throw IllegalArgumentException()
|
||||
preferredWidth = width
|
||||
invalidateHierarchy()
|
||||
}
|
||||
override fun getPrefHeight() = preferredHeight + headerHeight
|
||||
fun setPrefHeight(height: Float) {
|
||||
if (height - headerHeight !in minimumHeight..maximumHeight) throw IllegalArgumentException()
|
||||
preferredHeight = height - headerHeight
|
||||
invalidateHierarchy()
|
||||
}
|
||||
override fun getMinWidth() = minimumWidth
|
||||
override fun getMaxWidth() = maximumWidth
|
||||
override fun getMinHeight() = headerHeight + minimumHeight
|
||||
override fun getMaxHeight() = headerHeight + maximumHeight
|
||||
|
||||
//endregion
|
||||
//region API
|
||||
|
||||
/** @return Number of pages currently stored */
|
||||
fun pageCount() = pages.size
|
||||
|
||||
/** @return index of a page by its (untranslated) caption, or -1 if no such page exists */
|
||||
fun getPageIndex(caption: String) = pages.indexOfLast { it.button.name == caption }
|
||||
|
||||
/** Change the selected page by using its index.
|
||||
* @param index Page number or -1 to deselect the current page.
|
||||
* @return `true` if the page was successfully changed.
|
||||
*/
|
||||
fun selectPage(index: Int): Boolean {
|
||||
if (index !in -1 until pages.size) return false
|
||||
if (activePage == index) return false
|
||||
if (index >= 0 && pages[index].disabled) return false
|
||||
if (activePage != -1) {
|
||||
pages[activePage].apply {
|
||||
button.color = Color.WHITE
|
||||
scrollX = contentScroll.scrollX
|
||||
scrollY = contentScroll.scrollY
|
||||
contentScroll.removeActor(content)
|
||||
}
|
||||
}
|
||||
activePage = index
|
||||
if (index != -1) {
|
||||
pages[index].apply {
|
||||
button.color = highlightColor
|
||||
contentScroll.actor = content
|
||||
contentScroll.layout()
|
||||
if (scrollX < 0f) // was marked to center on first show
|
||||
scrollX = ((content.width - this@TabbedPager.width) / 2).coerceIn(0f, contentScroll.maxX)
|
||||
contentScroll.scrollX = scrollX
|
||||
contentScroll.scrollY = scrollY
|
||||
contentScroll.updateVisualScroll()
|
||||
headerScroll.let {
|
||||
it.scrollX = (buttonX + (buttonW - it.width) / 2).coerceIn(0f, it.maxX)
|
||||
}
|
||||
onActivation?.invoke(index, button.name)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/** Change the selected page by using its caption.
|
||||
* @param caption Caption of the page to select. A nonexistent name will deselect the current page.
|
||||
* @return `true` if the page was successfully changed.
|
||||
*/
|
||||
fun selectPage(caption: String) = selectPage(getPageIndex(caption))
|
||||
private fun selectPage(page: PageState) = selectPage(getPageIndex(page))
|
||||
|
||||
/** Change the disabled property of a page by its index.
|
||||
* @return previous value or `false` if index invalid.
|
||||
*/
|
||||
fun setPageDisabled(index: Int, disabled: Boolean): Boolean {
|
||||
if (index !in 0 until pages.size) return false
|
||||
val page = pages[index]
|
||||
val oldValue = page.disabled
|
||||
page.disabled = disabled
|
||||
page.button.isEnabled = !disabled
|
||||
if (disabled && index == activePage) selectPage(-1)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
/** Change the disabled property of a page by its caption.
|
||||
* @return previous value or `false` if caption not found.
|
||||
*/
|
||||
fun setPageDisabled(caption: String, disabled: Boolean) = setPageDisabled(getPageIndex(caption), disabled)
|
||||
|
||||
/** Remove a page by its index.
|
||||
* @return `true` if page successfully removed */
|
||||
fun removePage(index: Int): Boolean {
|
||||
if (index !in 0 until pages.size) return false
|
||||
if (index == activePage) selectPage(-1)
|
||||
val page = pages.removeAt(index)
|
||||
header.getCell(page.button).clearActor()
|
||||
header.cells.removeIndex(index)
|
||||
return true
|
||||
}
|
||||
|
||||
/** Remove a page by its caption.
|
||||
* @return `true` if page successfully removed */
|
||||
fun removePage(caption: String) = removePage(getPageIndex(caption))
|
||||
|
||||
/** Replace a page's content by its index. */
|
||||
fun replacePage(index: Int, content: Actor) {
|
||||
if (index !in 0 until pages.size) return
|
||||
val isActive = index == activePage
|
||||
if (isActive) selectPage(-1)
|
||||
pages[index].content = content
|
||||
if (isActive) selectPage(index)
|
||||
}
|
||||
|
||||
/** Replace a page's content by its caption. */
|
||||
fun replacePage(caption: String, content: Actor) = replacePage(getPageIndex(caption), content)
|
||||
|
||||
/** Add a page!
|
||||
* @param caption Text to be shown on the header button (automatically translated), can later be used to reference the page in other calls.
|
||||
* @param content Actor to show when this page is selected.
|
||||
* @param icon Actor, typically an [Image], to show before the caption.
|
||||
* @param iconSize Size for [icon] - if not zero, the icon is wrapped to allow a [setSize] even on [Image] which ignores size.
|
||||
* @param insertBefore -1 to add at the end or index of existing page to insert this before
|
||||
* @param secret Marks page as 'secret'. A password is asked once per [TabbedPager] and if it does not match the has passed in the constructor the page and all subsequent secret pages are dropped.
|
||||
* @param disabled Initial disabled state. Disabled pages cannot be selected even with [selectPage], their button is dimmed.
|
||||
* @param onActivation _Optional_ callback called when this page is shown (per actual change to this page, not per header click). Lambda arguments are page index and caption.
|
||||
* @return The new page's index or -1 if it could not be immediately added (secret).
|
||||
*/
|
||||
fun addPage(
|
||||
caption: String,
|
||||
content: Actor? = null,
|
||||
icon: Actor? = null,
|
||||
iconSize: Float = 0f,
|
||||
insertBefore: Int = -1,
|
||||
secret: Boolean = false,
|
||||
disabled: Boolean = false,
|
||||
onActivation: ((Int, String)->Unit)? = null
|
||||
): Int {
|
||||
// Build page descriptor and header button
|
||||
val page = PageState(content ?: Group(), disabled, onActivation)
|
||||
page.button.apply {
|
||||
name = caption // enable finding pages by untranslated caption without needing our own field
|
||||
if (icon != null) {
|
||||
if (iconSize != 0f) {
|
||||
val wrapper = Group().apply {
|
||||
isTransform =
|
||||
false // performance helper - nothing here is rotated or scaled
|
||||
setSize(iconSize, iconSize)
|
||||
icon.setSize(iconSize, iconSize)
|
||||
icon.center(this)
|
||||
addActor(icon)
|
||||
}
|
||||
add(wrapper).padRight(headerPadding * 0.5f)
|
||||
} else {
|
||||
add(icon)
|
||||
}
|
||||
}
|
||||
add(caption.toLabel(headerFontColor, headerFontSize))
|
||||
isEnabled = !disabled
|
||||
onClick {
|
||||
selectPage(page)
|
||||
}
|
||||
pack()
|
||||
if (height + 2 * headerPadding > headerHeight) {
|
||||
headerHeight = height + 2 * headerPadding
|
||||
if (activePage >= 0) this@TabbedPager.invalidateHierarchy()
|
||||
}
|
||||
}
|
||||
|
||||
// Support 'secret' pages
|
||||
if (secret) {
|
||||
deferredSecretPages.addLast(page)
|
||||
return -1
|
||||
}
|
||||
|
||||
return addAndShowPage(page, insertBefore)
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate any [secret][addPage] pages by asking for the password.
|
||||
*
|
||||
* If the parent of this Widget is a Popup, then this needs to be called _after_ the parent
|
||||
* is shown to ensure proper popup stacking.
|
||||
*/
|
||||
fun askForPassword(secretHashCode: Int = 0) {
|
||||
class PassPopup(screen: CameraStageBaseScreen, unlockAction: ()->Unit, lockAction: ()->Unit) : Popup(screen) {
|
||||
val passEntry = TextField("", CameraStageBaseScreen.skin)
|
||||
init {
|
||||
passEntry.isPasswordMode = true
|
||||
add(passEntry).row()
|
||||
addOKButton {
|
||||
if (passEntry.text.hashCode() == secretHashCode) unlockAction() else lockAction()
|
||||
}
|
||||
this.keyboardFocus = passEntry
|
||||
}
|
||||
}
|
||||
|
||||
if (!UncivGame.isCurrentInitialized() || askPasswordLock || deferredSecretPages.isEmpty()) return
|
||||
askPasswordLock = true // race condition: Popup closes _first_, then deferredSecretPages is emptied -> parent shows and calls us again
|
||||
|
||||
PassPopup(UncivGame.Current.screen as CameraStageBaseScreen, {
|
||||
addDeferredSecrets()
|
||||
}, {
|
||||
deferredSecretPages.clear()
|
||||
}).open(true)
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region Helper routines
|
||||
|
||||
private fun getPageIndex(page: PageState) = pages.indexOf(page)
|
||||
|
||||
private fun addAndShowPage(page: PageState, insertBefore: Int): Int {
|
||||
// Update pages array and header table
|
||||
val newIndex: Int
|
||||
val buttonCell: Cell<Button>
|
||||
if (insertBefore >= 0 && insertBefore < pages.size) {
|
||||
newIndex = insertBefore
|
||||
pages.add(insertBefore, page)
|
||||
header.addActorAt(insertBefore, page.button)
|
||||
buttonCell = header.getCell(page.button)
|
||||
} else {
|
||||
newIndex = pages.size
|
||||
pages.add(page)
|
||||
buttonCell = header.add(page.button)
|
||||
}
|
||||
page.buttonX = if (newIndex == 0) 0f else pages[newIndex-1].run { buttonX + buttonW }
|
||||
page.buttonW = buttonCell.run { prefWidth + padLeft + padRight }
|
||||
for (i in newIndex + 1 until pages.size)
|
||||
pages[i].buttonX += page.buttonW
|
||||
|
||||
// Content Sizing
|
||||
if (page.content is WidgetGroup) {
|
||||
(page.content as WidgetGroup).packIfNeeded()
|
||||
val contentWidth = min(page.content.width, limitWidth)
|
||||
if (contentWidth > preferredWidth) {
|
||||
preferredWidth = contentWidth
|
||||
if (activePage >= 0) invalidateHierarchy()
|
||||
}
|
||||
val contentHeight = min(page.content.height, limitHeight)
|
||||
if (contentHeight > preferredHeight) {
|
||||
preferredHeight = contentHeight
|
||||
if (activePage >= 0) invalidateHierarchy()
|
||||
}
|
||||
page.scrollX = -1f // mark to center later when all pages are measured
|
||||
}
|
||||
if (growMaxWidth) maximumWidth = minimumWidth
|
||||
if (growMaxHeight) maximumHeight = minimumHeight
|
||||
|
||||
return newIndex
|
||||
}
|
||||
|
||||
private fun addDeferredSecrets() {
|
||||
while (true) {
|
||||
val page = deferredSecretPages.removeFirstOrNull() ?: return
|
||||
addAndShowPage(page, -1)
|
||||
}
|
||||
}
|
||||
}
|
44
core/src/com/unciv/ui/utils/WrappableLabel.kt
Normal file
44
core/src/com/unciv/ui/utils/WrappableLabel.kt
Normal file
@ -0,0 +1,44 @@
|
||||
package com.unciv.ui.utils
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||
import com.unciv.models.translations.tr
|
||||
import kotlin.math.min
|
||||
|
||||
/** A [Label] that unlike the original participates correctly in layout
|
||||
* Caveat: You still need to turn wrap on _after_ instantiation, doing it here in init leads to hell.
|
||||
*
|
||||
* @param text Automatically translated text
|
||||
* @param expectedWidth Upper limit for the preferred width the Label will report
|
||||
*/
|
||||
class WrappableLabel(
|
||||
text: String,
|
||||
private val expectedWidth: Float,
|
||||
fontColor: Color = Color.WHITE,
|
||||
fontSize: Int = 18
|
||||
) : Label(text.tr(), CameraStageBaseScreen.skin) {
|
||||
private var _measuredWidth = 0f
|
||||
|
||||
init {
|
||||
if (fontColor != Color.WHITE || fontSize!=18) {
|
||||
val style = LabelStyle(this.style)
|
||||
style.fontColor = fontColor
|
||||
if (fontSize != 18) {
|
||||
style.font = Fonts.font
|
||||
setFontScale(fontSize / Fonts.ORIGINAL_FONT_SIZE)
|
||||
}
|
||||
setStyle(style)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setWrap(wrap: Boolean) {
|
||||
_measuredWidth = super.getPrefWidth()
|
||||
super.setWrap(wrap)
|
||||
}
|
||||
|
||||
private fun getMeasuredWidth(): Float = if (wrap) _measuredWidth else super.getPrefWidth()
|
||||
|
||||
override fun getMinWidth() = 48f // ~ 2 chars
|
||||
override fun getPrefWidth() = min(getMeasuredWidth(), expectedWidth)
|
||||
override fun getMaxWidth() = getMeasuredWidth()
|
||||
}
|
@ -3,93 +3,157 @@ package com.unciv.ui.worldscreen.mainmenu
|
||||
import com.badlogic.gdx.Application
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.*
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.Constants
|
||||
import com.unciv.MainMenuScreen
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.models.UncivSound
|
||||
import com.unciv.models.metadata.BaseRuleset
|
||||
import com.unciv.models.ruleset.Ruleset.CheckModLinksStatus
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.tilesets.TileSetCache
|
||||
import com.unciv.models.translations.TranslationFileWriter
|
||||
import com.unciv.models.translations.Translations
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.civilopedia.MarkupRenderer
|
||||
import com.unciv.ui.civilopedia.SimpleCivilopediaText
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.utils.LanguageTable.Companion.addLanguageTables
|
||||
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||
import com.unciv.ui.worldscreen.WorldScreen
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.math.min
|
||||
import com.badlogic.gdx.utils.Array as GdxArray
|
||||
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
|
||||
|
||||
class Language(val language:String, val percentComplete:Int){
|
||||
override fun toString(): String {
|
||||
val spaceSplitLang = language.replace("_"," ")
|
||||
return "$spaceSplitLang - $percentComplete%"
|
||||
}
|
||||
}
|
||||
|
||||
class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScreen) {
|
||||
private var selectedLanguage: String = "English"
|
||||
/**
|
||||
* The Options (Settings) Popup
|
||||
* @param previousScreen Tha caller - note if this is a [WorldScreen] or [MainMenuScreen] they will be rebuilt when major options change.
|
||||
*/
|
||||
//region Fields
|
||||
class OptionsPopup(val previousScreen: CameraStageBaseScreen) : Popup(previousScreen) {
|
||||
private val settings = previousScreen.game.settings
|
||||
private val optionsTable = Table(CameraStageBaseScreen.skin)
|
||||
private val resolutionArray = GdxArray(arrayOf("750x500", "900x600", "1050x700", "1200x800", "1500x1000"))
|
||||
private val tabs: TabbedPager
|
||||
private val resolutionArray = com.badlogic.gdx.utils.Array(arrayOf("750x500", "900x600", "1050x700", "1200x800", "1500x1000"))
|
||||
private var modCheckFirstRun = true // marker for automatic first run on selecting the page
|
||||
private var modCheckCheckBox: CheckBox? = null
|
||||
private var modCheckResultCell: Cell<Actor>? = null
|
||||
private val selectBoxMinWidth: Float
|
||||
|
||||
//endregion
|
||||
|
||||
init {
|
||||
settings.addCompletedTutorialTask("Open the options table")
|
||||
|
||||
optionsTable.defaults().pad(2.5f)
|
||||
rebuildOptionsTable()
|
||||
innerTable.pad(0f)
|
||||
val tabMaxWidth: Float
|
||||
val tabMinWidth: Float
|
||||
val tabMaxHeight: Float
|
||||
previousScreen.run {
|
||||
selectBoxMinWidth = if (stage.width < 600f) 200f else 240f
|
||||
tabMaxWidth = if (isPortrait()) stage.width - 10f else 0.8f * stage.width
|
||||
tabMinWidth = 0.6f * stage.width
|
||||
tabMaxHeight = (if (isPortrait()) 0.7f else 0.8f) * stage.height
|
||||
}
|
||||
tabs = TabbedPager(tabMinWidth, tabMaxWidth, 0f, tabMaxHeight,
|
||||
headerFontSize = 21, backgroundColor = Color.CLEAR, capacity = 8)
|
||||
add(tabs).pad(0f).grow().row()
|
||||
|
||||
val scrollPane = ScrollPane(optionsTable, skin)
|
||||
scrollPane.setOverscroll(false, false)
|
||||
scrollPane.fadeScrollBars = false
|
||||
scrollPane.setScrollingDisabled(true, false)
|
||||
add(scrollPane).maxHeight(screen.stage.height * 0.6f).row()
|
||||
tabs.addPage("About", getAboutTab(), ImageGetter.getExternalImage("Icon.png"), 24f)
|
||||
tabs.addPage("Display", getDisplayTab(), ImageGetter.getImage("UnitPromotionIcons/Scouting"), 24f)
|
||||
tabs.addPage("Gameplay", getGamePlayTab(), ImageGetter.getImage("OtherIcons/Options"), 24f)
|
||||
tabs.addPage("Language", getLanguageTab(), ImageGetter.getImage("FlagIcons/${settings.language}"), 24f)
|
||||
tabs.addPage("Sound", getSoundTab(), ImageGetter.getImage("OtherIcons/Speaker"), 24f)
|
||||
// at the moment the notification service only exists on Android
|
||||
if (Gdx.app.type == Application.ApplicationType.Android)
|
||||
tabs.addPage("Multiplayer", getMultiplayerTab(), ImageGetter.getImage("OtherIcons/Multiplayer"), 24f)
|
||||
tabs.addPage("Advanced", getAdvancedTab(), ImageGetter.getImage("OtherIcons/Settings"), 24f)
|
||||
if (RulesetCache.size > 1) {
|
||||
tabs.addPage("Locate mod errors", getModCheckTab(), ImageGetter.getImage("OtherIcons/Mods"), 24f) { _, _ ->
|
||||
if (modCheckFirstRun) runModChecker()
|
||||
}
|
||||
}
|
||||
if (Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT) && Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)) {
|
||||
tabs.addPage("Debug", getDebugTab(), ImageGetter.getImage("OtherIcons/SecretOptions"), 24f, secret = true)
|
||||
}
|
||||
|
||||
addCloseButton {
|
||||
previousScreen.game.limitOrientationsHelper?.allowPortrait(settings.allowAndroidPortrait)
|
||||
if (previousScreen is WorldScreen)
|
||||
previousScreen.enableNextTurnButtonAfterOptions()
|
||||
}
|
||||
}.padBottom(10f)
|
||||
|
||||
pack() // Needed to show the background.
|
||||
center(previousScreen.stage)
|
||||
}
|
||||
|
||||
private fun addHeader(text: String) {
|
||||
optionsTable.add(text.toLabel(fontSize = 24)).colspan(2).padTop(if (optionsTable.cells.isEmpty) 0f else 20f).row()
|
||||
}
|
||||
|
||||
private fun addYesNoRow(text: String, initialValue: Boolean, updateWorld: Boolean = false, action: ((Boolean) -> Unit)) {
|
||||
optionsTable.add(text.toLabel())
|
||||
val button = YesNoButton(initialValue, CameraStageBaseScreen.skin) {
|
||||
action(it)
|
||||
settings.save()
|
||||
if (updateWorld && previousScreen is WorldScreen)
|
||||
previousScreen.shouldUpdate = true
|
||||
}
|
||||
optionsTable.add(button).row()
|
||||
override fun setVisible(visible: Boolean) {
|
||||
super.setVisible(visible)
|
||||
if (!visible) return
|
||||
tabs.askForPassword(secretHashCode = 2747985)
|
||||
if (tabs.activePage < 0) tabs.selectPage(2)
|
||||
}
|
||||
|
||||
/** Reload this Popup after major changes (resolution, tileset, language) */
|
||||
private fun reloadWorldAndOptions() {
|
||||
settings.save()
|
||||
if (previousScreen is WorldScreen) {
|
||||
previousScreen.game.worldScreen = WorldScreen(previousScreen.gameInfo, previousScreen.viewingCiv)
|
||||
previousScreen.game.setWorldScreen()
|
||||
|
||||
} else if (previousScreen is MainMenuScreen) {
|
||||
previousScreen.game.setScreen(MainMenuScreen())
|
||||
}
|
||||
(previousScreen.game.screen as CameraStageBaseScreen).openOptionsPopup()
|
||||
}
|
||||
|
||||
private fun rebuildOptionsTable() {
|
||||
settings.save()
|
||||
optionsTable.clear()
|
||||
//region Page builders
|
||||
|
||||
addHeader("Display options")
|
||||
private fun getAboutTab(): Table {
|
||||
defaults().pad(5f)
|
||||
val version = previousScreen.game.version
|
||||
val versionAnchor = version.replace(".","")
|
||||
val lines = sequence {
|
||||
yield(FormattedLine(extraImage = "banner", imageSize = 240f, centered = true))
|
||||
yield(FormattedLine())
|
||||
yield(FormattedLine("{Version}: $version", link = "https://github.com/yairm210/Unciv/blob/master/changelog.md#$versionAnchor"))
|
||||
yield(FormattedLine("See online Readme", link = "https://github.com/yairm210/Unciv/blob/master/README.md#unciv---foss-civ-v-for-androiddesktop"))
|
||||
yield(FormattedLine("Visit repository", link = "https://github.com/yairm210/Unciv"))
|
||||
}
|
||||
return MarkupRenderer.render(lines.toList()).pad(20f)
|
||||
}
|
||||
|
||||
private fun getLanguageTab() = Table(CameraStageBaseScreen.skin).apply {
|
||||
val languageTables = this.addLanguageTables(tabs.prefWidth * 0.9f - 10f)
|
||||
|
||||
var chosenLanguage = settings.language
|
||||
fun selectLanguage() {
|
||||
settings.language = chosenLanguage
|
||||
previousScreen.game.translations.tryReadTranslationForCurrentLanguage()
|
||||
reloadWorldAndOptions()
|
||||
}
|
||||
fun updateSelection() {
|
||||
languageTables.forEach { it.update(chosenLanguage) }
|
||||
if (chosenLanguage != settings.language)
|
||||
selectLanguage()
|
||||
}
|
||||
updateSelection()
|
||||
|
||||
languageTables.forEach {
|
||||
it.onClick {
|
||||
chosenLanguage = it.language
|
||||
updateSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDisplayTab() = Table(CameraStageBaseScreen.skin).apply {
|
||||
pad(10f)
|
||||
defaults().pad(2.5f)
|
||||
|
||||
addYesNoRow("Show worked tiles", settings.showWorkedTiles, true) { settings.showWorkedTiles = it }
|
||||
addYesNoRow("Show resources and improvements", settings.showResourcesAndImprovements, true) { settings.showResourcesAndImprovements = it }
|
||||
@ -100,8 +164,6 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
|
||||
addYesNoRow("Show pixel units", settings.showPixelUnits, true) { settings.showPixelUnits = it }
|
||||
addYesNoRow("Show pixel improvements", settings.showPixelImprovements, true) { settings.showPixelImprovements = it }
|
||||
|
||||
addLanguageSelectBox()
|
||||
|
||||
addResolutionSelectBox()
|
||||
|
||||
addTileSetSelectBox()
|
||||
@ -112,16 +174,21 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
|
||||
}
|
||||
|
||||
val continuousRenderingDescription = "When disabled, saves battery life but certain animations will be suspended"
|
||||
optionsTable.add(continuousRenderingDescription.toLabel(fontSize = 14)).colspan(2).padTop(20f).row()
|
||||
|
||||
addHeader("Gameplay options")
|
||||
val continuousRenderingLabel = WrappableLabel(continuousRenderingDescription,
|
||||
tabs.prefWidth, Color.ORANGE.cpy().lerp(Color.WHITE, 0.7f), 14)
|
||||
continuousRenderingLabel.wrap = true
|
||||
add(continuousRenderingLabel).colspan(2).padTop(10f).row()
|
||||
}
|
||||
|
||||
private fun getGamePlayTab() = Table(CameraStageBaseScreen.skin).apply {
|
||||
pad(10f)
|
||||
defaults().pad(5f)
|
||||
addYesNoRow("Check for idle units", settings.checkForDueUnits, true) { settings.checkForDueUnits = it }
|
||||
addYesNoRow("Move units with a single tap", settings.singleTapMove) { settings.singleTapMove = it }
|
||||
addYesNoRow("Auto-assign city production", settings.autoAssignCityProduction, true) {
|
||||
settings.autoAssignCityProduction = it
|
||||
if (it && previousScreen is WorldScreen &&
|
||||
previousScreen.viewingCiv.isCurrentPlayer() && previousScreen.viewingCiv.playerType == PlayerType.Human) {
|
||||
previousScreen.viewingCiv.isCurrentPlayer() && previousScreen.viewingCiv.playerType == PlayerType.Human) {
|
||||
previousScreen.gameInfo.currentPlayerCiv.cities.forEach { city ->
|
||||
city.cityConstructions.chooseNextConstruction()
|
||||
}
|
||||
@ -130,25 +197,54 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
|
||||
addYesNoRow("Auto-build roads", settings.autoBuildingRoads) { settings.autoBuildingRoads = it }
|
||||
addYesNoRow("Automated workers replace improvements", settings.automatedWorkersReplaceImprovements) { settings.automatedWorkersReplaceImprovements = it }
|
||||
addYesNoRow("Order trade offers by amount", settings.orderTradeOffersByAmount) { settings.orderTradeOffersByAmount = it }
|
||||
}
|
||||
|
||||
private fun getSoundTab() = Table(CameraStageBaseScreen.skin).apply {
|
||||
pad(10f)
|
||||
defaults().pad(5f)
|
||||
|
||||
addSoundEffectsVolumeSlider()
|
||||
|
||||
val musicLocation = Gdx.files.local(previousScreen.game.musicLocation)
|
||||
if (musicLocation.exists())
|
||||
addMusicVolumeSlider()
|
||||
else
|
||||
addDownloadMusic(musicLocation)
|
||||
}
|
||||
|
||||
private fun getMultiplayerTab(): Table = Table(CameraStageBaseScreen.skin).apply {
|
||||
pad(10f)
|
||||
defaults().pad(5f)
|
||||
|
||||
addYesNoRow("Enable out-of-game turn notifications", settings.multiplayerTurnCheckerEnabled) {
|
||||
settings.multiplayerTurnCheckerEnabled = it
|
||||
settings.save()
|
||||
tabs.replacePage("Multiplayer", getMultiplayerTab())
|
||||
}
|
||||
|
||||
if (settings.multiplayerTurnCheckerEnabled) {
|
||||
addMultiplayerTurnCheckerDelayBox()
|
||||
|
||||
addYesNoRow("Show persistent notification for turn notifier service", settings.multiplayerTurnCheckerPersistentNotificationEnabled)
|
||||
{ settings.multiplayerTurnCheckerPersistentNotificationEnabled = it }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAdvancedTab() = Table(CameraStageBaseScreen.skin).apply {
|
||||
pad(10f)
|
||||
defaults().pad(5f)
|
||||
|
||||
addAutosaveTurnsSelectBox()
|
||||
|
||||
// at the moment the notification service only exists on Android
|
||||
addNotificationOptions()
|
||||
|
||||
addHeader("Other options")
|
||||
|
||||
|
||||
addYesNoRow("{Show experimental world wrap for maps}\n{HIGHLY EXPERIMENTAL - YOU HAVE BEEN WARNED!}".tr(),
|
||||
settings.showExperimentalWorldWrap) {
|
||||
addYesNoRow("{Show experimental world wrap for maps}\n{HIGHLY EXPERIMENTAL - YOU HAVE BEEN WARNED!}",
|
||||
settings.showExperimentalWorldWrap) {
|
||||
settings.showExperimentalWorldWrap = it
|
||||
}
|
||||
addYesNoRow("{Enable experimental religion in start games}\n{HIGHLY EXPERIMENTAL - UPDATES WILL BREAK SAVES!}".tr(),
|
||||
settings.showExperimentalReligion) {
|
||||
addYesNoRow("{Enable experimental religion in start games}\n{HIGHLY EXPERIMENTAL - UPDATES WILL BREAK SAVES!}",
|
||||
settings.showExperimentalReligion) {
|
||||
settings.showExperimentalReligion = it
|
||||
}
|
||||
|
||||
|
||||
if (previousScreen.game.limitOrientationsHelper != null) {
|
||||
addYesNoRow("Enable portrait orientation", settings.allowAndroidPortrait) {
|
||||
settings.allowAndroidPortrait = it
|
||||
@ -157,26 +253,77 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
|
||||
}
|
||||
}
|
||||
|
||||
addSoundEffectsVolumeSlider()
|
||||
addMusicVolumeSlider()
|
||||
|
||||
addTranslationGeneration()
|
||||
addModCheckerPopup()
|
||||
addSetUserId()
|
||||
|
||||
optionsTable.add("Version".toLabel()).pad(10f)
|
||||
val versionLabel = previousScreen.game.version.toLabel()
|
||||
if (previousScreen.game.version[0] in '0'..'9')
|
||||
versionLabel.onClick {
|
||||
val url = "https://github.com/yairm210/Unciv/blob/master/changelog.md#" +
|
||||
previousScreen.game.version.replace(".","")
|
||||
Gdx.net.openURI(url)
|
||||
}
|
||||
optionsTable.add(versionLabel).pad(10f).row()
|
||||
addSetUserId()
|
||||
}
|
||||
|
||||
private fun addMinimapSizeSlider() {
|
||||
optionsTable.add("Show minimap".tr())
|
||||
private fun getModCheckTab() = Table(CameraStageBaseScreen.skin).apply {
|
||||
defaults().pad(10f).align(Align.top)
|
||||
modCheckCheckBox = "Check extension mods based on vanilla".toCheckBox {
|
||||
runModChecker(it)
|
||||
}
|
||||
add(modCheckCheckBox).row()
|
||||
modCheckResultCell = add("Checking mods for errors...".toLabel())
|
||||
}
|
||||
|
||||
private fun runModChecker(complex: Boolean = false) {
|
||||
modCheckFirstRun = false
|
||||
if (modCheckCheckBox == null) return
|
||||
modCheckCheckBox!!.disable()
|
||||
if (modCheckResultCell == null) return
|
||||
thread(name="ModChecker") {
|
||||
val lines = ArrayList<FormattedLine>()
|
||||
var noProblem = true
|
||||
for (mod in RulesetCache.values.sortedBy { it.name }) {
|
||||
val modLinks = if (complex) RulesetCache.checkCombinedModLinks(linkedSetOf(mod.name))
|
||||
else mod.checkModLinks()
|
||||
val color = when (modLinks.status) {
|
||||
CheckModLinksStatus.OK -> "#0F0"
|
||||
CheckModLinksStatus.Warning -> "#FF0"
|
||||
CheckModLinksStatus.Error -> "#F00"
|
||||
}
|
||||
val label = if (mod.name.isEmpty()) BaseRuleset.Civ_V_Vanilla.fullName else mod.name
|
||||
lines += FormattedLine("$label{}", starred = true, color = color, header = 3)
|
||||
if (modLinks.isNotOK()) {
|
||||
lines += FormattedLine(modLinks.message)
|
||||
noProblem = false
|
||||
}
|
||||
lines += FormattedLine()
|
||||
}
|
||||
if (noProblem) lines += FormattedLine("{No problems found}.")
|
||||
|
||||
Gdx.app.postRunnable {
|
||||
val result = SimpleCivilopediaText(lines).renderCivilopediaText(tabs.prefWidth - 25f)
|
||||
modCheckResultCell?.setActor(result)
|
||||
modCheckCheckBox!!.enable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDebugTab() = Table(CameraStageBaseScreen.skin).apply {
|
||||
pad(10f)
|
||||
defaults().pad(5f)
|
||||
|
||||
val game = UncivGame.Current
|
||||
add("Supercharged".toCheckBox(game.superchargedForDebug) {
|
||||
game.superchargedForDebug = it
|
||||
}).row()
|
||||
add("View entire map".toCheckBox(game.viewEntireMapForDebug) {
|
||||
game.viewEntireMapForDebug = it
|
||||
}).row()
|
||||
if (game.isGameInfoInitialized()) {
|
||||
add("God mode (current game)".toCheckBox(game.gameInfo.gameParameters.godMode) {
|
||||
game.gameInfo.gameParameters.godMode = it
|
||||
}).row()
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region Row builders
|
||||
|
||||
private fun Table.addMinimapSizeSlider() {
|
||||
add("Show minimap".toLabel()).left().fillX()
|
||||
|
||||
// The meaning of the values needs a formula to be synchronized between here and
|
||||
// [Minimap.init]. It goes off-10%-11%..29%-30%-35%-40%-45%-50% - and the percentages
|
||||
@ -203,49 +350,161 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
|
||||
if (previousScreen is WorldScreen)
|
||||
previousScreen.shouldUpdate = true
|
||||
}
|
||||
optionsTable.add(minimapSlider).pad(10f).row()
|
||||
add(minimapSlider).pad(10f).row()
|
||||
}
|
||||
|
||||
private fun addSetUserId() {
|
||||
val idSetLabel = "".toLabel()
|
||||
val takeUserIdFromClipboardButton = "Take user ID from clipboard".toTextButton()
|
||||
.onClick {
|
||||
try {
|
||||
val clipboardContents = Gdx.app.clipboard.contents.trim()
|
||||
UUID.fromString(clipboardContents)
|
||||
YesNoPopup("Doing this will reset your current user ID to the clipboard contents - are you sure?",
|
||||
{
|
||||
settings.userId = clipboardContents
|
||||
settings.save()
|
||||
idSetLabel.setFontColor(Color.WHITE).setText("ID successfully set!".tr())
|
||||
}, previousScreen).open(true)
|
||||
idSetLabel.isVisible = true
|
||||
} catch (ex: Exception) {
|
||||
idSetLabel.isVisible = true
|
||||
idSetLabel.setFontColor(Color.RED).setText("Invalid ID!".tr())
|
||||
private fun Table.addResolutionSelectBox() {
|
||||
add("Resolution".toLabel()).left().fillX()
|
||||
|
||||
val resolutionSelectBox = SelectBox<String>(skin)
|
||||
resolutionSelectBox.items = resolutionArray
|
||||
resolutionSelectBox.selected = settings.resolution
|
||||
add(resolutionSelectBox).minWidth(selectBoxMinWidth).pad(10f).row()
|
||||
|
||||
resolutionSelectBox.onChange {
|
||||
settings.resolution = resolutionSelectBox.selected
|
||||
reloadWorldAndOptions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Table.addTileSetSelectBox() {
|
||||
add("Tileset".toLabel()).left().fillX()
|
||||
|
||||
val tileSetSelectBox = SelectBox<String>(skin)
|
||||
val tileSetArray = GdxArray<String>()
|
||||
val tileSets = ImageGetter.getAvailableTilesets()
|
||||
for (tileset in tileSets) tileSetArray.add(tileset)
|
||||
tileSetSelectBox.items = tileSetArray
|
||||
tileSetSelectBox.selected = settings.tileSet
|
||||
add(tileSetSelectBox).minWidth(selectBoxMinWidth).pad(10f).row()
|
||||
|
||||
tileSetSelectBox.onChange {
|
||||
settings.tileSet = tileSetSelectBox.selected
|
||||
TileSetCache.assembleTileSetConfigs()
|
||||
reloadWorldAndOptions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Table.addSoundEffectsVolumeSlider() {
|
||||
add("Sound effects volume".tr()).left().fillX()
|
||||
|
||||
val soundEffectsVolumeSlider = UncivSlider(0f, 1.0f, 0.1f,
|
||||
initial = settings.soundEffectsVolume
|
||||
) {
|
||||
settings.soundEffectsVolume = it
|
||||
settings.save()
|
||||
}
|
||||
add(soundEffectsVolumeSlider).pad(5f).row()
|
||||
}
|
||||
|
||||
private fun Table.addMusicVolumeSlider() {
|
||||
add("Music volume".tr()).left().fillX()
|
||||
|
||||
val musicVolumeSlider = UncivSlider(0f, 1.0f, 0.1f,
|
||||
initial = settings.musicVolume,
|
||||
sound = UncivSound.Silent
|
||||
) {
|
||||
settings.musicVolume = it
|
||||
settings.save()
|
||||
|
||||
val music = previousScreen.game.music
|
||||
if (music == null) // restart music, if it was off at the app start
|
||||
thread(name = "Music") { previousScreen.game.startMusic() }
|
||||
|
||||
music?.volume = 0.4f * it
|
||||
}
|
||||
musicVolumeSlider.value = settings.musicVolume
|
||||
add(musicVolumeSlider).pad(5f).row()
|
||||
}
|
||||
|
||||
private fun Table.addDownloadMusic(musicLocation: FileHandle) {
|
||||
val downloadMusicButton = "Download music".toTextButton()
|
||||
add(downloadMusicButton).colspan(2).row()
|
||||
val errorTable = Table()
|
||||
add(errorTable).colspan(2).row()
|
||||
|
||||
downloadMusicButton.onClick {
|
||||
downloadMusicButton.disable()
|
||||
errorTable.clear()
|
||||
errorTable.add("Downloading...".toLabel())
|
||||
|
||||
// So the whole game doesn't get stuck while downloading the file
|
||||
thread(name = "Music") {
|
||||
try {
|
||||
val file = DropBox.downloadFile("/Music/thatched-villagers.mp3")
|
||||
musicLocation.write(file, false)
|
||||
Gdx.app.postRunnable {
|
||||
tabs.replacePage("Sound", getSoundTab())
|
||||
previousScreen.game.startMusic()
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Gdx.app.postRunnable {
|
||||
errorTable.clear()
|
||||
errorTable.add("Could not download music!".toLabel(Color.RED))
|
||||
}
|
||||
}
|
||||
optionsTable.add(takeUserIdFromClipboardButton).pad(5f).colspan(2).row()
|
||||
optionsTable.add(idSetLabel).colspan(2).row()
|
||||
}
|
||||
|
||||
private fun addNotificationOptions() {
|
||||
if (Gdx.app.type == Application.ApplicationType.Android) {
|
||||
addHeader("Multiplayer options")
|
||||
|
||||
addYesNoRow("Enable out-of-game turn notifications", settings.multiplayerTurnCheckerEnabled)
|
||||
{ settings.multiplayerTurnCheckerEnabled = it }
|
||||
|
||||
if (settings.multiplayerTurnCheckerEnabled) {
|
||||
addMultiplayerTurnCheckerDelayBox()
|
||||
|
||||
addYesNoRow("Show persistent notification for turn notifier service", settings.multiplayerTurnCheckerPersistentNotificationEnabled)
|
||||
{ settings.multiplayerTurnCheckerPersistentNotificationEnabled = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTranslationGeneration() {
|
||||
private fun Table.addMultiplayerTurnCheckerDelayBox() {
|
||||
add("Time between turn checks out-of-game (in minutes)".toLabel()).left().fillX()
|
||||
|
||||
val checkDelaySelectBox = SelectBox<Int>(skin)
|
||||
val possibleDelaysArray = GdxArray<Int>()
|
||||
possibleDelaysArray.addAll(1, 2, 5, 15)
|
||||
checkDelaySelectBox.items = possibleDelaysArray
|
||||
checkDelaySelectBox.selected = settings.multiplayerTurnCheckerDelayInMinutes
|
||||
|
||||
add(checkDelaySelectBox).pad(10f).row()
|
||||
|
||||
checkDelaySelectBox.onChange {
|
||||
settings.multiplayerTurnCheckerDelayInMinutes = checkDelaySelectBox.selected
|
||||
settings.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Table.addSetUserId() {
|
||||
val idSetLabel = "".toLabel()
|
||||
val takeUserIdFromClipboardButton = "Take user ID from clipboard".toTextButton()
|
||||
.onClick {
|
||||
try {
|
||||
val clipboardContents = Gdx.app.clipboard.contents.trim()
|
||||
UUID.fromString(clipboardContents)
|
||||
YesNoPopup("Doing this will reset your current user ID to the clipboard contents - are you sure?",
|
||||
{
|
||||
settings.userId = clipboardContents
|
||||
settings.save()
|
||||
idSetLabel.setFontColor(Color.WHITE).setText("ID successfully set!".tr())
|
||||
}, previousScreen).open(true)
|
||||
idSetLabel.isVisible = true
|
||||
} catch (ex: Exception) {
|
||||
idSetLabel.isVisible = true
|
||||
idSetLabel.setFontColor(Color.RED).setText("Invalid ID!".tr())
|
||||
}
|
||||
}
|
||||
add(takeUserIdFromClipboardButton).pad(5f).colspan(2).row()
|
||||
add(idSetLabel).colspan(2).row()
|
||||
}
|
||||
|
||||
private fun Table.addAutosaveTurnsSelectBox() {
|
||||
add("Turns between autosaves".toLabel()).left().fillX()
|
||||
|
||||
val autosaveTurnsSelectBox = SelectBox<Int>(skin)
|
||||
val autosaveTurnsArray = GdxArray<Int>()
|
||||
autosaveTurnsArray.addAll(1, 2, 5, 10)
|
||||
autosaveTurnsSelectBox.items = autosaveTurnsArray
|
||||
autosaveTurnsSelectBox.selected = settings.turnsBetweenAutosaves
|
||||
|
||||
add(autosaveTurnsSelectBox).pad(10f).row()
|
||||
|
||||
autosaveTurnsSelectBox.onChange {
|
||||
settings.turnsBetweenAutosaves = autosaveTurnsSelectBox.selected
|
||||
settings.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Table.addTranslationGeneration() {
|
||||
if (Gdx.app.type == Application.ApplicationType.Desktop) {
|
||||
val generateTranslationsButton = "Generate translation files".toTextButton()
|
||||
val generateAction = {
|
||||
@ -259,218 +518,55 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
|
||||
generateTranslationsButton.onClick(generateAction)
|
||||
keyPressDispatcher[Input.Keys.F12] = generateAction
|
||||
generateTranslationsButton.addTooltip("F12",18f)
|
||||
optionsTable.add(generateTranslationsButton).colspan(2).row()
|
||||
add(generateTranslationsButton).colspan(2).row()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addModCheckerPopup() {
|
||||
//if (RulesetCache.isEmpty()) return
|
||||
val modCheckerButton = "Locate mod errors".toTextButton()
|
||||
modCheckerButton.onClick {
|
||||
val lines = ArrayList<String>()
|
||||
for (mod in RulesetCache.values) {
|
||||
val modLinks = mod.checkModLinks()
|
||||
if (modLinks.isNotOK()) {
|
||||
lines += ""
|
||||
lines += mod.name
|
||||
lines += ""
|
||||
lines += modLinks.message
|
||||
lines += ""
|
||||
}
|
||||
}
|
||||
if (lines.isEmpty()) lines += "{No problems found}."
|
||||
val popup = Popup(screen)
|
||||
popup.name = "ModCheckerPopup"
|
||||
popup.add(ScrollPane(lines.joinToString("\n").toLabel()).apply { setOverscroll(false, false) })
|
||||
.maxHeight(screen.stage.height / 2).row()
|
||||
popup.addCloseButton()
|
||||
popup.open(true)
|
||||
}
|
||||
optionsTable.add(modCheckerButton).colspan(2).row()
|
||||
}
|
||||
|
||||
private fun addSoundEffectsVolumeSlider() {
|
||||
optionsTable.add("Sound effects volume".tr())
|
||||
|
||||
val soundEffectsVolumeSlider = UncivSlider(0f, 1.0f, 0.1f,
|
||||
initial = settings.soundEffectsVolume
|
||||
) {
|
||||
settings.soundEffectsVolume = it
|
||||
private fun Table.addYesNoRow(text: String, initialValue: Boolean, updateWorld: Boolean = false, action: ((Boolean) -> Unit)) {
|
||||
val wrapWidth = tabs.prefWidth - 60f
|
||||
add(WrappableLabel(text, wrapWidth).apply { wrap = true })
|
||||
.left().fillX()
|
||||
.maxWidth(wrapWidth)
|
||||
val button = YesNoButton(initialValue, CameraStageBaseScreen.skin) {
|
||||
action(it)
|
||||
settings.save()
|
||||
if (updateWorld && previousScreen is WorldScreen)
|
||||
previousScreen.shouldUpdate = true
|
||||
}
|
||||
optionsTable.add(soundEffectsVolumeSlider).pad(5f).row()
|
||||
add(button).row()
|
||||
}
|
||||
|
||||
private fun addMusicVolumeSlider() {
|
||||
val musicLocation = Gdx.files.local(previousScreen.game.musicLocation)
|
||||
if (musicLocation.exists()) {
|
||||
optionsTable.add("Music volume".tr())
|
||||
//endregion
|
||||
|
||||
val musicVolumeSlider = UncivSlider(0f, 1.0f, 0.1f,
|
||||
initial = settings.musicVolume,
|
||||
sound = UncivSound.Silent
|
||||
) {
|
||||
settings.musicVolume = it
|
||||
settings.save()
|
||||
/**
|
||||
* This TextButton subclass helps to keep looks and behaviour of our Yes/No
|
||||
* in one place, but it also helps keeping context for those action lambdas.
|
||||
*
|
||||
* Usage: YesNoButton(someSetting: Boolean, skin) { someSetting = it; sideEffects() }
|
||||
*/
|
||||
private class YesNoButton(
|
||||
initialValue: Boolean,
|
||||
skin: Skin,
|
||||
action: (Boolean) -> Unit
|
||||
) : TextButton (initialValue.toYesNo(), skin ) {
|
||||
|
||||
val music = previousScreen.game.music
|
||||
if (music == null) // restart music, if it was off at the app start
|
||||
thread(name = "Music") { previousScreen.game.startMusic() }
|
||||
|
||||
music?.volume = 0.4f * it
|
||||
var value = initialValue
|
||||
private set(value) {
|
||||
field = value
|
||||
setText(value.toYesNo())
|
||||
}
|
||||
musicVolumeSlider.value = settings.musicVolume
|
||||
optionsTable.add(musicVolumeSlider).pad(5f).row()
|
||||
} else {
|
||||
val downloadMusicButton = "Download music".toTextButton()
|
||||
optionsTable.add(downloadMusicButton).colspan(2).row()
|
||||
val errorTable = Table()
|
||||
optionsTable.add(errorTable).colspan(2).row()
|
||||
|
||||
downloadMusicButton.onClick {
|
||||
downloadMusicButton.disable()
|
||||
errorTable.clear()
|
||||
errorTable.add("Downloading...".toLabel())
|
||||
|
||||
// So the whole game doesn't get stuck while downloading the file
|
||||
thread(name = "Music") {
|
||||
try {
|
||||
val file = DropBox.downloadFile("/Music/thatched-villagers.mp3")
|
||||
musicLocation.write(file, false)
|
||||
Gdx.app.postRunnable {
|
||||
rebuildOptionsTable()
|
||||
previousScreen.game.startMusic()
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Gdx.app.postRunnable {
|
||||
errorTable.clear()
|
||||
errorTable.add("Could not download music!".toLabel(Color.RED))
|
||||
}
|
||||
}
|
||||
}
|
||||
init {
|
||||
color = ImageGetter.getBlue()
|
||||
onClick {
|
||||
value = !value
|
||||
action.invoke(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addResolutionSelectBox() {
|
||||
optionsTable.add("Resolution".toLabel())
|
||||
|
||||
val resolutionSelectBox = SelectBox<String>(skin)
|
||||
resolutionSelectBox.items = resolutionArray
|
||||
resolutionSelectBox.selected = settings.resolution
|
||||
optionsTable.add(resolutionSelectBox).minWidth(240f).pad(10f).row()
|
||||
|
||||
resolutionSelectBox.onChange {
|
||||
settings.resolution = resolutionSelectBox.selected
|
||||
reloadWorldAndOptions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTileSetSelectBox() {
|
||||
optionsTable.add("Tileset".toLabel())
|
||||
|
||||
val tileSetSelectBox = SelectBox<String>(skin)
|
||||
val tileSetArray = GdxArray<String>()
|
||||
val tileSets = ImageGetter.getAvailableTilesets()
|
||||
for (tileset in tileSets) tileSetArray.add(tileset)
|
||||
tileSetSelectBox.items = tileSetArray
|
||||
tileSetSelectBox.selected = settings.tileSet
|
||||
optionsTable.add(tileSetSelectBox).minWidth(240f).pad(10f).row()
|
||||
|
||||
tileSetSelectBox.onChange {
|
||||
settings.tileSet = tileSetSelectBox.selected
|
||||
TileSetCache.assembleTileSetConfigs()
|
||||
reloadWorldAndOptions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addAutosaveTurnsSelectBox() {
|
||||
optionsTable.add("Turns between autosaves".toLabel())
|
||||
|
||||
val autosaveTurnsSelectBox = SelectBox<Int>(skin)
|
||||
val autosaveTurnsArray = GdxArray<Int>()
|
||||
autosaveTurnsArray.addAll(1, 2, 5, 10)
|
||||
autosaveTurnsSelectBox.items = autosaveTurnsArray
|
||||
autosaveTurnsSelectBox.selected = settings.turnsBetweenAutosaves
|
||||
|
||||
optionsTable.add(autosaveTurnsSelectBox).pad(10f).row()
|
||||
|
||||
autosaveTurnsSelectBox.onChange {
|
||||
settings.turnsBetweenAutosaves = autosaveTurnsSelectBox.selected
|
||||
settings.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMultiplayerTurnCheckerDelayBox() {
|
||||
optionsTable.add("Time between turn checks out-of-game (in minutes)".toLabel())
|
||||
|
||||
val checkDelaySelectBox = SelectBox<Int>(skin)
|
||||
val possibleDelaysArray = GdxArray<Int>()
|
||||
possibleDelaysArray.addAll(1, 2, 5, 15)
|
||||
checkDelaySelectBox.items = possibleDelaysArray
|
||||
checkDelaySelectBox.selected = settings.multiplayerTurnCheckerDelayInMinutes
|
||||
|
||||
optionsTable.add(checkDelaySelectBox).pad(10f).row()
|
||||
|
||||
checkDelaySelectBox.onChange {
|
||||
settings.multiplayerTurnCheckerDelayInMinutes = checkDelaySelectBox.selected
|
||||
settings.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addLanguageSelectBox() {
|
||||
val languageSelectBox = SelectBox<Language>(skin)
|
||||
val languageArray = GdxArray<Language>()
|
||||
previousScreen.game.translations.percentCompleteOfLanguages
|
||||
.map { Language(it.key, if (it.key == "English") 100 else it.value) }
|
||||
.sortedByDescending { it.percentComplete }
|
||||
.forEach { languageArray.add(it) }
|
||||
if (languageArray.size == 0) return
|
||||
|
||||
optionsTable.add("Language".toLabel())
|
||||
languageSelectBox.items = languageArray
|
||||
val matchingLanguage = languageArray.firstOrNull { it.language == settings.language }
|
||||
languageSelectBox.selected = matchingLanguage ?: languageArray.first()
|
||||
optionsTable.add(languageSelectBox).minWidth(240f).pad(10f).row()
|
||||
|
||||
languageSelectBox.onChange {
|
||||
// Sometimes the "changed" is triggered even when we didn't choose something
|
||||
selectedLanguage = languageSelectBox.selected.language
|
||||
|
||||
if (selectedLanguage != settings.language)
|
||||
selectLanguage()
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectLanguage() {
|
||||
settings.language = selectedLanguage
|
||||
previousScreen.game.translations.tryReadTranslationForCurrentLanguage()
|
||||
reloadWorldAndOptions()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
This TextButton subclass helps to keep looks and behaviour of our Yes/No
|
||||
in one place, but it also helps keeping context for those action lambdas.
|
||||
|
||||
Usage: YesNoButton(someSetting: Boolean, skin) { someSetting = it; sideEffects() }
|
||||
*/
|
||||
private fun Boolean.toYesNo(): String = (if (this) Constants.yes else Constants.no).tr()
|
||||
private class YesNoButton(initialValue: Boolean, skin: Skin, action: (Boolean) -> Unit)
|
||||
: TextButton (initialValue.toYesNo(), skin ) {
|
||||
|
||||
var value = initialValue
|
||||
private set(value) {
|
||||
field = value
|
||||
setText(value.toYesNo())
|
||||
}
|
||||
|
||||
init {
|
||||
color = ImageGetter.getBlue()
|
||||
onClick {
|
||||
value = !value
|
||||
action.invoke(value)
|
||||
companion object {
|
||||
fun Boolean.toYesNo(): String = (if (this) Constants.yes else Constants.no).tr()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user