├── docs ├── requirements.txt ├── out │ ├── media │ │ └── img │ │ │ └── book_bg.jpg │ └── index.html ├── src │ ├── introduction.md │ ├── index.md │ ├── base.html │ ├── files.md │ ├── i18n.md │ ├── implicit.md │ ├── creating.md │ ├── HTML.md │ └── API.md └── make.py ├── .gitignore ├── games ├── media │ ├── img │ │ ├── page_bg.jpg │ │ ├── text_bg.jpg │ │ ├── title_bg.jpg │ │ ├── tools_bg.jpg │ │ ├── iphone │ │ │ ├── icon.png │ │ │ └── splash.png │ │ ├── toolbar_bg.jpg │ │ └── external_link.png │ ├── games │ │ └── tutorial │ │ │ ├── woodcut1.png │ │ │ ├── woodcut2.png │ │ │ ├── woodcut3.png │ │ │ ├── tutorial.game.en.js │ │ │ ├── tutorial.game.ru.js │ │ │ └── tutorial.game.de.js │ ├── js │ │ └── lang │ │ │ ├── ru.js │ │ │ ├── fr.js │ │ │ └── de.js │ └── css │ │ ├── undum-mobile.css │ │ └── undum.css ├── tutorial.de.html ├── tutorial.en.html └── tutorial.ru.html ├── test_webservers ├── serve.rb └── README.md ├── CREDITS ├── LICENSE └── README.md /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2>=2 2 | Markdown>=2.6 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | docs/out/doc 3 | ve 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /games/media/img/page_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/img/page_bg.jpg -------------------------------------------------------------------------------- /games/media/img/text_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/img/text_bg.jpg -------------------------------------------------------------------------------- /games/media/img/title_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/img/title_bg.jpg -------------------------------------------------------------------------------- /games/media/img/tools_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/img/tools_bg.jpg -------------------------------------------------------------------------------- /docs/out/media/img/book_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/docs/out/media/img/book_bg.jpg -------------------------------------------------------------------------------- /games/media/img/iphone/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/img/iphone/icon.png -------------------------------------------------------------------------------- /games/media/img/toolbar_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/img/toolbar_bg.jpg -------------------------------------------------------------------------------- /games/media/img/external_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/img/external_link.png -------------------------------------------------------------------------------- /games/media/img/iphone/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/img/iphone/splash.png -------------------------------------------------------------------------------- /games/media/games/tutorial/woodcut1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/games/tutorial/woodcut1.png -------------------------------------------------------------------------------- /games/media/games/tutorial/woodcut2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/games/tutorial/woodcut2.png -------------------------------------------------------------------------------- /games/media/games/tutorial/woodcut3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/undum/HEAD/games/media/games/tutorial/woodcut3.png -------------------------------------------------------------------------------- /test_webservers/serve.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'webrick' 4 | 5 | include WEBrick 6 | 7 | def start_webrick(config={}) 8 | config.update(:Port => 8000) 9 | puts "Server starting. Visit http://localhost:8000/ in your browser." 10 | server = HTTPServer.new(config) 11 | yield server if block_given? 12 | ['INT', 'TERM'].each { |signal| 13 | trap(signal){ server.shutdown} 14 | } 15 | server.start 16 | end 17 | 18 | start_webrick(:DocumentRoot => '../games') -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | This is a list of people who've helped and contributed to Undum. If 2 | your name should be here, but is missing, please add it and send me a 3 | pull request. Similarly if you want to change the form your name 4 | appears. Names are listed in alphabetical order. 5 | 6 | Alexander Yakovlev 7 | Andrew Plotkin 8 | Bruno Dias 9 | Dan Fabulich 10 | David Eyk 11 | Dmitry Eliseev 12 | Ian Millington 13 | Ivan Narozhny 14 | Joshua Grams 15 | Juhana Leinonen 16 | Michael Neal Tenuis 17 | Selene 18 | Zonnah 19 | 20 | An additional thanks to everyone who's used Undum in a game. 21 | -------------------------------------------------------------------------------- /test_webservers/README.md: -------------------------------------------------------------------------------- 1 | # Web Servers 2 | 3 | This directory contains minimal web-servers in common scripting languages. 4 | These are designed to expose the `games` directory at http://localhost:8000/ 5 | so that you can debug your creation in the browser more easily. This is 6 | needed because some browsers refuse to store data in localSettings when 7 | serving files from a file:/// url. Simply start one of these servers 8 | and use the http://localhost:8000/ address instead. 9 | 10 | For example, the ruby server (requires Ruby installed): 11 | 12 | ruby serve.rb 13 | 14 | Or on a unix-type machine; 15 | 16 | ./serve.rb 17 | 18 | Similarly for Python (again you'll need Python installed) 19 | 20 | chdir games 21 | python -m http.server 22 | 23 | You might already have Python installed, it is installed on Macs and Linux 24 | machines by default. 25 | 26 | If you have a minimal (< 20 lines) web server that can do this job in another 27 | language, please ping me and I'll add it here. I am particularly interested in 28 | servers that will run out of the box on Windows. I will not include .exe files, 29 | however. 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | ---- 3 | 4 | Copyright (c) 2009-2010 Ian Millington 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /games/media/js/lang/ru.js: -------------------------------------------------------------------------------- 1 | /* This a language file. It contains ALL the strings of the Undum core. And 2 | * so separates the translation from the implementation. If you want to 3 | * translate Undum to another language, use this file as a basis. - Oreolek 4 | */ 5 | undum.language["ru"] = { 6 | terrible: "ужасно", 7 | poor: "бедно", 8 | mediocre: "средненько", 9 | fair: "нормально", 10 | good: "хорошо", 11 | great: "прекрасно", 12 | superb: "великолепно", 13 | yes: "да", 14 | no: "нет", 15 | no_group_definition: "Невозможно найти определение группы {id}.", 16 | link_not_valid: "Ссылка '{link}' не выглядит правильной.", 17 | link_no_action: "Ссылка с ситуацией '.' должна иметь действие.", 18 | unknown_situation: "Вы не можете переместиться в неизвестную ситуацию {id}.", 19 | erase_message: "Это навсегда удалит вашего персонажа и немедленно вернёт вас к началу игры. Вы уверены?", 20 | no_current_situation: "Я не могу отобразить, потому что у нас нет текущей ситуации.", 21 | no_local_storage: "Локальное хранилище недоступно.", 22 | random_seed_error: "Вы должны задать верное случайное зерно.", 23 | random_error: "Проинициализируйте Random с непустым зерном перед использованием.", 24 | dice_string_error: "не могу интерпретировать вашу строку кубиков: '{string}'." 25 | }; 26 | -------------------------------------------------------------------------------- /games/media/js/lang/fr.js: -------------------------------------------------------------------------------- 1 | /* This a language file. It contains ALL the strings of the Undum core. And 2 | * so separates the translation from the implementation. If you want to 3 | * translate Undum to another language, use this file as a basis. - Oreolek 4 | */ 5 | /* French version by Christophe Gragnic (Grahack) 6 | */ 7 | undum.language["fr"] = { 8 | terrible: "archinul", 9 | poor: "nul", 10 | mediocre: "moyen", 11 | fair: "correct", 12 | good: "bon", 13 | great: "très bon", 14 | superb: "superbe", 15 | yes: "oui", 16 | no: "non", 17 | no_group_definition: "Aucun groupe défini par {id} n’a pu être trouvé.", 18 | link_not_valid: "Le lien '{link}' semble invalide.", 19 | link_no_action: "Un lien avec une situation '.' doit posséder une action.", 20 | unknown_situation: "Vous ne pouvez pas aller à une situation inconnue : {id}.", 21 | erase_message: "Attention ! Vous allez supprimer ce personnage et retourner au début du jeu. Êtes-vous sûr de vouloir cela ?", 22 | no_current_situation: "Il n’y a rien à afficher car il n’y a pas de situation courante.", 23 | no_local_storage: "Votre navigateur ne permet pas de sauvegarder la partie.", 24 | random_seed_error: "Vous devez fournir une graine valide.", 25 | random_error: "Veuillez initialiser le générateur pseudo-aléatoir avec une graine non vide.", 26 | dice_string_error: "Votre chaîne de dé n’est pas valide : '{string}'." 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /games/media/js/lang/de.js: -------------------------------------------------------------------------------- 1 | /* This a language file. It contains ALL the strings of the Undum core. And 2 | * so separates the translation from the implementation. If you want to 3 | * translate Undum to another language, use this file as a basis. - Oreolek 4 | */ 5 | /* German version by St.JohnLimbo 6 | */ 7 | undum.language["de"] = { 8 | terrible: "fürchterlich", 9 | poor: "schlecht", 10 | mediocre: "mittelmäßig", 11 | fair: "ordentlich", 12 | good: "gut", 13 | great: "großartig", 14 | superb: "superb", 15 | yes: "ja", 16 | no: "nein", 17 | no_group_definition: "Fuer {id} konnte keine Gruppendefinition gefunden werden.", 18 | link_not_valid: "Der Link '{link}' scheint nicht gueltig zu sein.", 19 | link_no_action: "Ein Link mit der Situation '.' muss eine Aktion beinhalten.", 20 | unknown_situation: "Es kann nicht zu einer unbekannten Situation: {id} gewechselt werden.", 21 | erase_message: "Achtung! Hierdurch wird der momentane Spielstand geloescht, und das Spiel beginnt von neuem! Bist du sicher?", 22 | no_current_situation: "Anzeige nicht moeglich, denn wir befinden uns momentan nicht in einer Situation.", 23 | no_local_storage: "Kein lokaler Speicherplatz verfuegbar.", 24 | random_seed_error: "Es muss ein gueltiger Anfangswert fuer den Zufallsgenerator angegeben werden.", 25 | random_error: "Der Zufallsgenerator muss mit einem nicht-leeren Anfangswert initialisiert werden.", 26 | dice_string_error: "Das Schema fuer den Wuerfelwurf wurde nicht verstanden: '{string}'." 27 | }; 28 | -------------------------------------------------------------------------------- /docs/src/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | # High Level Overview 4 | 5 | Undum games are based around three concepts: Situations, Actions and 6 | Qualities. 7 | 8 | ## Situations 9 | 10 | A situation is a chunk of code that is responsible for adding content 11 | to the screen and responding to user interaction. All user interaction 12 | is performed by clicking links in the content. 13 | 14 | Often a link will change the current situation, in which case another 15 | situation is loaded, can write to the screen, and can begin responding 16 | to user interaction. When a situation changes, all links that were 17 | previously available are removed, so that the player can't unfairly go 18 | back and try alternative options after committing to one. It is 19 | possible to override this behavior, see the section on 'Sticky', 20 | below. 21 | 22 | There is always exactly one active situation. These situations, and 23 | the links between them, form the structure of the game. 24 | 25 | ## Actions 26 | 27 | A situation may offer the player a series of actions to perform. These 28 | actions are internal to that situation and normally do not cause the 29 | situation to change. Actions may output more content, including new 30 | links for the user to select. 31 | 32 | ## Qualities 33 | 34 | Qualities represent the current state of the character. Internally 35 | they are all numeric values, able to take on any decimal value, 36 | positive or negative. They have no meaning to Undum - they are given 37 | meaning by your code as you perform calculations or make decisions 38 | based on their value. 39 | 40 | Qualities display themselves to the user through a formatting 41 | function, which can turn the number into any kind of indicator: a 42 | progress bar, a symbol, a word, an integer, and so on. So as far as 43 | the user is concerned, qualities can represent any kind of value. 44 | 45 | ## Other Concepts 46 | 47 | There are a handful of other elements to an Undum game, but they are 48 | very much in a supporting role. Quality groups allow you to display a 49 | set of qualities under a common heading; and character text is a short 50 | chunk of HTML that you can use to summarize a character's qualities, 51 | or to give hints. 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | # Home 2 | 3 | # Contents 4 | 5 | ## High level overview 6 | 7 | Describes the concepts that go into an Undum game, and how they 8 | interact. 9 | 10 | ## Creating an Undum Game 11 | 12 | Describes the files you need to create to make an Undum game, and what 13 | needs to go into them. 14 | 15 | ## Implicit Choices 16 | 17 | Version 2 of Undum includes a new framework for automatically 18 | generating choices for the player, based on information stored in 19 | situations. 20 | 21 | ## HTML API 22 | 23 | Undum games use HTML to output text and media, but has some 24 | constraints on the HTML you can use. Undum also provides built-in 25 | behavior tied to specific HTML classes that you can use in your 26 | output. In v.2, Undum also added support for defining many kinds of 27 | situation entirely in HTML, with no Javascript needed. 28 | 29 | ## Javascript API 30 | 31 | A complete breakdown of the API exposed to your code via 32 | Javascript. This details the methods that you call to output content, 33 | find random numbers and translate your game. 34 | 35 | ## Translation and Internationalization 36 | 37 | Describes Undum's core translation system, and how you can create 38 | games that support multiple languages. 39 | 40 | ## Loading and Saving 41 | 42 | Undum has an unusual way of supporting loading and saving of games, to 43 | support a wide range of browsers, sites and game styles. This document 44 | contains the technical details, and how this affects you as a game 45 | author. 46 | 47 | 48 | # Undum Changelog 49 | 50 | ## 2011-05-27 51 | 52 | - Added `System.writeBefore` to do out of order insertion. (credit: 53 | ekyrath) 54 | 55 | - Added an optional selector to `System.write` and 56 | `System.writeBefore` to support out of order insertion. (credit: 57 | ekyrath) 58 | 59 | - Removed the use of `__defineGetter__`, so that the core code now 60 | works on IE7 (thanks for the bug report juhana and bloomengine). 61 | 62 | ## 2011-08-18 63 | 64 | - Added support for functions in the `content` and `header` of a 65 | `SimpleSituation` (credit: David Eyk). 66 | 67 | ## 2013-09-27 68 | 69 | - Updated to v.2, including support for implicit choices and HTML-defined situations. -------------------------------------------------------------------------------- /games/media/css/undum-mobile.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-size: 18.5px; 4 | line-height: 1.5em; 5 | -webkit-text-size-adjust: none; 6 | } 7 | 8 | /* Structure */ 9 | #page { 10 | padding-top: 1.9em; 11 | margin: 0.5em; 12 | } 13 | #mid_panel { 14 | margin: 0; 15 | } 16 | 17 | /* Title */ 18 | #title { 19 | margin-top: -1.5em; 20 | padding: 1.0em 0.5em; 21 | } 22 | #title .label { 23 | font-size: 0.65em; 24 | max-width: 25em; 25 | padding: 2.0em; 26 | } 27 | 28 | /* Side panels */ 29 | #tools_wrapper { 30 | position: static; 31 | } 32 | .tools { 33 | background-image: url("../img/text_bg.jpg"); 34 | position: relative; 35 | width: auto; 36 | } 37 | #character_panel { 38 | display: none; 39 | } 40 | #info_panel { 41 | display: none; 42 | } 43 | #tools_wrapper { 44 | display: block; 45 | } 46 | 47 | /* Main content */ 48 | #content_wrapper { 49 | width: auto; 50 | padding: 2.0em; 51 | } 52 | #content { 53 | font-size: 16px; 54 | line-height: 1.5em; 55 | } 56 | 57 | /* Toolbar and menu */ 58 | #toolbar { 59 | position: fixed; 60 | z-index: 300; 61 | left: 0; 62 | right: 0; 63 | top: 0; 64 | background: transparent url("../img/toolbar_bg.jpg") repeat-x top left; 65 | height: 36px; 66 | padding: 8px; 67 | overflow: hidden; 68 | -webkit-box-shadow: 0 0 16px rgba(0,0,0,0.75); 69 | box-shadow: 0 0 16px rgba(0,0,0,0.75); 70 | } 71 | #toolbar h1 { 72 | float: left; 73 | font-weight: normal; 74 | font-size: 22px; 75 | margin: 8px 0 0 0; 76 | padding: 0 10px; 77 | color: #fc6; 78 | text-shadow: 0 -1px 0 rgba(0,0,0,0.4); 79 | } 80 | #toolbar .nav { 81 | float: right; 82 | margin: 0; 83 | } 84 | #toolbar .nav a { 85 | font-size: 16px; 86 | line-height: 20px; 87 | color: white; 88 | padding: 4px 16px; 89 | float: right; 90 | text-decoration: none;\ 91 | text-shadow: 0 1px 0 rgba(0,0,0,0.4); 92 | -webkit-border-radius: 6px; 93 | background-image: -webkit-gradient(linear, left top, left bottom, 94 | from(#C00), color-stop(0.45, #a00), 95 | color-stop(0.55, #900), to(#900)); 96 | border: 2px solid #600; 97 | } 98 | #menu { 99 | display: none; 100 | position: fixed; 101 | top: 52px; 102 | left: 0; 103 | right: 0; 104 | font-size: 16px; 105 | background-image: url("../img/tools_bg.jpg"); 106 | z-index: 200; 107 | list-style-type: none; 108 | padding: 10px 0 0 0; 109 | margin: 0; 110 | opacity: 0.95; 111 | -webkit-box-shadow: 0 0 16px rgba(0,0,0,0.75); 112 | box-shadow: 0 0 16px rgba(0,0,0,0.75); 113 | } 114 | #menu li { 115 | border-bottom: 1px solid rgba(0,0,0,0.25); 116 | } 117 | #menu li:last-child { 118 | border-bottom: none; 119 | } 120 | #menu a { 121 | display: block; 122 | padding: 10px 20px; 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /docs/src/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 119 | 120 | 121 |
122 |
123 |

Undum Documentation: {{title}}

124 |
125 |
126 | 130 |

Contents

