From dd53b0b7e156a635e1fcbb53d454499fcf95f44b Mon Sep 17 00:00:00 2001 From: <> Date: Wed, 27 Nov 2024 18:19:56 +0000 Subject: [PATCH] Deployed ed16fb9 with MkDocs version: 1.6.1 --- .nojekyll | 0 404.html | 1270 ++++ Credits/index.html | 3306 ++++++++ Developers/Building-Locally/index.html | 1660 ++++ Developers/Coding-standards/index.html | 1411 ++++ Developers/From-code-to-deployment/index.html | 1526 ++++ Developers/Game-Making-Tips/index.html | 1760 +++++ Developers/Map-rendering/index.html | 1425 ++++ .../index.html | 1804 +++++ .../Saved-games-and-transients/index.html | 1327 ++++ Developers/Testing-Android-Builds/index.html | 1513 ++++ .../index.html | 1495 ++++ Developers/UI-development/index.html | 1516 ++++ Developers/Uniques/index.html | 1470 ++++ Guiding-Principles/index.html | 1424 ++++ Modders/Autoupdates/index.html | 1390 ++++ Modders/Creating-a-UI-skin/index.html | 2417 ++++++ Modders/Creating-a-custom-tileset/index.html | 1772 +++++ Modders/Images-and-Audio/index.html | 2481 ++++++ Modders/Making-a-new-Civilization/index.html | 1594 ++++ .../Mod-file-structure/1-Overview/index.html | 1538 ++++ .../index.html | 2399 ++++++ .../3-Map-related-JSON-files/index.html | 2042 +++++ .../4-Unit-related-JSON-files/index.html | 1654 ++++ .../5-Miscellaneous-JSON-files/index.html | 2915 +++++++ Modders/Mods/index.html | 1620 ++++ Modders/Scenarios/index.html | 1400 ++++ Modders/Type-checking/index.html | 1491 ++++ Modders/Unique-parameters/index.html | 2001 +++++ Modders/schemas/buildings.json | 73 + Modders/schemas/civilopediaText.json | 22 + Modders/schemas/color.json | 11 + Modders/schemas/events.json | 32 + Modders/schemas/nations.json | 66 + Modders/schemas/stats.json | 14 + Modders/schemas/techs.json | 58 + Modders/schemas/terrains.json | 41 + Modders/schemas/tileImprovements.json | 38 + Modders/schemas/tileResources.json | 54 + Modders/schemas/uniques.json | 18 + Modders/schemas/unitPromotions.json | 29 + Modders/schemas/unitTypes.json | 19 + Modders/schemas/units.json | 56 + Modders/uniques/index.html | 5551 ++++++++++++++ Other/Force-rating-calculation/index.html | 1557 ++++ Other/Installing-on-macOS/index.html | 1329 ++++ .../index.html | 1429 ++++ Other/Multiplayer/index.html | 1522 ++++ Other/Regions/index.html | 1434 ++++ Other/Translating/index.html | 1670 ++++ Privacy-Policy/index.html | 1387 ++++ assets/Android_SDK_Platforms.png | Bin 0 -> 38563 bytes assets/Android_SDK_Tools.png | Bin 0 -> 51636 bytes assets/Desktop_Build.png | Bin 0 -> 20675 bytes assets/FasterUIDevelopment.png | Bin 0 -> 17369 bytes assets/Icon.png | Bin 0 -> 69531 bytes assets/favicon.png | Bin 0 -> 6626 bytes assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.83f73b43.min.js | 16 + assets/javascripts/bundle.83f73b43.min.js.map | 7 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + assets/javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.6ce7567c.min.js | 42 + .../workers/search.6ce7567c.min.js.map | 7 + assets/stylesheets/main.6f8fc17f.min.css | 1 + assets/stylesheets/main.6f8fc17f.min.css.map | 1 + assets/stylesheets/palette.06af60db.min.css | 1 + .../stylesheets/palette.06af60db.min.css.map | 1 + img.png | Bin 0 -> 822 bytes index.html | 1393 ++++ search/search_index.json | 1 + sitemap.xml | 3 + sitemap.xml.gz | Bin 0 -> 127 bytes 105 files changed, 74705 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 Credits/index.html create mode 100644 Developers/Building-Locally/index.html create mode 100644 Developers/Coding-standards/index.html create mode 100644 Developers/From-code-to-deployment/index.html create mode 100644 Developers/Game-Making-Tips/index.html create mode 100644 Developers/Map-rendering/index.html create mode 100644 Developers/Project-structure-and-major-classes/index.html create mode 100644 Developers/Saved-games-and-transients/index.html create mode 100644 Developers/Testing-Android-Builds/index.html create mode 100644 Developers/Translations,-mods,-and-modding-freedom-in-Open-Source/index.html create mode 100644 Developers/UI-development/index.html create mode 100644 Developers/Uniques/index.html create mode 100644 Guiding-Principles/index.html create mode 100644 Modders/Autoupdates/index.html create mode 100644 Modders/Creating-a-UI-skin/index.html create mode 100644 Modders/Creating-a-custom-tileset/index.html create mode 100644 Modders/Images-and-Audio/index.html create mode 100644 Modders/Making-a-new-Civilization/index.html create mode 100644 Modders/Mod-file-structure/1-Overview/index.html create mode 100644 Modders/Mod-file-structure/2-Civilization-related-JSON-files/index.html create mode 100644 Modders/Mod-file-structure/3-Map-related-JSON-files/index.html create mode 100644 Modders/Mod-file-structure/4-Unit-related-JSON-files/index.html create mode 100644 Modders/Mod-file-structure/5-Miscellaneous-JSON-files/index.html create mode 100644 Modders/Mods/index.html create mode 100644 Modders/Scenarios/index.html create mode 100644 Modders/Type-checking/index.html create mode 100644 Modders/Unique-parameters/index.html create mode 100644 Modders/schemas/buildings.json create mode 100644 Modders/schemas/civilopediaText.json create mode 100644 Modders/schemas/color.json create mode 100644 Modders/schemas/events.json create mode 100644 Modders/schemas/nations.json create mode 100644 Modders/schemas/stats.json create mode 100644 Modders/schemas/techs.json create mode 100644 Modders/schemas/terrains.json create mode 100644 Modders/schemas/tileImprovements.json create mode 100644 Modders/schemas/tileResources.json create mode 100644 Modders/schemas/uniques.json create mode 100644 Modders/schemas/unitPromotions.json create mode 100644 Modders/schemas/unitTypes.json create mode 100644 Modders/schemas/units.json create mode 100644 Modders/uniques/index.html create mode 100644 Other/Force-rating-calculation/index.html create mode 100644 Other/Installing-on-macOS/index.html create mode 100644 Other/Intentional-departures-from-Civ-V/index.html create mode 100644 Other/Multiplayer/index.html create mode 100644 Other/Regions/index.html create mode 100644 Other/Translating/index.html create mode 100644 Privacy-Policy/index.html create mode 100644 assets/Android_SDK_Platforms.png create mode 100644 assets/Android_SDK_Tools.png create mode 100644 assets/Desktop_Build.png create mode 100644 assets/FasterUIDevelopment.png create mode 100644 assets/Icon.png create mode 100644 assets/favicon.png create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.83f73b43.min.js create mode 100644 assets/javascripts/bundle.83f73b43.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.6ce7567c.min.js create mode 100644 assets/javascripts/workers/search.6ce7567c.min.js.map create mode 100644 assets/stylesheets/main.6f8fc17f.min.css create mode 100644 assets/stylesheets/main.6f8fc17f.min.css.map create mode 100644 assets/stylesheets/palette.06af60db.min.css create mode 100644 assets/stylesheets/palette.06af60db.min.css.map create mode 100644 img.png create mode 100644 index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/404.html b/404.html new file mode 100644 index 0000000000000..727dc4048f4e6 --- /dev/null +++ b/404.html @@ -0,0 +1,1270 @@ + + + +
+ + + + + + + + + + + + + + +Flag Icons made by Freepik from www.flaticon.com and licensed by Creative Commons 3.0, except for:
+New Unciv logo made by u-ndefined on Discord
+Base tile icons for the "Fantasy Hex" tileset belong to CuddlyClover @ https://cuddlyclover.itch.io/fantasy-hex-tiles with a few additions by various contributors
+Promotional trailer for Steam and other storefronts made by letstalkaboutdune
+Unless otherwise specified, all the following are from the Noun Project licenced under either Creative Commons or Public Domain
+Unless otherwise specified, units for the AbsoluteUnits unitset are made by letstalkaboutdune and are licensed under Creative Commons Attribution 4.0 International
+Barbarian variants by Pelo, made for Playable Barbarians:
+By Basil:
+Unless otherwise specified, Tile improvements and units, as well as the terrains and improvements for HexaRealm tileset, are made by The Bucketeer / @GeneralWadaling and are licenced under Creative Commons 3.0
+HexaRealm tileset images by legacymtgsalvationuser69544 here:
+Sounds are from FreeSound.org unless otherwise noted and are either Creative Commons or Public Domain unless otherwise noted
+The following music is from https://filmmusic.io +"Thatched Villagers" by Kevin MacLeod (https://incompetech.com)
+The following audio is from https://pixabay.com/ Pixabay License +- Beyond New Horizons - Free Epic Viking Medieval Soundtrack by GioeleFazzeri for background music +- Beep 6 by Eponn for beep in Cross-Platform Play section +- Cinematic Boom by Rizzard for final boom +- Cymbal Swell 2 by rubberduckie for cymbal swells +- hit of orchestral cymbals and bass drum by Selector for intro crash
+The fireworks on the City Screen of a WLTK-celebrating city are loosely based on the Fireworks.p file included in Particle Park. +All differences and edits done by the Unciv team. +License quoted: +
Particle Park Fireworks License
+
+------------------------------------------------------------------------------------------
+
+Copyright © 2019 Raymond Buckley
+
+Particle Park Fireworks can be used under the Creative Commons Attribution 4.0 International license.
+
+See a human readable version here: https://creativecommons.org/licenses/by/4.0/
+
+------------------------------------------------------------------------------------------
+
By the end of this guide, you will have Unciv running locally from code, so you can make changes and test them locally.
+https://github.com/<YourUsername>/Unciv
Unable to find method ''void org.apache.commons.compress.archivers.zip.ZipFile.<init>(java.nio.channels.SeekableByteChannel)''
errors when you try to sync. If you have this problem go into File > Settings > Languages & Frameworks > Android SDKUnciv.desktop.main
(Unciv.desktop
for Bumblebee or below), main class to com.unciv.app.desktop.DesktopLauncher
and $ProjectFileDir$/android/assets
as the Working directory, OK to close the window-Xmx4096m -Xms256m -XX:MaxMetaspaceSize=256m
to allow a debugged game a little more memory. Or, use the -DnoLog=
or -DonlyLog=
options to control console logging. See the Log.kt comments for details.../../docs/uniques.md (No such file or directory)
error that means you forgot to set the working directory!
+android/assets/SaveFiles
folder, "Mark directory as" > Excludedandroid/assets/mods
folder and any other files you may create while testing that do not belong in the public project.Unciv uses Gradle to specify dependencies and how to run. In the background, the Gradle gnomes will be off fetching the packages (a one-time effort) and, once that's done, will build the project!
+Unciv uses Gradle 8.7 and the Android Gradle Plugin 8.5. Can check in File > Project Structure > Project
+++Note: advanced build commands (as described in the next paragraph), specifically
+gradlew desktop:dist
to build a jar, run just fine in Android Studio's terminal (Alt+F12), with most dependencies already taken care of.
gradlew desktop:run
gradlew desktop:dist
./gradlew desktop:run
./gradlew desktop:dist
If the terminal returns Permission denied
or Command not found
on Mac/Linux, run chmod +x ./gradlew
first. This is a one-time procedure.
If you get an error that Android SDK folder wasn't found, install it by running:
+sudo apt update && sudo apt install android-sdk
(Debian, Ubuntu, Mint etc.)
Then, set the SDK location in the local.properties
file by adding:
sdk.dir = /path/to/android/sdk
- for example, /usr/lib/android-sdk
If during initial launch you get an error that the JDK version is wrong, install the JDK from here.
+++Note: Gradle may take up to several minutes to download files +After building, the output .JAR file should be in
+/desktop/build/libs/Unciv.jar
For actual development, you'll probably need to download Android Studio and build it yourself - see above :)
+Sometimes, checking things out on the desktop version is not enough and you need to debug Unciv running on an Android device. +For an introduction, see Testing android builds.
+Congratulations! Unciv should now be running on your computer! Now we can start changing some code, and later we'll see how your changes make it into the main repository!
+Now would be a good time to get to know the project in general at the Project Structure overview!
+You can (and in some cases should) run and even debug the unit tests locally.
+:tests:test
and "Arguments" to --tests "com.unciv.*"
, OK to close the window.Detekt checks for code smells and other linting issues. +To generate Detekt reports:
+detekt-cli
with detekt-cli.bat
.PATH/TO/DETEKT/detekt-cli --parallel --report html:detekt/reports.html --config .github/workflows/detekt_config/detekt-warnings.yml
PATH/TO/DETEKT/detekt-cli --parallel --report html:detekt/reports.html --config .github/workflows/detekt_config/detekt-errors.yml
detekt/reports.html
The simple multiplayer host included in the sources can be set up to debug or run analogously to the main game:
+- In Android Studio, Run > Edit configurations.
+ - Click "+" to add a new configuration
+ - Choose "Application" and name the config, e.g. "UncivServer"
+ - Set the module to Unciv.server.main
(Unciv.server
for Studio versions Bumblebee or below), main class to com.unciv.app.server.UncivServer
and <repo_folder>/android/assets/
as the Working directory, OK to close the window.
+- Select the UncivServer configuration and click the green arrow button to run! Or start a debug session as above.
To build a jar file, refer to Without Android Studio and replace 'desktop' with 'server'. That is, run ./gradlew server:dist
and when it's done look for /server/build/libs/UncivServer.jar
As an open-source project, there will be a lot of eyes on our code.
+The main purpose of having a coding standard is for the code to be as immediately readable as possible to as many potential contributors, and hence most of it focuses on defaulting to coding structures that exist in other similar languages (Java, C#) when possible.
+.let{}
and ?:
Kotlin is made greater for being strict with nullability. Don't let this fact confuse people new to it. These can be simply replaced by if(x!=null)
which is much more readable. They all probably compile to the same bytecode anyway, so when in doubt - readability.
for(item in list)
and not list.forEach{}
For loops go waaaay back, forEach doesn't. As an added bonus, I'm pretty sure that because forEach accepts a function parameter, then when debugging it won't automatically step into these lines, unlike for.
+There's no need to create an interface if there is only one implementation of that interface. Doing so obfuscates the actual code that's running and increases the Time To Relevant Code. If abstraction becomes necessary later, we can always do it later.
+ + + + + + + + + + + + + +So, your code works! You've solved all the bugs and now you just need to get it out to everyone!
+So, how does THAT work?
+The process has two major parts, one is "Getting your code in the main repository" and the other is "Deploying versions" - as a developer, you'll be taking an active part in the first process, but the second process is on me =)
+When I'm ready to release a new version I:
+https://monitor.f-droid.org/builds/log/com.unciv.app/<appCodeNumber>
), and the new release will eventually be available hereIn case a fix is urgent because a bug in the last release severely affects a large number of players, any Collaborator (next step up from Contributor) is able to trigger a patch release.
+We start at a 10% rollout, after a day with no major problems go to 30%, and after another day to 100%. If you were counting that means that most players will get the new version after 2+ days.
+If there were problems, we halt the current rollout, fix the problems, and release a patch version, which starts at 10% again.
+Dear future me - the automation was extremely annoying guesswork to set up, so the facts you need to know are:
+Pages for the Unciv Github Wiki are kept in the main repository under docs.
+The process to edit the wiki is as follows:
+update wiki
".Doing things this way has several distinct advantages over using the Github Wiki web interface directly:
+However, it also imposes a couple of conventions about how links should best be formatted:
+Link type | +Format | +Example | +
---|---|---|
Inter-wiki | +Should begin with "./", and include ".md". | +./Mods.md#other |
+
Code or asset file | +Should begin with "https://github.com/yairm210/Unciv/blob/master/", and be relative to the project root. | +https://github.com/yairm210/Unciv/blob/master/android/assets/game.png |
+
These formats will allow IDEs like Android studio to resolve these links and check for broken links, while also working on the Github code browser.
+The bot that updates the wiki from the main repository automatically translates them into formats that are compatible with Github Wikis, which have somewhat non-standard requirements.
+ + + + + + + + + + + + + +Here are a bunch of things I've learned from by brief excursion into the world of game making.
+Some of our will be obvious to you, some will not.
+Unciv started its life as a Unity project in C#, was shifted to Java and LibGDX, and finally to Kotlin.
+I regret every minute that I spent writing events in Java, this is probably the most significant change that your application could see.
+Unless you plan on creating images on the fly, you'll probably be using prerendered assets.
+Placing them manually is akin to manually positioning html tags, instead of using html hierarchy and css to guide positions.
+So too is Scene2d - as a placement framework. it's relatively simple to understand, especially when you...
+I personally found that table has all the functionality of the above, and more.
+Each class has a different syntax too, so I found it much simpler to just stick with Table for everything.
+Table does just about EVERYTHING! It's insanely amazing!
+The top-down CPU chart is the best code profiler I've ever seen, use it to your advantage!
+Caching is a trade-off between purer, state-agnostic code and higher performance. +Coming from a PC background, I automatically assume that anything less than O(n^2) is less than a millisecond and therefore, not a caching candidate. +This is not so in mobile development.
+This becomes especially relevant when you need to save and load game data which has lots of connected parts - you have to avoid circular references, and you want to minimise the save size, but you need to reconstruct the missing links when loading.
+All the tip and tricks you've heard to minimize String operations? Use them!
+String constants should be consts, use StringBuilders (or just ArrayLists of strings that you later .joinToString())
+One thing I did not expect to be such an issue is intermediate lists when sorting and mapping.
+But apparently, the memory allocation for these tasks is Serious Business.
+So whenever possible, take your list and .asSequence() it before activating list operations - this results in huge savings of both time and memory!
+The only time you shouldn't be doing this, though, is when you want to cache the specific values for future use - sequences will go through the whole process every time you iterate on them, so just .toList() them when you've gotten the final results!
+I think that most Open Source games suffer from this problem - those that are in are way in, but those that are out and want to join have to learn the ecosystem.
+Documentation is a big issue here, but so are detailed instructions - and I mean "Spoonfeeding".
+Treat new developers as if they've never used Git before - it's possible they haven't!
+Explain how to download the sourecode, the tools, how to get the game running locally, how to make changes and how to submit them.
+Same think with new players - getting the game up and running should be AS SIMPLE AS HUMANLY POSSIBLE - you want people to play your game, don't you?
+This includes:
+I, personally, underestimated this point for about a year after launch.
+I communicated with players through the Google Play Store and Github issues, and that seemed to be enough.
+It was only after repeated urgings from players that I opened a Discord server - and that gradually lead to a massive change!
+You see, it's not ABOUT programmer-to-player interaction. There will always be a small number of core devs relative to the large playerbase.
+The key to the community is the player-to-player interaction. Explaining things, questions, ideas, things that players bounce off each other, +not only make the amorphous community a better place, but actually lead to a better game!
+Another think to remember is that there's a larger community around you - the Open Source community, the Linux community, etc.
+There are lots of people who will play your game only because it's open source, and it also means they don't have as many options.
+For example...
+Your game's name, the icon, screenshots, everything a player sees about your game is marketing.
+Icons and bylines are especially important, since they're the first things your players will probably see.
+I saw an almost 50% (!) by changing the icon, after several experiments, which Google Play lets you conduct very easily.
+This may be slightly controversial, so I'll explain.
+We went though a number of iterations regarding how to save translations until we arrived at the current format.
+The important parts are:
+Game translation files should be AUTO GENERATED. This allows you to add new objects into the game with impunity, + knowing that corresponding lines will be auto-added to the translations.
+Translations for each language should be stored separately - this allows concurrent modification of several independent languages with no risk of conflict
+Translations should be PR'd in! This allows other speakers to question or change the proposed translations, and allows you to run tests on your translations. + If you require a specific format, this is invaluable as it means that bad translations will be rejected at the door.
+TL;DR, consider using APIs that are free, even if they're not Open Source.
+Multiplayer requires syncing game files between clients, even when one of them is not currently online.
+The 'correct' way to solve this would probably be to have an online DB and a service which handles user requests.
+Since this is an Open Source game, I'm working on a 0$ budget, so we just store all the files in Dropbox and upload/download there.
+Is this secure? No, but does it need to be? You need to think of the cost vs the value.
+Same thing with Mods. Steam is big and secure so it handles its mods itself.
+We are small and open, so we just allow to download from Github, which lets us use all of Github's built in functions (user management, readmes, stars, versioning...) at no extra cost.
+And unlike the Dropbox usage, which is basically abuse, Github is built for this kind of thing! +This is exactly the kind of use case they were thinking of to start with!
+There comes a time in every project where the cool stuff is done. All the cutting-edge awesomeness and algorithmic playdough is done, and now all (hah) it needs is polish.
+You know who loves polish? Players! Sure, there are some that say "a good game is good even if it's basic" but they have standards for what a basic game should have as well.
+And the numbers don't lie. Polished games sell themselves better, and so are played more.
+You know who doesn't love polish? DEVELOPERS.
+When your game is relatively simple, then the options for polish are more limited, but the more complex the game, the more polish-venues there are.
+And it can be an ABSOLUTE GRIND. Another weird use-case, another ingame option, "better performance" (I must have spent dozens of hours on different performance related actions)
+And the worst thing is, that everyone notices when it's missing, but no one notices when it's there. A hundred versions of polish - literally - and the average player may notice only a slight change.
+And then comes the moment when you ask yourself, why bother? What are we even doing here?
+For me, the answers are as follows:
+A. To build something truly great, you have to keep going way beyond when it stops being fun.
+B. There's a community of people that like what you're doing and want there to be more of it :)
+C. You know you want to keep coding, and what, you think you're going to start another project and it'll work out as well? You've tried that multiple times, and let's face it the chance of you making a second game that goes so well is really small unless you invest in it as much time as you have in this, and yeah, then you'll be back in this position again.
+And that's basically the loop I've been in for the last hundred versions or so! Solve bugs, fix edge cases, improve AI, accept PRs. Lots of mod-related changes, both to stop the game breaking when people do things in mods that they shouldn't and to allow them more freedom in making them.
+I don't think I'll ever really continue to finish G&K, I'm DEFINITELY not planning on implementing BNW mechanics which frankly I think are...not great.
+That's where I am right now. Kind of done with the game, but considering that I thought that half a year ago and releases are still releasing roughly every week, also kind of not.
+ + + + + + + + + + + + + +Images in LibGDX are displayed on screen by a SpriteBatch, which uses GL to bind textures to load them in-memory, and can then very quickly display them on-screen. +The actually rendering is then very fast, but the binding process is slow. +Therefore, ideally we'd want as little bindings as possible, so the textures should contain as many images as possible. +This is why we compile images (ImagePacker.packImages()) into large PNGs.
+However, due to limitations in different chipsets etc, these images are limited to a maximum size of 2048*2048 pixels, and the game contains more images than would fit into a single square of that size. +What we do, then, is separate them by category, and thus rendering proximity. +The 'android' folder contains Images, but also various sub-categories - Images.Flags, Images.Tech, etc. +Each of these sub-categories is compiled into a separate PNG file in the 'android/assets' folder.
+When rendering, the major time-sink is in rebinding textures. We therefore need to be careful to minimize the number of -rebinds, or 'swapping between different categories'.
+Each map tile is comprised of several layers, and each layer needs to be rendered for all tiles before the next layer is. +For example, we don't want one tile's unit sprite to be overlayed by another's improvement. +This layering is done in TileGroupMap, where we take the individual parts for all tiles, separate them into the layers, and add them all to one big group. +This also has a performance advantage, since e.g. text and contruction images in the various city buttons are not rendered until the very end, and therefore swap per the number of of cities and not for every single tile. +This also means that mods which add their own tilesets or unit sprites have better performance than 'render entire tile; would provide, since we first render all terrains, then all improvements, etc, +so if my tileset provides all terrains, it won't be swapped out until we're done.
+Android Studio's built-in profiler has a CPU profiler which is perfect for this. +Boot up the game on your Android device, open a game, start recording CPU, move the screen around a bit, and stop recording. +Select the "GL Thread" from the list of threads, and change visualization to a flame graph. You'll then see what's actually taking rendering time.
+You can find various games to test on here - This for example is a crowded one.
+ + + + + + + + + + + + + +Since LibGDX, and therefore Unciv, are built for multi-platform support, the project structure is built accordingly.
+99% of the code is in the core project, which contains all the platform-independent code.
+The desktop and android folders contain platform-specific things, and the Android folder also contains the game Images and the all-important Assets, which are required for running from Desktop as well, so we bundle them up into the .jar file when releasing.
+The tests folder contains tests that can be run manually via gradle with ./gradlew tests:test
, and are run automatically by Travis for every push.
The server folder contains the sources for the UncivServer (a host enabling communication between multiplayer game instances), which is packaged into its own separate jar.
+Before we get to the Classes, a word on Languages. Unciv is playable in several handfuls of languages, and there's magic to support that. Whenever you include a new string in code you will need to give it a quick evaluation - will users see it, and if so, what do I need to do to support its translations. Sometimes you may not need to do anything, sometimes you will add a line to the translation templates, and sometimes you will adapt the string formatting to support the translations. For details, see the 'Translation generation - for developers' chapter.
+Civ, and therefore Unciv, is a game with endless interconnectivity - everything affects everything else.
+In order to have some semblance of order, we'll go over the main classes in the order in which they are serialized.
+So yes, you can - for instance - get the center tile of a city, a TileInfo, directly from CityInfo. But delving into all the connections would only harm the point of this overview, that's what the actual code is for ;)
+The Game State:
+The UI:
+GameInfo
First off, let's clarify: When we say "The Game", we mean the state of the game (what turn it is, who the players are, what each one has etc) and not the UI of the game.
+That is, The Game is the currently played game, not Unciv.
+The game contains three major parts:
+List<CivilizationInfo>
TileMap
RuleSet
. This includes what technologies, buildings, units etc. are available, and IS NOT serialized and deserialized, but comes straight from the game files - more on that later.When we save the game, or load the game, we're actually serializing and deserializing this class, which means that the this class is the root of the entire game state.
+Most objects in the "state tree" have a transient reference to their parent, meaning the tree can be traversed in-code in all directions, and frequently is.
+CivilizationInfo
This represents one of the players of the game, and NOT a specific nation - meaning, not France, but rather "Player X who is France in this game". In another game, there will be another France.
+As one of the focal points of the game, it contains a lot of important information, the most important of which are:
+List<CityInfo>
PolicyManager
, GoldenAgeManager
, GreatPersonManager
, TechManager
, VictoryManager
, DiplomacyManager
CityInfo
This contains the information about a specific city.
+Beyond basic information like name, location on map etc, the most important classes it contains are:
+CityStats
PopulationManager
, CityConstructions
, CityExpansionManager
TileMap
This contains mostly helper functions and acts as a wrapper for the list of tiles it contains
+TileInfo
Each tile is comprised of several layers, and so has information for each.
+Tiles have, primarily:
+Terrain
(part of the ruleset)Terrain
(part of the ruleset)TileResource
(part of the ruleset)TileImprovement
(part of the ruleset)MapUnit
MapUnit
Unlike buildings, Unit in Unciv has two meanings. One is a Type of unit (like Spearman), and one is a specific instance of a unit (say, a Babylonian Spearman, at a certain position, with X health).
+MapUnit
is a specific instance of a unit, whereas BaseUnit
is the type of unit.
Main information:
+BaseUnit
UnitPromotions
So far so good - but what of everything that makes Civ, Civ? The units, the buildings, the nations, the improvements etc?
+Since these things remain the same for every game, these are not saved on a per-game basis, but rather are saved in json files in Unciv's asset folder.
+Each class in the game state that saves one of these will reference it by name, and when the game is running it will check the Ruleset to find the relevant information for that object.
+The various objects are:
+Technology
- referenced mainly in CivilizationInfo.TechManager
Nations
- referenced mainly in CivilizationInfo
Policy
- referenced mainly in CivilizationInfo.PolicyManager
(seeing a pattern here?)Building
- referenced mainly in CityInfo.ConstructionManager
BaseUnit
- referenced mainly in MapUnit
Promotion
- referenced mainly in MapUnit
Terrain
- referenced mainly in TileInfo
TileResource
- referenced mainly in TileInfo
TileImprovement
- referenced mainly in TileInfo
There are also Translations in the Ruleset, but they technically have nothing to do with the game state but rather with the UI display.
+The information for all of these is in json files in android\assets\jsons
UncivGame
is the 'base' class for the UI, from which everything starts, but it itself doesn't do much.
When we change a screen, we're changing a value in UncivGame, the interesting stuff happens in the screens themselves.
+MainMenuScreen
This is what the user sees when first entering the game. It acts as a hub to loading games, adding mods, options etc, without loading an actual game upfront - this allows us to differentiate between "User can't enter game" and "User can't load game" problems
+NewGameScreen
This is basically a giant setting screen for GameOptions and MapOptions classes, divided into:
+WorldScreen
90% of the game is spent on this screen, so naturally it's the fullest, with the most things happening.
+This is the main hub of the game, with all other screens being opened from it, and closing back to reveal it.
+Most notable are:
+TileMapHolder
- with each of the rendered tiles being a TileGroup
WorldScreenTopBar
for stats and resources, UnitTable
for the currently selected unit, TileInfoTable
or the currently selected tile, BattleTable
for battle simulation, and NotificationsScroll
for the notificationsMinimapHolder
TechPickerScreen
, EmpireOverviewScreen
, and PolicyPickerScreen
CityScreen
The second-most important screen.
+Notable parts:
+ConstructionsTable
CityInfoTable
A few words need to be said about the NextTurn process, but there isn't really a good place for it so I'll put it here.
+We clone the GameInfo and use a "new" GameInfo for each turn because of 2 reasons.
+The first is multithreading and thread safety, and the second is multiplayer reproducibility.
+The first point is pretty basic. The NextTurn needs to happen in a separate thread so that the user can still have a responsive game when it's off doing stuff. Stuff in the GameInfo changes on NextTurn, so if you're rendering that same GameInfo, this could cause conflicts. Also, after NextTurn we generally autosave, and if stuff changes in the state while we're trying to serialize it to put it in the save file, that's Not Fun. A single clone solves both of these problems at once.
+The second point is less obvious. If we use our mutable state, changing stuff in place, then what happens when we're playing in Multiplayer? Multiplayer is based upon the fact that you can receive an entire game state and go from there, and in fact the move to multiplayer was what made the whole "clone" thing necessary (on the way it also solved the aforementioned threading problems)
+ + + + + + + + + + + + + +Unciv is a game where many things are interconnected. Each map unit, for example, belongs to a civ, is located on a tile, can have several promotions and "inherits" from a base unit.
+When saving a game state, we want it to be as small as possible - so we limit the information saved to the bare minimum. We save names instead of pointers, and anything that can be recalculated is simply not saved.
+But during runtime, we need these links for performance - why perform a lookup every time if we can save a reference on the object?
+Classes are therefore freeze dried on serialization, and rehydrated for runtime. Since these fields are marked in Kotlin as @Transient fields, we call the rehydration function setTransients
.
Take the map unit for example. How can we calculate the uniques for that unit? They can come from several places:
+So from the save file, we get the civ name, unit name, and promotion names; for runtime, we'll want a reference to the civ, base unit, and promotions.
+We can find these by looking up the civ in the game, and the unit and promotions from the ruleset.
+The civ itself - a game object - references the nation - a ruleset object, which is another link. The base unit references the unit type, another link.
+The nation, base unit, and promotions, all contain uniques - which are saved as strings. For performance, these too get saved at runtime as Unique instances - which contain the unique type, the parameters, conditionals, etc.
+Beyond the fact that each ruleset object retains a "hydrated" list of its uniques, the unit's uniques don't change very often - so we can add yet another layer of caching by saving all the unit's uniques, and rebuilding this every time there's a change
+All of this is VITAL for performance - Unciv is built to run on potatoes, and even hash lookups are expensive when performed often, not to mention Regexes required for Unique parsing!
+ + + + + + + + + + + + + +This is a work in progress - feel free to contribute. Much of this information is not specific to Unciv and publicly available.
+Unciv.android.main
Automatically attach on Debug.waitForDebugger()
Debugging on physical devices is actually easiest. +With Studio running, you will have adb running, and any newly connected device that generally allows debugging will ask to confirm your desktop's fingerprint (use an USB cable for this tutorial, IP is another matter). +Once adb sees the device and your desktop is authorized from the device, it will be available and preselected on the device select-box to the right of your "android" run configuration and you can start debugging just like the desktop version. +Note A debug session does not end after selecting Exit from Unciv's menus - swipe it out of the recents list to end the debug session. Hitting the stop button in Studio is less recommended. That's an Android feature.
+Android Studio has a menu entry "Build -> Build Bundle(s) / APK(s) -> Build APK(s)." +This will build a ready-to-install APK, and when it is finished, pop a message that offers to show you the file in your local file manager. +Important such locally built APK's are debug-signed and not interchangeable with Unciv downloaded from stores. You cannot update one with the other or switch without uninstalling first - losing all data.
+(TODO) +- Install Emulator +- Intel HAXM: Deprecated by Intel but still recommended +- Download system image + - Choice: Match host architecture, w/o Google, older is faster...? +- Configure AVD +- Debug on AVD
+Unciv's log system runs on top of the Android SDK one, and filters and tags the messages before passing them to the system.
+Like the desktop variant, it has a 'release' mode where all logging from Unciv code is dropped. +A release is detected when the actual APK manifest says debuggable=false - all possibilities discussed here are debug builds in that sense. +Running from Studio it does not matter which button you use - Run or Debug - both deploy a debug build, the difference is only whether it attaches the debugger right away. +An APK built from Studio is also always a debug build.
+Therefore, logging is always enabled unless you run a store version.
+You can override this by providing an intent extra: In your Run configuration, on the "General" Tab, add in the "Launch Flags" field: --ez debugLogging false
.
+The override can also be controlled without Studio using the activity manager:
+
adb shell am start com.unciv.app/com.unciv.app.AndroidLauncher --ez debugLogging true
+
am start...
directly from a device terminal) will turn on logging for a release (store) build.
+The log system's filtering capabilities that work by providing -D
options to the Java virtual machine cannot be controlled on Android as far as we know.
+(TODO - document those in the desktop/Studio wiki article)
(TODO)
+- Studio
+ - If the logcat window is missing: View - Tool Windows - Logcat
+- Studio's filtering
+ - When you debug Unciv, a matching filter is pre-applied to the Logcat window, but the tool can actually show the entire system log, including those for other apps.
+ - Using package:com.unciv.app tag:Unciv
as filter is useful to see only the output of Unciv's own logging system.
+- logcat apps on the device
+ - com.pluscubed.matloglibre
? Outdated.
+- logcat apps need root or specific authorization
+ - adb shell pm grant <logcat app's package id> android.permission.READ_LOGS
Unciv is, at its core, a remake of Civ V, meaning mechanics-wise there's almost by definition not much place for innovation. +In terms of UI, there's nothing here that hasn't been done dozens of times, with far greater polish. +However, there is one area where Unciv is groundbreaking: in its accessibility of translations, the possibility space of its mods, and the relationship between them.
+So let's start with translation. Surely this is a solved problem, right? Source text + language = translated text, and this information needs to be in a file so the game can read it. What makes us different from, for example, Firaxis?
+There are a couple of things, but the most significant is that this is an open-source game, and thus the translations are open-source as well. +This means translators are both amateurs and not obligated to translate, so if translating is difficult, they simply won't.
+Amateurs can make mistakes, which is why it's vital that mistakes are easy to spot. That means that formats like "translation key" - e.g. DIPLOMACY_GREETING = Siamo lieti di fare la vostra conoscenza.
are much less effective than A pleasure to meet you. = Siamo lieti di fare la vostra conoscenza.
This format lends itself both the easier translation (it's immediately obvious what needs to be translated) and actual collaboration.
A common suggestion that we get (by people with little familiarity with the project) is to "use a website for translation". This is not bad advice for a small open source game, but there are multiple disadvantages that (for now) no translation website provides enough advantage to outweigh:
+Here are some ways that we managed to go wrong in the past:
+The format we decided to go for is one file per language, delimited by " = " for visual separation, in a .properties file. Lines starting in # are considered comments, so we can add comments for translators.
+As stated, Unciv releases versions semiweekly, and very often these changes include new objects or new UI elements. How do we keep all translation files up to date?
+In Unciv, all object data is stored in json format. This allows us to iterate on all objects, regardless of type, and extract the various text fields (strings or lists of strings). We avoid duplication by saving all translation texts we've already added, and use the existing translations to populate the "value" for each translation "key" we found in the json files.
+Since we rebuild the entire translation file every time, there's currently no way for translators to retain their own comments for future translators.
+But on the other hand, since for each line that we add we already know if it's translated or not, this allows us to add a # Requires translation
line before every non-translated line, which helps translators for languages that are almost fully translated to easily locate the new or changed terms for translation with ctrl+f (and of course this marking will disappear the next time we rebuild the file).
Since there are UI texts that are not part of any specific object (like "Start new game"), we have a separate template.properties file for texts to translate that are not in the json files. Unlike adding objects, where the developer doesn't need to address the translation files at all since it's all linked, when adding UI elements with new texts devs need to remember to add the texts to template.properties file.
+This is all well and good for specific text-to-text translations, but what about translating "A Temple has been built in Rome"? The same template could potentially be any building name, or any city name!
+We do this with placeholders, which looks something like this: [construction] has been built in [cityName] = [cityName] ha costruito [construction]
.
+As you can see, the placement of the parameters can change between languages, so we have to name all parameters.
This also means that there can be explicitly wrong translations - if any parameter that appears in the source does not appear in the translated version, we won't be able to display this in-game! This is one of the translation tests that we mentioned earlier - when a translator opens a PR, the game undergoes build & test via the Github Actions, and will notify on failures. Finding the text that warns of the failure within the action output is currently mostly done by devs, but I hope to be able to automate this too someday.
+To translate a text like "[Temple] has been built in [Rome]", therefore, we need to:
+[cityName] ha costruito [construction]
, replace "[cityName]" with translation of "Rome", and "[construction]" with translation of "Temple")The translation generation reads information from "a ruleset", i.e. the set of jsons defining the game's objects. +Every mod is also a ruleset, either replacing or adding to the base ruleset defined in the game. +This means that the same translation generation that we do for the base game can also be applied to mods, and so each modder can decide (from within the game) to generate translation files for his mod, and since mods are uploaded to Github to be widely available as part of the mod release methodology, translators will be able to translate those files the exact same way that they translate Unciv's base ruleset.
+ + + + + + + + + + + + + +Unciv is backed by GDX's scene2d for the UI, so check out their official documentation for more info about that.
+We mainly use the Table
class of scene2d, because it offers nice flexibility in laying out all the user interface.
FasterUIDevelopment
classThis class is basically just a small helper GDX application to help develop UI components faster.
+It sets up the very basics of Unciv, so that you can then show one single UI component instantly. This gives you much faster response times for when you change something, so that you can immediately see the changes you made, without having to restart the game, load a bunch of stuff and navigate to where your UI component would actually be.
+To use it, you change the DevElement
class within the FasterUIDevelopment.kt
file so that the actor
field is set to the UI element you want to develop. A very basic usage is there by default, just showing a label, but you can put any UI element there instead.
Once it's up and running, your UI element is centered in the window and gets an orange border. You can toggle Scene2D debugging with the middle mouse button (click the wheel).
+Note that the parent your UI element will get doesn't set the size (which in reactive nested layouts would be its responsibility), so if your element is a WidgetGroup like Table, just pack() it. Or ignore the orange dot left over from the border. Also please do not resize the window - there's no support for that at the moment and no guarantees can be given for the results.
+class DevElement(
+ val screen: UIDevScreen
+) {
+ lateinit var actor: Actor
+ fun createDevElement() {
+ actor = "This could be your UI element in development!".toLabel()
+ }
+
+ fun afterAdd() {
+ }
+}
+
You can then simply run the main
method of FasterUIDevelopment
to show your UI element.
There's two ways to do so:
+* Open this file in Android Studio under project:docs/Developers. That line above has a little green arrow in the left gutter that can be clicked.
+* Create a Run Configuration similar to the desktop one described here, but with classpath Unciv.tests.test
and main class com.unciv.dev.FasterUIDevelopment
.
+
This option (on the secret 'Debug' page) turns on several UI debug features: +* Gdx Actor debug lines +* Mouse coordinates and FPS +* Descriptor of the Actor under the mouse +* Coordinate scales
+See Gdx wiki
+The Scene2D debug option turns on mouse-over Gdx debug lines at the stage level using setDebugUnderMouse
, setDebugTableUnderMouse
and setDebugParentUnderMouse
.
+* Blue lines are Table bounds - Each Table has dimensions as a Widget and as a logical table determined by the cells. They will coincide if there is both expandX and expandY set somewhere.
+* Red lines are Cell bounds - the part within cell padding.
+* Green lines are Actor bounds of the Cell contents. If the Cell has both Fill directions set, they disappear below the red cell bounds (that is, unless the content Actor has a maxSize limiting the fill).
On the bottom right is a semi-transparent panel with 3 numbers on top: X and Y of the mouse in Gdx stage coordinates, and FPS.
+The lower part of said panel shows a string helping to identify which Actor the mouse is over. This will look for the actor's parent and potentially children, and the optional Actor.name field. Java class names or Label text are used when appropriate - it tries to build something short, but just descriptive enough. It uses the following separators / symbols:
+* :
a colon separates class name from Actor.name - or Actor.name is used undecorated if it contains the class name.
+* "
double-quotes show actual Label or TextButton text, max 20 characters, prefixed directly with the class name.
+* .
a dot separates parent from hit Actor: If the above alone does not yield a descriptive label, the parent (if any) is added as descriptive label per the rules above or as simple class name.
+* (
..)
after the above designates a sample from the children (the first nicely descriptive one), only if the parent won't add good recognition value.
The bottom and right edges of the screen get tiny tick marks, each 20 units in the Gdx stage coordinates, to help estimate paddings or sizes.
+ + + + + + + + + + + + + +Objects in the game - terrains, units, buildings, improvements, etc - differ by their stats, but what makes them truly different mechanically are their special abilities, or as we call them - Uniques.
+Each game object can have any number of these Uniques.
+The different possible types of uniques are available here, with each unique having a string value, e.g. "Gain [amount] [stat/resource]"
These are unique types, because they are in fact templates for possible concrete uniques, where the parameters in square brackets are filled in with specific values - e.g. "Gain [20] [Gold]"
+Game objects should have concrete uniques (parameters filled in)
Every parameter in square brackets, is defined by its type, a list of which is available here - each parameter type has its own text value, e.g. "amount" means an integer. +That determines possible values that this parameter can contain, e.g. "amount" should only contain strings that can be serialized as integers.
+Concrete uniques that contain incorrect values (e.g. "Gain [three] [money]"
) are warned against in the mod checker, and if they're serious enough, also when starting a new game with the mod
Sometimes uniques are deprecated - Unciv provides autoupdating, meaning you only need to click a button to update the deprecated uniques in your mod!
+Uniques can be modified to do certain things, using special Uniques that are shown in the uniques list within <these brackets>
.
This is done by adding these modifiers after the unique like so: "Gain [30] [Gold] <after discovering [Steam Power]>"
The most common type of modifier is a conditional - basically limiting the unique to only apply under certain conditions - so all modifiers are sometimes refered to as conditionals.
+Other more specialized types of modifiers exist:
+As you can see, these conditionals also can contain parameters, and these follow the same rules for parameters as the regular uniques.
+Most uniques are long-term effects - they apply as long as you have the object containing them (building, resource, tech, policy). +Trigger uniques are different - they are one-time effects, but which may be triggered several times.
+Trigger uniques come in two flavors - civ-wide triggers and unit-wide triggers. +Unit triggerables are only relevant in the context of a specific unit, so they can be attached to units, promotions or unit types.
+Any triggerable unique added to a unit which has unit action modifiers will be considered as a unit action. +Unit actions can contain civ-wide effects as well.
+Trigger uniques specify their activation with trigger conditions. +Like the triggers themselves, these come in two flavors - civ-wide conditions and unit-wide conditions. +Trigger uniques with no trigger modifiers are activated upon construction (units, buildings, improvements) or discovery (techs, policies).
+Events are a ruleset object that act as a sort of "extended trigger" - they are activated by the "Triggers a [event] event"
unique, and they trigger other uniques depending on user choice.
We parse the unique by comparing the string given by the modder minus square bracket contents, to the known list of uniques. +If we find a match - Congrats, we set that as the unique type.
+When we check for uniques in the code, it's always for uniques of a specific type, so we can just check the 'unique type' we previously assigned
+We then take the parameters of that unique, which we also determined previously by scanning for all strings within square brackets, and use their values in determining the effect of the unique
+There is a LOT of caching involved everywhere to make this all as fast as possible, but you really don't need to worry about that, that's my job ;)
+ + + + + + + + + + + + + +In a perfect world, the AI would pass the "Turing test" of gameplay - you would't be able to tell if you're playing against a human or AI.
+Examples:
+There is a fine line here between "exploitable" and "no fun" regarding trade - regular players may refuse any trade you offer them on principle. +We don't want that from the AI, which leaves us slightly open to exploits, but that's a trade-off we make knowingly.
+As a new modder it's easy to get lost in the sheer number of uniques.
+Our aim is to minimize the number of uniques as much as possible, but enable "emergent modding" by allowing combinations.
+Examples:
+A crash stacktrace is halfway to a solution - a game save which reliably produces it is 90% there.
+Whenever an unexpected situation occurs - the game has reached an incorrect state - we should crash, to allow the problem to be fixed as soon as possible.
+Persisting with an incorrect state makes the eventual resulting problems further from the cause, and complicates debugging.
+ + + + + + + + + + + + + +Unciv contains built-in capabilities for packing images and autoupdating uniques.
+You can automate these updates by adding a GitHub Actions workflow to your repository.
+On every commit, and once per day, it will:
+If there are changes, this will create a PR to your repo - here's an example - which you can choose to accept
+If you see that the autoupdate isn't 100% - in which case talk to me and we'll sort it out 🙂
+If you get a remote: Permission to <...>.git denied to github-actions[bot].
+You need to:
You should read the Mods page first before proceeding
+In order to add a UI skin mod (yes, UI skins are just another type of mod), all you need to do is add your images under Images/Skins/MyCoolSkinExample
and enable the mod as a permanent visual mod.
The game will then recognize the skin, and allow you to pick it in the options menu.
+Just like tilesets, UI skins can be used to alter the appearance of Unciv. Please note that UI skins do not support custom icons and fonts and not every UI element can be customized yet too.
+We use so called 9.png (or Ninepatch) files for every skin image because UI elements need a way to be resized based on game window size and resolution. Ninepatch files can be created manually by adding black pixels around your custom images in a specific manner or by using Android Studio's Draw 9-patch tool or this tool by romannurik for example. You may also check if your favorite image creation tool supports nine patches itself to generate them more easily.
+A skin image can either be gray scale and later be colored in game by modifying the tint
in the skinConfig or be colored directly in the image. When coloring the image directly it is important to set the tint of the UI element to white. Please note that tileable ninepatches and ninepatches with multiple stretch areas are not supported because of technical restrictions by libgdx.
There are 6 basic shapes which can be placed inside the Images/Skins/MyCoolSkinExample
folder:
+ - checkbox
+ - checkbox-pressed
+ - rectangleWithOutline
+ - roundedEdgeRectangle
+ - select-box
+ - select-box-pressed
These shapes are used all over Unciv and can be replaced to make a lot of UI elements change appearance at once. To change just one specific element use the table below to create an image at the specified directory using the specified name inside Images/Skins/MyCoolSkinExample
. See the image below for an example file structure.
Directory | +Name | +Default shape | +Image | +
---|---|---|---|
AnimatedMenu/ | +Button | +roundedEdgeRectangleMid | ++ |
CityScreen/ | +CityPickerTable | +roundedEdgeRectangle | ++ |
CityScreen/CitizenManagementTable/ | +AvoidCell | +null | ++ |
CityScreen/CitizenManagementTable/ | +FocusCell | +null | ++ |
CityScreen/CitizenManagementTable/ | +ResetCell | +null | ++ |
CityScreen/CityConstructionTable/ | +AvailableConstructionsTable | +null | ++ |
CityScreen/CityConstructionTable/ | +ConstructionsQueueTable | +null | ++ |
CityScreen/CityConstructionTable/ | +Header | +null | ++ |
CityScreen/CityConstructionTable/ | +PickConstructionButton | +null | ++ |
CityScreen/CityConstructionTable/ | +PickConstructionButtonSelected | +null | ++ |
CityScreen/CityConstructionTable/ | +QueueEntry | +null | ++ |
CityScreen/CityConstructionTable/ | +QueueEntrySelected | +null | ++ |
CityScreen/CityScreenTileTable/ | +Background | +null | ++ |
CityScreen/CityScreenTileTable/ | +InnerTable | +null | ++ |
CityScreen/CityStatsTable/ | +Background | +null | ++ |
CityScreen/CityStatsTable/ | +InnerTable | +null | ++ |
CityScreen/ConstructionInfoTable/ | +Background | +null | ++ |
CityScreen/ConstructionInfoTable/ | +SelectedConstructionTable | +null | ++ |
CivilopediaScreen/ | +EntryButton | +null | ++ |
DiplomacyScreen/ | +LeftSide | +null | ++ |
DiplomacyScreen/ | +RightSide | +null | ++ |
DiplomacyScreen/ | +SelectedCiv | +null | ++ |
General/ | +AnimatedMenu | +roundedEdgeRectangle | ++ |
General/ | +Border | +null | ++ |
General/ | +ExpanderTab | +null | ++ |
General/ | +HealthBar | +null | ++ |
General/ | +KeyCapturingButton | +roundedEdgeRectangleSmall | ++ |
General/ | +TabbedPager | +null | ++ |
General/ | +Tooltip | +roundedEdgeRectangle | ++ |
General/Popup/ | +Background | +null | ++ |
General/Popup/ | +InnerTable | +null | ++ |
LanguagePickerScreen/ | +LanguageTable | +null | ++ |
LoadGameScreen/ | +BottomTable | +null | ++ |
LoadGameScreen/ | +TopTable | +null | ++ |
MainMenuScreen/ | +Background | +null | ++ |
MainMenuScreen/ | +MenuButton | +roundedEdgeRectangle | ++ |
MainMenuScreen/ | +Version | +roundedEdgeRectangle | ++ |
MapEditor/MapEditorToolsDrawer/ | +Handle | +null | ++ |
ModManagementOptions/ | +ExpanderTab | +null | ++ |
ModManagementScreen/ | +BottomTable | +null | ++ |
ModManagementScreen/ | +TopTable | +null | ++ |
MultiplayerScreen/ | +BottomTable | +null | ++ |
MultiplayerScreen/ | +TopTable | +null | ++ |
NewGameScreen/ | +BottomTable | +null | ++ |
NewGameScreen/ | +GameOptionsTable | +null | ++ |
NewGameScreen/ | +MapOptionsTable | +null | ++ |
NewGameScreen/ | +PlayerPickerTable | +null | ++ |
NewGameScreen/ | +TopTable | +null | ++ |
NewGameScreen/NationTable/ | +Background | +null | ++ |
NewGameScreen/NationTable/ | +BorderTable | +null | ++ |
NewGameScreen/NationTable/ | +RightInnerTable | +null | ++ |
NewGameScreen/NationTable/ | +Title | +null | ++ |
NewGameScreen/PlayerPickerTable/ | +PlayerTable | +null | ++ |
OverviewScreen/DiplomacyOverviewTab/ | +CivTable | +null | ++ |
OverviewScreen/NotificationOverviewTable/ | +Notification | +roundedEdgeRectangle | ++ |
OverviewScreen/ReligionOverviewTab/ | +BeliefDescription | +null | ++ |
OverviewScreen/TradesOverviewTab/ | +OffersTable | +null | ++ |
OverviewScreen/UnitOverviewTab/ | +UnitSupplyTable | +null | ++ |
PlayerReadyScreen/ | +Background | +null | ++ |
PolicyScreen/ | +PolicyBranchAdoptButton | +roundedEdgeRectangleSmall | ++ |
PolicyScreen/ | +PolicyBranchAdoptButtonBorder | +roundedEdgeRectangleSmall | ++ |
PolicyScreen/ | +PolicyBranchBackground | +rectangleWithOutline | ++ |
PolicyScreen/ | +PolicyBranchBackgroundBorder | +rectangleWithOutline | ++ |
PolicyScreen/ | +PolicyBranchHeader | +rectangleWithOutline | ++ |
PolicyScreen/ | +PolicyBranchHeaderBorder | +rectangleWithOutline | ++ |
PolicyScreen/Colors/ | +BranchBGAdopted | +50,45,5 | ++ |
PolicyScreen/Colors/ | +BranchBGCompleted | +255,205,0 | ++ |
PolicyScreen/Colors/ | +BranchBGNotAdopted | +5,45,65 | ++ |
PolicyScreen/Colors/ | +BranchHeaderBG | +47,90,92 | ++ |
PolicyScreen/Colors/ | +BranchLabelAdopted | +150,70,40 | ++ |
PolicyScreen/Colors/ | +BranchLabelNotPickable | +0xffffff7f | ++ |
PolicyScreen/Colors/ | +BranchLabelPickable | +WHITE | ++ |
PolicyScreen/Colors/ | +ButtonBGAdopted | +1,17,19 | ++ |
PolicyScreen/Colors/ | +ButtonBGAdoptedSelected | +1,17,19 | ++ |
PolicyScreen/Colors/ | +ButtonBGNotPickable | +20,20,20 | ++ |
PolicyScreen/Colors/ | +ButtonBGNotPickableSelected | +20,20,20 | ++ |
PolicyScreen/Colors/ | +ButtonBGPickable | +32,46,64 | ++ |
PolicyScreen/Colors/ | +ButtonBGPickableSelected | +37,87,82 | ++ |
PolicyScreen/Colors/ | +ButtonIconAdopted | +GOLD | ++ |
PolicyScreen/Colors/ | +ButtonIconAdoptedSelected | +GOLD | ++ |
PolicyScreen/Colors/ | +ButtonIconNotPickable | +0xffffff33 | ++ |
PolicyScreen/Colors/ | +ButtonIconNotPickableSelected | +0xffffff33 | ++ |
PolicyScreen/Colors/ | +ButtonIconPickable | +WHITE | ++ |
PolicyScreen/Colors/ | +ButtonIconPickableSelected | +WHITE | ++ |
PromotionScreen/ | +PromotionButton | +roundedEdgeRectangleMid | ++ |
PromotionScreen/ | +PromotionButtonBorder | +roundedEdgeRectangleMidBorder | ++ |
TechPickerScreen/ | +Background | +null | ++ |
TechPickerScreen/ | +BottomTable | +null | ++ |
TechPickerScreen/ | +CurrentTechColor | +72, 147, 175 | ++ |
TechPickerScreen/ | +QueuedTechColor | +72, 462, 43*2 | ++ |
TechPickerScreen/ | +ResearchableTechColor | +28, 170, 0 | ++ |
TechPickerScreen/ | +ResearchedFutureTechColor | +127, 50, 0 | ++ |
TechPickerScreen/ | +ResearchedTechColor | +255, 215, 0 | ++ |
TechPickerScreen/ | +TechButtonIconsOutline | +roundedEdgeRectangleSmall | ++ |
VictoryScreen/ | +CivGroup | +roundedEdgeRectangle | ++ |
WorldScreen/ | +AirUnitTable | +null | ++ |
WorldScreen/ | +BattleTable | +null | ++ |
WorldScreen/ | +Notification | +roundedEdgeRectangle | ++ |
WorldScreen/ | +PickTechButton | +roundedEdgeRectangle | ++ |
WorldScreen/ | +TileInfoTable | +null | ++ |
WorldScreen/ | +TutorialTaskTable | +null | ++ |
WorldScreen/ | +UnitTable | +roundedEdgeRectangleMid | ++ |
WorldScreen/CityButton/ | +AirUnitTable | +roundedEdgeRectangleSmall | ++ |
WorldScreen/CityButton/ | +AirUnitTableBorder | +roundedEdgeRectangleSmall | ++ |
WorldScreen/CityButton/ | +DefenceTable | +roundedTopEdgeRectangleSmall | ++ |
WorldScreen/CityButton/ | +DefenceTableBorder | +roundedTopEdgeRectangleSmallBorder | ++ |
WorldScreen/CityButton/ | +IconTable | +roundedEdgeRectangleMid | ++ |
WorldScreen/CityButton/ | +IconTableBorder | +roundedEdgeRectangleMidBorder | ++ |
WorldScreen/CityButton/ | +InfluenceBar | +null | ++ |
WorldScreen/Minimap/ | +Background | +null | ++ |
WorldScreen/Minimap/ | +Border | +null | ++ |
WorldScreen/NextTurn/ | +ProgressBar | +null | ++ |
WorldScreen/NextTurn/ | +ProgressColor | +FOREST | ++ |
WorldScreen/TopBar/ | +LeftAttachment | +roundedEdgeRectangle | ++ |
WorldScreen/TopBar/ | +ResourceTable | +null | ++ |
WorldScreen/TopBar/ | +RightAttachment | +roundedEdgeRectangle | ++ |
WorldScreen/TopBar/ | +StatsTable | +null | ++ |
WorldScreenMusicPopup/TrackList/ | +Down | +null | ++ |
WorldScreenMusicPopup/TrackList/ | +Over | +null | ++ |
WorldScreenMusicPopup/TrackList/ | +Up | +null | ++ |
The skinConfig is similar to the tilesetConfig and can be used to define different colors and shapes for unciv to use.
+To create a config for your skin you just need to create a new .json file under jsons/Skins/
. Just create a .txt file and rename it to MyCoolSkinExample.json. You only have to add things if you want to change them. Else the default values will be used.
This is an example of such a config file that will be explain below:
+{
+ "baseColor": {"r":1,"g":0,"b":0,"a":1},
+ "defaultVariantTint": {"r":1,"g":1,"b":1,"a":1},
+ "skinVariants": {
+ "MainMenuScreen/MenuButton": {
+ "image": "MyCoolNewDesign",
+ "foregroundColor": {"r": 0, "g": 0, "b": 1, "a": 1},
+ "iconColor": {"r": 0, "g": 1, "b": 0, "a": 1}
+ },
+ "TechPickerScreen/TechButton": {
+ "image": "MyCoolNewDesign",
+ "alpha": 0.7
+ },
+ "WorldScreen/TopBar/ResourceTable": {
+ "alpha": 0.8
+ },
+ "WorldScreen/UnitTable": {
+ "tint": {"r": 1, "g": 0, "b": 0},
+ "image": "WorldScreen/TopBar/ResourceTable",
+ "alpha": 0.4
+ },
+ "WorldScreen/Minimap/Background": {
+ "tint": {"r": 0.2, "g": 0.4, "b": 0.45, "a": 1}
+ }
+ }
+}
+
A color defined with normalized RGBA values. Default value: {"r": 0, "g": 0.251, "b": 0.522, "a": 0.749}
Defines the color unciv uses in most ui elements as default
+A string. Default value: "Minimal".
+The name of another skin to use as a fallback if an image is not found or not specified in this skin. +Set to null to disable fallback.
+A color defined with normalized RGBA values. Default value: null
+The tint all skinVariants should use if not explicitly specified in a skinVariant.
+If you mostly use colored images set this to white ({"r": 1, "g": 1, "b": 1, "a": 1}
) to get
+the correct colors.
A dictionary mapping string to a SkinElement. Default value: empty
+These variants can be used to define a different image, tint and/or alpha for a specified UI element. The string used to identify the UI element can be taken from the table above by appending the name to the directory. +
| Directory | Name |
+|-----------------------|---------------|
+| WorldScreen/ | Notification | -> WorldScreen/Notification
+| WorldScreen/TopBar/ | StatsTable | -> WorldScreen/TopBar/StatsTable
+
A path to an image. The file is expected to be located alongside the 6 basic shapes inside the Images/Skins/MyCoolSkinExample
folder if just a name like MyCoolNewDesign
is given. The image path can also be another ui element like WorldScreen/TopBar/ResourceTable
so images can be reused by other elements.
A color defined with normalized RGBA values. Default value: null
+The tint this UI element should get. Is applied as a tint on top of the image. This means that if the +image is colored and the tint is not white the tint color will merge with the image color and not override it.
+A float value. Default value: null
+The alpha this UI element should have. Overwrites the alpha value of tint if specified.
+A color defined with normalized RGBA values. Default value: null
+The color this UI element should use for its font and icons. To color icon and font differently use
+the iconColor
in addition to this.
A color defined with normalized RGBA values. Default value: null
+The color this UI element should use for its icons. Overrides the foregroundColor
for icons if specified.
You should read the Mods page first before proceeding
+In order to add a tileset mod (yes, tilesets are just another type of mod), all you need to do is add your images under Images/Tilesets/MyCoolTilesetExample and enable the mod as a permanent visual mod - the game will recognize the tileset, and allow you to pick it in the options menu.
+Let's look at the example "Grassland+Jungle+Dyes+Trading post" to learn how the game decides which images it should use for this tile:
+All these images can also use era-dependant variants if you want to change the appearance of, let's say, "Trading post" throughout the game. Just create images and add the suffix "-[era name]". +E.g. "Trading post-Classical era", "Trading post-Industrial era", etc.
+It is advised to use the layered approach (1 and 3) often because it comes with a few advantages. Mainly:
+You should keep in mind that the default rendering order is: +BaseTerrain, TerrainFeatures, Resource, Improvement.
+This is where tileset configs shine. You can use these to alter the way Unicv renders tiles.
+To create a config for your tileset you just need to create a new .json file under jsons/Tilesets/. Just create a .txt file and rename it to MyCoolTilesetExample.json. You only have to add things if you want to change them. Else the default values will be used.
+This is an example of such a config file that will be explain below:
+{
+ "useColorAsBaseTerrain": "false",
+ "useSummaryImages": "true",
+ "unexploredTileColor": {"r":1,"g":1,"b":1,"a":1},
+ "fogOfWarColor": {"r":1,"g":0,"b":0,"a":1},
+ "fallbackTileSet": null,
+ "tileScale":0.9,
+ "tileScales": {
+ "City center":1.2,
+ "Citadel":1.5
+ },
+ "ruleVariants": {
+ "Grassland+Forest": ["Grassland","ForestForGrassland"],
+ "Grassland+Jungle+Dyes+Trading post": ["Grassland","JungleForGrasslandBack","Dyes+Trading post","JungleForGrasslandFront"]
+ }
+}
+
A boolean value ("true" or "false"). Default value: "false"
+If true, an additional "Hexagon" image is placed below each tile and colored in the corresponding BaseTerrain color. This removes the necessity to add individual BaseTerrain images. This is how the "Minimal" tileset works.
+A boolean value ("true" or "false"). Default value: "false"
+If true, summary images are used for specific groups of images instead of using individual tile images. The summary images must be placed in the same folder as every other tile image. Summary images used:
+Image group | +Summary image | +
---|---|
Natural wonders | +"NaturalWonder" | +
A color defined with normalized RGBA values. Default value: "{"r":0.24705882, "g":0.24705882, "b":0.24705882, "a":1}" (DarkGray)
+Defines the color of the unexplored tiles.
+A color defined with normalized RGBA values. Default value: "{"r":0, "g":0, "b":0, "a":1}" (Black)
+Defines the color of the fog of war. The color gets approximated by 60% to allow the colors of the images below to shine through.
+A string value. Default value: "FantasyHex"
+The name of another tileset whose images should be used if this tileset is missing images. Can be set to null to disable the the fallback tileset
+A float value. Default value: 1.0
+The scale of all tiles. Can be used to increase or decrease the size of every tile. Is being used by the tileset mod 5Hex (made by ravignir) to fake shadows.
+A dictionary mapping string to a float value. Default value: empty
+Used by the "Minimal" tileset to scale all its tiles except the base terrain down. Each entry overrides the tileScale value for the specified tile.
+A dictionary mapping string to a list of strings. Default value: empty
+The ruleVariants are the most powerful part of the tileset config. With this, you can define, for a specific tile, which images and in which order these images should be used.
+An example is given in the code above. For the tile "Grassland+Jungle+Dyes+Trading post" we then use the images "Grassland", "JungleForGrasslandBack", "Dyes+Trading post" and "JungleForGrasslandFront" in that order.
+Unciv distinguishes between "unexplored" tiles, which are tiles the Civ has never seen, +and "not visible" tiles, which are those that were seen once but now are not.
+Not visible tiles are grayed out by design, and on top of that have the CrosshatchHexagon.png
image applied to them.
Unexplored tiles display the UnexploredTile.png
image, on top of which CrosshatchHexagon.png
is applied.
You can set the CrosshatchHexagon to be functionally invisible by replacing it with a 1px by 1px invisible image.
+Unit images can be changed according to civ-specific styles (if a mod specifies a "style" variable for each civilization) and according to the owning civ's current era. Unciv attempts to load the unit images in the following order (where unitName is the unit name given in Units.json, styleName is optionally specified in Nations.json, and eraName is the era name given in Eras.json (including " era")).
+Era-specific sprites do not need to be specified for each era, only on eras where the sprites change. If a modder wants a Great General unit to change sprites starting in the Modern era, they only need to create a "Great General-Modern era.png" image. The Great General unit would use the default "Great General.png" sprite for all eras up to the Modern era then the Modern era sprite for the Modern era and all eras after unless there is a later era sprite for this unit.
+Unciv can colour units according to the civilization that owns them. [PR3231]
+This is used by providing multiple images per unit, each representing a coloured layer. The image suffixed with "-1" will be tinted to the civilization's inner colour, and the image suffixed with "-2" will be tinted to the civilization's outer colour. For example:
+Image | +Description | +Colour | +
---|---|---|
Archer.png | +Base image | +Untinted | +
Archer-1.png | +Colour layer | +Nation inner colour | +
Archer-2.png | +Colour layer | +Nation outer colour | +
The Civ Army Color Style Sheet mod by @AdityaMH and the 5Hex Tileset by @ravignir are very good practical examples of how this can be used.
+These are small animations that play on units when they receive damage.
+They can be for unit types (Archery, Seige, Cavalry) or for specific unit names
+The files should be in the format of <unit type/unit name>-attack-<frame number>
.
+For example, a 3 frame animation for Sword units would have the files Sword-attack-1.png
, Sword-attack-3.png
, Sword-attack-3.png
You can add additional images that will be drawn only when a tile is adjacent to another tile in a specific direction.
+The images should be placed in the Images/Tilesets/<tileset name>/Edges
folder, rather than in /Tiles
.
The name of the tile should be <tile name>-<origin tile filter>-<destination tile filter>-<neighbor direction>.png
, where direction of one of:
And where the tile filter is one of: +- Terrain name +- Feature name +- Terrain type (Land/Water)
+For example: Cliff-Hills-Coast-Top.png
The initial name has no bearing on the image used, it is just a way to group images together.
+ + + + + + + + + + + + + +Images need to be 'packed' before the game can use them. This preparation step needs to happen only once (as long as the original graphics are not changed).
+The result one ore more a pairs of files - a texture in png format and a corresponding atlas file.
+If you have a single Images
folder, the default such pair is named game.png
/game.atlas
.
+For your players, the individual images aren't important - only the combined images actually register to the game, so you need to include them in your repository and keep them up to date.
+We still recommend including the originals in a mod, so other developers running from source can access them.
+With original images included, you can use a development environment using git, have it linked to your repository, while using a symlink to allow Unciv to see the mod - and pack your atlas for you on every launch.
+If you're developing your mod on an Android version of Unciv (not recommended!) you won't be able to generate these packed files directly.
java.awt
to do heavy lifting, which is unavailable on Android 0_0)If your mod has lots of images (or large ones), the textures might 'spill' into additional texture files - 2048x2048 is the limit for a single texture pack. You will see a game2.png
, possibly a game3.png
or more appear.
+This is not good for performance, which is why the base game controls which kinds of images go together into one texture(+atlas).
+This works for mods, too: Create not only one Images folder, but several, the additional ones named "Images.xyz", where xyz will become the filename of the additional texture file (So don't use both Images and Images.game - those will clash). Look at the Unciv base game to get a better idea how that works.
+To minimize texture swaps, try to group them by the situation where in the game they are needed. You can distibute by folder, but having the same subfolders under several "Images.xyz" and distributing the images between them will also work.
A file Atlases.json
(uppercase 'A') in the mod root (not in Images
or in jsons
) controls which atlases to load, which in turn control which texture (.png
) files are read.
+This file is automatically created by the built-in packer. Only the game.atlas
file is read by default for backward compatibility.
+If you use external tools and multiple atlases, you will need to maintain this file yourself - it is a simple json array of strings, each a file name without the .atlas
extension (saved as UTF-8 without byte order mark).
The texture packers built into Unciv will look for a TexturePacker.settings
file in each Images
directory (not under jsons
).
+With this file you can tune the packer - e.g. control pixel interpolation filters.
+It is a json of a Gdx TexturePacker.Settings instance.
+The default settings are as shown in the Gdx documentation linked above if you do supply a settings file, but without such a file, some fields have different defaults.
+To get these changed defaults, start with the following as base for your custom TexturePacker.settings
file:
{
+ "fast": true,
+ "combineSubdirectories": true,
+ "maxWidth": 2048,
+ "maxHeight": 2048,
+ "paddingX": 8,
+ "paddingY": 8,
+ "duplicatePadding": true,
+ "filterMin": "MipMapLinearLinear",
+ "filterMag": "MipMapLinearLinear",
+}
+
Due to certain circumstances, please make sure names and paths that will be mapped in an atlas use only ascii. Not all parts of the loader enforce strict UTF-8 usage, sorry. +Symptoms if you fail to heed this: mod works on a Chinese Windows box but not on a western one or vice-versa, or mod works on a Chinese Windows box but not a Chinese Linux box or vice-versa, or mod works on a Chinese Windows box with default settings but not on the same box with "Use unicode UTF-8 for worldwide language support" turned on. +This does not technically apply to the atlas name itself when multiple atlases are used (the xyz part in "Images.xyz"), but we nevertheless recommend the same rule for consistency.
+The following chapters describe possibilities that will work while a mod is active. +It is either selected for the current game (during new game creation, cannot be changed after that for saved games), meaning all its rules and resources will be used. +Or it is marked as 'Permanent audiovisual mod' in the mod manager (you must select it in the 'installed' column to get the checkbox). +In that case only graphics and audio will be active, the rule changes will be ignored (if it contains any) unless the first way is also used. +Note that this feature includes graphics or sounds from the selected mod in all games, even those started before installing the mod. +Repeat: In case of a mod bringing both changed rules and audiovisuals, the 'permanent' feature will include only the media on all games, to use the rules you will still need to select the mod for a new game.
+Note that the Mod author can (and often should) control whether the checkbox appears using ModOptions uniques.
+If a mod supplies an image with the same name and path as one included in the base game (and its atlas is up to date), and the mod is active, the mod's graphics will be used instead of the built-in one.
+For example, if you include a file named "Images/OtherIcons/Link.png" in your mod, you will be overriding the little chain links icon denoting linked lines in Civilopedia. The first part of the path is not relevant for overriding, it controls which of a set of atlas files will carry the image, but for selection in the game only the rest of the path is relevant. So, to override "Images.Tech/TechIcons/Archery.png" you could place your image as "Images/TechIcons/Archery.png" and it would work because the "TechIcons/Archery" part is the key.
+Please note, as for adding items, your graphics should keep the size and color choices of the original, or the result may be surprising, e.g. when the game tries to tint such an image.
+You will need to supply the graphics for new elements - a new unit needs its icon just as a new nation does. The rules are:
+Image[.AtlasName]/Type-specific/Objectname.png
(Type-specific means "TechIcons" for a Technology, "NationIcons" for a Nation and so on. See vanilla game folders. Objectname is the exact name as defined in json, before translation.)UnitIcons/<UnitName>.png
does not exist, we fall back to UnitTypeIcons/<UnitType>.png
- this allows setting a single image for an entire type of units without fiddling with each one [Unitname] ability
". In such a case, if UnitIcons/Unitname.png
exists it will fall back to that unit icon when UnitPromotionIcons/Unitname ability.png
is missing.UnitPromotionIcons/Something.png
will be loaded.[Warrior] ability III
" will fall back to the Warrior unit icon and paint 3 Stars on it.Additionally, there there are some kinds of images where the game has display capability but does not supply graphics itself, as described in the next paragraphs:
+You can add custom .ttf
fonts into the game: place .ttf
file inside of /fonts/
directory of your mod. The font you have added will be visible and choosable in Options-Advanced
tab at the top of font list as <fontname> (<modname>)
.
All fonts are rendered by default at 50 pixel size and rescaled later for the game's needs. Currently fonts are NOT mipmapped on minification.
+The textures in the EmojiIcons subfolder and some others are mapped into the font at specific codepoints. They are used by the game, can be used in any text of a mod, and can be overridden by mod textures. +Additionally, some code points are normally provided by the chosen system font, but have EmojiIcons names that will override the font glyph if a mod supplies them (marked 'optional' in the table below). +Note textures provided for such codepoints do respect aspect ratio, they do not need to be square like many built-in icons are!
+Symbol | +Codepoint | +Unicode name | +Texture path | +Optional | +
---|---|---|---|---|
⛏ | +U+26CF | +pick | +EmojiIcons/Automate | ++ |
♪ | +U+266A | +eighth note | +EmojiIcons/Culture | ++ |
☠ | +U+2620 | +skull and crossbones | +EmojiIcons/Death | ++ |
☮ | +U+262E | +peace symbol | +EmojiIcons/Faith | ++ |
⁂ | +U+2042 | +asterism | +EmojiIcons/Food | ++ |
¤ | +U+00A4 | +currency sign | +EmojiIcons/Gold | ++ |
♬ | +U+266C | +sixteenth note | +EmojiIcons/Great Artist | ++ |
⚒ | +U+2692 | +hammer | +EmojiIcons/Great Engineer | ++ |
⛤ | +U+26E4 | +pentagram | +EmojiIcons/Great General | ++ |
⚖ | +U+2696 | +scale | +EmojiIcons/Great Merchant | ++ |
⚛ | +U+269B | +atom | +EmojiIcons/Great Scientist | ++ |
⌣ | +U+2323 | +smile | +EmojiIcons/Happiness | ++ |
∞ | +U+221E | +infinity | +EmojiIcons/Infinity | +* | +
⚙ | +U+2699 | +gear | +EmojiIcons/Production | ++ |
⍾ | +U+237E | +bell symbol | +EmojiIcons/Science | ++ |
↑ | +U+FFEA | +halfwidth upwards arrow | +EmojiIcons/SortedAscending | +* | +
◉ | +U+25C9 | +fisheye | +EmojiIcons/SortedByStatus | +* | +
⌚ | +U+231A | +watch | +EmojiIcons/SortedByTime | +* | +
↓ | +U+FFEC | +halfwidth upwards arrow | +EmojiIcons/SortedDescending | +* | +
✯ | +U+272F | +pinwheel star | +EmojiIcons/Star | +* | +
⏳ | +U+23F3 | +hourglass | +EmojiIcons/Turn | ++ |
ⅰ | +U+2170 | +small roman numeral one | +MayaCalendar/0 | ++ |
ⅱ | +U+2171 | +small roman numeral two | +MayaCalendar/1 | ++ |
ⅲ | +U+2172 | +small roman numeral three | +MayaCalendar/2 | ++ |
ⅳ | +U+2173 | +small roman numeral four | +MayaCalendar/3 | ++ |
ⅴ | +U+2174 | +small roman numeral five | +MayaCalendar/4 | ++ |
ⅵ | +U+2175 | +small roman numeral six | +MayaCalendar/5 | ++ |
ⅶ | +U+2176 | +small roman numeral seven | +MayaCalendar/6 | ++ |
ⅷ | +U+2177 | +small roman numeral eight | +MayaCalendar/7 | ++ |
ⅸ | +U+2178 | +small roman numeral nine | +MayaCalendar/8 | ++ |
ⅹ | +U+2179 | +small roman numeral ten | +MayaCalendar/9 | ++ |
ⅺ | +U+217A | +small roman numeral eleven | +MayaCalendar/10 | ++ |
ⅻ | +U+217B | +small roman numeral twelve | +MayaCalendar/11 | ++ |
ⅼ | +U+217C | +small roman numeral fifty | +MayaCalendar/12 | ++ |
ⅽ | +U+217D | +small roman numeral one hundred | +MayaCalendar/13 | ++ |
ⅾ | +U+217E | +small roman numeral five hundred | +MayaCalendar/14 | ++ |
ⅿ | +U+217F | +small roman numeral one thousand | +MayaCalendar/15 | ++ |
ↀ | +U+2180 | +roman numeral one thousand cd | +MayaCalendar/16 | ++ |
ↁ | +U+2181 | +roman numeral five thousand | +MayaCalendar/17 | ++ |
ↂ | +U+2182 | +roman numeral ten thousand | +MayaCalendar/18 | ++ |
Ↄ | +U+2183 | +roman numeral reversed one hundred | +MayaCalendar/19 | ++ |
ය | +U+0DBA | +sinhala letter yayanna | +MayaCalendar/Baktun | ++ |
ඹ | +U+0DB9 | +sinhala letter amba bayanna | +MayaCalendar/Katun | ++ |
ම | +U+0DB8 | +sinhala letter mayanna | +MayaCalendar/Tun | ++ |
➡ | +U+27A1 | +black rightwards arrow | +StatIcons/Movement | ++ |
… | +U+2026 | +horizontal ellipsis | +StatIcons/Range | ++ |
‡ | +U+2021 | +double dagger | +StatIcons/RangedStrength | ++ |
† | +U+2020 | +dagger | +StatIcons/Strength | ++ |
You can add wonder images to mods and they'll be displayed instead of the standard icon when a wonder is finished. The image needs to be a .png and 2:1 ratio so for example 200x100 px.
+Add the images to /Images/WonderImages/
. They need to be named according to the name field in Buildings.json
, so for example "Temple of Artemis.png" or "Stonehenge.png"
Remember, to be compatible with mobile devices, a fresh atlas needs to be generated including these.
+The base game comes without Leader Portraits, but is able to display them in greetings, Civilopedia, diplomacy screens, or the nation picker. A mod can supply these, by adding their images to /Images/LeaderIcons/
. The file name must correspond exactly with the leader name of a nation as defined in Nations.json, or they will be ignored.
These work best if they are square, between 100x100 and 256x256 pixels, and include some transparent border within that area.
+For example, here is mod showing how to add leader portraits, which can complement the base game.
+The base game uses flat icons, surrounded with colored circles as backgrounds (e.g. for units to fit the civilization's flag colors), to denote entities such as: units, buildings, techs, resources, improvements, religions, promotions, uniques, unit actions and nations in the UI. A mod can supply "Portraits" - static images that will remain uncolored - by adding images to /Images/<entityType>Portraits/
(e.g. /Images/BuildingPortraits/
, /Images/ResourcePortraits/
, etc), which will be used in all UI elements (except for unit icons in the world map). The file name must correspond exactly with the unit/building/tech/resource/etc name defined in corresponding JSONs (e.g. Units.json, Buildings.json, TileResources.json, etc) or have the same name as the file they suppose to replace, or they will be ignored.
If mod supplies '/Images/
For example, here is mod showing how to add custom portraits, which can complement the base game.
+Available <entityType>Portraits/
include:
The Unit Types as defined in UnitTypes.json have no icons in the base game, but Civilopedia can decorate their entries if you supply images named 'Images/UnitTypeIcons/
The individual Beliefs - as opposed to Belief types, as defined in Beliefs.json have no icons in the base game, but Civilopedia can decorate their entries if you supply images named 'Images/ReligionIcons/
You can enable pictures for each of the Victories, illustrating their progress. That could be a Spaceship under construction, showing the parts you've added, or cultural progress as you complete Policy branches. They will be shown on a new tab of the Victory Screen.
+For this, you need to create a number of images. In the following, <>
denote names as they appear in VictoryTypes.json, untranslated, and these file names (like any other in Unciv) are case-sensitive. All files are optional, except Background as noted:
VictoryIllustrations/<name>/Background.png
- this determines overall dimensions, the others must not exceed its size and should ideally have identical size. Mandatory, if this file is missing, no illustrations will be shown for this Victory Type.VictoryIllustrations/<name>/Won.png
- shown if you (the viewing player) won this Victory.VictoryIllustrations/<name>/Lost.png
- shown if a competitor won this Victory - or you have completed this Victory, but have won a different one before.VictoryIllustrations/<name>/<milestone>.png
- One image for each entry in the milestones
field without an [amount]
, name taken verbatim but without square brackets, spaces preserved.VictoryIllustrations/<name>/<milestone> <index>.png
- For entries in the milestones
field with an [amount]
, one image per step, starting at index 1.VictoryIllustrations/<name>/<component>.png
- One image for each unique entry in the requiredSpaceshipParts
field, that is, for parts that can only be built once. Spaces in unit names must be preserved.VictoryIllustrations/<name>/<component> <index>.png
- For parts in the requiredSpaceshipParts
field that must be built several times, one per instance. Spaces in unit names must be preserved, and there must be one space between the name and the index. Indexes start at 1.Remember - these are logical names as they are indexed in your atlas file, if you let Unciv pack for you, the VictoryIllustrations
folder should be placed under <mod>/Images
- or maybe <mod>/Images.Victories
if you want these images to occupy a separate Victories.atlas
(Do not omit the Images
folder even if left empty, the texture packer needs it as marker to do its task).
That's almost all there is - no json needed, and works as 'Permanent audiovisual mod'. The Background image is the trigger, and if it's present all part images must be present too, or your spaceship crashes before takeoff, taking Unciv along with it. That was a joke, all other images are optional, it could just look boring if you omit the wrong ones.
+As for "almost" - all images are overlaid one by one over the Background one, so they must all be the same size. Except for Won and Lost - those, if their condition is met, replace the entire rest, so they can be different sizes than the background. The part images are overlaid over the background image in no guaranteed order, therefore they should use transparency to avoid hiding parts of each other.
+One way to create a set is to take one final image, select all parts that should be the centerpiece itself not background (use lasso, magic wand or similar tools, use antialiasing and feathering as you see fit), copy and paste as new layer. Then apply desaturation and/or curves to the selection on the background layer to only leave a hint of how the completed victory will look like. Now take apart the centerpiece - do a selection fitting one part name, copy and paste as new layer (in place), then delete the selected part from the original centerpiece layer. Rinse and repeat, then export each layer separately as png with the appropriate filenames. +There's no suggested size, but keep in mind textures are a maximum of 2048x2048 pixels, and if you want your images packed properly, several should fit into one texture. They will be scaled down if needed to no more than 80% screen size, preserving aspect ratio.
+Standard values are below. The sounds themselves can be found here.
+Mods can add their own sounds, as long as any new value in attackSound has a corresponding sound file in the mod's sound folder, using one of the formats mp3, ogg or wav (file name extension must match codec used). Remember, names are case sensitive. Small sizes strongly recommended, Unciv's own sounds use 24kHz joint stereo 8-bit VBR at about 50-100kBps.
+This works like graphics, except no atlas is involved. E.g. you include a sounds/Click.mp3, it will play instead of the normal click sound. These files must stay short and small. A sound larger than 1MB when uncompressed may break or not play at all on mobile devices. Unciv tries to standardize on 24kHz sample rate, joint stereo, low-bitrate VBR (-128kbps) mp3. Only mp3 and ogg formats will be recognized (but an existing mp3 can be overridden with an ogg file).
+Sound files (mp3 or ogg) in a mod /music folder will be recognized and used when the mod is active. Except for context-specific music as described in the following paragraphs, tracks will play randomly from all available tracks (with a little bias to avoid close repetition of tracks). There is no overriding - a "thatched-villagers.mp3" in a mod will play in addition to and with the same likelihood as the file that the base game offers to download for you. There is no hard technical limit on bitrate or length, but large bandwidth requirements may lead to stuttering (The end of a "next turn", right before the world map is updated, and with very large maps, is the most likely to cause this).
+The Music Controller will generally play one track after another, with a pause (can be changed in options) between. While the "Leave game?" confirmation dialog is opened playback will fade out and pause and can resume when it is closed.
+There are various 'triggers' in the game code initiating a choice for a new track. The new track will, if necessary, fade out the currently playing track quickly before it starts playing. Track choice involves context provided by the trigger and a random factor, and an attempt is made to not repeat any track until at least eight others have played.
+Mods can provide their own music folder, and if they are active its contents will be treated exactly the same as those in the main music folder. Mods should control usage of their tracks by careful choice of file name. Mod developers can watch console output for messages logging track choice with trigger parameters or loading errors.
+One track is special: The Thatched Villagers (see also credits.md). The game is able to download it if the music folder is empty, and it is played when the music volume slider is used. It is also a fallback track should certain problems occur (a broken file, however, will shut down the player until another trigger happens).
+Triggers indicate context (call it intent, mood, whatever, it doesn't matter) by optionally providing a prefix and/or suffix to match against the file name. There are a few flags as well influencing choice or behaviour - one flag function is to make prefix or suffix mandatory, meaning if no available file matches the track chooser will do nothing. Otherwise, a next track will always be chosen from the available list by sorting and then picking the first entry. Sorting is done by in order of precedence: Prefix match, Suffix match, Recently played, and a random number. Therefore, as currently no triggers have an empty prefix, files matching none of the prefixes will never play unless there are less than eight files matching the requested prefix.
+The current list of triggers is as follows:
+Description | +Prefix | +[^M] | +Suffix | +[^X] | +Flags | +
---|---|---|---|---|---|
Automatic next-track[^0] | ++ | + | Ambient | ++ | + |
Launch game[^1] | ++ | + | Menu | ++ | + |
Every 10th turn | +(player civ name) | +[^M] | +Peace or War[^2] | ++ | [^F] | +
New game: Select a mod | +(mod name) | +[^M] | +Theme | ++ | [^S] | +
New game: Pick a nation for a player | +(nation name) | +[^M] | +Theme or Peace | ++ | [^S] | +
Diplomacy: Select player | +(nation name) | +[^M] | +Peace or War[^3] | ++ | [^S] | +
First contact[^4] | +(civ name) | +[^M] | +Theme or Peace | +[^X] | ++ |
War declaration[^5] | +(civ name) | +[^M] | +War | +[^X] | ++ |
Civ defeated | +(civ name) | ++ | Defeat | +[^X] | ++ |
Player wins | +(civ name) | ++ | Victory | +[^X] | ++ |
Golden Age | +(civ name) | +[^M] | +Golden | +[^X] | ++ |
Wonder built | +(wonder name) | +[^M] | +Wonder | +[^X] | ++ |
Tech researched | +(tech name) | +[^M] | +Researched | +[^X] | ++ |
Map editor: Select nation start location | +(nation name) | +[^M] | +Theme | ++ | [^S] | +
Options: Volume slider or Default track downloaded | ++ | + | + | + | [^D] | +
Music controls (Options or from Menu) Next track | ++ | + | Ambient | ++ | + |
Legend:
+Sound files named from a Nation name and the corresponding text message's field name,
+placed in a mod's voices
folder, will play whenever that message is displayed. Nation name and message name must be joined with a dot '.', for example voices/Zulu.defeated.ogg
.
Leader voice audio clips will be streamed, not cached, so they are allowed to be long - however, if another Leader voice or a city ambient sound needs to be played, they will be cut off without fade-out
+Also note that voices for City-State leaders work only for those messages a City-state can actually use: attacked
, defeated
, and introduction
.
Here's a list of special dates (or date ranges) Unciv will recognize: +|-----| +| AprilFoolsDay | +| DiaDeLosMuertos | +| Diwali | +| Easter | +| Friday13th | +| LunarNewYear | +| Passover | +| PrideDay | +| Qingming | +| Samhain | +| StarWarsDay | +| TowelDay | +| UncivBirthday | +| Xmas | +| YuleGoat |
+... When these are or what they mean - look it up, if in doubt in our sources (😈).
+An audiovisual Mod (which the user must then mark as permanent) can define textures named "EasterEggs/name
Notes:
+- You can test this by launching the jar and including -DeasterEgg=name
on the command line.
+- In case of overlapping holidays, only one is chosen - and the "impact" of longer holidays is equalized by reducing the chance inversely proportional to the number of days. e.g. DiaDeLosMuertos is two days, so each Unciv launch on these days has 50% chance to show the egg.
+- Unciv's "map-based" easter eggs work independently!
+- No cultural prejudice is intended. If you know a nice custom we should include the date test for, just ask.
By the end of this tutorial, you should have a working, generally-available mod that adds a new Civilization to the game
+Use this template
button - Create a new repository
Create repository from template
(keep setting on 'public'!)Each civ has some basic information - what the civ name is, the leader's name, colors and city names.
+In addition, each civ has flavor text when declaring war, intoduction etc.
+All of these need to be filled in in jsons/Nations.json
file - see here for the base game file for more examples
Each civ has an icon, like the wreath for Rome, for instant identification.
+All of these icons are white on a transparent background, and are 100x100 pixels - see icon considerations for details
+You'll need to put your icon in the Images/NationIcons
folder - you can navigate there and click Add file - Create a new file
(top-right corner)
Congrats, your Civ is now fully playable!
+Note
+You currently won't see any images from this mod, since it has no texture atlas - see here for more details +If you're on Desktop, you can restart Unciv to generate this atlas and see the images
+But this nation's abilities are exactly those of the base mod. To make it truly unique, we'll need to change some Uniques ;)
+Units are defined in the jsons/Units.json
- for the base game file, see here file, with an icon in the UnitIcons folder.
The icons must be 200x200 pixels, white on transparent background - see icon considerations for details - and go in the Images/UnitIcons
folder
Remember that these are unique units, so search for an existing unique unit to see how they replace their regular counterparts!
+Same as the units - info is in jsons/Buildings.json
- for the base game file, see Buildings.json file and icons in the BuildingIcons folder, same rules for the icons apply (200x200 pixels, icon considerations)
Icons go in Images/BuildingIcons
Check out our list of uniques to see all the cool special effects you can add to your civilization!
+To list your mod in the Unciv Mods screen:
+Congrats, your mod will now be shown in the mods page!
+The more stars your repo has, the higher towards the top it will appear, so start gaining fans :D
+ALL icons must be legally acceptable, meaning they either come from from open sources or you act according to their licence (for Creative Commons, for instance, you have to specify the source and the creator).
+Icons directly from the base game belong to Firaxis, so I'm not sure we're legally allowed to use them - please use other sources!
+One source I use constantly is The Noun Project - everything there is Creative Commons or open, so they can all be used!
+Credits for icons should go in a Credits.md
page.
You have a working mod, now it's time to go wild!
+These pages are a work in progress. Information they contain may be incomplete.
+The JSON files that make up mods can have many different fields, and as not all are used in the base game, this wiki page will contain the full information of each. It will also give a short explanation of the syntax of JSON files.
+Resources: json.org, ISO standard
+Almost all Unciv JSON files start with a "[" and end with a "]". In between these are different objects of the type you are describing, each of which is contained between a "{" and a "}". For example, a very simple units.json may look like:
+[
+ {
+ "name": "Warrior",
+ "cost": 16
+ },
+ {
+ "name": "Spearman",
+ "cost": 24,
+ "promotions": ["Shock I", "Drill I"]
+ }
+]
+
This file contains two unit objects, one for a warrior and one for a spearman. These objects have different attributes, in this case "name", "cost" and "promotions". All these attributes have a certain type, a String (text) for "name", an Integer for "cost" and a List of Strings for "promotions".
+There are different types of attributes:
+type | +notes | +
---|---|
String | +A word or sentence. Should be between double quotes (") | +
Integer | +A number. Can be both positive or negative. Should not be between quotes | +
Boolean | +A value that can either be 'true' or 'false'. Should not be between quotes | +
List of [type] | +If multiple values could apply (such as with the promotions above), they should be put inside a list. Each element of the list should be written like a normal attribute, separated by commas, and enclosed between square braces. E.g.: ["Shock I", "Shock II"] or [1, 2, 3]. | +
Object | +The most complicated type of attribute. An object is comprised of multiple attributes, each of which again has a type. These attributes have a key (the part before the ":") and a value (the part behind it). For an example, see below. | +
Example of a Buildings.json adding a new "Cultural Library" building which gives +50% science and +50% culture:
+[
+ {
+ "name": "Cultural Library"
+ "percentStatBonus" : {"science": 50, "culture": 50}
+ }
+]
+
The keys in this example are "science" and "culture", and both have the value "50".
+In some sense you can see from these types that JSON files themselves are actually a list of objects, each describing a single building, unit or something else.
+"Uniques" are a label used by Unciv for extensible and customizable effects. Nearly every "ruleset object" allows a set of them, as a List with the name "uniques".
+Every Unique follows a general structure: Unique type defining name [placeholder] more name [another placeholder] <condition or trigger> <condition or trigger>...
+The entire string, excluding all <>
-delimited conditionals or triggers with their separating blanks, and excluding the placeholders but not their []
delimiters, are used to look up the Unique's implementation.
+The content of the optional [placeholder]
s are implementation-dependant, they are parameters modifying the effect, and described in Unique parameters.
+All <condition or trigger>
s are optional (but if they are used the spaces separating them are mandatory), and each in turn follows the Unique structure rules for the part between the <>
angled brackets, including possible placeholders, but not nested conditionals.
Example: "uniques":["[+1 Gold] <with a garrison>"]
on a building - does almost the same thing as the "gold":1
attribute does, except it only applies when the city has a garrison. In this example, []
and with a garrison
are the keys Unciv uses to look up two Uniques, an effect (of type Stats
) and a condition (of type ConditionalWhenGarrisoned
).
All Unique "types" that have an implementation in Unciv are automatically documented in uniques. Note that file is entirely machine-generated from source code structures. Also kindly note the separate sections for conditionals and trigger conditions. +Uniques that do not correspond to any of those entries (verbatim including upper/lower case!) are called "untyped", will have no direct effect, and may result in the "Ruleset Validator" showing warnings (see the Options Tab "Locate mod errors", it also runs when starting new games). +A legitimate use of "untyped" Uniques is their use as markers that can be recognized elsewhere in filters (example: "Aircraft" in the vanilla rulesets used as Unit filter). +This use is recognized by the "Ruleset Validator" and not flagged as invalid - but a filtering Unique must also use no placeholders or conditionals to pass the test. +If you get the "not found in Unciv's unique types" warning, but are sure you are using a correct filtering Unique, please look for exactly identical spelling in all places, including upper/lower case. +Note: Currently some mods use untyped Uniques not for filtering purposes, but as purely informational tool. The team will try to think of an approach for that use that won't trigger validation warnings without reducing validation quality, but as of now, those are unavoidable.
+Many parts of Unciv are moddable, and for each there is a separate json file. There is a json file for buildings, for units, for promotions units can have, for technologies, etc. The different new buildings or units you define can also have lots of different attributes, though not all are required. Below are tables documenting all the different attributes everything can have. Only the attributes which are noted to be 'required' must be provided. All others have a default value that will be used when it is omitted.
+The individual files are described on separate pages.
+ + + + + + + + + + + + + +This file contains the beliefs that can be chosen for religions in your mod.
+Each belief has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
type | +Enum | +Required | +Type of belief. Value must be Pantheon, Founder, Follower or Enhancer | +
uniques | +List of Strings | +empty | +List of unique abilities this belief adds to cities following it | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
This file contains all the buildings and wonders you want to use in your mod.
+Each building has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
cost | +Integer | +-1 | +Amount of production required to build the building. If -1, the buildingCost from requiredTech column is used |
+
<stats> |
+Float | +0 | +Per-turn yield produced by the building | +
maintenance | +Integer | +0 | +Maintenance cost of the building | +
isWonder | +Boolean | +false | +Whether this building is a global wonder | +
isNationalWonder | +Boolean | +false | +Whether this building is a national wonder | +
requiredBuilding | +String | +none | +A building that has to be built before this building can be built. Must be in Buildings.json | +
requiredTech | +String | +none | +The tech that should be researched before this building may be built. Must be in Techs.json | +
requiredResource | +String | +none | +The resource that is consumed when building this building. Must be in TileResources.json | +
requiredNearbyImprovedResources | +List of Strings | +empty | +The building can only be built if any of the resources in this list are within the borders of this city and have been improved. Each resource must be in TileResources.json | +
replaces | +String | +none | +The name of a building that should be replaced by this building. Must be in Buildings.json | +
uniqueTo | +String | +none | +If supplied, only the nation with this name can build this building. Must be in Nations.json | +
cityStrength | +Integer | +0 | +Strength bonus the city in which this building is built receives | +
cityHealth | +Integer | +0 | +Health bonus the city in which this building is built receives | +
hurryCostModifier | +Integer | +0 | +When this building is bought using gold or faith, the price is increased by this much percent | +
quote | +String | +none | +If this building is a (national) wonder, this string will be shown on the completion popup | +
uniques | +List of Strings | +empty | +List of unique abilities this building has | +
replacementTextForUniques | +String | +none | +If provided, this string will be shown instead of all of the uniques | +
percentStatBonus | +Object | +none | +Percentual bonus for stats provided by the building. Same format as specialized stats (numbers are in percent. i.e. [30] represents 30% bonus to a stat) |
+
greatPersonPoints | +Object | +none | +Great person points by this building generated per turn. Valid keys are the names of units (Great Scientist, Warrior, etc.), valid values are Integers | +
specialistSlots | +Object | +none | +Specialist slots provided by this building. Valid keys are the names of specialists (as defined in Specialists.json), valid values are Integers, the amount of slots provided for this specialist | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
This file contains all the nations and city states, including Barbarians and Spectator.
+Each nation has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
leaderName | +String | +none | +Omit only for city states! If you want LeaderPortraits, the image file names must match exactly, including case | +
style | +String | +none | +Modifier appended to pixel unit image names | +
cityStateType | +String | +none | +Distinguishes major civilizations from city states (must be in CityStateTypes.json) | +
startBias | +List of strings | +empty | +Zero or more of: terrainFilter or "Avoid [terrainFilter]". [^S] | +
preferredVictoryType | +String | +Neutral | +The victory type major civilizations will pursue (need not be specified in VictoryTypes.json) | +
personality | +String | +none | +The name of the personality specified in Personalities.json | +
favoredReligion | +String | +none | +The religion major civilization will choose if available when founding a religion. Must be in Religions.json | +
startIntroPart1 | +String | +none | +Introductory blurb shown to Player on game start... | +
startIntroPart2 | +String | +none | +... second paragraph. NO "TBD"!!! Leave empty to skip that alert. | +
declaringWar | +String | +none | +Another greeting, voice hook supported [^V] | +
attacked | +String | +none | +Another greeting, voice hook supported [^V] | +
defeated | +String | +none | +Another greeting, voice hook supported [^V] | +
introduction | +String | +none | +Another greeting, voice hook supported [^V] | +
neutralHello | +String | +none | +Another greeting, voice hook supported [^V] | +
hateHello | +String | +none | +Another greeting, voice hook supported [^V] | +
tradeRequest | +String | +none | +Another greeting, voice hook supported [^V] | +
innerColor | +List of 3× Integer | +black | +RGB color for outer ring of nation icon | +
outerColor | +List of 3× Integer | +Required | +RGB color for inner circle of nation icon | +
uniqueName | +String | +none | +Decorative name for the special characteristic of this nation | +
uniqueText | +String | +none | +Replacement text for "uniques". If empty, uniques are listed individually | +
uniques | +List | +empty | +List of unique abilities this civilisation has | +
cities | +List | +empty | +City names used sequentially for newly founded cities. Required for major civilizations and city states | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
[^S]: A "Coast" preference (unless combined with "Avoid") is translated to a complex test for +coastal land tiles, tiles next to Lakes, river tiles or near-river tiles, and such civs are +processed first. Other startBias entries are ignored in that case. +Other positive (no "Avoid") startBias are processed next. Multiple positive preferences are treated +equally, but get no "fallback". +Single positive startBias can get a "fallback" region if there is no (or no more) region with that +primary type: any leftover region with as much of the specified terrain as possible will do. +Multiple "Avoid" entries are treated equally (and reduce chance for success - if no region is left +avoiding all specified types that civ gets a random one). +When combining preferred terrain with "Avoid", the latter takes precedence, and preferred terrain +only has minor weight when choosing between regions that are not of a type to avoid. +These notes are only valid when playing on generated maps, loaded maps from map editor get no " +regions" and startBias is processed differently (but you can expect single-entry startBias to work +best). +[^V]: See Supply Leader Voices
+This file contains all Personalities for computer players.
+Each personality has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
preferredVictoryType | +String | +Neutral | +The victory type major civilizations will pursue (need not be specified in VictoryTypes.json) | +
<stats> , <behaviors> |
+Float | +5 | +Amount of focus on the stat the computer player will have. Typically ranges from 0 (no focus) to 10 (double focus) | +
priorities | +Object | +none | +Priorities for each policy branch [^B] | +
uniques | +List | +empty | +List of unique abilities this personality has | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
[^B]: Similar to policy priorites The "priorities" object defines the priority +major civilizations' AI give to a policy branch. The AI chooses the policy branch with the highest +number for their preferred victory type. If two or more candidate branches have the same priority, +the AI chooses a random branch among the candidates.
+The object maps policy branches to priority values for the major civilization using the policy +branches name and integers. Any branches not listed have a default value of 0
+The code below is an example of a valid "priorities" definition.
+"priorities": {
+ "Tradition": 30,
+ "Liberty": 20,
+ "Honor": 10
+}
+
Personality Behaviours are not implemented yet and their names may change. Using them before they +are ready might make the mod unplayable. +[//]: # (There are 6 defining behaviours that influnce an AI Civilization's behaviour. A higher +value means they will behave more like the attribute.)
+This optional file is used for defining new types of city states. These types determine the benefits +major civilizations gets when they befriend or ally the city state with influence. If the file is +ommitted, the following are automatically added: +Cultured, Maritime, Mercantile, Militaristic, Religious.
+Each city state type has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
friendBonusUniques | +List of Strings | +empty | +List of unique abilities granted to major civilizations when friends with this city state | +
allyBonusUniques | +List of Strings | +empty | +List of unique abilities granted to major civilizations when allied to city state | +
color | +List of 3× Integer | +[255, 255, 255] | +RGB color of text in civilopedia | +
This file contains all the available social policies that can be "bought" with culture.
+They are organized in 'branches', each branch has an 'opener', one or more 'member' policies, and +a 'finisher'. Therefore this file is organized using two levels - branch and member policy.
+The properties of the 'opener' are defined with the branch level, while the 'finisher' is an entry
+on the member level which must be named as branch name + " Complete"
, case sensitive. For
+example, the finisher of a policy branch "Tradition" will have the name "Tradition Complete".
Each policy branch has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
era | +String | +Required | +Unlocking era as defined in Eras.json | +
priorities | +Object | +none | +Priorities for each victory type, see here | +
uniques | +List | +empty | +List of unique abilities this policy branch grants upon adopting it | +
policies | +List | +empty | +List of member policies and branch 'finisher' - pay attention to the nesting of {} and [] | +
Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
row | +Integer | +Required | +Placement in UI, each unit approximately half the icon size | +
column | +Integer | +Required | +Placement in UI, each unit approximately half the icon size | +
requires | +List | +empty | +List of prerequisite policy names | +
uniques | +List | +empty | +List of unique abilities this policy member grants upon adopting it | +
Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
uniques | +List | +empty | +List of unique abilities this finisher grants upon adopting all the policy members in the branch | +
The "priorities" object defines the priority major civilizations' AI give to a policy branch. The AI +chooses the policy branch with the highest sum of the peferred victory type listed here and the +number flisted in the personality's priority. If two or more candidate branches have the same +priority, the AI chooses a random branch among the candidates.
+The object maps victory types to priority values for the major civilization using strings and +integers. If the preferred victory type is not specified, the default priority value is set to 0.
+The code below is an example of a valid "priorities" definition.
+"priorities": {
+"Neutral": 0,
+"Cultural": 10,
+"Diplomatic": 0,
+"Domination": 0,
+"Scientific": 10
+}
+
This file contains the quests that may be given to major civilizations by city states.
+Each quest has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +Defines criteria of quest, see below | +
description | +String | +Required | +Description of the quest shown to players. Can add extra information based on name , see below |
+
type | +Enum | +Individual | +Individual or Global | +
influence | +Float | +40 | +Influence reward gained on quest completion | +
duration | +Integer | +0 | +Maximum number of turns to complete the quest. If 0, there is no turn limit | +
minimumCivs | +Integer | +1 | +Minimum number of Civs needed to start the quest. It is meaningful only for type = Global | +
weightForCityStateType | +Object | +none | +Relative weight multiplier to this quest for each city state type or city state personality (Friendly, Neutral, Hostile, Irrational), see below | +
The name of the quest defines the criteria for the quest. If they are not defined in the predefined
+enum, they will have no behavior. In the description, square brackets []
in the description of the
+quest is replaced with extra information (except for Invest
). The list of predefined quest names
+are as follows:
Name | +Criteria | +Additional info | +
---|---|---|
Route | +Connect the city state to the major civilization's capital using roads or railways | ++ |
Clear Barbarian Camp | +Destroy the target barbarian camp | ++ |
Construct Wonder | +Construct the target wonder | +target wonder |
+
Connect Resource | +Connect the target resource to the major civilization's trade network | +target tileResource |
+
Acquire Great Person | +Acquire the target great person | +target greatPerson |
+
Conquer City State | +Defeat the target city state | +target cityState |
+
Find Player | +Meet the target major civilization | +target civName |
+
Find Natural Wonder | +Find the target natural wonder | +target naturalWonder |
+
Give Gold | +Donate gold to the city state (amount does not matter) | +civName "bully" for city state |
+
Pledge to Protect | +Pledge to protect city state | +civName "bully" for city state |
+
Contest Culture | +Be the major civilization with the highest increase to culture during the duration | +major civilization's cultureGrowth |
+
Contest Faith | +Be the major civilization with the highest increase to faith during the duration | +major civilization's faithGrowth |
+
Contest Technology | +Be the major civilization with the most technologies researched during the duration | +major civilization's techsResearched |
+
Invest | +Donating gold yield extra Influence based on value provided | +IMPORTANT: value in square brackets is the extra influence in percent. i.e. [50] means 50% | +
Bully City State | +Demand tribute from the target city state | +target city state |
+
Denounce Civilization | +Denounce the major civilization which "bullied" the city state | +civName "bully" for city state |
+
Spread Religion | +Spread major civilization's religion to the city state | +major civilization's religionName |
+
The "weightForCityStateType" object determines the quest's weight multiplier. When a city state +initiates a quest, the initial weight is 1, and it is multiplied by values based +on city state type and personality (Friendly, Neutral, Hostile, Irrational). +The AI then randomly selects a quest based on the final weighted values.
+The object maps city state type and personality to the weight multipliers for the city state using +strings to floats. If the preferred victory type is not found, the default multiplier is 1.
+The code below is an example of a valid "weightForCityStateType" definition. In this case, a +friendly militaristic city state will be 0.4 (0.2 × 2) times as likely to pick this quest than a +quest with weight 1.
+"weightForCityStateType": {
+"Hostile": 2,
+"Friendly": 0.2,
+"Militaristic": 2
+}
+
This is just a list of Strings specifying all predefined religion names. Corresponding icons must +exist, that's all to it. After all, they're just containers for beliefs.
+This file should contain a list of all possible specialists that citizens can be assigned to.
+Each specialist has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
<stats> |
+Float | +0 | +Per-turn yield produced by the specialist | +
color | +List of 3× Integer | +Required | +Color of the image for this specialist | +
greatPersonPoints | +Object | +none | +Great person points generated by this specialist per turn. Valid keys are the names of units (Great Scientist, Warrior, etc.), valid values are Integers | +
This file contains all the technologies that can be researched with science. It is organized into an +outer list of 'columns', which in turn contains one or more tech each.
+Each tech column has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
columnNumber | +Integer | +Required | +Horizontal placement in the Tech Tree | +
era | +String | +Required | +Determines era reached after researching any technologies in this column. Must be in Eras.json | +
techCost | +Integer | +0 | +Default cost of the techs in this column | +
buildingCost | +Integer | +Required | +Default cost of buildings requiring this tech | +
wonderCost | +Integer | +Required | +Default cost of wonders requiring this tech | +
techs | +List | +Required | +List of techs - pay attention to the nesting of {} and [] | +
Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
row | +Integer | +0 | +Vertical placement in the Tech Tree, must be unique per column | +
cost | +Integer | +Column techCost | +The amount of science required to research this tech | +
prerequisites | +List of Strings | +empty | +A list of the names of techs that are prerequisites of this tech. Only direct prerequisites are necessary | +
quote | +String | +none | +A nice story presented to the player when they research this tech | +
uniques | +List of Strings | +empty | +List of unique abilities this technology grants | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
This file contains the base terrains, terrain features and natural wonders that can appear on the map.
+Each terrain entry has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +[^A] | +
type | +Enum | +Required | +Land, Water, TerrainFeature, NaturalWonder [^B] | +
occursOn | +List of Strings | +none | +Only for terrain features and Natural Wonders: The baseTerrain it can be placed on | +
turnsInto | +String | +none | +Only for NaturalWonder: optional mandatory base terrain [^C] | +
weight | +Integer | +10 | +Only for NaturalWonder: relative weight of being picked by the map generator | +
<stats> |
+Float | +0 | +Per-turn yield or bonus yield for the tile | +
overrideStats | +Boolean | +false | +If true, a feature's yields replace any yield from underlying terrain instead of adding to it | +
unbuildable | +Boolean | +false | +If true, nothing can be built here - not even resource improvements | +
impassable | +Boolean | +false | +No unit can enter unless it has a special unique | +
movementCost | +Integer | +1 | +Base movement cost | +
defenceBonus | +Float | +0 | +Combat bonus for units being attacked here | +
RGB | +List of 3× Integer | +Gold | +RGB color for 'Default' tileset display | +
uniques | +List of Strings | +empty | +List of unique abilities this terrain has | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
[^A]: Some names have special meanings. Grassland
is used as fallback in some cases - e.g. Civilopedia prefers to displays a TerrainFeature on top of it, unless occursOn
is not empty and does not contain it.
+ River
is hardcoded to be used to look up a Stats unique to determine the bonuses an actual River provides (remember, rivers live on the edges not as terrain).
+ River should always be a TerrainFeature and have the same uniques the one in the vanilla rulesets has - if you change that, expect surprises.
+[^B]: A base ruleset mod is always expected to provide at least one Land and at least one Water terrain. We do not support Land-only or Water-only mods, even if they might be possible to pull off.
+[^C]: If set, the base terrain is changed to this after placing the Natural Wonder, and terrain features cleared. Otherwise, terrain features are reduced to only those present in occursOn.
This file lists the improvements that can be constructed or created on a map tile by a unit having the appropriate unique.
+Note that improvements have two visual representations - icon and pixel graphic in the tileset. Omitting the icon results in a horribly ugly user interface, while omitting tileset graphics will just miss out on an optional visualization. If you provide a pixel graphic for FantasyHex, please be aware of the layering system and the ruleVariants in the tileset json. A single graphic may suffice if it has lots of transparency, as it will be drawn on top of all other terrain elements.
+Each improvement has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +[^A] | +
terrainsCanBeBuiltOn | +List of Strings | +empty | +Terrains that this improvement can be built on [^B]. Removable terrain features will need to be removed before building an improvement [^C]. Must be in Terrains.json | +
techRequired | +String | +none | +The name of the technology required to build this improvement | +
replaces | +String | +none | +The name of a improvement that should be replaced by this improvement. Must be in TileImprovements.json | +
uniqueTo | +String | +none | +The name of the nation this improvement is unique for | +
<stats> |
+Integer | +0 | +Per-turn bonus yield for the tile | +
turnsToBuild | +Integer | +-1 | +Number of turns a worker spends building this. If -1, the improvement is unbuildable [^D]. If 0, the improvement is always built in one turn | +
uniques | +List of Strings | +empty | +List of unique abilities this improvement has | +
shortcutKey | +String | +none | +Keyboard binding. Currently, only a single character is allowed (no function keys or Ctrl combinations) | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
[^A]: Special improvements: Road, Railroad, Remove *, Cancel improvement order, City ruins, City center, Barbarian encampment - these have special meanings hardcoded to their names.
+[^B]: Improvements with an empty terrainsCanBeBuiltOn
list and positive turnsToBuild
value can only be built on resources with improvedBy
or improvement
that contains the corresponding improvement.
+[^C]: The removal of terrain features is optional if the feature is named in terrainsCanBeBuiltOn
or the unique Does not need removal of [tileFilter]
is used (e.g. Camp allowed by resource).
+[^D]: They can still be created with the UnitAction unique Can instantly construct a [improvementFilter] improvement
.
This file lists the resources that a map tile can have.
+Note the predefined resourceType
enum cannot be altered in a json.
Note also that resources have two visual representations - icon and pixel graphic in the tileset. Omitting the icon results in a horribly ugly user interface, while omitting tileset graphics will miss out on a visualization on the map. If you provide a pixel graphic for FantasyHex, please be aware of the layering system and the ruleVariants in the tileset json. A single graphic may suffice if it has lots of transparency, as it will be drawn on top of terrain and features but below an improvement - if the single improvement graphic exists at all.
+Each resource has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
resourceType | +Enum | +Bonus | +Bonus, Luxury or Strategic | +
terrainsCanBeFoundOn | +List of Strings | +empty | +Terrains that this resource can be found on. Must be in Terrains.json | +
<stats> |
+Integer | +0 | +Per-turn bonus yield for the tile | +
improvementStats | +Object | +none | +The additional yield when improved, see specialized stats | +
revealedBy | +String | +none | +The technology name required to see, work and improve this resource | +
improvedBy | +List of strings | +empty | +The improvements required for obtaining this resource. Must be in TileImprovements.json | +
improvement | +String | +none | +The improvement required to obtain this resource. Must be in TileImprovements.json (redundant due to improvedBy ) |
+
unique | +List of Strings | +empty | +List of unique abilities this resource has | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
This optional file contains the possible rewards ancient ruins give. If omitted, the default file for the game is used, even in baseRuleSet mods.
+Each of the objects in the file represents a single reward you can get from ruins. It has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +Name of the ruins. Never shown to the user, but they have to be distinct | +
notification | +String | +Required | +Notification added to the user when this reward is chosen. If omitted, an empty notification is shown. Some notifications may have parameters, refer to the table below. | +
weight | +Integer (≥0) | +1 | +Relative weight this reward is chosen next [^E] | +
uniques | +List of Strings | +empty | +List of unique abilities that will trigger when entering the ruins. If more than 1 unique is added, the notification will be shown multiple times due to a bug (may be outdated) | +
excludedDifficulties | +List of Strings | +empty | +A list of all difficulties on which this reward may not be awarded | +
[^E]: The exact algorithm for choosing a reward is the following:
+Some of the rewards ruins can give will have results that are not deterministic when writing it in the JSON, so creating a good notification for it would be impossible. An example for this would be the "Gain [50]-[100] [Gold]" unique, which will give a random amount of gold. For this reason, we allow some notifications to have parameters, in which values will be filled, such as "You found [goldAmount] gold in the ruins!". All the uniques which have this property can be found below.
+ + +Unique | +Parameters | +
---|---|
Free [] found in the ruins | +The name of the unit will be filled in the notification, including unique units of the nation | +
[] population in a random city | +The name of the city to which the population is added will be filled in the notification | +
Gain []-[] [] | +The exact amount of the stat gained will be filled in the notification | +
[] free random reasearchable Tech(s) from the [] | +The notification must have placeholders equal to the number of techs granted this way. Each of the names of these free techs will be filled in the notification | +
Gain enough Faith for a Pantheon | +The amount of faith gained is filled in the notification | +
Gain enough Faith for []% of a Great Prophet | +The amount of faith gained is filled in the notification | +
A few uniques can be added to ancient ruin effects to modify when they can be earned. These are:
+A mod can define new Tilesets or add to existing ones, namely FantasyHex. There is one json file per Tileset, named same as the Tileset, and placed in a subfolder named "TileSets" relative to the other json files. This is called TileSetConfig and has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
useColorAsBaseTerrain | +Boolean | +false | ++ |
useSummaryImages | +Boolean | +false | ++ |
unexploredTileColor | +Color | +Dark Gray | +{"r":0.25,"g":0.25,"b":0.25,"a":1} |
+
fogOfWarColor | +Color | +Black | +{"r":0,"g":0,"b":0,"a":1} |
+
fallbackTileSet | +String | +"FantasyHex" | +null to disable | +
tileScale | +Float | +1.0 | +The scale of all tiles. Can be used to increase or decrease the size of every tile | +
tileScales | +Object | +empty | +Used by the "Minimal" tileset to scale all its tiles except the base terrain down. Overrides tileScale value for specified terrain |
+
ruleVariants | +Object | +empty | +See here | +
ruleVariants control substitutions when layering images for a tile, they are list looking like:
+"ruleVariants": {
+ "Grassland+Forest": ["Grassland", "GrasslandForest"],
+ "Plains+Forest": ["Plains", "PlainsForest"],
+ "Plains+Jungle": ["Plains", "PlainsJungle"],
+ // . . .
+}
+
Each line means "if the tile content is this... then combine the following png images". The key part follows a specific order and must match in its entirety, meaning "Plains+Forest" is not valid for "Plains+Forest+Deer", and when it matches no other image layering is done except roads and units (I think - WIP).
+When TileSetConfig's for the same Tileset are combined, for the first three properties the last mod wins, while ruleVariants are merged, meaning only an entry with the same key overwrites an earlier entry. (TODO)
+Terrains, features, resources and improvements may list yield statistics. The statistics can be one of the following:
+If an object carries general stat(s), it contains any combination (or none) of the above stats, each mapping to a corresponding number [^1]. For Example:
+"gold": 2,
+"improvement": "Quarry",
+
For specialized stats, they might come as sub-object in a named field. The sub-object contains any combination (or none) of the above stats, each mapping to a corresponding number [^1]. For Example:
+"improvement": "Quarry",
+"improvementStats": { "gold": 1, "production": 1 },
+
[^1]: The values are usually integers, though the underlying code supports floating point. The effects are, however, insufficiently tested and therefore -so far- using fractional stats is unsupported. Go ahead and thoroughly test that in a mod and help out with feedback 😁.
+ + + + + + + + + + + + + +This file should contain a list of all the units, both military and civilian, that you want to use in your mod.
+Each unit has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
unitType | +String | +Required | +The type of the unit. Must be in UnitTypes.json | +
cost | +Integer | +-1 | +The amount of production required to build this unit. The production needed is always positive | +
movement | +Integer | +0 | +The amount of movement points the unit has by | +
strength | +Integer | +0 | +The melee attack and defensive strength of the unit. If this and rangedStrength are omitted or 0, the unit will be a civilian | +
rangedStrength | +Integer | +0 | +The ranged attack and defensive strength of the unit. If omitted, the unit cannot ranged attack. If used, strength must be set too. | +
religiousStrength | +Integer | +0 | +The religious attack and defensive strength of the unit | +
range | +Integer | +2 | +The range from which ranged attacks can be preformed | +
interceptRange | +Integer | +0 | +Air units attacking within in this range will be intercepted | +
requiredTech | +String | +none | +The tech required to build this unit. Must be in Techs.json | +
obsoleteTech | +String | +none | +After researching this tech, the unit can no longer be build. Must be in Techs.json | +
requiredResource | +String | +none | +Resource that is consumed by building this unit. Must be in TileResources.json | +
upgradesTo | +String | +none | +Unit that this unit can upgrade to when it is available. Must be in Units.json | +
replaces | +String | +none | +If this unit is unique to a nation, this is the unit it replaces. Must be in Units.json | +
uniqueTo | +String | +none | +The nation that this unit is unique to. Must be in Nations.json | +
hurryCostModifier | +Integer | +0 | +If this unit is bought for gold, its price is increased by so much percent | +
promotions | +List of Strings | +empty | +A list of all the promotions the unit automatically receives upon being built. Each promotion must be in UnitPromotions.json | +
uniques | +List of Strings | +empty | +List of unique abilities this unit has | +
replacementTextForUniques | +String | +none | +If provided, this will be displayed instead of the list of uniques. Can be used for better formatting. | +
attackSound | +String | +none | +The sound that is to be played when this unit attacks. For possible values, see Sounds | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
This file lists the available unit promotions.
+Each promotion must have an icon, except progressions ending in " I", " II", " III" (no IV V VI allowed) are rendered by looking up an icon without those suffixes and adding stars.
+Remember, promotions can be "bought" with XP, but also granted by the unit type, buildings, wonders and such. They are preserved when a unit upgrades, therefore special properties of nation unique units that can be inherited when they upgrade should be in a promotion, not uniques/stats in the units json (example: Slinger withdraw).
+Each promotion has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +See above for "I, II, III" progressions | +
prerequisites | +List of Strings | +empty | +Prerequisite promotions | +
column | +Integer | +Optional | +Determines placement order on the promotion picker screen. Name is historical, these coordinates no longer control placement directly. Promotions without coordinates are ensured to be placed last. (…) | +
row | +Integer | +Optional | +… In base mods without any coordinates, promotions without prerequisites are sorted alphabetically and placed top down, the rest of the screen will structure the dependencies logically. If your mod has a "Heal instantly", it is suggested to use row=0 to place it on top | +
unitTypes | +List of Strings | +empty | +The unit types for which this promotion applies as specified in UnitTypes.json | +
uniques | +List of Strings | +empty | +List of unique abilities this promotion grants to the units | +
civilopediaText | +List | +empty | +See civilopediaText chapter | +
innerColor | +List | +empty | +Color of the icon | +
outerColor | +List | +empty | +Color of the background | +
This optional file is used for defining new types of units. The names of these can be used in unitFilters, and these types determine what domain the unit moves in: over land, over water or through the air. If the file is omitted, the following are automatically added: +Civilian, Melee, Ranged, Scout, Mounted, Armor, Siege, WaterCivilian, WaterMelee, WaterRanged, WaterSubmarine, WaterAircraftCarrier, Fighter, Bomber, AtomicBomber, and Missile.
+Each unit type has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
movementType | +Enum | +Required | +The domain through which the unit moves. Allowed values: "Water", "Land", "Air" | +
uniques | +List of String | +none | +List of unique abilities this promotion grants to units of this type | +
This file defines the difficulty levels a player can choose when starting a new game.
+Each difficulty level has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
baseHappiness | +Integer | +0 | ++ |
extraHappinessPerLuxury | +Float | +0 | ++ |
researchCostModifier | +Float | +1 | ++ |
unitCostModifier | +Float | +1 | ++ |
unitSupplyBase | +Integer | +5 | ++ |
unitSupplyPerCity | +Integer | +2 | ++ |
buildingCostModifier | +Float | +1 | ++ |
policyCostModifier | +Float | +1 | ++ |
unhappinessModifier | +Float | +1 | ++ |
barbarianBonus | +Float | +0 | ++ |
barbarianSpawnDelay | +Integer | +0 | ++ |
playerBonusStartingUnits | +List of Strings | +empty | +Can also be 'Era Starting Unit', maps to startingMilitaryUnit of the Eras file. All other units must be in Units.json. Applies only to human player civs |
+
aiCityGrowthModifier | +Float | +1 | ++ |
aiUnitCostModifier | +Float | +1 | ++ |
aiBuildingCostModifier | +Float | +1 | ++ |
aiWonderCostModifier | +Float | +1 | ++ |
aiBuildingMaintenanceModifier | +Float | +1 | ++ |
aiUnitMaintenanceModifier | +Float | +1 | ++ |
aiUnitSupplyModifier | +Integer | +5 | ++ |
aiFreeTechs | +List of Strings | +empty | +Must be in Techs.json | +
aiMajorCivBonusStartingUnits | +List of Strings | +empty | +Same rules as playerBonusStartingUnits, See above. Applies only to AI major civs | +
aiCityStateBonusStartingUnits | +List of Strings | +empty | +Same rules as playerBonusStartingUnits, See above. Applies only to city-state civs | +
aiUnhappinessModifier | +Float | +1 | ++ |
turnBarbariansCanEnterPlayerTiles | +Integer | +0 | ++ |
clearBarbarianCampReward | +Integer | +25 | ++ |
This file should contain all the era's you want to use in your mod.
+Each era can have the following attributes:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | ++ |
researchAgreementCost | +Integer (≥0) | +300 | +Cost of research agreements when the most technologically advanced civ is in this era | +
iconRGB | +List of 3× Integer | +white | +RGB color that icons for technologies of this era should have in the Tech screen | +
startingSettlerCount | +Integer (≥0) | +1 | +Amount of settler units that should be spawned when starting a game in this era (setting this to zero is discouraged [^1]) | +
startingSettlerUnit | +String | +"Settler" | +Name of the unit that should be used for the previous field. Must be in Units.json, or a unit with the "Founds a new city" unique must exist | +
startingWorkerCount | +Integer (≥0) | +0 | +Amount of worker units that should be spawned when starting a game in this era | +
startingWorkerUnit | +String | +"Worker" | +Name of the unit that should be used for the previous field. If startingWorkerCount>0, then it must exist in Units.json, or a unit with the "Can build [filter] improvements on tiles" unique must exist | +
startingMilitaryUnitCount | +Integer (≥0) | +1 | +Amount of military units that should be spawned when starting a game in this era | +
startingMilitaryUnit | +String | +"Warrior" | +Name of the unit that should be used for the previous field. Must be in Units.json | +
startingGold | +Integer (≥0) | +0 | +Amount of gold each civ should receive when starting a game in this era | +
startingCulture | +Integer (≥0) | +0 | +Amount of culture each civ should receive when starting a game in this era | +
settlerPopulation | +Integer (>0) | +1 | +Amount of population each city should have when settled when starting a game in this era | +
settlerBuildings | +List of Strings | +empty | +Buildings that should automatically be built whenever a city is settled when starting a game in this era | +
startingObsoleteWonders | +List of Strings | +empty | +Wonders (and technically buildings) that should be impossible to built when starting a game in this era. Used in the base game to remove all wonders older than 2 era's | +
baseUnitBuyCost | +Integer | +200 | +Default value used for the unique Can be purchased with [stat] [cityFilter] |
+
embarkDefense | +Integer | +3 | +Default defense for embarked unit in this era | +
startPercent | +Integer | +0 | +When starting, percentage ([0]%-[100]%) of turns skipped in total turns specified in Speed.json | +
citySound | +String | +"cityClassical" | +Sound used when city is founded in this era | +
[^1]: Successfully setting startingSettlerCount to zero in a mod (idea: conquer or die) is not easy. Some player-controlled settings require at least one Settler, through any source (see difficulties for other possible settler sources), or you won't be able to start a game: Once City Challenge requires one for all players, and allowing any city-states requires one for those. Would also affect defeat rules.
+This file should contain all the speeds you want to use in your mod.
+Each speed can have the following attributes:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +Name of the speed | +
modifier | +Float (≥0) | +1.0 | +Overall game speed modifier | +
productionCostModifier | +Float (≥0) | +modifier value |
+Scales production cost of units and buildings | +
goldCostModifier | +Float (≥0) | +modifier value |
+Scales gold costs | +
scienceCostModifier | +Float (≥0) | +modifier value |
+Scales science costs | +
cultureCostModifier | +Float (≥0) | +modifier value |
+Scales culture costs | +
faithCostModifier | +Float (≥0) | +modifier value |
+Scales faith costs | +
improvementBuildLengthModifier | +Float (≥0) | +modifier value |
+Scales the time it takes for a worker to build tile improvements | +
barbarianModifier | +Float (≥0) | +modifier value |
+Scales the time between barbarian spawns | +
goldGiftModifier | +Float (≥0) | +modifier value |
+Scales the influence gained from gifting gold to city-states | +
cityStateTributeScalingInterval | +Float (≥0) | +6.5 | +The number of turns it takes for the amount of gold a player demands from city-states to increase by 5 gold | +
goldenAgeLengthModifier | +Float (≥0) | +modifier value |
+Scales the length of golden ages | +
religiousPressureAdjacentCity | +Integer (≥0) | +6 | +Defines how much religious pressure a city exerts on nearby cities | +
peaceDealDuration | +Integer (≥0) | +10 | +The number of turns a peace deal lasts | +
dealDuration | +Integer (≥0) | +30 | +The number of turns a non-peace deal (research agreement, open borders, etc.) lasts | +
startYear | +Float | +-4000 | +The start year of the game (negative is BC/BCE) | +
turns | +List | +Required | +List of time interval per turn, see below | +
The "turns" attribute defines the number of years passed between turns. The attribute consists of a list of hashmaps, each hashmaps in turn having 2 required attributes: "yearsPerTurn" (Float) and "untilTurn" (Integer)
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
yearsPerTurn | +Integer | +Required | +Number of years passed between turns | +
untilTurn | +Integer | +Required | +Which turn that this "speed" is active until (if it is the last object, this is ignored) | +
The code below is an example of a valid "turns" definition and it specifies that the first 50 turns of a game last for 60 years each, then the next 30 turns (and any played after the 80th) last for 40 years each.
+"turns": [
+ {"yearsPerTurn": 60, "untilTurn": 50},
+ {"yearsPerTurn": 40, "untilTurn": 80}
+]
+
Events allow users to choose between options of triggers to activate.
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +Used for triggering via "Triggers a [event] event" unique | +
text | +String | +None | +Flavor text displayed to user | +
presentation | +One of: "None", "Alert", "Floating" | +Alert | +"Alert" indicates a regular popup, "None" means the choice is made randomly, "Floating" is for tutorial-style indicators | +
civilopediaText | +List | +Optional | +See civilopediaText chapter | +
choices | +List of EventChoices | ++ | User can choose to trigger one of the viable choices | +
You can use text and/or civilopediaText, if both are present both are shown (but why would you?)
+Event choices are comprised of:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
text | +String | +Required | +Displayed to user as button. Should be an action name - "Do X" | +
triggeredUniques | +List of trigger uniques | +Required | +The triggers that this choice activates upon being chosen | +
conditions | +List of conditional uniques | +Empty list | +If any conditional is not met, this option becomes unpickable (not shown) | +
keyShortcut | +key to select (name) | +none | +Key names see Gdx.Input.Keys | +
civilopediaText | +List | +Optional | +See civilopediaText chapter | +
Here, civilopediaText is shown outside the active Button, before the triggeredUniques.
+This file is a little different:
+Note that this file controls declarative mod compatibility (Work in progress) - e.g. there's uniques to say your Mod should only or never be used as 'Permanent audiovisual mod'. +Incompatibility filtering works so far between extension and base mods, but feel free to document known extension-to-extension incompatibilities using the same Unique now. Stay tuned!
+The file can have the following attributes, not including the values Unciv sets automatically:
+Attribute | +Type | +default | +Notes | +
---|---|---|---|
isBaseRuleset | +Boolean | +false | +Replaces vanilla ruleset if true | +
uniques | +List | +empty | +Mod-wide specials, see here | +
techsToRemove | +List | +empty | +List of Technologies or technologyFilter to remove (isBaseRuleset=false only) | +
buildingsToRemove | +List | +empty | +List of Buildings or Wonders or buildingFilter to remove (isBaseRuleset=false only) | +
unitsToRemove | +List | +empty | +List of Units or unitFilter to remove (isBaseRuleset=false only) | +
nationsToRemove | +List | +empty | +List of Nations or nationFilter to remove (isBaseRuleset=false only) | +
constants | +Object | +empty | +See ModConstants | +
tileset | +String | +empty | +Only applicable for base rulesets | +
unitset | +String | +empty | +Only applicable for base rulesets | +
The values normally set automatically from github metadata are:
+Attribute | +Type | ++ | Notes | +
---|---|---|---|
modUrl | +String | ++ | The github page the mod was downloaded from, or empty if a freely hosted zip was used | +
defaultBranch | +String | +master | +The repo's default branch | +
author | +String | ++ | Repo owner | +
lastUpdated | +String | ++ | ISO date | +
modSize | +Integer | +0 | +Size in kB | +
topics | +List | +empty | +A list of "unciv-mod-*" github topics | +
To clarify: When your Mod is distributed via github, including these in the Mod repo has no effect.
+However, when a Mod is distributed without a github repository, these values can and should be set by the author in the distributed ModOptions.json
.
Stored in ModOptions.constants, this is a collection of constants used internally in Unciv. +This is the only structure that is merged field by field from mods, not overwritten, so you can change XP from Barbarians in one mod +and city distance in another. In case of conflicts, there is no guarantee which mod wins, only that default values are ignored.
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
maxXPfromBarbarians | +Int | +30 | +[^A] | +
cityStrengthBase | +Float | +8.0 | +[^B] | +
cityStrengthPerPop | +Float | +0.4 | +[^B] | +
cityStrengthFromTechsMultiplier | +Float | +5.5 | +[^B] | +
cityStrengthFromTechsExponent | +Float | +2.8 | +[^B] | +
cityStrengthFromTechsFullMultiplier | +Float | +1.0 | +[^B] | +
cityStrengthFromGarrison | +Float | +0.2 | +[^B] | +
baseCityBombardRange | +Int | +2 | +[^S] | +
cityWorkRange | +Int | +3 | +[^T] | +
cityExpandRange | +Int | +5 | +[^U] | +
unitSupplyPerPopulation | +Float | +0.5 | +[^C] | +
minimalCityDistance | +Int | +3 | +[^D] | +
minimalCityDistanceOnDifferentContinents | +Int | +2 | +[^D] | +
unitUpgradeCost | +Object | +See below | +[^J] | +
naturalWonderCountMultiplier | +Float | +0.124 | +[^E] | +
naturalWonderCountAddedConstant | +Float | +0.1 | +[^E] | +
ancientRuinCountMultiplier | +Float | +0.02 | +[^F] | +
spawnIceBelowTemperature | +Float | +-0.8 | +[^G] | +
maxLakeSize | +Int | +10 | +[^H] | +
riverCountMultiplier | +Float | +0.01 | +[^I] | +
minRiverLength | +Int | +5 | +[^I] | +
maxRiverLength | +Int | +666 | +[^I] | +
religionLimitBase | +Int | +1 | +[^K] | +
religionLimitMultiplier | +Float | +0.5 | +[^K] | +
pantheonBase | +Int | +10 | +[^L] | +
pantheonGrowth | +Int | +5 | +[^L] | +
workboatAutomationSearchMaxTiles | +Int | +20 | +[^M] | +
maxSpyRank | +Int | +3 | +[^N] | +
spyRankSkillPercentBonus | +Float | +30 | +[^O] | +
minimumWarDuration | +Int | +10 | +[^P] | +
baseTurnsUntilRevolt | +Int | +4 | +[^Q] | +
cityStateElectionTurns | +Int | +15 | +[^R] | +
maxImprovementTechErasForward | +Int | +None | +[^S] | +
goldGiftMultiplier | +Float | +1 | +[^T] | +
goldGiftTradeMultiplier | +Float | +0.8 | +[^U] | +
goldGiftDegradationMultiplier | +Float | +1.0 | +[^V] | +
Legend:
+These values are not merged individually, only the entire sub-structure is.
+Attribute | +Type | ++ | Notes | +
---|---|---|---|
base | +Float | +10 | ++ |
perProduction | +Float | +2 | ++ |
eraMultiplier | +Float | +0 | ++ |
exponent | +Float | +1 | ++ |
roundTo | +Int | +5 | ++ |
The formula for the gold cost of a unit upgrade is (rounded down to a multiple of roundTo
):
+ (
+ max((base
+ perProduction
* (new_unit_cost - old_unit_cost)), 0)
+ * (1 + eraNumber * eraMultiplier
) * civModifier
+ ) ^ exponent
+With civModifier
being the multiplicative aggregate of "[relativeAmount]% Gold cost of upgrading" uniques that apply.
GlobalUniques defines uniques that apply globally. e.g. Vanilla rulesets define the effects of Unhappiness here.
+Only the uniques
field is used, but a name must still be set (the Ruleset validator might display it).
+When extension rulesets define GlobalUniques, all uniques are merged. At the moment there is no way to change/remove uniques set by a base mod.
Note a Base Ruleset mod can define a "welcome page" here by adding a "Tutorial" with a name equal to the name of the mod!
+As an exception to the general rule, this file in a Base Ruleset mod will not replace the default, but add to it like extension mods do.
+Also, place it under <mod>/jsons/
normally even if the original is found one level above the vanilla jsons.
Each tutorial has the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +Entry name | +
civilopediaText | +List | +Optional | +See civilopediaText chapter | +
steps | +List of Strings | +Optional | +Plain text | +
If an entry contains both steps
and civilopediaText
attributes, the civilopediaText
is shown first.
+Tutorials shown as Popup can show an show an external image (not part of the texture atlases) if there is an image unter ExtraImages (directly under assets or the Mod folder) having the same name.
+This is searched for, meaning the mod defining the Tutorial is irrelevant, mods can override builtin ExtraImages, and case sensitivity depends on the OS.
These files contain which victories this mod provides, and what milestones must be reached for someone to win a victory. +Most of the file contains of strings that are shown to the user in the victory screen, with the rest being the requirements for winning.
+Each victory have the following structure:
+Attribute | +Type | +Default | +Notes | +
---|---|---|---|
name | +String | +Required | +Name of the victory | +
victoryScreenHeader | +String | +none | +Shown in the footer of the victory in the our status in the victory screen |
+
victoryString | +String | +none | +Shown in the footer of the victory screen when you won the game with this victory | +
defeatString | +String | +none | +Shown in the footer of the victory screen when someone else won the game with this victory | +
hiddenInVictoryScreen | +Boolean | +false | +Whether progress of this victory is hidden in the victory screen | +
requiredSpaceshipParts | +List of Strings | +empty | +What spaceship parts must be added to the capital for the corresponding milestone | +
Milestones | +List of Strings | +Required | +List of milestones that must be accomplished to win, see below | +
Currently the following milestones are supported:
+Milestone | +Requirement | +
---|---|
Build [building] | +Build the building [building] in any city | +
Anyone should build [building] | +Anyone must build the building [building] for all players to have this milestone | +
Add all [comment] in capital | +Add all units in the requiredSpaceshipParts field of this victory to the capital |
+
Destroy all players | +You must be the only major civilization with any cities left | +
Capture all capitals | +Capture all the original capitals of major civilizations in the game | +
Complete [amount] Policy branches | +Fully complete at least [amount] policy branches | +
Win diplomatic vote | +At any point in the game win a diplomatic vote (UN). You may lose afterwards and still retain this milestone | +
Become the world religion | +Have your religion be the majority religion in a majority of cities of all major civs | +
Have highest score after max turns | +Basically time victory. Enables the 'max turn' slider and calculates score when that amount is reached | +
Any 'thing' defined in json and listed in the Civilopedia can supply extra text, specifically for the Civilopedia. This can be used to explain special considerations better when the automatically generated display is insufficient, or for 'flavour', background stories and the like. Such text can be formatted and linked to other Civilopedia entries, within limits.
+An example of the format is:
+"civilopediaText": [
+ { "text": "Ancient ruins provide a one-time random bonus when explored" },
+ { "separator": true },
+ {
+ "text": "This line is red and links to the Scout including icons",
+ "link": "Unit/Scout",
+ "color": "red"
+ },
+ {
+ "text": "A big fat header sporting a golden star",
+ "header": 1,
+ "starred": true,
+ "color": "#ffeb7f"
+ },
+],
+
List of attributes - note not all combinations are valid:
+Attribute | +Type | +Description | +
---|---|---|
text |
+String | +Text to display | +
link |
+String | +Create link and icon, format: Category/Name or external link ('http://','https://','mailto:') | +
icon |
+String | +Show icon without linking, format: Category/Name | +
extraImage |
+String | +Display an Image instead of text. Can be a path found in a texture atlas or or the name of a png or jpg in the ExtraImages folder | +
imageSize |
+Float | +Size in world units of the [extraImage], the smaller coordinate is calculated preserving aspect ratio. available width | +
header |
+Integer | +Header level. 1 means double text size and decreases from there | +
size |
+Integer | +Text size, is 18. Use size or header but not both |
+
indent |
+Integer | +Indent level. 0 means text will follow icons, 1 aligns to the right of all icons, each further step is 30 units | +
padding |
+Float | +Vertical padding between rows, 5 units | +
color |
+String | +Sets text color, accepts names or 6/3-digit web colors (e.g. #FFA040) | +
separator |
+Boolean | +Renders a separator line instead of text. Can be combined only with color and size (line width, default 2) |
+
starred |
+Boolean | +Decorates text with a star icon - if set, it receives the color instead of the text |
+
centered |
+Boolean | +Centers the line (and turns off automatic wrap). For an extraImage , turns on crop-to-content to equalize transparent borders |
+
The lines from json will 'surround' the automatically generated lines such that the latter are inserted just above the first json line carrying a link, if any. If no json lines have links, they will be inserted between the automatic title and the automatic info. This method may, however, change in the future.
+Note: text
now also supports inline color markup. Insert «color»
to start coloring text, «»
to stop. color
can be a name or 6/8-digit hex notation like #ffa040
(different from the color
attribute notation only by not allowing 3-digit codes, but allowing the alpha channel).
+Effectively, the «»
markers are replaced with []
after translation and then passed to gdx markup language.
Note: Using an ExtraImages folder in a mod was not working until version 4.11.5
+Certain objects can be specified to have its own unique color. The colors are defined by a list of 3× Integer in this order: red, green, blue. The range of color is from [0, 0, 0] (black) to [255, 255, 255] (white).
+Note: The default of some objects are gdx color classes. The values of the constants are as follows:
+name | +value | +
---|---|
gold | +[225, 215, 0] | +
white | +[255, 255, 255] | +
black | +[0, 0, 0] | +
Everyone has that thing they wish could be in the game. +Unfortunately, the game only understands code, so mods are our way to give a degree of freedom to those of us who don't code.
+Mods can add, replace and remove basic game definitions, such as units, nations, buildings, improvements, resources and terrains. +Games loaded with these mods will function according to the mod definition.
+The game only knows how to recognize existing definitions, so you can't add new unique abilities to nations/units/buildings/etc, only play around with existing ones
+There are three main kinds of mods:
+Creating and editing mods from your phone is NOT RECOMMENDED - it's much easier using a desktop device!
+Mods need to conform to github repo naming rules, but best stay simple and use only letters, digits, and dashes -
.
+Dashes are automatically converted to spaces for display and use within Unciv.
Many punctuation or extended unicode characters might work, but at best potential users won't find them attractive, at worst we'll refuse support when you run into problems :smiling_imp:
+Mods are located in a /mods
directory, on Desktop that should be next to your .jar file.
Mods typically have 2 subfolders:
+jsons
- here you should put files that alter the data of game objects, the order of the files is as in the base json files. More information on these can be found hereImages
- here you should put game images, as in the base image files.In order to remove objects from the game, you'll need to create a ModOptions file in the /jsons
subfolder - there's an example here.
Base Ruleset Mods are mods that 'start from scratch' - ALL the original objects are removed, and only the objects of the mod in question are used.
+This is done by adding a "isBaseRuleset":true
configuration to your modOptions file, like so.
In addition to changing the rules - or even without doing so - mods can override existing graphics or sounds, or add music tracks. For details, see Audiovisual Mods.
+Custom tilesets and unitsets are a subgroup of these - see Creating a custom tileset - as are UI skin mods, see Creating a UI skin.
+Such mods are candidates for the "Permanent audiovisual mod" switch available on the Mod Management Screen, see Permanent audiovisual mods.
+Images need to be 'packed' before the game can use them, which the desktop version can do for you. Please make sure to read the Texture atlas chapter!
+You can also add maps to mods, so they'll be available to players who download your mod.
+A mod can also be maps-only, if all you want to do is share your maps.
+When you've finished making your map in the Map Editor, save it, and it will be in the /maps
folder of your game.
Copy it to a /maps
folder in your mod, and you're done!
In order to make your mod downloadable by anyone, you need to create a Github repository (instructions here)
+The Images and jsons folders need to be in the root directory of the repo - see here for example.
+You can then manually download the mod from within the Mod Manager in Unciv:
+Once you've tested that your mod CAN be downloaded, and that it works well once downloaded, you're ready for the final stage - GETTING IT TO THE USERS AUTOMATICALLY.
+In order to do this, all you need to do is:
+Optionally add one or more of the following topics to mark your mod as belonging to specific categories:
+unciv-mod-rulesets
(for base ruleset mods)unciv-mod-expansions
(for mods extending vanilla rulesets - please use this, not unciv-mod-expansion)unciv-mod-graphics
(for mods altering graphics - icons, portraits, tilesets)unciv-mod-audio
(for mods supplying music or modifying sounds)unciv-mod-maps
(for mods containing maps)unciv-mod-fun
(for mods mainly tweaking mechanics or other gameplay aspects)unciv-mod-modsofmods
(for mods extending another mod's ruleset)When you open Unciv's Mod Manager, it will query Github's list of repos with that topic, and now YOUR repo will appear there! +The categories will appear als annotations on the mod buttons, and the user can filter for them. They are not required for the game to use the content - e.g. you can still load maps from mods lacking the unciv-mod-maps topic. +If you want new categories, github will accept any topic, but you'll have to ask the Unciv team to enable them in the game.
+If you feel there should be additional topics supported in-game, then the course of action is as follows:
+The primary use of mods is to add them when starting a new game, or configuring a map. This will mean that both the ruleset of the mod, and the images, will be in use for that specific game/map.
+For mods which are primarily visual or audio, there is a second use - through the mod manager, you can enable them as permanent audiovisual mods. This means that the images and/or sounds from the mod will replace the original media everywhere in the game, and contained music will be available - see here.
+In general, you should never be manually-loading your mods - not only is this clunky, it's also more error-prone. Unless you have a very specific use-case, you probably shouldn't be doing this
+When loading a mod, it needs to be in its own folder in /mods
- this is how you will work when you're editing your mod.
In Android, you can copy them into the Android/data/com.unciv.app/files/mods
directory.
When the app starts, they will be auto-copied into the /data/data/com.unciv.app/files/mods
, directory, that is inaccessible to users.
In Chromebook, go to "Play files", should be on the sidebar on the left side of the window under "My files". Click the 3 vertical dots on the top right-hand corner of the window below the "X".
+If the option "Show all Play folders" does not have a check next to it click it. You should see some new files that appear on your screen. Now navigate to Android/data/com.unciv.app/files/mods
You can add an image that will be displayed to users in the mod management screen by adding a "preview.jpg" or "preview.png" file.
+Existing mods can be found here!
+Now you should try to create your first mod!
+We recommend you start off by adding a new civilization as a mod, to get a hang of the process :)
+ + + + + + + + + + + + + +Scenarios are specific game states, set up so a player has a specific experience.
+These can range from just having cities and units in specific places, to having full-blown custom rulesets to support them.
+When creating a mod, we differentiate the ruleset from the scenario - the scenario is just a specific game state, or in other words - a saved game.
+To create a scenario:
+To open the console from the world screen, click the `` button on your keyboard.
+On mobile:
+To see available commands, click enter. This works for subcommands as well (e.g. when you entered tile
).
Object names (units buildings civs etc) are case-insensitive.
+Unit and building names with spaces in them, like "Great General", can be inputted in 2 ways:
+The console has autocompletion:
+Some commands operate on a tile or unit you need to select on the map before opening the console.
+The console does intentionally not follow all rules defined by the ruleset - e.g. it allows Farms on hills without fresh water or the Mobility promotion on a Worker. Any unexpected consequences are your responsibility.
+ + + + + + + + + + + + + +Mistakes happen. Misnamed fields, things we forgot to add, or even stuff we didn't know existed.
+Computers can handle a lot of that themselves, so we can let them do the work to ensure that our json files are correct, by using json schemas.
+This also allows autocompletion when writing jsons!
+As of now, only Buildings and Units have proper schema
+https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/buildings.json
*/Buildings.json
Tada! Now Android Studio will recognize all Buildings.json files as belonging to that schema, and will warn you of inconsistencies!
+ "json.schemas": [
+ {
+ "fileMatch": [
+ "*/Buildings.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/buildings.json"
+ },
+ {
+ "fileMatch": [
+ "*/Units.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/units.json"
+ },
+ {
+ "fileMatch": [
+ "*/Nations.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/nations.json"
+ },
+ {
+ "fileMatch": [
+ "*/TileImprovements.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/tileImprovements.json"
+ },
+ {
+ "fileMatch": [
+ "*/Techs.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/techs.json"
+ },
+ {
+ "fileMatch": [
+ "*/UnitTypes.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/unitTypes.json"
+ },
+ {
+ "fileMatch": [
+ "*/UnitPromotions.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/unitPromotions.json"
+ },
+ {
+ "fileMatch": [
+ "*/TileResources.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/tileResources.json"
+ },
+ {
+ "fileMatch": [
+ "*/Events.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/events.json"
+ },
+ {
+ "fileMatch": [
+ "*/Terrains.json"
+ ],
+ "url": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/terrains.json"
+ }
+ ]
+
If you don't use any of these tools, you can check your file online using this tool
+However, it can't handle the missing commas that vscode and Android Studio handle, so you may need to get your json up to spec to use it.
+The schema you want to validate against is: +
{
+ "$ref": "https://raw.githubusercontent.com/yairm210/Unciv/master/docs/Modders/schemas/buildings.json"
+}
+
This page contains an overview of all different parameters used in uniques and what values can be filled into them. +Each of the different parameter types is described below, including all possible values for them. +These are split into two categories:
+Text that looks like this
. This last one must be used exactly the sameNote that all of these are case-sensitive!
+All filters except for populationFilter
and resourceFilter
accept multiple values in the format: {A} {B} {C}
etc, meaning "the object must match ALL of these filters"
++Example:
+[{Military} {Water}] units
,[{Wounded} {Armor}] units
, etc.
No space or other text is allowed between the [
and the first {
, nor between the last }
and the ending ]
. The space in } {
, however, is mandatory.
All filters accept non-[filter]
as a possible value
++Example:
+[non-[Wounded]] units
These can be combined by nesting, with the exception that an "ALL" filter cannot contain another "ALL" filter, even with a NON-filter in between.
+++Example:
+[{non-[Wounded]} {Armor}] units
means unit is type Armor and at full health. +Example:[non-[{Wounded} {Armor}]] units
means unit is neither wounded nor an Armor one.
[{non-[{Wounded} {Armor}]} {Embarked}] units
WILL FAIL because the game will treat both "} {" at the same time and see non-[{Wounded
and Armor}]
, both invalid.
Display of complex filters in Civilopedia may become unreadable. If so, consider hiding that unique and provide a better wording using the Comment []
unique separately.
Allows filtering for specific civs.
+Allowed values:
+Human player
AI player
Allows filtering for specific nations. Used by ModOptions.nationsToRemove.
+Allowed values:
+All
City-States
, City-State
Major
Unit filters can be divided up into two parts: baseUnitFilter
s and mapUnitFilter
s.
+The former is tested against the unit as it appears in the json.
+This means it doesn't have an owning civ or tile it stands on, just its base properties.
+The latter is tested against the unit as it appears on the map, including its nation, tile, health and all other properties.
Allowed values:
+Land
, Water
, Air
land units
, water units
, air units
non-air
for non-air non-missile unitsMilitary
, military units
Civilian
, civilian units
All
Melee
Ranged
Nuclear Weapon
Great Person
, Great
Embarked
Modern Era
{filter1} {filter2}
and can match any number of filters. For example: [{Modern era} {Land}]
unitsThis indicates a unit as placed on the map. Compare with baseUnitFilter
.
Allowed values:
+Wounded
Embarked
City-State
Barbarians
, Barbarian
[{Wounded} {Water}]
units.You can check this in-game using the console with the unit checkfilter <filter>
command
Allows to only activate a unique for certain buildings.
+Allowed values:
+All
Buildings
, Building
Wonder
, Wonders
National Wonder
, National
World Wonder
, World
-- All wonders that are not national wondersspaceship part
)Culture
, Gold
, etc. if the building is stat-related
for that stat. Stat-related buildings are defined as one of the following:{filter1} {filter2}
up to any number of filters. For example [{Ancient era} {Food}]
buildings.cityFilters allow us to choose the range of cities affected by this unique:
+in this city
in all cities
in your cities
, Your
in all coastal cities
, Coastal
in capital
, Capital
in all non-occupied cities
, Non-occupied
- all cities that are not puppets and don't have extra unhappiness from being recently conqueredin all cities with a world wonder
in all cities connected to capital
in all cities with a garrison
, Garrisoned
in non-enemy foreign cities
- In all cities owned by civs other than you that you are not at war within enemy cities
, Enemy
in foreign cities
, Foreign
in annexed cities
, Annexed
in puppeted cities
, Puppeted
in cities being razed
, Razing
in holy cities
, Holy
in City-State cities
in cities following this religion
- Should only be used in pantheon/follower uniques for religionsin cities following our religion
in all cities in which the majority religion is a major religion
in all cities in which the majority religion is an enhanced religion
You can check this in-game using the console with the city checkfilter <filter>
command
For filtering a specific improvement.
+Allowed values:
+All
Great Improvements
, Great
All Road
- for Roads & RailroadsA filter determining a part of the population of a city.
+Allowed values:
+Population
Specialists
Unemployed
Followers of the Majority Religion
or Followers of this Religion
, both of which only apply when this religion is the majority religion in that cityAllowed values:
+All
or all
[policyBranchName] branch
Allowed values:
+City
, All
, or civFilter, for city combatantsSince mapUnitFilter contains civFilter, that means civFilter can be applied to combatantFilter for both units and cities.
+Used for dividing the world into regions in each of which a single player is placed at the start of the game.
+Allowed values are Hybrid
and the name of any terrain that has one of the following two uniques:
A Region is formed with at least [amount]% [simpleTerrain] tiles, with priority [amount]
A Region is formed with at least [amount]% [simpleTerrain] tiles and [simpleTerrain] tiles, with priority [amount]
Used by NaturalWonderGenerator to place natural wonders
+Allowed values:
+Land
Water
Elevated
This indicates a text comprised of specific stats and is slightly more complex.
+Each stats is comprised of several stat changes, each in the form of +{amount} {stat}
,
+where 'stat' is one of the seven major stats
+(eg Production
, Food
, Gold
, Science
, Culture
, Happiness
and Faith
).
+For example: +1 Science
.
These can be strung together with ", " between them, for example: +2 Production, +3 Food
.
At the moment, only used for the "Improves [resourceFilter] resource in this tile"
Unique on Improvements.
+Allows filtering resources by their name, their type, or by any Stat listed in their improvementStats
property. The all
keyword works too.
This indicates a text that corresponds to a custom Stockpile Resource.
+These are global civilization resources that act similar to the main Civ-wide resources like Gold
and Faith
.
+You can generate them and consume them. And actions that would consume them are blocked if you
+don't have enough left in stock.
To use, you need to first define a TileResources with the "Stockpiled" Unique. Then you can reference +them in other Uniques.
+At the moment only implemented for ModOptions.techsToRemove.
+Allowed values:
+All
This indicates the terrain on a single tile.
+Allowed values:
+All
Terrain
Water
, Land
Coastal
(at least one direct neighbor is a coast)River
(as in all 'river on tile' contexts, it means 'adjacent to a river on at least one side')Open terrain
, Rough terrain
(note all terrain not having the rough unique is counted as open)Friendly Land
- land belonging to you, or other civs with open borders to youForeign Land
- any land that isn't friendly landEnemy Land
- any land belonging to a civ you are at war withyour
- land belonging to youWater resource
, Strategic resource
, Luxury resource
, Bonus resource
, resource
Natural Wonder
(as opposed to above which means testing for a specific Natural Wonder by name, this tests for any of them)Please note all of these are case-sensitive.
+Note: Resource filters depend on whether a viewing civ is known in the context where the filter runs. Water and specific tests require a viewing civ, and if the resource needs a tech to be visible, that tech to be researched by the viewing civ. The other resource category tests can succeed without a known viewing civ only for resources not requiring any tech. So - test your mod!
+So for instance, the unique "[stats] from [tileFilter] tiles [cityFilter]" can match several cases:
+Allowed values:
+Improvement
or improved
for tiles with any improvementsunimproved
for tiles with no improvementYou can check this in-game using the console with the tile checkfilter <filter>
command
Used to indicate for what use the terrain should be viewed when dividing the world into regions, in each of which a single player is placed at the start of the game.
+Allowed values:
+Undesirable
Food
Desirable
Production
Indicates something that can be counted, used both for comparisons and for multiplying uniques
+Allowed values:
+year
, turns
Cities
, [cityFilter] Cities
City-States
- counts all undefeated city-statesUnits
, [mapUnitFilter] Units
[buildingFilter] Buildings
Remaining [civFilter] Civilizations
Owned [tileFilter] Tiles
For example: If a unique is placed on a building, then the retrieved resources will be of the city. If placed on a policy, they will be of the civilization.
+This can make a difference for e.g. local resources, which are counted per city.
+ + + + + + + + + + + + + +An overview of uniques can be found here
+Simple unique parameters are explained by mouseover. Complex parameters are explained in Unique parameter types
+Uniques that have immediate, one-time effects. These can be added to techs to trigger when researched, to policies to trigger when adopted, to eras to trigger when reached, to buildings to trigger when built. Alternatively, you can add a TriggerCondition to them to make them into Global uniques that activate upon a specific event.They can also be added to units to grant them the ability to trigger this effect as an action, which can be modified with UnitActionModifier and UnitTriggerCondition conditionals.
+Free buildings CANNOT be self-removing - this leads to an endless loop of trying to add the building +Example: "Gain a free [Library] [in all cities]"
+Applicable to: Triggerable, Global
+Example: "Remove [Culture] [in all cities]"
+Applicable to: Triggerable, Global
+Example: "Sell [Culture] buildings [in all cities]"
+Applicable to: Triggerable, Global
+Example: "Free [Musketman] appears"
+Applicable to: Triggerable
+Example: "[3] free [Musketman] units appear"
+Applicable to: Triggerable
+Applicable to: Triggerable
+Example: "[3] Free Social Policies"
+Applicable to: Triggerable
+Applicable to: Triggerable
+Example: "Empire enters a [3]-turn Golden Age"
+Applicable to: Triggerable
+Applicable to: Triggerable
+Example: "[3] population [in all cities]"
+Applicable to: Triggerable
+Example: "[3] population in a random city"
+Applicable to: Triggerable
+Example: "Discover [Agriculture]"
+Applicable to: Triggerable
+Example: "Adopt [Oligarchy]"
+Applicable to: Triggerable
+Example: "Remove [Oligarchy]"
+Applicable to: Triggerable
+Example: "Remove [Oligarchy] and refund [3]% of its cost"
+Applicable to: Triggerable
+Applicable to: Triggerable
+Example: "[3] Free Technologies"
+Applicable to: Triggerable
+Example: "[3] free random researchable Tech(s) from the [Ancient era]"
+Applicable to: Triggerable
+Applicable to: Triggerable
+Example: "Gain a free [Follower] belief"
+Applicable to: Triggerable
+Applicable to: Triggerable
+Example: "Instantly consumes [3] [Mana]"
+Applicable to: Triggerable
+Example: "Instantly provides [3] [Mana]"
+Applicable to: Triggerable
+Example: "Gain [3] [Culture]"
+This unique's effect can be modified with <(modified by game speed)> +Applicable to: Triggerable
+Example: "Gain [3]-[3] [Culture]"
+Applicable to: Triggerable
+Applicable to: Triggerable
+Example: "Gain enough Faith for [3]% of a Great Prophet"
+Applicable to: Triggerable
+Example: "Gain control over [Farm] tiles in a [3]-tile radius"
+Applicable to: Triggerable
+Example: "Reveal up to [3] [Farm] within a [3] tile radius"
+Applicable to: Triggerable
+Example: "Triggers the following global alert: [comment]"
+Applicable to: Triggerable
+Example: "Promotes all spies [3] time(s)"
+Applicable to: Triggerable
+Applicable to: Triggerable
+Example: "Turn this tile into a [Forest] tile"
+Applicable to: Triggerable
+Works only with promotions that are valid for the unit's type - or for promotions that do not specify any. +Example: "[Wounded] units gain the [Shock I] promotion"
+Applicable to: Triggerable
+Example: "Provides the cheapest [Culture] building in your first [3] cities for free"
+Applicable to: Triggerable
+Example: "Provides a [Library] in your first [3] cities for free"
+Applicable to: Triggerable
+Example: "Triggers a [Inspiration] event"
+Applicable to: Triggerable
+Example: "Mark tutorial [comment] complete"
+Applicable to: Triggerable
+Allows suppressing specific validation warnings. Errors, deprecation warnings, or warnings about untyped and non-filtering uniques should be heeded, not suppressed, and are therefore not accepted. Note that this can be used in ModOptions, in the uniques a warning is about, or as modifier on the unique triggering a warning - but you still need to be specific. Even in the modifier case you will need to specify a sufficiently selective portion of the warning text as parameter. +Example: "Suppress warning [Tinman is supposed to automatically upgrade at tech Clockwork, and therefore Servos for its upgrade Mecha may not yet be researched! -or- is supposed to automatically upgrade]"
+Applicable to: Triggerable, Terrain, Speed, ModOptions, MetaModifier
+Uniques that have immediate, one-time effects on a unit.They can be added to units (on unit, unit type, or promotion) to grant them the ability to trigger this effect as an action, which can be modified with UnitActionModifier and UnitTriggerCondition conditionals.
+Example: "[This Unit] heals [3] HP"
+Applicable to: UnitTriggerable
+Example: "[This Unit] takes [3] damage"
+Applicable to: UnitTriggerable
+Example: "[This Unit] gains [3] XP"
+Applicable to: UnitTriggerable
+Example: "[This Unit] upgrades for free"
+Applicable to: UnitTriggerable
+Example: "[This Unit] upgrades for free including special upgrades"
+Applicable to: UnitTriggerable
+Example: "[This Unit] gains the [Shock I] promotion"
+Applicable to: UnitTriggerable
+Example: "[This Unit] loses the [Shock I] promotion"
+Applicable to: UnitTriggerable
+Example: "[This Unit] gains [3] movement"
+Applicable to: UnitTriggerable
+Example: "[This Unit] loses [3] movement"
+Applicable to: UnitTriggerable
+Statuses are temporary promotions. They do not stack, and reapplying a specific status take the highest number - so reapplying a 3-turn on a 1-turn makes it 3, but doing the opposite will have no effect. Turns left on the status decrease at the start of turn, so bonuses applied for 1 turn are stll applied during other civ's turns. +Example: "[This Unit] gains the [Shock I] status for [3] turn(s)"
+Applicable to: UnitTriggerable
+Example: "[This Unit] loses the [Shock I] status"
+Applicable to: UnitTriggerable
+Example: "[This Unit] is destroyed"
+Applicable to: UnitTriggerable
+Uniques that apply globally. Civs gain the abilities of these uniques from nation uniques, reached eras, researched techs, adopted policies, built buildings, religion 'founder' uniques, owned resources, and ruleset-wide global uniques.
+Example: "[+1 Gold, +2 Production]"
+Applicable to: Global, Terrain, Improvement
+Example: "[+1 Gold, +2 Production] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from every specialist [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] per [3] population [in all cities]"
+Applicable to: Global, FollowerBelief
+Only works for civ-wide stats +Example: "[+1 Gold, +2 Production] per [3] social policies adopted"
+Applicable to: Global
+Example: "[+1 Gold, +2 Production] per every [3] [Gold]"
+Applicable to: Global
+Example: "[+1 Gold, +2 Production] in cities on [Fresh Water] tiles"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from all [Culture] buildings"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from [Farm] tiles [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from [Farm] tiles without [Farm] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from every [Farm]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from each Trade Route"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% [Culture]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% [Culture] from every [Farm]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Yield from every [Farm]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% [Culture] from City-States"
+Applicable to: Global
+Example: "[+20]% [Culture] from Trade Routes"
+Applicable to: Global
+Example: "Nullifies [Culture] [in all cities]"
+Applicable to: Global
+Example: "Nullifies Growth [in all cities]"
+Applicable to: Global
+Example: "[+20]% Production when constructing [Culture] buildings [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Production when constructing [Melee] units [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Production when constructing [Culture] wonders [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Production towards any buildings that already exist in the Capital"
+Applicable to: Global, FollowerBelief
+Example: "Military Units gifted from City-States start with [3] XP"
+Applicable to: Global
+Example: "Militaristic City-States grant units [3] times as fast when you are at war with a common nation"
+Applicable to: Global
+Example: "Gifts of Gold to City-States generate [+20]% more Influence"
+Applicable to: Global
+Example: "Can spend Gold to annex or puppet a City-State that has been your ally for [3] turns."
+Applicable to: Global
+Applicable to: Global
+Applicable to: Global
+Example: "[+20]% City-State Influence degradation"
+Applicable to: Global
+Example: "Resting point for Influence with City-States is increased by [3]"
+Applicable to: Global
+Example: "Allied City-States provide [Culture] equal to [+20]% of what they produce for themselves"
+Applicable to: Global
+Example: "[+20]% resources gifted by City-States"
+Applicable to: Global
+Example: "[+20]% Happiness from luxury resources gifted by City-States"
+Applicable to: Global
+Applicable to: Global
+Example: "[+20]% growth [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[3]% Food is carried over after population increases [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Food consumption by specialists [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% unhappiness from the number of cities"
+Applicable to: Global
+Example: "[+20]% Unhappiness from [Followers of this Religion] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[3] Happiness from each type of luxury resource"
+Applicable to: Global
+Example: "Retain [+20]% of the happiness from a luxury after the last copy has been traded away"
+Applicable to: Global
+Example: "[+20]% of excess happiness converted to [Culture]"
+Applicable to: Global
+Example: "Cannot build [Melee] units"
+Applicable to: Global
+Applicable to: Global
+Example: "May buy [Melee] units for [3] [Culture] [in all cities] at an increasing price ([3])"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Culture] buildings for [3] [Culture] [in all cities] at an increasing price ([3])"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Melee] units for [3] [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Culture] buildings for [3] [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Melee] units with [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Culture] buildings with [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Melee] units with [Culture] for [3] times their normal Production cost"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Culture] buildings with [Culture] for [3] times their normal Production cost"
+Applicable to: Global, FollowerBelief
+Example: "[Culture] cost of purchasing items in cities [+20]%"
+Applicable to: Global, FollowerBelief
+Example: "[Culture] cost of purchasing [Culture] buildings [+20]%"
+Applicable to: Global, FollowerBelief
+Example: "[Culture] cost of purchasing [Melee] units [+20]%"
+Applicable to: Global, FollowerBelief
+Example: "Enables conversion of city production to [Gold]"
+Applicable to: Global
+Example: "Production to [Gold] conversion in cities changed by [+20]%"
+Applicable to: Global
+Applicable to: Global
+Applicable to: Global
+Example: "[+20]% maintenance on road & railroads"
+Applicable to: Global
+Example: "No Maintenance costs for improvements in [Farm] tiles"
+Applicable to: Global
+Example: "[+20]% construction time for [All Road] improvements"
+Applicable to: Global, Unit
+Free buildings CANNOT be self-removing - this leads to an endless loop of trying to add the building +Example: "Gain a free [Library] [in all cities]"
+Applicable to: Triggerable, Global
+Example: "[+20]% maintenance cost for buildings [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "Remove [Culture] [in all cities]"
+Applicable to: Triggerable, Global
+Example: "Sell [Culture] buildings [in all cities]"
+Applicable to: Triggerable, Global
+Example: "[+20]% Culture cost of natural border growth [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Gold cost of acquiring tiles [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "Each city founded increases culture cost of policies [+20]% less than normal"
+Applicable to: Global
+Example: "[+20]% Culture cost of adopting new Policies"
+Applicable to: Global
+Example: "[+1 Gold, +2 Production] for every known Natural Wonder"
+Applicable to: Global
+Example: "[+1 Gold, +2 Production] for discovering a Natural Wonder (bonus enhanced to [+1 Gold, +2 Production] if first to discover it)"
+Applicable to: Global
+Example: "[+20]% Great Person generation [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Gold from Great Merchant trade missions"
+Applicable to: Global
+Applicable to: Global, Unit
+Example: "Receive a free Great Person at the end of every [comment] (every 394 years), after researching [Agriculture]. Each bonus person can only be chosen once."
+Applicable to: Global
+Applicable to: Global
+Example: "[3] Unit Supply"
+Applicable to: Global
+Example: "[3] Unit Supply per [3] population [in all cities]"
+Applicable to: Global
+Example: "[3] Unit Supply per city"
+Applicable to: Global
+Example: "[3] units cost no maintenance"
+Applicable to: Global
+Applicable to: Global
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Global
+Example: "Enables [Wounded] units to enter ocean tiles"
+Applicable to: Global
+Example: "Land units may cross [Forest] tiles after the first [Melee] is earned"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Global
+Example: "Enemy [Wounded] units must spend [3] extra movement points when inside your territory"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Global
+Example: "New [Melee] units start with [3] Experience [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "All newly-trained [Melee] units [in all cities] receive the [Shock I] promotion"
+Applicable to: Global, FollowerBelief
+Example: "[Wounded] Units adjacent to this city heal [3] HP per turn when healing"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% City Strength from defensive buildings"
+Applicable to: Global
+Example: "[+20]% Strength for cities"
+Applicable to: Global, FollowerBelief
+Example: "Provides [3] [Iron]"
+Applicable to: Global, FollowerBelief, Improvement
+Example: "Quantity of strategic resources produced by the empire +[+20]%"
+Applicable to: Global
+Example: "Double quantity of [Iron] produced"
+Applicable to: Global
+Applicable to: Global
+Applicable to: Global
+Example: "Science gained from research agreements [+20]%"
+Applicable to: Global
+Applicable to: Global
+Example: "When declaring friendship, both parties gain a [+20]% boost to great person generation"
+Applicable to: Global
+Example: "Influence of all other civilizations with all city-states degrades [+20]% faster"
+Applicable to: Global
+Example: "Gain [3] Influence with a [Melee] gift to a City-State"
+Applicable to: Global
+Example: "Resting point for Influence with City-States following this religion [3]"
+Applicable to: Global
+Applicable to: Global
+Applicable to: Global
+Example: "When conquering an encampment, earn [3] Gold and recruit a Barbarian unit"
+Applicable to: Global
+Example: "When defeating a [Wounded] unit, earn [3] Gold and recruit it"
+Applicable to: Global
+Example: "May choose [3] additional [Follower] beliefs when [founding] a religion"
+Applicable to: Global
+Example: "May choose [3] additional belief(s) of any type when [founding] a religion"
+Applicable to: Global
+Example: "[+1 Gold, +2 Production] when a city adopts this religion for the first time"
+This unique's effect can be modified with <(modified by game speed)> +Applicable to: Global
+Example: "[+20]% Natural religion spread [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "Religion naturally spreads to cities [3] tiles away"
+Applicable to: Global, FollowerBelief
+Applicable to: Global
+Example: "[+20]% Faith cost of generating Great Prophet equivalents"
+Applicable to: Global
+Example: "[+20]% spy effectiveness [in all cities]"
+Applicable to: Global
+Example: "[+20]% enemy spy effectiveness [in all cities]"
+Applicable to: Global
+Example: "New spies start with [3] level(s)"
+Applicable to: Global
+Applicable to: Global
+Applicable to: Global
+Applicable to: Global
+Applicable to: Global
+Applicable to: Global
+Example: "Cities are razed [3] times as fast"
+Applicable to: Global
+Applicable to: Global
+Example: "[+20]% Golden Age length"
+Applicable to: Global
+Example: "Population loss from nuclear attacks [+20]% [in all cities]"
+Applicable to: Global
+Example: "Damage to garrison from nuclear attacks [+20]% [in all cities]"
+Applicable to: Global
+Applicable to: Global
+Example: "[+20]% Strength"
+Applicable to: Global, Unit
+Example: "[+20]% Strength decreasing with distance from the capital"
+Applicable to: Global, Unit
+Example: "[+20]% to Flank Attack bonuses"
+Applicable to: Global, Unit
+Example: "[3] additional attacks per turn"
+Applicable to: Global, Unit
+Example: "[3] Movement"
+Applicable to: Global, Unit
+Example: "[3] Sight"
+Applicable to: Global, Unit, Terrain
+Example: "[3] Range"
+Applicable to: Global, Unit
+Example: "[+20] Air Interception Range"
+Applicable to: Global, Unit
+Example: "[3] HP when healing"
+Applicable to: Global, Unit
+Example: "[+20]% Spread Religion Strength"
+Applicable to: Global, Unit
+Example: "When spreading religion to a city, gain [3] times the amount of followers of other religions as [Culture]"
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Example: "Heals [3] damage if it kills a unit"
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Example: "[+20]% maintenance costs"
+Applicable to: Global, Unit
+Example: "[+20]% Gold cost of upgrading"
+Applicable to: Global, Unit
+Example: "Earn [3]% of the damage done to [City] units as [Gold]"
+Applicable to: Global, Unit
+Example: "Upon capturing a city, receive [3] times its [Culture] production as [Gold] immediately"
+Applicable to: Global, Unit
+Example: "Earn [3]% of killed [Wounded] unit's [Cost] as [Gold]"
+Applicable to: Global, Unit
+Example: "[3] XP gained from combat"
+Applicable to: Global, Unit
+Example: "[+20]% XP gained from combat"
+Applicable to: Global, Unit
+Example: "[Great General] is earned [+20]% faster"
+Applicable to: Global, Unit
+Example: "[3] Movement point cost to disembark"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Global, Unit
+Example: "[3] Movement point cost to embark"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Global, Unit
+Applicable to: Nation
+Example: "Starts with [Agriculture]"
+Applicable to: Nation
+Example: "Starts with [Oligarchy] adopted"
+Applicable to: Nation
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Nation
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Nation
+Applicable to: Nation, Terrain, Improvement, Resource
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Example: "Will not build [Melee]"
+Applicable to: Personality
+Applicable to: Era
+Applicable to: Era
+Applicable to: Tech
+Applicable to: Tech
+Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Applicable to: Tech, Building
+Example: "[+20]% weight to this choice for AI decisions"
+Applicable to: Tech, Policy, Promotion
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Example: "[+20]% weight to this choice for AI decisions"
+Applicable to: Tech, Policy, Promotion
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Uniques for Founder and Enhancer type Beliefs, that will apply to the founder of this religion
+Example: "[+1 Gold, +2 Production] for each global city following this religion"
+Applicable to: FounderBelief
+Example: "[+1 Gold, +2 Production] from every [3] global followers [in all cities]"
+Applicable to: FounderBelief
+Example: "[+20]% [Culture] from every follower, up to [+20]%"
+Applicable to: FounderBelief, FollowerBelief
+Meant to be used together with conditionals, like "Only available
Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Uniques for Pantheon and Follower type beliefs, that will apply to each city where the religion is the majority religion
+Example: "[+1 Gold, +2 Production] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from every specialist [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] per [3] population [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] in cities on [Fresh Water] tiles"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from all [Culture] buildings"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from [Farm] tiles [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from [Farm] tiles without [Farm] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from every [Farm]"
+Applicable to: Global, FollowerBelief
+Example: "[+1 Gold, +2 Production] from each Trade Route"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% [Culture]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% [Culture] from every [Farm]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Yield from every [Farm]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% [Culture] from every follower, up to [+20]%"
+Applicable to: FounderBelief, FollowerBelief
+Example: "[+20]% Production when constructing [Culture] buildings [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Production when constructing [Melee] units [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Production when constructing [Culture] wonders [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Production towards any buildings that already exist in the Capital"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% growth [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[3]% Food is carried over after population increases [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Food consumption by specialists [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Unhappiness from [Followers of this Religion] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Melee] units for [3] [Culture] [in all cities] at an increasing price ([3])"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Culture] buildings for [3] [Culture] [in all cities] at an increasing price ([3])"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Melee] units for [3] [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Culture] buildings for [3] [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Melee] units with [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Culture] buildings with [Culture] [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Melee] units with [Culture] for [3] times their normal Production cost"
+Applicable to: Global, FollowerBelief
+Example: "May buy [Culture] buildings with [Culture] for [3] times their normal Production cost"
+Applicable to: Global, FollowerBelief
+Example: "[Culture] cost of purchasing items in cities [+20]%"
+Applicable to: Global, FollowerBelief
+Example: "[Culture] cost of purchasing [Culture] buildings [+20]%"
+Applicable to: Global, FollowerBelief
+Example: "[Culture] cost of purchasing [Melee] units [+20]%"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% maintenance cost for buildings [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Culture cost of natural border growth [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Gold cost of acquiring tiles [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Great Person generation [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "New [Melee] units start with [3] Experience [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "All newly-trained [Melee] units [in all cities] receive the [Shock I] promotion"
+Applicable to: Global, FollowerBelief
+Example: "[Wounded] Units adjacent to this city heal [3] HP per turn when healing"
+Applicable to: Global, FollowerBelief
+Example: "[+20]% Strength for cities"
+Applicable to: Global, FollowerBelief
+Example: "Provides [3] [Iron]"
+Applicable to: Global, FollowerBelief, Improvement
+Example: "[+20]% Natural religion spread [in all cities]"
+Applicable to: Global, FollowerBelief
+Example: "Religion naturally spreads to cities [3] tiles away"
+Applicable to: Global, FollowerBelief
+Meant to be used together with conditionals, like "Only available
Example: "Earn [3]% of [Wounded] unit's [Cost] as [Gold] when killed within 4 tiles of a city following this religion"
+Applicable to: FollowerBelief
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Example: "Consumes [3] [Iron]"
+Applicable to: Building, Unit, Improvement
+These resources are removed when work begins on the construction. Do not confuse with "costs [amount] [stockpiledResource]" (lowercase 'c'), the Unit Action Modifier. +Example: "Costs [3] [Mana]"
+Applicable to: Building, Unit, Improvement
+Blocks from being built, possibly by conditional. However it can still appear in the menu and be bought with other means such as Gold or Faith +Applicable to: Building, Unit, Improvement
+Applicable to: Building, Unit
+Example: "Can be purchased with [Culture] [in all cities]"
+Applicable to: Building, Unit
+Example: "Can be purchased for [3] [Culture] [in all cities]"
+Applicable to: Building, Unit
+Example: "Limited to [3] per Civilization"
+Applicable to: Building, Unit
+Example: "Hidden until [3] social policy branches have been completed"
+Applicable to: Building, Unit
+Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Applicable to: Building, Unit
+Example: "Requires at least [3] population"
+Applicable to: Building, Unit
+Applicable to: Building, Unit
+Applicable to: Building, Unit
+Example: "Cost increases by [3] per owned city"
+Applicable to: Building, Unit
+Example: "Cost increases by [3] when built"
+Applicable to: Building, Unit
+Intended to be used with conditionals to dynamically alter construction costs +Example: "[3]% production cost"
+Applicable to: Building, Unit
+Meant to be used together with conditionals, like "Can only be built
Example: "Must have an owned [Farm] within [3] tiles"
+Applicable to: Building
+Applicable to: Building
+Example: "Must be on [Farm]"
+Applicable to: Building
+Example: "Must not be on [Farm]"
+Applicable to: Building
+Example: "Must be next to [Farm]"
+Applicable to: Building, Improvement
+Example: "Must not be next to [Farm]"
+Applicable to: Building
+Applicable to: Building
+Example: "Obsolete with [Agriculture]"
+Applicable to: Building, Improvement, Resource
+Applicable to: Building
+Applicable to: Building
+Applicable to: Building
+Applicable to: Building
+Applicable to: Building
+Applicable to: Building
+Applicable to: Building
+Applicable to: Building
+Applicable to: Building
+Example: "Creates a [Trading Post] improvement on a specific tile"
+Applicable to: Building
+Applicable to: Building, Unit
+Applicable to: Tech, Building
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Applicable to: Building, Unit
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Uniques that affect a unit's actions, and can be modified by UnitActionModifiers
+Applicable to: UnitAction
+Example: "Can instantly construct a [All Road] improvement"
+Applicable to: UnitAction
+Applicable to: UnitAction
+Applicable to: UnitAction
+Applicable to: UnitAction
+Applicable to: UnitAction
+By default consumes all movement +Example: "Can transform to [Musketman]"
+Applicable to: UnitAction
+Uniques that can be added to units, unit types, or promotions
+Example: "[+20]% construction time for [All Road] improvements"
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Example: "Consumes [3] [Iron]"
+Applicable to: Building, Unit, Improvement
+These resources are removed when work begins on the construction. Do not confuse with "costs [amount] [stockpiledResource]" (lowercase 'c'), the Unit Action Modifier. +Example: "Costs [3] [Mana]"
+Applicable to: Building, Unit, Improvement
+Blocks from being built, possibly by conditional. However it can still appear in the menu and be bought with other means such as Gold or Faith +Applicable to: Building, Unit, Improvement
+Applicable to: Building, Unit
+Example: "Can be purchased with [Culture] [in all cities]"
+Applicable to: Building, Unit
+Example: "Can be purchased for [3] [Culture] [in all cities]"
+Applicable to: Building, Unit
+Example: "Limited to [3] per Civilization"
+Applicable to: Building, Unit
+Example: "Hidden until [3] social policy branches have been completed"
+Applicable to: Building, Unit
+Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Applicable to: Building, Unit
+Example: "Requires at least [3] population"
+Applicable to: Building, Unit
+Applicable to: Building, Unit
+Applicable to: Building, Unit
+Example: "Cost increases by [3] per owned city"
+Applicable to: Building, Unit
+Example: "Cost increases by [3] when built"
+Applicable to: Building, Unit
+Intended to be used with conditionals to dynamically alter construction costs +Example: "[3]% production cost"
+Applicable to: Building, Unit
+Meant to be used together with conditionals, like "Can only be built
Applicable to: Unit
+Example: "Can build [All Road] improvements on tiles"
+Applicable to: Unit
+Example: "Can be added to [comment] in the Capital"
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Example: "May Paradrop up to [3] tiles from inside friendly territory"
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Example: "Can undertake a trade mission with City-State, giving a large sum of gold and [3] Influence"
+Applicable to: Unit
+Applicable to: Unit
+Example: "[+20]% Strength"
+Applicable to: Global, Unit
+Example: "[+20]% Strength decreasing with distance from the capital"
+Applicable to: Global, Unit
+Example: "[+20]% to Flank Attack bonuses"
+Applicable to: Global, Unit
+Example: "[+20]% Strength for enemy [Wounded] units in adjacent [Farm] tiles"
+Applicable to: Unit
+Example: "[+20]% Strength when stacked with [Wounded]"
+Applicable to: Unit
+Example: "[+20]% Strength bonus for [Wounded] units within [3] tiles"
+Applicable to: Unit
+Example: "[3] additional attacks per turn"
+Applicable to: Global, Unit
+Example: "[3] Movement"
+Applicable to: Global, Unit
+Example: "[3] Sight"
+Applicable to: Global, Unit, Terrain
+Example: "[3] Range"
+Applicable to: Global, Unit
+Example: "[+20] Air Interception Range"
+Applicable to: Global, Unit
+Example: "[3] HP when healing"
+Applicable to: Global, Unit
+Example: "[+20]% Spread Religion Strength"
+Applicable to: Global, Unit
+Example: "When spreading religion to a city, gain [3] times the amount of followers of other religions as [Culture]"
+Applicable to: Global, Unit
+Example: "Can only attack [City] units"
+Applicable to: Unit
+Example: "Can only attack [Farm] tiles"
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Example: "Blast radius [3]"
+Applicable to: Unit
+Applicable to: Global, Unit
+Example: "Nuclear weapon of Strength [3]"
+Applicable to: Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Unit
+Example: "Transfer Movement to [Wounded]"
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Example: "Heals [3] damage if it kills a unit"
+Applicable to: Global, Unit
+Applicable to: Global, Unit
+Applicable to: Unit
+Example: "All adjacent units heal [3] HP when healing"
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Unit
+Example: "Can carry [3] [Wounded] units"
+Applicable to: Unit
+Example: "Can carry [3] extra [Wounded] units"
+Applicable to: Unit
+Example: "Cannot be carried by [Wounded] units"
+Applicable to: Unit
+Example: "[+20]% chance to intercept air attacks"
+Applicable to: Unit
+Example: "Damage taken from interception reduced by [+20]%"
+Applicable to: Unit
+Example: "[+20]% Damage when intercepting"
+Applicable to: Unit
+Example: "[3] extra interceptions may be made per turn"
+Applicable to: Unit
+Applicable to: Unit
+Example: "Cannot intercept [Wounded] units"
+Applicable to: Unit
+Example: "[+20]% Strength when performing Air Sweep"
+Applicable to: Unit
+Example: "[+20]% maintenance costs"
+Applicable to: Global, Unit
+Example: "[+20]% Gold cost of upgrading"
+Applicable to: Global, Unit
+Example: "Earn [3]% of the damage done to [City] units as [Gold]"
+Applicable to: Global, Unit
+Example: "Upon capturing a city, receive [3] times its [Culture] production as [Gold] immediately"
+Applicable to: Global, Unit
+Example: "Earn [3]% of killed [Wounded] unit's [Cost] as [Gold]"
+Applicable to: Global, Unit
+Example: "May capture killed [Wounded] units"
+Applicable to: Unit
+Example: "[3] XP gained from combat"
+Applicable to: Global, Unit
+Example: "[+20]% XP gained from combat"
+Applicable to: Global, Unit
+Applicable to: Unit
+Example: "[Great General] is earned [+20]% faster"
+Applicable to: Global, Unit
+Applicable to: Unit
+Applicable to: Unit
+Example: "Can see invisible [Wounded] units"
+Applicable to: Unit
+Example: "May upgrade to [Musketman] through ruins-like effects"
+Applicable to: Unit
+Example: "Can upgrade to [Musketman]"
+Applicable to: Unit
+Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Example: "Double movement in [Fresh Water]"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Applicable to: Unit
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Example: "May enter foreign tiles without open borders, but loses [3] religious strength each turn it ends there"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Unit
+Example: "[3] Movement point cost to disembark"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Global, Unit
+Example: "[3] Movement point cost to embark"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Global, Unit
+Applicable to: Unit
+Applicable to: Unit
+Applicable to: Building, Unit
+Applicable to: Unit
+Example: "Great Person - [comment]"
+Applicable to: Unit
+Great people in the same group increase teach other's costs when gained. Gaining one will make all others in the same group cost more GPP. +Example: "Is part of Great Person group [comment]"
+Applicable to: Unit
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Applicable to: Building, Unit
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Applicable to: Promotion
+Applicable to: Promotion
+Example: "[+20]% weight to this choice for AI decisions"
+Applicable to: Tech, Policy, Promotion
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Example: "[+1 Gold, +2 Production]"
+Applicable to: Global, Terrain, Improvement
+Example: "[3] Sight"
+Applicable to: Global, Unit, Terrain
+Example: "Must be adjacent to [3] [Elevated] tiles"
+Applicable to: Terrain
+Example: "Must be adjacent to [3] to [3] [Elevated] tiles"
+Applicable to: Terrain
+Example: "Must not be on [3] largest landmasses"
+Applicable to: Terrain
+Example: "Must be on [3] largest landmasses"
+Applicable to: Terrain
+Example: "Occurs on latitudes from [3] to [3] percent of distance equator to pole"
+Applicable to: Terrain
+Example: "Occurs in groups of [3] to [3] tiles"
+Applicable to: Terrain
+Supports conditionals that need only a Tile as context and nothing else, like <with [n]% chance>
, and applies them per neighbor.
If your mod renames Coast or Lakes, do not use this with one of these as parameter, as the code preventing artifacts won't work. + Example: "Neighboring tiles will convert to [Grassland]"
+Applicable to: Terrain
+
+Example: "Grants [+1 Gold, +2 Production] to the first civilization to discover it"
+Applicable to: Terrain
+Example: "Units ending their turn on this terrain take [3] damage"
+Due to performance considerations, this unique is cached, thus conditionals that may change within a turn may not work. +Applicable to: Terrain
+Example: "Grants [Shock I] ([comment]) to adjacent [Wounded] units for the rest of the game"
+Applicable to: Terrain
+Example: "[3] Strength for cities built on this terrain"
+Applicable to: Terrain
+Applicable to: Terrain
+Applicable to: Terrain, Improvement
+Applicable to: Terrain, Improvement
+Applicable to: Terrain
+Example: "Only [All Road] improvements may be built on this tile"
+Applicable to: Terrain
+Applicable to: Terrain
+Example: "Has an elevation of [3] for visibility calculations"
+Applicable to: Terrain
+Example: "Always Fertility [3] for Map Generation"
+Applicable to: Terrain
+Example: "[3] to Fertility for Map Generation"
+Applicable to: Terrain
+Example: "A Region is formed with at least [3]% [Elevated] tiles, with priority [3]"
+Applicable to: Terrain
+Example: "A Region is formed with at least [3]% [Elevated] tiles and [Elevated] tiles, with priority [3]"
+Applicable to: Terrain
+Example: "A Region can not contain more [Elevated] tiles than [Elevated] tiles"
+Applicable to: Terrain
+Applicable to: Terrain
+Example: "Starts in regions of this type receive an extra [Iron]"
+Applicable to: Terrain
+Applicable to: Terrain
+Example: "Becomes [Forest] when adjacent to [Fresh Water]"
+Applicable to: Terrain
+Example: "Considered [Undesirable] when determining start locations"
+Applicable to: Terrain
+Applicable to: Terrain, Resource
+Example: "Occurs at temperature between [0.5] and [0.5] and humidity between [0.5] and [0.5]"
+Applicable to: Terrain, Resource
+Applicable to: Terrain
+Applicable to: Terrain
+Example: "Every [3] tiles with this terrain will receive a major deposit of a strategic resource."
+Applicable to: Terrain
+Applicable to: Terrain
+Example: "[3]% Chance to be destroyed by nukes"
+Applicable to: Terrain
+Applicable to: Terrain
+Applicable to: Terrain
+Applicable to: Nation, Terrain, Improvement, Resource
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows suppressing specific validation warnings. Errors, deprecation warnings, or warnings about untyped and non-filtering uniques should be heeded, not suppressed, and are therefore not accepted. Note that this can be used in ModOptions, in the uniques a warning is about, or as modifier on the unique triggering a warning - but you still need to be specific. Even in the modifier case you will need to specify a sufficiently selective portion of the warning text as parameter. +Example: "Suppress warning [Tinman is supposed to automatically upgrade at tech Clockwork, and therefore Servos for its upgrade Mecha may not yet be researched! -or- is supposed to automatically upgrade]"
+Applicable to: Triggerable, Terrain, Speed, ModOptions, MetaModifier
+Example: "[+1 Gold, +2 Production]"
+Applicable to: Global, Terrain, Improvement
+Example: "Consumes [3] [Iron]"
+Applicable to: Building, Unit, Improvement
+Example: "Provides [3] [Iron]"
+Applicable to: Global, FollowerBelief, Improvement
+These resources are removed when work begins on the construction. Do not confuse with "costs [amount] [stockpiledResource]" (lowercase 'c'), the Unit Action Modifier. +Example: "Costs [3] [Mana]"
+Applicable to: Building, Unit, Improvement
+Blocks from being built, possibly by conditional. However it can still appear in the menu and be bought with other means such as Gold or Faith +Applicable to: Building, Unit, Improvement
+Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Example: "Must be next to [Farm]"
+Applicable to: Building, Improvement
+Example: "Obsolete with [Agriculture]"
+Applicable to: Building, Improvement, Resource
+Applicable to: Terrain, Improvement
+Applicable to: Terrain, Improvement
+Applicable to: Nation, Terrain, Improvement, Resource
+Applicable to: Improvement
+Example: "[+1 Gold, +2 Production] from [Farm] tiles"
+Applicable to: Improvement
+Example: "[+1 Gold, +2 Production] for each adjacent [Farm]"
+Applicable to: Improvement
+Example: "Ensures a minimum tile yield of [+1 Gold, +2 Production]"
+Applicable to: Improvement
+Applicable to: Improvement
+Applicable to: Improvement
+Example: "Can only be built on [Farm] tiles"
+Applicable to: Improvement
+Example: "Cannot be built on [Farm] tiles"
+Applicable to: Improvement
+Applicable to: Improvement
+Example: "Does not need removal of [Farm]"
+Applicable to: Improvement
+Applicable to: Improvement
+Does not accept unit-based conditionals +Example: "Gives a defensive bonus of [+20]%"
+Applicable to: Improvement
+Example: "Costs [3] [Culture] per turn when in your territory"
+Applicable to: Improvement
+Example: "Costs [3] [Culture] per turn"
+Applicable to: Improvement
+Example: "Adjacent enemy units ending their turn take [3] damage"
+Applicable to: Improvement
+Applicable to: Improvement
+Applicable to: Improvement
+Applicable to: Improvement
+Example: "Pillaging this improvement yields approximately [+1 Gold, +2 Production]"
+Applicable to: Improvement
+Example: "Pillaging this improvement yields [+1 Gold, +2 Production]"
+Applicable to: Improvement
+Applicable to: Improvement
+Applicable to: Improvement
+This is offered as an alternative to the improvedBy field of a resource. The result will be cached within the resource definition when loading a game, without knowledge about terrain, cities, civs, units or time. Therefore, most conditionals will not work, only those not dependent on game state. +Example: "Improves [Strategic] resource in this tile"
+Applicable to: Improvement
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Example: "Obsolete with [Agriculture]"
+Applicable to: Building, Improvement, Resource
+Applicable to: Terrain, Resource
+Example: "Occurs at temperature between [0.5] and [0.5] and humidity between [0.5] and [0.5]"
+Applicable to: Terrain, Resource
+Applicable to: Nation, Terrain, Improvement, Resource
+Example: "Deposits in [Farm] tiles always provide [3] resources"
+Applicable to: Resource
+Applicable to: Resource
+This resource is accumulated each turn, rather than having a set of producers and consumers at a given moment.The current stockpiled amount can be affected with trigger uniques. +Applicable to: Resource
+This resource is calculated on a per-city level rather than a per-civ level +Applicable to: Resource
+Applicable to: Resource
+Applicable to: Resource
+The probability for this resource to be chosen is (this resource weight) / (sum weight of all eligible resources). Resources without a unique are given weight 1
+Example: "Generated with weight [3]"
Applicable to: Resource
+The probability for this resource to be chosen is (this resource weight) / (sum weight of all eligible resources). Resources without a unique are not generated as minor deposits. +Example: "Minor deposits generated with weight [3]"
+Applicable to: Resource
+The probability for this resource to be chosen is (this resource weight) / (sum weight of all eligible resources). Only assignable to luxuries, resources without a unique are given weight 1
+Example: "Generated near City States with weight [3]"
Applicable to: Resource
+Applicable to: Resource
+Example: "Generated on every [3] tiles"
+Applicable to: Resource
+Applicable to: Resource
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Example: "Free [Musketman] found in the ruins"
+Applicable to: Ruins
+Example: "From a randomly chosen tile [3] tiles away from the ruins, reveal tiles up to [3] tiles away with [3]% chance"
+Applicable to: Ruins
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows suppressing specific validation warnings. Errors, deprecation warnings, or warnings about untyped and non-filtering uniques should be heeded, not suppressed, and are therefore not accepted. Note that this can be used in ModOptions, in the uniques a warning is about, or as modifier on the unique triggering a warning - but you still need to be specific. Even in the modifier case you will need to specify a sufficiently selective portion of the warning text as parameter. +Example: "Suppress warning [Tinman is supposed to automatically upgrade at tech Clockwork, and therefore Servos for its upgrade Mecha may not yet be researched! -or- is supposed to automatically upgrade]"
+Applicable to: Triggerable, Terrain, Speed, ModOptions, MetaModifier
+Example: "Provides military units every ≈[3] turns"
+Applicable to: CityState
+Applicable to: CityState
+Applicable to: ModOptions
+Applicable to: ModOptions
+Applicable to: ModOptions
+Example: "Can trade civilization introductions for [3] Gold"
+Applicable to: ModOptions
+Applicable to: ModOptions
+Applicable to: ModOptions
+Applicable to: ModOptions
+Applicable to: ModOptions
+Allows suppressing specific validation warnings. Errors, deprecation warnings, or warnings about untyped and non-filtering uniques should be heeded, not suppressed, and are therefore not accepted. Note that this can be used in ModOptions, in the uniques a warning is about, or as modifier on the unique triggering a warning - but you still need to be specific. Even in the modifier case you will need to specify a sufficiently selective portion of the warning text as parameter. +Example: "Suppress warning [Tinman is supposed to automatically upgrade at tech Clockwork, and therefore Servos for its upgrade Mecha may not yet be researched! -or- is supposed to automatically upgrade]"
+Applicable to: Triggerable, Terrain, Speed, ModOptions, MetaModifier
+Specifies that your Mod is incompatible with another. Always treated symmetrically, and cannot be overridden by the Mod you are declaring as incompatible. +Example: "Mod is incompatible with [DeCiv Redux]"
+Applicable to: ModOptions
+Specifies that your Extension Mod is only available if any other Mod matching the filter is active. +Example: "Mod requires [DeCiv Redux]"
+Applicable to: ModOptions
+Applicable to: ModOptions
+Applicable to: ModOptions
+Applicable to: ModOptions
+Only meaningful for Mods containing several maps. When this mod is selected on the new game screen's custom maps mod dropdown, the named map will be selected on the map dropdown. Also disables selection by recently modified. Case insensitive. +Example: "Mod preselects map [comment]"
+Applicable to: ModOptions
+Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Meant to be used together with conditionals, like "Only available
Meant to be used together with conditionals, like "Unavailable
Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. +Example: "Comment [comment]"
+Applicable to: Nation, Tech, Policy, FounderBelief, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed, EventChoice
+Modifiers that can be added to other uniques to limit when they will be active
+Example: "<every [3] turns>"
+Applicable to: Conditional
+Example: "<before turn number [3]>"
+Applicable to: Conditional
+Example: "<after turn number [3]>"
+Applicable to: Conditional
+Example: "<on [Quick] game speed>"
+Applicable to: Conditional
+Example: "<on [Prince] difficulty>"
+Applicable to: Conditional
+Example: "<when [Domination] Victory is enabled>"
+Applicable to: Conditional
+Example: "<when [Domination] Victory is disabled>"
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<with [3]% chance>"
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<if tutorial [comment] is completed>"
+Applicable to: Conditional
+Example: "<for [City-States] Civilizations>"
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<when between [3] and [3] Happiness>"
+Applicable to: Conditional
+Example: "<when above [3] Happiness>"
+Applicable to: Conditional
+Example: "<when below [3] Happiness>"
+Applicable to: Conditional
+Example: "<during the [Ancient era]>"
+Applicable to: Conditional
+Example: "<before the [Ancient era]>"
+Applicable to: Conditional
+Example: "<starting from the [Ancient era]>"
+Applicable to: Conditional
+Example: "<if starting in the [Ancient era]>"
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<after discovering [Agriculture]>"
+Applicable to: Conditional
+Example: "<before discovering [Agriculture]>"
+Applicable to: Conditional
+This condition is fulfilled while the technology is actively being researched (it is the one research points are added to) +Example: "<while researching [Agriculture]>"
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<after adopting [Oligarchy]>"
+Applicable to: Conditional
+Example: "<before adopting [Oligarchy]>"
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<if [Culture] is constructed>"
+Applicable to: Conditional
+Example: "<if [Culture] is not constructed>"
+Applicable to: Conditional
+Example: "<if [Culture] is constructed in all [in all cities] cities>"
+Applicable to: Conditional
+Example: "<if [Culture] is constructed in at least [3] of [in all cities] cities>"
+Applicable to: Conditional
+Example: "<if [Culture] is constructed by anybody>"
+Applicable to: Conditional
+Example: "<with [Iron]>"
+Applicable to: Conditional
+Example: "<without [Iron]>"
+Applicable to: Conditional
+Stats refers to the accumulated stat, not stat-per-turn +Example: "<when above [3] [Culture]>"
+This unique's effect can be modified with <(modified by game speed)> +Applicable to: Conditional
+Stats refers to the accumulated stat, not stat-per-turn +Example: "<when below [3] [Culture]>"
+This unique's effect can be modified with <(modified by game speed)> +Applicable to: Conditional
+Stats refers to the accumulated stat, not stat-per-turn +Example: "<when between [3] and [3] [Culture]>"
+This unique's effect can be modified with <(modified by game speed)> +Applicable to: Conditional
+Applicable to: Conditional
+Example: "<in [in all cities] cities>"
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<in cities with a [Culture]>"
+Applicable to: Conditional
+Example: "<in cities without a [Culture]>"
+Applicable to: Conditional
+Example: "<in cities with at least [3] [Followers of this Religion]>"
+Applicable to: Conditional
+Example: "<in cities with [3] [Followers of this Religion]>"
+Applicable to: Conditional
+Example: "<in cities with between [3] and [3] [Followers of this Religion]>"
+Applicable to: Conditional
+Example: "<in cities with less than [3] [Followers of this Religion]>"
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<for [Wounded] units>"
+Applicable to: Conditional
+Example: "<when [Wounded]>"
+Applicable to: Conditional
+Also applies to units with temporary status +Example: "<for units with [Shock I]>"
+Applicable to: Conditional
+Also applies to units with temporary status +Example: "<for units without [Shock I]>"
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<vs [Wounded] units>"
+Applicable to: Conditional
+Example: "<vs [City]>"
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<when fighting in [Farm] tiles>"
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<when adjacent to a [Wounded] unit>"
+Applicable to: Conditional
+Example: "<when above [3] HP>"
+Applicable to: Conditional
+Example: "<when below [3] HP>"
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<with [3] to [3] neighboring [Farm] tiles>"
+Applicable to: Conditional
+Example: "<in [Farm] tiles>"
+Applicable to: Conditional
+Example: "<in tiles without [Farm]>"
+Applicable to: Conditional
+Example: "<within [3] tiles of a [Farm]>"
+Applicable to: Conditional
+Example: "<in tiles adjacent to [Farm] tiles>"
+Applicable to: Conditional
+Example: "<in tiles not adjacent to [Farm] tiles>"
+Applicable to: Conditional
+Applicable to: Conditional
+Example: "<in [Hybrid] Regions>"
+Applicable to: Conditional
+Example: "<in all except [Hybrid] Regions>"
+Applicable to: Conditional
+Example: "<when number of [1000] is equal to [1000]>"
+Applicable to: Conditional
+Example: "<when number of [1000] is different than [1000]>"
+Applicable to: Conditional
+Example: "<when number of [1000] is more than [1000]>"
+Applicable to: Conditional
+Example: "<when number of [1000] is less than [1000]>"
+Applicable to: Conditional
+Example: "<when number of [1000] is between [1000] and [1000]>"
+Applicable to: Conditional
+Example: "<if [DeCiv Redux] is enabled>"
+Applicable to: Conditional
+Example: "<if [DeCiv Redux] is not enabled>"
+Applicable to: Conditional
+Special conditionals that can be added to Triggerable uniques, to make them activate upon specific actions.
+Example: "<upon discovering [Agriculture] technology>"
+Applicable to: TriggerCondition
+Example: "<upon entering the [Ancient era]>"
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Example: "<upon adopting [Oligarchy]>"
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition, UnitTriggerCondition
+Applicable to: TriggerCondition
+Example: "<upon building a [All Road] improvement>"
+Applicable to: TriggerCondition, UnitTriggerCondition
+Applicable to: TriggerCondition
+Example: "<upon constructing [Culture]>"
+Applicable to: TriggerCondition
+Example: "<upon constructing [Culture] [in all cities]>"
+Applicable to: TriggerCondition
+Example: "<upon gaining a [Melee] unit>"
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Applicable to: TriggerCondition
+Special conditionals that can be added to UnitTriggerable uniques, to make them activate upon specific actions.
+Applicable to: TriggerCondition, UnitTriggerCondition
+Example: "<upon building a [All Road] improvement>"
+Applicable to: TriggerCondition, UnitTriggerCondition
+Can apply triggers to to damaged unit by setting the first parameter to 'Target Unit' +Example: "<upon damaging a [Wounded] unit>"
+Applicable to: UnitTriggerCondition
+Example: "<upon defeating a [Wounded] unit>"
+Applicable to: UnitTriggerCondition
+Example: "<upon expending a [Wounded] unit>"
+Applicable to: UnitTriggerCondition
+Applicable to: UnitTriggerCondition
+Applicable to: UnitTriggerCondition
+Example: "<upon gaining the [Shock I] promotion>"
+Applicable to: UnitTriggerCondition
+Example: "<upon losing the [Shock I] promotion>"
+Applicable to: UnitTriggerCondition
+Example: "<upon gaining the [Shock I] status>"
+Applicable to: UnitTriggerCondition
+Example: "<upon losing the [Shock I] status>"
+Applicable to: UnitTriggerCondition
+Example: "<upon losing at least [3] HP in a single attack>"
+Applicable to: UnitTriggerCondition
+Example: "<upon ending a turn in a [Farm] tile>"
+Applicable to: UnitTriggerCondition
+Example: "<upon discovering a [Farm] tile>"
+Applicable to: UnitTriggerCondition
+Modifiers that can be added to UnitAction uniques as conditionals
+Applicable to: UnitActionModifier
+Will consume up to [amount] of Movement to execute +Example: "<for [3] movement>"
+Applicable to: UnitActionModifier
+Will consume all Movement to execute +Applicable to: UnitActionModifier
+Requires [amount] of Movement to execute. Unit's Movement is rounded up +Example: "<requires [3] movement>"
+Applicable to: UnitActionModifier
+A positive Integer value will be subtracted from your stock. Food and Production will be removed from Closest City's current stock +Example: "<costs [+1 Gold, +2 Production] stats>"
+Applicable to: UnitActionModifier
+A positive Integer value will be subtracted from your stock. Do not confuse with "Costs [amount] [stockpiledResource]" (uppercase 'C') for Improvements, Buildings, and Units. +Example: "<costs [3] [Mana]>"
+Applicable to: UnitActionModifier
+Removes the promotion/status from the unit - this is not a cost, units will be able to activate the action even without the promotion/status. To limit, use
Applicable to: UnitActionModifier
+Applicable to: UnitActionModifier
+Example: "<[3] times>"
+Applicable to: UnitActionModifier
+Example: "<[3] additional time(s)>"
+Applicable to: UnitActionModifier
+Applicable to: UnitActionModifier
+Modifiers that can be added to other uniques changing user experience, not their behavior
+Turns this unique into a trigger, activating this unique as a global unique for a number of turns +Example: "<for [3] turns>"
+Applicable to: MetaModifier
+Applicable to: MetaModifier
+Example: "<for every [1000]>"
+Applicable to: MetaModifier
+Example: "<for every adjacent [Farm]>"
+Applicable to: MetaModifier
+Example: "<for every [3] [1000]>"
+Applicable to: MetaModifier
+Can only be applied to certain uniques, see details of each unique for specifics +Applicable to: MetaModifier
+Allows suppressing specific validation warnings. Errors, deprecation warnings, or warnings about untyped and non-filtering uniques should be heeded, not suppressed, and are therefore not accepted. Note that this can be used in ModOptions, in the uniques a warning is about, or as modifier on the unique triggering a warning - but you still need to be specific. Even in the modifier case you will need to specify a sufficiently selective portion of the warning text as parameter. +Example: "<Suppress warning [Tinman is supposed to automatically upgrade at tech Clockwork, and therefore Servos for its upgrade Mecha may not yet be researched! -or- is supposed to automatically upgrade]>"
+Applicable to: Triggerable, Terrain, Speed, ModOptions, MetaModifier
+Since the question has come up several times, here is a summary of how Force ratings are calculated.
+First the base unit gets a force evaluation. +If the unit has a ranged attack, the starting force is the ranged strength ^ 1.45. Otherwise the starting force is strength ^ 1.5. +This is multiplied by the unit's movement ^ 0.3. Nukes get +4000.
+Then this is multiplied by a bunch of modifiers:
+Each individual unit has a Force equal to the Base Unit Force,
+The civs Force Ranking is based on the sum of all their units' Force Evaluation (cities are not counted). +Only half the Force of naval units is counted. +This is multiplied by a gold modifier equal to the square root of current gold, as a percentage. +The gold multiplier is constrained to be between 1 and 2, so the max multiplier is 2 which is reached at 10000 gold.
+Scout 13
Archer 19
Slinger 19
Dromon 23
Warrior 27
Maori Warrior 27
Brute 27
Bowman 29
Jaguar 36
Catapult 39
Composite Bowman 39
Galleass 41
Chariot Archer 42
War Elephant 44
War Chariot 45
Horse Archer 45
Trireme 46
Spearman 49
Ballista 55
Persian Immortal 56
Horseman 62
Hoplite 63
Swordsman 64
Chu-Ko-Nu 66
Quinquereme 69
African Forest Elephant 72
Battering Ram 80
Cataphract 80
Crossbowman 81
Longbowman 81
Companion Cavalry 84
Legion 86
Mohawk Warrior 86
Pikeman 87
Landsknecht 87
Trebuchet 88
Keshik 89
Frigate 100
Hwach'a 110
Longswordsman 118
Camel Archer 124
Samurai 126
Berserker 133
Knight 134
Conquistador 134
Mandekalu Cavalry 134
Caravel 134
Ship of the Line 139
Musketman 144
Cannon 151
Minuteman 154
Janissary 162
Gatling Gun 169
Musketeer 182
Tercio 182
Naresuan's Elephant 194
Lancer 204
Hakkapeliitta 204
Sipahi 218
Privateer 222
Rifleman 243
Carolean 243
Sea Beggar 244
Artillery 245
Battleship 269
Great War Bomber 290
Cavalry 300
Hussar 320
Triplane 325
Turtle Ship 327
Cossack 337
Norwegian Ski Infantry 345
Guided Missile 378
Carrier 408
Submarine 420
Bomber 425
Great War Infantry 434
Machine Gun 465
Fighter 470
Foreign Legion 477
Ironclad 486
Zero 508
Anti-Tank Gun 542
B17 551
Marine 645
Landship 703
Infantry 720
Nuclear Submarine 735
Stealth Bomber 771
Paratrooper 806
Anti-Aircraft Gun 819
Destroyer 870
Missile Cruiser 888
Rocket Artillery 930
Tank 948
Jet Fighter 988
Helicopter Gunship 992
Mechanized Infantry 1186
Panzer 1223
Mobile SAM 1376
Modern Armor 1620
Giant Death Robot 2977
Atomic Bomb 4714
Nuclear Missile 7906
There are currently several ways to install Unciv on MacOS.
+It is recommended that you do not install from source, since the end result will be the same.
+Details here - simply run sudo port install unciv
from command line and you're good to go!
Does not require a JDK to be preinstalled.
+brew install java
java -jar Unciv.jar
from a Terminal.(Sadly Unciv does not auto update when installing it using this method on MacOS so you will need to download the latest Unciv.jar from Github every time you want to update the game.)
+For instructions on how to install Unciv from source see Building locally without Android Studio. It is not recommended to use this method as it achieves the same result as the first method whilst being much more complicated and prone to errors along the way.
+(Sadly Unciv does not auto update when installing it using this method on MacOS so you will need to follow these steps every time you want to update the game.)
+ + + + + + + + + + + + + +A result of a Discord poll. Water melee units in Civ V are considerably underpowered, so much so that even on maps with water they weren't considered competitive.
+This change brings back an edge to water melee units that makes them worth building.
+This allows players to assign workers to improve tiles, and then to reconsider and change improvement or move them elsewhere.
+Building stuff takes time!
+In Civ V, this is the behavior of Hills and Mountains, but not of Jungle and Forest.
+And yet, Jungle and Forest can block Hills, and Hills+Forest can block Mountains from view - so they must be the same height!
+This part of Civ V visibility makes no sense, considering the otherwise well-structured visibility logic, and we consider this to be a bug.
+This is a discrepancy in Civ V rules - if all jungles are like roads, then they shouldn't be considered railroads for production purposes.
+The idea is that if you can quickly transport goods with railroads that yields a production bonus - moving fast through jungles may be possible, but not with industrial amounts of goods.
+ + + + + + + + + + + + + +Multiplayer in Unciv is based on simple save file up/download, which is why it is based on a free Dropbox account by default. However, a lot of people use this default, so it is uncertain if you'll actually be able to access it consistently. See Hosting a Multiplayer server for hosting your own server.
+Main Menu -> Options -> Multiplayer
)Main Menu -> Multiplayer -> Copy user ID
).Main Menu -> Multiplayer -> Friends list
).Main Menu -> Start new game
, check Online multiplayer
on the left. On the right, add more human players and input the user IDs of the players you want to play with. Press Start game!
.Main Menu -> Multiplayer -> Copy game ID
). Send this game ID to the other players.Main Menu -> Multiplayer -> Add multiplayer game
and enter the game ID you just sent them. They can then join the game from this multiplayer screen.Due to certain limitations on Dropbox's API, with the current influx of players, we've many times reached the point that Dropbox has become unavailable.
+Therefore, you can now host your own Unciv server on any computer that can run Java programs.
+This guide is written for people with a moderate amount of technical knowledge about computer software and who are able to search the web to learn stuff they might not know. If you're completely new to this, you'll likely not be able to follow without some larger time investment to learn.
+If you're proficient in server hosting, there's another how-to for you at the end.
+Before starting, you must have a Java JDK installed. You'll also have to download the latest UncivServer.jar.
+From the directory where the UncivServer.jar
file is located, create a folder named "MultiplayerFiles", open a terminal (in Windows, Shift+RightClick in the folder) and run the following command in the directory:
+java -jar UncivServer.jar
Your server has now started!
+To check if everything works, you can start Unciv on the same computer, go to "Options > Multiplayer", then enter http://localhost:8080
as the "Server address" and click "Check connection to server". You should now get a "Success!" result, which means it's working!
To connect with other devices outside your local network or to make your server accessible from the web, you'll need a real IP. If your ISP provides you with a real IP already, forward your server's port (default 8080) with your router, and your server would be exposed to the internet! In this case you can also use http://<your-real-ip-adress>:<your-forwarded-port>
. For example, if you have the IP 203.0.113.1
and forwarded the port of your server to port 1234
, your server can be accessed from the internet from the url http://203.0.113.1:1234
. Additionally, since the HTTP
protocol defaults to port 80
, if you have forwarded your server to port 80
, you wouldn’t need to specify any port. For example, if you forward the server's port to port 80
of your real IP, your server would be exposed to http://<your-real-ip>
or in this case http://203.0.113.1
.
On the other device, enter the URL to your server (http://<your IP address>:<your chosen port>
), click 'check connection' from the new device, and if you get the same "Success!" result - congratulations, you're connected to the same server and can start a multiplayer game!
Please note:
+* Devices not connected to the same server will not be able to participate in multiplayer games together
+* In many places, your external IP address changes periodically. If that is the case, you either have to update the IP all the time or use something like a dynamic DNS service.
+* To start your server from some special ports like 80
or 443
, you would need admin privileges. If you want to use those ports, run PowerShell as admin. However, if you use port forwarding from a router, you really don't need to do this. You can start the server from port 8080
and forward it to 80
.
java -jar UncivServer.jar --help
-p
, default 8080
), writing files in a folder (-f
, default ./MultiplayerFiles/
), so it needs appropriate permissions.java -jar UncivServer.jar -p 8080 -f /some/folder/
These servers are run by the community and not official servers. These servers may become (temporarily or permanently) unavailable and lose your game saves. They might also collect data like your IP, how often you play, or other data. Use these only if you accept these risks and trust the server owners.
+https://uncivserver.xyz/
- Run by PikaPika#2315 on their Discord (Source Code)During the generation of a random map (only; not pre-made maps) the map is split into a number of regions equal to the number of major civs. Each region gets classified according to its prevalent terrain, or if unable to be classified is called a "hybrid" region. +The region type corresponds to the start bias of the civs as they are distributed. +The region type also determines start placement and what luxuries will appear in the region.
+The game will work without any extra json definitions, but if you want the region system to work well when generating maps for your mod, these are the relevant uniques to define.
+"Always Fertility [amount] for Map Generation", "[amount] to Fertility for Map Generation" - these determine how good a terrain is for purposes of dividing land up fairly. The numbers are arbitrary but should reflect the relative value of the terrains.
+"A Region is formed with at least [amount]% [simpleTerrain] tiles, with priority [amount]", +"A Region is formed with at least [amount]% [simpleTerrain] tiles and [simpleTerrain] tiles, with priority [amount]" - these determine the rules for when a region is classified as eg a "desert" region. Terrains are evaluated in ascending priority order, so in the base ruleset tundra regions are checked first. +"A Region can not contain more [simpleTerrain] tiles than [simpleTerrain] tiles" - a useful compliment to the sum-of-two-terrains criterium above, if both terrains are in and of themselves terrain types. So in the base ruleset a large enough sum of jungle and forest allows a region to be classified as jungle, but only if there is more jungle than forest. +"Base Terrain on this tile is not counted for Region determination" - for terrain features that are unremovable or otherwise dominate the tile. Used for Hills in the base ruleset. +A region not fulfilling any criteria is classified as "Hybrid"
+"Considered [terrainQuality] when determining start locations" - where "terrainQuality" is one of "Food", "Production", "Desirable", "Undesirable". Usually used together with the "
The translation files are at /android/assets/jsons/translations
+If you're adding a new language, see Adding a new language.
+If you're adding stuff to an existing language, simply start editing the file!
+You don't need to download anything, all translation work can be done on the Github website :)
+When you feel that you're ready to add your translation to the game, you'll need to create a merge request, which takes your changes and puts them into the main version of the game - it's pretty straightforward once you do it
+Please note that Right-to-Left languages such as Arabic and Hebrew are not supported by the framework :/
+There are two special entries that won't show in the game but are automatically used to provide short and long descriptions for F-Droid (and possibly other stores soon). They're near the beginning of each language file and marked "Fastlane". See the comments just above each for help, and where to find the actual english original to translate. Do not overlook the note on line breaks in Other notes for the full description!
+=
") contains square brackets, you will have to include each of them verbatim in your translation, but you can move them. Upper/lower case is relevant! e.g. All [personFilter] are cool
can be translated as Tous les [personFilter] sont cool
, but not as Tous les [personnages] sont cool
, and neither as Nous sommes vraiment cool
. Failing this is the main cause of your PR's showing up with red "x"es and "checks failed".Like most open-source projects, Unciv is developed at Github, so if you don't have a user you'll first have to create one. The way Github works is the following:
+When you ask to 'edit' a file in yairm210/Unciv, these stages happen automatically - but it's important to understand what's happening behind the scenes do you understand where the changes actually are!
+Each untranslated phrase will have a ` # Requires translation!" line before it, so you can quickly find them. +You don't need to remove them yourself - they will be automatically removed the next time we rebuild the file.
+Do as much as you're comfortable with - it's a big game with a lot of named objects, so don't feel pressured into doing everything =)
+If you're making changes to your own repo, make sure that you make the branch you're changing is based on Unciv's master branch
+Some entries have line breaks expressed as \n
: Your translation can and in most cases should use them as well, but you do not need to distribute them exactly as in the original. Try to find a translation that reads nicely, then place the line break codes at roughly the same intervals as the original uses (less if your language's glyphs are wider than latin ones). Important: You cannot use normal line breaks, you must use the \n
codes, normal line breaks are not part of a translation.
Chinese tutorial: 如果你是中国人,那么恭喜你运气不错!这里有Unciv中文开发者们专门为中文翻译工作者准备的(十分详尽)教程视频。:(Video On Bilibili)
+If any of the following steps are beyond your skillset, ask for help. All but the first two steps can be postponed.
+=
corresponds exactly with your new language file name, case-sensitive, without extension.When displaying text, the underlying libraries (libGdx and possibly lwjgl3/GWT) that Unciv uses assume one codepoint in the UTF-16 representation corresponds to one rendered glyph, +which causes incorrect display of languages making heavy use of diacritics or of characters outside the basic multilinguial plane like most emoji. +A language file can activate a "trick", where combinations of codepoints that should render as one single glyph are mapped into a "fake alphabet", +which is created on the fly in the Private Use Area defined by Unicode.
+To activate this feature, set diacritics_support = true
in your translation. There are a few additional "settings" - translation lines where the "translation" is some control instruction instead.
+All of these are optional, though your language may show glitches unless you define some. For example, Bangla needs a definition for U+09CD, where the Unicode category does not fully define the required behaviour.
Each of the following definitions represents zero or more characters, and can simply list them as one string. +For readability, they can also be quoted (" surrounding the entire definition), characters can be separated by spaces, or you can use standard "U+xxxx" representations (these need space separators). +These entries, unlike the rest of a translation file, also support entry-specific comments: After the code(s), from a '#' to the end of the line. +Search for the information about the Unicode support in your language, e.g. on https://www.unicode.org/charts/ for information on which codes you might need to specify. +If your language does not need these, feel free to ignore, or use "" to avoid the "requires translation" mark.
+diacritics_support
: This entry must be set to "true" for the diacritics support to work at all. Any other value will cause text to be passed through unchanged.unicode_block_start_character
and unicode_block_end_character
: These define the range of characters that should be considered. One character or code each. Defaults to the entire BMP range.
+ All characters in this range will be categorized, those undefined by Unicode, controls or punctuation, or those outside the range will pass through and reset the diacritics engine for the rest of the line, that is, pending potential combinations will be flushed.
+ Limiting this range - e.g. to the Unicode page dedicated to your language - is a performance optimization, but ultimately not required.left_joining_diacritics
: Optionally define additional codes meant to join with the character to the left of them, by default the unicode categories "Mn" and "Mc" within the range described above are used.right_joining_diacritics
: Optionally define additional codes meant to join with the character to the right of them, by default none.left_and_right_joiners
: Optionally define additional codes meant to join with the character to the left AND with the character to the right, by default noneThese are processed in listed order and can override previous categorizations per character codepoint.
+Thus a code specified in left_and_right_joiners
can be in the "Mn" unicode category, which would put it into the left_joining_diacritics
, but will still work, because the later definition overrides the earlier one.
Before releasing every version, we regenerate the translation files.
+Sometimes, new strings (names, uniques, etc) are added in the json files. In order to not have to add every single one to the translation files manually, we have a class - TranslationFileWriter - that, for every language:
+This means that every text that ISN'T in the jsons or the UniqueType system needs to be added manually to the template.properties in order to be translated! +That also means if you've been adding new json structures you (or someone) should check TranslationFileWriter and see if it is able to cope with them.
+Building a new UI and doing something like popup.add("Hello world".toLabel())
is a typical case: This is not contained in json data, so you'll have to add the template to template.properties
yourself. For this example, adding Hello world =
somewhere in a line of its own could suffice.
Note the space at the end - it's absolutely required, and see to it your editor does not destroy your work. If you want to make sure, use Android Studio for git integration, but edit the file in an external editor, then run the unit tests locally before pushing. (to do: add link for instructions how to do that)
+Leading spaces on a translation line or more than one space between the text and the =
would mean these spaces are a key part of the string to be translated. That can work, but be warned: translators often overlook that those spaces are a required part of both template and translation, so if you can do without, then doing without is safer.
Translation templates can use placeholders, and there's two varieties: []
and {}
. Square ones take precedence over curly ones, and nesting works only with a single level of curly nested inside one level of square. I both cases the symbols themselves ([]{}
) are removed by the translation engine.
Square brackets []
mean the outer and inner components are both translated individually. The outer template will use alias names inside the brackets - example: Your code outputs "Everyone gains [5000] gold!", then the translation template should be "Everyone gains [amount] gold! = ". The translation engine would translate the "Everyone gains [] gold!" and "5000" individually and reassemble them - of course, the number is simply passed through. But in other cases that could be e.g. a Unit name that would be translated, and you could trust that translations for units are already handled just fine. Note that uniques often use the feature, but it is in no way limited to them. It it makes life easier for translators, use it.
Curly brackets {}
are simpler - the contents within the brackets are translated individually, while the outer parts are passed through verbatim. Example: "+$amount${Fonts.gold} {Gold}".toLabel()
- note the first ${}
is a kotlin template while the second pair becomes part of the string. It tells the translation engine to ignore the numbers and the symbol but to translate the single word "Gold".
The [], {} and <> bracket types are used internally and cannot be part of a translatable text. Use () instead.
+If you can run desktop with the mod installed, then provide at least one valid translation of something that is present in your mod or the base game in that file. The file can be empty otherwise. Now run Unciv and use options-advanced-"Generate translation files". Reload your translation file and it will have added all the necessary "requires translation" entries specific to your mod (I repeat, works only if there's at least one valid entry already there). AFAIK you can also override base game translations, but those won't be output by the "Generate translation files" tool.
+Here's an example: +Say you have a new nation in your mod named "The Borg". You create the translations folder, create an empty file named, say, "Hungarian.properties", add "The Borg = The Borg" to that, run Unciv and run the translation generator from options. Reload the new file, bingo all what Unciv would like to see is there.
+If you're modding on Android only - don't. That said, it's not impossible, just make do without the described tool and add everything yourself, test, rinse, repeat. Be aware that the game does not read changed files from disk if it doesn't need to, so on Droid you could either edit locally and force-stop to ensure changes are read, or edit on a github repo and re-install from there, or...
+Adding new languages in a mod is not supported (because the completionPercentages.properties file determines which languages Unciv deems as known, and I'm not saying impossible as one could manipulate their GameSettings.json).
+Remember, exact case is important both in translations left of the "=" and file names.
+Sometimes you'll see a English.properties
in the translation folder. For example, if you see gold = credits
in English.properties
, It means the word 'gold' will be displayed as 'credits' in the English version.
+So in your translation file, though 'gold' is already translated in vanilla unciv, you should sill translate the line.
+
gold = credits ( <- in your language)
+NOT:gold = gold ( <- in your language)
+
Most Base Ruleset mods contain this feature, so you'd better be careful translating those, or you'll make the translation work really 'amuzing':D
+Another thing about translation is 'extra translating'. The aim of 'extra translating' is to make your mod translation closer to the gaming content and give the players a better gaming experience. +A great example is from @SpacedOutChicken's mod Deciv. @The Bucketeer made some 'extra translations' which are excellent.(link is here)I've got a few lines here so you can take it as a reference. +
Your warmongering ways are unacceptable to us. = 即使在野蠻的荒土世界,窮兵黷武的行徑還是無法容忍的!
+(English meaning: These warmongering ways are still unaccepable enen in this world of savage)
+
At all, in any way.
+If you want to use an alternative server backend, you can set your server URL in the Options menu
+ + + + + + + + + + + + + +gl@puWKurXTwEa0 zCesl2So0HL0xu+tQ2?rXFwzI0Ems}3Zr{w-wRb)+RLy#RS3}mMP{vEHAnyOs@ibd^ zH#??}noy|i9@uVi{WCDmZR_EhNI1J^-PauO?mp%_pxTpr9jf7>)x#z%*l@-1ZGEzw z6Z7>cJRnb_t_a`*%20s35d~p=+VQd1F$Ki^m;t~%-;wMdQ$zVu-z^lP9nQ8~1^t4P z6nVLA(8cFI$J*BZX^m*S9L8p!FOX~te--9^R~hW?5{zPucRW84qWl%`waap6zd~zY z+@jaH<>ERAa7yi&LsO2Dz^I>eIa#RNVK5(Sj`|bJ&VrxS+$mO$nZtF%g?yt`oh&k& zlY`|L;73?fXeiX0#$pt4{bcxsRMOBf z#)o)}<$@BN=4l%{O%_3+e&a%RsX34ue3&`bu#~N S4Hc&ve)k$QJKNTY7;&k`_!Pnakc;%R&E-B(Ss-UfY> zK5AsC0{mj3t^DL;OMA><$BZ46xiqeAKi|l)@!{Oq0A=V})b28BN51Gi==q*NbjFbg z@h4e7wfK|E#$k)4Z4d`nN+LE*eoOWA@ijCrE*1tYxXuBHn0}-LOt6#g#@w`o5NQFY{F#;Ind- znk9z?m*Tz_b+w9nUvNh|Do^~?5qLe_ec*kkVBkw>U-legU|F$WxNy}Kn-=Or`_`7-bZWyqO~i3YqptNHz8+phjI&rBfYz+;SXy0 zX{^z$J+8G1Xt4BTzqa1Z7H!H}1to;Z9$WM$K4E>B>Y)W;blD$mqY!2PeJn4C%wxYm zfzqhk6}^V{0`0^oJa_lA!+qBnjE?J~bXAd&L;KndpzGfo#6Y!@sXW0*?}RXKHs623 z|AG9hwC83A&O;GTJ=T$LIMt%P34|s?FWr51Yu~d2r{G%-$qUtiH<^;cAD_$oYXSBe zKooE5o`C_qI;l5lx9Kwgv3Uq{0Gh-)Wy8JII~Q86ykEb2l=JwX8wkA7#7TJ=zWJbE zJwjQ}Eh7Fk{uaol0iPHJNMO&Oo-X(kTePT{k 8+7C6WwB6U0N~8| zH z45$n`5J&MpzWPGtAi9scr1?hWC+3v)f3hK(yU0IxHvV5x^B5S+3E16otqo%gG@+zy z{{q5AWj0GUpLE2YJQ3oBI2?=i4dJqWY<|bf+u&YaUAbLkeb8(u-7-;8#AV1{(Nf-E zb2yQ?97%tEKS)X{g;J=cWkm{iU_he$DxzL~3FQInkN!xEy0Cu{l?l}^2v`iY9HMz1 zwh{5#H;Cl2{?+0g(iXcQ!wMYCYywMgj1P07b!7J5Q9SXW%f~XdG|Z8iPEb2nvs44H zn7d|OrHoUe1WP=ce`A?K_oLP-{fy1CYIL`Yk^sz7>M?JKmNH;(2saGxVM_%8$Y#JG zTe(IRFwwY=ibS+l7Vkegrztcy9+nmbh7^-g^-3ZGjn=}(mx@`s_9K?CYa?TIU>a{k z)(g`TqyoY&&Csd)@@7|IbONJiEMf#~KHLz=zy+qVc*}jbXN%!eDsgUa-kDAT rhFN!iUNLwtv@ 8 zcXLk8ZxMx8Vzy^|uR@FWG}O-*8C}a~eg(rTfU~gjk(3Y=h!>zHWz1pdvy#=C>Jc$+ z>w+t8x5x&vb?(IXQ=wQOoWJdP@Ng3TRb&ZBP%pQZ2>F07*{v~@^ZFAfRYIdd$ C|p;uG22Dphug&tjluZ=VG7Ii#^;-5!GfC {?su=+eB4X;r7_}mNZevI!Eqt= zh~7X%6^0uAJ8F2*dq(CWB^`FV;!KJvZPbX@3uX)~7Q1p1G#C^OLTa-_{VBV1_t4N* z{&x0U@(`~xXDsMM6}qJ97{T`aZD*K4Ssg~}N3?PUs0ZdC*CF;7#+ zMiwFor{#w1(v^?4%=J;eo%QAELNVTayXG#={<4~>=(cp_*`?!4&stXHg77x~dJ@LY zF^GZ9NPCC@WQ>$utid|+rOcZ~L_KP=_sRV~m9_ldi9#YIC{OZ+64*V!RV#TDL-Lxk zt+AtIDYW2Os msA% H3UTRfhO&T(eoQP4f&HDmHf4 eUuJ(#W+0GyBz%ux@=U)nG4 iwfz)I`72-Ra6&XaG >oTD0Fw8eicY}f94>~1BN&NnRAUv|y9O_G(MXcyEf&~#zbCAHo(pt1{+ zQ9@MzXx_H@UQ(j1SvWhb1?}XzXQ}k0gjI{?pBvcrF)&88M|L!zgQay1Y4G9v#f9s% zH&J}g2gSnXah=-zCwkV8%KiR|ztsZSBfaT|1nbkmS4pq1G eWVhN-@s? zgzkR@;a=H1m2i?BYvqs0jhvyxGSq&79M0HIgoN%W0MZyembR`5ug`V%TzdM?jq^my zC~uXbD^inNtWQLaxUlCMQQl%#;^qmg;q4_f`up?y$oFHq|FTz%WZRNvBZ@If)C?wO zx)q3{%SuDmq|-5DG=}&$|KkST8RIN*H^TeulT^;WcTMe#(8&lqg0l4R`t&o 9{a@T^}H=eyy9UQ8~T zxIfBdwVx$Y!dDZoTbWS3YD}7pZpYU Rs zl&<}QIJ`Uae}mTce?o#9P-zMA-;88OMlOqUgC81p)&H}Y(~pm|qXhD$V674o(Ix?2 zw?}Wu>`)s1m6Z*v{7yn2P$3mDc}$7S6BbE%0&8^==0!Z6I%rfZA$He%oHRto<;9kA zap-Ip2zTw|d;bj&V}GaFc4Ze6Mzsi`ssYSLe}S_SDiZ*YHt=xI>&$m3o~8o<_(W}| zi%6Mh#vTqXJi2?R03}&2$CraXQr~KIyOZp9cgx3;CH3xf=VY`wsX8H?31+F@MZs+D zz{J-&MgKb@4D^f~7MjB_? Kr zii)^0^q}NXj)gQ2W2u?#y9!0(*a3iiv8@@lu{F+fuRkzB?LIrDPAqTP*3GBcF4Bs4 zpUt1VZ&|4wGD#Go)n=KHf(4`}%TdT`hpqYSRDT*Q@i=Y3-p3!om2lxVS2+6s31N9= zIplAGyVc1&h{?_)PilVQ)fxyo=+bmvxLVZtS!DYs6`iah{}Q_8*Y2sLZ&eo;&*<$0 z`FUMY6s(dHI=3KX!9y`k_70z8adjlP)yiLwN3wQ3SPepX +faR@>VrR*~KmB~=zg2{@n z`c8m{I?D@Q8RQb%jtyUr&1p@FrNtv@)avgSFEZW!5>lVV5v?6Gk?hrAe1>dSgS2I# zyA=-vtCi`jD^Bn!s=IJyX*X`dToH>aW%`X))t}Xx`z~J<(hVu~N70tE4=-tS`)UX9 z2p66t+u^oqGO|3bWR?6**34kEmTWeo!SXtgrI@Y#%kOvjCmBtGWODH(_Nkk*Pu26< ze|kKqZ(HnVO|7DNF3AU?6l*hrewN6M8rBa+Y?W!=lXR-(S^GjV;gw68>+M(!jFH(C z?_qtn+=q+!_%#sJN;!y&+m@D&@Qfb(>F?>4ttJX+IT#rSLk|;Sw@9G{giXOyC3VL; znTOmdmDZ(1ieiMTTL5RH+S}c&QPXP+0WHasmsn(vzk=mngHb%?I|=!%kq|rNfdn_k zJfuj50luqTVJ)yFcjl9vbF@)QUFYQfS<~o;H*6v^z-`1Tu)8?2y|p}jsKIpFaai}g zbDak+6TWICw06+^4{f)keU!O$Y_6!=F$JMad1kg87OG}9QlI>-eL!v)ny=sSIFPwf zw+J>7n}7v=C8Pd!&q`h78O6wig=ze%96n=A=!}LD5Pl7+NA2lPb6l^m*zmBkOx~+p z>*imbWS(@(Ql22Q1r^m)HB~b|49t+;J$*~F11yPjW9`b)b?j|$OnI9uq@la?fhxYV zqOCuk&5&kE$Rzu1mSV3 l=GCdWgU+{DdVw z3%ccHTJAp2dRSS!5^`{-Z{Q7e{zElyFRo%q?>joUqRye4gy~glXdu=%v(2JMyUyeBk+wQ*+zNpf0)p zt(s4tEw}vFzEhy{?17OZzvWfwlHW^2nF*X~r*!X~r-J|$xgVRp_rAMgdFu49uY}(U z-{@>?Mq$y!_h{sk=$WMR8!pAfkB5H4+uV3dQOaXsqF~Dlzy3@@E^`vs5yZ!$M)s9C zv@Q&;X3lu2 my;URqQjp&~w-T`F7``O0jb~AIs>t zWr2)(UNxfNY5zC3&-_T{MC44E956Ce$94(}?x177REC2)=@re<4}D?r=0@dc+Od8E z7F{a;F%?Wf&B!#la8KSQ?M*Zj@vi0@ug#*HMsMl-wi~{TR7e(Y{kymKUP*GsHS>BL zv1yy=DMm*qJzv%DNvt4EuD;1m4G`$qnbg5xF`tW#r8Npd7HEh8SU&upU PIE{1pmh{%?6cSGA#eO^ZxqaxG(RR-1w zRJ``Rt>k@`2q6MrPt~0&U#&M#HA!miT(Pbf-{Tf%1I54S5sBHbj`cPVAPL7JbGv >{Z*LLN~i)nfB; zCO0mW {Xyh7ppHW6gUGX<1qT zQrPS?=ex3;9;mT0ugisNBsff`keoW)I{^f1Ino(_3$h_(z&gcBTbIX&X8#316T*jj z^ZXO{!MUdAHNW-rXTGgFOG)qMD9^$w*Di}0mB{BSk$Y svU(B0f2Hv1SIM*y^!MJSWFB|S(A3@ zo#)P4UbC_-r-Rgi)el!{u3;k;Ee*Y|_Sf&ocRPO#mF(l@7R8vl+;1 qDO2WbSta?&o5FP@cu( ze_9x%@QCxG(O43%<@g4jljo6zh%1p`fzvz02~DI7Lm;R}B^U2R(@MR!YM*4jdqLy4 zz4#U42yD)w!u3q&3+W}NetrEQ5>w>I0={(H9$PxKBKiERRN{0Lo_ya|s%&n}^034T zq_tkAtlX?G#JPxqQrOwel98GPs#=D=mF}cq?Mk{=x&UqKVwm3wdFs{|OZg{hoWG#7 z{Xiu4goW?@SQE;F`y-W#8u{kE5eghBqXe5PhBX$KufQ0Pl~~6q#VUB{ z*5^LYPzL1_gV@nQT_FcYpD$t7K1H=j)$K1$` GGi IX}-MF&r2rJb7h-?{F~Gbb%T4TXsP zymUg (QzDj<$7M@$r{wQ(R@U;C|IMU88P!b;~EKnQDx3Do;iZ-zLCprm*&-A6@+>t^Z^X z`JtDYGObcof)!y9&GR(Xzh&yMyjF{G2hWWbR)cn8b-5tkBjOd1Bn6P0{1W&erDLr* zbt6d_a-%&U^OiH-$J!LTysAQjT`9+mB$c(ug1^w8IEm=rTO>*Af?6p+Z_;HJfpedH z ft4`C0c&wJ}gWee=hP6-1(`skdPMi;Bh=$Sve_uVW&E7K@NE7GgVy5Zg zl6dhj7G{XmBs@QM+~OX~F9@FjDC%mP_`T$OW0T2)kMPIyGd#>!H7PiOn!(Sj)`}<0 ztfNW4FGPppUL{%?DjIds1rwN>a5If9X01K!rYDhL-^2D$hdT&dBzch&^AS #hvEHjd znw|XONPX6?zc2gU=_T%0Hx%2a0wo6~DA~l;r1#BQvL ){QQY3mIeda zueP??T XlT3pe+G+5=GkNJ7 z-;2>liMNXIiDkwqg(yrCwLDQRE>XCzDA42tPUEQGBjYNWL!B!nOwU=Y4ZQxSG+of; zGcj<*X;lB4{>jU;1>^$3M?BK$XFwdb eNIu8Dmjk zoi{#lQ*Q;g?h9~aK@MRLs>RHni=wqO&mgYS)R+VvZ&`~jFYI2E=rgeClLPB;2;G;z z9*Lsp+$3gW2m}CzdpCc{L|B!N9(NjiF5b|TZ6}xO`8OR>AqI)3(5oIDGzMYZYN&@{ zIB`)x!x5~PO&tIjPF19+)er?f>6O@GrorO=fb-C+a#dy!I%0B#CPg5CZ*%nkWK%m; zkNEwjg4`h~2&{vp7DxgIwAdlKswd%qc(g|8e=^Bys3M20YyJQD(NF^nR418oWSo{g z)eDVquZDdPyii;;zo|9PE ;v+l`mYd(Ni1`Vy %UiOc!JE&E6`T`4pf ziJb3dQ#0;Mv@*}L8o&^2KdE9;n@r4&%bt=NrHHQ-UY%0KZ_mlG{Jh_;DMf!JYRIoX z?&Ni%qrk@EMcSjQO%GUjcr7|jazbY$o=pv;#B}xhkNx)gM8ovKBmb1`PY=ufNqNUr z;pSv6Y)SRsO@jxR8Y>xJ2?!l!x2-*!2kU8S^9U~lE2iXKKVRQ1I1ES6U5Ua_{v1Oz zPP}Gj(j^?X>LSqj6PQY1|7P HrnhNspnPl zXRNysV=(>Q$|`@gva|q6-n=-RU$T0UDDGZ(^#-;oF%mJ*Q(Bugt9(3>2W4NB_p6Cj zYm6|{jB4@h9l|_YOP~EX3XM!&+Q!*0Chy@RVIvU#-nXz5yy6MO57O=Gs8+D=|GrK{ zjAJE3nG|kYR+DA3VD&D|VN`=@@o=KON<&-ddA0cv$Xx!riKJu;7^T%FP9UY;sFRe+ zttw&Ou267zZvFaVpJH5C&8+&TuQO+GO5yM0@wBX!TdAOQ{j9_K`m;>J-cSNNV}=$T zD=TIfx03~YJ{D(YxOndm)5n3!kG!?nF{ Yf1kh4Ef=!v}ZIj2R&AcRg z<>d6^gXNa*Lj2_kv$6n~SGu#5%}|uw)!v)B+=KMH2COT2xUo}`QmN>x@)H3tmF~O0 zEVN_Tv$^?5jap3D^o6F=kqMXA4e?!jcOL3AWL3b*-(QUgf=q4BQmr`qMeL>h+SfB; zL+pD%#weN0l8@s2IG1IeXZlf3=sT17v=IA=9?YLla&~)!(!%8x2=}|Z+kl+wlB-Dl zpEIY+3QUXSJX|~e99i9he+*V(K6Le*Hg#8pOP0$D)IazxUClB^L}cMsI1^c+rC C@na(aQ8Q*c^w6>L1>MOok}`%(t7gR%NMaE=YjCRs z_?j8+IjJyR9>zL_IeuUnj6ax#a;T9d8uQ87aCAGjvM2)4Q8!IBCLvyr>nh-KF!6nQ zt;w>G?uGiEr}%P|n&6T1X5e(Fyk*AyE !BvwIibL(g`fz zigUSyV@B7UX<&B7ex%s4aRh%_MeQ|)ab)j<3r+oAvDuPiS9Sl|cs?x-9@JBA&uqJ5 ziQAu+J#!5lE&m{Y@8REEw6h{_#&_5k*(@;DIJW1Vtzj7;hw(#pO!?5g=jXSAEcEQ= ztnyT+)w0If9OWXO5uiLwcH%oXBHd~s) C~!AH zxL#*RZC!@lc_iP-*TpZ$ho}TYy12P*@iXHO)e~khpN;^QLA%!m6Gvjf8OgpFpp;k; zV;{>mLK^wdtJuf9wyl12mTW#kDdFTNK4DhzoT_3EXew DyaDxO{{R3v=X8gp+(% +(W#S&gT1uI*g$6wcIijv`B!APUm@v-y3B0-l(4);TRdE2cL*yuVYbl`SS~ zISnT#-}VIO#7vRFY1yg6!p#kQtGS;fSv>L(h3j+hnB?*I-Dt3xyJi9_t#Y|=wBy^Z zpGK?1Xg;J7OJH{sdj5rHQG|vou8mrr(#Dvr=<9;UL>TkWfKjEL(&3BercLi%Ww^MG zM4Q$}1r8O<*enFb9~AVrpA5>?{*w3XanY^bns93 6uR%FT%3NnjLbR zr^iCVUg(z?aZ8hYR5xE@p(Uj8rKSvy%h>U9vgOKCgrxiE`nQP-wiOdQ4{r-I^t-~z zYu&D6bl&{=wNkPZ8mE&fcQwi+8%vI6HLG1idyXfV*1u}i*z)j{DGv-!V$}O6cHhRT zF@M+|K?;jz&h CH*lRVYax%=ZLO%`b+ca>-I`{CqD(MZ^MiI+tv{uNT1?WwYkP zFTuecc*h Ko^3K9Td}c&1kDR%6$TD $7@#IDGoPYn zr_3lyOLvq4$=_!IU84ZLG(iOUX8kS#e(#o_7=ey&+j)nBc>uix$=K2gW|uZ?-h1 zUE0*aU3RI|!OlLE*M}R$zaIv2KKR1*J3-Uu3$vJ%rwdt;pAlu2%%s+K8TW>%_U`xG zq!Ae7Mj@HzcSNMPpuz#lf-IyBKpO)dR2H5B%7s}M)<0u90wx!$|7!2dCo`kjku;1c zV(DxeqR~FyuA~YaIeQagE$6SJe6r#K6H5w5Rj@Hl%TBk_S^d*oqE_O++sNDRxzSwr zwgn0CPa5b -s;4 z`^tc*y0%@^XHW@IDHRcv96F>+P`ZciP`bN8K|mVm?uH?xq(!8=bLftthOV>6c=UO{ z_j}KG&iQftr|#K%uh?r{_kCa2wb(Gg+Aqyu{foV1&DX;UG^9U`7lke+v7|Z#cr6t1 zbQabz2v2*G&b+rMDH#b- RaHngDPK}*mojG_Oe zgXWGk1MUMiXT(*TuNF7!avXioaw#{jqyX<7U~83#mV4r~?L}%viLiAeA)<8TT^F^A z!!e@6h$C_ei71eiF~ciUbTKI_MPFfr#Z?42Ddw0fl)dRL(SJTcWuMsFDIj3uSx%X& z|0Y9gq35|nXZWg%{6_iRD7-IdKdUr>q#{#yFjp{(M0&9 0^KR_0#6qTm4wK( zIK4k`s!>2PN YhSrb= &jTq@MjdRQv{z8z9EsKT pQSTENPa6Eawe(bFmDJ;8--h&q@&T>|oHm>ImWN+>*%wvrM`^ zHXO=y-S;N*j@9u0o5quSY1#!bW=NPITQkyp&@X#AGCQ&MLR_ zXh`B)^^r HWRQ*@ZlXFoDZOC#T^})=duGZ#&ux~m*koKWxyu&%5G8lXuLjG=o%>o)8e!40s zOW(r8cP8as!O12N37(#?Q`0jX*Oblkh2EfYqc~-K?8{I={kJZf1Bz3-KgZMmuEFh8 z{khI(+~V*$cf>rc;am7Z3{K#DhY?nJvRs7dTvVk|S~JCNLk^EnwRV=XMJwk3*IKi} z^sA4ba7?L;^`UY)C^yX|_7!;#=Ou^Zs4Dw4%g6}47V6vW`UNbF^nTNl!aN7 rA3abj_N=5iL%Inq5 zV44uL5!ZZ`TXax~TY+|#M KJE%W+`8gZk!uzPDngeN<(G^mlKNT`#--Ib zc{dH{kOE(1K^2fMX&~iG-bl?AIcP~0*E{;J%PGFkbnfECXUZ9r;S*|!<*(^Y<0Q@~ zr;d-q7a`FWkXFD!%G)dNHqm~t$R4LKU(Yj;S;C})ZMl@4S-IO0&~8?@P1Wcj2m4Pr z>Z@w~0uLr5Z@()cOAz|%q#x~hN
+ZcCX_qp*J+7&qf>41 ly>%bH%9}vfDR-cZ~xR*rS z!LbG#8uRz Xl{#zv>q8}frscH-B4hn*&Iu+#t9i3ixTD`Q zqL=srC&VBd6S-GI5I2nLxnsHY(B ^g x?J!A7)|Xs zvCRJPv$cl#!*&_D@k%;D<|(^R9eJJJqpG4REbGq KKL@84n;1H48hx|z?1r)p1a#QQ8Tl7w(g`AUx%+n|xM z3i<=)zR2pbgj~5rdx8K1|9p`GMGWy;ehr&=;=18R+^4+phaWvhu|jIY+9xq{{_3z0 ztsiDNS9?Vp%H8Gf1Yx9*BVuuT{6^n>A~er}^STqG4cAoOLyIEu{m8t}RwL^qOE+y+ zS{A=vv^oPkBa~uBsRM)871**Qmy!LNOfE#y^q`CUuX5|{%z$c}^RDPD?gu~0!PorR zw?zfnF$Wd41Dt4;Xi{Pl0cOXpe{ wHCH(aeI z)p<48p{OL(H26U>yOt_VT~I-U^r$Qv9x4&=Y;ESvRGjy?F|ugzv^xMZv)~5BuxfY8 zN!_FNvUkx6nqlL1C0{-y=4kqmDP$PMJpWXpA3@o{J`=ItoQ6S;z-r;^5gs&UHN z4nAVGFD*2ME`HcuceT^nMgVR=n!Fk=;4z#*DpjO_Qak*|ZaTtGKfFHhEax75J}M+z z0p|u+-9>d`9FFN;=ge!2iCmFF#JNTzLD_X(j!_q}X>Z-E?H+Ac0k#d;+JPqQ7;$2Z zE)nhtH@ABX+!SR5upI=%R<8m2JCvL@L0=|JnM#ab0eqC{O8?aP%#I5bG^7mVHnYN1 z)x^-qOU**8!K>S7Qqfsfw+2MoYh=0*=J+kG4uf{BHEfUHecD~yAI4J_Ld)vPI{(U# z^goNi|A|LeGgURHNK*$yK_4--s_8XRMWcA15Va-%$yTZS`iYH PN#bYs9D) z{!)NAB$A>-ip%lP9EjA2rZh ;ucB8)l%j2{_O%oOShJ&&!u*A-0e(TV1`HEP>%ZY~oC4@B@B9M-*Vf`^FmUs*^w zO21Fr{djV}OH@= -W@4AWIaLp6@7wYpG|0DZnK`D&8QSyo2MK#j2|$y;AG9Cx zwnHGT2K?>qkE@aY0Wg7o;LY!U`M)=ZTVDE`EAw$#YObq_=0_^V$;c}bNdiYQ{^RJq znOA-WkjsLv5AusNznj%PSn(F8r+*a^jr>`BxaScs&&ggVJ-|bzT->I<8O`f#9af#- z?DpM|LgtIjcZNkpaV8SW=+-nN6>ZX_w5w-PfI33+x#G*vqIi#!hI9V5Ybaw 06eOZS9m4T$PBQ4Y7Gl~h_%?b8?R|A z%{qZ w6$D`>3ygdyC$fDM6WmM~Yd4j;g*LAMP_lWmwl3ugQm1L1% zMI5byin5(A(mU@%^#&!nBpST>e4iME88m!%al?m#9c2@ZyGVO zY=U@h^tR|L_%wfAs(^>;JMtfQfYI +l4-PfSz_xRI9Z4 zf!ZmPaD6$d>d~X_vrLZlw<;!l#`x7$`*yUYvw88s)0l+5f;T=p321i(v>4yovunbB zjA9Q;QYzlqOtze8c(R4Ze}LP+pvP`Q*Eyhd*DkL}!OKbNqhfNP7{BPHq-y+dW?IhR z;(<_6@I=UxDmv5;ROIxW%xL9JU`AZnu7=D-IMZ+@Je|7s1)6b3W=aNwZfkFnNqq)D z3A Bh+RV=%M73;s|0M?hf7_o5 z?n-?o4St+5lw$lLMt-D~)h4tv^^wNUy>fBfqKDBQ)Gd_>=MB3{?7fVaLB*7;(7$x( zc~$y^*#vAzrKL;>9&Wiw;48tyTAoLeg(*5vp}L|_I{crkxR!6fW2AqS|FmrsQ`!<` z3>P`^N jz3U%+u(BO3asA7kUn`|vLfo*MH!ZXymEC5nlR61 zz;J45i7fVwF`vDeQyM{H7Z+eOuqI#C@eG`^kV3 V*{%Hig9`Lr0`-CTiipR!^rlHW1rb}; zRA) Tq;CZ}^($lY*PA 2DR9nJc(WFa-xP$x}jmG)56VcXErQbr6%z?K@iW#fLtNsMgCIq zlaljI{B0izb}Wf|Qtqi@s!sQ^K(2cySiZ8_fisCc5l?Q{%Ow!$lm2ehYrv%ntMuOO zx+rC0(^}nJ*nPKwX=8cGY#o7@=mnUO(r0w$Id6=aT8@<5LM5=Rsl^*rg|o!@EnbCl z3=$Ce3+3lvRF8~ytK0F8X*~3=`NvnNKc9qiM*(1vj&aLLC4B9ixm+qW=uOt+YYNMb zy +JPOS$DpkPRImN_h*^YIr<6g=1#tI?yHZIJ>PU! z!c$KDS9h5bIHL7zAeMwfWgW&{CYbgCcOUGhl|1rvg-MKuCtb`(>re}zUH`JIa$F0M zH(V0vL1*AeT5qk*fOPWlOk=Yv(cnl4C_5T8!E~RN6$`HkGmrP`;bhFN-Tr$Eu$M;j zVN;D$bT55g#DY|4{9N{!^`yf{#GUWfScEx($GDd!S9h>BvgV08b;K>)4x5V9L@mVb zny9w8i(sLImhL!_J-3h@2+weu#dyM^`8H7qZ8@YsW*`|B(95=cJAoPxjUu%;!H1~H zKaZBLq3aEHugQs^p*~qG3ubt)d(3709_$e--l2$=X~#q2#ef?V38ZEvjtRG#X}%19 zz}l}3S$X%nOI9*d+>WeSmdjCr!r!k6owX@?V${AZF>~6ya@vKdbzO9+rnLmlc=`sb z*>o^$gP8pq3O9PPl4sqERjHDT{clP7xiK{xMCM7l?z R!=R`!U+%SiEj0fFYKJf9Y`busT2f@x~*jEQH8r=R;zx{b`$2`-BJf z#WbJ*H*T!o>s-wRNcL^j-wRN81P^#de4s*>ljCINK%1H1Ql`(p*`Ru|2?gak@MZ$s zv(*%l4Utb|q+)Q144vG!J}&K_l^R`~Ec3jxX~vl&?M!#t4h>Th6Q|{?s?b|z4!VT* znRcv=S{i>HG44msdP&$2V3`NlEMqLr$b%oaasBZ+Kf%b4?;q%{!`X^tVV@gOmtUmA zd5YYck2hJtV{VB}6aYTL0j893u-9D7m`6ckF_}jmP66MiO+SN>R YNz+?M7NV_>F2*<42;Wa&mwhfkAyYf| z&w>1rfBLy$0KL;5OjAwU-g;RuxQLTHsv>x_X%Au8e1U?(g2!8Q&M^?i^WpquJIRSX z>%rm)Nk{>_WP~VD7;TjMU7dly G_y=9M8fEImlOfj`Uv5N_i0=*~)YS zp`^4O?^C(PO6KxJaEI|Jpp6izY{|iWFG#F1EW5GCT(WLaH0+$`X&Rs0K4ID)*)%t= zXcx5~c?KeAAHDkg?YZV?4&MGpE^<&jv)`~W8cxy;7m5G&EpVRXh5@Qg5LN@@^U;eC zgllsg&XDfmV`=G~-U(Nfw_lF;^BkpySHhCG_OoNdk^}|;15*{JH)LnMNoVB7-g50W zHu9ieoH-mk&2;+-@4C&s!4TclNX;OLj;5z^EW2wR*&&&C)yC50JMAcpxJ^25b6BW1 z*%O0&;pG&eLJI3pU83F_f5RN~ACbyWR0(#0uzxD+U4^%s&$Nk2Z>6@B`jbFpLc@&t zA%^HEUmkd9W-b)C6jNT##_)AV$oCT}U|t!9EfSfVDPFQMeDbm``QBx~itv=C%^s7! z;oLNKt&lM9y`Z656=`R(FdC~Tl!ex6$gg@?@l!HW{N+e6c52K+iL5YjfH8lt%YFB6 zB>E#V18_9UUnkWnpDC`PnBGQWOBKol``~|q)_+_&cl!=X&GlaDF}p))_ARXt`+&b! zmw!yk|Ab=x@j3m6?es@5^uI@w|LErb1QmY7DnB=Zoxg6r)p)mpyUBS*k#JZfTqF2z z=hgSfhU4E3>38MD;_p@K?{*DutA77k;P3uUc=ubmnQaLeVQAT}EiUf6L>C^{+{EJJ zb2@AfqO+-G*u?&=Hd)q-QU(Dxdb|InF(4pz$*~G>MgFHp9of)qV s zjLn_nW+fj7ylm#{*J6X;G~A!ki_N=dG$3!_+Q!U~q=Yhyb7Rtzqcve`#>`^kr4YV) zY^I>ynqo8HcNd3=MH+7V#sRSMz9| S7Rm%t*TVES@TK=Ouyaw`G-UsR)|GBDT~;#4h1u3R(~cnn=C@xi#T$We!SIq);_RX~VR AQ4dkkbv&|h^kF=doyQk2`S62*kA1{=~%z2I8j^h#>|-IefnGb zGRqmGgr*7DnDhh2ih$=Kz7|GhmgU9H@!~3fn|d28Jv=--y(SZ{6?3E(*K5n+_CSN4 z^ZBa23mc34CwoA?sdm~)pZsK+YFS)0)dZ#HPXD#DcW``Zc+sbFGlK4{aSO8! HYxFP)!dZ^Ml4X5yD=y+8wKFWfhfCRp-_ZO>DWXY(m1+1D@lC=7?t)UI6v zf0q6*MnDlke; zM$j{~o7Eb+NzpalmGwpzz<7+= z8UPATMuW>#BJN_bi;oM|7uFZHU!D+Sd0fOcPm44n_M_)Q_$lBGU~8odBa4rIs2BB{ zMHHSm>gc~Tv|z%Vob|z!unaMJH^MT6^esGeUnZ2KpA!UkUyR@FMW=_7%d)vL+DPre zWZ)&KGN061G`&p)@VE+o+b?usWn#|bEXOJ>QiGdTA(>p7GLjgg8MA1QH+TO?6|$TY z1zuyve9D0Q5MfmM5uV-B7lY_!h>C7E@r*M~$y1BdcRf3_3EYE|L }Q|LuIsK zb~r1q`D95I66TTRtC4ERki86@#VjNSLFs%oP_X*1)rdPn>jn);?eAA^$=+(8_o2p} zESRG+bzu1-%<5)T2AzJhj2cf^HS z+U3W{q1p3IZdAO3_s_UKcJ2P;Qh#Owdpgnm0yMV!k!NI(ZrDr}%3sNGLYVgRJx*)d zk5@IrOz$|1=o5&oVX!@YYxZ{aZKD2FMvA5~B2#iiONmu|lJg+FwW~tmlVrn3C$nWh zQd$%6u5rV6tDdjEf6~r+*z9z d2Q0K4bK!D7(_ebZ<`OJ?HyN`y;N&ui#9i!{pNgR~omH#Blw z$U$|irEbM*Ms`h!bRaW=EjBlU)(4EM7T#`ECWf xzugI0!DWV?*f`OX?ZiDaGdn^X@~V%b#yvSSnEyU<5i+e`rMHrHQ%&77NkO?N7Hl z!X2*7ssqzOsx%rRBU1CGwEN&}d(~%SNhM__z$D&1oSq<`sAVd}G3~4<0|c==_Wi)( zx0}^0>ie$S5+J^ek^Y(*%VD0E<7P&Ug`rz)7m8(ZI0fGycud<$a)N#oHs#C8HyMl4 zL-6Y6Cfykos9bBWWX407ZOYa`0ldn4s8zX(IvjhMUbEx|C&=T0d^(@d(Pn-OY`m ^q?$w=Wn+G2!eP+08WEfQ;-J0y-7c{C$Zop8uMvyEowm1JeHG-1J!lve%) zk$8Ky7HAm!QxQa_55@Hj#L~7&(z~XPsc*X(-NCXF-Nmc^X=(-i5vE>LB)3P$VI41W zQHlrTEVW(t^VOQUA;k!GbEZLkhELQP*`-_8#sO=KcylTvwSJjSZ(uI LcF^Z|5*bp0RXUEkYjg3bKm+#R-_- zbvg~1+W9q8s7jNTw2lET`B6hmCs{?%zv3HH%Hu1Hj=uJtJqBvhs)7)6#V}>MC#bBb zkh|LHzrHlbIcj>hD>$rj#%TrEJtg?X#GG<*H<3FCQlM5W`)i{^8sIl=yB}Sak&-o4 zzC>;hDS$uNuRGx)f6;RTzzRV>mQ4lZKd@sV2V3mNU?YDr%Z;31ls_ie4-f#H1o8iJ z6Bz3q&05 jj9)W+D-Ibsvk) z4YBxYVCBfC=~*0ue6q4t<*l#ue{4nbd&4Hrwmhc^C`4QHM}O}0oBMwlTr_%3CO=N* z=Bwoq%} *;O2#Xh+xhA;B!Cz+dhPpR8rqVZr8qcI5U Go?=@zL6#)!S9MJ3DsnZB@5) z1W^re1zCxf-y1_22pI8OYyljWnLp17 YU11oRHLbGLit6VM`COWL zJ9F&g@S#>nNZ+^$>*5V07GNJc<0B{qC;_*luN2kpvu5d-dsvT&@`p2uqT5}k!DVG! z{w7T|7&+=wrtGKzn7s9t2lgIW&;ZR2Gs(7wR)=4zDyS4rz5kTfshKPl>8a7|4I7t| zM_WgK(VP7EQtS_ysL%z>wC`4Dw!_`lhrWP7t~3#=A|DAFk|8j@E!2ul%}Cas$kRzN z^HKD*p|uMw%$dRGvo2Qho+`v6+7_#dUz0={Z7B&a`|Rg0<2{{+lMnG?4K2q*flq8S z_-~9zQA@C?Srki5Q%?)nTt$nPhb@|*9WjXp7- 9XFIE^ z+K2nNc`JL>rH)GmCv67cxH=yz>ki)R`F4R1SV!Mn;s2+BY~Gw!1esps8eY-vKx>BC zqmP4b+x-;bo-du&a290H*129vY7Vuxrik#AtAZ$)$rR }d?vDm}5QOT9mg zy+kvQc?cLdZHiC6ab{cf_%Pk(MR7FfA-9Q93319|dm8C3;8f|N?4U5fF>oh%k8(6D z?3|fr6HU Hw-aQ$ETFGvr+y|(n53N||OmE9^$FS>~wJUp-ro}#r#fgQx z>pS3M#_rg}?kpl0bx$BA-PW-_g7YFc;^}4w )WSzzrz-J_3MCl4oKBoQk34H)-Pb266I zlM#o3N&O4}I{%4|$_&c56Sq#_MRvZ1Y+hL%g9{`s1=$*TlVz%2<~Dm=ullvWSiEbT z-0(F^uaDfN!#Sex&?w8*+jQ=0L|Uj_0z584T0l*cbHQ#N>rLJOhm1 +hu zOdyt8uD!3cc#7nrjq-KTLZUL5g3%Qw_7h!V&^4)LRjGE|M9hHRglC`t+yL0P>Z{GV zvpS*=k7xi0ee(}+28Flgdz-QF)WoZnh$#*Pwv{7wl(XW_a@Qu)j5R_{W;Q0uTP<9c zH5a6D7*e^`UvdV2hS4$bn(OoPJrazDT=r3GbzwV0>m?E2R-|JG>vgH7mc{2Nd_$5e zm{};X45ZBbZ+x4GQ1)Wh`RsT&x2yYffheQGWCe!MVgJPLQW{A~$9qi(!^e@)t1wk; zQD382lpFibAz b^*Whftdtg6?x2P^<5?+VRfoTK=2$i~DxL;3dA?ljHcA01 zgZ33dpe*n=6im&Ir|-V~1s;3UD@GULnL6+2P=WVLYw u>;7 z;$r5vxiiqL5h0p1pr#D1#!Gsn!q*kU;V;YS+U%@6&_)H! bJpawV#Q z#f1??n|An!I#cBWo!WSDrkF|dcr4tne3jxWQ@&T 4y)dp$_o*&ip@5u7POFH> |M7>zJ1{vPnZiU|BzI4^3c$Sjy-R(*z@>j0MF%BgrWq;4ZpDon);X2qj_Ap zl@nCS?l=+gI{GZb^<63vo@R;U-u~z{rLDh!OpG|%sX=8Xs79b-2B9Wv-}Pub_PkJr zIcCs|ugNEDys=UYQ9S~xqJv9cR_n`k1`l<8W?e}K(% zkF(Tk?_zbBdVMjg->^1}3ZlVeV=!w9mSQYfu3=yt(QaC$sXU7nFE`UuY8%l!9oudg z*FD^@d1F;AQXph?Bnz2*&&JNEdFsQY1mMs`hT>~?4v(R;)X^5|E*DVb*-L-FwjkQN z&CHWQG3Py2tlf#iP@>YslWB9obOO=nLEhCfjZ|}nY}h3H$R3i#Oii=tsg95QfJ<}r zCNA}pMw#T?7bHrw&|ABglr%okNpL5fQnkF2en0;TL%0YcYtG_ owNG5Cr0wwkxZvy!NqHySYy{(k+^$3&@CjAvTsCbqA48BO#} zWD82L(Hy<77s%A{N!XSg>@xj!o4!>4Gm0zHtfv})FLf$;PUk-*%d8#R6FO%e?K7z3 zb~UN-jQnU+x-5f)ej*my_0}0a$QaymnX0c}SBs`4SpAln#HvI^bPQfm`%_2a)Ej zXr3}^Vj?@yJWCRzI?ctQ4gCB@Z#mLapqze Pm?|JGq0zVTWorR&ns)fF z+4BD%t$$h0n2jVlPewr&G4=iaZ&1G7B)jA8K)FS|{r<=nGG#?XgGA#m?)(k}>?R#o z>W<*kz(Hk4d@-I85qpiuWZ!o)qhH}fr9W_X(E7#M5xvKLd33$I6B7K3L$$x3xq1C} zATkX7x1jltz@!rGI|1ZN95UjM_!aU0M5mm*9RCrXrf{AtUZ$LTp9$wz)tPQw1Me>} z@bJ{)b`;#6v4x_GS$z` IV`>z%X>|#&GwRFf5>K(G@kTIF)&zS5_R2jap ziM5f|=oFGoBC#e#X)`zPMe!-N@T4yb(3*aPha~VSu_sONCUUZehp9wW#G5;AKj~p^ zb{|n>kC4z2Z`p~sYw~RZJpQ79b`dVi !q2u bh)t~es$X3%Sr(frhH_2kk@(DpAEGS65F2Rb4zmnJhmaB zMQnEnWUjTK19Hmme$4>@SS(Xytv&C1j6NmZIN2n5x*a`U8M7EWea=pH@qW+zs2HAy z;ciMFVht=7e1lDeGS64RPb$vMGf<%%4dS9Dz%feAh!Gbr^S}Jh 3 *58Q7H6;n9|0{c%%QMfzIHV3rVsH;k2?WS?N3lTBI8-+l3- zypP_`2;FEONYvQ4BPPRFk}p>fapq)#l~2Jjr0OaK%S%7_mrg{Ve@{g)s0LfJ64N%s z9rm{7-(k{_%~c&=%14O5jwZV1cIBx~X}is2&FJZ-JWOG+1I~Lhuut{xVM@E0!<=x! zJ7#_a12q+vtef=*2|#)-25Z6^aIII~0D34tAKzUHI+j9T2u)*_tS1NuV65M6p118Q z27|zl=F22_h5P+ ch0V=O(>t{`6 z8Km@M@*bxlW)BUJel*9dMKpvS2ffosKj+jNYydy_M?wsUa$Gqld*)ad5R7K1YdWy= zwpti&1Y* D;zBHHOE+*xc;WmvHBSEvz}tC){kswtd$3$ zdU@Z2pWP=CmU}|mDAk6UkpljL=qWza6YzjGIILvO?t2^?U cq%hh6$F)QvSZgL#eD*Q~B=bagW1 zn8>#b92`4*VIid&zSqF)a=Q)s{IVAK%7sB=tJQmTcTaEU^(MLx$#!TcAPTc49t2DC z^FE4F$u~SS2@u D!sbc2+|VYb+e1$vc;^k4{l{nK+ert6sO zxk%PMbMj^%XIJh)n0Y3s%hb+2-Zd7zf!)UI#Pg%Xw#|&pmMm*^C~NOE2*WH{?5O-4 zVO$+^S>=Z~`j~J2fZ4@mbNUb0m{7bz{P%j`)vi1r40=`_=3n^XUqEsWQ#JGV>|%P0 znVbNAG{ztCOOvCP;-mTV~(`9L4W7|mrxBbp9%!|Ov;DDM)zzeKHLlV zm;ipQOn{ck{z%^@WBxczHOhVoW5d2abHpTl)LlaEMM01q6-LVa^Lwr(h0uD5;aIVn zD(@G3*VAD%fyyIk$>kHq?ItHx*x%Yj3TOq*B !nxCH`mr`tb{kD)gWYRFn5? zzCfp)BK=={F&k0;2E73|rXloAL99GZtqvDJhPrLP4(2b!7Dkut)^}7R0MjO(!F6qy zp`MX<^~f$unqjh*wxu7c4{_kuU~9mfvG5lTF`LS3nqKr3!nIIuGhkS w~>TNbZ&YwQc%roLLe38h7U(OnPPF=T7)CpQWL8TBys$phyNW4cTH zt@G=16p6Lwa|c78>e84YS0sw)5(^%AqO ze_Cma=-1FEs=h 9CGkR*jDg-af9YKMwDQVZ9jAF)WXL^ z?W#O{J#owBs`-QbP>pj|31NQcCM &p03x+T)ZHe3GEtZ>R1?kbpnPx6V#E zb<~gPqE=$Vvh-YoAVO9;j%ixnM!+mS$F3v9@80=6ff9u5dF8Z1LVuWBO?UFAJ}Toz zA^Kr!5~HM1nvg(c;}YJ|=HawTf18z}3zgRJ(|J^3aIlUQ8X}6QmoR+6gV&VWUmX)8vo8bfL;rd_xmzvSDr zuVkUxvKK0VMhspQv&IYrueR!DpT|6C{BYNw<)&xjE1blBwUNT2umi@#$+8$yz6!Z& z4!u+~NqV$=L2yr^d`bP#ooMV Bmh(uNb q1MH zeW|-l2Ln!Cn zWBa9YM+R!r9!t-eouwZ+C?dkVRXE3QbaU<4spNfhoU7c@6InkYfG5^!rksB20vo z$3LCJP0OEcqXPC5KX&GzPNgEZW8m29 XJe7-br*g2Lxq3HDJYs)G7gHOcSfdaCC&(b{QOJnK^2r}T#}7ioA7vLb z sEA3Gft@*fgz{vX}szauFC&ZWQ0JGJ}ME)Lt%7d>Z0$QsazHnLiSVhsRh z`g&VjlwY5F?RmKpz&G$Xi7%={9CZHbb>y{3W7gl%Q*@Cgwh=x;vnQys{$Q9|>45W( zg2kV)mp>S?(|fho56~ko=XYfIfb(*In<3T2zsf`~Dm<}9E+Y4%<74HF*^n!QG>gHj z{~388uTmP0Po?>pUMPeJMl%(*TyHwy$QG_7j8)+GrWmYcIs`xM0vAj<2zD51(fkp% zb+kQSTD(y|k>*(y>Svf;^{HA27(4>y&pr-!U`YbY>L08K$UJyH*175uAzSlzJ<|&8 zxOBjF3Fo_@=zo{&emG98RAjUqgHsF0r*?M-6|n3}x37_)>IDoyc}5>lQTVqGzUc#3 z$L-oyo0%_lWKf!J7WQ(J$)Pew0A7`i*lR~Q3|u_0okOU|k#K~j)i>kC8+5S)-52a% zww^hJO!$oq<0`q=JP7qHt_v)h(|E%IX17WfKtuX+{*ILIfsgl=s6F<#uj*F6Tpew@ zJ6s&9MFD+%P}MNOO43z-a|k1gmR9uRUj6hCr*jygbQ1^h+VsG^3JzGsNfi?qE#6OU z6C&}jt5(A0d9oee_1u0vPPNA+k51mQ#WGoK3|BIEpSQ~du)N)Rvjj8CSBByPc1! 3^E*2PqZX^JM6QUBt1m>cE{}!d(uV+B>RBn_Y9W#;2VUS5?2DsLgxQzI z66p#wH!cd7E|@viY98s@uwjL~ru{8sVg^^I6#R$=;x5_GIdhn4+j**xleDMT@Ytk` z*z_DPA6C+H3RUtik4>}~Jewx4y`VC$Zhtu1Jzp&T(w?w|-)w}uj@3|%`Kuc~=hM-U z7|j)N-8mC7aMh;YU-}d@qaT%Tj7fccG%dS%P18jtTdirUg%w}a3^uT!y7LmpC;#vp z>%vJ-`S-#9S>LQgqX%8B7hMUxpSjxixX@ZUZBd&!g>Ac25P2N1T}f>}QUH&w#irO- zFSwkoO2>zW^IZ5fj60rRA`r)I_5q{}oY(puZQ2JE@8V$Mwd?sgO;7o^%57+$S&d1) zR=2}3aq6rF=YeJh^=Hg9ssR^7%;kvs?0%cd+K7(URyKJ8`)x}@@hL;pVsL_FwFS@0 zsSekISH;wa6ClV@AjNHSMvEuy`Y36_ID%3U>fIZw@svjdHdwxsLTAR7dj=y>qU0?E zG0r=X)fDoST*ztcwK{AOF8w3r;0QjRJKAQoxR!h|$#*f>%X_6xcM0FVgm;(}#ov-Q zhA{9VYO<@~*!KA@xH9b5vuFV&id$qs#>0;$CJx`UKW3f3h~&75);d^x50wIw@qR>- zu!;BeY3rTwiZ?TQuf=AXXt!UepxH0k#9vI=X^QHvZ_h1c2VE7K>kUvOXPQcM%?*)o zF%EGlkaEl}-(eMEmg`3MojV>Kq4-iEtJYK#5K!OYRbA6}eh9a`iFi-jg)w2b<(KH& za8e=t5AFdlT;9@`>q>d3EOkeXzV@AfDxwJUYpbP1rP_)9>kd-Ic7)dq-)VQ2dGQDH z9WTY|Zkm~7fp>@!Qogt@<$k$Bg>m6QXb(1&vh|*pgV;>-=OHxFpoq*ANg1~>D86KH z#M)j>@O$&%F^mu5&;VGYxFipc6rX*CKS?6hz=;Yi%0`yT4VbkZdi?_v6txdpO lsGT3pk!d`3nn^W3?nN%w2&8+6ea3|N%Y1vn6pe_8+>!pwBdtdxMp>_e#ex3l1 z%;T+IzR8`&?3c&EOA>$yGlimHp|lilgTtCX%-crlSBOarDyqF~@i4x8larBrv81J1 zpG^t6h| `L@vjWF&Td-J1tJ%Vaf)91XCWK*4msR8uvYh)5(De%LheF<@}Wb*6%n=)vOU4o z(O56`UEaAt@4kQ3aMj91M3;(M2b>ai;kUUZ{e(`@D(!)ElACq v_PK{8uQC=>EN6t-e9W4Ah9d@T&18gltX@L1 z9 !IPX?-lT30A)%@HiMHW|JK`w2xnIRTTBr3!W-QymMc zy#7`V)F&JB*j!6W!0Bdl+6~_|^Er^6*@46+0}uy=gWf&EPtm^5&L;}!m|gkhi-woO zeb*Xt7ux}Ua#s@s_b=+*{BWm|;6$D4iCCWt3&5|8R-OLf5kXMx-Nsy3F|HiTS{?Ev zhUvBfPE|e`*-7`91uKh}psf}bl=9MXoe(dBqqK BmIm% z7G*Y_v;dpuWSS^F{$OBuMMB`qDjF^__>}|%Xizm*b@BQKl~k9ie8#w 4aVPkH zvU|QHi8p52rf5ToFNW)-W|un5%IQBm0|AQQ-w1fj;D@Ij8rtc$=$P TgBc#0<-@@VH!4Dm~9pxtv{LQR;$eZgE#a?alvdM z@o2in hL7R5jrmY6n;8T zioi1$wK=s&4t8L6vO|1{6G4sNYZlrn>`nOE;ii-kc-TBJ*rb-)6}KZfTAdOoAt%K< zn@>v>gC6_AK8`xygmgq-NB7J1z|tWv!%idC;0LzZ9`eDP!r8)slQ+I;NiUkRdH#d- z##s6%1m-Ic#+bOqI5T0_N&iGToIiWjxMgsyqAHR)eBS9RfXU^U^cY^oez&!AFEXQ7 zs-D|IRxutI`;#b=5LvcIn43$?3(NwQivH^A`3?*GCQ}H*dkApWnV9&>kl?**GH>2A zj0u);=?unx(z{i
Ru-T+zjyF^XQqvM+RvBEq4@*G`Gdtgib>LPux1y13Y0 zFQS+l0FB^=EW<>LGf1`G1ywS}Knv^0>a630es!Hp&FjPimkb;eX0Dm6qdK gD-$ntDlgd7kE~0}&`Oy$F8kYWy*r(qLiebYjMLbskMxcu# zpn+fae26tFaOo=3 oQ-6k?IXx;Z`97xlRxLtBnL-gd^h=b*{H<)&HHy?wU=yF3x9&;uM+(HN5W5#0hO ze{ATm-EQl3SUElYsL^gQs VfVP=1Il& 7IRUY)^ z(eU|yEjIsTr8E&v479=x_D}9H{F9Xe0F834n`yZb52a#X0#KOATD?y-^=;*2*NVD9 zhw+$!BV @V8dwhbl&NNqJ3)1JSgS4_4P>#wbSC$(w7JVUze~+`%vEuo-c!T zc)CIs6^G{z_JQ}jE~6eEjh3&xoCdkfCfo)xG;bw;?Zdis;8ll+9mdClTmL4%F8Fl> z?vUt8avL zJR~?f=;gLguyUBE`Q!CODqVLU*D+E~lNmlo8=ZoO3)r+9l2mu*Gu2qP%JbDQc;5OA zJvpxVFr!zdwP*FFb4S{pNcWvPUmo`rfRFJBzM1Obhnj5fx3y$1o-}uxzy3!|1MsP} zcLO7nYP(;$R#SzpxYXbVnUiWrouzC$cvm+*BT=iFIWdl%z8Nx8;=kw;cEo&~U}5B5 zmW|+P&|6G8W^AWErOVz}cGy+B`K>p-mDOZe)MD>tx<4D;;s(X{v~|$nXC*`6li8h8 z3}kuzCT$YLOcNTnvm!E@fb|$y@W$!wgNsFtGPu_(ytr4-o5p>WQ)WV_dOeVN=ej1{ z5f;)hI^xmkm5)Kv?fYgg0dmxT;%IYKj>o?Pjw=Kx+B+in_8-!|H-aw-kMIt!Be23% zFO9X85e>3Vg(N+HYQCXyPz>9z_HgaR>iJfmt>%shp3vn-%X`gBp=hh2IMcN+&vp?W z=JnN$2=<@?mrF@bYOtLD;lVe|@;Kum?a5bAIl&BFt54d{HgbTO`Yap&$AD>xJr zMM55$rfKbHY}8P--tPH_CbzWz(vUiom^32I5+D`p8H<`rKBxP3U4MkHY_JtRt~u3= z%BVIDT1-FUP{&f=V)cyB8o$d1)NcOdbNo*-!5yUXvWtaL5sQ_K4b(#zcn`-XE_R2w zXhdxEwQR1Vhg5i)TSF6d=CZi3T Dy)%dB lOt$^C}U0U)ntjTd`$y^S6xISQmp z`ec53jPYExjDO@Q)tb?@oYu}9w(Bo?xPZ9l^)I>x_gZU0KAiJ}95egXoog8XYns6E iO-|qqyujA*fBsbtQ4jMA4@?C$ggjmST-G@yGywpYwiF2f literal 0 HcmV?d00001 diff --git a/assets/Android_SDK_Tools.png b/assets/Android_SDK_Tools.png new file mode 100644 index 0000000000000000000000000000000000000000..0d3250f34fa7906e74176b219e9d5241cd833d71 GIT binary patch literal 51636 zcmbSy1#lcqljX>gES6<4S %Y7YP)I{f(uSvSIU0RV=?#02>iT(nN#pgfQj zyJ=sQ9aWHi9HAilQXP#`)JQl@%A)&6oRk&Ufq(}NisUn&B1p5yhS|JGHHHGbCD>M_Sm zBU9m9QH92+2utoA(`{E%#J@e&r8Q9a|GpY0$(%*#u)E@cS*^>_*dNn4xA7_okZ1~- z`LemD+>}%ON*J JQ|`S!pQPywhz?>AC` zXqu4F{Un^mxiOb FIv=}leHq9n zmo>w1x#?YjJ+JJnFrQaDBy|iN-I5aNWM4Tg ~=blxpgNwh!0(7cS}#Ng-O3GvFK_-5&CdD&wv& H@0-q(QYT87Mt@FK5+JSzOw)Gs@maM>>`0A zYJ*IbV%}yXf`?s9;nTuHVa|TeK2}HN>-A&tMe2@lB+hX4y>$M&&NmmO&?nLne_0lr zF)g`(>_)*@E7k`lxR0|0ZjT${#DAXf aN;pbhcB`2hGN%_ z+jpNUa(`Z5T^0N|;@k-NXG8Lu7V*E@BeL+it@vGbcI+O{(hMzV=GLMEhnps8*}sUy z;ndLBiTD bz`uovVUaZ~Mqo%N+@5`)G%GH90P#J>gXLusV^hFR*&pWWf z>X`M8>{QHE<-o;4DJjOfdTa7Oo5=@ jQ>)VMM8kU>zr)7Na>OpKED?E6Kc5HOoEXWuLc!{iJ1`#9J|JkQCnNk zeEgV-r&hu&+{)HzHKxDsy^p9fzHr^05q~^P18kcsr+G6`D3(`Prnyb4DO*Q{?(g6g z#PCn;gi{2t*t+em*{qTwGE8;Q=x|XDN|L~ zaQ&4-Pmb_5YRk!+GQxzH!%+%d9}B-d)$I^22|H_{sShFp_`)#_L{zIDmBkQJd9H2D zhiEcGNsYDH?k3C1(N=)ufp9V0y=WcNC>?S<8cA5ld%hK9d9~Pd{KREvwvBI+dq0(Z z_^ta!GgZ_CawR6FFAV#ul*5}dJBaM>vaeH3m+qAbG&OKJ >=zr{~?ElD~a`W(LWB$`yu>VBIjg~0_fL(e-CSv-J#pRvw4V3)b z?|)}`|38QPuO|HQl;a}dGs^RA#L)t&FeV5fAQ5$Icn-H9W>U?|EI#nh4BQfbQwV0E zuhHE|fkNEI^@HFI%J96sYI^o0;CjXO&;4`TT#7m 0x`3{Gxy1)xKs^Z{Q1_|x$4qf4Rr?rP*kqay3=Z!Y_!gLe|r{< z`#W$PaNuxh5pmIJNI;Qk2Y)9aJ^f+C-%EsZ3<&2uf0_Uvb`9Y_&KB?=o`8u2ir(ZV z)~J9r2D3;0y2 FymL)X^4vW6VI*WTlyDgZMEL*wxD6nfo+v@#_^v)U@zfREW4I9`QuQRd`$~H;+ zRI1$yc;&Ccn%CDQcIUUJ8)F%5QwwZ%&dky4rt~5q-ix1qUF)sd&&tHwNKCa0C++5E zKgRQ!O6|_s5c!ELwl~eS#%Us421?Ua!O9gy=WBmPsO(=@t|Mc{ 55T+ZH}9mfGIk0u+5_?OsWluc6MkV;5F_<-Rq1K@Y6H4%p5{N^yeC5F zi(-|x7VEDvDpwMEEWD4{tbd)CE&c>eOHC{I3J0cpxYqe@eH1u `SZl~3@)wj1^)6c%0_OH1rP^Soa699f%<_E;UwpiKw?9NN<45k qIVa(e_$tU3 zz54ziM8;$zHY(?0?f8@=9{Gk*oNkdz skiB<|hL< zp6=1ym}i!|E9&h9cs?ucoZw?ntMPWIbwBlYm6-c9k)A*W?W3yJMCx`w1qia{HGUiM z#VSc@^pqE7=Z{)@Bo%?+i>8&qV?@z$3zvsvZ&Sa%Ax!?kFv=C7B1
H)0+gUb-FsFm@k4QLbvtk;3A!{K}(i*B6LTHE$F-iNxMuPd=rI=!BHtWfLArNu( z1%9B??R}JsCSt97Jb&ZT;s2IZ-pm*K%okxPeS%}K!DpycwN@gl;2vta(x{cb;#S7a z{jo~N ;o0wfqM %@M4B=LIWMkoG7-mA804TW#K9+9pn@4k3air>Gw3Ur!Y`yo#0l z=-crsB`k(EF+!ry?|C_2ZLXQ%$)e|a2*k3ACJETOcIjn`jYwFKW>9K|+Wj~atAm4D z0yBKWZ@Ww}9aI_LpO#E()QF5bK)S|hlY-67P81a$vCY-Iyeg(5j>|8cXct&_GJMOi z%w3VsZSXuvQ0G&*Fj<36@u)IoV}PQzmCJe1th(T)M7kih`&?_67h6Cg$%LEz4w$4i zDd%U@>cB3{kn8k%``p{ltkoeH*jZk$-Qo}<#q*$9&bJrMPrG7xqPszoQ^zR<2Ci%gUuO+98%LCo6KccIYk0d79+zyZAtV#q2 QbR*lEZj;Ji|(t8hUK+XY$N )-l#JQlU{X5;Fgti!;xv-eW?Am zFZZ^C@2u|OwuK1#`Jm#+Vv(BFVGwKzyQ`e7*wMp=NXOVn*2>L?7oMYYms)P6D_#U_ zZ8!D`wp|r*7@Gh~mjRyZ2xe4_K`k@&!25l7;)H1{X5!|@1?~Z%QQ^9}aA9qMi{DR< zNAF#o^mXJPlduVcKg^+|WiF8!u&G?o?00HZ$WzWpP(uW%Df3mA1?ztWA9mbfHg^BI z(`h9p-i{G74UU7Xzo`4ZY76lc8Q1Qi!-Mk^aEMc u{y%FwjWEkZ&irC!B0E6_k%z5croNDj&DfiB%=UZjZcVO(8N*BzfOQ-RQ~@ z5cUmx&>NR&cmR#*i6PEUEgDZ95OSlKqn|=f;7a|mBV5nLuKAx(Yzw+VNM{?BT?168 zOzhTEcQ(YnW4QJ_d_x-W 10;0E<`-`N0!mZAA;qQ|Rh#9;1#qu%V zWXVxqO`5ZqjCo{*!v?qa;|MKr$YClpgs-JE$fEm44F<~FYCB4xmz=ZA2 yr-y>n)H)65&K zOsjqZXrQT;BEVTK6ojFbiJ9`%LwYR+$B>LveU3^?H(t!o@_L=py1Nq-VZs9UY0!m} z*zKOF4J%Lr>q *c~A)Q zJed OcXnK0fUYCCkB4I(}k?Yc=1O< IUc rDqXjC9*fSw}nLV!?iz+`zy9BIm)|a~u~bZCtZ1 zvxX(^5C&GlNW_%GlcCttUWRk^8F7k?l6c` )hU zz@)0wU}obXjT=A&y8WeAyXSdR)lMBa2D_|h_(A831! U)WCaSss=g{$`V-2}BsnK$D%9g*VL2^A _NhA${VG zUx%(o7uitYl5s~Wvq=tlxK**_j^B-d+ w!;Jmd)L!}G~!oB0~BAK zN+7!Qu>x~0jaSiUGk^r}qJLY8hRS|8goaD`1Jo+Q;5%UcLDXF8Iw<@I{>jPcreJ{C z-rg!&c(*^7eAdaTps#jhcs3ANM1zb60sO7HlRDQA!-{R3U;kjeyx+$7pTA(zjXKOk z@x0A??1lQ!q=5Ls9xilxe|VwMGR?R4IO6yi11AN@{smpek5CQzvd-$Fv%&3|ZwvTg zVrtqv>$109KsoIK`Iq?Ql_`(|Mt=7Ke>qb(irc||F#P`v^#4}`{(pz^ytDpU-r ?Yxh z00hYy9B+r35Fo;oe|}DL*KA|VTR;Fid6Sut+&v03AM&5|CA}{90s(u-p;jMB*MlwD zRaCfeGgUWiwM#yuB|JFG;GiUEL;Evlx7xpGTgSLYa(FB E;8RlBI7Cl*)`ybzt78ptma2=e+< z7vE%Qrs5(LzA40&OIE$c$gh7U>V7g387S1q`|(nc7dt!dmx3cpKE0qlf}aB$9hab@ z0K|!30NLe?Oa=^T5n(tj7yVe)@%<>mQK8;8U2Pz55TZL~U|q?2b8#R^D+2GB QydRNR{54oXmn@zhbv8bUrdXA+z@aKDVo; zuP?bQ{FM5*3WEE%LR %a-4ocw3Xa>#IFJAxuf^z~l2A?0 9Se0b4{0_k-|?4+_v>>$KZrYAgzHc$L+L6E6Oa}I 9k10+9r#6ea4hv=Hk#eC#Q39;-(Le6@WRPUJ~rxX*E@EtR= z$QN;ZU1c-95M_%b@$o? AdnBs>#N z9JTg6*U^zkaVbPEu4FSDZaEs~D3!^eCf}eh>wREq@m3uL4#&SPC8~FYryeVI`&dOH z5Y>LXGE0q>Bd7s?NNl>AguNpX5nkdQDQa|hd`LVVORzW)ZO6(ojK9zwVxwJSDp9}+ z9$#cnT}a?Kc3|K5g=rT5=LZ)LeIyIXo G_ZN^&3S+apX-88%~xZkv;_-JoPV^Ca%H~N`S&tHTrg%KM4>f4_Mpb3xoqR`Gxf`o}BneGp4ms19E8zb|p~f6dTzozs zAP3t~MmRD|9cEIsO8ntAvFT&21c5U;j6Lrn3T6!h;B`0gM9uB$M713mLlt`4%0(89 zz*8~ii5DJDx}T1sSR0e_LljM6a&n=fj2}^i?5cL}nU4xPZP~qI8Myka4_mAc3(It3 zGt>0s9P_E7@%E0|`~5b1K~iGnb4N~7%lT{)#6!}gj4VBMP1@n^d<_$IYiFC~!_;>v z4}1S;ivlhENGTcS?w!YH0}XanQ|yV>;q~V?b@w4Tmepks20`(A HeUGhfZfR+`crbE*tv9n9u1Tb5pV5=^+mNm1tRCSfa*o_!A8hjXmQo zmSs%6&0~_fX~{h^EY6yXxM~SWT2>-g?vX@s5q0kxyP@9c(Obd+b#s?Q0J{RC6`t61 zJ`~c9Oy^COwMXt~%MH6kMTEYqkeQzI=%@va=S}vun%uzuvG)Y0iceTbWxOA+epQ1) za8~I=_CJFXH>`XGi&E$GoKv1^kd%wV!Ce-Rt P3xTEc8I}d0SU;SjAQD36tG|tGCVpR$4YLt`}u&6=8EzDrK?+}OZZ|sAau3;5% z3Ts@ZAl7QP`1&QmeEw1+A*^s5t2Fs{RQA>8c0(ua6;HXu;W~El`)8&vB^wX#IMrlj zI~gBjjyBKpPB#_P85Z-^xeQ5DgQ*B5f!1H;V$08CyFJUg!p|RHR?g&z43w-slTK_8 zhe@o??=srr=+YWh2jKTr-ErM>0yM34Q$|u3mw6=wh$Jsjg+w#En}|wBw1j+Pz!VnO z8i+a*aZ2cehLJ*Omtt6cAhYpjJ$tGzSds5BpjHC;!=2`s%%3IB5-emwZPc_`WkOT% z6e;r9lm&~a>-H&R4Qp)=oT^@e8pt;tOt`GUk1u*epjYuq_HW(z0iU^HCoUx`sx za@Z(F`nIZ;?de1i97Ll*Lp >h@g7`u5fa$Or94qWdIWi8pF7H C3%aUZ5B5^KH8Qd7Uog6~4MA2;! z+3!(WS<_k$UaHI;jXBoL?o#|CNSqPt(2ktu`BD5>ts}o{*@I35)j_n5Te`hjG!+^! z2`}5J7CB4c tG|LljS5{H<-#R~;x$09hbXv6PunHqhr>YuE5VTE8EF*X_lJ-dHu zO2iyy3yFb>-O6(2Tq_;TKZPSlQa;N{Eb>ZDn#Rnvf=)qfp(3UgEuNBUn%@b%Wz9!c ztqx+L@*-}i+mNIv&Dw*Wrp3htf`!u-wTa29q|iSVR5C`+OtX1x7Z!c}dl+TJB2wyv zCH5MG+*bTMKP1L_SyLslAj0WN-{ti$39kH7V%K~fV vzzSzI!0N|O1&AkX=B zC$c8$IPy2;Yp&3F=I;avswfZ=tG5F_V+2_RGitO=m-DK_E8lcYzKA_3rLrowWz^`Q zw%edF6C-|rAJy0o)UaKTVKhhO0jL6Yd(QDqmR{**8y6N5hXl=-O5Oy)Et;0OeWgjY zWTZNHDC1x#P$x$sBD d!o-n+Fj{5bLna&+Smlumnv=-kx{ytn(cUq6Ua{R*EK*Ng zYb3yh-zS@@*BI(Di0F5OIE?4=gnGgmN-NVK^mcM@HY!{mCz~)&8psJ&wuN;sGZdIw z_T|$kK<9lujs^e@c`b|iO^6`-F^=jiC3U)H7m~G>5{p~H=P3t$@X{BzKH&HNMNS~P zbClCQ`%58HQg5Y_ntmZCq+EMB#?62D9?WT_Tof E~2iP#ExjmJRh%}x-3p%UpJ{tZ$iFO9Pq0iS>LEb zVcSA1Lt7_KNf27M4oyM32VD+3s~WH6{LD|kiIOLSl6ro6(eH+<`)g?7Zpd2xaCtKN za^g^OCRR&MGjgq3CfH=tPfS9hmpxmdkpkVB8OwCH;&}hpv65!yfr?F-x!dHWx)Cc$ z?ZWkDV`QL}^Z0>6V$ZmbrJ7d*73cke+r38+RIw;QOUYGR+$8~C8TpMu))LQpM1QKr zvDu?IVVS|u-?DEOAHrTm7aj`W1K(@B;abV)!Ufr}z^wcS))s6$(Tr00oQ$v f6< zn--03sXYMVa%6wp{3|C>^(umyJ4|<{xI|?oVkN6+9L|EZhUDQz_twG7Ye_dl1)Bsb z8YY?%8|(GD3vA D&IJdSg!!VOSLwDm(ngYNA6N zhs}y&)K9vB@I-?V6D#}HPy2zx*aUhV)#Y?yM_<;P{nPas1sYnu_kms{6DvEC&j|?a zd=3>`Hf$VA5TNJ*!;S;lRg5kG`UxaptZ64o28sZLgj$%N049_`x+B+VtOQ{pNyF!l zaG)%4_G#joVt1MdsD~**!eRj7eGq~cc;agx_{2+gOCk_BYhJ|&1;{>V8F$79wB%*F zq46(y5HzuY0sOl=I6%%)fs_K>pKj+RFd6=kLq+@oB)|^<;QuOB`>#yL|JG