Andor's Trail Wiki

Open-source roguelike RPG on Android

User Tools

Site Tools


andors_trail_wiki:developer_section:quest_making

Quest Creation Tutorial

This document describes the concepts of what makes up a quest in Andor’s Trail. It is aimed to be a tutorial for those that want to help create new quests in Andor’s Trail.

This area will help you learn to develop quests for Andor's Trail. Be sure that you have read the World & Lore sections to assist with tying your idea in with current story lines.

Please check the list of available NPC's before crossing your quest idea with another.

Prerequisites

  • You should of course have played the game a bit, to get a feel for the types of quests that are in the game right now.
  • You should have a basic understanding of the content editor http://andors-trail.googlecode.com/git/AndorsTrailEdit/editor.html (all details of it are not required, this document aims to explain most of it)
  • You should have a basic understanding of Tiled, the map editor http://www.mapeditor.org/
  • You love creating creative content
  • Expect to write a lot of conversation text :)

Overview

The mini-howto on making a quest for Andor’s Trail is something like this:

1. Define the structure

  • a. Think up a general idea for what the quest should be about.
  • b. Create a rough flowchart for the different choices and actions that the player may take.
  • c. Write the quest log.

2. New content

  • a. Create NPCs using the content editor.
  • b. Place NPCs on maps.
  • c. Create quest items and droplists using the content editor.

3. Dialogue

  • a. Write the dialogue for all involved NPCs.

4. Publish

The above steps will be described in detail below.

The hardest part is by far to write the dialogue. Writing the dialogue is also the most interesting parts, since it allows you to customize the tone of the quest, and the characteristics of each involved NPC. It’s the story-telling part of the quest.

NPC=Non player character. For example, a quest giver.

Define the Structure

General idea

In order to come up with an idea for a quest, there are a lot of good resources:

http://www.squid.org/rpg-random-generator
http://dicelog.com/yafnagen
http://donjon.bin.sh/name/

For this tutorial, we will assume that we want to create a quest called “You remind me of the babe, what babe?“, where the player is requested to retrieve a lost treasure map from a goblin cave. The quest will be given by an NPC named Cileth in Fallhaven (this quest is not actually present in Andor’s Trail). The player will be given a choice of whether to spare the goblin boss monster or not.

Flowchart

A flowchart is an excellent tool to use to help concretize the structure of the quest. It is of course not always needed, but can sometimes help a lot when planning the high-level structure of a quest, in order to not get stuck on the details too quick.

For the existing quests in Andor’s Trail, various tools have been used to create flowcharts for quests, but the tool that (at the moment) wins in ease-of-use is to create a drawing in Google Docs.

For the example quest in this tutorial, a flowchart could look something like this:

quest_making1.jpg

The flowchart will be an excellent help when writing the quest log, in the next section.

The quest log

In the game, the text you see on the character screen, in the “Quests” tab, is called the quest log. Each quest is composed of several stages, where each stage has an assigned internal integer value. As the player progresses in a quest, their progress will be represented by which of these stages that the player has reached.

Looking at the flowchart for the quest, each box will be represented by roughly one quest stage.

As you may have noticed, the quest log is always written in the first tense, as if the player wrote the text. For example, “I saw a ..” compared to “The player saw a ..”.

Our quest with Cileth might have these quest stages:

Stage Description
10 I heard a rumor in Fallhaven about someone named Cileth that lost something very valuable.
20 I talked to Cileth in Fallhaven. She told me about a treasure map that she used to have, but that she lost it a few days ago, while travelling in the wilderness.
30 I have agreed to help Cileth find the treasure map.
40 Cileth believes that she might have lost the map near a cave that was filled with goblins, outside the village. She has given me directions to the cave. I should go there and search for the map inside.
50 Inside the goblin cave, I encountered a goblin named Olgnuur. Olgnuur told me that the goblins just want to be left alone by all the pesky humans. If I leave him alone, he will give me the map.
60 I have agreed to spare Olgnuur’s life.
65 Olgnuur has given me the map.
70 I have started attacking Olgnuur. I should return with the map to Cileth once Olgnuur is dead.
80 I have given the map back to Cileth.
90 Cileth rewarded me with some gold for helping her recover the map.

In this example, notice that the player might choose to reach 60, but never 70, or vice versa.