131 | 136 |
137 |
138 | {{ content }} 139 |
140 | 147 |
148 | 149 | 150 | -------------------------------------------------------------------------------- /docs/src/files.md: -------------------------------------------------------------------------------- 1 | # Loading and Saving 2 | 3 | # Loading and Saving 4 | 5 | There is no API for you to manually access loading and saving 6 | functionality. The load and save functions are triggered by the 7 | buttons on the user interface. But the way loading and saving work 8 | does have a big impact on the way you write your code. 9 | 10 | Undum makes a big feature of the fact that you can scroll back through 11 | the story you've helped create. To save your character's progress you 12 | would have to save not only the current situation and the character's 13 | qualities, but also all the text that has been generated up to that 14 | point. This would not be a problem in a downloaded game, because your 15 | hard-drive is plenty big enough. Unfortunately in a browser game, 16 | there is a limit on how much data you can store. Worse, this data 17 | limit is per-domain. So if you hosted 10 Undum games on a server, the 18 | saved games from all ten would have to fit into the limited 19 | space. This makes it impractical to save the complete text history. 20 | 21 | Instead Undum saves all the actions you've taken as a player. It 22 | doesn't save the character's qualities or any other information, just 23 | the actions. It expects to be able to rebuild the complete text, and 24 | the complete current state of the character, by playing back these 25 | actions when the file is loaded. We call this determinism, because the 26 | current state of the character and the content needs to be determined 27 | from just the actions they take. 28 | 29 | # Writing with Saving / Loading in Mind 30 | 31 | This means that you must take care to write all your code in a way 32 | that will generate *exactly* the same results if the game was played 33 | again in the same way. 34 | 35 | There are two major limitations to this, firstly it means you can't 36 | use random events, and secondly you can't use timed events. Both of 37 | these are such serious limitations that Undum provides ways around 38 | them. 39 | 40 | If you do not follow these restrictions, then it is likely that saved 41 | games will not load correctly. A player may save their game at one 42 | point and find most of their progress wiped out when they load it 43 | again. 44 | 45 | ## Random Numbers 46 | 47 | Undum provides your code with a random number generator in 48 | `System.rng`, which is an object with the prototype `Random`. This 49 | random number generator works with the loading and saving code to make 50 | sure that, when you load a saved game, the exact same random number 51 | requests will produce the exact same result. This means you get all 52 | the benefits of randomness (i.e. two separate games may have different 53 | results for the same action), but we can always replay a game 54 | exactly. Dealing with random actions is a difficult issue in testing 55 | any interactive fiction game. This approach solves that problem. You 56 | should, therefore, *never* use Javascript's built-in random number 57 | generate (`Math.random`), only ever use the one provided by Undum. 58 | 59 | ## Timing 60 | 61 | Finally, Undum also provides your code timing information in 62 | `System.time`, which is the number of seconds (and fractional seconds) 63 | elapsed since the player began the game. You can use this timing 64 | information to implement puzzles in your game (such as asking the 65 | player to complete a series of tasks in a specified amount of 66 | time). This timing system coordinates with the loading and saving 67 | system to make sure that, when you save and load the game, the timing 68 | picks up where it left off. Feel free to make decisions in your code 69 | based on the current value of `System.time`, but never use 70 | Javascript's `Date` object. 71 | 72 | ## Detecting Replay When Loading 73 | 74 | If your game has elements that should not be triggered while Undum is 75 | replaying a saved game, for example sound effects or popup notifications, 76 | you can use `undum.isInteractive()` to test whether the game is being 77 | played normally (returns `true`) or being loaded from a save game 78 | (returns `false`). 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/make.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | try: 4 | import markdown 5 | import jinja2 6 | except ImportError: 7 | print("You must have the Markdown and Jinja2 python libraries installed.") 8 | sys.exit(-1) 9 | import os.path 10 | from html.parser import HTMLParser 11 | 12 | BASE_DIR = os.path.abspath(os.path.dirname(__file__)) 13 | SRC_DIR = os.path.join(BASE_DIR, 'src') 14 | OUT_DIR = os.path.join(BASE_DIR, 'out', 'doc') 15 | 16 | class TOCParser(HTMLParser): 17 | def __init__(self, tags=['h1', 'h2', 'h3']): 18 | HTMLParser.__init__(self) 19 | self.tags = tags 20 | self.toc = [] 21 | self.output = [] 22 | self.accum = None 23 | self.header_index = 1 24 | 25 | self.online = True # Are we outputting the content we find? 26 | self.title = None 27 | 28 | def _convert_attr(self, attr): 29 | return "".join([ 30 | ' %s="%s"' % (key, value) 31 | for key, value in attr 32 | ]) 33 | 34 | def handle_starttag(self, tag, attr): 35 | if tag in self.tags: 36 | # Start tracking content 37 | assert self.accum is None 38 | self.accum = [] 39 | 40 | # Check if we're the first top level (interpret that as 41 | # the page title). 42 | level = self.tags.index(tag) 43 | if level == 0 and self.title is None: 44 | self.online = False 45 | return 46 | 47 | # Add a reference to this header 48 | id = 'h_%d' % self.header_index 49 | attr.append(('id', id)) 50 | 51 | # Link back to the top, unless we're already there. 52 | if self.header_index > 1: 53 | self.output.append('
') 54 | self.output.append( 55 | '
'+ 56 | '▲
' 57 | ) 58 | 59 | # Accumulate the transformed version 60 | content = "<%s%s>" % (tag, self._convert_attr(attr)) 61 | self.output.append(content) 62 | 63 | def handle_startendtag(self, tag, attr): 64 | if self.online: 65 | content = "<%s%s>" % (tag, self._convert_attr(attr)) 66 | self.output.append(content) 67 | 68 | def handle_entityref(self, name): 69 | if self.online: 70 | self.output.append("&%s;" % name) 71 | 72 | def handle_charref(self, name): 73 | if self.online: 74 | self.output.append("&#%s;" % name) 75 | 76 | def handle_data(self, data): 77 | if self.accum is not None: 78 | self.accum.append(data) 79 | if self.online: 80 | self.output.append(data) 81 | 82 | def handle_endtag(self, tag): 83 | if tag in self.tags: 84 | content = "".join(self.accum) 85 | level = self.tags.index(tag) 86 | self.accum = None 87 | 88 | if level == 0 and self.title is None: 89 | self.online = True 90 | self.title = content 91 | return 92 | 93 | self.toc.append((level, '#h_%d' % self.header_index, content)) 94 | self.output.append("" % tag) 95 | if self.header_index > 1: 96 | self.output.append('
') 97 | self.header_index += 1 98 | else: 99 | self.output.append("" % tag) 100 | 101 | def get_html(self): 102 | return "".join(self.output) 103 | 104 | def build(): 105 | # Make sure we have the output directory 106 | if not os.path.exists(OUT_DIR): 107 | os.makedirs(OUT_DIR) 108 | 109 | # Find the template we'll use 110 | env = jinja2.Environment(loader=jinja2.FileSystemLoader(SRC_DIR)) 111 | template = env.get_template('base.html') 112 | 113 | # Convert all our markdown source files. 114 | for filename in os.listdir(SRC_DIR): 115 | if filename.endswith('.md'): 116 | # Load the markdown, convert it 117 | md = open(os.path.join(SRC_DIR, filename),'r').read() 118 | html = markdown.markdown(md) 119 | 120 | # Parse the generated HTML to extract the TOC 121 | parser = TOCParser() 122 | parser.feed(html) 123 | parser.close() 124 | 125 | # Create the final result 126 | result = template.render( 127 | title = parser.title, 128 | content = parser.get_html(), 129 | toc = parser.toc 130 | ) 131 | 132 | # Write it out 133 | destination = os.path.join(OUT_DIR, "%s.html" % filename[:-3]) 134 | open(destination, 'w').write(result) 135 | print("Built: %s" % destination) 136 | 137 | if __name__ == '__main__': 138 | build() 139 | -------------------------------------------------------------------------------- /docs/src/i18n.md: -------------------------------------------------------------------------------- 1 | 2 | # Translation and Internationalization 3 | 4 | Undum provides support for translations and internationalization. 5 | In particular, if you feel you want to translate Undum, then a lot 6 | of work has already been done for you. I am very grateful to Oreolek 7 | for this assistance. He translated the Russian version within days 8 | of the code being released, and advised on the tools that would 9 | make translation easier. 10 | 11 | To write a game in another language, you need only to write the game 12 | content in that language. The identifiers you use in the game (to 13 | represent situations, actions, qualities and quality groups) must use 14 | only unaccented lower case latin letters and numbers, but the text you 15 | generate can contain any content you choose. Including right to left 16 | content or ideographs. 17 | 18 | Undum itself has a small number of error messages and pieces of text 19 | that it uses. These include the default names for the 20 | FudgeAdjectivesQuality values. These strings are all found at the end 21 | of the `undum.js` file. They can be overridden. Simply define a new 22 | language file for your language (e.g. `lang/gk.js`) and override the 23 | appropriate strings. In your HTML file, after importing `undum.js`, 24 | import you strings file. For example, the end of the Russian 25 | translation of the tutorial (`tutorial.ru.html`) has: 26 | 27 | 28 | 29 | 30 | These translation strings are given as an object mapping the string 31 | name to the translated strings. This object is given as part of the 32 | `undum.translation` object, with a property name equal to the language 33 | name. So, for example, the UK English translation might begin: 34 | 35 | undum.lanuage["en-UK"] = { 36 | no_group_definition: "Couldn't find a group definition for {id}.", 37 | 38 | Within the translation strings, data to be incorporated later is given 39 | in curly braces. 40 | 41 | 42 | ## Language Codes 43 | 44 | Undum uses a simplified version of the IETF standards for language 45 | code. For our purposes this consists of three parts, only the first of 46 | which is required: `language-Script-REGION` where `language` is a two 47 | letter lower-case ISO language code, `Script` is a four letter 48 | title-case script identifier, and `REGION` is a two letter country or 49 | region code, all capitalized. The script is omitted when it is the 50 | default script for a language and locale. You would specify a script 51 | if you were using romaji (Latin letters) to write Japanese, but not if 52 | you were writing it in Kanji and kana. 53 | 54 | The major virtue of this standard is that it allows fall through when 55 | a translation is not available. For example a translation into 56 | Brazillian Portuguese `pt-BR` might be different to one into Angolan 57 | Portuguese `pt-AO`, but there may be some strings they have in 58 | common. This allows a translator to create a base file for just plain 59 | Portuguese `pt`, then each country's dialect can define its own file 60 | that just overrides a few of the strings. 61 | 62 | 63 | ## Filename Conventions 64 | 65 | It is only a convention, but for all files that occur in language 66 | specific versions, I have used the filename convention of placing the 67 | language code before the extension, e.g. `filename.lang.extension`. The 68 | game file is similar `filename.game.lang.js`. You are free to use any 69 | format you choose, of course, but if you want to contribute back to 70 | the trunk, please follow this convention, to save having to rename 71 | things and connect up the links later. 72 | 73 | 74 | ## API 75 | 76 | The translation system provides a simple API based on the `Globalite` 77 | package of Nicolas Merouze (the implementation is unique to Undum). It 78 | adds a `l()` method to the Javascript `String` prototype. This method 79 | has the signature `l(data)`, where data can be any object mapping 80 | strings to other strings. 81 | 82 | The method attempts to figure out what the current language is by 83 | looking at the `lang` attribute of the top level HTML tag (you'll 84 | notice in the tutorial games this is defined in all cases, you should 85 | do the same). It then tries to find a matching object containing 86 | translations in `undum.language`. If it finds such an object, then it 87 | looks up the original string in that object to find a translation. It 88 | then merges any data passed into the `l` method into the string, 89 | before returning the result. 90 | 91 | The translation look-up honors the IETF rules for language fallback, 92 | so (continuing the previous example) if your game file is in Brazilian 93 | Portuguese `pt-BR`, and a translation isn't found, then generic 94 | Portuguese translation `pt` is also checked. Finally, if no valid 95 | translation is found, then the default version is used. Since Undum 96 | was written in English, this default version is the English 97 | version. This is purely by my convenience, and isn't part of the IETF 98 | spec! 99 | -------------------------------------------------------------------------------- /docs/out/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Undum – 5 | A client side framework for hypertext interactive fiction games 6 | 7 | 73 | 74 | 75 |
76 |
77 |

Undum

78 |

79 | A client-side framework for
80 | hypertext interactive fiction games. 81 |

82 |

Current Version (v. 2)

83 |

84 | Tutorial Game
85 | Русский

86 | 87 | Reference Documentation
88 | Source Code 89 |

90 |

Previous Version (v. 1)

91 |

92 | Tutorial game in: 93 | Deutsch, 94 |

95 | 100 |
101 | Pro captu lectoris habent sua fata libelli. 103 |
104 | 107 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Undum 2 | 3 | Undum is a game framework for building a sophisticated form of 4 | hypertext interactive fiction. 5 | 6 | If that means nothing to you, then let's go back a few steps. Remember 7 | those Choose Your Own Adventure, or Fighting Fantasy books? Where you 8 | got to choose what your character does next? Well if you think of that 9 | in a web-page you have hypertext interactive fiction, or HIF. Instead 10 | of turning to a particular page, you click a link, and the next bit of 11 | content appears. 12 | 13 | The problem is that those kinds of games are pretty limited. Every 14 | time the player does something, the story could go in different 15 | directions. So the author has to either write masses of branches, or 16 | else the decisions you make as a player have to be relatively short 17 | lived. If you played CYOA books you'll know that the wrong move either 18 | ended the story pretty quickly, or else it didn't really matter what 19 | you did because you'd end up at the same place. 20 | 21 | To beat this limitation, Undum allows you to make the output 22 | dynamic. It allows you to keep track of what has happened to the 23 | character (any kinds of data, in fact), and to then change the text 24 | that gets output accordingly. Effectively it is like writing a CYOA 25 | page that is different each time you read it. This allows for far 26 | richer and more rewarding game design. 27 | 28 | Undum is a pure client client-side library. It consists of a HTML file 29 | and three Javascript files. The HTML file uses a nice bit of styling, 30 | so there's a bunch of CSS and images in the default package too, but 31 | that can be replaced if you want. To create your own game, you edit 32 | the HTML file a little (mainly just changing the title and author), 33 | and edit one of the Javascript files. 34 | 35 | Because the game is written in Javascript, you get the full power of a 36 | dynamic and efficient programming language. This isn't a CYOA 37 | scripting system with limited functionality. You can take control of 38 | anything you want. Or you can just keep things simple using a bunch of 39 | simple functions provided by Undum. 40 | 41 | 42 | ## Compatibility 43 | 44 | Undum is designed for HTML5 and CSS3 browsers. It has been tested on 45 | Firefox 3.6, Chrome 5, and Safari 5. Older browsers may work okay too, 46 | but some of the animation won't work, the styles may render poorly, 47 | and saving and loading of games is unlikely to work. Anyone who wants 48 | to hack around with it and make it work more widely is welcome. Just 49 | fork this project on Github. 50 | 51 | The local storage system on some browsers does not work when loading a 52 | page from your hard drive. To test your game when developing it, you 53 | may want to start up a simple local webserver. I have found that 54 | Chrome seems to reliably provide local storage for local 55 | development. It also has excellent Javascript debugging tools. 56 | 57 | 58 | ## Getting Started 59 | 60 | 1. Download Undum. Use the 'download zip' link in the right column of 61 | this page. 62 | 63 | 2. Unzip Undum somewhere on your hard-drive. 64 | 65 | 3. Open games/tutorial.html in your browser, and play through the tutorial. 66 | 67 | 4. Copy games/tutorial.html to a file that reflects your game name. 68 | 69 | 5. Edit your HTML file and add the title, author and description of 70 | the game you want to write. At the bottom of the file change the 71 | name of `tutorial.game.js` to something else (by convention 72 | *your-game-name*`.game.js`. 73 | 74 | 6. Copy `tutorial.game.js` to the file name you chose in the last 75 | step. Open it and begin creating your game. 76 | 77 | 78 | Reference documentation, including full API details, is at 79 | [http://idmillington.github.io/undum/](http://idmillington.github.io/undum/), 80 | and is also included in the repository. 81 | 82 | The source code for all the files is also heavily commented, so if you 83 | get stuck, go in and read it. 84 | 85 | 86 | ## Deploying 87 | 88 | To deploy your game, just upload your HTML file and the `media` folder 89 | to your webserver. You can serve several games with the same look and 90 | feel from the same directory. You need a different HTML file for each 91 | game, and each one should load the correct `.game.js` file at the 92 | end. Add any media you need for your game (images, audio, video), and 93 | the remaining files will be reused. 94 | 95 | For example, if you had 3 games: `episode1`, `episode2`, and 96 | `christmas-special`. You'd have a directory structure: 97 | 98 | episode1.html 99 | episode2.html 100 | christmas-special.html 101 | media/ 102 | css/ ... 103 | img/ ... 104 | js/ 105 | jquery-1.4.2.min.js 106 | undum.js 107 | games/ 108 | episode1/ 109 | episode1.game.js 110 | ... media for episode 1 ... 111 | episode2/ 112 | episode2.game.js 113 | ... media for episode 1 ... 114 | christmas-special/ 115 | christmas-special.game.js 116 | ... media for christmas special ... 117 | 118 | This assumes you use the same directory lay out that I do. You are 119 | welcome to change things around, of course, as long as you work and 120 | change the references. 121 | 122 | 123 | ## Undum 124 | 125 | The name `undum` came from a little project that preceded this code 126 | base. In 2008 I put together a simple browser based game. It was 127 | narrative, but used the grind-based mechanics of games such as 128 | Farmville and Mafia Wars. Because of the grinding, I called it 129 | Carborundum, which I found I couldn't type at speed, so it became 130 | Undum. The code has changed out of all recognition since them, as the 131 | grind-based game moved to Flash. But the name stuck for the Javascript 132 | framework. 133 | 134 | 135 | ## License 136 | 137 | The code, documentation, styles, design and images are all distributed 138 | under the MIT license. This permits you to modify and use them, even 139 | for commercial use. A copy of the MIT license is found in the LICENSE 140 | file. 141 | -------------------------------------------------------------------------------- /games/media/css/undum.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #170804 url("../img/page_bg.jpg") repeat-x left top; 3 | font-family: Palatino, Times, "Times New Roman", serif; 4 | font-size: 18px; 5 | line-height: 1.6em; 6 | background-attachment: fixed; 7 | overflow-y: scroll; 8 | } 9 | 10 | /* Basic structure */ 11 | #page { 12 | margin: 0 auto; 13 | position: relative; 14 | } 15 | #mid_panel { 16 | margin: 0 10.5em; 17 | } 18 | 19 | /* The title block */ 20 | #title, #title .label, #content, .tools { 21 | border-radius: 2px; 22 | } 23 | #title { 24 | max-width: 28em; 25 | background: #e6e6c6 url("../img/title_bg.jpg") repeat -1.1em -1.1em; 26 | margin: 2.2em auto 1.1em auto; 27 | padding: 1.7em; 28 | border: 1.1em solid rgba(0,0,0,0.25); 29 | cursor: pointer; /* Until we click to start. */ 30 | } 31 | #title .label { 32 | overflow: hidden; 33 | background: #e6e6c6 url("../img/text_bg.jpg") repeat left top; 34 | padding: 2.0em; 35 | margin: auto; 36 | max-width: 18em; 37 | -webkit-box-shadow: 0 0 4px rgba(0,0,0,0.4); 38 | box-shadow: 0 0 4px rgba(0,0,0,0.4); 39 | position: relative; 40 | } 41 | #title h1 { 42 | font-size: 1.6em; 43 | line-height: 1.4em; 44 | letter-spacing: 0.2em; 45 | font-weight: normal; 46 | padding-bottom: 1.1em; 47 | border-bottom: 1px solid #321; 48 | } 49 | #title h2 { 50 | font-size: 1.2em; 51 | font-weight: normal; 52 | text-align: center; 53 | margin: 1.1em 0 0 0; 54 | } 55 | #title h3 { 56 | font-size: 1.0em; 57 | font-weight: normal; 58 | text-align: center; 59 | margin: 1.1em 0 0 0; 60 | } 61 | #title h1, #title h2, #title h3 { 62 | color: rgba(33,17,0,0.9); 63 | text-shadow: rgba(255,255,255,0.5) 2px 2px 2px, 64 | rgba(0,0,0,0.1) -1px -1px 2px; 65 | } 66 | #title h1 span.fancy { 67 | font-size: 2.5em; 68 | line-height: 0; 69 | font-family: Tangerine, Palatino, Times, "Times New Roman", serif; 70 | font-style: italic; 71 | margin: 0 -0.2em; 72 | } 73 | #title .click_message { 74 | display: none; 75 | left: 0; 76 | right: 0; 77 | bottom: 0; 78 | position: absolute; 79 | font-size: 0.9em; 80 | font-style: italic; 81 | text-align: center; 82 | color: #987; 83 | } 84 | #title .noscript_message { 85 | left: 0; 86 | right: 0; 87 | bottom: 0; 88 | position: absolute; 89 | font-size: 0.9em; 90 | font-style: italic; 91 | text-align: center; 92 | color: #943; 93 | } 94 | 95 | /* Main content */ 96 | #content_wrapper { 97 | max-width: 28em; 98 | position: relative; 99 | background: #e6e6c6 url("../img/text_bg.jpg") repeat left top; 100 | margin: 0.6em auto 1.1em auto; 101 | padding: 2.8em; 102 | display: none; /* Shown by Javascript */ 103 | overflow: auto; 104 | } 105 | span.drop + p { 106 | text-indent: -0.4em; 107 | } 108 | p { 109 | margin: 0; 110 | -webkit-transition: text-indent 0.25s ease; 111 | transition: text-indent 0.25s ease; 112 | } 113 | hr { 114 | border: none; 115 | background-color: rgba(0,0,0,0.25); 116 | margin: -1px 0 -1px -2.8em; 117 | width: 1.1em; 118 | height: 2px; 119 | } 120 | p + p, p + img + p, p + hr + p { 121 | text-indent: 1.6em; 122 | } 123 | #content h1 + p:first-line, 124 | #content h1 + img + p:first-line { 125 | font-weight: bold; 126 | color: rgba(0,0,0,0.85); 127 | } 128 | #content h1 + p:first-letter, 129 | #content h1 + img + p:first-letter { 130 | position: relative; 131 | padding-top: 0.1em; 132 | display: block; 133 | float: left; 134 | font-weight: normal; 135 | font-size: 3.2em; 136 | line-height: 0.8em; 137 | color: #210; 138 | } 139 | ul { 140 | margin: 0; 141 | padding: 0 0 0 1em; 142 | } 143 | ul.options { 144 | border: 2px solid #876; 145 | padding: 0; 146 | margin-top: 0.5em; 147 | margin-bottom: 0.7em; 148 | list-style-type: none; 149 | border-radius: 4px; 150 | } 151 | ul.options li { 152 | border-bottom: 1px solid #876; 153 | padding: 0.5em; 154 | } 155 | ul.options li:hover { 156 | background-color: rgba(153,136,119,0.2); 157 | cursor: pointer; 158 | } 159 | ul.options li:last-child { 160 | border-bottom: none; 161 | } 162 | 163 | h1 { 164 | font-size: 1.0em; 165 | text-transform: uppercase; 166 | letter-spacing: 2px; 167 | margin: 2.3em 0 1.1em 0; 168 | color: #210; 169 | text-align: center; 170 | } 171 | h1:first-child { 172 | margin-top: 0; 173 | } 174 | a { 175 | color: #900; 176 | text-decoration: none; 177 | border-bottom: 1px solid transparent; 178 | } 179 | a.raw { 180 | padding-right: 14px; 181 | background: transparent url("../img/external_link.png") no-repeat right 4px; 182 | } 183 | a:hover { 184 | border-bottom: 1px dotted #900; 185 | } 186 | 187 | img.float_right { 188 | float: right; 189 | margin: 1.1em 0 1.1em 1.1em; 190 | } 191 | img.float_left { 192 | float: left; 193 | margin: 1.1em 1.1em 1.1em 0; 194 | } 195 | 196 | #toolbar { 197 | display: none; 198 | } 199 | 200 | #tools_wrapper { 201 | position: fixed; 202 | width: 100%; 203 | max-width: 56em; 204 | top: 2.2em; 205 | left: 0; 206 | right: 0; 207 | margin: 0 auto; 208 | display: none; /* Shown by Javascript */ 209 | } 210 | .tools { 211 | padding: 0.6em; 212 | width: 8.9em; 213 | background: #cec3ae url("../img/tools_bg.jpg") repeat left top; 214 | position: absolute; 215 | } 216 | .tools p { 217 | font-size: 0.95em; 218 | line-height: 1.5em; 219 | } 220 | .tools.left { 221 | left: 0.4em; 222 | } 223 | .tools.right { 224 | right: 0.4em; 225 | } 226 | 227 | .tools h1 { 228 | font-size: 1.0em; 229 | font-weight: normal; 230 | border-bottom: 1px solid #321; 231 | margin-bottom: 0.6em; 232 | } 233 | .buttons { 234 | padding-top: 0.6em; 235 | margin-top: 0.6em; 236 | border-top: 1px solid #321; 237 | text-align: center; 238 | } 239 | .buttons button { 240 | font-size: 0.8em; 241 | background: #876; 242 | color: #e6e6c6; 243 | border: none; 244 | padding: 0.3em 1.0em; 245 | cursor: pointer; 246 | border-radius: 4px; 247 | } 248 | .buttons button:hover { 249 | background: #987; 250 | } 251 | .buttons button + button { 252 | margin-left: 0.3em; 253 | } 254 | .buttons button[disabled], .buttons button[disabled]:hover { 255 | background: #ba9; 256 | color: #dcb; 257 | cursor: default; 258 | } 259 | 260 | #legal { 261 | max-width: 33em; 262 | color: #654; 263 | margin: 1em auto 0 auto; 264 | padding-bottom: 2.2em; 265 | display: none; /* Shown by Javascript */ 266 | } 267 | #legal p { 268 | font-size: 0.7em; 269 | line-height: 1.3em; 270 | margin-bottom: 0.5em; 271 | } 272 | #legal p + p { 273 | text-indent: 0; 274 | } 275 | 276 | #character { 277 | font-size: 1.0em; 278 | line-height: 1.4em; 279 | } 280 | #qualities .quality, #character_text { 281 | position: relative; 282 | clear: both; 283 | overflow: hidden; 284 | margin: 0 -0.25em; 285 | padding: 0 0.25em; 286 | } 287 | #character_text { 288 | margin-bottom: 0.6em; 289 | } 290 | #character_text_content { 291 | position: relative; 292 | z-index: 100; 293 | } 294 | #qualities span { 295 | position: relative; 296 | z-index: 100; 297 | } 298 | #qualities span.name { 299 | float: left; 300 | } 301 | #qualities span.value { 302 | float: right; 303 | } 304 | .highlight { 305 | background: rgba(255, 255, 0, 0.75); 306 | position: absolute; 307 | left: -4px; 308 | right: -4px; 309 | top: 0; 310 | bottom: 0; 311 | } 312 | #qualities h2 { 313 | margin: 0.5em 0 0.25em 0; 314 | font-size: 1.0em; 315 | border-bottom: 1px solid #321; 316 | } 317 | 318 | .progress_bar { 319 | position: relative; 320 | overflow: hidden; 321 | margin: 0.6em 0; 322 | } 323 | .progress_bar_track { 324 | z-index: 100; 325 | background: rgba(255,255,255,0.25); 326 | border: 2px solid #876; 327 | height: 0.75em; 328 | width: 27.7em; 329 | clear: both; 330 | border-radius: 4px; 331 | } 332 | .progress_bar_color { 333 | background: #987; 334 | width: 0; 335 | height: 0.75em; 336 | } 337 | .progress_bar span { 338 | z-index: 100; 339 | } 340 | .progress_bar .name { 341 | font-weight: bold; 342 | } 343 | .progress_bar .value { 344 | float: right; 345 | } 346 | .progress_bar .left_label { 347 | float: left; 348 | } 349 | .progress_bar .right_label { 350 | float: right; 351 | } 352 | #content_library, #ui_library { 353 | display: none; 354 | } 355 | #menu { 356 | display: none; 357 | } 358 | -------------------------------------------------------------------------------- /docs/src/implicit.md: -------------------------------------------------------------------------------- 1 | # New Choice System 2 | 3 | # New Choice System 4 | 5 | *New in Version 2* 6 | 7 | In version one of Undum, all links from one situation to another had 8 | to be created manually using HTML. If you wanted a set of choices to 9 | offer, you had to create the links yourself, put them in the 10 | appropriate HTML container. If you wanted those choices to depend on 11 | the current state of the game (say, a choice to look behind a secret 12 | panel that was only visible when the panel was discovered), you had to 13 | code that logic yourself in HTML. This meant that, in practice, most 14 | Undum games were simpler, and rarely used branches that depended on 15 | context. 16 | 17 | In version 2 both of these issues were addressed: the ability to 18 | quickly build the links to new situations in the standard HTML format 19 | (using the `System.writeChoices` method), and the ability to generate 20 | a list of situations that are available, given the current state of 21 | the game. 22 | 23 | # Generating Choice HTML 24 | 25 | Thew new `System.writeChoices` method allows you to give a list of 26 | situation id's and have Undum generate a standard looking block of 27 | choices. 28 | 29 | It does this by asking each choice how it prefers to be displayed, by 30 | calling its `optionText` method. This allows situtions to change how 31 | they are displayed depending on the current state of the character. 32 | 33 | Also called is the situations `canChoose` method. This will normally 34 | return true, but if it returns false, the option will still be 35 | displayed, but not as a link, and will not be clickable. This allows 36 | you to show the player that an option would be available, if they did 37 | something else first, such as increase a quality. 38 | 39 | On its own `System.writeChoices` is still mostly manual: it finds the 40 | link text for you, and builds the HTML, but you still have to give it 41 | a set of situation ids that you want for your choices. 42 | 43 | # Generating Choices 44 | 45 | Undum now also provides the `System.getSituationIdChoices` method 46 | which automatically compiles a list of situation ids, which can then 47 | be passed to `System.writeChoices` for display. This method is 48 | powerful and complex, so we'll explore its use in increasing depth. 49 | 50 | ## Generating Choices by Tags 51 | 52 | Situations now can have one or more tags associated with them. You can 53 | ask `System.getSituationIdChoices` to return the ids of any situtions 54 | that match a tag. This allows you to easily build decisions that you 55 | can extend later. You might have a 'chapter' tag, and you mark each 56 | situation which begins a chapter using this tag, you can then do. 57 | 58 | system.getSituationIdChoices(['#chapter']) 59 | 60 | to return all chapter choices. 61 | 62 | The way tags are processed tries to be intelligent. You can match on 63 | more than one tag, and any situation matching either tag will be 64 | returned, but each situation will be returned no more than once. You 65 | can even mix tags and explicit situation ids: 66 | 67 | system.getSituationIdChoices(['#chapter', 'introduction', '#endmatter']) 68 | 69 | When you only need to pass one tag to `System.getSituationIdChoices` 70 | you can do so without using a list, so the first example above could 71 | be equally written: 72 | 73 | system.getSituationIdChoices('#chapter') 74 | 75 | 76 | ## Ordering Choices 77 | 78 | Choices returned by `System.getSituationIdChoices` will be ordered 79 | based on a new `displayOrder` numeric property of situations. This 80 | allows you to make sure situations appear in a logical order, 81 | regardless of whether they were selected by id or by tag. 82 | 83 | 84 | ## Conditional Appearance 85 | 86 | So far, any matched situation will always be featured in the list of 87 | choices. A situation can't be visible some times and not others, 88 | depending on the current state of the game. (As we saw with the 89 | `canChoose` method, we can have it be clickable only in some states). 90 | 91 | To allow situations to be totally absent in some cases, there is a new 92 | `canView` method on situations. This is only used by 93 | `system.getSituationIdChoices`, and allows a situation to opt out of 94 | being included, if its conditions for appearance are not met. This 95 | allows us to implement the secret panel from the introduction. 96 | 97 | We could have a 'go-to-the-basement' situation with tag 98 | 'from-the-hallway' available always, while 'go-to-the-secret-room' has 99 | the tag 'from-the-hallway', but a canView method: 100 | 101 | secretRoomSituation.canView = function(character, system, host) { 102 | return character.sandbox.has_found_secret_panel; 103 | } 104 | 105 | We can then call `system.getSituationIdChoices(['#from-the-hallway'])` 106 | and have the correct choices displayed, depending on the current state 107 | of the game. 108 | 109 | 110 | ## Priority 111 | 112 | One common requirement is to have a set of choices which can be 113 | 'overrulled' if some condition is true. So we might want the player to 114 | choose where on the island to go to, but if the character is injured, 115 | we might want to only allow the character to go to the hospital. 116 | 117 | By default, all situations have a priority of 1. If you give a 118 | situation a higher priority, then it will be considered first. If its 119 | `canView` method returns true, then the high priority situation will 120 | be the only one displayed. 121 | 122 | So the injury example might have 123 | 124 | - Docks tag:location canView:always priority:1 125 | - Market tag:location canView:always priority:1 126 | - Hospital tag:location canView:when hits < 10 priority:2 127 | - Doctor tag:location canView:when hits < 10 priority:2 128 | 129 | Then if the hits quality = 10, we'd see 130 | 131 | - Docks 132 | - Market 133 | 134 | but if the hits quality dropped to 9, we'd see just 135 | 136 | - Hospital 137 | - Doctor 138 | 139 | Note that any number of results can be displayed in both cases, but 140 | the higher priorities mask the lower ones. 141 | 142 | If you are working with choices that all have a canView function, it 143 | is a good idea to have a 'fallback' situation that can always be 144 | viewed, but has a priority of zero. This will only be visible if none 145 | of the others are available, and will prevent the game from ending at 146 | that point. 147 | 148 | 149 | ## Maximum Choices 150 | 151 | When you ask for a list of choices, you can specify a maximum number 152 | to return. If more than this number of choices are available, then the 153 | system will select a random subset to return, to make sure you get the 154 | number you asked for. 155 | 156 | By default, all matching situations are equally likely to be returned, 157 | but you can make some situations rarer or more common by setting their 158 | `frequency` value. By default, this value is 1 for a situation. 159 | 160 | A situation with a frequency of 100 will be chosen 100 times more 161 | often than a situation with a frequency of 1, if there is one 162 | situation that needs selecting. In cases where more than one situation 163 | needs chosing, the frequencies are a little less intuitive. 164 | 165 | Consider three situations with frequencies of 1, 1, 100, competing for 166 | two spaces. The 100-frequency situation will be chosen almost every 167 | time, but for the other space, one of the 1-frequency situations must 168 | be chosen. So the actual probabilities will be roughly 50%, 50%, 100%, 169 | even though the frequencies were 1, 1, 100. When selecting more than 170 | one result, frequencies can only be a guide. 171 | 172 | 173 | ## Minimum Choices 174 | 175 | Although rarely used, you can also ask for a minimum number of 176 | choices. This changes the way priority values work. It starts from the 177 | highest priority situations, as normal, but rather than returning only 178 | those at the highest priority level, it checks to see if that set is 179 | enough to meet its minimum. If not, then the next priority level down 180 | will be considered as well, and so on, until the minimum is reached. 181 | 182 | If a minimum and a maximum is given (the maximum being at least as 183 | large as the minimum), then only the lowest priority situations will 184 | be randomly selected for any remaining spaces, higher priority 185 | situations will be guaranteed to be chosen. 186 | 187 | The most common use of this parameter is to set a very high value, 188 | such as Number.INT_MAX, with no maximum limit. This ensures that all 189 | valid situations are returned, regardless of their priority level. 190 | 191 | 192 | ## Choices and SimpleSituation 193 | 194 | `SimpleSituation` provides built-in support for ending its content 195 | with a block of choices, using `System.getSituationIdChoices` to 196 | generate the list and `System.writeChoices` to generate the HTML. To 197 | use this, simply pass in the list of ids and tags as the `choices` 198 | option. You can additionally specify `minChoices` and `maxChoices` if 199 | you need them. 200 | 201 | As for `System.getSituationIdChoices`, if you are only using a single 202 | tag or id, you can give this as a string, rather than a single element 203 | list. 204 | 205 | -------------------------------------------------------------------------------- /games/tutorial.de.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Undum 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 |
31 | 33 |

