Core Libraries | Monogatari Documentation
# Core Libraries | Monogatari Documentation [Advanced: Monogatari Development](.)Core LibrariesGet the best of Monogatari by learning more about the libraries it uses on its core!On the very core of Monogatari, you'll find it has been built around the Aegis Framework libraries. These libraries are used both for functionality and styling so learning more about them will allow you to create even more amazing thingsArtemis: JavaScript LibraryArtemis is used for DOM manipulation, storage, …
Unclaimed Agent
Are you the maintainer? Claim this agent to manage its listing and increase its trust score.
# Core Libraries | Monogatari Documentation [Advanced: Monogatari Development](.)Core LibrariesGet the best of Monogatari by learning more about the libraries it uses on its core!On the very core of Monogatari, you'll find it has been built around the Aegis Framework libraries. These libraries are used both for functionality and styling so learning more about them will allow you to create even more amazing thingsArtemis: JavaScript LibraryArtemis is used for DOM manipulation, storage, HTTP requests handling, text transformations and moreKayros: CSS LibraryKayros is the very base of Monogatari's style and lookPandora: Web Components LibraryPandora is what allows monogatari to have self contained, custom HTML elements and allows them to have custom functionality.[PreviousWeb](releasing-your-game/web.md)[NextArtemis](advanced-monogatari-development/core-libraries/artemis.md)Last updated 5 years agoWas this helpful? # Script & Labels | Monogatari Documentation [Building Blocks](.)Script & Labels[](.)OverviewThe script is the soul and body of your game, it is the place where you define everything that happens on it. It is made out of labels that are just like the chapters of a book and inside of each label, you have a list of statements that will be run one by one as your story unfolds.[](.)ScriptA monogatari script is nothing more than a JSON object. In simple terms, a JSON object is defined with two curly braces {}. Inside these curly braces, you can have a list of named properties that point to a value. For example, if we wanted to define a person using the JSON notation, we could do something like this:Copyconst person = { 'name': 'Jane Doe', 'age': 24 };This is what we call key/value pairs, where a key like name or age points to a value like Jane Doe or 24. Notice how each key/value pair has a comma in the end separating it from the next one. Missing commas is the #1 issue that people have on their scripts.In Monogatari's case, your script is a list of keys (the names of the [labels](building-blocks/script-and-labels.md) on your game) and values (the list of statements these labels are made of). For example:Copy{ 'Start': [ 'Hi there!', 'This is a list of statements', 'jump myLabel' ], 'myLabel': [ 'And this is yet another label', 'Also a list of statements', 'end' ] }Now, while that object does defines a monogatari script, just by itself it doesn't do anything. You must use the script function to let monogatari know what's your script:Copymonogatari.script ({ 'Start': [ 'Hi there!', 'This is a list of statements', 'jump myLabel' ], 'myLabel': [ 'And this is yet another label', 'Also a list of statements', 'end' ] });By default, the script.js file contains the initial script for any monogatari game and you can build your game from there by adding more content and removing the old one.[](.)LabelsLet's take a look at an incredibly simple script, this is the most basic script a game could have:Copymonogatari.script ({ 'Start': [ 'This is a statement.', 'end' ] });Notice that Start string. By default, Start is the first label that Monogatari will play when a game starts. As you can see, this label is pointing to a list of statements that monogatari will run one by one.In programming terms, a label is As we said before, labels are just like the chapters of a book but can also provide you with logical ways of dividing your game. [](.)Jumping between labelsAs books can have many chapters, you can have many labels as well! Not only that, you can make your game move from one label to another using the [Jump action](script-actions/jump.md).Copymonogatari.script ({ 'Start':[ 'This is a statement.', 'jump mylabel' ], 'mylabel':[ 'This is another statement.', 'Pretty easy huh?', 'end' ] });As you can see from this script, we have two labels, Start and mylabel. Players will start in the Start one by default which will print out the first dialog and when the player clicks to advance, it will reach the jump mylabel statement. When reached, the game will simply carry on with the statements inside the mylabel label without the player ever noticing something happened.[](.)Translating your ScriptMaking your game available in many languages is super simple, head over to the Internationalization guide to learn more.[Internationalization](configuration-options/game-configuration/internationalization.md)[](.)Splitting your script in multiple filesAs your game script grows, having it all in the same file might become troublesome. Splitting your script into multiple files is a good way to get organized and make it less cluttered. Learn how to do this in the Split files section.[Split Files](configuration-options/split.md)[PreviousDiagnosing Errors](diagnosing-errors.md)[NextCharacters](building-blocks/characters.md)Last updated 4 years agoWas this helpful? # Credits Screen | Monogatari Documentation [Components](.)Credits ScreenThe credits page so players can see who made the game[](.)DescriptionCopy<credits-screen></credits-screen>Attribution is important. People like knowing who made the games they love, and collaborators deserve credit for their work.Copymonogatari.configuration ('credits', { "Developers": { "Artist": "Yui", "Scenario Writer": "Gale", }, "Donors": { "Top Level Donors": [ "Tommy", "Scotty", "Lanny", "Robby", "Josie", "Freddy", "Bobby", "Lindsey" ], "Patrons": [ "Alex", "Shine", "Mika" ], }, "Special Thanks to": { "My parents": ["Mom", "Dad"], "My siblings": ["Brother", "Sister"], } });This will create a credits page that looks like this!A credits page listing all of the people written in the example code above.As you can see, Credits are stored as an object, just like most things in Monogatari. Also, like most things in Monogatari, the Credits page supports HTML, so if you want to make each of those names a clickable link that leads to the person's website, or email address, or something like that, you can easily achieve all of these things by inserting hyperlinks, like:Copymonogatari.configuration ('credits', { Developers: { Coder: "<a href="https://monogatari.io">Hyuchia</a>", } });If the credits object in your script.js file is empty, the credits button will not appear. No need to worry the User with an empty credits page, but as soon as you fill the credits object with something, the button will appear on the main menu title screen!The default Monogatari main menu with a visible credits button.[PreviousWait](script-actions/wait.md)[NextQuick Menu](components/quick-menu.md)Last updated 4 years agoWas this helpful? # Game Configuration | Monogatari Documentation [Configuration Options](.)Game Configuration[Asset Preloading](configuration-options/game-configuration/asset-preloading.md)[Internationalization](configuration-options/game-configuration/internationalization.md)[Saving](configuration-options/game-configuration/saving.md)[Skip Main Menu](configuration-options/game-configuration/skip-menu.md)[Storage](configuration-options/game-configuration/storage-engine.md)[PreviousText-Box](components/text-box.md)[NextAsset Preloading](configuration-options/game-configuration/asset-preloading.md)Last updated 4 years agoWas this helpful? # Diagnosing Errors | Monogatari Documentation Diagnosing Errors[](.)The Browser Developer ToolsThe browser's developer tools are your best friend. Learning your way around them will make wonders for you. Here are a few resources you might find useful:What are browser developer tools?Chrome DevToolsMost importantly when dealing with errors on your novel, the console included on those dev tools is essential. To open your browser dev tools, you can press Ctrl + Shift + i or Command + Shift + i if you're on a mac. Alternatively, you can right click on the page and choose the Inspect element option.Once the dev tools pop open, select the Console tab to see the logs printed by your game.[](.)Tips for asking for helpWe all need some help every once in a while, here is a list of things that will make the experience of asking for help better for everyone and more useful to youProvide as much detail as you canWhen asking for help, please provide ALL the necessary information, more often than not you will be asked "what version are you using?" and "please show me your code" when you don't provide that information from the beginning.[](.)When dealing with errors:Provide what monogatari version you're usingProvide what browser you're usingProvide the full error message you're getting, whether from Monogatari or from your browser's consoleProvide the piece of code / script causing the issue and the surrounding code (at least 5 lines before and after)[](.)When dealing with unexpected behaviorsProvide what monogatari version you're usingProvide what browser you're usingProvide the piece of code / script that's not behaving as you expected and the surrounding code (at least 5 lines before and after)Provide a description of what you are attempting to do and what is happening[](.)Ensure your code is properly indentedIf you're sharing code, ensuring it's properly indented will help a lot for the people who's trying to help you out as it will be easier to read and easier to catch what's going on.[](.)Badly Indented CodeCopymonogatari.script ({ // The game starts here. 'Start': [ 'show scene #f7f6f6 with fadeIn', 'show notification Welcome', { 'Input': { 'Text': 'What is your name?', 'Validation': function (input) { return input.trim ().length > 0; }, 'Save': function (input) { this.storage ({ player: { name: input}}); return true; }, 'Revert': function () { this.storage ({ player: { name: '' } });}, 'Warning': 'You must enter a name!' } }, 'y Hi {{player.name}} Welcome to Monogatari!', { 'Choice': { 'Dialog': 'y Have you already read some documentation?', 'Yes': { 'Text': 'Yes', 'Do': 'jump Yes' }, 'No': { 'Text': 'No', 'Do': 'jump No' } } } ] });[](.)Perfectly Indented CodeCopymonogatari.script ({ // The game starts here. 'Start': [ 'show scene #f7f6f6 with fadeIn', 'show notification Welcome', { 'Input': { 'Text': 'What is your name?', 'Validation': function (input) { return input.trim ().length > 0; }, 'Save': function (input) { this.storage ({ player: { name: input } }); return true; }, 'Revert': function () { this.storage ({ player: { name: '' } }); }, 'Warning': 'You must enter a name!' } }, 'y Hi {{player.name}} Welcome to Monogatari!', { 'Choice': { 'Dialog': 'y Have you already read some documentation?', 'Yes': { 'Text': 'Yes', 'Do': 'jump Yes' }, 'No': { 'Text': 'No', 'Do': 'jump No' } } } ] });[](.)Generic errorsWhen an error unknown to monogatari occurs, you'll get a similar screen to this one. This usually means you have an error on your script. More information about the error like the file and line the error is in will be available in your browser's console. The exact error message varies from browser to browser.This specific example of an error was caused by the following script:Paying some attention to it might reveal there's a missing comma after the show scene statement. Going to the browser's console, allowed us to see more information about it.This is what Firefox showed:Chrome on the other hand, showed an error a little bit more generic:They both however, referred to the same line as causing the issue, shown at the end of the error as script.js:164.Our code editor also showed there was an error there, highlighting that same line in red:So, even if the errors don't exactly scream "You're missing a comma!" back to you, they at least let you know around what line you're having an issue. Paying closer attention to that area will most likely lead you to finding what's the actual error. Other errors have much more specific error messages that will let you know what's the error right away but this one is a good example since it's very common and the messages provided by browsers are not perfect.[](.)Game Apparently StuckIf it appears the game is stuck and won't advance anymore no matter how much you click, chances are an error occurred. Check your browser's console to see what's going on. If no information is visible, then try enabling the debug mode.[](.)Using the Debug ModeMonogatari has a lot of debug logs that are disabled by default. You can enable them by adding this piece of code to your main.js file, right before the monogatari.init () function.Copymonogatari.debug.level (5);Once added, reload the page and open the browser's console, you should see a whole bunch of new things being logged now.[](.)Common Errors[](.)Missing CommasAfter every statement of your script, there must be a comma unless it is the last statement in the label:Copymonogatari.script ({ 'Start': [ 'show scene #f7f6f6 with fadeIn', // This comma right here 'Hi!', // This comma right here 'end' ] });A comma must be used to separate different labels in a script:Copymonogatari.script ({ 'Start': [ 'show scene #f7f6f6 with fadeIn', 'Hi!', 'jump myLabel' ], // This comma right here 'myLabel': [ 'How are you?', 'end' ] });[](.)Incorrectly Closed Labels or ScriptsA script is a JSON object, it must start with a curly brace ({) and end with one as well (})Labels are arrays and as such, they must start with a square bracket ([) and end with one (])[PreviousF.A.Q.](f.a.q..md)[NextScript & Labels](building-blocks/script-and-labels.md)Last updated 4 years agoWas this helpful? # F.A.Q. | Monogatari Documentation F.A.Q.[](.)Is it free?Yes[](.)Can I use it commercially?Yes, you can use it for commercial and non-commercial games completely freeIs it really a game engine?Short Answer: For common purposes, sure, you can think of Monogatari as a visual novel engineTechnical Answer: Monogatari is a web development framework created to build modern websites that just happen to be games, it lacks quite a lot of elements to be considered a real game engine as it is only using an already available platform: your browser.[](.)Who develops Monogatari?A lot of awesome people help developing Monogatari, you can view the whole list here: https://monogatari.io/contributors[PreviousUpgrading from v1.4.1](upgrading-from-v1.4.1.md)[NextDiagnosing Errors](diagnosing-errors.md)Last updated 4 years agoWas this helpful? # Getting Started | Monogatari Documentation Getting Started[](.)4 Simple steps to get you started[Step 1: Setup Your Environment](getting-started/step-1-setup-your-environment.md)[Step 2: Download Monogatari](getting-started/getting-monogatari.md)[Step 3: Get Familiarized](getting-started/step-3-get-familiarized.md)[Step 4: Make Your First Visual Novel](getting-started/step-4-make-your-first-visual-novel.md)[PreviousWelcome](index.md)[NextStep 1: Setup Your Environment](getting-started/step-1-setup-your-environment.md)Last updated 4 years agoWas this helpful? # Welcome | Monogatari Documentation WelcomeMonogatari Visual Novel Engine Documentation[](.)OverviewWhile this documentation covers many parts of Monogatari's functioning, it does not cover everything you could actually do with it. Remember, your VN is a website now and that means the possibilities are endless. Everything you've seen on a website before can be done in your game.But how to look out for help on doing this things? Well, another benefit of your game being a website is: There is lots and lots of tutorials and documentation out there!If you google something, for example if you want something to happen when someone clicks an image, you can google it as "JavaScript click image", there's no need to look for a Monogatari-specific answer. Everything that applies to a website also applies for Monogatari, even the step-by-step tutorials on web development are useful to get you started.Of course, you are not alone! If you have any doubt, problem or just want some help please reach outWhere to Ask QuestionsThe best place to ask questions is the Community Forums, where there is a good community and you can receive help from people making games just like you! You can also find us on the Discord Server, if you prefer something a little more immediate.If you have some other doubts or prefer other type of communication, you can contact me on Mastodon or via email which you can find in my website.If you want to report bugs or request new features, then please report them on the GitHub repository.You can also check the FAQ to see if any of your questions have been answered there.[F.A.Q.](f.a.q..md)[](.)Sponsor the ProjectIf you like Monogatari and would like to support it, becoming an sponsor is the best way to do it. There are a lot of ways of donating or sponsoring the project. You'll find them out in the website. Sponsors can also get benefits depending on their tier like hosting for their games.[NextGetting Started](getting-started.md)Last updated 4 years agoWas this helpful? # v2.0.0.alpha.8 | Monogatari Documentation [Releases](.)v2.0.0.alpha.8Please read the [update guide](releases/v2.0.0.alpha.8.md) for this release if you're updating your game from a previous iteration of Monogatari v2 as there's a small but really important change you need to make. This one will the last of the alpha releases and we'll start going through the beta series from now on as things have finally stabilized enough. This release has several bug fixes to improve compatibility with save files made with the v1.4.1 release or older.Download: https://datadyne.perfectdark.space/monogatari/releases/Monogatari-v2.0.0.alpha.8.zipThis one is also the first release that has been officially released as a npm package. It has been released as an UMD module, therefore it's possible to use it either on a browser as a global library, using es6 modules or nodejs modules.[](.)BrowserCopy<script src='./monogatari.js'></script>Copyconst monogatari = Monogatari.default;[](.) ES6 ModulesCopyimport Monogatari from '@monogatari/core';[](.) Node JSCopyconst Monogatari = require ('@monogatari/core');[](.)Change Log[](.)New FeaturesStorage variable interpolation can now be used on the input modal message and choice buttons textArabic language has been added as an UI translationThe core translation () function can now return a translation object given the nameText input will now be focused automatically right after showing up[](.)Bug FixesAudio media was not playing because of a bug introduced when the directory names where pluralizedThe volume property on the play action was not implemented and adding a volume had no effect on the volume of the media being playedComponents with an empty text prop would render it as true instead of showing blankAn error would show some times if a component had multiple spaces on its class propertyCharacter history from save files with an old format was not being transformed to the new format and made an error occur when trying to load itCharacter history files src property was not being changed to use the assets directory instead of the old ones and caused the files not to load correctly[](.)Other ChangesDependencies have been updated to their latest versionSmall improvements on text box stylingElectron related functionality has been updated to match latest version (7.x.x)[](.)Update GuideThis guide only applies for games that were written using the 2.0.0.alpha.3 version or above.On the options.js file, almost at the beginning of the file you should find this line:Copyconst { Monogatari: monogatari } = Monogatari;Replace it with this:Copyconst monogatari = Monogatari.default;Finally, as always, just copy the contents inside the engine/core/ directory from the new release to the one you are using.[PreviousEvents](advanced-monogatari-development/events.md)[Nextv2.0.0.alpha.7](releases/v2.0.0.alpha.7.md)Last updated 5 years agoWas this helpful? # Chrome App | Monogatari Documentation [Releasing Your Game](.)Chrome AppGoogle has discontinued Web Apps on the Chrome Web Store. We recommend you release your game as a website instead.[Web](releasing-your-game/web.md)[PreviousImage Menus](style-and-design/image-menus.md)[NextDesktop App](releasing-your-game/desktop.md)Last updated 4 years agoWas this helpful? # Choices | Monogatari Documentation [Script Actions](.)ChoicesLearn about choices, their properties and how to use themOverviewChoices allow players to take decisions and then react based on whatever they chose. They are represented on your script as Objects and they allow you to define as many choices as you can fit on the screenGlobal PropertiesNameTypeOptionalDescriptionClassstringYesA space separated list of classes to add to the choice container.DialogstringYesSpecifies a dialog to be shown along with the choices.TimerobjectyesA timer configuration object....ChoicesObjectsNoAny other object added will be considered as a choice. Choices have their own individual properties that are described below.[](.)Choices PropertiesNameTypeOptionalDescriptionClassstringYesA space separated list of classes to add to the choice.ClickablefunctionYesA function returning a boolean, whether the option should allow to be clicked or not, effectively disabling a choice while still showing it if needed.ConditionfunctionYesA function returning a boolean, whether the choice should be shown or not.DostatementNoThe action to perform if the the choice is picked. Contrary to the onChosen property, this one should be a Monogatari statement, meaning it can be anything you are able to use in your script.onChosenfunctionYesA function that will be run if the choice is picked.onRevertfunctionYesA function that will revert the actions done on the onChosen function of the choice should the player go back.TextstringNoThe text that will be shown in the choice.[](.)ExamplesEvery choice requires 2 properties: the text to display and what to do when it's clicked.Copy{'Choice': { 'Developer': { 'Text': 'I’m a developer.', 'Do': 'jump Developer' }, 'Writer': { 'Text': 'I’m a writer.', 'Do': 'jump Writer' }, 'Artist': { 'Text': 'I’m an artist.', 'Do': 'jump Artist' }, 'Player': { 'Text': 'I’m a Player.', 'Do': 'jump Player' } }}[](.)Conditional ChoicesThere are some cases where you would only want to show a choice if certain conditions are met. For this example, let's assume this is your storage variable:Copy'use strict'; // Persistent Storage Variable monogatari.storage ({ played: false });As you can see, we added a 'played' variable inside the storage and set its default to false. Now, let's see what would happen with the following Choices:Copy{'Choice': { 'Developer': { 'Text': 'I’m a developer.', 'Do': 'jump Developer' }, 'Player': { 'Text': 'I’m a Player.', 'Do': 'jump Player', 'Condition': function () { return this.storage ('played'); // The 'Player' option will only be shown if this returns true. } } }}'Player' will only be shown if the 'played' variable we added is true, since we defined it as false, then it won't be shown however, if somewhere along the script we were to change that variable to true then this choice will be shown.[](.)Showing Text when Showing the ChoicesYou might want to show a dialog along with the choices, this is possible using the Dialog property inside the object:Copy{'Choice': { 'Dialog': 'e Before I continue, let me ask you, what are you?', 'Developer': { 'Text': 'I’m a developer.', 'Do': 'jump Developer' }, 'Player':{ 'Text': 'I’m a Player.', 'Do': 'jump Player', 'Condition': function () { return this.storage ('played'); // The 'Player' option will only be shown if this returns true. } } }}That way the 'Dialog' property is the one that will be shown with the choices, as you can see, just like with normal dialogs, you can specify what character is the one talking and take full advantage of other things like side images on it.[](.)Unclickable ChoicesEarlier we made a conditional choice that would only be shown if a condition was satisfied, but what if you want to show the player that there is a choice they could be making if certain conditions were satisfied.For this example, let's assume this is your storage variable:Copy'use strict'; // Persistent Storage Variable monogatari.storage ({ haveKey: false });As you can see, we added a haveKey variable inside the storage and set its default value to false. With that established, consider the following code.Copy{'Choice':{ 'Dialog': 'Do you unlock the locked door?', 'FirstOption':{ 'Text': 'Yes', 'Do': 'jump label1', 'Clickable': function(){ return this.storage().haveKey } }, 'SecondOption':{ 'Text': 'No', 'Do': 'jump label2' } }},The 'Clickable' property, as its name implies, decides whether or not a button can be clicked.Yes will be shown regardless of whether or not the variable we added is true, but it will only be clickable if it is. On that note, if the variable haveKey is false, then the Yes button will not be clickable. However, this might be confusing to your players, as they will see a choice, try to click it, and clicking it will do nothing. We can help out with this confusion with a little bit of visual design.[](.)Styling your ButtonsThe 'Class' property allows you to give a CSS class to your buttons for special styles. Suppose you had the following CSS in your main.css file:Copy.boldedText { font-weight: bold; } .italicText { font-style: italic; }The boldedText and italicText classes can then be applied to your buttons by setting their 'Class' properties in your script, like so:Copy{"Choice":{ "FirstOption":{ "Text": "Yes", "Do": "jump label1", "Class": "boldedText" //A css class to apply to the button. }, "SecondOption":{ "Text": "No", "Do": "jump label2", "Class": "italicText" } }},This way, the 'Yes' button will have its text bolded and the 'No' button will have its text italicized. You can also use CSS styling to do things like give buttons background images and make the text on them invisible, so you can have buttons with pictures!Earlier, we mentioned you could style unclickable buttons as well. This is easy to do as the buttons in Monogatari's choices are HTML buttons, and when a button is made unclickable, it is given the 'disabled' attribute. This means that we can style disabled buttons with CSS attribute styling. Consider the following example:Copy[disabled] { opacity: .5; }With this CSS styling, buttons containing the 'disabled' attribute will render semi-transparent, which will visually indicate to the player that they are different, so hopefully they won't be confused when they are unable to click it.You can also use classes and attributes at the same time:Copy[disabled].classname { text-decoration: line-through; }In this example, the text would have a line through it if and only if it is both disabled, and the button has the '.classname' class. If you wanted to make a specific class for a specific button, you could use the ::after pseudo selector and the 'content' property to append an explanation as to why you can't click the button too, or you could make two separate buttons where one displays if a condition is met, and the other displays if the condition is unmet.[](.)onChosen FunctionsThe 'onChosen' property allows us to run a function when the player clicks your button. For this example, we'll assume that our storage contains the following:Copy'use strict'; // Persistent Storage Variable monogatari.storage ({ enemyHealth: 100, playerAttack: 20 });In our script.js file, somewhere outside of our game's script, we could have a simple example function for attacking the enemy.Copyfunction attackEnemy(){ monogatari.storage().enemyHealth = monogatari.storage().enemyHealth - monogatari.storage().playerAttack };This function sets the enemyHealth value to be equal to its current value minus the playerAttack value.Then, in our script, we could have a choice for the player:Copy{"Choice":{ "Dialog": "The enemy wants to fight! What do you do?", "FirstOption":{ "Text": "Attack!", "onChosen": function(){attackEnemy()}, "Do": "You attack the enemy for {{playerAttack}} damage!" }, "SecondOption":{ "Text": "Defend", "Do": "You defend instead of attacking." } }}, "The enemy now has {{enemyHealth}} health points left."You could also have the function written out in the script itself for one-time functions that won't be called again.Copy{"Choice":{ "Dialog": "The enemy wants to fight! What do you do?", "FirstOption":{ "Text": "Attack!", "onChosen": function (){ monogatari.storage().enemyHealth = monogatari.storage().enemyHealth - monogatari.storage().playerAttack }, "Do": "You attack the enemy for {{playerAttack}} damage!" }, "SecondOption":{ "Text": "Defend", "Do": "You defend instead of attacking." } }}, "The enemy now has {{enemyHealth}} health points left.",This achieves the same result. You could also use these functions to invisibly keep track of which options the player clicked, or any other ideas you might have.[](.)onRevertThe 'onRevert' property should contain a function that will run when the player rewinds the game using the back button before a choice selection that runs an onChosen function. Ideally, this function should be used to undo whatever was done by the onChosen function, assuming the onChosen function did anything at all. For example, if you have an onChosen function that gives the player 5 gold, then the onRevert function should remove 5 gold, or else the player could just choose the option, rewind, choose it again, until they have as much gold as they want.Copy{"Choice":{ "Dialog": "Would you like some Gold?", "Yes": { "Text": "Yes please.", "Do": "p Yes please.", "onChosen": function() { monogatari.storage().playerGold += 5; }, "onRevert": function() { monogatari.storage().playerGold -= 5; } }, "No": { "Text": "No thank you.", "Do": "No thank you.", }, }}, "Okay! As a result of your decision, you now have {{playerGold}} Gold!",Monogatari remembers for the player which option they picked, so if they chose Yes in the above example, when they rewind, it will run the onRevert function for "yes", whereas if they chose no, it will not run anything special, as No does not contain an onRevert or onChosen function.The 'onRevert' property is required when you use the onChosen function, or else the player will not be able to use the "back" button to revert before the choice. If you don't care whether or not your player is allowed to rewind before a decision, then you don't need to use onRevert but that might make your player assume that there's a bug if some choices are reversible and others aren't.[](.)TimersThe Timer object takes two methods: "time" and "callback". Time is simply the amount of time in miliseconds you want the timer to last, and callback is a function to run after that timer has finished.Timers can be used to require the player to think quickly, and make something happen if they take too long to select a choice. In this example, assume that we have a css class "Invisible" that simply applies a display:none to the element.Copy{'Choice': { 'Dialog': 'y Have you already read some documentation?', 'Timer': { // Time in milliseconds time: 5000, // The function to run when the time is over callback: () => { //Click the "tookTooLong" button. monogatari.element().find('[data-choice="tookTooLong"]').get(0).click(); // Promise friendly! return Promise.resolve (); } }, 'Yes': { 'Text': 'Yes', 'Do': 'jump Yes' }, 'No': { 'Text': 'No', 'Do': 'jump No' }, 'tookTooLong':{ 'Text': 'TookTooLong', 'Do': 'jump tookTooLong', 'Class': 'invisible', } } },In this example, there are three buttons, but only two of them are actually visible to the player. After 5 seconds passes, the third button is clicked automatically.You can also do other things with timers, such as increment a counter, play a sound, or anything else you like. Please note, however, that there is no corresponding revert function for timers. Anything that happens in a timer will not be undone when the player clicks the back button. (Of course, you'd probably not want the player to be able to undo a mistake like allowing a timer to conclude, so if you use timers in this way, consider disabling the rollback function.)[PreviousBuilt-in Functions](building-blocks/components/built-in-functions.md)[NextClear](script-actions/clear.md)Last updated 4 years agoWas this helpful? # Responsiveness | Monogatari Documentation [Style & Design](.)ResponsivenessPart of what makes Monogatari unique is how it is responsive out of the box, meaning people in all kind of devices with different form factors will be able to play your game.It is important to note that responsiveness is not the same as simply scaling your game up or down to fit the screen it's being displayed on. Responsiveness actually let's you create different stylings and layouts for different sizes so you can take advantage when there's more space or adapt your elements when it's less.Adapting to the device is done using CSS media queries.[](.)Default Screen-Width BreakpointsCopy/** Extra Small Devices, Phones (480px) **/ @media screen and (min-width : 30em) {} /** Medium Screens, Phablets (601px) **/ @media screen and (min-width: 37.56255em) {} /** Medium Devices, Tablets (992px)**/ @media screen and (min-width: 62em) {} /** HD Screen, Large Devices, Wide Screens, Desktop (1200px) **/ @media screen and (min-width: 75em) {} /** Full HD Screen, Large Devices, Wide Screens, Large Desktops (1920px) **/ @media screen and (min-width: 120em) {} /** Retina Screen , Large Devices, Wide Screens(2560px) **/ @media screen and (min-width: 160em) {} /** 4k Screens, Large Devices, Wide Screens (3840px) **/ @media screen and (min-width: 240em) {} /** 5k Screens, Large Devices, Wide Screens (5000px) **/ @media screen and (min-width: 312.5em) {} /** 8k Screens, Large Devices, Wide Screens (8000px) **/ @media screen and (min-width: 500em) {}[PreviousSplit Files](configuration-options/split.md)[NextCSS Classes](style-and-design/classes.md)Last updated 4 years agoWas this helpful? # Upgrading from v1.4.1 | Monogatari Documentation Upgrading from v1.4.1Learn all you need to know about upgrading your game!Monogatari has been completely rewritten and many things have changed, however, the script syntax and overall functionality has stayed almost the same so porting your game from v1.4.1 to the newest version is not that complicated1. Moving your filesLets start with the easiest task, the best way to port your game is starting fresh and that means downloading the newest version available and move your files there, step by step instead of trying to update your old project.[](.)AssetsLets start with the assets, they are now saved on a directory called assets. Here is an equivalence of where you used to store your assets before and where you should store them now:AssetOld DirectoryNew DirectoryMusicaudio/music/assets/music/Voiceaudio/voice/assets/voices/Soundsaudio/sound/assets/sounds/Charactersimg/charactersassets/characters/Backgrounds / Scenesimg/scenes/assets/scenes/UI Assetsimg/ui/assets/ui/Videovideoassets/videos/Why did this changed you may ask. Well, by creating an universal assets directory, it is easier to both keep your assets out of version control systems (git and others) and also, it allows you to host your assets remotely in an easier way. More info on this will be made available later on.[](.)2. StorageYour players most likely have lots of save files and we want them to keep them even though we're updating the engine. By default, the new version handles storage in a different way which will result on the save files not showing up. To allow your players to keep their save files, we need to change this.Go over to the options.js fileFind the Storage configuration almost at the bottom of the fileEdit the Adapter property and set it to an empty string ('')Edit the Store property and set it to an empty string ('')[](.)Copy your storage objectNow its time to copy your storage object. The js/storage.js file is still the place where you'll put all the items you want to save on your game, the syntax has changed just a bit. Storage on v1.4.1storage.jsCopylet storage = { player: { name: "" } }; Storage on v2.0.0storage.jsCopymonogatari.storage ({ player: { name: '' } });As you can see, it really is just a matter of changing the let storage = { ...storage }; format to the new monogatari.storage ({ ...storage }); format, you can copy your storage object exactly as it is! [](.)Update the syntax to set / get values from the storagePreviously, when you wanted to modify the storage, you would modify the variable directly like this:script.jsCopystorage.someVariable = someValue; Modifying the storage like that is no longer possible since it has been moved to a function. While you can look at all the new syntax on the [storage documentation](building-blocks/data-storage.md), the easiest way to update the syntax is by replacing all occurrences of it you have on your script for something like this:Copymonogatari.storage ().someVariable = someValue;[](.)3. Assets DeclarationsJust like with the storage, assets declaration syntax has changed just a bit, here's a small comparative on how the assets declaration looks like on your script.js file on both the old and the new version. Assets declaration on v1.4.1script.jsCopy// Define the Particles JS Configurations used in the game let particles = { }; // Define the music used in the game. const music = { }; // Define the voice files used in the game. const voice = { }; // Define the sounds used in the game. const sound = { }; // Define the videos used in the game. const videos = { }; // Define the images used in the game. const images = { }; // Define the backgrounds for each scene. const scenes = { };Assets declaration on v2.0.0script.jsCopy// Define the Particles JS Configurations used in the game monogatari.action ('particles').particles ({ }); // Define the music used in the game. monogatari.assets ('music', { }); // Define the voice files used in the game. monogatari.assets ('voices', { }); // Define the sounds used in the game. monogatari.assets ('sounds', { }); // Define the videos used in the game. monogatari.assets ('videos', { }); // Define the images used in the game. monogatari.assets ('images', { }); // Define the backgrounds for each scene. monogatari.assets ('scenes', { });[](.)4. Script and Labels DeclarationsJust as with the storage and assets, the way to declare the script for your game has changed its syntax, here's a comparison on the old and new way:Script declaration on v1.4.1Copylet script = { // The game starts here. "Start": [ "notify Welcome", { "Input": { "Text": "What is your name?", "Validation": function (input) { return input.trim().length > 0; }, "Save": function (input) { storage.player.Name = input; return true; }, "Warning": "You must enter a name!" } }, "h Hi {{player.Name}} Welcome to Monogatari!", { "Choice": { "Dialog": "h Have you already read some documentation?", "Yes": { "Text": "Yes", "Do": "jump Yes" }, "No": { "Text": "No", "Do": "jump No" } } } ], "Yes": [ "h That's awesome!", "h Then you are ready to go ahead and create an amazing Game!", "h I can't wait to see what story you'll tell!", "end" ], "No": [ "h You can do it now.", "display message Help", "h Go ahead and create an amazing Game!", "h I can't wait to see what story you'll tell!", "end" ] };Script declaration on v2.0.0Copymonogatari.script ({ // The game starts here. 'Start': [ 'show notification Welcome', { 'Input': { 'Text': 'What is your name?', 'Validation': function (input) { return input.trim ().length > 0; }, 'Save': function (input) { this.storage ({ player: { name: input } }); return true; }, 'Revert': function () { this.storage ({ player: { name: '' } }); }, 'Warning': 'You must enter a name!' } }, 'y Hi {{player.name}} Welcome to Monogatari!', { 'Choice': { 'Dialog': 'y Have you already read some documentation?', 'Yes': { 'Text': 'Yes', 'Do': 'jump Yes' }, 'No': { 'Text': 'No', 'Do': 'jump No' } } } ], 'Yes': [ 'y Thats awesome!', 'y Then you are ready to go ahead and create an amazing Game!', 'y I can’t wait to see what story you’ll tell!', 'end' ], 'No': [ 'y You can do it now.', 'show message Help', 'y Go ahead and create an amazing Game!', 'y I can’t wait to see what story you’ll tell!', 'end' ] });[](.)Independent LabelsDeclaring independent labels for your script is possible as always, and really useful to split your game into multiple files, here's a comparison on how this was achieved on the past and how it's done now.Individual Labels on v1.4.1Singe Language[](.)Multi Language[](.)Copyscript["myLabel"]["English"] = [ "some statement", "other statement" ];Copyscript["myLabel"] = [ "some statement", "other statement" ];Individual Labels on v2.0.0Single Language[](.)Multi Language[](.)Copymonogatari.label ('myLabel', [ "some statement", "other statement" ]);Copymonogatari.label ('myLabel', 'English', [ "some statement", "other statement" ]);[](.)5. Script Actions / StatementsThe syntax for some of the actions you can perform on your script has been changed. Here's a small list of the ones that changed, however, it is best if you check their individual page to see all the new features!ActionOld SyntaxNew Syntax[Show a character sprite](script-actions/characters.md)show <character_id> <sprite_id>show character <character_id> <sprite_id>[Hide a character sprite](script-actions/hide-character.md)hide <character_id>hide character <character_id>[Show an image](script-actions/show-image.md)show <image_id>show image <image_id>[Hide an image](script-actions/hide-image.md)hide <image_id>hide image <image_id>[Show a message](script-actions/message.md)display message <message_id>show message <message_id>[Show a scene / background image](script-actions/show-scene.md)scene <scene_id>show scene <scene_id>[Show a notification](script-actions/notifications.md)notify <notification_id>show notification <notification_id>[Show a particle system](script-actions/particles.md)particles <particles_id>show particles <particles_id>[Hide a particle system](script-actions/hide-particles.md)stop particleshide particles[](.)6. String TranslationsPreviously, a file called strings.js was distributed with Monogatari where you would put all your string translations, mainly used for the UI. Now, those translations are handled internally but you can still add strings to them or modify them. Here's some comparison on how it worked before and how it works now.Strings declaration on v1.4.1Copyconst strings = { "Español": { "AdvanceHelp": "Para avanzar en el juego, presiona espacio o haz click.", "Audio": "Audio" } };Strings addition on v1.4.1Copystrings["Español"]["AllowPlaybac"] = "Click here to allow audio playback";Strings declaration and addition on v2.0.0Copymonogatari.translation ('Español', { 'AdvanceHelp': 'Para avanzar en el juego, presiona espacio o haz click', 'AllowPlayback': 'Click here to allow audio playback' });[PreviousStep 4: Make Your First Visual Novel](getting-started/step-4-make-your-first-visual-novel.md)[NextF.A.Q.](f.a.q..md)Last updated 5 years agoWas this helpful? # Actions | Monogatari Documentation [Advanced: Monogatari Development](../advanced-monogatari-development.md)ActionsThis page has been moved to:[Actions](../building-blocks/actions.md)[PreviousPandora](core-libraries/pandora.md)[NextComponents](components.md)Last updated 4 years agoWas this helpful? # Components | Monogatari Documentation [Advanced: Monogatari Development](../advanced-monogatari-development.md)ComponentsThis page has been moved to:[Components](../building-blocks/components.md)[PreviousActions](actions.md)[NextTranslations](translations.md)Last updated 4 years agoWas this helpful? # Core Libraries | Monogatari Documentation [Advanced: Monogatari Development](../advanced-monogatari-development.md)Core LibrariesGet the best of Monogatari by learning more about the libraries it uses on its core!On the very core of Monogatari, you'll find it has been built around the Aegis Framework libraries. These libraries are used both for functionality and styling so learning more about them will allow you to create even more amazing thingsArtemis: JavaScript LibraryArtemis is used for DOM manipulation, storage, HTTP requests handling, text transformations and moreKayros: CSS LibraryKayros is the very base of Monogatari's style and lookPandora: Web Components LibraryPandora is what allows monogatari to have self contained, custom HTML elements and allows them to have custom functionality.[PreviousWeb](../releasing-your-game/web.md)[NextArtemis](core-libraries/artemis.md)Last updated 5 years agoWas this helpful? # Events | Monogatari Documentation [Advanced: Monogatari Development](../advanced-monogatari-development.md)EventsMonogatari has a lot of useful events that fire when certain engine events happen. These can be used to run your own custom functions with monogatari.on (). For example, the following code will create a function that will be run every time the didLoadGame event fires:Copymonogatari.on ('didLoadGame', () => { // Something here });Below is an in-progress list of all events Monogatari has.[](.)Initialization, Setup, and BindingEventDescriptionDetailswillInitwillSetupdidSetupwillBinddidBinddidInit[](.)Asset PreloadingEventDescriptionDetailswillPreloadAssetsassetLoadedassetQueueddidPreloadAssets[](.)ActionsEventDescriptionDetailswillRunActiondidRunActionwillRevertActiondidRevertAction[](.)ConfigurationsconfigurationElementWillUpdateconfigurationElementUpdate::<Options.js Key>configurationElementDidUpdate[](.)LocalizationEventDescriptionDetailswillLocalizedidLocalize[](.)Save file LoadingEventDescriptionDetailswillLoadGameEvent fires at the start of a save file loadAfter the player clicks to load the game but before the game finishes loading.didLoadGameEvent fires after a save file loadAfter a save file has finished loading and all storage variable are set, but before anything is drawn to the screen and before players have control.[](.)Other:EventDescriptionDetailsdidFinishTypingEvent fires after dialog text finishesAfter every line of dialog in Monogatari's typewriter animation finishes and the player is able to click to get a new line. This includes at the end of the animation, as well as after clicking to skip the animation, or after every fed line if typewriter animation is disabled.[PreviousTranslations](translations.md)[Nextv2.0.0.alpha.8](../releases/v2.0.0.alpha.8.md)Last updated 4 years agoWas this helpful? # Translations | Monogatari Documentation [Advanced: Monogatari Development](../advanced-monogatari-development.md)TranslationsLearn everything about translating Monogatari's UI to other languagesOverviewApart from allowing you to have a script for different languages, Monogatari's UI can also be translated to multiple languages. If you are someone who knows a language different from English, your help on translating the project is more than welcomeGetting startedThe first step is to figure out whether a translation for you language already exists or if it has to be created, you can find all languages available here.If there is a translation already available for your language, then all you'll have to do is update or add whatever strings are missing, if there isn't one yet, then you'll have to create a new file for it.All translations take the English strings file as its base so you'll need to check it out to see what you need to translate. Below is a sample of the file, be aware this is for demonstration purposes only and may be outdated so you should always check the source file instead. It is important that you only translate the strings on the right, as opposed to their keys on the left ('SomeKey': 'Some string to translate').English.jsCopy/** * ============================================================ * English * ============================================================ * * Translators: * * Hyuchia <[email protected]> */ export default { 'AdvanceHelp': 'To advance through the game, left-click or tap anywhere on the game screen or press the space key', 'AllowPlayback': 'Click here to allow audio playback', 'Audio': 'Audio', 'AutoPlay': 'Auto', 'AutoPlayButton': 'Enable auto play', 'AutoPlaySpeed': 'Autoplay Speed', 'Back': 'Back', 'BackButton': 'Go back', 'Cancel': 'Cancel', 'Close': 'Close', 'Confirm': 'Do you want to quit?', 'Credits': 'Credits', 'Delete': 'Delete', 'DialogLogButton': 'Show the dialog log', 'FullScreen': 'Full Screen', 'Gallery': 'Gallery', 'Help': 'Help', 'Hide': 'Hide', 'HideButton': 'Hide the text box', 'iOSAudioWarning': 'Audio settings are not supported on iOS', 'KeyboardShortcuts': 'Keyboard Shortcuts', 'Language': 'Language', 'Load': 'Load', 'LoadAutoSaveSlots': 'Auto Saved Games', 'LoadButton': 'Open the Load Screen', 'Loading': 'Loading', 'LoadingMessage': 'Wait while the assets are loaded', 'LoadSlots': 'Saved Games', 'LocalStorageWarning': 'Local Storage is not available in this browser', 'Log': 'Log', 'Music': 'Music Volume', 'NewContent': 'There is new content available, reload the page to get the latest version', 'NoSavedGames': 'No saved games', 'NoAutoSavedGames': 'No automatically saved games', 'NoDialogsAvailable': 'No dialogs available. Dialogs will appear here as they show up', 'OK': 'OK', 'OrientationWarning': 'Please rotate your device to play', 'Overwrite': 'Overwrite', 'QuickButtons': 'Quick Menu Buttons', 'QuickMenu': 'Quick Menu', 'Quit': 'Quit', 'QuitButton': 'Quit Game', 'Resolution': 'Resolution', 'Save': 'Save', 'SaveButton': 'Open the Save Screen', 'SaveInSlot': 'Save in slot', 'SelectYourLanguage': 'Select your language', 'Settings': 'Settings', 'SettingsButton': 'Open the Settings Screen', 'Show': 'Show', 'Skip': 'Skip', 'SkipButton': 'Enter skip mode', 'SlotDeletion': 'Are you sure you want to delete this slot?', 'SlotOverwrite': 'Are you sure you want to overwrite this slot?', 'Sound': 'Sound Volume', 'Start': 'Start', 'Stop': 'Stop', 'TextSpeed': 'Text Speed', 'Video': 'Video Volume', 'Voice': 'Voice Volume', 'Windowed': 'Windowed' };[](.)Updating an Available LanguageIf a translation for your language already existed, then all you need to do is check the English file and compare it to the one in your language to make sure every string translated is updated (meaning the English version and the translation match on its wording/meaning) and adding any string that was not previously there.[](.)Adding a New LanguageIf there was no translation for your language available, then you first need to create a file for it. The file should be placed next to all the other translations and should be named after the language you are translating for. Preferably, keeping the name of your language in the language itself so:English => English.jsSpanish => Español.jsJapanese => 日本語.jsYou get the idea. Once you've created the file, simply copy the contents of the English translation file and you're all set to start translating all the strings thereCredit YourselfWhen you finish, be sure to place your name/nickname on the translations file, you can also leave your email or website next to it. You did an awesome work so you deserve to take credit for it[NextEvents](events.md)Last updated 4 years agoWas this helpful? # Artemis | Monogatari Documentation [Advanced: Monogatari Development](../../advanced-monogatari-development.md)[Core Libraries](../core-libraries.md)ArtemisDOM manipulation, storage, HTTP requests handling, text transformations, etc.[PreviousCore Libraries](../core-libraries.md)[NextKayros](kayros.md)Last updated 4 years agoWas this helpful? # Kayros | Monogatari Documentation [Advanced: Monogatari Development](../../advanced-monogatari-development.md)[Core Libraries](../core-libraries.md)Kayros[PreviousArtemis](artemis.md)[NextPandora](pandora.md)Last updated 4 years agoWas this helpful? # Pandora | Monogatari Documentation [Advanced: Monogatari Development](../../advanced-monogatari-development.md)[Core Libraries](../core-libraries.md)Pandora[PreviousKayros](kayros.md)[NextActions](../actions.md)Last updated 4 years agoWas this helpful? # Actions | Monogatari Documentation [Building Blocks](../building-blocks.md)Actions[](.)OverviewActions are what defines what any given statement in a script should do. The following code displays a very simple example of an action that just does something when applied (advancing through the game) and something else when reverted (rolling back through the game). It will match all statements that start with myaction, for example:Copy'myaction' 'myaction something' 'myaction one two three'Copyclass MyAction extends Monogatari.Action { static matchString ([action]) { return action === 'myaction'; } constructor ([myaction, ...args]) { super (); } apply () { // Do something } revert () { // Do something } } MyAction.id = 'MyAction';Now that we have our action class, we need to register it in Monogatari so it knows it exists:Copymonogatari.registerAction (MyAction);[PreviousVariables & Data Storage](data-storage.md)[NextLife Cycle](actions/life-cycle.md)Last updated 4 years agoWas this helpful? # Characters | Monogatari Documentation [Building Blocks](../building-blocks.md)CharactersLearn about characters, their properties and how to create themOverviewCharacters are probably one of the most important parts of your novel, not only used for displaying them as sprites but also to display their dialogs.[](.)PropertiesThe following table lists all the properties you can set for each character.NameTypeOptionalDescriptionnamestringYesThe name that will be shown when this character speaks.Supports storage and translation interpolations.colorstringYesA valid CSS color which will be used to color the character's name.directorystringYesSpecifies the sub-directory where the sprites and expressions images for the character are stored in case they are not in the root assets/characters directory.spritesobjectYesAn object with the identifiers and file names for each [sprite](../v/develop/script-actions/characters.md) available for the character.default_expressionstringYes [Side image](../v/develop/script-actions/dialogs.md) to show every time the character speaks.expressionsobjectYesAn object with the identifiers and file names for each [side expression](../v/develop/script-actions/dialogs.md) available for the character.nvlbooleanYesDefault is false.Whether the character's dialogs should be shown in [NVL mode](../v/develop/script-actions/dialogs.md).type_animationbooleanYesDefault is true. This property indicates whether the typewriter animation should be used when the character speaks or not.[](.)DeclarationDeclaring characters is really simple. First, you need to define an identifier. This is what you'll use for the [dialog](../script-actions/dialogs.md) and [show character](../script-actions/characters.md) actions. We'll choose the identifier y, short for Yui in this tutorial.Copymonogatari.characters ({ 'y': { } });Do you have more than one character? No problem! You can define as many characters as you want, just make sure that each character has a unique identifier.Copymonogatari.characters ({ 'y': { }, 'm': { } });We've come up with our identifiers and they point to an empty object. Inside this object is where we'll define each of the properties we need for each character.[](.)Name and ColorThe most basic properties are the name and color for our characters. The name will appear in the textbox every time the character speaks and the color specifies what color that name will be. Copymonogatari.characters ({ 'y': { name: 'Yui', color: '#00bfff' } });[](.)Character SpritesSince we are building visual novels, chances are we also need images for our characters. All your character's sprites should be placed in your assets/characters directory, let's see what that looks like.[](.)1. Enter the assets directory[](.)2. Enter the characters directory[](.)3. Place all your sprite images[](.)4. Declare your assetsNow that we have all our sprites in our assets directory, we need to declare them so monogatari knows about them. To do so, we'll use the sprites property of our character:Copymonogatari.characters ({ 'y': { name: 'Yui', color: '#00bfff', sprites: { angry: 'normal.png', happy: 'happy.png', normal: 'normal.png', sad: 'sad.png', surprised: 'surprised.png' } } });Notice how for each sprite we assigned two things: A key or identifier which is the way we'll refer to that specific sprite.The name of the file.Copyidentifier: 'file.name'Both items are independent of each other so while in the last example we used identifiers similar to the file names, we could have chosen any identifier, the following would be perfectly valid for example:Copy someKey: 'normal.png' happy: 'my_file_name.png'To learn how to show a character sprite, go over to the Show Character action.[Show Character](../script-actions/characters.md)[](.)Custom Sub-directoryIn the [Character Sprites](.) section we added all of our character sprite images into the assets/characters directory. This approach is pretty straight forward but can become a bit troublesome as you add more characters. For starters, the directory could start feeling cluttered and you would not be able to use the same name on different files without them getting overwritten.A solution for this issue is to have a different directory for each character where we'll place all the assets for that character alone. This is achieved by specifying the directory property in your character declaration. This property expects the name of a sub-directory inside the assets/characters directory. For example, if you create a sub-directory assets/characters/my-character then you should set the directory property to 'my-character'. Let's take a look at the whole process:[](.)1. Create your sub-directory[](.)2. Place all your character assets in that sub-directory[](.)3. Set the directory property in your declarationNow that we've created the sub-directory and placed all our assets for a specific character in it, then we must specify the directory property in our character so monogatari knows where the files are located.Copymonogatari.characters ({ 'y': { name: 'Yui', color: '#00bfff', directory: 'yui', sprites: { angry: 'normal.png', happy: 'happy.png', normal: 'normal.png', sad: 'sad.png', surprised: 'surprised.png' } } });[](.)Expressions / Side ImagesIt is also possible to define side images or expressions to show on the textbox when your character speaks.[](.)1. Add your expression images to your assetsFor this example we'll add all our expressions in their own directory. This is not required though and you can place them right where your character's sprites are.Now that we've created our expressions sub-directory, we'll place all our expression images in it.[](.)2. Add the expressions to your declarationNow that we've added our files, we need to define them in our character declaration by defining the expressions property. Just like the sprites property, it expects an identifier/filename list.Copymonogatari.characters ({ 'y': { name: 'Yui', color: '#00bfff', directory: 'yui', sprites: { angry: 'normal.png', happy: 'happy.png', normal: 'normal.png', sad: 'sad.png', surprised: 'surprised.png' }, expressions: { angry: 'expressions/normal.png', happy: 'expressions/happy.png', normal: 'expressions/normal.png', sad: 'expressions/sad.png', surprised: 'expressions/surprised.png' } } });Notice how we added the expressions/ prefix to all of the file names to account for the expressions directory we put the files in. If we hadn't done that and we had placed them right with our sprites, the prefix would not be needed but we would have to change the names of the files so that they don't overlap with the sprites ones.To learn how to show this expressions in a dialog, go over the Dialog action.[Dialogs](../script-actions/dialogs.md)[](.)Default ExpressionIt is also possible to define a default side image / expression for your character. This default one will be shown for all of your character's dialogs that do not specify any other expression.The default_expression property expects the identifier of one of your expressions as its value. Previously we defined the normal expression for our character:Copynormal: 'expressions/normal.png'If we wanted to make that one the default expression, then we'd need to set the default_expression property to normal:Copymonogatari.characters ({ 'y': { name: 'Yui', color: '#00bfff', directory: 'yui', sprites: { angry: 'normal.png', happy: 'happy.png', normal: 'normal.png', sad: 'sad.png', surprised: 'surprised.png' }, expressions: { angry: 'expressions/normal.png', happy: 'expressions/happy.png', normal: 'expressions/normal.png', sad: 'expressions/sad.png', surprised: 'expressions/surprised.png' }, default_expression: 'normal' } });[](.)NVL ModeBy default, characters will speak in adv mode. In ADV mode, the textbox appears in the bottom of the screen and only displays a dialog at a time. NVL mode will make the textbox cover the whole screen and will display several dialogs in a log format.The nvl property accepts a boolean value (true or false) and is set to false by default. Setting it to true will make all of the character's dialogs to be shown in nvl mode.Copymonogatari.characters ({ 'y': { name: 'Yui', color: '#00bfff', directory: 'yui', sprites: { angry: 'normal.png', happy: 'happy.png', normal: 'normal.png', sad: 'sad.png', surprised: 'surprised.png' }, expressions: { angry: 'expressions/normal.png', happy: 'expressions/happy.png', normal: 'expressions/normal.png', sad: 'expressions/sad.png', surprised: 'expressions/surprised.png' }, default_expression: 'normal', nvl: true } });If you want to learn more about the different dialog modes, check the Dialog action and the Text Box component.[Dialogs](../script-actions/dialogs.md)[Text-Box](../components/text-box.md)[](.)Typewrite AnimationMonogatari allows you to disable the typewrite animation for your game from your game's settings, however, some times you only want to disable the animation for a specific character.The type_animation property accepts a boolean value (true or false) and is set to true by default. Setting it to false will disable the animation for all of that character's dialogs.Copymonogatari.characters ({ 'y': { name: 'Yui', color: '#00bfff', directory: 'yui', sprites: { angry: 'normal.png', happy: 'happy.png', normal: 'normal.png', sad: 'sad.png', surprised: 'surprised.png' }, expressions: { angry: 'expressions/normal.png', happy: 'expressions/happy.png', normal: 'expressions/normal.png', sad: 'expressions/sad.png', surprised: 'expressions/surprised.png' }, default_expression: 'normal', nvl: true, type_animation: false } });[PreviousScript & Labels](script-and-labels.md)[NextVariables & Data Storage](data-storage.md)Last updated 4 years agoWas this helpful? # Components | Monogatari Documentation [Building Blocks](../building-blocks.md)Components[](.)OverviewA component is a custom, self-contained HTML element. It is defined as a JavaScript class and in that class, it contains all of its functionality and the HTML code that defines it. This enables you to The following code displays the most simple component you can create where it just renders some HTML you tell it to:Copyclass MyCustomElement extends Monogatari.Component { render () { return `<h1>This is my component</h1>`; } } MyCustomElement.tag = 'my-custom-element';Now that we have our component class, we need to register it in Monogatari so it knows it exists:Copymonogatari.registerComponent (MyCustomElement);And finally, just place it in our index.html with the tag we defined for it:Copy<my-custom-element></my-custom-element>When monogatari gets initialized, our component will be registered and its render method will be executed, making its final HTML be:Copy<my-custom-element class="animated" data-component="my-custom-element"> <h1>This is my component</h1> </my-custom-element>[](.)The move to componentsThe index.html file changed significantly from Monogatari v1 to v2. Here's a comparison of what the HTML looks now and how it looked before:New HTML[](.)Old HTML[](.)Copy<div id="monogatari"> <visual-novel> <language-selection-screen></language-selection-screen> <loading-screen></loading-screen> <main-screen> <main-menu></main-menu> </main-screen> <game-screen> <dialog-log></dialog-log> <text-box></text-box> <quick-menu></quick-menu> </game-screen> <gallery-screen></gallery-screen> <credits-screen></credits-screen> <load-screen></load-screen> <save-screen></save-screen> <settings-screen></settings-screen> <help-screen></help-screen> </visual-novel> </div>Copy<!-- Notice messages --> <div data-notice="exit" class="modal"> <div class="row spaced"> <p data-string="Confirm" class="col xs12 m12 l12 xl12">Do you want to quit</p> <div class="col xs12 m12 l12 xl12"> <button data-action="quit" data-string="Quit">Quit</button> <button data-action="dismiss-notice" data-string="Cancel">Cancel</button> </div> </div> </div> <div data-notice="slot-deletion" class="modal"> <div class="row spaced"> <p data-string="SlotDeletion" class="col xs12 m12 l12 xl12">Are you sure you want to delete this slot?</p> <p class="col xs12 m12 l12 xl12"><small></small></p> <div class="col xs12 m12 l12 xl12"> <button data-action="delete-slot" data-string="Delete">Delete</button> <button data-action="dismiss-notice" data-string="Cancel">Cancel</button> </div> </div> </div> <div data-notice="slot-overwrite" class="modal"> <div class="row spaced"> <p data-string="SlotOverwrite" class="col xs12 m12 l12 xl12">Are you sure you want to overwrite this slot?</p> <input type="text" name="name" class="margin-1 col xs12 m12 l12 xl12" required> <div class="col xs12 m12 l12 xl12"> <button data-action="overwrite-slot" data-string="Delete">Overwrite</button> <button data-action="dismiss-notice" data-string="Cancel">Cancel</button> </div> </div> </div> <!--Game Screen --> <section id="game" class="unselectable"> <div id="particles-js" data-ui="particles"></div> <div id="background" data-ui="background"></div> <div> <audio type="audio/mpeg" data-component="music"></audio> <audio type="audio/mpeg" data-component="voice"></audio> <audio type="audio/mpeg" data-component="sound"></audio> <div class="video-wrapper align-center vertical middle" data-component="video" data-ui="video-player"> <video type="video/mp4" data-ui="player" controls="true"></video> <button data-action="close-video" data-string="Close">Close</button> </div> <div data-component="modal" data-ui="messages" class="middle"> <div data-ui="message-content"></div> <div class="horizontal align-center" data-ui="inner-menu"> <button data-action="close" data-close="messages" data-string="Close">Close</button> </div> </div> <div data-component="modal" data-ui="input" class="middle"> <p data-ui="input-message" class="block"></p> <input type="text"> <small data-ui="warning" class="block"></small> <div> <button data-action="submit">Ok</button> </div> </div> </div> <div data-ui="choices" class="vertical align-center middle"></div> <div data-ui="text"> <img data-ui="face" alt=""> <span data-ui="who"></span> <p data-ui="say"></p> </div> <div data-ui="quick-menu" class="align-right"> <span data-action="back"><span class="fa fa-arrow-left"></span> <span data-string="Back">Back</span></span> <span data-action="distraction-free"><span class="fa fa-eye" data-action="distraction-free"></span> <span data-string="Hide" data-action="distraction-free">Hide</span></span> <span data-action="auto-play"><span class="fa fa-play-circle" data-action="auto-play"></span> <span data-string="AutoPlay" data-action="auto-play">Auto</span></span> <span data-action="open-menu" data-open="save"><span class="fa fa-save" data-action="open-menu" data-open="save"></span> <span data-string="Save" data-action="open-menu" data-open="save">Save</span></span> <span data-action="open-menu" data-open="load"><span class="fa fa-undo" data-action="open-menu" data-open="load"></span> <span data-string="Load" data-action="open-menu" data-open="load">Load</span></span> <span data-action="open-menu" data-open="settings"><span class="fa fa-gear" data-action="open-menu" data-open="settings"></span> <span data-string="Settings" data-action="open-menu" data-open="settings">Settings</span></span> <span data-action="end"><span class="fa fa-times-circle-o" data-action="end"></span> <span data-string="Quit" data-action="end">Quit</span></span> </div> </section> <!-- Loading Screen --> <section data-menu="loading" data-background=""> <div class="middle"> <h2 data-string="Loading">Loading</h2> <progress data-ui="load-progress" value="0" max="100"></progress> <small data-string="LoadingMessage">Wait while the assets are loaded.</small> </div> </section> <!-- Main Screen --> <section data-menu="main" data-background=""> <audio type="audio/mpeg" data-component="ambient"></audio> <div class="vertical align-right bottom animated bounceIn" data-ui="inner-menu"> <button data-action="start" data-string="Start">Start</button> <button data-action="open-menu" data-open="load" data-string="Load">Load</button> <button data-action="open-menu" data-open="settings" data-string="Settings">Settings</button> <button data-action="open-menu" data-open="help" data-string="Help">Help</button> </div> </section> <!-- Save Screen --> <section data-menu="save" data-background=""> <button class="fa fa-arrow-left top left" data-action="back"></button> <div class="horizontal"> <input type="text" placeholder="Save Slot Name" data-input="slotName" required> <button data-string="Save" data-action="save">Save</button> </div> <div data-ui="slots" class="row spaced align-center"></div> </section> <!-- Load Screen --> <section data-menu="load" data-background=""> <button class="fa fa-arrow-left top left" data-action="back"></button> <h2 data-string="Load">Load</h2> <div data-ui="saveSlots"> <h3 data-string="LoadSlots">Saved Games</h3> <div data-ui="slots" class="row spaced align-center"></div> </div> <div data-ui="autoSaveSlots"> <h3 data-string="LoadAutoSaveSlots">Auto Saved Games</h3> <div data-ui="slots" class="row spaced align-center"></div> </div> </section> <!-- Settings Screen --> <section data-menu="settings" data-background="" class="align-center"> <button class="fa fa-arrow-left top left" data-action="back"></button> <h2 data-string="Settings">Settings</h2> <div class="row spaced padded-1 align-center"> <div class="col xs12 s12 m6 l6 xl6 r6"> <div data-settings="audio" class="vertical align-center"> <h3 data-string="Audio">Audio</h3> <span data-string="Music">Music Volume:</span> <input type="range" min="0.0" max="1.0" step="0.1" data-action="set-volume" data-target="music"> <span data-string="Sound">Sound Volume:</span> <input type="range" min="0.0" max="1.0" step="0.1" data-action="set-volume" data-target="sound"> <span data-string="Voice">Voice Volume:</span> <input type="range" min="0.0" max="1.0" step="0.1" data-action="set-volume" data-target="voice"> </div> </div> <div class="col xs12 s12 m6 l6 xl6 r6"> <div data-settings="text-speed"> <h3 data-string="TextSpeed">Text Speed</h3> <input type="range" min="1" max="50" step="1" data-action="set-text-speed"> </div> <div data-settings="auto-play-speed"> <h3 data-string="AutoPlaySpeed">Auto Play Speed</h3> <input type="range" min="0" max="60" step="1" data-action="set-auto-play-speed"> </div> <div data-settings="language"> <h3 data-string="Language">Language</h3> <div class="horizontal"> <select data-action="set-language"> <option value="English">English</option> <option value="Español">Español</option> <option value="Français">Français</option> <option value="日本語">日本語</option> <option value="Nederlands">Nederlands</option> </select> <span class="fa fa-unsorted" data-select="set-language"></span> </div> </div> <div data-settings="resolution" data-platform="electron"> <h3 data-string="Resolution">Resolution</h3> <div class="horizontal"> <select data-action="set-resolution"></select> <span class="fa fa-unsorted" data-select="set-resolution"></span> </div> </div> </div> </div> </section> <!--Help Screen --> <section data-menu="help" data-background=""> <button class="fa fa-arrow-left top left" data-action="back"></button> <h2 data-string="Help">Help</h2> <div class="align-left padded-1"> <p data-string="AdvanceHelp">To advance through the game, click anywhere on the game screen or press the space key.</p> <h3 data-string="QuickButtons">Quick Menu Buttons</h3> <p><span class="fa fa-arrow-left"></span> <span data-string="BackButton">Back.</span></p> <p><span class="fa fa-eye"></span> <span data-string="HideButton">Hide Text.</span></p> <p><span class="fa fa-save"></span> <span data-string="SaveButon">Open the Save Screen.</span></p> <p><span class="fa fa-undo"></span> <span data-string="LoadButton">Open the Load Screen.</span></p> <p><span class="fa fa-gear"></span> <span data-string="SettingsButton">Open the Settings Screen.</span></p> <p><span class="fa fa-times-circle-o"></span> <span data-string="QuitButton">Quit Game.</span></p> </div> </section>That's a HUGE change right? Previously, all the HTML was right there in the index.html file and that was fine, it allowed people edit the HTML of their novel in a simple way and have a full understanding on where everything was. It also was helpful when styling because identifying all the elements and thus their selectors or even adding classes to them was a simple task.[](.)The problems it solvedSo if it was so good, why did it got changed? Well, sure, it was good but only up to a certain point. [](.)Engine UpgradesThe main issue with having all the HTML there and allowing people to change it as they pleased was that it actually made it harder for everyone whenever a new engine version was released.New releases often change some of that HTML, either by adding elements, removing some, editing properties or element types etc. and if game developers had changed that HTML in their games, the only way to upgrade to the new engine version was to manually review the new code, compare it with what they had and perform all the required changes to cover up for the differences which many times was not an easy task. For example, when a bunch of data properties get changed, developers would have to go element by element changing the data properties manually which was a horrible experience for game developers and this was only part of the problem. Often developers had to edit the engine's core in order to make the HTML they added work with monogatari, making the JavaScript code another area where they had to manually see what changed and re-adapt everything every time an update got released.Now that components are self-contained, their HTML and styling can still be changed freely by the users but whenever an engine update gets released, upgrading to it is as super simple, just having to change the monogatari core files. Even when a component's code gets changed and requires developers to update their modifications to it, it only means they'll have to change those that they actually care about instead of having to review each one of them. [](.)Code Sharing and Re-usabilityAnother important issue was that it was kind of hard to share code with others or even within different games from the same developers. Since it involved changing several files core to monogatari, the process was often complex and prone to errors.With components being self-contained, you can share it using just two files: a JavaScript file with the component class and a CSS file with its styling which people can just use in their projects by importing those files and not having to do much else.[](.)The problems it createdThis change has brought some confusion to people who expect all the HTML to be there for them to edit. It now results confusing where the code is coming from and how to update it which has caused monogatari not to be as beginner friendly as it used to be. However, I strongly believe these problems are the result of the lack of proper documentation. Components are in fact simple to use and work with, it just needs a mind shift and a proper explanation.[](.)Changing the contents of a componentYou might want to change the HTML of a component, either to add some elements for your game or because you want to change the way it looks. Every component has a render function like the one we showed in the simple example above. This function defines the default structure of any component but there are several ways to modify it.[](.)Appending ElementsIf all you want is to add some HTML element to the component, you can do so by adding your HTML in the index.html file as children of the component you want to edit. Let's retake our simple component example:Copyclass MyCustomElement extends Monogatari.Component { render () { return `<h1>This is my component</h1>`; } } MyCustomElement.tag = 'my-custom-element';We already know that we need to add it in our index.html by placing the tags for it but notice how our tags are left empty, there's nothing inside of them.Copy<my-custom-element></my-custom-element>When it gets rendered, the component runs its render function and places the results inside of it:Copy<my-custom-element class="animated" data-component="my-custom-element"> <h1>This is my component</h1> </my-custom-element>So now, let's say we wanted to add another element to it, a subtitle right below our h1 element we already defined. We could of course simply update our render function to include the new element:Copyrender () { return ` <h1>This is my component</h1> <h2>This is my subtitle</h2> `; }However, this time we were able to modify the render function because we had created our own component. What if we want to append some content to a component we didn't create like the ones monogatari ships with by default?Well, you can just place your contents in the index.html file. Instead of leaving the component tags empty, you can place any content you want there and it will get appended to the contents of your render function:Copy<my-custom-element> <h2>This is my subtitle</h2> </my-custom-element>[](.)Changing the TemplateSome times however, the changes we want to do are more complex than simply appending something to the HTML of an element. This is where the template method that all components have by default comes in.If you wanted to change a component, for example the game-screen element monogatari ships with, these are the steps you'd need to follow:[](.)1. Identify the component's render methodTo modify the structure of any component, we first need to know what it looks like by default. To do so, we need to dive into the code of the component we want to change, you'll find all component's in the monogatari source code repository. Inside the index.js file of every component, you'll find the class that defines it and most importantly, its render method. Let's take a look at the game-screen component it's render method looks something like this (It may have changed since the writing of this documentation):Copyrender () { return ` <div data-content="visuals"> <div id="tsparticles" data-ui="particles"></div> <div id="background" data-ui="background"></div> </div> `; }[](.)2. Create your template methodNow that we know the contents of its render method, we can create our own template to add or modify the code as we need. In our main.js, we would get the component and then, use its template method, copying the contents it currently has on the render method:Copymonogatari.component ('game-screen').template (() => { return ` <div data-content="visuals"> <div id="tsparticles" data-ui="particles"></div> <div id="background" data-ui="background"></div> </div> `; });[](.)3. Modify the contents to fit your needsNow that our template method is ready, we can use it to modify the component's structure by changing it any way we need to:Copymonogatari.component ('game-screen').template (() => { return ` <h1>My Title</h1> <div data-content="visuals"> <div id="tsparticles" data-ui="particles"></div> <div id="background" data-ui="background"></div> <div id="someCustomElement"></div> </div> `; });This will cause the component to use the template method we provided for it instead of the render method to get its contents giving us the freedom to modify the structure in a way almost as simple as if we had created the component ourselves.[](.)Performing DOM OperationsA component in the end is just another HTML element, you can still perform operations over it using the DOM as you normally would. Let's try to replicate what we did to the game-screen component just using DOM operations.[](.)Vanilla JavaScriptDue to current limitations in the [Pandora library](../advanced-monogatari-development/core-libraries/pandora.md) used for Monogatari's components, it is not recommended to interact with the innerHTML property of any component as it's prone to cause the duplication of the children inside such component. The approach shown here, while more verbose, avoids this situation.Copy// Get the game-screen HTML element const gameScreen = document.querySelector ('game-screen'); // Create the title element const title = document.createElement ('h1'); title.innerText = 'My Title'; // Add our title gameScreen.insertBefore (title, gameScreen.childNodes[0]); // Get the visuals HTML element const visuals = document.querySelector ('game-screen [data-content="visuals"]'); // Create the custom div element const div = document.createElement ('div'); div.id = 'someCustomElement'; // Add our custom div to the visuals element visuals.appendChild (div);[](.)Using the Component built-in DOM OperationsCopy// Get the game-screen Artemis element const gameScreen = document.querySelector ('game-screen').element (); // Add our title gameScreen.prepend ('<h1>My Title</h1>'); // Add our custom div to the visuals element gameScreen.content ('visuals').append ('<div id="someCustomElement"></div>');[](.)Using ArtemisThe component built in operations from above actually use [Artemis](../advanced-monogatari-development/core-libraries/artemis.md) internally, so they're equivalent to the following code:Copyconst { $_ } = Monogatari; // Add our title $_('game-screen').prepend ('<h1>My Title</h1>'); // Add our custom div to the visuals element $_('game-screen [data-content="visuals"]').append ('<div id="someCustomElement"></div>');[PreviousLife Cycle](actions/life-cycle.md)[NextLife Cycle](components/life-cycle.md)Last updated 4 years agoWas this helpful? # Variables & Data Storage | Monogatari Documentation [Building Blocks](../building-blocks.md)Variables & Data Storage[](.)OverviewChances are you'll eventually want to save some information about the player. It may be a name, some stats, the amount of in-game currency the player has or any other sort of variable you use in your game and want to persist every time the player saves the game.Monogatari has a built-in storage object where you can and should place all the variables you'll use and want to save. There's already a declaration made for you in the storage.js file. We recommend you keep your storage in that file since it's easier to keep track of all your variables and also means you only have to update that one when you add, remove or update any of them.By default, the storage looks something like this:Copymonogatari.storage ({ player: { name: '' } });[](.)Adding more variablesThe storage is a simple JSON object, meaning we can add as much variables as we want. Let's say we wanted to save the player's age, an important flag for our game and also some stats. We can add some new properties to our storage:Copymonogatari.storage ({ player: { name: '', age: 0 }, myFlag: false, stats: { hp: 100, mp: 100, inventory: { gold: 1000, iron: 10 } } });Notice that the values we provide in this initial declaration will be the default values for these variables.[](.)Updating a valueWhen something happens in our game and we want to update the value for one of our variables in our storage, we can use the same storage function to do so. Let's say we want to update the player's age:Copymonogatari.storage ({ player: { age: 18 } });Calling the storage function and providing a new object will not replace the initial one. It will instead take the values we provide and update the storage for us. We can of course update multiple variables in a single call:Copymonogatari.storage ({ player: { age: 18 }, myFlag: true, stats: { hp: 80 } });It is also possible to update a variable by doing a direct assignment:Copymonogatari.storage ().player.age = 18;[](.)Retrieving a variable's valueRetrieving the value of a variable once again involves the storage function. Let's say we wanted to retrieve the player's name. All of the following options are valid ways to do so:Copy// Providing the property we want to access const { name } = monogatari.storage ('player'); const name = monogatari.storage ('player').name; // Not providing any property const { player: { name } } = monogatari.storage (); const name = monogatari.storage ().player.name;As you can see from that example, it is possible to send the storage function a string, in this case player and it will return us that specific variable which can save us some code. Because of this feature, retrieving a non-nested variable like the myFlag one can be done in these ways:Copy// Providing the property we want to access const flag = monogatari.storage ('myFlag'); // Not providing any property const { myFlag } = monogatari.storage (); const flag = monogatari.storage ().myFlag;[](.)The retrieve, transform and update patternSome times, we want to update a variable in a way that the new value is the result of performing an operation over the current value. For example, let's say we wanted to add or substract 20 hp points from the player's stats. Copy// First, we have to retrieve the current value // for the hp variable const { hp } = monogatari.storage ('stats'); // Now, we can either substract or add those 20 points. const newHP = hp + 20; // Finally, we can perform the update monogatari.storage ({ stats: { hp: newHP } });For simple transformations as this one, we can actually perform the transformation and update in a single step:Copy// First, we have to retrieve the current value // for the hp variable const { hp } = monogatari.storage ('stats'); // We add the 20 points and update the value // in a single step monogatari.storage ({ stats: { hp: hp + 20 } });[](.)Data InterpolationIt is possible that we'll want to use the variables in the storage inside a dialog or even in a character's name. Monogatari features some custom string interpolation which you can use by writing a storage variable's key or name inside of two curly braces. Let's say you wanted to use the player's name and age in a dialog. Here's what our statement would look like:Copy'Hi {{player.name}}! I can see you\'re {{player.age}} years old.'Notice how you're able to interpolate variables even if they're nested as the player variables are.[](.)Mixing it togetherMonogatari has a lot of actions that will let you take advantage of the storage, from inputs and custom functions to dialogs and nice tricks with interpolations. Here's an example of a script using the storage on different actions.Copymonogatari.script ({ 'Start': [ {'Input': { 'Text': 'What is your name?', 'Validation': (input) => { return input.trim().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input, age: 18 } }); }, }}, 'Your name is {{player.name}}', {'Function':{ 'Apply': () => { // We'll overwrite the player's name but save the old one in a new // value so that we can roll back and restore it if needed. const { name } = monogatari.storage ('player'); const { hp, inventory: { gold } } = monogatari.storage ('stats'); monogatari.storage ({ player: { name: 'Georg', }, oldName: name, stats: { hp: hp - 50, inventory { gold: gold + 250 } } }); }, 'Revert': () => { // When rolling back, we'll restore the name to what it was before. const oldName = monogatari.storage ('oldName'); const { hp, inventory: { gold } } = monogatari.storage ('stats'); monogatari.storage ({ player: { name: oldName, }, stats: { hp: hp + 50, inventory { gold: gold - 250 } } }); } }}, '{{player.name}} is {{player_info.age}} years old.', '{{stats.mp}} costs the ability "Fire Storm".', '{{stats.inventory.gold}}g is in his bag of gold.', 'end' ] });[](.)Other ResourcesHere are some other resources that might be helpful to you regarding the usage of the storage.A tutorial on how to build a simple stat system using the storage (outdated as it was made for v1.4.1)[PreviousCharacters](characters.md)[NextActions](actions.md)Last updated 4 years agoWas this helpful? # Script & Labels | Monogatari Documentation [Building Blocks](../building-blocks.md)Script & Labels[](.)OverviewThe script is the soul and body of your game, it is the place where you define everything that happens on it. It is made out of labels that are just like the chapters of a book and inside of each label, you have a list of statements that will be run one by one as your story unfolds.[](.)ScriptA monogatari script is nothing more than a JSON object. In simple terms, a JSON object is defined with two curly braces {}. Inside these curly braces, you can have a list of named properties that point to a value. For example, if we wanted to define a person using the JSON notation, we could do something like this:Copyconst person = { 'name': 'Jane Doe', 'age': 24 };This is what we call key/value pairs, where a key like name or age points to a value like Jane Doe or 24. Notice how each key/value pair has a comma in the end separating it from the next one. Missing commas is the #1 issue that people have on their scripts.In Monogatari's case, your script is a list of keys (the names of the [labels](.) on your game) and values (the list of statements these labels are made of). For example:Copy{ 'Start': [ 'Hi there!', 'This is a list of statements', 'jump myLabel' ], 'myLabel': [ 'And this is yet another label', 'Also a list of statements', 'end' ] }Now, while that object does defines a monogatari script, just by itself it doesn't do anything. You must use the script function to let monogatari know what's your script:Copymonogatari.script ({ 'Start': [ 'Hi there!', 'This is a list of statements', 'jump myLabel' ], 'myLabel': [ 'And this is yet another label', 'Also a list of statements', 'end' ] });By default, the script.js file contains the initial script for any monogatari game and you can build your game from there by adding more content and removing the old one.[](.)LabelsLet's take a look at an incredibly simple script, this is the most basic script a game could have:Copymonogatari.script ({ 'Start': [ 'This is a statement.', 'end' ] });Notice that Start string. By default, Start is the first label that Monogatari will play when a game starts. As you can see, this label is pointing to a list of statements that monogatari will run one by one.In programming terms, a label is As we said before, labels are just like the chapters of a book but can also provide you with logical ways of dividing your game. [](.)Jumping between labelsAs books can have many chapters, you can have many labels as well! Not only that, you can make your game move from one label to another using the [Jump action](../script-actions/jump.md).Copymonogatari.script ({ 'Start':[ 'This is a statement.', 'jump mylabel' ], 'mylabel':[ 'This is another statement.', 'Pretty easy huh?', 'end' ] });As you can see from this script, we have two labels, Start and mylabel. Players will start in the Start one by default which will print out the first dialog and when the player clicks to advance, it will reach the jump mylabel statement. When reached, the game will simply carry on with the statements inside the mylabel label without the player ever noticing something happened.[](.)Translating your ScriptMaking your game available in many languages is super simple, head over to the Internationalization guide to learn more.[Internationalization](../configuration-options/game-configuration/internationalization.md)[](.)Splitting your script in multiple filesAs your game script grows, having it all in the same file might become troublesome. Splitting your script into multiple files is a good way to get organized and make it less cluttered. Learn how to do this in the Split files section.[Split Files](../configuration-options/split.md)[PreviousDiagnosing Errors](../diagnosing-errors.md)[NextCharacters](characters.md)Last updated 4 years agoWas this helpful? # Life Cycle | Monogatari Documentation [Building Blocks](../../building-blocks.md)[Actions](../actions.md)Life CycleAn action describes the functionality for a Monogatari statement, when Monogatari reads a part of the script (a statement), it will look for an action that matches the statement and run it.The life cycle of an action is divided in three parts: Mounting, Application and Reverting.[](.)Mounting CycleThe mounting cycle has 3 steps:[](.)SetupHere the action needs to set up everything it will need for working generally, in this section an action will register the variables it needs such as object histories and state variables or even add the HTML contents to the document.[](.)BindOnce the action has been setup, its time to bind all the necessary event listeners or perform more operations on the DOM once all elements have been setup.[](.)InitFinally, once the action was setup and it performed all the needed bindings, it may declare or modify variables that needed the HTML to be setup first or perform any other needed final operations.As noted, the Mounting cycle is mostly about getting everything setup for a correct operation of the action. The Application and Reverting cycles are used for the actual workings of an action in a game.Before executing an action Monogatari will check if the current statement matches with the action, therefore the Action must implement a matching function. If the statement to match should be a String, the action must implement the matchString () method, if it should be an Object, the action must implement the matchObject () method. Both should return a boolean on whether the action matches the given statement or not.[](.)Application CycleThe Application cycle refers to the cycle of an Action when it is run because of a statement in the script.The Application Cycle has 3 steps as well:[](.)Will ApplyExecuted when the action will be applied, if any operations need to be done before its application, this is the place.[](.)ApplyThe application itself, this is where all the logic regarding the action must be applied. Of course every action will implement its own logic depending on what it has to do.[](.)Did ApplyExecuted after the action was applied, this function is great for cleanup operations or any other thing that needs to be done after the action was applied.[](.)Revert CycleWhile the Application cycle is all about executing the action, the Revert cycle is the opposite and it reverts the things the Application cycle does. Reverting is used when the player goes back in the game and has equivalent steps to the Application Cycle:[](.)Will RevertExecuted when the action will be reverted, if any operations need to be done before its reversion such as checking for history elements or any other check, this is the place.[](.)RevertThe reversion of the action, its common that the actions revert to previous states or revert other changes done by the application of an action. Every action will implement its own logic depending on what it has to do.[](.)Did RevertExecuted after the action was reverted, this function is great for cleanup operations or any other thing that needs to be done after the action was reverted.[PreviousActions](../actions.md)[NextComponents](../components.md)Last updated 4 years agoWas this helpful? # Built-in Functions | Monogatari Documentation [Building Blocks](../../building-blocks.md)[Components](../components.md)Built-in FunctionsComponents come with a series of built-in functions that will make it easier to interact with them.[](.)Modify the HTML structureCopystatic template ()This function allows you to either get the HTML structure from a component, or set a new one to it.[](.)ExamplesCopymonogatari.component ('main-screen').template (() => { return ` <h1>My Awesome Game</h1> <main-menu></main-menu> `; });[](.)Running an arbitrary line of MonogatariCopymonogatari.run ("string")Runs a line of the script as though it were a regular line in the script array. Can be invoked from the JavaScript development console for testing, such as monogatari.run("jump myLabel") if you want to rapidly test a particular label. This can also be used as part of a function if you need to do something specific or fancy. Up to you.[PreviousBuilt-in Properties](built-in-properties.md)[NextChoices](../../script-actions/choices.md)Last updated 4 years agoWas this helpful? # Built-in Properties | Monogatari Documentation [Building Blocks](../../building-blocks.md)[Components](../components.md)Built-in PropertiesEvery component has some built-in properties that are used for its functionality and also to display them.[](.)IDCopyComponent._id = '';[](.)StateCopyComponent._state = {};[PreviousLife Cycle](life-cycle.md)[NextBuilt-in Functions](built-in-functions.md)Last updated 4 years agoWas this helpful? # Life Cycle | Monogatari Documentation [Building Blocks](../../building-blocks.md)[Components](../components.md)Life CycleA component represents an object or content in the game such as screens, menus and all other visual or structural elements.[](.)Mounting CycleThe life cycle of an component follows the Mounting cycle for actions.The mounting cycle has 3 steps:Setup - Here the action needs to set up everything it will need for working generally, in this section a component will generally add its HTML content to the global Monogatari object and will set up any needed configuration or state variables.Bind - Once the component has been setup, its time to bind all the necessary event listeners or perfom more operations on the DOM once all elements have been setup. Components will generally bind all the listeners needed for their inner elements to work correctly.Init - Finally, once the component was setup and it performed all the needed bindings, it may start performing its operations and perform all further needed operations.[PreviousComponents](../components.md)[NextBuilt-in Properties](built-in-properties.md)Last updated 6 years agoWas this helpful? # Choice Container | Monogatari Documentation [Components](../components.md)Choice Container[](.)Component StructureThe following code is this component's initial HTML structure. Remember you can change this structure any time by using the [template() component built-in function](../building-blocks/components/built-in-functions.md).Copyconst choices = this.props.choices.map ((choice) => { if (typeof choice.Clickable === 'function') { return new Promise ((resolve, reject) => { this.engine.assertAsync (choice.Clickable, this.engine).then (() => { resolve (`<button data-do="${choice.Do}" ${ choice.Class ? `class="${choice.Class}"`: ''} data-choice="${choice._key}">${choice.Text}</button>`); }).catch (() => { resolve (`<button data-do="${choice.Do}" ${ choice.Class ? `class="${choice.Class}"`: ''} data-choice="${choice._key}" disabled>${choice.Text}</button>`); }); }); } return Promise.resolve (`<button data-do="${choice.Do}" ${ choice.Class ? `class="${choice.Class}"`: ''} data-choice="${choice._key}">${choice.Text}</button>`); }); return Promise.all (choices).then ((choices) => ` <div data-content="wrapper"> ${ choices.join('') } </div> `);[PreviousMain Screen](main-screen.md)[NextSave Slot](save-slot.md)Last updated 5 years agoWas this helpful? # Credits Screen | Monogatari Documentation [Components](../components.md)Credits ScreenThe credits page so players can see who made the game[](.)DescriptionCopy<credits-screen></credits-screen>Attribution is important. People like knowing who made the games they love, and collaborators deserve credit for their work.Copymonogatari.configuration ('credits', { "Developers": { "Artist": "Yui", "Scenario Writer": "Gale", }, "Donors": { "Top Level Donors": [ "Tommy", "Scotty", "Lanny", "Robby", "Josie", "Freddy", "Bobby", "Lindsey" ], "Patrons": [ "Alex", "Shine", "Mika" ], }, "Special Thanks to": { "My parents": ["Mom", "Dad"], "My siblings": ["Brother", "Sister"], } });This will create a credits page that looks like this!A credits page listing all of the people written in the example code above.As you can see, Credits are stored as an object, just like most things in Monogatari. Also, like most things in Monogatari, the Credits page supports HTML, so if you want to make each of those names a clickable link that leads to the person's website, or email address, or something like that, you can easily achieve all of these things by inserting hyperlinks, like:Copymonogatari.configuration ('credits', { Developers: { Coder: "<a href="https://monogatari.io">Hyuchia</a>", } });If the credits object in your script.js file is empty, the credits button will not appear. No need to worry the User with an empty credits page, but as soon as you fill the credits object with something, the button will appear on the main menu title screen!The default Monogatari main menu with a visible credits button.[PreviousWait](../script-actions/wait.md)[NextQuick Menu](quick-menu.md)Last updated 4 years agoWas this helpful? # Loading Screen | Monogatari Documentation [Components](../components.md)Loading ScreenThe screen showing the asset preloading progress[](.)DescriptionCopy<loading-screen></loading-screen>This is the first screen that will be shown if Asset Preloading is enabled on the game's configuration.Component tag: loading-screen[](.)Component StructureThe following code is this component's initial HTML structure. Remember you can change this structure any time by using the [template() component built-in function](../building-blocks/components/built-in-functions.md).Copy<section data-component="loading_screen" data-screen="loading"> <div data-content="wrapper"> <h2 data-string="Loading" data-content="title">Loading</h2> <progress data-ui="load-progress" value="0" max="100" data-content="progress_bar"></progress> <small data-string="LoadingMessage" data-content="message">Wait while the assets are loaded.</small> </div> </section>[PreviousQuick Menu](quick-menu.md)[NextMain Screen](main-screen.md)Last updated 5 years agoWas this helpful? # Main Screen | Monogatari Documentation [Components](../components.md)Main Screen[](.)Component StructureThe following code is this component's initial HTML structure. Remember you can change this structure any time by using the [template() component built-in function](../building-blocks/components/built-in-functions.md).Copy[PreviousLoading Screen](loading-screen.md)[NextChoice Container](choice-container.md)Last updated 5 years agoWas this helpful? # Quick Menu | Monogatari Documentation [Components](../components.md)Quick Menu[](.)DescriptionCopy<quick-menu></quick-menu>The quick menu is the small menu rendered in the bottom of the screen, or on the top if you're in mobile. It's a menu that allows access to certain features during the game such as saving, loading, showing the log etc. The quick menu will render each of the buttons it has on its global configuration[](.)Configuration[](.)Buttons[](.)Methods[](.)Add ButtonCopystatic addButton ({ string, icon, data, ... }): voidAdds a button to the end of the menu.ExampleCopymonogatari.component ('quick-menu').addButton ({ string: 'Stats', icon: 'fas fa-tasks', data: { action: 'show-stats' } });[](.)Add Button AfterCopystatic addButtonAfter (after: string, { string, icon, data, ... }): voidAdds a button immediately after the button that has the string specified in the after argument.ExampleThe following sample will add the button after the Back buttonCopymonogatari.component ('quick-menu').addButtonAfter ('Back', { string: 'Stats', icon: 'fas fa-tasks', data: { action: 'show-stats' } });[](.)Add Button BeforeCopystatic addButtonBefore (before: string, { string, icon, data, ... }): voidAdds a button immediately before the button that has the string specified in the before argument.ExampleThe following sample will add the button before the Back buttonCopymonogatari.component ('quick-menu').addButtonBefore ('Back', { string: 'Stats', icon: 'fas fa-tasks', data: { action: 'show-stats' } });[](.)Remove ButtonThe following sample will remove the "Hide" button.Copymonogatari.component ('quick-menu').removeButton ('Hide');[PreviousCredits Screen](credits.md)[NextLoading Screen](loading-screen.md)Last updated 4 years agoWas this helpful? # Save Slot | Monogatari Documentation [Components](../components.md)Save Slot[](.)Component StructureThe following code is this component's initial HTML structure. Remember you can change this structure any time by using the [template() component built-in function](../building-blocks/components/built-in-functions.md).Copylet background = ''; const hasImage = this.props.image && this.engine.asset ('scenes', this.props.image); if (hasImage) { background = `url(${this.engine.setting ('AssetsPath').root}/${this.engine.setting ('AssetsPath').scenes}/${this.engine.asset ('scenes', this.props.image)})`; } else if (this.data.game.state.scene) { background = this.data.game.state.scene; if (background.indexOf (' with ') > -1) { background = Text.prefix (' with ', background); } background = Text.suffix ('show scene', background); } else if (this.data.game.state.background) { background = this.data.game.state.background; if (background.indexOf (' with ') > -1) { background = Text.prefix (' with ', background); } background = Text.suffix ('show background', background); } return ` <button data-delete='${this.props.slot}'><span class='fas fa-times'></span></button> <small class='badge'>${this.props.name}</small> <div data-content="background" style="${hasImage ? 'background-image' : 'background'}: ${background}"></div> <figcaption>${moment (this.props.date).format ('MMMM Do YYYY, h:mm:ss a')}</figcaption> `;[PreviousChoice Container](choice-container.md)[NextText-Box](text-box.md)Last updated 5 years agoWas this helpful? # Text-Box | Monogatari Documentation [Components](../components.md)Text-Box[](.)DescriptionCopy<text-box></text-box>Source Code: https://github.com/Monogatari/Monogatari/tree/develop/src/components/text-box[](.)PropertiesNameDescriptionmodeDefault is adv.The mode the textbox is currently displaying dialogs on, whether adv or nvl mode.[](.)Methods[](.)show(): voidShows the text-box element.[](.)checkUnread(): voidThis method is called by the [Dialog action](../script-actions/dialogs.md)after writing an NVL dialog. It will check if the player has an unread dialog (one that was added and the player has to scroll to read it). If there is one, the class unread will be added to the text-box element.[PreviousSave Slot](save-slot.md)[NextGame Configuration](../configuration-options/game-configuration.md)Last updated 4 years agoWas this helpful? # Game Configuration | Monogatari Documentation [Configuration Options](../configuration-options.md)Game Configuration[Asset Preloading](game-configuration/asset-preloading.md)[Internationalization](game-configuration/internationalization.md)[Saving](game-configuration/saving.md)[Skip Main Menu](game-configuration/skip-menu.md)[Storage](game-configuration/storage-engine.md)[PreviousText-Box](../components/text-box.md)[NextAsset Preloading](game-configuration/asset-preloading.md)Last updated 4 years agoWas this helpful? # Player Preferences | Monogatari Documentation [Configuration Options](../configuration-options.md)Player Preferences[PreviousStorage](game-configuration/storage-engine.md)[NextSplit Files](split.md)Last updated 4 years agoWas this helpful? # Split Files | Monogatari Documentation [Configuration Options](../configuration-options.md)Split FilesSplitting your Script into multiple files.As you work on your Monogatari game, you might find that your script.js file becomes very long and cumbersome to work with. This was an involved process in 1.4.1 but luckily, in Monogatari 2.0 it is now very easy to split your script into multiple files and for Monogatari to run all of them without issue.[](.)Formatting Your Second Script FileIn Monogatari 2.0, your second script file can be formatted the same way your first script file is formatted. For example, the script section of your script.js file might read:Copymonogatari.script ({ // The game starts here. 'Start': [ "y Hello there Protaganist-senpai!", "y Wow after one line of dialog this is starting to feel pretty long.", "y Let's jump to another label in another file!", "jump theNextLabel", ] });And the script of your second script file can say:Copymonogatari.script ({ // The Game Continues!! 'theNextLabel': [ "y Yeah there we go! That feels so much better.", "end" ] });And this will work just fine! All you have to do after that is make sure your index.html file actually reads the file. [](.)Getting Monogatari to read your Split FilesAll we need to do in order to achieve this is to edit our index.html file's source. Find this part of the source code:Copy <!-- Monogatari JavaScript Libraries --> <script src="./engine/debug/debug.js"></script> <script src="./engine/core/monogatari.js"></script> <script src="./js/options.js"></script> <script src="./js/storage.js"></script> <script src="./js/script.js"></script> <script src="./js/main.js"></script>And we're going to add another <script src=""></script>, with the source pointing to your file. For example, if you named your file script2.js and put it in the same folder with script.js, then we would change this to:Copy <!-- Monogatari JavaScript Libraries --> <script src="./engine/debug/debug.js"></script> <script src="./engine/core/monogatari.js"></script> <script src="./js/options.js"></script> <script src="./js/storage.js"></script> <script src="./js/script.js"></script> <script src="./js/script2.js"></script> <script src="./js/main.js"></script>Just like that!Note that these files will load in order from top to bottom, so if you have any conflicting labels that are the same between them, whatever's in the later files will overwrite the ones above them. Also note that main.js should be the last file in the list.[](.)Internationalization with Split FilesLet's say you want to make your game multi-lingual, like in the [Internationalization](../v/develop/configuration-options/game-configuration/internationalization.md) article. Split files can help to keep you organized with that too! Let's take this example from that page:Copymonogatari.script ({ 'English':{ 'Start':[ 'Hi, welcome to your first Visual Novel with Monogatari.' ] }, 'Español':{ 'Start':[ 'Hola, bienvenido a tu primer Novela Visual con Monogatari.' ] } });If you like, you could split this up into two files, like this:script.js[](.)scriptES.js[](.)Copymonogatari.script ({ 'English':{ 'Start':[ 'Hi, welcome to your first Visual Novel with Monogatari.' ] } });Copymonogatari.script ({ 'Español':{ 'Start':[ 'Hola, bienvenido a tu primer Novela Visual con Monogatari.' ] } });Then just make sure your index.html file points to scriptES.js and you're on your wayAnother Way, for Splitting Internationalized FilesAfter looking at the above two examples, you might want to split up files to help organize your story into chapters, and also have multiple languages at the same time! Unfortunately, if you did that by combining the above examples, you would more than likely come across a "Start Label Was Not Found" error. This would happen because although the monogatari.script() method adds new labels, if any label names conflict with already existing ones, it replaces them. Normally this is fine, but when working with Multi-Language games, the "English" label would be replaced in the second file, overwriting all of the work you did at run time! Luckily, there's a way around this.Copymonogatari.label('yourLabelName','English',[ "Let's continue the story here without issue!", ]);monogatari.label() looks at its arguments, first the name of the label you want to write to inside the monogatari._script object, and then if the next argument is a string, it processes that as a language. After that, it takes an array, and from there you just write the script to your game, the same way you do the script for a label, normally!You can also use monogatari.label() for all of your labels. You have plenty of options[NextResponsiveness](../style-and-design/responsiveness.md)Last updated 4 years agoWas this helpful? # Asset Preloading | Monogatari Documentation [Configuration Options](../../configuration-options.md)[Game Configuration](../game-configuration.md)Asset PreloadingWhen releasing your game online, the asset loading is one of the most important issues to be solved, it must be nice enough so that people can enjoy playing your game online even on slow connections and that's why since v1.3.2 Asset Preloading is built in right in the engine.[](.)Built In PreloadingThe asset preloading will show a progress bar when you enter the game, the entire preloading can be disabled using the Preload property in the options.js:This property default value is true meaning it will show the Preload Screen and do all the Asset PreloadingCopy'Preload': trueTo skip it, just change the value to false but remember that every asset will be loaded when you use it and may make a poor experience for users on slow connections.Copy'Preload': false[](.)Service Workers CacheOne of the benefits the Service Workers in Monogatari provide is the use of the Cache to serve your files even when offline once they've been loaded, this means that instead of taking the asset from the network, Monogatari will first check if the asset has been loaded in the cache, if it has then it will immediately load it from there but still making a network request to update the asset in the cache.This means the users will see the asset load instantly and will still receive the updates you make. More details about this strategy are available here and here.This functionality is inside the service-worker.js file. There you'll need to take notice of the three properties: name, version.The name and version properties are used to control the cache of your game, change the name to your game's name, with no spaces or special characters and the version property as a control.Copy// The name of your game, no spaces or special characters. const name = 'Monogatari'; // The version of the cache, changing this will force everything to be cached // again. const version = '0.1.0';What does using the version as a control means? changing the version will force all the assets to re-cache, useful specially when you make significant changes to them but remember that the cache strategy used will update your files eventually without the need of changing this version. Also the version does not need to be changed when removing/adding files to the cache. Most of the times you won't need to change it at all.This makes part of the effort to transform Monogatari games deployed online into full Progressive Web Apps which provides a lot of benefits. You can read more about what you need to do and what that means for your players in the Web Deployment SectionUse of Service Workers can also be disabled from the options.js file using the 'ServiceWorkers' property.This property default value is true meaning it will make use of Service Workers to cache your assets, Service Workers may be used for other activities in the future as well, disabling this option will disable them all.Copy'ServiceWorkers': trueTo disable it, just change it to false but remember that every asset will be loaded when you use it and may make a poor experience for users on slow connections.Copy'ServiceWorkers': falseIt's important to note that Service Workers are only available through HTTP or HTTPS protocols, meaning they will only be available when serving your game through a server. If you just open the files on the browser, you'll see an URL that starts with file:// and then in the console you'll find an error that says:Failed to register a ServiceWorker: The URL protocol of the current origin ('null') is not supported.Or, if using a newer Monogatari version, a warning that says:Service Workers are available only when serving your files through a server, once you upload your game this warning will go away. You can also try using a simple server like this one for development: Web Server for Chrome.Either way is completely normal, as soon as you upload your game online to make it playable, that message will go away on it's own.[PreviousGame Configuration](../game-configuration.md)[NextInternationalization](internationalization.md)Last updated 4 years agoWas this helpful? # Internationalization | Monogatari Documentation [Configuration Options](../../configuration-options.md)[Game Configuration](../game-configuration.md)InternationalizationMaking Monogatari games multilingual.[](.)OverviewYou can create a multi-language game pretty easily. To do so you need to follow these simple steps:[](.)1. Enable the MultiLanguage settingGo to the options.js file and change the 'MultiLanguage' value to true.Copy // Change to true for a MultiLanguage GameScreen. 'MultiLanguage': true,This will let monogatari know that you're planning to have your script in multiple languages. If you reload the page however, you'll most likely get an error like this one:This is normal and is caused because while we have indicated we would be having multiple versions of our script in multiple languages, we haven't changed the structure of our script to match that.[](.)2. Change your Script FormatNext we will be changing the script to support Multi Language games. A single language script as you know is defined as an object with a list of labels and usually looks something like this:Copymonogatari.script ({ 'Start': [ 'Hi, welcome to your first Visual Novel with Monogatari.', 'jump other' ], 'other': [ 'Another Label!', 'end' ] });Note how each label is in the top level of the script objects hierarchy. A Multi Language script will instead have language objects in the top level and inside of them, the labels of the script that correspond to that language, for example:Copymonogatari.script ({ 'English':{ 'Start': [ 'Hi, welcome to your first Visual Novel with Monogatari.', 'jump other' ], 'other': [ 'Another Label!', 'end' ] }, 'Español':{ 'Start': [ 'Hola, bienvenido a tu primer Novela Visual con Monogatari.', 'jump other' ], 'other': [ 'Otro label!', 'end' ] } });Note how we essentially duplicated our script inside this new language objects and translated it for each language. When saving a game, monogatari saves the label and step (index of the statement) it's currently on. For saved games to be fully compatible with each other, your different scripts should have the same label names and number of statements in them, otherwise, a game saved in one language would be invalid if the player changed language again and tried to load it. If you do a [jump](../../script-actions/jump.md) to a label that exists in one translation, but does not exist in the language that the player has selected for example, they will get an error telling them that the label does not exist, so be careful when crafting your script! In case some differences can't be avoided, you can rely on the [next action](../../script-actions/next.md) to fill act as a filler and make all scripts have the same size. If you have your script split in multiple files, you can read more on how to configure internationalization with split files here:[Split Files](../split.md)[](.)3. The Language Selection ScreenOnce you've formatted your script correctly, a language selection screen will appear for players that haven't selected a language yet. It will automatically detect the languages in your script and show buttons like the ones shown in the image.You most likely won't see this screen appear to you because you transitioned from a single language game to a multi language one and thus, your settings were already set. If you want to trigger this screen, you'll have to remove the settings from your storage using the dev tools.Players are also able to change their language from the settings screen. The following image shows the language selection setting that appears automatically when a multi language game is configured:In case you don't want the language selection screen to appear or have built your own, it can be disabled from the options.js file by changing the LanguageSelectionScreen property to false:Copy// If the 'Multilanguage' setting is set to `true`. This will enable a // language selection screen that will be shown before the asset loading // screen. If set to false, the loading screen will appear first instead and // players will have to change the language from the settings screen. 'LanguageSelectionScreen': false,[](.)4. Set the Default Language PreferenceFinally, you need to provide a default language for your game. While players will be able to choose their language, monogatari does need something to fall back to. Go to the options.js file once more and this time, go to the preferences section in the bottom. There, you should change the 'Language' property to the name of the language you want to have by default. Copy// Initial Language for Multilanguage Games or for the Default GUI Language. 'Language': 'Français',[](.)Supported LanguagesMonogatari already has built-in support for the following languages:БеларускаяDeutschEnglishEspañolFrançaisNederlandsPortuguêsРусскийاللغه العربية한국어日本語简体中文toki ponaThat means UI translations for those languages are available and the full UI will be shown in the correct language. You can also retrieve the list of available translations programmatically using the monogatari.translations () function. This function will return an object with all the available languages and the UI string translations for them.[](.)Translating the Engine UI for a not-yet-supported languageIf you need to translate the game into a language that Monogatari doesn't include in the [supported languages](.) or simply want to modify an existing translation, you can define your own UI translations as follows:Copymonogatari.translation ('YourLanguage', { 'SomeString': 'Your Translation' });You can also contribute to monogatari by adding new language translations to it or updating existing ones. You can learn more here:[Translations](../../advanced-monogatari-development/translations.md)[](.)Adding the UI translationsTo make a new language available for your players to choose from, you'll have to add translations for all the strings the UI requires. You can take the English strings file as reference so you can gather all the required strings and add your own translations for your language.You can also get a complete list of every string to translate programmatically by using the monogatari.translation ('<language_key>') function, for example:[](.)Adding the language metadataEach language also requires some metadata that will be used by monogatari.MetadataDescriptioncodeThe 2 letter code that represents that language. This code must be a valid ISO 639-1 code since it will be used to format the dates shown on the save slots.iconAn emoji that will be shown as the icon for the language in the language selection screen. Here's an example of the definition of a language metadata:Copymonogatari.languageMetadata ('Español', { code: 'es', icon: '🇲🇽' });Once you've setup all the string translations and language metadata, all you need is adding your script for your new language and the option will appear automatically both on the language selection and the settings screens.[PreviousAsset Preloading](asset-preloading.md)[NextSaving](saving.md)Last updated 4 years agoWas this helpful? # Saving | Monogatari Documentation [Configuration Options](../../configuration-options.md)[Game Configuration](../game-configuration.md)Saving[](.)Overview[](.)Multiple Games in the same domain.You may have multiple games in the same domain, if you keep the default save name in all of them, you'll notice they'll share the save and load slots. That's why, you can change the name prefix of the save slots in the options.js file, just change the value of the engine['slots'] variable.[](.)Change Number of Save SlotsYou can easily increase or decrease the number of save slots available for your game. Simply go to the options.js file and change the engine['Slots'] value to any number you want.[](.)Auto-SavingYou can make your game have autosaves every certain time, to do so, just change in the options.js file the 'AutoSave' property.By default, this property is set to 0, meaning AutoSaving is off.Copy'AutoSave': 0You can change this number to the number of every minutes you want your game to be autosaved. For example, the following will autosave the game every 5 minutes:Copy'AutoSave': 5You can also change the AutoSave Labels Name, to do so change the 'AutoSaveLabel' property in the options.js file.[PreviousInternationalization](internationalization.md)[NextSkip Main Menu](skip-menu.md)Last updated 4 years agoWas this helpful? # Skip Main Menu | Monogatari Documentation [Configuration Options](../../configuration-options.md)[Game Configuration](../game-configuration.md)Skip Main MenuYou may not want to have a main menu, in order for the game to start right away after the user enters.To do that, just change the 'ShowMenu' property in the options.js.This property default value is true meaning it will show the Main menu.Copy'ShowMenu': trueTo skip it, just change the value to false:Copy'ShowMenu': false[PreviousSaving](saving.md)[NextStorage](storage-engine.md)Last updated 6 years agoWas this helpful? # Storage | Monogatari Documentation [Configuration Options](../../configuration-options.md)[Game Configuration](../game-configuration.md)Storage[](.)OverviewMonogatari can use different storage engines so you can choose whichever works for your game best. This configuration can be found inside your options.js file. By default, it will look something like this:Copy // Define what storage engine should be used to save the game data. * // Adapters Available: // - LocalStorage: This one is used by default // - SessionStorage: Same as LocalStorage but will be cleared when the page // is closed. // - IndexedDB: The information is saved using the IndexedDB web API // - RemoteStorage: The information will be sent and retrieved from a given // URL Endpoint providing a REST API. 'Storage': { 'Adapter': 'LocalStorage', 'Store': 'GameData', 'Endpoint': '' }When releasing a game as a website, it's important to note that if people enter your game using the incognito mode in their browsers, their data will be erased when they close that window unless you choose to roll out your own server-based storage with the [Remote Storage](.) option.[](.)Local StorageThis is the default storage engine used by Monogatari and it uses your browser's local storage.[](.)Session StorageThe session storage works just like the local storage engine with the important difference that their data will be erased the moment they close the game.[](.)Indexed DB[](.)Remote Storage[PreviousSkip Main Menu](skip-menu.md)[NextPlayer Preferences](../player-preferences.md)Last updated 4 years agoWas this helpful? # Step 2: Download Monogatari | Monogatari Documentation [Getting Started](../getting-started.md)Step 2: Download MonogatariDownload the latest release of MonogatariOnce you've got your [environment set up](step-1-setup-your-environment.md), it's time to download Monogatari!Monogatari can be found in three places:[](.)1. Downloading it from the websiteThe website (https://monogatari.io/) always has Here you will have the choice between the stable version and the Nightly version. Given that you are on the documentation for version 2.0, nightly is the version you want.[](.)2. Dowloading it from GitHub[](.)3. Dowloading it from a Release AnnouncementThe GitHub page. https://github.com/Monogatari/Monogatari There you can choose between the different branches. For the newest version of Monogatari, you will want the "Development" branch. This will always be the most up to date version. This version also contains a bunch of source code that won't be necessary to make a game in monogatari, but might be useful to you if you'd like to help contribute to the core engine.The Monogatari Forums. https://community.monogatari.io/t/news Here there are always up to date announcements on any new release. Should you get the version from the main website, or the version linked in news announcements, you simply need to unzip the files and you're ready to go! If you instead opt to get the version on the GitHub page, you'll need to unzip the folder, and then take the contents of the dist folder.[PreviousStep 1: Setup Your Environment](step-1-setup-your-environment.md)[NextStep 3: Get Familiarized](step-3-get-familiarized.md)Last updated 4 years agoWas this helpful? # Step 1: Setup Your Environment | Monogatari Documentation [Getting Started](../getting-started.md)Step 1: Setup Your EnvironmentInstall all the tools you need to create your visual novelTo develop in Monogatari you'll need the same tools used for web development. This means that all you need is a text editor, with which **you'll be editing HTML, JavaScript and CSS files. Optionally, you may also use a local web server** while developing your novel.[](.)Getting a BrowserYou already have one of this but is it the best one for you to develop your game? We recommend you using either Chrome or Firefoxfor development. While your game will run in any modern browser, not all of them offer a great experience while developing. Both Chrome and Firefox have some pretty cool developer tools that will certainly make your life easier.[](.)Getting a Code EditorWhat editor to use comes down to personal preference but almost any text / code editor should work! You can even use Windows Notepad. However, to make your life easier, you should really try using a text editor that has been created for coding since those have features such as code syntax highlighting (all the different colors for functions, variables etc.) and some may even help you out while writing your code!Here are some that are pretty awesome to get you started and they are completely free to use:Visual Studio CodeAtomBracketsIf you've never used a code editor before, feel free to try them all! You should stick to the one you feel more comfortable with.[](.)Getting a Local Web Server (Optional)A web server, in simple terms is the piece of software that retrieves and sends the correct files to your browser when you enter a website. While it is completely optional to install one, it's important to establish why you would want a local web server.Here is a list of features that will only work if your game is running under a web server:[Asset preloading](../configuration-options/game-configuration/asset-preloading.md): Asset preloading, as it's name sounds, is the feature that allows your game to preload all images, sounds, videos etc. and save them up in the browser cache so that they are loaded faster and work offline. This feature requires a web server since it uses service workers and those are only available through HTTP or HTTPS protocols.[](.)Which server should you use?There are many web servers out there, some that may come up on search results include Apache and Nginx, however, these may be more complicated and a bit too much for what you'll need.The most simple option is to install the Web Server for Chrome extension that will allow you to create a very simple web server using the Chrome browser. Even if you prefer not to use chrome for your development, you can simply set up the server and then access it through any other browser.[PreviousGetting Started](../getting-started.md)[NextStep 2: Download Monogatari](getting-monogatari.md)Last updated 4 years agoWas this helpful? # Step 3: Get Familiarized | Monogatari Documentation [Getting Started](../getting-started.md)Step 3: Get FamiliarizedLearn the purpose of all directories and filesOnce you've [downloaded the latest Monogatari release](getting-monogatari.md), take some time to get familiar with the files and directories inside it.You can take a look at a more visual representation of the Monogatari project structure in this simple mind map.[](.)DirectoriesYou'll find some directories, here is what you should use them for.DirectoryContainsassetsThis is where all your assets such as images, audio files, font files etc. should goengineThe core engine files that make everything work. Updating Monogatari is as simple as replacing this directory.jsThis is where you'll set your game configuration and create your game script.styleCSS files for your project (CSS defines the look and style of your game).[](.)Root DirectoryInside the main directory you'll find some pretty useful files.FileContainsindex.htmlThis is your main file, open this file in a browser and there's your game!manifest.jsonrobots.txtThis file asks crawlers and other bots to keep your assets directory unindexed from web searches.service-worker.jsThis file defines your service worker, used for asset preloading and caching.htaccessThis file provides a basic Apache configuration in case you are using that web server.More about what to do and how to use this specific files will come later as you read the documentation.JS DirectoryJavaScript is the programming language you'll be using in Monogatari, it is what makes all your game work.FileContainsmain.jsIf you want to add more javascript, this is the file to do it!options.jsInitial settings of your game and engine settings.script.jsThe main script of your game. (Here's where your story, characters, images etc are declared)storage.jsThis is where you'll declare your custom variables that you want to save in your games.Style DirectoryCSS is the markup language used to style your game, from setting colors to improving the appearance, CSS is what will allow you do them all. FileContainsmain.cssAdd your styling in this file.[PreviousStep 2: Download Monogatari](getting-monogatari.md)[NextStep 4: Make Your First Visual Novel](step-4-make-your-first-visual-novel.md)Last updated 4 years agoWas this helpful? # Step 4: Make Your First Visual Novel | Monogatari Documentation [Getting Started](../getting-started.md)Step 4: Make Your First Visual NovelLearn the basic workflow for working on your gameOk, now you have some idea on [what the files you got are for](step-3-get-familiarized.md) so how can you start developing your game?Try the game first, open the index.html file inside the directory you just unzipped and play the sample game through.Once you've played it once, open the directory (the one you unzipped) with the editor you chose to start making changes.Open the script.js file with your editor and find the script section, as you'll see, all the dialogs you just saw are just a simple list in there. More information can be found in [the documentation](../building-blocks/script-and-labels.md).Change one of the dialogs, save the file and reload the game (just like you reload a website).Play it again and you'll see the dialog changed just like you made it. Now try adding more dialog to it and you'll quickly get how things are done.Once you've gotten yourself used to adding dialogs, [add a scene](../script-actions/show-scene.md) as a challenge, that means you'll have to add your image file to the assets/scenes/ directory, more instructions are on the link.If you manage to do all that, congratulations! You just made your first game and are probably more familiarized with the workflow you'll be using, just make changes, save, reload, try and repeat[NextUpgrading from v1.4.1](../upgrading-from-v1.4.1.md)Last updated 4 years agoWas this helpful? # v2.0.0.alpha.3 | Monogatari Documentation [Releases](../releases.md)v2.0.0.alpha.3The v2.0.0.alpha.3 release might not look so different from previous alpha versions but it has gone through a huge rewrite on some parts of the engine that will certainly add more possibilities in the future!Download: https://datadyne.perfectdark.space/monogatari/releases/Monogatari-v2.0.0.alpha.3.zip[](.)Change Log[](.)New FeaturesA video volume control slider has been added to the settings screenComponents are now represented by custom HTML elements using the web components APIPortuguese is now available as a translationAspect ratio can now be enforced on web deployed novels[](.)Bug FixesVideo playing would not make the game advance correctlyJump rollback caused unintended changed on the jump history and thus only worked onceStop audio action would cause issues with non looping media[](.)Other ChangesThe engine directory has been restructured to offer more organizationThe html error pages have now been moved to the engine directoryElectron features have been updated to improve app securityCSS classes and selectors are being restructured[](.)Update GuideThis guide only applies for games that were written using either the 2.0.0.alpha.1 or 2.0.0.alpha.2 testing versions.[](.)Update the Engine DirectoryDownload the new version and replace the engine directory on your game with the one on the latest version. Once you've replaced it, you can go ahead and remove the error directory in your game since it has been moved inside the engine directory.You can also delete the electron.js file you had in the project, it's now also being included as part of the engine directory.[](.)Add the new HTML LayoutThe alpha 2 version lacked any relevant HTML code on the index.html file which was in some times confusing for many people. The way Monogatari handles the HTML code has had it's third major rewrite and now, Monogatari uses custom elements that provide quite a lot of awesome functionality. So, the first change needed is adding the new HTML layout to your index.html file:2.0.0.alpha3 Layout[](.)2.0.0.alpha2 Layout[](.)index.htmlCopy<div id="monogatari"> <visual-novel> <loading-screen></loading-screen> <main-screen> <main-menu></main-menu> </main-screen> <game-screen> <dialog-log></dialog-log> <text-box></text-box> <quick-menu></quick-menu> </game-screen> <gallery-screen></gallery-screen> <load-screen></load-screen> <save-screen></save-screen> <settings-screen></settings-screen> <help-screen></help-screen> </visual-novel> </div> Previously, only a div was available:index.htmlCopy<div id="monogatari"></div>[](.)Update your CSSWith so many changes going on, there's a chance some CSS you had is no longer working, if you want to check the CSS applied to each component, you can check it at the component's directory.There's also still a small amount of CSS here that you can check as well.[](.)Rename the html () functionIf you made some changes to the HTML on previous alpha versions, you probably used the html () function. This function has now been renamed to template. For example, the following code will change the main screen's HTML code for the one we're giving.main.jsCopymonogatari.component ('main-screen').template (function () { return ` <h1>My Game Title</h1> <main-menu></main-menu> `; });In case the changes you are making to the HTML code of a component are simple such as the example above where no programming is required, you can now also use HTML templates!Simply add an HTML template tag to the body on theindex.html file with the id of the component you want it to apply to as follows:Example[](.)Full Body of Example[](.)index.htmlCopy<template id="main-screen"> <h1>My Game Title</h1> <main-menu></main-menu> </template>index.htmlCopy<body> <!-- Fallback when JavaScript is not available --> <noscript> <div class="middle text--center"> <h2>JavaScript Disabled or not Supported.</h2> <small>To play this game, please enable JavaScript executing or use a different browser.</small> </div> </noscript> <div id="monogatari"> <visual-novel> <loading-screen></loading-screen> <main-screen> <main-menu></main-menu> </main-screen> <game-screen> <dialog-log></dialog-log> <text-box></text-box> <quick-menu></quick-menu> </game-screen> <gallery-screen></gallery-screen> <load-screen></load-screen> <save-screen></save-screen> <settings-screen></settings-screen> <help-screen></help-screen> </visual-novel> </div> <template id="main-screen"> <h1>My Game Title</h1> <main-menu></main-menu> </template> </body> The HTML template and template function will have the same result.[](.)Add the ForceAspectRatio Setting Open your options.js file and add this new setting in the settings section, right bellow the AspectRatio one. v2.0.0.alpha.3 Settings[](.)v2.0.0.alpha.2 Settings[](.)Copy// The Aspect Ratio your background images are on. This only has effect on // web deployed novels if forceAspectRatio flag is on. 'AspectRatio': '16:9', // Force aspect ratio, it will make all images to comply with aspect ratio. // Values: 'None' (don't force), 'Visuals' (force only visuals) // or 'Global' (force all game) 'ForceAspectRatio': 'None', // Enables or disables the typing text animation for the whole game. 'TypeAnimation': true,Copy// The Aspect Ratio your background images are on. This only has effect on // web deployed novels if forceAspectRatio flag is on. 'AspectRatio': '16:9', // Enables or disables the typing text animation for the whole game. 'TypeAnimation': true,[](.)Add the Video volume PreferenceVideo volume can now be controlled by the player as it was previously possible with music, sounds and voice media. Open your options.js file and add this new preference in the preferences section of the file (right at the bottom).v2.0.0.alpha.3 Preferences[](.)v2.0.0.alpha.2 Preferences[](.)Copy// Initial Volumes from 0.0 to 1. 'Volume': { 'Music': 1, 'Voice': 1, 'Sound': 1, 'Video': 1 },Copy// Initial Volumes from 0.0 to 1. 'Volume': { 'Music': 1, 'Voice': 1, 'Sound': 1 },[Previousv2.0.0.alpha.4](v2.0.0.alpha.4.md)Last updated 6 years agoWas this helpful? # v2.0.0.alpha.4 | Monogatari Documentation [Releases](../releases.md)v2.0.0.alpha.4This release comes with various bug fixes and some new features!Download: https://datadyne.perfectdark.space/monogatari/releases/Monogatari-v2.0.0.alpha.4.zip[](.)Change Log[](.)New FeaturesChoices now have a `Clickable` property, allowing you to disable any choice while still showing it. More information about this property is available at the [Choices documentation](../script-actions/choices.md).It is now possible to define a class for the choices container from the Choice object. More information about this is available at the [Choices documentation](../script-actions/choices.md).Russian language is now available as a translation for the UI.The image gallery has been restored and is working again.A show background action has been added that acts the same as scene, but without removing any game elements.[](.)Bug FixesUsing named colors (black ,white, etc.) to show scenes would not work and the background color would not be changed.Choices would not be shown correctly when using a normal dialog while on NVL mode or the other way around.Clearing the text box would remove the nvl class from it, causing some flickering and undesired behaviors.Save slots would not display correctly a non-image background.A library was missing from the Canvas action implementation, preventing its functioning.The spacing between the titles on audio settings has been fixed[](.)Other ChangesAll core components functionality has been moved to the Pandora library.Dependencies have been updated[](.)Update GuideThis guide only applies for games that were written using the 2.0.0.alpha.3 version.To update your game, all you need is copying the contents inside the engine/core directory from the new release to the one you are using.[Previousv2.0.0.alpha.5](v2.0.0.alpha.5.md)[Nextv2.0.0.alpha.3](v2.0.0.alpha.3.md)Last updated 6 years agoWas this helpful? # v2.0.0.alpha.5 | Monogatari Documentation [Releases](../releases.md)v2.0.0.alpha.5This release comes with quite some bug fixes!Download: https://datadyne.perfectdark.space/monogatari/releases/Monogatari-v2.0.0.alpha.5.zip[](.)Change Log[](.)New FeaturesBelarusian and Korean languages have been added[](.)Bug FixesForceAspectRatio flag would not resize the game and its elements correctlyCharacters keeping the classes between show statements caused some unexpected behaviorsDialog log placeholder would not be removed after logging the first dialog on itGlobal audio stop statement was not working correctly and it wouldn't stop the mediaMain menu music would not be playedText speed setting was not working and speed remained the same if changedRemoving a listener would not remove the keyboard shortcut[](.)Other ChangesAction ids are now case insensitiveMonogatari now holds a global configuration and components should use that instead of keeping one internallyMain and Quick menus now use the render function as all the other components to create and show their buttonsThe main.js file now includes a few words on where code should be placedNew errors have been added to provide more feedback to developers on common mistakesThe quick-menu element is now being hidden on the splash screen by placing a splash-screen class on itString interpolation has been optimized so it is done only once while trying to find a matching action for the statement[](.)Update GuideThis guide only applies for games that were written using the 2.0.0.alpha.3 version or above.To update your game, all you need is copying the contents inside the engine/core directory from the new release to the one you are using.[Previousv2.0.0.alpha.6](v2.0.0.alpha.6.md)[Nextv2.0.0.alpha.4](v2.0.0.alpha.4.md)Last updated 5 years agoWas this helpful? # v2.0.0.alpha.6 | Monogatari Documentation [Releases](../releases.md)v2.0.0.alpha.6This releases fixes some bugs with the image gallery and credits screen as well as with the flow of the game.Download: https://datadyne.perfectdark.space/monogatari/releases/Monogatari-v2.0.0.alpha.6.zip[](.)Change Log[](.)New FeaturesA new block has been added to prevent the game from continuing / backtracking when an action is still pending[](.)Bug FixesImage Gallery would not be updated automatically when locking/unlocking an image and thus the player needed to refresh the page to see the changesThe credits screen used previous functionality that had been removed, it has now been updated and works again[](.)Other ChangesDependencies have been updated to their latest version[](.)Update GuideThis guide only applies for games that were written using the 2.0.0.alpha.3 version or above.To update your game, all you need is copying the contents inside the engine/core directory from the new release to the one you are using.[Previousv2.0.0.alpha.7](v2.0.0.alpha.7.md)[Nextv2.0.0.alpha.5](v2.0.0.alpha.5.md)Last updated 5 years agoWas this helpful? # v2.0.0.alpha.7 | Monogatari Documentation [Releases](../releases.md)v2.0.0.alpha.7This release is mainly just to change some assets directories naming so that they are consistent.Download: https://datadyne.perfectdark.space/monogatari/releases/Monogatari-v2.0.0.alpha.7.zip[](.)Change LogSome assets directories have been renamed to ensure their names are consistent.[](.)Update GuideThis guide only applies for games that were written using the 2.0.0.alpha.3 version or above.[](.)Rename your Assets Directories and declarationsThe following three assets directories have been renamed to a pluralized version to be consistent with the others so you'll have to rename them as well.AssetOld DirectoryNew DirectoryVoiceassets/voice/assets/voices/Soundsassets/sound/assets/sounds/Videoassets/video/assets/videos/Now, go to the options.js file and find your assets directories declarations, they should look something like this:options.jsCopy'AssetsPath': { 'root': 'assets', 'characters': 'characters', 'icons': 'icons', 'images': 'images', 'music': 'music', 'scenes': 'scenes', 'sound': 'sound', 'ui': 'ui', 'video': 'video', 'voice': 'voice', 'gallery': 'gallery' },You'll have to apply the same naming changes as before, the resulting object would then be:Copy'AssetsPath': { 'root': 'assets', 'characters': 'characters', 'icons': 'icons', 'images': 'images', 'music': 'music', 'scenes': 'scenes', 'sounds': 'sounds', 'ui': 'ui', 'videos': 'videos', 'voices': 'voices', 'gallery': 'gallery' },Notice how videos, voices and sounds are now pluralized. Finally, the assets declaration for those three asset types also need to be changed:Before:Copy// Define the voice files used in the game. monogatari.assets ('voice', { }); // Define the sounds used in the game. monogatari.assets ('sound', { }); // Define the videos used in the game. monogatari.assets ('video', { });After:Copy// Define the voice files used in the game. monogatari.assets ('voices', { }); // Define the sounds used in the game. monogatari.assets ('sounds', { }); // Define the videos used in the game. monogatari.assets ('videos', { });[Previousv2.0.0.alpha.8](v2.0.0.alpha.8.md)[Nextv2.0.0.alpha.6](v2.0.0.alpha.6.md)Last updated 5 years agoWas this helpful? # v2.0.0.alpha.8 | Monogatari Documentation [Releases](../releases.md)v2.0.0.alpha.8Please read the [update guide](.) for this release if you're updating your game from a previous iteration of Monogatari v2 as there's a small but really important change you need to make. This one will the last of the alpha releases and we'll start going through the beta series from now on as things have finally stabilized enough. This release has several bug fixes to improve compatibility with save files made with the v1.4.1 release or older.Download: https://datadyne.perfectdark.space/monogatari/releases/Monogatari-v2.0.0.alpha.8.zipThis one is also the first release that has been officially released as a npm package. It has been released as an UMD module, therefore it's possible to use it either on a browser as a global library, using es6 modules or nodejs modules.[](.)BrowserCopy<script src='./monogatari.js'></script>Copyconst monogatari = Monogatari.default;[](.) ES6 ModulesCopyimport Monogatari from '@monogatari/core';[](.) Node JSCopyconst Monogatari = require ('@monogatari/core');[](.)Change Log[](.)New FeaturesStorage variable interpolation can now be used on the input modal message and choice buttons textArabic language has been added as an UI translationThe core translation () function can now return a translation object given the nameText input will now be focused automatically right after showing up[](.)Bug FixesAudio media was not playing because of a bug introduced when the directory names where pluralizedThe volume property on the play action was not implemented and adding a volume had no effect on the volume of the media being playedComponents with an empty text prop would render it as true instead of showing blankAn error would show some times if a component had multiple spaces on its class propertyCharacter history from save files with an old format was not being transformed to the new format and made an error occur when trying to load itCharacter history files src property was not being changed to use the assets directory instead of the old ones and caused the files not to load correctly[](.)Other ChangesDependencies have been updated to their latest versionSmall improvements on text box stylingElectron related functionality has been updated to match latest version (7.x.x)[](.)Update GuideThis guide only applies for games that were written using the 2.0.0.alpha.3 version or above.On the options.js file, almost at the beginning of the file you should find this line:Copyconst { Monogatari: monogatari } = Monogatari;Replace it with this:Copyconst monogatari = Monogatari.default;Finally, as always, just copy the contents inside the engine/core/ directory from the new release to the one you are using.[PreviousEvents](../advanced-monogatari-development/events.md)[Nextv2.0.0.alpha.7](v2.0.0.alpha.7.md)Last updated 5 years agoWas this helpful? # Chrome App | Monogatari Documentation [Releasing Your Game](../releasing-your-game.md)Chrome AppGoogle has discontinued Web Apps on the Chrome Web Store. We recommend you release your game as a website instead.[Web](web.md)[PreviousImage Menus](../style-and-design/image-menus.md)[NextDesktop App](desktop.md)Last updated 4 years agoWas this helpful? # Desktop App | Monogatari Documentation [Releasing Your Game](../releasing-your-game.md)Desktop AppMonogatari comes with support for Electron out of the box.[](.)RequirementsNode.jsYarnCopy$ yarn install[](.)WindowsCopy$ yarn build:windows[](.)macOSSince macOS Catalina, Apple requires all apps made for mac to be notarized. This requires you to both own a mac and register as an Apple Developer (with an anual cost of $100 USD). If you want to distribute your game for mac but don't meet with these requirements, we can notarize your app for you, just reach out !Copy$ yarn build:mac[](.)LinuxCopy$ yarn build:linux[PreviousChrome App](chrome.md)[NextMobile](mobile.md)Last updated 4 years agoWas this helpful? # Mobile | Monogatari Documentation [Releasing Your Game](../releasing-your-game.md)Mobile[](.)RequirementsNode.js[](.)Installing Apache CordovaCopy$ npm install -g cordova [](.)Creating your projectCopy$ cordova create MyGameRunning this command will make Cordova create all the directories and files it needs to run. When it's over, copy all the files of your monogatari game to the www directory Cordova created.[](.)Building your App for AndroidCopy$ cordova build android --prod --release[](.)Distribution via APK files[](.)Distribution on the Play StoreDistributing your game in the play store requires you to become a developer with a one-time payment.[](.)Building your App for iOSCopy$ cordova build iOS --prod --release[](.)[PreviousDesktop App](desktop.md)[NextWeb](web.md)Last updated 4 years agoWas this helpful? # Web | Monogatari Documentation [Releasing Your Game](../releasing-your-game.md)WebMonogatari was built with this in mind, releasing your game online and thus making it available to play almost anywhere! To release your game online, you should first take care of the following steps.[](.)Complete your Meta TagsInside your index.html file, you'll notice there's a lot of meta tags, every single one of them have the purpose of improving your game SEO so it comes up in search result, make social sharing better and improve the experience on mobile devices. Filling them up is not necessary for your game to work but when releasing it online, failing to do so may result in a poor experience for your players.Many come already pre-filled but others are for you to fill in, in the case of all the icon files, since v1.3.2 Monogatari provides placeholder icons inside the img/icons/ directory, be sure to replace those with your own as well as the favicon.ico file before releasing your game. Here is the section of meta tags you'll find and that need to be filled in case they aren't already:Copy <title></title> <!--Up to 60-70 Characters. Optimal Format: Primary Keyword - Secondary Keyword | Brand Name--> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1"> <meta name="description" content=""> <!--Page description. No longer than 155 characters.--> <meta name="keywords" content=""> <meta name="author" content=""> <!--Name of the author.--> <!--Facebook Meta Tags--> <meta property="og:image" content="http://" /> <!--URL of Image to show--> <meta property="og:description" content="" /> <!--Page Description--> <meta property="og:site_name" content="" /> <!--The Name Here--> <meta property="og:url" content="http://" /> <!--The Web main URL--> <meta property="og:title" content="" /> <!--The Title Here--> <!--Google Meta Tags--> <meta itemprop="name" content=""> <!--The Name or Title Here--> <meta itemprop="description" content=""> <!--Page Description--> <meta itemprop="image" content="http://"> <!--URL of Image to show--> <!--Twitter Meta Tags - You'll have to validate your website here: https://dev.twitter.com/docs/cards/validation/validator--> <meta name="twitter:card" content="summary_large_image"> <!--Content Card Type--> <meta name="twitter:domain" content=""> <!--Your web's domain--> <meta name="twitter:site" content="@"> <!--@publisher--> <meta name="twitter:title" content=""> <!--Page Title--> <meta name="twitter:description" content=""> <!--Page description less than 200 characters--> <meta name="twitter:creator" content="@"> <!--@author--> <meta name="twitter:image:src" content="http://"> <!--URL of Image to show--> <!--Android Meta Tags--> <meta name="mobile-web-app-capable" content="yes"> <link rel="icon" sizes="192x192" href="img/icons/icon_192x192.png"> <!--192 x 192 Icon--> <link rel="icon" sizes="128x128" href="img/icons/icon_128x128.png"> <!--128 x 128 Icon--> <!--Apple Meta Tags--> <meta name="apple-mobile-web-app-title" content=""> <!--App Title or Name--> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <!--Styling for the iOS Status Bar--> <link rel="apple-touch-icon" href="img/icons/icon_60x60.png"> <!--60 x 60 Icon--> <link rel="apple-touch-icon" sizes="76x76" href="img/icons/icon_76x76.png"> <!--76 x 76 Icon--> <link rel="apple-touch-icon" sizes="120x120" href="img/icons/icon_120x120.png"> <!--120 x 120 Icon--> <link rel="apple-touch-icon" sizes="152x152" href="img/icons/icon_152x152.png"> <!--152 x 152 Icon--> <link rel="apple-touch-icon" sizes="152x152" href="img/icons/icon_167x167.png"> <!--167 x 167 Icon--> <link rel="apple-touch-icon" sizes="152x152" href="img/icons/icon_180x180.png"> <!--180 x 180 Icon--> <!--Microsoft Tags--> <meta name="msapplication-TileColor" content=""> <!--Color of the tile on Windows. In hexadecimal format--> <meta name="application-name" content="" /> <!-- App Title --> <meta name="msapplication-tooltip" content="" /> <!--Small text on hover--> <meta name="msapplication-starturl" content="http://" /> <!-- URL to start in --> <meta name="msapplication-square70x70logo" content="img/icons/icon_70x70.png" /> <!--Image for Tile 70x70--> <meta name="msapplication-square150x150logo" content="img/icons/icon_150x150.png" /> <!--Image for Tile 150x150--> <meta name="msapplication-wide310x150logo" content="img/icons/icon_310x150.png" /> <!--Image for Tile 310x150--> <meta name="msapplication-square310x310logo" content="img/icons/icon_310x310.png" /> <!--Image for Tile 310x310--> <link rel="publisher" href=""> <!--Publisher's Google+ URL--> <meta name="theme-color" content=""><!--Theme color for browsers in hexadecimal format.--> <link rel="shortcut icon" href="img/favicon.ico" /> <!--Favicon. Good tool for creating one: http://xiconeditor.com/ Create all sizes.--> <link rel="canonical" href=""> <!--Canonical URL of your webpage-->[](.)Optimize your imagesOptimizing your images is imperative to provide a better experience, we all hate when pages take a lot of time to load, even when using the preload features built in on Monogatari, optimizing your images will provide a better experience, the method you choose to do so is entirely yours to pick.[](.)Improve Asset LoadingTo improve asset loading and enable offline playing, you'll have to complete the files list you need to save in the cache for your game to be able to work, information about this can be found in the Asset Preload Configuration.[](.)Improve the Mobile experienceA manifest.json file is shipped on Monogatari since v1.3.2, this file is needed to provide a better mobile experience, specifically it's another requirement to transform your app in a Progressive Web App which will enable a great experience for your users, specially if they add your game to their home screen which you should encourage! adding it to home screen will show your game icon just as with other app, then show a splash screen when loaded using the background_color and icons defined in your mainfest.json file and if you've set up correctly your service worker cache, it will also be possible to play it offline, making it work almost like a native mobile app, an experience your players will truly love.Inside this file you'll only need to pay attention to four properties:Copy"short_name": "Monogatari", // Maximum 12 characters "name": "Monogatari Visual Novel", // Maximum 45 characters "background_color": "#F7F7F7", // Background color for the Splash Screen "theme_color": "#F7F7F7", // General theme color for the browserFor more information about the manifest and what every property does, check this site.[](.)Upload your game[PreviousMobile](mobile.md)[NextCore Libraries](../advanced-monogatari-development/core-libraries.md)Last updated 4 years agoWas this helpful? # Show Canvas | Monogatari Documentation [Script Actions](../script-actions.md)Show Canvas[](.)DescriptionCopy'show canvas <object_id> <mode> [with [...classes]]'Action ID: CanvasReversible: YesRequires User Interaction: Depends on the mode the canvas is displayed on[](.)ParametersNameOptionalDescriptionobject_idNoThe ID of a canvas object previously defined.modeNoDefines what way you want to show the video like.Possible Values:modal - Shows the video as a displayableimmersive - Shows the canvas covering the full game screen background - Shows the canvas as a background for your characters...classesYesList of CSS classes to add to the component.[](.)Canvas ObjectsCopy{ layers: [], props: {}, state: {}, start: function (layers, props, state, container) { // In here we do everything we need to start // our canvas and perform the updates }, stop: function (layers, props, state, container) { // Any actions we need to perform to stop the // canvas such as canceling animations and // freeing resources before the element gets removed }, resize: function (layers, props, state, container) { // Perform any actions required when the window }, }PropertyTypeDescriptionlayersArray<string>Most of the times, a single canvas HTML element is not enough to represent all we require and it may not be efficient either since it would have to re-draw all its elements every time we needed to clear it. The layers property is a list of strings (names of layers) that you want your canvas to have. A HTML canvas element with a data-layer property will be created for every layer you list here.If none is provided, a single canvas will be created with a default base layer.propsobjectThe props object provides a place to add any static functions or variables you might want to share between your object functions. These are properties that don't change throughout the execution of your canvas object.stateobjectThe state object provides a place where you can add any variables you wish to share between your object functions. These are properties you expect to change throughout the execution of your canvas object.startfunctionThe function that will be called right after the HTML element for the canvas is created. In here, you should draw your contents to the layers you setup and setup any update required.stopfunctionThis function will be called by the hide canvas action to stop and remove your canvas. In here you should cancel any animations and free up any resources in use by your canvas.resizefunctionThis functioon will be called every time the window gets resized, it's useful for reacting to this changes by changing your canvas width and height.[](.)Showing as a background[](.)Defining the ObjectThe first thing we need to do is create our canvas object, we'll call this one stars. Copymonogatari.action ('Canvas').objects ({ stars: { } });We want two layers for it, one represents the sky and the other one is where the stars will be drawn, therefore, we'll provide these layers to our object:Copymonogatari.action ('Canvas').objects ({ stars: { layers: ['sky', 'stars'], } });Next, we'll define the functions used to draw both the sky and stars in the object's props property. We're placing them there because we'll want to re-draw the canvas when the window is resized so having them in a place that we can easily access from both the start and resize functions would be the best.Copymonogatari.action ('Canvas').objects ({ stars: { layers: ['sky', 'stars'], props: { drawStar: (ctx, r) => { ctx.save(); ctx.beginPath(); ctx.moveTo(r, 0); for (let i = 0; i < 9; i++) { ctx.rotate(Math.PI / 5); if (i % 2 === 0) { ctx.lineTo((r / 0.525731) * 0.200811, 0); } else { ctx.lineTo(r, 0); } } ctx.closePath(); ctx.fill(); ctx.restore(); }, drawSky: (sky) => { const width = sky.width; const height = sky.height; const ctx = sky.getContext('2d'); ctx.fillRect(0, 0, width, height); ctx.translate(width / 2, height / 2); // Create a circular clipping path ctx.beginPath(); ctx.arc(0, 0, width * 0.4, 0, Math.PI * 2, true); ctx.clip(); // draw background const lingrad = ctx.createLinearGradient(0, -1 * width / 2, 0, height / 2); lingrad.addColorStop(0, '#232256'); lingrad.addColorStop(1, '#143778'); ctx.fillStyle = lingrad; ctx.fillRect(-width/2, -height/2, width, height); }, drawStars: (stars, drawStar) => { const width = stars.width; const height = stars.height; const ctx = stars.getContext('2d'); // draw stars for (var j = 1; j < 50; j++) { ctx.save(); ctx.fillStyle = '#fff'; ctx.translate(width - Math.floor(Math.random() * width), height - Math.floor(Math.random() * height)); drawStar(ctx, Math.floor(Math.random() * 4) + 2); ctx.restore(); } } }, } });We're now set to create our start function. On it we'll use the functions we added on the props property to actually draw our canvas. This time we didn't provided a state object because we don't have any variable that will be changing.Copymonogatari.action ('Canvas').objects ({ stars: { layers: ['sky', 'stars'], props: { drawStar: (ctx, r) => { ctx.save(); ctx.beginPath(); ctx.moveTo(r, 0); for (let i = 0; i < 9; i++) { ctx.rotate(Math.PI / 5); if (i % 2 === 0) { ctx.lineTo((r / 0.525731) * 0.200811, 0); } else { ctx.lineTo(r, 0); } } ctx.closePath(); ctx.fill(); ctx.restore(); }, drawSky: (sky) => { const width = sky.width; const height = sky.height; const ctx = sky.getContext('2d'); ctx.fillRect(0, 0, width, height); ctx.translate(width / 2, height / 2); // Create a circular clipping path ctx.beginPath(); ctx.arc(0, 0, width * 0.4, 0, Math.PI * 2, true); ctx.clip(); // draw background const lingrad = ctx.createLinearGradient(0, -1 * width / 2, 0, height / 2); lingrad.addColorStop(0, '#232256'); lingrad.addColorStop(1, '#143778'); ctx.fillStyle = lingrad; ctx.fillRect(-width/2, -height/2, width, height); }, drawStars: (stars, drawStar) => { const width = stars.width; const height = stars.height; const ctx = stars.getContext('2d'); // draw stars for (var j = 1; j < 50; j++) { ctx.save(); ctx.fillStyle = '#fff'; ctx.translate(width - Math.floor(Math.random() * width), height - Math.floor(Math.random() * height)); drawStar(ctx, Math.floor(Math.random() * 4) + 2); ctx.restore(); } } }, start: function ({ sky, stars }, props, state, container) { let width = 150; let height = 150; if (container.props.mode === 'background') { width = this.width (); height = this.height (); } sky.width = width; sky.height = height; stars.width = width; stars.height = height; props.drawSky (sky); props.drawStars (stars, props.drawStar); return Promise.resolve (); } });Next, we'll create our stop function. On canvas objects that feature animations this is a very important function since all animations need to be canceled. Otherwise, they will drain the memory of the browser even if they're no longer being shown. In this case, we don't have any animations but we'll clear the canvas object completely when stopped.Copymonogatari.action ('Canvas').objects ({ stars: { layers: ['sky', 'stars'], props: { drawStar: (ctx, r) => { ctx.save(); ctx.beginPath(); ctx.moveTo(r, 0); for (let i = 0; i < 9; i++) { ctx.rotate(Math.PI / 5); if (i % 2 === 0) { ctx.lineTo((r / 0.525731) * 0.200811, 0); } else { ctx.lineTo(r, 0); } } ctx.closePath(); ctx.fill(); ctx.restore(); }, drawSky: (sky) => { const width = sky.width; const height = sky.height; const ctx = sky.getContext('2d'); ctx.fillRect(0, 0, width, height); ctx.translate(width / 2, height / 2); // Create a circular clipping path ctx.beginPath(); ctx.arc(0, 0, width * 0.4, 0, Math.PI * 2, true); ctx.clip(); // draw background const lingrad = ctx.createLinearGradient(0, -1 * width / 2, 0, height / 2); lingrad.addColorStop(0, '#232256'); lingrad.addColorStop(1, '#143778'); ctx.fillStyle = lingrad; ctx.fillRect(-width/2, -height/2, width, height); }, drawStars: (stars, drawStar) => { const width = stars.width; const height = stars.height; const ctx = stars.getContext('2d'); // draw stars for (var j = 1; j < 50; j++) { ctx.save(); ctx.fillStyle = '#fff'; ctx.translate(width - Math.floor(Math.random() * width), height - Math.floor(Math.random() * height)); drawStar(ctx, Math.floor(Math.random() * 4) + 2); ctx.restore(); } } }, start: function ({ sky, stars }, props, state, container) { let width = 150; let height = 150; if (container.props.mode === 'background') { width = this.width (); height = this.height (); } sky.width = width; sky.height = height; stars.width = width; stars.height = height; props.drawSky (sky); props.drawStars (stars, props.drawStar); return Promise.resolve (); }, stop: ({ sky, stars }, props, state, container) => { sky.getContext('2d').clearRect (0, 0, sky.width, sky.height); stars.getContext('2d').clearRect (0, 0, stars.width, stars.height); } });Finally, since we mentioned we wanted to re-draw our canvas when the window got resized (to make it always fit the entire screen), we'll create our resize function with code very similar to our start function.Copymonogatari.action ('Canvas').objects ({ stars: { layers: ['sky', 'stars'], props: { drawStar: (ctx, r) => { ctx.save(); ctx.beginPath(); ctx.moveTo(r, 0); for (let i = 0; i < 9; i++) { ctx.rotate(Math.PI / 5); if (i % 2 === 0) { ctx.lineTo((r / 0.525731) * 0.200811, 0); } else { ctx.lineTo(r, 0); } } ctx.closePath(); ctx.fill(); ctx.restore(); }, drawSky: (sky) => { const width = sky.width; const height = sky.height; const ctx = sky.getContext('2d'); ctx.fillRect(0, 0, width, height); ctx.translate(width / 2, height / 2); // Create a circular clipping path ctx.beginPath(); ctx.arc(0, 0, width * 0.4, 0, Math.PI * 2, true); ctx.clip(); // draw background const lingrad = ctx.createLinearGradient(0, -1 * width / 2, 0, height / 2); lingrad.addColorStop(0, '#232256'); lingrad.addColorStop(1, '#143778'); ctx.fillStyle = lingrad; ctx.fillRect(-width/2, -height/2, width, height); }, drawStars: (stars, drawStar) => { const width = stars.width; const height = stars.height; const ctx = stars.getContext('2d'); // draw stars for (var j = 1; j < 50; j++) { ctx.save(); ctx.fillStyle = '#fff'; ctx.translate(width - Math.floor(Math.random() * width), height - Math.floor(Math.random() * height)); drawStar(ctx, Math.floor(Math.random() * 4) + 2); ctx.restore(); } } }, start: function ({ sky, stars }, props, state, container) { let width = 150; let height = 150; if (container.props.mode === 'background') { width = this.width (); height = this.height (); } sky.width = width; sky.height = height; stars.width = width; stars.height = height; props.drawSky (sky); props.drawStars (stars, props.drawStar); return Promise.resolve (); }, stop: ({ sky, stars }, props, state, container) => { sky.getContext('2d').clearRect (0, 0, sky.width, sky.height); stars.getContext('2d').clearRect (0, 0, stars.width, stars.height); }, resize: function ({ sky, stars }, props, state, container) { if (container.props.mode === 'background') { const width = this.width (); const height = this.height (); sky.getContext('2d').clearRect (0, 0, sky.width, sky.height); stars.getContext('2d').clearRect (0, 0, stars.width, stars.height); sky.width = width; sky.height = height; stars.width = width; stars.height = height; props.drawSky (sky); props.drawStars (stars, props.drawStar); } } } });[](.)Using it in our scriptOnce we've got our canvas object setup, we can go ahead and show it in our game.Copymonogatari.script ({ // The game starts here. 'Start': [ 'show canvas stars background with fadeIn', 'Shooting for the stars!', 'end' ] });[PreviousPlay Voice](play-voice.md)[NextShow Background](show-background.md)Last updated 4 years agoWas this helpful? # Show Character | Monogatari Documentation [Script Actions](../script-actions.md)Show CharacterShow a character's sprite[](.)DescriptionCopy'show character <character_id> <sprite_id> [at [class]] [with [animations] [classes] [properties]]'The character action allows you to display a character's sprite. For other kind of images, take a look at the [show image action](show-image.md).Action ID: Show::CharacterReversible: YesRequires User Interaction: No[](.)PropertiesThese are special properties/classes that can be used when showing a character:NameDescriptiondurationDuration affects the animation-duration CSS property so you can change the duration of any animations applied to the character.moveWith this class, the character will move nicely from one position to another one.transitionTransition affects the transition-duration CSS property so you can change the duration of the transition for the move class.end-<className>When using this property, the provided class will be added to the sprite when it gets changed.[](.)ExamplesRemember every character image must be declared in the characters object.Copy'show character e normal at center with fadeIn',The animation is completely optional, and if a position is not given, it will show in the center by default.Copy'show character e normal', 'show character e normal with fadeIn',[](.)Exit AnimationsNew to version 2.0: You can now also set an animation that will play when the character is removed or replaced.Copyshow character <character_id> <sprite_id> [at [class]] [with [animation] [end-[animation]] [classes]][](.)DurationThis is useful if you want to smoothly transition the same character's sprites, such as this example involving crossfades.The following code will show the character with a fadeIn animation that will take 20 seconds to complete.Copy "Hello", "show character s Normal at center with fadeIn end-fadeOut", "s Hi theeeeere.", "show character s Happy at center with fadeIn end-fadeOut", "I'm happy now, and my smile just faded onto my face.", "show character s Normal at center with fadeIn", "Normal."Copy'show character e normal with fadeIn duration 20s'[](.)End AnimationsEnd animations are a way of preparing an animation to happen whenever a sprite gets changed, the following code will show the character first and when it reaches the next line, the character will fade out while the new sprite fades in.Copy'show character e normal with end-fadeOut', 'show character e happy with fadeIn'[PreviousShow Background](show-background.md)[NextShow Image](show-image.md)Last updated 4 years agoWas this helpful? # Choices | Monogatari Documentation [Script Actions](../script-actions.md)ChoicesLearn about choices, their properties and how to use themOverviewChoices allow players to take decisions and then react based on whatever they chose. They are represented on your script as Objects and they allow you to define as many choices as you can fit on the screenGlobal PropertiesNameTypeOptionalDescriptionClassstringYesA space separated list of classes to add to the choice container.DialogstringYesSpecifies a dialog to be shown along with the choices.TimerobjectyesA timer configuration object....ChoicesObjectsNoAny other object added will be considered as a choice. Choices have their own individual properties that are described below.[](.)Choices PropertiesNameTypeOptionalDescriptionClassstringYesA space separated list of classes to add to the choice.ClickablefunctionYesA function returning a boolean, whether the option should allow to be clicked or not, effectively disabling a choice while still showing it if needed.ConditionfunctionYesA function returning a boolean, whether the choice should be shown or not.DostatementNoThe action to perform if the the choice is picked. Contrary to the onChosen property, this one should be a Monogatari statement, meaning it can be anything you are able to use in your script.onChosenfunctionYesA function that will be run if the choice is picked.onRevertfunctionYesA function that will revert the actions done on the onChosen function of the choice should the player go back.TextstringNoThe text that will be shown in the choice.[](.)ExamplesEvery choice requires 2 properties: the text to display and what to do when it's clicked.Copy{'Choice': { 'Developer': { 'Text': 'I’m a developer.', 'Do': 'jump Developer' }, 'Writer': { 'Text': 'I’m a writer.', 'Do': 'jump Writer' }, 'Artist': { 'Text': 'I’m an artist.', 'Do': 'jump Artist' }, 'Player': { 'Text': 'I’m a Player.', 'Do': 'jump Player' } }}[](.)Conditional ChoicesThere are some cases where you would only want to show a choice if certain conditions are met. For this example, let's assume this is your storage variable:Copy'use strict'; // Persistent Storage Variable monogatari.storage ({ played: false });As you can see, we added a 'played' variable inside the storage and set its default to false. Now, let's see what would happen with the following Choices:Copy{'Choice': { 'Developer': { 'Text': 'I’m a developer.', 'Do': 'jump Developer' }, 'Player': { 'Text': 'I’m a Player.', 'Do': 'jump Player', 'Condition': function () { return this.storage ('played'); // The 'Player' option will only be shown if this returns true. } } }}'Player' will only be shown if the 'played' variable we added is true, since we defined it as false, then it won't be shown however, if somewhere along the script we were to change that variable to true then this choice will be shown.[](.)Showing Text when Showing the ChoicesYou might want to show a dialog along with the choices, this is possible using the Dialog property inside the object:Copy{'Choice': { 'Dialog': 'e Before I continue, let me ask you, what are you?', 'Developer': { 'Text': 'I’m a developer.', 'Do': 'jump Developer' }, 'Player':{ 'Text': 'I’m a Player.', 'Do': 'jump Player', 'Condition': function () { return this.storage ('played'); // The 'Player' option will only be shown if this returns true. } } }}That way the 'Dialog' property is the one that will be shown with the choices, as you can see, just like with normal dialogs, you can specify what character is the one talking and take full advantage of other things like side images on it.[](.)Unclickable ChoicesEarlier we made a conditional choice that would only be shown if a condition was satisfied, but what if you want to show the player that there is a choice they could be making if certain conditions were satisfied.For this example, let's assume this is your storage variable:Copy'use strict'; // Persistent Storage Variable monogatari.storage ({ haveKey: false });As you can see, we added a haveKey variable inside the storage and set its default value to false. With that established, consider the following code.Copy{'Choice':{ 'Dialog': 'Do you unlock the locked door?', 'FirstOption':{ 'Text': 'Yes', 'Do': 'jump label1', 'Clickable': function(){ return this.storage().haveKey } }, 'SecondOption':{ 'Text': 'No', 'Do': 'jump label2' } }},The 'Clickable' property, as its name implies, decides whether or not a button can be clicked.Yes will be shown regardless of whether or not the variable we added is true, but it will only be clickable if it is. On that note, if the variable haveKey is false, then the Yes button will not be clickable. However, this might be confusing to your players, as they will see a choice, try to click it, and clicking it will do nothing. We can help out with this confusion with a little bit of visual design.[](.)Styling your ButtonsThe 'Class' property allows you to give a CSS class to your buttons for special styles. Suppose you had the following CSS in your main.css file:Copy.boldedText { font-weight: bold; } .italicText { font-style: italic; }The boldedText and italicText classes can then be applied to your buttons by setting their 'Class' properties in your script, like so:Copy{"Choice":{ "FirstOption":{ "Text": "Yes", "Do": "jump label1", "Class": "boldedText" //A css class to apply to the button. }, "SecondOption":{ "Text": "No", "Do": "jump label2", "Class": "italicText" } }},This way, the 'Yes' button will have its text bolded and the 'No' button will have its text italicized. You can also use CSS styling to do things like give buttons background images and make the text on them invisible, so you can have buttons with pictures!Earlier, we mentioned you could style unclickable buttons as well. This is easy to do as the buttons in Monogatari's choices are HTML buttons, and when a button is made unclickable, it is given the 'disabled' attribute. This means that we can style disabled buttons with CSS attribute styling. Consider the following example:Copy[disabled] { opacity: .5; }With this CSS styling, buttons containing the 'disabled' attribute will render semi-transparent, which will visually indicate to the player that they are different, so hopefully they won't be confused when they are unable to click it.You can also use classes and attributes at the same time:Copy[disabled].classname { text-decoration: line-through; }In this example, the text would have a line through it if and only if it is both disabled, and the button has the '.classname' class. If you wanted to make a specific class for a specific button, you could use the ::after pseudo selector and the 'content' property to append an explanation as to why you can't click the button too, or you could make two separate buttons where one displays if a condition is met, and the other displays if the condition is unmet.[](.)onChosen FunctionsThe 'onChosen' property allows us to run a function when the player clicks your button. For this example, we'll assume that our storage contains the following:Copy'use strict'; // Persistent Storage Variable monogatari.storage ({ enemyHealth: 100, playerAttack: 20 });In our script.js file, somewhere outside of our game's script, we could have a simple example function for attacking the enemy.Copyfunction attackEnemy(){ monogatari.storage().enemyHealth = monogatari.storage().enemyHealth - monogatari.storage().playerAttack };This function sets the enemyHealth value to be equal to its current value minus the playerAttack value.Then, in our script, we could have a choice for the player:Copy{"Choice":{ "Dialog": "The enemy wants to fight! What do you do?", "FirstOption":{ "Text": "Attack!", "onChosen": function(){attackEnemy()}, "Do": "You attack the enemy for {{playerAttack}} damage!" }, "SecondOption":{ "Text": "Defend", "Do": "You defend instead of attacking." } }}, "The enemy now has {{enemyHealth}} health points left."You could also have the function written out in the script itself for one-time functions that won't be called again.Copy{"Choice":{ "Dialog": "The enemy wants to fight! What do you do?", "FirstOption":{ "Text": "Attack!", "onChosen": function (){ monogatari.storage().enemyHealth = monogatari.storage().enemyHealth - monogatari.storage().playerAttack }, "Do": "You attack the enemy for {{playerAttack}} damage!" }, "SecondOption":{ "Text": "Defend", "Do": "You defend instead of attacking." } }}, "The enemy now has {{enemyHealth}} health points left.",This achieves the same result. You could also use these functions to invisibly keep track of which options the player clicked, or any other ideas you might have.[](.)onRevertThe 'onRevert' property should contain a function that will run when the player rewinds the game using the back button before a choice selection that runs an onChosen function. Ideally, this function should be used to undo whatever was done by the onChosen function, assuming the onChosen function did anything at all. For example, if you have an onChosen function that gives the player 5 gold, then the onRevert function should remove 5 gold, or else the player could just choose the option, rewind, choose it again, until they have as much gold as they want.Copy{"Choice":{ "Dialog": "Would you like some Gold?", "Yes": { "Text": "Yes please.", "Do": "p Yes please.", "onChosen": function() { monogatari.storage().playerGold += 5; }, "onRevert": function() { monogatari.storage().playerGold -= 5; } }, "No": { "Text": "No thank you.", "Do": "No thank you.", }, }}, "Okay! As a result of your decision, you now have {{playerGold}} Gold!",Monogatari remembers for the player which option they picked, so if they chose Yes in the above example, when they rewind, it will run the onRevert function for "yes", whereas if they chose no, it will not run anything special, as No does not contain an onRevert or onChosen function.The 'onRevert' property is required when you use the onChosen function, or else the player will not be able to use the "back" button to revert before the choice. If you don't care whether or not your player is allowed to rewind before a decision, then you don't need to use onRevert but that might make your player assume that there's a bug if some choices are reversible and others aren't.[](.)TimersThe Timer object takes two methods: "time" and "callback". Time is simply the amount of time in miliseconds you want the timer to last, and callback is a function to run after that timer has finished.Timers can be used to require the player to think quickly, and make something happen if they take too long to select a choice. In this example, assume that we have a css class "Invisible" that simply applies a display:none to the element.Copy{'Choice': { 'Dialog': 'y Have you already read some documentation?', 'Timer': { // Time in milliseconds time: 5000, // The function to run when the time is over callback: () => { //Click the "tookTooLong" button. monogatari.element().find('[data-choice="tookTooLong"]').get(0).click(); // Promise friendly! return Promise.resolve (); } }, 'Yes': { 'Text': 'Yes', 'Do': 'jump Yes' }, 'No': { 'Text': 'No', 'Do': 'jump No' }, 'tookTooLong':{ 'Text': 'TookTooLong', 'Do': 'jump tookTooLong', 'Class': 'invisible', } } },In this example, there are three buttons, but only two of them are actually visible to the player. After 5 seconds passes, the third button is clicked automatically.You can also do other things with timers, such as increment a counter, play a sound, or anything else you like. Please note, however, that there is no corresponding revert function for timers. Anything that happens in a timer will not be undone when the player clicks the back button. (Of course, you'd probably not want the player to be able to undo a mistake like allowing a timer to conclude, so if you use timers in this way, consider disabling the rollback function.)[PreviousBuilt-in Functions](../building-blocks/components/built-in-functions.md)[NextClear](clear.md)Last updated 4 years agoWas this helpful? # Clear | Monogatari Documentation [Script Actions](../script-actions.md)ClearClear the textbox[](.)DescriptionCopy'clear'The clear action will clear the textbox, removing any dialogs in it and leaving it completely blank.Action ID: ClearReversible: YesRequires User Interaction: No[PreviousChoices](choices.md)[NextConditionals](conditionals.md)Last updated 4 years agoWas this helpful? # Conditionals | Monogatari Documentation [Script Actions](../script-actions.md)ConditionalsControl the flow of your gameChances are, at some point you'll want to control the flow of your game depending on certain conditions, like jumping to certain labels or even show different dialogs depending on some condition.To do this, Monogatari has Conditional objects, let's take a look at this example:Copy{'Conditional': { 'Condition': function () { return this.storage ('evelyn_name') == 'Evelyn'; }, 'True': 'e Evelyn... That’s a lovely name! I love it!', 'False': 'e {{evelyn_name}}... Yeah, sounds good!' }},As you can see, a Conditional is made up of 3 properties, a 'Condition', the function that will be used to determine whether the condition is met or not. In these case, we are checking if the variable storage.evelyn_name is equal to 'Evelyn', if the condition returns true, the statement inside the 'True' property will be run, if it's false then the statement inside the 'False' property will be run.In this case, those statements are dialogs so that simply means that if the condition is true, the first dialog will be shown, if not then the second one will be shown.Of course dialogs is not the only thing we can do here, we can also use Conditionals to jump to certain labels like this:Copy{'Conditional': { 'Condition': function(){ return this.storage ('played'); }, 'True': 'jump Played', 'False': 'jump NewGame' }},[](.)Non-Boolean ConditionsThe 'Conditional' object also supports strings. In this example, assume your storage contains a variable called 'money' and that the money variable represents currency the player can find, and then an ending is chosen when they come to a place to purchase food.Copy{'Conditional': { 'Condition': function(){ if(this.storage().money < 1) { return "Too poor"; } else if (this.storage < 4) { return this.storage ('money') + ''; } else { return 'Rich'; } }, 'Too poor': 'jump goHomeEmptyHanded', '1': 'jump buyADollarItem', '2': 'jump buySomethingGood', '3': 'jump buyAComboMeal', 'Rich': 'jump buyTheWholeStore', }},You could write multiple conditional statements, that first check if you have 0 or not, then check if you have 1 or not, etc, but this way you only need to write one single conditional branch that functions a little like a switch statement! Please note: This only works with strings. You can't use Numbers in place of keys, which is why in the above example we add an empty string to our numerical value money so that instead of returning a number, it would return the number as a string.Also note: The Conditional object does not have an else of any kind, and Monogatari will throw an error if the Condition block returns something that is not found in the list of choices. For this reason, in the above example, we accounted for cases where money is less than 1, or where money is 4 or more. This way, if the person playing the game managed to acquire 5, 6, or even Seven whole dollars, the game will know what to do with that kind of money.[PreviousClear](clear.md)[NextDialogs](dialogs.md)Last updated 4 years agoWas this helpful? # Dialogs | Monogatari Documentation [Script Actions](../script-actions.md)DialogsThe text your players are going to read.[](.)DescriptionCopy'[character_id][:<expression_id>] <dialog_text>'Action ID: DialogReversible: YesRequires User Interaction: YesRelated Components:[Text Box](../components/text-box.md)[](.)Narrator DialogsAny statement in your script that does not match any other action will be displayed as a narrator dialog. Narrator dialogs do not show any name on the textbox.[](.)Character DialogsThe say statement is used... well, for a character to say something. The syntax is as follows:'[Character Identifier] [Text to Say]'Copy'y Hi! My name is Yui.'A fresh Monogatari game showing Yui speaking, saying "Hi! My name is Yui."It also accepts HTML, so you can show many things in a text like the Font Awesome icons.Copy'y The <span class="fa fa-arrow-left"></span> button is the back button, press it to return to a previous state of the game.'An example of HTML being used inline, in this case to show a back arrow icon.If no character identifier is given, it will be considered as a narration and no name will be shown.Copy'This would be a narrator.'The narrator speaking. [](.)Clear the TextThe 'clear' command sends an empty line of dialog to remove all text on the screen.Copy'clear'The clear command automatically runs the next line without requiring a click from the player. You may want to insert a 'wait' command immediately after it if you want the player to see a blank dialog box.[](.)Side ImagesIf you've defined the expressions property for your characters, adding a list of images to show, you can use them as side images with each dialog, to do so, you should use a format like this one:Copy'y:Smiling Hi! My name is Yui.'Yui saying "Hi! My name is Yui." with a side image of a picture of her smiling.This assumes you have a Side image called Smiling, which means with every dialog you can specify what side image to use. If you add the 'expression' property then that image will be shown with every dialog without the need of specifying it inside the statement.[](.)Centered DialogsThe 'centered' command is like a special character-id that goes at the beginning of dialog text to display a special floating box that hovers in the very center of the screen.Copy'centered This is an example of centered text.'The text box that displays 'centered' text is special. Rather than being inside of a <text-box> tag, it is instead inside of a <centered-dialog> tag.Copy<centered-dialog class="animated" data-component="centered-dialog"> <div data-content="wrapper">Here's some more centered text.</div> </centered-dialog>While 'centered' text is visible on screen, the main <text-box> is hidden with a CSS display:none; attribute.HTML can be used inside of 'centered' text the same as normal dialog, so you can use this to display a special image that removes the text box when it displays it, or something similar, if you want to get creativeNVL DialogsThe 'nvl' command is similar to the 'centered' command in that it is used at the beginning of a line of dialog to present a special display, similar to games like Fate/stay Night, or Radical Dreamers.Copy'nvl Here is an example of NVL text.', 'nvl Here is some more NVL text.', 'nvl One more line.'The 'nvl' text differs from normal Monogatari text in that clicking does not clear the current text off of the screen, and instead leaves it there, feeding consecutive NVL dialogue one at a time as the player clicks to progress.NVL text is displayed on screen inside of a <text-box> with a CSS class'nvl'. By default, this textbox is styled to fill the entire screen. Additionally, each line of NVL text is contained inside of a <div> with a data-spoken value for whichever character is speaking. Normally it's the narrator, but if you give a character the nvl attribute and set it to true then that character can be used as an NVL character, and their text can have special CSS rules if you want to get creative with that.An example of some characters speaking in NVL mode.Note that Expression side images are not supported in NVL mode.The NVL mode text box is scrollable, like other text boxes in Monogatari, should there be too much text to display at once. Additionally, you can use HTML in NVL mode the same as all of the other modes[NextEnd](end.md)Last updated 4 years agoWas this helpful? # End | Monogatari Documentation [Script Actions](../script-actions.md)EndEnd your game[](.)DescriptionCopy'end'The end action is how all of your scripts must finish. End will cause the game to finish and will return to the main menu. It will also reset all of the storage, actions and components to their initial state.Action ID: EndReversible: NoRequires User Interaction: No[PreviousDialogs](dialogs.md)[NextGallery](image-gallery.md)Last updated 4 years agoWas this helpful? # Hide Canvas | Monogatari Documentation [Script Actions](../script-actions.md)Hide Canvas[](.)DescriptionCopy'hide canvas <canvas_id> [with [animations] [classes]]'The hide canvas action allows you to stop and remove a canvas object.To show a canvas, check the [Show Canvas action](canvas.md).Action ID: Hide::CanvasReversible: YesRequires User Interaction: No[PreviousGallery](image-gallery.md)[NextHide Character](hide-character.md)Last updated 4 years agoWas this helpful? # Hide Character | Monogatari Documentation [Script Actions](../script-actions.md)Hide CharacterRemove a character's sprite from screen[](.)DescriptionCopy'hide character <character_id> [with [animations] [classes]]'The character action allows you to remove a character's sprite from the screen.To show a character, check the [Show Character action](characters.md).Action ID: Hide::CharacterReversible: YesRequires User Interaction: No[](.)ExamplesTo hide a character, you only need the 'hide' command. Syntax: 'hide [Character Identifier] with [Animation]'Copy'hide character e'You can also hide them with one of the exit animations available.Copy'hide character e with fadeOut'[PreviousHide Canvas](hide-canvas.md)[NextHide Image](hide-image.md)Last updated 6 years agoWas this helpful? # Hide Image | Monogatari Documentation [Script Actions](../script-actions.md)Hide Image[](.)DescriptionCopy'hide image <image_id> [with [animations] [classes]]'The character action allows you to remove an image from the screen.To show an image, check the [Show Image action](show-image.md).Action ID: Hide::ImageReversible: YesRequires User Interaction: No[](.)ExamplesTo hide this images, you'll use the hide command as well.Copy'hide image flower', // If flower is declared 'hide image flower.png' // If flower isn't declaredYou can also hide them with one of the exit animations available.Copy'hide image flower with fadeOut'[PreviousHide Character](hide-character.md)[NextHide Particles](hide-particles.md)Last updated 6 years agoWas this helpful? # Hide Particles | Monogatari Documentation [Script Actions](../script-actions.md)Hide ParticlesHide a particle system animation[](.)DescriptionCopy'hide particles'This hide action will stop the currently shown particle system. To learn more about the particle systems, read the [Show Particles documentation](particles.md).Action ID: Particles::HideReversible: YesRequires User Interaction: No[](.)ParametersNone[](.)ExamplesThe following sample will show the particle system named snow, previously declared on the [particles action configuration](particles.md) and then hide it.Script[](.)Particles Configuration[](.)CopyMonogatari.script ({ 'Start': [ 'show particles snow', 'Snow is now falling from the sky!', 'hide particles', 'There is no more snow :(' 'end' ] });CopyMonogatari.action('Particles').particles({ 'snow': { 'particles': { 'number': { 'value': 400, 'density': { 'enable': true, 'value_area': 800 } }, 'color': { 'value': '#fff' }, 'shape': { 'type': 'circle', 'stroke': { 'width': 0, 'color': '#000000' }, 'polygon': { 'nb_sides': 5 }, 'image': { 'src': 'img\/github.svg', 'width': 100, 'height': 100 } }, 'opacity': { 'value': 0.5, 'random': true, 'anim': { 'enable': false, 'speed': 1, 'opacity_min': 0.1, 'sync': false } }, 'size': { 'value': 10, 'random': true, 'anim': { 'enable': false, 'speed': 40, 'size_min': 0.1, 'sync': false } }, 'line_linked': { 'enable': false, 'distance': 500, 'color': '#ffffff', 'opacity': 0.4, 'width': 2 }, 'move': { 'enable': true, 'speed': 6, 'direction': 'bottom', 'random': false, 'straight': false, 'out_mode': 'out', 'bounce': false, 'attract': { 'enable': false, 'rotateX': 600, 'rotateY': 1200 } } }, 'interactivity': { 'detect_on': 'canvas', 'events': { 'onhover': { 'enable': true, 'mode': 'bubble' }, 'onclick': { 'enable': true, 'mode': 'repulse' }, 'resize': true }, 'modes': { 'grab': { 'distance': 400, 'line_linked': { 'opacity': 0.5 } }, 'bubble': { 'distance': 400, 'size': 4, 'duration': 0.3, 'opacity': 1, 'speed': 3 }, 'repulse': { 'distance': 200, 'duration': 0.4 }, 'push': { 'particles_nb': 4 }, 'remove': { 'particles_nb': 2 } } }, 'retina_detect': true } });[PreviousHide Image](hide-image.md)[NextHide Video](hide-video.md)Last updated 6 years agoWas this helpful? # Hide Video | Monogatari Documentation [Script Actions](../script-actions.md)Hide Video[](.)DescriptionCopy'hide video <video_id> [with [animations] [classes]]'The hide video action allows you to stop and remove a video currently playing on the screen.To show a video, check the [Show Video action](show-video.md).Action ID: Hide::VideoReversible: YesRequires User Interaction: No[PreviousHide Particles](hide-particles.md)[NextInput](input.md)Last updated 4 years agoWas this helpful? # Gallery | Monogatari Documentation [Script Actions](../script-actions.md)GalleryAn Image Gallery of images for your players to unlockNew to Monogatari v2 is an Image Gallery feature. With it you can create a gallery of images that your players can unlock by going through the game and seeing all the images, like you will find in many visual novels.[](.)Creating the GalleryBy default, the Image Gallery doesn't appear, since it would be pretty disappointing to your players to see a Gallery button on the main menu and have nothing inside of it. Monogatari's Default Main Menu with no GalleryThis is easily fixed by simply declaring some images! First you put some images in the Gallery folder. If you don't already have a Gallery folder because you previously updated the Engine Folder from an earlier version of Monogatari v2, then you may create a new one.The "gallery" folder is located inside of your "assets" folder in your game's directory.Once you have an image or images in the gallery folder, you'll declare them in script.js . Copymonogatari.assets ('gallery', { 'someImage': 'happy-shine.png' });Once that's done, you'll see that a new option has appeared on the Main Screen of your game.Monogatari's Default Main Menu with the Gallery option available.[](.)Unlocking Gallery ImagesBy default, on a fresh new game, all gallery images are locked, as these are rewards for the player for having completed parts of the game and met certain conditions.The default Gallery screen with one image, with that one image being locked.In order for the player to see this locked image, the player must reach a part of the script that enables it. To make this possible for your player, you'll include "gallery unlock" in your game script.Copy"y You've unlocked the gallery image", "gallery unlock someImage", //unlocks the image "y Great job!"Once the player crosses over this line in the script, it will unlock the gallery image!The default Gallery screen with one image, with that one image being Unlocked.Monogatari gallery unlocks are not dependent on any save files, so if you, for whatever reason, want to lock the player out of being able to see any of the gallery images for any reason, like as part of, for instance, a story event that master resets the game, which includes losing the gallery, you may re-lock images by writing "gallery lock someImage" in your script.Copy"y This will lock the image away so that you can't see it anymore.", "gallery lock someImage", "y Goodbye, Protagonist-sama. I'll never forget you."[PreviousEnd](end.md)[NextHide Canvas](hide-canvas.md)Last updated 5 years agoWas this helpful? # Input | Monogatari Documentation [Script Actions](../script-actions.md)InputDisplays an input dialog to the player[](.)DescriptionThere are many cases where we need input from the user, like when you want to know their name. The Input statement is represented in your Script as an Object and it allows you to define the text to show to your players, the type of input, the default value, validations, etc.Action ID: InputReversible: Yes, as long as a [Revert function](.) is implemented.Requires User Interaction: Yes[](.)PropertiesNameTypeOptionalDescriptionTextstringNoThe text to be displayed in the input dialog.Supports storage and translation interpolations.TypestringYesDefault is text.The kind of input you want to show.Possible Values:textnumberpasswordemailcolorselectradiocheckboxDefaultstring YesThe default value for the input.Supports storage and translation interpolations.OptionsArray<object>YesThe list of options to present to the player if the input has a type of select, radio or checkbox.Both the label and value properties in the options support storage and translation interpolations.ValidationfunctionYesThere are times when you want to validate the user input, to see if it's not empty for example, the Validation property should be a function returning a boolean, the validation process is up to what you want. This example checks to see if it is empty, but you might put in if trees to disable certain names, or something like that.SavefunctionYesThe save function specifies Monogatari will do with the input once it's validated, normally you would save it on storage, but you can do whatever you want with it, the save property is then a function that receives the input, what you do with it is also up to you.WarningstringYesThe message that will be shown in case the input fails the validation, something useful for the player to know what you expect from them.Supports storage and translation interpolations.actionStringstringYesDefault is 'OK'.The Key of a translation string that contains the text to show in the submit button of the element.ClassstringYesA space separated list of CSS class names to apply to the input element.TimerobjectYesA timer configuration object.AttributesobjectYesThe list of attributes to add to the input HTML element created.[](.)Input TextThe Text property specifies the text that will appear to the player in the input dialog. This is the only property that is required.[](.)Input ValidationThe Validation property expects a function. When the player presses the submit button in your input dialog, the value they provided will be passed to your Validation function. In there, you can perform any validation you need depending on your game's context and what was the purpose of your input. The function should return a boolean value (true or false) to indicate if the value was valid or not. If your validation has to perform some asynchronous operations such as consulting with an external server, then you can also return a Promise that resolves to the boolean value.If the validation fails, the [input's warning](.) will be shown to the player.[](.)Synchronous ValidationLet's take a look at the following input, by default an input that doesn't specifies a Type property has the text type so it expects the player to type something into a text field:Copy{'Input': { 'Text': 'What is your name?', 'Validation': (input) => { return input.trim ().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' }},As you can see, we have provided a simple validation function there:Copy(input) => { return input.trim ().length > 0; }This function is receiving the player's input and then, checks if that input is valid by checking if it has a length greater than 0 or, in simpler words, whether it received a non-empty input which could happen if the player didn't type anything and just pressed the submit button.By returning the result of that validation, it will return true when the input is not empty and false when the input is empty. Our code doesn't deal with any asynchronous function so we can just return a boolean value this time.[](.)Asynchronous ValidationThere may come a time when for some reason, you want some complex validation to be perform on an input's value. For example, when you have to check with a server to determine whether it's valid or not. Let's say we have a server setup that given a name, it will returns us an object like this one based on some requirements we have, for example whether it's a full name (name and last name) or not.Copy{ valid: true } // When the name is valid { valid: false } // When the name is not validNow, checking with our server is not an operation that we can know how much it'll take to finish so, we want to wait for it to return us the result and only then, we can determine in our game if it was actually valid or not. Here's a sample code on how we could achieve that:Copy{'Input': { 'Text': 'What is your name?', 'Validation': (input) => { if (input.trim ().length === 0) { return false; } return fetch (`https://myserver.com/api/validate?name=${input}`) .then ((response) => { return response.json (); }).then (({ valid }) => { return Promise.resolve (valid); }); }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' }},Let's pay closer attention to our validation function this time:Copy(input) => { if (input.trim ().length === 0) { return false; } return fetch (`https://myserver.com/api/validate?name=${input}`) .then ((response) => { return response.json (); }).then (({ valid }) => { return Promise.resolve (valid); }); }As you can see, we first did a simple check to determine if the input wasn't empty (length 0) because if it was, we can tell right away it wasn't a valid name and there's no need to check with our server so we make an early return if that's the case.If the input was not empty, we perform the call to our server which should return us the object we discussed before letting us know if the name was valid or not. Once we receive that object, we can finally tell our validation function to return that Promise.resolve (valid) value which in the end, contains the valid property returned to us by the server.[](.)Input WarningThe Warning property expects a string value that will be shown to the player if the validation function determines the input was invalid. Keeping the name example from before, this input will validate the value provided by checking if it wasn't empty: Copy{'Input': { 'Text': 'What is your name?', 'Validation': (input) => { return input.trim ().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' }}If the player provided an empty value (thus causing the validation function to deem it invalid), the text in the Warning property will be shown to the player so they know something was wrong with the info they provided and try again:[](.)Input SaveOnce the Validation function is run and it determines the input was valid, the value will be passed along to the Save function. Inputs were created mostly to get information out of the player so the Save function is where you would, as it name says, save that information or perform any other action you need to do now that you know what the player provided is valid.Normally this operation would involve some variable in your game's storage:Copy{'Input': { 'Text': 'What is your name?', 'Validation': (input) => { if (input.trim ().length === 0) { return false; } return fetch (`https://myserver.com/api/validate?name=${input}`) .then ((response) => { return response.json (); }).then (({ valid }) => { return Promise.resolve (valid); }); }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' }},If we take a closer look to the Save function, we can see it's saving the player's input in the player.name property on the storage:Copy(input) => { monogatari.storage ({ player: { name: input }}); }[](.)Changing the game flowSomething really important to know about this function is that the value it returns will affect the flow of the game. If the value returns void (nothing like the one above) or true, the game will continue to the next statement on the script as soon as the save operation finishes. If it returns false, then the input dialog will disappear but the player will have to click once more for the game to carry on.Just like the [Validation function](.), this one is also able to perform asynchronous operations and the flow will be affected by what the Promise it returns resolves to.[](.)Input RevertYou probably want players to be able to go back and enter a different value or simply go back to any point of the script even if that means passing through a previously answered input statement.In order to do so, you need to provide a Revert function. This function should be implemented by you and should do everything that's needed to revert what you did in your Save function. Let's take the following input as an example:Copy{'Input': { 'Text': 'What is your name?', 'Validation': (input) => { return input.trim ().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' }}In the Save function, we're saving the player's name to the storage:Copy'Save': (input) => { monogatari.storage ({ player: { name: input }}); }Therefore, what we need to do in our Revert function is return that value in the storage to its original or default value:Copy'Revert': () => { monogatari.storage ({ player: { name: '' }}); }That way, whenever the player rolls back an input, the state of your game gets reset to what it was before. If you do not provide a Revert function however, players won't be able to go back when reaching an input statement, you can of course use this fact to your advantage if that's the behaviour you want, otherwise we always recommend implementing your Revertfunction so players can have the best experience possible.[](.)Input StylingWhile you can always modify the overall style of your input dialogs by adding your own css code, some times you might want to have different styles for specific inputs in your game. As with most of the things in Monogatari, doing this involves using CSS classes.In your input object, you can provide a Class property. This property expects a string with space separated class names that will be added to your input dialog when it gets shown. First, let's remind this is what a un-styled input dialog looks like:Now, if we wanted to style it by adding some CSS classes that we have in our main.css file such as these ones:Copy.myInput .modal__content { background: #222; color: #fff; width: 100%; } .someClass { font-size: 2em; } .otherClass { background: rgba(0,184,212, 0.5); }Then, all we'd have to do is add the Class property to our input and list those class names right there:Copy{'Input': { 'Class': 'myInput someClass otherClass', 'Text': 'What is your name?', 'Validation': (input) => { return input.trim ().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' }},This will result in a stylized input dialog such as this one:[](.)Input TimerCopy{'Input': { 'Text': 'What is your name?', 'Validation': (input) => { return input.trim ().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' 'Timer': { // Time in milliseconds time: 5000, // The function to run when the time is over callback: () => { // Get all choices being shown and that are not disabled const input = monogatari.element ().find ('text-input').get (0); input.content ('field').value ('My Name'); // // Pick one of those options randomly const submit = input.element ().find ('button').get (0); // // Fake a click on it submit.click (); // Promise friendly! return Promise.resolve (); } }, }}, [](.)Input Types[](.)TextA text input allows the player to enter any kind of text, that includes numbers, symbols and even emojis. If the player doesn't enter any value, the Validation and Save functions will receive an empty string ('') as the player's input.[](.)Sample CodeCopy{'Input': { 'Text': 'What is your name?', 'Validation': (input) => { return input.trim ().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' }}[](.)NumberA number input allows the player to enter any kind of numeric value. Note however that the input received in the Validation and Save functions will be a string so it has to be parsed to a number. If the player doesn't enter any value, the Validation and Save functions will receive an empty string ('') as the player's input.[](.)Sample CodeCopy{'Input': { 'Text': 'Enter your age', 'Type': 'number', 'Validation': (input) => { // Check if the input wasn't empty if (input.trim ().length === 0) { return false; } // Transform the input string to an integer number const age = parseInt (input); return age > 18; }, 'Save': (input) => { monogatari.storage ({ player: { age: parseInt (input) }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must be at least 18 years old to continue.' }}[](.)PasswordA password will allow the player to enter any text just like the text input but whatever they type will be obscured with a symbol such as the asterisk (*) or a dot (•). If the player doesn't enter any value, the Validation and Save functions will receive an empty string ('') as the player's input.[](.)Sample CodeCopy{'Input': { 'Text': 'Enter the secret code', 'Type': 'password', 'Validation': (input) => { // Check what the player entered against our code return input.trim () === 'SecretC0de'; }, 'Save': (input) => { // Do something here, might not be necessary to // save anything for password inputs. }, 'Revert': () => { // Revert what we did in the save function }, 'Warning': 'That\'s not the right code.' }}[](.)SelectA select input allows you to provide different possible values for the player to choose from, they however will only be able to choose one of them. If the player doesn't enter any value, the Validation and Save functions will receive an empty string ('') as the player's input.[](.)Sample CodeCopy{'Input': { 'Text': 'What\'s your favorite color?', 'Type': 'select', 'Options': [ { label: 'Pink', value: 'pink', }, { label: 'Red', value: 'red', }, { label: 'Orange', value: 'orange', }, { label: 'Blue', value: 'blue', }, { label: 'Yellow', value: 'yellow', }, { label: 'Green', value: 'green', } ], 'Validation': (input) => { // We'll receive the 'value' property of the option // the player selected. return input.trim ().length > 0; }, 'Save': (input) => { // Save the favorite color in the storage monogatari.storage ({ player: { favorite_color: input }}); }, 'Revert': () => { // Reset the favorite color property monogatari.storage ({ player: { favorite_color: '' }}); }, 'Warning': 'You must select a color.' }}[](.)RadioA radio input, just like the select input allows you to provide different possible values for the player to choose from and only allows them to choose one. If the player doesn't enter any value, the Validation and Save functions will receive an empty string ('') as the player's input.Copy{'Input': { 'Text': 'What\'s your favorite color?', 'Type': 'radio', 'Options': [ { label: 'Pink', value: 'pink', }, { label: 'Red', value: 'red', }, { label: 'Orange', value: 'orange', }, { label: 'Blue', value: 'blue', }, { label: 'Yellow', value: 'yellow', }, { label: 'Green', value: 'green', } ], 'Validation': (input) => { // We'll receive the 'value' property of the option // the player selected. return input.trim ().length > 0; }, 'Save': (input) => { // Save the favorite color in the storage monogatari.storage ({ player: { favorite_color: input }}); }, 'Revert': () => { // Reset the favorite color property monogatari.storage ({ player: { favorite_color: '' }}); }, 'Warning': 'You must select a color.' }}[](.)CheckboxA checkbox also allows you to provide different possible values for the player to choose from but, contrary to the radio or select inputs, players will be able to choose more than one option. Since this input allows multiple choices to be chosen, the input argument received by the Validation and Save functions will be an array of strings instead of a string, for example:Copy['pink', 'yellow']If the player doesn't enter any value, the Validation and Save functions will receive an empty array ([]) as the player's input.[](.)Sample CodeCopy{'Input': { 'Text': 'What are your favorite colors?', 'Type': 'checkbox', 'Options': [ { label: 'Pink', value: 'pink', }, { label: 'Red', value: 'red', }, { label: 'Orange', value: 'orange', }, { label: 'Blue', value: 'blue', }, { label: 'Yellow', value: 'yellow', }, { label: 'Green', value: 'green', } ], 'Validation': (input) => { // In this case, input is not a single string but an array // of strings. return input.length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { favorite_colors: input }}); }, 'Warning': 'You must select at least one color!' }}[](.)Input Default ValueYou can also provide a default value for the input by providing a Default property. For text-based inputs such as text and password, whatever you pass as the value of this property will be what's written by default on the input field.Copy{'Input': { 'Text': 'What is your name?', 'Default': 'Jane Doe', 'Validation': (input) => { return input.trim ().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!' }}For option-based inputs such as select, radio and checkbox, your Default property will have to match one of the value properties of the options you provided:Copy{'Input': { 'Text': 'What are your favorite colors?', 'Type': 'checkbox', 'Default': 'orange', 'Options': [ { label: 'Pink', value: 'pink', }, { label: 'Red', value: 'red', }, { label: 'Orange', value: 'orange', }, { label: 'Blue', value: 'blue', }, { label: 'Yellow', value: 'yellow', }, { label: 'Green', value: 'green', } ], 'Validation': (input) => { // In this case, input is not a single string but an array // of strings. return input.length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { favorite_colors: input }}); }, 'Warning': 'You must select at least one color!' }}[](.)Input Action StringBy default, the submit button in an input has the text 'OK' in it, however this may not match the purpose or context of your input. This text can be changed by providing a translation key as the value for the actionString property. First, you'll need to register your new string, if your game is a multilanguage one, you'll have to register it for all of the languages your game is available on. Otherwise, you will only need to register in the default language of your game.Copymonogatari.translation ('English', { 'EnterCode': 'Enter Code' }); monogatari.translation ('Español', { 'EnterCode': 'Ingresar Código' });Once you've registered your translation string, you can pass it to the input:Copy{'Input': { 'Text': 'Enter the secret code', 'Type': 'password', 'actionString': 'EnterCode', 'Validation': (input) => { // Check what the player entered against our code return input.trim () === 'SecretC0de'; }, 'Save': (input) => { // Do something here, might not be necessary to // save anything for password inputs. }, 'Revert': () => { // Revert what we did in the save function }, 'Warning': 'That\'s not the right code.' }}Your input will then show the text of your string rather than the default one:[](.)Input AttributesHTML inputs allow you to set some very useful attributes for them, such as the max and min values they allow. You'll be able to set these attributes using the Attributes property.Copy{'Input': { 'Text': 'What is your name?', 'Validation': (input) => { return input.trim ().length > 0; }, 'Save': (input) => { monogatari.storage ({ player: { name: input }}); }, 'Revert': () => { monogatari.storage ({ player: { name: '' }}); }, 'Warning': 'You must enter a name!', 'Attributes': { 'placeholder': 'Enter your name', 'minlength': 3, 'maxlength': 20 } }}[](.)Available AttributesThe following table shows the possible attributes you can set. Some may only be available for a certain type of input. If you have doubts on the usage of these attributes, you should refer to the documentation for the HTML input element and its types.AttributeDescriptionmaxlengthThe maximum number of characters the input should accept. For numeric inputs you should use the max attribute instead.minlengthThe minimum number of characters long the input can be and still be considered valid. For numeric inputs you should use the min attribute instead.placeholderAn exemplar value to display in the input field whenever it is empty.patternA regular expression the input's contents must match in order to be valid.maxThe maximum value to accept for this input.minThe minimum value to accept for this input.stepA stepping interval to use when using up and down arrows to adjust the value, as well as for validation. The amount a numeric value will increment or decrement by.[PreviousHide Video](hide-video.md)[NextFunctions](javascript.md)Last updated 4 years agoWas this helpful? # Functions | Monogatari Documentation [Script Actions](../script-actions.md)FunctionsRun JavaScript functions in your script[](.)DescriptionEven though you can put more JavaScript code in the main.js file and do a lot of things there, sometimes we want to do something in some part of our game. This is why you can also put some JavaScript inside your game's script. [](.)Reversible FunctionsSo far, we've been using normal JavaScript functions to achieve more functionality but one problem was that this functions were not reversible, meaning that the players were not able to go back over a function.Let's see what we mean by that, let's say you are building some kind of RPG elements in your game and thus your player has stats. Normally, those stats would be declared inside your storage variable like this:Copy'use strict'; // Persistent Storage Variable Monogatari.storage ({ player: { intelligence: 0, ability: 0, strength: 0 } });Now, inside your script you would probably make some modifications of those stats depending on what the user does or how the story goes.Copylet script = { // The game starts here. 'Start': [ 'h Currently you have {{player.intelligence}} points of Intelligence but you seem far more intelligent, how about we add five points?', function () { this.storage ('player', { intelligence: this.storage ('player') + 5 }); return true; }, 'h There you have it, you now have {{player.intelligence}} points of Intelligence', 'end' ] }So, if we played this game, the first text would appear, then after we click for the next one, the function would be run adding 5 points to our intelligence stat and immediately would show the next text. If we wanted to go back to the first text it wouldn't be possible. This is mainly due to Monogatari not knowing what you are doing exactly in your function, if it were to allow you to go back, we could be getting infinite points just by going back and playing it again because there is no way to know what changed.To solve this problem and allow users to go back, Monoagatari v1.4 introduced reversible 'Function' objects, as with all the special script objects, these are defined in a JSON format, let's take a look at how the same situation as above would look like:Copylet script = { // The game starts here. 'Start': [ 'h Currently you have {{player.intelligence}} points of Intelligence but you seem far more intelligent, how about we add five points?', {'Function':{ 'Apply': function () { this.storage ('player', { intelligence: this.storage ('player') + 5 }); return true; }, 'Reverse': function () { this.storage ('player', { intelligence: this.storage ('player') - 5 }); } }}, 'h There you have it, you now have {{player.intelligence}} points of Intelligence', 'end' ] }As you can see, we replaced the function with a 'Function' object which has 2 properties, an 'Apply' function which will run when going over the game and the 'Reverse' function which will be run when going back. This now solves the previous problem we had since we are using 'Apply' to add the 5 points and 'Reverse' to subtract them in case the player went back and thus makes possible for players to go back even when a function was run. Just as with common functions, you can use Promises and also control the flow of the game by returning true or false in the 'Apply' function.[](.)Normal FunctionsUsing normal JavaScript functions is not recommended because they can't be reverted and thus, players can't roll back their game on parts where functions are present which gives the impression that it's an error on the game rather than it being planned.To do it, you can actually using JavaScript functions. Yes, just like you would on any other JS file, you can use function syntax inside your script.To control the flow of your game, you can make the function return either true (Will immediately execute the next statement.) or false (Will wait until the user clicks again.) if it does not return one of those two, it will wait by default.Example:Copy'Hi there!', function () { alert('This is pretty useful!'); return true; // Will make the engine execute the next statement when the function finishes. }, 'Isn’t it?'While that may be ok for a few functions, as you can see after a while your code starts to become more complicated, a way to simplify things is declaring the functions before your script and then just using the name to call it in the script like this:Copyfunction sendAlert () { alert('This is pretty useful!'); return true; } Monogatari.script ({ // The game starts here. 'Start': [ 'Hello, after this the function will be run.', sendAlert, // You can use just the name of the function! 'Well, that looks a lot better!' ] });Using that technique will make your game a lot more easy to debug and updateAsync FunctionsWhile the advanced way adds a lot of more possibilities to your game, it still doesn't solve the fact that you may need to realize some async tasks such as a request or other activities. Since v1.3.2, using JavaScript Promises is possible.Copyfunction asyncFunction () { return new Promise ((resolve, reject) => { // All your code should be here setTimeout (function (){ // You need to resolve the promise once your task is done resolve ('Success!'); }, 2150); }).then ((successMessage) => { alert (successMessage); return true; }); } Monogatari.script ({ // The game starts here. 'Start': [ 'Hello, after this the function will be run.', asyncFunction, 'Well, that looks a lot better!' ] });Just as with the common functions, if you return a true value from your promise, the next statement will be executed as soon as it's done and will wait if you return anything else. The game will also block on the meantime so the player won't be able to continue until the Promise is resolved.[](.)[PreviousInput](input.md)[NextJump](jump.md)Last updated 4 years agoWas this helpful? # Jump | Monogatari Documentation [Script Actions](../script-actions.md)Jump[](.)DescriptionCopy'jump <label_id>'Action ID: JumpReversible: YesRequires User Interaction: No[PreviousFunctions](javascript.md)[NextNext](next.md)Last updated 4 years agoWas this helpful? # Show Message | Monogatari Documentation [Script Actions](../script-actions.md)Show MessageShow a message[](.)DescriptionCopy'show message <message_id>'The message action let's you show a message to the player. A message is a nice way of showing something that requires more text such as an email or instructions. You could also use it as a mailbox or something of the sorts in your game.Each message has a close button so the user is able to close it when he's finished reading it.Action ID: MessageReversible: YesRequires User Interaction: Yes, the user needs to close the message before continuing.[](.)ParametersNameTypeDescriptionmessage_idstringThe name of the message you want to show. These must be declared beforehand using this action configuration functions.[](.)ConfigurationTo show a message, you must first declare it with all of it's characteristics. To do so, the message action has a configuration function where you can define your id or name for each message and their respective information.CopyMonogatari.action ('Message').messages ({ '<message_id>': { title: '', subtitle: '', body: '' } });[](.)PropertiesNameTypeDescriptiontitlestringThe title of the messagesubtitlestringA subtitle for the messagebodystringThe body or contents of the message[](.)Examples[](.)Text MessageThe following script will show a simple text message:Script[](.)Message Configuration[](.)CopyMonogatari.script ({ 'Start': [ 'show message SampleWriting', 'end' ] });CopyMonogatari.action ('Message').messages ({ 'SampleWriting':{ title: 'Some sample writing', subtitle: 'From Evelyn', body:'Just look how easy it is!' } });[](.)HTML MessageYou can also include HTML on your message, the following script and configuration will show a message with HTML on it.Script[](.)Message Configuration[](.)CopyMonogatari.script ({ 'Start': [ 'show message SampleHTML', 'end' ] });CopyMonogatari.action ('Message').messages ({ 'SampleHTML':{ title: 'Some sample writing', subtitle: 'From Evelyn', body: ` <p>This message is being formatted with HTML</p> <img src="assets/images/message.png"> ` } });[PreviousShow Image](show-image.md)[NextShow Notification](notifications.md)Last updated 6 years agoWas this helpful? # Next | Monogatari Documentation [Script Actions](../script-actions.md)NextContinue to the next statement on the script[](.)DescriptionCopy'next'The next action will simply continue to the next statement on the script without doing anything else. This action is meant to be used in the Do property of [Choices](choices.md) to simply carry on with the script or, as fillers to fill up a space left by a deleted statement in an already-released game.Action ID: NextReversible: YesRequires User Interaction: No[PreviousJump](jump.md)[NextPlaceholder](placeholder.md)Last updated 4 years agoWas this helpful? # Show Notification | Monogatari Documentation [Script Actions](../script-actions.md)Show NotificationShow a notification to the player[](.)DescriptionCopy'show notification <notification_id> [time]'The notification action let's you show a notification to the player. Notifications can be useful for letting them know about a certain event, or even achievement unlock.Action ID: NotificationReversible: YesRequires User Interaction: If no time was provided, the player will need to dismiss the notification but the game will not be interrupted.[](.)ParametersNameTypeDescriptionnotification_idstringThe name of the notification you want to show. These must be declared beforehand using this action configuration functions.timenumberOptional. The time in milliseconds after which the notification will be automatically dismissed.[](.)ConfigurationTo show a notification, you must first declare it with all of it's characteristics. To do so, the notification action has a configuration function where you can define your id or name for each notification and their respective information.CopyMonogatari.action ('Notification').notifications ({ '<notification_id>': { title: '', body: '', icon: '' } });[](.)PropertiesNameTypeDescriptiontitlestringThe title for the notificationbodystringThe body of the notificationiconstringA path to an image that will be shown as the icon for the notification[](.)Examples[](.)Simple NotificationThe following script will show a notification that the player will have to manually dismiss.Script[](.)Notification Configuration[](.)CopyMonogatari.script ({ 'Start': [ 'show notification SampleNotification', 'end' ] });CopyMonogatari.action ('Notification').notifications ({ 'SampleNotification':{ title: 'Hey!', body: 'This is a notification', icon: 'assets/images/notification.png' }, });[](.)Timed NotificationThe following script will make the notification go away automatically after 5 seconds have elapsed. Remember this action receives the time in milliseconds so we'll use 5000 to dismiss it after the 5 secondsScript[](.)Notification Configuration[](.)CopyMonogatari.script ({ 'Start': [ 'show notification SampleNotification 5000', 'end' ] });CopyMonogatari.action ('Notification').notifications ({ 'SampleNotification':{ title: 'Hey!', body: 'This is a notification', icon: 'assets/images/notification.png' }, });[PreviousShow Message](message.md)[NextShow Particles](particles.md)Last updated 6 years agoWas this helpful? # Show Particles | Monogatari Documentation [Script Actions](../script-actions.md)Show ParticlesShow a particle system animation[](.)DescriptionCopy'show particles <particles_id>'The particles action let's you show amazing animations using particle systems. A particle system is useful when you want to add dynamic elements such as snow, stars, rain etc. and can be an awesome addition for your game!To hide the particle systems, read the [Hide Particles documentation](hide-particles.md).Action ID: ParticlesReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionparticles_idstringThe name of the particles system you want to show. These must be declared beforehand using this action configuration functions.[](.)ConfigurationTo use a particle system, you must first declare it with all of it's characteristics. To do so, the particles action has a configuration function where you can define your id for each particle system and their configuration on a JSON Object.CopyMonogatari.action ('Particles').particles ({ '<particles_id>': ParticlesJSONObject });This action uses the Particles.js library, reading their documentation you can find more information about it and also more information on how to create a custom particle system.[](.)SamplesHere are some examples that may be useful and also provide a good start point:Snow[](.)Night Sky[](.)Fireflies[](.)Fire Sparks[](.)CopyMonogatari.action('Particles').particles({ 'snow': { 'particles': { 'number': { 'value': 400, 'density': { 'enable': true, 'value_area': 800 } }, 'color': { 'value': '#fff' }, 'shape': { 'type': 'circle', 'stroke': { 'width': 0, 'color': '#000000' }, 'polygon': { 'nb_sides': 5 }, 'image': { 'src': 'img\/github.svg', 'width': 100, 'height': 100 } }, 'opacity': { 'value': 0.5, 'random': true, 'anim': { 'enable': false, 'speed': 1, 'opacity_min': 0.1, 'sync': false } }, 'size': { 'value': 10, 'random': true, 'anim': { 'enable': false, 'speed': 40, 'size_min': 0.1, 'sync': false } }, 'line_linked': { 'enable': false, 'distance': 500, 'color': '#ffffff', 'opacity': 0.4, 'width': 2 }, 'move': { 'enable': true, 'speed': 6, 'direction': 'bottom', 'random': false, 'straight': false, 'out_mode': 'out', 'bounce': false, 'attract': { 'enable': false, 'rotateX': 600, 'rotateY': 1200 } } }, 'interactivity': { 'detect_on': 'canvas', 'events': { 'onhover': { 'enable': true, 'mode': 'bubble' }, 'onclick': { 'enable': true, 'mode': 'repulse' }, 'resize': true }, 'modes': { 'grab': { 'distance': 400, 'line_linked': { 'opacity': 0.5 } }, 'bubble': { 'distance': 400, 'size': 4, 'duration': 0.3, 'opacity': 1, 'speed': 3 }, 'repulse': { 'distance': 200, 'duration': 0.4 }, 'push': { 'particles_nb': 4 }, 'remove': { 'particles_nb': 2 } } }, 'retina_detect': true } });CopyMonogatari.action('Particles').particles({ 'stars': { 'particles': { 'number': { 'value': 355, 'density': { 'enable': true, 'value_area': 789.1476416322727 } }, 'color': { 'value': '#ffffff' }, 'shape': { 'type': 'circle', 'stroke': { 'width': 0, 'color': '#000000' }, 'polygon': { 'nb_sides': 5 }, 'image': { 'src': '', 'width': 100, 'height': 100 } }, 'opacity': { 'value': 0.48927153781200905, 'random': false, 'anim': { 'enable': true, 'speed': 0.2, 'opacity_min': 0, 'sync': false } }, 'size': { 'value': 2, 'random': true, 'anim': { 'enable': true, 'speed': 2, 'size_min': 0, 'sync': false } }, 'line_linked': { 'enable': false, 'distance': 150, 'color': '#ffffff', 'opacity': 0.4, 'width': 1 }, 'move': { 'enable': true, 'speed': 0.2, 'direction': 'none', 'random': true, 'straight': false, 'out_mode': 'out', 'bounce': false, 'attract': { 'enable': false, 'rotateX': 600, 'rotateY': 1200 } } }, 'interactivity': { 'detect_on': 'canvas', 'events': { 'onhover': { 'enable': true, 'mode': 'bubble' }, 'onclick': { 'enable': true, 'mode': 'push' }, 'resize': true }, 'modes': { 'grab': { 'distance': 400, 'line_linked': { 'opacity': 1 } }, 'bubble': { 'distance': 83.91608391608392, 'size': 1, 'duration': 3, 'opacity': 1, 'speed': 3 }, 'repulse': { 'distance': 200, 'duration': 0.4 }, 'push': { 'particles_nb': 4 }, 'remove': { 'particles_nb': 2 } } }, 'retina_detect': true } });CopyMonogatari.action('Particles').particles({ 'fireflies': { 'particles': { 'number': { 'value': 202, 'density': { 'enable': true, 'value_area': 800 } }, 'color': { 'value': '#0bd318' }, 'shape': { 'type': 'circle', 'stroke': { 'width': 0, 'color': '#000000' }, 'polygon': { 'nb_sides': 5 }, 'image': { 'src': 'img/github.svg', 'width': 100, 'height': 100 } }, 'opacity': { 'value': 0.9299789953020032, 'random': true, 'anim': { 'enable': true, 'speed': 1, 'opacity_min': 0, 'sync': false } }, 'size': { 'value': 3, 'random': true, 'anim': { 'enable': false, 'speed': 4, 'size_min': 0.3, 'sync': false } }, 'line_linked': { 'enable': false, 'distance': 150, 'color': '#ffffff', 'opacity': 0.4, 'width': 1 }, 'move': { 'enable': true, 'speed': 3.017060304327615, 'direction': 'none', 'random': true, 'straight': false, 'out_mode': 'out', 'bounce': false, 'attract': { 'enable': false, 'rotateX': 1042.21783956259, 'rotateY': 600 } } }, 'interactivity': { 'detect_on': 'canvas', 'events': { 'onhover': { 'enable': true, 'mode': 'bubble' }, 'onclick': { 'enable': true, 'mode': 'repulse' }, 'resize': true }, 'modes': { 'grab': { 'distance': 400, 'line_linked': { 'opacity': 1 } }, 'bubble': { 'distance': 250, 'size': 0, 'duration': 2, 'opacity': 0, 'speed': 3 }, 'repulse': { 'distance': 400, 'duration': 0.4 }, 'push': { 'particles_nb': 4 }, 'remove': { 'particles_nb': 2 } } }, 'retina_detect': true } });CopyMonogatari.action('Particles').particles({ 'fireSparks': { 'particles': { 'number': { 'value': 400, 'density': { 'enable': true, 'value_area': 3000 } }, 'color': { 'value': '#fc0000' }, 'shape': { 'type': 'circle', 'stroke': { 'width': 0, 'color': '#000000' }, 'polygon': { 'nb_sides': 3 }, 'image': { 'src': 'img/github.svg', 'width': 100, 'height': 100 } }, 'opacity': { 'value': 0.5, 'random': true, 'anim': { 'enable': false, 'speed': 1, 'opacity_min': 0.1, 'sync': false } }, 'size': { 'value': 2, 'random': true, 'anim': { 'enable': true, 'speed': 5, 'size_min': 0, 'sync': false } }, 'line_linked': { 'enable': false, 'distance': 500, 'color': '#ffffff', 'opacity': 0.4, 'width': 2 }, 'move': { 'enable': true, 'speed': 7.8914764163227265, 'direction': 'top', 'random': true, 'straight': false, 'out_mode': 'out', 'bounce': false, 'attract': { 'enable': false, 'rotateX': 600, 'rotateY': 1200 } } }, 'interactivity': { 'detect_on': 'canvas', 'events': { 'onhover': { 'enable': false, 'mode': 'bubble' }, 'onclick': { 'enable': false, 'mode': 'repulse' }, 'resize': true }, 'modes': { 'grab': { 'distance': 400, 'line_linked': { 'opacity': 0.5 } }, 'bubble': { 'distance': 400, 'size': 4, 'duration': 0.3, 'opacity': 1, 'speed': 3 }, 'repulse': { 'distance': 200, 'duration': 0.4 }, 'push': { 'particles_nb': 4 }, 'remove': { 'particles_nb': 2 } } }, 'retina_detect': true } });You can also play with the Particles.js interactive tool to create your own particle systems.[](.)ExamplesFor this examples, we'll assume you've used one of the sample configurations provided. Showing a particle system is as simple as:CopyMonogatari.script ({ 'Start': [ 'show particles snow', 'end' ] });[PreviousShow Notification](notifications.md)[NextShow Scene](show-scene.md)Last updated 6 years agoWas this helpful? # Placeholder | Monogatari Documentation [Script Actions](../script-actions.md)Placeholder[](.)DescriptionCopy'$ <placeholder_name>'A placeholder allows you to save some action to use within your game by placing a placeholder on your script wherever you want to call it. This is particularly useful when you have an action that is repeated multiple times in your game or when loading your script from an external source.Action ID: PlaceholderReversible: Depends on the action providedRequires User Interaction: Depends on the action provided[](.)ParametersNameTypeDescriptionplaceholder_namestringThe name with which you saved the action you want to call.[](.)Action DeclarationsTo declare an action placeholder, you need to provide a name for it and a value. The value can be any valid monogatari action.Copymonogatari.$ ('menu', {"Choice":{ "Text": "Let's see, what do you want to know about?", "Animations":{ "Text": "Animations", "Do": "jump Animations" }, "Media":{ "Text": "Multimedia", "Do": "jump Media" }, "Scripting":{ "Text": "Scripting", "Do": "jump Script" }, "Playing":{ "Text": "Playing", "Do": "jump Playing" }, "Nothing": { "Text": "Nothing", "Do": "jump Nothing", "Condition": function () { return storage.playing && storage.media && storage.scripting && storage.animations; } } }});[](.)Examples[](.)Using a Choice in multiple placesThe following will declare the placeholder shown above, featuring a choice action. The recommended place to declare all your placeholders is your main.js file. Copymonogatari.$ ('menu', {"Choice":{ "Text": "Let's see, what do you want to know about?", "Animations":{ "Text": "Animations", "Do": "jump Animations" }, "Media":{ "Text": "Multimedia", "Do": "jump Media" }, "Scripting":{ "Text": "Scripting", "Do": "jump Script" }, "Playing":{ "Text": "Playing", "Do": "jump Playing" }, "Nothing": { "Text": "Nothing", "Do": "jump Nothing", "Condition": function () { return monogatari.storage ('someFlag'); } } }});And then, whenever you want to use that in your script, you just need to place the following statement, saving a lot of space and making it easier to have the same choice in multiple places:Copy'$ menu'[](.)Dynamic Action GenerationAnother great way of using placeholders is generating actions dynamically. To make this happen, be sure that the name you use for your placeholders starts with _The value for your placeholder should be a function that can return any action. Your function will be evaluated before executing it to retrieve the action to run.Beware, this is only recommended for advanced users. When using dynamic action generation, you will be responsible on providing the way to rollback what you did, this means you'll need to save up your own markers in order to know what action you executed before.Copymonogatari.$ ('_dialog', function () { // Within this function, `this` refers to the Monogatari object. if (this.storage ().someFlag) { return 'y This means the flag was true!'; } else { return 'y This means the flag was false!'; } });[](.)Passing Arguments into PlaceholdersArguments can be used in Placeholders like so:Copymonogatari.$ ('_myAction', (arg1, arg2) => { return `${arg1} ${arg2}`; });Which would be called like this:Copy'$ _myAction something something'Placeholders can also have argument passed into them for dynamic functions. The following is an example of a placeholder action that yields a returnable function object block that adds a string argument to an array.Copymonogatari.$ ('_addToInventory', (myArgument) => ({'Function':{ 'Apply': function(){ monogatari.storage().inventory.unshift(myArgument); }, 'Reverse':function(){ monogatari.storage().inventory.shift(); } }}));We'll declare our inventory as an array in storage.jsCopymonogatari.storage ({ inventory: ["Sword"], });And then in our script, we'd call the placeholder like this!Copymonogatari.script ({ 'Start': [ "That's a cool potion. Let's pick it up and put it into our inventory.", "$ _addToInventory Potion", "Now I am holding a {{inventory.0}}", ] });Please note that placeholder arguments are delimited by spaces, so if you want to use spaces in the passed argument strings, you'll need to get a little creative. There are plenty of ways around this. One easy way is to use instead of spaces.Copymonogatari.script ({ 'Start': [ "That's a cool potion. Let's pick it up and put it into our inventory.", "$ _addToInventory Potion of Strength.", "Now I am holding a {{inventory.0}}", ] });Another way to do this would be to capture all arguments and concatenate them into one string, then process that string. Up to you how you want to work with this feature[NextPlay Music](play-music.md)Last updated 4 years agoWas this helpful? # Play Music | Monogatari Documentation [Script Actions](../script-actions.md)Play MusicPlay music media[](.)DescriptionCopy'play music <music_id> [with [properties]]'The play music action let's you, as it name says, play some background music for your game. You can play as many songs as you want simultaneously.To stop the music, check out the [Stop Music documentation](stop-music.md).Action ID: MusicReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionmusic_idstringThe name of the music you want to play. These assets must be declared beforehand.propertiesstringOptional. A list of comma separated properties with their respective value.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of the play music action.Property NameTypeDescriptionfadestringThe fade property let's you add a fade in effect to the music, it accepts a time in seconds, representing how much time you want it to take until the music reaches it's maximum volume.volumenumberThe volume property let's you define how high the music will be played. loopnoneMake the music loop. This property does not require any value.[](.)Assets DeclarationsTo play a song, you must first add the file to your assets/music/ directory and then declare it. To do so, Monogatari has an has a function that will let you declare all kinds of assets for your game.Copymonogatari.assets ('music', { '<music_id>': 'musicFileName' });[](.)Supported FormatsEach browser has it's own format compatibility. MP3 however is the format supported by every browser. If you wish to use other formats, you can check a compatibility table to discover what browsers will be able to play it.[](.)Examples[](.)Play MusicThe following will play the song, and once the song ends, it will simply stop.Script[](.)Music Assets[](.)Copymonogatari.script ({ 'Start': [ 'play music mainTheme' 'end' ] });Copymonogatari.assets ('music', { 'mainTheme': 'mainThemeSong.mp3' });[](.)Loop MusicThe following will play the song, and once the song ends, it will start over on an infinite loop until it is stopped using the [Stop Music Action](stop-music.md).Script[](.)Music Assets[](.)Copymonogatari.script ({ 'Start': [ 'play music mainTheme with loop' 'end' ] });Copymonogatari.assets ('music', { 'mainTheme': 'mainThemeSong.mp3' });[](.)Fade In effectThe following will play the song, and will use a fade in effect.Script[](.)Music Assets[](.)Copymonogatari.script ({ 'Start': [ 'play music mainTheme with fade 3' 'end' ] });Copymonogatari.assets ('music', { 'mainTheme': 'mainThemeSong.mp3' });[](.)Custom VolumeThe following will set the volume of this song to 73%. Script[](.)Music Assets[](.)Copymonogatari.script ({ 'Start': [ 'play music mainTheme with volume 73' 'end' ] });Copymonogatari.assets ('music', { 'mainTheme': 'mainThemeSong.mp3' });Please note however, that the user's preferences regarding volumes are always respected, which means that this percentage is taken from the current player preferences, meaning that if the player has set the volume to 50%, the actual volume value for the song will be the result of:50∗0.73=36.550 * 0.73 = 36.5% 50∗0.73=36.5[](.)All TogetherOf course, you can combine all of this properties, and remember the order doesn't really matter, you can write the properties on the order that feels more natural to you.Script[](.)Music Assets[](.)Copymonogatari.script ({ 'Start': [ 'play music mainTheme with volume 100 loop fade 20' 'end' ] });Copymonogatari.assets ('music', { 'mainTheme': 'mainThemeSong.mp3' }[PreviousPlaceholder](placeholder.md)[NextPlay Sound](play-sound.md)Last updated 4 years agoWas this helpful? # Play Sound | Monogatari Documentation [Script Actions](../script-actions.md)Play SoundPlay a sound effect[](.)DescriptionCopy'play sound <sound_id> [with [properties]]'The play sound action let's you, as it name says, play sound effects in your game. You can play as many sound effects as you want simultaneously.To stop the sound, check out the [Stop Sound documentation](stop-sound.md).Action ID: SoundReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionsound_idstringThe name of the sound you want to play. These assets must be declared beforehand.propertiesstringOptional. A list of comma separated properties with their respective value.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of this action.Property NameTypeDescriptionfadestringThe fade property let's you add a fade in effect to the sound, it accepts a time in seconds, representing how much time you want it to take until the sound reaches it's maximum volume.volumenumberThe volume property let's you define how high the sound will be played. loopnoneMake the sound loop. This property does not require any value.[](.)Assets DeclarationsTo play a sound, you must first add the file to your assets/sound/ directory and then declare it. To do so, Monogatari has an has a function that will let you declare all kinds of assets for your game.CopyMonogatari.assets ('sound', { '<sound_id>': 'soundFileName' });[](.)Supported FormatsEach browser has it's own format compatibility. MP3 however is the format supported by every browser. If you wish to use other formats, you can check a compatibility table to discover what browsers will be able to play it.[](.)Examples[](.)Play SoundThe following will play the sound, and once the sound ends, it will simply stop.Script[](.)Sound Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play sound riverFlow' 'end' ] });CopyMonogatari.assets ('sound', { 'riverFlow': 'river_water_flowing.mp3' });[](.)Loop SoundThe following will play the sound, and once the sound ends, it will start over on an infinite loop until it is stopped using the [Stop Sound Action](stop-sound.md).Script[](.)Sound Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play sound riverFlow with loop' 'end' ] });CopyMonogatari.assets ('sound', { 'riverFlow': 'river_water_flowing.mp3' });[](.)Fade In effectThe following will play the sound, and will use a fade in effect.Script[](.)Sound Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play sound riverFlow with fade 3' 'end' ] });CopyMonogatari.assets ('sound', { 'riverFlow': 'river_water_flowing.mp3' });[](.)Custom VolumeThe following will set the volume of this sound to 73%. Script[](.)Sound Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play sound riverFlow with volume 73' 'end' ] });CopyMonogatari.assets ('sound', { 'riverFlow': 'river_water_flowing.mp3' });Please note however, that the user's preferences regarding volumes are always respected, which means that this percentage is taken from the current player preferences, meaning that if the player has set the volume to 50%, the actual volume value for the sound will be the result of:50∗0.73=36.550 * 0.73 = 36.5% 50∗0.73=36.5[](.)All TogetherOf course, you can combine all of this properties, and remember the order doesn't really matter, you can write the properties on the order that feels more natural to you.Script[](.)Sound Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play sound riverFlow with volume 100 loop fade 20' 'end' ] });CopyMonogatari.assets ('sound', { 'riverFlow': 'river_water_flowing.mp3' });[PreviousPlay Music](play-music.md)[NextPlay Voice](play-voice.md)Last updated 6 years agoWas this helpful? # Play Voice | Monogatari Documentation [Script Actions](../script-actions.md)Play VoicePlay a voice audio file[](.)DescriptionCopy'play voice <voice_id> [with [properties]]'The play voice action let's you, as it name says, play voice files so that you can make your characters speak. You can play as many voices as you want simultaneously.To stop a voice, check out the [Stop Voice documentation](stop-voice.md).Action ID: VoiceReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionvoice_idstringThe name of the voice file you want to play. These assets must be declared beforehand.propertiesstringOptional. A list of comma separated properties with their respective value.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of this action.Property NameTypeDescriptionfadestringThe fade property let's you add a fade in effect to the voice, it accepts a time in seconds, representing how much time you want it to take until the voice reaches it's maximum volume.volumenumberThe volume property let's you define how high the voice will be played. loopnoneMake the voice loop. This property does not require any value.[](.)Assets DeclarationsTo play a voice, you must first add the file to your assets/voice/ directory and then declare it. To do so, Monogatari has an has a function that will let you declare all kinds of assets for your game.Copymonogatari.assets ('voice', { '<voice_id>': 'voiceFileName' });[](.)Supported FormatsEach browser has it's own format compatibility. MP3 however is the format supported by every browser. If you wish to use other formats, you can check a compatibility table to discover what browsers will be able to play it.[](.)Examples[](.)Play VoiceThe following will play the sound, and once the sound ends, it will simply stop.Script[](.)Voice Assets[](.)Copymonogatari.script ({ 'Start': [ 'play voice dialog_001', 'This is the dialog that the voice file is narrating', 'end' ] });Copymonogatari.assets ('voice', { 'dialog_001': 'dialog_file_1.mp3' });[](.)Loop VoiceThe following will play the voice file, and once it ends, it will start over on an infinite loop until it is stopped using the [Stop Voice Action](stop-voice.md).Script[](.)Sound Assets[](.)Copymonogatari.script ({ 'Start': [ 'play voice dialog_001 with loop', 'This is the dialog that the voice file is narrating', 'end' ] });Copymonogatari.assets ('voice', { 'dialog_001': 'dialog_file_1.mp3' });[](.)Fade In effectThe following will play the voice file, and will use a fade in effect.Script[](.)Sound Assets[](.)Copymonogatari.script ({ 'Start': [ 'play voice dialog_001 with fade 3', 'This is the dialog that the voice file is narrating', 'end' ] });Copymonogatari.assets ('voice', { 'dialog_001': 'dialog_file_1.mp3' });[](.)Custom VolumeThe following will set the volume of this voice to 73%. Script[](.)Sound Assets[](.)Copymonogatari.script ({ 'Start': [ 'play voice dialog_001 with volume 73', 'This is the dialog that the voice file is narrating', 'end' ] });Copymonogatari.assets ('voice', { 'dialog_001': 'dialog_file_1.mp3' });Please note however, that the user's preferences regarding volumes are always respected, which means that this percentage is taken from the current player preferences, meaning that if the player has set the volume to 50%, the actual volume value for the voice will be the result of:50∗0.73=36.550 * 0.73 = 36.5% 50∗0.73=36.5[](.)All TogetherOf course, you can combine all of this properties, and remember the order doesn't really matter, you can write the properties on the order that feels more natural to you.Script[](.)Sound Assets[](.)Copymonogatari.script ({ 'Start': [ 'play voice dialog_001 with volume 100 loop fade 20', 'This is the dialog that the voice file is narrating', 'end' ] });Copymonogatari.assets ('voice', { 'dialog_001': 'dialog_file_1.mp3' });[PreviousPlay Sound](play-sound.md)[NextShow Canvas](canvas.md)Last updated 5 years agoWas this helpful? # Show Background | Monogatari Documentation [Script Actions](../script-actions.md)Show BackgroundShow a background without removing any game elements.[](.)DescriptionCopy'show background <resource> [with [animations] [classes] [properties]]'The show background action will change the background without removing any game elements (as opposed to the [scene action](show-scene.md)).This action uses the assets from the scene action so they still need to be declared as scene assets.Action ID: Show::BackgroundReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionresourcestringThe resource to use as the background. This resource may be one of the following: Asset ID: The ID of a scene asset previously defined.CSS Property: Any valid non-spaced value for the background CSS property.animationsstringOptional. A list of comma separated animation names with which the background will be shown.classesstringOptional. A list of comma separated CSS class names that will be added to the background element. You can create custom classes on your CSS and add them to your background dynamically using this list.propertiesstringOptional. A list of comma separated properties with their respective value.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of the background action.Property NameTypeDescriptiondurationstringThe duration for the animations used. The value for this property must be a non-spaced valid value for the animation-duration CSS property.[](.)Examples[](.)Using an image as the backgroundIf you want to use an image for the background, remember you first have to declare your image assets and place all your files under the assets/scenes/ directory.Script[](.)Scenes Assets[](.)CopyMonogatari.script ({ 'Start': [ 'show background mountain' 'end' ] });CopyMonogatari.assets ('scenes', { 'mountain': 'mountain.png', 'sea': 'sea.png' });[](.)Using CSS properties as the backgroundIf you'll use CSS to set a custom background, you can use any valid non-spaced value for the background-image or background-color CSS properties. Using CSS is perfect for when you want your background to be a solid color. Here are some valid statements:CopyMonogatari.script ({ 'Start': [ 'show background #fff' 'show background rgb(0, 0, 0)' 'show background url("assets/scenes/mountain.png")' 'end' ] });You can also use CSS gradients for backgrounds.CopyMonogatari.script ({ 'Start': [ "We're about to show a linear gradient.", "show background linear-gradient(to right, red, yellow)", "Isn't that lovely?", "end" ] });[](.)Using AnimationsMonogatari comes with some built-in animations ready for you to use, you can see the list of animations and visualize them here. Using animations is as simple as indicating their name!Script[](.)Scenes Assets[](.)CopyMonogatari.script ({ 'Start': [ 'show background sea with fadeIn' 'end' ] });CopyMonogatari.assets ('scenes', { 'mountain': 'mountain.png', 'sea': 'sea.png' });[](.)Modifying an animation durationThe following will set the background to a solid color using a CSS value and modify the duration of the fadeIn animation to 20 seconds.CopyMonogatari.script ({ 'Start': [ 'show background #424242 with fadeIn duration 20s' 'end' ] });[](.)Creating a custom animationYou can also use CSS to create your own animations, you'll have to apply them to a CSS class and then use the show background statement as follows:For example, note the following CSS code creating a simple Ken Burn Animation:Script[](.)Scenes Assets[](.)CSS[](.)CopyMonogatari.script ({ 'Start': [ 'show background mountain with ken-burn' 'end' ] });CopyMonogatari.assets ('scenes', { 'mountain': 'mountain.png', 'sea': 'sea.png' });Copy.ken-burn { animation-name: ken-burns; /* Name of the animation to use */ animation-duration: 30s; animation-iteration-count: infinite; /* 1 if it should only move once */ animation-timing-function: ease; animation-fill-mode: forwards; animation-delay: 0s; -moz-transition: ease 1s all; -o-transition: ease 1s all; -webkit-transition: ease 1s all; transition: ease 1s all; } @keyframes ken-burns { 0% { transform: translateX(0); } 50% { transform: translateX(-500px); } 100% { transform: translateX(0px); } }[PreviousShow Canvas](canvas.md)[NextShow Character](characters.md)Last updated 5 years agoWas this helpful? # Show Image | Monogatari Documentation [Script Actions](../script-actions.md)Show ImageShow an image[](.)DescriptionCopy'show image <resource> [with [animations] [classes]]'The image allows you to display an image. For character sprites, take a look at the [show character action](characters.md).Action ID: ImageReversible: YesRequires User Interaction: No[](.)ExamplesIn case of images, they can be declared to use an identifier or you can also use the file's name. The root directory for images will always be img.Copy'show image flower center with fadeIn', // If flower is declared 'show image flower.png center with fadeIn' // If flower isn't declared[PreviousShow Character](characters.md)[NextShow Message](message.md)Last updated 6 years agoWas this helpful? # Show Scene | Monogatari Documentation [Script Actions](../script-actions.md)Show SceneChange the background[](.)DescriptionCopy'show scene <resource> [with [animations] [classes] [properties]]'The scene action will change the background and clear the screen, removing all characters, images and text currently displayed.Action ID: SceneReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionresourcestringThe resource to use as the background for the scene. This resource may be one of the following: Scene ID: The ID of a scene asset previously defined.CSS Property: Any valid non-spaced value for the background CSS property.animationsstringOptional. A list of comma separated animation names with which the scene will be shown.classesstringOptional. A list of comma separated CSS class names that will be added to the background element. You can create custom classes on your CSS and add them to your background dynamically using this list.propertiesstringOptional. A list of comma separated properties with their respective value.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of the scene action.Property NameTypeDescriptiondurationstringThe duration for the animations used. The value for this property must be a non-spaced valid value for the animation-duration CSS property.[](.)Using an image as the backgroundThe most common case ofIf you want to use an image for the background, remember you first have to declare your image assets and place all your files under the assets/scenes/ directory.Script[](.)Scenes Assets[](.)Copymonogatari.script ({ 'Start': [ 'show scene mountain' 'end' ] });Copymonogatari.assets ('scenes', { 'mountain': 'mountain.png', 'sea': 'sea.png' });[](.)Using CSS properties as the backgroundIf you'll use CSS to set a custom background, you can use any valid non-spaced value for the background-image or background-color CSS properties. Using CSS is perfect for when you want your background to be a solid color. Here are some valid statements:Copymonogatari.script ({ 'Start': [ 'show scene #fff' 'show scene rgb(0, 0, 0)' 'show scene url("assets/scenes/mountain.png")' 'end' ] });[](.)Using AnimationsMonogatari comes with some built-in animations ready for you to use, you can see the list of animations and visualize them here. Using animations is as simple as indicating their name!Script[](.)Scenes Assets[](.)Copymonogatari.script ({ 'Start': [ 'show scene sea with fadeIn' 'end' ] });Copymonogatari.assets ('scenes', { 'mountain': 'mountain.png', 'sea': 'sea.png' });[](.)Modifying an animation durationThe following will set the background to a solid color using a CSS value and modify the duration of the fadeIn animation to 20 seconds.Copymonogatari.script ({ 'Start': [ 'show scene #424242 with fadeIn duration 20s' 'end' ] });[](.)Creating a custom animationYou can also use CSS to create your own animations, you'll have to apply them to a CSS class and then use the scene statement as follows:For example, note the following CSS code creating a simple Ken Burn Animation:Script[](.)Scenes Assets[](.)CSS[](.)Copymonogatari.script ({ 'Start': [ 'show scene mountain with ken-burn' 'end' ] });Copymonogatari.assets ('scenes', { 'mountain': 'mountain.png', 'sea': 'sea.png' });Copy.ken-burn { animation-name: ken-burns; /* Name of the animation to use */ animation-duration: 30s; animation-iteration-count: infinite; /* 1 if it should only move once */ animation-timing-function: ease; animation-fill-mode: forwards; animation-delay: 0s; -moz-transition: ease 1s all; -o-transition: ease 1s all; -webkit-transition: ease 1s all; transition: ease 1s all; } @keyframes ken-burns { 0% { transform: translateX(0); } 50% { transform: translateX(-500px); } 100% { transform: translateX(0px); } }[PreviousShow Particles](particles.md)[NextShow Video](show-video.md)Last updated 4 years agoWas this helpful? # Show Video | Monogatari Documentation [Script Actions](../script-actions.md)Show Video[](.)DescriptionCopy'show video <video_id> <mode> [with [properties]]'The video action allows you to show videos on your novel in different modes.Action ID: VideoReversible: YesRequires User Interaction: No, unless the close property is not given, then the user will have to click once the video is over to advance.[](.)ParametersNameTypeDescriptionmodestringDefines what way you want to show the video like.Possible Values:modal - Shows the video as a immersive - Shows the video covering the full game screen background - Shows the video as a background for your charactersfullscreen - Attempts to show the video in full screen, if permission is denied, it will fallback to the immersive mode.video_idstringThe ID of a video asset previously defined.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of the video action.NameTypeDescriptioncontrolsNo value requiredOptional. Adding this property will make the video controls (play, pause, seeking) visible for the player. closeNo value requiredOptional. Adding this property will make the video close itself once it's over.loopNo value requiredOptional. Adding this property will make the video loop. The close property will not have any effect if the loop property is added.[](.)Assets DeclarationsTo play a video, you must first add the file to your assets/video/ directory and then declare it. To do so, Monogatari has an has a function that will let you declare all kinds of assets for your game.CopyMonogatari.assets ('videos', { '<video_id>': 'videoFileName' });[](.)Supported FormatsEach browser has it's own format compatibility. MP4 however is the format supported by most browsers. If you wish to use other formats, you can check a compatibility table to discover what browsers will be able to play it.[](.)ExamplesScript[](.)Video Assets[](.)CopyMonogatari.script ({ 'Start': [ 'show video flowerTimelapse modal' 'end' ] });CopyMonogatari.assets ('video', { '<video_id>': 'videoFileName' });[PreviousShow Scene](show-scene.md)[NextStop Music](stop-music.md)Last updated 5 years agoWas this helpful? # Stop Music | Monogatari Documentation [Script Actions](../script-actions.md)Stop MusicStop playing music[](.)DescriptionCopy'stop music [music_id] [with [properties]]'The stop music action will let you stop either all music currently playing or only one in specific. To learn more about music, read the[Play Music documentation](play-music.md).Action ID: Music::StopReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionmusic_idstringOptional. The name of the specific music you want to stop.propertiesstringOptional. A list of comma separated properties with their respective value.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of the stop music action.NameTypeDescriptionfadenumberThe fade property let's you add a fade out effect to the music, it accepts a time in seconds, representing how much time you want it to take until the music volume is zero.[](.)Examples[](.)Stop a specific MusicThe following will stop a specific music, identified by it's name.Script[](.)Music Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play music mainTheme loop', 'play music mistery loop', 'Two songs are currently playing', 'stop music mainTheme', 'Only the mistery song is playing now', 'end' ] });CopyMonogatari.assets ('music', { 'mainTheme': 'mainThemeSong.mp3', 'mistery': 'misterious_song.ogg' });[](.)Stop all MusicThe following will stop all music currently playingScript[](.)Music Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play music mainTheme loop', 'play music mistery loop', 'Two songs are currently playing', 'stop music', 'No music is playing anymore', 'end' ] });CopyMonogatari.assets ('music', { 'mainTheme': 'mainThemeSong.mp3', 'mistery': 'misterious_song.ogg' });[](.)Fade Out EffectThe following will stop the song, and will use a fade out effect to do so. You can also use a fade out effect when stopping all music.Script[](.)Music Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play music mainTheme loop', 'play music mistery loop', 'Two songs are currently playing', 'stop music mistery with fade 5', 'No music is playing anymore', 'end' ] });CopyMonogatari.assets ('music', { 'mainTheme': 'mainThemeSong.mp3', 'mistery': 'misterious_song.ogg' });[PreviousShow Video](show-video.md)[NextStop Sound](stop-sound.md)Last updated 6 years agoWas this helpful? # Stop Sound | Monogatari Documentation [Script Actions](../script-actions.md)Stop Sound[](.)DescriptionCopy'stop sound [sound_id] [with [properties]]'The stop sound action will let you stop either all sounds currently playing or only one in specific. To learn more about sound, read the[Play Sound documentation](play-sound.md).Action ID: Sound::StopReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionsound_idstringOptional. The name of the specific sound you want to stop.propertiesstringOptional. A list of comma separated properties with their respective value.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of the stop sound action.NameTypeDescriptionfadenumberThe fade property let's you add a fade out effect to the sound, it accepts a time in seconds, representing how much time you want it to take until the sound volume is zero.[](.)Examples[](.)Stop a specific SoundThe following will stop a specific sound, identified by it's name.Script[](.)Sound Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play sound night loop', 'play sound fireCracks loop', 'Two sounds are currently playing', 'stop sound fireCracks', 'I guess some one put out the fire, only the night sounds are heard now', 'end' ] });CopyMonogatari.assets ('sound', { 'fireCracks': 'fire-cracks.mp3', 'night': 'night.mp3' });[](.)Stop all SoundsThe following will stop all sounds currently playing.Script[](.)Sound Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play sound night loop', 'play sound fireCracks loop', 'Two sounds are currently playing', 'stop sound', 'It really is a silent night, nothing is heard anymore', 'end' ] });CopyMonogatari.assets ('sound', { 'fireCracks': 'fire-cracks.mp3', 'night': 'night.mp3' });[](.)Fade Out EffectThe following will stop the sound, and will use a fade out effect to do so. You can also use a fade out effect when stopping all sounds.Script[](.)Sound Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play sound night loop', 'play sound fireCracks loop', 'Two sounds are currently playing', 'stop sound fireCracks with fade 12', 'I guess some one put out the fire, only the night sounds are heard now', 'end' ] });CopyMonogatari.assets ('sound', { 'fireCracks': 'fire-cracks.mp3', 'night': 'night.mp3' });[PreviousStop Music](stop-music.md)[NextStop Voice](stop-voice.md)Last updated 6 years agoWas this helpful? # Stop Voice | Monogatari Documentation [Script Actions](../script-actions.md)Stop Voice[](.)DescriptionCopy'stop voice [voice_id] [with [properties]]'The stop voice action will let you stop either all voices currently playing or only one in specific. To learn more about voices, read the[Play Voice documentation](play-voice.md).Action ID: Voice::StopReversible: YesRequires User Interaction: No[](.)ParametersNameTypeDescriptionvoice_idstringOptional. The name of the specific voice you want to stop.propertiesstringOptional. A list of comma separated properties with their respective value.[](.)PropertiesThe following is a comprehensive list of the properties available for you to modify certain behaviors of the stop voice action.NameTypeDescriptionfadenumberThe fade property let's you add a fade out effect to the voice, it accepts a time in seconds, representing how much time you want it to take until the voice volume is zero.[](.)Examples[](.)Stop a specific VoiceThe following will stop a specific voice, identified by it's name.Script[](.)Voice Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play voice dialog_002 with loop', 'The previous voice will be repeating itself over and over', 'play voice dialog_001', 'Two voices are currently playing', 'stop voice dialog_002', 'Now the first one has stopped and the second one will stop as soon as it ends', 'end' ] });CopyMonogatari.assets ('voice', { 'dialog_001': 'dialog_file_1.mp3', 'dialog_002': 'dialog_file_2.mp3' });[](.)Stop all VoicesThe following will stop all sounds currently playing.Script[](.)Voice Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play voice dialog_002 with loop', 'The previous voice will be repeating itself over and over', 'play voice dialog_001', 'Two voices are currently playing', 'stop voice', 'No voice is playing now', 'end' ] });CopyMonogatari.assets ('voice', { 'dialog_001': 'dialog_file_1.mp3', 'dialog_002': 'dialog_file_2.mp3' });[](.)Fade Out EffectThe following will stop the voice, and will use a fade out effect to do so. You can also use a fade out effect when stopping all voices.Script[](.)Voice Assets[](.)CopyMonogatari.script ({ 'Start': [ 'play voice dialog_002 with loop', 'The previous voice will be repeating itself over and over', 'play voice dialog_001', 'Two voices are currently playing', 'stop voice dialog_002 with fade 12', 'Now the first will be slowly stopped and the second one will stop as soon as it ends', 'end' ] });CopyMonogatari.assets ('voice', { 'dialog_001': 'dialog_file_1.mp3', 'dialog_002': 'dialog_file_2.mp3' });[PreviousStop Sound](stop-sound.md)[NextVibrate](vibrate.md)Last updated 6 years agoWas this helpful? # Vibrate | Monogatari Documentation [Script Actions](../script-actions.md)VibrateMake the player's device vibrate[](.)DescriptionCopy'vibrate <pattern>'The vibrate action allows you to make the player's device vibrate on a given pattern. By vibration we refer to actual vibration, not shaking the screen so this is only supported on mobile devices with such capabilities. If this feature is not supported on the player's device, nothing will happen and the game will continue with it's normal flow.Action ID: VibrateReversible: YesRequires User Interaction: No[](.)ParametersNameTypeOptionalDescriptionpatternnumbersNoList of space separated times in milliseconds. While there's no limit on how large a pattern can be, at least one time is required.[](.)Single VibrationYou can make the device vibrate only once, for example, the following will make it vibrate for 200ms and then stop.Copymonogatari.script ({ 'Start': [ 'vibrate 200' 'end' ] });[](.)Pattern VibrationYou can also make the device vibrate following a pattern by providing a list of times, the following will make the device vibrate for 200ms, then 100ms, then 150ms and so on.Copymonogatari.script ({ 'Start': [ 'vibrate 200 100 150 50 200 300' 'end' ] });[PreviousStop Voice](stop-voice.md)[NextWait](wait.md)Last updated 4 years agoWas this helpful? # Wait | Monogatari Documentation [Script Actions](../script-actions.md)WaitWait an amount of time before continuing[](.)DescriptionCopy'wait [time]'The wait action allows us to make the game wait for a certain amount of time or for before continuing. Once the time has passed, the game will automatically continue to the next statement.Action ID: WaitReversible: YesRequires User Interaction: Optional[](.)ParametersNameTypeOptionalDescriptiontimenumberYesThe time in milliseconds that you want the game to wait.[](.)Waiting a certain amount of timeCopymonogatari.script ({ 'Start': [ 'Hello there! I want you to wait 5 seconds now', 'wait 5000', 'Wow, that was a long time!', 'end' ] });In this case, the first dialog will be shown, then, when the player clicks to continue, the wait statement will make it wait for 5 seconds before the next dialog is shown.You may be wondering why it says 5000 instead of just 5 in order to wait for 5 seconds, the reason behind it is that the wait action accepts the time in milliseconds, therefore we have to make the conversion from seconds to milliseconds for it to work as we want.[](.)Waiting for player interactionCopymonogatari.script ({ 'Start': [ 'Hello there! I want you to wait 5 seconds now', 'wait', 'Wow, that was a long time!', 'end' ] });If no time was provided, then when the player reaches the wait statement, the game will just stop and the player will have to click/interact again for it to continue to the next dialog.[PreviousVibrate](vibrate.md)[NextCredits Screen](../components/credits.md)Last updated 4 years agoWas this helpful? # CSS Classes | Monogatari Documentation [Style & Design](../style-and-design.md)CSS ClassesThere are already some CSS classes declared, to make your designing a little easier. As you may have already noticed, many are already being used in the basic interface you get by default.ClassDescriptioncenterPositions the element in the horizontal center of the screen.leftPositions the element in the left side of the screen.rightPositions the element in the right side of the screen.bottomPositions the element in the bottom of the screen.middlePositions the element in the middle of the screen, vertically and horizontally.text--leftAligns the content to the left.text--centerAligns the content to the center.text--rightAligns the content to the right.verticalDistributes the content vertically.horizontalDistributes the content horizontally.[PreviousResponsiveness](responsiveness.md)[NextHTML Data Attributes](data-attributes.md)Last updated 4 years agoWas this helpful? # HTML Data Attributes | Monogatari Documentation [Style & Design](../style-and-design.md)HTML Data Attributes[](.)Overview[](.)Built-in AttributesIf you've already seen the HTML, you'll notice many "data" attributes in the elements; this are used by the engine so you may change the structure and using the right datas everything will just work.This table will resume the general datas and what are they for.DataFunctiondata-actionAll data-actions well... do something, they are handled by the $("[data-action]").click() function in the engine.data-uiThe data-uis are part of the user interface, things that the user will see.data-componentThe data-uis are part of the user interface, however the user will not see them.data-menuThe data-menus are the screens and menus of your game.data-backgroundPut an image url on it and it will apply that image as a background to that element.data-closeCloses another element by it's data-ui name.data-targetThis is mainly used for internal functions, just to target the specific media player.data-stringUsed for Multilanguage Games, will change the text of the element with the proper translation from the strings.js file.data-jumpUsed in choices, to state what a choice will do when it's clicked.Now, that gives you some idea of what everything is for, now you need to understand what every one of them is used for.DataFunctiondata-action="open-menu"Opens the menu/screen from the data-open next to it and closes the others.data-action="back"While playing, it will go back to the previous statement, while on a menu, it will just go back to the main screen.data-action="close"Will trigger the close of an element, using the data-close next to it.data-action="close-video"Closes the Video element.data-action="set-language"Used for the language setting.data-action="set-volume"Changes the volume of the element given in the data-target next to it.data-action="jump"Declares an element that when clicked will make the game jump to the label declared inside it's data-jump.data-jump="[label]"Declares the label the game will jump to when this data-action="jump" element is clicked.data-component="modal"Defines a modal window like the one used for the messages.data-ui="inner-menu"Defines a menu inside another menu/screen.data-ui="slots"Defines the place where the save and load slots will be displayed.data-ui="text"Defines the place where the "Say" text and character's name is displayed.data-ui="who"Defines the place where the character's name is displayed.data-ui="say"Defines the place where the "Say" text is displayed.[PreviousCSS Classes](classes.md)[NextIcons](icons.md)Last updated 4 years agoWas this helpful? # Icons | Monogatari Documentation [Style & Design](../style-and-design.md)IconsIcons are very useful, specially for a game, Font Awesome is included in Monogatari and as you can see from below, they sure have a lot of icons that you can use! To learn more about how to use them, visit their webpage.[PreviousHTML Data Attributes](data-attributes.md)[NextImage Menus](image-menus.md)Last updated 6 years agoWas this helpful? # Image Menus | Monogatari Documentation [Style & Design](../style-and-design.md)Image MenusMany use Image Menus for their interfaces, however, I really discourage this in Monogatari. It's fairly easy to do it as you can see in this tutorial however using image maps is not good for the responsiveness and since we want everyone to be able to enjoy our game, I really encourage you to do everything using HTML and CSS. Sure, the buttons, headers, texts etc. can have background images, but try to avoid using them as the main interface.[PreviousIcons](icons.md)[NextChrome App](../releasing-your-game/chrome.md)Last updated 6 years agoWas this helpful? # Responsiveness | Monogatari Documentation [Style & Design](../style-and-design.md)ResponsivenessPart of what makes Monogatari unique is how it is responsive out of the box, meaning people in all kind of devices with different form factors will be able to play your game.It is important to note that responsiveness is not the same as simply scaling your game up or down to fit the screen it's being displayed on. Responsiveness actually let's you create different stylings and layouts for different sizes so you can take advantage when there's more space or adapt your elements when it's less.Adapting to the device is done using CSS media queries.[](.)Default Screen-Width BreakpointsCopy/** Extra Small Devices, Phones (480px) **/ @media screen and (min-width : 30em) {} /** Medium Screens, Phablets (601px) **/ @media screen and (min-width: 37.56255em) {} /** Medium Devices, Tablets (992px)**/ @media screen and (min-width: 62em) {} /** HD Screen, Large Devices, Wide Screens, Desktop (1200px) **/ @media screen and (min-width: 75em) {} /** Full HD Screen, Large Devices, Wide Screens, Large Desktops (1920px) **/ @media screen and (min-width: 120em) {} /** Retina Screen , Large Devices, Wide Screens(2560px) **/ @media screen and (min-width: 160em) {} /** 4k Screens, Large Devices, Wide Screens (3840px) **/ @media screen and (min-width: 240em) {} /** 5k Screens, Large Devices, Wide Screens (5000px) **/ @media screen and (min-width: 312.5em) {} /** 8k Screens, Large Devices, Wide Screens (8000px) **/ @media screen and (min-width: 500em) {}[PreviousSplit Files](../configuration-options/split.md)[NextCSS Classes](classes.md)Last updated 4 years agoWas this helpful? # Internationalization | Monogatari Documentation [Configuration Options](../../../../configuration-options.md)[Game Configuration](../../../../configuration-options/game-configuration.md)InternationalizationMaking Monogatari games multilingual.[](.)OverviewYou can create a multi-language game pretty easily. To do so you need to follow these simple steps:[](.)1. Enable the MultiLanguage settingGo to the options.js file and change the 'MultiLanguage' value to true.Copy // Change to true for a MultiLanguage GameScreen. 'MultiLanguage': true,This will let monogatari know that you're planning to have your script in multiple languages. If you reload the page however, you'll most likely get an error like this one:This is normal and is caused because while we have indicated we would be having multiple versions of our script in multiple languages, we haven't changed the structure of our script to match that.[](.)2. Change your Script FormatNext we will be changing the script to support Multi Language games. A single language script as you know is defined as an object with a list of labels and usually looks something like this:Copymonogatari.script ({ 'Start': [ 'Hi, welcome to your first Visual Novel with Monogatari.', 'jump other' ], 'other': [ 'Another Label!', 'end' ] });Note how each label is in the top level of the script objects hierarchy. A Multi Language script will instead have language objects in the top level and inside of them, the labels of the script that correspond to that language, for example:Copymonogatari.script ({ 'English':{ 'Start': [ 'Hi, welcome to your first Visual Novel with Monogatari.', 'jump other' ], 'other': [ 'Another Label!', 'end' ] }, 'Español':{ 'Start': [ 'Hola, bienvenido a tu primer Novela Visual con Monogatari.', 'jump other' ], 'other': [ 'Otro label!', 'end' ] } });Note how we essentially duplicated our script inside this new language objects and translated it for each language. When saving a game, monogatari saves the label and step (index of the statement) it's currently on. For saved games to be fully compatible with each other, your different scripts should have the same label names and number of statements in them, otherwise, a game saved in one language would be invalid if the player changed language again and tried to load it. If you do a [jump](../../../../script-actions/jump.md) to a label that exists in one translation, but does not exist in the language that the player has selected for example, they will get an error telling them that the label does not exist, so be careful when crafting your script! In case some differences can't be avoided, you can rely on the [next action](../../../../script-actions/next.md) to fill act as a filler and make all scripts have the same size. If you have your script split in multiple files, you can read more on how to configure internationalization with split files here:[Split Files](../../../../configuration-options/split.md)[](.)3. The Language Selection ScreenOnce you've formatted your script correctly, a language selection screen will appear for players that haven't selected a language yet. It will automatically detect the languages in your script and show buttons like the ones shown in the image.You most likely won't see this screen appear to you because you transitioned from a single language game to a multi language one and thus, your settings were already set. If you want to trigger this screen, you'll have to remove the settings from your storage using the dev tools.Players are also able to change their language from the settings screen. The following image shows the language selection setting that appears automatically when a multi language game is configured:In case you don't want the language selection screen to appear or have built your own, it can be disabled from the options.js file by changing the LanguageSelectionScreen property to false:Copy// If the 'Multilanguage' setting is set to `true`. This will enable a // language selection screen that will be shown before the asset loading // screen. If set to false, the loading screen will appear first instead and // players will have to change the language from the settings screen. 'LanguageSelectionScreen': false,[](.)4. Set the Default Language PreferenceFinally, you need to provide a default language for your game. While players will be able to choose their language, monogatari does need something to fall back to. Go to the options.js file once more and this time, go to the preferences section in the bottom. There, you should change the 'Language' property to the name of the language you want to have by default. Copy// Initial Language for Multilanguage Games or for the Default GUI Language. 'Language': 'Français',[](.)Supported LanguagesMonogatari already has built-in support for the following languages:БеларускаяDeutschEnglishEspañolFrançaisNederlandsPortuguêsРусскийاللغه العربية한국어日本語简体中文toki ponaThat means UI translations for those languages are available and the full UI will be shown in the correct language. You can also retrieve the list of available translations programmatically using the monogatari.translations () function. This function will return an object with all the available languages and the UI string translations for them.[](.)Translating the Engine UI for a not-yet-supported languageIf you need to translate the game into a language that Monogatari doesn't include in the [supported languages](../../../../configuration-options/game-configuration/internationalization.md) or simply want to modify an existing translation, you can define your own UI translations as follows:Copymonogatari.translation ('YourLanguage', { 'SomeString': 'Your Translation' });You can also contribute to monogatari by adding new language translations to it or updating existing ones. You can learn more here:[Translations](../../../../advanced-monogatari-development/translations.md)[](.)Adding the UI translationsTo make a new language available for your players to choose from, you'll have to add translations for all the strings the UI requires. You can take the English strings file as reference so you can gather all the required strings and add your own translations for your language.You can also get a complete list of every string to translate programmatically by using the monogatari.translation ('<language_key>') function, for example:[](.)Adding the language metadataEach language also requires some metadata that will be used by monogatari.MetadataDescriptioncodeThe 2 letter code that represents that language. This code must be a valid ISO 639-1 code since it will be used to format the dates shown on the save slots.iconAn emoji that will be shown as the icon for the language in the language selection screen. Here's an example of the definition of a language metadata:Copymonogatari.languageMetadata ('Español', { code: 'es', icon: '🇲🇽' });Once you've setup all the string translations and language metadata, all you need is adding your script for your new language and the option will appear automatically both on the language selection and the settings screens.[PreviousAsset Preloading](../../../../configuration-options/game-configuration/asset-preloading.md)[NextSaving](../../../../configuration-options/game-configuration/saving.md)Last updated 4 years agoWas this helpful? # Show Character | Monogatari Documentation [Script Actions](../../../script-actions.md)Show CharacterShow a character's sprite[](.)DescriptionCopy'show character <character_id> <sprite_id> [at [class]] [with [animations] [classes] [properties]]'The character action allows you to display a character's sprite. For other kind of images, take a look at the [show image action](../../../script-actions/show-image.md).Action ID: Show::CharacterReversible: YesRequires User Interaction: No[](.)PropertiesThese are special properties/classes that can be used when showing a character:NameDescriptiondurationDuration affects the animation-duration CSS property so you can change the duration of any animations applied to the character.moveWith this class, the character will move nicely from one position to another one.transitionTransition affects the transition-duration CSS property so you can change the duration of the transition for the move class.end-<className>When using this property, the provided class will be added to the sprite when it gets changed.[](.)ExamplesRemember every character image must be declared in the characters object.Copy'show character e normal at center with fadeIn',The animation is completely optional, and if a position is not given, it will show in the center by default.Copy'show character e normal', 'show character e normal with fadeIn',[](.)Exit AnimationsNew to version 2.0: You can now also set an animation that will play when the character is removed or replaced.Copyshow character <character_id> <sprite_id> [at [class]] [with [animation] [end-[animation]] [classes]][](.)DurationThis is useful if you want to smoothly transition the same character's sprites, such as this example involving crossfades.The following code will show the character with a fadeIn animation that will take 20 seconds to complete.Copy "Hello", "show character s Normal at center with fadeIn end-fadeOut", "s Hi theeeeere.", "show character s Happy at center with fadeIn end-fadeOut", "I'm happy now, and my smile just faded onto my face.", "show character s Normal at center with fadeIn", "Normal."Copy'show character e normal with fadeIn duration 20s'[](.)End AnimationsEnd animations are a way of preparing an animation to happen whenever a sprite gets changed, the following code will show the character first and when it reaches the next line, the character will fade out while the new sprite fades in.Copy'show character e normal with end-fadeOut', 'show character e happy with fadeIn'[PreviousShow Background](../../../script-actions/show-background.md)[NextShow Image](../../../script-actions/show-image.md)Last updated 4 years agoWas this helpful? # Dialogs | Monogatari Documentation [Script Actions](../../../script-actions.md)DialogsThe text your players are going to read.[](.)DescriptionCopy'[character_id][:<expression_id>] <dialog_text>'Action ID: DialogReversible: YesRequires User Interaction: YesRelated Components:[Text Box](../../../components/text-box.md)[](.)Narrator DialogsAny statement in your script that does not match any other action will be displayed as a narrator dialog. Narrator dialogs do not show any name on the textbox.[](.)Character DialogsThe say statement is used... well, for a character to say something. The syntax is as follows:'[Character Identifier] [Text to Say]'Copy'y Hi! My name is Yui.'A fresh Monogatari game showing Yui speaking, saying "Hi! My name is Yui."It also accepts HTML, so you can show many things in a text like the Font Awesome icons.Copy'y The <span class="fa fa-arrow-left"></span> button is the back button, press it to return to a previous state of the game.'An example of HTML being used inline, in this case to show a back arrow icon.If no character identifier is given, it will be considered as a narration and no name will be shown.Copy'This would be a narrator.'The narrator speaking. [](.)Clear the TextThe 'clear' command sends an empty line of dialog to remove all text on the screen.Copy'clear'The clear command automatically runs the next line without requiring a click from the player. You may want to insert a 'wait' command immediately after it if you want the player to see a blank dialog box.[](.)Side ImagesIf you've defined the expressions property for your characters, adding a list of images to show, you can use them as side images with each dialog, to do so, you should use a format like this one:Copy'y:Smiling Hi! My name is Yui.'Yui saying "Hi! My name is Yui." with a side image of a picture of her smiling.This assumes you have a Side image called Smiling, which means with every dialog you can specify what side image to use. If you add the 'expression' property then that image will be shown with every dialog without the need of specifying it inside the statement.[](.)Centered DialogsThe 'centered' command is like a special character-id that goes at the beginning of dialog text to display a special floating box that hovers in the very center of the screen.Copy'centered This is an example of centered text.'The text box that displays 'centered' text is special. Rather than being inside of a <text-box> tag, it is instead inside of a <centered-dialog> tag.Copy<centered-dialog class="animated" data-component="centered-dialog"> <div data-content="wrapper">Here's some more centered text.</div> </centered-dialog>While 'centered' text is visible on screen, the main <text-box> is hidden with a CSS display:none; attribute.HTML can be used inside of 'centered' text the same as normal dialog, so you can use this to display a special image that removes the text box when it displays it, or something similar, if you want to get creativeNVL DialogsThe 'nvl' command is similar to the 'centered' command in that it is used at the beginning of a line of dialog to present a special display, similar to games like Fate/stay Night, or Radical Dreamers.Copy'nvl Here is an example of NVL text.', 'nvl Here is some more NVL text.', 'nvl One more line.'The 'nvl' text differs from normal Monogatari text in that clicking does not clear the current text off of the screen, and instead leaves it there, feeding consecutive NVL dialogue one at a time as the player clicks to progress.NVL text is displayed on screen inside of a <text-box> with a CSS class'nvl'. By default, this textbox is styled to fill the entire screen. Additionally, each line of NVL text is contained inside of a <div> with a data-spoken value for whichever character is speaking. Normally it's the narrator, but if you give a character the nvl attribute and set it to true then that character can be used as an NVL character, and their text can have special CSS rules if you want to get creative with that.An example of some characters speaking in NVL mode.Note that Expression side images are not supported in NVL mode.The NVL mode text box is scrollable, like other text boxes in Monogatari, should there be too much text to display at once. Additionally, you can use HTML in NVL mode the same as all of the other modes[NextEnd](../../../script-actions/end.md)Last updated 4 years agoWas this helpful?