Commit d51e7085 authored by cobaltcore's avatar cobaltcore
Browse files

Switch all examples to YAML

parent 9ff3da6f
.lm-select {
box-sizing: border-box;
position: relative;
display: inline-block;
width: 75px;
text-align: left;
font: 16px Roboto, sans-serif
}
.lm-select * {
box-sizing: border-box;
}
.lm-select-ico {
position: absolute;
top: 50%;
right: 8px;
margin-top: -3px;
border-width: 6px;
border-style: solid;
border-color: #c1e4fe transparent transparent;
}
.lm-select-value {
padding: 10px 10px;
margin-top: 4px;
cursor: pointer;
z-index: 4;
border: 1px solid rgba(222, 76, 248, 0.8);
border-radius: 6px;
}
.lm-select-active .lm-select-value {
z-index: 1;
}
.lm-select-options {
position: absolute;
top: 0;
transition: transform 0.2s ease, opacity 0.2s ease;
left: 0px;
width: 100%;
visibility: hidden;
z-index: 3;
opacity: 0;
transform: scale(.9, .9);
background: rgb(126, 91, 191);
box-shadow: 0 0 16px 0 rgba(0, 0, 0, .14);
border-radius: 8px;
overflow: hidden;
}
.lm-select-animating .lm-select-options {
visibility: visible;
}
.lm-select-active .lm-select-options {
transform: scale(1, 1);
opacity: 1;
}
.lm-select-list {
position: relative;
width: 100%;
max-height: 266px;
overflow: auto;
}
.lm-select-item {
padding: 12px 11px;
cursor: pointer;
}
.lm-select-item:hover {
background: rgba(25, 0, 255, 0.1);
background: rgba(222, 76, 248, 0.5);
}
.lm-select-item:active {
background: rgba(222, 76, 248, 0.8);
transition: background 0.2s ease;
}
.lm-select-item.lm-select-selected {
background: rgba(222, 76, 248, 0.8);
}
.lm-select-overflow {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
display: none;
}
.lm-select-active .lm-select-overflow {
display: block;
}
function select_callback(data) {
window.location.href = "/doc/" + data.value;
}
function loadJSON(callback) {
var request = new XMLHttpRequest();
request.overrideMimeType("application/json");
var version_regex = /\/doc\/(\d\.\d)\//;
var current_version = version_regex.exec(window.location.href)[1];
request.open('GET', '/doc/' + current_version + '/data/versions.json', true);
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == "200") {
callback(request.responseText);
}
};
request.send(null);
}
loadJSON(function(response) {
var version_data = JSON.parse(response);
var version_select = lmSelect(document.querySelector('#version_select'), version_data, select_callback);
});
/**
* Like material select
* @select html element
* @onChange callback
*/
function lmSelect(select, version_data, onChange) {
var selectValue = select.querySelector('.lm-select-value');
var options = select.querySelector('.lm-select-options');
var list = select.querySelector('.lm-select-list');
for (var key in version_data) {
var item = document.createElement('div');
item.classList.add("lm-select-item");
item.innerText = "v" + key;
item.setAttribute("data-value", key);
list.append(item);
}
var items = select.querySelectorAll('.lm-select-item');
var overflow = select.querySelector('.lm-select-overflow');
var bodyScroll;
function selectItem(selected, isUpdate) {
var value = selected.getAttribute('data-value') || selected.textContent;
selected.classList.add('lm-select-selected');
selectValue.textContent = selected.textContent;
selectValue.setAttribute('data-value', value);
if (onChange && isUpdate) {
onChange(getData());
}
}
function getData() {
return { text: selectValue.textContent, value: selectValue.getAttribute('data-value') };
}
function hideSelect() {
select.classList.remove("lm-select-active");
removeDScroll();
setTimeout(function() {
select.classList.remove("lm-select-animating");
}, 400);
}
function handleChange(event) {
var old = select.querySelector('.lm-select-selected');
if (old) {
old.classList.remove('lm-select-selected');
}
selectItem(event.target, true);
hideSelect();
}
function handleOpen() {
setDScroll();
var selected = select.querySelector('.lm-select-selected');
select.classList.add("lm-select-animating");
var scrollTop = (selected.offsetTop + (selected.offsetHeight / 2)) - list.offsetHeight / 2;
list.scrollTop = Math.max(0, scrollTop);
var listTop = Math.min(select.offsetTop - 10, selected.offsetTop - list.scrollTop) * -1
options.style.top = listTop + "px";
setTimeout(function() {
select.classList.add("lm-select-active");
}, 10);
}
function setDScroll() {
bodyScroll = document.body.scrollTop;
if (document.documentElement.clientHeight < document.documentElement.offsetHeight) {
document.documentElement.style.overflowY = "scroll";
}
document.body.style.position = "fixed";
document.body.style.width = "100%";
document.body.style.top = bodyScroll * -1 + "px";
}
function removeDScroll() {
document.body.style.position = "";
document.body.style.width = "";
document.body.style.top = "";
document.body.scrollTop = bodyScroll;
document.documentElement.style.overflowY = "";
}
selectItem(select.querySelector('.lm-select-selected') || select.querySelector('.lm-select-item'));
selectValue.addEventListener('click', handleOpen);
for (var i = 0, l = items.length; i < l; i++) {
items[i].addEventListener('click', handleChange);
}
overflow.addEventListener('click', hideSelect);
return {
get: getData
}
}
......@@ -86,7 +86,7 @@ The pose grouping is followed by a second layer of folders (which you can also s
│ ├── swimsuit.png
│ ├── swimsuit_covered.png
│ └── uniform.png
└── character.json
└── character.yml
```
=== "Cornelia"
......@@ -162,7 +162,7 @@ The pose grouping is followed by a second layer of folders (which you can also s
│ ├── uniform.png
│ ├── uniform_b.png
│ └── yukata.png
└── character.json
└── character.yml
```
### Expressions
......@@ -200,50 +200,46 @@ Now, at this point, if you were to add your assembled character folder to your s
This means data that helps the game display your character correctly, e.g. scaling information, the proper display name, the color of their name and some other things.
The game will run fine without, but your character will have a very barebones setup.
To do this, you will have to create a new file in the folder of the character you want to add metadata to, which should be named `character.json`.
To do this, you will have to create a new file in the folder of the character you want to add metadata to, which should be named `character.yml`.
!!! warning
One some operating systems, file extensions are hidden by default, so you might have trouble seeing what extension your file actually has. Please consult a guide for your specific operating system to figure out how to show these extensions if you're having problems with the game not picking up your newly created file.
Student Transfer operates on a need-to-know basis, meaning that you can add as many or as little of the expected information as you want, depending on how complex you want to get. Every single line save for the first and last in the following example is optional, so keep that in mind while going through it.
Into your newly created `character.json` file you can insert the below template. ==Please note that you will have to change these values before they will work for your character!==
```json
{
"name": "Eliza Hintre",
"name_color": "#915f40",
"voice": "girl",
"eye_line": 0.195,
"scale": 0.6,
"poses": {
"a": {
"facing": "right"
}
}
}
Into your newly created `character.yml` file you can insert the below template. ==Please note that you will have to change these values before they will work for your character!==
```yaml
name: Eliza Hintre
name_color: "#915f40"
voice: girl
eye_line: 0.195
scale: 0.6
poses:
a:
facing: right
```
!!! tip
This is what's called a `JSON` file. These files follow a simple format where the name of a property is on the left-hand side of every statement, followed by a `:` and the actual value of that statement. Take care to only ever edit the right-hand side of these lines as Student Transfer relies on the correct spelling of the property name to detect your data.
This is what's called a `YAML` file. These files follow a simple format where the name of a property is on the left-hand side of every statement, followed by a `:` and the actual value of that statement. Take care to only ever edit the right-hand side of these lines as Student Transfer relies on the correct spelling of the property name to detect your data.
==If you're having problems with the game not accepting your `character.json` file, try running it through a [JSON Validator](https://jsonlint.com), which will show you any errors that you might have made while entering your data.==
==If you're having problems with the game not accepting your `character.yml` file, try running it through a [YAML Validator](http://www.yamllint.com/), which will show you any errors that you might have made while entering your data.==
You can find the same file in the `example` scenario, specifically for the character `eliza`, should you want to take a look at it in the wild or possibly play around with it when it's already set up.
Ideally, every character you add should have one of these files in its folder. It might look intimidating at first, but don't worry, we'll go over what each line does in detail right now.
```json
"name": "Eliza Hintre",
```yaml
name: Eliza Hintre
```
The right-hand-side of this statement defines the name that will actually be shown in-game and it can be anything you want. ==Please try to choose a name that's not already in use to avoid confusion when multiple scenarios are installed.==
```json
"name_color": "#915f40",
```yaml
name_color: "#915f40"
```
This sets the color of the name that will eventually show up in-game. It is a hex-color, so you can use any [color-picker](https://www.htmlcolor-picker.com) you want to either convert whatever color-format you have to hex or just straight-up pick a color you want and copy it in this format.
```json
"voice": "girl",
```yaml
voice: girl
```
This sets the tone of the "beeps" you hear when you enable "voice" in the preferences. There are several voices to choose from, each with a different tone to it, which mainly comes down to pitch in our case:
......@@ -256,30 +252,28 @@ This sets the tone of the "beeps" you hear when you enable "voice" in the prefer
* `girl`
* `child`
```json
"eye_line": 0.195,
```yaml
eye_line: 0.195
```
This should be set to a value ranging from 0 to 1 and determines the offset from the top of the image to the eyes of the sprite. This is only used for the `scry` effect, and you will most likely be able to ignore it entirely and just leave the value at 0. However, if you notice that the scry effect is zooming to the wrong part of the sprite, you can remedy that by adjusting this value.
```json
"scale": 0.6,
```yaml
scale: 0.6
```
This is more of a convenience function. If you character appears too large or too small, you can set a value other than `1.0` to scale them up or down respectively, depending on whether you choose a larger or smaller number.
!!! warning
Try to get as close to a scale of 1.0 as possible. Sprites that are too small will look pixelated in the game while sprites with a much higher resolution waste disk space and will negatively affect performance.
```json
"default_outfit": "uniform",
```yaml
default_outfit: uniform
```
This defines the outfit that your character appears in when nothing else is specified, so it should be an outfit that actually exists. The name should be the name of an outfit you defined in the previous step.
```json
"poses": {
"<pose>": {
"facing": "right"
}
}
```yaml
poses:
<pose>:
facing: right
```
Student Transfer presumes that all your sprites are oriented such that they face to the left side of the image. Should you have added a pose that has the character look the other way (to the right), you'll have to tell the game about this deviation from the norm. This is used within the game to ensure that all characters consistently face the correct way.
For every pose you have that does not face to the left, add the inner part of the above snippet, replacing `<pose>` with the actual name of the pose you want the game to know about. Leave the `"facing": "right"` line as it is.
......@@ -311,7 +305,7 @@ When you create a scenario, you will find two folders next to the `story/` folde
The same goes for the `assets/` folder, except here you don't have to watch the image size or aspect ratio. There is no difference between these two as far as functionality goes, it is merely a way of logically distinguishing between background images and other images you might need that don't fill the whole screen. You can see examples for both kinds of images in the `example` scenario.
The last thing you have to be aware of before you're able to use your custom images in-game is the way they're referenced in the code. The format of the image names generally follows the same rules as were explained above, however to prevent conflicts with other people's images, your image names will be prefixed by a scenario-specific string, mostly just a few characters long, which is defined in the `scenario.json` file of the respective package. In the case of the `example` scenario that would be `example`, so any custom background images would look like this:
The last thing you have to be aware of before you're able to use your custom images in-game is the way they're referenced in the code. The format of the image names generally follows the same rules as were explained above, however to prevent conflicts with other people's images, your image names will be prefixed by a scenario-specific string, mostly just a few characters long, which is defined in the `scenario.yml` file of the respective package. In the case of the `example` scenario that would be `example`, so any custom background images would look like this:
```
example_bg <image name>
```
......
......@@ -6,33 +6,30 @@ In the game you should already be able to see the newly created scenario in the
???+ info
Student Transfer will have to reload when you create a new scenario, so the game might lag or hang for a few seconds after you clicked the button. This is normal and the game will return to working order very quickly, so don't panic.
Now, first things first: Tell us something about your scenario! For this you'll have to open the `scenario.json` file, which will look something like this:
```json
{
"description": "Description",
"title": "Title",
"tags": [
"tag1",
"tag2",
"tag3"
],
"label": "scenario_template",
"version": "0.0.1",
"author": "Author",
"prefix": "example"
}
Now, first things first: Tell us something about your scenario! For this you'll have to open the `scenario.yml` file, which will look something like this:
```yaml
description: Description
title: Title
tags:
- tag1
- tag2
- tag3
label: scenario_template
version: 0.0.1
author: Author
prefix: example
```
This file tells us some meta-data about your scenario and it should be pretty self-explanatory. The items to the left of the `:` tell you what it is that data is used for, the right-hand side is the actual data for that field. If you want to change the title of your scenario for example, you would do this:
```json
"title": "Jamie's very peculiar adventure",
```yaml
title: Jamie's very peculiar adventure
```
???+ warning
Though not strictly necessary, it is recommended to properly version your scenarios. The version string is split into three numbers like this `1.2.3`. Each position has a name: `major.minor.patch`, following the [SemVer standard](https://semver.org).
You do not have to use all three numbers, for example in Student Transfer itself, for a long time we followed a very simple rule using only the `major` and the `minor` numbers:
==If new content is added or this is the first release, increase the `major` number.==
==If no new content is added but resources are changed or code fixes are rolled out, increase the `minor` number.==
Though not strictly necessary, it is recommended to properly version your scenarios. The version string is split into three numbers like this `1.2.3`. Each position has a name: `major.minor.patch`, following the [SemVer standard](https://semver.org).
You do not have to use all three numbers, for example in Student Transfer itself, for a long time we followed a very simple rule using only the `major` and the `minor` numbers:
==If new content is added or this is the first release, increase the `major` number.==
==If no new content is added but resources are changed or code fixes are rolled out, increase the `minor` number.==
We would recommend you stick to the same or at least similar style of versioning, since it will allow your users to easily figure out what the newest version of your scenario is as well as allow the game to properly update your scenario if it's installed via the GUI and already exists.
The only thing you have to watch out for is the `label` part. This should always contain the name of the label that your story starts off in (by default it is `scenario_template`), but you can - *and should* - change that to a more descriptive term. If you adjust it, just take care to change it in this file as well, as this is how the game knows where to start players off in your scenario.
......@@ -52,8 +49,8 @@ label scenario_template:
"Hello World!"
```
Line 1 is the most important for you: `label scenario_template:`
==This is the entry point into your scenario.== Ren'Py works by jumping between "labels", which describe sections of content. To make your new scenario work, you will have to choose a unique label that is not used anywhere else in the game. To make this easier, putting the prefix `scenario_` in front of whatever name you decide on will most likely make it work. Remember that labels are case-sensitive, so `scenario_Test` will not be the same as `scenario_test`. This label is also the one you should put into the `scenario.json` file that we discussed above. If they don't match, your scenario will most likely not function correctly (or at all, for that matter).
Line 1 is the most important for you: `label scenario_template:`
==This is the entry point into your scenario.== Ren'Py works by jumping between "labels", which describe sections of content. To make your new scenario work, you will have to choose a unique label that is not used anywhere else in the game. To make this easier, putting the prefix `scenario_` in front of whatever name you decide on will most likely make it work. Remember that labels are case-sensitive, so `scenario_Test` will not be the same as `scenario_test`. This label is also the one you should put into the `scenario.yml` file that we discussed above. If they don't match, your scenario will most likely not function correctly (or at all, for that matter).
It is necessary at this point to tell, or rather, beg you to please, please put a prefix or suffix in all label names within your scenario, as this greatly reduces the amount of conflicts you might otherwise cause when installed alongside other scenarios. Not everybody can go to `label beach:` at the same time, naturally, even if they really want to.
Here is an example of how a modified version could look like:
......@@ -62,9 +59,9 @@ label scenario_test:
"Hello World!"
```
And within the `scenario.json` file:
```json
"label": "scenario_test",
And within the `scenario.yml` file:
```yaml
label: scenario_test
```
Provided you have changed some meta-data in the `scenario.json` file, if you now restart or reload the game, the default scenario from before should have changed, displaying the text you modified previously. The content of the scenario will obviously be the same as the example scenario since we didn't make any changes on that front yet, but this is the basic starting point for setting up a new scenario.
Provided you have changed some meta-data in the `scenario.yml` file, if you now restart or reload the game, the default scenario from before should have changed, displaying the text you modified previously. The content of the scenario will obviously be the same as the example scenario since we didn't make any changes on that front yet, but this is the basic starting point for setting up a new scenario.
......@@ -45,8 +45,8 @@ If you take a look at the files in the top level, you will see something akin to
- ... (other files)
```
Right, this is a bit more interesting. First off, the files ending on `.rpa` are archive files, a bit like the one you've downloaded this game as, and they contain all the assets that you won't need to have direct access to. It is safe to ignore them for now. The `cache/` folder is also not important, it contains temporary files that Ren'Py uses internally.
For those of you blessed with an especially curious nature, you can take a look at the `images/characters/` folder, where all the individual assets for our character sprites are stored.
Right, this is a bit more interesting. First off, the files ending on `.rpa` are archive files, a bit like the one you've downloaded this game as, and they contain all the assets that you won't need to have direct access to. It is safe to ignore them for now. The `cache/` folder is also not important, it contains temporary files that Ren'Py uses internally.
For those of you blessed with an especially curious nature, you can take a look at the `images/characters/` folder, where all the individual assets for our character sprites are stored.
==The `scenario/` folder is what we're after.== This is where all our work will be based in. If you enter it, you will find there are already some folders in there:
```
- example/
......@@ -65,8 +65,8 @@ These are scenario packages. Each folder beneath the `scenarios/` folder denotes
- music/
- sfx/
- story/
- scenario.json
- scenario.yml
```
If you just want to start writing, then the `story/` folder is where it's at. In it, you will find `.rpy` as well as `.rpyc` files. The good news is that you can ignore 50% of those, as the ones ending on `.rpyc` are, just like the aforementioned `cache/` folder, mere helper files for Ren'Py internally. You can have as many files in there as you need for your scenario, so you can split it up into chunks for easier maintainability once it grows above a size feasible to put into a single file.
If you just want to start writing, then the `story/` folder is where it's at. In it, you will find `.rpy` as well as `.rpyc` files. The good news is that you can ignore 50% of those, as the ones ending on `.rpyc` are, just like the aforementioned `cache/` folder, mere helper files for Ren'Py internally. You can have as many files in there as you need for your scenario, so you can split it up into chunks for easier maintainability once it grows above a size feasible to put into a single file.
The other two folders are for custom images, should you not be able to find what you're looking for in the ones provided by the game. How they work will be explained later on, in the section on [using and adding custom images](characters_and_images.md#images).
......@@ -60,7 +60,7 @@ Let's take a look at a very simple character first, Yui. The following snippets
│ ├── swimsuit.png
│ ├── swimsuit_covered.png
│ └── uniform.png
└── character.json
└── character.yml
```
What you can see in this "graphic" is that essentially, the flat file structure we had before has been pulled out into multiple sub-directories. Basically we have traded super-long, unreadable filenames for shorter, much easier to understand directories and simple filenames. Most of the data is now inferred by StudentTransfer from the folder structure itself and it is much more logically structured. We hope that this change makes it easier for people to understand even the more complex capabilities such as mutations, in the best case simply by looking at the file structure.
......@@ -68,12 +68,10 @@ What you can see in this "graphic" is that essentially, the flat file structure
The below is all the metadata you need for Yui. Only the most perceptive of you will notice the almost uncanny resemblance to the previous format.
??? example "Character data for Yui"
```json
{
"name_color": "#77569c",
"eye_line": 0.04,
"scale": 0.9
}
```yaml
name_color: "#77569c"
eye_line: 0.04
scale: 0.9
```
And now, for something a bit more complex. Cornelia is our most complicated character at the moment, so watch what happens when we apply the new character format:
......@@ -151,7 +149,7 @@ And now, for something a bit more complex. Cornelia is our most complicated char
│ ├── uniform.png
│ ├── uniform_b.png
│ └── yukata.png
└── character.json
└── character.yml
```
This already looks a bit more complicated but if you take a closer look, you'll notice that it is mostly the same as for Yui. This structure especially showcases how mutations work, as they are basically a priority set of images that gets chosen over the default one when a specific outfit is active.
......@@ -159,21 +157,24 @@ This already looks a bit more complicated but if you take a closer look, you'll
And this is all the metadata you need for Cornelia. You can see that we have to enter a bit more information since we have a few special cases in here, e.g. sprites facing the wrong direction as well as several mutations.
??? example "Character data for Cornelia"
```json
{
"name_color": "#fbca86",
"voice": "child",
"eye_line": 0.1,
"scale": 0.9,
"poses": {
"b": {
"facing": "right"
}
},
"mutations": {
"twintails": ["casual", "costume", "swimwear", "uniform", "uniform_b", "yukata", "cheer", "cheer_inverted"]
}
}
```yaml
name_color: "#fbca86"
voice: child
eye_line: 0.1
scale: 0.9
poses:
b:
facing: right
mutations:
twintails:
- casual
- costume
- swimwear
- uniform
- uniform_b
- yukata
- cheer
- cheer_inverted
```
There is one important piece of information you should keep in mind: ==The new format does not add or remove any images from custom characters, it only rearranges them.== The only thing that has changed with the new v4 format is the layout of the files on disk. Any existing character that works in StudentTransfer v3.1 will work in v4 just by rearranging the existing files.
......@@ -197,16 +198,13 @@ For those brave few souls that actually want to run the script themselves, you c
There are only two things you as the scenario author will have to do once Harry Porter has finished running:
==If you have characters with poses that are, by default, facing to the right (meaning their body is turned to the right-hand side of the image), you will have to let the game know about this deviation from the norm.==
Either you flip your character images or, the simpler solution, you go into the "character.json" file for the affected character and add the following snippet:
```json
"poses": {
"a": {
"facing": "right"
},
"b": {
"facing": "right"
}
},
Either you flip your character images or, the simpler solution, you go into the "character.yml" file for the affected character and add the following snippet:
```yaml
poses:
a:
facing: right
b:
facing: right
```
You only have to add poses that are facing the right-hand side of the image. Simply add the affected poses with the template above and you'll be good to go.
......@@ -214,19 +212,16 @@ You only have to add poses that are facing the right-hand side of the image. Sim
The other thing you have to do might be slightly more tedious, depending on how good you've been with managing character images so far. Since image sizes are now inferred from the actual image files themselves (instead of you having to manually enter them), ==we do not support characters with legs anymore==. Well, we still do, but they will show up with their legs intact and reach out of the top of the screen because of that. As such, you will have to go through and pull a Katawa Shoujo and remove the legs from all characters that have them.
If you really, really can not be assed to do that (even though it is the preferred option and will save on file size as well), ==you are still able to specify crop values like you could in the old format==, like this:
```json
"poses": {
"a": {
"facing": "right",
"image_width": 123,
"image_height": 456,
"center_width": 789,
"center_height": 1011,
},
"b": {
"facing": "right"
}
},
```yaml
poses:
a:
facing: right
image_width: 123
image_height: 456
center_width: 789
center_height: 1011
b:
facing: right
```
All of these are optional, so only supply the ones you actually need, and only if you *really* need them.
......@@ -2,17 +2,17 @@
With v4 of Student Transfer, the game now finally supports playing scenarios on Android. While a large part of the system remains the same, there are a few peculiarities specific to the Android version which will be explained here.
## Installing Scenarios on Android
Generally, all scenarios that can be installed on the PC or Mac version of the game will also work on Android. However, installing them follows a slightly different procedure due to how Android operates in comparison to more desktop-oriented systems.
Generally, all scenarios that can be installed on the PC or Mac version of the game will also work on Android. However, installing them follows a slightly different procedure due to how Android operates in comparison to more desktop-oriented systems.
There are two main ways of installing scenarios which cater for different use cases.
### Online Scenario Mode
With the introduction of scenarios on Android we have also introduced something we call "Online Scenario Mode" (abbreviated `OSM`). This is basically a service which integrates a simple way of installing a curated set of scenarios via the in-game menu. It can be accessed by enabling `Online Scenario Mode` in the preferences of the game, at which point a new tab will become available in the scenario menu called `Online`.
It will list all the scenarios that are available and they can be installed by selecting them and clicking the `Install` button. Nothing more is required.
With the introduction of scenarios on Android we have also introduced something we call "Online Scenario Mode" (abbreviated `OSM`). This is basically a service which integrates a simple way of installing a curated set of scenarios via the in-game menu. It can be accessed by enabling `Online Scenario Mode` in the preferences of the game, at which point a new tab will become available in the scenario menu called `Online`.
It will list all the scenarios that are available and they can be installed by selecting them and clicking the `Install` button. Nothing more is required.
Scenarios showcased in this way are selected at the discretion of the development team, so not all scenarios will be available. However, you can expect scenarios made available through this mode to work and be relatively bug-free, but of course we can't guarantee they will work 100% of the time.
### Manual Installation
While Online Scenario Mode is a convenient way to install a subset of all available scenarios, sometimes you might want to play one that is not featured on `OSM` yet. It is because of this that we provide support for manually installing scenarios as well.
The general process works the same as on the desktop: You download a scenario package, extract its contents and then move the resulting folder into the location where Student Transfer loads scenarios from.
While Online Scenario Mode is a convenient way to install a subset of all available scenarios, sometimes you might want to play one that is not featured on `OSM` yet. It is because of this that we provide support for manually installing scenarios as well.
The general process works the same as on the desktop: You download a scenario package, extract its contents and then move the resulting folder into the location where Student Transfer loads scenarios from.
The part that's different is finding out where exactly the scenarios folder of the game is stored, as it can vary between devices and flavors of Android. ==In most cases however, the location of Student Transfer's game files will be something akin to the following:==
```
/storage/emulated/0/Android/data/com.tfgamessite.cobaltcore.studenttransfer/files
......@@ -31,10 +31,10 @@ scenarios/
├── example/
| ├── story/
| ├── ... (some more folders of this scenario)