Undum

34 | 37 |
38 | 43 | 44 |
45 | 46 |
47 |
48 | 49 | 50 |

Undum

51 |

52 | Undum ist ein System, mit dem man digitale Spielbücher erstellen 53 | kann. Es läuft komplett im Browser des Spielers und wurde für HTML 5 und 54 | CSS 3 entworfen. Weitere Informationen (auf Englisch) und den Quellcode 55 | gibt es hier. 56 |

57 | 58 | 59 |
60 | 61 |
62 |
63 | 64 |
65 |

Spielfigur

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 75 |
76 |
77 |
78 | 79 | 80 |

Undum-Tutorium &
81 | interaktives Beispiel

82 |

von I D Millington

83 |

übersetzt von StJohnLimbo

84 | 85 | 86 | 87 |

Klicke hier, um anzufangen

88 |
89 |
90 | 91 |
92 |
93 |
94 | 95 |
96 | 97 | 109 |
110 |
111 | 112 | 115 |
116 |
117 | 118 | 119 |
120 | 121 |
122 |

123 |
124 |
125 |
126 | 127 |
128 | 129 | 130 |
131 |
132 |
133 |
134 | 135 | 136 |
137 | 138 |
139 |
140 | 141 | 146 |
147 |
148 |

Situationen

149 |

150 | Jede Interaktion findet in Undum innerhalb einer 'Situation' statt. 151 | Das ist so ähnlich wie ein 'Raum' in klassischen Textadventures 152 | (aber weniger flexibel) oder wie eine 'Seite' in einem Spielbuch (bloß flexibler). 153 | Die Hauptfigur ist immer in genau einer Situation, und diese Situation legt fest, 154 | was mit der Figur passiert. Situationen sind Code-Abschnitte, 155 | die den Text erzeugen, den du gerade liest. Dieser Absatz wurde z. B. durch 156 | die enter-Methode der ersten Situation erzeugt. 157 |

158 |

159 | Lass uns fortfahren. 160 |

161 |
162 | 163 |
164 |

Speichern, Laden, Löschen

165 |

Was wir bisher noch nicht angesprochen haben, sind 166 | die Schaltflächen 'Speichern' und 'Löschen' im Feld links. 167 | Diese werden nur dann angezeigt, wenn der Browser des Spielers 168 | die lokale ('client-side') Speicherung von Daten erlaubt. 169 | Mit einem Klick auf 'Speichern' wird der momentane Spielstand 170 | gesichert, so dass man später an derselben Stelle fortfahren 171 | kann. Durch einen Klick auf 'Löschen' löscht man den 172 | Spielstand und versetzt das Spiel wieder in den Anfangszustand. 173 | Es gibt keinen 'Laden'-Knopf; ein gespeicherter Spielstand wird 174 | automatisch geladen, wenn die gesamte Seite aufgerufen wird. 175 | Zur Zeit gibt es auch noch keine Möglichkeit, mehrere 176 | Spielstände zu speichern und zwischen ihnen auszuwählen. 177 |

178 |

179 |
180 | Ein Undum-Spiel kann unter Umständen sehr viel Text erzeugen. 181 | Das würde zu Schwierigkeiten führen, wenn man alles 182 | lokal im Browser speichern wollte (der verfügbare Speicherplatz 183 | variiert unvorhersehbar), besonders falls in Zukunft mehrere Spielstände 184 | gespeichert werden sollen. Daher speichert Undum den Spielstand ab, indem 185 | es eine Liste der angeklickten Links sichert. Wenn ein Spielstand geladen 186 | wird, wird also das Spiel anhand dieser Liste noch einmal schnell durchgespielt. 187 | Das ist auch fürs Testen und die Fehlerkorrektur nützlich: 188 | Wenn du deinen Text durchsiehst und Tippfehler korrigierst, kannst du den 189 | Spielstand sichern, die Seite neu laden und durch das Transkript scrollen, 190 | um direkt die Verbesserungen zu sehen. Wenn wir stattdessen den kompletten 191 | Text abspeicherten, hätte der gespeicherte Spielstand noch den Fehler 192 | und du müsstest das gesamte Spiel per Hand noch einmal durchspielen, 193 | bis du zur korrigierten Stelle kommst. 194 |

195 |

196 | Zeit für den letzten Abschnitt. 197 |

198 |
199 |
200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /games/tutorial.en.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Undum 7 | 8 | 9 | 11 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 34 |
35 | 37 |

Undum

38 | 41 |
42 | 47 | 48 |
49 | 50 |
51 |
52 | 53 | 54 |

Undum

55 |

56 | Undum is a pure client-side game framework for narrative 57 | interactive fiction. It is designed for HTML 5 and 58 | CSS 3. You can read more and download the source 59 | code here. 60 |

61 | 62 | 63 |
64 | 65 |
66 |
67 | 68 |
69 |

Character

70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | 79 |
80 |
81 |
82 | 83 | 84 |

The Undum Tutorial &
85 | Interactive Example

86 |

by I D Millington

87 | 88 | 89 | 91 |

click to begin

92 |
93 |
94 | 95 |
96 |
97 |
98 | 99 |
100 | 101 | 109 |
110 |
111 | 112 | 114 |
115 |
116 | 117 | 118 |
119 | 120 |
121 |

122 |
123 |
124 |
125 | 126 |
127 | 128 | 129 |
130 |
131 |
132 |
133 | 134 | 135 |
136 | 137 |
138 |
139 | 140 | 144 |
145 |
146 |

Situations

147 |

148 | In Undum, all interaction takes place in a situation. You 149 | can think of it either as a 'Room' in traditional 150 | interactive fiction (although it is less flexible than 151 | that), or as a 'Page' in a Choose Your Own Adventure book 152 | (though it is more flexible than that). 153 |

154 |

155 | At any point, the character is in exactly one situation, and 156 | that situation is responsible for everything that happens to 157 | them. Situations are chunks of code that generate the output 158 | you are reading here. For example, this text was generated 159 | by the enter method of the 'situations' situation. 160 |

161 |

162 | Let's move on again. 163 |

164 |
165 | 166 |
167 |

Saving and Loading

168 |

The only piece of the UI we haven't talked about is the 169 | 'Save' and 'Erase' buttons on the left panel. These are only 170 | visible if your browser supports client-side data 171 | storage. Clicking 'Save' stores your game, so you can pick 172 | it up again later. There is currently no 'Load' button, the 173 | game loads when the page loads. There is also no way to save 174 | multiple games, and select which one you want to play. These 175 | are both things I'd like to change in the future. 176 |

177 |

178 | Potentially your game could generate huge amounts of 179 | text. And that would be difficult to store client side 180 | (there are unpredictable limits), especially when we move 181 | towards having multiple save files. So instead Undum saves 182 | your character as the list of links you clicked. Loading a 183 | save-file consists of playing through your game again, 184 | quickly. This is a beneficial approach for debugging too. It 185 | means when you're polishing and correcting typos, you can 186 | save and load the game and scroll through the transcript to 187 | see your corrections. If we saved the text, your save file 188 | would have the error in it and you'd have to manually replay 189 | the game to see the correction. 190 |

191 |

192 | Let's return to the topic list. 193 |

194 |
195 | 196 | 197 |
199 |

Topics

200 |

Choose a topic to find out about next. If 201 | in doubt, go through the topics in order.

202 |
203 | 204 |
207 |

When you're writing an Undum game, you often need certain 208 | options to be available only when certain conditions hold. You 209 | might have an option to remove a secret panel in a haunted 210 | house, but that should only be visible if the character knows 211 | where it is. If options are available in lots of situations, it 212 | can be very difficult to keep track of what options are 213 | allowed, and to produce just the right list of choices.

214 |

To help with this, Undum can generate a list of situation 215 | links for you. It does this in three steps. Firstly, each 216 | situation can be given one or more tags, this allows you to 217 | easily ask for links to all situations with the 'in-hallway' 218 | tag, for example. Secondly, situations can have 219 | a canView method, which gets to decide whether that 220 | situation should appear. That method can look at the 221 | current state of the character to decide whether to appear 222 | or not. Thirdly, SimpleSituations have a choices 223 | option. If it is set to one or more tags, it handles the building 224 | of the choice list.

225 |

You've seen this already in the topic list. The topic list 226 | is generated automatically. All of the situations in the topic 227 | list are always available, however. 228 | So here is an example of 229 | automatic options that may change:

230 |
231 | 232 |
233 |

You can return to the 234 | Topic List or choose another option from 235 | this example:

236 |
237 |
238 | 239 | 240 | 241 | 242 | 243 | 244 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /docs/src/creating.md: -------------------------------------------------------------------------------- 1 | # Creating an Undum Game 2 | 3 | # File Structure 4 | 5 | An Undum game consists of a single HTML file. This imports the game 6 | engine and any supporting files. It also provides the structure needed 7 | for Undum to place content correctly on-screen. 8 | 9 | The Undum engine consists of a single Javascript file, `undum.js`. In 10 | order to work, however, it needs supporting files. It requires the 11 | jQuery library (http://jquery.com) to be already imported, and it 12 | requires a game definition file to be imported after it. 13 | 14 | The normal pattern of imports in the HTML file is therefore: 15 | 16 | 17 | 18 | 19 | 20 | By convention, the Javascript files are held in a `media/js` 21 | directory, but this is not enforced, you can arrange these files as 22 | you like, as long as you update the references to match. 23 | 24 | In addition to the Javascript files, Undum expects the HTML file that 25 | imports it to have a particular structure. Although there is a good 26 | deal of flexibility, if you need it, you should start with the HTML 27 | file that is provided. 28 | 29 | Finally, the HTML file will include a CSS file that controls the look 30 | and feel of the page. There are some elements in the CSS file which 31 | are used by Undum, and so, as for the HTML page, you should start with 32 | the CSS files provided. In most cases you will be able to leave the 33 | CSS file untouched, or else just tweak colors and image imports to 34 | match your game's style. 35 | 36 | ## The HTML File 37 | 38 | The sample HTML file provided shows the key points to edit. They are: 39 | 40 | * The game's title. 41 | 42 | * The top-left panel title and summary (you can leave this as an 43 | explanation of Undum, or change it to be more game-specific). 44 | 45 | * The copyright message. Please note that there is also a message 46 | that acknowledges your game is based on Undum. You can remove this 47 | entirely, but if you do leave it in place, that helps people 48 | find the software and perhaps write their own game. 49 | 50 | * The path to your game definition file. By convention, game 51 | definition files are named `yourgamename.game.js`. 52 | 53 | In addition, from v.2 onwards, Undum supports defining Situations 54 | entirely within HTML. These situations are scanned and added to 55 | Situations defined in your Javascript Game Definition file, allowing 56 | you to have the best of both worlds. HTML Situations are discussed in 57 | the page on the HTML API. 58 | 59 | 60 | ## The Game Definition File 61 | 62 | The game definition file where you create your game. To define a game 63 | you must provide the following data: 64 | 65 | * A unique ID for the game, which is used to make sure saved games 66 | don't get confused. 67 | 68 | * A version number that allows you to manage upgrades to your 69 | content. 70 | 71 | * All the Situations that make up your game. 72 | 73 | * The identifier of the situation that starts the game. 74 | 75 | In addition it is very common to provide the following data: 76 | 77 | * Descriptions of each quality your game will use, and how that should 78 | be displayed to the player. 79 | 80 | * Definitions of what groups of qualities should appear in the 81 | character panel and what headings to use to label them. 82 | 83 | * An Initialization function that sets up the game ready for 84 | playing. This is where you typically assign any starting qualities 85 | to the character, or set their initial character text. 86 | 87 | And finally, there are a range of other data you can provide: 88 | 89 | * Functions to be called whenever any situation is entered, or exited. 90 | 91 | * Functions to be called before or after any action is carried out. 92 | 93 | ### Identifiers 94 | 95 | Identifiers are small snippets of text that allow you to refer to 96 | something in Undum. There are two types of identifier. The Game 97 | identifier represents your whole game, and has its own particular 98 | requirements (described below). 99 | 100 | Lots of other things in Undum have identifiers (Situations, Actions, 101 | Qualities, Quality Groups), and they all have the same 102 | requirements. These identifiers must consist of Latin lower-case 103 | letters (i.e. a-z, no accents), hyphens, and the digits 0-9 only. 104 | 105 | ### Game ID 106 | 107 | The game identifier should be a string that is unlikely to be used in 108 | other games. You could use a UUID (my preference), or you could use a 109 | variation on your email `mygame-myname@mydomain.com`, or a URL you 110 | control `http://mydomain.com/undum-games/mygame`. If and when Undum 111 | games end up being re-distributable (as I hope they will), having a 112 | universally unique identifier will mean that saved games don't clash. 113 | 114 | As stated previously, the game identifier doesn't have the same 115 | requirements as any other identifier in Undum, you can use any 116 | characters to make up your game ID. 117 | 118 | ### Version Number 119 | 120 | The version number is a string of text that indicates what version of 121 | the content is in the file. There is no set format for this version 122 | text, so you can use any scheme that suits you. I have used the 123 | "major.minor" approach. 124 | 125 | Like the Game ID, this value is used to make sure that saved games 126 | don't clash. If you change you content, then previous saved games may 127 | not work correctly. By updating the version number, you allow the 128 | player to be notified of this directly, rather than suffering a 129 | silent crash. The effect of this is that you don't need to update the 130 | version number if you make a change to something that doesn't alter 131 | the structure of the game. If you just change some text, for example, 132 | or add an extra action that merely outputs a piece of description. 133 | 134 | ### Situation List 135 | 136 | Situations are defined in a Javascript object, placed in the 137 | `undum.game.situations` property. Situations in the object use their 138 | situation identifier as a property name, and a `Situation` object as 139 | its value. For example: 140 | 141 | undum.game.situations = { 142 | doorway: new Situation(...), 143 | lobby: new SimpleSituation(...), 144 | lobby-upstairs-closed: new MyCustomSituation(...), 145 | ... etc ... 146 | }; 147 | 148 | The situation objects are described more fully below. 149 | 150 | ### Starting Situation 151 | 152 | This is placed in the `undum.game.start` property. It should consist 153 | of the situation identifier as a string. It must be given. 154 | 155 | ### Qualities 156 | 157 | Qualities don't have to be displayed to the user. They can just 158 | function as internal properties for use by your code. Qualities that 159 | will never be displayed don't need to be declared, you can just set 160 | them when you need them (we'll look at the API for setting qualities 161 | below). 162 | 163 | Often you want the quality to be displayed, however, and so you need 164 | to tell Undum to display the quality, and how it should be 165 | formatted. The `QualityDefinition` object does that. Any quality that 166 | doesn't have a corresponding quality definition will be invisible to 167 | the player. 168 | 169 | Quality definitions are placed in an object in the 170 | `undum.game.qualities` property. Within this object, the property 171 | names are the quality identifiers, and the values they contain are 172 | `QualityDefinition` objects. For example: 173 | 174 | undum.game.qualities = { 175 | investigation: new IntegerQuality("Investigation", ...), 176 | "found-mr-black": new OnOffQuality("Found Mr. Black", ...), 177 | ... etc ... 178 | }; 179 | 180 | There are a number of `QualityDefinition` constructors defined which 181 | automatically format in common ways, you can also define your own and 182 | take complete control of the output. We'll return to discussion of the 183 | API, below. 184 | 185 | ### Quality Groups 186 | 187 | Qualities can be assigned to groups, and displayed under a common 188 | heading in the character panel. To define groups, place an object in 189 | the `undum.game.qualityGroups` property. This object should have 190 | properties which are the group identifiers, and values that are 191 | `QualityGroup` objects. For example: 192 | 193 | undum.game.qualityGroups = { 194 | stats: new QualityGroup(...), 195 | clues: new QualityGroup(...), 196 | equipment: new QualityGroup(...) 197 | } 198 | 199 | A common source of puzzlement is that you don't use this data 200 | structure to define which qualities belong in which group. Instead, 201 | each quality that you want to assign to a group, should be given the 202 | identifier of the group it belongs to. So your `undum.game.qualities` 203 | property will look something like: 204 | 205 | undum.game.qualities = { 206 | investigation: new IntegerQuality("Investigation", {group:'stats'}), 207 | "found-mr-black": new OnOffQuality("Found Mr. Black", {group:'clues'}), 208 | ... etc ... 209 | }; 210 | 211 | Again, see the API documentation below for more details about what you 212 | can pass into these constructors. 213 | 214 | ### Initialization Function 215 | 216 | Your initialization function should be placed in the `undum.game.init` 217 | property. Normally its job is to configure the character at the start 218 | of the game. For example: 219 | 220 | undum.game.init = function(character, system) { 221 | character.qualities.investigating = 0; 222 | character.qualities.money = 100; 223 | ... etc ... 224 | system.setCharacterText(...); 225 | }; 226 | 227 | Initialization functions can, but normally doesn't, output 228 | text. Instead the starting situation is tasked with outputting the 229 | initial content. 230 | 231 | ### Global Event Functions 232 | 233 | Most of the time you want Situations to handle user 234 | interaction. Occasionally, however, you have to handle something that 235 | spans situations. It would be inconvenient to duplicate the same code 236 | in every situation. So Undum provides a set of hooks for you to 237 | respond globally to user interaction. There are five of these: 238 | `enter`, `afterEnter`, `exit`, `beforeAction` and `afterAction`. You 239 | can define functions in your game file using the properties: 240 | `undum.game.enter`, `undum.game.afterEnter`, 241 | `undum.game.exit`, `undum.game.beforeAction`, and 242 | `undum.game.afterAction`. 243 | 244 | The `enter`, `afterEnter`, and `exit` functions look like this: 245 | 246 | undum.game.enter = function(character, system, from, to) { 247 | ... 248 | }; 249 | 250 | where `from` and `to` are the identifiers of the situations being left 251 | and entered, respectively. And the `beforeAction` and `afterAction` 252 | functions look like this: 253 | 254 | undum.game.beforeAction = function(character, system, situation, action) { 255 | ... 256 | }; 257 | 258 | where `situation` is the current situation identifier, and `action` is 259 | the identifier of the action being carried out. 260 | 261 | These functions intentionally look like the `enter`, `exit` and `act` 262 | methods of the `Situation` object. These are described in more detail 263 | in the API reference. 264 | -------------------------------------------------------------------------------- /games/tutorial.ru.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Undum 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 27 |
28 | 29 |

