Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Civilopedia Welcome, moddable #8940

Merged
merged 1 commit into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions android/assets/jsons/Tutorials.json
Original file line number Diff line number Diff line change
Expand Up @@ -411,5 +411,27 @@
{"text":"For discussion about missing entries, see the linked github issue.","link":"https://github.com/yairm210/Unciv/issues/8862"}
]
// "uniques": ["Will not be displayed in Civilopedia"] // would prevent use for help link
},
{
"name": "Civilopedia",
"civilopediaText": [
{"text":"Welcome to the Civilopedia!","header":4,"color": "#fa0"},
{"text":"Here you can find information - general help, rules, and what makes up the game world."},
{},
{"text":"How to find information","header":4},
{"text":"Select categories with the buttons on top of the screen. Also up there is the button to leave Civilopedia and go back to where you were before.","starred":true,"color":"#666"},
{"text":"Each category has a list of entries on the left of the screen, sorted alphabetically (with few exceptions). Clicking an entry will update the center pane were you are currently reading this.","starred":true,"color":"#666"},
{"text":"Lines can link to other Civilopedia entries, they are marked with a chain link symbol like this one. You can click anywhere on the line to follow the link.", "link":"Tutorial/Introduction"},
{"text":"The current category is special - all articles on general concepts are here. It is called 'Tutorials' because you can revisit these here, too.","starred":true,"color":"#666"},
{},
{"text":"What information can I find","header":4},
{"text":"The data shown is not dependent on your current game's situation, e.g. bonuses for the nation you are playing or difficulty modifiers will not affect the numbers."},
{"text":"However, it will reflect the mods you are playing! The combination of base ruleset and extension mods you select define the rules of a game, what objects exist and how they interact, and the Civilopedia mirrors these rules."},
{"text":"If you opened the Civilopedia from the main menu, the \"Ruleset\" will be that of the last game you started."},
{},
{"text":"Keyboard","header":4},
{"text":"Letters can select categories, and when there are multiple categories matching the same letter, you can press that repeatedly to cycle between these.","starred":true,"color":"#666"},
{"text":"The arrow keys allow navigation as well - left/right for categories, up/down for entries.","starred":true,"color":"#666"}
]
}
]
24 changes: 20 additions & 4 deletions core/src/com/unciv/ui/screens/basescreen/TutorialController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,26 @@ class TutorialController(screen: BaseScreen) {
var allTutorialsShowedCallback: (() -> Unit)? = null
private val tutorialRender = TutorialRender(screen)

//todo These should live in a ruleset allowing moddability
private val tutorials: LinkedHashMap<String, Tutorial> =
json().fromJsonFile(Array<Tutorial>::class.java, "jsons/Tutorials.json")
.associateByTo(linkedMapOf()) { it.name }
private val tutorials: LinkedHashMap<String, Tutorial> = loadTutorialsFromJson()

companion object {
// static to allow use from TutorialTranslationTests
fun loadTutorialsFromJson(includeMods: Boolean = true): LinkedHashMap<String, Tutorial> {
val result = linkedMapOf<String, Tutorial>()
for (path in tutorialFiles(includeMods)) {
json().fromJsonFile(Array<Tutorial>::class.java, path)
.associateByTo(result) { it.name }
}
return result
}
private fun tutorialFiles(includeMods: Boolean) = sequence<String> {
yield("jsons/Tutorials.json")
if (!includeMods) return@sequence
val mods = UncivGame.Current.gameInfo?.ruleset?.mods ?: return@sequence
val names = mods.asSequence().map { "mods/$it/jsons/Tutorials.json" }
yieldAll(names.filter { Gdx.files.local(it).exists() })
}
}

fun showTutorial(tutorial: TutorialTrigger) {
tutorialQueue.add(tutorial)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.models.ruleset.Belief
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.ruleset.unique.IHasUniques
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.UnitType
Expand Down Expand Up @@ -182,6 +183,14 @@ class CivilopediaScreen(
it.color = if (it.name == entry.name) Color.BLUE else Color.WHITE
}
}
private fun selectDefaultEntry() {
val name = ruleset.mods.asSequence()
.filter { RulesetCache[it]?.modOptions?.isBaseRuleset == true }
.plus("Civilopedia")
.firstOrNull { it in entryIndex.keys }
?: return
selectEntry(name , noScrollAnimation = true)
}

init {
val imageSize = 50f
Expand Down Expand Up @@ -287,6 +296,9 @@ class CivilopediaScreen(

if (link.isEmpty() || '/' !in link)
selectCategory(category)
// show a default entry when opened without a target
if (link.isEmpty() && category == CivilopediaCategories.Tutorial)
selectDefaultEntry()
if (link.isNotEmpty())
if ('/' in link)
selectLink(link)
Expand Down
13 changes: 7 additions & 6 deletions docs/Modders/JSON-files-for-mods.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ The JSON files that make up mods can have many different fields, and as not all
- [UnitPromotions.json](../Other/Unit-related-JSON-files.md#unitpromotionsjson)
- [UnitTypes.json](../Other/Unit-related-JSON-files.md#unittypesjson)
- [Miscellaneous JSON files](../Other/Miscellaneous-JSON-files.md)
- [Difficulties.json](../Other/Miscellaneous-JSON-files.md#difficultiesjson)
- [Eras.json](../Other/Miscellaneous-JSON-files.md#erasjson)
- [ModOptions.json](../Other/Miscellaneous-JSON-files.md#modoptionsjson)
- [Difficulties.json](../Other/Miscellaneous-JSON-files.md#difficultiesjson)
- [Eras.json](../Other/Miscellaneous-JSON-files.md#erasjson)
- [ModOptions.json](../Other/Miscellaneous-JSON-files.md#modoptionsjson)
- [Tutorials.json](../Other/Miscellaneous-JSON-files.md#tutorialsjson)
- [Stats](../Other/Map-related-JSON-files.md#stats)
- [Sounds](../Other/Unit-related-JSON-files.md#sounds)
- [Sounds](Images-and-Audio.md#sounds)
- [Civilopedia text](../Other/Miscellaneous-JSON-files.md#civilopedia-text)

## General Overview of JSON files
Expand Down Expand Up @@ -62,7 +63,7 @@ There are different types of attributes:
| 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, seperated by comma's, and enclosed between square braces. E.g.: ["Shock I", "Shock II"] or [1, 2, 3]. |
| 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:
Expand All @@ -82,6 +83,6 @@ In some sense you can see from these types that JSON files themselves are actual

## Information on JSON files used in the game

Many parts of Unciv are moddable, and for each there is a seperate 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.
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](#Table-of-Contents).
16 changes: 16 additions & 0 deletions docs/Other/Miscellaneous-JSON-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,22 @@ The formula for the gold cost of a unit upgrade is (rounded down to a multiple o
) ^ `exponent`
With `civModifier` being the multiplicative aggregate of ["\[relativeAmount\]% Gold cost of upgrading"](../Modders/uniques.md#global-uniques) uniques that apply.

## Tutorials.json

[link to original](https://github.com/yairm210/Unciv/tree/master/android/assets/jsons/Tutorials.json)

**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.

| Attribute | Type | Optional | Notes |
|-----------------|--------------| -------- |----------------------------------------------------------|
| name | String | Required | Entry name |
| civilopediaText | List | Optional | [see here](Miscellaneous-JSON-files.md#civilopedia-text) |
| steps | List(String) | Optional | Plain text |

If an entry contains both `steps`and `civilopediaText` attributes, the `civilopediaText` is shown first.


## VictoryTypes.json

Expand Down
6 changes: 2 additions & 4 deletions tests/src/com/unciv/testing/TutorialTranslationTests.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.unciv.testing

import com.unciv.json.fromJsonFile
import com.unciv.json.json
import com.unciv.models.TutorialTrigger
import com.unciv.models.ruleset.Tutorial
import com.unciv.ui.tutorials.TutorialController
import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -15,8 +14,7 @@ class TutorialTranslationTests {

init {
try {
tutorials = json().fromJsonFile(Array<Tutorial>::class.java, "jsons/Tutorials.json")
.associateByTo(linkedMapOf()) { it.name }
tutorials = TutorialController.loadTutorialsFromJson(includeMods = false)
} catch (ex: Throwable) {
exception = ex
}
Expand Down