The stage values can be anything, as long as they are positive numbers. They will be presented in the player’s quest log in order by stage value.

To implement this quest log in the game, we’ll have to write it as a quest in the Andor’s Trail Content editor: http://andors-trail.googlecode.com/git/AndorsTrailEdit/editor.html

quest_making2.jpg

The internal id can be anything that uniquely defines this quest, it will not be shown to the player. Notice how stage 65 and 90 also gives some exp rewards when reached. Also notice how stage 90 marks the quest as completed.

In the content editor, notice the “Export” button in the top right. The output from that is what will go into the actual game files when this quest is integrated in the game later. The export of this quest looks like this:

[id|name|showInLog|stages[progress|logText|rewardExperience|finishesQuest|]|];
{cileth_map|You remind me of the babe, what babe?|1|{
    {10|I have heard a rumor in Fallhaven about someone named Cileth that has lost something very valuable.|||}
    {20|I have talked to Cileth in Fallhaven. She told me about a treasure map that she used to have, but that she lost a few days ago, while travelling in the wilderness.|||}
    {30|I have agreed to help Cileth find the treasure map.|||}
    {40|Cileth believes that she might have lost the map near a cave that was filled with goblins, outside the village. She has given me directions to the cave. I should go there and search for the map inside.|||}
    {50|Inside the goblin cave, I encountered a goblin named Olgnuur. Olgnuur told me that the goblins just want to be left alone from all the pesky humans. If I spare his life, he will give me the map.|||}
    {60|I have agreed to spare Olgnuur\'s life.|||}
    {65|Olgnuur has given me the map.|500||}
    {70|I have started attacking Olgnuur. I should return with the map to Cileth once Olgnuur is dead.|||}
    {80|I have given the map back to Cileth.|||}
    {90|Cileth rewarded me with some gold for helping her recover the map.|500|1|}
    }|};

Adding new content

Creating NPCs

The example quest in this tutorial will require three unique NPCs that progress the quest. There is the NPC that will provide the rumor about Cileth having lost something, there is Cileth herself and there is the goblin boss monster Olgnuur.

Each of these NPCs can be created in the content editor:

Cileth

quest_making3.jpg

Olgnuur

quest_making4.jpg

The export of them (visible from the export button in the top right of the content editor) is as follows:

[id|iconID|name|tags|size|monsterClass|unique|faction|maxHP|maxAP|moveCost|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance|damageResistance|droplistID|phraseID|hasHitEffect|onHit_boostHP_Min|onHit_boostHP_Max|onHit_boostAP_Min|onHit_boostAP_Max|onHit_conditionsSource[condition|magnitude|duration|chance|]|onHit_conditionsTarget[condition|magnitude|duration|chance|]|];
{gwend|monsters_rltiles3:14|Gwend|gwend||0|||||||||||||||gwend||||||||};
{olgnuur|monsters_rltiles2:129|Olgnuur|olgnuur|1x1|0|1||69|10|5|3|140|20|2|2|13|60||olgnuur|olgnuur||||||||};
{cileth|monsters_rltiles1:68|Cileth|cileth||0|||||||||||||||cileth||||||||};

Notice that Olgnuur has combat stats, since the player might choose to fight him. Cileth, on the other hand, does not have combat stats since there will be no option to fight her.

Most fields in the monster/NPC editor should be self-explanatory, but some fields worth noting are:

Field Description
Internal Id Can be just about anything, just make it uniquely define the monster. Will not be visible to the player. Prefer short values in lower-case. Use _ instead of spaces.
Spawngroup This value will be used on the map where the monster or NPC is spawned, to define what types of monsters the spawn area should contain. For example, if several monsters specify their spawngroup as “goblin1”, and the map contains an area that spawns group “goblin1”, the game will randomly select one monster type from that group to spawn there. For NPCs, this value should be equal to their internal id.
Faction Advanced feature for quests that causes monsters to become hostile without interacting with them. Just leave it empty for now.
Conversation Phrase ID The id of the starting Phrase that will be shown when the player engages in dialogue with the NPC. See “Dialogue” below for more info on what a Phrase is defined as. For NPCs, prefer to name the starting phrase to the same as the NPC’s internal id.
Droplist ID The id of the list of things that will be dropped when the player kills the monster or NPC. See “Quest items” below for more info.

Also note that Olgnuur has a droplist specified, but Cileth does not have one. This is again because Cileth will not be available for combat - only Olgnuur will.

Placing NPCs

The maps in Andor’s Trail are created by using Tiled as map editor. http://www.mapeditor.org/

To place these three NPCs on maps, we open the map files using Tiled, and create object areas of type “spawn” where they should be placed:

Placing gwend (the NPC that tells the rumor)

quest_making5.jpg

Placing Cileth:

quest_making6.jpg

Notice that the names of the spawn areas on the maps correspond to the values in the “spawngroup” field of the monsters that should be spawned there.

Quest items

This quest will require a quest item, the map that Cileth has lost. It can be created in the content editor:

quest_making7.jpg

The export of this item (visible from the export button in the top right of the content editor) is as follows:

[id|iconID|name|category|displaytype|hasManualPrice|baseMarketCost|hasEquipEffect|equip_boostMaxHP|equip_boostMaxAP|equip_moveCostPenalty|equip_attackCost|equip_attackChance|equip_criticalChance|equip_criticalMultiplier|equip_attackDamage_Min|equip_attackDamage_Max|equip_blockChance|equip_damageResistance|equip_conditions[condition|magnitude|]|hasUseEffect|use_boostHP_Min|use_boostHP_Max|use_boostAP_Min|use_boostAP_Max|use_conditionsSource[condition|magnitude|duration|chance|]|hasHitEffect|hit_boostHP_Min|hit_boostHP_Max|hit_boostAP_Min|hit_boostAP_Max|hit_conditionsSource[condition|magnitude|duration|chance|]|hit_conditionsTarget[condition|magnitude|duration|chance|]|hasKillEffect|kill_boostHP_Min|kill_boostHP_Max|kill_boostAP_Min|kill_boostAP_Max|kill_conditionsSource[condition|magnitude|duration|chance|]|];
{cileth_map|items_books:9|Cileth\'s map|31|1|1|0|||||||||||||||||||||||||||||||||};

To make Olgnuur drop this particular item when he is killed, we’ll create a droplist in the content editor:

Droplists

quest_making8.jpg

Olgnuur will drop a few more items when killed, to provide some loot for the player to collect. Notice how all of them have 100% chance of being dropped, and in particular the map itself - it could break the quest if Olgnuur could be killed but would not always drop the map. Quest items should always have 100% drop chance.

The export of this droplist is as follows:

[id|items[itemID|quantity_Min|quantity_Max|chance|]|];
{olgnuur|{
    {gold|4|12|100|}
    {gem2|1|1|100|}
    {health|1|1|100|}
    {cileth_map|1|1|100|}
    {gloves1|1|1|100|}
    }|};

Notice how the internal id of the droplist corresponds to the field “droplist id” in the monster that we defined previously.

Dialogue

The conversations are always triggered by the player engaging conversation with a NPC, or the player stepping on certain tiles. As you noticed in the monster/NPC editor above, all NPCs will have a phrase ID that points to the start of the conversation of that particular monster type.

A conversation can be seen as a large state-chart of phrases that the player visits by selecting different replies. An example of a conversation tree could be like this:

quest_making9.jpg

Notice how the conversation flow can return to places where it has already been, and how some conversation choices can lead to more interesting stories. This is all done in Andor’s Trail by what is aptly named phrases and replies.

To create a conversation in Andor’s Trail, you don’t have to draw a flowchart like the one above - this one is only for illustrative purposes in this tutorial.

Phrases & Replies

Looking at the flowchart above, let’s call each blue box that the NPC speaks a Phrase, and let’s call the text on each arrow a Reply. The basic structure for conversations in Andor’s Trail is that a conversation is made up of a list of these phrases, where each phrase has a list of possible replies. Each reply defines what the next phrase will be, and each phrase defines what available replies there are.

First, we assign an internal id to each phrase. It can be just about anything, as long as the id is unique over the whole game. Prefer to use the NPC’s name to make it unique, and prefer short ids in lowercase.

quest_making10.jpg

Let’s take one phrase from the above flowchart as an example of how just one phrase is represented:

quest_making11.jpg

This phrase defines three possible replies, and can be represented in the following format:

——————————————————– formatting problem

Phrase id Text Possible replies
gwend_2 How may I help you? Reply text Next phrase id
That looks good, what is that you are having? gwend_4
What do you do here? gwend_3
Who are those noisy fellas over there? gwend_5

Another phrase might look like this:

quest_making12.jpg

Which, in turn will be represented as the following information:

Phrase id Text Possible replies
gwend_5 I don't know. They do make a lot of noise though. Reply text Next phrase id
Have you heard anything interesting from their shouting? gwend_6
Let's go back to the other questions I had. gwend_2

By separating the phrases from the replies, the whole conversation can then be defined like this - as just the list of phrases and what replies they have. In fact, all conversations in Andor’s Trail is stored as a huge list of phrases, with info on their corresponding replies, that in turn lead to other phrases.

Special phrases & replies

As explained in the previous chapter, replies will lead to new phrases. In some cases, we want the something else to happen when selecting a reply, such as ending the conversation or initiating combat. For these special actions, there are some predefined phrase id:s that the replies can point to:

Phrase id Meaning
X Conversation ends.
S Conversation ends, and the player will start trading with the NPC that the conversation is with. The NPC will have items to sell available according to the droplist of the NPC.
F The NPC that the conversation is with will be turned hostile. Combat starts with the player having the first turn.
R Conversation ends, and the NPC that the conversation is with will be removed from the current map.

These special phrase ids are not actually visible in the content editor. Instead, they are added if you select anything other than “NPC phrase in the drop-down list labelled “phrase leads to”.

Also, sometimes we just want to do a chain of phrases, where an NPC can speak several phrases while the player just clicks the “Next”-button to advance conversation. This is done by creating just one reply for a phrase, and using only an upper-case N as reply text (visible in the editor by the checkbox “Phrase leads directly to another phrase without replies”).

Reply text Meaning
N The player will only need to click “Next” to continue on to the next phrase.

Phrase rewards

To create an actual quest using these conversations, we would need more things to happen other than just having the NPCs speak. For this, we introduce the concept of phrases being able to give rewards when reached. Rewards do not necessarily have to be positive of course :)

Each phrase has a list of rewards that will all be awarded as soon as the player reaches the phrase.

The possible types of rewards that a phrase can give are currently:

Type Meaning
Quest progress The player advances to a specified stage for some quest.
Droplist Player will be given all items specified by a droplist.
Skill Player will be given a level increase to a skill. Can of course also include skills that are otherwise hidden (quest skills).
Actor condition Either the player can be given one magnitude of a condition (poison/bless/rotworms), or the player can have a condition completely removed.
Faction change The player’s faction score for a certain faction can be changed. If a faction score reaches below zero, all monsters of that faction will be automatically turned hostile.

The most common type is of course to advance some quest for the player. Each reward will define an id and a value of the reward. The values in id and value mean different things, depending on the type of reward:

Type Meaning of “id” and “value”
Quest progress id = The quest id that will be advanced. value = The quest stage that the player now has reached.
Droplist id = The id of the droplist that contains items that the player will be given. value = unused.
Skill id = internal id of the skill that will be awarded. value = unused.
Actor condition id = The internal id of the actor condition that will be added/removed. value = The duration of the condition. If specified as 999, it will be a permanent condition. If specified as -99, the condition will be removed. Unless value is 999 or -99, the condition will be added as magnitude 1.
Faction change id = Internal id of the faction whose score should change. value = How much the faction score should change.

Reply conditions

To create a complete quest, we also need some way of restricting the choices that the player can make, until some conditions have been reached. For example, the player should of course not be able to select the reply “Here is your map” while talking to Cileth until the player actually has the map itself.

For this, we use a concept of conditions that each reply might have. In order for a reply to be visible to the player, all of the conditions must be satisfied. For example, the reply “Here is your map” while talking to Cileth, might have a condition saying that the player both has to have the map in inventory, and also must have reached stage 30 of the quest (to ensure that the quest has been started).

Replies can require these types of conditions:

Condition type Meaning
Inventory (removed) The player must have some item in inventory, and the item(s) will be removed from the player’s inventory by selecting the reply.
Inventory (kept) The player must have some item in inventory. The item(s) will not be removed from the player’s inventory by selecting the reply.
Worn The player must currently be wearing some item in any of the equipment slots. The item will not be removed by selecting the reply.
Quest progress The player must have reached a specific stage in some quest in order to select the reply. Quest stages are typically specified as questname:stage. For example “cileth_map:30” means stage 30 in the quest with internal id “cileth_map”.

It is important to note that a reply will be completely hidden for the player if at least one of the conditions are not satisfied.

Advanced special-cases

The concept of phrases & replies is also used in Andor’s Trail to build complex conditional branching logic. Let’s look at the goblin boss monster Olgnuur’s dialogue as an example:

quest_making13.jpg

This is the basic conversation flow for Olgnuur. To make him use this conversation, we would put “olgnuur_1” as starting phrase in the definition of him as a monster, in the monster editor. However, consider these special cases that may occur:

  • The player might press “Leave” at any time during the conversation.
  • The player might return to speak with Olgnuur again after receiving the map.
  • The player might not have started the quest at all.

In all of these cases, if we were to start conversation at “olgnuur_1” every time, the conversation would feel really odd - Olgnuur would seem to have a really bad memory. To fix this, we need to have some way of making the conversation start at different phrases depending on what the player has done earlier.

This is done in Andor’s Trail by using a phrase that does not have any display text. For phrases that do not have display text, the game will automatically advance conversation to the first reply that has all of its reply conditions satisfied. This way, we can re-use the concept of reply conditions discussed in the previous chapters, into creating a complex quest.

We introduce one of these phrases that does not have any display text at the start of Olgnuur’s dialogue:

quest_making14.jpg

Notice how reply 1 goes to phrase “olgnuur_2”, which in turn will engage combat. Reply 1 will be selected if the player previously has reached quest stage 70. Reply 2 will be selected if the player has reached stage 65, by previously receiving the map from Olgnuur, and will cause Olgnuur to just say that he wants to be left alone again. Reply 3 will be selected if the player has started the quest, but has not talked to Olgnuur before, and will trigger the conversation tree started by the phrase “olgnuur_1”. The fourth reply does not have any conditions, and will be selected if neither of the other replies were selected - for example, if the player has not started the quest at all.

Note that the game selects the replies in order, and proceeds with the first one where all conditions are satisfied.

This way, by placing phrase “olgnuur” as starting phrase, we can handle all the special cases described above. The game will evaluate the phrase “olgnuur” and pick one of its replies automatically, which will make Olgnuur say different things when starting his conversation. It will seem like he has remembered previous conversations.

Enough with the theory, let’s see an example!

Gwend’s dialogue

Gwend’s dialogue from chapter 4.1 above is implemented in the dialogue editor: http://andors-trail.googlecode.com/git/AndorsTrailEdit/editor.html

quest_making15.jpg

The export of this conversation is as follows:

[id|message|rewards[rewardType|rewardID|value|]|replies[text|nextPhraseID|requires_Progress|requires_itemID|requires_Quantity|requires_Type|]|];
{gwend|||{
    {|gwend_r|cileth_map:10||||}
    {|gwend_1|||||}
    }|};
{gwend_r|Hello again.||{{N|gwend_2|||||}}|};
{gwend_1|Hello there, I am Gwend.||{{N|gwend_2|||||}}|};
{gwend_2|How may I help you?||{
    {That looks good, what is that you are having?|gwend_4|||||}
    {What do you do here?|gwend_3|||||}
    {Who are those noisy fellas over there?|gwend_5|||||}
    }|};
{gwend_3|Oh, not much. Just enjoying my meal.||{{That looks good.|gwend_4|||||}}|};
{gwend_4|Yes, it sure is good. Boar stew.||{{N|gwend_2|||||}}|};
{gwend_5|I don\'t know. They do make a lot of noise though.||{
    {Let\'s go back to the other questions I had.|gwend_2|||||}
    {Have you heard anything interesting from their shouting?|gwend_6|||||}
    }|};
{gwend_6|Oh yes, there was one thing I heard.||{{N|gwend_7|||||}}|};
{gwend_7|One of them spoke of someone called Cileth, or something like that.||{{N|gwend_8|||||}}|};
{gwend_8|Apparently, she lost something valuable to her.||{{N|gwend_9|||||}}|};
{gwend_9|I did not hear what it was though, but they sure seemed eager to recover it.||{{N|gwend_10|||||}}|};
{gwend_10|I\'m sure they said her name was Cileth. She lives somewhere here in Fallhaven apparently.|{{0|cileth_map|10|}}|{{N|gwend_11|||||}}|};
{gwend_11|That\'s all I know. Now, if you\'ll excuse me, I would really like to finish my meal here.||{{Thank you|X|||||}}|};

Notice how the export data contains each phrase with its replies on separate lines. The first phrase, with id “gwend”, is a phrase without text, as explained in section 4.5. Since it does not have any text, it will automatically select the first reply that matches. In this case, it means that if the player has reached stage 10 in quest “cileth_map”, Gwend will start talking at phrase “gwend_r”. Otherwise, gwend will start talking at “gwend_1”.

Notice also how phrase “gwend_10” rewards the player with a quest progress. If the player reaches this phrase, then stage 10 of quest “cileth_map” will be shown in the player’s quest log.

Olgnuur’s dialogue

Olgnuur’s conversation from section 4.5. is also implemented in the content editor:

quest_making16.jpg

The export of this conversation is as follows:

[id|message|rewards[rewardType|rewardID|value|]|replies[text|nextPhraseID|requires_Progress|requires_itemID|requires_Quantity|requires_Type|]|];
{olgnuur|||{
    {|olgnuur_2|cileth_map:70||||}
    {|olgnuur_friend_1|cileth_map:65||||}
    {|olgnuur_1|cileth_map:40||||}
    {|olgnuur_notstarted|||||}
    }|};
{olgnuur_friend_1|You promised you leave us alone!||{{Sorry, just wanted to say hi again.|X|||||}}|};
{olgnuur_notstarted|Go away, pesky human!|||};
{olgnuur_1|Olgnuur angry!||{
    {Why are you angry, Olgnuur?|olgnuur_4|||||}
    {Die, goblin scum!|olgnuur_2|||||}
    }|};
{olgnuur_2|Aargh, you one of them! You not rule us!||{{N|olgnuur_3|||||}}|};
{olgnuur_3|Goblins kill you!|{{0|cileth_map|70|}}|{{N|F|||||}}|};
{olgnuur_4|Pesky humans. Always coming here, stealing our things.||{{N|olgnuur_5|||||}}|};
{olgnuur_5|Killing our cattle.||{
    {I am not here to steal anything. I am looking for a map.|olgnuur_6|||||}
    {Die, goblin scum!|olgnuur_2|||||}
    }|};
{olgnuur_6|Map? Olgnuur has map.||{
    {What do you want in return for that map?|olgnuur_7|||||}
    {Good. I\'ll get that map from your cold corpse.|olgnuur_2|||||}
    }|};
{olgnuur_7|Pesky humans. Never leaving us alone.||{{What if I were to leave you alone, would you give me the map then?|olgnuur_8|||||}}|};
{olgnuur_8|Oh yes. Leave us alone. Yes. Map give.|{{0|cileth_map|50|}}|{
    {Ok. I\'ll leave you alone, and you give me that map.|olgnuur_9|||||}
    {How about I just kill you instead?|olgnuur_2|||||}
    }|};
{olgnuur_9|Good. Here is map.|{{0|cileth_map|60|}{0|cileth_map|65|}{1|olgnuur_map||}}|{{Thank you.|X|||||}}|};

Some things to note in this conversation is the first phrase “olgnuur”, that also is a phrase without text, as described in section 4.5 above. It will ensure that Olgnuur’s dialogue starts with the correct phrase. Also note how phrase “olgnuur_9” gives two quest rewards, and a reward of a droplist with id “olgnuur_map” (which will have to be created).

Cileth’s dialogue

Cileth’s conversation is left as an exercise for the reader. Consider what should happen when engaging conversation with her during the different stages of the quest. How could you make sure that Cileth only gives her reward once?

Publish

Once you have the quest ready, including the conversations, monsters and items required for it, you should be ready to publish it so that it can be included in the game. What should be posted is the exports from the content editor of the things that make up your quest; conversations, quest log, items, droplists and monsters.

The most common way of getting input is posting it on the game forums, and encouraging people to give feedback on it. You are also welcome to email it to someone in the development team, or post your quest on the issue tracker: http://code.google.com/p/andors-trail/issues/list

We are really looking forward to reading your content! Good luck with your quest!

andors_trail_wiki/developer_section/quest_making.txt · Last modified: 2018/12/19 02:37 (external edit)