Undum

30 | 33 |
34 | 39 | 40 |
41 | 42 |
43 |
44 | 45 | 46 |

Undum

47 |

48 | Undum - это чисто клиентский (client-side) игровой движок для повествовательной интерактивной литературы. 49 | Он создан для HTML 5 и CSS 3. Вы можете прочитать о нём и скачать исходный код 50 | здесь. 51 |

52 | 53 | 54 |
55 | 56 |
57 |
58 | 59 |
60 |

Персонаж

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | 70 |
71 |
72 |
73 | 74 | 75 |

Undum

76 |

Движок гипертекстовых игр

77 | 78 | 79 |

Нажмите, чтобы начать

80 |
81 |
82 | 83 |
84 |
85 |
86 | 87 |
88 | 89 | 99 |
100 |
101 | 102 | 103 |
104 |
105 | 106 | 107 |
108 | 109 |
110 |

111 |
112 |
113 |
114 | 115 |
116 | 117 | 118 |
119 |
120 |
121 |
122 | 123 | 124 |
125 | 126 |
127 |
128 | 129 | 133 |
134 |
135 |

Ситуации

136 |

В Undum, любое взаимодействие происходит в ситуации. Вы можете думать о ситуации 137 | либо как о 'Комнате' (Room) в традиционной интерактивной литературе (хотя 138 | этот подход не так гибок), или как о 'Странице' в книге-игре. В любой момент времени, 139 | персонаж находится точно в одной ситуации, и эта ситуация отвечает за всё, 140 | что происхожит с ним.

141 |

Ситуации - это кусочки кода, которые генерируют вывод - то есть то, что вы сейчас читаете. 142 | Например, этот текст был сгенерирован методом enter первой ситуации. 143 |

144 |

145 | Давайте снова сделаем шаг вперёд. 146 |

147 |
148 | 149 |
150 |

Сохранение и загрузка

151 |

Единственный элемент интерфейса, о котором мы не заговорили, это кнопки 152 | «Сохранить» и «Стереть» на левой панели. Они видны только если ваш браузер 153 | поддерживает клиентское хранилище данных.

154 |

Нажатие на «Сохранить» складывает вашу игру в хранилище, чтобы вы смогли 155 | подобрать её позже. В настоящее время здесь нет кнопки «Загрузить», и 156 | сохранённая игра загружается, когда загружается страница. Также нет 157 | способа сделать несколько сохранений, и выбрать то, с которым вы хотели бы 158 | играть. Это те вещи, которые я хотел бы изменить в будущем. 159 |

160 |

161 | Потенциально, ваша игра может генерировать огромное количество текста. 162 | И это было бы сложно сохранять в на стороне клиента (существуют непредсказуемые ограничения), особенно 163 | если мы хотим двигаться к владению несколькими файлами сохранений. 164 | Поэтому вместо того Undum сохраняет вашего персонажа как список ссылок, 165 | на которые вы нажали. Загрузка файла сохранения состоит из переигрывания вашей игры 166 | заново, только очень быстро. Этот подход удобен и в отладке тоже. 167 | Это означает, что, когда вы отшлифовываете и исправляете ошибки, вы можете 168 | сохранить, затем загрузить игру и пройтись по всему транскрипту, чтобы увидеть свои правки. 169 | Если бы мы сохраняли текст, ваш файл сохранения был бы ошибочным, и вам бы пришлось вручную переигрывать 170 | всю игру, чтобы увидеть изменения. 171 |

172 |

173 | Давайте вернёмся к списку тем. 174 |

175 |
176 | 177 | 178 |
180 |

Темы

181 |

Выберите тему, о которой вы хотите узнать подробнее. Если вы сомневаетесь, пройдите по порядку.

182 |
183 | 184 |
187 |

Когда вы пишете игру на Undum, вам может понадобиться сделать так, 188 | чтобы некоторые возможности появлялись только при соблюдении определённых 189 | условий. У вас может быть возможность снять секретную панель в старинном 190 | доме с привидениями, но она должна быть доступна только если персонаж знает, где 191 | она находится.

192 |

Если возможности доступны в нескольких ситуациях, за ними будет трудно следить, 193 | чтобы нарисовать список из именно доступных вариантов выбора.

194 |

Чтобы помочь, Undum может составлять списки ссылок на ситуации за вас. 195 | Он делает это в три шага. Сначала, каждой ситуации вы назначаете одну или 196 | больше меток (тегов). Это позволит вам, например, попросить список ситуаций 197 | с меткой "в коридоре".

198 |

Во-вторых, ситуации имеют метод canView, который решает, может 199 | ли эта ситуация появляться в списках. Этот метод может следить за сложными 200 | зависимостями, по-своему оценивать текущее состояние игры.

201 |

Наконец, ситуации SimpleSituation имеют опцию choices. 202 | Если вы запишете в неё одну или несколько меток, то построение списков выбора 203 | будет полностью автоматизировано.

204 |

Вы уже видели это в списке тем. Список тем генерируется автоматически. 205 | В нём включена опция скрытия тем, которые вы уже посещали. 206 | Давайте посмотрим на более сложный пример автоматического 207 | списка, который может меняться:

208 |
209 | 210 |
211 |

Вы можете вернуться к списку тем или выбрать другую 212 | ссылку из этого примера:

213 |
214 |
215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 225 | 226 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /docs/src/HTML.md: -------------------------------------------------------------------------------- 1 | # HTML API 2 | 3 | # HTML 4 | 5 | All of Undum's output is HTML. This allows you to style your content 6 | in any way you choose. It also allows you to include rich media into 7 | your output, such as images, video or audio. 8 | 9 | ## Display Content 10 | 11 | All HTML output should be in a format we call "Display Content" - this 12 | has particular rules: 13 | 14 | * Display Content must always start and end with a HTML tag. It may 15 | consist of more than one tag internally, however. So we can have the 16 | structure: `

...

`, for example, but not `some 17 | text...`. 18 | 19 | * It must be a complete snippet of HTML in its own right. So any tags 20 | that represent a range of content must have their closing tags 21 | present. So we can do `

...

`, but not 22 | `

...

`. 23 | 24 | ## CSS Styles 25 | 26 | Undum also uses some of HTML's capabilities to control its own 27 | behavior. In particular, it uses a series of CSS classes to control 28 | how content behaves. 29 | 30 | #### `class="transient"` 31 | 32 | The CSS class `transient` can be used to mark HTML content that should 33 | be removed when the user changes their situation. When a situation 34 | changes, Undum will go back and remove any links from the text 35 | (leaving the text that was in the link). Any HTML content that has the 36 | CSS class `transient` will be completely removed at this time. Undum 37 | uses a fading animation to show the user this is happening, to avoid 38 | the user seeing an abrupt disappearance but being unable to work out 39 | what was removed. 40 | 41 | Any HTML tag can be marked as `transient`. It is most commonly used on 42 | a paragraph of text that gives the user a set of options. Undum is 43 | designed to allow game developers to produce beautiful narratives - 44 | you don't want that narrative littered by "You could do X, or you 45 | could do Y." statements. Mark this content as transient, and it will 46 | not form part of the permanent record. 47 | 48 | #### `class="sticky"` 49 | 50 | When you change situations, links in previous situations will be 51 | removed. This prevents the user backtracking and picking options that 52 | no longer apply. Sometimes you want links to be available for a longer 53 | time, however. In this case mark them with the CSS class 54 | `sticky`. Sticky only applies to links, so should only be added to 55 | `` tags. 56 | 57 | #### `class="raw"` 58 | 59 | Links can also have the `raw` CSS class. This class prevents Undum 60 | from interpreting the link as an instruction to the game. If you want 61 | to add a link to an external resource, you can add this class to 62 | it. Note that raw links are still removed when you change situations, 63 | so if you want a raw link permanently available, you should also make 64 | it sticky. 65 | 66 | For some URLs, Undum can guess that the link is supposed to be an 67 | external link, and will automatically add the `raw` class for 68 | you. This isn't perfect, however, and you are better off not relying 69 | on it. If you have a link that you don't want Undum to capture, always 70 | use the `raw` class. 71 | 72 | #### `class="once"` 73 | 74 | Although links will be removed when the situation changes, often you 75 | want to remove them before that, as a result of an action within the 76 | current situation. There is an API tool available to do that in your 77 | code. 78 | 79 | For convenience, there is also the `once` CSS class. Adding this to a 80 | link means that the link will be removed as soon as it is 81 | clicked. This is mostly useful for action links, because a link that 82 | changes the situation will automatically cause previous links to 83 | disappear. 84 | 85 | Note that once is smart about this removal. It removes all links to 86 | the same action, not just the link that was clicked. So if you have 87 | the same action available in two links in your content, both will be 88 | removed. 89 | 90 | #### `class="options"` 91 | 92 | The options class only works on an unordered list. The default CSS 93 | also styles this differently. The list items will be presented as 94 | options within a table of choices. On devices with a mouse or pointer, 95 | the rows will change color when they are hovered over. The player can 96 | click anywhere on the row and the first link that it contains will be 97 | executed. 98 | 99 | This class is intended for smarter presentation of standard option 100 | blocks, if you don't want your choices to be embedded into the 101 | hypertext. 102 | 103 | Note that if you use this style, the unordered list will automatically 104 | disappear after being clicked, regardless of whether it is marked as 105 | "`transient`". 106 | 107 | ### Headings 108 | 109 | In the default CSS for Undum, the only heading level expected in the 110 | text is `

`. You can use other headings, but you'll have to create 111 | your own CSS styles. One level of heading is almost always enough (if 112 | not too much) for narrative works. 113 | 114 | 115 | ## Types of Link 116 | 117 | Undum captures the links you put into your display content, and uses 118 | them to control the flow of the game. There are three formats of link 119 | that Undum understands, plus raw links, which it ignores. 120 | 121 | ### Situation Links 122 | 123 | Situation links are of the form `situation-id`. They must have no 124 | additional punctuation. As we'll see below, situation identifiers 125 | consist of lower case Latin letters, hyphens and the digits 0-9 only. 126 | 127 | Clicking a situation link changes the current situation. 128 | 129 | ### Action Links 130 | 131 | Action links are of the form `./action-id`. As for situations, action 132 | identifiers consist of lower case Latin letters, the digits 0-9 and 133 | hyphens only. 134 | 135 | Clicking an action link carries out the specified action in the 136 | current situation. 137 | 138 | ### Combined Links 139 | 140 | Combined links are of the form `situation-id/action-id`. They combine 141 | both the previous steps: they change to the given situation, and then 142 | they carry out the given action once there. They are rarely used. 143 | 144 | It is possible to use the combined form to refer to an action in the 145 | current situation. Undum is smart enough to understand that it doesn't 146 | need to change situation in that case, so it is entirely equivalent to 147 | the action link. It is rarely used (because it is so much more 148 | verbose), but can be useful. For example, we might want a sticky link 149 | to always take the character into their study and drink a potion, 150 | having this sticky link point to `bedroom/drink-potion`, achieves 151 | this. If they are already in the bedroom, then Undum notices this and 152 | doesn't try to make them enter it again. 153 | 154 | ### External Links 155 | 156 | As described above, you can add any other links to external content in 157 | your display content, as long as you mark it with the `raw` CSS 158 | class. It is also *highly* recommended that you mark all such links as 159 | opening in a new window or tab (add the `target="_blank"` attribute to 160 | the `` tag). If you link the player to another page, and it 161 | replaces the Undum page, then for most browsers, all progress will be 162 | lost. Chrome appears to keep the progress, so that if you hit the back 163 | button you will be where you left off. Other browsers reset the game 164 | to the last saved location, or back to the beginning if the game 165 | hasn't been saved. 166 | 167 | ### Link Data 168 | 169 | *This section describes a feature that is planned, or in development. 170 | It may not be functional in the current version of the code.* 171 | 172 | Undum links are allowed to add query data, 173 | e.g. `./action-id?foo=1&bar=2`. This extra data is URL-encoded content 174 | which will be parsed and passed back to your code. For situation links 175 | the data will be passed into the old situation's `exit` handler, and 176 | the new situation's `enter` handler. For action links, the data will 177 | be passed into the situation's `act` handler. For combined links the 178 | data will be passed into both sets of handlers. 179 | 180 | Raw links, as usual, do not have any processing performed. If they 181 | contain query data, it will be passed on to their target as it would 182 | for any link in any HTML document. 183 | 184 | 185 | # Defining Situations in HTML 186 | 187 | *New in Version 2* 188 | 189 | From v.2, Undum supports defining situations entirely from HTML. The 190 | tutorial game files show this in action, if you want to see a concrete 191 | example. 192 | 193 | ### Required Data 194 | 195 | Situations defined in HTML should be diven as a `div` with 196 | `class="situation"` set. The content of the `div` will be turned into 197 | a new `SimpleSituation` (see the API 198 | document), with the content of the div output when the situation's 199 | `enter` method is called. In addition to the `class="situation"` and 200 | the HTML content, the `div` also requires its `id` attribute to be 201 | set, and the value of this id will be used as the situation id for the 202 | newly created situation. Situations created from HTML are instances of 203 | the `SimpleSituation` prototype. 204 | 205 | The game scans the HTML file for situations, compiles them and adds 206 | them to the main `game.situation` dictionary. Because the situations 207 | end up in the same place, you can freely mix the two methods, defining 208 | some situations in HTML, and others in Javascript. For this reason, 209 | you can't have a situation defined in the HTML with the same id as one 210 | in your Javascript file. This will raise an error. 211 | 212 | ### Optional Data 213 | 214 | In addition to the id and content of the situation, additional options 215 | can be set by defining one or more of the following `data-` attributes 216 | on the `div`. Each is interpreted and passed to a corresponding option 217 | on a `SimpleSituation` object, so to find more information on what 218 | each value means, see the Javascript API. 219 | 220 | #### data-option-text 221 | 222 | If defined, this specifies the label that will be used when the 223 | situation appears in an choice block. In Javascript, this can be 224 | either fixed text, or a function. In the HTML API, only fixed text is 225 | supported. 226 | 227 | #### data-can-view 228 | 229 | If defined, this should be the body of a function that will be 230 | compiled and attached to the situation's canView method. The 231 | content of this attribute will be wrapped with: 232 | 233 | (function(character, system, situation) { 234 | ... HTML attribute ... 235 | }) 236 | 237 | to make the final function. So, your definition should end with a 238 | `return` statement. For example: 239 | 240 |
242 | 243 | This is intended to be similar to the way browsers function with event 244 | handlers on HTML tags, such as `onClick`. The actual semantics are 245 | difficult to replicate exactly, and there may be use-cases that are 246 | impossible, but for most uses, this will do the right thing. 247 | 248 | #### data-can-choose 249 | 250 | If defined, this should be the body of a function that will be 251 | compiled and attached to the situations canChoose method. See the 252 | description of `canView`, above, for details of how the value in this 253 | attribute is interpreted. 254 | 255 | #### data-priority 256 | 257 | If given, this is parsed as a number and set as the situation's 258 | `priority` value. 259 | 260 | #### data-frequency 261 | 262 | If given, this is parsed as a number and set as the situation's 263 | `frequency` value. 264 | 265 | #### data-display-order 266 | 267 | If given, this is parsed as a number and set as the situation's 268 | `displayOrder` value. 269 | 270 | #### data-tags 271 | 272 | If given this should be a set of tags to label the situation with. The 273 | string is split at commas, spaces and tabs. So "foo, bar cube 274 | dock-trog" is parsed as four tags: "foo", "bar", "cube" and 275 | "dock-trog". 276 | 277 | #### data-heading 278 | 279 | If given, then this heading will be used as the `heading` property for 280 | the SimpleSituation. If no `data-option-text` is given, the heading 281 | will also be used in choice blocks. 282 | 283 | #### data-choices 284 | 285 | If given, this should be a list of items to use in generating an 286 | implicit set of situation choices, exactly as for the `choices` 287 | property of `SimpleSituation`. The values of this list is parsed 288 | exactly as for `data-tags`, above, splitting at commas, spaces and 289 | tags. 290 | 291 | #### data-min-choices 292 | 293 | If `data-choices` is given, this value can also be specified. If so, 294 | it is interpreted as an integer and passed to the `minChoices` option 295 | of `SimpleSituation`. 296 | 297 | 298 | #### data-max-choices 299 | 300 | If `data-choices` is given, this value can also be specified. If so, 301 | it is interpreted as an integer and passed to the `maxChoices` option 302 | of `SimpleSituation`. -------------------------------------------------------------------------------- /games/media/games/tutorial/tutorial.game.en.js: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // Edit this file to define your game. It should have at least four 3 | // sets of content: undum.game.situations, undum.game.start, 4 | // undum.game.qualities, and undum.game.init. 5 | // --------------------------------------------------------------------------- 6 | 7 | /* A unique id for your game. This is never displayed. I use a UUID, 8 | * but you can use anything that is guaranteed unique (a URL you own, 9 | * or a variation on your email address, for example). */ 10 | undum.game.id = "be1c95b9-cbc7-48c6-8e6a-89837aa9113e"; 11 | 12 | /* A string indicating what version of the game this is. Versions are 13 | * used to control saved-games. If you change the content of a game, 14 | * the saved games are unlikely to work. Changing this version number 15 | * prevents Undum from trying to load the saved-game and crashing. */ 16 | undum.game.version = "1.0"; 17 | 18 | /* A variable that changes the fade out speed of the option text on 19 | * a mobile. */ 20 | undum.game.mobileHide = 2000 21 | 22 | /* A variable that changes the options fade out speed. */ 23 | undum.game.fadeSpeed = 1500 24 | 25 | /* A variable that changes the slide up speed after clicking on an 26 | * option. */ 27 | undum.game.slideUpSpeed = 500 28 | 29 | /* The situations that the game can be in. Each has a unique ID. */ 30 | undum.game.situations = { 31 | start: new undum.SimpleSituation( 32 | "

Starting Out with Undum

\ 33 | \ 34 |

Welcome to the Undum tutorial. Undum is a tool for writing\ 35 | hypertext interactive fiction. It has some unique features\ 36 | and a visual design that encourages narrative games.

\ 37 | \ 38 |

Hypertext interactive fiction is the digital equivalent of the\ 39 | Choose Your Own Adventure (CYOA) books that were popular in the\ 40 | 1980s. The story is told in chunks, and you select from a range\ 41 | of options to move it forward. Unlike the book form, however, the\ 42 | digital form gives you far more flexibility to tell rich stories\ 43 | and introduce more interesting game elements.

\ 44 | \ 45 |

Click this link to\ 46 | continue...

" 47 | ), 48 | 49 | // NB: The 'hub' situation which is the main list of topics, is 50 | // defined wholly in the HTML file, and doesn't have an entry in 51 | // the game.situations dictionary in this file. 52 | 53 | // For variety, here we define a situation using the top-level 54 | // Situation type. This is a neat approach to generate text by 55 | // looking it up in the HTML document. For static text that makes 56 | // more sense than writing it longhand. 57 | situations: new undum.Situation({ 58 | enter: function(character, system, from) { 59 | system.write($("#s_situations").html()); 60 | }, 61 | tags: ["topic"], 62 | optionText: "What Undum Games are Made Of", 63 | displayOrder: 1 64 | }), 65 | todo: new undum.SimpleSituation( 66 | "

Two things can happen in a situation. The character either\ 67 | leaves the situation and enters another one, or\ 68 | they carry out some action. Actions may\ 69 | perform some processing, they may display some results, but\ 70 | ultimately they put the character back into the same situation\ 71 | again.

\ 72 | \ 73 |

When you are designing your game, use situations to reflect a\ 74 | change in what the character can do. So you would change situation if\ 75 | the character pulls a lever to open a trapdoor, for example. Actions\ 76 | are intended for situations where the character can examine things\ 77 | more closely, or maybe top up their magic by drinking a potion.\ 78 | Things that don't affect the state of the world around them.

\ 79 | \ 80 |

Situations generate content when they are entered,\ 81 | exited, and when they receive an action (the\ 82 | italicised words are the names of the three methods that do this).\ 83 | You can write code to generate content in any way you like, so the\ 84 | content that is displayed can be totally dynamic: taking into\ 85 | account the current state of the character.\ 86 | Content is just plain HTML, so you use regular HTML tags to make\ 87 | things bold or italic, or to include\ 88 | images. This gives you a lot of flexibility. For example, since Undum\ 89 | targets HTML5 browsers, you could use the audio or\ 90 | video tags to include rich media.

\ 91 | \ 92 |

Make sure you've carried out the action above,\ 93 | then return to the topic list.

", 94 | { 95 | actions: { 96 | 'do-something': "

You carried out the action, well done.\ 97 | You'll notice that the links for this\ 98 | situation are still active. This means you\ 99 | can click to perform the action again.

" 100 | } 101 | } 102 | ), 103 | links: new undum.SimpleSituation( 104 | "

Between each chunk of new text, Undum inserts a discreet line\ 105 | in the margin. This allows you to see at a glance everything that\ 106 | has been output as a result of your last click.\ 107 | It is particularly useful for small devices, or when\ 108 | lots of content has appeared. The window also scrolls so the start\ 109 | of the new content is as near to the top of the window as possible.\ 110 | This is also designed to help you read more naturally.

\ 111 | \ 112 |

If you've been watching carefully, you will have noticed that\ 113 | parts of the text have been disappearing when you move between\ 114 | situations. This isn't a bug! One of the aims of Undum is to give\ 115 | game designers the ability to make the transcript of\ 116 | the game read as a coherent piece of narrative. However, you often\ 117 | need chunks of text that do nothing but offer the reader choices.\ 118 | Undum defines a special CSS class to add to your HTML for this\ 119 | (remember generated content is just HTML). It is transient,\ 120 | and can be applied to paragraphs, divs, or just\ 121 | spans (such as this one).

\ 122 | \ 123 |

You may also have noticed that, when you move situations, all the\ 124 | links in the previous situation turn into regular text. This is to\ 125 | stop you backtracking and trying previous options when you've already\ 126 | committed to one. In other H-IF systems, this is\ 127 | done by completely removing the content from previous pages.\ 128 | That prevents you going back and reading your story, however.

\ 129 | \ 130 |

The 'Different Kinds of Links' topic has more\ 131 | about these links.\ 132 | Let's return to the topic list.

", 133 | { 134 | heading: "Disappearing Content", 135 | diplayOrder: 2, 136 | tags: ["topic"] 137 | } 138 | ), 139 | sticky: new undum.SimpleSituation( 140 | "

There are three types of link in Undum. The first two we've seen\ 141 | in previous topics:\ 142 | links to change situation and links to carry out an action. When you\ 143 | include a link in your output, Undum parses it and wires it up\ 144 | correctly. If you create a link with a HTML href attribute\ 145 | containing just a name ('ballroom', for\ 146 | example) this will send the character to the situation with that\ 147 | name. Links\ 148 | with two components ('ballroom/view-painting', for example) send\ 149 | the character to a new situation and then carry out the\ 150 | named action ('view-painting' in this case). To carry out an action\ 151 | in the current situation, you can replace the situation name with a\ 152 | dot (so it would be './view-painting'). In all cases, if the\ 153 | character is already in that situation, then the situation's\ 154 | enter method won't be called again.

\ 155 | \ 156 | \ 157 |

The third type of link, then, is a general hyperlink. If your\ 158 | link doesn't consist of a single element or pair of elements, as\ 159 | above, then Undum will guess that you have a normal hyperlink. As\ 160 | in this link.\ 161 | If you have a link that does look like an Undum link, you\ 162 | can still force Undum not to interpret it as an action or situation\ 163 | move, by adding the CSS class raw to the HTML a tag.\ 164 | links that don't have the raw class, but that are considered\ 165 | to be non-Undum links (like the link above), will have raw\ 166 | added to them before display. This could allow you to style external\ 167 | links differently, as we have done here.

\ 168 | \ 169 |

In the last situation I said you can prevent links from being\ 170 | turned into regular text when you move situations. This is done\ 171 | by another CSS class: sticky. When you\ 172 | leave this situation, you'll notice the\ 173 | external link stays active. This can allow you to have options that\ 174 | stay valid throughout the narrative, for example, such as a spell to\ 175 | teleport home.

", 176 | { 177 | tags: ["topic"], 178 | displayOrder: 3, 179 | heading: "Different Kinds of Links" 180 | } 181 | ), 182 | oneshot: new undum.SimpleSituation( 183 | "

There is one final option for links. If you give a link\ 184 | the once CSS class, then that link will disappear\ 185 | after it is clicked. This is used (as in\ 186 | this link) for\ 187 | actions that you only want to be possible once. There is no\ 188 | point using 'once' on situation links because they'll be turned\ 189 | into text as soon as you click them anyway (unless they are also\ 190 | sticky, of course).

Once links are useful\ 191 | for actions such as examining an object more carefully. You\ 192 | don't want lots of repeated descriptions, so making the link\ 193 | a once link is more user friendly.

\ 194 |

If you have more than one link to the same action, then all\ 195 | matching links will be removed, so you don't have to worry about\ 196 | the player having an alternative way to carry out the action.

\ 197 |

After you've clicked the link, let's\ 198 | move on.

", 199 | { 200 | actions: { 201 | "one-time-action": "

As I said, one time actions are\ 202 | mostly used to describe something in\ 203 | more detail, where you don't want the\ 204 | same descriptive text repeated over and\ 205 | over

" 206 | } 207 | } 208 | ), 209 | qualities: new undum.SimpleSituation( 210 | "

Let's talk about the character.\ 211 | The character is described by a series of qualities. These\ 212 | are numeric values that can describe anything from natural abilities\ 213 | to how much of a resource the character controls. Qualities are\ 214 | shown in the box on the right of the text.

\ 215 | \ 216 |

The qualities there are those you started the game with. When you\ 217 | go to the next situation, keep your\ 218 | eyes on the character panel. You'll notice I'll give you a boost to\ 219 | your stamina quality. This process is animated and highlighted to\ 220 | draw your attention to it. You could also get a boost of skill\ 221 | by carrying out this action as many\ 222 | times as you like.

", 223 | { 224 | heading: "Qualities and the Character", 225 | tags: ["topic"], 226 | displayOrder: 4, 227 | actions: { 228 | "skill-boost": function(character, system, action) { 229 | system.setQuality("skill", character.qualities.skill+1); 230 | } 231 | }, 232 | exit: function(character, system, to) { 233 | system.setQuality("stamina", character.qualities.stamina+1); 234 | } 235 | } 236 | ), 237 | "quality-types": new undum.SimpleSituation( 238 | "

Not all the qualities in the character panel are displayed as\ 239 | numeric. Internally they are all numeric, but different qualities\ 240 | get to choose how to display themselves. So 'Luck', for example, is\ 241 | displayed as words (based on the FUDGE RPG's adjective scale),\ 242 | and 'Novice' is using just a check-mark.

\ 243 | \ 244 |

To see how Luck changes, try using this\ 245 | luck-boosting action or this\ 246 | luck-reducing action. Notice that\ 247 | luck uses a numeric bonus when it runs out of words. There are a range\ 248 | of different display types provided with Undum, and you can easily\ 249 | add your own too.

\ 250 | \ 251 |

When you leave this situation,\ 252 | I'll set 'Novice' to zero. Watch\ 253 | the character panel, and you'll see that Novice decides it doesn't\ 254 | need to be displayed any more and will be removed. You will also see\ 255 | that when the last\ 256 | quality in a group is removed ('Novice' is in the 'Progress' group),\ 257 | then the group heading is also removed. You can tell Undum what\ 258 | group each quality belongs to, and what order they should be listed.\ 259 |

", 260 | { 261 | actions: { 262 | "luck-boost": function(character, system, action) { 263 | system.setQuality("luck", character.qualities.luck+1); 264 | }, 265 | "luck-reduce": function(character, system, action) { 266 | system.setQuality("luck", character.qualities.luck-1); 267 | } 268 | }, 269 | exit: function(character, system, to) { 270 | system.setQuality("novice", 0); 271 | } 272 | } 273 | ), 274 | "character-text": new undum.SimpleSituation( 275 | "

Character Text

\ 276 |

Above the list of qualities is a short piece of text, called\ 277 | the character-text. This describes the character in some way. It\ 278 | can be set by any action or when entering or leaving a situation.\ 279 | It is just regular HTML content, as for all text in Undum. It can\ 280 | also contain Undum links, so this is another place you can put\ 281 | actions that the character can carry out over a long period of time.\ 282 |

\ 283 |

Let's go back to the\ 284 | topic list. As you do, I'll change the\ 285 | character text. Notice that it is highlighted, just the same as\ 286 | when a quality is altered.

", 287 | { 288 | exit: function(character, system, to) { 289 | system.setCharacterText( 290 | "

We're nearing the end of the road.

" 291 | ); 292 | } 293 | } 294 | ), 295 | progress: new undum.SimpleSituation( 296 | "

Sometimes you want to make the change in a quality into a more\ 297 | significant event. You can do this by animating the change in\ 298 | quality. If you boost your\ 299 | stamina, you will see the stamina change in the normal\ 300 | way in the character panel. But you will also see a progress\ 301 | bar appear and animate below.

", 302 | { 303 | tags: ["topic"], 304 | heading: "Showing a Progress Bar", 305 | displayOrder: 5, 306 | actions: { 307 | // I'm going indirect here - the link carries out an 308 | // action, which then uses doLink to directly change 309 | // the situation. This isn't the recommended way (I 310 | // could have just changed situation in the link), but 311 | // it illustrates the use of doLink. 312 | "boost-stamina-action": function(character, system, action) { 313 | system.doLink("boost-stamina"); 314 | } 315 | }, 316 | exit: function(character, system, to) { 317 | system.animateQuality( 318 | 'stamina', character.qualities.stamina+1 319 | ); 320 | } 321 | } 322 | ), 323 | "boost-stamina": new undum.SimpleSituation( 324 | "

\ 325 | \ 326 | The progress bar is also useful in situations where the\ 327 | character block is displaying just the whole number of a quality,\ 328 | whereas some action changes a fraction. If the quality is displaying\ 329 | the character's level, for example, you might want to show a progress\ 330 | bar to indicate how near the character is to levelling up.

\ 331 | \ 332 |

After a few seconds, the progress bar disappears, to keep the\ 333 | focus on the text. Undum isn't designed for games where a lot of\ 334 | statistic management is needed. If you want a change to be part\ 335 | of the permanent record of the game, then write it in text.

\ 336 | \ 337 |

Let's return to the topic list.

" 338 | ), 339 | // Again, we'll retrieve the text we want from the HTML file. 340 | "saving": new undum.Situation({ 341 | enter: function(character, system, from) { 342 | system.write($("#s_saving").html()); 343 | }, 344 | tags: ["topic"], 345 | displayOrder: 6, 346 | optionText: "Saving and Loading" 347 | }), 348 | 349 | "implicit-boost": new undum.SimpleSituation( 350 | "

Your luck has been boosted, check the\ 351 | list of options to see if they have changed.

", 352 | { 353 | tags: ["example"], 354 | enter: function(character, system, from) { 355 | system.animateQuality("luck", character.qualities.luck+1) 356 | system.doLink('example-choices'); 357 | }, 358 | optionText: "Boost Your Luck", 359 | displayOrder: 1, 360 | canView: function(character, system, host) { 361 | return character.qualities.luck < 4; 362 | } 363 | } 364 | ), 365 | "implicit-drop": new undum.SimpleSituation( 366 | "

Your luck has been reduced, check the\ 367 | list of options to see if they have changed.

", 368 | { 369 | tags: ["example"], 370 | enter: function(character, system, from) { 371 | system.animateQuality("luck", character.qualities.luck-1) 372 | system.doLink('example-choices'); 373 | }, 374 | optionText: "Reduce Your Luck", 375 | displayOrder: 2, 376 | canView: function(character, system, host) { 377 | return character.qualities.luck > -4; 378 | } 379 | } 380 | ), 381 | "high-luck-only": new undum.SimpleSituation( 382 | "

Your luck is higher than 'fair'. The link to this \ 383 | situation would not\ 384 | have appeared if it were lower.

", 385 | { 386 | tags: ["example"], 387 | enter: function(character, system, from) { 388 | system.doLink('example-choices'); 389 | }, 390 | optionText: "High Luck Option", 391 | displayOrder: 3, 392 | canView: function(character, system, host) { 393 | return character.qualities.luck > 0; 394 | } 395 | } 396 | ), 397 | "low-luck-only": new undum.SimpleSituation( 398 | "

Your luck is lower than 'fair'. The link to this situation \ 399 | appears whether\ 400 | it is low or high, but can only be chosen if it is low. It does this\ 401 | by defining a canChoose method.

", 402 | { 403 | tags: ["example"], 404 | enter: function(character, system, from) { 405 | system.doLink('example-choices'); 406 | }, 407 | optionText: "Low Luck Option (requires low luck to be clickable)", 408 | displayOrder: 3, 409 | canChoose: function(character, system, host) { 410 | return character.qualities.luck < 0; 411 | } 412 | } 413 | ), 414 | 415 | "last": new undum.SimpleSituation( 416 | "

Where to Go Now

\ 417 |

So that's it. We've covered all of Undum. This situation is the\ 418 | end, because it has no further links. The 'The End' message is\ 419 | just in the HTML output of this situation, it isn't anything special\ 420 | to Undum

\ 421 | \ 422 |

I've added an\ 423 | inspiration quality to your character list. Its time for you to\ 424 | crack open the game file and write your own story.

\ 425 |

The End

", 426 | { 427 | tags: ["topic"], 428 | optionText: "Finish the Tutorial", 429 | displayOrder: 8, 430 | enter: function(character, system, from) { 431 | system.setQuality("inspiration", 1); 432 | system.setCharacterText( 433 | "

You feel all inspired, why not have a go?

" 434 | ); 435 | } 436 | } 437 | ) 438 | }; 439 | 440 | // --------------------------------------------------------------------------- 441 | /* The Id of the starting situation. */ 442 | undum.game.start = "start"; 443 | 444 | // --------------------------------------------------------------------------- 445 | /* Here we define all the qualities that our characters could 446 | * possess. We don't have to be exhaustive, but if we miss one out then 447 | * that quality will never show up in the character bar in the UI. */ 448 | undum.game.qualities = { 449 | skill: new undum.IntegerQuality( 450 | "Skill", {priority:"0001", group:'stats'} 451 | ), 452 | stamina: new undum.NumericQuality( 453 | "Stamina", {priority:"0002", group:'stats'} 454 | ), 455 | luck: new undum.FudgeAdjectivesQuality( // Fudge as in the FUDGE RPG 456 | "Luck", 457 | {priority:"0003", group:'stats'} 458 | ), 459 | 460 | inspiration: new undum.NonZeroIntegerQuality( 461 | "Inspiration", {priority:"0001", group:'progress'} 462 | ), 463 | novice: new undum.OnOffQuality( 464 | "Novice", {priority:"0002", group:'progress', onDisplay:"✓"} 465 | ) 466 | }; 467 | 468 | // --------------------------------------------------------------------------- 469 | /* The qualities are displayed in groups in the character bar. This 470 | * determines the groups, their heading (which can be null for no 471 | * heading) and ordering. QualityDefinitions without a group appear at 472 | * the end. It is an error to have a quality definition belong to a 473 | * non-existent group. */ 474 | undum.game.qualityGroups = { 475 | stats: new undum.QualityGroup(null, {priority:"0001"}), 476 | progress: new undum.QualityGroup('Progress', {priority:"0002"}) 477 | }; 478 | 479 | // --------------------------------------------------------------------------- 480 | /* This function gets run before the game begins. It is normally used 481 | * to configure the character at the start of play. */ 482 | undum.game.init = function(character, system) { 483 | character.qualities.skill = 12; 484 | character.qualities.stamina = 12; 485 | character.qualities.luck = 0; 486 | character.qualities.novice = 1; 487 | character.qualities.inspiration = 0; 488 | system.setCharacterText("

You are starting on an exciting journey.

"); 489 | }; 490 | -------------------------------------------------------------------------------- /games/media/games/tutorial/tutorial.game.ru.js: -------------------------------------------------------------------------------- 1 | // Перевод на русский: Oreolek, 2013 2 | // --------------------------------------------------------------------------- 3 | // Отредактируйте этот файл, чтобы описать вашу игру. Он должен иметь не 4 | // менее четырёх разделов данных: undum.game.situations, undum.game.start, 5 | // undum.game.qualities, и undum.game.init. 6 | // --------------------------------------------------------------------------- 7 | 8 | /* Уникальный идентификатор вашей игры. Он никогда не отображается. 9 | * Я использую UUID, но вы можеет использовать всё что гарантированно 10 | * уникально (URL, которым вы владеете, или вариацию вашего адреса 11 | * e-mail, например).*/ 12 | undum.game.id = "349baf43-9ade-49a8-86d0-24e3de3ce042"; 13 | 14 | /* A string indicating what version of the game this is. Versions are 15 | * used to control saved-games. If you change the content of a game, 16 | * the saved games are unlikely to work. Changing this version number 17 | * prevents Undum from trying to load the saved-game and crashing. */ 18 | undum.game.version = "1.0"; 19 | 20 | /* Ситуации, в которых может быть игра. Каждая имеет уникальный ID. */ 21 | 22 | // Строки локализации 23 | undum.language["ru"]["start_title"] = "Знакомство с Undum"; 24 | undum.language["ru"]["situations_title"] = "Из чего сделаны игры Undum"; 25 | undum.language["ru"]["links_title"] = "Смена наполнения"; 26 | undum.language["ru"]["sticky_title"] = "Разные виды ссылок"; 27 | undum.language["en"]["qualities_title"] = "Qualities and the Character"; 28 | undum.language["ru"]["qualities_title"] = "Качества и персонаж"; 29 | undum.language["en"]["progress_title"] = "Showing a Progress Bar"; 30 | undum.language["ru"]["progress_title"] = "Отображение прогресса"; 31 | undum.language["en"]["saving_title"] = "Saving and Loading"; 32 | undum.language["ru"]["saving_title"] = "Сохранение и загрузка"; 33 | undum.language["en"]["last_title"] = "Finish the Tutorial"; 34 | undum.language["ru"]["last_title"] = "Закончить обучение"; 35 | 36 | undum.game.situations = { 37 | start: new undum.SimpleSituation( 38 | "

"+"start_title".l()+"

\ 39 | \ 40 |

Добро пожаловать в обучающую игру по Undum. Undum - это инструмент для написания\ 41 | гипертекстовой интерактивной литературы. Он обладает некоторыми уникальными возможностями\ 42 | и визуальным дизайном, который поддерживает повествующие игры.

\ 43 | \ 44 |

Гипертекстовая интерактивная литература - это цифровой эквивалент книгам\ 45 | Choose Your Own Adventure (CYOA), которые были популярны в\ 46 | 1980х. История рассказывается кусочками, и вы выбираете из нескольких \ 47 | вариантов, чтобы продвинуть её дальше. Тем не менее, в отличие от книжной формы, цифровая\ 48 | версия даёт вам намного больше гибкости, чтобы рассказать богатые истории\ 49 | и представить более интересные элементы игры.

\ 50 | \ 51 |

А сейчас давайте продолжим обучение.\ 52 | Нажмите на эту ссылку, чтобы продвинуться дальше.

" 53 | ), 54 | // NB: Ситуация 'hub', основной список тем, задана полностью в HTML, и не редактируется вручную 55 | // через словарь game.situations в этом файле. 56 | 57 | // Для разнообразия, здесь мы задаём ситуацию, используя высокоуровневый тип Situation. 58 | // Это прекрасный подход, чтобы сгенерировать текст, просто найдя его в HTML документе. 59 | // Для статичного текста в этом больше смысла, чем записывать его просто так. 60 | situations: new undum.Situation({ 61 | enter: function(character, system, from) { 62 | system.write($("#s_situations").html()); 63 | }, 64 | tags: ["topic"], 65 | optionText: "situations_title".l(), 66 | displayOrder: 1 67 | }), 68 | todo: new undum.SimpleSituation( 69 | "

В ситуации может произойти две вещи. Либо персонаж\ 70 | покидает ситуацию и входит в другую, либо\ 71 | он выполняет некоторое действие. Действия могут\ 72 | выполнять некоторые вычисления, могут выводить какие-нибудь результаты, но\ 73 | все они возвращают персонажа обратно в ту же ситуацию.

\ 74 | \ 75 |

Когда вы создаёте свою игру, используйте ситуации, чтобы отразить\ 76 | изменения в том, что може сделать персонаж. Так, вы можете изменить ситуацию если персонаж\ 77 | тянет рычаг, чтобы открыть люк, например. Действия\ 78 | предназначены для ситуаций, где персонаж может изучать вещи более пристально\ 79 | , а может быть улучшить свою магию, выпив зелья.\ 80 | Это вещи, которые не влияют на мир вокруг них.

\ 81 | \ 82 |

Ситуации генерируют содержимое, когда в них входят (метод enter),\ 83 | выходят (exit), и когда они получают действие (act).\ 84 | Вы можете написать код, который будет генерировать текст любым удобным вам способом, \ 85 | чтобы отображаемое содержимое могло быть полностью динамичным, \ 86 | принимая во внимание текущее состояние персонажа.

\ 87 | Содержимое - это обычный HTML, поэтому вы можете использовать все HTML теги, чтобы выделить\ 88 | что-то в тексте или добавить иллюстрации.Это даёт вам огромную гибкость. Например, так как Undum\ 89 | нацелен на HTML 5, вы можете использовать теги audio или\ 90 | video, чтобы вставить музыкальное или видео оформление.

\ 91 | \ 92 |

Убедитесь в том, что вы выполнили действие выше,\ 93 | а затем продолжайте.

", 94 | { 95 | actions: { 96 | 'do-something': "

Браво, вы выполнили действие.\ 97 | Заметьте, что ссылки для этой ситуации всё ещё активны.\ 98 | Это означает,что вы можете нажать на них, чтобы\ 99 | выполнить действие ещё раз.

" 100 | } 101 | } 102 | ), 103 | links: new undum.SimpleSituation( 104 | "

Между каждым кусочком нового текста Undum вставляет отчётливую линию\ 105 | на полях. Она позволяет вам быстро увидеть всё, что было выдано\ 106 | в качестве результата вашего последнего клика.\ 107 | Это очень удобно для маленьких устройств, или когда появилось\ 108 | очень много текста. Также окно перемещается таким образом, что начало\ 109 | нового содержимого приходится настолько близко к верхнему краю окна, насколько возможно.\ 110 | Это также сделано для того, чтобы помочь вам читать натуральнее.

\ 111 | \ 112 |

Если вы внимательно смотрели, вы заметили, что некоторые части\ 113 | текста исчезали, когда вы выходили из ситуаций. Это не баг!\ 114 | Одна из целей Undum - это дать геймдизайнерам возможность сделать\ 115 | запись игры связным рассказом.\ 116 | Тем не менее, вам часто нужны кусочки текста, которые ничего не делают, но просто\ 117 | предлагают читателю выбор.\ 118 | Для этого Undum определяет специальный CSS класс для добавления в ваш HTML\ 119 | (помните, сгенерированное содержимое - это просто HTML). Этот класс называется transient\ 120 | и может быть применён к параграфам, div'ам или просто\ 121 | span'ам (как вот этот).

\ 122 | \ 123 |

также вы могли заметить, что, когда вы перемещаетесь в другую ситуацию, все ссылки\ 124 | в предыдущей превращаются в обычный текст. Это сделано для того, чтобы не дать вам\ 125 | отступить и попробовать предыдущие варианты ответа, когда вы уже выбрали один из них.\ 126 | В других системах гипертекстовой интерактивной литературы это делается при помощи полной очистки предыдущих страниц.\ 127 | Но это не позволяет вам вернуться и перечитать вашу историю.

\ 128 | \ 129 |

Тема \""+"sticky_title".l()+"\" содержит больше информации об этих ссылках.\ 130 | Вернёмся к списку тем.

.", 131 | { 132 | heading: "links_title".l(), 133 | diplayOrder: 2, 134 | tags: ["topic"] 135 | } 136 | ), 137 | sticky: new undum.SimpleSituation( 138 | "

В Undum существует три типа ссылок. Мы уже видели первые два:\ 139 | ссылки смены ситцаций и ссылки, вызывающие действие. Когда вы\ 140 | включаете ссылку в ваш текст, undum распознаёт её и оформляет\ 141 | подобающим образом. Если вы создаёте ссылку с атрибутом HTML href,\ 142 | содержащим только имя ('зал', например), то она отправит персонажа\ 143 | в ситуацию с этим именем. Ссылки с двумя компонентами ('зал/посмотреть-картину')\ 144 | отправят персонажа в новую ситуацию и тогда вызовут действие в\ 145 | текущей ситуации. Вы можете заменить имя ситуации точкой ('./посмотреть-картину'). В\ 146 | любом случае, если персонаж уже находится в этой ситуации, то её метод enter\ 147 | ещё раз вызываться не будет.

\ 148 | \ 149 | \ 150 |

Наконец, третий тип ссылок - это обычная гиперссылка. Если ваша\ 151 | ссылка не состоит из одного элемента или пары элементов, как описано выше, то\ 152 | Undum догадается, что вы имели в виду обычную гиперссылку. Например, \ 153 | как в этом случае.\ 154 | Если ваша ссылка всё-таки выглядит как ссылка Undum, то вы\ 155 | всё ещё можете заставить Undum не интерпретировать её как ситуацию или действие, добавив\ 156 | CSS класс raw к тегу HTML a.\ 157 | Ссылкам, которые не относятся к классу raw, но угадываются как\ 158 | не-Undum, класс raw добавится автоматически перед отображением.\ 159 | Это позволит вам выделить внешние ссылки особым образом, как мы сделали в этой игре.

\ 160 | \ 161 |

В последней ситуации я сказал, что вы можете остановить ссылки от превращения в обычный текст, когда\ 162 | вы меняете ситуацию. Это делается при помощи другого класса\ 163 | CSS: sticky. Когда вы\ 164 | покинете эту ситуацию, вы заметите, что внешняя ссылка осталась активной.\ 165 | Это позволяет вам предлагать ссылки, которые останутся рабочими в течение всего рассказа,\ 166 | например, ссылка заклинания телепортации домой.

", 167 | { 168 | tags: ["topic"], 169 | displayOrder: 3, 170 | heading: "sticky_title".l() 171 | } 172 | ), 173 | oneshot: new undum.SimpleSituation( 174 | "

Ещё одна последняя настройка ссылок. Если вы назначите ссылке CSS класс\ 175 | once, то она исчезнет после нажатия. Это используется \ 176 | (проверьте сами) для\ 177 | действий, которые вы хотите сделать однократными. Нет смысла использовать\ 178 | 'once' на ссылках ситуаций, потому что они всё равно превратятся в текст, как только вы на них нажмёте\ 179 | ( конечно, если они не помечены как sticky).

Однократные ссылки полезны\ 180 | для таких действий, как внимательное изучение объекта. Вам не захочется\ 181 | повторять описание много раз, поэтому сделать ссылку однократной было бы\ 182 | дружелюбнее для пользователя.

\ 183 |

Если у вас более, чем одна ссылка относится к одному действию, то все соответсвующие ссылки\ 184 | будут удалены, так что вам не придётся заботиться о игроке, который найдёт способ\ 185 | ещё раз вызвать действие.

\ 186 |

После того, как вы нажмёте на ссылку, мы \ 187 | продолжим.

", 188 | { 189 | actions: { 190 | "one-time-action": "

Как я уже говорил, однократные действия\ 191 | в большинстве своём используются для описания\ 192 | чего-то в деталях, когда вы не хотите повторять\ 193 | один и тот же текст описания в разных местах рассказа.

" 194 | } 195 | } 196 | ), 197 | qualities: new undum.SimpleSituation( 198 | "

Хватит о ситуациях, давайте поговорим о персонаже. Персонаж описывается\ 199 | при помощи серии качеств. Это численные значения, которые могут описать\ 200 | всё от врождённых способностей до количества ресурсов,подконтрольных персонажу.\ 201 | Качества показаны в блоке справа от текста.

\ 202 | \ 203 |

Качества в этом блоке - это те, с которыми вы начали игру. Когда вы\ 204 | перейдёте к следующей ситуации, не отводите глаз\ 205 | от панели персонажа. Вы заметите, что я подниму ваше качество Ловкости.\ 206 | Этот процесс анимирован и подсвечен, чтобы привлечь ваше внимание.\ 207 | Также вы можете получить повышение Силы, выполнив\ 208 | это действие столько раз, сколько захочется.

", 209 | { 210 | heading: "qualities_title".l(), 211 | tags: ["topic"], 212 | displayOrder: 4, 213 | actions: { 214 | "skill-boost": function(character, system, action) { 215 | system.setQuality("skill", character.qualities.skill+1); 216 | } 217 | }, 218 | exit: function(character, system, to) { 219 | system.setQuality("stamina", character.qualities.stamina+1); 220 | } 221 | } 222 | ), 223 | "quality-types": new undum.SimpleSituation( 224 | "

Не все качества в панели персонажа отображаются как числа. Внутри они\ 225 | все числовые, но разные качества могут выбирать, как отображать себя. Например,\ 226 | Удача отображается словами (на основе шкалы прилагательных FUDGE RPG)\ 227 | ,а «Новичок» использует лишь галочку.

\ 228 | \ 229 |

Чтобы увидеть, как изменяется Удача, попробуйте использовать это\ 230 | удачное действие или это\ 231 | неудачное действие. Заметьте, как Удача\ 232 | использует числовой бонус, когда у неё заканчиваются слова. В Undum\ 233 | существует несколько различных видов отображения, и вы можете легко добавить своё.

\ 234 | \ 235 |

Когда вы покинете эту ситуацию,\ 236 | я поставлю «Новичок» на ноль. Смотрите на панель персонажа, и вы\ 237 | увидите, что Новичок решает, что ему более не нужно быть видимым, и удаляет себя.\ 238 | Вы также увидите,что,когда последнее качество в группе удаляется(а «Новичок» находится в группе «Прогресс»),\ 239 | тогда заголовок группы также удаляется. Вы можете сообщить Undum, какой группе\ 240 | принадлежит каждое из качеств, и в каком порядке они должны стоять.\ 241 |

", 242 | { 243 | actions: { 244 | "luck-boost": function(character, system, action) { 245 | system.setQuality("luck", character.qualities.luck+1); 246 | }, 247 | "luck-reduce": function(character, system, action) { 248 | system.setQuality("luck", character.qualities.luck-1); 249 | } 250 | }, 251 | exit: function(character, system, to) { 252 | system.setQuality("novice", 0); 253 | } 254 | } 255 | ), 256 | "character-text": new undum.SimpleSituation( 257 | "

Текст персонажа

\ 258 |

Над списком качеств находится коротенький кусочек текста, называемый\ 259 | текстом персонажа. Он как-то описывает героя. Он может быть задан\ 260 | любым действием или когда вы входите в или покидаете ситуацию.\ 261 | Это обычное содержимое HTML, как и любой другой текст в Undum.\ 262 | Он также может содержать ссылки Undum, так что это - ещё одно место, куда\ 263 | вы можете поместить действия, которые персонаж может выполнять в течение долгого периода времени.\ 264 |

\ 265 |

Давайте вернёмся к списку.\ 266 | А пока вы это сделаете, я сменю\ 267 | текст персонажа. Заметьте, как он подсвечивается, в точности как и качество,\ 268 | когда изменяется.

", 269 | { 270 | exit: function(character, system, to) { 271 | system.setCharacterText( 272 | "

Мы приближаемся к концу дороги.

" 273 | ); 274 | } 275 | } 276 | ), 277 | progress: new undum.SimpleSituation( 278 | "

Иногда вы хотите сделать изменение качества более заметным событием.\ 279 | Вы можете сделать это, анимировав его. Если вы\ 280 | поднимете вашу Ловкость,\ 281 | то вы увидите, что Ловкость изменяется как обычно в панели персонажа.\ 282 | Но вы также увидите появление и анимацию полоски прогресса чуть ниже.

", 283 | { 284 | tags: ["topic"], 285 | heading: "progress_title".l(), 286 | displayOrder: 5, 287 | actions: { 288 | // Здесь я иду по неявному пути - ссылка выдаёт действие, 289 | // которое затем использует doLink, чтобы напрямую изменить 290 | // ситуацию. Это не рекомендованный путь (я мог бы 291 | // просто изменить ситуацию по ссылке, но он демонстрирует 292 | // использование doLink. 293 | "boost-stamina-action": function(character, system, action) { 294 | system.doLink("boost-stamina"); 295 | } 296 | }, 297 | exit: function(character, system, to) { 298 | system.animateQuality( 299 | 'stamina', character.qualities.stamina+1 300 | ); 301 | } 302 | } 303 | ), 304 | "boost-stamina": new undum.SimpleSituation( 305 | "

Полоса прогресса также удобна в ситуациях, где блок персонажа\ 306 | показывает только общее число качеств, а тем временем какое-нибудь действие меняет\ 307 | одно из них. Например, вы можете захотеть показать прогресс персонажа к следующему уровню - \ 308 | имея уровень персонажа качеством, конечно.

\ 309 | \ 310 | \ 311 |

После нескольких секунд, полоса прогресса исчезает, чтобы оставить\ 312 | внимание на тексте. Undum не сделан для игр, где нужно большое управление\ 313 | статистиками. Если вы хотите, чтобы изменение было частью постоянной\ 314 | записи игры, напишите его в тексте.

\ 315 | \ 316 |

Давайте почитаем про что-нибудь ещё.

" 317 | ), 318 | // Опять, мы получаем текст из HTML файла. 319 | "saving": new undum.Situation({ 320 | enter: function(character, system, from) { 321 | system.write($("#s_saving").html()); 322 | }, 323 | tags: ["topic"], 324 | displayOrder: 6, 325 | optionText: "saving_title".l(), 326 | }), 327 | "implicit-boost": new undum.SimpleSituation( 328 | "

Ваша Удача была увеличена, проверьте список \ 329 | ссылок, вдруг они изменились?.

", 330 | { 331 | tags: ["example"], 332 | enter: function(character, system, from) { 333 | system.animateQuality("luck", character.qualities.luck+1) 334 | system.doLink('example-choices'); 335 | }, 336 | optionText: "Увеличить вашу Удачу", 337 | displayOrder: 1, 338 | canView: function(character, system, host) { 339 | return character.qualities.luck < 4; 340 | } 341 | } 342 | ), 343 | "implicit-drop": new undum.SimpleSituation( 344 | "

Ваша Удача была уменьшена, проверьте список \ 345 | ссылок, вдруг они изменились?.

", 346 | { 347 | tags: ["example"], 348 | enter: function(character, system, from) { 349 | system.animateQuality("luck", character.qualities.luck-1) 350 | system.doLink('example-choices'); 351 | }, 352 | optionText: "Уменьшить вашу Удачу", 353 | displayOrder: 2, 354 | canView: function(character, system, host) { 355 | return character.qualities.luck > -4; 356 | } 357 | } 358 | ), 359 | "high-luck-only": new undum.SimpleSituation( 360 | "

Ваша Удача выше чем 'нормально'. Ссылка на эту ситуацию не появилась бы, \ 361 | если бы вам она была меньше.

", 362 | { 363 | tags: ["example"], 364 | enter: function(character, system, from) { 365 | system.doLink('example-choices'); 366 | }, 367 | optionText: "Выбор Большой Удачи", 368 | displayOrder: 3, 369 | canView: function(character, system, host) { 370 | return character.qualities.luck > 0; 371 | } 372 | } 373 | ), 374 | "low-luck-only": new undum.SimpleSituation( 375 | "

Ваша Удача ниже, чем 'нормально'. Ссылка на эту ситуацию появляется \ 376 | в любом случае, но вы можете выбрать её только если вы сбросите Удачу.\ 377 | Это проверяет метод canChoose.

", 378 | { 379 | tags: ["example"], 380 | enter: function(character, system, from) { 381 | system.doLink('example-choices'); 382 | }, 383 | optionText: "Выбор Низкой Удачи (треубет низкую Удачу для нажатия)", 384 | displayOrder: 3, 385 | canChoose: function(character, system, host) { 386 | return character.qualities.luck < 0; 387 | } 388 | } 389 | ), 390 | 391 | "last": new undum.SimpleSituation( 392 | "

Куда теперь

\ 393 |

Итак, это всё. Мы показали весь Undum. Эта ситуация - конечная, поскольку\ 394 | в ней нет дальнейших ссылок. Сообщение «Конец» - это просто HTML вывод ситуации.\ 395 | В нём нет ничего особенного для Undum.

\ 396 | \ 397 |

Я добавил качество вдохновления в ваш листок персонажа.\ 398 | Настало время для вас вскрыть файл с игрой и написать свою собственную историю.

\ 399 |

Конец

", 400 | { 401 | tags: ["topic"], 402 | optionText: "last_title".l(), 403 | displayOrder: 8, 404 | enter: function(character, system, from) { 405 | system.setQuality("inspiration", 1); 406 | system.setCharacterText( 407 | "

Вы чувствуете себя вдохновлённым, почему бы не попробовать?

" 408 | ); 409 | } 410 | } 411 | ) 412 | }; 413 | 414 | // --------------------------------------------------------------------------- 415 | /* Идентификатор стартовой ситуации. */ 416 | undum.game.start = "start"; 417 | 418 | // --------------------------------------------------------------------------- 419 | /* Здесь мы определяем все качества, присущие нашим персонажам. 420 | * Мы не должны быть исчерпывающими, но если мы что-то пропустим, то 421 | * это качество никогда не покажется в панели персонажа. */ 422 | undum.game.qualities = { 423 | skill: new undum.IntegerQuality( 424 | "Сила", {priority:"0001", group:'stats'} 425 | ), 426 | stamina: new undum.NumericQuality( 427 | "Ловкость", {priority:"0002", group:'stats'} 428 | ), 429 | luck: new undum.FudgeAdjectivesQuality( // Fudge as in the FUDGE RPG 430 | "Удача", 431 | {priority:"0003", group:'stats'} 432 | ), 433 | 434 | inspiration: new undum.IntegerQuality( 435 | "Вдохновение", {priority:"0001", group:'progress'} 436 | ), 437 | novice: new undum.OnOffQuality( 438 | "Новичок", {priority:"0002", group:'progress', onDisplay:"✓"} 439 | ) 440 | }; 441 | 442 | // --------------------------------------------------------------------------- 443 | /* Качества отображаются по группам в панели персонажа. Эта функция определяет группы, 444 | * их заголовки (которые могут быть null - нет заголовка), и порядок. 445 | * Качества без группы появляются в конце. Описание качества, 446 | * приналежащее к несуществующей группе, является ошибкой.*/ 447 | undum.game.qualityGroups = { 448 | stats: new undum.QualityGroup(null, {priority:"0001"}), 449 | progress: new undum.QualityGroup('Прогресс', {priority:"0002"}) 450 | }; 451 | 452 | // --------------------------------------------------------------------------- 453 | /* Эта функция запускается перед началом игры. Обычно она используется 454 | * для того, чтобы настроить персонажа перед игрой. */ 455 | undum.game.init = function(character, system) { 456 | character.qualities.skill = 12; 457 | character.qualities.stamina = 12; 458 | character.qualities.luck = 0; 459 | character.qualities.novice = 1; 460 | system.setCharacterText("

Вы начинаете захватывающее путешествие.

"); 461 | }; 462 | -------------------------------------------------------------------------------- /games/media/games/tutorial/tutorial.game.de.js: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // Bearbeite diese Datei, um dein Spiel zu erstellen. Sie sollte 3 | // mindestens die folgenden vier Abschnitte enthalten: 4 | // undum.game.situations, undum.game.start, 5 | // undum.game.qualities, und undum.game.init. 6 | // --------------------------------------------------------------------------- 7 | 8 | /* Eine eindeutige Kennung fuer das Spiel (wird nicht im Spiel angezeigt) 9 | * Man kann eine UUID benutzen oder etwas anderes, was garantiert einzigartig 10 | * ist, z.B. eine eigene URL oder eine Variation der eigenen E-Mail-Adresse). */ 11 | undum.game.id = "349baf43-9ade-49a8-86d0-24e3de3ce072"; 12 | 13 | /* Eine Zeichenkette, die angibt, um welche Version des Spiels es sich handelt. 14 | * Dies ist wichtig fuers Speichern und Laden: Wenn man den Inhalt eines Spiels 15 | * aendert, funktioniert ein gespeicherter Spielstand wahrscheinlich nicht mehr. 16 | * Wenn man nach einer Aenderung des Inhalts diese Versionsnummer ebenfalls 17 | * aendert, dann wird damit verhindert, dass Undum einen Spielstand aus einer 18 | * alten Version zu laden versucht und dabei eventuell abstuerzt. */ 19 | undum.game.version = "1.0"; 20 | 21 | /* Die Situationen, in denen das Spiel sich befinden kann. Jede hat eine eindeutige Kennung. */ 22 | undum.game.situations = { 23 | start: new undum.SimpleSituation( 24 | "

Los geht's mit Undum

\ 25 | \ 26 |

Willkommen beim Undum-Tutorium. Mit Undum kann man\ 27 | interaktive Literatur in Hypertext-Form schreiben. Seine Features\ 28 | und seine visuelle Gestaltung sind dazu gedacht, Spiele zu machen, die eine\ 29 | Geschichte erzählen.

\ 30 | \ 31 |

Interaktive Literatur im Hypertext-Format ist im Grunde eine digitale\ 32 | Ausgabe der beliebten Spielbücher. Die Geschichte wird in kleinen\ 33 | Abschnitten erzählt, und der Spieler wählt eine von mehreren\ 34 | Möglichkeiten aus, wie es weitergehen soll. Anders als die Bücher\ 35 | von früher ist die digitale Ausgabe aber viel flexibler - man kann\ 36 | variantenreichere Geschichten erzählen und interessantere\ 37 | Spiel-Elemente einbauen.

\ 38 | \ 39 |

Erstmal geht's jetzt mit dem Tutorium weiter.\ 40 | Klick auf diesen Link, um fortzufahren.

" 41 | ), 42 | // Zur Abwechslung definieren wir hier mal eine Situation mit Hilfe des 43 | // allgemeinen "Situation"-Datentyps. Auf diese Weise erzeugen wir 44 | // Text, indem wir ihn aus der HTML-Datei des Spiels ziehen. Das ist bei statischem Text 45 | // sinnvoller als ihn hier komplett auszuschreiben. 46 | rooms: new undum.Situation({ 47 | enter: function(character, system, from) { 48 | system.write($("#s_rooms").html()); 49 | } 50 | }), 51 | todo: new undum.SimpleSituation( 52 | "

Zwei Dinge können in einer Situation passieren. Entweder\ 53 | verlässt die Spielfigur diese Situation und gerät\ 54 | in eine andere, oder sie führt eine Aktion aus.\ 55 | Aktionen können z.B. Daten verarbeiten und Ergebnisse anzeigen,\ 56 | aber letztlich kehrt die Spielfigur dabei immer wieder in dieselbe Situation\ 57 | zurück.

\ 58 | \ 59 |

Wenn du dein Spiel entwirfst, dann benutze Situationen, um darzustellen,\ 60 | dass die Spielfigur nun etwas Neues tun kann. Wechsle z.B. in eine neue\ 61 | Situation, wenn der Spieler einen Hebel zieht und dadurch eine Falltür\ 62 | öffnet. Aktionen sind dagegen für Situationen gedacht, in denen\ 63 | die Spielfigur Dinge genauer untersuchen kann, oder z.B. einen Zaubertrank\ 64 | trinken kann, um ihren Magie-Wert aufzustocken. Kurz gesagt Handlungen,\ 65 | die den Zustand der Umgebung nicht verändern.

\ 66 | \ 67 |

Situationen erzeugen Text, wenn man in sie hineingerät (enter),\ 68 | wenn man sie verlässt (exit), und wenn man in ihnen\ 69 | eine Aktion ausführt (act) (die kursiv gesetzten\ 70 | Begriffe sind die Namen der drei Methoden, die im Code dafür benutzt werden).\ 71 | Wenn man möchte, kann man mit eigenem Code den erzeugten\ 72 | Inhalt beliebig verändern; der Inhalt ist dynamisch, dadurch\ 73 | kann man z.B. den momentanen Zustand der Spielfigur berücksichtigen.\ 74 | Der Inhalt besteht einfach aus normalem HTML, also kann man die\ 75 | üblichen HTML-Tags benutzen, um Text\ 76 | fett oder kursiv zu machen,\ 77 | oder um Bilder einzubauen. Hier hast du viele Möglichkeiten,\ 78 | z.B. könntest du audio oder\ 79 | video Tags nehmen, um Multimedia-Inhalte anzuzeigen\ 80 | (Undum ist auf HTML5 ausgelegt).

\ 81 | \ 82 |

Probiere bitte die Aktion oben aus,\ 83 | dann geht's weiter.

", 84 | { 85 | actions: { 86 | // wenn eine Aktions-Kennung einen Bindestrich enthaelt wie die Folgende, 87 | // dann sollte sie in (einfachen oder doppelten) Anfuehrungsstrichen stehen 88 | 'do-something': "

Du hast die Aktion ausgeführt, gut gemacht.\ 89 | Wie du siehst, sind die Links für diese\ 90 | Situation immer noch aktiv. Das bedeutet,\ 91 | dass du sie noch einmal anklicken und damit\ 92 | die Aktion ausführen kannst.

" 93 | } 94 | } 95 | ), 96 | links: new undum.SimpleSituation( 97 | "

Veränderlicher Inhalt

\ 98 |

Vor jedem neuen Textabschnitt fügt Undum eine\ 99 | kleine Linie am Rand ein. Damit kann der Spieler auf einen Blick\ 100 | erfassen, was durch den letzten Klick neu ausgegeben wurde.\ 101 | Das ist besonders auf mobilen Geräten mit kleinen Bildschirmen\ 102 | nützlich, oder wenn eine Menge Text auf einmal\ 103 | dazugekommen ist. Die Anzeige wird außerdem so gescrollt,\ 104 | dass der Anfang des neuen Abschnitts nah am oberen Fensterrand\ 105 | ist.\ 106 | Dies soll einen natürlichen Lesefluss ermöglichen.

\ 107 | \ 108 |

Wenn du genau hingeschaut hast, ist dir bestimmt aufgefallen,\ 109 | dass Teile des Textes verschwunden sind, als in neue Situationen\ 110 | gewechselt wurde. Das ist kein Bug! Es ist eines der Ziele von Undum,\ 111 | dass Entwickler ihr Spiel so gestalten können, dass das\ 112 | Transkript des Spielablaufs sich wie eine durchgehende, kohärente\ 113 | Erzählung liest. Allerdings benötigt man oft Textschnipsel, die\ 114 | nur dazu da sind, dem Spieler Auswahlmöglichkeiten anzuzeigen.\ 115 | Undum stellt dafür eine besondere CSS-Klasse bereit, die man in\ 116 | den eigenen HTML-Inhalt einbaut (wie gesagt, sämtlicher Inhalt\ 117 | besteht ja einfach aus HTML). Diese Klasse heißt transient\ 118 | ('vorübergehend') und man kann sie auf Absätze, divs, oder\ 119 | spans (so wie hier) anwenden.

\ 120 | \ 121 |

Vielleicht ist dir auch aufgefallen, dass beim Wechsel in eine neue\ 122 | Situation alle Links in der vorherigen Situation sich in normalen Text verwandeln.\ 123 | Das verhindert, dass der Spieler zurückgeht und eine andere Option\ 124 | ausprobiert, nachdem er sich bereits für eine entschieden hat.\ 125 | In anderen Hypertext-Systemen wird das oft so gehandhabt, dass\ 126 | der Inhalt früherer Seiten komplett gelöscht wird.\ 127 | Allerdings kann man in solchen Systemen dann nicht mehr\ 128 | zurückblättern, um die gesamte Geschichte zu lesen.

\ 129 | \ 130 |

Gleich erfährst du mehr über diese Links\ 131 | und wie du ihre Wirkungsweise ändern kannst.

" 132 | ), 133 | sticky: new undum.SimpleSituation( 134 | "

Links

\ 135 |

Es gibt drei Arten von Links in Undum. Zwei kennen wir schon:\ 136 | Links, mit denen man in eine neue Situation wechselt, und Links,\ 137 | die eine Aktion ausführen. Wenn du einen Link in deinen\ 138 | Ausgabetext einbaust, dann verarbeitet Undum den Link und\ 139 | sorgt für die richtige Verbindung. Wenn du einen Link machst,\ 140 | in dem das HTML-Attribut href nur einen Namen\ 141 | enthält (z.B. 'ballsaal'), dann schickt der Link die Spielfigur\ 142 | zu der Situation, die diesen Namen hat. Links mit zwei Teilen\ 143 | ('ballsaal/betrachte-gemaelde', z.B.) schicken die Spielfigur in\ 144 | die so benannte Situation und führen dann\ 145 | die genannte Aktion aus ('betrachte-gemaelde').\ 146 | Um innerhalb der momentanen Situation eine Aktion\ 147 | ausführen zu lassen, kannst du den Situationsnamen durch\ 148 | einen Punkt ersetzen (z.B. so: './betrachte-gemaelde').\ 149 | In allen Fällen gilt: Wenn die Spielfigur schon in der Situation\ 150 | ist, dann wird deren enter-Methode nicht noch einmal aufgerufen.

\ 151 | \ 152 | \ 153 |

Die dritte Art von Link ist ein allgemeiner Hyperlink. Wenn dein\ 154 | Link nicht aus nur einem Element bzw. zwei Elementen besteht wie\ 155 | oben, dann vermutet Undum, dass es sich um einen normalen Hyperlink\ 156 | handelt, so wie bei diesem.\ 157 | Wenn du einen Link hast, der wie ein Undum-Link aussieht,\ 158 | kannst du Undum trotzdem zwingen, ihn nicht als Aktions- oder\ 159 | Situations-Link zu behandeln, indem du dem HTML a-Tag\ 160 | die CSS-Klasse raw hinzufügst.\ 161 | Denjenigen Links, die nicht die raw-Klasse haben, und die aber als\ 162 | Nicht-Undum-Links angesehen werden (wie der oben), wird raw\ 163 | hinzugefügt, bevor sie angezeigt werden. Das erlaubt dir, externe\ 164 | Links in einem anderen Stil darzustellen (siehe oben).

\ 165 | \ 166 |

In der letzten Situation wurde erwähnt, dass man verhindern\ 167 | kann, dass Links bei einem Situationswechsel in normalen Text verwandelt\ 168 | werden. Das geht mit einer weiteren CSS-Klasse: sticky ('klebrig').\ 169 | Wenn du diese Situation verlässt, wirst du sehen,\ 170 | dass der externe Link aktiv bleibt. Dadurch ist es möglich, Optionen\ 171 | anzubieten, die während der gesamten Geschichte gültig bleiben, z.B.\ 172 | einen Zauberspruch, mit dem die Spielfigur sich nach Hause teleportieren kann.

" 173 | ), 174 | oneshot: new undum.SimpleSituation( 175 | "

Es gibt noch folgende Variante für die Link-Darstellung:\ 176 | Wenn man einem Link die CSS-Klasse once ('einmal') verpasst,\ 177 | dann verschwindet der Link, nachdem er angeklickt wurde. Das kannst\ 178 | du (wie bei diesem Link)\ 179 | für Aktionen benutzen, die nur einmal ausführbar sein\ 180 | sollen. Bei Situations-Links ist once witzlos, denn die werden ja\ 181 | sowieso in normalen Text verwandelt, sobald sie angeklickt wurden\ 182 | (außer natürlich, wenn sie als sticky\ 183 | deklariert sind).

Die once-Links eignen sich z.B.\ 184 | für eine Aktion wie das genaue Untersuchen eines Gegenstands.\ 185 | Es sähe nicht schön aus, wenn so eine Beschreibung eines\ 186 | Gegenstands mehrfach wiederholt würde - ein\ 187 | once-Link ist da benutzerfreundlicher.

\ 188 |

Wenn mehr als ein Link zu derselben Aktion führt,\ 189 | dann werden alle diese Links deaktiviert, also brauchst du\ 190 | dir keine Sorgen zu machen, dass der Spieler die Aktion\ 191 | noch einmal auf anderem Wege ausführen könnte.

\ 192 |

Nachdem du oben auf den Link geklickt hast, können wir\ 193 | weitermachen.

", 194 | { 195 | actions: { 196 | "one-time-action": "

Wie gesagt, einmalige Aktionen\ 197 | werden oft dazu genutzt, Dinge detaillierter\ 198 | zu beschreiben; man möchte dabei nicht,\ 199 | dass ein und derselbe Beschreibungstext\ 200 | über und über wiederholt wird.

" 201 | } 202 | } 203 | ), 204 | qualities: new undum.SimpleSituation( 205 | "

Eigenschaften

\ 206 |

Genug von Situationen! Lass uns über die Spielfigur sprechen.\ 207 | Die Spielfigur wird durch ihre qualities ('Eigenschaften')\ 208 | bestimmt. Das sind Zahlenwerte, die für alles mögliche\ 209 | stehen können, z.B. Fähigkeiten oder Ressourcen,\ 210 | die die Figur besitzt. Die Eigenschaften werden in dem Feld\ 211 | rechts neben dem Haupttext angezeigt.

\ 212 | \ 213 |

Die Eigenschaften dort sind bei Spielbeginn zugewiesen worden. Wenn\ 214 | du gleich zur nächsten Situation gehst,\ 215 | schau genau auf das Feld. Wie du siehst, bekommst du\ 216 | einen Stärke-Bonus. Dieser Vorgang wird animiert und hervorgehoben,\ 217 | damit der Spieler darauf aufmerksam wird. Wenn du möchtest,\ 218 | kannst du dir auch noch mit dieser Aktion\ 219 | einen Gewandtheits-Bonus holen - so oft wie du willst.

", 220 | { 221 | actions: { 222 | "skill-boost": function(character, system, action) { 223 | system.setQuality("skill", character.qualities.skill+1); 224 | } 225 | }, 226 | exit: function(character, system, to) { 227 | system.setQuality("stamina", character.qualities.stamina+1); 228 | } 229 | } 230 | ), 231 | // wenn eine Situations-Kennung einen Bindestrich enthaelt wie die Folgende, 232 | // dann sollte sie in Anfuehrungsstrichen stehen 233 | "quality-types": new undum.SimpleSituation( 234 | "

Nicht alle Eigenschaften werden im Spielfiguren-Feld als\ 235 | Zahlen aufgelistet. Intern sind sie zwar alle Zahlen, aber sie\ 236 | können verschieden angezeigt werden. 'Glück' z.B.\ 237 | wird in Worten dargestellt (die auf der Skala aus dem\ 238 | FUDGE-Rollenspielsystem beruhen), und 'Novize' hat nur\ 239 | ein Häkchen.

\ 240 | \ 241 |

Du kannst sehen, wie sich das Glück verändert, wenn\ 242 | du mal diese glücks-erhöhende\ 243 | und diese glücks-verringernde\ 244 | Aktion probierst. Wenn die Wort-Skala ausgeschöpft ist, wird\ 245 | ein zahlenmäßer Bonus bzw. Malus angehängt.\ 246 | Undum bringt eine Reihe verschiedener Anzeigetypen mit, und man\ 247 | kann auch einfach eigene hinzufügen.

\ 248 | \ 249 |

Wenn du diese Situation verlässt,\ 250 | wird 'Novize' auf Null gesetzt. Im Spielfiguren-Feld kannst du gleich sehen,\ 251 | dass 'Novize' verschwindet, und wenn die letzte Eigenschaft aus einer\ 252 | Gruppe entfernt wird ('Novize' ist in der Gruppe 'Fortschritt'), dann\ 253 | verschwindet die Gruppen-Überschrift ebenfalls. Man kann selbst\ 254 | festlegen, zu welcher Gruppe jede Eigenschaft gehört, und in\ 255 | welcher Reihenfolge sie aufgelistet werden sollen.\ 256 |

", 257 | { 258 | actions: { 259 | "luck-boost": function(character, system, action) { 260 | system.setQuality("luck", character.qualities.luck+1); 261 | }, 262 | "luck-reduce": function(character, system, action) { 263 | system.setQuality("luck", character.qualities.luck-1); 264 | } 265 | }, 266 | exit: function(character, system, to) { 267 | system.setQuality("novice", 0); 268 | } 269 | } 270 | ), 271 | "character-text": new undum.SimpleSituation( 272 | "

Spielfiguren-Text

\ 273 |

Über der Liste mit den Eigenschaften steht ein kurzer Text,\ 274 | den wir den Spielfigur-Text ('character-text') nennen wollen. Dieser kann\ 275 | dazu genutzt werden, eine kurze Beschreibung der Spielfigur zu geben.\ 276 | Der Text kann durch eine Aktion oder beim Wechsel zwischen Situationen\ 277 | geändert werden. Es handelt sich, wie auch sonst überall in\ 278 | Undum, um normales HTML. Man kann auch hier Undum-Links einbauen,\ 279 | z.B. Aktionen, die die Spielfigur zu jedem Zeitpunkt ausführen kann.\ 280 |

\ 281 |

Lass uns zur\ 282 | nächsten Situation gehen.\ 283 | Währenddessen ändere ich den Spielfiguren-Text. Er wird dabei\ 284 | hervorgehoben, so wie wenn man eine Eigenschaft ändert.

", 285 | { 286 | exit: function(character, system, to) { 287 | system.setCharacterText( 288 | "

Wir nähern uns dem Ende des Wegs.

" 289 | ); 290 | } 291 | } 292 | ), 293 | progress: new undum.SimpleSituation( 294 | "

Fortschritt anzeigen

\ 295 |

Manchmal möchte man, dass es als wichtiges Ereignis\ 296 | wahrgenommen wird, wenn sich eine Eigenschaft ändert. Deshalb\ 297 | kann man die Änderung auch animiert darstellen. Wenn du\ 298 | deine Stärke steigerst,\ 299 | wirst du die Änderung wie üblich im Spielfiguren-Feld sehen,\ 300 | aber zusätzlich erscheint unten eine animierte Fortschrittsanzeige.

", 301 | { 302 | actions: { 303 | // Hier gehen wir einen indirekten Weg - der Link fuehrt eine Aktion aus, 304 | // die ihrerseits doLink benutzt, um die Situation direkt zu aendern. 305 | // Das ist nicht die empfohlene Methode (man haette die Situation 306 | // einfach im Link aendern koennen), aber sie zeigt, wie man doLink verwendet. 307 | "boost-stamina-action": function(character, system, action) { 308 | system.doLink("boost-stamina"); 309 | } 310 | }, 311 | exit: function(character, system, to) { 312 | system.animateQuality( 313 | 'stamina', character.qualities.stamina+1 314 | ); 315 | } 316 | } 317 | ), 318 | "boost-stamina": new undum.SimpleSituation( 319 | "

Die Fortschrittsanzeige ist auch nützlich, wenn im\ 320 | Spielfigur-Feld der Wert einer Eigenschaft als ganze Zahl\ 321 | angezeigt wird, und eine Aktion nur eine Nachkomma-Stelle der\ 322 | Eigenschaft ändert. Wenn eine Eigenschaft z.B. die\ 323 | Erfahrungsstufe der Spielfigur anzeigt und die Figur dann einige\ 324 | Erfahrungspunkte hinzugewinnt, dann kann man die\ 325 | Fortschrittsanzeige benutzen, um anzuzeigen, wie nah die\ 326 | Figur an der nächsthöheren Stufe ist.

\ 327 | \ 328 | \ 329 |

Nach ein paar Sekunden verschwindet die Fortschrittsanzeige,\ 330 | damit der Leser nicht zu sehr vom Text abgelenkt wird. Undum ist\ 331 | nicht unbedingt dafür gemacht, z.B. ein Strategiespiel\ 332 | darzustellen, bei dem man ständig viele Statistiken im Blick haben\ 333 | muss. Wenn eine Eigenschaftsänderung ein permanenter Teil\ 334 | der aufgezeichneten Geschichte werden soll, dann sollte sie im\ 335 | Text auftauchen.

\ 336 | \ 337 |

Nun sind wir bald am Ende des Weges angelangt. Bisher\ 338 | haben wir uns jedoch nur linear durch dieses Tutorium fortbewegt - \ 339 | von einer Situation zur nächsten, ganz ohne Entscheidungen.\ 340 | Undum ist aber natürlich darauf ausgelegt, Erzählungen zu\ 341 | schreiben, die sich verzweigen und wieder zusammenfließen.

\ 342 | \ 343 |

Hier ein winziges Beispiel zur\ 344 | Veranschaulichung - wähle einen der Zweige:

\ 345 | \ 349 |

Der Optionen-Block oben ist eine normale HTML\ 350 | 'unordered list' (ul-Tag) mit der speziellen\ 351 | options-Klasse. Klick auf eine der Zeilen (die gesamte Breite\ 352 | ist anklickbar), um die entsprechende Option zu wählen.

" 353 | ), 354 | one: new undum.SimpleSituation( 355 | "

Option Eins

\ 356 |

Du hast Option Eins gewählt, was wahrscheinlich auch besser so ist, denn\ 357 | Option Zwei ist in schlecht gereimtem Koptisch verfasst.\ 358 |

\ 359 |

Von hier ist es nur ein kleiner Schritt zum\ 360 | Rest des Tutoriums.

" 361 | ), 362 | "two": new undum.SimpleSituation( 363 | "

Option Zwei

\ 364 |

Du hast Option Zwei gewählt - die gefällt mir auch am besten.\ 365 | Die Photos, die bei Option Eins auftauchen, sind einfach zu verstörend.\ 366 | Fingernägel sollte man nicht auf so eine Art verbiegen...

\ 367 |

Von hier ist es nur ein kleiner Schritt zum\ 368 | Rest des Tutoriums.

" 369 | ), 370 | // An dieser Stelle ziehen wir den gewuenschten Text wieder aus der HTML-Datei. 371 | "saving": new undum.Situation({ 372 | enter: function(character, system, from) { 373 | system.write($("#s_saving").html()); 374 | } 375 | }), 376 | "last": new undum.SimpleSituation( 377 | "

Wie geht es nun weiter

\ 378 |

So, das war's mit unserem Rundgang durch Undum. Diese Situation\ 379 | ist das Ende, denn sie stellt keine weiteren Links bereit. Der 'Ende'-Hinweis unten ist\ 380 | einfach Teil des ausgegebenen HTMLs in dieser Situation, er ist nicht speziell\ 381 | in Undum eingebaut.

\ 382 | \ 383 |

Deine Figur hat jetzt eine Eigenschaft hinzugewonnen - Inspiration!\ 384 | Zeit für dich, die Spieldatei zu öffnen und deine eigene Geschichte\ 385 | zu schreiben!

\ 386 |

Ende

", 387 | { 388 | enter: function(character, system, from) { 389 | system.setQuality("inspiration", 1); 390 | system.setCharacterText( 391 | "

Du fühlst dich inspiriert - versuch's doch auch einmal!

" 392 | ); 393 | } 394 | } 395 | ) 396 | }; 397 | 398 | // --------------------------------------------------------------------------- 399 | /* Die Kennung der Start-Situation. */ 400 | undum.game.start = "start"; 401 | 402 | // --------------------------------------------------------------------------- 403 | /* Hier definieren wir die Eigenschaften (qualities) unserer Hauptfigur. 404 | * Wir muessen sie nicht erschoepfend auflisten, aber wenn eine hier nicht 405 | * auftaucht, dann wird sie nicht in der "Spielfigur"-Leiste der Benutzeroberflaeche angezeigt. */ 406 | undum.game.qualities = { 407 | skill: new undum.IntegerQuality( 408 | "Gewandtheit", {priority:"0001", group:'stats'} 409 | ), 410 | stamina: new undum.NumericQuality( 411 | "Stärke", {priority:"0002", group:'stats'} 412 | ), 413 | luck: new undum.FudgeAdjectivesQuality( // Fudge bezieht sich auf das FUDGE-Rollenspielsystem 414 | "Glück", 415 | {priority:"0003", group:'stats'} 416 | ), 417 | inspiration: new undum.NonZeroIntegerQuality( 418 | "Inspiration", {priority:"0001", group:'progress'} 419 | ), 420 | novice: new undum.OnOffQuality( 421 | "Novize", {priority:"0002", group:'progress', onDisplay:"✓"} 422 | ) 423 | }; 424 | 425 | // --------------------------------------------------------------------------- 426 | /* Die Eigenschaften werden in der "Spielfigur"-Leiste in Gruppen angezeigt. 427 | * Hier werden die Gruppen festgelegt und ihre Reihenfolge und ihre Ueberschriften 428 | * bestimmt (wobei null bedeutet, dass die Gruppe keine Ueberschrift hat). 429 | * Eigenschaftsdefinitionen ohne Gruppe erscheinen am Ende. 430 | * Wenn eine Eigenschaftsdefinition einer nicht-existenten Gruppe 431 | * zugeordnet wird, ist das ein Fehler. */ 432 | undum.game.qualityGroups = { 433 | stats: new undum.QualityGroup(null, {priority:"0001"}), 434 | progress: new undum.QualityGroup('Fortschritt', {priority:"0002"}) 435 | }; 436 | 437 | // --------------------------------------------------------------------------- 438 | /* Diese Funktion wird vor dem Start des Spiels ausgefuehrt. Normalerweise 439 | * konfiguriert man hiermit die Eigenschaften der Hauptfigur bei Spielbeginn. */ 440 | undum.game.init = function(character, system) { 441 | character.qualities.skill = 12; 442 | character.qualities.stamina = 12; 443 | character.qualities.luck = 0; 444 | character.qualities.novice = 1; 445 | character.qualities.inspiration = 0; 446 | system.setCharacterText("

Du bist am Beginn einer aufregenden Reise.

"); 447 | }; 448 | -------------------------------------------------------------------------------- /docs/src/API.md: -------------------------------------------------------------------------------- 1 | # Javascript API 2 | 3 | # `Character` 4 | 5 | The character is created for you, but is passed into most of the 6 | functions that you define. It consists of an object with no methods 7 | and two properties: 8 | 9 | #### `qualities` 10 | 11 | The qualities object maps quality identifiers to their current 12 | value. Your code finds the current value associated with a quality by 13 | reading from this object, for example: 14 | 15 | var gold = character.qualities.gold; 16 | 17 | To set a quality, you have two choices. If you know the quality you 18 | want to set will not appear in the user interface, then you can set it 19 | directly: 20 | 21 | character.qualities.gold += 1; 22 | 23 | If it does appear on-screen, then this approach will mean that the 24 | character panel doesn't update, and the screen will be out of sync 25 | with the actual value. Instead it is better to use the `System` method 26 | `setQuality`, which also updates the UI: 27 | 28 | system.setQuality('gold', character.qualities.gold+1); 29 | 30 | It is fine to use `setQuality` if the quality isn't visible, making 31 | this the preferred option for all quality modifications. 32 | 33 | #### `sandbox` 34 | 35 | Not every bit of data you want to associate with a character fits 36 | nicely into a quality. The sandbox is a general storage space for any 37 | further data you want to store in the course of your game, for 38 | example: 39 | 40 | character.sandbox.roomsSearched.push('bathroom'); 41 | 42 | Sandbox data is never visible to the user, so you can use any data 43 | structures you like here, to any depth. 44 | 45 | # `System` 46 | 47 | The system represents the interface between your code and the 48 | user-interface. You don't create your own `System` object, it is 49 | passed into your code. 50 | 51 | #### `clearContent(elementSelector)` 52 | 53 | *Since version 2* 54 | 55 | Removes all content from the page, clearing the main content area. 56 | 57 | Although Undum is designed to keep the flow of the narrative on one 58 | page, sometimes you do need to start a new page, and this allows you 59 | to do so. 60 | 61 | The `elementSelector` is options. If you give it, then the DOM element 62 | matching the selector is cleared, rather than the whole document. 63 | 64 | #### `write(content, elementSelector)` 65 | 66 | Writes new content to the main flow of the story. The content you pass 67 | in must be either valid DOM elements already, or else be a string 68 | containing text in Display Content format. 69 | 70 | The `elementSelector` is optional. If you provide it, then the new 71 | content will be added after the DOM element in the document that 72 | matches the selector. This allows you to do out-of-order addition of 73 | content. Simply add a paragraph with an id in your game, then later 74 | you can give this id as a selector to write, and the new content will 75 | be inserted immediately following that paragraph, regardless of how 76 | much extra content has been added since that point. If no selector is 77 | given then `#content` is used, i.e. the content is added at the end of 78 | the document. The `writeBefore` method inserts content at the start of 79 | the document, or before a selector. 80 | 81 | The story will scroll to the start of the insertion point. If you do 82 | not wish to animate this scrolling, but just jump right there, you can 83 | switch off jQuery's animation system by adding `jQuery.fx.off=true` to 84 | your initialization code. This is particularly useful when debugging. 85 | 86 | #### `writeHeading(content, elementSelector)` 87 | 88 | Writes new content into the story and formats it as a heading. This 89 | method work exactly as `write`, but wraps the content you provide into 90 | a `h1` html tag. 91 | 92 | #### `writeBefore(content, elementSelector)` 93 | 94 | Writes content into the story. This method is identical to `write`, 95 | above, except that the content is written at the start of the story, 96 | or if a selector is given, inserted before the matching element. On 97 | browsers that support it, the story will be scrolled to the insertion 98 | point. 99 | 100 | #### `writeInto(content, elementSelector)` 101 | 102 | Writes content into the element matched by `elementSelector`. When 103 | used without specifying a selector, this method is identical to 104 | `write`. When a selector is supplied, the content is written as an 105 | additional child node of the matched element, instead of as a new 106 | node just after that element, as is the case with `write`. 107 | 108 | #### `replaceWith(content, elementSelector)` 109 | 110 | Replaces an element with `content`. If `elementSelector` isn't 111 | supplied, this will replace the entire situation with the given 112 | `content`. If it is, the matched element will be replaced, including 113 | the matched set of tags; `content` slides in place of the matched 114 | element in the DOM. 115 | 116 | #### `writeChoices(listOfSituationIds)` 117 | 118 | *Since version 2* 119 | 120 | Creates a standard block of choices, one for each of the given 121 | situation ids. The text used in the links will be whatever is returned 122 | by the situation's `optionText` method. In addition, if the 123 | situation's `canChoose` method returns `false`, then the option will 124 | be displayed, but will not be clickable. 125 | 126 | #### `getSituationIdChoices(listOfIdsOrTags, minChoices, maxChoices)` 127 | 128 | *Since version 2* 129 | 130 | This function is a complex and powerful way of compiling implicit 131 | situation choices. You give it a list of situation ids and situation 132 | tags. Tags should be prefixed with a hash # to differentiate them from 133 | situation ids. The function then considers all matching situations in 134 | descending priority order, calling their canView functions and 135 | filtering out any that should not be shown, given the current 136 | state. Without additional parameters the function returns a list of 137 | the situation ids at the highest level of priority that has any valid 138 | results. So, for example, if a tag #places matches three situations, 139 | one with priority 2, and two with priority 3, and all of them can be 140 | viewed in the current context, then only the two with priority 3 will 141 | be returned. This allows you to have high-priority situations that 142 | trump any lower situations when they are valid, such as situations 143 | that force the player to go to one destination if the player is out of 144 | money, for example. 145 | 146 | If a `minChoices` value is given, then the function will attempt to 147 | return at least that many results. If not enough results are available 148 | at the highest priority, then lower priorities will be considered in 149 | turn, until enough situations are found. In the example above, if we 150 | had a minChoices of three, then all three situations would be 151 | returned, even though they have different priorities. If you need to 152 | return all valid situations, regardless of their priorities, set 153 | minChoices to a large number, such as `Number.MAX_VALUE`, and leave 154 | maxChoices undefined. 155 | 156 | If a `maxChoices` value is given, then the function will not return any 157 | more than the given number of results. If there are more than this 158 | number of results possible, then the highest priority resuls will be 159 | guaranteed to be returned, but the lowest priority group will have to 160 | fight it out for the remaining places. In this case, a random sample 161 | is chosen, taking into account the frequency of each situation. So a 162 | situation with a frequency of 100 will be chosen 100 times more often 163 | than a situation with a frequency of 1, if there is one space 164 | available. Often these frequencies have to be taken as a guideline, 165 | and the actual probabilities will only be approximate. Consider three 166 | situations with frequencies of 1, 1, 100, competing for two 167 | spaces. The 100-frequency situation will be chosen almost every time, 168 | but for the other space, one of the 1-frequency situations must be 169 | chosen. So the actual probabilities will be roughly 50%, 50%, 170 | 100%. When selecting more than one result, frequencies can only be a 171 | guide. 172 | 173 | Before this function returns its result, it sorts the situations in 174 | increasing order of their `displayOrder` properties. 175 | 176 | 177 | #### `doLink(URL)` 178 | 179 | Carries out the action associated with the given URL, as if it had 180 | been the `href` of a HTML link that the user clicked. This allows you 181 | to procedurally change situation and carry out actions from your code. 182 | 183 | #### `rnd` 184 | 185 | This holds a general purpose random number generator. It is an object 186 | derived from the `Random` prototype, so see `Random` below for details 187 | on its API. 188 | 189 | #### `time` 190 | 191 | This is a numeric value holding the current time, in seconds, since 192 | the player began playing the game. This value is correctly propagated 193 | across saves, so it is the only way you should track timing. In 194 | particular you should never call `new Date()` and use that value to 195 | determine the outcome of any event. You can use the current date to 196 | display the current date, for example, but not to control what actions 197 | or situations are available. See the section on Loading and Saving for 198 | more details of why this is important. 199 | 200 | #### `setQuality(qualityId, newValue)` 201 | 202 | Sets the character's given quality to the given value. This function 203 | also updates the character panel, animating the change in value if 204 | that is necessary. Do not directly set quality values in the 205 | character, because the user-interface will not detect and reflect 206 | those changes. 207 | 208 | #### `animateQuality(qualityId, newValue, options)` 209 | 210 | Like `setQuality`, this function changes the current value of the 211 | given quality. In addition, however, it displays a progress bar that 212 | shows to the user how the value has changed. The `options` parameter 213 | should be an object containing options for how the bar should 214 | display. The available options are: 215 | 216 | - `from`: The proportion along the progress bar where the animation 217 | starts. Defaults to 0, valid range is 0-1. 218 | 219 | - `to`: The proportion along the progress bar where the animation 220 | ends. Defaults to 1, valid range is 0-1. 221 | 222 | - `showValue`: If `true` (the default) then the new value of the 223 | quality is displayed above the progress bar. 224 | 225 | - `displayValue`: If this is given, and `showValue` is `true`, then 226 | the given value is used above the progress bar. If this isn't 227 | given, and `showValue` is `true`, then the display value will be 228 | calculated from the `QualityDefinition`, as normal. This option is 229 | useful for qualities that don't have a definition, because they 230 | don't normally appear in the UI. 231 | 232 | - `title`: The title of the progress bar. If this is not given, then 233 | the title of the quality is used. As for `displayValue` this is 234 | primarily used when the progress bar doesn't have a 235 | `QualityDefinition`, and therefore doesn't have a title. 236 | 237 | - `leftLabel`, `rightLabel`: Underneath the progress bar you can place 238 | two labels at the left and right extent of the track. These can 239 | help to give scale to the bar. So if the bar signifies going from 240 | 10.2 to 10.5, you might label the left and right extents with "10" 241 | and "11" respectively and have the `from` and `to` value be 0.2 and 242 | 0.5 respectively. If these are not given, then the labels will be 243 | omitted. 244 | 245 | #### `setCharacterText(content)` 246 | 247 | Sets the block of character text that appears in the character 248 | panel. As for the `write` method, this text should be either valid DOM 249 | elements, or a string meeting the Display Content requirements. 250 | 251 | #### `clearLinks(URL)` 252 | 253 | Call this function with an Undum link URL (e.g. `ballroom`, or 254 | `ballroom/open-cabinet`). It will remove all occurrences of that link 255 | from the page. This is equivalent to what happens when you change 256 | situation, or when you click a link marked with the `once` CSS 257 | class. It allows you to control what options are available 258 | dynamically, from your code. 259 | 260 | ## `Random` 261 | 262 | The `Random` object provides a set of tools for simple random number 263 | generation, in a way that is guaranteed to work with the Loading and 264 | Saving functionality in Undum. An instance of `Random` is provided in 265 | the `rnd` property of the `System` object, so you will never need to 266 | create your own. It has the following methods: 267 | 268 | #### `random()` 269 | 270 | Generates a random number between 0 and 1, where 0 is inclusive, and 1 271 | is exclusive. You can use this to check against known probabilities, 272 | such as: 273 | 274 | if (system.rnd.random() > 0.5) { 275 | ... 276 | } 277 | 278 | To check for a 50/50 chance. 279 | 280 | #### `randomInt(min, max)` 281 | 282 | Return a random number between the given two values, where both values 283 | are inclusive. So `randomInt(2,3)` generates either 2 or 3. 284 | 285 | #### `dice(n, dx, plus)` 286 | 287 | Rolls _n_ dice with _dx_ sides and adds _plus_ to the result. This 288 | allows you to easily get results for rolls from regular RPG-style 289 | games, such as 3d6+2. The `plus` parameter may be negative or 290 | positive. 291 | 292 | #### `aveDice(n, plus)` 293 | 294 | Rolls _n_ averaging dice, and adds _plus_ to the result. Averaging dice 295 | are a special type of d6 with sides marked [2,3,3,4,4,5]. They represent 296 | the fact that most people are fairly average, and results should not lie 297 | at the extremes. 298 | 299 | #### `diceString(definition)` 300 | 301 | Rolls dice according to the given definition string. The string should 302 | be of the form xdy+z, where the x component and z component are 303 | optional. This rolls x dice of with y sides, and adds z to the result, 304 | the z component can also be negative: xdy-z. The y component can be 305 | either a number of sides, or can be the special values 'F', for a 306 | fudge die (with 3 sides, +,0,-), '%' for a 100 sided die, or 'A' for 307 | an averaging die (with sides 2,3,3,4,4,5). 308 | 309 | # `Situation` 310 | 311 | The `Situation` object is the prototype of all the situations in your 312 | game. It can be used directly, or through its more common derived 313 | type, `SimpleSituation`. The base `Situation` gives you maximum 314 | flexibility, but `SimpleSituation` provides more functionality and can 315 | produce terser code. 316 | 317 | #### `new Situation(options)` 318 | 319 | Creates a new situation. The options array can specify your 320 | implementation for any or all of the following methods of this class: 321 | 322 | - `enter` 323 | - `exit` 324 | - `act` 325 | - `optionText` 326 | - `canView` 327 | - `canChoose` 328 | 329 | (see below for explanations of those methods). This allows 330 | you to easily create situations that override certain behaviors with 331 | code such as: 332 | 333 | Situation({ 334 | enter: function(character, system, from) { 335 | ... your implementation ... 336 | } 337 | }); 338 | 339 | without having to subclass `Situation` to provide your own 340 | implementations. 341 | 342 | In addition the following options can be passed in. 343 | 344 | - `tags`: A list of tags with which to label this situation. These are 345 | primarily used to generate implicit lists of choices with 346 | `System.getSituationIdChoices`. Tags are arbitrary strings. You can 347 | pass a list of strings, or a single string. If you pass a single 348 | string, it will be split at spaces, commas and tabs to form a list of 349 | tags. For this reason, tags normally do not contain spaces, commas or 350 | tabs (though if you pass in a list, and don't expect Undum to do the 351 | splitting for you, you can include any characters you like in a tag). 352 | 353 | - `optionText` (as a string): If given as a string, rather than a 354 | function, this text will be returned whenever `optionText(...)` is 355 | called. 356 | 357 | - `displayOrder`: A numeric value, defaults to 1. When displaying 358 | lists of implicitly generated choices, the options be displayed in 359 | increasing value of this parameter. 360 | 361 | - `priority`: Can be any number, defaults to 1. When generating lists 362 | of choices implicitly, situations are considered in descending 363 | priority order. If higher priority situations can be displayed, lower 364 | priority situations will be hidden. See `System.getSituationIdChoices` 365 | for details of the algorithm. 366 | 367 | - `frequency`: Any number, defaults to 1. When generating lists of 368 | implicit choices, where there are more choices available that slots to 369 | display them, situations will be chosen randomly, with a probability 370 | based on this value. See `System.getSituationIdChoices` for details of 371 | the algorithm. 372 | 373 | #### `enter(character, system, from)` 374 | 375 | This is called when Undum enters a situation. The `character` and 376 | `system` are instances of `Character` and `System` as described 377 | above. The `from` parameter is a string containing the situation 378 | identifier for the situation that we're arriving from. 379 | 380 | This method is the most commonly overridden. It is commonly used to 381 | describe the current situation (by sending content to 382 | `system.write()`) and to update the character (by calling 383 | `system.setQuality()` or by changing data in the character's `sandbox` 384 | object).. 385 | 386 | #### `exit(character, system, to)` 387 | 388 | This method takes the same `character` and `system` parameters as 389 | `enter`. Its third parameter, `to`, is a string containing the 390 | identifier of the situation we're exiting to. 391 | 392 | #### `act(character, system, action)` 393 | 394 | This method again takes the same `character` and `system` parameters 395 | as before. Its third parameter is a string containing the action 396 | identifier corresponding to the link the player clicked. It is common 397 | to use an `if` statement or a `switch` to query this action identifier 398 | and decide what to do accordingly. For situations in which many 399 | different actions are possible, consider using the `SimpleSituation` 400 | prototype, which provides this switching behavior for you. 401 | 402 | #### `optionText(character, system, hostSituation)` 403 | 404 | This method is called by `System.writeChoices` to generate a label for 405 | links to this situation. The `hostSituation` is the situation that has 406 | asked for the choices to be displayed. 407 | 408 | #### `canView(character, system, hostSituation)` 409 | 410 | This method is called by `System.getSituationIdChoices` to determine 411 | whether this situation can be part of a list of choices in the current 412 | game state. It should return true or false. 413 | 414 | #### `canChoose(character, system, hostSituation)` 415 | 416 | This method is called by `System.writeChoices` to determine whether a 417 | link should be added to allow the user to enter this situation. If 418 | not, the choice will still appear, but will not be clickable. 419 | 420 | 421 | 422 | ## SimpleSituation 423 | 424 | This prototype builds on the basic `Situation`, providing tools to 425 | make it easy to output content in the `enter` method, and to switch 426 | between different functions depending on the action identifier passed 427 | into the `act` method. The `exit` method of `SimpleSituation` is 428 | exactly as for the base type `Situation`. 429 | 430 | #### `new SimpleSituation(content, options)` 431 | 432 | Creates a new simple situation that will display the given content 433 | when its `enter` method is called. The given options dictionary 434 | provides further control of the behavior of this type. Valid options 435 | are: 436 | 437 | - `enter`: Providing an enter function in the `options` parameter 438 | allows you to add additional behavior to the enter method. Your 439 | custom function will be called *in addition to* and *after* the 440 | default content is written to the screen. You cannot override 441 | `SimpleSituation`'s `enter` method by providing this function. To 442 | override the method, you would have to create a derived type. If 443 | you provide an `enter` function, it should have the same form as 444 | `Situation.enter`. 445 | 446 | - `act`: Pass in a function to add additional behavior to the act 447 | method. As for `enter`, your method is called *in addition to* and 448 | *after* the built-in functionality. 449 | 450 | - `exit`: Because `SimpleSituation` has no default behavior for 451 | `exit`, any function you pass in here will be the only exit 452 | behavior for the object you are creating. 453 | 454 | - `heading`: The `content` that you specify will be written out 455 | verbatim. You can include headings in this content. Often it is 456 | more convenient to pass in just the text in the `content` 457 | parameter. In that case you may define this `heading` parameter to 458 | display a heading before the text. Unlike `content`, this doesn't 459 | need to conform to the Display Content requirements. 460 | 461 | - `actions`: This should be an object that maps action identifiers to 462 | responses. A response should be either some Display Content to 463 | write to the screen, or a function that will process that 464 | request. These functions should have the same signature as the 465 | `Situation.act` method. Each function will only be called if the 466 | situation receives a call to `act` with its corresponding 467 | identifier. This allows you to simply define functions that only 468 | get called when particular actions happen. 469 | 470 | - `choices`: An optional list of tags and situation-ids, with tags 471 | prefixed by a has symbol to distinguish them from situation ids. If 472 | given, this will cause the SimpleSituation to output an implicit block 473 | of choices after the content. 474 | 475 | - `minChoices`: If you have given a `choices` definition, you can set 476 | this to an integer value to change the number of choices that will 477 | appear. See `System.getSituationIdChoices` for more information on 478 | how this affects the output. 479 | 480 | - `maxChoices`: If you have given a `choices` definition, you can set 481 | this to an integer value to change the number of choices that will 482 | appear. See `System.getSituationIdChoices` for more information on 483 | how this affects the output. 484 | 485 | An example `SimpleSituation` definition might be: 486 | 487 | new SimpleSituation( 488 | "

...content...

", 489 | { 490 | heading: "Title", 491 | actions: { 492 | listen: function(character, system, action) { 493 | if (character.qualities.hearing > 5) { 494 | system.write("

You hear a tinkling inside.

"); 495 | } else { 496 | system.write("

You hear nothing.

"); 497 | } 498 | }, 499 | search: "

You find nothing.

" 500 | } 501 | } 502 | ); 503 | 504 | notice how the `listen` function is responsible for its own output, 505 | where the `search` property is a string in Display Content format, 506 | ready for output. 507 | 508 | #### Functions in `SimpleSituation` 509 | 510 | Both the `content` and the `heading` of a simple situation can be 511 | provided either as plain text, or as a function. If you provide a 512 | function, then it will be called with no arguments, and it should 513 | return a string to use for the output. This enables `SimpleSituation` 514 | to be used with other formatting and templating systems. 515 | 516 | # QualityDefinition 517 | 518 | Quality definitions tell Undum how and where to display a quality in 519 | the character panel. Each quality definition has one method, `format`, 520 | which is responsible for converting a numeric quality value into a 521 | displayable quantity. 522 | 523 | You define your qualities in your `undum.game.qualities` property. 524 | 525 | 526 | #### `new QualityDefinition(title, options)` 527 | 528 | Creates a new `QualityDefinition`. It is rare to call this constructor 529 | yourself, most often one of the derived types of `QualityDefinition` 530 | are used. They are defined below. 531 | 532 | The `title` should be a string, and can contain HTML. It is used to 533 | label the quality in the character panel. It can be any string, it 534 | doesn't have to be in Display Content format. 535 | 536 | Options are passed in in the `options` parameter. The following 537 | options are available. 538 | 539 | - `priority`: A string used to sort qualities within their 540 | groups. When the system displays a list of qualities they will be 541 | sorted by this string. If you don't give a priority, then the title 542 | will be used, so you'll get alphabetic order. Normally you either 543 | don't give a priority, or else use a priority string containing 544 | 0-padded numbers (e.g. "00001"). 545 | 546 | - `group`: The identifier of a group in which to display this 547 | parameter. If a group is given, then it must be defined in your 548 | `undum.game.qualityGroups` property. 549 | 550 | - `extraClasses`: These classes will be attached to the `
` tag 551 | that surrounds the quality when it is displayed. A common use for 552 | this is to add icons representing the quality. In your CSS define a 553 | class for each icon, then pass those classes into the appropriate 554 | quality definitions. 555 | 556 | #### `format(character, value)` 557 | 558 | This is called by Undum to get a human readable string representing 559 | the given quality value for the given character. The method may return 560 | an empty string if the value has no need to be displayed, or it may 561 | return `null` if the quantity itself shouldn't be displayed. The 562 | difference here is significant. If your `QualityDefinition` returns 563 | the empty string, then the quality will appear in the character panel, 564 | but it will have no value marked. If it returns `null`, then it will 565 | be removed from the character panel entirely. 566 | 567 | Most commonly the `character` parameter is ignored, but in your own 568 | derived types you can take advantage of being able to access other 569 | information about the character. 570 | 571 | You may call this function yourself, but there is commonly no need. It 572 | will be called by Undum any time the corresponding quality needs to be 573 | displayed. 574 | 575 | ## `IntegerQuality` 576 | 577 | This is a derived type of `QualityDefinition` that displays the 578 | quality value by rounding it down to the nearest integer. This is 579 | ideal for most numeric statistics. 580 | 581 | ## `NonZeroIntegerQuality` 582 | 583 | This is a derived type of `IntegerQuality` that only displays its 584 | value when it is non-zero. If it is non-zero then it formats in the 585 | same way as `IntegerQuality`. Whereas `IntegerQuality` whould show 586 | zero values as '0', this type of quality displays nothing. 587 | 588 | ## `NumericQuality` 589 | 590 | This is a derived type of `QualityDefinition` that displays the 591 | quality value directly, as a full floating point value. 592 | 593 | ## `WordScaleQuality` 594 | 595 | Sometimes you want qualities displayed in words rather than 596 | numbers. This is a derived type of `QualityDefinition` that allows you 597 | to define words corresponding to possible quality values. 598 | 599 | #### `new WordScaleQuality(title, words, options)` 600 | 601 | The `title` parameter is exactly as for `QualityDefinition`. 602 | 603 | The `words` parameter determines what words will be used. It should be 604 | an array of strings. By default the first string will be used to 605 | represent a value of zero (after rounding down), and the second string 606 | a value of 1, and so on to the end of the array. Values outside the 607 | array are treated differently depending on the value of `useBonuses` 608 | in the `options` parameter. 609 | 610 | The `options` parameter supports the same three options as 611 | `QualityDefinition`. It also takes the following additional 612 | parameters: 613 | 614 | - `offset`: With offset=0 (the default), a quantity value of 0 will 615 | map to the first word, and so on. If offset is non-zero then the 616 | value given will correspond to the first word in the list. So if 617 | offset=4, then the first word in the list will be used for value=4, 618 | the second for value=5. You can specify a non-integer offset value, 619 | in this case the offset is applied *before* the value is rounded 620 | down. 621 | 622 | - `useBonuses`: If this is true (the default), then values outside the 623 | range of words will be constructed from the word and a numeric 624 | bonus. So with offset=0 and five words, the last of which is 625 | 'amazing', a score of six would give 'amazing+1'. if this is 626 | false, then the bonus would be omitted, so anything beyond 627 | 'amazing' is still 'amazing'. 628 | 629 | ## `FudgeAdjectivesQuality` 630 | 631 | This is a derived type of `WordScaleQuality` that doesn't require you 632 | to specify the words you wish to use. It uses the word scale from the 633 | Fudge RPG: "terrible", "poor", "mediocre", "fair", "good", "great" and 634 | "superb". 635 | 636 | #### `new FudgeAdjectivesQuality(title, options)` 637 | 638 | The parameters `title` and `options` are as for the `WordScaleQuality` 639 | constructor. The `offset` option defaults to -3, however (in 640 | `WordScaleQuality` it defaults to 0), making "fair" the display value 641 | for 0. 642 | 643 | ## `OnOffQuality` 644 | 645 | An `OnOffQuality` returns `null` from its `format` method 646 | (i.e. removes itself from the character panel) when the corresponding 647 | quality value is zero. Otherwise it returns the empty string (i.e. it 648 | is shown in the panel, but doesn't have a value label). See 649 | `QualityDisplay.format` above for more details on this distinction. 650 | 651 | #### `new OnOffQuality(title, options)` 652 | 653 | The constructor for this type is the same as for `QualityDefinition` 654 | from which it is derived. It accepts one extra option: 655 | 656 | - `onDisplay`: If given, then rather than displaying the empty string, 657 | it displays the given string when its corresponding value is 658 | non-zero. This can be used to display a check-mark, for example 659 | (`{onDisplay:"✓"}`), or even a HTML `img` tag. 660 | 661 | ## YesNoQuality 662 | 663 | A `YesNoQuality` displays one of two strings depending whether its 664 | value is zero or not. 665 | 666 | #### `new YesNoQuality(title, options)` 667 | 668 | The constructor for this type is the same as for `QualityDefinition` 669 | from which it is derived. It accepts two extra options: 670 | 671 | - `yesDisplay`, `noDisplay`: Either or both of these may be given. If 672 | they are given, then they should be set to a string, which will be 673 | used to indicate non-zero or zero values, respectively. By default 674 | "yes" and "no" are used. 675 | 676 | # `QualityGroup` 677 | 678 | A quality group defines a set of qualities that should be displayed 679 | together in the character panel, under an optional subheading. You 680 | could use quality groups to distinguish between qualities representing 681 | a character's innate abilities and their equipment, for example. 682 | 683 | You define your quality groups in your `undum.game.qualityGroups` 684 | property. 685 | 686 | 687 | #### `new QualityGroup(title, options)` 688 | 689 | Constructs a new quality group that will have the given `title` for a 690 | subheading. The title may be `null`, indicating that this group does 691 | not need a heading. 692 | 693 | The `options` parameter should be an object with the given optional 694 | parameters: 695 | 696 | - `priority`: A string used to sort quality groups. When the system 697 | displays more than one quality group, they will be sorted by this 698 | string. If you don't give a priority, then the title will be used, 699 | so you'll get alphabetic order. Normally you either don't give a 700 | priority, or else use a priority string containing 0-padded numbers 701 | (e.g. "00001"). 702 | 703 | - `extraClasses`: These classes will be attached to the `
` tag 704 | that surrounds the entire quality group when it is displayed. You 705 | can use this in addition to your CSS to radically change the way 706 | certain qualities are displayed. 707 | --------------------------------------------------------------------------------